summaryrefslogtreecommitdiffstats
path: root/src/jaegertracing/thrift/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/jaegertracing/thrift/lib')
-rw-r--r--src/jaegertracing/thrift/lib/Makefile.am127
-rw-r--r--src/jaegertracing/thrift/lib/as3/CMakeLists.txt68
-rw-r--r--src/jaegertracing/thrift/lib/as3/Makefile.am60
-rw-r--r--src/jaegertracing/thrift/lib/as3/README.md37
-rw-r--r--src/jaegertracing/thrift/lib/as3/build.gradle56
-rw-r--r--src/jaegertracing/thrift/lib/as3/coding_standards.md1
-rw-r--r--src/jaegertracing/thrift/lib/as3/gradle.properties23
-rw-r--r--src/jaegertracing/thrift/lib/as3/gradle/publishing.gradle96
-rw-r--r--src/jaegertracing/thrift/lib/as3/gradle/wrapper/gradle-wrapper.jarbin0 -> 55190 bytes
-rw-r--r--src/jaegertracing/thrift/lib/as3/gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xsrc/jaegertracing/thrift/lib/as3/gradlew172
-rw-r--r--src/jaegertracing/thrift/lib/as3/gradlew.bat84
-rw-r--r--src/jaegertracing/thrift/lib/as3/settings.gradle20
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/AbstractMethodError.as31
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/Set.as82
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TApplicationError.as106
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TBase.as67
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TError.as29
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TFieldRequirementType.as32
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TProcessor.as32
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/FieldMetaData.as57
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/FieldValueMetaData.as44
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/ListMetaData.as31
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/MapMetaData.as33
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/SetMetaData.as31
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/StructMetaData.as31
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TBinaryProtocol.as316
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TField.as43
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TList.as33
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TMap.as33
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TMessage.as42
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TMessageType.as28
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TProtocol.as124
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TProtocolError.as39
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TProtocolFactory.as27
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TProtocolUtil.as148
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TSet.as33
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TStruct.as31
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TType.as39
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/TFullDuplexHttpClient.as251
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/THttpClient.as134
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/TSocket.as194
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/TTransport.as127
-rw-r--r--src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/TTransportError.as37
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/CMakeLists.txt87
-rwxr-xr-xsrc/jaegertracing/thrift/lib/c_glib/Makefile.am118
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/README.md49
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/coding_standards.md5
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_dispatch_processor.c143
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_dispatch_processor.h95
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_multiplexed_processor.c346
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_multiplexed_processor.h114
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_processor.c45
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_processor.h76
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.c911
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.h72
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol_factory.c50
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol_factory.h56
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol.c1609
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol.h107
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol_factory.c140
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol_factory.h70
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_multiplexed_protocol.c158
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_multiplexed_protocol.h76
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol.c646
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol.h341
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_decorator.c623
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_decorator.h72
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_factory.c43
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_factory.h73
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_stored_message_protocol.c192
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_stored_message_protocol.h78
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/server/thrift_server.c195
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/server/thrift_server.h93
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/server/thrift_simple_server.c138
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/server/thrift_simple_server.h70
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift.c101
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift.h49
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift_application_exception.c277
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift_application_exception.h86
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift_struct.c52
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift_struct.h68
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport.c390
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport.h77
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport_factory.c55
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport_factory.h86
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_fd_transport.c265
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_fd_transport.h74
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport.c383
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport.h77
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport_factory.c55
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport_factory.h86
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_memory_buffer.c285
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_memory_buffer.h72
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_platform_socket.h120
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_server_socket.c307
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_server_socket.h91
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_server_transport.c62
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_server_transport.h89
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_socket.c427
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_socket.h73
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_ssl_socket.c804
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_ssl_socket.h218
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.c162
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.h176
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_transport_factory.c44
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_transport_factory.h71
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/test/CMakeLists.txt202
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/test/ContainerTest.thrift35
-rwxr-xr-xsrc/jaegertracing/thrift/lib/c_glib/test/Makefile.am324
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/test/glib.suppress64
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/test/testapplicationexception.c180
-rwxr-xr-xsrc/jaegertracing/thrift/lib/c_glib/test/testbinaryprotocol.c859
-rwxr-xr-xsrc/jaegertracing/thrift/lib/c_glib/test/testbufferedtransport.c325
-rwxr-xr-xsrc/jaegertracing/thrift/lib/c_glib/test/testcompactprotocol.c1293
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/test/testcontainertest.c529
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/test/testdebugproto.c941
-rwxr-xr-xsrc/jaegertracing/thrift/lib/c_glib/test/testfdtransport.c184
-rwxr-xr-xsrc/jaegertracing/thrift/lib/c_glib/test/testframedtransport.c323
-rwxr-xr-xsrc/jaegertracing/thrift/lib/c_glib/test/testmemorybuffer.c223
-rwxr-xr-xsrc/jaegertracing/thrift/lib/c_glib/test/testoptionalrequired.c227
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/test/testserialization.c95
-rwxr-xr-xsrc/jaegertracing/thrift/lib/c_glib/test/testsimpleserver.c122
-rwxr-xr-xsrc/jaegertracing/thrift/lib/c_glib/test/teststruct.c111
-rwxr-xr-xsrc/jaegertracing/thrift/lib/c_glib/test/testthrifttest.c112
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/test/testthrifttestclient.cpp639
-rwxr-xr-xsrc/jaegertracing/thrift/lib/c_glib/test/testtransportsocket.c361
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/test/testtransportsslsocket.c542
-rw-r--r--src/jaegertracing/thrift/lib/c_glib/thrift_c_glib.pc.in30
-rw-r--r--src/jaegertracing/thrift/lib/cl/Makefile.am40
-rw-r--r--src/jaegertracing/thrift/lib/cl/README.md253
-rw-r--r--src/jaegertracing/thrift/lib/cl/READMES/readme-cassandra.lisp64
-rwxr-xr-xsrc/jaegertracing/thrift/lib/cl/ensure-externals.sh16
-rw-r--r--src/jaegertracing/thrift/lib/cl/load-locally.lisp23
-rw-r--r--src/jaegertracing/thrift/lib/cl/test/make-test-binary.lisp31
-rw-r--r--src/jaegertracing/thrift/lib/cpp/3rdparty.props25
-rwxr-xr-xsrc/jaegertracing/thrift/lib/cpp/CMakeLists.txt191
-rwxr-xr-xsrc/jaegertracing/thrift/lib/cpp/Makefile.am256
-rwxr-xr-xsrc/jaegertracing/thrift/lib/cpp/README.md278
-rw-r--r--src/jaegertracing/thrift/lib/cpp/coding_standards.md4
-rw-r--r--src/jaegertracing/thrift/lib/cpp/libthrift.vcxproj366
-rw-r--r--src/jaegertracing/thrift/lib/cpp/libthrift.vcxproj.filters283
-rwxr-xr-xsrc/jaegertracing/thrift/lib/cpp/libthriftnb.vcxproj298
-rw-r--r--src/jaegertracing/thrift/lib/cpp/libthriftnb.vcxproj.filters75
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/TApplicationException.cpp81
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/TApplicationException.h115
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/TBase.h38
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/TDispatchProcessor.h141
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/TLogging.h195
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/TOutput.cpp128
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/TOutput.h60
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/TProcessor.h229
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/TToString.h114
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/Thrift.h133
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/VirtualProfiling.cpp425
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncBufferProcessor.h46
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncChannel.cpp36
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncChannel.h74
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncDispatchProcessor.h151
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncProcessor.h84
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncProtocolProcessor.cpp53
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncProtocolProcessor.h55
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/async/TConcurrentClientSyncInfo.cpp243
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/async/TConcurrentClientSyncInfo.h126
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/async/TEvhttpClientChannel.cpp156
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/async/TEvhttpClientChannel.h88
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/async/TEvhttpServer.cpp160
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/async/TEvhttpServer.h74
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Exception.h64
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/FunctionRunner.h118
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Monitor.cpp185
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Monitor.h126
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Mutex.cpp63
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Mutex.h89
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Thread.cpp37
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Thread.h174
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/ThreadFactory.cpp40
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/ThreadFactory.h76
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/ThreadManager.cpp583
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/ThreadManager.h213
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/TimerManager.cpp321
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/TimerManager.h137
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/processor/PeekProcessor.cpp131
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/processor/PeekProcessor.h83
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/processor/StatsProcessor.h242
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/processor/TMultiplexedProcessor.h224
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TBase64Utils.cpp315
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TBase64Utils.h45
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TBinaryProtocol.h250
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TBinaryProtocol.tcc454
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TCompactProtocol.h266
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TCompactProtocol.tcc826
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TDebugProtocol.cpp393
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TDebugProtocol.h204
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/THeaderProtocol.cpp253
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/THeaderProtocol.h210
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp1098
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TJSONProtocol.h325
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TMultiplexedProtocol.cpp40
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TMultiplexedProtocol.h95
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocol.cpp33
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocol.h762
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocolDecorator.h151
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocolException.h107
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocolTap.h177
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocolTypes.h36
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TVirtualProtocol.h513
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/qt/CMakeLists.txt28
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/qt/TQIODeviceTransport.cpp169
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/qt/TQIODeviceTransport.h68
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/qt/TQTcpServer.cpp151
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/qt/TQTcpServer.h81
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/server/TConnectedClient.cpp122
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/server/TConnectedClient.h110
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/server/TNonblockingServer.cpp1518
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/server/TNonblockingServer.h860
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/server/TServer.cpp52
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/server/TServer.h273
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/server/TServerFramework.cpp244
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/server/TServerFramework.h184
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/server/TSimpleServer.cpp106
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/server/TSimpleServer.h77
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/server/TThreadPoolServer.cpp131
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/server/TThreadPoolServer.h101
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/server/TThreadedServer.cpp151
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/server/TThreadedServer.h143
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/thrift-config.h24
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/thrift_export.h20
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/PlatformSocket.h134
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TBufferTransports.cpp415
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TBufferTransports.h747
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TFDTransport.cpp93
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TFDTransport.h77
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TFileTransport.cpp1068
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TFileTransport.h439
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THeaderTransport.cpp611
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THeaderTransport.h275
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpClient.cpp122
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpClient.h50
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpServer.cpp168
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpServer.h64
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpTransport.cpp270
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpTransport.h104
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingSSLServerSocket.cpp58
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingSSLServerSocket.h76
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingServerSocket.cpp549
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingServerSocket.h136
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingServerTransport.h100
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TPipe.cpp398
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TPipe.h113
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TPipeServer.cpp481
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TPipeServer.h103
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSSLServerSocket.cpp61
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSSLServerSocket.h76
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSSLSocket.cpp1120
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSSLSocket.h436
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TServerSocket.cpp698
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TServerSocket.h184
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TServerTransport.h113
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TShortReadTransport.h82
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSimpleFileTransport.cpp67
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSimpleFileTransport.h42
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSocket.cpp959
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSocket.h353
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSocketPool.cpp266
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSocketPool.h195
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransport.h271
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransportException.cpp59
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransportException.h106
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransportUtils.cpp189
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransportUtils.h314
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TVirtualTransport.h140
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TZlibTransport.cpp402
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TZlibTransport.h242
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/windows/GetTimeOfDay.cpp95
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/windows/GetTimeOfDay.h44
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/windows/Operators.h40
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/windows/OverlappedSubmissionThread.cpp151
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/windows/OverlappedSubmissionThread.h133
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/windows/SocketPair.cpp100
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/windows/SocketPair.h37
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/windows/Sync.h104
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/windows/TWinsockSingleton.cpp59
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/windows/TWinsockSingleton.h73
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/windows/WinFcntl.cpp101
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/windows/WinFcntl.h56
-rw-r--r--src/jaegertracing/thrift/lib/cpp/src/thrift/windows/config.h72
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.cpp47
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.tcc225
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/AnnotationTest.cpp68
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/Base64Test.cpp74
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/Benchmark.cpp244
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/CMakeLists.txt390
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest.cpp310
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest_extras.cpp35
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/EnumTest.cpp108
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/GenericHelpers.h107
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/JSONProtoTest.cpp343
-rwxr-xr-xsrc/jaegertracing/thrift/lib/cpp/test/Makefile.am425
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/OneWayHTTPTest.cpp242
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/OneWayTest.thrift45
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/OpenSSLManualInitTest.cpp93
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/OptionalRequiredTest.cpp386
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/RecursiveTest.cpp92
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/RenderedDoubleConstantsTest.cpp122
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/SecurityTest.cpp278
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/SpecializationTest.cpp103
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TBufferBaseTest.cpp639
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TFDTransportTest.cpp50
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TFileTransportTest.cpp404
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TMemoryBufferTest.cpp160
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TNonblockingSSLServerTest.cpp286
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TNonblockingServerTest.cpp215
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TPipeInterruptTest.cpp90
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TPipedTransportTest.cpp53
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TSSLSocketInterruptTest.cpp282
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TServerIntegrationTest.cpp536
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TServerSocketTest.cpp69
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TServerTransportTest.cpp58
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TSocketInterruptTest.cpp146
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TTransportCheckThrow.h44
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/ThriftTest_extras.cpp33
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/ToStringTest.cpp137
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TransportTest.cpp1089
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TypedefTest.cpp28
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/UnitTestMain.cpp21
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/ZlibTest.cpp475
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/concurrency/Tests.cpp224
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadFactoryTests.h308
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadManagerTests.h639
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/concurrency/TimerManagerTests.h273
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/link/LinkTest.cpp22
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService1.cpp26
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService2.cpp26
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.cpp135
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.h95
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/processor/Handlers.h338
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/processor/ProcessorTest.cpp929
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.cpp152
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.h135
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/processor/proc.thrift22
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/qt/CMakeLists.txt32
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/qt/TQTcpServerTest.cpp113
-rwxr-xr-xsrc/jaegertracing/thrift/lib/cpp/thrift-nb.pc.in30
-rwxr-xr-xsrc/jaegertracing/thrift/lib/cpp/thrift-qt5.pc.in30
-rwxr-xr-xsrc/jaegertracing/thrift/lib/cpp/thrift-z.pc.in30
-rwxr-xr-xsrc/jaegertracing/thrift/lib/cpp/thrift.pc.in29
-rw-r--r--src/jaegertracing/thrift/lib/cpp/thrift.sln61
-rw-r--r--src/jaegertracing/thrift/lib/csharp/Makefile.am114
-rw-r--r--src/jaegertracing/thrift/lib/csharp/README.md32
-rw-r--r--src/jaegertracing/thrift/lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs60
-rw-r--r--src/jaegertracing/thrift/lib/csharp/ThriftMSBuildTask/ThriftBuild.cs246
-rw-r--r--src/jaegertracing/thrift/lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj118
-rw-r--r--src/jaegertracing/thrift/lib/csharp/coding_standards.md6
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Collections/TCollections.cs94
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Collections/THashSet.cs160
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Net35/ExtensionsNet35.cs31
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Properties/AssemblyInfo.cs55
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TAbstractBase.cs29
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TBase.cs29
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TBase64Utils.cs100
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TBinaryProtocol.cs395
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TCompactProtocol.cs849
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TField.cs62
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TJSONProtocol.cs1124
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TList.cs54
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TMap.cs62
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TMessage.cs62
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TMessageType.cs31
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TMultiplexedProcessor.cs183
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TMultiplexedProtocol.cs103
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocol.cs142
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocolDecorator.cs261
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocolException.cs67
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocolFactory.cs33
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocolUtil.cs108
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TSet.cs59
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TStruct.cs46
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Protocol/TType.cs44
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Server/TServer.cs155
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Server/TServerEventHandler.cs53
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Server/TSimpleServer.cs180
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Server/TThreadPoolServer.cs295
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Server/TThreadedServer.cs282
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/TApplicationException.cs146
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/TAsyncProcessor.cs38
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/TControllingHandler.cs29
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/TException.cs40
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/TProcessor.cs33
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/TProcessorFactory.cs30
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/TPrototypeProcessorFactory.cs55
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/TSingletonProcessorFactory.cs43
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Thrift.45.csproj138
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Thrift.csproj166
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Thrift.sln47
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/TBufferedTransport.cs194
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/TFramedTransport.cs205
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/THttpClient.cs486
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/THttpHandler.cs102
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/THttpTaskAsyncHandler.cs97
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/TMemoryBuffer.cs117
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/TNamedPipeClientTransport.cs111
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/TNamedPipeServerTransport.cs296
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/TServerSocket.cs176
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/TServerTransport.cs44
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/TSilverlightSocket.cs393
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/TSocket.cs245
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/TSocketVersionizer.cs78
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/TStreamTransport.cs128
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/TTLSServerSocket.cs223
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/TTLSSocket.cs445
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/TTransport.cs146
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/TTransportException.cs69
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/Transport/TTransportFactory.cs42
-rw-r--r--src/jaegertracing/thrift/lib/csharp/src/thrift.snkbin0 -> 596 bytes
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/JSON/JSONTest.csproj85
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/JSON/Program.cs95
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/JSON/Properties/AssemblyInfo.cs55
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/JSON/app.config21
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/Multiplex/Client/Multiplex.Test.Client.cs82
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/Multiplex/Client/MultiplexClient.csproj148
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/Multiplex/Client/Properties/AssemblyInfo.cs55
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/Multiplex/Makefile.am63
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/Multiplex/Multiplex.Test.Common.cs40
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/Multiplex/Server/Multiplex.Test.Server.cs107
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/Multiplex/Server/MultiplexServer.csproj148
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/Multiplex/Server/Properties/AssemblyInfo.cs55
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/App_Start/FilterConfig.cs31
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/App_Start/RouteConfig.cs39
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/AsyncHttpHandler.cs32
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Controllers/HomeController.cs70
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Global.asax19
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Global.asax.cs34
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Properties/AssemblyInfo.cs53
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/SecondServiceImpl.cs37
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/SyncHttpHandler.cs32
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/ThriftMVCTest.csproj200
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Views/Home/Index.cshtml25
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Views/Shared/_Layout.cshtml30
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Views/Web.config60
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Views/_ViewStart.cshtml22
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Web.Debug.config45
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Web.Release.config46
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Web.config92
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/favicon.icobin0 -> 32038 bytes
-rw-r--r--src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/packages.config25
-rw-r--r--src/jaegertracing/thrift/lib/d/Makefile.am198
-rw-r--r--src/jaegertracing/thrift/lib/d/README.md49
-rw-r--r--src/jaegertracing/thrift/lib/d/coding_standards.md1
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/async/base.d228
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/async/libevent.d461
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/async/socket.d358
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/async/ssl.d292
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/base.d123
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/codegen/async_client.d255
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/codegen/async_client_pool.d906
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/codegen/base.d1021
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/codegen/client.d486
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/codegen/client_pool.d262
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/codegen/idlgen.d770
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/codegen/processor.d497
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/index.d33
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/internal/algorithm.d55
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/internal/codegen.d451
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/internal/ctfe.d98
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/internal/endian.d75
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/internal/resource_pool.d431
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/internal/socket.d96
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/internal/ssl.d240
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/internal/ssl_bio.d190
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/internal/test/protocol.d183
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/internal/test/server.d110
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/internal/traits.d33
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/protocol/base.d449
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/protocol/binary.d414
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/protocol/compact.d698
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/protocol/json.d1037
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/protocol/processor.d145
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/server/base.d179
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/server/nonblocking.d1397
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/server/simple.d183
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/server/taskpool.d304
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/server/threaded.d217
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/server/transport/base.d137
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/server/transport/socket.d380
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/server/transport/ssl.d88
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/transport/base.d370
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/transport/buffered.d215
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/transport/file.d1101
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/transport/framed.d334
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/transport/http.d459
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/transport/memory.d233
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/transport/piped.d219
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/transport/range.d147
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/transport/socket.d454
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/transport/ssl.d690
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/transport/zlib.d497
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/util/awaitable.d212
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/util/cancellation.d105
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/util/future.d549
-rw-r--r--src/jaegertracing/thrift/lib/d/src/thrift/util/hashset.d146
-rwxr-xr-xsrc/jaegertracing/thrift/lib/d/test/Makefile.am112
-rw-r--r--src/jaegertracing/thrift/lib/d/test/async_test.d396
-rwxr-xr-xsrc/jaegertracing/thrift/lib/d/test/async_test_runner.sh31
-rw-r--r--src/jaegertracing/thrift/lib/d/test/client_pool_test.d442
-rw-r--r--src/jaegertracing/thrift/lib/d/test/serialization_benchmark.d70
-rw-r--r--src/jaegertracing/thrift/lib/d/test/stress_test_server.d81
-rw-r--r--src/jaegertracing/thrift/lib/d/test/test_utils.d96
-rw-r--r--src/jaegertracing/thrift/lib/d/test/thrift_test_client.d386
-rw-r--r--src/jaegertracing/thrift/lib/d/test/thrift_test_common.d92
-rwxr-xr-xsrc/jaegertracing/thrift/lib/d/test/thrift_test_runner.sh93
-rw-r--r--src/jaegertracing/thrift/lib/d/test/thrift_test_server.d337
-rw-r--r--src/jaegertracing/thrift/lib/d/test/transport_test.d803
-rw-r--r--src/jaegertracing/thrift/lib/dart/.analysis_options2
-rw-r--r--src/jaegertracing/thrift/lib/dart/LICENSE16
-rw-r--r--src/jaegertracing/thrift/lib/dart/Makefile.am39
-rw-r--r--src/jaegertracing/thrift/lib/dart/README.md26
-rw-r--r--src/jaegertracing/thrift/lib/dart/coding_standards.md6
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/browser/t_web_socket.dart129
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/console/t_tcp_socket.dart81
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/console/t_web_socket.dart88
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_binary_protocol.dart281
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_compact_protocol.dart470
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_field.dart26
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_json_protocol.dart784
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_list.dart25
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_map.dart26
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_message.dart35
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_multiplexed_protocol.dart43
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol.dart95
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol_decorator.dart150
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol_error.dart33
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol_factory.dart22
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol_util.dart107
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_set.dart25
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_struct.dart24
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_type.dart34
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/serializer/t_deserializer.dart50
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/serializer/t_serializer.dart48
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/t_application_error.dart104
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/t_base.dart37
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/t_error.dart27
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/t_processor.dart24
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/transport/t_buffered_transport.dart98
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/transport/t_framed_transport.dart169
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/transport/t_http_transport.dart99
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/transport/t_message_reader.dart99
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/transport/t_socket.dart38
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/transport/t_socket_transport.dart177
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/transport/t_transport.dart70
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/transport/t_transport_error.dart31
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/src/transport/t_transport_factory.dart27
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/thrift.dart65
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/thrift_browser.dart22
-rw-r--r--src/jaegertracing/thrift/lib/dart/lib/thrift_console.dart23
-rw-r--r--src/jaegertracing/thrift/lib/dart/pubspec.yaml38
-rw-r--r--src/jaegertracing/thrift/lib/dart/test/protocol/t_protocol_test.dart406
-rw-r--r--src/jaegertracing/thrift/lib/dart/test/serializer/serializer_test.dart119
-rw-r--r--src/jaegertracing/thrift/lib/dart/test/serializer/serializer_test_data.dart342
-rw-r--r--src/jaegertracing/thrift/lib/dart/test/t_application_error_test.dart46
-rw-r--r--src/jaegertracing/thrift/lib/dart/test/transport/t_framed_transport_test.dart175
-rw-r--r--src/jaegertracing/thrift/lib/dart/test/transport/t_http_transport_test.dart164
-rw-r--r--src/jaegertracing/thrift/lib/dart/test/transport/t_socket_transport_test.dart311
-rw-r--r--src/jaegertracing/thrift/lib/dart/test/transport/t_transport_test.dart41
-rw-r--r--src/jaegertracing/thrift/lib/dart/tool/dev.dart33
-rw-r--r--src/jaegertracing/thrift/lib/delphi/DelphiThrift.groupproj156
-rw-r--r--src/jaegertracing/thrift/lib/delphi/README.md30
-rw-r--r--src/jaegertracing/thrift/lib/delphi/coding_standards.md1
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Collections.pas692
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Defines.inc50
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Exception.pas62
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Processor.Multiplex.pas231
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.Compact.pas1118
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.JSON.pas1237
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.Multiplex.pas107
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.pas1370
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Serializer.pas230
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Server.pas423
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Socket.pas1617
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Stream.pas319
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Transport.MsxmlHTTP.pas268
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Transport.Pipes.pas1044
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Transport.WinHTTP.pas408
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Transport.pas1523
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.TypeRegistry.pas95
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Utils.pas336
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.WinHTTP.pas1273
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.pas239
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/ConsoleHelper.pas132
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/Performance/DataFactory.pas176
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/Performance/PerfTests.pas173
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/TestClient.pas1506
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/TestConstants.pas164
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/TestServer.pas684
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/TestServerEvents.pas174
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/client.dpr77
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/codegen/README.md28
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/codegen/run-Pascal-Codegen-Tests.bat.tmpl173
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/keywords/ReservedIncluded.thrift25
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/keywords/ReservedKeywords.dpr15
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/keywords/ReservedKeywords.dproj112
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/keywords/ReservedKeywords.thrift138
-rwxr-xr-xsrc/jaegertracing/thrift/lib/delphi/test/maketest.sh23
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Client.Main.pas131
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Server.Main.pas201
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Test.Client.dpr68
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Test.Common.pas35
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Test.Server.dpr69
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/serializer/TestSerializer.Data.pas354
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/serializer/TestSerializer.dpr283
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/server.dpr74
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/skip/README.md11
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/skip/idl/skiptest_version_1.thrift45
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/skip/idl/skiptest_version_2.thrift69
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/skip/skiptest_version1.dpr202
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/skip/skiptest_version2.dpr229
-rw-r--r--src/jaegertracing/thrift/lib/delphi/test/typeregistry/TestTypeRegistry.dpr91
-rw-r--r--src/jaegertracing/thrift/lib/erl/Makefile.am97
-rw-r--r--src/jaegertracing/thrift/lib/erl/README.md51
-rw-r--r--src/jaegertracing/thrift/lib/erl/coding_standards.md3
-rw-r--r--src/jaegertracing/thrift/lib/erl/include/thrift_constants.hrl62
-rw-r--r--src/jaegertracing/thrift/lib/erl/include/thrift_protocol.hrl66
-rw-r--r--src/jaegertracing/thrift/lib/erl/include/thrift_protocol_behaviour.hrl37
-rw-r--r--src/jaegertracing/thrift/lib/erl/include/thrift_transport_behaviour.hrl31
-rw-r--r--src/jaegertracing/thrift/lib/erl/rebar.config3
-rw-r--r--src/jaegertracing/thrift/lib/erl/rebar.config.script7
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift.app.src74
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_base64_transport.erl69
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_binary_protocol.erl347
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_buffered_transport.erl98
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_client.erl162
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_client_util.erl112
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_compact_protocol.erl390
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_disk_log_transport.erl123
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_file_transport.erl115
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_framed_transport.erl126
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_http_transport.erl116
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_json_parser.erl419
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_json_protocol.erl567
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_membuffer_transport.erl83
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_memory_buffer.erl47
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_multiplexed_map_wrapper.erl57
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_multiplexed_protocol.erl83
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_processor.erl219
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_protocol.erl412
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_reconnecting_client.erl258
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_server.erl183
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_service.erl25
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_socket_server.erl324
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_socket_transport.erl176
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_sslsocket_transport.erl147
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_transport.erl126
-rw-r--r--src/jaegertracing/thrift/lib/erl/src/thrift_transport_state_test.erl117
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/Thrift1151.thrift3
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/Thrift1475.thrift34
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/Thrift_omit_with.thrift22
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/flags/LegacyNames.thrift33
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/flags/Thrift3214.thrift23
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/legacy_names_test.erl69
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/multiplexing.thrift7
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/multiplexing_test.erl57
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/name_conflict_test.erl299
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/stress_server.erl64
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/test_const.erl54
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/test_disklog.erl99
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/test_omit.erl79
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/test_rendered_double_constants.erl68
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/test_thrift_1151.erl34
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/test_thrift_3214.erl60
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/test_thrift_buffered_transport.erl359
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/test_thrift_compact_protocol.erl219
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/test_thrift_file_transport.erl213
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/test_thrift_framed_transport.erl404
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/test_thrift_membuffer_transport.erl167
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/test_thrift_socket_transport.erl199
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/thrift_socket_server_test.erl49
-rw-r--r--src/jaegertracing/thrift/lib/erl/test/thrift_test_test.erl643
-rw-r--r--src/jaegertracing/thrift/lib/go/Makefile.am45
-rw-r--r--src/jaegertracing/thrift/lib/go/README.md83
-rw-r--r--src/jaegertracing/thrift/lib/go/coding_standards.md1
-rw-r--r--src/jaegertracing/thrift/lib/go/test/BinaryKeyTest.thrift24
-rw-r--r--src/jaegertracing/thrift/lib/go/test/ConflictNamespaceServiceTest.thrift25
-rw-r--r--src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestA.thrift23
-rw-r--r--src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestB.thrift23
-rw-r--r--src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestC.thrift23
-rw-r--r--src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestD.thrift23
-rw-r--r--src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestSuperThing.thrift29
-rw-r--r--src/jaegertracing/thrift/lib/go/test/DontExportRWTest.thrift27
-rw-r--r--src/jaegertracing/thrift/lib/go/test/ErrorTest.thrift35
-rw-r--r--src/jaegertracing/thrift/lib/go/test/GoTagTest.thrift25
-rw-r--r--src/jaegertracing/thrift/lib/go/test/IgnoreInitialismsTest.thrift26
-rw-r--r--src/jaegertracing/thrift/lib/go/test/IncludesTest.thrift67
-rw-r--r--src/jaegertracing/thrift/lib/go/test/InitialismsTest.thrift24
-rw-r--r--src/jaegertracing/thrift/lib/go/test/Makefile.am131
-rw-r--r--src/jaegertracing/thrift/lib/go/test/MultiplexedProtocolTest.thrift27
-rw-r--r--src/jaegertracing/thrift/lib/go/test/NamesTest.thrift32
-rw-r--r--src/jaegertracing/thrift/lib/go/test/NamespacedTest.thrift40
-rw-r--r--src/jaegertracing/thrift/lib/go/test/OnewayTest.thrift24
-rw-r--r--src/jaegertracing/thrift/lib/go/test/OptionalFieldsTest.thrift50
-rw-r--r--src/jaegertracing/thrift/lib/go/test/RefAnnotationFieldsTest.thrift58
-rw-r--r--src/jaegertracing/thrift/lib/go/test/RequiredFieldTest.thrift7
-rw-r--r--src/jaegertracing/thrift/lib/go/test/ServicesTest.thrift111
-rw-r--r--src/jaegertracing/thrift/lib/go/test/TypedefFieldTest.thrift39
-rw-r--r--src/jaegertracing/thrift/lib/go/test/UnionBinaryTest.thrift25
-rw-r--r--src/jaegertracing/thrift/lib/go/test/UnionDefaultValueTest.thrift34
-rw-r--r--src/jaegertracing/thrift/lib/go/test/dontexportrwtest/compile_test.go37
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/binary_key_test.go31
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/client_error_test.go873
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/context.go26
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/encoding_json_test.go79
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/gotag_test.go62
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/ignoreinitialisms_test.go51
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/initialisms_test.go43
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/multiplexed_protocol_test.go197
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/names_test.go35
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/one_way_test.go91
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/optional_fields_test.go280
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/protocol_mock.go513
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/protocols_test.go97
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/required_fields_test.go130
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/struct_args_rets_test.go36
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/thrifttest_driver.go236
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/thrifttest_handler.go210
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/union_binary_test.go36
-rw-r--r--src/jaegertracing/thrift/lib/go/test/tests/union_default_value_test.go33
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/application_exception.go170
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/application_exception_test.go41
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/binary_protocol.go505
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/binary_protocol_test.go28
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/buffered_transport.go92
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/buffered_transport_test.go29
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/client.go95
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/common_test.go30
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/compact_protocol.go810
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/compact_protocol_test.go60
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/context.go24
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/debug_protocol.go270
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/deserializer.go58
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/exception.go44
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/exception_test.go69
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/field.go79
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/framed_transport.go187
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/framed_transport_test.go29
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/header_context.go101
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/header_context_test.go128
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/header_protocol.go305
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/header_protocol_test.go28
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/header_transport.go723
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/header_transport_test.go130
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/http_client.go242
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/http_client_test.go106
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/http_transport.go63
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/iostream_transport.go214
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/iostream_transport_test.go52
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/json_protocol.go581
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/json_protocol_test.go650
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/lowlevel_benchmarks_test.go540
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/memory_buffer.go80
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/memory_buffer_test.go29
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/messagetype.go31
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/multiplexed_protocol.go170
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/numeric.go164
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/pointerize.go52
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/processor_factory.go70
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/protocol.go177
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/protocol_exception.go77
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/protocol_factory.go25
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/protocol_test.go517
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/rich_transport.go68
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/rich_transport_test.go89
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/serializer.go79
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/serializer_test.go170
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/serializer_types_test.go633
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/server.go35
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/server_socket.go137
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/server_socket_test.go60
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/server_test.go28
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/server_transport.go34
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/simple_json_protocol.go1338
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/simple_json_protocol_test.go738
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/simple_server.go278
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/simple_server_test.go156
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/socket.go166
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/ssl_server_socket.go112
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/ssl_socket.go176
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/transport.go70
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/transport_exception.go90
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/transport_exception_test.go60
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/transport_factory.go39
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/transport_test.go177
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/type.go69
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/zlib_transport.go132
-rw-r--r--src/jaegertracing/thrift/lib/go/thrift/zlib_transport_test.go62
-rw-r--r--src/jaegertracing/thrift/lib/haxe/README.md162
-rw-r--r--src/jaegertracing/thrift/lib/haxe/coding_standards.md1
-rw-r--r--src/jaegertracing/thrift/lib/haxe/haxelib.json12
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/AbstractMethodError.hx40
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/ArgumentError.hx29
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/Limits.hx44
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TApplicationException.hx104
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TBase.hx73
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TException.hx36
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TFieldRequirementType.hx31
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TProcessor.hx30
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/BitConverter.hx170
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/Int64Map.hx295
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/IntSet.hx96
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/ObjectSet.hx96
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/StringSet.hx96
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/ZigZag.hx158
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/FieldMetaData.hx56
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/FieldValueMetaData.hx43
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/ListMetaData.hx31
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/MapMetaData.hx33
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/SetMetaData.hx30
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/StructMetaData.hx30
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TBinaryProtocol.hx301
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TBinaryProtocolFactory.hx45
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TCompactProtocol.hx718
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TCompactProtocolFactory.hx40
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TCompactTypes.hx41
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TField.hx43
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TJSONProtocol.hx1073
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TJSONProtocolFactory.hx40
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TList.hx32
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMap.hx34
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMessage.hx41
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMessageType.hx28
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProcessor.hx177
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProtocol.hx97
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocol.hx85
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocolDecorator.hx226
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocolException.hx41
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocolFactory.hx26
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocolUtil.hx110
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TRecursionTracker.hx48
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TSet.hx32
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TStruct.hx30
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TType.hx37
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/server/TServer.hx111
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/server/TServerEventHandler.hx41
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/server/TSimpleServer.hx141
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TBufferedTransport.hx155
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TBufferedTransportFactory.hx37
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TFileStream.hx101
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TFramedTransport.hx158
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TFramedTransportFactory.hx37
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TFullDuplexHttpClient.hx253
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/THttpClient.hx103
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TServerSocket.hx130
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TServerTransport.hx43
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TSocket.hx318
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TStream.hx32
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TStreamTransport.hx103
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TTransport.hx139
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TTransportException.hx39
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TTransportFactory.hx44
-rw-r--r--src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TWrappingServerTransport.hx47
-rw-r--r--src/jaegertracing/thrift/lib/haxe/test/HaxeTests.hxproj69
-rw-r--r--src/jaegertracing/thrift/lib/haxe/test/Makefile.am85
-rw-r--r--src/jaegertracing/thrift/lib/haxe/test/cpp.hxml41
-rw-r--r--src/jaegertracing/thrift/lib/haxe/test/csharp.hxml38
-rw-r--r--src/jaegertracing/thrift/lib/haxe/test/flash.hxml38
-rw-r--r--src/jaegertracing/thrift/lib/haxe/test/java.hxml38
-rw-r--r--src/jaegertracing/thrift/lib/haxe/test/javascript.hxml44
-rw-r--r--src/jaegertracing/thrift/lib/haxe/test/make_all.bat70
-rw-r--r--src/jaegertracing/thrift/lib/haxe/test/make_all.sh43
-rw-r--r--src/jaegertracing/thrift/lib/haxe/test/neko.hxml38
-rw-r--r--src/jaegertracing/thrift/lib/haxe/test/php.hxml39
-rw-r--r--src/jaegertracing/thrift/lib/haxe/test/project.hide67
-rw-r--r--src/jaegertracing/thrift/lib/haxe/test/python.hxml38
-rw-r--r--src/jaegertracing/thrift/lib/haxe/test/src/Main.hx93
-rw-r--r--src/jaegertracing/thrift/lib/haxe/test/src/MultiplexTest.hx224
-rw-r--r--src/jaegertracing/thrift/lib/haxe/test/src/StreamTest.hx95
-rw-r--r--src/jaegertracing/thrift/lib/haxe/test/src/TestBase.hx46
-rw-r--r--src/jaegertracing/thrift/lib/hs/CMakeLists.txt93
-rw-r--r--src/jaegertracing/thrift/lib/hs/LICENSE202
-rw-r--r--src/jaegertracing/thrift/lib/hs/Makefile.am53
-rw-r--r--src/jaegertracing/thrift/lib/hs/README.md113
-rwxr-xr-xsrc/jaegertracing/thrift/lib/hs/Setup.lhs21
-rw-r--r--src/jaegertracing/thrift/lib/hs/TODO2
-rw-r--r--src/jaegertracing/thrift/lib/hs/coding_standards.md1
-rw-r--r--src/jaegertracing/thrift/lib/hs/src/Thrift.hs114
-rw-r--r--src/jaegertracing/thrift/lib/hs/src/Thrift/Arbitraries.hs55
-rw-r--r--src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol.hs136
-rw-r--r--src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol/Binary.hs212
-rw-r--r--src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol/Compact.hs311
-rw-r--r--src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol/Header.hs141
-rw-r--r--src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol/JSON.hs362
-rw-r--r--src/jaegertracing/thrift/lib/hs/src/Thrift/Server.hs66
-rw-r--r--src/jaegertracing/thrift/lib/hs/src/Thrift/Transport.hs65
-rw-r--r--src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Empty.hs36
-rw-r--r--src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Framed.hs99
-rw-r--r--src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Handle.hs78
-rw-r--r--src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Header.hs354
-rw-r--r--src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/HttpClient.hs101
-rw-r--r--src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/IOBuffer.hs69
-rw-r--r--src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Memory.hs77
-rw-r--r--src/jaegertracing/thrift/lib/hs/src/Thrift/Types.hs130
-rw-r--r--src/jaegertracing/thrift/lib/hs/test/BinarySpec.hs91
-rw-r--r--src/jaegertracing/thrift/lib/hs/test/CompactSpec.hs81
-rw-r--r--src/jaegertracing/thrift/lib/hs/test/JSONSpec.hs225
-rw-r--r--src/jaegertracing/thrift/lib/hs/test/Spec.hs38
-rw-r--r--src/jaegertracing/thrift/lib/hs/thrift.cabal84
-rw-r--r--src/jaegertracing/thrift/lib/java/CMakeLists.txt94
-rw-r--r--src/jaegertracing/thrift/lib/java/Makefile.am72
-rw-r--r--src/jaegertracing/thrift/lib/java/README.md188
-rw-r--r--src/jaegertracing/thrift/lib/java/android/build.gradle50
-rw-r--r--src/jaegertracing/thrift/lib/java/android/settings.gradle1
-rw-r--r--src/jaegertracing/thrift/lib/java/android/src/main/AndroidManifest.xml4
-rw-r--r--src/jaegertracing/thrift/lib/java/build.gradle63
-rw-r--r--src/jaegertracing/thrift/lib/java/code_quality_tools/findbugs-filter.xml51
-rw-r--r--src/jaegertracing/thrift/lib/java/coding_standards.md1
-rw-r--r--src/jaegertracing/thrift/lib/java/gradle.properties34
-rw-r--r--src/jaegertracing/thrift/lib/java/gradle/additionalArtifacts.gradle40
-rw-r--r--src/jaegertracing/thrift/lib/java/gradle/cloverCoverage.gradle48
-rw-r--r--src/jaegertracing/thrift/lib/java/gradle/codeQualityChecks.gradle39
-rw-r--r--src/jaegertracing/thrift/lib/java/gradle/environment.gradle75
-rw-r--r--src/jaegertracing/thrift/lib/java/gradle/functionalTests.gradle155
-rw-r--r--src/jaegertracing/thrift/lib/java/gradle/generateTestThrift.gradle120
-rw-r--r--src/jaegertracing/thrift/lib/java/gradle/publishing.gradle119
-rw-r--r--src/jaegertracing/thrift/lib/java/gradle/sourceConfiguration.gradle84
-rw-r--r--src/jaegertracing/thrift/lib/java/gradle/unitTests.gradle82
-rw-r--r--src/jaegertracing/thrift/lib/java/gradle/wrapper/gradle-wrapper.jarbin0 -> 55616 bytes
-rw-r--r--src/jaegertracing/thrift/lib/java/gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xsrc/jaegertracing/thrift/lib/java/gradlew188
-rw-r--r--src/jaegertracing/thrift/lib/java/gradlew.bat100
-rw-r--r--src/jaegertracing/thrift/lib/java/settings.gradle20
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/AsyncProcessFunction.java55
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/EncodingUtils.java148
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/Option.java125
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/ProcessFunction.java88
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TApplicationException.java150
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TAsyncProcessor.java33
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBase.java67
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseAsyncProcessor.java114
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseHelper.java297
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseProcessor.java41
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TByteArrayOutputStream.java61
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TDeserializer.java339
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnum.java24
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnumHelper.java56
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TException.java45
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldIdEnum.java34
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldRequirementType.java30
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TMultiplexedProcessor.java158
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchClient.java399
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchStats.java80
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessor.java30
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessorFactory.java43
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializable.java44
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializer.java110
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClient.java91
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClientFactory.java45
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TUnion.java279
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/annotation/Nullable.java33
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/AsyncMethodCallback.java51
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClient.java102
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientFactory.java25
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientManager.java201
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncMethodCall.java284
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/EnumMetaData.java31
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldMetaData.java70
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldValueMetaData.java72
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/ListMetaData.java29
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/MapMetaData.java31
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/SetMetaData.java29
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/StructMetaData.java31
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/ShortStack.java77
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBase64Utils.java127
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBinaryProtocol.java457
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java904
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TField.java65
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TJSONProtocol.java973
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TList.java38
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMap.java40
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessage.java76
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessageType.java31
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMultiplexedProtocol.java93
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocol.java162
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolDecorator.java213
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolException.java82
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolFactory.java31
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolUtil.java221
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSet.java42
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSimpleJSONProtocol.java483
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TStruct.java36
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java98
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TType.java40
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/IScheme.java29
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/SchemeFactory.java25
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/StandardScheme.java25
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/TupleScheme.java25
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java618
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/Invocation.java20
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/ServerContext.java26
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TExtensibleServlet.java171
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/THsHaServer.java204
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TNonblockingServer.java247
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServer.java177
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServerEventHandler.java59
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServlet.java119
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TSimpleServer.java115
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadPoolServer.java359
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadedSelectorServer.java744
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBuffer.java50
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java84
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferWriteTransport.java88
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TByteBuffer.java87
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFastFramedTransport.java200
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileProcessor.java118
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileTransport.java624
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFramedTransport.java190
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/THttpClient.java362
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TIOStreamTransport.java162
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryBuffer.java104
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryInputTransport.java96
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerSocket.java163
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerTransport.java31
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingSocket.java210
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingTransport.java47
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java447
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslClientTransport.java108
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslServerTransport.java230
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslTransport.java575
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSeekableFile.java33
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerSocket.java158
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerTransport.java80
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSimpleFileTransport.java216
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSocket.java248
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TStandardFile.java60
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransport.java163
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportException.java81
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportFactory.java41
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TZlibTransport.java148
-rw-r--r--src/jaegertracing/thrift/lib/java/test/.keystorebin0 -> 2429 bytes
-rw-r--r--src/jaegertracing/thrift/lib/java/test/.truststorebin0 -> 1149 bytes
-rw-r--r--src/jaegertracing/thrift/lib/java/test/log4j.properties6
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/Fixtures.java340
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestDeepCopy.java34
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestEnumContainers.java81
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestFullCamel.java59
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestMultiplexedProcessor.java85
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestOptionType.java66
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestOptionals.java88
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestRenderedDoubleConstants.java179
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestReuse.java57
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestStruct.java396
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTBaseHelper.java209
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTDeserializer.java126
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTEnumHelper.java41
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTUnion.java268
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestUnsafeBinaries.java146
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/async/TestTAsyncClient.java28
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/async/TestTAsyncClientManager.java378
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/BenchmarkProtocols.java88
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/ProtocolTestBase.java427
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestShortStack.java42
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTCompactProtocol.java56
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTField.java60
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTJSONProtocol.java48
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTProtocolUtil.java83
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTSimpleJSONProtocol.java94
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTTupleProtocol.java27
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/scheme/TestStandardScheme.java40
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/ServerTestBase.java715
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestAsyncServer.java28
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestHsHaServer.java30
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestNonblockingServer.java123
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestThreadedSelectorServer.java30
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/EqualityTest.java663
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/JavaBeansTest.java112
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/ReadStruct.java62
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/SerializationBenchmark.java80
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestClient.java811
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestNonblockingServer.java76
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestServer.java310
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/WriteStruct.java48
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/ReadCountingTransport.java61
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBuffer.java37
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferReadTransport.java50
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferWriteTransport.java69
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTByteBuffer.java36
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTFastFramedTransport.java33
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTFramedTransport.java214
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTMemoryInputTransport.java85
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactory.java92
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryCustomClient1.java35
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryCustomClient2.java34
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryStreamedStore.java62
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSaslTransports.java471
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSimpleFileTransport.java74
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTZlibTransport.java140
-rw-r--r--src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/WriteCountingTransport.java54
-rw-r--r--src/jaegertracing/thrift/lib/javame/coding_standards.md1
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TApplicationException.java132
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TBase.java46
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TBaseHelper.java209
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TByteArrayOutputStream.java41
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TDeserializer.java95
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TEnum.java24
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TException.java45
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TFieldRequirementType.java48
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TProcessor.java32
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TProcessorFactory.java39
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TSerializer.java110
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TServiceClient.java39
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/FieldMetaData.java88
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/FieldValueMetaData.java61
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/ListMetaData.java49
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/MapMetaData.java51
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/SetMetaData.java47
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/StructMetaData.java51
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TBase64Utils.java127
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TBinaryProtocol.java329
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TField.java38
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TJSONProtocol.java973
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TList.java36
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMap.java38
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMessage.java38
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMessageType.java30
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocol.java169
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolException.java81
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolFactory.java30
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolUtil.java159
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TSet.java36
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TStruct.java34
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TType.java40
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TFramedTransport.java122
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/THttpClient.java163
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TIOStreamTransport.java153
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TMemoryBuffer.java97
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransport.java121
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransportException.java80
-rw-r--r--src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransportFactory.java41
-rw-r--r--src/jaegertracing/thrift/lib/js/Gruntfile.js327
-rw-r--r--src/jaegertracing/thrift/lib/js/Makefile.am62
-rw-r--r--src/jaegertracing/thrift/lib/js/README.md145
-rw-r--r--src/jaegertracing/thrift/lib/js/coding_standards.md1
-rw-r--r--src/jaegertracing/thrift/lib/js/package-lock.json4344
-rw-r--r--src/jaegertracing/thrift/lib/js/package.json31
-rw-r--r--src/jaegertracing/thrift/lib/js/src/thrift.js1624
-rwxr-xr-xsrc/jaegertracing/thrift/lib/js/test/Makefile.am29
-rw-r--r--src/jaegertracing/thrift/lib/js/test/README.md68
-rw-r--r--src/jaegertracing/thrift/lib/js/test/build.properties5
-rwxr-xr-xsrc/jaegertracing/thrift/lib/js/test/build.xml248
-rw-r--r--src/jaegertracing/thrift/lib/js/test/deep-constructor.test.js213
-rwxr-xr-xsrc/jaegertracing/thrift/lib/js/test/jsTestDriver.conf18
-rw-r--r--src/jaegertracing/thrift/lib/js/test/phantom-client.js382
-rwxr-xr-xsrc/jaegertracing/thrift/lib/js/test/phantomjs-qunit.js91
-rw-r--r--src/jaegertracing/thrift/lib/js/test/server_http.js55
-rw-r--r--src/jaegertracing/thrift/lib/js/test/server_https.js59
-rw-r--r--src/jaegertracing/thrift/lib/js/test/src/test/Httpd.java323
-rw-r--r--src/jaegertracing/thrift/lib/js/test/test-async.js370
-rwxr-xr-xsrc/jaegertracing/thrift/lib/js/test/test-deep-constructor.html50
-rw-r--r--src/jaegertracing/thrift/lib/js/test/test-double-rendering.html55
-rw-r--r--src/jaegertracing/thrift/lib/js/test/test-double-rendering.js125
-rw-r--r--src/jaegertracing/thrift/lib/js/test/test-es6.html65
-rw-r--r--src/jaegertracing/thrift/lib/js/test/test-es6.js374
-rw-r--r--src/jaegertracing/thrift/lib/js/test/test-int64.html56
-rw-r--r--src/jaegertracing/thrift/lib/js/test/test-int64.js97
-rw-r--r--src/jaegertracing/thrift/lib/js/test/test-jq.js159
-rw-r--r--src/jaegertracing/thrift/lib/js/test/test-nojq.html55
-rw-r--r--src/jaegertracing/thrift/lib/js/test/test-nojq.js48
-rwxr-xr-xsrc/jaegertracing/thrift/lib/js/test/test.html57
-rwxr-xr-xsrc/jaegertracing/thrift/lib/js/test/test.js417
-rw-r--r--src/jaegertracing/thrift/lib/js/test/test_handler.js202
-rw-r--r--src/jaegertracing/thrift/lib/js/test/testws.html65
-rw-r--r--src/jaegertracing/thrift/lib/json/Makefile.am33
-rw-r--r--src/jaegertracing/thrift/lib/json/schema.json344
-rw-r--r--src/jaegertracing/thrift/lib/json/test/Makefile.am26
-rw-r--r--src/jaegertracing/thrift/lib/json/test/build.properties10
-rw-r--r--src/jaegertracing/thrift/lib/json/test/build.xml144
-rw-r--r--src/jaegertracing/thrift/lib/lua/Makefile.am73
-rw-r--r--src/jaegertracing/thrift/lib/lua/TBinaryProtocol.lua264
-rw-r--r--src/jaegertracing/thrift/lib/lua/TBufferedTransport.lua91
-rw-r--r--src/jaegertracing/thrift/lib/lua/TCompactProtocol.lua457
-rw-r--r--src/jaegertracing/thrift/lib/lua/TFramedTransport.lua118
-rw-r--r--src/jaegertracing/thrift/lib/lua/THttpTransport.lua182
-rw-r--r--src/jaegertracing/thrift/lib/lua/TJsonProtocol.lua727
-rw-r--r--src/jaegertracing/thrift/lib/lua/TMemoryBuffer.lua91
-rw-r--r--src/jaegertracing/thrift/lib/lua/TProtocol.lua164
-rw-r--r--src/jaegertracing/thrift/lib/lua/TServer.lua140
-rw-r--r--src/jaegertracing/thrift/lib/lua/TSocket.lua132
-rw-r--r--src/jaegertracing/thrift/lib/lua/TTransport.lua93
-rw-r--r--src/jaegertracing/thrift/lib/lua/Thrift.lua281
-rw-r--r--src/jaegertracing/thrift/lib/lua/coding_standards.md1
-rw-r--r--src/jaegertracing/thrift/lib/lua/src/longnumberutils.c47
-rw-r--r--src/jaegertracing/thrift/lib/lua/src/luabitwise.c83
-rw-r--r--src/jaegertracing/thrift/lib/lua/src/luabpack.c308
-rw-r--r--src/jaegertracing/thrift/lib/lua/src/lualongnumber.c228
-rw-r--r--src/jaegertracing/thrift/lib/lua/src/luasocket.c380
-rw-r--r--src/jaegertracing/thrift/lib/lua/src/socket.h78
-rw-r--r--src/jaegertracing/thrift/lib/lua/src/usocket.c376
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Makefile.am58
-rw-r--r--src/jaegertracing/thrift/lib/netcore/README.md29
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Tests/Thrift.IntegrationTests/.gitignore2
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Tests/Thrift.IntegrationTests/Protocols/ProtocolsOperationsTests.cs502
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj29
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/.gitignore4
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/CassandraTest.thrift705
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/Properties/AssemblyInfo.cs40
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj36
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Collections/TCollectionsTests.cs83
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Collections/THashSetTests.cs71
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Protocols/TJsonProtocolHelperTests.cs172
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Protocols/TJsonProtocolTests.cs67
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Thrift.Tests.csproj18
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift.sln85
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Collections/TCollections.cs101
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Collections/THashSet.cs67
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/ITAsyncProcessor.cs29
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/ITProcessorFactory.cs28
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Properties/AssemblyInfo.cs56
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TField.cs37
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TList.cs33
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TMap.cs36
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TMessage.cs37
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TMessageType.cs28
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TSet.cs38
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TStruct.cs30
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TType.cs37
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/ITProtocolFactory.cs27
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TAbstractBase.cs28
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TBase.cs28
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TBinaryProtocol.cs613
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TCompactProtocol.cs922
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TJSONProtocol.cs981
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TMultiplexedProtocol.cs91
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TProtocol.cs376
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TProtocolDecorator.cs247
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TProtocolException.cs59
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TBase64Helper.cs101
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TBase64Utils.cs101
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TJsonProtocolConstants.cs61
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TJsonProtocolHelper.cs176
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TProtocolUtil.cs110
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Server/AsyncBaseServer.cs183
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Server/TBaseServer.cs79
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Server/TServerEventHandler.cs54
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/SingletonTProcessorFactory.cs38
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/TApplicationException.cs150
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/TBaseClient.cs91
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/TException.cs34
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/TMultiplexedProcessor.cs143
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Thrift.csproj33
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TBufferedClientTransport.cs206
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TFramedClientTransport.cs201
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/THttpClientTransport.cs227
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TMemoryBufferClientTransport.cs97
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TNamedPipeClientTransport.cs95
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TSocketClientTransport.cs139
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TStreamClientTransport.cs110
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TTlsSocketClientTransport.cs237
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/THttpServerTransport.cs98
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/TNamedPipeServerTransport.cs191
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/TServerFramedTransport.cs150
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/TServerSocketTransport.cs174
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/TTlsServerSocketTransport.cs177
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Transports/TClientTransport.cs179
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Transports/TServerTransport.cs54
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Transports/TTransportException.cs58
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/Transports/TTransportFactory.cs35
-rw-r--r--src/jaegertracing/thrift/lib/netcore/Thrift/thrift.snkbin0 -> 596 bytes
-rw-r--r--src/jaegertracing/thrift/lib/netcore/build.cmd27
-rw-r--r--src/jaegertracing/thrift/lib/netcore/build.sh32
-rw-r--r--src/jaegertracing/thrift/lib/netcore/runtests.cmd28
-rw-r--r--src/jaegertracing/thrift/lib/netcore/runtests.sh26
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Makefile.am58
-rw-r--r--src/jaegertracing/thrift/lib/netstd/README.md54
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Tests/Thrift.IntegrationTests/Protocols/ProtocolsOperationsTests.cs502
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj48
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/CassandraTest.thrift705
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Properties/AssemblyInfo.cs40
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj54
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Collections/TCollectionsTests.cs83
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Collections/THashSetTests.cs71
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Protocols/TJsonProtocolHelperTests.cs172
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Protocols/TJsonProtocolTests.cs67
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Thrift.Tests.csproj36
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift.sln85
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Collections/TCollections.cs101
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Collections/THashSet.cs78
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Processor/ITAsyncProcessor.cs28
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Processor/ITProcessorFactory.cs28
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Processor/TMultiplexedProcessor.cs143
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Processor/TSingletonProcessorFactory.cs38
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Properties/AssemblyInfo.cs57
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TField.cs37
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TList.cs33
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TMap.cs36
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TMessage.cs37
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TMessageType.cs28
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TSet.cs38
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TStruct.cs30
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TType.cs37
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TBase.cs33
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TBinaryProtocol.cs600
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TCompactProtocol.cs919
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TJSONProtocol.cs981
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TMultiplexedProtocol.cs91
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TProtocol.cs376
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TProtocolDecorator.cs247
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TProtocolException.cs62
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TProtocolFactory.cs27
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Utilities/TBase64Utils.cs101
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Utilities/TJsonProtocolConstants.cs61
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Utilities/TJsonProtocolHelper.cs176
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Utilities/TProtocolUtil.cs110
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Server/TServer.cs87
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Server/TServerEventHandler.cs54
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Server/TSimpleAsyncServer.cs230
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Server/TThreadPoolAsyncServer.cs297
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/TApplicationException.cs150
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/TBaseClient.cs91
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/TException.cs34
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Thrift.csproj59
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/THttpTransport.cs222
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TMemoryBufferTransport.cs179
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TNamedPipeTransport.cs108
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TSocketTransport.cs162
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TStreamTransport.cs109
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TTlsSocketTransport.cs267
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/NullLogger.cs56
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/THttpServerTransport.cs118
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/TNamedPipeServerTransport.cs308
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/TServerSocketTransport.cs139
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/TTlsServerSocketTransport.cs150
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TBufferedTransport.cs198
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TFramedTransport.cs194
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TServerTransport.cs54
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TTransport.cs190
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TTransportException.cs60
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TTransportFactory.cs35
-rw-r--r--src/jaegertracing/thrift/lib/netstd/Thrift/thrift.snkbin0 -> 596 bytes
-rw-r--r--src/jaegertracing/thrift/lib/netstd/build.cmd27
-rw-r--r--src/jaegertracing/thrift/lib/netstd/build.sh32
-rw-r--r--src/jaegertracing/thrift/lib/netstd/runtests.cmd28
-rw-r--r--src/jaegertracing/thrift/lib/netstd/runtests.sh26
-rwxr-xr-xsrc/jaegertracing/thrift/lib/nodejs/Makefile.am45
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/README.md111
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/coding_standards.md1
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/examples/Makefile24
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/examples/README.md40
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/examples/client.js49
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/examples/client_multitransport.js58
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/examples/hello.html65
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/examples/hello.js63
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/examples/hello.thrift27
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/examples/httpClient.js23
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/examples/httpServer.js31
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/examples/httpServer.py20
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/examples/parse.js46
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/examples/server.js39
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/examples/server_http.js53
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/examples/server_multitransport.js46
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/examples/user.thrift27
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/binary.js168
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/binary_protocol.js367
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/browser.js36
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/buffered_transport.js186
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/compact_protocol.js915
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/connection.js396
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/create_client.js54
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/framed_transport.js185
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/header_protocol.js256
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/header_transport.js339
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/http_connection.js263
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/index.js75
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/input_buffer_underrun_error.js30
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/int64_util.js91
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/json_parse.js299
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/json_protocol.js799
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/log.js87
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/multiplexed_processor.js63
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/multiplexed_protocol.js73
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/protocol.js22
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/server.js123
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/thrift.js232
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/transport.js22
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/web_server.js567
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/ws_connection.js286
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/ws_transport.js206
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/lib/thrift/xhr_connection.js280
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/binary.test.js214
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/certificates.README7
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/client.js170
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/deep-constructor.test.js333
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/episodic-code-generation-test/client.js77
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/episodic-code-generation-test/episodic_compilation.package.json3
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/episodic-code-generation-test/server.js50
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/exceptions.js146
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/header.test.js78
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/helpers.js38
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/int64.test.js92
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/server.crt25
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/server.js137
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/server.key28
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/test-cases.js180
-rwxr-xr-xsrc/jaegertracing/thrift/lib/nodejs/test/testAll.sh151
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/test_driver.js361
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/test_handler.js220
-rw-r--r--src/jaegertracing/thrift/lib/nodejs/test/test_header_payloadbin0 -> 76 bytes
-rw-r--r--src/jaegertracing/thrift/lib/nodets/.gitignore1
-rwxr-xr-xsrc/jaegertracing/thrift/lib/nodets/Makefile.am46
-rw-r--r--src/jaegertracing/thrift/lib/nodets/coding_standards.md1
-rw-r--r--src/jaegertracing/thrift/lib/nodets/test/client.ts63
-rw-r--r--src/jaegertracing/thrift/lib/nodets/test/int64.test.ts88
-rwxr-xr-xsrc/jaegertracing/thrift/lib/nodets/test/runClient.sh18
-rwxr-xr-xsrc/jaegertracing/thrift/lib/nodets/test/runServer.sh20
-rw-r--r--src/jaegertracing/thrift/lib/nodets/test/server.ts26
-rw-r--r--src/jaegertracing/thrift/lib/nodets/test/test-cases.ts114
-rwxr-xr-xsrc/jaegertracing/thrift/lib/nodets/test/testAll.sh42
-rw-r--r--src/jaegertracing/thrift/lib/nodets/test/test_driver.ts278
-rw-r--r--src/jaegertracing/thrift/lib/nodets/test/test_handler.ts300
-rw-r--r--src/jaegertracing/thrift/lib/nodets/test/tsconfig.json22
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/.gitignore11
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/DEVELOPMENT76
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/README.md119
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/TODO5
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/_oasis19
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/coding_standards.md1
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/descr1
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/opam8
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/src/Makefile26
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/src/TBinaryProtocol.ml171
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/src/TChannelTransport.ml39
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/src/TFramedTransport.ml93
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/src/TServer.ml42
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/src/TServerSocket.ml41
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/src/TSimpleServer.ml40
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/src/TSocket.ml59
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/src/TThreadedServer.ml45
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/src/Thrift.ml382
-rw-r--r--src/jaegertracing/thrift/lib/ocaml/url2
-rw-r--r--src/jaegertracing/thrift/lib/perl/MANIFEST.SKIP14
-rw-r--r--src/jaegertracing/thrift/lib/perl/Makefile.PL49
-rw-r--r--src/jaegertracing/thrift/lib/perl/Makefile.am108
-rw-r--r--src/jaegertracing/thrift/lib/perl/README.md124
-rwxr-xr-xsrc/jaegertracing/thrift/lib/perl/build-cpan-dist.sh56
-rw-r--r--src/jaegertracing/thrift/lib/perl/coding_standards.md2
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift.pm36
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/BinaryProtocol.pm518
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/BufferedTransport.pm139
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/Exception.pm161
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/FramedTransport.pm194
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/HttpClient.pm204
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/MemoryBuffer.pm148
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/MessageType.pm37
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/MultiplexedProcessor.pm133
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/MultiplexedProtocol.pm68
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/Protocol.pm549
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/ProtocolDecorator.pm363
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/SSLServerSocket.pm76
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/SSLSocket.pm126
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/Server.pm311
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/ServerSocket.pm125
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/Socket.pm327
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/Transport.pm180
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/Type.pm50
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/UnixServerSocket.pm84
-rw-r--r--src/jaegertracing/thrift/lib/perl/lib/Thrift/UnixSocket.pm67
-rw-r--r--src/jaegertracing/thrift/lib/perl/t/Makefile.am20
-rw-r--r--src/jaegertracing/thrift/lib/perl/t/memory_buffer.t53
-rw-r--r--src/jaegertracing/thrift/lib/perl/t/multiplex.t201
-rw-r--r--src/jaegertracing/thrift/lib/perl/t/processor.t104
-rw-r--r--src/jaegertracing/thrift/lib/perl/test.pl25
-rw-r--r--src/jaegertracing/thrift/lib/perl/tools/FixupDist.pl35
-rwxr-xr-xsrc/jaegertracing/thrift/lib/php/Makefile.am152
-rw-r--r--src/jaegertracing/thrift/lib/php/README.apache.md74
-rw-r--r--src/jaegertracing/thrift/lib/php/README.md60
-rw-r--r--src/jaegertracing/thrift/lib/php/coding_standards.md5
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Base/TBase.php382
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/ClassLoader/ThriftClassLoader.php206
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Exception/TApplicationException.php76
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Exception/TException.php384
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Exception/TProtocolException.php50
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Exception/TTransportException.php40
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Factory/TBinaryProtocolFactory.php45
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Factory/TCompactProtocolFactory.php40
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Factory/TJSONProtocolFactory.php40
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Factory/TProtocolFactory.php36
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Factory/TStringFuncFactory.php66
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Factory/TTransportFactory.php18
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Protocol/JSON/BaseContext.php39
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Protocol/JSON/ListContext.php54
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Protocol/JSON/LookaheadReader.php57
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Protocol/JSON/PairContext.php64
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/CollectionMapKeyException.php33
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/Context.php35
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/ListContext.php45
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/MapContext.php47
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/StructContext.php52
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Protocol/TBinaryProtocol.php453
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Protocol/TBinaryProtocolAccelerated.php67
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Protocol/TCompactProtocol.php739
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Protocol/TJSONProtocol.php815
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Protocol/TMultiplexedProtocol.php85
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Protocol/TProtocol.php352
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Protocol/TProtocolDecorator.php285
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Protocol/TSimpleJSONProtocol.php374
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Serializer/TBinarySerializer.php87
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Server/TForkingServer.php125
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Server/TSSLServerSocket.php97
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Server/TServer.php102
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Server/TServerSocket.php124
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Server/TServerTransport.php56
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Server/TSimpleServer.php60
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/StoredMessageProtocol.php53
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/StringFunc/Core.php40
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/StringFunc/Mbstring.php46
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/StringFunc/TStringFunc.php28
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/TMultiplexedProcessor.php118
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Transport/TBufferedTransport.php206
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Transport/TCurlClient.php281
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Transport/TFramedTransport.php192
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Transport/THttpClient.php258
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Transport/TMemoryBuffer.php106
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Transport/TNullTransport.php56
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Transport/TPhpStream.php124
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Transport/TSSLSocket.php117
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Transport/TSocket.php366
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Transport/TSocketPool.php310
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Transport/TTransport.php98
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Type/TConstant.php52
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Type/TMessageType.php34
-rw-r--r--src/jaegertracing/thrift/lib/php/lib/Type/TType.php47
-rw-r--r--src/jaegertracing/thrift/lib/php/src/TStringUtils.php90
-rw-r--r--src/jaegertracing/thrift/lib/php/src/Thrift.php821
-rw-r--r--src/jaegertracing/thrift/lib/php/src/autoload.php51
-rw-r--r--src/jaegertracing/thrift/lib/php/src/ext/thrift_protocol/config.m434
-rw-r--r--src/jaegertracing/thrift/lib/php/src/ext/thrift_protocol/config.w328
-rw-r--r--src/jaegertracing/thrift/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp1172
-rw-r--r--src/jaegertracing/thrift/lib/php/src/ext/thrift_protocol/php_thrift_protocol.h28
-rw-r--r--src/jaegertracing/thrift/lib/php/test/Fixtures.php194
-rw-r--r--src/jaegertracing/thrift/lib/php/test/JsonSerialize/JsonSerializeTest.php116
-rwxr-xr-xsrc/jaegertracing/thrift/lib/php/test/Makefile.am55
-rw-r--r--src/jaegertracing/thrift/lib/php/test/Protocol/BinarySerializerTest.php60
-rw-r--r--src/jaegertracing/thrift/lib/php/test/Protocol/TJSONProtocolFixtures.php74
-rw-r--r--src/jaegertracing/thrift/lib/php/test/Protocol/TJSONProtocolTest.php518
-rw-r--r--src/jaegertracing/thrift/lib/php/test/Protocol/TSimpleJSONProtocolFixtures.php67
-rw-r--r--src/jaegertracing/thrift/lib/php/test/Protocol/TSimpleJSONProtocolTest.php254
-rw-r--r--src/jaegertracing/thrift/lib/php/test/TestValidators.thrift31
-rw-r--r--src/jaegertracing/thrift/lib/php/test/Validator/BaseValidatorTest.php154
-rw-r--r--src/jaegertracing/thrift/lib/php/test/Validator/ValidatorTest.php41
-rw-r--r--src/jaegertracing/thrift/lib/php/test/Validator/ValidatorTestOop.php41
-rw-r--r--src/jaegertracing/thrift/lib/php/thrift_protocol.ini1
-rw-r--r--src/jaegertracing/thrift/lib/py/CMakeLists.txt31
-rw-r--r--src/jaegertracing/thrift/lib/py/MANIFEST.in1
-rw-r--r--src/jaegertracing/thrift/lib/py/Makefile.am65
-rw-r--r--src/jaegertracing/thrift/lib/py/README.md35
-rw-r--r--src/jaegertracing/thrift/lib/py/coding_standards.md7
-rw-r--r--src/jaegertracing/thrift/lib/py/compat/win32/stdint.h247
-rw-r--r--src/jaegertracing/thrift/lib/py/setup.cfg6
-rw-r--r--src/jaegertracing/thrift/lib/py/setup.py139
-rw-r--r--src/jaegertracing/thrift/lib/py/src/TMultiplexedProcessor.py82
-rw-r--r--src/jaegertracing/thrift/lib/py/src/TRecursive.py83
-rw-r--r--src/jaegertracing/thrift/lib/py/src/TSCons.py36
-rw-r--r--src/jaegertracing/thrift/lib/py/src/TSerialization.py38
-rw-r--r--src/jaegertracing/thrift/lib/py/src/TTornado.py188
-rw-r--r--src/jaegertracing/thrift/lib/py/src/Thrift.py204
-rw-r--r--src/jaegertracing/thrift/lib/py/src/__init__.py20
-rw-r--r--src/jaegertracing/thrift/lib/py/src/compat.py46
-rw-r--r--src/jaegertracing/thrift/lib/py/src/ext/binary.cpp38
-rw-r--r--src/jaegertracing/thrift/lib/py/src/ext/binary.h217
-rw-r--r--src/jaegertracing/thrift/lib/py/src/ext/compact.cpp107
-rw-r--r--src/jaegertracing/thrift/lib/py/src/ext/compact.h368
-rw-r--r--src/jaegertracing/thrift/lib/py/src/ext/endian.h96
-rw-r--r--src/jaegertracing/thrift/lib/py/src/ext/module.cpp203
-rw-r--r--src/jaegertracing/thrift/lib/py/src/ext/protocol.h96
-rw-r--r--src/jaegertracing/thrift/lib/py/src/ext/protocol.tcc913
-rw-r--r--src/jaegertracing/thrift/lib/py/src/ext/types.cpp113
-rw-r--r--src/jaegertracing/thrift/lib/py/src/ext/types.h192
-rw-r--r--src/jaegertracing/thrift/lib/py/src/protocol/TBase.py82
-rw-r--r--src/jaegertracing/thrift/lib/py/src/protocol/TBinaryProtocol.py301
-rw-r--r--src/jaegertracing/thrift/lib/py/src/protocol/TCompactProtocol.py487
-rw-r--r--src/jaegertracing/thrift/lib/py/src/protocol/THeaderProtocol.py225
-rw-r--r--src/jaegertracing/thrift/lib/py/src/protocol/TJSONProtocol.py677
-rw-r--r--src/jaegertracing/thrift/lib/py/src/protocol/TMultiplexedProtocol.py39
-rw-r--r--src/jaegertracing/thrift/lib/py/src/protocol/TProtocol.py422
-rw-r--r--src/jaegertracing/thrift/lib/py/src/protocol/TProtocolDecorator.py26
-rw-r--r--src/jaegertracing/thrift/lib/py/src/protocol/__init__.py21
-rw-r--r--src/jaegertracing/thrift/lib/py/src/server/THttpServer.py131
-rw-r--r--src/jaegertracing/thrift/lib/py/src/server/TNonblockingServer.py370
-rw-r--r--src/jaegertracing/thrift/lib/py/src/server/TProcessPoolServer.py123
-rw-r--r--src/jaegertracing/thrift/lib/py/src/server/TServer.py323
-rw-r--r--src/jaegertracing/thrift/lib/py/src/server/__init__.py20
-rw-r--r--src/jaegertracing/thrift/lib/py/src/transport/THeaderTransport.py352
-rw-r--r--src/jaegertracing/thrift/lib/py/src/transport/THttpClient.py187
-rw-r--r--src/jaegertracing/thrift/lib/py/src/transport/TSSLSocket.py408
-rw-r--r--src/jaegertracing/thrift/lib/py/src/transport/TSocket.py215
-rw-r--r--src/jaegertracing/thrift/lib/py/src/transport/TTransport.py456
-rw-r--r--src/jaegertracing/thrift/lib/py/src/transport/TTwisted.py329
-rw-r--r--src/jaegertracing/thrift/lib/py/src/transport/TZlibTransport.py248
-rw-r--r--src/jaegertracing/thrift/lib/py/src/transport/__init__.py20
-rw-r--r--src/jaegertracing/thrift/lib/py/src/transport/sslcompat.py100
-rw-r--r--src/jaegertracing/thrift/lib/py/test/_import_local_thrift.py30
-rw-r--r--src/jaegertracing/thrift/lib/py/test/test_sslsocket.py353
-rw-r--r--src/jaegertracing/thrift/lib/py/test/thrift_json.py51
-rw-r--r--src/jaegertracing/thrift/lib/rb/Gemfile4
-rwxr-xr-xsrc/jaegertracing/thrift/lib/rb/Makefile.am55
-rw-r--r--src/jaegertracing/thrift/lib/rb/README.md43
-rw-r--r--src/jaegertracing/thrift/lib/rb/Rakefile120
-rw-r--r--src/jaegertracing/thrift/lib/rb/benchmark/Benchmark.thrift24
-rw-r--r--src/jaegertracing/thrift/lib/rb/benchmark/benchmark.rb271
-rw-r--r--src/jaegertracing/thrift/lib/rb/benchmark/client.rb74
-rw-r--r--src/jaegertracing/thrift/lib/rb/benchmark/server.rb82
-rw-r--r--src/jaegertracing/thrift/lib/rb/benchmark/thin_server.rb44
-rw-r--r--src/jaegertracing/thrift/lib/rb/coding_standards.md1
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/binary_protocol_accelerated.c460
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/binary_protocol_accelerated.h20
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/bytes.c36
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/bytes.h31
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/compact_protocol.c637
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/compact_protocol.h20
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/constants.h99
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/extconf.rb34
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/macros.h41
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/memory_buffer.c134
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/memory_buffer.h20
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/protocol.c0
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/protocol.h0
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/strlcpy.c41
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/strlcpy.h34
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/struct.c711
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/struct.h25
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/thrift_native.c201
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift.rb70
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/bytes.rb131
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/client.rb71
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/core_ext.rb23
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/core_ext/fixnum.rb29
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/exceptions.rb87
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/multiplexed_processor.rb76
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/processor.rb75
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/base_protocol.rb387
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/binary_protocol.rb244
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/binary_protocol_accelerated.rb47
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/compact_protocol.rb443
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/json_protocol.rb786
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/multiplexed_protocol.rb44
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/protocol_decorator.rb194
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/serializer/deserializer.rb33
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/serializer/serializer.rb34
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/server/base_server.rb37
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/server/mongrel_http_server.rb60
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/server/nonblocking_server.rb305
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/server/simple_server.rb47
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/server/thin_http_server.rb91
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/server/thread_pool_server.rb79
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/server/threaded_server.rb51
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/struct.rb237
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/struct_union.rb192
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/thrift_native.rb24
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/base_server_transport.rb37
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/base_transport.rb117
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/buffered_transport.rb122
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/framed_transport.rb125
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/http_client_transport.rb61
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/io_stream_transport.rb42
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/memory_buffer_transport.rb129
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/server_socket.rb68
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/socket.rb143
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/ssl_server_socket.rb41
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/ssl_socket.rb51
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/unix_server_socket.rb64
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/unix_socket.rb44
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/types.rb101
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/union.rb176
-rw-r--r--src/jaegertracing/thrift/lib/rb/script/proto_benchmark.rb121
-rw-r--r--src/jaegertracing/thrift/lib/rb/script/read_struct.rb43
-rw-r--r--src/jaegertracing/thrift/lib/rb/script/write_struct.rb30
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/BaseService.thrift27
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/ExtendedService.thrift25
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/Referenced.thrift44
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/ThriftNamespacedSpec.thrift53
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/ThriftSpec.thrift183
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/base_protocol_spec.rb225
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/base_transport_spec.rb388
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/binary_protocol_accelerated_spec.rb46
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/binary_protocol_spec.rb74
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/binary_protocol_spec_shared.rb458
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/bytes_spec.rb160
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/client_spec.rb98
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/compact_protocol_spec.rb158
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/exception_spec.rb141
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/flat_spec.rb62
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/http_client_spec.rb139
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/json_protocol_spec.rb552
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/namespaced_spec.rb67
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/nonblocking_server_spec.rb263
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/processor_spec.rb80
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/serializer_spec.rb67
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/server_socket_spec.rb84
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/server_spec.rb187
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/socket_spec.rb68
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/socket_spec_shared.rb104
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/spec_helper.rb64
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/ssl_server_socket_spec.rb34
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/ssl_socket_spec.rb78
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/struct_nested_containers_spec.rb191
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/struct_spec.rb293
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/thin_http_server_spec.rb141
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/types_spec.rb118
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/union_spec.rb214
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/unix_socket_spec.rb116
-rw-r--r--src/jaegertracing/thrift/lib/rb/thrift.gemspec40
-rw-r--r--src/jaegertracing/thrift/lib/rs/Cargo.toml18
-rw-r--r--src/jaegertracing/thrift/lib/rs/Makefile.am46
-rw-r--r--src/jaegertracing/thrift/lib/rs/README.md120
-rw-r--r--src/jaegertracing/thrift/lib/rs/RELEASING.md57
-rwxr-xr-xsrc/jaegertracing/thrift/lib/rs/release.sh26
-rw-r--r--src/jaegertracing/thrift/lib/rs/src/autogen.rs45
-rw-r--r--src/jaegertracing/thrift/lib/rs/src/errors.rs667
-rw-r--r--src/jaegertracing/thrift/lib/rs/src/lib.rs91
-rw-r--r--src/jaegertracing/thrift/lib/rs/src/protocol/binary.rs956
-rw-r--r--src/jaegertracing/thrift/lib/rs/src/protocol/compact.rs2385
-rw-r--r--src/jaegertracing/thrift/lib/rs/src/protocol/mod.rs968
-rw-r--r--src/jaegertracing/thrift/lib/rs/src/protocol/multiplexed.rs239
-rw-r--r--src/jaegertracing/thrift/lib/rs/src/protocol/stored.rs195
-rw-r--r--src/jaegertracing/thrift/lib/rs/src/server/mod.rs123
-rw-r--r--src/jaegertracing/thrift/lib/rs/src/server/multiplexed.rs351
-rw-r--r--src/jaegertracing/thrift/lib/rs/src/server/threaded.rs233
-rw-r--r--src/jaegertracing/thrift/lib/rs/src/transport/buffered.rs483
-rw-r--r--src/jaegertracing/thrift/lib/rs/src/transport/framed.rs459
-rw-r--r--src/jaegertracing/thrift/lib/rs/src/transport/mem.rs385
-rw-r--r--src/jaegertracing/thrift/lib/rs/src/transport/mod.rs291
-rw-r--r--src/jaegertracing/thrift/lib/rs/src/transport/socket.rs168
-rw-r--r--src/jaegertracing/thrift/lib/rs/test/Cargo.toml15
-rw-r--r--src/jaegertracing/thrift/lib/rs/test/Makefile.am55
-rw-r--r--src/jaegertracing/thrift/lib/rs/test/src/bin/kitchen_sink_client.rs239
-rw-r--r--src/jaegertracing/thrift/lib/rs/test/src/bin/kitchen_sink_server.rs313
-rw-r--r--src/jaegertracing/thrift/lib/rs/test/src/lib.rs55
-rw-r--r--src/jaegertracing/thrift/lib/rs/test/thrifts/Base_One.thrift84
-rw-r--r--src/jaegertracing/thrift/lib/rs/test/thrifts/Base_Two.thrift50
-rw-r--r--src/jaegertracing/thrift/lib/rs/test/thrifts/Midlayer.thrift71
-rw-r--r--src/jaegertracing/thrift/lib/rs/test/thrifts/Ultimate.thrift66
-rw-r--r--src/jaegertracing/thrift/lib/st/README.md39
-rw-r--r--src/jaegertracing/thrift/lib/st/coding_standards.md1
-rw-r--r--src/jaegertracing/thrift/lib/st/package.xml26
-rw-r--r--src/jaegertracing/thrift/lib/st/thrift.st815
-rw-r--r--src/jaegertracing/thrift/lib/swift/Makefile.am46
-rw-r--r--src/jaegertracing/thrift/lib/swift/Package.swift24
-rw-r--r--src/jaegertracing/thrift/lib/swift/README.md215
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/LinuxHelper.swift46
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TApplicationError.swift157
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TBinary.swift32
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TBinaryProtocol.swift384
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TClient.swift55
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TCompactProtocol.swift571
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TEnum.swift32
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TError.swift77
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TFileHandleTransport.swift56
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TFileTransport.swift101
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TFramedTransport.swift123
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/THTTPSessionTransport.swift184
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TList.swift138
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TMap.swift190
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TMemoryBufferTransport.swift74
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TMultiplexedProtocol.swift47
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TProcessor.swift29
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TProtocol.swift182
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TProtocolError.swift146
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TSSLSocketTransport.swift236
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TSSLSocketTransportError.swift48
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TSerializable.swift136
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TSet.swift189
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TSocketServer.swift148
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TSocketTransport.swift216
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TStreamTransport.swift150
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TStruct.swift100
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TTransport.swift64
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TTransportError.swift86
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/TWrappedProtocol.swift208
-rw-r--r--src/jaegertracing/thrift/lib/swift/Sources/Thrift.swift3
-rw-r--r--src/jaegertracing/thrift/lib/swift/Tests/LinuxMain.swift8
-rw-r--r--src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/TBinaryProtocolTests.swift168
-rw-r--r--src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/TCompactProtocolTests.swift210
-rw-r--r--src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/ThriftTests.swift18
-rw-r--r--src/jaegertracing/thrift/lib/ts/.gitignore2
-rw-r--r--src/jaegertracing/thrift/lib/ts/Gruntfile.js163
-rw-r--r--src/jaegertracing/thrift/lib/ts/Makefile.am58
-rw-r--r--src/jaegertracing/thrift/lib/ts/coding_standards.md1
-rw-r--r--src/jaegertracing/thrift/lib/ts/dist/thrift.js0
-rw-r--r--src/jaegertracing/thrift/lib/ts/dist/thrift.min.js1
-rw-r--r--src/jaegertracing/thrift/lib/ts/package-lock.json4955
-rw-r--r--src/jaegertracing/thrift/lib/ts/package.json39
-rwxr-xr-xsrc/jaegertracing/thrift/lib/ts/test/build.xml250
-rw-r--r--src/jaegertracing/thrift/lib/ts/test/phantom-client.ts352
-rw-r--r--src/jaegertracing/thrift/lib/ts/test/server_http.js55
-rw-r--r--src/jaegertracing/thrift/lib/ts/test/test-int64.html46
-rw-r--r--src/jaegertracing/thrift/lib/ts/test/test-int64.ts101
-rwxr-xr-xsrc/jaegertracing/thrift/lib/ts/test/test.html53
-rw-r--r--src/jaegertracing/thrift/lib/ts/test/test.ts342
-rw-r--r--src/jaegertracing/thrift/lib/ts/test/test_handler.js202
-rw-r--r--src/jaegertracing/thrift/lib/ts/thrift.d.ts699
-rw-r--r--src/jaegertracing/thrift/lib/ts/tsconfig.json31
-rw-r--r--src/jaegertracing/thrift/lib/xml/Makefile.am29
-rw-r--r--src/jaegertracing/thrift/lib/xml/test/Makefile.am26
-rw-r--r--src/jaegertracing/thrift/lib/xml/test/build.xml112
-rw-r--r--src/jaegertracing/thrift/lib/xml/thrift-idl.xsd283
1801 files changed, 303644 insertions, 0 deletions
diff --git a/src/jaegertracing/thrift/lib/Makefile.am b/src/jaegertracing/thrift/lib/Makefile.am
new file mode 100644
index 000000000..73326a56d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/Makefile.am
@@ -0,0 +1,127 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+SUBDIRS = json xml
+PRECROSS_TARGET =
+
+if WITH_AS3
+SUBDIRS += as3
+endif
+
+if WITH_CPP
+SUBDIRS += cpp
+endif
+
+if WITH_C_GLIB
+SUBDIRS += c_glib
+endif
+
+if WITH_MONO
+SUBDIRS += csharp
+endif
+
+if WITH_JAVA
+SUBDIRS += java
+PRECROSS_TARGET += precross-java
+# JavaScript unit test depends on java
+# so test only if java, ant & co is available
+SUBDIRS += js
+endif
+
+if WITH_PYTHON
+SUBDIRS += py
+endif
+
+if WITH_ERLANG
+SUBDIRS += erl
+endif
+
+if WITH_RUBY
+SUBDIRS += rb
+endif
+
+if WITH_HASKELL
+SUBDIRS += hs
+endif
+
+if WITH_PERL
+SUBDIRS += perl
+endif
+
+if WITH_PHP
+SUBDIRS += php
+endif
+
+if WITH_DART
+SUBDIRS += dart
+endif
+
+if WITH_DOTNET
+SUBDIRS += netcore
+SUBDIRS += netstd
+endif
+
+if WITH_GO
+SUBDIRS += go
+endif
+
+if WITH_D
+SUBDIRS += d
+PRECROSS_TARGET += precross-d
+endif
+
+if WITH_NODEJS
+SUBDIRS += nodejs
+PRECROSS_TARGET += precross-nodejs
+SUBDIRS += nodets
+endif
+
+if WITH_LUA
+SUBDIRS += lua
+endif
+
+if WITH_RS
+SUBDIRS += rs
+endif
+
+if WITH_CL
+SUBDIRS += cl
+endif
+
+if WITH_SWIFT
+SUBDIRS += swift
+endif
+
+# All of the libs that don't use Automake need to go in here
+# so they will end up in our release tarballs.
+EXTRA_DIST = \
+ as3 \
+ d \
+ dart \
+ delphi \
+ haxe \
+ javame \
+ js \
+ ocaml \
+ st \
+ ts
+
+precross-%:
+ $(MAKE) -C $* precross
+precross: $(PRECROSS_TARGET)
diff --git a/src/jaegertracing/thrift/lib/as3/CMakeLists.txt b/src/jaegertracing/thrift/lib/as3/CMakeLists.txt
new file mode 100644
index 000000000..999905da0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/CMakeLists.txt
@@ -0,0 +1,68 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+if (IS_ABSOLUTE "${LIB_INSTALL_DIR}")
+ set(AS3_INSTALL_DIR "${LIB_INSTALL_DIR}/as3")
+else ()
+ set(AS3_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/as3")
+endif ()
+
+set(PRELEASE "true")
+if (CMAKE_BUILD_TYPE MATCHES DEBUG)
+ set(PRELEASE "false")
+endif ()
+
+add_custom_target(ThriftAs3 ALL
+ COMMENT "Building as3 library using Gradle Wrapper"
+ COMMAND ${GRADLEW_EXECUTABLE} ${GRADLE_OPTS} compileFlex
+ --console=plain --no-daemon
+ -Prelease=${PRELEASE}
+ "-Pbuild.dir=${CMAKE_CURRENT_BINARY_DIR}/build"
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+
+# Enable publishing from CMake if the publishing information is provided
+if (NOT CMAKE_BUILD_TYPE MATCHES DEBUG)
+ add_custom_target(MavenPublishAs3
+ COMMENT "Publishing as3 library to Apache Maven staging"
+ COMMAND ${GRADLEW_EXECUTABLE} ${GRADLE_OPTS} clean publishMavenPublicationToMavenRepository
+ --console=plain --no-daemon
+ -Prelease=${PRELEASE}
+ -Psign=${PRELEASE}
+ "-Pbuild.dir=${CMAKE_CURRENT_BINARY_DIR}/build"
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+endif ()
+
+# Hook the CMake install process to the results from make ALL.
+# This works best when 'make all && sudo make install/fast' is used.
+# Using slash to end the source location to avoid copying the directory path.
+install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build/libs/
+ DESTINATION ${AS3_INSTALL_DIR}
+ FILES_MATCHING PATTERN "libthrift-as3.swc")
+
+if (BUILD_TESTING)
+ add_test(NAME As3Test
+ COMMAND ${GRADLEW_EXECUTABLE} ${GRADLE_OPTS} test
+ --console=plain --no-daemon
+ -Prelease=${PRELEASE}
+ "-Pbuild.dir=${CMAKE_CURRENT_BINARY_DIR}/build"
+ "-Pthrift.compiler=${THRIFT_COMPILER}"
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+endif ()
diff --git a/src/jaegertracing/thrift/lib/as3/Makefile.am b/src/jaegertracing/thrift/lib/as3/Makefile.am
new file mode 100644
index 000000000..0b3c3befc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/Makefile.am
@@ -0,0 +1,60 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+all-local:
+ ./gradlew $(GRADLE_OPTS) compile \
+ -Prelease=true \
+ --console=plain
+
+install-exec-hook:
+ ./gradlew $(GRADLE_OPTS) publishToMavenLocal \
+ -Prelease=true \
+ --console=plain
+
+clean-local:
+ ./gradlew $(GRADLE_OPTS) clean \
+ -Prelease=true \
+ --console=plain
+ $(RM) -r .gradle
+
+check-local: $(THRIFT)
+ ./gradlew $(GRADLE_OPTS) test \
+ -Prelease=true \
+ --console=plain
+
+maven-publish:
+ ./gradlew $(GRADLE_OPTS) publishMavenPublicationToMavenRepository \
+ -Prelease=true \
+ -Psign=true \
+ --console=plain
+
+dist-hook:
+ $(RM) -r $(distdir)/.gradle/
+
+EXTRA_DIST = \
+ CMakeLists.txt \
+ README.md \
+ build.gradle \
+ coding_standards.md \
+ gradle \
+ gradle.properties \
+ gradlew \
+ gradlew.bat \
+ settings.gradle \
+ src
diff --git a/src/jaegertracing/thrift/lib/as3/README.md b/src/jaegertracing/thrift/lib/as3/README.md
new file mode 100644
index 000000000..c14f8c78b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/README.md
@@ -0,0 +1,37 @@
+# Apache Thrift ActionScript Library
+
+## Building
+
+We use gradle and gradlefx to build the as3 library. Unfortunately gradlefx requires
+an older version of gradle (2.5) but it still works - for now. If you use the docker
+container to do the build, the Adobe Flex SDK 4.6 is installed and the FLEX_HOME
+environment variable is configured:
+
+ dev@ubuntu:~/thrift$ docker run -v $(pwd):/thrift/src:rw -it thrift/thrift-build:ubuntu-bionic /bin/bash
+ root@7624b61bbf84:/thrift/src# cd lib/as3
+ root@7624b61bbf84:/thrift/src/lib/as3# ./gradlew -Prelease=true compileFlex
+
+ ...
+
+ :compileFlex UP-TO-DATE
+
+ BUILD SUCCESSFUL
+
+ Total time: 10.784 secs
+
+ root@7624b61bbf84:/thrift/src/lib/as3# ls -ls build/
+ total 4
+ 4 -rw-r--r-- 1 root root 1379 Jan 22 19:23 libthrift-as3.swc
+
+## Publishing
+
+We use a similar gradle-based signing and publishing mechanism as in the java
+library. See the java library [README.md](../java/README.md) for more details.
+
+To publish into a local .m2 repository you can mount a directory into the docker container,
+for example:
+
+ dev@ubuntu:~/thrift$ docker run -v~/.m2:/root/.m2 -v $(pwd):/thrift/src:rw -it thrift/thrift-build:ubuntu-bionic /bin/bash
+ root@7624b61bbf84:/thrift/src/lib/as3# ./gradlew -Prelease=true publishToMavenLocal
+
+You will find your `~/.m2` directory is now populated with a release build `swc`.
diff --git a/src/jaegertracing/thrift/lib/as3/build.gradle b/src/jaegertracing/thrift/lib/as3/build.gradle
new file mode 100644
index 000000000..78534996a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/build.gradle
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+buildscript {
+ repositories {
+ mavenLocal()
+ mavenCentral()
+ }
+ dependencies {
+ classpath group: 'org.gradlefx', name: 'gradlefx', version: '1.5.0'
+ }
+}
+
+plugins {
+ id 'maven-publish'
+ id 'signing'
+}
+
+apply plugin: 'gradlefx'
+
+description = 'Apache Thrift ActionScript Library'
+frameworkLinkage = 'none'
+group = property('thrift.groupid')
+srcDirs = ['src']
+type = 'swc'
+
+// We use the SNAPSHOT suffix for non-release versions
+if (Boolean.parseBoolean(project.release)) {
+ additionalCompilerOptions = ['-compiler.debug=false', '-compiler.strict=true']
+ version = property('thrift.version')
+} else {
+ additionalCompilerOptions = ['-compiler.debug=true', '-compiler.strict=true']
+ version = property('thrift.version') + '-SNAPSHOT'
+}
+
+defaultTasks 'compile'
+
+// Keeping the rest of the build logic in functional named scripts for clarity
+apply from: 'gradle/publishing.gradle'
+
diff --git a/src/jaegertracing/thrift/lib/as3/coding_standards.md b/src/jaegertracing/thrift/lib/as3/coding_standards.md
new file mode 100644
index 000000000..fa0390bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/coding_standards.md
@@ -0,0 +1 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
diff --git a/src/jaegertracing/thrift/lib/as3/gradle.properties b/src/jaegertracing/thrift/lib/as3/gradle.properties
new file mode 100644
index 000000000..02d5eefad
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/gradle.properties
@@ -0,0 +1,23 @@
+# This file is shared currently between this Gradle build and the
+# Ant builds for fd303 and JavaScript. Keep the dotted notation for
+# the properties to minimize the changes in the dependencies.
+thrift.version=0.13.0
+thrift.groupid=org.apache.thrift
+release=false
+sign=false
+
+# Local Install paths
+install.path=/usr/local/lib
+install.javadoc.path=/usr/local/lib
+
+# Test execution properties
+testPort=9090
+
+# Maven dependency download locations
+mvn.repo=http://repo1.maven.org/maven2
+apache.repo=https://repository.apache.org/content/repositories/releases
+
+# Apache Maven publish
+license=http://www.apache.org/licenses/LICENSE-2.0.txt
+maven-repository-url=https://repository.apache.org/service/local/staging/deploy/maven2
+maven-repository-id=apache.releases.https
diff --git a/src/jaegertracing/thrift/lib/as3/gradle/publishing.gradle b/src/jaegertracing/thrift/lib/as3/gradle/publishing.gradle
new file mode 100644
index 000000000..3e0ecf319
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/gradle/publishing.gradle
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Following Gradle best practices to keep build logic organized
+
+model {
+ tasks.signMavenPublication {
+ dependsOn compileFlex
+ }
+}
+
+publishing {
+ publications {
+ maven(MavenPublication) {
+
+ groupId = "$group"
+ artifactId = "${project.name}"
+ version = "$version"
+
+ def swcFile = file("$buildDir/libthrift-as3.swc")
+ artifact(swcFile)
+
+ pom {
+ description = 'Thrift is a software framework for scalable cross-language services development.'
+ packaging = 'swc'
+
+ // older gradle doesn't recognize all the properties, so we inject them..
+ withXml {
+ asNode().with {
+ appendNode('name', 'Apache Thrift')
+ appendNode('url', 'http://thrift.apache.org/')
+ appendNode('scm').with {
+ appendNode('url', 'https://github.com/apache/thrift/')
+ appendNode('connection', 'scm:git:https://github.com/apache/thrift.git')
+ appendNode('developerConnection', 'scm:git:git@github.com:apache/thrift.git')
+ }
+ appendNode('issueManagement').with {
+ appendNode('url', 'https://issues.apache.org/jira/projects/THRIFT/')
+ appendNode('system', 'Jira')
+ }
+ appendNode('licenses').with {
+ appendNode('license').with {
+ appendNode('name', 'The Apache Software License, Version 2.0')
+ appendNode('url', "${project.license}")
+ }
+ }
+ appendNode('organization').with {
+ appendNode('name', 'The Apache Software Foundation')
+ appendNode('url', 'http://www.apache.org/')
+ }
+ appendNode('developers').with {
+ appendNode('developer').with {
+ appendNode('id', 'dev')
+ appendNode('name', 'Apache Thrift Developers')
+ appendNode('email', 'dev@thrift.apache.org')
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ repositories {
+ maven {
+ url = property('maven-repository-url')
+
+ if (project.hasProperty('mavenUser') && project.hasProperty('mavenPassword')) {
+ credentials {
+ username = property('mavenUser')
+ password = property('mavenPassword')
+ }
+ }
+ }
+ }
+}
+
+signing {
+ required { property('sign') }
+ sign publishing.publications.maven
+}
diff --git a/src/jaegertracing/thrift/lib/as3/gradle/wrapper/gradle-wrapper.jar b/src/jaegertracing/thrift/lib/as3/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..87b738cbd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/src/jaegertracing/thrift/lib/as3/gradle/wrapper/gradle-wrapper.properties b/src/jaegertracing/thrift/lib/as3/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..558870dad
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/src/jaegertracing/thrift/lib/as3/gradlew b/src/jaegertracing/thrift/lib/as3/gradlew
new file mode 100755
index 000000000..af6708ff2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/src/jaegertracing/thrift/lib/as3/gradlew.bat b/src/jaegertracing/thrift/lib/as3/gradlew.bat
new file mode 100644
index 000000000..6d57edc70
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/src/jaegertracing/thrift/lib/as3/settings.gradle b/src/jaegertracing/thrift/lib/as3/settings.gradle
new file mode 100644
index 000000000..493234641
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/settings.gradle
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+rootProject.name = 'libthrift-as3'
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/AbstractMethodError.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/AbstractMethodError.as
new file mode 100644
index 000000000..a2082b888
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/AbstractMethodError.as
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift {
+
+ import flash.errors.IllegalOperationError;
+
+ public class AbstractMethodError extends IllegalOperationError {
+
+ public function AbstractMethodError(message:String="") {
+ super("Attempt to call an abstract method");
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/Set.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/Set.as
new file mode 100644
index 000000000..ae5f42843
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/Set.as
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift {
+ import flash.utils.Dictionary;
+
+
+ public class Set {
+
+ private var _elements:Dictionary = new Dictionary();
+ private var _size:int = 0;
+
+ public function Set(... values) {
+ for each (var value:* in values) {
+ add(value);
+ }
+ }
+
+ public function add(o:*):Boolean {
+ var alreadyPresent:Boolean = _elements.hasOwnProperty(o);
+ if (! alreadyPresent) {
+ _size++;
+ _elements[o] = true;
+ }
+
+ return ! alreadyPresent;
+ }
+
+ public function clear():void {
+ for (var value:* in _elements) {
+ remove(value);
+ }
+ }
+
+ public function contains(o:Object):Boolean {
+ return _elements.hasOwnProperty(o);
+ }
+
+ public function isEmpty():Boolean {
+ return _size == 0;
+ }
+
+ public function remove(o:*):Boolean {
+ if (contains(o)) {
+ delete _elements[o];
+ _size--;
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ public function toArray():Array {
+ var ret:Array = new Array();
+ for (var key:* in _elements) {
+ ret.push(key);
+ }
+ return ret;
+ }
+
+ public function get size():int {
+ return _size;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TApplicationError.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TApplicationError.as
new file mode 100644
index 000000000..3448fce0e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TApplicationError.as
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift {
+
+ import org.apache.thrift.protocol.TField;
+ import org.apache.thrift.protocol.TProtocol;
+ import org.apache.thrift.protocol.TProtocolUtil;
+ import org.apache.thrift.protocol.TStruct;
+ import org.apache.thrift.protocol.TType;
+
+ /**
+ * Application level exception
+ */
+ public class TApplicationError extends TError {
+
+ private static const TAPPLICATION_EXCEPTION_STRUCT:TStruct = new TStruct("TApplicationException");
+ private static const MESSAGE_FIELD:TField = new TField("message", TType.STRING, 1);
+ private static const TYPE_FIELD:TField = new TField("type", TType.I32, 2);
+
+ public static const UNKNOWN:int = 0;
+ public static const UNKNOWN_METHOD:int = 1;
+ public static const INVALID_MESSAGE_TYPE:int = 2;
+ public static const WRONG_METHOD_NAME:int = 3;
+ public static const BAD_SEQUENCE_ID:int = 4;
+ public static const MISSING_RESULT:int = 5;
+ public static const INTERNAL_ERROR:int = 6;
+ public static const PROTOCOL_ERROR:int = 7;
+ public static const INVALID_TRANSFORM:int = 8;
+ public static const INVALID_PROTOCOL:int = 9;
+ public static const UNSUPPORTED_CLIENT_TYPE:int = 10;
+
+ public function TApplicationError(type:int = UNKNOWN, message:String = "") {
+ super(message, type);
+ }
+
+ public static function read(iprot:TProtocol):TApplicationError {
+ var field:TField;
+ iprot.readStructBegin();
+
+ var message:String = null;
+ var type:int = UNKNOWN;
+
+ while (true) {
+ field = iprot.readFieldBegin();
+ if (field.type == TType.STOP) {
+ break;
+ }
+ switch (field.id) {
+ case 1:
+ if (field.type == TType.STRING) {
+ message = iprot.readString();
+ }
+ else {
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ break;
+ case 2:
+ if (field.type == TType.I32) {
+ type = iprot.readI32();
+ }
+ else {
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ break;
+ default:
+ TProtocolUtil.skip(iprot, field.type);
+ break;
+ }
+ iprot.readFieldEnd();
+ }
+ iprot.readStructEnd();
+ return new TApplicationError(type, message);
+ }
+
+ public function write(oprot:TProtocol):void {
+ oprot.writeStructBegin(TAPPLICATION_EXCEPTION_STRUCT);
+ if (message != null) {
+ oprot.writeFieldBegin(MESSAGE_FIELD);
+ oprot.writeString(message);
+ oprot.writeFieldEnd();
+ }
+ oprot.writeFieldBegin(TYPE_FIELD);
+ oprot.writeI32(errorID);
+ oprot.writeFieldEnd();
+ oprot.writeFieldStop();
+ oprot.writeStructEnd();
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TBase.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TBase.as
new file mode 100644
index 000000000..615db1d75
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TBase.as
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift {
+
+ import org.apache.thrift.protocol.TProtocol;
+
+ /**
+ * Generic base interface for generated Thrift objects.
+ *
+ */
+ public interface TBase {
+
+ /**
+ * Reads the TObject from the given input protocol.
+ *
+ * @param iprot Input protocol
+ */
+ function read(iprot:TProtocol):void;
+
+ /**
+ * Writes the objects out to the protocol
+ *
+ * @param oprot Output protocol
+ */
+ function write(oprot:TProtocol):void;
+
+ /**
+ * Check if a field is currently set or unset.
+ *
+ * @param fieldId The field's id tag as found in the IDL.
+ */
+ function isSet(fieldId:int):Boolean;
+
+ /**
+ * Get a field's value by id. Primitive types will be wrapped in the
+ * appropriate "boxed" types.
+ *
+ * @param fieldId The field's id tag as found in the IDL.
+ */
+ function getFieldValue(fieldId:int):*;
+
+ /**
+ * Set a field's value by id. Primitive types must be "boxed" in the
+ * appropriate object wrapper type.
+ *
+ * @param fieldId The field's id tag as found in the IDL.
+ */
+ function setFieldValue(fieldId:int, value:*):void;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TError.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TError.as
new file mode 100644
index 000000000..ccc13b554
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TError.as
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift {
+
+ public class TError extends Error {
+
+ public function TError(message:String = "", errorCode:int = 0) {
+ super(message, errorCode);
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TFieldRequirementType.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TFieldRequirementType.as
new file mode 100644
index 000000000..6fb4e58f9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TFieldRequirementType.as
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift {
+
+ /**
+ * Requirement type constants.
+ *
+ */
+ public class TFieldRequirementType {
+ public static const REQUIRED:int = 1;
+ public static const OPTIONAL:int = 2;
+ public static const DEFAULT:int = 3;
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TProcessor.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TProcessor.as
new file mode 100644
index 000000000..850acc946
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/TProcessor.as
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+ package org.apache.thrift {
+
+import org.apache.thrift.protocol.TProtocol;
+
+ /**
+ * A processor is a generic object which operates upon an input stream and
+ * writes to some output stream.
+ *
+ */
+ public interface TProcessor {
+ function process(input:TProtocol, output:TProtocol):Boolean;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/FieldMetaData.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/FieldMetaData.as
new file mode 100644
index 000000000..cb18a1445
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/FieldMetaData.as
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data {
+
+ import flash.utils.Dictionary;
+
+ /**
+ * This class is used to store meta data about thrift fields. Every field in a
+ * a struct should have a corresponding instance of this class describing it.
+ *
+ */
+ public class FieldMetaData {
+
+ public var fieldName:String;
+ public var requirementType:int;
+ public var valueMetaData:FieldValueMetaData;
+
+ private static var structMap:Dictionary = new Dictionary();
+
+ public function FieldMetaData(name:String, req:int, vMetaData:FieldValueMetaData) {
+ this.fieldName = name;
+ this.requirementType = req;
+ this.valueMetaData = vMetaData;
+ }
+
+ public static function addStructMetaDataMap(sClass:Class, map:Dictionary):void{
+ structMap[sClass] = map;
+ }
+
+ /**
+ * Returns a map with metadata (i.e. instances of FieldMetaData) that
+ * describe the fields of the given class.
+ *
+ * @param sClass The TBase class for which the metadata map is requested
+ */
+ public static function getStructMetaDataMap(sClass:Class):Dictionary {
+ return structMap[sClass];
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/FieldValueMetaData.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/FieldValueMetaData.as
new file mode 100644
index 000000000..07fe1be20
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/FieldValueMetaData.as
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data {
+
+ import org.apache.thrift.protocol.TType;
+
+ /**
+ * FieldValueMetaData and collection of subclasses to store metadata about
+ * the value(s) of a field
+ */
+ public class FieldValueMetaData {
+
+ public var type:int;
+
+ public function FieldValueMetaData(type:int) {
+ this.type = type;
+ }
+
+ public function isStruct():Boolean {
+ return type == TType.STRUCT;
+ }
+
+ public function isContainer():Boolean {
+ return type == TType.LIST || type == TType.MAP || type == TType.SET;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/ListMetaData.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/ListMetaData.as
new file mode 100644
index 000000000..a2cc73288
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/ListMetaData.as
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data {
+
+ public class ListMetaData extends FieldValueMetaData {
+
+ public var elemMetaData:FieldValueMetaData;
+
+ public function ListMetaData(type:int, eMetaData:FieldValueMetaData) {
+ super(type);
+ this.elemMetaData = eMetaData;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/MapMetaData.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/MapMetaData.as
new file mode 100644
index 000000000..e7f1f9f08
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/MapMetaData.as
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data {
+
+ public class MapMetaData extends FieldValueMetaData {
+
+ public var keyMetaData:FieldValueMetaData;
+ public var valueMetaData:FieldValueMetaData;
+
+ public function MapMetaData(type:int, kMetaData:FieldValueMetaData, vMetaData:FieldValueMetaData) {
+ super(type);
+ this.keyMetaData = kMetaData;
+ this.valueMetaData = vMetaData;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/SetMetaData.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/SetMetaData.as
new file mode 100644
index 000000000..390f03468
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/SetMetaData.as
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data {
+
+ public class SetMetaData extends FieldValueMetaData {
+
+ public var elemMetaData:FieldValueMetaData;
+
+ public function SetMetaData(type:int, eMetaData:FieldValueMetaData) {
+ super(type);
+ this.elemMetaData = eMetaData;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/StructMetaData.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/StructMetaData.as
new file mode 100644
index 000000000..fc9b0bee6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/meta_data/StructMetaData.as
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data {
+
+ public class StructMetaData extends FieldValueMetaData {
+
+ public var structClass:Class;
+
+ public function StructMetaData(type:int, sClass:Class) {
+ super(type);
+ this.structClass = sClass;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TBinaryProtocol.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TBinaryProtocol.as
new file mode 100644
index 000000000..b2ff9d808
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TBinaryProtocol.as
@@ -0,0 +1,316 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol {
+
+ import flash.utils.ByteArray;
+
+ import org.apache.thrift.TError;
+ import org.apache.thrift.transport.THttpClient;
+ import org.apache.thrift.transport.TTransport;
+
+ /**
+ * Binary protocol implementation for thrift.
+ */
+ public class TBinaryProtocol implements TProtocol {
+
+ private static var ANONYMOUS_STRUCT:TStruct = new TStruct();
+
+ protected static const VERSION_MASK:int = int(0xffff0000);
+ protected static const VERSION_1:int = int(0x80010000);
+
+ protected var strictRead_:Boolean = false;
+ protected var strictWrite_:Boolean = true;
+
+ /**
+ * Factory
+ */
+ /*
+ public static class Factory implements TProtocolFactory {
+ protected boolean strictRead_ = false;
+ protected boolean strictWrite_ = true;
+
+ public Factory() {
+ this(false, true);
+ }
+
+ public Factory(boolean strictRead, boolean strictWrite) {
+ strictRead_ = strictRead;
+ strictWrite_ = strictWrite;
+ }
+
+ public TProtocol getProtocol(TTransport trans) {
+ return new TBinaryProtocol(trans, strictRead_, strictWrite_);
+ }
+ }
+ */
+
+ private var trans_:TTransport;
+
+ /**
+ * Constructor
+ */
+ public function TBinaryProtocol(trans:TTransport, strictRead:Boolean=false, strictWrite:Boolean=true) {
+ trans_ = trans;
+ strictRead_ = strictRead;
+ strictWrite_ = strictWrite;
+ }
+
+ public function getTransport():TTransport {
+ return trans_;
+ }
+
+ public function writeMessageBegin(message:TMessage):void {
+ if (strictWrite_) {
+ var version:int = VERSION_1 | message.type;
+ writeI32(version);
+ writeString(message.name);
+ writeI32(message.seqid);
+ } else {
+ writeString(message.name);
+ writeByte(message.type);
+ writeI32(message.seqid);
+ }
+ }
+
+ public function writeMessageEnd():void {}
+
+ public function writeStructBegin(struct:TStruct):void {}
+
+ public function writeStructEnd():void {}
+
+ public function writeFieldBegin(field:TField):void {
+ writeByte(field.type);
+ writeI16(field.id);
+ }
+
+ public function writeFieldEnd():void {}
+
+ public function writeFieldStop():void {
+ writeByte(TType.STOP);
+ }
+
+ public function writeMapBegin(map:TMap):void {
+ writeByte(map.keyType);
+ writeByte(map.valueType);
+ writeI32(map.size);
+ }
+
+ public function writeMapEnd():void {}
+
+ public function writeListBegin(list:TList):void {
+ writeByte(list.elemType);
+ writeI32(list.size);
+ }
+
+ public function writeListEnd():void {}
+
+ public function writeSetBegin(set:TSet):void {
+ writeByte(set.elemType);
+ writeI32(set.size);
+ }
+
+ public function writeSetEnd():void {}
+
+ public function writeBool(b:Boolean):void {
+ writeByte(b ? 1 : 0);
+ }
+
+ private var out:ByteArray = new ByteArray();
+ public function writeByte(b:int):void {
+ reset(out);
+ out.writeByte(b);
+ trans_.write(out, 0, 1);
+ }
+
+ public function writeI16(i16:int):void {
+ reset(out);
+ out.writeShort(i16);
+ trans_.write(out, 0, 2);
+ }
+
+ public function writeI32(i32:int):void {
+ reset(out);
+ out.writeInt(i32);
+ trans_.write(out, 0, 4);
+ }
+
+ //private byte[] i64out = new byte[8];
+ //public function writeI64(i64:Number):void {
+ //i64out[0] = (byte)(0xff & (i64 >> 56));
+ //i64out[1] = (byte)(0xff & (i64 >> 48));
+ //i64out[2] = (byte)(0xff & (i64 >> 40));
+ //i64out[3] = (byte)(0xff & (i64 >> 32));
+ //i64out[4] = (byte)(0xff & (i64 >> 24));
+ //i64out[5] = (byte)(0xff & (i64 >> 16));
+ //i64out[6] = (byte)(0xff & (i64 >> 8));
+ //i64out[7] = (byte)(0xff & (i64));
+ //trans_.write(i64out, 0, 8);
+ //}
+
+ public function writeDouble(dub:Number):void {
+ reset(out);
+ out.writeDouble(dub);
+ trans_.write(out, 0, 8);
+ }
+
+ private var stringOut:ByteArray = new ByteArray();
+
+ public function writeString(str:String):void {
+ reset(stringOut);
+ stringOut.writeUTFBytes(str);
+
+ writeI32(stringOut.length);
+ trans_.write(stringOut, 0, stringOut.length);
+ }
+
+ public function writeBinary(bin:ByteArray):void {
+ writeI32(bin.length);
+ trans_.write(bin, 0, bin.length);
+ }
+
+ /**
+ * Reading methods.
+ */
+
+ public function readMessageBegin():TMessage {
+ var size:int = readI32();
+ if (size < 0) {
+ var version:int = size & VERSION_MASK;
+ if (version != VERSION_1) {
+ throw new TProtocolError(TProtocolError.BAD_VERSION, "Bad version in readMessageBegin");
+ }
+ return new TMessage(readString(), size & 0x000000ff, readI32());
+ }
+ else {
+ if (strictRead_) {
+ throw new TProtocolError(TProtocolError.BAD_VERSION, "Missing version in readMessageBegin, old client?");
+ }
+ return new TMessage(readStringBody(size), readByte(), readI32());
+ }
+ }
+
+ public function readMessageEnd():void {}
+
+ public function readStructBegin():TStruct {
+ return ANONYMOUS_STRUCT;
+ }
+
+ public function readStructEnd():void {}
+
+ public function readFieldBegin():TField {
+ var type:int = readByte();
+ var id:int = type == TType.STOP ? 0 : readI16();
+ return new TField("", type, id);
+ }
+
+ public function readFieldEnd():void {}
+
+ public function readMapBegin():TMap {
+ return new TMap(readByte(), readByte(), readI32());
+ }
+
+ public function readMapEnd():void {}
+
+ public function readListBegin():TList {
+ return new TList(readByte(), readI32());
+ }
+
+ public function readListEnd():void {}
+
+ public function readSetBegin():TSet {
+ return new TSet(readByte(), readI32());
+ }
+
+ public function readSetEnd():void {}
+
+ public function readBool():Boolean {
+ return (readByte() == 1);
+ }
+
+ private var bytes:ByteArray = new ByteArray();
+
+ public function readByte():int {
+ readAll(1);
+ return bytes.readByte();
+ }
+
+ public function readI16():int {
+ readAll(2);
+ return bytes.readShort();
+ }
+
+ public function readI32():int {
+ readAll(4);
+ return bytes.readInt();
+ }
+
+ //private byte[] i64rd = new byte[8];
+ /*
+ public function readI64() throws TException {
+ readAll(i64rd, 0, 8);
+ return
+ ((long)(i64rd[0] & 0xff) << 56) |
+ ((long)(i64rd[1] & 0xff) << 48) |
+ ((long)(i64rd[2] & 0xff) << 40) |
+ ((long)(i64rd[3] & 0xff) << 32) |
+ ((long)(i64rd[4] & 0xff) << 24) |
+ ((long)(i64rd[5] & 0xff) << 16) |
+ ((long)(i64rd[6] & 0xff) << 8) |
+ ((long)(i64rd[7] & 0xff));
+ }
+ */
+
+ public function readDouble():Number {
+ readAll(8);
+ return bytes.readDouble();
+ }
+
+ public function readString():String {
+ var size:int = readI32();
+ readAll(size);
+ return bytes.readUTFBytes(size);
+ }
+
+ public function readStringBody(size:int):String {
+ readAll(size);
+ return bytes.readUTFBytes(size);
+ }
+
+ public function readBinary():ByteArray {
+ var size:int = readI32();
+ var buf:ByteArray = new ByteArray();
+ trans_.readAll(buf, 0, size);
+ return buf;
+ }
+
+ private function readAll(len:int):void {
+ reset(bytes);
+
+ trans_.readAll(bytes, 0, len);
+
+ bytes.position = 0;
+ }
+
+ private static function reset(arr:ByteArray):void {
+ arr.length = 0;
+ arr.position = 0;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TField.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TField.as
new file mode 100644
index 000000000..1277f3a18
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TField.as
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol {
+
+ public class TField {
+
+ public var name:String;
+ public var type:int;
+ public var id:int;
+
+ public function TField(n:String = "", t:int = 0, i:int = 0) {
+ name = n;
+ type = t;
+ id = i;
+ }
+
+ public function toString():String {
+ return "<TField name:'" + name + "' type:" + type + " field-id:" + id + ">";
+ }
+
+ public function equals(otherField:TField):Boolean {
+ return type == otherField.type && id == otherField.id;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TList.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TList.as
new file mode 100644
index 000000000..f0bdbadba
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TList.as
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol {
+
+ public class TList {
+
+ public var elemType:int;
+ public var size:int;
+
+ public function TList(t:int = 0, s:int = 0) {
+ elemType = t;
+ size = s;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TMap.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TMap.as
new file mode 100644
index 000000000..2298804fb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TMap.as
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol {
+ public class TMap {
+
+ public var keyType:int;
+ public var valueType:int;
+ public var size:int;
+
+ public function TMap(k:int = 0, v:int = 0, s:int = 0) {
+ keyType = k;
+ valueType = v;
+ size = s;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TMessage.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TMessage.as
new file mode 100644
index 000000000..9817235c3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TMessage.as
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol {
+
+ public class TMessage {
+
+ public var name:String;
+ public var type:int;
+ public var seqid:int;
+
+ public function TMessage(n:String = "", t:int = 0, s:int = 0) {
+ name = n;
+ type = t;
+ seqid = s;
+ }
+
+ public function toString():String {
+ return "<TMessage name:'" + name + "' type: " + type + " seqid:" + seqid + ">";
+ }
+
+ public function equals(other:TMessage):Boolean {
+ return name == other.name && type == other.type && seqid == other.seqid;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TMessageType.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TMessageType.as
new file mode 100644
index 000000000..56a9ba52b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TMessageType.as
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol {
+
+ public class TMessageType {
+ public static const CALL:int = 1;
+ public static const REPLY:int = 2;
+ public static const EXCEPTION:int = 3;
+ public static const ONEWAY:int = 4;
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TProtocol.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TProtocol.as
new file mode 100644
index 000000000..bb9d74472
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TProtocol.as
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol {
+
+ import org.apache.thrift.TError;
+ import org.apache.thrift.transport.TTransport;
+
+ import flash.utils.ByteArray;
+
+ /**
+ * Protocol interface definition
+ */
+ public interface TProtocol {
+
+ function TProtocol(trans:TTransport);
+
+ function getTransport():TTransport;
+
+ /**
+ * Writing methods.
+ */
+ function writeMessageBegin(message:TMessage):void;
+
+ function writeMessageEnd():void;
+
+ function writeStructBegin(struct:TStruct):void;
+
+ function writeStructEnd():void;
+
+ function writeFieldBegin(field:TField):void;
+
+ function writeFieldEnd():void;
+
+ function writeFieldStop():void;
+
+ function writeMapBegin(map:TMap):void;
+
+ function writeMapEnd():void;
+
+ function writeListBegin(list:TList):void;
+
+ function writeListEnd():void;
+
+ function writeSetBegin(set:TSet):void;
+
+ function writeSetEnd():void;
+
+ function writeBool(b:Boolean):void;
+
+ function writeByte(b:int):void;
+
+ function writeI16(i16:int):void;
+
+ function writeI32(i32:int):void;
+
+ //function writeI64(i64:Number):void;
+
+ function writeDouble(dub:Number):void;
+
+ function writeString(str:String):void;
+
+ function writeBinary(bin:ByteArray):void;
+
+ /**
+ * Reading methods.
+ */
+ function readMessageBegin():TMessage;
+
+ function readMessageEnd():void;
+
+ function readStructBegin():TStruct;
+
+ function readStructEnd():void;
+
+ function readFieldBegin():TField;
+
+ function readFieldEnd():void;
+
+ function readMapBegin():TMap;
+
+ function readMapEnd():void;
+
+ function readListBegin():TList;
+
+ function readListEnd():void;
+
+ function readSetBegin():TSet;
+
+ function readSetEnd():void;
+
+ function readBool():Boolean;
+
+ function readByte():int;
+
+ function readI16():int;
+
+ function readI32():int;
+
+ //function readI64():Number;
+
+ function readDouble():Number;
+
+ function readString():String;
+
+ function readBinary():ByteArray;
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TProtocolError.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TProtocolError.as
new file mode 100644
index 000000000..9fff73081
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TProtocolError.as
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol {
+
+ import org.apache.thrift.TError;
+
+ public class TProtocolError extends TError {
+
+ public static const UNKNOWN:int = 0;
+ public static const INVALID_DATA:int = 1;
+ public static const NEGATIVE_SIZE:int = 2;
+ public static const SIZE_LIMIT:int = 3;
+ public static const BAD_VERSION:int = 4;
+ public static const NOT_IMPLEMENTED:int = 5;
+ public static const DEPTH_LIMIT:int = 6;
+
+ public function TProtocolError(error:int = UNKNOWN, message:String = "") {
+ super(message, error);
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TProtocolFactory.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TProtocolFactory.as
new file mode 100644
index 000000000..c7f5e2995
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TProtocolFactory.as
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol {
+
+ import org.apache.thrift.transport.TTransport;
+
+ public interface TProtocolFactory {
+ function getProtocol(trans:TTransport):TProtocol;
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TProtocolUtil.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TProtocolUtil.as
new file mode 100644
index 000000000..22877b75b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TProtocolUtil.as
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol {
+
+ import org.apache.thrift.TError;
+
+ /**
+ * Utility class with static methods for interacting with protocol data
+ * streams.
+ *
+ */
+ public class TProtocolUtil {
+
+ /**
+ * The maximum recursive depth the skip() function will traverse before
+ * throwing a TException.
+ */
+ private static var maxSkipDepth:int = int.MAX_VALUE;
+
+ /**
+ * Specifies the maximum recursive depth that the skip function will
+ * traverse before throwing a TException. This is a global setting, so
+ * any call to skip in this JVM will enforce this value.
+ *
+ * @param depth the maximum recursive depth. A value of 2 would allow
+ * the skip function to skip a structure or collection with basic children,
+ * but it would not permit skipping a struct that had a field containing
+ * a child struct. A value of 1 would only allow skipping of simple
+ * types and empty structs/collections.
+ */
+ public function setMaxSkipDepth(depth:int):void {
+ maxSkipDepth = depth;
+ }
+
+ /**
+ * Skips over the next data element from the provided input TProtocol object.
+ *
+ * @param prot the protocol object to read from
+ * @param type the next value will be intepreted as this TType value.
+ */
+ public static function skip(prot:TProtocol, type:int):void {
+ skipMaxDepth(prot, type, maxSkipDepth);
+ }
+
+ /**
+ * Skips over the next data element from the provided input TProtocol object.
+ *
+ * @param prot the protocol object to read from
+ * @param type the next value will be intepreted as this TType value.
+ * @param maxDepth this function will only skip complex objects to this
+ * recursive depth, to prevent Java stack overflow.
+ */
+ public static function skipMaxDepth(prot:TProtocol, type:int, maxDepth:int):void {
+ if (maxDepth <= 0) {
+ throw new TError("Maximum skip depth exceeded");
+ }
+ switch (type) {
+ case TType.BOOL: {
+ prot.readBool();
+ break;
+ }
+ case TType.BYTE: {
+ prot.readByte();
+ break;
+ }
+ case TType.I16: {
+ prot.readI16();
+ break;
+ }
+ case TType.I32: {
+ prot.readI32();
+ break;
+ }
+ /*
+ case TType.I64: {
+ prot.readI64();
+ break;
+ }
+ */
+ case TType.DOUBLE: {
+ prot.readDouble();
+ break;
+ }
+ case TType.STRING: {
+ prot.readBinary();
+ break;
+ }
+ case TType.STRUCT: {
+ prot.readStructBegin();
+ while (true) {
+ var field:TField = prot.readFieldBegin();
+ if (field.type == TType.STOP) {
+ break;
+ }
+ skipMaxDepth(prot, field.type, maxDepth - 1);
+ prot.readFieldEnd();
+ }
+ prot.readStructEnd();
+ break;
+ }
+ case TType.MAP: {
+ var map:TMap = prot.readMapBegin();
+ for (var i:int = 0; i < map.size; i++) {
+ skipMaxDepth(prot, map.keyType, maxDepth - 1);
+ skipMaxDepth(prot, map.valueType, maxDepth - 1);
+ }
+ prot.readMapEnd();
+ break;
+ }
+ case TType.SET: {
+ var set:TSet = prot.readSetBegin();
+ for (var j:int = 0; j < set.size; j++) {
+ skipMaxDepth(prot, set.elemType, maxDepth - 1);
+ }
+ prot.readSetEnd();
+ break;
+ }
+ case TType.LIST: {
+ var list:TList = prot.readListBegin();
+ for (var k:int = 0; k < list.size; k++) {
+ skipMaxDepth(prot, list.elemType, maxDepth - 1);
+ }
+ prot.readListEnd();
+ break;
+ }
+ default:
+ throw new TProtocolError(TProtocolError.INVALID_DATA, "invalid data");
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TSet.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TSet.as
new file mode 100644
index 000000000..3f0e1a604
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TSet.as
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol {
+
+ public class TSet {
+
+ public var elemType:int;
+ public var size:int;
+
+ public function TSet(t:int = 0, s:int = 0) {
+ elemType = t;
+ size = s;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TStruct.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TStruct.as
new file mode 100644
index 000000000..dffad79f6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TStruct.as
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol {
+
+ public class TStruct {
+
+ public var name:String;
+
+ public function TStruct(n:String = "") {
+ name = n;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TType.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TType.as
new file mode 100644
index 000000000..69af208c1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/protocol/TType.as
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol {
+
+ public class TType {
+
+ public static const STOP:int = 0;
+ public static const VOID:int = 1;
+ public static const BOOL:int = 2;
+ public static const BYTE:int = 3;
+ public static const DOUBLE:int = 4;
+ public static const I16:int = 6;
+ public static const I32:int = 8;
+ public static const I64:int = 10;
+ public static const STRING:int = 11;
+ public static const STRUCT:int = 12;
+ public static const MAP:int = 13;
+ public static const SET:int = 14;
+ public static const LIST:int = 15;
+
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/TFullDuplexHttpClient.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/TFullDuplexHttpClient.as
new file mode 100644
index 000000000..863c59b35
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/TFullDuplexHttpClient.as
@@ -0,0 +1,251 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport
+{
+
+ import flash.errors.EOFError;
+ import flash.events.Event;
+ import flash.events.IOErrorEvent;
+ import flash.events.ProgressEvent;
+ import flash.events.SecurityErrorEvent;
+ import flash.net.URLLoader;
+ import flash.net.URLLoaderDataFormat;
+ import flash.net.URLRequest;
+ import flash.net.URLRequestMethod;
+ import flash.utils.IDataInput;
+ import flash.utils.IDataOutput;
+ import flash.utils.ByteArray;
+ import flash.net.Socket;
+ import flash.events.EventDispatcher;
+
+
+ /**
+ * HTTP implementation of the TTransport interface. Used for working with a
+ * Thrift web services implementation.
+ * Unlike Http Client, it uses a single POST, and chunk-encoding to transfer all messages.
+ */
+
+ public class TFullDuplexHttpClient extends TTransport
+ {
+ private var socket:Socket = null;
+
+ private var host:String;
+
+ private var port:int;
+
+ private var resource:String;
+
+ private var stripped:Boolean = false;
+
+ private var obuffer:ByteArray = new ByteArray();
+
+ private var input:IDataInput;
+
+ private var output:IDataOutput;
+
+ private var bytesInChunk:int = 0;
+
+ private var CRLF:ByteArray = new ByteArray();
+
+ private var ioCallback:Function = null;
+
+ private var eventDispatcher:EventDispatcher = new EventDispatcher();
+
+ public function TFullDuplexHttpClient(host:String, port:int, resource:String):void
+ {
+ CRLF.writeByte(13);
+ CRLF.writeByte(10);
+ this.host = host;
+ this.port = port;
+ this.resource = resource;
+ }
+
+ public override function close():void
+ {
+ this.input = null;
+ this.output = null;
+ this.stripped = false;
+ socket.close()
+ }
+
+ public override function peek():Boolean
+ {
+ if(socket.connected)
+ {
+ trace("Bytes remained:" + socket.bytesAvailable);
+ return socket.bytesAvailable>0;
+ }
+ return false;
+ }
+
+ public override function read(buf:ByteArray, off:int, len:int):int
+ {
+ var n1:int = 0, n2:int = 0, n3:int = 0, n4:int = 0, cidx:int = 2;
+ var chunkSize:ByteArray = new ByteArray();
+
+ try
+ {
+ while (!stripped)
+ {
+ n1 = n2;
+ n2 = n3;
+ n3 = n4;
+ n4 = input.readByte();
+ if ((n1 == 13) && (n2 == 10) && (n3 == 13) && (n4 == 10))
+ {
+ stripped = true;
+ }
+ }
+
+ // read chunk size
+ if (bytesInChunk == 0)
+ {
+ n1 = input.readByte();
+ n2 = input.readByte();
+
+ chunkSize.writeByte(n1);
+ chunkSize.writeByte(n2);
+
+ while (!((n1 == 13) && (n2 == 10)))
+ {
+ n1 = n2;
+ n2 = input.readByte();
+ chunkSize.writeByte(n2);
+ }
+
+ bytesInChunk = parseInt(chunkSize.toString(), 16);
+ }
+
+ input.readBytes(buf, off, len);
+ debugBuffer(buf);
+ bytesInChunk -= len;
+
+ if (bytesInChunk == 0)
+ {
+ // advance the : "\r\n"
+ input.readUTFBytes(2);
+ }
+ return len;
+ }
+ catch (e:EOFError)
+ {
+ trace(e);
+ throw new TTransportError(TTransportError.UNKNOWN, "No more data available.");
+ }
+ catch (e:Error)
+ {
+ trace(e);
+ // WTF??
+ throw new TTransportError(TTransportError.UNKNOWN, "Bad IO error:" + e);
+ }
+ return 0;
+ }
+
+ public function debugBuffer(buf:ByteArray):void
+ {
+ var debug:String = "BUFFER >>";
+ var i:int;
+ for (i = 0; i < buf.length; i++)
+ {
+ debug += buf[i] as int;
+ debug += " ";
+ }
+
+ trace(debug + "<<");
+ }
+
+ public override function write(buf:ByteArray, off:int, len:int):void
+ {
+ obuffer.writeBytes(buf, off, len);
+ }
+
+ public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
+ {
+ this.eventDispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
+ }
+
+ public override function open():void
+ {
+ this.socket = new Socket();
+ this.socket.addEventListener(Event.CONNECT, socketConnected);
+ this.socket.addEventListener(IOErrorEvent.IO_ERROR, socketError);
+ this.socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, socketSecurityError);
+ this.socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
+ this.socket.connect(host, port);
+ }
+
+ public function socketConnected(event:Event):void
+ {
+ this.output = this.socket;
+ this.input = this.socket;
+ this.output.writeUTF("CONNECT " + resource + " HTTP/1.1\n" + "Host: " + host + ":" + port + "\r\n" + "User-Agent: Thrift/AS3\r\n" + "Transfer-Encoding: chunked\r\n" + "content-type: application/x-thrift\r\n" + "Accept: */*\r\n\r\n");
+ this.eventDispatcher.dispatchEvent(event);
+ }
+
+ public function socketError(event:IOErrorEvent):void
+ {
+ trace("Error Connecting:" + event);
+ this.close();
+ if (ioCallback == null)
+ {
+ return;
+ }
+ ioCallback(new TTransportError(TTransportError.UNKNOWN, "IOError: " + event.text));
+ this.eventDispatcher.dispatchEvent(event);
+ }
+
+ public function socketSecurityError(event:SecurityErrorEvent):void
+ {
+ trace("Security Error Connecting:" + event);
+ this.close();
+ this.eventDispatcher.dispatchEvent(event);
+ }
+
+ public function socketDataHandler(event:ProgressEvent):void
+ {
+ trace("Got Data call:" +ioCallback);
+ if (ioCallback != null)
+ {
+ ioCallback(null);
+ };
+ this.eventDispatcher.dispatchEvent(event);
+ }
+
+ public override function flush(callback:Function = null):void
+ {
+ trace("set callback:" + callback);
+ this.ioCallback = callback;
+ this.output.writeUTF(this.obuffer.length.toString(16));
+ this.output.writeBytes(CRLF);
+ this.output.writeBytes(this.obuffer);
+ this.output.writeBytes(CRLF);
+ this.socket.flush();
+ // waiting for new Flex sdk 3.5
+ //this.obuffer.clear();
+ this.obuffer = new ByteArray();
+ }
+
+ public override function isOpen():Boolean
+ {
+ return (this.socket == null ? false : this.socket.connected);
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/THttpClient.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/THttpClient.as
new file mode 100644
index 000000000..435f91132
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/THttpClient.as
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport {
+
+ import flash.errors.EOFError;
+ import flash.events.Event;
+ import flash.events.IOErrorEvent;
+ import flash.events.SecurityErrorEvent;
+ import flash.net.URLLoader;
+ import flash.net.URLLoaderDataFormat;
+ import flash.net.URLRequest;
+ import flash.net.URLRequestMethod;
+ import flash.system.Capabilities;
+ import flash.utils.ByteArray;
+
+ /**
+ * HTTP implementation of the TTransport interface. Used for working with a
+ * Thrift web services implementation.
+ */
+ public class THttpClient extends TTransport {
+
+ private var request_:URLRequest = null;
+ private var requestBuffer_:ByteArray = new ByteArray();
+ private var responseBuffer_:ByteArray = null;
+ private var traceBuffers_:Boolean = Capabilities.isDebugger;
+
+
+ public function getBuffer():ByteArray {
+ return requestBuffer_;
+ }
+
+ public function THttpClient(request:URLRequest, traceBuffers:Boolean=true):void {
+ request.contentType = "application/x-thrift";
+ request_ = request;
+ if(traceBuffers == false) {
+ traceBuffers_ = traceBuffers;
+ }
+ }
+
+ public override function open():void {
+ }
+
+ public override function close():void {
+ }
+
+ public override function isOpen():Boolean {
+ return true;
+ }
+
+ public override function read(buf:ByteArray, off:int, len:int):int {
+ if (responseBuffer_ == null) {
+ throw new TTransportError(TTransportError.UNKNOWN, "Response buffer is empty, no request.");
+ }
+ try {
+ responseBuffer_.readBytes(buf, off, len);
+ if (traceBuffers_) {
+ dumpBuffer(buf, "READ");
+ }
+ return len;
+ }
+ catch (e:EOFError) {
+ if (traceBuffers_) {
+ dumpBuffer(requestBuffer_, "FAILED-RESPONSE-REQUEST");
+ dumpBuffer(responseBuffer_, "FAILED-RESPONSE");
+ }
+ throw new TTransportError(TTransportError.UNKNOWN, "No more data available.");
+ }
+ return 0;
+ }
+
+ public override function write(buf:ByteArray, off:int, len:int):void {
+ requestBuffer_.writeBytes(buf, off, len);
+ }
+
+ public override function flush(callback:Function=null):void {
+ var loader:URLLoader = new URLLoader();
+ if (callback != null) {
+ loader.addEventListener(Event.COMPLETE, function(event:Event):void {
+ responseBuffer_ = URLLoader(event.target).data;
+ if (traceBuffers_) {
+ dumpBuffer(responseBuffer_, "RESPONSE_BUFFER");
+ }
+ callback(null);
+ responseBuffer_ = null;
+ });
+ loader.addEventListener(IOErrorEvent.IO_ERROR, function(event:IOErrorEvent):void {
+ callback(new TTransportError(TTransportError.UNKNOWN, "IOError: " + event.text));
+ responseBuffer_ = null;
+ });
+ loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function(event:SecurityErrorEvent):void {
+ callback(new TTransportError(TTransportError.UNKNOWN, "SecurityError: " + event.text));
+ responseBuffer_ = null;
+ });
+ }
+ request_.method = URLRequestMethod.POST;
+ loader.dataFormat = URLLoaderDataFormat.BINARY;
+ requestBuffer_.position = 0;
+ request_.data = requestBuffer_;
+ loader.load(request_);
+ }
+
+ private function dumpBuffer(buf:ByteArray, prefix:String):String {
+ var debugString : String = prefix + " BUFFER ";
+ if (buf != null) {
+ debugString += "length: " + buf.length + ", ";
+ for (var i : int = 0; i < buf.length; i++) {
+ debugString += "[" + buf[i].toString(16) + "]";
+ }
+ } else {
+ debugString = "null";
+ }
+ trace(debugString);
+ return debugString;
+ }
+
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/TSocket.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/TSocket.as
new file mode 100644
index 000000000..c60d7115e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/TSocket.as
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport
+{
+
+ import flash.events.EventDispatcher;
+ import flash.events.Event;
+ import flash.events.IOErrorEvent;
+ import flash.events.ProgressEvent;
+ import flash.events.SecurityErrorEvent;
+ import flash.errors.EOFError;
+ import flash.errors.IOError;
+ import flash.net.URLLoader;
+ import flash.net.URLLoaderDataFormat;
+ import flash.net.URLRequest;
+ import flash.net.URLRequestMethod;
+ import flash.utils.IDataInput;
+ import flash.utils.IDataOutput;
+ import flash.utils.ByteArray;
+ import flash.net.Socket;
+
+
+ /**
+ * Socket implementation of the TTransport interface. Used for working with a
+ * Thrift Socket Server based implementations.
+ */
+
+ public class TSocket extends TTransport
+ {
+ private var socket:Socket = null;
+
+ private var host:String;
+
+ private var port:int;
+
+ private var obuffer:ByteArray = new ByteArray();
+
+ private var input:IDataInput;
+
+ private var output:IDataOutput;
+
+ private var ioCallback:Function = null;
+
+ private var eventDispatcher:EventDispatcher = new EventDispatcher();
+
+ public function TSocket(host:String, port:int):void
+ {
+ this.host = host;
+ this.port = port;
+ }
+
+ public override function close():void
+ {
+ this.input = null;
+ this.output = null;
+ socket.close()
+ }
+
+ public override function peek():Boolean
+ {
+ if(socket.connected)
+ {
+ trace("Bytes remained:" + socket.bytesAvailable);
+ return socket.bytesAvailable>0;
+ }
+ return false;
+ }
+
+ public override function read(buf:ByteArray, off:int, len:int):int
+ {
+ var n1:int = 0, n2:int = 0, n3:int = 0, n4:int = 0, cidx:int = 2;
+ var chunkSize:ByteArray = new ByteArray();
+
+ try
+ {
+ input.readBytes(buf, off, len);
+ return len;
+ }
+ catch (e:EOFError)
+ {
+ trace(e);
+ throw new TTransportError(TTransportError.END_OF_FILE, "No more data available.");
+ }
+ catch (e:IOError)
+ {
+ trace(e);
+ if(isOpen())
+ {
+ throw new TTransportError(TTransportError.UNKNOWN, "IO error while reading: " + e);
+ }
+ else
+ {
+ throw new TTransportError(TTransportError.NOT_OPEN, "Socket seem not to be opened: " + e);
+ }
+ }
+ catch (e:Error)
+ {
+ trace(e);
+ throw new TTransportError(TTransportError.UNKNOWN, "Bad IO error: " + e);
+ }
+ return 0;
+ }
+
+ public override function write(buf:ByteArray, off:int, len:int):void
+ {
+ obuffer.writeBytes(buf, off, len);
+ }
+
+ public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
+ {
+ this.eventDispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
+ }
+
+ public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
+ {
+ this.eventDispatcher.removeEventListener(type, listener, useCapture);
+ }
+
+ public override function open():void
+ {
+ this.socket = new Socket();
+ this.socket.addEventListener(Event.CONNECT, socketConnected);
+ this.socket.addEventListener(IOErrorEvent.IO_ERROR, socketError);
+ this.socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, socketSecurityError);
+ this.socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
+ this.socket.connect(host, port);
+ }
+
+ public function socketConnected(event:Event):void
+ {
+ this.output = this.socket;
+ this.input = this.socket;
+ this.eventDispatcher.dispatchEvent(event);
+ }
+
+ public function socketError(event:IOErrorEvent):void
+ {
+ trace("Error Connecting:" + event);
+ this.close();
+ if (ioCallback == null)
+ {
+ return;
+ }
+ ioCallback(new TTransportError(TTransportError.UNKNOWN, "IOError: " + event.text));
+ this.eventDispatcher.dispatchEvent(event);
+ }
+
+ public function socketSecurityError(event:SecurityErrorEvent):void
+ {
+ trace("Security Error Connecting:" + event);
+ this.close();
+ this.eventDispatcher.dispatchEvent(event);
+ }
+
+ public function socketDataHandler(event:ProgressEvent):void
+ {
+ if (ioCallback != null)
+ {
+ ioCallback(null);
+ }
+ this.eventDispatcher.dispatchEvent(event);
+ }
+
+ public override function flush(callback:Function = null):void
+ {
+ this.ioCallback = callback;
+ this.output.writeBytes(this.obuffer);
+ this.socket.flush();
+ this.obuffer.clear();
+ }
+
+ public override function isOpen():Boolean
+ {
+ return (this.socket == null ? false : this.socket.connected);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/TTransport.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/TTransport.as
new file mode 100644
index 000000000..83160af69
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/TTransport.as
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport {
+
+ import flash.utils.ByteArray;
+ import org.apache.thrift.AbstractMethodError;
+
+ public class TTransport {
+
+ /**
+ * Queries whether the transport is open.
+ *
+ * @return True if the transport is open.
+ */
+ public function isOpen():Boolean {
+ throw new AbstractMethodError();
+ }
+
+ /**
+ * Is there more data to be read?
+ *
+ * @return True if the remote side is still alive and feeding us
+ */
+ public function peek():Boolean {
+ return isOpen();
+ }
+
+ /**
+ * Opens the transport for reading/writing.
+ *
+ * @throws TTransportException if the transport could not be opened
+ */
+ public function open():void {
+ throw new AbstractMethodError();
+ }
+
+ /**
+ * Closes the transport.
+ */
+ public function close():void {
+ throw new AbstractMethodError();
+ };
+
+ /**
+ * Reads up to len bytes into buffer buf, starting att offset off.
+ *
+ * @param buf Array to read into
+ * @param off Index to start reading at
+ * @param len Maximum number of bytes to read
+ * @return The number of bytes actually read
+ * @throws TTransportException if there was an error reading data
+ */
+ public function read(buf:ByteArray, off:int, len:int):int {
+ throw new AbstractMethodError();
+ }
+
+ /**
+ * Guarantees that all of len bytes are actually read off the transport.
+ *
+ * @param buf Array to read into
+ * @param off Index to start reading at
+ * @param len Maximum number of bytes to read
+ * @return The number of bytes actually read, which must be equal to len
+ * @throws TTransportException if there was an error reading data
+ */
+ public function readAll(buf:ByteArray, off:int, len:int):int {
+ var got:int = 0;
+ var ret:int = 0;
+ while (got < len) {
+ ret = read(buf, off+got, len-got);
+ if (ret <= 0) {
+ throw new TTransportError(TTransportError.UNKNOWN, "Cannot read. Remote side has closed. Tried to read " + len + " bytes, but only got " + got + " bytes.");
+ }
+ got += ret;
+ }
+ return got;
+ }
+
+ /**
+ * Writes the buffer to the output
+ *
+ * @param buf The output data buffer
+ * @throws TTransportException if an error occurs writing data
+ */
+ public function writeAll(buf:ByteArray):void {
+ write(buf, 0, buf.length);
+ }
+
+ /**
+ * Writes up to len bytes from the buffer.
+ *
+ * @param buf The output data buffer
+ * @param off The offset to start writing from
+ * @param len The number of bytes to write
+ * @throws TTransportException if there was an error writing data
+ */
+ public function write(buf:ByteArray, off:int, len:int):void {
+ throw new AbstractMethodError();
+ }
+
+ /**
+ * Flush any pending data out of a transport buffer.
+ *
+ * @throws TTransportException if there was an error writing out data.
+ */
+ public function flush(callback:Function=null):void {
+ throw new AbstractMethodError();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/TTransportError.as b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/TTransportError.as
new file mode 100644
index 000000000..10a6f621b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/as3/src/org/apache/thrift/transport/TTransportError.as
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport {
+
+ import org.apache.thrift.TError;
+
+ public class TTransportError extends TError {
+
+ public static const UNKNOWN:int = 0;
+ public static const NOT_OPEN:int = 1;
+ public static const ALREADY_OPEN:int = 2;
+ public static const TIMED_OUT:int = 3;
+ public static const END_OF_FILE:int = 4;
+
+ public function TTransportError(error:int = UNKNOWN, message:String = "") {
+ super(message, error);
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/c_glib/CMakeLists.txt b/src/jaegertracing/thrift/lib/c_glib/CMakeLists.txt
new file mode 100644
index 000000000..3e4a154b8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/CMakeLists.txt
@@ -0,0 +1,87 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Find required packages
+find_package(GLIB REQUIRED COMPONENTS gobject)
+include_directories(${GLIB_INCLUDE_DIRS})
+
+include_directories(src)
+
+# SYSLIBS contains libraries that need to be linked to all lib targets
+set(SYSLIBS ${GLIB_LIBRARIES} ${GLIB_GOBJECT_LIBRARIES})
+
+# Create the thrift C glib library
+set(thrift_c_glib_SOURCES
+ src/thrift/c_glib/thrift.c
+ src/thrift/c_glib/thrift_struct.c
+ src/thrift/c_glib/thrift_application_exception.c
+ src/thrift/c_glib/processor/thrift_processor.c
+ src/thrift/c_glib/processor/thrift_dispatch_processor.c
+ src/thrift/c_glib/processor/thrift_multiplexed_processor.c
+ src/thrift/c_glib/protocol/thrift_protocol.c
+ src/thrift/c_glib/protocol/thrift_protocol_factory.c
+ src/thrift/c_glib/protocol/thrift_protocol_decorator.c
+ src/thrift/c_glib/protocol/thrift_binary_protocol.c
+ src/thrift/c_glib/protocol/thrift_stored_message_protocol.c
+ src/thrift/c_glib/protocol/thrift_multiplexed_protocol.c
+ src/thrift/c_glib/protocol/thrift_binary_protocol_factory.c
+ src/thrift/c_glib/protocol/thrift_compact_protocol.c
+ src/thrift/c_glib/protocol/thrift_compact_protocol_factory.c
+ src/thrift/c_glib/transport/thrift_transport.c
+ src/thrift/c_glib/transport/thrift_transport_factory.c
+ src/thrift/c_glib/transport/thrift_buffered_transport_factory.c
+ src/thrift/c_glib/transport/thrift_framed_transport_factory.c
+ src/thrift/c_glib/transport/thrift_socket.c
+ src/thrift/c_glib/transport/thrift_server_transport.c
+ src/thrift/c_glib/transport/thrift_server_socket.c
+ src/thrift/c_glib/transport/thrift_buffered_transport.c
+ src/thrift/c_glib/transport/thrift_fd_transport.c
+ src/thrift/c_glib/transport/thrift_framed_transport.c
+ src/thrift/c_glib/transport/thrift_memory_buffer.c
+ src/thrift/c_glib/server/thrift_server.c
+ src/thrift/c_glib/server/thrift_simple_server.c
+)
+
+# If OpenSSL is not found just ignore the OpenSSL stuff
+find_package(OpenSSL)
+if(OPENSSL_FOUND AND WITH_OPENSSL)
+ list( APPEND thrift_c_glib_SOURCES
+ src/thrift/c_glib/transport/thrift_ssl_socket.c
+ )
+ include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}")
+ list(APPEND SYSLIBS "${OPENSSL_LIBRARIES}")
+endif()
+
+
+# Contains the thrift specific ADD_LIBRARY_THRIFT and TARGET_LINK_LIBRARIES_THRIFT
+include(ThriftMacros)
+
+ADD_LIBRARY_THRIFT(thrift_c_glib ${thrift_c_glib_SOURCES})
+TARGET_LINK_LIBRARIES_THRIFT(thrift_c_glib ${SYSLIBS})
+
+# Install the headers
+install(DIRECTORY "src/thrift" DESTINATION "${INCLUDE_INSTALL_DIR}"
+ FILES_MATCHING PATTERN "*.h")
+# Copy config.h file
+install(DIRECTORY "${CMAKE_BINARY_DIR}/thrift" DESTINATION "${INCLUDE_INSTALL_DIR}"
+ FILES_MATCHING PATTERN "*.h")
+
+if(BUILD_TESTING)
+ add_subdirectory(test)
+endif()
diff --git a/src/jaegertracing/thrift/lib/c_glib/Makefile.am b/src/jaegertracing/thrift/lib/c_glib/Makefile.am
new file mode 100755
index 000000000..fd7f93d76
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/Makefile.am
@@ -0,0 +1,118 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+AUTOMAKE_OPTIONS = serial-tests nostdinc
+SUBDIRS = . test
+
+pkgconfigdir = $(libdir)/pkgconfig
+
+lib_LTLIBRARIES = libthrift_c_glib.la
+pkgconfig_DATA = thrift_c_glib.pc
+
+AM_CPPFLAGS = -Isrc -I src/thrift/c_glib
+AM_CFLAGS = -Wall -Wextra -pedantic
+
+# Define the source files for the module
+
+libthrift_c_glib_la_SOURCES = src/thrift/c_glib/thrift.c \
+ src/thrift/c_glib/thrift_struct.c \
+ src/thrift/c_glib/thrift_application_exception.c \
+ src/thrift/c_glib/processor/thrift_processor.c \
+ src/thrift/c_glib/processor/thrift_dispatch_processor.c \
+ src/thrift/c_glib/processor/thrift_multiplexed_processor.c \
+ src/thrift/c_glib/protocol/thrift_protocol.c \
+ src/thrift/c_glib/protocol/thrift_protocol_decorator.c \
+ src/thrift/c_glib/protocol/thrift_protocol_factory.c \
+ src/thrift/c_glib/protocol/thrift_binary_protocol.c \
+ src/thrift/c_glib/protocol/thrift_stored_message_protocol.c \
+ src/thrift/c_glib/protocol/thrift_multiplexed_protocol.c \
+ src/thrift/c_glib/protocol/thrift_binary_protocol_factory.c \
+ src/thrift/c_glib/protocol/thrift_compact_protocol.c \
+ src/thrift/c_glib/protocol/thrift_compact_protocol_factory.c \
+ src/thrift/c_glib/transport/thrift_transport.c \
+ src/thrift/c_glib/transport/thrift_transport_factory.c \
+ src/thrift/c_glib/transport/thrift_buffered_transport_factory.c \
+ src/thrift/c_glib/transport/thrift_framed_transport_factory.c \
+ src/thrift/c_glib/transport/thrift_socket.c \
+ src/thrift/c_glib/transport/thrift_ssl_socket.c \
+ src/thrift/c_glib/transport/thrift_server_transport.c \
+ src/thrift/c_glib/transport/thrift_server_socket.c \
+ src/thrift/c_glib/transport/thrift_buffered_transport.c \
+ src/thrift/c_glib/transport/thrift_fd_transport.c \
+ src/thrift/c_glib/transport/thrift_framed_transport.c \
+ src/thrift/c_glib/transport/thrift_memory_buffer.c \
+ src/thrift/c_glib/server/thrift_server.c \
+ src/thrift/c_glib/server/thrift_simple_server.c
+
+libthrift_c_glib_la_CFLAGS = $(AM_CFLAGS) $(GLIB_CFLAGS) $(GOBJECT_CFLAGS) $(OPENSSL_INCLUDES) -I$(top_builddir)/lib/c_glib/src/thrift
+libthrift_c_glib_la_LDFLAGS = $(AM_LDFLAGS) $(GLIB_LIBS) $(GOBJECT_LIBS) $(OPENSSL_LDFLAGS) $(OPENSSL_LIBS)
+
+include_thriftdir = $(includedir)/thrift/c_glib
+include_thrift_HEADERS = \
+ $(top_builddir)/config.h \
+ src/thrift/c_glib/thrift.h \
+ src/thrift/c_glib/thrift_application_exception.h \
+ src/thrift/c_glib/thrift_struct.h
+
+include_protocoldir = $(include_thriftdir)/protocol
+include_protocol_HEADERS = src/thrift/c_glib/protocol/thrift_protocol.h \
+ src/thrift/c_glib/protocol/thrift_protocol_decorator.h \
+ src/thrift/c_glib/protocol/thrift_protocol_factory.h \
+ src/thrift/c_glib/protocol/thrift_binary_protocol.h \
+ src/thrift/c_glib/protocol/thrift_binary_protocol_factory.h \
+ src/thrift/c_glib/protocol/thrift_compact_protocol.h \
+ src/thrift/c_glib/protocol/thrift_compact_protocol_factory.h \
+ src/thrift/c_glib/protocol/thrift_multiplexed_protocol.h \
+ src/thrift/c_glib/protocol/thrift_stored_message_protocol.h
+
+
+include_transportdir = $(include_thriftdir)/transport
+include_transport_HEADERS = src/thrift/c_glib/transport/thrift_buffered_transport.h \
+ src/thrift/c_glib/transport/thrift_fd_transport.h \
+ src/thrift/c_glib/transport/thrift_framed_transport.h \
+ src/thrift/c_glib/transport/thrift_memory_buffer.h \
+ src/thrift/c_glib/transport/thrift_server_socket.h \
+ src/thrift/c_glib/transport/thrift_server_transport.h \
+ src/thrift/c_glib/transport/thrift_socket.h \
+ src/thrift/c_glib/transport/thrift_platform_socket.h \
+ src/thrift/c_glib/transport/thrift_ssl_socket.h \
+ src/thrift/c_glib/transport/thrift_transport.h \
+ src/thrift/c_glib/transport/thrift_transport_factory.h \
+ src/thrift/c_glib/transport/thrift_buffered_transport_factory.h \
+ src/thrift/c_glib/transport/thrift_framed_transport_factory.h
+
+include_serverdir = $(include_thriftdir)/server
+include_server_HEADERS = src/thrift/c_glib/server/thrift_server.h \
+ src/thrift/c_glib/server/thrift_simple_server.h
+
+include_processordir = $(include_thriftdir)/processor
+include_processor_HEADERS = src/thrift/c_glib/processor/thrift_processor.h \
+ src/thrift/c_glib/processor/thrift_dispatch_processor.h \
+ src/thrift/c_glib/processor/thrift_multiplexed_processor.h
+
+
+EXTRA_DIST = \
+ CMakeLists.txt \
+ coding_standards.md \
+ README.md \
+ test/glib.suppress \
+ thrift_c_glib.pc.in
+
+CLEANFILES = \
+ *.gcno \
+ *.gcda
diff --git a/src/jaegertracing/thrift/lib/c_glib/README.md b/src/jaegertracing/thrift/lib/c_glib/README.md
new file mode 100644
index 000000000..dd84f3d35
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/README.md
@@ -0,0 +1,49 @@
+Thrift C Software Library
+
+License
+=======
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+Using Thrift with C
+===================
+
+The Thrift C libraries are built using the GNU tools. Follow the instructions
+in the top-level README in order to generate the Makefiles.
+
+Dependencies
+============
+
+GLib
+http://www.gtk.org/
+
+Breaking Changes
+================
+
+0.12.0
+------
+
+The compiler's handling of namespaces when generating the name of types,
+functions and header files has been improved. This means code written to use
+classes generated by previous versions of the compiler may need to be updated to
+reflect the proper convention for class names, which is
+
+- A lowercase, [snake-case](https://en.wikipedia.org/wiki/Snake_case)
+ representation of the class' namespace, followed by
+- An underscore and
+- A lowercase, snake-case representation of the class' name.
diff --git a/src/jaegertracing/thrift/lib/c_glib/coding_standards.md b/src/jaegertracing/thrift/lib/c_glib/coding_standards.md
new file mode 100644
index 000000000..24b3a0056
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/coding_standards.md
@@ -0,0 +1,5 @@
+## C Glib Coding Standards
+
+Please follow:
+ * [Thrift General Coding Standards](/doc/coding_standards.md)
+ * [GNOME C Coding Style](https://help.gnome.org/users/programming-guidelines/stable/c-coding-style.html.en)
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_dispatch_processor.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_dispatch_processor.c
new file mode 100644
index 000000000..57d621751
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_dispatch_processor.c
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/thrift_application_exception.h>
+#include <thrift/c_glib/processor/thrift_dispatch_processor.h>
+
+G_DEFINE_ABSTRACT_TYPE (ThriftDispatchProcessor,
+ thrift_dispatch_processor,
+ THRIFT_TYPE_PROCESSOR)
+
+gboolean
+thrift_dispatch_processor_process (ThriftProcessor *processor,
+ ThriftProtocol *in,
+ ThriftProtocol *out,
+ GError **error)
+{
+ gchar *fname;
+ ThriftMessageType mtype;
+ gint32 seqid;
+ ThriftDispatchProcessor *dispatch_processor =
+ THRIFT_DISPATCH_PROCESSOR (processor);
+
+ /* Read the start of the message, which we expect to be a method call */
+ if (thrift_protocol_read_message_begin (in,
+ &fname,
+ &mtype,
+ &seqid,
+ error) < 0) {
+ g_warning ("error reading start of message: %s",
+ (error != NULL) ? (*error)->message : "(null)");
+ return FALSE;
+ }
+ else if (mtype != T_CALL && mtype != T_ONEWAY) {
+ g_warning ("received invalid message type %d from client", mtype);
+ return FALSE;
+ }
+
+ /* Dispatch the method call */
+ return THRIFT_DISPATCH_PROCESSOR_GET_CLASS (dispatch_processor)
+ ->dispatch_call (dispatch_processor,
+ in,
+ out,
+ fname,
+ seqid,
+ error);
+}
+
+static gboolean
+thrift_dispatch_processor_real_dispatch_call (ThriftDispatchProcessor *self,
+ ThriftProtocol *in,
+ ThriftProtocol *out,
+ gchar *fname,
+ gint32 seqid,
+ GError **error)
+{
+ ThriftTransport *transport;
+ ThriftApplicationException *xception;
+ gchar *message;
+ gint32 result;
+ gboolean dispatch_result = FALSE;
+
+ THRIFT_UNUSED_VAR (self);
+
+ /* By default, return an application exception to the client indicating the
+ method name is not recognized. */
+
+ if ((thrift_protocol_skip (in, T_STRUCT, error) < 0) ||
+ (thrift_protocol_read_message_end (in, error) < 0))
+ return FALSE;
+
+ g_object_get (in, "transport", &transport, NULL);
+ result = thrift_transport_read_end (transport, error);
+ g_object_unref (transport);
+ if (result < 0)
+ return FALSE;
+
+ if (thrift_protocol_write_message_begin (out,
+ fname,
+ T_EXCEPTION,
+ seqid,
+ error) < 0)
+ return FALSE;
+ message = g_strconcat ("Invalid method name: '", fname, "'", NULL);
+ g_free (fname);
+ xception =
+ g_object_new (THRIFT_TYPE_APPLICATION_EXCEPTION,
+ "type", THRIFT_APPLICATION_EXCEPTION_ERROR_UNKNOWN_METHOD,
+ "message", message,
+ NULL);
+ g_free (message);
+ result = thrift_struct_write (THRIFT_STRUCT (xception),
+ out,
+ error);
+ g_object_unref (xception);
+ if ((result < 0) ||
+ (thrift_protocol_write_message_end (out, error) < 0))
+ return FALSE;
+
+ g_object_get (out, "transport", &transport, NULL);
+ dispatch_result =
+ ((thrift_transport_write_end (transport, error) >= 0) &&
+ (thrift_transport_flush (transport, error) >= 0));
+ g_object_unref (transport);
+
+ return dispatch_result;
+}
+
+static void
+thrift_dispatch_processor_init (ThriftDispatchProcessor *self)
+{
+ THRIFT_UNUSED_VAR (self);
+}
+
+static void
+thrift_dispatch_processor_class_init (ThriftDispatchProcessorClass *klass)
+{
+ ThriftProcessorClass *processor_class =
+ THRIFT_PROCESSOR_CLASS (klass);
+
+ /* Implement ThriftProcessor's process method */
+ processor_class->process = thrift_dispatch_processor_process;
+
+ /* Provide a default implement for dispatch_call, which returns an exception
+ to the client indicating the method name was not recognized */
+ klass->dispatch_call = thrift_dispatch_processor_real_dispatch_call;
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_dispatch_processor.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_dispatch_processor.h
new file mode 100644
index 000000000..5afb85ec8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_dispatch_processor.h
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_DISPATCH_PROCESSOR_H
+#define _THRIFT_DISPATCH_PROCESSOR_H
+
+#include <glib-object.h>
+
+#include <thrift/c_glib/processor/thrift_processor.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_dispatch_processor.h
+ * \brief Parses a method-call message header and invokes a function
+ * to dispatch the call by function name.
+ *
+ * ThriftDispatchProcessor is an abstract helper class that parses the
+ * header of a method-call message and invokes a member function,
+ * dispatch_call, with the method's name.
+ *
+ * Subclasses must implement dispatch_call to dispatch the method call
+ * to the implementing function.
+ */
+
+/* Type macros */
+#define THRIFT_TYPE_DISPATCH_PROCESSOR (thrift_dispatch_processor_get_type ())
+#define THRIFT_DISPATCH_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_DISPATCH_PROCESSOR, ThriftDispatchProcessor))
+#define THRIFT_IS_DISPATCH_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_DISPATCH_PROCESSOR))
+#define THRIFT_DISPATCH_PROCESSOR_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_DISPATCH_PROCESSOR, ThriftDispatchProcessorClass))
+#define THRIFT_IS_DISPATCH_PROCESSOR_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_DISPATCH_PROCESSOR))
+#define THRIFT_DISPATCH_PROCESSOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_DISPATCH_PROCESSOR, ThriftDispatchProcessorClass))
+
+/*!
+ * Thrift Dispatch Processor object
+ */
+struct _ThriftDispatchProcessor
+{
+ ThriftProcessor parent;
+};
+typedef struct _ThriftDispatchProcessor ThriftDispatchProcessor;
+
+/*!
+ * Thrift Dispatch Processor class
+ */
+struct _ThriftDispatchProcessorClass
+{
+ ThriftProcessorClass parent;
+
+ /* public */
+ gboolean (*process) (ThriftProcessor *processor,
+ ThriftProtocol *in,
+ ThriftProtocol *out,
+ GError **error);
+
+ /* protected */
+ gboolean (*dispatch_call) (ThriftDispatchProcessor *self,
+ ThriftProtocol *in,
+ ThriftProtocol *out,
+ gchar *fname,
+ gint32 seqid,
+ GError **error);
+};
+typedef struct _ThriftDispatchProcessorClass ThriftDispatchProcessorClass;
+
+/* Used by THRIFT_TYPE_DISPATCH_PROCESSOR */
+GType thrift_dispatch_processor_get_type (void);
+
+/*!
+ * Processes a request.
+ * \public \memberof ThriftDispatchProcessorClass
+ */
+gboolean thrift_dispatch_processor_process (ThriftProcessor *processor,
+ ThriftProtocol *in,
+ ThriftProtocol *out,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* _THRIFT_DISPATCH_PROCESSOR_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_multiplexed_processor.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_multiplexed_processor.c
new file mode 100644
index 000000000..68a0f4d46
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_multiplexed_processor.c
@@ -0,0 +1,346 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include <string.h>
+#include <stdio.h>
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/processor/thrift_processor.h>
+#include <thrift/c_glib/processor/thrift_multiplexed_processor.h>
+#include <thrift/c_glib/protocol/thrift_multiplexed_protocol.h>
+#include <thrift/c_glib/protocol/thrift_stored_message_protocol.h>
+#include <thrift/c_glib/thrift_application_exception.h>
+
+G_DEFINE_TYPE(ThriftMultiplexedProcessor, thrift_multiplexed_processor, THRIFT_TYPE_PROCESSOR)
+
+
+enum
+{
+ PROP_THRIFT_MULTIPLEXED_PROCESSOR_DEFAULT_SERVICE_NAME = 1,
+ PROP_THRIFT_MULTIPLEXED_PROCESSOR_END
+};
+
+static GParamSpec *thrift_multiplexed_processor_obj_properties[PROP_THRIFT_MULTIPLEXED_PROCESSOR_END] = { NULL, };
+
+
+static gboolean
+thrift_multiplexed_processor_register_processor_impl(ThriftProcessor *processor, const gchar * multiplexed_processor_name, ThriftProcessor * multiplexed_processor , GError **error)
+{
+ ThriftMultiplexedProcessor *self = THRIFT_MULTIPLEXED_PROCESSOR(processor);
+ g_hash_table_replace(self->multiplexed_services,
+ g_strdup(multiplexed_processor_name),
+ g_object_ref (multiplexed_processor));
+
+ /* Make first registered become default */
+ if(!self->default_processor_name){
+ self->default_processor_name = g_strdup(multiplexed_processor_name);
+ }
+ return TRUE;
+}
+
+
+static gboolean
+thrift_multiplexed_processor_process_impl (ThriftProcessor *processor, ThriftProtocol *in,
+ ThriftProtocol *out, GError **error)
+{
+ gboolean retval = FALSE;
+ gboolean token_error = FALSE;
+ ThriftApplicationException *xception;
+ ThriftStoredMessageProtocol *stored_message_protocol = NULL;
+ ThriftMessageType message_type;
+ ThriftMultiplexedProcessor *self = THRIFT_MULTIPLEXED_PROCESSOR(processor);
+ ThriftProcessor *multiplexed_processor = NULL;
+ ThriftTransport *transport;
+ char *token=NULL;
+ int token_index=0;
+ char *state=NULL;
+ gchar *fname=NULL;
+ gint32 seqid, result;
+
+ /* FIXME It seems that previous processor is not managing error correctly */
+ if(*error!=NULL) {
+ g_debug ("thrift_multiplexed_processor: last error not removed: %s",
+ *error != NULL ? (*error)->message : "(null)");
+ g_clear_error (error);
+ }
+
+
+ THRIFT_PROTOCOL_GET_CLASS(in)->read_message_begin(in, &fname, &message_type, &seqid, error);
+
+ if(!(message_type == T_CALL || message_type == T_ONEWAY)) {
+ g_set_error (error,
+ THRIFT_MULTIPLEXED_PROCESSOR_ERROR,
+ THRIFT_MULTIPLEXED_PROCESSOR_ERROR_MESSAGE_TYPE,
+ "message type invalid for this processor");
+ }else{
+ /* Split by the token */
+ for (token = strtok_r(fname, THRIFT_MULTIPLEXED_PROTOCOL_DEFAULT_SEPARATOR, &state),
+ token_index=0;
+ token != NULL && !token_error;
+ token = strtok_r(NULL, THRIFT_MULTIPLEXED_PROTOCOL_DEFAULT_SEPARATOR, &state),
+ token_index++)
+ {
+ switch(token_index){
+ case 0:
+ /* It should be the service name */
+ multiplexed_processor = g_hash_table_lookup(self->multiplexed_services, token);
+ if(multiplexed_processor==NULL){
+ token_error=TRUE;
+ }
+ break;
+ case 1:
+ /* It should be the function name */
+ stored_message_protocol = g_object_new (THRIFT_TYPE_STORED_MESSAGE_PROTOCOL,
+ "protocol", in,
+ "name", token,
+ "type", message_type,
+ "seqid", seqid,
+ NULL);
+ break;
+ default:
+ g_set_error (error,
+ THRIFT_MULTIPLEXED_PROCESSOR_ERROR,
+ THRIFT_MULTIPLEXED_PROCESSOR_ERROR_MESSAGE_WRONGLY_MULTIPLEXED,
+ "the message has more tokens than expected!");
+ token_error=TRUE;
+ break;
+ }
+ }
+ /* Set default */
+ if(!stored_message_protocol &&
+ !multiplexed_processor &&
+ token_index==1 && self->default_processor_name){
+ /* It should be the service name */
+ multiplexed_processor = g_hash_table_lookup(self->multiplexed_services, self->default_processor_name);
+ if(multiplexed_processor==NULL){
+ g_set_error (error,
+ THRIFT_MULTIPLEXED_PROCESSOR_ERROR,
+ THRIFT_MULTIPLEXED_PROCESSOR_ERROR_SERVICE_UNAVAILABLE,
+ "service %s not available on this processor",
+ self->default_processor_name);
+ }else{
+ /* Set the message name to the original name */
+ stored_message_protocol = g_object_new (THRIFT_TYPE_STORED_MESSAGE_PROTOCOL,
+ "protocol", in,
+ "name", fname,
+ "type", message_type,
+ "seqid", seqid,
+ NULL);
+ }
+
+ }
+
+ if(stored_message_protocol!=NULL && multiplexed_processor!=NULL){
+ retval = THRIFT_PROCESSOR_GET_CLASS (multiplexed_processor)->process (multiplexed_processor, (ThriftProtocol *) stored_message_protocol, out, error) ;
+ }else{
+ if(!error)
+ g_set_error (error,
+ THRIFT_MULTIPLEXED_PROCESSOR_ERROR,
+ THRIFT_MULTIPLEXED_PROCESSOR_ERROR_SERVICE_UNAVAILABLE,
+ "service %s is not multiplexed in this processor",
+ fname);
+ }
+
+
+ }
+
+ if(!retval){
+ /* By default, return an application exception to the client indicating the
+ method name is not recognized. */
+ /* Copied from dispach processor */
+
+ if ((thrift_protocol_skip (in, T_STRUCT, error) < 0) ||
+ (thrift_protocol_read_message_end (in, error) < 0))
+ return retval;
+
+ g_object_get (in, "transport", &transport, NULL);
+ result = thrift_transport_read_end (transport, error);
+ g_object_unref (transport);
+ if (result < 0) {
+ /* We must free fname */
+ g_free(fname);
+ return retval;
+ }
+
+ if (thrift_protocol_write_message_begin (out,
+ fname,
+ T_EXCEPTION,
+ seqid,
+ error) < 0){
+ /* We must free fname */
+ g_free(fname);
+
+ return retval;
+ }
+
+
+ xception =
+ g_object_new (THRIFT_TYPE_APPLICATION_EXCEPTION,
+ "type", THRIFT_APPLICATION_EXCEPTION_ERROR_UNKNOWN_METHOD,
+ "message", (*error)->message,
+ NULL);
+ result = thrift_struct_write (THRIFT_STRUCT (xception),
+ out,
+ error);
+ g_object_unref (xception);
+ if ((result < 0) ||
+ (thrift_protocol_write_message_end (out, error) < 0))
+ return retval;
+
+ g_object_get (out, "transport", &transport, NULL);
+ retval =
+ ((thrift_transport_write_end (transport, error) >= 0) &&
+ (thrift_transport_flush (transport, error) >= 0));
+ g_object_unref (transport);
+ }else{
+ /* The protocol now has a copy we can free it */
+ g_free(fname);
+
+ }
+
+ /*
+ FIXME This makes everything fail, I don't know why.
+ if(stored_message_protocol!=NULL){
+ // After its use we must free it
+ g_object_unref(stored_message_protocol);
+ }
+ */
+ return retval;
+}
+
+/* define the GError domain for Thrift transports */
+GQuark
+thrift_multiplexed_processor_error_quark (void)
+{
+ return g_quark_from_static_string (THRIFT_MULTIPLEXED_PROCESSOR_ERROR_DOMAIN);
+}
+
+
+static void
+thrift_multiplexed_processor_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ThriftMultiplexedProcessor *self = THRIFT_MULTIPLEXED_PROCESSOR (object);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_MULTIPLEXED_PROCESSOR_DEFAULT_SERVICE_NAME:
+ self->default_processor_name = g_value_dup_string (value);
+ break;
+
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+thrift_multiplexed_processor_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ThriftMultiplexedProcessor *self = THRIFT_MULTIPLEXED_PROCESSOR (object);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_MULTIPLEXED_PROCESSOR_DEFAULT_SERVICE_NAME:
+ g_value_set_string (value, self->default_processor_name);
+ break;
+
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/* destructor */
+static void
+thrift_multiplexed_processor_finalize (GObject *object)
+{
+ ThriftMultiplexedProcessor *self = THRIFT_MULTIPLEXED_PROCESSOR(object);
+
+ /* Free our multiplexed hash table */
+ g_hash_table_unref (self->multiplexed_services);
+ self->multiplexed_services = NULL;
+
+ if(self->default_processor_name){
+ g_free(self->default_processor_name);
+ self->default_processor_name=NULL;
+ }
+
+ /* Chain up to parent */
+ if (G_OBJECT_CLASS (thrift_multiplexed_processor_parent_class)->finalize)
+ (*G_OBJECT_CLASS (thrift_multiplexed_processor_parent_class)->finalize) (object);
+}
+
+/* class initializer for ThriftMultiplexedProcessor */
+static void
+thrift_multiplexed_processor_class_init (ThriftMultiplexedProcessorClass *cls)
+{
+ /* Override */
+ THRIFT_PROCESSOR_CLASS(cls)->process = thrift_multiplexed_processor_process_impl;
+ GObjectClass *gobject_class = G_OBJECT_CLASS (cls);
+
+ /* Object methods */
+ gobject_class->set_property = thrift_multiplexed_processor_set_property;
+ gobject_class->get_property = thrift_multiplexed_processor_get_property;
+ gobject_class->finalize = thrift_multiplexed_processor_finalize;
+
+ /* Class methods */
+ cls->register_processor = thrift_multiplexed_processor_register_processor_impl;
+
+
+ thrift_multiplexed_processor_obj_properties[PROP_THRIFT_MULTIPLEXED_PROCESSOR_DEFAULT_SERVICE_NAME] =
+ g_param_spec_string ("default",
+ "Default service name the protocol points to where no multiplexed client used",
+ "Set the default service name",
+ NULL,
+ (G_PARAM_READWRITE));
+
+ g_object_class_install_properties (gobject_class,
+ PROP_THRIFT_MULTIPLEXED_PROCESSOR_END,
+ thrift_multiplexed_processor_obj_properties);
+
+}
+
+static void
+thrift_multiplexed_processor_init (ThriftMultiplexedProcessor *self)
+{
+
+ /* Create our multiplexed services hash table */
+ self->multiplexed_services = g_hash_table_new_full (
+ g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
+ self->default_processor_name = NULL;
+}
+
+
+gboolean
+thrift_multiplexed_processor_register_processor(ThriftProcessor *processor, const gchar * multiplexed_processor_name, ThriftProcessor * multiplexed_processor , GError **error)
+{
+ return THRIFT_MULTIPLEXED_PROCESSOR_GET_CLASS(processor)->register_processor(processor, multiplexed_processor_name, multiplexed_processor, error);
+}
+
+
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_multiplexed_processor.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_multiplexed_processor.h
new file mode 100644
index 000000000..6406616c2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_multiplexed_processor.h
@@ -0,0 +1,114 @@
+/*
+ * thrift_multiplexed_processor.h
+ *
+ * Created on: 14 sept. 2017
+ * Author: gaguilar
+ */
+
+#ifndef _THRIFT_MULTIPLEXED_MULTIPLEXED_PROCESSOR_H_
+#define _THRIFT_MULTIPLEXED_MULTIPLEXED_PROCESSOR_H_
+
+
+#include <glib-object.h>
+
+#include <thrift/c_glib/processor/thrift_processor.h>
+
+
+G_BEGIN_DECLS
+
+/*! \file thrift_multiplexed_processor.h
+ * \brief The multiplexed processor for c_glib.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_MULTIPLEXED_PROCESSOR (thrift_multiplexed_processor_get_type ())
+#define THRIFT_MULTIPLEXED_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_MULTIPLEXED_PROCESSOR, ThriftMultiplexedProcessor))
+#define THRIFT_IS_MULTIPLEXED_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_MULTIPLEXED_PROCESSOR))
+#define THRIFT_MULTIPLEXED_PROCESSOR_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_MULTIPLEXED_PROCESSOR, ThriftMultiplexedProcessorClass))
+#define THRIFT_IS_MULTIPLEXED_PROCESSOR_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_MULTIPLEXED_PROCESSOR))
+#define THRIFT_MULTIPLEXED_PROCESSOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_MULTIPLEXED_PROCESSOR, ThriftMultiplexedProcessorClass))
+
+/* define the GError domain string */
+#define THRIFT_MULTIPLEXED_PROCESSOR_ERROR_DOMAIN "thrift-multiplexed-processor-error-quark"
+
+
+/*!
+ * Thrift MultiplexedProcessor object
+ */
+struct _ThriftMultiplexedProcessor
+{
+ ThriftProcessor parent;
+
+ /* private */
+ gchar * default_processor_name;
+ GHashTable *multiplexed_services;
+};
+typedef struct _ThriftMultiplexedProcessor ThriftMultiplexedProcessor;
+
+/*!
+ * Thrift MultiplexedProcessor class
+ */
+struct _ThriftMultiplexedProcessorClass
+{
+ ThriftProcessorClass parent;
+
+ gboolean (* register_processor) (ThriftProcessor *self, const gchar * multiplexed_processor_name, ThriftProcessor * multiplexed_processor , GError **error);
+
+};
+typedef struct _ThriftMultiplexedProcessorClass ThriftMultiplexedProcessorClass;
+
+/* used by THRIFT_TYPE_MULTIPLEXED_PROCESSOR */
+GType thrift_multiplexed_processor_get_type (void);
+
+/*!
+ * Processes the request.
+ * \public \memberof ThriftMultiplexedProcessorClass
+ */
+gboolean thrift_multiplexed_processor_process (ThriftMultiplexedProcessor *processor,
+ ThriftProtocol *in, ThriftProtocol *out,
+ GError **error);
+
+
+/* Public API */
+
+/**
+ * @brief Registers a processor in the multiplexed processor under its name. It
+ * will take a reference to the processor so refcount will be incremented.
+ * It will also be decremented on object destruction.
+ *
+ * The first registered processor becomes default. But you can override it with
+ * "default" property.
+ *
+ * It returns a compliant error if it cannot be registered.
+ *
+ * @param processor Pointer to the multiplexed processor.
+ * @param multiplexed_processor_name Name of the processor you want to register
+ * @param multiplexed_processor Pointer to implemented processor you want multiplex.
+ * @param error Error object where we should store errors.
+ *
+ * @see https://developer.gnome.org/glib/stable/glib-Error-Reporting.html#g-set-error
+ */
+gboolean thrift_multiplexed_processor_register_processor(ThriftProcessor *processor, const gchar * multiplexed_processor_name, ThriftProcessor * multiplexed_processor , GError **error);
+
+
+/* define error/exception types */
+typedef enum
+{
+ THRIFT_MULTIPLEXED_PROCESSOR_ERROR_UNKNOWN,
+ THRIFT_MULTIPLEXED_PROCESSOR_ERROR_SERVICE_UNAVAILABLE,
+ THRIFT_MULTIPLEXED_PROCESSOR_ERROR_MESSAGE_TYPE,
+ THRIFT_MULTIPLEXED_PROCESSOR_ERROR_MESSAGE_WRONGLY_MULTIPLEXED,
+ THRIFT_MULTIPLEXED_PROCESSOR_ERROR_SEND,
+ THRIFT_MULTIPLEXED_PROCESSOR_ERROR_RECEIVE,
+ THRIFT_MULTIPLEXED_PROCESSOR_ERROR_CLOSE
+} ThriftMultiplexedProcessorError;
+
+
+GQuark thrift_multiplexed_processor_error_quark (void);
+#define THRIFT_MULTIPLEXED_PROCESSOR_ERROR (thrift_multiplexed_processor_error_quark ())
+
+
+G_END_DECLS
+
+
+#endif /* _THRIFT_MULTIPLEXED_MULTIPLEXED_PROCESSOR_H_ */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_processor.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_processor.c
new file mode 100644
index 000000000..c242c2503
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_processor.c
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/processor/thrift_processor.h>
+
+G_DEFINE_ABSTRACT_TYPE(ThriftProcessor, thrift_processor, G_TYPE_OBJECT)
+
+gboolean
+thrift_processor_process (ThriftProcessor *processor, ThriftProtocol *in,
+ ThriftProtocol *out, GError **error)
+{
+ return
+ THRIFT_PROCESSOR_GET_CLASS (processor)->process (processor, in, out, error);
+}
+
+/* class initializer for ThriftProcessor */
+static void
+thrift_processor_class_init (ThriftProcessorClass *cls)
+{
+ /* set these as virtual methods to be implemented by a subclass */
+ cls->process = thrift_processor_process;
+}
+
+static void
+thrift_processor_init (ThriftProcessor *processor)
+{
+ THRIFT_UNUSED_VAR (processor);
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_processor.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_processor.h
new file mode 100644
index 000000000..ff2d2dae4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/processor/thrift_processor.h
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_PROCESSOR_H
+#define _THRIFT_PROCESSOR_H
+
+#include <glib-object.h>
+
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_processor.h
+ * \brief Abstract class for Thrift processors.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_PROCESSOR (thrift_processor_get_type ())
+#define THRIFT_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_PROCESSOR, ThriftProcessor))
+#define THRIFT_IS_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_PROCESSOR))
+#define THRIFT_PROCESSOR_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_PROCESSOR, ThriftProcessorClass))
+#define THRIFT_IS_PROCESSOR_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_PROCESSOR))
+#define THRIFT_PROCESSOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_PROCESSOR, ThriftProcessorClass))
+
+/*!
+ * Thrift Processorobject
+ */
+struct _ThriftProcessor
+{
+ GObject parent;
+};
+typedef struct _ThriftProcessor ThriftProcessor;
+
+/*!
+ * Thrift Processor class
+ */
+struct _ThriftProcessorClass
+{
+ GObjectClass parent;
+
+ /* vtable */
+ gboolean (*process) (ThriftProcessor *processor, ThriftProtocol *in,
+ ThriftProtocol *out, GError **error);
+};
+typedef struct _ThriftProcessorClass ThriftProcessorClass;
+
+/* used by THRIFT_TYPE_PROCESSOR */
+GType thrift_processor_get_type (void);
+
+/*!
+ * Processes the request.
+ * \public \memberof ThriftProcessorClass
+ */
+gboolean thrift_processor_process (ThriftProcessor *processor,
+ ThriftProtocol *in, ThriftProtocol *out,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* _THRIFT_PROCESSOR_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.c
new file mode 100644
index 000000000..7c2d017e1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.c
@@ -0,0 +1,911 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/protocol/thrift_binary_protocol.h>
+
+G_DEFINE_TYPE(ThriftBinaryProtocol, thrift_binary_protocol, THRIFT_TYPE_PROTOCOL)
+
+static guint64
+thrift_bitwise_cast_guint64 (gdouble v)
+{
+ union {
+ gdouble from;
+ guint64 to;
+ } u;
+ u.from = v;
+ return u.to;
+}
+
+static gdouble
+thrift_bitwise_cast_gdouble (guint64 v)
+{
+ union {
+ guint64 from;
+ gdouble to;
+ } u;
+ u.from = v;
+ return u.to;
+}
+
+gint32
+thrift_binary_protocol_write_message_begin (ThriftProtocol *protocol,
+ const gchar *name, const ThriftMessageType message_type,
+ const gint32 seqid, GError **error)
+{
+ gint32 version = (THRIFT_BINARY_PROTOCOL_VERSION_1)
+ | ((gint32) message_type);
+ gint32 ret;
+ gint32 xfer = 0;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ if ((ret = thrift_protocol_write_i32 (protocol, version, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+ if ((ret = thrift_protocol_write_string (protocol, name, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+ if ((ret = thrift_protocol_write_i32 (protocol, seqid, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+ return xfer;
+}
+
+gint32
+thrift_binary_protocol_write_message_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_binary_protocol_write_struct_begin (ThriftProtocol *protocol,
+ const gchar *name,
+ GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (name);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_binary_protocol_write_struct_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_binary_protocol_write_field_begin (ThriftProtocol *protocol,
+ const gchar *name,
+ const ThriftType field_type,
+ const gint16 field_id,
+ GError **error)
+{
+ gint32 ret;
+ gint32 xfer = 0;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ THRIFT_UNUSED_VAR (name);
+
+ if ((ret = thrift_protocol_write_byte (protocol, (gint8) field_type,
+ error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+ if ((ret = thrift_protocol_write_i16 (protocol, field_id, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+ return xfer;
+}
+
+gint32
+thrift_binary_protocol_write_field_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_binary_protocol_write_field_stop (ThriftProtocol *protocol,
+ GError **error)
+{
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+ return thrift_protocol_write_byte (protocol, (gint8) T_STOP, error);
+}
+
+gint32
+thrift_binary_protocol_write_map_begin (ThriftProtocol *protocol,
+ const ThriftType key_type,
+ const ThriftType value_type,
+ const guint32 size,
+ GError **error)
+{
+ gint32 ret;
+ gint32 xfer = 0;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ if ((ret = thrift_protocol_write_byte (protocol, (gint8) key_type,
+ error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+ if ((ret = thrift_protocol_write_byte (protocol, (gint8) value_type,
+ error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+ if ((ret = thrift_protocol_write_i32 (protocol, (gint32) size, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+ return xfer;
+}
+
+gint32
+thrift_binary_protocol_write_map_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_binary_protocol_write_list_begin (ThriftProtocol *protocol,
+ const ThriftType element_type,
+ const guint32 size,
+ GError **error)
+{
+ gint32 ret;
+ gint32 xfer = 0;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ if ((ret = thrift_protocol_write_byte (protocol, (gint8) element_type,
+ error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+
+ if ((ret = thrift_protocol_write_i32 (protocol, (gint32) size, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+
+ return xfer;
+}
+
+gint32
+thrift_binary_protocol_write_list_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_binary_protocol_write_set_begin (ThriftProtocol *protocol,
+ const ThriftType element_type,
+ const guint32 size,
+ GError **error)
+{
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ return thrift_protocol_write_list_begin (protocol, element_type,
+ size, error);
+}
+
+gint32
+thrift_binary_protocol_write_set_end (ThriftProtocol *protocol, GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_binary_protocol_write_bool (ThriftProtocol *protocol,
+ const gboolean value, GError **error)
+{
+ guint8 tmp;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ tmp = value ? 1 : 0;
+ return thrift_protocol_write_byte (protocol, tmp, error);
+}
+
+gint32
+thrift_binary_protocol_write_byte (ThriftProtocol *protocol, const gint8 value,
+ GError **error)
+{
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ if (thrift_transport_write (protocol->transport,
+ (const gpointer) &value, 1, error))
+ {
+ return 1;
+ } else {
+ return -1;
+ }
+}
+
+gint32
+thrift_binary_protocol_write_i16 (ThriftProtocol *protocol, const gint16 value,
+ GError **error)
+{
+ gint16 net;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ net = g_htons (value);
+ if (thrift_transport_write (protocol->transport,
+ (const gpointer) &net, 2, error))
+ {
+ return 2;
+ } else {
+ return -1;
+ }
+}
+
+gint32
+thrift_binary_protocol_write_i32 (ThriftProtocol *protocol, const gint32 value,
+ GError **error)
+{
+ gint32 net;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ net = g_htonl (value);
+ if (thrift_transport_write (protocol->transport,
+ (const gpointer) &net, 4, error))
+ {
+ return 4;
+ } else {
+ return -1;
+ }
+}
+
+gint32
+thrift_binary_protocol_write_i64 (ThriftProtocol *protocol, const gint64 value,
+ GError **error)
+{
+ gint64 net;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ net = GUINT64_TO_BE (value);
+ if (thrift_transport_write (protocol->transport,
+ (const gpointer) &net, 8, error))
+ {
+ return 8;
+ } else {
+ return -1;
+ }
+}
+
+gint32
+thrift_binary_protocol_write_double (ThriftProtocol *protocol,
+ const gdouble value, GError **error)
+{
+ guint64 bits;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ bits = GUINT64_FROM_BE (thrift_bitwise_cast_guint64 (value));
+ if (thrift_transport_write (protocol->transport,
+ (const gpointer) &bits, 8, error))
+ {
+ return 8;
+ } else {
+ return -1;
+ }
+}
+
+gint32
+thrift_binary_protocol_write_string (ThriftProtocol *protocol,
+ const gchar *str, GError **error)
+{
+ guint32 len;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ len = str != NULL ? strlen (str) : 0;
+ /* write the string length + 1 which includes the null terminator */
+ return thrift_protocol_write_binary (protocol, (const gpointer) str,
+ len, error);
+}
+
+gint32
+thrift_binary_protocol_write_binary (ThriftProtocol *protocol,
+ const gpointer buf,
+ const guint32 len, GError **error)
+{
+ gint32 ret;
+ gint32 xfer = 0;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ if ((ret = thrift_protocol_write_i32 (protocol, len, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+
+ if (len > 0)
+ {
+ if (thrift_transport_write (protocol->transport,
+ (const gpointer) buf, len, error) == FALSE)
+ {
+ return -1;
+ }
+ xfer += len;
+ }
+
+ return xfer;
+}
+
+gint32
+thrift_binary_protocol_read_message_begin (ThriftProtocol *protocol,
+ gchar **name,
+ ThriftMessageType *message_type,
+ gint32 *seqid, GError **error)
+{
+ gint32 ret;
+ gint32 xfer = 0;
+ gint32 sz;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ if ((ret = thrift_protocol_read_i32 (protocol, &sz, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+
+ if (sz < 0)
+ {
+ /* check for version */
+ guint32 version = sz & THRIFT_BINARY_PROTOCOL_VERSION_MASK;
+ if (version != THRIFT_BINARY_PROTOCOL_VERSION_1)
+ {
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_BAD_VERSION,
+ "expected version %d, got %d",
+ THRIFT_BINARY_PROTOCOL_VERSION_1, version);
+ return -1;
+ }
+
+ *message_type = (ThriftMessageType) (sz & 0x000000ff);
+
+ if ((ret = thrift_protocol_read_string (protocol, name, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+
+ if ((ret = thrift_protocol_read_i32 (protocol, seqid, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+ }
+ return xfer;
+}
+
+gint32
+thrift_binary_protocol_read_message_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_binary_protocol_read_struct_begin (ThriftProtocol *protocol,
+ gchar **name,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ *name = NULL;
+ return 0;
+}
+
+gint32
+thrift_binary_protocol_read_struct_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_binary_protocol_read_field_begin (ThriftProtocol *protocol,
+ gchar **name,
+ ThriftType *field_type,
+ gint16 *field_id,
+ GError **error)
+{
+ gint32 ret;
+ gint32 xfer = 0;
+ gint8 type;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ THRIFT_UNUSED_VAR (name);
+
+ if ((ret = thrift_protocol_read_byte (protocol, &type, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+ *field_type = (ThriftType) type;
+ if (*field_type == T_STOP)
+ {
+ *field_id = 0;
+ return xfer;
+ }
+ if ((ret = thrift_protocol_read_i16 (protocol, field_id, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+ return xfer;
+}
+
+gint32
+thrift_binary_protocol_read_field_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_binary_protocol_read_map_begin (ThriftProtocol *protocol,
+ ThriftType *key_type,
+ ThriftType *value_type,
+ guint32 *size,
+ GError **error)
+{
+ gint32 ret;
+ gint32 xfer = 0;
+ gint8 k, v;
+ gint32 sizei;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ if ((ret = thrift_protocol_read_byte (protocol, &k, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+ *key_type = (ThriftType) k;
+
+ if ((ret = thrift_protocol_read_byte (protocol, &v, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+ *value_type = (ThriftType) v;
+
+ if ((ret = thrift_protocol_read_i32 (protocol, &sizei, error)) <0)
+ {
+ return -1;
+ }
+ xfer += ret;
+
+ if (sizei < 0)
+ {
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_NEGATIVE_SIZE,
+ "got negative size of %d", sizei);
+ return -1;
+ }
+
+ *size = (guint32) sizei;
+ return xfer;
+}
+
+gint32
+thrift_binary_protocol_read_map_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_binary_protocol_read_list_begin (ThriftProtocol *protocol,
+ ThriftType *element_type,
+ guint32 *size, GError **error)
+{
+ gint32 ret;
+ gint32 xfer = 0;
+ gint8 e;
+ gint32 sizei;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ if ((ret = thrift_protocol_read_byte (protocol, &e, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+ *element_type = (ThriftType) e;
+
+ if ((ret = thrift_protocol_read_i32 (protocol, &sizei, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+
+ if (sizei < 0)
+ {
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_NEGATIVE_SIZE,
+ "got negative size of %d", sizei);
+ return -1;
+ }
+
+ *size = (guint32) sizei;
+ return xfer;
+}
+
+gint32
+thrift_binary_protocol_read_list_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_binary_protocol_read_set_begin (ThriftProtocol *protocol,
+ ThriftType *element_type,
+ guint32 *size, GError **error)
+{
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ return thrift_protocol_read_list_begin (protocol, element_type, size, error);
+}
+
+gint32
+thrift_binary_protocol_read_set_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_binary_protocol_read_bool (ThriftProtocol *protocol, gboolean *value,
+ GError **error)
+{
+ gint32 ret;
+ gpointer b[1];
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ if ((ret =
+ thrift_transport_read_all (protocol->transport,
+ b, 1, error)) < 0)
+ {
+ return -1;
+ }
+ *value = *(gint8 *) b != 0;
+ return ret;
+}
+
+gint32
+thrift_binary_protocol_read_byte (ThriftProtocol *protocol, gint8 *value,
+ GError **error)
+{
+ gint32 ret;
+ gpointer b[1];
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ if ((ret =
+ thrift_transport_read_all (protocol->transport,
+ b, 1, error)) < 0)
+ {
+ return -1;
+ }
+ *value = *(gint8 *) b;
+ return ret;
+}
+
+gint32
+thrift_binary_protocol_read_i16 (ThriftProtocol *protocol, gint16 *value,
+ GError **error)
+{
+ gint32 ret;
+ union
+ {
+ gint8 byte_array[2];
+ gint16 int16;
+ } b;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ if ((ret =
+ thrift_transport_read_all (protocol->transport,
+ b.byte_array, 2, error)) < 0)
+ {
+ return -1;
+ }
+ *value = g_ntohs (b.int16);
+ return ret;
+}
+
+gint32
+thrift_binary_protocol_read_i32 (ThriftProtocol *protocol, gint32 *value,
+ GError **error)
+{
+ gint32 ret;
+ union
+ {
+ gint8 byte_array[4];
+ gint32 int32;
+ } b;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ if ((ret =
+ thrift_transport_read_all (protocol->transport,
+ b.byte_array, 4, error)) < 0)
+ {
+ return -1;
+ }
+ *value = g_ntohl (b.int32);
+ return ret;
+}
+
+gint32
+thrift_binary_protocol_read_i64 (ThriftProtocol *protocol, gint64 *value,
+ GError **error)
+{
+ gint32 ret;
+ union
+ {
+ gint8 byte_array[8];
+ gint64 int64;
+ } b;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ if ((ret =
+ thrift_transport_read_all (protocol->transport,
+ b.byte_array, 8, error)) < 0)
+ {
+ return -1;
+ }
+ *value = GUINT64_FROM_BE (b.int64);
+ return ret;
+}
+
+gint32
+thrift_binary_protocol_read_double (ThriftProtocol *protocol,
+ gdouble *value, GError **error)
+{
+ gint32 ret;
+ union
+ {
+ gint8 byte_array[8];
+ guint64 uint64;
+ } b;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ if ((ret =
+ thrift_transport_read_all (protocol->transport,
+ b.byte_array, 8, error)) < 0)
+ {
+ return -1;
+ }
+ *value = thrift_bitwise_cast_gdouble (GUINT64_FROM_BE (b.uint64));
+ return ret;
+}
+
+gint32
+thrift_binary_protocol_read_string (ThriftProtocol *protocol,
+ gchar **str, GError **error)
+{
+ guint32 len;
+ gint32 ret;
+ gint32 xfer = 0;
+ gint32 read_len = 0;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ /* read the length into read_len */
+ if ((ret =
+ thrift_protocol_read_i32 (protocol, &read_len, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+
+ if (read_len < 0) {
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_NEGATIVE_SIZE,
+ "got negative size of %d", read_len);
+ *str = NULL;
+ return -1;
+ }
+
+ /* allocate the memory for the string */
+ len = (guint32) read_len + 1; /* space for null terminator */
+ *str = g_new0 (gchar, len);
+ if (read_len > 0) {
+ if ((ret =
+ thrift_transport_read_all (protocol->transport,
+ *str, read_len, error)) < 0)
+ {
+ g_free (*str);
+ *str = NULL;
+ len = 0;
+ return -1;
+ }
+ xfer += ret;
+ } else {
+ **str = 0;
+ }
+
+ return xfer;
+}
+
+gint32
+thrift_binary_protocol_read_binary (ThriftProtocol *protocol,
+ gpointer *buf, guint32 *len,
+ GError **error)
+{
+ gint32 ret;
+ gint32 xfer = 0;
+ gint32 read_len = 0;
+
+ g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1);
+
+ /* read the length into read_len */
+ if ((ret =
+ thrift_protocol_read_i32 (protocol, &read_len, error)) < 0)
+ {
+ return -1;
+ }
+ xfer += ret;
+
+ if (read_len > 0)
+ {
+ /* allocate the memory as an array of unsigned char for binary data */
+ *len = (guint32) read_len;
+ *buf = g_new (guchar, *len);
+ if ((ret =
+ thrift_transport_read_all (protocol->transport,
+ *buf, *len, error)) < 0)
+ {
+ g_free (*buf);
+ *buf = NULL;
+ *len = 0;
+ return -1;
+ }
+ xfer += ret;
+ } else {
+ *len = (guint32) read_len;
+ *buf = NULL;
+ }
+
+ return xfer;
+}
+
+static void
+thrift_binary_protocol_init (ThriftBinaryProtocol *protocol)
+{
+ THRIFT_UNUSED_VAR (protocol);
+}
+
+/* initialize the class */
+static void
+thrift_binary_protocol_class_init (ThriftBinaryProtocolClass *klass)
+{
+ ThriftProtocolClass *cls = THRIFT_PROTOCOL_CLASS (klass);
+
+ cls->write_message_begin = thrift_binary_protocol_write_message_begin;
+ cls->write_message_end = thrift_binary_protocol_write_message_end;
+ cls->write_struct_begin = thrift_binary_protocol_write_struct_begin;
+ cls->write_struct_end = thrift_binary_protocol_write_struct_end;
+ cls->write_field_begin = thrift_binary_protocol_write_field_begin;
+ cls->write_field_end = thrift_binary_protocol_write_field_end;
+ cls->write_field_stop = thrift_binary_protocol_write_field_stop;
+ cls->write_map_begin = thrift_binary_protocol_write_map_begin;
+ cls->write_map_end = thrift_binary_protocol_write_map_end;
+ cls->write_list_begin = thrift_binary_protocol_write_list_begin;
+ cls->write_list_end = thrift_binary_protocol_write_list_end;
+ cls->write_set_begin = thrift_binary_protocol_write_set_begin;
+ cls->write_set_end = thrift_binary_protocol_write_set_end;
+ cls->write_bool = thrift_binary_protocol_write_bool;
+ cls->write_byte = thrift_binary_protocol_write_byte;
+ cls->write_i16 = thrift_binary_protocol_write_i16;
+ cls->write_i32 = thrift_binary_protocol_write_i32;
+ cls->write_i64 = thrift_binary_protocol_write_i64;
+ cls->write_double = thrift_binary_protocol_write_double;
+ cls->write_string = thrift_binary_protocol_write_string;
+ cls->write_binary = thrift_binary_protocol_write_binary;
+ cls->read_message_begin = thrift_binary_protocol_read_message_begin;
+ cls->read_message_end = thrift_binary_protocol_read_message_end;
+ cls->read_struct_begin = thrift_binary_protocol_read_struct_begin;
+ cls->read_struct_end = thrift_binary_protocol_read_struct_end;
+ cls->read_field_begin = thrift_binary_protocol_read_field_begin;
+ cls->read_field_end = thrift_binary_protocol_read_field_end;
+ cls->read_map_begin = thrift_binary_protocol_read_map_begin;
+ cls->read_map_end = thrift_binary_protocol_read_map_end;
+ cls->read_list_begin = thrift_binary_protocol_read_list_begin;
+ cls->read_list_end = thrift_binary_protocol_read_list_end;
+ cls->read_set_begin = thrift_binary_protocol_read_set_begin;
+ cls->read_set_end = thrift_binary_protocol_read_set_end;
+ cls->read_bool = thrift_binary_protocol_read_bool;
+ cls->read_byte = thrift_binary_protocol_read_byte;
+ cls->read_i16 = thrift_binary_protocol_read_i16;
+ cls->read_i32 = thrift_binary_protocol_read_i32;
+ cls->read_i64 = thrift_binary_protocol_read_i64;
+ cls->read_double = thrift_binary_protocol_read_double;
+ cls->read_string = thrift_binary_protocol_read_string;
+ cls->read_binary = thrift_binary_protocol_read_binary;
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.h
new file mode 100644
index 000000000..5230bcced
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.h
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_BINARY_PROTOCOL_H
+#define _THRIFT_BINARY_PROTOCOL_H
+
+#include <glib-object.h>
+
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/transport/thrift_transport.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_binary_protocol.h
+ * \brief Binary protocol implementation of a Thrift protocol. Implements the
+ * ThriftProtocol interface.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_BINARY_PROTOCOL (thrift_binary_protocol_get_type ())
+#define THRIFT_BINARY_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_BINARY_PROTOCOL, ThriftBinaryProtocol))
+#define THRIFT_IS_BINARY_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_BINARY_PROTOCOL))
+#define THRIFT_BINARY_PROTOCOL_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_BINARY_PROTOCOL, ThriftBinaryProtocolClass))
+#define THRIFT_IS_BINARY_PROTOCOL_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_BINARY_PROTOCOL))
+#define THRIFT_BINARY_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_BINARY_PROTOCOL, ThriftBinaryProtocolClass))
+
+/* version numbers */
+#define THRIFT_BINARY_PROTOCOL_VERSION_1 0x80010000
+#define THRIFT_BINARY_PROTOCOL_VERSION_MASK 0xffff0000
+
+typedef struct _ThriftBinaryProtocol ThriftBinaryProtocol;
+
+/*!
+ * Thrift Binary Protocol instance.
+ */
+struct _ThriftBinaryProtocol
+{
+ ThriftProtocol parent;
+};
+
+typedef struct _ThriftBinaryProtocolClass ThriftBinaryProtocolClass;
+
+/*!
+ * Thrift Binary Protocol class.
+ */
+struct _ThriftBinaryProtocolClass
+{
+ ThriftProtocolClass parent;
+};
+
+/* used by THRIFT_TYPE_BINARY_PROTOCOL */
+GType thrift_binary_protocol_get_type (void);
+
+G_END_DECLS
+
+#endif /* _THRIFT_BINARY_PROTOCOL_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol_factory.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol_factory.c
new file mode 100644
index 000000000..774e9ad4a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol_factory.c
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/protocol/thrift_binary_protocol.h>
+#include <thrift/c_glib/protocol/thrift_binary_protocol_factory.h>
+
+G_DEFINE_TYPE(ThriftBinaryProtocolFactory, thrift_binary_protocol_factory, THRIFT_TYPE_PROTOCOL_FACTORY)
+
+ThriftProtocol *
+thrift_binary_protocol_factory_get_protocol (ThriftProtocolFactory *factory,
+ ThriftTransport *transport)
+{
+ ThriftBinaryProtocol *tb = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL,
+ "transport", transport, NULL);
+
+ THRIFT_UNUSED_VAR (factory);
+
+ return THRIFT_PROTOCOL (tb);
+}
+
+static void
+thrift_binary_protocol_factory_class_init (ThriftBinaryProtocolFactoryClass *cls)
+{
+ ThriftProtocolFactoryClass *protocol_factory_class = THRIFT_PROTOCOL_FACTORY_CLASS (cls);
+
+ protocol_factory_class->get_protocol = thrift_binary_protocol_factory_get_protocol;
+}
+
+static void
+thrift_binary_protocol_factory_init (ThriftBinaryProtocolFactory *factory)
+{
+ THRIFT_UNUSED_VAR (factory);
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol_factory.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol_factory.h
new file mode 100644
index 000000000..eddc07390
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol_factory.h
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_BINARY_PROTOCOL_FACTORY_H
+#define _THRIFT_BINARY_PROTOCOL_FACTORY_H
+
+#include <glib-object.h>
+
+#include <thrift/c_glib/protocol/thrift_protocol_factory.h>
+
+G_BEGIN_DECLS
+
+/* type macros */
+#define THRIFT_TYPE_BINARY_PROTOCOL_FACTORY (thrift_binary_protocol_factory_get_type ())
+#define THRIFT_BINARY_PROTOCOL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_BINARY_PROTOCOL_FACTORY, ThriftBinaryProtocolFactory))
+#define THRIFT_IS_BINARY_PROTOCOL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_BINARY_PROTOCOL_FACTORY))
+#define THRIFT_BINARY_PROTOCOL_FACTORY_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_BINARY_PROTOCOL_FACTORY, ThriftBinaryProtocolFactoryClass))
+#define THRIFT_IS_BINARY_PROTOCOL_FACTORY_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_BINARY_PROTOCOL_FACTORY))
+#define THRIFT_BINARY_PROTOCOL_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_BINARY_PROTOCOL_FACTORY, ThriftBinaryProtocolFactoryClass))
+
+typedef struct _ThriftBinaryProtocolFactory ThriftBinaryProtocolFactory;
+
+struct _ThriftBinaryProtocolFactory
+{
+ ThriftProtocolFactory parent;
+};
+
+typedef struct _ThriftBinaryProtocolFactoryClass ThriftBinaryProtocolFactoryClass;
+
+struct _ThriftBinaryProtocolFactoryClass
+{
+ ThriftProtocolFactoryClass parent;
+};
+
+/* used by THRIFT_TYPE_BINARY_PROTOCOL_FACTORY */
+GType thrift_binary_protocol_factory_get_type (void);
+
+G_END_DECLS
+
+#endif /* _THRIFT_BINARY_PROTOCOL_FACTORY_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol.c
new file mode 100644
index 000000000..cae4749f0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol.c
@@ -0,0 +1,1609 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/protocol/thrift_compact_protocol.h>
+
+#include <thrift/config.h>
+
+/*
+ * *_to_zigzag depend on the fact that the right shift
+ * operator on a signed integer is an arithmetic (sign-extending) shift.
+ * If this is not the case, the current implementation will not work.
+ */
+#if !defined(SIGNED_RIGHT_SHIFT_IS) || !defined(ARITHMETIC_RIGHT_SHIFT)
+# error "Unable to determine the behavior of a signed right shift"
+#endif
+#if SIGNED_RIGHT_SHIFT_IS != ARITHMETIC_RIGHT_SHIFT
+# error "thrift_compact_protocol only works if signed right shift is arithmetic"
+#endif
+
+/* object properties */
+enum _ThriftCompactProtocolProperties
+{
+ PROP_0,
+ PROP_THRIFT_COMPACT_PROTOCOL_STRING_LIMIT,
+ PROP_THRIFT_COMPACT_PROTOCOL_CONTAINER_LIMIT
+};
+
+G_DEFINE_TYPE (ThriftCompactProtocol, thrift_compact_protocol,
+ THRIFT_TYPE_PROTOCOL)
+
+static const gint8 PROTOCOL_ID = (gint8)0x82u;
+static const gint8 VERSION_N = 1;
+static const gint8 VERSION_MASK = 0x1f; /* 0001 1111 */
+static const gint8 TYPE_MASK = (gint8)0xe0u; /* 1110 0000 */
+static const gint8 TYPE_BITS = 0x07; /* 0000 0111 */
+static const gint32 TYPE_SHIFT_AMOUNT = 5;
+
+enum Types {
+ CT_STOP = 0x00,
+ CT_BOOLEAN_TRUE = 0x01,
+ CT_BOOLEAN_FALSE = 0x02,
+ CT_BYTE = 0x03,
+ CT_I16 = 0x04,
+ CT_I32 = 0x05,
+ CT_I64 = 0x06,
+ CT_DOUBLE = 0x07,
+ CT_BINARY = 0x08,
+ CT_LIST = 0x09,
+ CT_SET = 0x0A,
+ CT_MAP = 0x0B,
+ CT_STRUCT = 0x0C
+};
+
+static const gint8 TTypeToCType[16] = {
+ CT_STOP, /* T_STOP */
+ 0, /* unused */
+ CT_BOOLEAN_TRUE, /* T_BOOL */
+ CT_BYTE, /* T_BYTE */
+ CT_DOUBLE, /* T_DOUBLE */
+ 0, /* unused */
+ CT_I16, /* T_I16 */
+ 0, /* unused */
+ CT_I32, /* T_I32 */
+ 0, /* unused */
+ CT_I64, /* T_I64 */
+ CT_BINARY, /* T_STRING */
+ CT_STRUCT, /* T_STRUCT */
+ CT_MAP, /* T_MAP */
+ CT_SET, /* T_SET */
+ CT_LIST, /* T_LIST */
+};
+
+static guint64
+thrift_bitwise_cast_guint64 (const gdouble v)
+{
+ union {
+ gdouble from;
+ guint64 to;
+ } u;
+ u.from = v;
+ return u.to;
+}
+
+static gdouble
+thrift_bitwise_cast_gdouble (const guint64 v)
+{
+ union {
+ guint64 from;
+ gdouble to;
+ } u;
+ u.from = v;
+ return u.to;
+}
+
+/**
+ * Convert l into a zigzag long. This allows negative numbers to be
+ * represented compactly as a varint.
+ */
+static guint64
+i64_to_zigzag (const gint64 l)
+{
+ return (((guint64)l) << 1) ^ (l >> 63);
+}
+
+/**
+ * Convert n into a zigzag int. This allows negative numbers to be
+ * represented compactly as a varint.
+ */
+static guint32
+i32_to_zigzag (const gint32 n)
+{
+ return (((guint32)n) << 1) ^ (n >> 31);
+}
+
+/**
+ * Convert from zigzag int to int.
+ */
+static gint32
+zigzag_to_i32 (guint32 n)
+{
+ return (n >> 1) ^ (guint32) (-(gint32) (n & 1));
+}
+
+/**
+ * Convert from zigzag long to long.
+ */
+static gint64
+zigzag_to_i64 (guint64 n)
+{
+ return (n >> 1) ^ (guint64) (-(gint64) (n & 1));
+}
+
+ThriftType thrift_compact_protocol_get_ttype (ThriftCompactProtocol *protocol,
+ const gint8 type, GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+
+ switch (type) {
+ case T_STOP:
+ return T_STOP;
+ case CT_BOOLEAN_FALSE:
+ case CT_BOOLEAN_TRUE:
+ return T_BOOL;
+ case CT_BYTE:
+ return T_BYTE;
+ case CT_I16:
+ return T_I16;
+ case CT_I32:
+ return T_I32;
+ case CT_I64:
+ return T_I64;
+ case CT_DOUBLE:
+ return T_DOUBLE;
+ case CT_BINARY:
+ return T_STRING;
+ case CT_LIST:
+ return T_LIST;
+ case CT_SET:
+ return T_SET;
+ case CT_MAP:
+ return T_MAP;
+ case CT_STRUCT:
+ return T_STRUCT;
+ default:
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_INVALID_DATA,
+ "unrecognized type");
+ return -1;
+ }
+}
+
+/**
+ * Write an i32 as a varint. Results in 1-5 bytes on the wire.
+ */
+gint32
+thrift_compact_protocol_write_varint32 (ThriftCompactProtocol *protocol,
+ const guint32 n,
+ GError **error)
+{
+ guint8 buf[5];
+ gint32 xfer;
+ guint32 m;
+
+ THRIFT_UNUSED_VAR (error);
+
+ xfer = 0;
+ m = n;
+
+ while (TRUE) {
+ if ((m & ~0x7F) == 0) {
+ buf[xfer++] = (gint8)m;
+ break;
+ } else {
+ buf[xfer++] = (gint8)((m & 0x7F) | 0x80);
+ m >>= 7;
+ }
+ }
+
+ if (thrift_transport_write (THRIFT_PROTOCOL (protocol)->transport,
+ (const gpointer) buf, xfer, error)) {
+ return xfer;
+ } else {
+ return -1;
+ }
+}
+
+/**
+ * Write an i64 as a varint. Results in 1-10 bytes on the wire.
+ */
+gint32
+thrift_compact_protocol_write_varint64 (ThriftCompactProtocol *protocol,
+ const guint64 n,
+ GError **error)
+{
+ guint8 buf[10];
+ gint32 xfer;
+ guint64 m;
+
+ THRIFT_UNUSED_VAR (error);
+
+ xfer = 0;
+ m = n;
+
+ while (TRUE) {
+ if ((m & ~0x7FL) == 0) {
+ buf[xfer++] = (gint8)m;
+ break;
+ } else {
+ buf[xfer++] = (gint8)((m & 0x7F) | 0x80);
+ m >>= 7;
+ }
+ }
+
+ if (thrift_transport_write (THRIFT_PROTOCOL (protocol)->transport,
+ (const gpointer) buf, xfer, error)) {
+ return xfer;
+ } else {
+ return -1;
+ }
+}
+
+/**
+ * Read an i64 from the wire as a proper varint. The MSB of each byte is set
+ * if there is another byte to follow. This can read up to 10 bytes.
+ */
+gint32
+thrift_compact_protocol_read_varint64 (ThriftCompactProtocol *protocol,
+ gint64 *i64,
+ GError **error)
+{
+ ThriftProtocol *tp;
+ gint32 ret;
+ gint32 xfer;
+ guint64 val;
+ gint shift;
+ guint8 byte;
+
+ tp = THRIFT_PROTOCOL (protocol);
+ xfer = 0;
+ val = 0;
+ shift = 0;
+ byte = 0;
+
+ while (TRUE) {
+ if ((ret = thrift_transport_read_all (tp->transport,
+ (gpointer) &byte, 1, error)) < 0) {
+ return -1;
+ }
+ ++xfer;
+ val |= (guint64)(byte & 0x7f) << shift;
+ shift += 7;
+ if (!(byte & 0x80)) {
+ *i64 = (gint64) val;
+ return xfer;
+ }
+ if (G_UNLIKELY (xfer == 10)) { /* 7 * 9 < 64 < 7 * 10 */
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_INVALID_DATA,
+ "variable-length int over 10 bytes");
+ return -1;
+ }
+ }
+}
+
+/**
+ * Read an i32 from the wire as a varint. The MSB of each byte is set
+ * if there is another byte to follow. This can read up to 5 bytes.
+ */
+gint32
+thrift_compact_protocol_read_varint32 (ThriftCompactProtocol *protocol,
+ gint32 *i32,
+ GError **error)
+{
+ gint64 val;
+ gint32 ret;
+ gint32 xfer;
+
+ xfer = 0;
+
+ if ((ret = thrift_compact_protocol_read_varint64 (protocol, &val,
+ error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ *i32 = (gint32)val;
+
+ return xfer;
+}
+
+gint32
+thrift_compact_protocol_write_field_begin_internal (ThriftCompactProtocol
+ *protocol,
+ const gchar *name,
+ const ThriftType field_type,
+ const gint16 field_id,
+ const gint8 type_override,
+ GError **error)
+{
+ gint32 ret;
+ gint32 xfer;
+ gint8 type_to_write;
+
+ THRIFT_UNUSED_VAR (name);
+
+ xfer = 0;
+
+ /* if there's a type override, use that. */
+ type_to_write
+ = (type_override == -1 ? TTypeToCType[field_type] : type_override);
+
+ /* check if we can use delta encoding for the field id */
+ if (field_id > protocol->_last_field_id
+ && field_id - protocol->_last_field_id <= 15) {
+ /* write them together */
+ if ((ret = thrift_protocol_write_byte (THRIFT_PROTOCOL (protocol),
+ (gint8) ((field_id
+ - protocol->_last_field_id)
+ << 4 | type_to_write),
+ error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+ } else {
+ /* write them separate */
+ if ((ret = thrift_protocol_write_byte (THRIFT_PROTOCOL (protocol),
+ type_to_write, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ if ((ret = thrift_protocol_write_i16 (THRIFT_PROTOCOL (protocol), field_id,
+ error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+ }
+
+ protocol->_last_field_id = field_id;
+ return xfer;
+}
+
+/**
+ * Method for writing the start of lists and sets. List and sets on
+ * the wire differ only by the type indicator.
+ */
+gint32
+thrift_compact_protocol_write_collection_begin (ThriftCompactProtocol *protocol,
+ const ThriftType elem_type,
+ guint32 size, GError **error)
+{
+ gint32 ret;
+ gint32 xfer;
+
+ xfer = 0;
+
+ if (size <= 14) {
+ if ((ret = thrift_protocol_write_byte (THRIFT_PROTOCOL (protocol),
+ (gint8) (size << 4
+ | TTypeToCType[elem_type]),
+ error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+ } else {
+ if ((ret = thrift_protocol_write_byte (THRIFT_PROTOCOL (protocol),
+ (gint8) (0xf0
+ | TTypeToCType[elem_type]),
+ error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ if ((ret = thrift_compact_protocol_write_varint32 (protocol,
+ (guint32) size,
+ error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+ }
+
+ return xfer;
+}
+
+/*
+ * public methods
+ */
+
+gint32
+thrift_compact_protocol_write_message_begin (ThriftProtocol *protocol,
+ const gchar *name,
+ const ThriftMessageType
+ message_type,
+ const gint32 seqid, GError **error)
+{
+ gint8 version;
+ gint32 ret;
+ gint32 xfer;
+
+ ThriftCompactProtocol *cp;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ version = (VERSION_N & VERSION_MASK)
+ | (((gint32) message_type << TYPE_SHIFT_AMOUNT) & TYPE_MASK);
+ xfer = 0;
+
+ if ((ret = thrift_protocol_write_byte (protocol, PROTOCOL_ID, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ if ((ret = thrift_protocol_write_byte (protocol, version, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ if ((ret = thrift_compact_protocol_write_varint32 (cp,
+ (guint32) seqid,
+ error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ if ((ret = thrift_protocol_write_string (protocol, name, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ return xfer;
+}
+
+gint32
+thrift_compact_protocol_write_message_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_compact_protocol_write_struct_begin (ThriftProtocol *protocol,
+ const gchar *name,
+ GError **error)
+{
+ ThriftCompactProtocol *cp;
+ GQueue *q;
+
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (name);
+ THRIFT_UNUSED_VAR (error);
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+ q = &(cp->_last_field);
+
+ g_queue_push_tail (q, GINT_TO_POINTER ((gint) cp->_last_field_id));
+ cp->_last_field_id = 0;
+ return 0;
+}
+
+gint32
+thrift_compact_protocol_write_struct_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ ThriftCompactProtocol *cp;
+ GQueue *q;
+
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (error);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+ q = &(cp->_last_field);
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp->_last_field_id = (gint16) GPOINTER_TO_INT (g_queue_pop_tail (q));
+ return 0;
+}
+
+gint32
+thrift_compact_protocol_write_field_begin (ThriftProtocol *protocol,
+ const gchar *name,
+ const ThriftType field_type,
+ const gint16 field_id,
+ GError **error)
+{
+ ThriftCompactProtocol *cp;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ if (field_type == T_BOOL) {
+ cp->_bool_field_name = name;
+ cp->_bool_field_type = field_type;
+ cp->_bool_field_id = field_id;
+ return 0;
+ } else {
+ return thrift_compact_protocol_write_field_begin_internal (cp, name,
+ field_type,
+ field_id, -1,
+ error);
+ }
+}
+
+gint32
+thrift_compact_protocol_write_field_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_compact_protocol_write_field_stop (ThriftProtocol *protocol,
+ GError **error)
+{
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+ return thrift_protocol_write_byte (protocol, (gint8) T_STOP, error);
+}
+
+gint32
+thrift_compact_protocol_write_map_begin (ThriftProtocol *protocol,
+ const ThriftType key_type,
+ const ThriftType value_type,
+ const guint32 size,
+ GError **error)
+{
+ gint32 ret;
+ gint32 xfer;
+
+ ThriftCompactProtocol *cp;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ xfer = 0;
+
+ if ((ret = thrift_compact_protocol_write_varint32 (cp, (guint32) size,
+ error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ if (size > 0) {
+ if ((ret = thrift_protocol_write_byte (protocol,
+ (gint8) (TTypeToCType[key_type] << 4
+ | TTypeToCType[value_type]),
+ error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+ }
+
+ return xfer;
+}
+
+gint32
+thrift_compact_protocol_write_map_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_compact_protocol_write_list_begin (ThriftProtocol *protocol,
+ const ThriftType element_type,
+ const guint32 size,
+ GError **error)
+{
+ ThriftCompactProtocol *cp;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ return thrift_compact_protocol_write_collection_begin (cp, element_type,
+ size, error);
+}
+
+gint32
+thrift_compact_protocol_write_list_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_compact_protocol_write_set_begin (ThriftProtocol *protocol,
+ const ThriftType element_type,
+ const guint32 size,
+ GError **error)
+{
+ ThriftCompactProtocol *cp;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ return thrift_compact_protocol_write_collection_begin (cp, element_type,
+ size, error);
+}
+
+gint32
+thrift_compact_protocol_write_set_end (ThriftProtocol *protocol, GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_compact_protocol_write_bool (ThriftProtocol *protocol,
+ const gboolean value, GError **error)
+{
+ ThriftCompactProtocol *cp;
+
+ gint32 ret;
+ gint32 xfer;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ xfer = 0;
+
+ if (cp->_bool_field_name != NULL) {
+ /* we haven't written the field header yet */
+ if ((ret = thrift_compact_protocol_write_field_begin_internal (cp,
+ cp->_bool_field_name,
+ cp->_bool_field_type,
+ cp->_bool_field_id,
+ (gint8) (value
+ ? CT_BOOLEAN_TRUE : CT_BOOLEAN_FALSE),
+ error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ cp->_bool_field_name = NULL;
+ } else {
+ /* we're not part of a field, so just write the value */
+ if ((ret = thrift_protocol_write_byte (protocol,
+ (gint8) (value ? CT_BOOLEAN_TRUE
+ : CT_BOOLEAN_FALSE),
+ error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+ }
+ return xfer;
+}
+
+gint32
+thrift_compact_protocol_write_byte (ThriftProtocol *protocol, const gint8 value,
+ GError **error)
+{
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ if (thrift_transport_write (protocol->transport,
+ (const gpointer) &value, 1, error)) {
+ return 1;
+ } else {
+ return -1;
+ }
+}
+
+gint32
+thrift_compact_protocol_write_i16 (ThriftProtocol *protocol, const gint16 value,
+ GError **error)
+{
+ ThriftCompactProtocol *cp;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ return thrift_compact_protocol_write_varint32 (cp,
+ i32_to_zigzag ((gint32) value),
+ error);
+}
+
+gint32
+thrift_compact_protocol_write_i32 (ThriftProtocol *protocol, const gint32 value,
+ GError **error)
+{
+ ThriftCompactProtocol *cp;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ return thrift_compact_protocol_write_varint32 (cp,
+ i32_to_zigzag (value),
+ error);
+}
+
+gint32
+thrift_compact_protocol_write_i64 (ThriftProtocol *protocol, const gint64 value,
+ GError **error)
+{
+ ThriftCompactProtocol *cp;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ return thrift_compact_protocol_write_varint64 (cp,
+ i64_to_zigzag (value),
+ error);
+}
+
+gint32
+thrift_compact_protocol_write_double (ThriftProtocol *protocol,
+ const gdouble value, GError **error)
+{
+ guint64 bits;
+
+ g_assert (sizeof (gdouble) == sizeof (guint64));
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ bits = GUINT64_TO_LE (thrift_bitwise_cast_guint64 (value));
+ if (thrift_transport_write (protocol->transport,
+ (const gpointer) &bits, 8, error)) {
+ return 8;
+ } else {
+ return -1;
+ }
+}
+
+gint32
+thrift_compact_protocol_write_string (ThriftProtocol *protocol,
+ const gchar *str, GError **error)
+{
+ size_t len;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ len = str != NULL ? strlen (str) : 0;
+ if (len > G_MAXINT32) {
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_SIZE_LIMIT,
+ "string size (guess: %lu) is too large", (unsigned long) len);
+ return -1;
+ }
+
+ /* write the string length + 1 which includes the null terminator */
+ return thrift_protocol_write_binary (protocol, (const gpointer) str,
+ (const guint32) len, error);
+}
+
+gint32
+thrift_compact_protocol_write_binary (ThriftProtocol *protocol,
+ const gpointer buf,
+ const guint32 len, GError **error)
+{
+ ThriftCompactProtocol *cp;
+
+ gint32 ret;
+ gint32 xfer;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ xfer = 0;
+
+ if ((ret = thrift_compact_protocol_write_varint32 (cp, len, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ if (len > 0) {
+ /* checking len + xfer > uint_max, but we don't want to overflow while
+ * checking for overflows. transforming to len > uint_max - xfer.
+ */
+ if (len > (guint32) (G_MAXINT32 - xfer)) {
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_SIZE_LIMIT,
+ "size %d + %d is too large", len, xfer);
+ return -1;
+ }
+
+ if (thrift_transport_write (protocol->transport,
+ (const gpointer) buf, len, error) == FALSE) {
+ return -1;
+ }
+ xfer += len;
+ }
+
+ return xfer;
+}
+
+gint32
+thrift_compact_protocol_read_message_begin (ThriftProtocol *protocol,
+ gchar **name,
+ ThriftMessageType *message_type,
+ gint32 *seqid, GError **error)
+{
+ ThriftCompactProtocol *cp;
+
+ gint32 ret;
+ gint32 xfer;
+
+ gint8 protocol_id, version_and_type, version;
+
+ xfer = 0;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ if ((ret = thrift_protocol_read_byte (protocol, &protocol_id, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ if (protocol_id != PROTOCOL_ID) {
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_BAD_VERSION,
+ "bad protocol id");
+ return -1;
+ }
+
+ if ((ret = thrift_protocol_read_byte (protocol, &version_and_type,
+ error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ version = (gint8)(version_and_type & VERSION_MASK);
+ if (version != VERSION_N) {
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_BAD_VERSION,
+ "bad version and/or type");
+ return -1;
+ }
+
+ *message_type
+ = (ThriftMessageType)((version_and_type >> TYPE_SHIFT_AMOUNT) & TYPE_BITS);
+
+ if ((ret = thrift_compact_protocol_read_varint32 (cp, seqid, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ if ((ret = thrift_protocol_read_string (protocol, name, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ return xfer;
+}
+
+gint32
+thrift_compact_protocol_read_message_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_compact_protocol_read_struct_begin (ThriftProtocol *protocol,
+ gchar **name,
+ GError **error)
+{
+ ThriftCompactProtocol *cp;
+ GQueue *q;
+
+ THRIFT_UNUSED_VAR (error);
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+ q = &(cp->_last_field);
+
+ *name = NULL;
+
+ g_queue_push_tail (q, GINT_TO_POINTER ((gint) cp->_last_field_id));
+ cp->_last_field_id = 0;
+
+ return 0;
+}
+
+gint32
+thrift_compact_protocol_read_struct_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ ThriftCompactProtocol *cp;
+ GQueue *q;
+
+ THRIFT_UNUSED_VAR (error);
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+ q = &(cp->_last_field);
+
+ cp->_last_field_id = (gint16) GPOINTER_TO_INT (g_queue_pop_tail (q));
+
+ return 0;
+}
+
+gint32
+thrift_compact_protocol_read_field_begin (ThriftProtocol *protocol,
+ gchar **name,
+ ThriftType *field_type,
+ gint16 *field_id,
+ GError **error)
+{
+ ThriftCompactProtocol *cp;
+
+ gint32 ret;
+ gint32 xfer;
+
+ gint16 modifier;
+ gint8 byte;
+ gint8 type;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ THRIFT_UNUSED_VAR (name);
+
+ xfer = 0;
+
+ if ((ret = thrift_protocol_read_byte (protocol, &byte, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ type = (byte & 0x0f);
+
+ /* if it's a stop, then we can return immediately, as the struct is over. */
+ if (type == T_STOP) {
+ *field_type = T_STOP;
+ *field_id = 0;
+ return xfer;
+ }
+
+ /* mask off the 4 MSB of the type header.
+ * it could contain a field id delta.
+ */
+ modifier = (gint16)(((guint8)byte & 0xf0) >> 4);
+ if (modifier == 0) {
+ /* not a delta, look ahead for the zigzag varint field id. */
+ if ((ret = thrift_protocol_read_i16 (protocol, field_id, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+ } else {
+ *field_id = (gint16)(cp->_last_field_id + modifier);
+ }
+ if ((ret = thrift_compact_protocol_get_ttype (cp, type, error)) < 0) {
+ return -1;
+ }
+ *field_type = ret;
+
+ /* if this happens to be a boolean field, the value is encoded in the type */
+ if (type == CT_BOOLEAN_TRUE || type == CT_BOOLEAN_FALSE) {
+ /* save the boolean value in a special instance variable. */
+ cp->_has_bool_value = TRUE;
+ cp->_bool_value =
+ (type == CT_BOOLEAN_TRUE ? TRUE : FALSE);
+ }
+
+ /* push the new field onto the field stack so we can keep the deltas going. */
+ cp->_last_field_id = *field_id;
+
+ return xfer;
+}
+
+gint32
+thrift_compact_protocol_read_field_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_compact_protocol_read_map_begin (ThriftProtocol *protocol,
+ ThriftType *key_type,
+ ThriftType *value_type,
+ guint32 *size,
+ GError **error)
+{
+ gint32 ret;
+ gint32 xfer;
+
+ gint8 kv_type;
+ gint32 msize;
+
+ ThriftCompactProtocol *cp;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ kv_type = 0;
+ msize = 0;
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ xfer = 0;
+
+ if ((ret = thrift_compact_protocol_read_varint32 (cp, &msize, error)) <0) {
+ return -1;
+ }
+ xfer += ret;
+
+ /* still read the kv byte if negative size */
+ if (msize != 0) {
+ if ((ret = thrift_protocol_read_byte (protocol, &kv_type, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+ }
+
+ if (cp->container_limit > 0 && msize > cp->container_limit) {
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_SIZE_LIMIT,
+ "got size over limit (%d > %d)", msize, cp->container_limit);
+ return -1;
+ } else if (msize > 0) {
+ if ((ret = thrift_compact_protocol_get_ttype (cp,
+ (gint8)((guint8)kv_type
+ >> 4),
+ error)) < 0) {
+ return -1;
+ }
+ *key_type = ret;
+ if ((ret = thrift_compact_protocol_get_ttype (cp,
+ (gint8)((guint8)kv_type
+ & 0xf),
+ error)) < 0) {
+ return -1;
+ }
+ *value_type = ret;
+ *size = (guint32) msize;
+ } else if (msize == 0) {
+ *key_type = 0;
+ *value_type = 0;
+ *size = 0;
+ } else {
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_NEGATIVE_SIZE,
+ "got negative size of %d", msize);
+ return -1;
+ }
+
+ return xfer;
+}
+
+gint32
+thrift_compact_protocol_read_map_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_compact_protocol_read_list_begin (ThriftProtocol *protocol,
+ ThriftType *element_type,
+ guint32 *size, GError **error)
+{
+ ThriftCompactProtocol *cp;
+
+ gint32 ret;
+ gint32 xfer;
+
+ gint8 size_and_type;
+ gint32 lsize;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ size_and_type = 0;
+
+ xfer = 0;
+
+ if ((ret = thrift_protocol_read_byte (protocol, &size_and_type, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ lsize = ((guint8)size_and_type >> 4) & 0x0f;
+ if (lsize == 15) {
+ if ((ret = thrift_compact_protocol_read_varint32 (cp, &lsize, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+ }
+
+ if (lsize < 0) {
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_NEGATIVE_SIZE,
+ "got negative size of %d", lsize);
+ return -1;
+ } else if (cp->container_limit > 0 && lsize > cp->container_limit) {
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_SIZE_LIMIT,
+ "got size over limit (%d > %d)", lsize, cp->container_limit);
+ return -1;
+ }
+
+ if ((ret = thrift_compact_protocol_get_ttype (cp,
+ (gint8)(size_and_type & 0x0f),
+ error)) < 0) {
+ return -1;
+ }
+ *element_type = ret;
+ *size = (guint32) lsize;
+
+ return xfer;
+}
+
+gint32
+thrift_compact_protocol_read_list_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_compact_protocol_read_set_begin (ThriftProtocol *protocol,
+ ThriftType *element_type,
+ guint32 *size, GError **error)
+{
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ return thrift_protocol_read_list_begin (protocol, element_type, size, error);
+}
+
+gint32
+thrift_compact_protocol_read_set_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+ return 0;
+}
+
+gint32
+thrift_compact_protocol_read_bool (ThriftProtocol *protocol, gboolean *value,
+ GError **error)
+{
+ ThriftCompactProtocol *cp;
+
+ gint32 ret;
+ gint32 xfer;
+
+ gint8 val;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ xfer = 0;
+
+ if (cp->_has_bool_value == TRUE) {
+ *value = cp->_bool_value;
+ cp->_has_bool_value = FALSE;
+ return 0;
+ } else {
+ if ((ret = thrift_protocol_read_byte (protocol, &val, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ *value = (val == CT_BOOLEAN_TRUE);
+ return xfer;
+ }
+}
+
+gint32
+thrift_compact_protocol_read_byte (ThriftProtocol *protocol, gint8 *value,
+ GError **error)
+{
+ gint32 ret;
+ gpointer b[1];
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ if ((ret =
+ thrift_transport_read_all (protocol->transport,
+ b, 1, error)) < 0) {
+ return -1;
+ }
+ *value = *(gint8 *) b;
+ return ret;
+}
+
+gint32
+thrift_compact_protocol_read_i16 (ThriftProtocol *protocol, gint16 *value,
+ GError **error)
+{
+ ThriftCompactProtocol *cp;
+
+ gint32 ret;
+ gint32 val;
+ gint32 xfer;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ xfer = 0;
+
+ if ((ret = thrift_compact_protocol_read_varint32 (cp, &val, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ *value = (gint16) zigzag_to_i32 ((guint32) val);
+
+ return xfer;
+}
+
+gint32
+thrift_compact_protocol_read_i32 (ThriftProtocol *protocol, gint32 *value,
+ GError **error)
+{
+ ThriftCompactProtocol *cp;
+
+ gint32 ret;
+ gint32 val;
+ gint32 xfer;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ xfer = 0;
+
+ if ((ret = thrift_compact_protocol_read_varint32 (cp, &val, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ *value = zigzag_to_i32 ((guint32) val);
+
+ return xfer;
+}
+
+gint32
+thrift_compact_protocol_read_i64 (ThriftProtocol *protocol, gint64 *value,
+ GError **error)
+{
+ ThriftCompactProtocol *cp;
+
+ gint32 ret;
+ gint64 val;
+ gint32 xfer;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ xfer = 0;
+
+ if ((ret = thrift_compact_protocol_read_varint64 (cp, &val, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ *value = zigzag_to_i64 ((guint64) val);
+
+ return xfer;
+}
+
+gint32
+thrift_compact_protocol_read_double (ThriftProtocol *protocol,
+ gdouble *value, GError **error)
+{
+ gint32 ret;
+ union {
+ guint64 bits;
+ guint8 b[8];
+ } u;
+
+ g_assert (sizeof (gdouble) == sizeof (guint64));
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ if ((ret =
+ thrift_transport_read_all (protocol->transport,
+ u.b, 8, error)) < 0) {
+ return -1;
+ }
+ u.bits = GUINT64_FROM_LE (u.bits);
+ *value = thrift_bitwise_cast_gdouble (u.bits);
+ return ret;
+}
+
+gint32
+thrift_compact_protocol_read_string (ThriftProtocol *protocol,
+ gchar **str, GError **error)
+{
+ ThriftCompactProtocol *cp;
+
+ gint32 ret;
+ gint32 xfer;
+
+ gint32 read_len;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ xfer = 0;
+ read_len = 0;
+
+ /* read the length into read_len */
+ if ((ret =
+ thrift_compact_protocol_read_varint32 (cp, &read_len, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ if (cp->string_limit > 0 && read_len > cp->string_limit) {
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_SIZE_LIMIT,
+ "got size over limit (%d > %d)", read_len, cp->string_limit);
+ *str = NULL;
+ return -1;
+ }
+
+ if (read_len < 0) {
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_NEGATIVE_SIZE,
+ "got negative size of %d", read_len);
+ *str = NULL;
+ return -1;
+ }
+
+ /* allocate the memory as an array of unsigned char for binary data */
+ *str = g_new0 (gchar, read_len + 1);
+ if (read_len > 0) {
+ if ((ret =
+ thrift_transport_read_all (protocol->transport,
+ *str, read_len, error)) < 0) {
+ g_free (*str);
+ *str = NULL;
+ return -1;
+ }
+ xfer += ret;
+ } else {
+ **str = 0;
+ }
+
+ return xfer;
+}
+
+gint32
+thrift_compact_protocol_read_binary (ThriftProtocol *protocol,
+ gpointer *buf, guint32 *len,
+ GError **error)
+{
+ ThriftCompactProtocol *cp;
+
+ gint32 ret;
+ gint32 xfer;
+
+ gint32 read_len;
+
+ g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1);
+
+ cp = THRIFT_COMPACT_PROTOCOL (protocol);
+
+ xfer = 0;
+ read_len = 0;
+
+ /* read the length into read_len */
+ if ((ret =
+ thrift_compact_protocol_read_varint32 (cp, &read_len, error)) < 0) {
+ return -1;
+ }
+ xfer += ret;
+
+ if (cp->string_limit > 0 && read_len > cp->string_limit) {
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_SIZE_LIMIT,
+ "got size over limit (%d > %d)", read_len, cp->string_limit);
+ *buf = NULL;
+ *len = 0;
+ return -1;
+ }
+
+ if (read_len > 0) {
+ /* allocate the memory as an array of unsigned char for binary data */
+ *len = (guint32) read_len;
+ *buf = g_new (guchar, *len);
+ if ((ret =
+ thrift_transport_read_all (protocol->transport,
+ *buf, *len, error)) < 0) {
+ g_free (*buf);
+ *buf = NULL;
+ *len = 0;
+ return -1;
+ }
+ xfer += ret;
+
+ } else if (read_len == 0) {
+ *len = (guint32) read_len;
+ *buf = NULL;
+
+ } else {
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_NEGATIVE_SIZE,
+ "got negative size of %d", read_len);
+ *buf = NULL;
+ *len = 0;
+ return -1;
+ }
+
+ return xfer;
+}
+
+/* property accessor */
+void
+thrift_compact_protocol_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ ThriftCompactProtocol *tc;
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ tc = THRIFT_COMPACT_PROTOCOL (object);
+
+ switch (property_id) {
+ case PROP_THRIFT_COMPACT_PROTOCOL_STRING_LIMIT:
+ g_value_set_int (value, tc->string_limit);
+ break;
+ case PROP_THRIFT_COMPACT_PROTOCOL_CONTAINER_LIMIT:
+ g_value_set_int (value, tc->container_limit);
+ break;
+ }
+}
+
+/* property mutator */
+void
+thrift_compact_protocol_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ ThriftCompactProtocol *tc;
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ tc = THRIFT_COMPACT_PROTOCOL (object);
+
+ switch (property_id) {
+ case PROP_THRIFT_COMPACT_PROTOCOL_STRING_LIMIT:
+ tc->string_limit = g_value_get_int (value);
+ break;
+ case PROP_THRIFT_COMPACT_PROTOCOL_CONTAINER_LIMIT:
+ tc->container_limit = g_value_get_int (value);
+ break;
+ }
+}
+
+/* initialize the class */
+static void
+thrift_compact_protocol_class_init (ThriftCompactProtocolClass *klass)
+{
+ ThriftProtocolClass *cls;
+ GObjectClass *gobject_class;
+ GParamSpec *param_spec;
+
+ cls = THRIFT_PROTOCOL_CLASS (klass);
+ gobject_class = G_OBJECT_CLASS (klass);
+ param_spec = NULL;
+
+ /* setup accessors and mutators */
+ gobject_class->get_property = thrift_compact_protocol_get_property;
+ gobject_class->set_property = thrift_compact_protocol_set_property;
+
+ param_spec = g_param_spec_int ("string_limit",
+ "Max allowed string size",
+ "Set the max string limit",
+ 0, /* min */
+ G_MAXINT32, /* max */
+ 0, /* default value */
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_COMPACT_PROTOCOL_STRING_LIMIT,
+ param_spec);
+
+ param_spec = g_param_spec_int ("container_limit",
+ "Max allowed container size",
+ "Set the max container limit",
+ 0, /* min */
+ G_MAXINT32, /* max */
+ 0, /* default value */
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_COMPACT_PROTOCOL_CONTAINER_LIMIT,
+ param_spec);
+
+ cls->write_message_begin = thrift_compact_protocol_write_message_begin;
+ cls->write_message_end = thrift_compact_protocol_write_message_end;
+ cls->write_struct_begin = thrift_compact_protocol_write_struct_begin;
+ cls->write_struct_end = thrift_compact_protocol_write_struct_end;
+ cls->write_field_begin = thrift_compact_protocol_write_field_begin;
+ cls->write_field_end = thrift_compact_protocol_write_field_end;
+ cls->write_field_stop = thrift_compact_protocol_write_field_stop;
+ cls->write_map_begin = thrift_compact_protocol_write_map_begin;
+ cls->write_map_end = thrift_compact_protocol_write_map_end;
+ cls->write_list_begin = thrift_compact_protocol_write_list_begin;
+ cls->write_list_end = thrift_compact_protocol_write_list_end;
+ cls->write_set_begin = thrift_compact_protocol_write_set_begin;
+ cls->write_set_end = thrift_compact_protocol_write_set_end;
+ cls->write_bool = thrift_compact_protocol_write_bool;
+ cls->write_byte = thrift_compact_protocol_write_byte;
+ cls->write_i16 = thrift_compact_protocol_write_i16;
+ cls->write_i32 = thrift_compact_protocol_write_i32;
+ cls->write_i64 = thrift_compact_protocol_write_i64;
+ cls->write_double = thrift_compact_protocol_write_double;
+ cls->write_string = thrift_compact_protocol_write_string;
+ cls->write_binary = thrift_compact_protocol_write_binary;
+ cls->read_message_begin = thrift_compact_protocol_read_message_begin;
+ cls->read_message_end = thrift_compact_protocol_read_message_end;
+ cls->read_struct_begin = thrift_compact_protocol_read_struct_begin;
+ cls->read_struct_end = thrift_compact_protocol_read_struct_end;
+ cls->read_field_begin = thrift_compact_protocol_read_field_begin;
+ cls->read_field_end = thrift_compact_protocol_read_field_end;
+ cls->read_map_begin = thrift_compact_protocol_read_map_begin;
+ cls->read_map_end = thrift_compact_protocol_read_map_end;
+ cls->read_list_begin = thrift_compact_protocol_read_list_begin;
+ cls->read_list_end = thrift_compact_protocol_read_list_end;
+ cls->read_set_begin = thrift_compact_protocol_read_set_begin;
+ cls->read_set_end = thrift_compact_protocol_read_set_end;
+ cls->read_bool = thrift_compact_protocol_read_bool;
+ cls->read_byte = thrift_compact_protocol_read_byte;
+ cls->read_i16 = thrift_compact_protocol_read_i16;
+ cls->read_i32 = thrift_compact_protocol_read_i32;
+ cls->read_i64 = thrift_compact_protocol_read_i64;
+ cls->read_double = thrift_compact_protocol_read_double;
+ cls->read_string = thrift_compact_protocol_read_string;
+ cls->read_binary = thrift_compact_protocol_read_binary;
+}
+
+static void
+thrift_compact_protocol_init (ThriftCompactProtocol *self)
+{
+ g_queue_init (&(self->_last_field));
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol.h
new file mode 100644
index 000000000..effcaadf0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol.h
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_COMPACT_PROTOCOL_H
+#define _THRIFT_COMPACT_PROTOCOL_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/transport/thrift_transport.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_compact_protocol.h
+ * \brief Compact protocol implementation of a Thrift protocol. Implements the
+ * ThriftProtocol interface.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_COMPACT_PROTOCOL (thrift_compact_protocol_get_type ())
+#define THRIFT_COMPACT_PROTOCOL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_COMPACT_PROTOCOL, \
+ ThriftCompactProtocol))
+#define THRIFT_IS_COMPACT_PROTOCOL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_COMPACT_PROTOCOL))
+#define THRIFT_COMPACT_PROTOCOL_CLASS(c) \
+ (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_COMPACT_PROTOCOL, \
+ ThriftCompactProtocolClass))
+#define THRIFT_IS_COMPACT_PROTOCOL_CLASS(c) \
+ (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_COMPACT_PROTOCOL))
+#define THRIFT_COMPACT_PROTOCOL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_COMPACT_PROTOCOL, \
+ ThriftCompactProtocolClass))
+
+
+typedef struct _ThriftCompactProtocol ThriftCompactProtocol;
+
+/*!
+ * Thrift Compact Protocol instance.
+ */
+struct _ThriftCompactProtocol
+{
+ ThriftProtocol parent;
+
+ /* protected */
+ gint32 string_limit;
+ gint32 container_limit;
+
+ /* private */
+
+ /**
+ * (Writing) If we encounter a boolean field begin, save the TField here
+ * so it can have the value incorporated.
+ */
+ const gchar* _bool_field_name;
+ ThriftType _bool_field_type;
+ gint16 _bool_field_id;
+
+ /**
+ * (Reading) If we read a field header, and it's a boolean field, save
+ * the boolean value here so that read_bool can use it.
+ */
+ gboolean _has_bool_value;
+ gboolean _bool_value;
+
+ /**
+ * Used to keep track of the last field for the current and previous structs,
+ * so we can do the delta stuff.
+ */
+
+ GQueue _last_field;
+ gint16 _last_field_id;
+};
+
+typedef struct _ThriftCompactProtocolClass ThriftCompactProtocolClass;
+
+/*!
+ * Thrift Compact Protocol class.
+ */
+struct _ThriftCompactProtocolClass
+{
+ ThriftProtocolClass parent;
+};
+
+/* used by THRIFT_TYPE_COMPACT_PROTOCOL */
+GType thrift_compact_protocol_get_type (void);
+
+G_END_DECLS
+
+#endif /* _THRIFT_COMPACT_PROTOCOL_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol_factory.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol_factory.c
new file mode 100644
index 000000000..e725e1fd0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol_factory.c
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/protocol/thrift_compact_protocol.h>
+#include <thrift/c_glib/protocol/thrift_compact_protocol_factory.h>
+
+/* object properties */
+enum _ThriftCompactProtocolFactoryProperties
+{
+ PROP_0,
+ PROP_THRIFT_COMPACT_PROTOCOL_FACTORY_STRING_LIMIT,
+ PROP_THRIFT_COMPACT_PROTOCOL_FACTORY_CONTAINER_LIMIT
+};
+
+G_DEFINE_TYPE (ThriftCompactProtocolFactory, thrift_compact_protocol_factory,
+ THRIFT_TYPE_PROTOCOL_FACTORY)
+
+ThriftProtocol *
+thrift_compact_protocol_factory_get_protocol (ThriftProtocolFactory *factory,
+ ThriftTransport *transport)
+{
+ ThriftCompactProtocolFactory *tcf;
+ ThriftCompactProtocol *tc;
+
+ tcf = THRIFT_COMPACT_PROTOCOL_FACTORY (factory);
+
+ tc = g_object_new (THRIFT_TYPE_COMPACT_PROTOCOL,
+ "transport", transport,
+ "string_limit", tcf->string_limit,
+ "container_limit", tcf->container_limit,
+ NULL);
+
+ return THRIFT_PROTOCOL (tc);
+}
+
+/* property accessor */
+void
+thrift_compact_protocol_factory_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ ThriftCompactProtocolFactory *tcf;
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ tcf = THRIFT_COMPACT_PROTOCOL_FACTORY (object);
+
+ switch (property_id) {
+ case PROP_THRIFT_COMPACT_PROTOCOL_FACTORY_STRING_LIMIT:
+ g_value_set_int (value, tcf->string_limit);
+ break;
+ case PROP_THRIFT_COMPACT_PROTOCOL_FACTORY_CONTAINER_LIMIT:
+ g_value_set_int (value, tcf->container_limit);
+ break;
+ }
+}
+
+/* property mutator */
+void
+thrift_compact_protocol_factory_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec
+ *pspec)
+{
+ ThriftCompactProtocolFactory *tcf;
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ tcf = THRIFT_COMPACT_PROTOCOL_FACTORY (object);
+
+ switch (property_id) {
+ case PROP_THRIFT_COMPACT_PROTOCOL_FACTORY_STRING_LIMIT:
+ tcf->string_limit = g_value_get_int (value);
+ break;
+ case PROP_THRIFT_COMPACT_PROTOCOL_FACTORY_CONTAINER_LIMIT:
+ tcf->container_limit = g_value_get_int (value);
+ break;
+ }
+}
+
+static void
+thrift_compact_protocol_factory_class_init (ThriftCompactProtocolFactoryClass
+ *klass)
+{
+ ThriftProtocolFactoryClass *cls;
+ GObjectClass *gobject_class;
+ GParamSpec *param_spec;
+
+ cls = THRIFT_PROTOCOL_FACTORY_CLASS (klass);
+ gobject_class = G_OBJECT_CLASS (klass);
+ param_spec = NULL;
+
+ /* setup accessors and mutators */
+ gobject_class->get_property = thrift_compact_protocol_factory_get_property;
+ gobject_class->set_property = thrift_compact_protocol_factory_set_property;
+
+ param_spec = g_param_spec_int ("string_limit",
+ "Max allowed string size",
+ "Set the max string limit",
+ 0, /* min */
+ G_MAXINT32, /* max */
+ 0, /* default value */
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_COMPACT_PROTOCOL_FACTORY_STRING_LIMIT,
+ param_spec);
+
+ param_spec = g_param_spec_int ("container_limit",
+ "Max allowed container size",
+ "Set the max container limit",
+ 0, /* min */
+ G_MAXINT32, /* max */
+ 0, /* default value */
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_COMPACT_PROTOCOL_FACTORY_CONTAINER_LIMIT, param_spec);
+
+ cls->get_protocol = thrift_compact_protocol_factory_get_protocol;
+}
+
+static void
+thrift_compact_protocol_factory_init (ThriftCompactProtocolFactory *factory)
+{
+ THRIFT_UNUSED_VAR (factory);
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol_factory.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol_factory.h
new file mode 100644
index 000000000..3bf953b4c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol_factory.h
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_COMPACT_PROTOCOL_FACTORY_H
+#define _THRIFT_COMPACT_PROTOCOL_FACTORY_H
+
+#include <glib-object.h>
+
+#include <thrift/c_glib/protocol/thrift_protocol_factory.h>
+
+G_BEGIN_DECLS
+
+/* type macros */
+#define THRIFT_TYPE_COMPACT_PROTOCOL_FACTORY \
+ (thrift_compact_protocol_factory_get_type ())
+#define THRIFT_COMPACT_PROTOCOL_FACTORY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_COMPACT_PROTOCOL_FACTORY, \
+ ThriftCompactProtocolFactory))
+#define THRIFT_IS_COMPACT_PROTOCOL_FACTORY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_COMPACT_PROTOCOL_FACTORY))
+#define THRIFT_COMPACT_PROTOCOL_FACTORY_CLASS(c) \
+ (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_COMPACT_PROTOCOL_FACTORY, \
+ ThriftCompactProtocolFactoryClass))
+#define THRIFT_IS_COMPACT_PROTOCOL_FACTORY_CLASS(c) \
+ (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_COMPACT_PROTOCOL_FACTORY))
+#define THRIFT_COMPACT_PROTOCOL_FACTORY_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_COMPACT_PROTOCOL_FACTORY, \
+ ThriftCompactProtocolFactoryClass))
+
+typedef struct _ThriftCompactProtocolFactory ThriftCompactProtocolFactory;
+
+struct _ThriftCompactProtocolFactory
+{
+ ThriftProtocolFactory parent;
+
+ /* protected */
+ gint32 string_limit;
+ gint32 container_limit;
+};
+
+typedef struct _ThriftCompactProtocolFactoryClass
+ ThriftCompactProtocolFactoryClass;
+
+struct _ThriftCompactProtocolFactoryClass
+{
+ ThriftProtocolFactoryClass parent;
+};
+
+/* used by THRIFT_TYPE_COMPACT_PROTOCOL_FACTORY */
+GType thrift_compact_protocol_factory_get_type (void);
+
+G_END_DECLS
+
+#endif /* _THRIFT_COMPACT_PROTOCOL_FACTORY_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_multiplexed_protocol.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_multiplexed_protocol.c
new file mode 100644
index 000000000..5084ead47
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_multiplexed_protocol.c
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/protocol/thrift_protocol_decorator.h>
+#include <thrift/c_glib/protocol/thrift_multiplexed_protocol.h>
+
+
+enum
+{
+ PROP_THRIFT_MULTIPLEXED_PROTOCOL_SERVICE_NAME = 1,
+ PROP_THRIFT_MULTIPLEXED_PROTOCOL_END
+};
+
+G_DEFINE_TYPE(ThriftMultiplexedProtocol, thrift_multiplexed_protocol, THRIFT_TYPE_PROTOCOL_DECORATOR)
+
+
+static GParamSpec *thrift_multiplexed_protocol_obj_properties[PROP_THRIFT_MULTIPLEXED_PROTOCOL_END] = { NULL, };
+
+gint32
+thrift_multiplexed_protocol_write_message_begin (ThriftMultiplexedProtocol *protocol,
+ const gchar *name, const ThriftMessageType message_type,
+ const gint32 seqid, GError **error)
+{
+ gint32 ret;
+ gchar *service_name = NULL;
+ g_return_val_if_fail (THRIFT_IS_MULTIPLEXED_PROTOCOL (protocol), -1);
+
+ ThriftMultiplexedProtocol *self = THRIFT_MULTIPLEXED_PROTOCOL (protocol);
+
+ if( (message_type == T_CALL || message_type == T_ONEWAY) && self->service_name != NULL) {
+ service_name = g_strdup_printf("%s%s%s", self->service_name, THRIFT_MULTIPLEXED_PROTOCOL_DEFAULT_SEPARATOR, name);
+ }else{
+ service_name = g_strdup(name);
+ }
+
+ /* relay to the protocol_decorator */
+ ret = thrift_protocol_decorator_write_message_begin(protocol, service_name, message_type, seqid, error);
+
+ g_free(service_name);
+
+ return ret;
+}
+
+
+static void
+thrift_multiplexed_protocol_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ThriftMultiplexedProtocol *self = THRIFT_MULTIPLEXED_PROTOCOL (object);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_MULTIPLEXED_PROTOCOL_SERVICE_NAME:
+ g_free(self->service_name);
+ self->service_name = g_value_dup_string (value);
+ break;
+
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+thrift_multiplexed_protocol_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ThriftMultiplexedProtocol *self = THRIFT_MULTIPLEXED_PROTOCOL (object);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_MULTIPLEXED_PROTOCOL_SERVICE_NAME:
+ g_value_set_string (value, self->service_name);
+ break;
+
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+
+static void
+thrift_multiplexed_protocol_init (ThriftMultiplexedProtocol *protocol)
+{
+ protocol->service_name = NULL;
+}
+
+static void
+thrift_multiplexed_protocol_finalize (GObject *gobject)
+{
+ ThriftMultiplexedProtocol *self = THRIFT_MULTIPLEXED_PROTOCOL (gobject);
+
+ if (self->service_name) {
+ g_free(self->service_name);
+ self->service_name = NULL;
+ }
+
+ /* Always chain up to the parent class; as with dispose(), finalize()
+ * is guaranteed to exist on the parent's class virtual function table
+ */
+ G_OBJECT_CLASS (thrift_multiplexed_protocol_parent_class)->finalize(gobject);
+}
+
+
+/* initialize the class */
+static void
+thrift_multiplexed_protocol_class_init (ThriftMultiplexedProtocolClass *klass)
+{
+ ThriftProtocolClass *cls = THRIFT_PROTOCOL_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ cls->write_message_begin = thrift_multiplexed_protocol_write_message_begin;
+
+ object_class->set_property = thrift_multiplexed_protocol_set_property;
+ object_class->get_property = thrift_multiplexed_protocol_get_property;
+ object_class->finalize = thrift_multiplexed_protocol_finalize;
+
+ thrift_multiplexed_protocol_obj_properties[PROP_THRIFT_MULTIPLEXED_PROTOCOL_SERVICE_NAME] =
+ g_param_spec_string ("service-name",
+ "Service name the protocol points to",
+ "Set the service name",
+ NULL,
+ (G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+
+ g_object_class_install_properties (object_class,
+ PROP_THRIFT_MULTIPLEXED_PROTOCOL_END,
+ thrift_multiplexed_protocol_obj_properties);
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_multiplexed_protocol.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_multiplexed_protocol.h
new file mode 100644
index 000000000..c6e08fb19
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_multiplexed_protocol.h
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_MULTIPLEXED_PROTOCOL_H
+#define _THRIFT_MULTIPLEXED_PROTOCOL_H
+
+#include <glib-object.h>
+
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/protocol/thrift_protocol_decorator.h>
+#include <thrift/c_glib/transport/thrift_transport.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_multiplexed_protocol.h
+ * \brief Multiplexed protocol implementation of a Thrift protocol. Implements the
+ * ThriftProtocol interface.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_MULTIPLEXED_PROTOCOL (thrift_multiplexed_protocol_get_type ())
+#define THRIFT_MULTIPLEXED_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_MULTIPLEXED_PROTOCOL, ThriftMultiplexedProtocol))
+#define THRIFT_IS_MULTIPLEXED_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_MULTIPLEXED_PROTOCOL))
+#define THRIFT_MULTIPLEXED_PROTOCOL_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_MULTIPLEXED_PROTOCOL, ThriftMultiplexedProtocolClass))
+#define THRIFT_IS_MULTIPLEXED_PROTOCOL_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_MULTIPLEXED_PROTOCOL))
+#define THRIFT_MULTIPLEXED_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_MULTIPLEXED_PROTOCOL, ThriftMultiplexedProtocolClass))
+
+/* constant */
+#define THRIFT_MULTIPLEXED_PROTOCOL_DEFAULT_SEPARATOR ":"
+
+typedef struct _ThriftMultiplexedProtocol ThriftMultiplexedProtocol;
+
+
+
+/*!
+ * Thrift Multiplexed Protocol instance.
+ */
+struct _ThriftMultiplexedProtocol
+{
+ ThriftProtocolDecorator parent;
+
+ gchar *service_name;
+};
+
+typedef struct _ThriftMultiplexedProtocolClass ThriftMultiplexedProtocolClass;
+
+/*!
+ * Thrift Multiplexed Protocol class.
+ */
+struct _ThriftMultiplexedProtocolClass
+{
+ ThriftProtocolDecoratorClass parent;
+};
+
+/* used by THRIFT_TYPE_MULTIPLEXED_PROTOCOL */
+GType thrift_multiplexed_protocol_get_type (void);
+
+G_END_DECLS
+
+#endif /* _THRIFT_MULTIPLEXED_PROTOCOL_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol.c
new file mode 100644
index 000000000..6e6ae4d9a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol.c
@@ -0,0 +1,646 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/transport/thrift_transport.h>
+
+/* define the GError domain string */
+#define THRIFT_PROTOCOL_ERROR_DOMAIN "thrift-protocol-error-quark"
+
+/* object properties */
+enum _ThriftProtocolProperties
+{
+ PROP_0,
+ PROP_THRIFT_PROTOCOL_TRANSPORT
+};
+
+G_DEFINE_ABSTRACT_TYPE(ThriftProtocol, thrift_protocol, G_TYPE_OBJECT)
+
+void
+thrift_protocol_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ ThriftProtocol *protocol = THRIFT_PROTOCOL (object);
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_PROTOCOL_TRANSPORT:
+ g_value_set_object (value, protocol->transport);
+ break;
+ }
+}
+
+void
+thrift_protocol_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+
+ ThriftProtocol *protocol = THRIFT_PROTOCOL (object);
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_PROTOCOL_TRANSPORT:
+ protocol->transport = g_value_dup_object (value);
+ break;
+ }
+}
+
+
+gint32
+thrift_protocol_write_message_begin (ThriftProtocol *protocol,
+ const gchar *name,
+ const ThriftMessageType message_type,
+ const gint32 seqid, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_message_begin
+ (protocol, name,
+ message_type, seqid,
+ error);
+}
+
+gint32
+thrift_protocol_write_message_end (ThriftProtocol *protocol, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_message_end (protocol,
+ error);
+}
+
+gint32
+thrift_protocol_write_struct_begin (ThriftProtocol *protocol, const gchar *name,
+ GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_struct_begin (protocol,
+ name, error);
+}
+
+gint32
+thrift_protocol_write_struct_end (ThriftProtocol *protocol, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_struct_end (protocol,
+ error);
+}
+
+gint32
+thrift_protocol_write_field_begin (ThriftProtocol *protocol,
+ const gchar *name,
+ const ThriftType field_type,
+ const gint16 field_id,
+ GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_field_begin (protocol,
+ name, field_type,
+ field_id, error);
+}
+
+gint32
+thrift_protocol_write_field_end (ThriftProtocol *protocol, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_field_end (protocol,
+ error);
+}
+
+gint32
+thrift_protocol_write_field_stop (ThriftProtocol *protocol, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_field_stop (protocol,
+ error);
+}
+
+gint32
+thrift_protocol_write_map_begin (ThriftProtocol *protocol,
+ const ThriftType key_type,
+ const ThriftType value_type,
+ const guint32 size, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_map_begin (protocol,
+ key_type, value_type,
+ size, error);
+}
+
+gint32
+thrift_protocol_write_map_end (ThriftProtocol *protocol, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_map_end (protocol,
+ error);
+}
+
+gint32
+thrift_protocol_write_list_begin (ThriftProtocol *protocol,
+ const ThriftType element_type,
+ const guint32 size, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_list_begin (protocol,
+ element_type, size,
+ error);
+}
+
+gint32
+thrift_protocol_write_list_end (ThriftProtocol *protocol, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_list_end (protocol,
+ error);
+}
+
+gint32
+thrift_protocol_write_set_begin (ThriftProtocol *protocol,
+ const ThriftType element_type,
+ const guint32 size, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_set_begin (protocol,
+ element_type, size,
+ error);
+}
+
+gint32
+thrift_protocol_write_set_end (ThriftProtocol *protocol, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_set_end (protocol,
+ error);
+}
+
+gint32
+thrift_protocol_write_bool (ThriftProtocol *protocol,
+ const gboolean value, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_bool (protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_write_byte (ThriftProtocol *protocol, const gint8 value,
+ GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_byte (protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_write_i16 (ThriftProtocol *protocol, const gint16 value,
+ GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_i16 (protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_write_i32 (ThriftProtocol *protocol, const gint32 value,
+ GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_i32 (protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_write_i64 (ThriftProtocol *protocol, const gint64 value,
+ GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_i64 (protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_write_double (ThriftProtocol *protocol,
+ const gdouble value, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_double (protocol,
+ value, error);
+}
+
+gint32
+thrift_protocol_write_string (ThriftProtocol *protocol,
+ const gchar *str, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_string (protocol, str,
+ error);
+}
+
+gint32
+thrift_protocol_write_binary (ThriftProtocol *protocol, const gpointer buf,
+ const guint32 len, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_binary (protocol, buf,
+ len, error);
+}
+
+gint32
+thrift_protocol_read_message_begin (ThriftProtocol *protocol,
+ gchar **name,
+ ThriftMessageType *message_type,
+ gint32 *seqid, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_message_begin (protocol,
+ name, message_type,
+ seqid, error);
+}
+
+gint32
+thrift_protocol_read_message_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_message_end (protocol,
+ error);
+}
+
+gint32
+thrift_protocol_read_struct_begin (ThriftProtocol *protocol,
+ gchar **name,
+ GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_struct_begin (protocol,
+ name,
+ error);
+}
+
+gint32
+thrift_protocol_read_struct_end (ThriftProtocol *protocol, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_struct_end (protocol,
+ error);
+}
+
+gint32
+thrift_protocol_read_field_begin (ThriftProtocol *protocol,
+ gchar **name,
+ ThriftType *field_type,
+ gint16 *field_id,
+ GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_field_begin (protocol,
+ name,
+ field_type,
+ field_id,
+ error);
+}
+
+gint32
+thrift_protocol_read_field_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_field_end (protocol,
+ error);
+}
+
+gint32
+thrift_protocol_read_map_begin (ThriftProtocol *protocol,
+ ThriftType *key_type,
+ ThriftType *value_type, guint32 *size,
+ GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_map_begin (protocol,
+ key_type,
+ value_type,
+ size,
+ error);
+}
+
+gint32
+thrift_protocol_read_map_end (ThriftProtocol *protocol, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_map_end (protocol,
+ error);
+}
+
+gint32
+thrift_protocol_read_list_begin (ThriftProtocol *protocol,
+ ThriftType *element_type,
+ guint32 *size, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_list_begin (protocol,
+ element_type,
+ size, error);
+}
+
+gint32
+thrift_protocol_read_list_end (ThriftProtocol *protocol, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_list_end (protocol,
+ error);
+}
+
+gint32
+thrift_protocol_read_set_begin (ThriftProtocol *protocol,
+ ThriftType *element_type,
+ guint32 *size, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_set_begin (protocol,
+ element_type,
+ size, error);
+}
+
+gint32
+thrift_protocol_read_set_end (ThriftProtocol *protocol, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_set_end (protocol,
+ error);
+}
+
+gint32
+thrift_protocol_read_bool (ThriftProtocol *protocol, gboolean *value,
+ GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_bool (protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_read_byte (ThriftProtocol *protocol, gint8 *value,
+ GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_byte (protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_read_i16 (ThriftProtocol *protocol, gint16 *value,
+ GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_i16 (protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_read_i32 (ThriftProtocol *protocol, gint32 *value,
+ GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_i32 (protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_read_i64 (ThriftProtocol *protocol, gint64 *value,
+ GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_i64 (protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_read_double (ThriftProtocol *protocol,
+ gdouble *value, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_double (protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_read_string (ThriftProtocol *protocol,
+ gchar **str, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_string (protocol, str,
+ error);
+}
+
+gint32
+thrift_protocol_read_binary (ThriftProtocol *protocol, gpointer *buf,
+ guint32 *len, GError **error)
+{
+ return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_binary (protocol, buf,
+ len, error);
+}
+
+#define THRIFT_SKIP_RESULT_OR_RETURN(_RES, _CALL) \
+ { \
+ gint32 _x = (_CALL); \
+ if (_x < 0) { return _x; } \
+ (_RES) += _x; \
+ }
+
+gint32
+thrift_protocol_skip (ThriftProtocol *protocol, ThriftType type, GError **error)
+{
+ switch (type)
+ {
+ case T_BOOL:
+ {
+ gboolean boolv;
+ return thrift_protocol_read_bool (protocol, &boolv, error);
+ }
+ case T_BYTE:
+ {
+ gint8 bytev;
+ return thrift_protocol_read_byte (protocol, &bytev, error);
+ }
+
+ case T_I16:
+ {
+ gint16 i16;
+ return thrift_protocol_read_i16 (protocol, &i16, error);
+ }
+ case T_I32:
+ {
+ gint32 i32;
+ return thrift_protocol_read_i32 (protocol, &i32, error);
+ }
+ case T_I64:
+ {
+ gint64 i64;
+ return thrift_protocol_read_i64 (protocol, &i64, error);
+ }
+ case T_DOUBLE:
+ {
+ gdouble dub;
+ return thrift_protocol_read_double (protocol, &dub, error);
+ }
+ case T_STRING:
+ {
+ gpointer data;
+ guint32 len;
+ gint32 ret = thrift_protocol_read_binary (protocol, &data, &len, error);
+ g_free (data);
+ return ret;
+ }
+ case T_STRUCT:
+ {
+ gint32 result = 0;
+ gchar *name;
+ gint16 fid;
+ ThriftType ftype;
+ THRIFT_SKIP_RESULT_OR_RETURN(result,
+ thrift_protocol_read_struct_begin (protocol, &name, error))
+ while (1)
+ {
+ THRIFT_SKIP_RESULT_OR_RETURN(result,
+ thrift_protocol_read_field_begin (protocol, &name, &ftype,
+ &fid, error))
+ if (ftype == T_STOP)
+ {
+ break;
+ }
+ THRIFT_SKIP_RESULT_OR_RETURN(result,
+ thrift_protocol_skip (protocol, ftype, error))
+ THRIFT_SKIP_RESULT_OR_RETURN(result,
+ thrift_protocol_read_field_end (protocol, error))
+ }
+ THRIFT_SKIP_RESULT_OR_RETURN(result,
+ thrift_protocol_read_struct_end (protocol, error))
+ return result;
+ }
+ case T_SET:
+ {
+ gint32 result = 0;
+ ThriftType elem_type;
+ guint32 i, size;
+ THRIFT_SKIP_RESULT_OR_RETURN(result,
+ thrift_protocol_read_set_begin (protocol, &elem_type, &size,
+ error))
+ for (i = 0; i < size; i++)
+ {
+ THRIFT_SKIP_RESULT_OR_RETURN(result,
+ thrift_protocol_skip (protocol, elem_type, error))
+ }
+ THRIFT_SKIP_RESULT_OR_RETURN(result,
+ thrift_protocol_read_set_end (protocol, error))
+ return result;
+ }
+ case T_MAP:
+ {
+ gint32 result = 0;
+ ThriftType elem_type;
+ ThriftType key_type;
+ guint32 i, size;
+ THRIFT_SKIP_RESULT_OR_RETURN(result,
+ thrift_protocol_read_map_begin (protocol, &key_type, &elem_type, &size,
+ error))
+ for (i = 0; i < size; i++)
+ {
+ THRIFT_SKIP_RESULT_OR_RETURN(result,
+ thrift_protocol_skip (protocol, key_type, error))
+ THRIFT_SKIP_RESULT_OR_RETURN(result,
+ thrift_protocol_skip (protocol, elem_type, error))
+ }
+ THRIFT_SKIP_RESULT_OR_RETURN(result,
+ thrift_protocol_read_map_end (protocol, error))
+ return result;
+ }
+ case T_LIST:
+ {
+ gint32 result = 0;
+ ThriftType elem_type;
+ guint32 i, size;
+ THRIFT_SKIP_RESULT_OR_RETURN(result,
+ thrift_protocol_read_list_begin (protocol, &elem_type, &size,
+ error))
+ for (i = 0; i < size; i++)
+ {
+ THRIFT_SKIP_RESULT_OR_RETURN(result,
+ thrift_protocol_skip (protocol, elem_type, error))
+ }
+ THRIFT_SKIP_RESULT_OR_RETURN(result,
+ thrift_protocol_read_list_end (protocol, error))
+ return result;
+ }
+ default:
+ break;
+ }
+
+ g_set_error (error, THRIFT_PROTOCOL_ERROR,
+ THRIFT_PROTOCOL_ERROR_INVALID_DATA,
+ "unrecognized type");
+ return -1;
+}
+
+/* define the GError domain for Thrift protocols */
+GQuark
+thrift_protocol_error_quark (void)
+{
+ return g_quark_from_static_string (THRIFT_PROTOCOL_ERROR_DOMAIN);
+}
+
+
+static void
+thrift_protocol_init (ThriftProtocol *protocol)
+{
+ protocol->transport = NULL;
+}
+
+static void
+thrift_protocol_dispose (GObject *gobject)
+{
+ ThriftProtocol *self = THRIFT_PROTOCOL (gobject);
+
+ g_clear_object(&self->transport);
+
+ /* Always chain up to the parent class; there is no need to check if
+ * the parent class implements the dispose() virtual function: it is
+ * always guaranteed to do so
+ */
+ G_OBJECT_CLASS (thrift_protocol_parent_class)->dispose(gobject);
+}
+
+static void
+thrift_protocol_class_init (ThriftProtocolClass *cls)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (cls);
+
+ gobject_class->get_property = thrift_protocol_get_property;
+ gobject_class->set_property = thrift_protocol_set_property;
+ gobject_class->dispose = thrift_protocol_dispose;
+
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_PROTOCOL_TRANSPORT,
+ g_param_spec_object ("transport", "Transport", "Thrift Transport",
+ THRIFT_TYPE_TRANSPORT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ cls->write_message_begin = thrift_protocol_write_message_begin;
+ cls->write_message_end = thrift_protocol_write_message_end;
+ cls->write_struct_begin = thrift_protocol_write_struct_begin;
+ cls->write_struct_end = thrift_protocol_write_struct_end;
+ cls->write_field_begin = thrift_protocol_write_field_begin;
+ cls->write_field_end = thrift_protocol_write_field_end;
+ cls->write_field_stop = thrift_protocol_write_field_stop;
+ cls->write_map_begin = thrift_protocol_write_map_begin;
+ cls->write_map_end = thrift_protocol_write_map_end;
+ cls->write_list_begin = thrift_protocol_write_list_begin;
+ cls->write_list_end = thrift_protocol_write_list_end;
+ cls->write_set_begin = thrift_protocol_write_set_begin;
+ cls->write_set_end = thrift_protocol_write_set_end;
+ cls->write_bool = thrift_protocol_write_bool;
+ cls->write_byte = thrift_protocol_write_byte;
+ cls->write_i16 = thrift_protocol_write_i16;
+ cls->write_i32 = thrift_protocol_write_i32;
+ cls->write_i64 = thrift_protocol_write_i64;
+ cls->write_double = thrift_protocol_write_double;
+ cls->write_string = thrift_protocol_write_string;
+ cls->write_binary = thrift_protocol_write_binary;
+ cls->read_message_begin = thrift_protocol_read_message_begin;
+ cls->read_message_end = thrift_protocol_read_message_end;
+ cls->read_struct_begin = thrift_protocol_read_struct_begin;
+ cls->read_struct_end = thrift_protocol_read_struct_end;
+ cls->read_field_begin = thrift_protocol_read_field_begin;
+ cls->read_field_end = thrift_protocol_read_field_end;
+ cls->read_map_begin = thrift_protocol_read_map_begin;
+ cls->read_map_end = thrift_protocol_read_map_end;
+ cls->read_list_begin = thrift_protocol_read_list_begin;
+ cls->read_set_begin = thrift_protocol_read_set_begin;
+ cls->read_set_end = thrift_protocol_read_set_end;
+ cls->read_bool = thrift_protocol_read_bool;
+ cls->read_byte = thrift_protocol_read_byte;
+ cls->read_i16 = thrift_protocol_read_i16;
+ cls->read_i32 = thrift_protocol_read_i32;
+ cls->read_i64 = thrift_protocol_read_i64;
+ cls->read_double = thrift_protocol_read_double;
+ cls->read_string = thrift_protocol_read_string;
+ cls->read_binary = thrift_protocol_read_binary;
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol.h
new file mode 100644
index 000000000..58fe5e0af
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol.h
@@ -0,0 +1,341 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_PROTOCOL_H
+#define _THRIFT_PROTOCOL_H
+
+#include <glib-object.h>
+
+#include <thrift/c_glib/transport/thrift_transport.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_protocol.h
+ * \brief Abstract class for Thrift protocol implementations.
+ */
+
+/**
+ * Enumerated definition of the types that the Thrift protocol supports.
+ * Take special note of the T_END type which is used specifically to mark
+ * the end of a sequence of fields.
+ */
+typedef enum {
+ T_STOP = 0,
+ T_VOID = 1,
+ T_BOOL = 2,
+ T_BYTE = 3,
+ T_I08 = 3,
+ T_I16 = 6,
+ T_I32 = 8,
+ T_U64 = 9,
+ T_I64 = 10,
+ T_DOUBLE = 4,
+ T_STRING = 11,
+ T_UTF7 = 11,
+ T_STRUCT = 12,
+ T_MAP = 13,
+ T_SET = 14,
+ T_LIST = 15,
+ T_UTF8 = 16,
+ T_UTF16 = 17
+} ThriftType;
+
+/**
+ * Enumerated definition of the message types that the Thrift protocol
+ * supports.
+ */
+typedef enum {
+ T_CALL = 1,
+ T_REPLY = 2,
+ T_EXCEPTION = 3,
+ T_ONEWAY = 4
+} ThriftMessageType;
+
+/* type macros */
+#define THRIFT_TYPE_PROTOCOL (thrift_protocol_get_type ())
+#define THRIFT_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_PROTOCOL, ThriftProtocol))
+#define THRIFT_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_PROTOCOL))
+#define THRIFT_PROTOCOL_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_PROTOCOL, ThriftProtocolClass))
+#define THRIFT_IS_PROTOCOL_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_PROTOCOL))
+#define THRIFT_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_PROTOCOL, ThriftProtocolClass))
+
+typedef struct _ThriftProtocol ThriftProtocol;
+
+/*!
+ * Thrift Protocol object
+ */
+struct _ThriftProtocol
+{
+ GObject parent;
+
+ /* protected */
+ ThriftTransport *transport;
+};
+
+typedef struct _ThriftProtocolClass ThriftProtocolClass;
+
+/*!
+ * Thrift Protocol class
+ */
+struct _ThriftProtocolClass
+{
+ GObjectClass parent;
+
+ gint32 (*write_message_begin) (ThriftProtocol *protocol, const gchar *name,
+ const ThriftMessageType message_type,
+ const gint32 seqid, GError **error);
+ gint32 (*write_message_end) (ThriftProtocol *protocol, GError **error);
+ gint32 (*write_struct_begin) (ThriftProtocol *protocol, const gchar *name,
+ GError **error);
+ gint32 (*write_struct_end) (ThriftProtocol *protocol, GError **error);
+ gint32 (*write_field_begin) (ThriftProtocol *protocol, const gchar *name,
+ const ThriftType field_type,
+ const gint16 field_id, GError **error);
+ gint32 (*write_field_end) (ThriftProtocol *protocol, GError **error);
+ gint32 (*write_field_stop) (ThriftProtocol *protocol, GError **error);
+ gint32 (*write_map_begin) (ThriftProtocol *protocol,
+ const ThriftType key_type,
+ const ThriftType value_type,
+ const guint32 size, GError **error);
+ gint32 (*write_map_end) (ThriftProtocol *protocol, GError **error);
+ gint32 (*write_list_begin) (ThriftProtocol *protocol,
+ const ThriftType element_type,
+ const guint32 size, GError **error);
+ gint32 (*write_list_end) (ThriftProtocol *protocol, GError **error);
+ gint32 (*write_set_begin) (ThriftProtocol *protocol,
+ const ThriftType element_type,
+ const guint32 size, GError **error);
+ gint32 (*write_set_end) (ThriftProtocol *protocol, GError **error);
+ gint32 (*write_bool) (ThriftProtocol *protocol, const gboolean value,
+ GError **error);
+ gint32 (*write_byte) (ThriftProtocol *protocol, const gint8 value,
+ GError **error);
+ gint32 (*write_i16) (ThriftProtocol *protocol, const gint16 value,
+ GError **error);
+ gint32 (*write_i32) (ThriftProtocol *protocol, const gint32 value,
+ GError **error);
+ gint32 (*write_i64) (ThriftProtocol *protocol, const gint64 value,
+ GError **error);
+ gint32 (*write_double) (ThriftProtocol *protocol, const gdouble value,
+ GError **error);
+ gint32 (*write_string) (ThriftProtocol *protocol, const gchar *str,
+ GError **error);
+ gint32 (*write_binary) (ThriftProtocol *protocol, const gpointer buf,
+ const guint32 len, GError **error);
+
+ gint32 (*read_message_begin) (ThriftProtocol *thrift_protocol, gchar **name,
+ ThriftMessageType *message_type,
+ gint32 *seqid, GError **error);
+ gint32 (*read_message_end) (ThriftProtocol *protocol, GError **error);
+ gint32 (*read_struct_begin) (ThriftProtocol *protocol, gchar **name,
+ GError **error);
+ gint32 (*read_struct_end) (ThriftProtocol *protocol, GError **error);
+ gint32 (*read_field_begin) (ThriftProtocol *protocol, gchar **name,
+ ThriftType *field_type, gint16 *field_id,
+ GError **error);
+ gint32 (*read_field_end) (ThriftProtocol *protocol, GError **error);
+ gint32 (*read_map_begin) (ThriftProtocol *protocol, ThriftType *key_type,
+ ThriftType *value_type, guint32 *size,
+ GError **error);
+ gint32 (*read_map_end) (ThriftProtocol *protocol, GError **error);
+ gint32 (*read_list_begin) (ThriftProtocol *protocol, ThriftType *element_type,
+ guint32 *size, GError **error);
+ gint32 (*read_list_end) (ThriftProtocol *protocol, GError **error);
+ gint32 (*read_set_begin) (ThriftProtocol *protocol, ThriftType *element_type,
+ guint32 *size, GError **error);
+ gint32 (*read_set_end) (ThriftProtocol *protocol, GError **error);
+ gint32 (*read_bool) (ThriftProtocol *protocol, gboolean *value,
+ GError **error);
+ gint32 (*read_byte) (ThriftProtocol *protocol, gint8 *value, GError **error);
+ gint32 (*read_i16) (ThriftProtocol *protocol, gint16 *value, GError **error);
+ gint32 (*read_i32) (ThriftProtocol *protocol, gint32 *value, GError **error);
+ gint32 (*read_i64) (ThriftProtocol *protocol, gint64 *value, GError **error);
+ gint32 (*read_double) (ThriftProtocol *protocol, gdouble *value,
+ GError **error);
+ gint32 (*read_string) (ThriftProtocol *protocol, gchar **str, GError **error);
+ gint32 (*read_binary) (ThriftProtocol *protocol, gpointer *buf,
+ guint32 *len, GError **error);
+};
+
+/* used by THRIFT_TYPE_PROTOCOL */
+GType thrift_protocol_get_type (void);
+
+/* virtual public methods */
+gint32 thrift_protocol_write_message_begin (ThriftProtocol *protocol,
+ const gchar *name, const ThriftMessageType message_type,
+ const gint32 seqid, GError **error);
+
+gint32 thrift_protocol_write_message_end (ThriftProtocol *protocol,
+ GError **error);
+
+gint32 thrift_protocol_write_struct_begin (ThriftProtocol *protocol,
+ const gchar *name,
+ GError **error);
+
+gint32 thrift_protocol_write_struct_end (ThriftProtocol *protocol,
+ GError **error);
+
+gint32 thrift_protocol_write_field_begin (ThriftProtocol *protocol,
+ const gchar *name,
+ const ThriftType field_type,
+ const gint16 field_id,
+ GError **error);
+
+gint32 thrift_protocol_write_field_end (ThriftProtocol *protocol,
+ GError **error);
+
+gint32 thrift_protocol_write_field_stop (ThriftProtocol *protocol,
+ GError **error);
+
+gint32 thrift_protocol_write_map_begin (ThriftProtocol *protocol,
+ const ThriftType key_type,
+ const ThriftType value_type,
+ const guint32 size, GError **error);
+
+gint32 thrift_protocol_write_map_end (ThriftProtocol *protocol, GError **error);
+
+gint32 thrift_protocol_write_list_begin (ThriftProtocol *protocol,
+ const ThriftType element_type,
+ const guint32 size, GError **error);
+
+gint32 thrift_protocol_write_list_end (ThriftProtocol *protocol,
+ GError **error);
+
+gint32 thrift_protocol_write_set_begin (ThriftProtocol *protocol,
+ const ThriftType element_type,
+ const guint32 size, GError **error);
+
+gint32 thrift_protocol_write_set_end (ThriftProtocol *protocol,
+ GError **error);
+
+gint32 thrift_protocol_write_bool (ThriftProtocol *protocol,
+ const gboolean value, GError **error);
+
+gint32 thrift_protocol_write_byte (ThriftProtocol *protocol, const gint8 value,
+ GError **error);
+
+gint32 thrift_protocol_write_i16 (ThriftProtocol *protocol, const gint16 value,
+ GError **error);
+
+gint32 thrift_protocol_write_i32 (ThriftProtocol *protocol, const gint32 value,
+ GError **error);
+
+gint32 thrift_protocol_write_i64 (ThriftProtocol *protocol, const gint64 value,
+ GError **error);
+
+gint32 thrift_protocol_write_double (ThriftProtocol *protocol,
+ const gdouble value, GError **error);
+
+gint32 thrift_protocol_write_string (ThriftProtocol *protocol,
+ const gchar *str, GError **error);
+
+gint32 thrift_protocol_write_binary (ThriftProtocol *protocol,
+ const gpointer buf,
+ const guint32 len, GError **error);
+
+gint32 thrift_protocol_read_message_begin (ThriftProtocol *thrift_protocol,
+ gchar **name,
+ ThriftMessageType *message_type,
+ gint32 *seqid, GError **error);
+
+gint32 thrift_protocol_read_message_end (ThriftProtocol *protocol,
+ GError **error);
+
+gint32 thrift_protocol_read_struct_begin (ThriftProtocol *protocol,
+ gchar **name,
+ GError **error);
+
+gint32 thrift_protocol_read_struct_end (ThriftProtocol *protocol,
+ GError **error);
+
+gint32 thrift_protocol_read_field_begin (ThriftProtocol *protocol,
+ gchar **name,
+ ThriftType *field_type,
+ gint16 *field_id,
+ GError **error);
+
+gint32 thrift_protocol_read_field_end (ThriftProtocol *protocol,
+ GError **error);
+
+gint32 thrift_protocol_read_map_begin (ThriftProtocol *protocol,
+ ThriftType *key_type,
+ ThriftType *value_type, guint32 *size,
+ GError **error);
+
+gint32 thrift_protocol_read_map_end (ThriftProtocol *protocol, GError **error);
+
+gint32 thrift_protocol_read_list_begin (ThriftProtocol *protocol,
+ ThriftType *element_type,
+ guint32 *size, GError **error);
+
+gint32 thrift_protocol_read_list_end (ThriftProtocol *protocol, GError **error);
+
+gint32 thrift_protocol_read_set_begin (ThriftProtocol *protocol,
+ ThriftType *element_type,
+ guint32 *size, GError **error);
+
+gint32 thrift_protocol_read_set_end (ThriftProtocol *protocol, GError **error);
+
+gint32 thrift_protocol_read_bool (ThriftProtocol *protocol, gboolean *value,
+ GError **error);
+
+gint32 thrift_protocol_read_byte (ThriftProtocol *protocol, gint8 *value,
+ GError **error);
+
+gint32 thrift_protocol_read_i16 (ThriftProtocol *protocol, gint16 *value,
+ GError **error);
+
+gint32 thrift_protocol_read_i32 (ThriftProtocol *protocol, gint32 *value,
+ GError **error);
+
+gint32 thrift_protocol_read_i64 (ThriftProtocol *protocol, gint64 *value,
+ GError **error);
+
+gint32 thrift_protocol_read_double (ThriftProtocol *protocol,
+ gdouble *value, GError **error);
+
+gint32 thrift_protocol_read_string (ThriftProtocol *protocol,
+ gchar **str, GError **error);
+
+gint32 thrift_protocol_read_binary (ThriftProtocol *protocol,
+ gpointer *buf, guint32 *len,
+ GError **error);
+
+gint32 thrift_protocol_skip (ThriftProtocol *protocol, ThriftType type,
+ GError **error);
+
+/* define error types */
+typedef enum
+{
+ THRIFT_PROTOCOL_ERROR_UNKNOWN,
+ THRIFT_PROTOCOL_ERROR_INVALID_DATA,
+ THRIFT_PROTOCOL_ERROR_NEGATIVE_SIZE,
+ THRIFT_PROTOCOL_ERROR_SIZE_LIMIT,
+ THRIFT_PROTOCOL_ERROR_BAD_VERSION,
+ THRIFT_PROTOCOL_ERROR_NOT_IMPLEMENTED,
+ THRIFT_PROTOCOL_ERROR_DEPTH_LIMIT
+} ThriftProtocolError;
+
+/* define an error domain for GError to use */
+GQuark thrift_protocol_error_quark (void);
+#define THRIFT_PROTOCOL_ERROR (thrift_protocol_error_quark ())
+
+G_END_DECLS
+
+#endif /* _THRIFT_PROTOCOL_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_decorator.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_decorator.c
new file mode 100644
index 000000000..03f9420f7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_decorator.c
@@ -0,0 +1,623 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/protocol/thrift_binary_protocol.h>
+#include <thrift/c_glib/protocol/thrift_protocol_decorator.h>
+
+G_DEFINE_TYPE(ThriftProtocolDecorator, thrift_protocol_decorator, THRIFT_TYPE_PROTOCOL)
+
+
+enum
+{
+ PROP_THRIFT_TYPE_PROTOCOL_DECORATOR_CONCRETE_PROTOCOL = 1,
+ PROP_THRIFT_TYPE_PROTOCOL_DECORATOR_END
+};
+
+static GParamSpec *thrift_protocol_decorator_obj_properties[PROP_THRIFT_TYPE_PROTOCOL_DECORATOR_END] = { NULL, };
+
+
+
+
+
+gint32
+thrift_protocol_decorator_write_message_begin (ThriftProtocol *protocol,
+ const gchar *name,
+ const ThriftMessageType message_type,
+ const gint32 seqid, GError **error)
+{
+
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+ ThriftProtocolClass *proto = THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol);
+
+ g_debug("Concrete protocol %p | %p", (void *)self->concrete_protocol, (void *)proto);
+
+ return proto->write_message_begin (self->concrete_protocol, name,
+ message_type, seqid,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_write_message_end (ThriftProtocol *protocol, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_message_end (self->concrete_protocol,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_write_struct_begin (ThriftProtocol *protocol, const gchar *name,
+ GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_struct_begin (self->concrete_protocol,
+ name, error);
+}
+
+gint32
+thrift_protocol_decorator_write_struct_end (ThriftProtocol *protocol, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_struct_end (self->concrete_protocol,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_write_field_begin (ThriftProtocol *protocol,
+ const gchar *name,
+ const ThriftType field_type,
+ const gint16 field_id,
+ GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_field_begin (self->concrete_protocol,
+ name, field_type,
+ field_id, error);
+}
+
+gint32
+thrift_protocol_decorator_write_field_end (ThriftProtocol *protocol, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_field_end (self->concrete_protocol,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_write_field_stop (ThriftProtocol *protocol, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_field_stop (self->concrete_protocol,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_write_map_begin (ThriftProtocol *protocol,
+ const ThriftType key_type,
+ const ThriftType value_type,
+ const guint32 size, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_map_begin (self->concrete_protocol,
+ key_type, value_type,
+ size, error);
+}
+
+gint32
+thrift_protocol_decorator_write_map_end (ThriftProtocol *protocol, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_map_end (self->concrete_protocol,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_write_list_begin (ThriftProtocol *protocol,
+ const ThriftType element_type,
+ const guint32 size, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_list_begin (self->concrete_protocol,
+ element_type, size,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_write_list_end (ThriftProtocol *protocol, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_list_end (self->concrete_protocol,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_write_set_begin (ThriftProtocol *protocol,
+ const ThriftType element_type,
+ const guint32 size, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_set_begin (self->concrete_protocol,
+ element_type, size,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_write_set_end (ThriftProtocol *protocol, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_set_end (self->concrete_protocol,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_write_bool (ThriftProtocol *protocol,
+ const gboolean value, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_bool (self->concrete_protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_write_byte (ThriftProtocol *protocol, const gint8 value,
+ GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_byte (self->concrete_protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_write_i16 (ThriftProtocol *protocol, const gint16 value,
+ GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_i16 (self->concrete_protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_write_i32 (ThriftProtocol *protocol, const gint32 value,
+ GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_i32 (self->concrete_protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_write_i64 (ThriftProtocol *protocol, const gint64 value,
+ GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_i64 (self->concrete_protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_write_double (ThriftProtocol *protocol,
+ const gdouble value, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_double (self->concrete_protocol,
+ value, error);
+}
+
+gint32
+thrift_protocol_decorator_write_string (ThriftProtocol *protocol,
+ const gchar *str, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_string (self->concrete_protocol, str,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_write_binary (ThriftProtocol *protocol, const gpointer buf,
+ const guint32 len, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->write_binary (self->concrete_protocol, buf,
+ len, error);
+}
+
+gint32
+thrift_protocol_decorator_read_message_begin (ThriftProtocol *protocol,
+ gchar **name,
+ ThriftMessageType *message_type,
+ gint32 *seqid, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_message_begin (self->concrete_protocol,
+ name, message_type,
+ seqid, error);
+}
+
+gint32
+thrift_protocol_decorator_read_message_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_message_end (self->concrete_protocol,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_read_struct_begin (ThriftProtocol *protocol,
+ gchar **name,
+ GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_struct_begin (self->concrete_protocol,
+ name,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_read_struct_end (ThriftProtocol *protocol, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_struct_end (self->concrete_protocol,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_read_field_begin (ThriftProtocol *protocol,
+ gchar **name,
+ ThriftType *field_type,
+ gint16 *field_id,
+ GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_field_begin (self->concrete_protocol,
+ name,
+ field_type,
+ field_id,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_read_field_end (ThriftProtocol *protocol,
+ GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_field_end (self->concrete_protocol,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_read_map_begin (ThriftProtocol *protocol,
+ ThriftType *key_type,
+ ThriftType *value_type, guint32 *size,
+ GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_map_begin (self->concrete_protocol,
+ key_type,
+ value_type,
+ size,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_read_map_end (ThriftProtocol *protocol, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_map_end (self->concrete_protocol,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_read_list_begin (ThriftProtocol *protocol,
+ ThriftType *element_type,
+ guint32 *size, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_list_begin (self->concrete_protocol,
+ element_type,
+ size, error);
+}
+
+gint32
+thrift_protocol_decorator_read_list_end (ThriftProtocol *protocol, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_list_end (self->concrete_protocol,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_read_set_begin (ThriftProtocol *protocol,
+ ThriftType *element_type,
+ guint32 *size, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_set_begin (self->concrete_protocol,
+ element_type,
+ size, error);
+}
+
+gint32
+thrift_protocol_decorator_read_set_end (ThriftProtocol *protocol, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_set_end (self->concrete_protocol,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_read_bool (ThriftProtocol *protocol, gboolean *value,
+ GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_bool (self->concrete_protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_read_byte (ThriftProtocol *protocol, gint8 *value,
+ GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_byte (self->concrete_protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_read_i16 (ThriftProtocol *protocol, gint16 *value,
+ GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_i16 (self->concrete_protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_read_i32 (ThriftProtocol *protocol, gint32 *value,
+ GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_i32 (self->concrete_protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_read_i64 (ThriftProtocol *protocol, gint64 *value,
+ GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_i64 (self->concrete_protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_read_double (ThriftProtocol *protocol,
+ gdouble *value, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_double (self->concrete_protocol, value,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_read_string (ThriftProtocol *protocol,
+ gchar **str, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_string (self->concrete_protocol, str,
+ error);
+}
+
+gint32
+thrift_protocol_decorator_read_binary (ThriftProtocol *protocol, gpointer *buf,
+ guint32 *len, GError **error)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (protocol);
+
+ return THRIFT_PROTOCOL_GET_CLASS (self->concrete_protocol)->read_binary (self->concrete_protocol, buf,
+ len, error);
+}
+
+
+static void
+thrift_protocol_decorator_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (object);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_TYPE_PROTOCOL_DECORATOR_CONCRETE_PROTOCOL:
+ self->concrete_protocol = g_value_dup_object (value);
+ break;
+
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+thrift_protocol_decorator_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (object);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_TYPE_PROTOCOL_DECORATOR_CONCRETE_PROTOCOL:
+ g_value_set_object (value, self->concrete_protocol);
+ break;
+
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+
+ThriftProtocol *
+thrift_protocol_decorator_get_concrete_protocol(ThriftProtocolDecorator *protocol)
+{
+ ThriftProtocol *retval = NULL;
+ if(!THRIFT_IS_PROTOCOL_DECORATOR(protocol)){
+ g_warning("The type is not protocol decorator");
+ return NULL;
+ }
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR(protocol);
+ g_debug("Getting concrete protocol from %p -> %p", (void *)self, (void *)self->concrete_protocol);
+
+ return retval;
+}
+
+
+static void
+thrift_protocol_decorator_init (ThriftProtocolDecorator *protocol)
+{
+ protocol->concrete_protocol = NULL;
+}
+
+static void
+thrift_protocol_decorator_dispose (GObject *gobject)
+{
+ ThriftProtocolDecorator *self = THRIFT_PROTOCOL_DECORATOR (gobject);
+
+ g_clear_object(&self->concrete_protocol);
+
+ /* Always chain up to the parent class; there is no need to check if
+ * the parent class implements the dispose() virtual function: it is
+ * always guaranteed to do so
+ */
+ G_OBJECT_CLASS (thrift_protocol_decorator_parent_class)->dispose(gobject);
+}
+
+/* initialize the class */
+static void
+thrift_protocol_decorator_class_init (ThriftProtocolDecoratorClass *klass)
+{
+ ThriftProtocolClass *cls = THRIFT_PROTOCOL_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->set_property = thrift_protocol_decorator_set_property;
+ object_class->get_property = thrift_protocol_decorator_get_property;
+ object_class->dispose = thrift_protocol_decorator_dispose;
+
+ thrift_protocol_decorator_obj_properties[PROP_THRIFT_TYPE_PROTOCOL_DECORATOR_CONCRETE_PROTOCOL] =
+ g_param_spec_object ("protocol",
+ "Protocol",
+ "Set the protocol to be implemented", THRIFT_TYPE_PROTOCOL,
+ (G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+
+ g_object_class_install_properties (object_class,
+ PROP_THRIFT_TYPE_PROTOCOL_DECORATOR_END,
+ thrift_protocol_decorator_obj_properties);
+
+ g_debug("Current decorator write_message_begin addr %p, new %p", cls->write_message_begin, thrift_protocol_decorator_write_message_begin);
+
+ cls->write_message_begin = thrift_protocol_decorator_write_message_begin;
+ cls->write_message_end = thrift_protocol_decorator_write_message_end;
+ cls->write_struct_begin = thrift_protocol_decorator_write_struct_begin;
+ cls->write_struct_end = thrift_protocol_decorator_write_struct_end;
+ cls->write_field_begin = thrift_protocol_decorator_write_field_begin;
+ cls->write_field_end = thrift_protocol_decorator_write_field_end;
+ cls->write_field_stop = thrift_protocol_decorator_write_field_stop;
+ cls->write_map_begin = thrift_protocol_decorator_write_map_begin;
+ cls->write_map_end = thrift_protocol_decorator_write_map_end;
+ cls->write_list_begin = thrift_protocol_decorator_write_list_begin;
+ cls->write_list_end = thrift_protocol_decorator_write_list_end;
+ cls->write_set_begin = thrift_protocol_decorator_write_set_begin;
+ cls->write_set_end = thrift_protocol_decorator_write_set_end;
+ cls->write_bool = thrift_protocol_decorator_write_bool;
+ cls->write_byte = thrift_protocol_decorator_write_byte;
+ cls->write_i16 = thrift_protocol_decorator_write_i16;
+ cls->write_i32 = thrift_protocol_decorator_write_i32;
+ cls->write_i64 = thrift_protocol_decorator_write_i64;
+ cls->write_double = thrift_protocol_decorator_write_double;
+ cls->write_string = thrift_protocol_decorator_write_string;
+ cls->write_binary = thrift_protocol_decorator_write_binary;
+ cls->read_message_begin = thrift_protocol_decorator_read_message_begin;
+ cls->read_message_end = thrift_protocol_decorator_read_message_end;
+ cls->read_struct_begin = thrift_protocol_decorator_read_struct_begin;
+ cls->read_struct_end = thrift_protocol_decorator_read_struct_end;
+ cls->read_field_begin = thrift_protocol_decorator_read_field_begin;
+ cls->read_field_end = thrift_protocol_decorator_read_field_end;
+ cls->read_map_begin = thrift_protocol_decorator_read_map_begin;
+ cls->read_map_end = thrift_protocol_decorator_read_map_end;
+ cls->read_list_begin = thrift_protocol_decorator_read_list_begin;
+ cls->read_list_end = thrift_protocol_decorator_read_list_end;
+ cls->read_set_begin = thrift_protocol_decorator_read_set_begin;
+ cls->read_set_end = thrift_protocol_decorator_read_set_end;
+ cls->read_bool = thrift_protocol_decorator_read_bool;
+ cls->read_byte = thrift_protocol_decorator_read_byte;
+ cls->read_i16 = thrift_protocol_decorator_read_i16;
+ cls->read_i32 = thrift_protocol_decorator_read_i32;
+ cls->read_i64 = thrift_protocol_decorator_read_i64;
+ cls->read_double = thrift_protocol_decorator_read_double;
+ cls->read_string = thrift_protocol_decorator_read_string;
+ cls->read_binary = thrift_protocol_decorator_read_binary;
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_decorator.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_decorator.h
new file mode 100644
index 000000000..13b6af238
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_decorator.h
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_PROTOCOL_DECORATOR_H
+#define _THRIFT_PROTOCOL_DECORATOR_H
+
+#include <glib-object.h>
+
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/transport/thrift_transport.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_protocol_decorator.h
+ * \brief Multiplexed protocol implementation of a Thrift protocol. Implements the
+ * ThriftProtocol interface.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_PROTOCOL_DECORATOR (thrift_protocol_decorator_get_type ())
+#define THRIFT_PROTOCOL_DECORATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_PROTOCOL_DECORATOR, ThriftProtocolDecorator))
+#define THRIFT_IS_PROTOCOL_DECORATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_PROTOCOL_DECORATOR))
+#define THRIFT_PROTOCOL_DECORATOR_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_PROTOCOL_DECORATOR, ThriftProtocolDecoratorClass))
+#define THRIFT_IS_PROTOCOL_DECORATOR_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_PROTOCOL_DECORATOR))
+#define THRIFT_PROTOCOL_DECORATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_PROTOCOL_DECORATOR, ThriftProtocolDecoratorClass))
+
+typedef struct _ThriftProtocolDecorator ThriftProtocolDecorator;
+
+
+/*!
+ * Thrift Protocol Decorator instance.
+ */
+struct _ThriftProtocolDecorator
+{
+ ThriftProtocol parent;
+
+ ThriftProtocol *concrete_protocol;
+};
+
+typedef struct _ThriftProtocolDecoratorClass ThriftProtocolDecoratorClass;
+
+/*!
+ * Thrift Protocol Decorator class.
+ */
+struct _ThriftProtocolDecoratorClass
+{
+ ThriftProtocolClass parent;
+
+};
+
+/* used by THRIFT_TYPE_PROTOCOL_DECORATOR */
+GType thrift_protocol_decorator_get_type (void);
+
+G_END_DECLS
+
+#endif /* _THRIFT_PROTOCOL_DECORATOR_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_factory.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_factory.c
new file mode 100644
index 000000000..bef40876f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_factory.c
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/protocol/thrift_protocol_factory.h>
+
+G_DEFINE_ABSTRACT_TYPE(ThriftProtocolFactory, thrift_protocol_factory, G_TYPE_OBJECT)
+
+ThriftProtocol *
+thrift_protocol_factory_get_protocol(ThriftProtocolFactory *factory,
+ ThriftTransport *transport)
+{
+ return THRIFT_PROTOCOL_FACTORY_GET_CLASS (factory)->get_protocol (factory,
+ transport);
+}
+
+static void
+thrift_protocol_factory_init (ThriftProtocolFactory *factory)
+{
+ THRIFT_UNUSED_VAR (factory);
+}
+
+static void
+thrift_protocol_factory_class_init (ThriftProtocolFactoryClass *cls)
+{
+ cls->get_protocol = thrift_protocol_factory_get_protocol;
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_factory.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_factory.h
new file mode 100644
index 000000000..5f146dde3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_factory.h
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_PROTOCOL_FACTORY_H
+#define _THRIFT_PROTOCOL_FACTORY_H
+
+#include <glib-object.h>
+
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_protocol_factory.h
+ * \brief Abstract class for Thrift protocol factory implementations.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_PROTOCOL_FACTORY (thrift_protocol_factory_get_type ())
+#define THRIFT_PROTOCOL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_PROTOCOL_FACTORY, ThriftProtocolFactory))
+#define THRIFT_IS_PROTOCOL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_PROTOCOL_FACTORY))
+#define THRIFT_PROTOCOL_FACTORY_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_PROTOCOL_FACTORY, ThriftProtocolFactoryClass))
+#define THRIFT_IS_PROTOCOL_FACTORY_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_PROTOCOL_FACTORY))
+#define THRIFT_PROTOCOL_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_PROTOCOL_FACTORY, ThriftProtocolFactoryClass))
+
+typedef struct _ThriftProtocolFactory ThriftProtocolFactory;
+
+/*!
+ * Thrift Protocol Factory object
+ */
+struct _ThriftProtocolFactory
+{
+ GObject parent;
+};
+
+typedef struct _ThriftProtocolFactoryClass ThriftProtocolFactoryClass;
+
+/*!
+ * Thrift Protocol Factory class
+ */
+struct _ThriftProtocolFactoryClass
+{
+ GObjectClass parent;
+
+ ThriftProtocol *(*get_protocol) (ThriftProtocolFactory *factory,
+ ThriftTransport *transport);
+};
+
+/* used by THRIFT_TYPE_PROTOCOL_FACTORY */
+GType thrift_protocol_factory_get_type (void);
+
+/* virtual public methods */
+ThriftProtocol *thrift_protocol_factory_get_protocol(ThriftProtocolFactory *factory, ThriftTransport *transport);
+
+G_END_DECLS
+
+#endif /* _THRIFT_PROTOCOL_FACTORY_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_stored_message_protocol.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_stored_message_protocol.c
new file mode 100644
index 000000000..22aca8a29
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_stored_message_protocol.c
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/protocol/thrift_protocol_decorator.h>
+#include <thrift/c_glib/protocol/thrift_stored_message_protocol.h>
+
+
+enum
+{
+ PROP_THRIFT_STORED_MESSAGE_PROTOCOL_MESSAGE_NAME = 1,
+ PROP_THRIFT_STORED_MESSAGE_PROTOCOL_MESSAGE_TYPE,
+ PROP_THRIFT_STORED_MESSAGE_PROTOCOL_SEQUENCE_ID,
+ PROP_THRIFT_STORED_MESSAGE_PROTOCOL_TRANSPORT, /* TODO ugly hack */
+ PROP_THRIFT_STORED_MESSAGE_PROTOCOL_END
+};
+
+G_DEFINE_TYPE(ThriftStoredMessageProtocol, thrift_stored_message_protocol, THRIFT_TYPE_PROTOCOL_DECORATOR)
+
+
+static GParamSpec *thrift_stored_message_protocol_obj_properties[PROP_THRIFT_STORED_MESSAGE_PROTOCOL_END] = { NULL, };
+
+gint32
+thrift_stored_message_protocol_read_message_begin (ThriftProtocol *protocol,
+ gchar **name,
+ ThriftMessageType *message_type,
+ gint32 *seqid, GError **error)
+{
+ gint32 ret = 0;
+ g_return_val_if_fail (THRIFT_IS_STORED_MESSAGE_PROTOCOL (protocol), -1);
+ THRIFT_UNUSED_VAR (error);
+
+ ThriftStoredMessageProtocol *self = THRIFT_STORED_MESSAGE_PROTOCOL (protocol);
+
+ /* We return the stored values on construction */
+ *name = self->name;
+ *message_type = self->mtype;
+ *seqid = self->seqid;
+
+ return ret;
+}
+
+
+static void
+thrift_stored_message_protocol_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ThriftStoredMessageProtocol *self = THRIFT_STORED_MESSAGE_PROTOCOL (object);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_STORED_MESSAGE_PROTOCOL_MESSAGE_NAME:
+ self->name = g_value_dup_string (value);
+ break;
+ case PROP_THRIFT_STORED_MESSAGE_PROTOCOL_MESSAGE_TYPE:
+ self->mtype = g_value_get_int (value);
+ break;
+ case PROP_THRIFT_STORED_MESSAGE_PROTOCOL_SEQUENCE_ID:
+ self->seqid = g_value_get_int (value);
+ break;
+
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+thrift_stored_message_protocol_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ThriftStoredMessageProtocol *self = THRIFT_STORED_MESSAGE_PROTOCOL (object);
+ ThriftProtocolDecorator *decorator = THRIFT_PROTOCOL_DECORATOR (object);
+ ThriftTransport *transport=NULL;
+ switch (property_id)
+ {
+ case PROP_THRIFT_STORED_MESSAGE_PROTOCOL_MESSAGE_NAME:
+ g_value_set_string (value, self->name);
+ break;
+ case PROP_THRIFT_STORED_MESSAGE_PROTOCOL_TRANSPORT:
+ /* FIXME Since we don't override properties in the decorator as it should
+ we just override the properties that we know are used */
+ g_object_get(decorator->concrete_protocol,pspec->name, &transport, NULL);
+ g_value_set_pointer (value, transport);
+ break;
+
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+
+static void
+thrift_stored_message_protocol_init (ThriftStoredMessageProtocol *protocol)
+{
+ protocol->name = NULL;
+}
+
+static void
+thrift_stored_message_protocol_finalize (GObject *gobject)
+{
+ ThriftStoredMessageProtocol *self = THRIFT_STORED_MESSAGE_PROTOCOL (gobject);
+
+ if (self->name) {
+ g_free(self->name);
+ self->name = NULL;
+ }
+
+ /* Always chain up to the parent class; as with dispose(), finalize()
+ * is guaranteed to exist on the parent's class virtual function table
+ */
+ G_OBJECT_CLASS (thrift_stored_message_protocol_parent_class)->finalize(gobject);
+}
+
+
+/* initialize the class */
+static void
+thrift_stored_message_protocol_class_init (ThriftStoredMessageProtocolClass *klass)
+{
+ ThriftProtocolClass *cls = THRIFT_PROTOCOL_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ cls->read_message_begin = thrift_stored_message_protocol_read_message_begin;
+
+ object_class->set_property = thrift_stored_message_protocol_set_property;
+ object_class->get_property = thrift_stored_message_protocol_get_property;
+ object_class->finalize = thrift_stored_message_protocol_finalize;
+
+ thrift_stored_message_protocol_obj_properties[PROP_THRIFT_STORED_MESSAGE_PROTOCOL_MESSAGE_NAME] =
+ g_param_spec_string ("name",
+ "Service name the protocol points to",
+ "Set the service name",
+ NULL,
+ (G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ thrift_stored_message_protocol_obj_properties[PROP_THRIFT_STORED_MESSAGE_PROTOCOL_MESSAGE_TYPE] =
+ g_param_spec_int ("type",
+ "Message type in the wire",
+ "Set the message type in the wire",
+ T_CALL, T_ONEWAY,
+ T_CALL,
+ (G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ thrift_stored_message_protocol_obj_properties[PROP_THRIFT_STORED_MESSAGE_PROTOCOL_SEQUENCE_ID] =
+ g_param_spec_int ("seqid",
+ "Sequence id type in the wire",
+ "Set the Sequence id in the wire",
+ G_MININT, G_MAXINT,
+ 0,
+ (G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+
+ /* TODO Ugly hack, in theory we must override all properties from underlaying
+ protocol */
+ thrift_stored_message_protocol_obj_properties[PROP_THRIFT_STORED_MESSAGE_PROTOCOL_TRANSPORT] =
+ g_param_spec_pointer ("transport",
+ "Transport on the underlaying implementation",
+ "Transport of decorated protocol",
+ G_PARAM_READABLE);
+
+
+
+ g_object_class_install_properties (object_class,
+ PROP_THRIFT_STORED_MESSAGE_PROTOCOL_END,
+ thrift_stored_message_protocol_obj_properties);
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_stored_message_protocol.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_stored_message_protocol.h
new file mode 100644
index 000000000..88782ac36
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/protocol/thrift_stored_message_protocol.h
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_STORED_MESSAGE_PROTOCOL_H
+#define _THRIFT_STORED_MESSAGE_PROTOCOL_H
+
+#include <glib-object.h>
+
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/protocol/thrift_protocol_decorator.h>
+#include <thrift/c_glib/transport/thrift_transport.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_stored_message_protocol.h
+ * \brief StoredMessage protocol implementation of a pre-stored message header
+ * on Thrift protocol. Implements the ThriftProtocol interface.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_STORED_MESSAGE_PROTOCOL (thrift_stored_message_protocol_get_type ())
+#define THRIFT_STORED_MESSAGE_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_STORED_MESSAGE_PROTOCOL, ThriftStoredMessageProtocol))
+#define THRIFT_IS_STORED_MESSAGE_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_STORED_MESSAGE_PROTOCOL))
+#define THRIFT_STORED_MESSAGE_PROTOCOL_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_STORED_MESSAGE_PROTOCOL, ThriftStoredMessageProtocolClass))
+#define THRIFT_IS_STORED_MESSAGE_PROTOCOL_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_STORED_MESSAGE_PROTOCOL))
+#define THRIFT_STORED_MESSAGE_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_STORED_MESSAGE_PROTOCOL, ThriftStoredMessageProtocolClass))
+
+/* constant */
+#define THRIFT_STORED_MESSAGE_PROTOCOL_DEFAULT_SEPARATOR ":"
+
+typedef struct _ThriftStoredMessageProtocol ThriftStoredMessageProtocol;
+
+
+
+/*!
+ * Thrift StoredMessage Protocol instance.
+ */
+struct _ThriftStoredMessageProtocol
+{
+ ThriftProtocolDecorator parent;
+
+ gchar *name;
+ ThriftMessageType mtype;
+ gint32 seqid;
+};
+
+typedef struct _ThriftStoredMessageProtocolClass ThriftStoredMessageProtocolClass;
+
+/*!
+ * Thrift StoredMessage Protocol class.
+ */
+struct _ThriftStoredMessageProtocolClass
+{
+ ThriftProtocolDecoratorClass parent;
+};
+
+/* used by THRIFT_TYPE_STORED_MESSAGE_PROTOCOL */
+GType thrift_stored_message_protocol_get_type (void);
+
+G_END_DECLS
+
+#endif /* _THRIFT_STORED_MESSAGE_PROTOCOL_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/server/thrift_server.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/server/thrift_server.c
new file mode 100644
index 000000000..ccf9153db
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/server/thrift_server.c
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/c_glib/thrift.h>
+#include "thrift_server.h"
+
+/* object properties */
+enum _ThriftServerProperties
+{
+ PROP_0,
+ PROP_THRIFT_SERVER_PROCESSOR,
+ PROP_THRIFT_SERVER_SERVER_TRANSPORT,
+ PROP_THRIFT_SERVER_INPUT_TRANSPORT_FACTORY,
+ PROP_THRIFT_SERVER_OUTPUT_TRANSPORT_FACTORY,
+ PROP_THRIFT_SERVER_INPUT_PROTOCOL_FACTORY,
+ PROP_THRIFT_SERVER_OUTPUT_PROTOCOL_FACTORY
+};
+
+G_DEFINE_ABSTRACT_TYPE(ThriftServer, thrift_server, G_TYPE_OBJECT)
+
+void
+thrift_server_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ ThriftServer *server = THRIFT_SERVER (object);
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_SERVER_PROCESSOR:
+ g_value_set_object (value, server->processor);
+ break;
+ case PROP_THRIFT_SERVER_SERVER_TRANSPORT:
+ g_value_set_object (value, server->server_transport);
+ break;
+ case PROP_THRIFT_SERVER_INPUT_TRANSPORT_FACTORY:
+ g_value_set_object (value, server->input_transport_factory);
+ break;
+ case PROP_THRIFT_SERVER_OUTPUT_TRANSPORT_FACTORY:
+ g_value_set_object (value, server->output_transport_factory);
+ break;
+ case PROP_THRIFT_SERVER_INPUT_PROTOCOL_FACTORY:
+ g_value_set_object (value, server->input_protocol_factory);
+ break;
+ case PROP_THRIFT_SERVER_OUTPUT_PROTOCOL_FACTORY:
+ g_value_set_object (value, server->output_protocol_factory);
+ break;
+ }
+}
+
+void
+thrift_server_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ ThriftServer *server = THRIFT_SERVER (object);
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_SERVER_PROCESSOR:
+ server->processor = g_value_dup_object (value);
+ break;
+ case PROP_THRIFT_SERVER_SERVER_TRANSPORT:
+ server->server_transport = g_value_dup_object (value);
+ break;
+ case PROP_THRIFT_SERVER_INPUT_TRANSPORT_FACTORY:
+ server->input_transport_factory = g_value_dup_object (value);
+ break;
+ case PROP_THRIFT_SERVER_OUTPUT_TRANSPORT_FACTORY:
+ server->output_transport_factory = g_value_dup_object (value);
+ break;
+ case PROP_THRIFT_SERVER_INPUT_PROTOCOL_FACTORY:
+ server->input_protocol_factory = g_value_dup_object (value);
+ break;
+ case PROP_THRIFT_SERVER_OUTPUT_PROTOCOL_FACTORY:
+ server->output_protocol_factory = g_value_dup_object (value);
+ break;
+ }
+}
+
+gboolean
+thrift_server_serve (ThriftServer *server, GError **error)
+{
+ return THRIFT_SERVER_GET_CLASS (server)->serve (server, error);
+}
+
+void
+thrift_server_stop (ThriftServer *server)
+{
+ THRIFT_SERVER_GET_CLASS (server)->stop (server);
+}
+
+/* instance initializer for Thrift Server */
+static void
+thrift_server_init (ThriftServer *server)
+{
+ server->processor = NULL;
+ server->server_transport = NULL;
+ server->input_transport_factory = NULL;
+ server->output_transport_factory = NULL;
+ server->input_protocol_factory = NULL;
+ server->output_protocol_factory = NULL;
+}
+
+static void
+thrift_server_dispose (GObject *gobject)
+{
+ ThriftServer *self = THRIFT_SERVER (gobject);
+
+ g_clear_object(&self->output_protocol_factory);
+ g_clear_object(&self->input_protocol_factory);
+ g_clear_object(&self->output_transport_factory);
+ g_clear_object(&self->input_transport_factory);
+ g_clear_object(&self->server_transport);
+ g_clear_object(&self->processor);
+
+ /* Always chain up to the parent class; there is no need to check if
+ * the parent class implements the dispose() virtual function: it is
+ * always guaranteed to do so
+ */
+ G_OBJECT_CLASS (thrift_server_parent_class)->dispose(gobject);
+}
+
+/*
+ * class initializer for ThriftServer
+ * TODO: implement ServerEventHandler as a GClosure
+ */
+static void
+thrift_server_class_init (ThriftServerClass *cls)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (cls);
+
+ gobject_class->get_property = thrift_server_get_property;
+ gobject_class->set_property = thrift_server_set_property;
+ gobject_class->dispose = thrift_server_dispose;
+
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_SERVER_PROCESSOR,
+ g_param_spec_object ("processor", "Processor", "Thrift Processor",
+ THRIFT_TYPE_PROCESSOR,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_SERVER_SERVER_TRANSPORT,
+ g_param_spec_object ("server_transport", "Server Transport",
+ "Thrift Server Transport",
+ THRIFT_TYPE_SERVER_TRANSPORT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_SERVER_INPUT_TRANSPORT_FACTORY,
+ g_param_spec_object ("input_transport_factory", "Input Transport Factory",
+ "Thrift Server Input Transport Factory",
+ THRIFT_TYPE_TRANSPORT_FACTORY,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_SERVER_OUTPUT_TRANSPORT_FACTORY,
+ g_param_spec_object ("output_transport_factory",
+ "Output Transport Factory",
+ "Thrift Server Output Transport Factory",
+ THRIFT_TYPE_TRANSPORT_FACTORY,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_SERVER_INPUT_PROTOCOL_FACTORY,
+ g_param_spec_object ("input_protocol_factory", "Input Protocol Factory",
+ "Thrift Server Input Protocol Factory",
+ THRIFT_TYPE_PROTOCOL_FACTORY,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_SERVER_OUTPUT_PROTOCOL_FACTORY,
+ g_param_spec_object ("output_protocol_factory", "Output Protocol Factory",
+ "Thrift Server Output Protocol Factory",
+ THRIFT_TYPE_PROTOCOL_FACTORY,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /* set these as virtual methods to be implemented by a subclass */
+ cls->serve = thrift_server_serve;
+ cls->stop = thrift_server_stop;
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/server/thrift_server.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/server/thrift_server.h
new file mode 100644
index 000000000..49beddcbe
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/server/thrift_server.h
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_SERVER_H
+#define _THRIFT_SERVER_H
+
+#include <glib-object.h>
+
+#include <thrift/c_glib/processor/thrift_processor.h>
+#include <thrift/c_glib/transport/thrift_server_transport.h>
+#include <thrift/c_glib/transport/thrift_transport_factory.h>
+#include <thrift/c_glib/protocol/thrift_protocol_factory.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_server.h
+ * \brief Abstract class for Thrift servers.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_SERVER (thrift_server_get_type ())
+#define THRIFT_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_SERVER, ThriftServer))
+#define THRIFT_IS_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_SERVER))
+#define THRIFT_SERVER_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_SERVER, ThriftServerClass))
+#define THRIFT_IS_SERVER_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_SERVER))
+#define THRIFT_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_SERVER, ThriftServerClass))
+
+typedef struct _ThriftServer ThriftServer;
+
+/*!
+ * Thrift Server object
+ */
+struct _ThriftServer
+{
+ GObject parent;
+
+ /* protected */
+ ThriftProcessor *processor;
+ ThriftServerTransport *server_transport;
+ ThriftTransportFactory *input_transport_factory;
+ ThriftTransportFactory *output_transport_factory;
+ ThriftProtocolFactory *input_protocol_factory;
+ ThriftProtocolFactory *output_protocol_factory;
+};
+
+typedef struct _ThriftServerClass ThriftServerClass;
+
+/*!
+ * Thrift Server class
+ */
+struct _ThriftServerClass
+{
+ GObjectClass parent;
+
+ /* vtable */
+ gboolean (*serve) (ThriftServer *server, GError **error);
+ void (*stop) (ThriftServer *server);
+};
+
+/* used by THRIFT_TYPE_SERVER */
+GType thrift_server_get_type (void);
+
+/*!
+ * Processes the request.
+ * \public \memberof ThriftServerClass
+ */
+gboolean thrift_server_serve (ThriftServer *server, GError **error);
+
+/*!
+ * Stop handling requests.
+ */
+void thrift_server_stop (ThriftServer *server);
+
+G_END_DECLS
+
+#endif /* _THRIFT_SERVER_H */
+
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/server/thrift_simple_server.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/server/thrift_simple_server.c
new file mode 100644
index 000000000..22a96c7a4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/server/thrift_simple_server.c
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/c_glib/server/thrift_simple_server.h>
+#include <thrift/c_glib/transport/thrift_transport_factory.h>
+#include <thrift/c_glib/protocol/thrift_protocol_factory.h>
+#include <thrift/c_glib/protocol/thrift_binary_protocol_factory.h>
+
+G_DEFINE_TYPE(ThriftSimpleServer, thrift_simple_server, THRIFT_TYPE_SERVER)
+
+gboolean
+thrift_simple_server_serve (ThriftServer *server, GError **error)
+{
+ ThriftTransport *t = NULL;
+ ThriftTransport *input_transport = NULL, *output_transport = NULL;
+ ThriftProtocol *input_protocol = NULL, *output_protocol = NULL;
+ ThriftSimpleServer *tss = THRIFT_SIMPLE_SERVER(server);
+ GError *process_error = NULL;
+
+ g_return_val_if_fail (THRIFT_IS_SIMPLE_SERVER (server), FALSE);
+
+ if (thrift_server_transport_listen (server->server_transport, error)) {
+ tss->running = TRUE;
+ while (tss->running == TRUE)
+ {
+ t = thrift_server_transport_accept (server->server_transport,
+ error);
+ if (t != NULL && tss->running) {
+ input_transport =
+ THRIFT_TRANSPORT_FACTORY_GET_CLASS (server->input_transport_factory)
+ ->get_transport (server->input_transport_factory, t);
+ output_transport =
+ THRIFT_TRANSPORT_FACTORY_GET_CLASS (server->output_transport_factory)
+ ->get_transport (server->output_transport_factory, t);
+ input_protocol =
+ THRIFT_PROTOCOL_FACTORY_GET_CLASS (server->input_protocol_factory)
+ ->get_protocol (server->input_protocol_factory, input_transport);
+ output_protocol =
+ THRIFT_PROTOCOL_FACTORY_GET_CLASS (server->output_protocol_factory)
+ ->get_protocol (server->output_protocol_factory, output_transport);
+
+ while (THRIFT_PROCESSOR_GET_CLASS (server->processor)
+ ->process (server->processor,
+ input_protocol,
+ output_protocol,
+ &process_error) &&
+ thrift_transport_peek (input_transport, &process_error))
+ {
+ }
+
+ if (process_error != NULL)
+ {
+ g_message ("thrift_simple_server_serve: %s", process_error->message);
+ g_clear_error (&process_error);
+
+ /* Note we do not propagate processing errors to the caller as they
+ * normally are transient and not fatal to the server */
+ }
+
+ /* TODO: handle exceptions */
+ THRIFT_TRANSPORT_GET_CLASS (input_transport)->close (input_transport,
+ NULL);
+ THRIFT_TRANSPORT_GET_CLASS (output_transport)->close (output_transport,
+ NULL);
+ }
+ }
+
+ /* attempt to shutdown */
+ THRIFT_SERVER_TRANSPORT_GET_CLASS (server->server_transport)
+ ->close (server->server_transport, NULL);
+ }
+
+ /* Since this method is designed to run forever, it can only ever return on
+ * error */
+ return FALSE;
+}
+
+void
+thrift_simple_server_stop (ThriftServer *server)
+{
+ g_return_if_fail (THRIFT_IS_SIMPLE_SERVER (server));
+ (THRIFT_SIMPLE_SERVER (server))->running = FALSE;
+}
+
+static void
+thrift_simple_server_init (ThriftSimpleServer *tss)
+{
+ ThriftServer *server = THRIFT_SERVER(tss);
+
+ tss->running = FALSE;
+
+ if (server->input_transport_factory == NULL)
+ {
+ server->input_transport_factory =
+ g_object_new (THRIFT_TYPE_TRANSPORT_FACTORY, NULL);
+ }
+ if (server->output_transport_factory == NULL)
+ {
+ server->output_transport_factory =
+ g_object_new (THRIFT_TYPE_TRANSPORT_FACTORY, NULL);
+ }
+ if (server->input_protocol_factory == NULL)
+ {
+ server->input_protocol_factory =
+ g_object_new (THRIFT_TYPE_BINARY_PROTOCOL_FACTORY, NULL);
+ }
+ if (server->output_protocol_factory == NULL)
+ {
+ server->output_protocol_factory =
+ g_object_new (THRIFT_TYPE_BINARY_PROTOCOL_FACTORY, NULL);
+ }
+}
+
+/* initialize the class */
+static void
+thrift_simple_server_class_init (ThriftSimpleServerClass *class)
+{
+ ThriftServerClass *cls = THRIFT_SERVER_CLASS(class);
+
+ cls->serve = thrift_simple_server_serve;
+ cls->stop = thrift_simple_server_stop;
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/server/thrift_simple_server.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/server/thrift_simple_server.h
new file mode 100644
index 000000000..86b538b48
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/server/thrift_simple_server.h
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_SIMPLE_SERVER_H
+#define _THRIFT_SIMPLE_SERVER_H
+
+#include <glib-object.h>
+
+#include <thrift/c_glib/server/thrift_server.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_simple_server.h
+ * \brief A simple Thrift server, single-threaded.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_SIMPLE_SERVER (thrift_simple_server_get_type ())
+#define THRIFT_SIMPLE_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_SIMPLE_SERVER, ThriftSimpleServer))
+#define THRIFT_IS_SIMPLE_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_SIMPLE_SERVER))
+#define THRIFT_SIMPLE_SERVER_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c) THRIFT_TYPE_SIMPLE_SERVER, ThriftSimpleServerClass))
+#define THRIFT_IS_SIMPLE_SERVER_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_SIMPLE_SERVER))
+#define THRIFT_SIMPLE_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_SIMPLE_SERVER, ThriftSimpleServerClass))
+
+typedef struct _ThriftSimpleServer ThriftSimpleServer;
+
+/**
+ * Thrift Simple Server instance.
+ */
+struct _ThriftSimpleServer
+{
+ ThriftServer parent;
+
+ /* private */
+ volatile gboolean running;
+};
+
+typedef struct _ThriftSimpleServerClass ThriftSimpleServerClass;
+
+/**
+ * Thrift Simple Server class.
+ */
+struct _ThriftSimpleServerClass
+{
+ ThriftServerClass parent;
+};
+
+/* used by THRIFT_TYPE_SIMPLE_SERVER */
+GType thrift_simple_server_get_type (void);
+
+G_END_DECLS
+
+#endif /* _THRIFT_SIMPLE_SERVER_H */
+
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift.c
new file mode 100644
index 000000000..8de869f79
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift.c
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/c_glib/thrift.h>
+
+/**
+ * GHashTable callback to add keys to a GList.
+ */
+void
+thrift_hash_table_get_keys (gpointer key, gpointer value, gpointer user_data)
+{
+ GList **list = (GList **) user_data;
+
+ THRIFT_UNUSED_VAR (value);
+
+ *list = g_list_append (*list, key);
+}
+void thrift_safe_hash_table_destroy(GHashTable* hash_table)
+{
+ if (hash_table)
+ {
+ g_hash_table_destroy(hash_table);
+ }
+}
+
+guint thrift_boolean_hash(gconstpointer v)
+{
+ const gboolean* p = v;
+ return p && *p ? 1 : 0;
+}
+gboolean thrift_boolean_equal(gconstpointer a, gconstpointer b)
+{
+ if (a == b) {
+ return TRUE;
+ }
+ if (!a || !b) {
+ return FALSE;
+ }
+ const gboolean* pa = a;
+ const gboolean* pb = b;
+ return *pa == *pb;
+}
+
+guint thrift_int8_hash(gconstpointer v)
+{
+ const gint8* p = v;
+ return p ? *p : 0;
+}
+gboolean thrift_int8_equal(gconstpointer a, gconstpointer b)
+{
+ if (a == b) {
+ return TRUE;
+ }
+ if (!a || !b) {
+ return FALSE;
+ }
+ const gint8* pa = a;
+ const gint8* pb = b;
+ return *pa == *pb;
+}
+
+guint thrift_int16_hash(gconstpointer v)
+{
+ const gint16* p = v;
+ return p ? *p : 0;
+}
+gboolean thrift_int16_equal(gconstpointer a, gconstpointer b)
+{
+ if (a == b) {
+ return TRUE;
+ }
+ if (!a || !b) {
+ return FALSE;
+ }
+ const gint16* pa = a;
+ const gint16* pb = b;
+ return *pa == *pb;
+}
+
+void
+thrift_string_free (gpointer str)
+{
+ GByteArray* ptr = str;
+ g_byte_array_unref(ptr);
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift.h
new file mode 100644
index 000000000..94a647831
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift.h
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_H
+#define _THRIFT_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+
+/* this macro is called to satisfy -Wall hardcore compilation */
+#ifndef THRIFT_UNUSED_VAR
+# define THRIFT_UNUSED_VAR(x) ((void) x)
+#endif
+
+void thrift_hash_table_get_keys (gpointer key, gpointer value,
+ gpointer user_data);
+void thrift_safe_hash_table_destroy(GHashTable* hash_table);
+
+guint thrift_boolean_hash(gconstpointer v);
+gboolean thrift_boolean_equal(gconstpointer a, gconstpointer b);
+
+guint thrift_int8_hash(gconstpointer v);
+gboolean thrift_int8_equal(gconstpointer a, gconstpointer b);
+
+guint thrift_int16_hash(gconstpointer v);
+gboolean thrift_int16_equal(gconstpointer a, gconstpointer b);
+
+void thrift_string_free (gpointer str);
+
+#endif /* #ifndef _THRIFT_THRIFT_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift_application_exception.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift_application_exception.c
new file mode 100644
index 000000000..1234caef9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift_application_exception.c
@@ -0,0 +1,277 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "thrift_application_exception.h"
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+
+/* object properties */
+enum _ThriftApplicationExceptionProperties
+{
+ PROP_0,
+ PROP_THRIFT_APPLICATION_EXCEPTION_TYPE,
+ PROP_THRIFT_APPLICATION_EXCEPTION_MESSAGE
+};
+
+G_DEFINE_TYPE(ThriftApplicationException, thrift_application_exception, THRIFT_TYPE_STRUCT)
+
+gint32
+thrift_application_exception_read (ThriftStruct *object,
+ ThriftProtocol *protocol, GError **error)
+{
+ gint32 ret;
+ gint32 xfer = 0;
+ gchar *name;
+ ThriftType ftype;
+ gint16 fid;
+ ThriftApplicationException *this = THRIFT_APPLICATION_EXCEPTION (object);
+
+ /* read the struct begin marker */
+ if ((ret = thrift_protocol_read_struct_begin (protocol, &name, error)) < 0)
+ {
+ if (name) g_free (name);
+ return -1;
+ }
+ xfer += ret;
+ if (name) g_free (name);
+
+ while (1)
+ {
+ if ((ret = thrift_protocol_read_field_begin (protocol, &name, &ftype,
+ &fid, error)) < 0)
+ {
+ if (name) g_free (name);
+ return -1;
+ }
+ xfer += ret;
+ if (name) g_free (name);
+
+ /* break if we get a STOP field */
+ if (ftype == T_STOP)
+ {
+ break;
+ }
+
+ switch (fid)
+ {
+ case 1:
+ if (ftype == T_STRING)
+ {
+ if ((ret = thrift_protocol_read_string (protocol, &this->message,
+ error)) < 0)
+ return -1;
+ xfer += ret;
+ this->__isset_message = TRUE;
+ } else {
+ if ((ret = thrift_protocol_skip (protocol, ftype, error)) < 0)
+ return -1;
+ xfer += ret;
+ }
+ break;
+ case 2:
+ if (ftype == T_I32)
+ {
+ if ((ret = thrift_protocol_read_i32 (protocol, &this->type,
+ error)) < 0)
+ return -1;
+ xfer += ret;
+ this->__isset_type = TRUE;
+ } else {
+ if ((ret = thrift_protocol_skip (protocol, ftype, error)) < 0)
+ return -1;
+ xfer += ret;
+ }
+ break;
+ default:
+ if ((ret = thrift_protocol_skip (protocol, ftype, error)) < 0)
+ return -1;
+ xfer += ret;
+ break;
+ }
+ if ((ret = thrift_protocol_read_field_end (protocol, error)) < 0)
+ return -1;
+ xfer += ret;
+ }
+
+ if ((ret = thrift_protocol_read_struct_end (protocol, error)) < 0)
+ return -1;
+ xfer += ret;
+
+ return xfer;
+}
+
+gint32
+thrift_application_exception_write (ThriftStruct *object,
+ ThriftProtocol *protocol, GError **error)
+{
+ gint32 ret;
+ gint32 xfer = 0;
+
+ ThriftApplicationException *this = THRIFT_APPLICATION_EXCEPTION (object);
+
+ if ((ret = thrift_protocol_write_struct_begin (protocol,
+ "TApplicationException",
+ error)) < 0)
+ return -1;
+ xfer += ret;
+ if ((ret = thrift_protocol_write_field_begin (protocol, "message",
+ T_STRING, 1, error)) < 0)
+ return -1;
+ xfer += ret;
+ if ((ret = thrift_protocol_write_string (protocol, this->message, error)) < 0)
+ return -1;
+ xfer += ret;
+ if ((ret = thrift_protocol_write_field_end (protocol, error)) < 0)
+ return -1;
+ xfer += ret;
+ if ((ret = thrift_protocol_write_field_begin (protocol, "type",
+ T_I32, 2, error)) < 0)
+ return -1;
+ xfer += ret;
+ if ((ret = thrift_protocol_write_i32 (protocol, this->type, error)) < 0)
+ return -1;
+ xfer += ret;
+ if ((ret = thrift_protocol_write_field_end (protocol, error)) < 0)
+ return -1;
+ xfer += ret;
+ if ((ret = thrift_protocol_write_field_stop (protocol, error)) < 0)
+ return -1;
+ xfer += ret;
+ if ((ret = thrift_protocol_write_struct_end (protocol, error)) < 0)
+ return -1;
+ xfer += ret;
+
+ return xfer;
+}
+
+
+/* GError domain */
+#define THRIFT_APPLICATION_EXCEPTION_ERROR_DOMAIN "thrift-application-exception-error-quark"
+
+GQuark
+thrift_application_exception_error_quark (void)
+{
+ return g_quark_from_static_string (THRIFT_APPLICATION_EXCEPTION_ERROR_DOMAIN);
+}
+
+static void
+thrift_application_exception_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ThriftApplicationException *tae = THRIFT_APPLICATION_EXCEPTION (object);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_APPLICATION_EXCEPTION_TYPE:
+ g_value_set_int (value, tae->type);
+ break;
+ case PROP_THRIFT_APPLICATION_EXCEPTION_MESSAGE:
+ g_value_set_string (value, tae->message);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+thrift_application_exception_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ThriftApplicationException *tae = THRIFT_APPLICATION_EXCEPTION (object);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_APPLICATION_EXCEPTION_TYPE:
+ tae->type = g_value_get_int (value);
+ tae->__isset_type = TRUE;
+ break;
+ case PROP_THRIFT_APPLICATION_EXCEPTION_MESSAGE:
+ if (tae->message != NULL)
+ g_free (tae->message);
+
+ tae->message = g_value_dup_string (value);
+ tae->__isset_message = TRUE;
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+void
+thrift_application_exception_init (ThriftApplicationException *object)
+{
+ object->type = 0;
+ object->__isset_type = FALSE;
+ object->message = NULL;
+ object->__isset_message = FALSE;
+}
+
+void
+thrift_application_exception_finalize (GObject *object)
+{
+ ThriftApplicationException *tae = THRIFT_APPLICATION_EXCEPTION (object);
+
+ if (tae->__isset_message) {
+ g_free(tae->message);
+ }
+}
+
+void
+thrift_application_exception_class_init (ThriftApplicationExceptionClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS(class);
+ ThriftStructClass *cls = THRIFT_STRUCT_CLASS(class);
+ GParamSpec *param_spec;
+
+ cls->read = thrift_application_exception_read;
+ cls->write = thrift_application_exception_write;
+
+ gobject_class->finalize = thrift_application_exception_finalize;
+ gobject_class->get_property = thrift_application_exception_get_property;
+ gobject_class->set_property = thrift_application_exception_set_property;
+
+ param_spec = g_param_spec_int ("type",
+ "Exception type",
+ "The type of the exception, one of the "
+ "values defined by the "
+ "ThriftApplicationExceptionError "
+ "enumeration.",
+ 0,
+ THRIFT_APPLICATION_EXCEPTION_ERROR_N - 1,
+ 0,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_APPLICATION_EXCEPTION_TYPE,
+ param_spec);
+
+ param_spec = g_param_spec_string ("message",
+ "Exception message",
+ "A string describing the exception that "
+ "occurred.",
+ NULL,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_APPLICATION_EXCEPTION_MESSAGE,
+ param_spec);
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift_application_exception.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift_application_exception.h
new file mode 100644
index 000000000..733f793cb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift_application_exception.h
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_APPLICATION_EXCEPTION_H
+#define _THRIFT_APPLICATION_EXCEPTION_H
+
+#include <glib-object.h>
+#include "thrift_struct.h"
+
+G_BEGIN_DECLS
+
+/*! \file thrift_application_exception.h
+ * \brief C Implementation of a TApplicationException.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_APPLICATION_EXCEPTION (thrift_application_exception_get_type ())
+#define THRIFT_APPLICATION_EXCEPTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_APPLICATION_EXCEPTION, ThriftApplicationException))
+#define THRIFT_IS_APPLICATION_EXCEPTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_APPLICATION_EXCEPTION))
+#define THRIFT_APPLICATION_EXCEPTION_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_APPLICATION_EXCEPTION, ThriftApplicationExceptionClass))
+#define THRIFT_IS_APPLICATION_EXCEPTION_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_APPLICATION_EXCEPTION))
+#define THRIFT_APPLICATION_EXCEPTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_APPLICATION_EXCEPTION, ThriftApplicationExceptionClass))
+
+typedef struct _ThriftApplicationException ThriftApplicationException;
+
+struct _ThriftApplicationException
+{
+ ThriftStruct parent;
+
+ /* private */
+ gint32 type;
+ gboolean __isset_type;
+ gchar *message;
+ gboolean __isset_message;
+};
+
+typedef struct _ThriftApplicationExceptionClass ThriftApplicationExceptionClass;
+
+struct _ThriftApplicationExceptionClass
+{
+ ThriftStructClass parent;
+};
+
+GType thrift_application_exception_get_type (void);
+
+/* gerror codes */
+typedef enum
+{
+ THRIFT_APPLICATION_EXCEPTION_ERROR_UNKNOWN,
+ THRIFT_APPLICATION_EXCEPTION_ERROR_UNKNOWN_METHOD,
+ THRIFT_APPLICATION_EXCEPTION_ERROR_INVALID_MESSAGE_TYPE,
+ THRIFT_APPLICATION_EXCEPTION_ERROR_WRONG_METHOD_NAME,
+ THRIFT_APPLICATION_EXCEPTION_ERROR_BAD_SEQUENCE_ID,
+ THRIFT_APPLICATION_EXCEPTION_ERROR_MISSING_RESULT,
+ THRIFT_APPLICATION_EXCEPTION_ERROR_INTERNAL_ERROR,
+ THRIFT_APPLICATION_EXCEPTION_ERROR_PROTOCOL_ERROR,
+ THRIFT_APPLICATION_EXCEPTION_ERROR_INVALID_TRANSFORM,
+ THRIFT_APPLICATION_EXCEPTION_ERROR_INVALID_PROTOCOL,
+ THRIFT_APPLICATION_EXCEPTION_ERROR_UNSUPPORTED_CLIENT_TYPE,
+
+ THRIFT_APPLICATION_EXCEPTION_ERROR_N
+} ThriftApplicationExceptionError;
+
+/* define error domain for GError */
+GQuark thrift_application_exception_error_quark (void);
+#define THRIFT_APPLICATION_EXCEPTION_ERROR (thrift_application_exception_error_quark ())
+
+G_END_DECLS
+
+#endif /* _THRIFT_APPLICATION_EXCEPTION_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift_struct.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift_struct.c
new file mode 100644
index 000000000..f24f2a1c8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift_struct.c
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/c_glib/thrift.h>
+#include "thrift_struct.h"
+
+G_DEFINE_ABSTRACT_TYPE(ThriftStruct, thrift_struct, G_TYPE_OBJECT)
+
+gint32
+thrift_struct_read (ThriftStruct *object, ThriftProtocol *protocol,
+ GError **error)
+{
+ g_return_val_if_fail (THRIFT_IS_STRUCT (object), -1);
+ return THRIFT_STRUCT_GET_CLASS (object)->read (object, protocol, error);
+}
+
+gint32
+thrift_struct_write (ThriftStruct *object, ThriftProtocol *protocol,
+ GError **error)
+{
+ g_return_val_if_fail (THRIFT_IS_STRUCT (object), -1);
+ return THRIFT_STRUCT_GET_CLASS (object)->write (object, protocol, error);
+}
+
+static void
+thrift_struct_class_init (ThriftStructClass *cls)
+{
+ cls->read = thrift_struct_read;
+ cls->write = thrift_struct_write;
+}
+
+static void
+thrift_struct_init (ThriftStruct *structure)
+{
+ THRIFT_UNUSED_VAR (structure);
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift_struct.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift_struct.h
new file mode 100644
index 000000000..f4cfcb2c4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/thrift_struct.h
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef THRIFT_STRUCT_H
+#define THRIFT_STRUCT_H
+
+#include <glib-object.h>
+
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+
+G_BEGIN_DECLS
+
+#define THRIFT_TYPE_STRUCT (thrift_struct_get_type ())
+#define THRIFT_STRUCT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_STRUCT, ThriftStruct))
+#define THRIFT_STRUCT_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_STRUCT, ThriftStructClass))
+#define THRIFT_IS_STRUCT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_STRUCT))
+#define THRIFT_IS_STRUCT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_STRUCT))
+#define THRIFT_STRUCT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_STRUCT, ThriftStructClass))
+
+typedef struct _ThriftStruct ThriftStruct;
+
+/* struct */
+struct _ThriftStruct
+{
+ GObject parent;
+
+ /* private */
+};
+
+typedef struct _ThriftStructClass ThriftStructClass;
+
+struct _ThriftStructClass
+{
+ GObjectClass parent;
+
+ /* public */
+ gint32 (*read) (ThriftStruct *object, ThriftProtocol *protocol,
+ GError **error);
+ gint32 (*write) (ThriftStruct *object, ThriftProtocol *protocol,
+ GError **error);
+};
+
+GType thrift_struct_get_type (void);
+
+gint32 thrift_struct_read (ThriftStruct *object, ThriftProtocol *protocol,
+ GError **error);
+
+gint32 thrift_struct_write (ThriftStruct *object, ThriftProtocol *protocol,
+ GError **error);
+G_END_DECLS
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport.c
new file mode 100644
index 000000000..0ab3e9329
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport.c
@@ -0,0 +1,390 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <netdb.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/transport/thrift_buffered_transport.h>
+
+/* object properties */
+enum _ThriftBufferedTransportProperties
+{
+ PROP_0,
+ PROP_THRIFT_BUFFERED_TRANSPORT_TRANSPORT,
+ PROP_THRIFT_BUFFERED_TRANSPORT_READ_BUFFER_SIZE,
+ PROP_THRIFT_BUFFERED_TRANSPORT_WRITE_BUFFER_SIZE
+};
+
+G_DEFINE_TYPE(ThriftBufferedTransport, thrift_buffered_transport, THRIFT_TYPE_TRANSPORT)
+
+/* implements thrift_transport_is_open */
+gboolean
+thrift_buffered_transport_is_open (ThriftTransport *transport)
+{
+ ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport);
+ return THRIFT_TRANSPORT_GET_CLASS (t->transport)->is_open (t->transport);
+}
+
+/* overrides thrift_transport_peek */
+gboolean
+thrift_buffered_transport_peek (ThriftTransport *transport, GError **error)
+{
+ ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport);
+ return (t->r_buf->len > 0) || thrift_transport_peek (t->transport, error);
+}
+
+/* implements thrift_transport_open */
+gboolean
+thrift_buffered_transport_open (ThriftTransport *transport, GError **error)
+{
+ ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport);
+ return THRIFT_TRANSPORT_GET_CLASS (t->transport)->open (t->transport, error);
+}
+
+/* implements thrift_transport_close */
+gboolean
+thrift_buffered_transport_close (ThriftTransport *transport, GError **error)
+{
+ ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport);
+ return THRIFT_TRANSPORT_GET_CLASS (t->transport)->close (t->transport, error);
+}
+
+/* the actual read is "slow" because it calls the underlying transport */
+gint32
+thrift_buffered_transport_read_slow (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error)
+{
+ ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport);
+ gint ret = 0;
+ guint32 want = len;
+ guint32 got = 0;
+ guchar *tmpdata = g_alloca (len);
+ guint32 have = t->r_buf->len;
+
+ /* we shouldn't hit this unless the buffer doesn't have enough to read */
+ g_assert (t->r_buf->len < want);
+
+ /* first copy what we have in our buffer. */
+ if (have > 0)
+ {
+ memcpy (buf, t->r_buf, t->r_buf->len);
+ want -= t->r_buf->len;
+ t->r_buf = g_byte_array_remove_range (t->r_buf, 0, t->r_buf->len);
+ }
+
+ /* if the buffer is still smaller than what we want to read, then just
+ * read it directly. otherwise, fill the buffer and then give out
+ * enough to satisfy the read. */
+ if (t->r_buf_size < want)
+ {
+ if ((ret = THRIFT_TRANSPORT_GET_CLASS (t->transport)->read (t->transport,
+ tmpdata,
+ want,
+ error)) < 0) {
+ return ret;
+ }
+ got += ret;
+
+ /* copy the data starting from where we left off */
+ memcpy ((guint8 *)buf + have, tmpdata, got);
+ return got + have;
+ } else {
+ guint32 give;
+
+ if ((ret = THRIFT_TRANSPORT_GET_CLASS (t->transport)->read (t->transport,
+ tmpdata,
+ want,
+ error)) < 0) {
+ return ret;
+ }
+ got += ret;
+ t->r_buf = g_byte_array_append (t->r_buf, tmpdata, got);
+
+ /* hand over what we have up to what the caller wants */
+ give = want < t->r_buf->len ? want : t->r_buf->len;
+
+
+ memcpy ((guint8 *)buf + len - want, t->r_buf->data, give);
+ t->r_buf = g_byte_array_remove_range (t->r_buf, 0, give);
+ want -= give;
+
+ return (len - want);
+ }
+}
+
+/* implements thrift_transport_read */
+gint32
+thrift_buffered_transport_read (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error)
+{
+ ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport);
+
+ /* if we have enough buffer data to fulfill the read, just use
+ * a memcpy */
+ if (len <= t->r_buf->len)
+ {
+ memcpy (buf, t->r_buf->data, len);
+ g_byte_array_remove_range (t->r_buf, 0, len);
+ return len;
+ }
+
+ return thrift_buffered_transport_read_slow (transport, buf, len, error);
+}
+
+/* implements thrift_transport_read_end
+ * called when write is complete. nothing to do on our end. */
+gboolean
+thrift_buffered_transport_read_end (ThriftTransport *transport, GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (transport);
+ THRIFT_UNUSED_VAR (error);
+ return TRUE;
+}
+
+gboolean
+thrift_buffered_transport_write_slow (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error)
+{
+ ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport);
+ guint32 have_bytes = t->w_buf->len;
+ guint32 space = t->w_buf_size - t->w_buf->len;
+
+ /* we need two syscalls because the buffered data plus the buffer itself
+ * is too big. */
+ if ((have_bytes + len >= 2*t->w_buf_size) || (have_bytes == 0))
+ {
+ if (have_bytes > 0)
+ {
+ if (!THRIFT_TRANSPORT_GET_CLASS (t->transport)->write (t->transport,
+ t->w_buf->data,
+ have_bytes,
+ error)) {
+ return FALSE;
+ }
+ t->w_buf = g_byte_array_remove_range (t->w_buf, 0, have_bytes);
+ }
+ if (!THRIFT_TRANSPORT_GET_CLASS (t->transport)->write (t->transport,
+ buf, len, error)) {
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ t->w_buf = g_byte_array_append (t->w_buf, buf, space);
+ if (!THRIFT_TRANSPORT_GET_CLASS (t->transport)->write (t->transport,
+ t->w_buf->data,
+ t->w_buf->len,
+ error)) {
+ return FALSE;
+ }
+
+ t->w_buf = g_byte_array_remove_range (t->w_buf, 0, t->w_buf->len);
+ t->w_buf = g_byte_array_append (t->w_buf, (guint8 *)buf + space, len-space);
+
+ return TRUE;
+}
+
+/* implements thrift_transport_write */
+gboolean
+thrift_buffered_transport_write (ThriftTransport *transport,
+ const gpointer buf,
+ const guint32 len, GError **error)
+{
+ ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport);
+
+ /* the length of the current buffer plus the length of the data being read */
+ if (t->w_buf->len + len <= t->w_buf_size)
+ {
+ t->w_buf = g_byte_array_append (t->w_buf, buf, len);
+ return len;
+ }
+
+ return thrift_buffered_transport_write_slow (transport, buf, len, error);
+}
+
+/* implements thrift_transport_write_end
+ * called when write is complete. nothing to do on our end. */
+gboolean
+thrift_buffered_transport_write_end (ThriftTransport *transport, GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (transport);
+ THRIFT_UNUSED_VAR (error);
+ return TRUE;
+}
+
+/* implements thrift_transport_flush */
+gboolean
+thrift_buffered_transport_flush (ThriftTransport *transport, GError **error)
+{
+ ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport);
+
+ if (t->w_buf != NULL && t->w_buf->len > 0)
+ {
+ /* write the buffer and then empty it */
+ if (!THRIFT_TRANSPORT_GET_CLASS (t->transport)->write (t->transport,
+ t->w_buf->data,
+ t->w_buf->len,
+ error)) {
+ return FALSE;
+ }
+ t->w_buf = g_byte_array_remove_range (t->w_buf, 0, t->w_buf->len);
+ }
+ THRIFT_TRANSPORT_GET_CLASS (t->transport)->flush (t->transport,
+ error);
+
+ return TRUE;
+}
+
+/* initializes the instance */
+static void
+thrift_buffered_transport_init (ThriftBufferedTransport *transport)
+{
+ transport->transport = NULL;
+ transport->r_buf = g_byte_array_new ();
+ transport->w_buf = g_byte_array_new ();
+}
+
+/* destructor */
+static void
+thrift_buffered_transport_finalize (GObject *object)
+{
+ ThriftBufferedTransport *transport = THRIFT_BUFFERED_TRANSPORT (object);
+
+ if (transport->r_buf != NULL)
+ {
+ g_byte_array_free (transport->r_buf, TRUE);
+ }
+ transport->r_buf = NULL;
+
+ if (transport->w_buf != NULL)
+ {
+ g_byte_array_free (transport->w_buf, TRUE);
+ }
+ transport->w_buf = NULL;
+}
+
+/* property accessor */
+void
+thrift_buffered_transport_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ ThriftBufferedTransport *transport = THRIFT_BUFFERED_TRANSPORT (object);
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_BUFFERED_TRANSPORT_TRANSPORT:
+ g_value_set_object (value, transport->transport);
+ break;
+ case PROP_THRIFT_BUFFERED_TRANSPORT_READ_BUFFER_SIZE:
+ g_value_set_uint (value, transport->r_buf_size);
+ break;
+ case PROP_THRIFT_BUFFERED_TRANSPORT_WRITE_BUFFER_SIZE:
+ g_value_set_uint (value, transport->w_buf_size);
+ break;
+ }
+}
+
+/* property mutator */
+void
+thrift_buffered_transport_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ ThriftBufferedTransport *transport = THRIFT_BUFFERED_TRANSPORT (object);
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_BUFFERED_TRANSPORT_TRANSPORT:
+ transport->transport = g_value_get_object (value);
+ break;
+ case PROP_THRIFT_BUFFERED_TRANSPORT_READ_BUFFER_SIZE:
+ transport->r_buf_size = g_value_get_uint (value);
+ break;
+ case PROP_THRIFT_BUFFERED_TRANSPORT_WRITE_BUFFER_SIZE:
+ transport->w_buf_size = g_value_get_uint (value);
+ break;
+ }
+}
+
+/* initializes the class */
+static void
+thrift_buffered_transport_class_init (ThriftBufferedTransportClass *cls)
+{
+ ThriftTransportClass *ttc = THRIFT_TRANSPORT_CLASS (cls);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (cls);
+ GParamSpec *param_spec = NULL;
+
+ /* setup accessors and mutators */
+ gobject_class->get_property = thrift_buffered_transport_get_property;
+ gobject_class->set_property = thrift_buffered_transport_set_property;
+
+ param_spec = g_param_spec_object ("transport", "transport (construct)",
+ "Thrift transport",
+ THRIFT_TYPE_TRANSPORT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_BUFFERED_TRANSPORT_TRANSPORT,
+ param_spec);
+
+ param_spec = g_param_spec_uint ("r_buf_size",
+ "read buffer size (construct)",
+ "Set the read buffer size",
+ 0, /* min */
+ 1048576, /* max, 1024*1024 */
+ 512, /* default value */
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_BUFFERED_TRANSPORT_READ_BUFFER_SIZE,
+ param_spec);
+
+ param_spec = g_param_spec_uint ("w_buf_size",
+ "write buffer size (construct)",
+ "Set the write buffer size",
+ 0, /* min */
+ 1048576, /* max, 1024*1024 */
+ 512, /* default value */
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_BUFFERED_TRANSPORT_WRITE_BUFFER_SIZE,
+ param_spec);
+
+
+ gobject_class->finalize = thrift_buffered_transport_finalize;
+ ttc->is_open = thrift_buffered_transport_is_open;
+ ttc->peek = thrift_buffered_transport_peek;
+ ttc->open = thrift_buffered_transport_open;
+ ttc->close = thrift_buffered_transport_close;
+ ttc->read = thrift_buffered_transport_read;
+ ttc->read_end = thrift_buffered_transport_read_end;
+ ttc->write = thrift_buffered_transport_write;
+ ttc->write_end = thrift_buffered_transport_write_end;
+ ttc->flush = thrift_buffered_transport_flush;
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport.h
new file mode 100644
index 000000000..837f46770
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport.h
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_BUFFERED_TRANSPORT_H
+#define _THRIFT_BUFFERED_TRANSPORT_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <thrift/c_glib/transport/thrift_transport.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_buffered_transport.h
+ * \brief Implementation of a Thrift buffered transport. Subclasses
+ * the ThriftTransport class.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_BUFFERED_TRANSPORT (thrift_buffered_transport_get_type ())
+#define THRIFT_BUFFERED_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_BUFFERED_TRANSPORT, ThriftBufferedTransport))
+#define THRIFT_IS_BUFFERED_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_BUFFERED_TRANSPORT))
+#define THRIFT_BUFFERED_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_BUFFERED_TRANSPORT, ThriftBufferedTransportClass))
+#define THRIFT_IS_BUFFERED_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_BUFFERED_TRANSPORT)
+#define THRIFT_BUFFERED_TRANSPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_BUFFERED_TRANSPORT, ThriftBufferedTransportClass))
+
+typedef struct _ThriftBufferedTransport ThriftBufferedTransport;
+
+/*!
+ * ThriftBufferedTransport instance.
+ */
+struct _ThriftBufferedTransport
+{
+ ThriftTransport parent;
+
+ /* protected */
+ ThriftTransport *transport;
+
+ /* private */
+ GByteArray *r_buf;
+ GByteArray *w_buf;
+ guint32 r_buf_size;
+ guint32 w_buf_size;
+};
+
+typedef struct _ThriftBufferedTransportClass ThriftBufferedTransportClass;
+
+/*!
+ * ThriftBufferedTransport class.
+ */
+struct _ThriftBufferedTransportClass
+{
+ ThriftTransportClass parent;
+};
+
+/* used by THRIFT_TYPE_BUFFERED_TRANSPORT */
+GType thrift_buffered_transport_get_type (void);
+
+G_END_DECLS
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport_factory.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport_factory.c
new file mode 100644
index 000000000..86050b691
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport_factory.c
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/transport/thrift_buffered_transport.h>
+#include <thrift/c_glib/transport/thrift_buffered_transport_factory.h>
+
+G_DEFINE_TYPE (ThriftBufferedTransportFactory,
+ thrift_buffered_transport_factory,
+ THRIFT_TYPE_TRANSPORT_FACTORY)
+
+/* Wraps a transport with a ThriftBufferedTransport. */
+ThriftTransport *
+thrift_buffered_transport_factory_get_transport (ThriftTransportFactory *factory,
+ ThriftTransport *transport)
+{
+ THRIFT_UNUSED_VAR (factory);
+
+ return THRIFT_TRANSPORT (g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT,
+ "transport", transport,
+ NULL));
+}
+
+static void
+thrift_buffered_transport_factory_init (ThriftBufferedTransportFactory *self)
+{
+ THRIFT_UNUSED_VAR (self);
+}
+
+static void
+thrift_buffered_transport_factory_class_init (ThriftBufferedTransportFactoryClass *klass)
+{
+ ThriftTransportFactoryClass *base_class =
+ THRIFT_TRANSPORT_FACTORY_CLASS (klass);
+
+ base_class->get_transport =
+ klass->get_transport =
+ thrift_buffered_transport_factory_get_transport;
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport_factory.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport_factory.h
new file mode 100644
index 000000000..d43f4e4ad
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport_factory.h
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_BUFFERED_TRANSPORT_FACTORY_H
+#define _THRIFT_BUFFERED_TRANSPORT_FACTORY_H
+
+#include <glib-object.h>
+
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/transport/thrift_transport_factory.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_buffered_transport_factory.h
+ * \brief Wraps a transport with a ThriftBufferedTransport.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY \
+ (thrift_buffered_transport_factory_get_type ())
+#define THRIFT_BUFFERED_TRANSPORT_FACTORY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY, \
+ ThriftBufferedTransportFactory))
+#define THRIFT_IS_BUFFERED_TRANSPORT_FACTORY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY))
+#define THRIFT_BUFFERED_TRANSPORT_FACTORY_CLASS(c) \
+ (G_TYPE_CHECK_CLASS_CAST ((c), \
+ THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY, \
+ ThriftBufferedTransportFactoryClass))
+#define THRIFT_IS_BUFFERED_TRANSPORT_FACTORY_CLASS(c) \
+ (G_TYPE_CHECK_CLASS_TYPE ((c), \
+ THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY))
+#define THRIFT_BUFFERED_TRANSPORT_FACTORY_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY, \
+ ThriftBufferedTransportFactoryClass))
+
+typedef struct _ThriftBufferedTransportFactory ThriftBufferedTransportFactory;
+
+/* Thrift Buffered-Transport Factory instance */
+struct _ThriftBufferedTransportFactory
+{
+ ThriftTransportFactory parent;
+};
+
+typedef struct _ThriftBufferedTransportFactoryClass ThriftBufferedTransportFactoryClass;
+
+/* Thrift Buffered-Transport Factory class */
+struct _ThriftBufferedTransportFactoryClass
+{
+ ThriftTransportFactoryClass parent;
+
+ /* vtable */
+ ThriftTransport *(*get_transport) (ThriftTransportFactory *factory,
+ ThriftTransport *transport);
+};
+
+/* used by THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY */
+GType thrift_buffered_transport_factory_get_type (void);
+
+/* virtual public methods */
+ThriftTransport *
+thrift_buffered_transport_factory_get_transport (ThriftTransportFactory *factory,
+ ThriftTransport *transport);
+
+G_END_DECLS
+
+#endif /* _THRIFT_BUFFERED_TRANSPORT_FACTORY_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_fd_transport.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_fd_transport.c
new file mode 100644
index 000000000..14abff7a4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_fd_transport.c
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/transport/thrift_fd_transport.h>
+
+/* object properties */
+enum _ThriftFDTransportProperties
+{
+ PROP_0,
+ PROP_THRIFT_FD_TRANSPORT_FD
+};
+
+G_DEFINE_TYPE (ThriftFDTransport, thrift_fd_transport, THRIFT_TYPE_TRANSPORT)
+
+/* implements thrift_transport_is_open */
+gboolean
+thrift_fd_transport_is_open (ThriftTransport *transport)
+{
+ ThriftFDTransport *t;
+ t = THRIFT_FD_TRANSPORT (transport);
+ return t->fd >= 0 && ! (fcntl (t->fd, F_GETFL) == -1 && errno == EBADF);
+}
+
+/* implements thrift_transport_open */
+gboolean
+thrift_fd_transport_open (ThriftTransport *transport, GError **error)
+{
+ THRIFT_UNUSED_VAR (error);
+ return thrift_fd_transport_is_open (transport);
+}
+
+/* implements thrift_transport_close */
+gboolean
+thrift_fd_transport_close (ThriftTransport *transport, GError **error)
+{
+ ThriftFDTransport *t;
+ t = THRIFT_FD_TRANSPORT (transport);
+
+#if GLIB_CHECK_VERSION (2, 36, 0)
+ return g_close (t->fd, error);
+#else
+ if (close (t->fd) == 0) {
+ g_clear_error (error);
+ return TRUE;
+ } else {
+ g_set_error (error,
+ THRIFT_TRANSPORT_ERROR,
+ THRIFT_TRANSPORT_ERROR_CLOSE,
+ strerror (errno));
+ return FALSE;
+ }
+#endif
+}
+
+/* implements thrift_transport_read */
+gint32
+thrift_fd_transport_read (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error)
+{
+ ThriftFDTransport *t;
+ ssize_t n;
+
+ t = THRIFT_FD_TRANSPORT (transport);
+ n = read (t->fd, (guint8 *) buf, len);
+ if (n == -1) {
+ g_set_error (error,
+ THRIFT_TRANSPORT_ERROR,
+ THRIFT_TRANSPORT_ERROR_RECEIVE,
+ "Failed to read from fd: %s",
+ strerror (errno));
+ return -1;
+ }
+ return n;
+}
+
+/* implements thrift_transport_read_end
+ * called when write is complete. nothing to do on our end. */
+gboolean
+thrift_fd_transport_read_end (ThriftTransport *transport, GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (transport);
+ THRIFT_UNUSED_VAR (error);
+ return TRUE;
+}
+
+/* implements thrift_transport_write */
+gboolean
+thrift_fd_transport_write (ThriftTransport *transport,
+ const gpointer buf,
+ const guint32 len, GError **error)
+{
+ ThriftFDTransport *t;
+ guint8 *_buf;
+ guint32 _len;
+ ssize_t n;
+
+ t = THRIFT_FD_TRANSPORT (transport);
+ _buf = (guint8 *) buf;
+ _len = len;
+ while (_len > 0) {
+ n = write (t->fd, _buf, _len);
+ if (n == -1) {
+ g_set_error (error,
+ THRIFT_TRANSPORT_ERROR,
+ THRIFT_TRANSPORT_ERROR_SEND,
+ "Failed to write from fd: %s",
+ strerror (errno));
+ return FALSE;
+ } else {
+ _buf += n;
+ _len -= n;
+ }
+ }
+ return TRUE;
+}
+
+/* implements thrift_transport_write_end
+ * called when write is complete. nothing to do on our end. */
+gboolean
+thrift_fd_transport_write_end (ThriftTransport *transport, GError **error)
+{
+ THRIFT_UNUSED_VAR (transport);
+ THRIFT_UNUSED_VAR (error);
+ return TRUE;
+}
+
+/* implements thrift_transport_flush */
+gboolean
+thrift_fd_transport_flush (ThriftTransport *transport, GError **error)
+{
+ ThriftFDTransport *t;
+ t = THRIFT_FD_TRANSPORT (transport);
+ if (fsync (t->fd) == -1) {
+ g_set_error (error,
+ THRIFT_TRANSPORT_ERROR,
+ THRIFT_TRANSPORT_ERROR_UNKNOWN,
+ "Failed to flush fd: %s",
+ strerror (errno));
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
+/* initializes the instance */
+static void
+thrift_fd_transport_init (ThriftFDTransport *transport)
+{
+ transport->fd = -1;
+}
+
+/* destructor */
+static void
+thrift_fd_transport_finalize (GObject *object)
+{
+ THRIFT_UNUSED_VAR (object);
+}
+
+/* property accessor */
+void
+thrift_fd_transport_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ ThriftFDTransport *t;
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ t = THRIFT_FD_TRANSPORT (object);
+
+ switch (property_id) {
+ case PROP_THRIFT_FD_TRANSPORT_FD:
+ g_value_set_int (value, t->fd);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/* property mutator */
+void
+thrift_fd_transport_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ ThriftFDTransport *t;
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ t = THRIFT_FD_TRANSPORT (object);
+
+ switch (property_id) {
+ case PROP_THRIFT_FD_TRANSPORT_FD:
+ t->fd = g_value_get_int (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/* initializes the class */
+static void
+thrift_fd_transport_class_init (ThriftFDTransportClass *cls)
+{
+ ThriftTransportClass *ttc;
+ GObjectClass *gobject_class;
+ GParamSpec *param_spec;
+
+ ttc = THRIFT_TRANSPORT_CLASS (cls);
+ gobject_class = G_OBJECT_CLASS (cls);
+ param_spec = NULL;
+
+ /* setup accessors and mutators */
+ gobject_class->get_property = thrift_fd_transport_get_property;
+ gobject_class->set_property = thrift_fd_transport_set_property;
+
+ param_spec = g_param_spec_int ("fd",
+ "file descriptor (construct)",
+ "Set the file descriptor",
+ INT_MIN, /* min */
+ INT_MAX, /* max, 1024*1024 */
+ -1, /* default value */
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_FD_TRANSPORT_FD,
+ param_spec);
+
+ gobject_class->finalize = thrift_fd_transport_finalize;
+ ttc->is_open = thrift_fd_transport_is_open;
+ ttc->open = thrift_fd_transport_open;
+ ttc->close = thrift_fd_transport_close;
+ ttc->read = thrift_fd_transport_read;
+ ttc->read_end = thrift_fd_transport_read_end;
+ ttc->write = thrift_fd_transport_write;
+ ttc->write_end = thrift_fd_transport_write_end;
+ ttc->flush = thrift_fd_transport_flush;
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_fd_transport.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_fd_transport.h
new file mode 100644
index 000000000..0e6d5c4b3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_fd_transport.h
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_FD_TRANSPORT_H
+#define _THRIFT_FD_TRANSPORT_H
+
+#include <glib-object.h>
+
+#include "thrift_transport.h"
+
+G_BEGIN_DECLS
+
+/*! \file thrift_fd_transport.h
+ * \brief Class for Thrift file descriptor transports.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_FD_TRANSPORT (thrift_fd_transport_get_type ())
+#define THRIFT_FD_TRANSPORT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_FD_TRANSPORT, \
+ ThriftFDTransport))
+#define THRIFT_IS_FD_TRANSPORT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_FD_TRANSPORT))
+#define THRIFT_FD_TRANSPORT_CLASS(c) \
+ (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_FD_TRANSPORT, \
+ ThriftFDTransportClass))
+#define THRIFT_IS_FD_TRANSPORT_CLASS(c) \
+ (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_FD_TRANSPORT))
+#define THRIFT_FD_TRANSPORT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_FD_TRANSPORT, \
+ ThriftFDTransportClass))
+
+typedef struct _ThriftFDTransport ThriftFDTransport;
+
+struct _ThriftFDTransport
+{
+ ThriftTransport parent;
+
+ /* protected */
+ gint fd;
+};
+
+typedef struct _ThriftFDTransportClass ThriftFDTransportClass;
+
+/*!
+ * Thrift Transport class
+ */
+struct _ThriftFDTransportClass
+{
+ ThriftTransportClass parent;
+};
+
+/* used by THRIFT_TYPE_FD_TRANSPORT */
+GType thrift_fd_transport_get_type (void);
+
+G_END_DECLS
+
+#endif /* _THRIFT_FD_TRANSPORT_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport.c
new file mode 100644
index 000000000..c54824635
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport.c
@@ -0,0 +1,383 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <netdb.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/transport/thrift_framed_transport.h>
+
+/* object properties */
+enum _ThriftFramedTransportProperties
+{
+ PROP_0,
+ PROP_THRIFT_FRAMED_TRANSPORT_TRANSPORT,
+ PROP_THRIFT_FRAMED_TRANSPORT_READ_BUFFER_SIZE,
+ PROP_THRIFT_FRAMED_TRANSPORT_WRITE_BUFFER_SIZE
+};
+
+G_DEFINE_TYPE(ThriftFramedTransport, thrift_framed_transport, THRIFT_TYPE_TRANSPORT)
+
+/* implements thrift_transport_is_open */
+gboolean
+thrift_framed_transport_is_open (ThriftTransport *transport)
+{
+ ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport);
+ return THRIFT_TRANSPORT_GET_CLASS (t->transport)->is_open (t->transport);
+}
+
+/* overrides thrift_transport_peek */
+gboolean
+thrift_framed_transport_peek (ThriftTransport *transport, GError **error)
+{
+ ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport);
+ return (t->r_buf->len > 0) || thrift_transport_peek (t->transport, error);
+}
+
+/* implements thrift_transport_open */
+gboolean
+thrift_framed_transport_open (ThriftTransport *transport, GError **error)
+{
+ ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport);
+ return THRIFT_TRANSPORT_GET_CLASS (t->transport)->open (t->transport, error);
+}
+
+/* implements thrift_transport_close */
+gboolean
+thrift_framed_transport_close (ThriftTransport *transport, GError **error)
+{
+ ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport);
+ return THRIFT_TRANSPORT_GET_CLASS (t->transport)->close (t->transport, error);
+}
+
+/* reads a frame and puts it into the buffer */
+gboolean
+thrift_framed_transport_read_frame (ThriftTransport *transport,
+ GError **error)
+{
+ ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport);
+ guint32 sz;
+ gint32 bytes;
+ gboolean result = FALSE;
+
+ /* read the size */
+ if (thrift_transport_read (t->transport,
+ &sz,
+ sizeof (sz),
+ error) == sizeof (sz))
+ {
+ guchar *tmpdata;
+
+ sz = ntohl (sz);
+
+ /* create a buffer to hold the data and read that much data */
+ tmpdata = g_alloca (sz);
+ bytes = thrift_transport_read (t->transport, tmpdata, sz, error);
+
+ if (bytes > 0 && (error == NULL || *error == NULL))
+ {
+ /* add the data to the buffer */
+ g_byte_array_append (t->r_buf, tmpdata, bytes);
+
+ result = TRUE;
+ }
+ }
+
+ return result;
+}
+
+/* the actual read is "slow" because it calls the underlying transport */
+gint32
+thrift_framed_transport_read_slow (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error)
+{
+ ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport);
+ guint32 want = len;
+ guint32 have = t->r_buf->len;
+ gint32 result = -1;
+
+ /* we shouldn't hit this unless the buffer doesn't have enough to read */
+ g_assert (t->r_buf->len < want);
+
+ /* first copy what we have in our buffer, if there is anything left */
+ if (have > 0)
+ {
+ memcpy (buf, t->r_buf, t->r_buf->len);
+ want -= t->r_buf->len;
+ t->r_buf = g_byte_array_remove_range (t->r_buf, 0, t->r_buf->len);
+ }
+
+ /* read a frame of input and buffer it */
+ if (thrift_framed_transport_read_frame (transport, error) == TRUE)
+ {
+ /* hand over what we have up to what the caller wants */
+ guint32 give = want < t->r_buf->len ? want : t->r_buf->len;
+
+ /* copy the data into the buffer */
+ memcpy ((guint8 *)buf + len - want, t->r_buf->data, give);
+ t->r_buf = g_byte_array_remove_range (t->r_buf, 0, give);
+ want -= give;
+
+ result = len - want;
+ }
+
+ return result;
+}
+
+/* implements thrift_transport_read */
+gint32
+thrift_framed_transport_read (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error)
+{
+ ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport);
+
+ /* if we have enough buffer data to fulfill the read, just use
+ * a memcpy from the buffer */
+ if (len <= t->r_buf->len)
+ {
+ memcpy (buf, t->r_buf->data, len);
+ g_byte_array_remove_range (t->r_buf, 0, len);
+ return len;
+ }
+
+ return thrift_framed_transport_read_slow (transport, buf, len, error);
+}
+
+/* implements thrift_transport_read_end
+ * called when read is complete. nothing to do on our end. */
+gboolean
+thrift_framed_transport_read_end (ThriftTransport *transport, GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (transport);
+ THRIFT_UNUSED_VAR (error);
+ return TRUE;
+}
+
+gboolean
+thrift_framed_transport_write_slow (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error)
+{
+ ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport);
+
+ THRIFT_UNUSED_VAR (error);
+
+ /* append the data to the buffer and we're done */
+ g_byte_array_append (t->w_buf, buf, len);
+
+ return TRUE;
+}
+
+/* implements thrift_transport_write */
+gboolean
+thrift_framed_transport_write (ThriftTransport *transport,
+ const gpointer buf,
+ const guint32 len, GError **error)
+{
+ ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport);
+
+ /* the length of the current buffer plus the length of the data being read */
+ if (t->w_buf->len + len <= t->w_buf_size)
+ {
+ t->w_buf = g_byte_array_append (t->w_buf, buf, len);
+ return TRUE;
+ }
+
+ return thrift_framed_transport_write_slow (transport, buf, len, error);
+}
+
+/* implements thrift_transport_write_end
+ * called when write is complete. nothing to do on our end. */
+gboolean
+thrift_framed_transport_write_end (ThriftTransport *transport, GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (transport);
+ THRIFT_UNUSED_VAR (error);
+ return TRUE;
+}
+
+/* implements thrift_transport_flush */
+gboolean
+thrift_framed_transport_flush (ThriftTransport *transport, GError **error)
+{
+ ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport);
+ gint32 sz_hbo, sz_nbo;
+ guchar *tmpdata;
+
+ /* get the size of the frame in host and network byte order */
+ sz_hbo = t->w_buf->len + sizeof(sz_nbo);
+ sz_nbo = (gint32) htonl ((guint32) t->w_buf->len);
+
+ /* copy the size of the frame and then the frame itself */
+ tmpdata = g_alloca (sz_hbo);
+ memcpy (tmpdata, (guint8 *) &sz_nbo, sizeof (sz_nbo));
+
+ if (t->w_buf->len > 0)
+ {
+ memcpy (tmpdata + sizeof (sz_nbo), t->w_buf->data, t->w_buf->len);
+ t->w_buf = g_byte_array_remove_range (t->w_buf, 0, t->w_buf->len);
+ }
+
+ /* write the buffer and then empty it */
+ THRIFT_TRANSPORT_GET_CLASS (t->transport)->write (t->transport,
+ tmpdata, sz_hbo,
+ error);
+
+ THRIFT_TRANSPORT_GET_CLASS (t->transport)->flush (t->transport,
+ error);
+
+ return TRUE;
+}
+
+/* initializes the instance */
+static void
+thrift_framed_transport_init (ThriftFramedTransport *transport)
+{
+ transport->transport = NULL;
+ transport->r_buf = g_byte_array_new ();
+ transport->w_buf = g_byte_array_new ();
+}
+
+/* destructor */
+static void
+thrift_framed_transport_finalize (GObject *object)
+{
+ ThriftFramedTransport *transport = THRIFT_FRAMED_TRANSPORT (object);
+
+ if (transport->r_buf != NULL)
+ {
+ g_byte_array_free (transport->r_buf, TRUE);
+ }
+ transport->r_buf = NULL;
+
+ if (transport->w_buf != NULL)
+ {
+ g_byte_array_free (transport->w_buf, TRUE);
+ }
+ transport->w_buf = NULL;
+}
+
+/* property accessor */
+void
+thrift_framed_transport_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ ThriftFramedTransport *transport = THRIFT_FRAMED_TRANSPORT (object);
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_FRAMED_TRANSPORT_TRANSPORT:
+ g_value_set_object (value, transport->transport);
+ break;
+ case PROP_THRIFT_FRAMED_TRANSPORT_READ_BUFFER_SIZE:
+ g_value_set_uint (value, transport->r_buf_size);
+ break;
+ case PROP_THRIFT_FRAMED_TRANSPORT_WRITE_BUFFER_SIZE:
+ g_value_set_uint (value, transport->w_buf_size);
+ break;
+ }
+}
+
+/* property mutator */
+void
+thrift_framed_transport_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ ThriftFramedTransport *transport = THRIFT_FRAMED_TRANSPORT (object);
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_FRAMED_TRANSPORT_TRANSPORT:
+ transport->transport = g_value_get_object (value);
+ break;
+ case PROP_THRIFT_FRAMED_TRANSPORT_READ_BUFFER_SIZE:
+ transport->r_buf_size = g_value_get_uint (value);
+ break;
+ case PROP_THRIFT_FRAMED_TRANSPORT_WRITE_BUFFER_SIZE:
+ transport->w_buf_size = g_value_get_uint (value);
+ break;
+ }
+}
+
+/* initializes the class */
+static void
+thrift_framed_transport_class_init (ThriftFramedTransportClass *cls)
+{
+ ThriftTransportClass *ttc = THRIFT_TRANSPORT_CLASS (cls);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (cls);
+ GParamSpec *param_spec = NULL;
+
+ /* setup accessors and mutators */
+ gobject_class->get_property = thrift_framed_transport_get_property;
+ gobject_class->set_property = thrift_framed_transport_set_property;
+
+ param_spec = g_param_spec_object ("transport", "transport (construct)",
+ "Thrift transport",
+ THRIFT_TYPE_TRANSPORT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_FRAMED_TRANSPORT_TRANSPORT,
+ param_spec);
+
+ param_spec = g_param_spec_uint ("r_buf_size",
+ "read buffer size (construct)",
+ "Set the read buffer size",
+ 0, /* min */
+ 1048576, /* max, 1024*1024 */
+ 512, /* default value */
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_FRAMED_TRANSPORT_READ_BUFFER_SIZE,
+ param_spec);
+
+ param_spec = g_param_spec_uint ("w_buf_size",
+ "write buffer size (construct)",
+ "Set the write buffer size",
+ 0, /* min */
+ 1048576, /* max, 1024*1024 */
+ 512, /* default value */
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_FRAMED_TRANSPORT_WRITE_BUFFER_SIZE,
+ param_spec);
+
+ gobject_class->finalize = thrift_framed_transport_finalize;
+ ttc->is_open = thrift_framed_transport_is_open;
+ ttc->peek = thrift_framed_transport_peek;
+ ttc->open = thrift_framed_transport_open;
+ ttc->close = thrift_framed_transport_close;
+ ttc->read = thrift_framed_transport_read;
+ ttc->read_end = thrift_framed_transport_read_end;
+ ttc->write = thrift_framed_transport_write;
+ ttc->write_end = thrift_framed_transport_write_end;
+ ttc->flush = thrift_framed_transport_flush;
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport.h
new file mode 100644
index 000000000..95c012393
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport.h
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_FRAMED_TRANSPORT_H
+#define _THRIFT_FRAMED_TRANSPORT_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <thrift/c_glib/transport/thrift_transport.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_framed_transport.h
+ * \brief Implementation of a Thrift framed transport. Subclasses
+ * the ThriftTransport class.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_FRAMED_TRANSPORT (thrift_framed_transport_get_type ())
+#define THRIFT_FRAMED_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_FRAMED_TRANSPORT, ThriftFramedTransport))
+#define THRIFT_IS_FRAMED_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_FRAMED_TRANSPORT))
+#define THRIFT_FRAMED_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_FRAMED_TRANSPORT, ThriftFramedTransportClass))
+#define THRIFT_IS_FRAMED_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_FRAMED_TRANSPORT)
+#define THRIFT_FRAMED_TRANSPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_FRAMED_TRANSPORT, ThriftFramedTransportClass))
+
+typedef struct _ThriftFramedTransport ThriftFramedTransport;
+
+/*!
+ * ThriftFramedTransport instance.
+ */
+struct _ThriftFramedTransport
+{
+ ThriftTransport parent;
+
+ /* protected */
+ ThriftTransport *transport;
+
+ /* private */
+ GByteArray *r_buf;
+ GByteArray *w_buf;
+ guint32 r_buf_size;
+ guint32 w_buf_size;
+};
+
+typedef struct _ThriftFramedTransportClass ThriftFramedTransportClass;
+
+/*!
+ * ThriftFramedTransport class.
+ */
+struct _ThriftFramedTransportClass
+{
+ ThriftTransportClass parent;
+};
+
+/* used by THRIFT_TYPE_FRAMED_TRANSPORT */
+GType thrift_framed_transport_get_type (void);
+
+G_END_DECLS
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport_factory.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport_factory.c
new file mode 100644
index 000000000..e68fe0a24
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport_factory.c
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/transport/thrift_framed_transport.h>
+#include <thrift/c_glib/transport/thrift_framed_transport_factory.h>
+
+G_DEFINE_TYPE (ThriftFramedTransportFactory,
+ thrift_framed_transport_factory,
+ THRIFT_TYPE_TRANSPORT_FACTORY)
+
+/* Wraps a transport with a ThriftFramedTransport. */
+ThriftTransport *
+thrift_framed_transport_factory_get_transport (ThriftTransportFactory *factory,
+ ThriftTransport *transport)
+{
+ THRIFT_UNUSED_VAR (factory);
+
+ return THRIFT_TRANSPORT (g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT,
+ "transport", transport,
+ NULL));
+}
+
+static void
+thrift_framed_transport_factory_init (ThriftFramedTransportFactory *self)
+{
+ THRIFT_UNUSED_VAR (self);
+}
+
+static void
+thrift_framed_transport_factory_class_init (ThriftFramedTransportFactoryClass *klass)
+{
+ ThriftTransportFactoryClass *base_class =
+ THRIFT_TRANSPORT_FACTORY_CLASS (klass);
+
+ base_class->get_transport =
+ klass->get_transport =
+ thrift_framed_transport_factory_get_transport;
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport_factory.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport_factory.h
new file mode 100644
index 000000000..c3e94962f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport_factory.h
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_FRAMED_TRANSPORT_FACTORY_H
+#define _THRIFT_FRAMED_TRANSPORT_FACTORY_H
+
+#include <glib-object.h>
+
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/transport/thrift_transport_factory.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_framed_transport_factory.h
+ * \brief Wraps a transport with a ThriftFramedTransport.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_FRAMED_TRANSPORT_FACTORY \
+ (thrift_framed_transport_factory_get_type ())
+#define THRIFT_FRAMED_TRANSPORT_FACTORY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ THRIFT_TYPE_FRAMED_TRANSPORT_FACTORY, \
+ ThriftFramedTransportFactory))
+#define THRIFT_IS_FRAMED_TRANSPORT_FACTORY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ THRIFT_TYPE_FRAMED_TRANSPORT_FACTORY))
+#define THRIFT_FRAMED_TRANSPORT_FACTORY_CLASS(c) \
+ (G_TYPE_CHECK_CLASS_CAST ((c), \
+ THRIFT_TYPE_FRAMED_TRANSPORT_FACTORY, \
+ ThriftFramedTransportFactoryClass))
+#define THRIFT_IS_FRAMED_TRANSPORT_FACTORY_CLASS(c) \
+ (G_TYPE_CHECK_CLASS_TYPE ((c), \
+ THRIFT_TYPE_FRAMED_TRANSPORT_FACTORY))
+#define THRIFT_FRAMED_TRANSPORT_FACTORY_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ THRIFT_TYPE_FRAMED_TRANSPORT_FACTORY, \
+ ThriftFramedTransportFactoryClass))
+
+typedef struct _ThriftFramedTransportFactory ThriftFramedTransportFactory;
+
+/* Thrift Framed-Transport Factory instance */
+struct _ThriftFramedTransportFactory
+{
+ ThriftTransportFactory parent;
+};
+
+typedef struct _ThriftFramedTransportFactoryClass ThriftFramedTransportFactoryClass;
+
+/* Thrift Framed-Transport Factory class */
+struct _ThriftFramedTransportFactoryClass
+{
+ ThriftTransportFactoryClass parent;
+
+ /* vtable */
+ ThriftTransport *(*get_transport) (ThriftTransportFactory *factory,
+ ThriftTransport *transport);
+};
+
+/* used by THRIFT_TYPE_FRAMED_TRANSPORT_FACTORY */
+GType thrift_framed_transport_factory_get_type (void);
+
+/* virtual public methods */
+ThriftTransport *
+thrift_framed_transport_factory_get_transport (ThriftTransportFactory *factory,
+ ThriftTransport *transport);
+
+G_END_DECLS
+
+#endif /* _THRIFT_FRAMED_TRANSPORT_FACTORY_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_memory_buffer.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_memory_buffer.c
new file mode 100644
index 000000000..91818e91a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_memory_buffer.c
@@ -0,0 +1,285 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <netdb.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/transport/thrift_memory_buffer.h>
+
+/* object properties */
+enum _ThriftMemoryBufferProperties
+{
+ PROP_0,
+ PROP_THRIFT_MEMORY_BUFFER_BUFFER_SIZE,
+ PROP_THRIFT_MEMORY_BUFFER_BUFFER,
+ PROP_THRIFT_MEMORY_BUFFER_OWNER
+};
+
+G_DEFINE_TYPE(ThriftMemoryBuffer, thrift_memory_buffer, THRIFT_TYPE_TRANSPORT)
+
+/* implements thrift_transport_is_open */
+gboolean
+thrift_memory_buffer_is_open (ThriftTransport *transport)
+{
+ THRIFT_UNUSED_VAR (transport);
+ return TRUE;
+}
+
+/* implements thrift_transport_open */
+gboolean
+thrift_memory_buffer_open (ThriftTransport *transport, GError **error)
+{
+ THRIFT_UNUSED_VAR (transport);
+ THRIFT_UNUSED_VAR (error);
+ return TRUE;
+}
+
+/* implements thrift_transport_close */
+gboolean
+thrift_memory_buffer_close (ThriftTransport *transport, GError **error)
+{
+ THRIFT_UNUSED_VAR (transport);
+ THRIFT_UNUSED_VAR (error);
+ return TRUE;
+}
+
+/* implements thrift_transport_read */
+gint32
+thrift_memory_buffer_read (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error)
+{
+ ThriftMemoryBuffer *t = THRIFT_MEMORY_BUFFER (transport);
+ guint32 give = len;
+
+ THRIFT_UNUSED_VAR (error);
+
+ /* if the requested bytes are more than what we have available,
+ * just give all that we have the buffer */
+ if (t->buf->len < len)
+ {
+ give = t->buf->len;
+ }
+
+ memcpy (buf, t->buf->data, give);
+ g_byte_array_remove_range (t->buf, 0, give);
+
+ return give;
+}
+
+/* implements thrift_transport_read_end
+ * called when read is complete. nothing to do on our end. */
+gboolean
+thrift_memory_buffer_read_end (ThriftTransport *transport, GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (transport);
+ THRIFT_UNUSED_VAR (error);
+ return TRUE;
+}
+
+/* implements thrift_transport_write */
+gboolean
+thrift_memory_buffer_write (ThriftTransport *transport,
+ const gpointer buf,
+ const guint32 len, GError **error)
+{
+ ThriftMemoryBuffer *t = THRIFT_MEMORY_BUFFER (transport);
+
+ THRIFT_UNUSED_VAR (error);
+
+ /* return an exception if the buffer doesn't have enough space. */
+ if (len > t->buf_size - t->buf->len)
+ {
+ g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_SEND,
+ "unable to write %d bytes to buffer of length %d",
+ len, t->buf_size);
+ return FALSE;
+ } else {
+ t->buf = g_byte_array_append (t->buf, buf, len);
+ return TRUE;
+ }
+}
+
+/* implements thrift_transport_write_end
+ * called when write is complete. nothing to do on our end. */
+gboolean
+thrift_memory_buffer_write_end (ThriftTransport *transport, GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (transport);
+ THRIFT_UNUSED_VAR (error);
+ return TRUE;
+}
+
+/* implements thrift_transport_flush */
+gboolean
+thrift_memory_buffer_flush (ThriftTransport *transport, GError **error)
+{
+ THRIFT_UNUSED_VAR (transport);
+ THRIFT_UNUSED_VAR (error);
+
+ return TRUE;
+}
+
+/* initializes class before constructor properties are set */
+static void
+thrift_memory_buffer_init (ThriftMemoryBuffer *t)
+{
+ THRIFT_UNUSED_VAR (t);
+}
+
+/* destructor */
+static void
+thrift_memory_buffer_finalize (GObject *object)
+{
+ ThriftMemoryBuffer *t = THRIFT_MEMORY_BUFFER (object);
+
+ if (t->owner && t->buf != NULL)
+ {
+ g_byte_array_unref (t->buf);
+ }
+ t->buf = NULL;
+}
+
+/* property accessor */
+void
+thrift_memory_buffer_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ ThriftMemoryBuffer *t = THRIFT_MEMORY_BUFFER (object);
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_MEMORY_BUFFER_BUFFER_SIZE:
+ g_value_set_uint (value, t->buf_size);
+ break;
+ case PROP_THRIFT_MEMORY_BUFFER_BUFFER:
+ g_value_set_pointer (value, (gpointer) (t->buf));
+ break;
+ case PROP_THRIFT_MEMORY_BUFFER_OWNER:
+ g_value_set_boolean (value, t->owner);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/* property mutator */
+void
+thrift_memory_buffer_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ ThriftMemoryBuffer *t = THRIFT_MEMORY_BUFFER (object);
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_MEMORY_BUFFER_BUFFER_SIZE:
+ t->buf_size = g_value_get_uint (value);
+ break;
+ case PROP_THRIFT_MEMORY_BUFFER_BUFFER:
+ t->buf = (GByteArray*) g_value_get_pointer (value);
+ break;
+ case PROP_THRIFT_MEMORY_BUFFER_OWNER:
+ t->owner = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/* initializes class after constructor properties are set */
+static void
+thrift_memory_buffer_constructed (GObject *object)
+{
+ ThriftMemoryBuffer *t = THRIFT_MEMORY_BUFFER (object);
+
+ if (t->buf == NULL) {
+ t->buf = g_byte_array_new ();
+ }
+
+ G_OBJECT_CLASS (thrift_memory_buffer_parent_class)->constructed (object);
+}
+
+/* initializes the class */
+static void
+thrift_memory_buffer_class_init (ThriftMemoryBufferClass *cls)
+{
+ ThriftTransportClass *ttc = THRIFT_TRANSPORT_CLASS (cls);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (cls);
+ GParamSpec *param_spec = NULL;
+
+ /* setup accessors and mutators */
+ gobject_class->get_property = thrift_memory_buffer_get_property;
+ gobject_class->set_property = thrift_memory_buffer_set_property;
+
+ param_spec = g_param_spec_uint ("buf_size",
+ "buffer size (construct)",
+ "Set the read/write buffer size limit",
+ 0, /* min */
+ G_MAXUINT32, /* max */
+ G_MAXUINT32, /* default */
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_MEMORY_BUFFER_BUFFER_SIZE,
+ param_spec);
+
+ param_spec = g_param_spec_pointer ("buf",
+ "internal buffer (GByteArray)",
+ "Set the internal buffer (GByteArray)",
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_MEMORY_BUFFER_BUFFER,
+ param_spec);
+
+ param_spec = g_param_spec_boolean ("owner",
+ "internal buffer memory management policy",
+ "Set whether internal buffer should be"
+ " unreferenced when thrift_memory_buffer"
+ " is finalized",
+ TRUE,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_MEMORY_BUFFER_OWNER,
+ param_spec);
+
+ gobject_class->constructed = thrift_memory_buffer_constructed;
+ gobject_class->finalize = thrift_memory_buffer_finalize;
+ ttc->is_open = thrift_memory_buffer_is_open;
+ ttc->open = thrift_memory_buffer_open;
+ ttc->close = thrift_memory_buffer_close;
+ ttc->read = thrift_memory_buffer_read;
+ ttc->read_end = thrift_memory_buffer_read_end;
+ ttc->write = thrift_memory_buffer_write;
+ ttc->write_end = thrift_memory_buffer_write_end;
+ ttc->flush = thrift_memory_buffer_flush;
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_memory_buffer.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_memory_buffer.h
new file mode 100644
index 000000000..d5d47b390
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_memory_buffer.h
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_MEMORY_BUFFER_H
+#define _THRIFT_MEMORY_BUFFER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <thrift/c_glib/transport/thrift_transport.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_memory_buffer.h
+ * \brief Implementation of a Thrift memory buffer transport.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_MEMORY_BUFFER (thrift_memory_buffer_get_type ())
+#define THRIFT_MEMORY_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_MEMORY_BUFFER, ThriftMemoryBuffer))
+#define THRIFT_IS_MEMORY_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_MEMORY_BUFFER))
+#define THRIFT_MEMORY_BUFFER_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_MEMORY_BUFFER, ThriftMemoryBufferClass))
+#define THRIFT_IS_MEMORY_BUFFER_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_MEMORY_BUFFER)
+#define THRIFT_MEMORY_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_MEMORY_BUFFER, ThriftMemoryBufferClass))
+
+typedef struct _ThriftMemoryBuffer ThriftMemoryBuffer;
+
+/*!
+ * ThriftMemoryBuffer instance.
+ */
+struct _ThriftMemoryBuffer
+{
+ ThriftTransport parent;
+
+ /* private */
+ GByteArray *buf;
+ guint32 buf_size;
+ gboolean owner;
+};
+
+typedef struct _ThriftMemoryBufferClass ThriftMemoryBufferClass;
+
+/*!
+ * ThriftMemoryBuffer class.
+ */
+struct _ThriftMemoryBufferClass
+{
+ ThriftTransportClass parent;
+};
+
+/* used by THRIFT_TYPE_MEMORY_BUFFER */
+GType thrift_memory_buffer_get_type (void);
+
+G_END_DECLS
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_platform_socket.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_platform_socket.h
new file mode 100644
index 000000000..ede60f172
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_platform_socket.h
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* clang-format off */
+
+#ifndef _THRIFT_TRANSPORT_PLATFORM_SOCKET_H_
+# define _THRIFT_TRANSPORT_PLATFORM_SOCKET_H_
+
+#ifdef _WIN32
+# define THRIFT_GET_SOCKET_ERROR ::WSAGetLastError()
+# define THRIFT_ERRNO (*_errno())
+# define THRIFT_EINPROGRESS WSAEINPROGRESS
+# define THRIFT_EAGAIN WSAEWOULDBLOCK
+# define THRIFT_EINTR WSAEINTR
+# define THRIFT_ECONNRESET WSAECONNRESET
+# define THRIFT_ENOTCONN WSAENOTCONN
+# define THRIFT_ETIMEDOUT WSAETIMEDOUT
+# define THRIFT_EWOULDBLOCK WSAEWOULDBLOCK
+# define THRIFT_EPIPE WSAECONNRESET
+# define THRIFT_NO_SOCKET_CACHING SO_EXCLUSIVEADDRUSE
+# define THRIFT_INVALID_SOCKET INVALID_SOCKET
+# define THRIFT_SOCKETPAIR thrift_socketpair
+# define THRIFT_FCNTL thrift_fcntl
+# define THRIFT_O_NONBLOCK 1
+# define THRIFT_F_GETFL 0
+# define THRIFT_F_SETFL 1
+# define THRIFT_GETTIMEOFDAY thrift_gettimeofday
+# define THRIFT_CLOSESOCKET closesocket
+# define THRIFT_CLOSE _close
+# define THRIFT_OPEN _open
+# define THRIFT_FTRUNCATE _chsize_s
+# define THRIFT_FSYNC _commit
+# define THRIFT_LSEEK _lseek
+# define THRIFT_WRITE _write
+# define THRIFT_READ _read
+# define THRIFT_FSTAT _fstat
+# define THRIFT_STAT _stat
+# ifdef _WIN32_WCE
+# define THRIFT_GAI_STRERROR(...) thrift_wstr2str(gai_strerrorW(__VA_ARGS__))
+# else
+# define THRIFT_GAI_STRERROR gai_strerrorA
+# endif
+# define THRIFT_SSIZET ptrdiff_t
+# define THRIFT_SNPRINTF _snprintf
+# define THRIFT_SLEEP_SEC thrift_sleep
+# define THRIFT_SLEEP_USEC thrift_usleep
+# define THRIFT_TIMESPEC thrift_timespec
+# define THRIFT_CTIME_R thrift_ctime_r
+# define THRIFT_POLL thrift_poll
+# if WINVER <= 0x0502 /* XP, Server2003 */
+# define THRIFT_POLLFD thrift_pollfd
+# define THRIFT_POLLIN 0x0300
+# define THRIFT_POLLOUT 0x0010
+# else /* Vista, Win7... */
+# define THRIFT_POLLFD pollfd
+# define THRIFT_POLLIN POLLIN
+# define THRIFT_POLLOUT POLLOUT
+# endif /* WINVER */
+# define THRIFT_SHUT_RDWR SD_BOTH
+#else /* not _WIN32 */
+# include <errno.h>
+# define THRIFT_GET_SOCKET_ERROR errno
+# define THRIFT_ERRNO errno
+# define THRIFT_EINTR EINTR
+# define THRIFT_EINPROGRESS EINPROGRESS
+# define THRIFT_ECONNRESET ECONNRESET
+# define THRIFT_ENOTCONN ENOTCONN
+# define THRIFT_ETIMEDOUT ETIMEDOUT
+# define THRIFT_EWOULDBLOCK EWOULDBLOCK
+# define THRIFT_EAGAIN EAGAIN
+# define THRIFT_EPIPE EPIPE
+# define THRIFT_NO_SOCKET_CACHING SO_REUSEADDR
+# define THRIFT_INVALID_SOCKET (-1)
+# define THRIFT_SOCKETPAIR socketpair
+# define THRIFT_FCNTL fcntl
+# define THRIFT_O_NONBLOCK O_NONBLOCK
+# define THRIFT_F_GETFL F_GETFL
+# define THRIFT_F_SETFL F_SETFL
+# define THRIFT_GETTIMEOFDAY gettimeofday
+# define THRIFT_CLOSESOCKET close
+# define THRIFT_CLOSE close
+# define THRIFT_OPEN open
+# define THRIFT_FTRUNCATE ftruncate
+# define THRIFT_FSYNC fsync
+# define THRIFT_LSEEK lseek
+# define THRIFT_WRITE write
+# define THRIFT_READ read
+# define THRIFT_STAT stat
+# define THRIFT_FSTAT fstat
+# define THRIFT_GAI_STRERROR gai_strerror
+# define THRIFT_SSIZET ssize_t
+# define THRIFT_SNPRINTF snprintf
+# define THRIFT_SLEEP_SEC sleep
+# define THRIFT_SLEEP_USEC usleep
+# define THRIFT_TIMESPEC timespec
+# define THRIFT_CTIME_R ctime_r
+# define THRIFT_POLL poll
+# define THRIFT_POLLFD pollfd
+# define THRIFT_POLLIN POLLIN
+# define THRIFT_POLLOUT POLLOUT
+# define THRIFT_SHUT_RDWR SHUT_RDWR
+#endif
+
+#endif /* _THRIFT_TRANSPORT_PLATFORM_SOCKET_H_ */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_server_socket.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_server_socket.c
new file mode 100644
index 000000000..21ce1eee0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_server_socket.c
@@ -0,0 +1,307 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/transport/thrift_socket.h>
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/transport/thrift_server_transport.h>
+#include <thrift/c_glib/transport/thrift_server_socket.h>
+
+/* object properties */
+enum _ThriftServerSocketProperties
+{
+ PROP_0,
+ PROP_THRIFT_SERVER_SOCKET_PORT,
+ PROP_THRIFT_SERVER_SOCKET_PATH,
+ PROP_THRIFT_SERVER_SOCKET_BACKLOG
+};
+
+/* define the GError domain string */
+#define THRIFT_SERVER_SOCKET_ERROR_DOMAIN "thrift-server-socket-error-quark"
+
+G_DEFINE_TYPE(ThriftServerSocket, thrift_server_socket, THRIFT_TYPE_SERVER_TRANSPORT)
+
+gboolean
+thrift_server_socket_listen (ThriftServerTransport *transport, GError **error)
+{
+ int enabled = 1; /* for setsockopt() */
+ ThriftServerSocket *tsocket = THRIFT_SERVER_SOCKET (transport);
+
+ const int socket_domain = tsocket->path ? PF_UNIX : AF_INET;
+
+ /* create a socket */
+ if ((tsocket->sd = socket (socket_domain, SOCK_STREAM, 0)) == -1)
+ {
+ g_set_error (error, THRIFT_SERVER_SOCKET_ERROR,
+ THRIFT_SERVER_SOCKET_ERROR_SOCKET,
+ "failed to create socket - %s", strerror (errno));
+ return FALSE;
+ }
+
+ if (setsockopt(tsocket->sd, SOL_SOCKET, SO_REUSEADDR, &enabled,
+ sizeof(enabled)) == -1)
+ {
+ g_set_error (error, THRIFT_SERVER_SOCKET_ERROR,
+ THRIFT_SERVER_SOCKET_ERROR_SETSOCKOPT,
+ "unable to set SO_REUSEADDR - %s", strerror(errno));
+ return FALSE;
+ }
+
+ /* bind to the socket */
+ if (tsocket->path)
+ {
+ /* create a socket structure */
+ struct sockaddr_un pin;
+ memset (&pin, 0, sizeof(pin));
+ pin.sun_family = AF_UNIX;
+ memcpy(pin.sun_path, tsocket->path, strlen(tsocket->path) + 1);
+
+ if (bind(tsocket->sd, (struct sockaddr *) &pin, sizeof(pin)) == -1)
+ {
+ g_set_error (error, THRIFT_SERVER_SOCKET_ERROR,
+ THRIFT_SERVER_SOCKET_ERROR_BIND,
+ "failed to bind to path %s: - %s",
+ tsocket->path, strerror(errno));
+ return FALSE;
+ }
+ }
+ else
+ {
+ /* create a address structure */
+ struct sockaddr_in pin;
+ memset (&pin, 0, sizeof(pin));
+ pin.sin_family = AF_INET;
+ pin.sin_addr.s_addr = INADDR_ANY;
+ pin.sin_port = htons(tsocket->port);
+
+ if (bind(tsocket->sd, (struct sockaddr *) &pin, sizeof(pin)) == -1)
+ {
+ g_set_error (error, THRIFT_SERVER_SOCKET_ERROR,
+ THRIFT_SERVER_SOCKET_ERROR_BIND,
+ "failed to bind to port %d - %s",
+ tsocket->port, strerror(errno));
+ return FALSE;
+ }
+ }
+
+ if (listen(tsocket->sd, tsocket->backlog) == -1)
+ {
+ if (tsocket->path)
+ {
+ g_set_error (error, THRIFT_SERVER_SOCKET_ERROR,
+ THRIFT_SERVER_SOCKET_ERROR_BIND,
+ "failed to bind to path %s: - %s",
+ tsocket->path, strerror(errno));
+ return FALSE;
+ }
+ else
+ {
+ g_set_error (error, THRIFT_SERVER_SOCKET_ERROR,
+ THRIFT_SERVER_SOCKET_ERROR_LISTEN,
+ "failed to listen to port %d - %s",
+ tsocket->port, strerror(errno));
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+ThriftTransport *
+thrift_server_socket_accept (ThriftServerTransport *transport, GError **error)
+{
+ int sd = THRIFT_INVALID_SOCKET;
+ guint addrlen = 0;
+ struct sockaddr_in address;
+ ThriftSocket *socket = NULL;
+
+ ThriftServerSocket *tsocket = THRIFT_SERVER_SOCKET (transport);
+
+ if ((sd = accept(tsocket->sd, (struct sockaddr *) &address, &addrlen)) == -1)
+ {
+ g_set_error (error, THRIFT_SERVER_SOCKET_ERROR,
+ THRIFT_SERVER_SOCKET_ERROR_ACCEPT,
+ "failed to accept connection - %s",
+ strerror(errno));
+ return FALSE;
+ }
+
+ socket = g_object_new (THRIFT_TYPE_SOCKET, NULL);
+ socket->sd = sd;
+
+ return THRIFT_TRANSPORT(socket);
+}
+
+gboolean
+thrift_server_socket_close (ThriftServerTransport *transport, GError **error)
+{
+ ThriftServerSocket *tsocket = THRIFT_SERVER_SOCKET (transport);
+
+ if (close (tsocket->sd) == -1)
+ {
+ g_set_error (error, THRIFT_SERVER_SOCKET_ERROR,
+ THRIFT_SERVER_SOCKET_ERROR_CLOSE,
+ "unable to close socket - %s", strerror(errno));
+ return FALSE;
+ }
+ tsocket->sd = THRIFT_INVALID_SOCKET;
+
+ return TRUE;
+}
+
+/* define the GError domain for this implementation */
+GQuark
+thrift_server_socket_error_quark (void)
+{
+ return g_quark_from_static_string(THRIFT_SERVER_SOCKET_ERROR_DOMAIN);
+}
+
+/* initializes the instance */
+static void
+thrift_server_socket_init (ThriftServerSocket *socket)
+{
+ socket->sd = THRIFT_INVALID_SOCKET;
+}
+
+/* destructor */
+static void
+thrift_server_socket_finalize (GObject *object)
+{
+ ThriftServerSocket *socket = THRIFT_SERVER_SOCKET (object);
+
+ if (socket->sd != THRIFT_INVALID_SOCKET)
+ {
+ close (socket->sd);
+ }
+ socket->sd = THRIFT_INVALID_SOCKET;
+}
+
+/* property accessor */
+void
+thrift_server_socket_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ ThriftServerSocket *socket = THRIFT_SERVER_SOCKET (object);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_SERVER_SOCKET_PORT:
+ g_value_set_uint (value, socket->port);
+ break;
+ case PROP_THRIFT_SERVER_SOCKET_PATH:
+ g_value_set_string (value, socket->path);
+ break;
+ case PROP_THRIFT_SERVER_SOCKET_BACKLOG:
+ g_value_set_uint (value, socket->backlog);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/* property mutator */
+void
+thrift_server_socket_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ ThriftServerSocket *socket = THRIFT_SERVER_SOCKET (object);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_SERVER_SOCKET_PORT:
+ socket->port = g_value_get_uint (value);
+ break;
+ case PROP_THRIFT_SERVER_SOCKET_PATH:
+ if (socket->path) {
+ g_free(socket->path);
+ }
+ socket->path = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_THRIFT_SERVER_SOCKET_BACKLOG:
+ socket->backlog = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/* initializes the class */
+static void
+thrift_server_socket_class_init (ThriftServerSocketClass *cls)
+{
+ ThriftServerTransportClass *tstc = THRIFT_SERVER_TRANSPORT_CLASS (cls);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (cls);
+ GParamSpec *param_spec = NULL;
+
+ /* setup accessors and mutators */
+ gobject_class->get_property = thrift_server_socket_get_property;
+ gobject_class->set_property = thrift_server_socket_set_property;
+
+ param_spec = g_param_spec_uint ("port",
+ "port (construct)",
+ "Set the port to listen to",
+ 0, /* min */
+ 65535, /* max */
+ 9090, /* default by convention */
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_SERVER_SOCKET_PORT,
+ param_spec);
+
+ param_spec = g_param_spec_string ("path",
+ "path (construct)",
+ "Set the path to listen to",
+ NULL, /* default value */
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_SERVER_SOCKET_PATH,
+ param_spec);
+
+ param_spec = g_param_spec_uint ("backlog",
+ "backlog (construct)",
+ "Set the accept backlog",
+ 0, /* max */
+ 65534, /* max */
+ 1024, /* default */
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_THRIFT_SERVER_SOCKET_BACKLOG,
+ param_spec);
+
+ gobject_class->finalize = thrift_server_socket_finalize;
+
+ tstc->listen = thrift_server_socket_listen;
+ tstc->accept = thrift_server_socket_accept;
+ tstc->close = thrift_server_socket_close;
+}
+
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_server_socket.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_server_socket.h
new file mode 100644
index 000000000..7710d5161
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_server_socket.h
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_SERVER_SOCKET_H
+#define _THRIFT_SERVER_SOCKET_H
+
+#include <glib-object.h>
+
+#include "thrift_server_transport.h"
+
+G_BEGIN_DECLS
+
+/*! \file thrift_server_socket.h
+ * \brief Socket implementation of a Thrift server transport. Implements the
+ * ThriftServerTransport class.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_SERVER_SOCKET (thrift_server_socket_get_type ())
+#define THRIFT_SERVER_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_SERVER_SOCKET, ThriftServerSocket))
+#define THRIFT_IS_SERVER_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_SERVER_SOCKET))
+#define THRIFT_SERVER_SOCKET_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_SERVER_SOCKET, ThriftServerSocketClass))
+#define THRIFT_IS_SERVER_SOCKET_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_SERVER_SOCKET))
+#define THRIFT_SERVER_SOCKET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_SERVER_SOCKET, ThriftServerSocketClass))
+
+typedef struct _ThriftServerSocket ThriftServerSocket;
+
+/*!
+ * Thrift ServerSocket instance.
+ */
+struct _ThriftServerSocket
+{
+ ThriftServerTransport parent;
+
+ /* private */
+ guint port;
+ gchar *path;
+ gshort backlog;
+ int sd;
+ guint8 *buf;
+ guint32 buf_size;
+ guint32 buf_len;
+};
+
+typedef struct _ThriftServerSocketClass ThriftServerSocketClass;
+
+/*!
+ * Thrift ServerSocket class.
+ */
+struct _ThriftServerSocketClass
+{
+ ThriftServerTransportClass parent;
+};
+
+/* used by THRIFT_TYPE_SERVER_SOCKET */
+GType thrift_server_socket_get_type (void);
+
+/* define error/exception types */
+typedef enum
+{
+ THRIFT_SERVER_SOCKET_ERROR_SOCKET,
+ THRIFT_SERVER_SOCKET_ERROR_SETSOCKOPT,
+ THRIFT_SERVER_SOCKET_ERROR_BIND,
+ THRIFT_SERVER_SOCKET_ERROR_LISTEN,
+ THRIFT_SERVER_SOCKET_ERROR_ACCEPT,
+ THRIFT_SERVER_SOCKET_ERROR_CLOSE
+} ThriftServerSocketError;
+
+/* define a error domain for GError to use */
+GQuark thrift_server_socket_error_quark (void);
+#define THRIFT_SERVER_SOCKET_ERROR (thrift_server_socket_error_quark ())
+
+G_END_DECLS
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_server_transport.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_server_transport.c
new file mode 100644
index 000000000..c25d1384f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_server_transport.c
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/transport/thrift_server_transport.h>
+
+G_DEFINE_ABSTRACT_TYPE(ThriftServerTransport, thrift_server_transport, G_TYPE_OBJECT)
+
+/* base initializer for the server transport interface */
+static void
+thrift_server_transport_class_init (ThriftServerTransportClass *c)
+{
+ c->listen = thrift_server_transport_listen;
+ c->accept = thrift_server_transport_accept;
+ c->close = thrift_server_transport_close;
+}
+
+static void
+thrift_server_transport_init (ThriftServerTransport *transport)
+{
+ THRIFT_UNUSED_VAR (transport);
+}
+
+gboolean
+thrift_server_transport_listen (ThriftServerTransport *transport,
+ GError **error)
+{
+ return THRIFT_SERVER_TRANSPORT_GET_CLASS (transport)->listen (transport,
+ error);
+}
+
+ThriftTransport *
+thrift_server_transport_accept (ThriftServerTransport *transport,
+ GError **error)
+{
+ return THRIFT_SERVER_TRANSPORT_GET_CLASS (transport)->accept (transport,
+ error);
+}
+
+gboolean
+thrift_server_transport_close (ThriftServerTransport *transport, GError **error)
+{
+ return THRIFT_SERVER_TRANSPORT_GET_CLASS (transport)->close (transport,
+ error);
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_server_transport.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_server_transport.h
new file mode 100644
index 000000000..98a9191bd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_server_transport.h
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_SERVER_TRANSPORT_H
+#define _THRIFT_SERVER_TRANSPORT_H
+
+#include <glib-object.h>
+
+#include "thrift_transport.h"
+
+G_BEGIN_DECLS
+
+/*! \file thrift_server_transport.h
+ * \brief Abstract class for Thrift server transports.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_SERVER_TRANSPORT (thrift_server_transport_get_type ())
+#define THRIFT_SERVER_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_SERVER_TRANSPORT, ThriftServerTransport))
+#define THRIFT_IS_SERVER_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_SERVER_TRANSPORT))
+#define THRIFT_SERVER_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_SERVER_TRANSPORT, ThriftServerTransportClass))
+#define THRIFT_IS_SERVER_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_SERVER_TRANSPORT))
+#define THRIFT_SERVER_TRANSPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_SERVER_TRANSPORT, ThriftServerTransportClass))
+
+typedef struct _ThriftServerTransport ThriftServerTransport;
+
+struct _ThriftServerTransport
+{
+ GObject parent;
+};
+
+typedef struct _ThriftServerTransportClass ThriftServerTransportClass;
+
+/*!
+ * Thrift Transport class
+ */
+struct _ThriftServerTransportClass
+{
+ GObjectClass parent;
+
+ /* vtable */
+ gboolean (*listen) (ThriftServerTransport *transport, GError **error);
+ ThriftTransport *(*accept) (ThriftServerTransport *transport, GError **error);
+ gboolean (*close) (ThriftServerTransport *transport, GError **error);
+};
+
+/* used by THRIFT_TYPE_SERVER_TRANSPORT */
+GType thrift_server_transport_get_type (void);
+
+/*!
+ * Listen for new connections.
+ * \public \memberof ThriftServerTransportClass
+ */
+gboolean thrift_server_transport_listen (ThriftServerTransport *transport,
+ GError **error);
+
+/*!
+ * Accept a connection.
+ * \public \memberof ThriftServerTransportClass
+ */
+ThriftTransport *thrift_server_transport_accept
+ (ThriftServerTransport *transport, GError **error);
+
+/*!
+ * Close the transport.
+ * \public \memberof ThriftServerTransportClass
+ */
+gboolean thrift_server_transport_close (ThriftServerTransport *transport,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* _THRIFT_SERVER_TRANSPORT_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_socket.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_socket.c
new file mode 100644
index 000000000..b7b413910
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_socket.c
@@ -0,0 +1,427 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <errno.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/transport/thrift_socket.h>
+
+/* object properties */
+enum _ThriftSocketProperties
+{
+ PROP_0,
+ PROP_THRIFT_SOCKET_HOSTNAME,
+ PROP_THRIFT_SOCKET_PORT,
+ PROP_THRIFT_SOCKET_PATH
+};
+
+G_DEFINE_TYPE(ThriftSocket, thrift_socket, THRIFT_TYPE_TRANSPORT)
+
+/* implements thrift_transport_is_open */
+gboolean
+thrift_socket_is_open (ThriftTransport *transport)
+{
+ ThriftSocket *socket = THRIFT_SOCKET (transport);
+ return socket->sd != THRIFT_INVALID_SOCKET;
+}
+
+/* overrides thrift_transport_peek */
+gboolean
+thrift_socket_peek (ThriftTransport *transport, GError **error)
+{
+ gboolean result = FALSE;
+ guint8 buf;
+ int r;
+ int errno_copy;
+
+ ThriftSocket *socket = THRIFT_SOCKET (transport);
+
+ if (thrift_socket_is_open (transport))
+ {
+ r = recv (socket->sd, &buf, 1, MSG_PEEK);
+ if (r == -1)
+ {
+ errno_copy = errno;
+
+ #if defined __FreeBSD__ || defined __MACH__
+ /* FreeBSD returns -1 and ECONNRESET if the socket was closed by the other
+ side */
+ if (errno_copy == ECONNRESET)
+ {
+ thrift_socket_close (transport, error);
+ }
+ else
+ {
+ #endif
+
+ g_set_error (error,
+ THRIFT_TRANSPORT_ERROR,
+ THRIFT_TRANSPORT_ERROR_SOCKET,
+ "failed to peek at socket - %s",
+ strerror (errno_copy));
+
+ #if defined __FreeBSD__ || defined __MACH__
+ }
+ #endif
+ }
+ else if (r > 0)
+ {
+ result = TRUE;
+ }
+ }
+
+ return result;
+}
+
+
+/* implements thrift_transport_close */
+gboolean
+thrift_socket_close (ThriftTransport *transport, GError **error)
+{
+ ThriftSocket *socket = THRIFT_SOCKET (transport);
+
+ if (close (socket->sd) == -1)
+ {
+ g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_CLOSE,
+ "unable to close socket - %s",
+ strerror(errno));
+ return FALSE;
+ }
+
+ socket->sd = THRIFT_INVALID_SOCKET;
+ return TRUE;
+}
+
+/* implements thrift_transport_open */
+gboolean
+thrift_socket_open (ThriftTransport *transport, GError **error)
+{
+ struct hostent *hp = NULL;
+ struct sockaddr_in pin;
+ int err;
+#if defined(HAVE_GETHOSTBYNAME_R)
+ struct hostent he;
+ char buf[1024];
+#endif
+
+ ThriftSocket *tsocket = THRIFT_SOCKET (transport);
+ g_return_val_if_fail (tsocket->sd == THRIFT_INVALID_SOCKET, FALSE);
+
+ if (tsocket->path) {
+ /* create a socket structure */
+ struct sockaddr_un pin;
+ memset (&pin, 0, sizeof(pin));
+ pin.sun_family = AF_UNIX;
+ memcpy(pin.sun_path, tsocket->path, strlen(tsocket->path) + 1);
+
+ /* create the socket */
+ if ((tsocket->sd = socket (PF_UNIX, SOCK_STREAM, 0)) == -1)
+ {
+ g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_SOCKET,
+ "failed to create socket for path %s: - %s",
+ tsocket->path,
+ strerror(errno));
+ return FALSE;
+ }
+
+ /* open a connection */
+ if (connect (tsocket->sd, (struct sockaddr *) &pin, sizeof(pin)) == -1)
+ {
+ thrift_socket_close(tsocket, NULL);
+ g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_CONNECT,
+ "failed to connect to path %s: - %s",
+ tsocket->path, strerror(errno));
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ /* lookup the destination host */
+#if defined(HAVE_GETHOSTBYNAME_R)
+ if (gethostbyname_r (tsocket->hostname, &he, buf, 1024, &hp, &err) != 0 || hp == NULL)
+#else
+ if ((hp = gethostbyname (tsocket->hostname)) == NULL && (err = h_errno))
+#endif
+ {
+ /* host lookup failed, bail out with an error */
+ g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_HOST,
+ "host lookup failed for %s:%d - %s",
+ tsocket->hostname, tsocket->port,
+ hstrerror (err));
+ return FALSE;
+ }
+
+ /* create a socket structure */
+ memset (&pin, 0, sizeof(pin));
+ pin.sin_family = AF_INET;
+ pin.sin_addr.s_addr = ((struct in_addr *) (hp->h_addr_list[0]))->s_addr;
+ pin.sin_port = htons (tsocket->port);
+
+ /* create the socket */
+ if ((tsocket->sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
+ {
+ g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_SOCKET,
+ "failed to create socket for host %s:%d - %s",
+ tsocket->hostname, tsocket->port,
+ strerror(errno));
+ return FALSE;
+ }
+
+ /* open a connection */
+ if (connect (tsocket->sd, (struct sockaddr *) &pin, sizeof(pin)) == -1)
+ {
+ thrift_socket_close(transport, NULL);
+ g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_CONNECT,
+ "failed to connect to host %s:%d - %s",
+ tsocket->hostname, tsocket->port, strerror(errno));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/* implements thrift_transport_read */
+gint32
+thrift_socket_read (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error)
+{
+ gint ret = 0;
+ guint got = 0;
+
+ ThriftSocket *socket = THRIFT_SOCKET (transport);
+
+ while (got < len)
+ {
+ ret = recv (socket->sd, (guint8 *)buf + got, len-got, 0);
+ if (ret <= 0)
+ {
+ g_set_error (error, THRIFT_TRANSPORT_ERROR,
+ THRIFT_TRANSPORT_ERROR_RECEIVE,
+ "failed to read %d bytes - %s", len, strerror(errno));
+ return -1;
+ }
+ got += ret;
+ }
+
+ return got;
+}
+
+/* implements thrift_transport_read_end
+ * called when write is complete. nothing to do on our end. */
+gboolean
+thrift_socket_read_end (ThriftTransport *transport, GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (transport);
+ THRIFT_UNUSED_VAR (error);
+ return TRUE;
+}
+
+/* implements thrift_transport_write */
+gboolean
+thrift_socket_write (ThriftTransport *transport, const gpointer buf,
+ const guint32 len, GError **error)
+{
+ gint ret = 0;
+ guint sent = 0;
+
+ ThriftSocket *socket = THRIFT_SOCKET (transport);
+ g_return_val_if_fail (socket->sd != THRIFT_INVALID_SOCKET, FALSE);
+
+ while (sent < len)
+ {
+ ret = send (socket->sd, (guint8 *)buf + sent, len - sent, 0);
+ if (ret < 0)
+ {
+ g_set_error (error, THRIFT_TRANSPORT_ERROR,
+ THRIFT_TRANSPORT_ERROR_SEND,
+ "failed to send %d bytes - %s", len, strerror(errno));
+ return FALSE;
+ }
+ sent += ret;
+ }
+
+ return TRUE;
+}
+
+/* implements thrift_transport_write_end
+ * called when write is complete. nothing to do on our end. */
+gboolean
+thrift_socket_write_end (ThriftTransport *transport, GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (transport);
+ THRIFT_UNUSED_VAR (error);
+ return TRUE;
+}
+
+/* implements thrift_transport_flush
+ * flush pending data. since we are not buffered, this is a no-op */
+gboolean
+thrift_socket_flush (ThriftTransport *transport, GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (transport);
+ THRIFT_UNUSED_VAR (error);
+ return TRUE;
+}
+
+/* initializes the instance */
+static void
+thrift_socket_init (ThriftSocket *socket)
+{
+ socket->sd = THRIFT_INVALID_SOCKET;
+}
+
+/* destructor */
+static void
+thrift_socket_finalize (GObject *object)
+{
+ ThriftSocket *socket = THRIFT_SOCKET (object);
+
+ if (socket->hostname != NULL)
+ {
+ g_free (socket->hostname);
+ }
+ socket->hostname = NULL;
+ if (socket->path != NULL)
+ {
+ g_free (socket->path);
+ }
+
+ if (socket->sd != THRIFT_INVALID_SOCKET)
+ {
+ close (socket->sd);
+ }
+ socket->sd = THRIFT_INVALID_SOCKET;
+}
+
+/* property accessor */
+void
+thrift_socket_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ ThriftSocket *socket = THRIFT_SOCKET (object);
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_SOCKET_HOSTNAME:
+ g_value_set_string (value, socket->hostname);
+ break;
+ case PROP_THRIFT_SOCKET_PORT:
+ g_value_set_uint (value, socket->port);
+ break;
+ case PROP_THRIFT_SOCKET_PATH:
+ g_value_set_string (value, socket->path);
+ break;
+ }
+}
+
+/* property mutator */
+void
+thrift_socket_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ ThriftSocket *socket = THRIFT_SOCKET (object);
+
+ THRIFT_UNUSED_VAR (pspec);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_SOCKET_HOSTNAME:
+ if (socket->hostname) {
+ g_free(socket->hostname);
+ }
+ socket->hostname = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_THRIFT_SOCKET_PORT:
+ socket->port = g_value_get_uint (value);
+ break;
+ case PROP_THRIFT_SOCKET_PATH:
+ if (socket->path) {
+ g_free(socket->path);
+ }
+ socket->path = g_strdup (g_value_get_string (value));
+ break;
+ }
+}
+
+/* initializes the class */
+static void
+thrift_socket_class_init (ThriftSocketClass *cls)
+{
+ ThriftTransportClass *ttc = THRIFT_TRANSPORT_CLASS (cls);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (cls);
+ GParamSpec *param_spec = NULL;
+
+ /* setup accessors and mutators */
+ gobject_class->get_property = thrift_socket_get_property;
+ gobject_class->set_property = thrift_socket_set_property;
+
+ param_spec = g_param_spec_string ("hostname",
+ "hostname (construct)",
+ "Set the hostname of the remote host",
+ "localhost", /* default value */
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_THRIFT_SOCKET_HOSTNAME,
+ param_spec);
+
+ param_spec = g_param_spec_uint ("port",
+ "port (construct)",
+ "Set the port of the remote host",
+ 0u, /* min */
+ 65535u, /* max */
+ 9090, /* default by convention */
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_THRIFT_SOCKET_PORT,
+ param_spec);
+
+ param_spec = g_param_spec_string ("path",
+ "path (construct)",
+ "Set the path of the remote host",
+ NULL, /* default value */
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_THRIFT_SOCKET_PATH,
+ param_spec);
+
+ gobject_class->finalize = thrift_socket_finalize;
+ ttc->is_open = thrift_socket_is_open;
+ ttc->peek = thrift_socket_peek;
+ ttc->open = thrift_socket_open;
+ ttc->close = thrift_socket_close;
+ ttc->read = thrift_socket_read;
+ ttc->read_end = thrift_socket_read_end;
+ ttc->write = thrift_socket_write;
+ ttc->write_end = thrift_socket_write_end;
+ ttc->flush = thrift_socket_flush;
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_socket.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_socket.h
new file mode 100644
index 000000000..c91f52f18
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_socket.h
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_SOCKET_H
+#define _THRIFT_SOCKET_H
+
+#include <glib-object.h>
+
+#include <thrift/c_glib/transport/thrift_transport.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_socket.h
+ * \brief Socket implementation of a Thrift transport. Subclasses the
+ * ThriftTransport class.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_SOCKET (thrift_socket_get_type ())
+#define THRIFT_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_SOCKET, ThriftSocket))
+#define THRIFT_IS_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_SOCKET))
+#define THRIFT_SOCKET_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_SOCKET, ThriftSocketClass))
+#define THRIFT_IS_SOCKET_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_SOCKET))
+#define THRIFT_SOCKET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_SOCKET, ThriftSocketClass))
+
+typedef struct _ThriftSocket ThriftSocket;
+
+/*!
+ * Thrift Socket instance.
+ */
+struct _ThriftSocket
+{
+ ThriftTransport parent;
+
+ /* private */
+ gchar *hostname;
+ guint port;
+ gchar *path;
+ int sd;
+};
+
+typedef struct _ThriftSocketClass ThriftSocketClass;
+
+/*!
+ * Thrift Socket class.
+ */
+struct _ThriftSocketClass
+{
+ ThriftTransportClass parent;
+};
+
+/* used by THRIFT_TYPE_SOCKET */
+GType thrift_socket_get_type (void);
+
+G_END_DECLS
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_ssl_socket.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_ssl_socket.c
new file mode 100644
index 000000000..df17fa6ee
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_ssl_socket.c
@@ -0,0 +1,804 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <errno.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <openssl/ssl.h>
+#include <pthread.h>
+
+#include <glib-object.h>
+#include <glib.h>
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/transport/thrift_socket.h>
+#include <thrift/c_glib/transport/thrift_ssl_socket.h>
+
+
+#if defined(WIN32)
+#define MUTEX_TYPE HANDLE
+#define MUTEX_SETUP(x) (x) = CreateMutex(NULL, FALSE, NULL)
+#define MUTEX_CLEANUP(x) CloseHandle(x)
+#define MUTEX_LOCK(x) WaitForSingleObject((x), INFINITE)
+#define MUTEX_UNLOCK(x) ReleaseMutex(x)
+#else
+#define MUTEX_TYPE pthread_mutex_t
+#define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL)
+#define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x))
+#define MUTEX_LOCK(x) pthread_mutex_lock(&(x))
+#define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x))
+#endif
+
+#define OPENSSL_VERSION_NO_THREAD_ID 0x10000000L
+
+
+/* object properties */
+enum _ThriftSSLSocketProperties
+{
+ PROP_THRIFT_SSL_SOCKET_CONTEXT = 3,
+ PROP_THRIFT_SSL_SELF_SIGNED
+};
+
+/* To hold a global state management of openssl for all instances */
+static gboolean thrift_ssl_socket_openssl_initialized=FALSE;
+/* This array will store all of the mutexes available to OpenSSL. */
+static MUTEX_TYPE *thrift_ssl_socket_global_mutex_buf=NULL;
+
+
+/**
+ * OpenSSL uniq id function.
+ *
+ * @return thread id
+ */
+static unsigned long thrift_ssl_socket_static_id_function(void)
+{
+#if defined(WIN32)
+ return GetCurrentThreadId();
+#else
+ return ((unsigned long) pthread_self());
+#endif
+}
+
+static void thrift_ssl_socket_static_locking_callback(int mode, int n, const char* unk, int id) {
+ if (mode & CRYPTO_LOCK)
+ MUTEX_LOCK(thrift_ssl_socket_global_mutex_buf[n]);
+ else
+ MUTEX_UNLOCK(thrift_ssl_socket_global_mutex_buf[n]);
+}
+
+static int thrift_ssl_socket_static_thread_setup(void)
+{
+ int i;
+
+ thrift_ssl_socket_global_mutex_buf = malloc(CRYPTO_num_locks() * sizeof(MUTEX_TYPE));
+ if (!thrift_ssl_socket_global_mutex_buf)
+ return 0;
+ for (i = 0; i < CRYPTO_num_locks( ); i++)
+ MUTEX_SETUP(thrift_ssl_socket_global_mutex_buf[i]);
+ CRYPTO_set_id_callback(thrift_ssl_socket_static_id_function);
+ CRYPTO_set_locking_callback(thrift_ssl_socket_static_locking_callback);
+ return 1;
+}
+
+static int thrift_ssl_socket_static_thread_cleanup(void)
+{
+ int i;
+ if (!thrift_ssl_socket_global_mutex_buf)
+ return 0;
+ CRYPTO_set_id_callback(NULL);
+ CRYPTO_set_locking_callback(NULL);
+ for (i = 0; i < CRYPTO_num_locks( ); i++)
+ MUTEX_CLEANUP(thrift_ssl_socket_global_mutex_buf[i]);
+ free(thrift_ssl_socket_global_mutex_buf);
+ thrift_ssl_socket_global_mutex_buf = NULL;
+ return 1;
+}
+
+/*
+static void* thrift_ssl_socket_dyn_lock_create_callback(const char* unk, int id) {
+ g_print("We should create a lock\n");
+ return NULL;
+}
+
+static void thrift_ssl_socket_dyn_lock_callback(int mode, void* lock, const char* unk, int id) {
+ if (lock != NULL) {
+ if (mode & CRYPTO_LOCK) {
+ g_printf("We should lock thread %d\n");
+ } else {
+ g_printf("We should unlock thread %d\n");
+ }
+ }
+}
+
+static void thrift_ssl_socket_dyn_lock_destroy_callback(void* lock, const char* unk, int id) {
+ g_printf("We must destroy the lock\n");
+}
+ */
+
+
+G_DEFINE_TYPE(ThriftSSLSocket, thrift_ssl_socket, THRIFT_TYPE_SOCKET)
+
+
+
+/**
+ * When there's a thread context attached, we pass the SSL socket context so it
+ * can check if the error is outside SSL, on I/O for example
+ * @param socket
+ * @param error_msg
+ * @param thrift_error_no
+ * @param ssl_error
+ * @param error
+ */
+static
+void thrift_ssl_socket_get_ssl_error(ThriftSSLSocket *socket, const guchar *error_msg, guint thrift_error_no, int ssl_error, GError **error)
+{
+ unsigned long error_code;
+ char buffer[1024];
+ int buffer_size=1024;
+ gboolean first_error = TRUE;
+ int ssl_error_type = SSL_get_error(socket->ssl, ssl_error);
+ if(ssl_error_type>0){
+ switch(ssl_error_type){
+ case SSL_ERROR_SSL:
+ buffer_size-=snprintf(buffer, buffer_size, "SSL %s: ", error_msg);
+ while ((error_code = ERR_get_error()) != 0 && buffer_size>1) {
+ const char* reason = ERR_reason_error_string(error_code);
+ if(reason!=NULL){
+ if(!first_error) {
+ buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "\n\t");
+ first_error=FALSE;
+ }
+ buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%lX(%s) -> %s", error_code, reason, SSL_state_string(socket->ssl));
+ }
+ }
+ break;
+ case SSL_ERROR_SYSCALL:
+ buffer_size-=snprintf(buffer, buffer_size, "%s: ", error_msg);
+ buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%lX -> %s", errno, strerror(errno));
+ break;
+ case SSL_ERROR_WANT_READ:
+ buffer_size-=snprintf(buffer, buffer_size, "%s: ", error_msg);
+ buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%lX -> %s", ssl_error_type, "Error while reading from underlaying layer");
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ buffer_size-=snprintf(buffer, buffer_size, "%s: ", error_msg);
+ buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%lX -> %s", ssl_error_type, "Error while writting to underlaying layer");
+ break;
+
+ }
+ g_set_error (error, THRIFT_TRANSPORT_ERROR,
+ thrift_error_no, "%s", buffer);
+ }
+}
+
+/**
+ * For global SSL errors
+ * @param error_msg
+ * @param thrift_error_no
+ * @param error
+ */
+static
+void thrift_ssl_socket_get_error(const guchar *error_msg, guint thrift_error_no, GError **error)
+{
+ unsigned long error_code;
+ while ((error_code = ERR_get_error()) != 0) {
+ const char* reason = ERR_reason_error_string(error_code);
+ if (reason == NULL) {
+ g_set_error (error, THRIFT_TRANSPORT_ERROR,
+ thrift_error_no,
+ "SSL error %lX: %s", error_code, error_msg);
+ }else{
+ g_set_error (error, THRIFT_TRANSPORT_ERROR,
+ thrift_error_no,
+ "SSL error %lX %s: %s", error_code,reason, error_msg);
+ }
+ }
+}
+
+
+
+/* implements thrift_transport_is_open */
+gboolean
+thrift_ssl_socket_is_open (ThriftTransport *transport)
+{
+ return thrift_socket_is_open(transport);
+}
+
+/* overrides thrift_transport_peek */
+gboolean
+thrift_ssl_socket_peek (ThriftTransport *transport, GError **error)
+{
+ gboolean retval = FALSE;
+ ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport);
+ if (thrift_ssl_socket_is_open (transport))
+ {
+ int rc;
+ gchar byte;
+ rc = SSL_peek(ssl_socket->ssl, &byte, 1);
+ if (rc < 0) {
+ thrift_ssl_socket_get_ssl_error(ssl_socket, "Check socket data",
+ THRIFT_SSL_SOCKET_ERROR_SSL, rc, error);
+ }
+ if (rc == 0) {
+ ERR_clear_error();
+ }
+ retval = (rc > 0);
+ }
+ return retval;
+}
+
+/* implements thrift_transport_open */
+gboolean
+thrift_ssl_socket_open (ThriftTransport *transport, GError **error)
+{
+ ERR_clear_error();
+
+ if (!thrift_socket_open(transport, error)) {
+ return FALSE;
+ }
+
+ if (!THRIFT_SSL_SOCKET_GET_CLASS(transport)->handle_handshake(transport, error)) {
+ thrift_ssl_socket_close(transport, NULL);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* implements thrift_transport_close */
+gboolean
+thrift_ssl_socket_close (ThriftTransport *transport, GError **error)
+{
+ gboolean retval = FALSE;
+ ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET(transport);
+ if(ssl_socket!=NULL && ssl_socket->ssl) {
+ int rc = SSL_shutdown(ssl_socket->ssl);
+/* if (rc < 0) {
+ int errno_copy = THRIFT_SSL_SOCKET_ERROR_SSL;
+ }*/
+ SSL_free(ssl_socket->ssl);
+ ssl_socket->ssl = NULL;
+ ERR_remove_state(0);
+ }
+ return thrift_socket_close(transport, error);
+}
+
+/* implements thrift_transport_read */
+gint32
+thrift_ssl_socket_read (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error)
+{
+ guint maxRecvRetries_ = 10;
+ ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport);
+ guint bytes = 0;
+ guint retries = 0;
+ ThriftSocket *socket = THRIFT_SOCKET (transport);
+ g_return_val_if_fail (socket->sd != THRIFT_INVALID_SOCKET && ssl_socket->ssl!=NULL, FALSE);
+
+ for (retries=0; retries < maxRecvRetries_; retries++) {
+ bytes = SSL_read(ssl_socket->ssl, buf, len);
+ if (bytes >= 0)
+ break;
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ if (SSL_get_error(ssl_socket->ssl, bytes) == SSL_ERROR_SYSCALL) {
+ if (ERR_get_error() == 0 && errno_copy == THRIFT_EINTR) {
+ continue;
+ }
+ }else{
+ thrift_ssl_socket_get_ssl_error(ssl_socket, "Receive error",
+ THRIFT_SSL_SOCKET_ERROR_SSL, bytes, error);
+
+ }
+ return -1;
+ }
+ return bytes;
+}
+
+/* implements thrift_transport_read_end
+ * called when write is complete. nothing to do on our end. */
+gboolean
+thrift_ssl_socket_read_end (ThriftTransport *transport, GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (transport);
+ THRIFT_UNUSED_VAR (error);
+ return TRUE;
+}
+
+/* implements thrift_transport_write */
+gboolean
+thrift_ssl_socket_write (ThriftTransport *transport, const gpointer buf,
+ const guint32 len, GError **error)
+{
+ ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport);
+ gint ret = 0;
+ guint sent = 0;
+ ThriftSocket *socket = THRIFT_SOCKET (transport);
+ g_return_val_if_fail (socket->sd != THRIFT_INVALID_SOCKET && ssl_socket->ssl!=NULL, FALSE);
+
+ while (sent < len)
+ {
+ ret = SSL_write (ssl_socket->ssl, (guint8 *)buf + sent, len - sent);
+ if (ret < 0)
+ {
+ thrift_ssl_socket_get_ssl_error(ssl_socket, "Send error",
+ THRIFT_SSL_SOCKET_ERROR_SSL, ret, error);
+ return FALSE;
+ }
+ sent += ret;
+ }
+
+ return sent==len;
+}
+
+/* implements thrift_transport_write_end
+ * called when write is complete. nothing to do on our end. */
+gboolean
+thrift_ssl_socket_write_end (ThriftTransport *transport, GError **error)
+{
+ /* satisfy -Wall */
+ THRIFT_UNUSED_VAR (transport);
+ THRIFT_UNUSED_VAR (error);
+ return TRUE;
+}
+
+/* implements thrift_transport_flush
+ * flush pending data. since we are not buffered, this is a no-op */
+gboolean
+thrift_ssl_socket_flush (ThriftTransport *transport, GError **error)
+{
+ ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport);
+
+ ThriftSocket *socket = THRIFT_SOCKET (transport);
+ g_return_val_if_fail (socket->sd != THRIFT_INVALID_SOCKET && ssl_socket->ssl!=NULL, FALSE);
+
+ BIO* bio = SSL_get_wbio(ssl_socket->ssl);
+ if (bio == NULL) {
+ g_set_error (error, THRIFT_TRANSPORT_ERROR,
+ THRIFT_TRANSPORT_ERROR_SEND,
+ "failed to flush, wbio returned null");
+ return FALSE;
+ }
+ if (BIO_flush(bio) != 1) {
+ g_set_error (error, THRIFT_TRANSPORT_ERROR,
+ THRIFT_TRANSPORT_ERROR_SEND,
+ "failed to flush it returned error");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+gboolean
+thrift_ssl_socket_handle_handshake(ThriftTransport * transport, GError **error)
+{
+ ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport);
+ ThriftSocket *socket = THRIFT_SOCKET (transport);
+ g_return_val_if_fail (thrift_transport_is_open (transport), FALSE);
+
+ if(THRIFT_SSL_SOCKET_GET_CLASS(ssl_socket)->create_ssl_context(transport, error)){
+ /*Context created*/
+ SSL_set_fd(ssl_socket->ssl, socket->sd);
+ int rc;
+ if(ssl_socket->server){
+ rc = SSL_accept(ssl_socket->ssl);
+ }else{
+ rc = SSL_connect(ssl_socket->ssl);
+ }
+ if (rc <= 0) {
+ thrift_ssl_socket_get_ssl_error(ssl_socket, "Error while connect/bind", THRIFT_SSL_SOCKET_ERROR_CONNECT_BIND, rc, error);
+ return FALSE;
+ }
+ }else
+ return FALSE;
+
+ return thrift_ssl_socket_authorize(transport, error);
+}
+
+gboolean
+thrift_ssl_socket_create_ssl_context(ThriftTransport * transport, GError **error)
+{
+ ThriftSSLSocket *socket = THRIFT_SSL_SOCKET (transport);
+
+ if(socket->ctx!=NULL){
+ if(socket->ssl!=NULL) {
+ return TRUE;
+ }
+
+ socket->ssl = SSL_new(socket->ctx);
+ if (socket->ssl == NULL) {
+ g_set_error (error, THRIFT_TRANSPORT_ERROR,
+ THRIFT_SSL_SOCKET_ERROR_TRANSPORT,
+ "Unable to create default SSL context");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+gboolean thrift_ssl_load_cert_from_file(ThriftSSLSocket *ssl_socket, const char *file_name)
+{
+ char error_buffer[255];
+ if (!thrift_ssl_socket_openssl_initialized) {
+ g_error("OpenSSL is not initialized yet");
+ return FALSE;
+ }
+ int rc = SSL_CTX_load_verify_locations(ssl_socket->ctx, file_name, NULL);
+ if (rc != 1) { /*verify authentication result*/
+ ERR_error_string_n(ERR_get_error(), error_buffer, 254);
+ g_warning("Load of certificates failed: %s!", error_buffer);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+gboolean thrift_ssl_load_cert_from_buffer(ThriftSSLSocket *ssl_socket, const char chain_certs[])
+{
+ gboolean retval = FALSE;
+ /* Load chain of certs*/
+ X509 *cacert=NULL;
+ BIO *mem = BIO_new_mem_buf(chain_certs,strlen(chain_certs));
+ X509_STORE *cert_store = SSL_CTX_get_cert_store(ssl_socket->ctx);
+
+ if(cert_store!=NULL){
+ int index = 0;
+ while ((cacert = PEM_read_bio_X509(mem, NULL, 0, NULL))!=NULL) {
+ if(cacert) {
+ X509_STORE_add_cert(cert_store, cacert);
+ X509_free(cacert);
+ cacert=NULL;
+ } /* Free immediately */
+ index++;
+ }
+ retval=TRUE;
+ }
+ BIO_free(mem);
+ return retval;
+}
+
+gboolean
+thrift_ssl_socket_authorize(ThriftTransport * transport, GError **error)
+{
+ ThriftSocket *socket = THRIFT_SOCKET (transport);
+ ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport);
+ ThriftSSLSocketClass *cls = THRIFT_SSL_SOCKET_GET_CLASS(ssl_socket);
+ gboolean authorization_result = FALSE;
+
+ if(cls!=NULL && ssl_socket->ssl!=NULL){
+ int rc = SSL_get_verify_result(ssl_socket->ssl);
+ if (rc != X509_V_OK) { /* verify authentication result */
+ if (rc == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && ssl_socket->allow_selfsigned) {
+ g_debug("The certificate is a self-signed certificate and configuration allows it");
+ } else {
+ g_set_error (error,
+ THRIFT_TRANSPORT_ERROR,
+ THRIFT_SSL_SOCKET_ERROR_SSL_CERT_VALIDATION_FAILED,
+ "The certificate verification failed: %s (%d)", X509_verify_cert_error_string(rc), rc);
+ return FALSE;
+ }
+ }
+
+ X509* cert = SSL_get_peer_certificate(ssl_socket->ssl);
+ if (cert == NULL) {
+ if (SSL_get_verify_mode(ssl_socket->ssl) & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+ g_set_error (error,
+ THRIFT_TRANSPORT_ERROR,
+ THRIFT_SSL_SOCKET_ERROR_SSL_CERT_VALIDATION_FAILED,
+ "No certificate present. Are you connecting SSL server?");
+ return FALSE;
+ }
+ g_debug("No certificate required");
+ return TRUE;
+ }
+
+ /* certificate is present, since we don't support access manager we are done */
+ if (cls->authorize_peer == NULL) {
+ X509_free(cert);
+ g_debug("Certificate presented but we're not checking it");
+ return TRUE;
+ } else {
+ /* both certificate and access manager are present */
+ struct sockaddr_storage sa;
+ socklen_t saLength = sizeof(struct sockaddr_storage);
+ if (getpeername(socket->sd, (struct sockaddr*)&sa, &saLength) != 0) {
+ sa.ss_family = AF_UNSPEC;
+ }
+ authorization_result = cls->authorize_peer(transport, cert, &sa, error);
+ }
+ if(cert != NULL) {
+ X509_free(cert);
+ }
+ }
+
+ return authorization_result;
+}
+
+
+/* initializes the instance */
+static void
+thrift_ssl_socket_init (ThriftSSLSocket *socket)
+{
+ GError *error = NULL;
+ socket->ssl = NULL;
+ socket->ctx = thrift_ssl_socket_context_initialize(SSLTLS, &error);
+ if(socket->ctx == NULL) {
+ g_info("The SSL context was not automatically initialized with protocol %d", SSLTLS);
+ if(error!=NULL){
+ g_info("Reported reason %s", error->message);
+ g_error_free (error);
+ }
+ }
+ socket->server = FALSE;
+ socket->allow_selfsigned = FALSE;
+
+}
+
+/* destructor */
+static void
+thrift_ssl_socket_finalize (GObject *object)
+{
+ ThriftSSLSocket *socket = THRIFT_SSL_SOCKET (object);
+ GError *error=NULL;
+ if(socket!=NULL){
+ g_debug("Instance %p destroyed", (void *)socket);
+ if(socket->ssl != NULL)
+ {
+ thrift_ssl_socket_close(THRIFT_TRANSPORT(object), &error);
+ socket->ssl=NULL;
+ }
+
+ if(socket->ctx!=NULL){
+ g_debug("Freeing the context for the instance");
+ SSL_CTX_free(socket->ctx);
+ socket->ctx=NULL;
+ }
+ }
+
+ if (G_OBJECT_CLASS (thrift_ssl_socket_parent_class)->finalize)
+ (*G_OBJECT_CLASS (thrift_ssl_socket_parent_class)->finalize) (object);
+}
+
+/* property accessor */
+void
+thrift_ssl_socket_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ ThriftSSLSocket *socket = THRIFT_SSL_SOCKET (object);
+ THRIFT_UNUSED_VAR (pspec);
+
+ switch (property_id)
+ {
+ case PROP_THRIFT_SSL_SOCKET_CONTEXT:
+ g_value_set_pointer (value, socket->ctx);
+ break;
+ }
+}
+
+/* property mutator */
+void
+thrift_ssl_socket_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ ThriftSSLSocket *socket = THRIFT_SSL_SOCKET (object);
+
+ THRIFT_UNUSED_VAR (pspec);
+ switch (property_id)
+ {
+ case PROP_THRIFT_SSL_SOCKET_CONTEXT:
+ if(socket->ctx!=NULL){
+ g_debug("Freeing the context since we are setting a new one");
+ SSL_CTX_free(socket->ctx);
+ }
+ socket->ctx = g_value_get_pointer(value); /* We copy the context */
+ break;
+
+ case PROP_THRIFT_SSL_SELF_SIGNED:
+ socket->allow_selfsigned = g_value_get_boolean(value);
+ break;
+ default:
+ g_warning("Trying to set property %i that doesn't exists!", property_id);
+ /* thrift_socket_set_property(object, property_id, value, pspec); */
+ break;
+ }
+}
+
+void
+thrift_ssl_socket_initialize_openssl(void)
+{
+ if(thrift_ssl_socket_openssl_initialized){
+ return;
+ }
+ thrift_ssl_socket_openssl_initialized=TRUE;
+ SSL_library_init();
+ ERR_load_crypto_strings();
+ SSL_load_error_strings();
+ ERR_load_BIO_strings();
+
+ /* Setup locking */
+ g_debug("We setup %d threads locks", thrift_ssl_socket_static_thread_setup());
+
+ /* dynamic locking
+ CRYPTO_set_dynlock_create_callback(thrift_ssl_socket_dyn_lock_create_callback);
+ CRYPTO_set_dynlock_lock_callback(thrift_ssl_socket_dyn_lock_callback);
+ CRYPTO_set_dynlock_destroy_callback(thrift_ssl_socket_dyn_lock_destroy_callback);
+ */
+}
+
+
+void thrift_ssl_socket_finalize_openssl(void)
+{
+ if (!thrift_ssl_socket_openssl_initialized) {
+ return;
+ }
+ thrift_ssl_socket_openssl_initialized = FALSE;
+
+ g_debug("We cleared %d threads locks", thrift_ssl_socket_static_thread_cleanup());
+ /* Not supported
+ CRYPTO_set_locking_callback(NULL);
+ CRYPTO_set_dynlock_create_callback(NULL);
+ CRYPTO_set_dynlock_lock_callback(NULL);
+ CRYPTO_set_dynlock_destroy_callback(NULL);
+ */
+ ERR_free_strings();
+ EVP_cleanup();
+ CRYPTO_cleanup_all_ex_data();
+ ERR_remove_state(0);
+}
+
+
+/* initializes the class */
+static void
+thrift_ssl_socket_class_init (ThriftSSLSocketClass *cls)
+{
+ ThriftTransportClass *ttc = THRIFT_TRANSPORT_CLASS (cls);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (cls);
+ GParamSpec *param_spec = NULL;
+
+ g_debug("Initialization of ThriftSSLSocketClass");
+ /* setup accessors and mutators */
+ gobject_class->get_property = thrift_ssl_socket_get_property;
+ gobject_class->set_property = thrift_ssl_socket_set_property;
+ param_spec = g_param_spec_pointer ("ssl_context",
+ "SSLContext",
+ "Set the SSL context for handshake with the remote host",
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_THRIFT_SSL_SOCKET_CONTEXT,
+ param_spec);
+ param_spec = g_param_spec_boolean ("ssl_accept_selfsigned",
+ "Accept Self Signed",
+ "Whether or not accept self signed certificate",
+ FALSE,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_THRIFT_SSL_SELF_SIGNED,
+ param_spec);
+ /* Class methods */
+ cls->handle_handshake = thrift_ssl_socket_handle_handshake;
+ cls->create_ssl_context = thrift_ssl_socket_create_ssl_context;
+
+ /* Override */
+ gobject_class->finalize = thrift_ssl_socket_finalize;
+ ttc->is_open = thrift_ssl_socket_is_open;
+ ttc->peek = thrift_ssl_socket_peek;
+ ttc->open = thrift_ssl_socket_open;
+ ttc->close = thrift_ssl_socket_close;
+ ttc->read = thrift_ssl_socket_read;
+ ttc->read_end = thrift_ssl_socket_read_end;
+ ttc->write = thrift_ssl_socket_write;
+ ttc->write_end = thrift_ssl_socket_write_end;
+ ttc->flush = thrift_ssl_socket_flush;
+}
+
+
+/*
+ * Public API
+ */
+ThriftSSLSocket*
+thrift_ssl_socket_new(ThriftSSLSocketProtocol ssl_protocol, GError **error)
+{
+ ThriftSSLSocket *thriftSSLSocket = NULL;
+ SSL_CTX *ssl_context = NULL;
+ /* Create the context */
+ if((ssl_context=thrift_ssl_socket_context_initialize(ssl_protocol, error))==NULL){
+ g_warning("We cannot initialize context for protocol %d", ssl_protocol);
+ return thriftSSLSocket;
+ }
+
+ /* FIXME if the protocol is different? */
+ thriftSSLSocket = g_object_new (THRIFT_TYPE_SSL_SOCKET, "ssl_context", ssl_context, NULL);
+ return thriftSSLSocket;
+}
+
+ThriftSSLSocket*
+thrift_ssl_socket_new_with_host(ThriftSSLSocketProtocol ssl_protocol, gchar *hostname, guint port, GError **error)
+{
+ ThriftSSLSocket *thriftSSLSocket = NULL;
+ SSL_CTX *ssl_context = NULL;
+ /* Create the context */
+ if((ssl_context=thrift_ssl_socket_context_initialize(ssl_protocol, error))==NULL){
+ /* FIXME Do error control */
+ return thriftSSLSocket;
+ }
+ /* FIXME if the protocol is different? */
+ thriftSSLSocket = g_object_new (THRIFT_TYPE_SSL_SOCKET, "ssl_context", ssl_context, "hostname", hostname, "port", port, NULL);
+ return thriftSSLSocket;
+}
+
+void thrift_ssl_socket_set_manager(ThriftSSLSocket *ssl_socket, AUTHORIZATION_MANAGER_CALLBACK callback)
+{
+ ThriftSSLSocketClass *sslSocketClass = THRIFT_SSL_SOCKET_GET_CLASS (ssl_socket);
+ if(sslSocketClass){
+ sslSocketClass->authorize_peer = callback;
+ }
+}
+
+
+SSL_CTX*
+thrift_ssl_socket_context_initialize(ThriftSSLSocketProtocol ssl_protocol, GError **error)
+{
+ SSL_CTX* context = NULL;
+ switch(ssl_protocol){
+ case SSLTLS:
+ context = SSL_CTX_new(SSLv23_method());
+ break;
+#ifndef OPENSSL_NO_SSL3
+ case SSLv3:
+ context = SSL_CTX_new(SSLv3_method());
+ break;
+#endif
+ case TLSv1_0:
+ context = SSL_CTX_new(TLSv1_method());
+ break;
+ case TLSv1_1:
+ context = SSL_CTX_new(TLSv1_1_method());
+ break;
+ case TLSv1_2:
+ context = SSL_CTX_new(TLSv1_2_method());
+ break;
+ default:
+ g_set_error (error, THRIFT_TRANSPORT_ERROR,
+ THRIFT_SSL_SOCKET_ERROR_CIPHER_NOT_AVAILABLE,
+ "The SSL protocol is unknown for %d", ssl_protocol);
+ return NULL;
+ break;
+ }
+
+ if (context == NULL) {
+ thrift_ssl_socket_get_error("No cipher overlay", THRIFT_SSL_SOCKET_ERROR_CIPHER_NOT_AVAILABLE, error);
+ return NULL;
+ }
+ SSL_CTX_set_mode(context, SSL_MODE_AUTO_RETRY);
+
+ /* Disable horribly insecure SSLv2 and SSLv3 protocols but allow a handshake
+ with older clients so they get a graceful denial. */
+ if (ssl_protocol == SSLTLS) {
+ SSL_CTX_set_options(context, SSL_OP_NO_SSLv2);
+ SSL_CTX_set_options(context, SSL_OP_NO_SSLv3); /* THRIFT-3164 */
+ }
+
+ return context;
+}
+
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_ssl_socket.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_ssl_socket.h
new file mode 100644
index 000000000..0ca465a0f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_ssl_socket.h
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_SSL_SOCKET_H
+#define _THRIFT_SSL_SOCKET_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+#include <sys/socket.h>
+
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/transport/thrift_socket.h>
+#include <thrift/c_glib/transport/thrift_platform_socket.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_ssl_socket.h
+ * \brief SSL Socket implementation of a Thrift transport. Subclasses the
+ * ThriftSocket class. Based on plain openssl.
+ * In the future we should take a look to https://issues.apache.org/jira/browse/THRIFT-1016
+ */
+
+/* type macros */
+#define THRIFT_TYPE_SSL_SOCKET (thrift_ssl_socket_get_type ())
+#define THRIFT_SSL_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_SSL_SOCKET, ThriftSSLSocket))
+#define THRIFT_IS_SSL_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_SSL_SOCKET))
+#define THRIFT_SSL_SOCKET_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_SSL_SOCKET, ThriftSSLSocketClass))
+#define THRIFT_IS_SSL_SOCKET_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_SSL_SOCKET))
+#define THRIFT_SSL_SOCKET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_SSL_SOCKET, ThriftSSLSocketClass))
+
+
+/* define error/exception types */
+typedef enum
+{
+ THRIFT_SSL_SOCKET_ERROR_TRANSPORT=7,
+ THRIFT_SSL_SOCKET_ERROR_CONNECT_BIND,
+ THRIFT_SSL_SOCKET_ERROR_CIPHER_NOT_AVAILABLE,
+ THRIFT_SSL_SOCKET_ERROR_SSL,
+ THRIFT_SSL_SOCKET_ERROR_SSL_CERT_VALIDATION_FAILED
+} ThriftSSLSocketError;
+
+
+typedef struct _ThriftSSLSocket ThriftSSLSocket;
+
+/*!
+ * Thrift SSL Socket instance.
+ */
+struct _ThriftSSLSocket
+{
+ ThriftSocket parent;
+
+ /* private */
+ SSL *ssl;
+ SSL_CTX* ctx;
+ gboolean server;
+ gboolean allow_selfsigned;
+};
+
+typedef struct _ThriftSSLSocketClass ThriftSSLSocketClass;
+typedef gboolean (* AUTHORIZATION_MANAGER_CALLBACK) (ThriftTransport * transport, X509 *cert, struct sockaddr_storage *addr, GError **error);
+
+/*!
+ * Thrift Socket class.
+ */
+struct _ThriftSSLSocketClass
+{
+ ThriftSocketClass parent;
+
+ gboolean (* handle_handshake) (ThriftTransport * transport, GError **error);
+ gboolean (* create_ssl_context) (ThriftTransport * transport, GError **error);
+ gboolean (* authorize_peer) (ThriftTransport * transport, X509 *cert, struct sockaddr_storage *addr, GError **error);
+
+ /* Padding to allow adding up to 12 new virtual functions without
+ * breaking ABI. */
+ gpointer padding[12];
+};
+
+enum _ThriftSSLSocketProtocol {
+ SSLTLS = 0, /* Supports SSLv2 and SSLv3 handshake but only negotiates at TLSv1_0 or later. */
+/*SSLv2 = 1, HORRIBLY INSECURE! */
+ SSLv3 = 2, /* Supports SSLv3 only - also horribly insecure! */
+ TLSv1_0 = 3, /* Supports TLSv1_0 or later. */
+ TLSv1_1 = 4, /* Supports TLSv1_1 or later. */
+ TLSv1_2 = 5, /* Supports TLSv1_2 or later. */
+ LATEST = TLSv1_2
+};
+typedef enum _ThriftSSLSocketProtocol ThriftSSLSocketProtocol;
+
+
+/* Internal functions */
+SSL_CTX*
+thrift_ssl_socket_context_initialize(ThriftSSLSocketProtocol ssl_protocol, GError **error);
+
+
+/* used by THRIFT_TYPE_SSL_SOCKET */
+GType thrift_ssl_socket_get_type (void);
+
+/* Public API */
+
+/**
+ * @brief Set a pinning manager instead of the default one.
+ *
+ * The pinning manager will be used during the SSL handshake to check certificate
+ * and pinning parameters.
+ *
+ * @param ssl_socket SSL Socket to operate on.
+ * @param callback function that will take the control while validating pinning
+ *
+ */
+void thrift_ssl_socket_set_manager(ThriftSSLSocket *ssl_socket, AUTHORIZATION_MANAGER_CALLBACK callback);
+
+/* This is the SSL API */
+/**
+ * Convenience function to create a new SSL context with the protocol specified
+ * and assign this new context to the created ThriftSSLSocket with specified host:port.
+ * @param ssl_protocol
+ * @param hostname
+ * @param port
+ * @param error
+ * @return
+ */
+ThriftSSLSocket*
+thrift_ssl_socket_new_with_host(ThriftSSLSocketProtocol ssl_protocol, gchar *hostname, guint port, GError **error);
+
+/**
+ * Convenience function to create a new SSL context with the protocol specified
+ * and assign this new context to the created ThriftSSLSocket.
+ * @param ssl_protocol
+ * @param error
+ * @return
+ */
+ThriftSSLSocket*
+thrift_ssl_socket_new(ThriftSSLSocketProtocol ssl_protocol, GError **error);
+
+/**
+ * Load a certificate chain from a PEM file.
+ * @param ssl_socket The ssl socket
+ * @param file_name The file name of the PEM certificate chain
+ * @return
+ */
+gboolean
+thrift_ssl_load_cert_from_file(ThriftSSLSocket *ssl_socket, const char *file_name);
+
+/**
+ * Load a certificate chain from memory
+ * @param ssl_socket the ssl socket
+ * @param chain_certs the buffer to load PEM from
+ * @return
+ */
+gboolean
+thrift_ssl_load_cert_from_buffer(ThriftSSLSocket *ssl_socket, const char chain_certs[]);
+
+/**
+ * Check if the ssl socket is open and ready to send and receive
+ * @param transport
+ * @return true if open
+ */
+gboolean
+thrift_ssl_socket_is_open (ThriftTransport *transport);
+
+
+/**
+ * Open connection if required and set the socket to be ready to send and receive
+ * @param transport
+ * @param error
+ * @return true if operation was correct
+ */
+gboolean
+thrift_ssl_socket_open (ThriftTransport *transport, GError **error);
+
+
+/**
+ * @brief Initialization function
+ *
+ * It will initialize OpenSSL function. This initialization will be done app
+ * wide. So if you want to initialize it by yourself you should not call it.
+ * But it means you must handle OpenSSL initialization and handle locking.
+ *
+ * It should be called before anything else.
+ *
+ *
+ */
+void
+thrift_ssl_socket_initialize_openssl(void);
+/**
+ * @brief Finalization function
+ *
+ * It clears all resources initialized in initialize function.
+ *
+ * It should be called after anything else.
+ *
+ *
+ */
+void
+thrift_ssl_socket_finalize_openssl(void);
+
+G_END_DECLS
+#endif
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.c
new file mode 100644
index 000000000..9dd267143
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.c
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/transport/thrift_transport.h>
+
+/* define the GError domain string */
+#define THRIFT_TRANSPORT_ERROR_DOMAIN "thrift-transport-error-quark"
+
+G_DEFINE_ABSTRACT_TYPE(ThriftTransport, thrift_transport, G_TYPE_OBJECT)
+
+gboolean
+thrift_transport_is_open (ThriftTransport *transport)
+{
+ return THRIFT_TRANSPORT_GET_CLASS (transport)->is_open (transport);
+}
+
+gboolean
+thrift_transport_peek (ThriftTransport *transport, GError **error)
+{
+ return THRIFT_TRANSPORT_GET_CLASS (transport)->peek (transport, error);
+}
+
+gboolean
+thrift_transport_open (ThriftTransport *transport, GError **error)
+{
+ return THRIFT_TRANSPORT_GET_CLASS (transport)->open (transport, error);
+}
+
+gboolean
+thrift_transport_close (ThriftTransport *transport, GError **error)
+{
+ return THRIFT_TRANSPORT_GET_CLASS (transport)->close (transport, error);
+}
+
+gint32
+thrift_transport_read (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error)
+{
+ return THRIFT_TRANSPORT_GET_CLASS (transport)->read (transport, buf,
+ len, error);
+}
+
+gboolean
+thrift_transport_read_end (ThriftTransport *transport, GError **error)
+{
+ return THRIFT_TRANSPORT_GET_CLASS (transport)->read_end (transport,
+ error);
+}
+
+gboolean
+thrift_transport_write (ThriftTransport *transport, const gpointer buf,
+ const guint32 len, GError **error)
+{
+ return THRIFT_TRANSPORT_GET_CLASS (transport)->write (transport, buf,
+ len, error);
+}
+
+gboolean
+thrift_transport_write_end (ThriftTransport *transport, GError **error)
+{
+ return THRIFT_TRANSPORT_GET_CLASS (transport)->write_end (transport,
+ error);
+}
+
+gboolean
+thrift_transport_flush (ThriftTransport *transport, GError **error)
+{
+ return THRIFT_TRANSPORT_GET_CLASS (transport)->flush (transport, error);
+}
+
+gint32
+thrift_transport_read_all (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error)
+{
+ return THRIFT_TRANSPORT_GET_CLASS (transport)->read_all (transport, buf,
+ len, error);
+}
+
+/* by default, peek returns true if and only if the transport is open */
+static gboolean
+thrift_transport_real_peek (ThriftTransport *transport, GError **error)
+{
+ THRIFT_UNUSED_VAR (error);
+
+ return THRIFT_TRANSPORT_GET_CLASS (transport)->is_open (transport);
+}
+
+static gint32
+thrift_transport_real_read_all (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error)
+{
+ ThriftTransportClass *ttc;
+ guint32 have;
+ gint32 ret;
+ gint8 *bytes;
+
+ THRIFT_UNUSED_VAR (error);
+
+ ttc = THRIFT_TRANSPORT_GET_CLASS (transport);
+ have = 0;
+ ret = 0;
+ bytes = (gint8*) buf;
+
+ while (have < len) {
+ if ((ret = ttc->read (transport, (gpointer) (bytes + have), len - have,
+ error)) < 0) {
+ return ret;
+ }
+ have += ret;
+ }
+
+ return have;
+}
+
+/* define the GError domain for Thrift transports */
+GQuark
+thrift_transport_error_quark (void)
+{
+ return g_quark_from_static_string (THRIFT_TRANSPORT_ERROR_DOMAIN);
+}
+
+/* class initializer for ThriftTransport */
+static void
+thrift_transport_class_init (ThriftTransportClass *cls)
+{
+ /* set these as virtual methods to be implemented by a subclass */
+ cls->is_open = thrift_transport_is_open;
+ cls->open = thrift_transport_open;
+ cls->close = thrift_transport_close;
+ cls->read = thrift_transport_read;
+ cls->read_end = thrift_transport_read_end;
+ cls->write = thrift_transport_write;
+ cls->write_end = thrift_transport_write_end;
+ cls->flush = thrift_transport_flush;
+
+ /* provide a default implementation for the peek and read_all methods */
+ cls->peek = thrift_transport_real_peek;
+ cls->read_all = thrift_transport_real_read_all;
+}
+
+static void
+thrift_transport_init (ThriftTransport *transport)
+{
+ THRIFT_UNUSED_VAR (transport);
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.h
new file mode 100644
index 000000000..94bb6f507
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.h
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_H
+#define _THRIFT_TRANSPORT_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/*! \file thrift_transport.h
+ * \brief Abstract class for Thrift transports.
+ *
+ * An abstract class is used instead of an interface because:
+ * - interfaces can't seem to be used as properties. ThriftProtocol has
+ * a ThriftTransport as an object property.
+ * - if a method needs to be added that all subclasses can use, a class
+ * is necessary.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_TRANSPORT (thrift_transport_get_type ())
+#define THRIFT_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_TRANSPORT, ThriftTransport))
+#define THRIFT_IS_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_TRANSPORT))
+#define THRIFT_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_TRANSPORT, ThriftTransportClass))
+#define THRIFT_IS_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_TRANSPORT))
+#define THRIFT_TRANSPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_TRANSPORT, ThriftTransportClass))
+
+typedef struct _ThriftTransport ThriftTransport;
+
+/*!
+ * Thrift Protocol object
+ */
+struct _ThriftTransport
+{
+ GObject parent;
+};
+
+typedef struct _ThriftTransportClass ThriftTransportClass;
+
+/*!
+ * Thrift Transport class
+ */
+struct _ThriftTransportClass
+{
+ GObjectClass parent;
+
+ /* vtable */
+ gboolean (*is_open) (ThriftTransport *transport);
+ gboolean (*peek) (ThriftTransport *transport, GError **error);
+ gboolean (*open) (ThriftTransport *transport, GError **error);
+ gboolean (*close) (ThriftTransport *transport, GError **error);
+ gint32 (*read) (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error);
+ gboolean (*read_end) (ThriftTransport *transport, GError **error);
+ gboolean (*write) (ThriftTransport *transport, const gpointer buf,
+ const guint32 len, GError **error);
+ gboolean (*write_end) (ThriftTransport *transport, GError **error);
+ gboolean (*flush) (ThriftTransport *transport, GError **error);
+ gint32 (*read_all) (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error);
+};
+
+/* used by THRIFT_TYPE_TRANSPORT */
+GType thrift_transport_get_type (void);
+
+/* virtual public methods */
+
+/*!
+ * Checks if this transport is opened.
+ * \public \memberof ThriftTransportInterface
+ */
+gboolean thrift_transport_is_open (ThriftTransport *transport);
+
+/*!
+ * Open the transport for reading and writing.
+ * \public \memberof ThriftTransportInterface
+ */
+gboolean thrift_transport_open (ThriftTransport *transport, GError **error);
+
+/*!
+ * Tests whether there is more data to read or if the remote side is still
+ * open. By default this is true whenever the transport is open, but
+ * implementations should add logic to test for this condition where possible
+ * (i.e. on a socket).
+ *
+ * This is used by a server to check if it should listen for another request.
+ * \public \memberof ThriftTransportInterface
+ */
+gboolean thrift_transport_peek (ThriftTransport *transport, GError **error);
+
+/*!
+ * Close the transport.
+ * \public \memberof ThriftTransportInterface
+ */
+gboolean thrift_transport_close (ThriftTransport *transport, GError **error);
+
+/*!
+ * Read some data into the buffer buf.
+ * \public \memberof ThriftTransportInterface
+ */
+gint32 thrift_transport_read (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error);
+
+/*!
+ * Called when read is completed.
+ * \public \memberof ThriftTransportInterface
+ */
+gboolean thrift_transport_read_end (ThriftTransport *transport, GError **error);
+
+/*!
+ * Writes data from a buffer to the transport.
+ * \public \memberof ThriftTransportInterface
+ */
+gboolean thrift_transport_write (ThriftTransport *transport, const gpointer buf,
+ const guint32 len, GError **error);
+
+/*!
+ * Called when write is completed.
+ * \public \memberof ThriftTransportInterface
+ */
+gboolean thrift_transport_write_end (ThriftTransport *transport,
+ GError **error);
+
+/*!
+ * Flushes any pending data to be written. Typically used with buffered
+ * transport mechanisms.
+ * \public \memberof ThriftTransportInterface
+ */
+gboolean thrift_transport_flush (ThriftTransport *transport, GError **error);
+
+/*!
+ * Read len bytes of data into the buffer buf.
+ * \public \memberof ThriftTransportInterface
+ */
+gint32 thrift_transport_read_all (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error);
+
+/* define error/exception types */
+typedef enum
+{
+ THRIFT_TRANSPORT_ERROR_UNKNOWN,
+ THRIFT_TRANSPORT_ERROR_HOST,
+ THRIFT_TRANSPORT_ERROR_SOCKET,
+ THRIFT_TRANSPORT_ERROR_CONNECT,
+ THRIFT_TRANSPORT_ERROR_SEND,
+ THRIFT_TRANSPORT_ERROR_RECEIVE,
+ THRIFT_TRANSPORT_ERROR_CLOSE
+} ThriftTransportError;
+
+/* define an error domain for GError to use */
+GQuark thrift_transport_error_quark (void);
+#define THRIFT_TRANSPORT_ERROR (thrift_transport_error_quark ())
+
+/* define macro for invalid socket */
+#define THRIFT_INVALID_SOCKET (-1)
+
+G_END_DECLS
+
+#endif /* _THRIFT_TRANSPORT_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_transport_factory.c b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_transport_factory.c
new file mode 100644
index 000000000..a2025cf4b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_transport_factory.c
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/transport/thrift_transport_factory.h>
+
+G_DEFINE_TYPE(ThriftTransportFactory, thrift_transport_factory, G_TYPE_OBJECT)
+
+/* builds a transport from the base transport. */
+ThriftTransport *
+thrift_transport_factory_get_transport (ThriftTransportFactory *factory,
+ ThriftTransport *transport)
+{
+ THRIFT_UNUSED_VAR (factory);
+ return transport;
+}
+
+static void
+thrift_transport_factory_class_init (ThriftTransportFactoryClass *cls)
+{
+ cls->get_transport = thrift_transport_factory_get_transport;
+}
+
+static void
+thrift_transport_factory_init (ThriftTransportFactory *factory)
+{
+ THRIFT_UNUSED_VAR (factory);
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_transport_factory.h b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_transport_factory.h
new file mode 100644
index 000000000..0e3da302e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/src/thrift/c_glib/transport/thrift_transport_factory.h
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_FACTORY_H
+#define _THRIFT_TRANSPORT_FACTORY_H
+
+#include <glib-object.h>
+
+#include "thrift_transport.h"
+
+G_BEGIN_DECLS
+
+/*! \file thrift_transport_factory.h
+ * \brief Base class for Thrift Transport Factories. Used by Thrift Servers
+ * to obtain a client transport from an existing transport. The default
+ * implementation simply clones the provided transport.
+ */
+
+/* type macros */
+#define THRIFT_TYPE_TRANSPORT_FACTORY (thrift_transport_factory_get_type ())
+#define THRIFT_TRANSPORT_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_TRANSPORT_FACTORY, ThriftTransportFactory))
+#define THRIFT_IS_TRANSPORT_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_TRANSPORT_FACTORY))
+#define THRIFT_TRANSPORT_FACTORY_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_TRANSPORT_FACTORY, ThriftTransportFactoryClass))
+#define THRIFT_IS_TRANSPORT_FACTORY_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_TRANSPORT_FACTORY))
+#define THRIFT_TRANSPORT_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_TRANSPORT_FACTORY, ThriftTransportFactoryClass))
+
+typedef struct _ThriftTransportFactory ThriftTransportFactory;
+
+/* Thrift Transport Factory instance */
+struct _ThriftTransportFactory
+{
+ GObject parent;
+};
+
+typedef struct _ThriftTransportFactoryClass ThriftTransportFactoryClass;
+
+/* Thrift Transport Factory class */
+struct _ThriftTransportFactoryClass
+{
+ GObjectClass parent;
+
+ /* vtable */
+ ThriftTransport *(*get_transport) (ThriftTransportFactory *factory,
+ ThriftTransport *transport);
+};
+
+/* used by THRIFT_TYPE_TRANSPORT_FACTORY */
+GType thrift_transport_factory_get_type (void);
+
+/* virtual public methods */
+ThriftTransport *thrift_transport_factory_get_transport (ThriftTransportFactory *factory, ThriftTransport *transport);
+
+G_END_DECLS
+
+#endif /* _THRIFT_TRANSPORT_FACTORY_H */
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/CMakeLists.txt b/src/jaegertracing/thrift/lib/c_glib/test/CMakeLists.txt
new file mode 100644
index 000000000..318b57629
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/CMakeLists.txt
@@ -0,0 +1,202 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+set(TEST_PREFIX "c_glib")
+
+# include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
+
+#Make sure gen-cpp and gen-c_glib files can be included
+include_directories("${CMAKE_CURRENT_BINARY_DIR}")
+
+# Create the thrift C test library
+set(testgenc_SOURCES
+ gen-c_glib/t_test_debug_proto_test_types.c
+ gen-c_glib/t_test_enum_test_types.c
+ gen-c_glib/t_test_enum_test_service.c
+ gen-c_glib/t_test_empty_service.c
+ gen-c_glib/t_test_inherited.c
+ gen-c_glib/t_test_optional_required_test_types.c
+ gen-c_glib/t_test_reverse_order_service.c
+ gen-c_glib/t_test_second_service.c
+ gen-c_glib/t_test_service_for_exception_with_a_map.c
+ gen-c_glib/t_test_srv.c
+ gen-c_glib/t_test_thrift_test.c
+ gen-c_glib/t_test_thrift_test_types.c
+ gen-c_glib/t_test_debug_proto_test_types.h
+ gen-c_glib/t_test_enum_test_types.h
+ gen-c_glib/t_test_enum_test_service.h
+ gen-c_glib/t_test_empty_service.h
+ gen-c_glib/t_test_inherited.h
+ gen-c_glib/t_test_optional_required_test_types.h
+ gen-c_glib/t_test_reverse_order_service.h
+ gen-c_glib/t_test_second_service.h
+ gen-c_glib/t_test_service_for_exception_with_a_map.h
+ gen-c_glib/t_test_srv.h
+ gen-c_glib/t_test_thrift_test.h
+ gen-c_glib/t_test_thrift_test_types.h
+)
+
+add_library(testgenc STATIC ${testgenc_SOURCES})
+LINK_AGAINST_THRIFT_LIBRARY(testgenc thrift_c_glib)
+
+
+add_executable(testserialization testserialization.c)
+target_link_libraries(testserialization testgenc)
+LINK_AGAINST_THRIFT_LIBRARY(testserialization thrift_c_glib)
+add_test(NAME testserialization COMMAND testserialization)
+
+add_executable(testapplicationexception testapplicationexception.c)
+LINK_AGAINST_THRIFT_LIBRARY(testapplicationexception thrift_c_glib)
+add_test(NAME testapplicationexception COMMAND testapplicationexception)
+
+add_executable(testtransportsocket testtransportsocket.c)
+LINK_AGAINST_THRIFT_LIBRARY(testtransportsocket thrift_c_glib)
+add_test(NAME testtransportsocket COMMAND testtransportsocket)
+
+add_executable(testbinaryprotocol testbinaryprotocol.c)
+LINK_AGAINST_THRIFT_LIBRARY(testbinaryprotocol thrift_c_glib)
+add_test(NAME testbinaryprotocol COMMAND testbinaryprotocol)
+
+add_executable(testcompactprotocol testcompactprotocol.c)
+LINK_AGAINST_THRIFT_LIBRARY(testcompactprotocol thrift_c_glib)
+add_test(NAME testcompactprotocol COMMAND testcompactprotocol)
+
+add_executable(testbufferedtransport testbufferedtransport.c)
+LINK_AGAINST_THRIFT_LIBRARY(testbufferedtransport thrift_c_glib)
+add_test(NAME testbufferedtransport COMMAND testbufferedtransport)
+
+add_executable(testframedtransport testframedtransport.c)
+LINK_AGAINST_THRIFT_LIBRARY(testframedtransport thrift_c_glib)
+add_test(NAME testframedtransport COMMAND testframedtransport)
+
+add_executable(testfdtransport testfdtransport.c)
+LINK_AGAINST_THRIFT_LIBRARY(testfdtransport thrift_c_glib)
+add_test(NAME testfdtransport COMMAND testfdtransport)
+
+add_executable(testmemorybuffer testmemorybuffer.c)
+LINK_AGAINST_THRIFT_LIBRARY(testmemorybuffer thrift_c_glib)
+add_test(NAME testmemorybuffer COMMAND testmemorybuffer)
+
+add_executable(testsimpleserver testsimpleserver.c)
+LINK_AGAINST_THRIFT_LIBRARY(testsimpleserver thrift_c_glib)
+add_test(NAME testsimpleserver COMMAND testsimpleserver)
+
+add_executable(testdebugproto testdebugproto.c)
+target_link_libraries(testdebugproto testgenc)
+add_test(NAME testdebugproto COMMAND testdebugproto)
+
+add_executable(testoptionalrequired testoptionalrequired.c)
+target_link_libraries(testoptionalrequired testgenc)
+add_test(NAME testoptionalrequired COMMAND testoptionalrequired)
+
+include_directories("${PROJECT_SOURCE_DIR}/test/c_glib/src" "${CMAKE_CURRENT_BINARY_DIR}/gen-c_glib")
+
+add_executable(testthrifttest testthrifttest.c
+ ${PROJECT_SOURCE_DIR}/test/c_glib/src/thrift_test_handler.c
+ ${PROJECT_SOURCE_DIR}/test/c_glib/src/thrift_test_handler.h
+ ${PROJECT_SOURCE_DIR}/test/c_glib/src/thrift_second_service_handler.c
+ ${PROJECT_SOURCE_DIR}/test/c_glib/src/thrift_second_service_handler.h
+ gen-c_glib/t_test_thrift_test_types.h)
+target_link_libraries(testthrifttest testgenc)
+add_test(NAME testthrifttest COMMAND testthrifttest)
+
+
+if(BUILD_CPP)
+
+ include_directories("${PROJECT_SOURCE_DIR}/lib/cpp/src")
+
+ # Create the thrift C++ test library
+ set(testgenc_cpp_SOURCES
+ gen-cpp/ThriftTest.cpp
+ gen-cpp/ThriftTest_constants.cpp
+ gen-cpp/ThriftTest_types.cpp
+ gen-cpp/ThriftTest.h
+ gen-cpp/ThriftTest_constants.h
+ gen-cpp/ThriftTest_types.h
+ )
+
+ add_library(testgenc_cpp STATIC ${testgenc_cpp_SOURCES})
+ LINK_AGAINST_THRIFT_LIBRARY(testgenc_cpp thrift)
+
+ #HACK: testthrifttestclient.cpp includes ThriftTest.h without gen-*/ prefixes
+ # so we include it here
+ include_directories("${CMAKE_CURRENT_BINARY_DIR}/gen-cpp" "${CMAKE_CURRENT_BINARY_DIR}/gen-c_glib")
+
+ add_executable(testthrifttestclient testthrifttestclient.cpp)
+ target_link_libraries(testthrifttestclient testgenc testgenc_cpp ${ZLIB_LIBRARIES})
+ add_test(NAME testthrifttestclient COMMAND testthrifttestclient)
+
+endif(BUILD_CPP)
+
+#
+# Common thrift code generation rules
+#
+
+add_custom_command(OUTPUT
+ gen-c_glib/t_test_debug_proto_test_types.c
+ gen-c_glib/t_test_debug_proto_test_types.h
+ gen-c_glib/t_test_empty_service.c
+ gen-c_glib/t_test_empty_service.h
+ gen-c_glib/t_test_inherited.c
+ gen-c_glib/t_test_inherited.h
+ gen-c_glib/t_test_reverse_order_service.c
+ gen-c_glib/t_test_reverse_order_service.h
+ gen-c_glib/t_test_service_for_exception_with_a_map.c
+ gen-c_glib/t_test_service_for_exception_with_a_map.h
+ gen-c_glib/t_test_srv.c
+ gen-c_glib/t_test_srv.h
+ COMMAND ${THRIFT_COMPILER} --gen c_glib ${PROJECT_SOURCE_DIR}/test/DebugProtoTest.thrift
+)
+
+add_custom_command(OUTPUT
+ gen-c_glib/t_test_enum_test_types.c
+ gen-c_glib/t_test_enum_test_types.h
+ gen-c_glib/t_test_enum_test_service.c
+ gen-c_glib/t_test_enum_test_service.h
+ COMMAND ${THRIFT_COMPILER} --gen c_glib ${PROJECT_SOURCE_DIR}/test/EnumTest.thrift
+)
+
+add_custom_command(OUTPUT
+ gen-c_glib/t_test_optional_required_test_types.c
+ gen-c_glib/t_test_optional_required_test_types.h
+ COMMAND ${THRIFT_COMPILER} --gen c_glib ${PROJECT_SOURCE_DIR}/test/OptionalRequiredTest.thrift
+)
+
+add_custom_command(OUTPUT
+ gen-c_glib/t_test_second_service.c
+ gen-c_glib/t_test_thrift_test.c
+ gen-c_glib/t_test_thrift_test_types.c
+ gen-c_glib/t_test_second_service.h
+ gen-c_glib/t_test_thrift_test.h
+ gen-c_glib/t_test_thrift_test_types.h
+ COMMAND ${THRIFT_COMPILER} --gen c_glib ${PROJECT_SOURCE_DIR}/test/ThriftTest.thrift
+)
+
+add_custom_command(OUTPUT
+ gen-cpp/ThriftTest.cpp
+ gen-cpp/ThriftTest_constants.cpp
+ gen-cpp/ThriftTest_types.cpp
+ gen-cpp/ThriftTest.h
+ gen-cpp/ThriftTest_constants.h
+ gen-cpp/ThriftTest_types.h
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/ThriftTest.thrift
+)
+
+# TODO: Add memory checks using ctest_memcheck or similar
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/ContainerTest.thrift b/src/jaegertracing/thrift/lib/c_glib/test/ContainerTest.thrift
new file mode 100644
index 000000000..a92a9a51c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/ContainerTest.thrift
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace c_glib TTest
+
+typedef list<string> StringList
+typedef list<StringList> ListStringList
+
+struct ContainersWithDefaultValues {
+ 1: list<string> StringList = [ "Apache", "Thrift" ];
+}
+
+service ContainerService {
+ void receiveStringList(1: list<string> stringList);
+ list<string> returnStringList();
+
+ list<list<string>> returnListStringList();
+ ListStringList returnTypedefdListStringList();
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/Makefile.am b/src/jaegertracing/thrift/lib/c_glib/test/Makefile.am
new file mode 100755
index 000000000..c99e0da83
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/Makefile.am
@@ -0,0 +1,324 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+AUTOMAKE_OPTIONS = subdir-objects serial-tests nostdinc
+
+SUBDIRS =
+
+BUILT_SOURCES = \
+ gen-c_glib/t_test_container_test_types.c \
+ gen-c_glib/t_test_container_test_types.h \
+ gen-c_glib/t_test_debug_proto_test_types.h \
+ gen-c_glib/t_test_empty_service.h \
+ gen-c_glib/t_test_inherited.h \
+ gen-c_glib/t_test_optional_required_test_types.h \
+ gen-c_glib/t_test_reverse_order_service.h \
+ gen-c_glib/t_test_second_service.h \
+ gen-c_glib/t_test_service_for_exception_with_a_map.h \
+ gen-c_glib/t_test_container_service.c \
+ gen-c_glib/t_test_container_service.h \
+ gen-c_glib/t_test_srv.h \
+ gen-c_glib/t_test_thrift_test.h \
+ gen-c_glib/t_test_thrift_test_types.h
+
+AM_CPPFLAGS = -I../src -I./gen-c_glib -I$(top_builddir)/lib/c_glib/src/thrift
+AM_CFLAGS = -g -Wall -Wextra -pedantic $(GLIB_CFLAGS) $(GOBJECT_CFLAGS) $(OPENSSL_INCLUDES) \
+ @GCOV_CFLAGS@
+AM_CXXFLAGS = $(AM_CFLAGS)
+AM_LDFLAGS = $(GLIB_LIBS) $(GOBJECT_LIBS) $(OPENSSL_LIBS) @GCOV_LDFLAGS@
+
+check_PROGRAMS = \
+ testserialization \
+ testapplicationexception \
+ testcontainertest \
+ testtransportsocket \
+ testtransportsslsocket \
+ testbinaryprotocol \
+ testcompactprotocol \
+ testbufferedtransport \
+ testframedtransport \
+ testfdtransport \
+ testmemorybuffer \
+ teststruct \
+ testsimpleserver \
+ testdebugproto \
+ testoptionalrequired \
+ testthrifttest
+
+if WITH_CPP
+ BUILT_SOURCES += gen-cpp/ThriftTest_types.cpp
+ check_PROGRAMS += testthrifttestclient
+endif
+
+testserialization_SOURCES = testserialization.c
+testserialization_LDADD = \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_protocol.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \
+ libtestgenc.la
+
+testapplicationexception_SOURCES = testapplicationexception.c
+testapplicationexception_LDADD = \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_application_exception.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_protocol.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_struct.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o
+
+testcontainertest_SOURCES = testcontainertest.c
+testcontainertest_LDADD = \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_struct.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_protocol.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport_factory.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/processor/libthrift_c_glib_la-thrift_processor.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_protocol_factory.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_binary_protocol.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_binary_protocol_factory.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_socket.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/server/libthrift_c_glib_la-thrift_server.o \
+ libtestgenc.la
+
+testtransportsocket_SOURCES = testtransportsocket.c
+testtransportsocket_LDADD = \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_buffered_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o
+
+
+testtransportsslsocket_SOURCES = testtransportsslsocket.c
+testtransportsslsocket_LDADD = \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_socket.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_buffered_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o
+
+
+testbinaryprotocol_SOURCES = testbinaryprotocol.c
+testbinaryprotocol_LDADD = \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_protocol.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_framed_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_socket.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o
+
+testcompactprotocol_SOURCES = testcompactprotocol.c
+testcompactprotocol_LDADD = \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_protocol.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_framed_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_socket.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o
+
+testbufferedtransport_SOURCES = testbufferedtransport.c
+testbufferedtransport_LDADD = \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_socket.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o
+
+testframedtransport_SOURCES = testframedtransport.c
+testframedtransport_LDADD = \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_socket.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o
+
+testfdtransport_SOURCES = testfdtransport.c
+testfdtransport_LDADD = \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_fd_transport.o
+
+testmemorybuffer_SOURCES = testmemorybuffer.c
+testmemorybuffer_LDADD = \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o
+
+teststruct_SOURCES = teststruct.c
+teststruct_LDADD = \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_protocol.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o
+
+testsimpleserver_SOURCES = testsimpleserver.c
+testsimpleserver_LDADD = \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_protocol.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport_factory.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/processor/libthrift_c_glib_la-thrift_processor.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_protocol_factory.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_binary_protocol.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_binary_protocol_factory.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_socket.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/server/libthrift_c_glib_la-thrift_server.o
+
+testdebugproto_SOURCES = testdebugproto.c
+testdebugproto_LDADD = libtestgenc.la
+
+testoptionalrequired_SOURCES = testoptionalrequired.c
+testoptionalrequired_LDADD = \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_protocol.o \
+ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \
+ libtestgenc.la
+
+testthrifttest_SOURCES = testthrifttest.c
+testthrifttest_LDADD = libtestgenc.la \
+ $(top_builddir)/test/c_glib/src/thrift_test_handler.o
+testthrifttest_CFLAGS = -I$(top_srcdir)/test/c_glib/src -I./gen-c_glib $(GLIB_CFLAGS)
+
+testthrifttestclient_SOURCES = testthrifttestclient.cpp
+testthrifttestclient_CPPFLAGS = -I../../cpp/src $(BOOST_CPPFLAGS) -I./gen-cpp -I../src -I./gen-c_glib $(GLIB_CFLAGS)
+testthrifttestclient_LDADD = ../../cpp/.libs/libthrift.la ../libthrift_c_glib.la libtestgenc.la libtestgencpp.la
+testthrifttestclient_LDFLAGS = -L../.libs -L../../cpp/.libs $(GLIB_LIBS) $(GOBJECT_LIBS)
+
+check_LTLIBRARIES = libtestgenc.la
+
+if WITH_CPP
+ check_LTLIBRARIES += libtestgencpp.la
+endif
+
+nodist_libtestgenc_la_SOURCES = \
+ gen-c_glib/t_test_container_test_types.c \
+ gen-c_glib/t_test_debug_proto_test_types.c \
+ gen-c_glib/t_test_enum_test_types.c \
+ gen-c_glib/t_test_enum_test_service.c \
+ gen-c_glib/t_test_empty_service.c \
+ gen-c_glib/t_test_inherited.c \
+ gen-c_glib/t_test_optional_required_test_types.c \
+ gen-c_glib/t_test_reverse_order_service.c \
+ gen-c_glib/t_test_second_service.c \
+ gen-c_glib/t_test_service_for_exception_with_a_map.c \
+ gen-c_glib/t_test_srv.c \
+ gen-c_glib/t_test_container_service.c \
+ gen-c_glib/t_test_thrift_test.c \
+ gen-c_glib/t_test_thrift_test_types.c \
+ gen-c_glib/t_test_container_test_types.h \
+ gen-c_glib/t_test_debug_proto_test_types.h \
+ gen-c_glib/t_test_enum_test_types.h \
+ gen-c_glib/t_test_enum_test_service.h \
+ gen-c_glib/t_test_empty_service.h \
+ gen-c_glib/t_test_inherited.h \
+ gen-c_glib/t_test_optional_required_test_types.h \
+ gen-c_glib/t_test_reverse_order_service.h \
+ gen-c_glib/t_test_second_service.h \
+ gen-c_glib/t_test_service_for_exception_with_a_map.h \
+ gen-c_glib/t_test_srv.h \
+ gen-c_glib/t_test_container_service.h \
+ gen-c_glib/t_test_thrift_test.h \
+ gen-c_glib/t_test_thrift_test_types.h
+libtestgenc_la_LIBADD = $(top_builddir)/lib/c_glib/libthrift_c_glib.la
+libtestgenc_la_CPPFLAGS = $(AM_CPPFLAGS) -Wno-unused-function
+
+nodist_libtestgencpp_la_SOURCES = \
+ gen-cpp/ThriftTest.cpp \
+ gen-cpp/ThriftTest_constants.cpp \
+ gen-cpp/ThriftTest_types.cpp \
+ gen-cpp/ThriftTest.h \
+ gen-cpp/ThriftTest_constants.h \
+ gen-cpp/ThriftTest_types.h
+libtestgencpp_la_CPPFLAGS = -I../../cpp/src $(BOOST_CPPFLAGS) -I./gen-cpp
+
+gen-c_glib/t_test_container_test_types.c gen-c_glib/t_test_container_test_types.h gen-c_glib/t_test_container_service.c gen-c_glib/t_test_container_service.h: ContainerTest.thrift $(THRIFT)
+ $(THRIFT) --gen c_glib $<
+
+gen-c_glib/t_test_debug_proto_test_types.c gen-c_glib/t_test_debug_proto_test_types.h gen-c_glib/t_test_empty_service.c gen-c_glib/t_test_empty_service.h gen-c_glib/t_test_inherited.c gen-c_glib/t_test_inherited.h gen-c_glib/t_test_reverse_order_service.c gen-c_glib/t_test_reverse_order_service.h gen-c_glib/t_test_service_for_exception_with_a_map.c gen-c_glib/t_test_service_for_exception_with_a_map.h gen-c_glib/t_test_srv.c gen-c_glib/t_test_srv.h: ../../../test/DebugProtoTest.thrift $(THRIFT)
+ $(THRIFT) --gen c_glib $<
+
+gen-c_glib/t_test_enum_test_types.c gen-c_glib/t_test_enum_test_types.h gen-c_glib/t_test_enum_test_service.c gen-c_glib/t_test_enum_test_service.h : ../../../test/EnumTest.thrift $(THRIFT)
+ $(THRIFT) --gen c_glib $<
+
+gen-c_glib/t_test_optional_required_test_types.c gen-c_glib/t_test_optional_required_test_types.h: ../../../test/OptionalRequiredTest.thrift $(THRIFT)
+ $(THRIFT) --gen c_glib $<
+
+gen-c_glib/t_test_second_service.c gen-c_glib/t_test_thrift_test.c gen-c_glib/t_test_thrift_test_types.c gen-c_glib/t_test_second_service.h gen-c_glib/t_test_thrift_test.h gen-c_glib/t_test_thrift_test_types.h: ../../../test/ThriftTest.thrift $(THRIFT)
+ $(THRIFT) --gen c_glib $<
+
+gen-cpp/ThriftTest.cpp gen-cpp/ThriftTest_constants.cpp gen-cpp/ThriftTest_types.cpp: ../../../test/ThriftTest.thrift $(THRIFT)
+ $(THRIFT) --gen cpp $<
+
+TESTS = \
+ $(check_PROGRAMS) \
+ $(check_SCRIPTS)
+
+# globally added to all instances of valgrind calls
+# VALGRIND_OPTS = --suppressions=glib.suppress
+VALGRIND_OPTS =
+
+# globally added to all memcheck calls
+VALGRIND_MEM_OPTS = --tool=memcheck \
+ --num-callers=10 \
+ ${myextravalgrindmemopts}
+
+# globally added to all leakcheck calls
+VALGRIND_LEAK_OPTS = --tool=memcheck \
+ --num-callers=10 \
+ --leak-check=full \
+ --leak-resolution=high \
+ ${myextravalgrindleakopts}
+
+memcheck: $(check_PROGRAMS)
+ @for x in $(check_PROGRAMS); \
+ do \
+ $(MAKE) memcheck-$$x; \
+ done
+
+leakcheck: $(check_PROGRAMS)
+ @for x in $(check_PROGRAMS); \
+ do \
+ $(MAKE) leakcheck-$$x; \
+ done
+
+memcheck-%: %
+ @echo "*****************************************"; \
+ echo "MEMCHECK: $<"; \
+ echo "ARGS: ${VALGRIND_OPTS} ${VALGRIND_MEM_OPTS} ${$<_VALGRIND_MEM_OPTS}"; \
+ $(LIBTOOL) --mode=execute \
+ valgrind \
+ ${VALGRIND_OPTS} \
+ ${VALGRIND_MEM_OPTS} \
+ ${$<_VALGRIND_MEM_OPTS} ./$<
+
+leakcheck-%: %
+ @echo "*****************************************"; \
+ echo "LEAKCHECK: $<"; \
+ echo "ARGS: ${VALGRIND_OPTS} ${VALGRIND_LEAK_OPTS} ${$<_VALGRIND_LEAK_OPTS}"; \
+ G_SLICE=always-malloc $(LIBTOOL) --mode=execute \
+ valgrind \
+ ${VALGRIND_OPTS} \
+ ${VALGRIND_LEAK_OPTS} \
+ ${$<_VALGRIND_LEAK_OPTS} ./$<
+
+clean-local:
+ $(RM) gen-c_glib/* gen-cpp/*
+
+CLEANFILES = \
+ *.bb \
+ *.bbg \
+ *.da \
+ *.gcno \
+ *.gcda \
+ *.gcov
+
+EXTRA_DIST = \
+ CMakeLists.txt \
+ ContainerTest.thrift
+
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/glib.suppress b/src/jaegertracing/thrift/lib/c_glib/test/glib.suppress
new file mode 100644
index 000000000..0e0e9fe27
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/glib.suppress
@@ -0,0 +1,64 @@
+{
+ g_type_init_1
+ Memcheck:Leak
+ fun:malloc
+ ...
+ fun:g_type_init_with_debug_flags
+}
+
+{
+ g_type_init_2
+ Memcheck:Leak
+ fun:calloc
+ ...
+ fun:g_type_init_with_debug_flags
+}
+
+{
+ g_type_init_3
+ Memcheck:Leak
+ fun:realloc
+ ...
+ fun:g_type_init_with_debug_flags
+}
+
+{
+ g_type_register_static_1
+ Memcheck:Leak
+ fun:realloc
+ ...
+ fun:g_type_register_static
+}
+
+{
+ g_type_register_statuc_2
+ Memcheck:Leak
+ fun:malloc
+ fun:realloc
+ fun:g_realloc
+ ...
+ fun:g_type_register_static
+}
+
+{
+ type_class_init_Wm1
+ Memcheck:Leak
+ fun:calloc
+ fun:g_malloc0
+ fun:type_class_init_Wm
+ fun:g_type_class_ref
+ ...
+ fun:g_object_newv
+}
+
+{
+ type_class_init_Wm2
+ Memcheck:Leak
+ fun:calloc
+ fun:g_malloc0
+ fun:type_class_init_Wm
+ fun:g_type_class_ref
+ ...
+ fun:type_class_init_Wm
+}
+
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/testapplicationexception.c b/src/jaegertracing/thrift/lib/c_glib/test/testapplicationexception.c
new file mode 100644
index 000000000..89e39e262
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/testapplicationexception.c
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include <thrift/c_glib/thrift_application_exception.h>
+
+static void
+test_create_and_destroy (void)
+{
+ GObject *object = NULL;
+
+ /* A ThriftApplicationException can be created... */
+ object = g_object_new (THRIFT_TYPE_APPLICATION_EXCEPTION, NULL);
+
+ g_assert (object != NULL);
+ g_assert (THRIFT_IS_APPLICATION_EXCEPTION (object));
+
+ /* ...and destroyed */
+ g_object_unref (object);
+}
+
+static void
+test_initialize (void)
+{
+ ThriftApplicationException *xception = NULL;
+ gint32 type = THRIFT_APPLICATION_EXCEPTION_ERROR_INTERNAL_ERROR;
+ gchar *message = "Exception message";
+ gint32 retrieved_type = 0;
+ gchar *retrieved_message = NULL;
+
+ /* A ThriftApplicationException has "type" and "message" properties that can
+ be initialized at object creation */
+ xception =
+ g_object_new (THRIFT_TYPE_APPLICATION_EXCEPTION,
+ "type", type,
+ "message", message,
+ NULL);
+
+ g_assert (xception != NULL);
+
+ /* A ThriftApplicationException's properties can be retrieved */
+ g_object_get (xception,
+ "type", &retrieved_type,
+ "message", &retrieved_message,
+ NULL);
+
+ g_assert (retrieved_type == type);
+ g_assert (retrieved_message != NULL);
+ g_assert_cmpstr (retrieved_message, ==, message);
+
+ g_free (retrieved_message);
+ g_object_unref (xception);
+}
+
+static void
+test_properties_test (void)
+{
+ ThriftApplicationException *xception = NULL;
+ gint32 retrieved_type;
+
+ xception = g_object_new (THRIFT_TYPE_APPLICATION_EXCEPTION, NULL);
+
+#define TEST_TYPE_VALUE(_type) \
+ retrieved_type = -1; \
+ g_object_set (xception, "type", _type, NULL); \
+ g_object_get (xception, "type", &retrieved_type, NULL); \
+ g_assert_cmpint (retrieved_type, ==, _type);
+
+ /* The "type" property can be set to any valid Thrift exception type */
+ TEST_TYPE_VALUE (THRIFT_APPLICATION_EXCEPTION_ERROR_UNKNOWN);
+ TEST_TYPE_VALUE (THRIFT_APPLICATION_EXCEPTION_ERROR_UNKNOWN_METHOD);
+ TEST_TYPE_VALUE (THRIFT_APPLICATION_EXCEPTION_ERROR_INVALID_MESSAGE_TYPE);
+ TEST_TYPE_VALUE (THRIFT_APPLICATION_EXCEPTION_ERROR_WRONG_METHOD_NAME);
+ TEST_TYPE_VALUE (THRIFT_APPLICATION_EXCEPTION_ERROR_BAD_SEQUENCE_ID);
+ TEST_TYPE_VALUE (THRIFT_APPLICATION_EXCEPTION_ERROR_MISSING_RESULT);
+ TEST_TYPE_VALUE (THRIFT_APPLICATION_EXCEPTION_ERROR_INTERNAL_ERROR);
+ TEST_TYPE_VALUE (THRIFT_APPLICATION_EXCEPTION_ERROR_PROTOCOL_ERROR);
+ TEST_TYPE_VALUE (THRIFT_APPLICATION_EXCEPTION_ERROR_INVALID_TRANSFORM);
+ TEST_TYPE_VALUE (THRIFT_APPLICATION_EXCEPTION_ERROR_INVALID_PROTOCOL);
+ TEST_TYPE_VALUE (THRIFT_APPLICATION_EXCEPTION_ERROR_UNSUPPORTED_CLIENT_TYPE);
+
+/* "g_test_expect_message" is required for the property range tests below but is
+ not present in GLib before version 2.34 */
+#if (GLIB_CHECK_VERSION (2, 34, 0))
+ g_object_set (xception,
+ "type", THRIFT_APPLICATION_EXCEPTION_ERROR_UNKNOWN,
+ NULL);
+
+ /* The "type" property cannot be set to a value too low (less than zero) */
+ g_test_expect_message ("GLib-GObject",
+ G_LOG_LEVEL_WARNING,
+ "value*out of range*type*");
+ g_object_set (xception, "type", -1, NULL);
+ g_test_assert_expected_messages ();
+
+ g_object_get (xception, "type", &retrieved_type, NULL);
+ g_assert_cmpint (retrieved_type, !=, -1);
+ g_assert_cmpint (retrieved_type,
+ ==,
+ THRIFT_APPLICATION_EXCEPTION_ERROR_UNKNOWN);
+
+ /* The "type" property cannot be set to a value too high (greater than the
+ highest defined exception-type value) */
+ g_test_expect_message ("GLib-GObject",
+ G_LOG_LEVEL_WARNING,
+ "value*out of range*type*");
+ g_object_set (xception, "type", THRIFT_APPLICATION_EXCEPTION_ERROR_N, NULL);
+ g_test_assert_expected_messages ();
+
+ g_object_get (xception, "type", &retrieved_type, NULL);
+ g_assert_cmpint (retrieved_type, !=, THRIFT_APPLICATION_EXCEPTION_ERROR_N);
+ g_assert_cmpint (retrieved_type,
+ ==,
+ THRIFT_APPLICATION_EXCEPTION_ERROR_UNKNOWN);
+#endif
+
+ g_object_unref (xception);
+}
+
+static void
+test_properties_message (void)
+{
+ ThriftApplicationException *xception = NULL;
+ gchar *message = "Exception message";
+ gchar *retrieved_message;
+
+ xception = g_object_new (THRIFT_TYPE_APPLICATION_EXCEPTION, NULL);
+
+ /* The "message" property can be set to NULL */
+ g_object_set (xception, "message", NULL, NULL);
+ g_object_get (xception, "message", &retrieved_message, NULL);
+ g_assert (retrieved_message == NULL);
+
+ /* The "message" property can be set to a valid string */
+ g_object_set (xception, "message", message, NULL);
+ g_object_get (xception, "message", &retrieved_message, NULL);
+ g_assert_cmpint (strcmp (retrieved_message, message), ==, 0);
+
+ g_free (retrieved_message);
+ g_object_unref (xception);
+}
+
+int
+main (int argc, char **argv)
+{
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init ();
+#endif
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/testapplicationexception/CreateAndDestroy",
+ test_create_and_destroy);
+ g_test_add_func ("/testapplicationexception/Initialize",
+ test_initialize);
+ g_test_add_func ("/testapplicationexception/Properties/test",
+ test_properties_test);
+ g_test_add_func ("/testapplicationexception/Properties/message",
+ test_properties_message);
+
+ return g_test_run ();
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/testbinaryprotocol.c b/src/jaegertracing/thrift/lib/c_glib/test/testbinaryprotocol.c
new file mode 100755
index 000000000..14f4fb063
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/testbinaryprotocol.c
@@ -0,0 +1,859 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Disable string-function optimizations when glibc is used, as these produce
+ compiler warnings about string length when a string function is used inside
+ a call to g_assert () */
+#ifdef __GLIBC__
+#include <features.h>
+#define __NO_STRING_INLINES 1
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <sys/wait.h>
+
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/transport/thrift_socket.h>
+#include <thrift/c_glib/transport/thrift_server_socket.h>
+#include <thrift/c_glib/transport/thrift_framed_transport.h>
+
+#define TEST_BOOL TRUE
+#define TEST_BYTE 123
+#define TEST_I16 12345
+#define TEST_I32 1234567890
+#define TEST_I64 G_GINT64_CONSTANT (123456789012345)
+#define TEST_DOUBLE 1234567890.123
+#define TEST_STRING "this is a test string 1234567890!@#$%^&*()"
+#define TEST_PORT 51199
+
+static int transport_read_count = 0;
+static int transport_read_error = 0;
+static int transport_read_error_at = -1;
+gint32
+my_thrift_transport_read_all (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error)
+{
+ if (transport_read_count != transport_read_error_at
+ && transport_read_error == 0)
+ {
+ transport_read_count++;
+ return thrift_transport_read_all (transport, buf, len, error);
+ }
+ return -1;
+}
+
+static int transport_write_count = 0;
+static int transport_write_error = 0;
+static int transport_write_error_at = -1;
+gboolean
+my_thrift_transport_write (ThriftTransport *transport, const gpointer buf,
+ const guint32 len, GError **error)
+{
+ if (transport_write_count != transport_write_error_at
+ && transport_write_error == 0)
+ {
+ transport_write_count++;
+ return thrift_transport_write (transport, buf, len, error);
+ }
+ return FALSE;
+}
+
+#define thrift_transport_read_all my_thrift_transport_read_all
+#define thrift_transport_write my_thrift_transport_write
+#include "../src/thrift/c_glib/protocol/thrift_binary_protocol.c"
+#undef thrift_transport_read_all
+#undef thrift_transport_write
+
+static void thrift_server_primitives (const int port);
+static void thrift_server_complex_types (const int port);
+static void thrift_server_many_frames (const int port);
+
+static void
+test_create_and_destroy(void)
+{
+ GObject *object = NULL;
+
+ /* create an object and then destroy it */
+ object = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, NULL);
+ g_assert (object != NULL);
+ g_object_unref (object);
+}
+
+static void
+test_initialize(void)
+{
+ ThriftSocket *tsocket = NULL;
+ ThriftBinaryProtocol *protocol = NULL;
+ ThriftSocket *temp = NULL;
+
+ /* create a ThriftTransport */
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
+ "port", 51188, NULL);
+ g_assert (tsocket != NULL);
+ /* create a ThriftBinaryProtocol using the Transport */
+ protocol = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport",
+ tsocket, NULL);
+ g_assert (protocol != NULL);
+ /* fetch the properties */
+ g_object_get (G_OBJECT(protocol), "transport", &temp, NULL);
+ g_object_unref (temp);
+
+ /* clean up memory */
+ g_object_unref (protocol);
+ g_object_unref (tsocket);
+}
+
+static void
+test_read_and_write_primitives(void)
+{
+ int status;
+ pid_t pid;
+ ThriftSocket *tsocket = NULL;
+ ThriftTransport *transport = NULL;
+ ThriftBinaryProtocol *tb = NULL;
+ ThriftProtocol *protocol = NULL;
+ gpointer binary = (gpointer *) TEST_STRING;
+ guint32 len = strlen (TEST_STRING);
+ int port = TEST_PORT;
+
+ /* fork a server from the client */
+ pid = fork ();
+ g_assert (pid >= 0);
+
+ if (pid == 0)
+ {
+ /* child listens */
+ thrift_server_primitives (port);
+ exit (0);
+ } else {
+ /* parent. wait a bit for the socket to be created. */
+ sleep (1);
+
+ /* create a ThriftSocket */
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
+ "port", port, NULL);
+ transport = THRIFT_TRANSPORT (tsocket);
+ thrift_transport_open (transport, NULL);
+ g_assert (thrift_transport_is_open (transport));
+
+ /* create a ThriftBinaryTransport */
+ tb = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport",
+ tsocket, NULL);
+ protocol = THRIFT_PROTOCOL (tb);
+ g_assert (protocol != NULL);
+
+ /* write a bunch of primitives */
+ g_assert (thrift_binary_protocol_write_bool (protocol, TEST_BOOL, NULL) > 0);
+ g_assert (thrift_binary_protocol_write_byte (protocol, TEST_BYTE, NULL) > 0);
+ g_assert (thrift_binary_protocol_write_i16 (protocol, TEST_I16, NULL) > 0);
+ g_assert (thrift_binary_protocol_write_i32 (protocol, TEST_I32, NULL) > 0);
+ g_assert (thrift_binary_protocol_write_i64 (protocol, TEST_I64, NULL) > 0);
+ g_assert (thrift_binary_protocol_write_double (protocol,
+ TEST_DOUBLE, NULL) > 0);
+ g_assert (thrift_binary_protocol_write_string (protocol,
+ TEST_STRING, NULL) > 0);
+ g_assert (thrift_binary_protocol_write_string (protocol, "", NULL) > 0);
+ g_assert (thrift_binary_protocol_write_binary (protocol, binary,
+ len, NULL) > 0);
+ g_assert (thrift_binary_protocol_write_binary (protocol, NULL, 0, NULL) > 0);
+ g_assert (thrift_binary_protocol_write_binary (protocol, binary,
+ len, NULL) > 0);
+
+ /* test write errors */
+ transport_write_error = 1;
+ g_assert (thrift_binary_protocol_write_byte (protocol, TEST_BYTE,
+ NULL) == -1);
+ g_assert (thrift_binary_protocol_write_i16 (protocol, TEST_I16, NULL) == -1);
+ g_assert (thrift_binary_protocol_write_i32 (protocol, TEST_I32, NULL) == -1);
+ g_assert (thrift_binary_protocol_write_i64 (protocol, TEST_I64, NULL) == -1);
+ g_assert (thrift_binary_protocol_write_double (protocol, TEST_DOUBLE,
+ NULL) == -1);
+ g_assert (thrift_binary_protocol_write_binary (protocol, binary, len,
+ NULL) == -1);
+ transport_write_error = 0;
+
+ /* test binary partial failure */
+ transport_write_count = 0;
+ transport_write_error_at = 1;
+ g_assert (thrift_binary_protocol_write_binary (protocol, binary,
+ len, NULL) == -1);
+ transport_write_error_at = -1;
+
+ /* clean up */
+ thrift_transport_close (transport, NULL);
+ g_object_unref (tsocket);
+ g_object_unref (protocol);
+ g_assert (wait (&status) == pid);
+ g_assert (status == 0);
+ }
+}
+
+static void
+test_read_and_write_complex_types (void)
+{
+ int status;
+ pid_t pid;
+ ThriftSocket *tsocket = NULL;
+ ThriftTransport *transport = NULL;
+ ThriftBinaryProtocol *tb = NULL;
+ ThriftProtocol *protocol = NULL;
+ int port = TEST_PORT;
+
+ /* fork a server from the client */
+ pid = fork ();
+ g_assert (pid >= 0);
+
+ if (pid == 0)
+ {
+ /* child listens */
+ thrift_server_complex_types (port);
+ exit (0);
+ } else {
+ /* parent. wait a bit for the socket to be created. */
+ sleep (1);
+
+ /* create a ThriftSocket */
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
+ "port", port, NULL);
+ transport = THRIFT_TRANSPORT (tsocket);
+ thrift_transport_open (transport, NULL);
+ g_assert (thrift_transport_is_open (transport));
+
+ /* create a ThriftBinaryTransport */
+ tb = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport",
+ tsocket, NULL);
+ protocol = THRIFT_PROTOCOL (tb);
+ g_assert (protocol != NULL);
+
+ /* test structures */
+ g_assert (thrift_binary_protocol_write_struct_begin (protocol,
+ NULL, NULL) == 0);
+ g_assert (thrift_binary_protocol_write_struct_end (protocol, NULL) == 0);
+
+ g_assert (thrift_binary_protocol_write_field_begin (protocol, "test", T_VOID,
+ 1, NULL) > 0);
+ g_assert (thrift_binary_protocol_write_field_end (protocol, NULL) == 0);
+
+ /* test write error */
+ transport_write_error = 1;
+ g_assert (thrift_binary_protocol_write_field_begin (protocol, "test", T_VOID,
+ 1, NULL) == -1);
+ transport_write_error = 0;
+
+ /* test 2nd write error */
+ transport_write_count = 0;
+ transport_write_error_at = 1;
+ g_assert (thrift_binary_protocol_write_field_begin (protocol, "test", T_VOID,
+ 1, NULL) == -1);
+ transport_write_error_at = -1;
+
+ /* test 2nd read failure on a field */
+ thrift_binary_protocol_write_byte (protocol, T_VOID, NULL);
+
+ /* test write_field_stop */
+ g_assert (thrift_binary_protocol_write_field_stop (protocol, NULL) > 0);
+
+ /* write a map */
+ g_assert (thrift_binary_protocol_write_map_begin (protocol, T_VOID, T_VOID,
+ 1, NULL) > 0);
+ g_assert (thrift_binary_protocol_write_map_end (protocol, NULL) == 0);
+
+ /* test 2nd read failure on a map */
+ thrift_binary_protocol_write_byte (protocol, T_VOID, NULL);
+
+ /* test 3rd read failure on a map */
+ thrift_binary_protocol_write_byte (protocol, T_VOID, NULL);
+ thrift_binary_protocol_write_byte (protocol, T_VOID, NULL);
+
+ /* test 1st write failure on a map */
+ transport_write_error = 1;
+ g_assert (thrift_binary_protocol_write_map_begin (protocol, T_VOID, T_VOID,
+ 1, NULL) == -1);
+ transport_write_error = 0;
+
+ /* test 2nd write failure on a map */
+ transport_write_count = 0;
+ transport_write_error_at = 1;
+ g_assert (thrift_binary_protocol_write_map_begin (protocol, T_VOID, T_VOID,
+ 1, NULL) == -1);
+ transport_write_error_at = -1;
+
+ /* test 3rd write failure on a map */
+ transport_write_count = 0;
+ transport_write_error_at = 2;
+ g_assert (thrift_binary_protocol_write_map_begin (protocol, T_VOID, T_VOID,
+ 1, NULL) == -1);
+ transport_write_error_at = -1;
+
+ /* test negative map size */
+ thrift_binary_protocol_write_byte (protocol, T_VOID, NULL);
+ thrift_binary_protocol_write_byte (protocol, T_VOID, NULL);
+ thrift_binary_protocol_write_i32 (protocol, -10, NULL);
+
+ /* test list operations */
+ g_assert (thrift_binary_protocol_write_list_begin (protocol, T_VOID,
+ 1, NULL) > 0);
+ g_assert (thrift_binary_protocol_write_list_end (protocol, NULL) == 0);
+
+ /* test 2nd read failure on a list */
+ thrift_binary_protocol_write_byte (protocol, T_VOID, NULL);
+
+ /* test negative list size */
+ thrift_binary_protocol_write_byte (protocol, T_VOID, NULL);
+ thrift_binary_protocol_write_i32 (protocol, -10, NULL);
+
+ /* test first write error on a list */
+ transport_write_error = 1;
+ g_assert (thrift_binary_protocol_write_list_begin (protocol, T_VOID,
+ 1, NULL) == -1);
+ transport_write_error = 0;
+
+ /* test 2nd write error on a list */
+ transport_write_count = 0;
+ transport_write_error_at = 1;
+ g_assert (thrift_binary_protocol_write_list_begin (protocol, T_VOID,
+ 1, NULL) == -1);
+ transport_write_error_at = -1;
+
+ /* test set operation s*/
+ g_assert (thrift_binary_protocol_write_set_begin (protocol, T_VOID,
+ 1, NULL) > 0);
+ g_assert (thrift_binary_protocol_write_set_end (protocol, NULL) == 0);
+
+ /* invalid version */
+ g_assert (thrift_binary_protocol_write_i32 (protocol, -1, NULL) > 0);
+
+ /* sz > 0 for a message */
+ g_assert (thrift_binary_protocol_write_i32 (protocol, 1, NULL) > 0);
+
+ /* send a valid message */
+ thrift_binary_protocol_write_i32 (protocol, 0x80010000, NULL);
+ thrift_binary_protocol_write_string (protocol, "test", NULL);
+ thrift_binary_protocol_write_i32 (protocol, 1, NULL);
+
+ /* broken 2nd read */
+ thrift_binary_protocol_write_i32 (protocol, 0x80010000, NULL);
+
+ /* send a broken 3rd read */
+ thrift_binary_protocol_write_i32 (protocol, 0x80010000, NULL);
+ thrift_binary_protocol_write_string (protocol, "test", NULL);
+
+ /* send a valid message */
+ g_assert (thrift_binary_protocol_write_message_begin (protocol, "test",
+ T_CALL, 1, NULL) > 0);
+
+ g_assert (thrift_binary_protocol_write_message_end (protocol, NULL) == 0);
+
+ /* send broken writes */
+ transport_write_error = 1;
+ g_assert (thrift_binary_protocol_write_message_begin (protocol, "test",
+ T_CALL, 1, NULL) == -1);
+ transport_write_error = 0;
+
+ transport_write_count = 0;
+ transport_write_error_at = 2;
+ g_assert (thrift_binary_protocol_write_message_begin (protocol, "test",
+ T_CALL, 1, NULL) == -1);
+ transport_write_error_at = -1;
+
+ transport_write_count = 0;
+ transport_write_error_at = 3;
+ g_assert (thrift_binary_protocol_write_message_begin (protocol, "test",
+ T_CALL, 1, NULL) == -1);
+ transport_write_error_at = -1;
+
+ /* clean up */
+ thrift_transport_close (transport, NULL);
+ g_object_unref (tsocket);
+ g_object_unref (protocol);
+ g_assert (wait (&status) == pid);
+ g_assert (status == 0);
+ }
+}
+
+static void
+test_read_and_write_many_frames (void)
+{
+ int status;
+ pid_t pid;
+ ThriftSocket *tsocket = NULL;
+ ThriftTransport *transport = NULL;
+ ThriftFramedTransport *ft = NULL;
+ ThriftBinaryProtocol *tb = NULL;
+ ThriftProtocol *protocol = NULL;
+ gpointer binary = (gpointer *) TEST_STRING;
+ const guint32 len = strlen (TEST_STRING);
+ int port = TEST_PORT;
+
+ /* fork a server from the client */
+ pid = fork ();
+ g_assert (pid >= 0);
+
+ if (pid == 0)
+ {
+ /* child listens */
+ thrift_server_many_frames (port);
+ exit (0);
+ } else {
+ /* parent. wait a bit for the socket to be created. */
+ sleep (1);
+
+ /* create a ThriftSocket */
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
+ "port", port, NULL);
+ g_assert (tsocket != NULL);
+ transport = THRIFT_TRANSPORT (tsocket);
+
+ /* wrap in a framed transport */
+ ft = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT, "transport", transport,
+ "w_buf_size", 1, NULL);
+ g_assert (ft != NULL);
+ transport = THRIFT_TRANSPORT (ft);
+
+ thrift_transport_open (transport, NULL);
+ g_assert (thrift_transport_is_open (transport));
+
+ /* create a binary protocol */
+ tb = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport",
+ transport, NULL);
+ protocol = THRIFT_PROTOCOL (tb);
+ g_assert (protocol != NULL);
+
+ /* write a bunch of primitives */
+ g_assert (thrift_binary_protocol_write_bool (protocol, TEST_BOOL, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_binary_protocol_write_byte (protocol, TEST_BYTE, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_binary_protocol_write_i16 (protocol, TEST_I16, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_binary_protocol_write_i32 (protocol, TEST_I32, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_binary_protocol_write_i64 (protocol, TEST_I64, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_binary_protocol_write_double (protocol,
+ TEST_DOUBLE, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_binary_protocol_write_string (protocol,
+ TEST_STRING, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_binary_protocol_write_string (protocol, "", NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_binary_protocol_write_binary (protocol, binary,
+ len, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_binary_protocol_write_binary (protocol, NULL, 0, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_binary_protocol_write_binary (protocol, binary,
+ len, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+
+ /* clean up */
+ thrift_transport_write_end (transport, NULL);
+ thrift_transport_close (transport, NULL);
+ g_object_unref (ft);
+ g_object_unref (tsocket);
+ g_object_unref (tb);
+ g_assert (wait (&status) == pid);
+ g_assert (status == 0);
+ }
+}
+
+
+static void
+thrift_server_primitives (const int port)
+{
+ ThriftServerTransport *transport = NULL;
+ ThriftTransport *client = NULL;
+ ThriftBinaryProtocol *tbp = NULL;
+ ThriftProtocol *protocol = NULL;
+ gboolean value_boolean = FALSE;
+ gint8 value_byte = 0;
+ gint16 value_16 = 0;
+ gint32 value_32 = 0;
+ gint64 value_64 = 0;
+ gdouble value_double = 0;
+ gchar *string = NULL;
+ gchar *empty_string = NULL;
+ gpointer binary = NULL;
+ guint32 len = 0;
+ void *comparator = (void *) TEST_STRING;
+
+ ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+ "port", port, NULL);
+ transport = THRIFT_SERVER_TRANSPORT (tsocket);
+ thrift_server_transport_listen (transport, NULL);
+ client = thrift_server_transport_accept (transport, NULL);
+ g_assert (client != NULL);
+
+ tbp = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport",
+ client, NULL);
+ protocol = THRIFT_PROTOCOL (tbp);
+
+ g_assert (thrift_binary_protocol_read_bool (protocol,
+ &value_boolean, NULL) > 0);
+ g_assert (thrift_binary_protocol_read_byte (protocol, &value_byte, NULL) > 0);
+ g_assert (thrift_binary_protocol_read_i16 (protocol, &value_16, NULL) > 0);
+ g_assert (thrift_binary_protocol_read_i32 (protocol, &value_32, NULL) > 0);
+ g_assert (thrift_binary_protocol_read_i64 (protocol, &value_64, NULL) > 0);
+ g_assert (thrift_binary_protocol_read_double (protocol,
+ &value_double, NULL) > 0);
+ g_assert (thrift_binary_protocol_read_string (protocol, &string, NULL) > 0);
+ g_assert (thrift_binary_protocol_read_string (protocol, &empty_string,
+ NULL) > 0);
+ g_assert (thrift_binary_protocol_read_binary (protocol, &binary,
+ &len, NULL) > 0);
+
+ g_assert (value_boolean == TEST_BOOL);
+ g_assert (value_byte == TEST_BYTE);
+ g_assert (value_16 == TEST_I16);
+ g_assert (value_32 == TEST_I32);
+ g_assert (value_64 == TEST_I64);
+ g_assert (value_double == TEST_DOUBLE);
+ g_assert (strcmp (TEST_STRING, string) == 0);
+ g_assert (strcmp ("", empty_string) == 0);
+ g_assert (memcmp (comparator, binary, len) == 0);
+
+ g_free (string);
+ g_free (empty_string);
+ g_free (binary);
+
+ g_assert (thrift_binary_protocol_read_binary (protocol, &binary,
+ &len, NULL) > 0);
+ g_assert (binary == NULL);
+ g_assert (len == 0);
+ g_free (binary);
+
+ transport_read_count = 0;
+ transport_read_error_at = 0;
+ g_assert (thrift_binary_protocol_read_binary (protocol, &binary,
+ &len, NULL) == -1);
+ transport_read_error_at = -1;
+
+ transport_read_count = 0;
+ transport_read_error_at = 1;
+ g_assert (thrift_binary_protocol_read_binary (protocol, &binary,
+ &len, NULL) == -1);
+ transport_read_error_at = -1;
+
+ transport_read_error = 1;
+ g_assert (thrift_binary_protocol_read_bool (protocol,
+ &value_boolean, NULL) == -1);
+ g_assert (thrift_binary_protocol_read_byte (protocol,
+ &value_byte, NULL) == -1);
+ g_assert (thrift_binary_protocol_read_i16 (protocol,
+ &value_16, NULL) == -1);
+ g_assert (thrift_binary_protocol_read_i32 (protocol, &value_32, NULL) == -1);
+ g_assert (thrift_binary_protocol_read_i64 (protocol, &value_64, NULL) == -1);
+ g_assert (thrift_binary_protocol_read_double (protocol,
+ &value_double, NULL) == -1);
+ transport_read_error = 0;
+
+ /* test partial write failure */
+ thrift_protocol_read_i32 (protocol, &value_32, NULL);
+
+ thrift_transport_read_end (client, NULL);
+ thrift_transport_close (client, NULL);
+
+ g_object_unref (tbp);
+ g_object_unref (client);
+ g_object_unref (tsocket);
+}
+
+static void
+thrift_server_complex_types (const int port)
+{
+ ThriftServerTransport *transport = NULL;
+ ThriftTransport *client = NULL;
+ ThriftBinaryProtocol *tbp = NULL;
+ ThriftProtocol *protocol = NULL;
+ gchar *struct_name = NULL;
+ gchar *field_name = NULL;
+ gchar *message_name = NULL;
+ ThriftType element_type, key_type, value_type, field_type;
+ ThriftMessageType message_type;
+ gint8 value = 0;
+ gint16 field_id = 0;
+ guint32 size = 0;
+ gint32 seqid = 0;
+ gint32 version = 0;
+
+ ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+ "port", port, NULL);
+ transport = THRIFT_SERVER_TRANSPORT (tsocket);
+ thrift_server_transport_listen (transport, NULL);
+ client = thrift_server_transport_accept (transport, NULL);
+ g_assert (client != NULL);
+
+ tbp = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport",
+ client, NULL);
+ protocol = THRIFT_PROTOCOL (tbp);
+
+ thrift_binary_protocol_read_struct_begin (protocol, &struct_name, NULL);
+ thrift_binary_protocol_read_struct_end (protocol, NULL);
+
+ thrift_binary_protocol_read_field_begin (protocol, &field_name, &field_type,
+ &field_id, NULL);
+ thrift_binary_protocol_read_field_end (protocol, NULL);
+
+ /* test first read error on a field */
+ transport_read_error = 1;
+ g_assert (thrift_binary_protocol_read_field_begin (protocol,
+ &field_name, &field_type,
+ &field_id, NULL) == -1);
+ transport_read_error = 0;
+
+ /* test 2nd write failure */
+ thrift_binary_protocol_read_byte (protocol, &value, NULL);
+
+ /* test 2nd read failure on a field */
+ transport_read_count = 0;
+ transport_read_error_at = 1;
+ g_assert (thrift_binary_protocol_read_field_begin (protocol,
+ &field_name, &field_type,
+ &field_id, NULL) == -1);
+ transport_read_error_at = -1;
+
+ /* test field stop */
+ thrift_binary_protocol_read_field_begin (protocol, &field_name, &field_type,
+ &field_id, NULL);
+
+ thrift_binary_protocol_read_map_begin (protocol, &key_type, &value_type,
+ &size, NULL);
+ thrift_binary_protocol_read_map_end (protocol, NULL);
+
+ /* test read failure on a map */
+ transport_read_count = 0;
+ transport_read_error_at = 0;
+ g_assert (thrift_binary_protocol_read_map_begin (protocol,
+ &key_type, &value_type,
+ &size, NULL) == -1);
+ transport_read_error_at = -1;
+
+ /* test 2nd read failure on a map */
+ transport_read_count = 0;
+ transport_read_error_at = 1;
+ g_assert (thrift_binary_protocol_read_map_begin (protocol,
+ &key_type, &value_type,
+ &size, NULL) == -1);
+ transport_read_error_at = -1;
+
+ /* test 3rd read failure on a map */
+ transport_read_count = 0;
+ transport_read_error_at = 2;
+ g_assert (thrift_binary_protocol_read_map_begin (protocol,
+ &key_type, &value_type,
+ &size, NULL) == -1);
+ transport_read_error_at = -1;
+
+ /* test 2nd write failure */
+ thrift_binary_protocol_read_byte (protocol, &value, NULL);
+
+ /* test 3rd write failure */
+ thrift_binary_protocol_read_byte (protocol, &value, NULL);
+ thrift_binary_protocol_read_byte (protocol, &value, NULL);
+
+ /* test negative map size */
+ g_assert (thrift_binary_protocol_read_map_begin (protocol,
+ &key_type, &value_type,
+ &size, NULL) == -1);
+
+ /* test list operations */
+ thrift_binary_protocol_read_list_begin (protocol, &element_type, &size, NULL);
+ thrift_binary_protocol_read_list_end (protocol, NULL);
+
+ /* test read failure */
+ transport_read_error = 1;
+ g_assert (thrift_binary_protocol_read_list_begin (protocol, &element_type,
+ &size, NULL) == -1);
+ transport_read_error = 0;
+
+ /* test 2nd read failure */
+ transport_read_count = 0;
+ transport_read_error_at = 1;
+ thrift_binary_protocol_read_list_begin (protocol, &element_type, &size, NULL);
+ transport_read_error_at = -1;
+
+ /* test negative list size failure */
+ thrift_binary_protocol_read_list_begin (protocol, &element_type, &size, NULL);
+
+ /* test 2nd write failure */
+ thrift_binary_protocol_read_byte (protocol, &value, NULL);
+
+ /* test set operations */
+ thrift_binary_protocol_read_set_begin (protocol, &element_type, &size, NULL);
+ thrift_binary_protocol_read_set_end (protocol, NULL);
+
+ /* broken read */
+ transport_read_error = 1;
+ g_assert (thrift_binary_protocol_read_message_begin (protocol, &message_name,
+ &message_type, &seqid,
+ NULL) == -1);
+ transport_read_error = 0;
+
+ /* invalid protocol version */
+ g_assert (thrift_binary_protocol_read_message_begin (protocol, &message_name,
+ &message_type, &seqid,
+ NULL) == -1);
+
+ /* sz > 0 */
+ g_assert (thrift_binary_protocol_read_message_begin (protocol, &message_name,
+ &message_type, &seqid,
+ NULL) > 0);
+
+ /* read a valid message */
+ g_assert (thrift_binary_protocol_read_message_begin (protocol, &message_name,
+ &message_type, &seqid,
+ NULL) > 0);
+ g_free (message_name);
+
+ /* broken 2nd read on a message */
+ transport_read_count = 0;
+ transport_read_error_at = 1;
+ g_assert (thrift_binary_protocol_read_message_begin (protocol, &message_name,
+ &message_type, &seqid,
+ NULL) == -1);
+ transport_read_error_at = -1;
+
+ /* broken 3rd read on a message */
+ transport_read_count = 0;
+ transport_read_error_at = 3; /* read_string does two reads */
+ g_assert (thrift_binary_protocol_read_message_begin (protocol, &message_name,
+ &message_type, &seqid,
+ NULL) == -1);
+ g_free (message_name);
+ transport_read_error_at = -1;
+
+ /* read a valid message */
+ g_assert (thrift_binary_protocol_read_message_begin (protocol, &message_name,
+ &message_type, &seqid,
+ NULL) > 0);
+ g_free (message_name);
+
+ g_assert (thrift_binary_protocol_read_message_end (protocol, NULL) == 0);
+
+ /* handle 2nd write failure on a message */
+ thrift_binary_protocol_read_i32 (protocol, &version, NULL);
+
+ /* handle 2nd write failure on a message */
+ thrift_binary_protocol_read_i32 (protocol, &version, NULL);
+ thrift_binary_protocol_read_string (protocol, &message_name, NULL);
+
+ g_object_unref (client);
+ /* TODO: investigate g_object_unref (tbp); */
+ g_object_unref (tsocket);
+}
+
+static void
+thrift_server_many_frames (const int port)
+{
+ ThriftServerTransport *transport = NULL;
+ ThriftTransport *client = NULL;
+ ThriftBinaryProtocol *tbp = NULL;
+ ThriftProtocol *protocol = NULL;
+ ThriftServerSocket *tsocket = NULL;
+ gboolean value_boolean = FALSE;
+ gint8 value_byte = 0;
+ gint16 value_16 = 0;
+ gint32 value_32 = 0;
+ gint64 value_64 = 0;
+ gdouble value_double = 0;
+ gchar *string = NULL;
+ gchar *empty_string = NULL;
+ gpointer binary = NULL;
+ guint32 len = 0;
+ void *comparator = (void *) TEST_STRING;
+
+ tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET, "port", port, NULL);
+ transport = THRIFT_SERVER_TRANSPORT (tsocket);
+ thrift_server_transport_listen (transport, NULL);
+
+ /* wrap the client in a framed transport */
+ client = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT, "transport",
+ thrift_server_transport_accept (transport, NULL),
+ "r_buf_size", 1, NULL);
+ g_assert (client != NULL);
+
+ tbp = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport",
+ client, NULL);
+ protocol = THRIFT_PROTOCOL (tbp);
+
+ g_assert (thrift_binary_protocol_read_bool (protocol,
+ &value_boolean, NULL) > 0);
+ g_assert (thrift_binary_protocol_read_byte (protocol, &value_byte, NULL) > 0);
+ g_assert (thrift_binary_protocol_read_i16 (protocol, &value_16, NULL) > 0);
+ g_assert (thrift_binary_protocol_read_i32 (protocol, &value_32, NULL) > 0);
+ g_assert (thrift_binary_protocol_read_i64 (protocol, &value_64, NULL) > 0);
+ g_assert (thrift_binary_protocol_read_double (protocol,
+ &value_double, NULL) > 0);
+ g_assert (thrift_binary_protocol_read_string (protocol, &string, NULL) > 0);
+ g_assert (thrift_binary_protocol_read_string (protocol, &empty_string,
+ NULL) > 0);
+ g_assert (thrift_binary_protocol_read_binary (protocol, &binary,
+ &len, NULL) > 0);
+
+ g_assert (value_boolean == TEST_BOOL);
+ g_assert (value_byte == TEST_BYTE);
+ g_assert (value_16 == TEST_I16);
+ g_assert (value_32 == TEST_I32);
+ g_assert (value_64 == TEST_I64);
+ g_assert (value_double == TEST_DOUBLE);
+ g_assert (strcmp (TEST_STRING, string) == 0);
+ g_assert (strcmp ("", empty_string) == 0);
+ g_assert (memcmp (comparator, binary, len) == 0);
+
+ g_free (string);
+ g_free (empty_string);
+ g_free (binary);
+
+ g_assert (thrift_binary_protocol_read_binary (protocol, &binary,
+ &len, NULL) > 0);
+ g_assert (binary == NULL);
+ g_assert (len == 0);
+ g_free (binary);
+
+ thrift_transport_read_end (client, NULL);
+ thrift_transport_close (client, NULL);
+
+ g_object_unref (tbp);
+ g_object_unref (client);
+ g_object_unref (tsocket);
+}
+
+int
+main(int argc, char *argv[])
+{
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init();
+#endif
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/testbinaryprotocol/CreateAndDestroy", test_create_and_destroy);
+ g_test_add_func ("/testbinaryprotocol/Initialize", test_initialize);
+ g_test_add_func ("/testbinaryprotocol/ReadAndWritePrimitives", test_read_and_write_primitives);
+ g_test_add_func ("/testbinaryprotocol/ReadAndWriteComplexTypes", test_read_and_write_complex_types);
+ g_test_add_func ("/testbinaryprotocol/ReadAndWriteManyFrames",
+ test_read_and_write_many_frames);
+
+ return g_test_run ();
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/testbufferedtransport.c b/src/jaegertracing/thrift/lib/c_glib/test/testbufferedtransport.c
new file mode 100755
index 000000000..d01806d61
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/testbufferedtransport.c
@@ -0,0 +1,325 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <netdb.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/transport/thrift_socket.h>
+#include <thrift/c_glib/transport/thrift_server_transport.h>
+#include <thrift/c_glib/transport/thrift_server_socket.h>
+
+#define TEST_DATA { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }
+
+#include "../src/thrift/c_glib/transport/thrift_buffered_transport.c"
+
+static void thrift_server (const int port);
+static void thrift_socket_server_open (const int port, int times);
+
+/* test object creation and destruction */
+static void
+test_create_and_destroy(void)
+{
+ ThriftTransport *transport = NULL;
+ guint r_buf_size = 0;
+ guint w_buf_size = 0;
+
+ GObject *object = NULL;
+ object = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT, NULL);
+ g_assert (object != NULL);
+ g_object_get (G_OBJECT (object), "transport", &transport,
+ "r_buf_size", &r_buf_size,
+ "w_buf_size", &w_buf_size, NULL);
+ g_object_unref (object);
+}
+
+static void
+test_open_and_close(void)
+{
+ ThriftSocket *tsocket = NULL;
+ ThriftTransport *transport = NULL;
+ GError *err = NULL;
+ pid_t pid;
+ int port = 51199;
+ int status;
+
+ pid = fork ();
+ g_assert ( pid >= 0 );
+
+ if ( pid == 0 )
+ {
+ /* child listens */
+ thrift_socket_server_open (port,1);
+ exit (0);
+ } else {
+ /* parent connects, wait a bit for the socket to be created */
+ sleep (1);
+ /* create a ThriftSocket */
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
+ "port", port, NULL);
+
+ /* create a BufferedTransport wrapper of the Socket */
+ transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT,
+ "transport", THRIFT_TRANSPORT (tsocket), NULL);
+
+ /* this shouldn't work */
+ g_assert (thrift_buffered_transport_open (transport, NULL) == TRUE);
+ g_assert (thrift_buffered_transport_is_open (transport) == TRUE);
+ g_assert (thrift_buffered_transport_close (transport, NULL) == TRUE);
+ g_object_unref (transport);
+ g_object_unref (tsocket);
+
+ /* try and underlying socket failure */
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost.broken",
+ NULL);
+
+ /* create a BufferedTransport wrapper of the Socket */
+ transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT,
+ "transport", THRIFT_TRANSPORT (tsocket), NULL);
+
+ g_assert (thrift_buffered_transport_open (transport, &err) == FALSE);
+ g_object_unref (transport);
+ g_object_unref (tsocket);
+ g_error_free (err);
+ err = NULL;
+ g_assert ( wait (&status) == pid );
+ g_assert ( status == 0 );
+ }
+}
+
+static void
+test_read_and_write(void)
+{
+ int status;
+ pid_t pid;
+ ThriftSocket *tsocket = NULL;
+ ThriftTransport *transport = NULL;
+ int port = 51199;
+ guchar buf[10] = TEST_DATA; /* a buffer */
+
+ pid = fork ();
+ g_assert ( pid >= 0 );
+
+ if ( pid == 0 )
+ {
+ /* child listens */
+ thrift_server (port);
+ exit (0);
+ } else {
+ /* parent connects, wait a bit for the socket to be created */
+ sleep (1);
+
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
+ "port", port, NULL);
+ transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT,
+ "transport", THRIFT_TRANSPORT (tsocket),
+ "w_buf_size", 4, NULL);
+
+ g_assert (thrift_buffered_transport_open (transport, NULL) == TRUE);
+ g_assert (thrift_buffered_transport_is_open (transport));
+
+ /* write 10 bytes */
+ thrift_buffered_transport_write (transport, buf, 10, NULL);
+
+ /* write 1 byte at a time */
+ thrift_buffered_transport_write (transport, buf, 1, NULL);
+ thrift_buffered_transport_write (transport, buf, 1, NULL);
+ thrift_buffered_transport_write (transport, buf, 1, NULL);
+
+ /* overflow the buffer */
+ thrift_buffered_transport_write (transport, buf, 2, NULL);
+ thrift_buffered_transport_write (transport, buf, 1, NULL);
+ thrift_buffered_transport_flush (transport, NULL);
+
+ /* write 1 byte and flush */
+ thrift_buffered_transport_write (transport, buf, 1, NULL);
+ thrift_buffered_transport_flush (transport, NULL);
+
+ /* write and overflow buffer with 2 system calls */
+ thrift_buffered_transport_write (transport, buf, 1, NULL);
+ thrift_buffered_transport_write (transport, buf, 3, NULL);
+
+ /* write 10 bytes */
+ thrift_buffered_transport_write (transport, buf, 10, NULL);
+
+ thrift_buffered_transport_write_end (transport, NULL);
+ thrift_buffered_transport_flush (transport, NULL);
+ thrift_buffered_transport_close (transport, NULL);
+
+ g_object_unref (transport);
+ g_object_unref (tsocket);
+
+ g_assert ( wait (&status) == pid );
+ g_assert ( status == 0 );
+ }
+}
+
+
+static void
+thrift_socket_server_open (const int port, int times)
+{
+ ThriftServerTransport *transport = NULL;
+ ThriftTransport *client = NULL;
+ int i;
+ ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+ "port", port, NULL);
+
+ transport = THRIFT_SERVER_TRANSPORT (tsocket);
+ thrift_server_transport_listen (transport, NULL);
+ for(i=0;i<times;i++){
+ client = thrift_server_transport_accept (transport, NULL);
+ g_assert (client != NULL);
+ thrift_socket_close (client, NULL);
+ g_object_unref (client);
+ }
+ g_object_unref (tsocket);
+}
+
+static void
+thrift_server (const int port)
+{
+ int bytes = 0;
+ ThriftServerTransport *transport = NULL;
+ ThriftTransport *client = NULL;
+ guchar buf[10]; /* a buffer */
+ guchar match[10] = TEST_DATA;
+
+ ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+ "port", port, NULL);
+
+ transport = THRIFT_SERVER_TRANSPORT (tsocket);
+ thrift_server_transport_listen (transport, NULL);
+
+ /* wrap the client in a BufferedTransport */
+ client = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT, "transport",
+ thrift_server_transport_accept (transport, NULL),
+ "r_buf_size", 5, NULL);
+ g_assert (client != NULL);
+
+ /* read 10 bytes */
+ bytes = thrift_buffered_transport_read (client, buf, 10, NULL);
+ g_assert (bytes == 10); /* make sure we've read 10 bytes */
+ g_assert ( memcmp (buf, match, 10) == 0 ); /* make sure what we got matches */
+
+ /* read 1 byte */
+ bytes = thrift_buffered_transport_read (client, buf, 1, NULL);
+
+ bytes = thrift_buffered_transport_read (client, buf, 6, NULL);
+ bytes = thrift_buffered_transport_read (client, buf, 2, NULL);
+ bytes = thrift_buffered_transport_read (client, buf, 1, NULL);
+
+ thrift_buffered_transport_read_end (client, NULL);
+ thrift_buffered_transport_close (client, NULL);
+ g_object_unref (client);
+ g_object_unref (tsocket);
+}
+
+static void
+test_write_fail(void)
+{
+ int status;
+ pid_t pid;
+ ThriftSocket *tsocket = NULL;
+ ThriftTransport *transport = NULL;
+ int port = 51198;
+ guchar buf[10] = TEST_DATA; /* a buffer */
+
+ /* SIGPIPE when send to disconnected socket */
+ signal(SIGPIPE, SIG_IGN);
+
+ pid = fork ();
+ g_assert ( pid >= 0 );
+
+ if ( pid == 0 )
+ {
+ /* child listens */
+ ThriftServerTransport *transport = NULL;
+ ThriftTransport *client = NULL;
+
+ ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+ "port", port, NULL);
+
+ transport = THRIFT_SERVER_TRANSPORT (tsocket);
+ thrift_server_transport_listen (transport, NULL);
+
+ /* wrap the client in a BufferedTransport */
+ client = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT, "transport",
+ thrift_server_transport_accept (transport, NULL),
+ "r_buf_size", 5, NULL);
+ g_assert (client != NULL);
+
+ /* just close socket */
+ thrift_buffered_transport_close (client, NULL);
+ g_object_unref (client);
+ g_object_unref (tsocket);
+ exit (0);
+ } else {
+ /* parent connects, wait a bit for the socket to be created */
+ sleep (1);
+
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
+ "port", port, NULL);
+ transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT,
+ "transport", THRIFT_TRANSPORT (tsocket),
+ "w_buf_size", 4, NULL);
+
+
+ g_assert (thrift_buffered_transport_open (transport, NULL) == TRUE);
+ g_assert (thrift_buffered_transport_is_open (transport));
+
+ /* recognize disconnection */
+ sleep(1);
+ g_assert (thrift_buffered_transport_write (transport, buf, 10, NULL) == TRUE);
+ g_assert (thrift_buffered_transport_write (transport, buf, 10, NULL) == FALSE);
+
+ /* write and overflow buffer */
+ g_assert (thrift_buffered_transport_write (transport, buf, 10, NULL) == FALSE);
+
+ /* write 1 and flush */
+ g_assert (thrift_buffered_transport_write (transport, buf, 1, NULL) == TRUE);
+ g_assert (thrift_buffered_transport_flush (transport, NULL) == FALSE);
+
+ thrift_buffered_transport_close (transport, NULL);
+
+ g_object_unref (transport);
+ g_object_unref (tsocket);
+
+ g_assert ( wait (&status) == pid );
+ g_assert ( status == 0 );
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init();
+#endif
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/testbufferedtransport/CreateAndDestroy", test_create_and_destroy);
+ g_test_add_func ("/testbufferedtransport/OpenAndClose", test_open_and_close);
+ g_test_add_func ("/testbufferedtransport/ReadAndWrite", test_read_and_write);
+ g_test_add_func ("/testbufferedtransport/WriteFail", test_write_fail);
+
+ return g_test_run ();
+}
+
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/testcompactprotocol.c b/src/jaegertracing/thrift/lib/c_glib/test/testcompactprotocol.c
new file mode 100755
index 000000000..e5ad49120
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/testcompactprotocol.c
@@ -0,0 +1,1293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Disable string-function optimizations when glibc is used, as these produce
+ compiler warnings about string length when a string function is used inside
+ a call to g_assert () */
+#if !defined(__APPLE__) && !defined(__FreeBSD__) && \
+ !defined(__OpenBSD__) && !defined(__NetBSD__)
+#include <features.h>
+#endif
+
+#ifdef __GLIBC__
+#define __NO_STRING_INLINES 1
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <sys/wait.h>
+
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/transport/thrift_socket.h>
+#include <thrift/c_glib/transport/thrift_server_socket.h>
+#include <thrift/c_glib/transport/thrift_framed_transport.h>
+
+#define TEST_BOOL TRUE
+#define TEST_BYTE 123
+#define TEST_I16 12345
+#define TEST_I32 1234567890
+#define TEST_I64 123456789012345
+#define TEST_NI16 (-12345)
+#define TEST_NI32 (-1234567890)
+#define TEST_NI64 (-123456789012345)
+#define TEST_DOUBLE 1234567890.123
+#define TEST_STRING "this is a test string 1234567890!@#$%^&*()"
+#define TEST_PORT 51199
+
+static int transport_read_count = 0;
+static int transport_read_error = 0;
+static int transport_read_error_at = -1;
+gint32
+my_thrift_transport_read_all (ThriftTransport *transport, gpointer buf,
+ guint32 len, GError **error)
+{
+ if (transport_read_count != transport_read_error_at
+ && transport_read_error == 0)
+ {
+ transport_read_count++;
+ return thrift_transport_read_all (transport, buf, len, error);
+ }
+ return -1;
+}
+
+static int transport_write_count = 0;
+static int transport_write_error = 0;
+static int transport_write_error_at = -1;
+gboolean
+my_thrift_transport_write (ThriftTransport *transport, const gpointer buf,
+ const guint32 len, GError **error)
+{
+ if (transport_write_count != transport_write_error_at
+ && transport_write_error == 0)
+ {
+ transport_write_count++;
+ return thrift_transport_write (transport, buf, len, error);
+ }
+ return FALSE;
+}
+
+#define thrift_transport_read_all my_thrift_transport_read_all
+#define thrift_transport_write my_thrift_transport_write
+#include "../src/thrift/c_glib/protocol/thrift_compact_protocol.c"
+#undef thrift_transport_read_all
+#undef thrift_transport_write
+
+static void thrift_server_primitives (const int port);
+static void thrift_server_complex_types (const int port);
+static void thrift_server_many_frames (const int port);
+
+static void
+test_create_and_destroy (void)
+{
+ GObject *object = NULL;
+
+ /* create an object and then destroy it */
+ object = g_object_new (THRIFT_TYPE_COMPACT_PROTOCOL, NULL);
+ g_assert (object != NULL);
+ g_object_unref (object);
+}
+
+static void
+test_initialize (void)
+{
+ ThriftSocket *tsocket = NULL;
+ ThriftCompactProtocol *protocol = NULL;
+ ThriftSocket *temp = NULL;
+
+ /* create a ThriftTransport */
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
+ "port", 51188, NULL);
+ g_assert (tsocket != NULL);
+ /* create a ThriftCompactProtocol using the Transport */
+ protocol = g_object_new (THRIFT_TYPE_COMPACT_PROTOCOL, "transport",
+ tsocket, NULL);
+ g_assert (protocol != NULL);
+ /* fetch the properties */
+ g_object_get (G_OBJECT (protocol), "transport", &temp, NULL);
+ g_object_unref (temp);
+
+ /* clean up memory */
+ g_object_unref (protocol);
+ g_object_unref (tsocket);
+}
+
+static void
+test_read_and_write_primitives (void)
+{
+ int status;
+ pid_t pid;
+ ThriftSocket *tsocket = NULL;
+ ThriftTransport *transport = NULL;
+ ThriftCompactProtocol *tc = NULL;
+ ThriftProtocol *protocol = NULL;
+ gpointer binary = (gpointer *) TEST_STRING;
+ guint32 len = strlen (TEST_STRING);
+ int port = TEST_PORT;
+
+ /* fork a server from the client */
+ pid = fork ();
+ g_assert (pid >= 0);
+
+ if (pid == 0)
+ {
+ /* child listens */
+ thrift_server_primitives (port);
+ exit (0);
+ } else {
+ /* parent. wait a bit for the socket to be created. */
+ sleep (1);
+
+ /* create a ThriftSocket */
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
+ "port", port, NULL);
+ transport = THRIFT_TRANSPORT (tsocket);
+ thrift_transport_open (transport, NULL);
+ g_assert (thrift_transport_is_open (transport));
+
+ /* create a ThriftCompactTransport */
+ tc = g_object_new (THRIFT_TYPE_COMPACT_PROTOCOL, "transport",
+ tsocket, NULL);
+ protocol = THRIFT_PROTOCOL (tc);
+ g_assert (protocol != NULL);
+
+ /* write a bunch of primitives */
+ g_assert (thrift_compact_protocol_write_bool (protocol, TEST_BOOL, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_byte (protocol, TEST_BYTE, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_i16 (protocol, TEST_I16, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_i32 (protocol, TEST_I32, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_i64 (protocol, TEST_I64, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_i16 (protocol, TEST_NI16, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_i32 (protocol, TEST_NI32, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_i64 (protocol, TEST_NI64, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_i16 (protocol, 2, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_i32 (protocol, 2, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_i64 (protocol, 2, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_i16 (protocol, -2, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_i32 (protocol, -2, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_i64 (protocol, -2, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_double (protocol,
+ TEST_DOUBLE, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_string (protocol,
+ TEST_STRING, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_string (protocol, "", NULL) > 0);
+ g_assert (thrift_compact_protocol_write_binary (protocol, binary,
+ len, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_binary (protocol, NULL, 0, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_binary (protocol, binary,
+ len, NULL) > 0);
+
+ /* test write errors */
+ transport_write_error = 1;
+ g_assert (thrift_compact_protocol_write_byte (protocol, TEST_BYTE,
+ NULL) == -1);
+ g_assert (thrift_compact_protocol_write_i16 (protocol, TEST_I16, NULL) == -1);
+ g_assert (thrift_compact_protocol_write_i32 (protocol, TEST_I32, NULL) == -1);
+ g_assert (thrift_compact_protocol_write_i64 (protocol, TEST_I64, NULL) == -1);
+ g_assert (thrift_compact_protocol_write_i16 (protocol, TEST_NI16,
+ NULL) == -1);
+ g_assert (thrift_compact_protocol_write_i32 (protocol, TEST_NI32,
+ NULL) == -1);
+ g_assert (thrift_compact_protocol_write_i64 (protocol, TEST_NI64,
+ NULL) == -1);
+ g_assert (thrift_compact_protocol_write_double (protocol, TEST_DOUBLE,
+ NULL) == -1);
+ g_assert (thrift_compact_protocol_write_binary (protocol, binary, len,
+ NULL) == -1);
+ transport_write_error = 0;
+
+ /* test binary partial failure */
+ transport_write_count = 0;
+ transport_write_error_at = 1;
+ g_assert (thrift_compact_protocol_write_binary (protocol, binary,
+ len, NULL) == -1);
+ transport_write_error_at = -1;
+
+ /* clean up */
+ thrift_transport_close (transport, NULL);
+ g_object_unref (tsocket);
+ g_object_unref (protocol);
+ g_assert (wait (&status) == pid);
+ g_assert (status == 0);
+ }
+}
+
+static void
+test_read_and_write_complex_types (void)
+{
+ int status;
+ pid_t pid;
+ ThriftSocket *tsocket = NULL;
+ ThriftTransport *transport = NULL;
+ ThriftCompactProtocol *tc = NULL;
+ ThriftProtocol *protocol = NULL;
+ int port = TEST_PORT;
+
+ /* fork a server from the client */
+ pid = fork ();
+ g_assert (pid >= 0);
+
+ if (pid == 0)
+ {
+ /* child listens */
+ thrift_server_complex_types (port);
+ exit (0);
+ } else {
+ /* parent. wait a bit for the socket to be created. */
+ sleep (1);
+
+ /* create a ThriftSocket */
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
+ "port", port, NULL);
+ transport = THRIFT_TRANSPORT (tsocket);
+ thrift_transport_open (transport, NULL);
+ g_assert (thrift_transport_is_open (transport));
+
+ /* create a ThriftCompactTransport */
+ tc = g_object_new (THRIFT_TYPE_COMPACT_PROTOCOL, "transport",
+ tsocket, NULL);
+ protocol = THRIFT_PROTOCOL (tc);
+ g_assert (protocol != NULL);
+
+ /* test structures */
+ g_assert (thrift_compact_protocol_write_struct_begin (protocol,
+ NULL, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_struct_end (protocol, NULL) == 0);
+
+ /* test field state w.r.t. deltas */
+
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE, 1, NULL) == 1);
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 16, NULL) == 1);
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 17, NULL) == 1);
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 15, NULL) > 1);
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 30, NULL) == 1);
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 46, NULL) > 1);
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 47, NULL) == 1);
+
+ /* test fields */
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 1, NULL) > 1);
+ g_assert (thrift_compact_protocol_write_field_end (protocol, NULL) == 0);
+
+ /* test field state w.r.t. structs */
+
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 1, NULL) > 1);
+ g_assert (thrift_compact_protocol_write_field_end (protocol, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 16, NULL) == 1);
+ g_assert (thrift_compact_protocol_write_field_end (protocol, NULL) == 0);
+
+ g_assert (thrift_compact_protocol_write_struct_begin (protocol,
+ NULL, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 17, NULL) > 1);
+ g_assert (thrift_compact_protocol_write_field_end (protocol, NULL) == 0);
+
+ g_assert (thrift_compact_protocol_write_struct_begin (protocol,
+ NULL, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 18, NULL) > 1);
+ g_assert (thrift_compact_protocol_write_field_end (protocol, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 19, NULL) == 1);
+ g_assert (thrift_compact_protocol_write_field_end (protocol, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_struct_end (protocol, NULL) == 0);
+
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 18, NULL) == 1);
+ g_assert (thrift_compact_protocol_write_field_end (protocol, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 25, NULL) == 1);
+ g_assert (thrift_compact_protocol_write_field_end (protocol, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_struct_end (protocol, NULL) == 0);
+
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 17, NULL) == 1);
+ g_assert (thrift_compact_protocol_write_field_end (protocol, NULL) == 0);
+
+ /* test field state w.r.t. bools */
+
+ /* deltas */
+ /* non-bool field -> bool field -> non-bool field */
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 18, NULL) == 1);
+ g_assert (thrift_compact_protocol_write_field_end (protocol, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test", T_BOOL,
+ 19, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_bool (protocol, TEST_BOOL,
+ NULL) == 1);
+ g_assert (thrift_compact_protocol_write_field_end (protocol, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 20, NULL) == 1);
+ g_assert (thrift_compact_protocol_write_field_end (protocol, NULL) == 0);
+ /* bool -> bool field -> bool */
+ g_assert (thrift_compact_protocol_write_bool (protocol, TEST_BOOL, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test", T_BOOL,
+ 21, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_bool (protocol, TEST_BOOL,
+ NULL) == 1);
+ g_assert (thrift_compact_protocol_write_field_end (protocol, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_bool (protocol, TEST_BOOL, NULL) > 0);
+
+ /* no deltas */
+ /* non-bool field -> bool field -> non-bool field */
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 1, NULL) > 1);
+ g_assert (thrift_compact_protocol_write_field_end (protocol, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test", T_BOOL,
+ 1, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_bool (protocol, TEST_BOOL, NULL) > 1);
+ g_assert (thrift_compact_protocol_write_field_end (protocol, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 1, NULL) > 1);
+ g_assert (thrift_compact_protocol_write_field_end (protocol, NULL) == 0);
+ /* bool -> bool field -> bool */
+ g_assert (thrift_compact_protocol_write_bool (protocol, TEST_BOOL, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test", T_BOOL,
+ 1, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_bool (protocol, TEST_BOOL, NULL) > 1);
+ g_assert (thrift_compact_protocol_write_field_end (protocol, NULL) == 0);
+ g_assert (thrift_compact_protocol_write_bool (protocol, TEST_BOOL, NULL) > 0);
+
+ /* test write error */
+ transport_write_error = 1;
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 1, NULL) == -1);
+ transport_write_error = 0;
+
+ /* test 2nd write error */
+ transport_write_count = 0;
+ transport_write_error_at = 1;
+ g_assert (thrift_compact_protocol_write_field_begin (protocol, "test",
+ T_DOUBLE,
+ 1, NULL) == -1);
+ transport_write_error_at = -1;
+
+ /* test 2nd read failure on a field */
+ thrift_compact_protocol_write_byte (protocol, T_DOUBLE, NULL);
+
+ /* test write_field_stop */
+ g_assert (thrift_compact_protocol_write_field_stop (protocol, NULL) > 0);
+
+ /* write a map */
+ g_assert (thrift_compact_protocol_write_map_begin (protocol, T_DOUBLE,
+ T_DOUBLE,
+ 1, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_map_end (protocol, NULL) == 0);
+
+ /* test 1st read failure on map---nothing to do on our side */
+
+ /* test 2nd read failure on a map */
+ thrift_compact_protocol_write_byte (protocol, T_DOUBLE, NULL);
+
+ /* test 1st write failure on a map */
+ transport_write_error = 1;
+ g_assert (thrift_compact_protocol_write_map_begin (protocol, T_DOUBLE,
+ T_DOUBLE,
+ 1, NULL) == -1);
+ transport_write_error = 0;
+
+ /* test 2nd write failure on a map */
+ transport_write_count = 0;
+ transport_write_error_at = 1;
+ g_assert (thrift_compact_protocol_write_map_begin (protocol, T_DOUBLE,
+ T_DOUBLE,
+ 1, NULL) == -1);
+ transport_write_error_at = -1;
+
+ /* test negative map size */
+ thrift_compact_protocol_write_varint32 (tc, -10, NULL);
+ thrift_compact_protocol_write_byte (protocol, T_DOUBLE, NULL);
+
+ /* test list operations */
+ g_assert (thrift_compact_protocol_write_list_begin (protocol, T_DOUBLE,
+ 15, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_list_end (protocol, NULL) == 0);
+
+ /* test 1st read failure on a small list---nothing to do on our end */
+
+ /* test 1st read failure on a big list---nothing to do on our end */
+
+ /* test 2nd read failure on a big list */
+ thrift_compact_protocol_write_byte (protocol, (gint8) 0xf0, NULL);
+
+ /* test negative list size */
+ thrift_compact_protocol_write_byte (protocol, (gint8) 0xf0, NULL);
+ thrift_compact_protocol_write_varint32 (tc, -10, NULL);
+
+ /* test first write error on a small list */
+ transport_write_error = 1;
+ g_assert (thrift_compact_protocol_write_list_begin (protocol, T_DOUBLE,
+ 14, NULL) == -1);
+ transport_write_error = 0;
+
+ /* test first write error on a big list */
+ transport_write_error = 1;
+ g_assert (thrift_compact_protocol_write_list_begin (protocol, T_DOUBLE,
+ 15, NULL) == -1);
+ transport_write_error = 0;
+
+ /* test 2nd write error on a big list */
+ transport_write_count = 0;
+ transport_write_error_at = 1;
+ g_assert (thrift_compact_protocol_write_list_begin (protocol, T_DOUBLE,
+ 15, NULL) == -1);
+ transport_write_error_at = -1;
+
+ /* test set operation s*/
+ g_assert (thrift_compact_protocol_write_set_begin (protocol, T_DOUBLE,
+ 1, NULL) > 0);
+ g_assert (thrift_compact_protocol_write_set_end (protocol, NULL) == 0);
+
+ /* invalid protocol */
+ g_assert (thrift_compact_protocol_write_byte (protocol, 0, NULL) > 0);
+
+ /* invalid version */
+ g_assert (thrift_compact_protocol_write_byte (protocol, (gint8) 0x82u,
+ NULL) > 0);
+ g_assert (thrift_compact_protocol_write_byte (protocol, 0, NULL) > 0);
+
+ /* send a valid message */
+ g_assert (thrift_compact_protocol_write_byte (protocol, (gint8) 0x82u,
+ NULL) > 0);
+ g_assert (thrift_compact_protocol_write_byte (protocol, 0x01u, NULL) > 0);
+ thrift_compact_protocol_write_varint32 (tc, 1, NULL);
+ thrift_compact_protocol_write_string (protocol, "test", NULL);
+
+ /* broken 2nd read */
+ thrift_compact_protocol_write_byte (protocol, (gint8) 0x82u, NULL);
+
+ /* send a broken 3rd read */
+ thrift_compact_protocol_write_byte (protocol, (gint8) 0x82u, NULL);
+ thrift_compact_protocol_write_byte (protocol, 0x01u, NULL);
+
+ /* send a broken 4th read */
+ thrift_compact_protocol_write_byte (protocol, (gint8) 0x82u, NULL);
+ thrift_compact_protocol_write_byte (protocol, 0x01u, NULL);
+ thrift_compact_protocol_write_varint32 (tc, 1, NULL);
+
+ /* send a valid message */
+ g_assert (thrift_compact_protocol_write_message_begin (protocol, "test",
+ T_CALL, 1, NULL) > 0);
+
+ g_assert (thrift_compact_protocol_write_message_end (protocol, NULL) == 0);
+
+ /* send broken writes */
+ transport_write_error = 1;
+ g_assert (thrift_compact_protocol_write_message_begin (protocol, "test",
+ T_CALL, 1, NULL) == -1);
+ transport_write_error = 0;
+
+ transport_write_count = 0;
+ transport_write_error_at = 1;
+ g_assert (thrift_compact_protocol_write_message_begin (protocol, "test",
+ T_CALL, 1, NULL) == -1);
+ transport_write_error_at = -1;
+
+ transport_write_count = 0;
+ transport_write_error_at = 2;
+ g_assert (thrift_compact_protocol_write_message_begin (protocol, "test",
+ T_CALL, 1, NULL) == -1);
+ transport_write_error_at = -1;
+
+ transport_write_count = 0;
+ transport_write_error_at = 3;
+ g_assert (thrift_compact_protocol_write_message_begin (protocol, "test",
+ T_CALL, 1, NULL) == -1);
+ transport_write_error_at = -1;
+
+ /* clean up */
+ thrift_transport_close (transport, NULL);
+ g_object_unref (tsocket);
+ g_object_unref (protocol);
+ g_assert (wait (&status) == pid);
+ g_assert (status == 0);
+ }
+}
+
+static void
+test_read_and_write_many_frames (void)
+{
+ int status;
+ pid_t pid;
+ ThriftSocket *tsocket = NULL;
+ ThriftTransport *transport = NULL;
+ ThriftFramedTransport *ft = NULL;
+ ThriftCompactProtocol *tc = NULL;
+ ThriftProtocol *protocol = NULL;
+ gpointer binary = (gpointer *) TEST_STRING;
+ const guint32 len = strlen (TEST_STRING);
+ int port = TEST_PORT;
+
+ /* fork a server from the client */
+ pid = fork ();
+ g_assert (pid >= 0);
+
+ if (pid == 0)
+ {
+ /* child listens */
+ thrift_server_many_frames (port);
+ exit (0);
+ } else {
+ /* parent. wait a bit for the socket to be created. */
+ sleep (1);
+
+ /* create a ThriftSocket */
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
+ "port", port, NULL);
+ g_assert (tsocket != NULL);
+ transport = THRIFT_TRANSPORT (tsocket);
+
+ /* wrap in a framed transport */
+ ft = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT, "transport", transport,
+ "w_buf_size", 1, NULL);
+ g_assert (ft != NULL);
+ transport = THRIFT_TRANSPORT (ft);
+
+ thrift_transport_open (transport, NULL);
+ g_assert (thrift_transport_is_open (transport));
+
+ /* create a compact protocol */
+ tc = g_object_new (THRIFT_TYPE_COMPACT_PROTOCOL, "transport",
+ transport, NULL);
+ protocol = THRIFT_PROTOCOL (tc);
+ g_assert (protocol != NULL);
+
+ /* write a bunch of primitives */
+ g_assert (thrift_compact_protocol_write_bool (protocol, TEST_BOOL,
+ NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_byte (protocol, TEST_BYTE,
+ NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_i16 (protocol, TEST_I16, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_i32 (protocol, TEST_I32, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_i64 (protocol, TEST_I64, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_i16 (protocol, TEST_NI16, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_i32 (protocol, TEST_NI32, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_i64 (protocol, TEST_NI64, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_i16 (protocol, 2, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_i32 (protocol, 2, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_i64 (protocol, 2, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_i16 (protocol, -2, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_i32 (protocol, -2, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_i64 (protocol, -2, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_double (protocol,
+ TEST_DOUBLE, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_string (protocol,
+ TEST_STRING, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_string (protocol, "", NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_binary (protocol, binary,
+ len, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_binary (protocol, NULL,
+ 0, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+ g_assert (thrift_compact_protocol_write_binary (protocol, binary,
+ len, NULL) > 0);
+ thrift_transport_flush (transport, NULL);
+
+ /* clean up */
+ thrift_transport_write_end (transport, NULL);
+ thrift_transport_close (transport, NULL);
+ g_object_unref (ft);
+ g_object_unref (tsocket);
+ g_object_unref (tc);
+ g_assert (wait (&status) == pid);
+ g_assert (status == 0);
+ }
+}
+
+
+static void
+thrift_server_primitives (const int port)
+{
+ ThriftServerTransport *transport = NULL;
+ ThriftTransport *client = NULL;
+ ThriftCompactProtocol *tc = NULL;
+ ThriftProtocol *protocol = NULL;
+ gboolean value_boolean = FALSE;
+ gint8 value_byte = 0,
+ zigzag_p16 = 0, zigzag_p32 = 0, zigzag_p64 = 0,
+ zigzag_n16 = 0, zigzag_n32 = 0, zigzag_n64 = 0;
+ gint16 value_16 = 0;
+ gint32 value_32 = 0;
+ gint64 value_64 = 0;
+ gint16 value_n16 = 0;
+ gint32 value_n32 = 0;
+ gint64 value_n64 = 0;
+ gdouble value_double = 0;
+ gchar *string = NULL;
+ gchar *empty_string = NULL;
+ gpointer binary = NULL;
+ guint32 len = 0;
+ void *comparator = (void *) TEST_STRING;
+
+ ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+ "port", port, NULL);
+ transport = THRIFT_SERVER_TRANSPORT (tsocket);
+ thrift_server_transport_listen (transport, NULL);
+ client = thrift_server_transport_accept (transport, NULL);
+ g_assert (client != NULL);
+
+ tc = g_object_new (THRIFT_TYPE_COMPACT_PROTOCOL, "transport",
+ client, NULL);
+ protocol = THRIFT_PROTOCOL (tc);
+
+ g_assert (thrift_compact_protocol_read_bool (protocol,
+ &value_boolean, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_byte (protocol, &value_byte, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_i16 (protocol, &value_16, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_i32 (protocol, &value_32, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_i64 (protocol, &value_64, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_i16 (protocol, &value_n16, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_i32 (protocol, &value_n32, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_i64 (protocol, &value_n64, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_byte (protocol, &zigzag_p16, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_byte (protocol, &zigzag_p32, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_byte (protocol, &zigzag_p64, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_byte (protocol, &zigzag_n16, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_byte (protocol, &zigzag_n32, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_byte (protocol, &zigzag_n64, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_double (protocol,
+ &value_double, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_string (protocol, &string, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_string (protocol, &empty_string,
+ NULL) > 0);
+ g_assert (thrift_compact_protocol_read_binary (protocol, &binary,
+ &len, NULL) > 0);
+
+ g_assert (value_boolean == TEST_BOOL);
+ g_assert (value_byte == TEST_BYTE);
+ g_assert (value_16 == TEST_I16);
+ g_assert (value_32 == TEST_I32);
+ g_assert (value_64 == TEST_I64);
+ g_assert (value_n16 == TEST_NI16);
+ g_assert (value_n32 == TEST_NI32);
+ g_assert (value_n64 == TEST_NI64);
+ g_assert (zigzag_p16 == 4);
+ g_assert (zigzag_p32 == 4);
+ g_assert (zigzag_p64 == 4);
+ g_assert (zigzag_n16 == 3);
+ g_assert (zigzag_n32 == 3);
+ g_assert (zigzag_n64 == 3);
+ g_assert (value_double == TEST_DOUBLE);
+ g_assert (strcmp (TEST_STRING, string) == 0);
+ g_assert (strcmp ("", empty_string) == 0);
+ g_assert (memcmp (comparator, binary, len) == 0);
+
+ g_free (string);
+ g_free (empty_string);
+ g_free (binary);
+
+ g_assert (thrift_compact_protocol_read_binary (protocol, &binary,
+ &len, NULL) > 0);
+ g_assert (binary == NULL);
+ g_assert (len == 0);
+ g_free (binary);
+
+ transport_read_count = 0;
+ transport_read_error_at = 0;
+ g_assert (thrift_compact_protocol_read_binary (protocol, &binary,
+ &len, NULL) == -1);
+ transport_read_error_at = -1;
+
+ transport_read_count = 0;
+ transport_read_error_at = 1;
+ g_assert (thrift_compact_protocol_read_binary (protocol, &binary,
+ &len, NULL) == -1);
+ transport_read_error_at = -1;
+
+ transport_read_error = 1;
+ g_assert (thrift_compact_protocol_read_bool (protocol,
+ &value_boolean, NULL) == -1);
+ g_assert (thrift_compact_protocol_read_byte (protocol,
+ &value_byte, NULL) == -1);
+ g_assert (thrift_compact_protocol_read_i16 (protocol,
+ &value_16, NULL) == -1);
+ g_assert (thrift_compact_protocol_read_i32 (protocol, &value_32, NULL) == -1);
+ g_assert (thrift_compact_protocol_read_i64 (protocol, &value_64, NULL) == -1);
+ g_assert (thrift_compact_protocol_read_i16 (protocol,
+ &value_n16, NULL) == -1);
+ g_assert (thrift_compact_protocol_read_i32 (protocol, &value_n32, NULL) == -1);
+ g_assert (thrift_compact_protocol_read_i64 (protocol, &value_n64, NULL) == -1);
+ g_assert (thrift_compact_protocol_read_double (protocol,
+ &value_double, NULL) == -1);
+ transport_read_error = 0;
+
+ /* test partial write failure */
+ thrift_protocol_read_i32 (protocol, &value_32, NULL);
+
+ thrift_transport_read_end (client, NULL);
+ thrift_transport_close (client, NULL);
+
+ g_object_unref (tc);
+ g_object_unref (client);
+ g_object_unref (tsocket);
+}
+
+static void
+thrift_server_complex_types (const int port)
+{
+ ThriftServerTransport *transport = NULL;
+ ThriftTransport *client = NULL;
+ ThriftCompactProtocol *tc = NULL;
+ ThriftProtocol *protocol = NULL;
+ gchar *struct_name = NULL;
+ gchar *field_name = NULL;
+ gchar *message_name = NULL;
+ ThriftType element_type, key_type, value_type, field_type;
+ ThriftMessageType message_type;
+ gboolean value_boolean = ! TEST_BOOL;
+ gint8 value = 0;
+ gint16 field_id = 0;
+ guint32 size = 0;
+ gint32 seqid = 0;
+ gint8 version_and_type = 0;
+ gint8 protocol_id = 0;
+
+ ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+ "port", port, NULL);
+ transport = THRIFT_SERVER_TRANSPORT (tsocket);
+ thrift_server_transport_listen (transport, NULL);
+ client = thrift_server_transport_accept (transport, NULL);
+ g_assert (client != NULL);
+
+ tc = g_object_new (THRIFT_TYPE_COMPACT_PROTOCOL, "transport",
+ client, NULL);
+ protocol = THRIFT_PROTOCOL (tc);
+
+ /* test struct operations */
+
+ thrift_compact_protocol_read_struct_begin (protocol, &struct_name, NULL);
+ thrift_compact_protocol_read_struct_end (protocol, NULL);
+
+ /* test field state w.r.t. deltas */
+
+ field_id = 0;
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) == 1);
+ g_assert (field_id == 1);
+ field_id = 0;
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) == 1);
+ g_assert (field_id == 16);
+ field_id = 0;
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) == 1);
+ g_assert (field_id == 17);
+ field_id = 0;
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) > 1);
+ g_assert (field_id == 15);
+ field_id = 0;
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) == 1);
+ g_assert (field_id == 30);
+ field_id = 0;
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) > 1);
+ g_assert (field_id == 46);
+ field_id = 0;
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) == 1);
+ g_assert (field_id == 47);
+ field_id = 0;
+
+ /* test field operations */
+
+ thrift_compact_protocol_read_field_begin (protocol, &field_name, &field_type,
+ &field_id, NULL);
+ thrift_compact_protocol_read_field_end (protocol, NULL);
+
+ /* test field state w.r.t. structs */
+
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) > 1);
+ g_assert (field_id == 1);
+ field_id = 0;
+ thrift_compact_protocol_read_field_end (protocol, NULL);
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) == 1);
+ g_assert (field_id == 16);
+ field_id = 0;
+ thrift_compact_protocol_read_field_end (protocol, NULL);
+
+ g_assert (thrift_compact_protocol_read_struct_begin (protocol,
+ &struct_name, NULL) == 0);
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) > 1);
+ g_assert (field_id == 17);
+ field_id = 0;
+ thrift_compact_protocol_read_field_end (protocol, NULL);
+
+ g_assert (thrift_compact_protocol_read_struct_begin (protocol,
+ &struct_name, NULL) == 0);
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) > 1);
+ g_assert (field_id == 18);
+ field_id = 0;
+ thrift_compact_protocol_read_field_end (protocol, NULL);
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) == 1);
+ g_assert (field_id == 19);
+ field_id = 0;
+ thrift_compact_protocol_read_field_end (protocol, NULL);
+ g_assert (thrift_compact_protocol_read_struct_end (protocol, NULL) == 0);
+
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) == 1);
+ g_assert (field_id == 18);
+ field_id = 0;
+ thrift_compact_protocol_read_field_end (protocol, NULL);
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) == 1);
+ g_assert (field_id == 25);
+ field_id = 0;
+ thrift_compact_protocol_read_field_end (protocol, NULL);
+ g_assert (thrift_compact_protocol_read_struct_end (protocol, NULL) == 0);
+
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) == 1);
+ g_assert (field_id == 17);
+ field_id = 0;
+ thrift_compact_protocol_read_field_end (protocol, NULL);
+
+ /* test field state w.r.t. bools */
+
+ /* deltas */
+ /* non-bool field -> bool field -> non-bool field */
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) == 1);
+ thrift_compact_protocol_read_field_end (protocol, NULL);
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) == 1);
+ g_assert (field_type == T_BOOL);
+ g_assert (thrift_compact_protocol_read_bool (protocol,
+ &value_boolean, NULL) == 0);
+ g_assert (value_boolean == TEST_BOOL);
+ value_boolean = ! TEST_BOOL;
+ thrift_compact_protocol_read_field_end (protocol, NULL);
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) == 1);
+ thrift_compact_protocol_read_field_end (protocol, NULL);
+ /* bool -> bool field -> bool */
+ g_assert (thrift_compact_protocol_read_bool (protocol,
+ &value_boolean, NULL) > 0);
+ g_assert (value_boolean == TEST_BOOL);
+ value_boolean = ! TEST_BOOL;
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) == 1);
+ g_assert (field_type == T_BOOL);
+ g_assert (thrift_compact_protocol_read_bool (protocol,
+ &value_boolean, NULL) == 0);
+ g_assert (value_boolean == TEST_BOOL);
+ value_boolean = ! TEST_BOOL;
+ thrift_compact_protocol_read_field_end (protocol, NULL);
+ g_assert (thrift_compact_protocol_read_bool (protocol,
+ &value_boolean, NULL) > 0);
+ g_assert (value_boolean == TEST_BOOL);
+ value_boolean = ! TEST_BOOL;
+
+ /* no deltas */
+ /* non-bool field -> bool field -> non-bool field */
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) > 1);
+ thrift_compact_protocol_read_field_end (protocol, NULL);
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) > 1);
+ g_assert (field_type == T_BOOL);
+ g_assert (thrift_compact_protocol_read_bool (protocol,
+ &value_boolean, NULL) == 0);
+ g_assert (value_boolean == TEST_BOOL);
+ value_boolean = ! TEST_BOOL;
+ thrift_compact_protocol_read_field_end (protocol, NULL);
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) > 1);
+ thrift_compact_protocol_read_field_end (protocol, NULL);
+ /* bool -> bool field -> bool */
+ g_assert (thrift_compact_protocol_read_bool (protocol,
+ &value_boolean, NULL) > 0);
+ g_assert (value_boolean == TEST_BOOL);
+ value_boolean = ! TEST_BOOL;
+ g_assert (thrift_compact_protocol_read_field_begin (protocol, &field_name,
+ &field_type,
+ &field_id, NULL) > 1);
+ g_assert (field_type == T_BOOL);
+ g_assert (thrift_compact_protocol_read_bool (protocol,
+ &value_boolean, NULL) == 0);
+ g_assert (value_boolean == TEST_BOOL);
+ value_boolean = ! TEST_BOOL;
+ thrift_compact_protocol_read_field_end (protocol, NULL);
+ g_assert (thrift_compact_protocol_read_bool (protocol,
+ &value_boolean, NULL) > 0);
+ g_assert (value_boolean == TEST_BOOL);
+ value_boolean = ! TEST_BOOL;
+
+ /* test first read error on a field */
+ transport_read_error = 1;
+ g_assert (thrift_compact_protocol_read_field_begin (protocol,
+ &field_name, &field_type,
+ &field_id, NULL) == -1);
+ transport_read_error = 0;
+
+ /* test 2nd write failure */
+ thrift_compact_protocol_read_byte (protocol, &value, NULL);
+
+ /* test 2nd read failure on a field */
+ transport_read_count = 0;
+ transport_read_error_at = 1;
+ g_assert (thrift_compact_protocol_read_field_begin (protocol,
+ &field_name, &field_type,
+ &field_id, NULL) == -1);
+ transport_read_error_at = -1;
+
+ /* test field stop */
+ thrift_compact_protocol_read_field_begin (protocol, &field_name, &field_type,
+ &field_id, NULL);
+
+ /* test map operations */
+
+ thrift_compact_protocol_read_map_begin (protocol, &key_type, &value_type,
+ &size, NULL);
+ thrift_compact_protocol_read_map_end (protocol, NULL);
+
+ /* test 1st read failure on a map */
+ transport_read_count = 0;
+ transport_read_error_at = 0;
+ g_assert (thrift_compact_protocol_read_map_begin (protocol,
+ &key_type, &value_type,
+ &size, NULL) == -1);
+ transport_read_error_at = -1;
+
+ /* test 2nd read failure on a map */
+ transport_read_count = 0;
+ transport_read_error_at = 1;
+ g_assert (thrift_compact_protocol_read_map_begin (protocol,
+ &key_type, &value_type,
+ &size, NULL) == -1);
+ transport_read_error_at = -1;
+
+ /* test 1st write failure on map---nothing to do on our side */
+
+ /* test 2nd write failure */
+ thrift_compact_protocol_read_byte (protocol, &value, NULL);
+
+ /* test negative map size */
+ g_assert (thrift_compact_protocol_read_map_begin (protocol,
+ &key_type, &value_type,
+ &size, NULL) == -1);
+
+ /* test list operations */
+ thrift_compact_protocol_read_list_begin (protocol, &element_type, &size,
+ NULL);
+ thrift_compact_protocol_read_list_end (protocol, NULL);
+
+ /* test small list 1st read failure */
+ transport_read_error = 1;
+ g_assert (thrift_compact_protocol_read_list_begin (protocol, &element_type,
+ &size, NULL) == -1);
+ transport_read_error = 0;
+
+ /* test big list 1st read failure */
+ transport_read_error = 1;
+ g_assert (thrift_compact_protocol_read_list_begin (protocol, &element_type,
+ &size, NULL) == -1);
+ transport_read_error = 0;
+
+ /* test big list 2nd read failure */
+ transport_read_count = 0;
+ transport_read_error_at = 1;
+ thrift_compact_protocol_read_list_begin (protocol, &element_type, &size,
+ NULL);
+ transport_read_error_at = -1;
+
+ /* test negative list size failure */
+ thrift_compact_protocol_read_list_begin (protocol, &element_type, &size,
+ NULL);
+
+ /* test small list 1st write failure---nothing to do on our end */
+
+ /* test big list 1st write failure---nothing to do on our end */
+
+ /* test big list 2nd write failure */
+ thrift_compact_protocol_read_byte (protocol, &value, NULL);
+
+ /* test set operations */
+ thrift_compact_protocol_read_set_begin (protocol, &element_type, &size, NULL);
+ thrift_compact_protocol_read_set_end (protocol, NULL);
+
+ /* broken read */
+ transport_read_error = 1;
+ g_assert (thrift_compact_protocol_read_message_begin (protocol, &message_name,
+ &message_type, &seqid,
+ NULL) == -1);
+ transport_read_error = 0;
+
+ /* invalid protocol */
+ g_assert (thrift_compact_protocol_read_message_begin (protocol, &message_name,
+ &message_type, &seqid,
+ NULL) == -1);
+
+ /* invalid version */
+ g_assert (thrift_compact_protocol_read_message_begin (protocol, &message_name,
+ &message_type, &seqid,
+ NULL) == -1);
+
+ /* read a valid message */
+ g_assert (thrift_compact_protocol_read_message_begin (protocol, &message_name,
+ &message_type, &seqid,
+ NULL) > 0);
+ g_free (message_name);
+
+ /* broken 2nd read on a message */
+ transport_read_count = 0;
+ transport_read_error_at = 1;
+ g_assert (thrift_compact_protocol_read_message_begin (protocol, &message_name,
+ &message_type, &seqid,
+ NULL) == -1);
+ transport_read_error_at = -1;
+
+ /* broken 3rd read on a message */
+ transport_read_count = 0;
+ transport_read_error_at = 2;
+ g_assert (thrift_compact_protocol_read_message_begin (protocol, &message_name,
+ &message_type, &seqid,
+ NULL) == -1);
+ transport_read_error_at = -1;
+
+ /* broken 4th read on a message */
+ transport_read_count = 0;
+ transport_read_error_at = 3;
+ g_assert (thrift_compact_protocol_read_message_begin (protocol, &message_name,
+ &message_type, &seqid,
+ NULL) == -1);
+ transport_read_error_at = -1;
+
+ /* read a valid message */
+ g_assert (thrift_compact_protocol_read_message_begin (protocol, &message_name,
+ &message_type, &seqid,
+ NULL) > 0);
+ g_free (message_name);
+
+ g_assert (thrift_compact_protocol_read_message_end (protocol, NULL) == 0);
+
+ /* handle 2nd write failure on a message */
+ thrift_compact_protocol_read_byte (protocol, &protocol_id, NULL);
+
+ /* handle 3rd write failure on a message */
+ thrift_compact_protocol_read_byte (protocol, &protocol_id, NULL);
+ thrift_compact_protocol_read_byte (protocol, &version_and_type, NULL);
+
+ /* handle 4th write failure on a message */
+ thrift_compact_protocol_read_byte (protocol, &protocol_id, NULL);
+ thrift_compact_protocol_read_byte (protocol, &version_and_type, NULL);
+ thrift_compact_protocol_read_varint32 (tc, &seqid, NULL);
+
+ g_object_unref (client);
+ g_object_unref (tsocket);
+}
+
+static void
+thrift_server_many_frames (const int port)
+{
+ ThriftServerTransport *transport = NULL;
+ ThriftTransport *client = NULL;
+ ThriftCompactProtocol *tcp = NULL;
+ ThriftProtocol *protocol = NULL;
+ ThriftServerSocket *tsocket = NULL;
+ gboolean value_boolean = FALSE;
+ gint8 value_byte = 0,
+ zigzag_p16 = 0, zigzag_p32 = 0, zigzag_p64 = 0,
+ zigzag_n16 = 0, zigzag_n32 = 0, zigzag_n64 = 0;
+ gint16 value_16 = 0;
+ gint32 value_32 = 0;
+ gint64 value_64 = 0;
+ gint16 value_n16 = 0;
+ gint32 value_n32 = 0;
+ gint64 value_n64 = 0;
+ gdouble value_double = 0;
+ gchar *string = NULL;
+ gchar *empty_string = NULL;
+ gpointer binary = NULL;
+ guint32 len = 0;
+ void *comparator = (void *) TEST_STRING;
+
+ tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET, "port", port, NULL);
+ transport = THRIFT_SERVER_TRANSPORT (tsocket);
+ thrift_server_transport_listen (transport, NULL);
+
+ /* wrap the client in a framed transport */
+ client = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT, "transport",
+ thrift_server_transport_accept (transport, NULL),
+ "r_buf_size", 1, NULL);
+ g_assert (client != NULL);
+
+ tcp = g_object_new (THRIFT_TYPE_COMPACT_PROTOCOL, "transport",
+ client, NULL);
+ protocol = THRIFT_PROTOCOL (tcp);
+
+ g_assert (thrift_compact_protocol_read_bool (protocol,
+ &value_boolean, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_byte (protocol, &value_byte, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_i16 (protocol, &value_16, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_i32 (protocol, &value_32, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_i64 (protocol, &value_64, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_i16 (protocol, &value_n16, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_i32 (protocol, &value_n32, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_i64 (protocol, &value_n64, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_byte (protocol, &zigzag_p16, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_byte (protocol, &zigzag_p32, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_byte (protocol, &zigzag_p64, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_byte (protocol, &zigzag_n16, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_byte (protocol, &zigzag_n32, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_byte (protocol, &zigzag_n64, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_double (protocol,
+ &value_double, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_string (protocol, &string, NULL) > 0);
+ g_assert (thrift_compact_protocol_read_string (protocol, &empty_string,
+ NULL) > 0);
+ g_assert (thrift_compact_protocol_read_binary (protocol, &binary,
+ &len, NULL) > 0);
+
+ g_assert (value_boolean == TEST_BOOL);
+ g_assert (value_byte == TEST_BYTE);
+ g_assert (value_16 == TEST_I16);
+ g_assert (value_32 == TEST_I32);
+ g_assert (value_64 == TEST_I64);
+ g_assert (value_n16 == TEST_NI16);
+ g_assert (value_n32 == TEST_NI32);
+ g_assert (value_n64 == TEST_NI64);
+ g_assert (zigzag_p16 == 4);
+ g_assert (zigzag_p32 == 4);
+ g_assert (zigzag_p64 == 4);
+ g_assert (zigzag_n16 == 3);
+ g_assert (zigzag_n32 == 3);
+ g_assert (zigzag_n64 == 3);
+ g_assert (value_double == TEST_DOUBLE);
+ g_assert (strcmp (TEST_STRING, string) == 0);
+ g_assert (strcmp ("", empty_string) == 0);
+ g_assert (memcmp (comparator, binary, len) == 0);
+
+ g_free (string);
+ g_free (empty_string);
+ g_free (binary);
+
+ g_assert (thrift_compact_protocol_read_binary (protocol, &binary,
+ &len, NULL) > 0);
+ g_assert (binary == NULL);
+ g_assert (len == 0);
+ g_free (binary);
+
+ thrift_transport_read_end (client, NULL);
+ thrift_transport_close (client, NULL);
+
+ g_object_unref (tcp);
+ g_object_unref (client);
+ g_object_unref (tsocket);
+}
+
+int
+main (int argc, char *argv[])
+{
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init ();
+#endif
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/testcompactprotocol/CreateAndDestroy",
+ test_create_and_destroy);
+ g_test_add_func ("/testcompactprotocol/Initialize", test_initialize);
+ g_test_add_func ("/testcompactprotocol/ReadAndWritePrimitives",
+ test_read_and_write_primitives);
+ g_test_add_func ("/testcompactprotocol/ReadAndWriteComplexTypes",
+ test_read_and_write_complex_types);
+ g_test_add_func ("/testcompactprotocol/ReadAndWriteManyFrames",
+ test_read_and_write_many_frames);
+
+ return g_test_run ();
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/testcontainertest.c b/src/jaegertracing/thrift/lib/c_glib/test/testcontainertest.c
new file mode 100644
index 000000000..5fc51d516
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/testcontainertest.c
@@ -0,0 +1,529 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "gen-c_glib/t_test_container_test_types.h"
+#include "gen-c_glib/t_test_container_service.h"
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/protocol/thrift_binary_protocol_factory.h>
+#include <thrift/c_glib/protocol/thrift_binary_protocol.h>
+#include <thrift/c_glib/protocol/thrift_protocol_factory.h>
+#include <thrift/c_glib/server/thrift_server.h>
+#include <thrift/c_glib/server/thrift_simple_server.h>
+#include <thrift/c_glib/transport/thrift_buffered_transport_factory.h>
+#include <thrift/c_glib/transport/thrift_buffered_transport.h>
+#include <thrift/c_glib/transport/thrift_server_socket.h>
+#include <thrift/c_glib/transport/thrift_server_transport.h>
+#include <thrift/c_glib/transport/thrift_socket.h>
+
+#include <glib-object.h>
+#include <glib.h>
+
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+
+#define TEST_SERVER_HOSTNAME "localhost"
+#define TEST_SERVER_PORT 9090
+
+/* --------------------------------------------------------------------------
+ The ContainerService handler we'll use for testing */
+
+G_BEGIN_DECLS
+
+GType test_container_service_handler_get_type (void);
+
+#define TYPE_TEST_CONTAINER_SERVICE_HANDLER \
+ (test_container_service_handler_get_type ())
+
+#define TEST_CONTAINER_SERVICE_HANDLER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ TYPE_TEST_CONTAINER_SERVICE_HANDLER, \
+ TestContainerServiceHandler))
+#define TEST_CONTAINER_SERVICE_HANDLER_CLASS(c) \
+ (G_TYPE_CHECK_CLASS_CAST ((c), \
+ TYPE_TEST_CONTAINER_SERVICE_HANDLER, \
+ TestContainerServiceHandlerClass))
+#define IS_TEST_CONTAINER_SERVICE_HANDLER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ TYPE_TEST_CONTAINER_SERVICE_HANDLER))
+#define IS_TEST_CONTAINER_SERVICE_HANDLER_CLASS(c) \
+ (G_TYPE_CHECK_CLASS_TYPE ((c), \
+ TYPE_TEST_CONTAINER_SERVICE_HANDLER))
+#define TEST_CONTAINER_SERVICE_HANDLER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ TYPE_TEST_CONTAINER_SERVICE_HANDLER, \
+ TestContainerServiceHandlerClass))
+
+struct _TestContainerServiceHandler {
+ TTestContainerServiceHandler parent_instance;
+
+ /* private */
+ GPtrArray *string_list;
+};
+typedef struct _TestContainerServiceHandler TestContainerServiceHandler;
+
+struct _TestContainerServiceHandlerClass {
+ TTestContainerServiceHandlerClass parent_class;
+};
+typedef struct _TestContainerServiceHandlerClass
+ TestContainerServiceHandlerClass;
+
+G_END_DECLS
+
+/* -------------------------------------------------------------------------- */
+
+G_DEFINE_TYPE (TestContainerServiceHandler,
+ test_container_service_handler,
+ T_TEST_TYPE_CONTAINER_SERVICE_HANDLER)
+
+/* A helper function used to append copies of strings to a string list */
+static void append_string_to_ptr_array (gpointer element, gpointer ptr_array)
+{
+ g_ptr_array_add ((GPtrArray *)ptr_array, g_strdup ((gchar *)element));
+}
+
+/* Accept a string list from the client and append its contents to our internal
+ list */
+static gboolean
+test_container_service_handler_receive_string_list (TTestContainerServiceIf *iface,
+ const GPtrArray *stringList,
+ GError **error)
+{
+ TestContainerServiceHandler *self = TEST_CONTAINER_SERVICE_HANDLER (iface);
+
+ /* Append the client's strings to our own internal string list */
+ g_ptr_array_foreach ((GPtrArray *)stringList,
+ append_string_to_ptr_array,
+ self->string_list);
+
+ g_clear_error (error);
+ return TRUE;
+}
+
+/* Return the contents of our internal string list to the client */
+static gboolean
+test_container_service_handler_return_string_list (TTestContainerServiceIf *iface,
+ GPtrArray **_return,
+ GError **error)
+{
+ TestContainerServiceHandler *self = TEST_CONTAINER_SERVICE_HANDLER (iface);
+
+ /* Return (copies of) the strings contained in our list */
+ g_ptr_array_foreach (self->string_list,
+ append_string_to_ptr_array,
+ *_return);
+
+ g_clear_error (error);
+ return TRUE;
+}
+
+static gboolean
+test_container_service_handler_return_list_string_list (TTestContainerServiceIf *iface,
+ GPtrArray **_return,
+ GError **error)
+{
+ TestContainerServiceHandler *self = TEST_CONTAINER_SERVICE_HANDLER (iface);
+ GPtrArray *nested_list;
+
+ /* Return a list containing our list of strings */
+ nested_list
+ = g_ptr_array_new_with_free_func ((GDestroyNotify)g_ptr_array_unref);
+ g_ptr_array_add (nested_list, self->string_list);
+ g_ptr_array_ref (self->string_list);
+
+ g_ptr_array_add (*_return, nested_list);
+
+ g_clear_error (error);
+ return TRUE;
+}
+
+static gboolean
+test_container_service_handler_return_typedefd_list_string_list (TTestContainerServiceIf *iface,
+ TTestListStringList **_return,
+ GError **error)
+{
+ TestContainerServiceHandler *self = TEST_CONTAINER_SERVICE_HANDLER (iface);
+ TTestStringList *nested_list;
+
+ /* Return a list containing our list of strings */
+ nested_list
+ = g_ptr_array_new_with_free_func ((GDestroyNotify)g_ptr_array_unref);
+ g_ptr_array_add (nested_list, self->string_list);
+ g_ptr_array_ref (self->string_list);
+
+ g_ptr_array_add (*_return, nested_list);
+
+ g_clear_error (error);
+ return TRUE;
+}
+
+static void
+test_container_service_handler_finalize (GObject *object) {
+ TestContainerServiceHandler *self = TEST_CONTAINER_SERVICE_HANDLER (object);
+
+ /* Destroy our internal containers */
+ g_ptr_array_unref (self->string_list);
+ self->string_list = NULL;
+
+ G_OBJECT_CLASS (test_container_service_handler_parent_class)->
+ finalize (object);
+}
+
+static void
+test_container_service_handler_init (TestContainerServiceHandler *self)
+{
+ /* Create our internal containers */
+ self->string_list = g_ptr_array_new_with_free_func (g_free);
+}
+
+static void
+test_container_service_handler_class_init (TestContainerServiceHandlerClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ TTestContainerServiceHandlerClass *parent_class =
+ T_TEST_CONTAINER_SERVICE_HANDLER_CLASS (klass);
+
+ gobject_class->finalize = test_container_service_handler_finalize;
+
+ parent_class->receive_string_list =
+ test_container_service_handler_receive_string_list;
+ parent_class->return_string_list =
+ test_container_service_handler_return_string_list;
+ parent_class->return_list_string_list =
+ test_container_service_handler_return_list_string_list;
+ parent_class->return_typedefd_list_string_list =
+ test_container_service_handler_return_typedefd_list_string_list;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Our test server, declared globally so we can access it within a signal
+ handler */
+ThriftServer *server = NULL;
+
+/* A signal handler used to detect when the child process (the test suite) has
+ exited so we know to shut down the server and terminate ourselves */
+static void
+sigchld_handler (int signal_number)
+{
+ THRIFT_UNUSED_VAR (signal_number);
+
+ /* The child process (the tests) has exited or been terminated; shut down the
+ server gracefully */
+ if (server != NULL)
+ thrift_server_stop (server);
+}
+
+/* A helper function that executes a test case against a newly constructed
+ service client */
+static void
+execute_with_service_client (void (*test_case)(TTestContainerServiceIf *,
+ GError **))
+{
+ ThriftSocket *socket;
+ ThriftTransport *transport;
+ ThriftProtocol *protocol;
+
+ TTestContainerServiceIf *client;
+
+ GError *error = NULL;
+
+ /* Create a client with which to access the server */
+ socket = g_object_new (THRIFT_TYPE_SOCKET,
+ "hostname", TEST_SERVER_HOSTNAME,
+ "port", TEST_SERVER_PORT,
+ NULL);
+ transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT,
+ "transport", socket,
+ NULL);
+ protocol = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL,
+ "transport", transport,
+ NULL);
+
+ thrift_transport_open (transport, &error);
+ g_assert_no_error (error);
+
+ client = g_object_new (T_TEST_TYPE_CONTAINER_SERVICE_CLIENT,
+ "input_protocol", protocol,
+ "output_protocol", protocol,
+ NULL);
+
+ /* Execute the test against this client */
+ (*test_case)(client, &error);
+ g_assert_no_error (error);
+
+ /* Clean up and exit */
+ thrift_transport_close (transport, NULL);
+
+ g_object_unref (client);
+ g_object_unref (protocol);
+ g_object_unref (transport);
+ g_object_unref (socket);
+}
+
+static void
+test_containers_with_default_values (void)
+{
+ TTestContainersWithDefaultValues *default_values;
+ GPtrArray *string_list;
+
+ /* Fetch a new ContainersWithDefaultValues struct and its StringList member */
+ default_values = g_object_new (T_TEST_TYPE_CONTAINERS_WITH_DEFAULT_VALUES,
+ NULL);
+ g_object_get (default_values,
+ "StringList", &string_list,
+ NULL);
+
+ /* Make sure the list has been populated with its default values */
+ g_assert_cmpint (string_list->len, ==, 2);
+ g_assert_cmpstr (((gchar **)string_list->pdata)[0], ==, "Apache");
+ g_assert_cmpstr (((gchar **)string_list->pdata)[1], ==, "Thrift");
+
+ g_ptr_array_unref (string_list);
+ g_object_unref (default_values);
+}
+
+static void
+test_container_service_string_list_inner (TTestContainerServiceIf *client,
+ GError **error)
+{
+ gchar *test_data[] = { "one", "two", "three" };
+
+ GPtrArray *outgoing_string_list;
+ GPtrArray *incoming_string_list;
+ guint index;
+
+ g_clear_error (error);
+
+ /* Prepare our test data (our string list to send) */
+ outgoing_string_list = g_ptr_array_new ();
+ for (index = 0; index < 3; index += 1)
+ g_ptr_array_add (outgoing_string_list, &test_data[index]);
+
+ /* Send our data to the server and make sure we get the same data back on
+ retrieve */
+ g_assert
+ (t_test_container_service_client_receive_string_list (client,
+ outgoing_string_list,
+ error) &&
+ *error == NULL);
+
+ incoming_string_list = g_ptr_array_new ();
+ g_assert
+ (t_test_container_service_client_return_string_list (client,
+ &incoming_string_list,
+ error) &&
+ *error == NULL);
+
+ /* Make sure the two lists are equivalent */
+ g_assert_cmpint (incoming_string_list->len, ==, outgoing_string_list->len);
+ for (index = 0; index < incoming_string_list->len; index += 1)
+ g_assert_cmpstr (((gchar **)incoming_string_list->pdata)[index],
+ ==,
+ ((gchar **)outgoing_string_list->pdata)[index]);
+
+ /* Clean up and exit */
+ g_ptr_array_unref (incoming_string_list);
+ g_ptr_array_unref (outgoing_string_list);
+}
+
+static void
+test_container_service_string_list (void)
+{
+ execute_with_service_client (test_container_service_string_list_inner);
+}
+
+static void
+test_container_service_list_string_list_inner (TTestContainerServiceIf *client,
+ GError **error)
+{
+ GPtrArray *incoming_list;
+ GPtrArray *nested_list;
+
+ g_clear_error (error);
+
+ /* Receive a list of string lists from the server */
+ incoming_list =
+ g_ptr_array_new_with_free_func ((GDestroyNotify)g_ptr_array_unref);
+ g_assert
+ (t_test_container_service_client_return_list_string_list (client,
+ &incoming_list,
+ error) &&
+ *error == NULL);
+
+ /* Make sure the list and its contents are valid */
+ g_assert_cmpint (incoming_list->len, >, 0);
+
+ nested_list = (GPtrArray *)g_ptr_array_index (incoming_list, 0);
+ g_assert (nested_list != NULL);
+ g_assert_cmpint (nested_list->len, >=, 0);
+
+ /* Clean up and exit */
+ g_ptr_array_unref (incoming_list);
+}
+
+static void
+test_container_service_list_string_list (void)
+{
+ execute_with_service_client (test_container_service_list_string_list_inner);
+}
+
+static void
+test_container_service_typedefd_list_string_list_inner (TTestContainerServiceIf *client,
+ GError **error)
+{
+ TTestListStringList *incoming_list;
+ TTestStringList *nested_list;
+
+ g_clear_error (error);
+
+ /* Receive a list of string lists from the server */
+ incoming_list =
+ g_ptr_array_new_with_free_func ((GDestroyNotify)g_ptr_array_unref);
+ g_assert
+ (t_test_container_service_client_return_list_string_list (client,
+ &incoming_list,
+ error) &&
+ *error == NULL);
+
+ /* Make sure the list and its contents are valid */
+ g_assert_cmpint (incoming_list->len, >, 0);
+
+ nested_list = (TTestStringList *)g_ptr_array_index (incoming_list, 0);
+ g_assert (nested_list != NULL);
+ g_assert_cmpint (nested_list->len, >=, 0);
+
+ /* Clean up and exit */
+ g_ptr_array_unref (incoming_list);
+}
+
+static void
+test_container_service_typedefd_list_string_list (void)
+{
+ execute_with_service_client
+ (test_container_service_typedefd_list_string_list_inner);
+}
+
+int
+main(int argc, char *argv[])
+{
+ pid_t pid;
+ int status;
+
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init ();
+#endif
+
+ /* Fork to run our test suite in a child process */
+ pid = fork ();
+ g_assert_cmpint (pid, >=, 0);
+
+ if (pid == 0) { /* The child process */
+ /* Wait a moment for the server to finish starting */
+ sleep (1);
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func
+ ("/testcontainertest/ContainerTest/Structs/ContainersWithDefaultValues",
+ test_containers_with_default_values);
+ g_test_add_func
+ ("/testcontainertest/ContainerTest/Services/ContainerService/StringList",
+ test_container_service_string_list);
+ g_test_add_func
+ ("/testcontainertest/ContainerTest/Services/ContainerService/ListStringList",
+ test_container_service_list_string_list);
+ g_test_add_func
+ ("/testcontainertest/ContainerTest/Services/ContainerService/TypedefdListStringList",
+ test_container_service_typedefd_list_string_list);
+
+ /* Run the tests and make the result available to our parent process */
+ _exit (g_test_run ());
+ }
+ else {
+ TTestContainerServiceHandler *handler;
+ TTestContainerServiceProcessor *processor;
+
+ ThriftServerTransport *server_transport;
+ ThriftTransportFactory *transport_factory;
+ ThriftProtocolFactory *protocol_factory;
+
+ struct sigaction sigchld_action;
+
+ GError *error = NULL;
+ int exit_status = 1;
+
+ /* Trap the event of the child process terminating so we know to stop the
+ server and exit */
+ memset (&sigchld_action, 0, sizeof (sigchld_action));
+ sigchld_action.sa_handler = sigchld_handler;
+ sigchld_action.sa_flags = SA_RESETHAND;
+ sigaction (SIGCHLD, &sigchld_action, NULL);
+
+ /* Create our test server */
+ handler = g_object_new (TYPE_TEST_CONTAINER_SERVICE_HANDLER,
+ NULL);
+ processor = g_object_new (T_TEST_TYPE_CONTAINER_SERVICE_PROCESSOR,
+ "handler", handler,
+ NULL);
+ server_transport = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+ "port", TEST_SERVER_PORT,
+ NULL);
+ transport_factory = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY,
+ NULL);
+ protocol_factory = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL_FACTORY,
+ NULL);
+
+ server = g_object_new (THRIFT_TYPE_SIMPLE_SERVER,
+ "processor", processor,
+ "server_transport", server_transport,
+ "input_transport_factory", transport_factory,
+ "output_transport_factory", transport_factory,
+ "input_protocol_factory", protocol_factory,
+ "output_protocol_factory", protocol_factory,
+ NULL);
+
+ /* Start the server */
+ thrift_server_serve (server, &error);
+
+ /* Make sure the server stopped only because it was interrupted (by the
+ child process terminating) */
+ g_assert(!error || g_error_matches(error,
+ THRIFT_SERVER_SOCKET_ERROR,
+ THRIFT_SERVER_SOCKET_ERROR_ACCEPT));
+
+ /* Free our resources */
+ g_clear_object (&server);
+ g_clear_object (&protocol_factory);
+ g_clear_object (&transport_factory);
+ g_clear_object (&server_transport);
+ g_clear_object (&processor);
+ g_clear_object (&handler);
+
+ /* Wait for the child process to complete and return its exit status */
+ g_assert (wait (&status) == pid);
+ if (WIFEXITED (status))
+ exit_status = WEXITSTATUS (status);
+
+ return exit_status;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/testdebugproto.c b/src/jaegertracing/thrift/lib/c_glib/test/testdebugproto.c
new file mode 100644
index 000000000..109a48b50
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/testdebugproto.c
@@ -0,0 +1,941 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <math.h>
+#include <string.h>
+#include <glib-object.h>
+
+#ifndef M_PI
+#define M_PI 3.1415926535897932385
+#endif
+
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/protocol/thrift_binary_protocol.h>
+
+#include "gen-c_glib/t_test_debug_proto_test_types.h"
+#include "gen-c_glib/t_test_srv.h"
+#include "gen-c_glib/t_test_inherited.h"
+
+static void
+test_structs_doubles_create_and_destroy (void)
+{
+ GObject *object = NULL;
+
+ /* A Doubles structure can be created... */
+ object = g_object_new (T_TEST_TYPE_DOUBLES, NULL);
+
+ g_assert (object != NULL);
+ g_assert (T_TEST_IS_DOUBLES (object));
+
+ /* ...and destroyed */
+ g_object_unref (object);
+}
+
+static void
+test_structs_doubles_initialize (void)
+{
+ TTestDoubles *doubles = NULL;
+ gdouble nan;
+ gdouble inf;
+ gdouble neginf;
+ gdouble repeating;
+ gdouble big;
+ gdouble tiny;
+ gdouble zero;
+ gdouble negzero;
+
+ /* Note there seems to be no way to get not-a-number ("NAN") values past
+ GObject's range-checking, so that portion of the test has been commented
+ out below. */
+
+ /* A Doubles structure's members are available as GObject properties
+ that can be initialized at construction... */
+ doubles = g_object_new (T_TEST_TYPE_DOUBLES,
+ /* "nan", 0 * INFINITY, */
+ "inf", INFINITY,
+ "neginf", -INFINITY,
+ "repeating", 1.0 / 3,
+ "big", G_MAXDOUBLE,
+ "tiny", 10E-101,
+ "zero", 1.0 * 0,
+ "negzero", -1.0 * 0,
+ NULL);
+
+ g_assert (doubles != NULL);
+
+ /* ...and later retrieved */
+ g_object_get (doubles,
+ "nan", &nan,
+ "inf", &inf,
+ "neginf", &neginf,
+ "repeating", &repeating,
+ "big", &big,
+ "tiny", &tiny,
+ "zero", &zero,
+ "negzero", &negzero,
+ NULL);
+
+ /* g_assert_cmpint (isnan (nan), !=, 0); */
+ g_assert_cmpint (isinf (inf), ==, 1);
+ g_assert_cmpint (isinf (neginf), ==, -1);
+
+ g_assert_cmpfloat (repeating, ==, 1.0 / 3);
+ g_assert_cmpfloat (big, ==, G_MAXDOUBLE);
+ g_assert_cmpfloat (tiny, ==, 10E-101);
+ g_assert_cmpfloat (zero, ==, 1.0 * 0);
+ g_assert_cmpfloat (negzero, ==, -1.0 * 0);
+
+ g_object_unref (doubles);
+}
+
+static void
+test_structs_one_of_each_create_and_destroy (void)
+{
+ GObject *object = NULL;
+
+ /* A OneOfEach structure can be created... */
+ object = g_object_new (T_TEST_TYPE_ONE_OF_EACH, NULL);
+
+ g_assert (object != NULL);
+ g_assert (T_TEST_IS_ONE_OF_EACH (object));
+
+ /* ...and destroyed */
+ g_object_unref (object);
+}
+
+static void
+test_structs_one_of_each_initialize_default_values (void)
+{
+ TTestOneOfEach *one_of_each = NULL;
+ gint a_bite;
+ gint integer16;
+ gint64 integer64;
+ GArray *byte_list;
+ GArray *i16_list;
+ GArray *i64_list;
+
+ /* A OneOfEach structure created with no explicit property values
+ will hold the default values specified in the .thrift file */
+ one_of_each = g_object_new (T_TEST_TYPE_ONE_OF_EACH, NULL);
+
+ g_object_get (one_of_each,
+ "a_bite", &a_bite,
+ "integer16", &integer16,
+ "integer64", &integer64,
+ "byte_list", &byte_list,
+ "i16_list", &i16_list,
+ "i64_list", &i64_list,
+ NULL);
+
+ g_assert_cmpint (a_bite, ==, 0x7f);
+ g_assert_cmpint (integer16, ==, 0x7fff);
+ g_assert_cmpint (integer64, ==, G_GINT64_CONSTANT (10000000000));
+
+ g_assert (byte_list != NULL);
+ g_assert_cmpint (byte_list->len, ==, 3);
+ g_assert_cmpint (g_array_index (byte_list, gint8, 0), ==, 1);
+ g_assert_cmpint (g_array_index (byte_list, gint8, 1), ==, 2);
+ g_assert_cmpint (g_array_index (byte_list, gint8, 2), ==, 3);
+
+ g_assert (i16_list != NULL);
+ g_assert_cmpint (i16_list->len, ==, 3);
+ g_assert_cmpint (g_array_index (i16_list, gint16, 0), ==, 1);
+ g_assert_cmpint (g_array_index (i16_list, gint16, 1), ==, 2);
+ g_assert_cmpint (g_array_index (i16_list, gint16, 2), ==, 3);
+
+ g_assert (i64_list != NULL);
+ g_assert_cmpint (i64_list->len, ==, 3);
+ g_assert_cmpint (g_array_index (i64_list, gint64, 0), ==, 1);
+ g_assert_cmpint (g_array_index (i64_list, gint64, 1), ==, 2);
+ g_assert_cmpint (g_array_index (i64_list, gint64, 2), ==, 3);
+
+ g_array_unref (i64_list);
+ g_array_unref (i16_list);
+ g_array_unref (byte_list);
+ g_object_unref (one_of_each);
+}
+
+static void
+test_structs_one_of_each_initialize_specified_values (void)
+{
+ static const gint8 initial_byte_list[5] = { 13, 21, 34, 55, 89 };
+ static const gint16 initial_i16_list[5] = { 4181, 6765, 10946, 17711, 28657 };
+ static const gint64 initial_i64_list[5] =
+ {
+ G_GINT64_CONSTANT (1100087778366101931),
+ G_GINT64_CONSTANT (1779979416004714189),
+ G_GINT64_CONSTANT (2880067194370816120),
+ G_GINT64_CONSTANT (4660046610375530309),
+ G_GINT64_CONSTANT (7540113804746346429)
+ };
+ static const guint8 initial_base64[8] =
+ {
+ 0x56, 0x47, 0x68, 0x79, 0x61, 0x57, 0x5a, 0x30
+ };
+
+ TTestOneOfEach *one_of_each;
+ gboolean im_true;
+ gboolean im_false;
+ gint a_bite;
+ gint integer16;
+ gint integer32;
+ gint64 integer64;
+ double double_precision;
+ gchar *some_characters;
+ gchar *zomg_unicode;
+ gboolean what_who;
+ GByteArray *base64;
+ GArray *byte_list;
+ GArray *i16_list;
+ GArray *i64_list;
+
+ base64 = g_byte_array_new ();
+ g_byte_array_append (base64, initial_base64, 8);
+
+ byte_list = g_array_new (FALSE, FALSE, sizeof (gint8));
+ g_array_append_vals (byte_list, initial_byte_list, 5);
+
+ i16_list = g_array_new (FALSE, FALSE, sizeof (gint16));
+ g_array_append_vals (i16_list, initial_i16_list, 5);
+
+ i64_list = g_array_new (FALSE, FALSE, sizeof (gint64));
+ g_array_append_vals (i64_list, initial_i64_list, 5);
+
+ /* All of OneOfEach's properties can be set at construction... */
+ one_of_each =
+ g_object_new (T_TEST_TYPE_ONE_OF_EACH,
+ "im_true", TRUE,
+ "im_false", FALSE,
+ "a_bite", 0x50,
+ "integer16", 0x7e57,
+ "integer32", 0xdeadbeef,
+ "integer64", G_GINT64_CONSTANT (0xfa15efacade15bad),
+ "double_precision", M_PI,
+ "some_characters", "Debug THIS!",
+ "zomg_unicode", "\xd7\n\a\t",
+ "what_who", TRUE,
+ "base64", base64,
+ "byte_list", byte_list,
+ "i16_list", i16_list,
+ "i64_list", i64_list,
+ NULL);
+ g_assert (one_of_each != NULL);
+
+ g_array_unref (i64_list);
+ i64_list = NULL;
+ g_array_unref (i16_list);
+ i16_list = NULL;
+ g_array_unref (byte_list);
+ byte_list = NULL;
+ g_byte_array_unref (base64);
+ base64 = NULL;
+
+ /* ...and later retrieved */
+ g_object_get (one_of_each,
+ "im_true", &im_true,
+ "im_false", &im_false,
+ "a_bite", &a_bite,
+ "integer16", &integer16,
+ "integer32", &integer32,
+ "integer64", &integer64,
+ "double_precision", &double_precision,
+ "some_characters", &some_characters,
+ "zomg_unicode", &zomg_unicode,
+ "what_who", &what_who,
+ "base64", &base64,
+ "byte_list", &byte_list,
+ "i16_list", &i16_list,
+ "i64_list", &i64_list,
+ NULL);
+
+ g_assert (im_true == TRUE);
+ g_assert (im_false == FALSE);
+
+ g_assert_cmphex (a_bite, ==, 0x50);
+ g_assert_cmphex (integer16, ==, 0x7e57);
+ g_assert_cmphex (integer32, ==, (gint32)0xdeadbeef);
+ g_assert_cmphex (integer64, ==, G_GINT64_CONSTANT (0xfa15efacade15bad));
+
+ g_assert_cmpfloat (double_precision, ==, M_PI);
+
+ g_assert_cmpstr (some_characters, ==, "Debug THIS!");
+ g_assert_cmpstr (zomg_unicode, ==, "\xd7\n\a\t");
+
+ g_assert (what_who == TRUE);
+
+ g_assert_cmpint (base64->len, ==, 8);
+ g_assert_cmpint (memcmp (base64->data,
+ initial_base64,
+ 8 * sizeof (guint8)), ==, 0);
+
+ g_assert_cmpint (byte_list->len, ==, 5);
+ g_assert_cmpint (memcmp (byte_list->data,
+ initial_byte_list,
+ 5 * sizeof (gint8)), ==, 0);
+
+ g_assert_cmpint (i16_list->len, ==, 5);
+ g_assert_cmpint (memcmp (i16_list->data,
+ initial_i16_list,
+ 5 * sizeof (gint16)), ==, 0);
+
+ g_assert_cmpint (i64_list->len, ==, 5);
+ g_assert_cmpint (memcmp (i64_list->data,
+ initial_i64_list,
+ 5 * sizeof (gint64)), ==, 0);
+
+ g_array_unref (i64_list);
+ g_array_unref (i16_list);
+ g_array_unref (byte_list);
+ g_byte_array_unref (base64);
+
+ g_object_unref (one_of_each);
+}
+
+static void
+test_structs_one_of_each_properties_byte_list (void)
+{
+ TTestOneOfEach *one_of_each;
+ GArray *byte_list = NULL;
+
+ one_of_each = g_object_new (T_TEST_TYPE_ONE_OF_EACH, NULL);
+
+ /* OneOfEach's "byte_list" member is a list that holds eight-bit-wide integer
+ values */
+ g_object_get (one_of_each, "byte_list", &byte_list, NULL);
+
+ g_assert (byte_list != NULL);
+ g_assert_cmpint (g_array_get_element_size (byte_list), ==, sizeof (gint8));
+
+ g_array_unref (byte_list);
+ g_object_unref (one_of_each);
+}
+
+static void
+test_structs_one_of_each_properties_i16_list (void)
+{
+ TTestOneOfEach *one_of_each;
+ GArray *i16_list = NULL;
+
+ one_of_each = g_object_new (T_TEST_TYPE_ONE_OF_EACH, NULL);
+
+ /* OneOfEach's "i16_list" member is a list that holds sixteen-bit-wide integer
+ values */
+ g_object_get (one_of_each, "i16_list", &i16_list, NULL);
+
+ g_assert (i16_list != NULL);
+ g_assert_cmpint (g_array_get_element_size (i16_list), ==, sizeof (gint16));
+
+ g_array_unref (i16_list);
+ g_object_unref (one_of_each);
+}
+
+static void
+test_structs_one_of_each_properties_i64_list (void)
+{
+ TTestOneOfEach *one_of_each;
+ GArray *i64_list = NULL;
+
+ one_of_each = g_object_new (T_TEST_TYPE_ONE_OF_EACH, NULL);
+
+ /* OneOfEach's "i64_list" member is a list that holds sixty-four-bit-wide
+ integer values */
+ g_object_get (one_of_each, "i64_list", &i64_list, NULL);
+
+ g_assert (i64_list != NULL);
+ g_assert_cmpint (g_array_get_element_size (i64_list), ==, sizeof (gint64));
+
+ g_array_unref (i64_list);
+ g_object_unref (one_of_each);
+}
+
+static void
+test_structs_nesting_create_and_destroy (void)
+{
+ GObject *object = NULL;
+
+ /* A Nesting structure can be created... */
+ object = g_object_new (T_TEST_TYPE_NESTING, NULL);
+
+ g_assert (object != NULL);
+ g_assert (T_TEST_IS_NESTING (object));
+
+ /* ...and destroyed */
+ g_object_unref (object);
+}
+
+static void
+test_structs_nesting_properties_my_bonk (void)
+{
+ TTestNesting *nesting;
+ TTestBonk *bonk = NULL;
+ gint type;
+ gchar *message;
+
+ nesting = g_object_new (T_TEST_TYPE_NESTING, NULL);
+
+ /* Nesting's "my_bonk" member is initialized with a new, default Bonk object
+ during construction */
+ g_object_get (nesting, "my_bonk", &bonk, NULL);
+
+ g_assert (bonk != NULL);
+ g_assert (T_TEST_IS_BONK (bonk));
+
+ g_object_get (bonk,
+ "type", &type,
+ "message", &message,
+ NULL);
+
+ g_assert_cmpint (type, ==, 0);
+ g_assert (message == NULL);
+
+ g_object_unref (bonk);
+ bonk = NULL;
+
+ /* It can be replaced... */
+ bonk = g_object_new (T_TEST_TYPE_BONK,
+ "type", 100,
+ "message", "Replacement Bonk",
+ NULL);
+ g_object_set (nesting, "my_bonk", bonk, NULL);
+ g_object_unref (bonk);
+ bonk = NULL;
+
+ g_object_get (nesting, "my_bonk", &bonk, NULL);
+
+ g_assert (bonk != NULL);
+ g_assert (T_TEST_IS_BONK (bonk));
+
+ g_object_get (bonk,
+ "type", &type,
+ "message", &message,
+ NULL);
+
+ g_assert_cmpint (type, ==, 100);
+ g_assert_cmpstr (message, ==, "Replacement Bonk");
+
+ g_free (message);
+ g_object_unref (bonk);
+ bonk = NULL;
+
+ /* ...or set to null */
+ g_object_set (nesting, "my_bonk", NULL, NULL);
+ g_object_get (nesting, "my_bonk", &bonk, NULL);
+
+ g_assert (bonk == NULL);
+
+ g_object_unref (nesting);
+}
+
+static void
+test_structs_nesting_properties_my_ooe (void)
+{
+ TTestNesting *nesting;
+ TTestOneOfEach *one_of_each = NULL;
+ gint a_bite;
+ gint integer16;
+
+ nesting = g_object_new (T_TEST_TYPE_NESTING, NULL);
+
+ /* Nesting's "my_ooe" member is initialized with a new, default OneOfEach
+ object during construction */
+ g_object_get (nesting, "my_ooe", &one_of_each, NULL);
+
+ g_assert (one_of_each != NULL);
+ g_assert (T_TEST_IS_ONE_OF_EACH (one_of_each));
+
+ g_object_get (one_of_each,
+ "a_bite", &a_bite,
+ "integer16", &integer16,
+ NULL);
+
+ g_assert_cmphex (a_bite, ==, 0x7f);
+ g_assert_cmphex (integer16, ==, 0x7fff);
+
+ g_object_unref (one_of_each);
+ one_of_each = NULL;
+
+ /* It can be replaced... */
+ one_of_each = g_object_new (T_TEST_TYPE_ONE_OF_EACH,
+ "a_bite", 0x50,
+ "integer16", 0x5050,
+ NULL);
+ g_object_set (nesting, "my_ooe", one_of_each, NULL);
+ g_object_unref (one_of_each);
+ one_of_each = NULL;
+
+ g_object_get (nesting, "my_ooe", &one_of_each, NULL);
+
+ g_assert (one_of_each != NULL);
+ g_assert (T_TEST_IS_ONE_OF_EACH (one_of_each));
+
+ g_object_get (one_of_each,
+ "a_bite", &a_bite,
+ "integer16", &integer16,
+ NULL);
+
+ g_assert_cmphex (a_bite, ==, 0x50);
+ g_assert_cmphex (integer16, ==, 0x5050);
+
+ g_object_unref (one_of_each);
+ one_of_each = NULL;
+
+ /* ...or set to null */
+ g_object_set (nesting, "my_ooe", NULL, NULL);
+ g_object_get (nesting, "my_ooe", &one_of_each, NULL);
+
+ g_assert (one_of_each == NULL);
+
+ g_object_unref (nesting);
+}
+
+static void
+test_structs_holy_moley_create_and_destroy (void)
+{
+ GObject *object = NULL;
+
+ /* A HolyMoley structure can be created... */
+ object = g_object_new (T_TEST_TYPE_HOLY_MOLEY, NULL);
+
+ g_assert (object != NULL);
+ g_assert (T_TEST_IS_HOLY_MOLEY (object));
+
+ /* ...and destroyed */
+ g_object_unref (object);
+}
+
+static void
+test_structs_holy_moley_properties_big (void)
+{
+ TTestHolyMoley *holy_moley;
+ GPtrArray *big = NULL;
+ gint a_bite = 0;
+ gint integer16 = 0;
+
+ holy_moley = g_object_new (T_TEST_TYPE_HOLY_MOLEY, NULL);
+
+ /* A HolyMoley's "big" member is is initialized on construction */
+ g_object_get (holy_moley, "big", &big, NULL);
+
+ g_assert (big != NULL);
+ g_assert_cmpint (big->len, ==, 0);
+
+ /* It can be modified... */
+ g_ptr_array_add (big,
+ g_object_new (T_TEST_TYPE_ONE_OF_EACH,
+ "a_bite", 0x50,
+ "integer16", 0x5050,
+ NULL));
+
+ g_ptr_array_unref (big);
+ big = NULL;
+
+ g_object_get (holy_moley, "big", &big, NULL);
+
+ g_assert_cmpint (big->len, ==, 1);
+ g_object_get (g_ptr_array_index (big, 0),
+ "a_bite", &a_bite,
+ "integer16", &integer16,
+ NULL);
+
+ g_assert_cmphex (a_bite, ==, 0x50);
+ g_assert_cmphex (integer16, ==, 0x5050);
+
+ g_ptr_array_unref (big);
+ big = NULL;
+
+ /* ...replaced... */
+ big = g_ptr_array_new_with_free_func (g_object_unref);
+ g_ptr_array_add (big,
+ g_object_new (T_TEST_TYPE_ONE_OF_EACH,
+ "a_bite", 0x64,
+ "integer16", 0x1541,
+ NULL));
+
+ g_object_set (holy_moley, "big", big, NULL);
+
+ g_ptr_array_unref (big);
+ big = NULL;
+
+ g_object_get (holy_moley, "big", &big, NULL);
+
+ g_assert_cmpint (big->len, ==, 1);
+ g_object_get (g_ptr_array_index (big, 0),
+ "a_bite", &a_bite,
+ "integer16", &integer16,
+ NULL);
+
+ g_assert_cmphex (a_bite, ==, 0x64);
+ g_assert_cmphex (integer16, ==, 0x1541);
+
+ g_ptr_array_unref (big);
+ big = NULL;
+
+ /* ...or set to NULL */
+ g_object_set (holy_moley, "big", NULL, NULL);
+ g_object_get (holy_moley, "big", &big, NULL);
+
+ g_assert (big == NULL);
+
+ g_object_unref (holy_moley);
+}
+
+static void
+test_structs_holy_moley_properties_contain (void)
+{
+ static gchar *strings[2] = { "Apache", "Thrift" };
+
+ TTestHolyMoley *holy_moley;
+ GHashTable *contain = NULL;
+ GPtrArray *string_list;
+ GList *key_list;
+
+ holy_moley = g_object_new (T_TEST_TYPE_HOLY_MOLEY, NULL);
+
+ /* A HolyMoley's "contain" member is initialized on construction */
+ g_object_get (holy_moley, "contain", &contain, NULL);
+
+ g_assert (contain != NULL);
+ g_assert_cmpint (g_hash_table_size (contain), ==, 0);
+
+ /* It can be modified... */
+ string_list = g_ptr_array_new ();
+ g_ptr_array_add (string_list, strings[0]);
+ g_ptr_array_add (string_list, strings[1]);
+
+ g_hash_table_insert (contain, string_list, NULL);
+ string_list = NULL;
+
+ g_hash_table_unref (contain);
+ contain = NULL;
+
+ g_object_get (holy_moley, "contain", &contain, NULL);
+
+ g_assert_cmpint (g_hash_table_size (contain), ==, 1);
+
+ key_list = g_hash_table_get_keys (contain);
+ string_list = g_list_nth_data (key_list, 0);
+
+ g_assert_cmpint (string_list->len, ==, 2);
+ g_assert_cmpstr (g_ptr_array_index (string_list, 0), ==, "Apache");
+ g_assert_cmpstr (g_ptr_array_index (string_list, 1), ==, "Thrift");
+
+ g_list_free (key_list);
+ g_hash_table_unref (contain);
+ contain = NULL;
+
+ /* ...replaced... */
+ contain = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ (GDestroyNotify) g_ptr_array_unref,
+ NULL);
+ g_object_set (holy_moley, "contain", contain, NULL);
+ g_hash_table_unref (contain);
+ contain = NULL;
+
+ g_object_get (holy_moley, "contain", &contain, NULL);
+
+ g_assert_cmpint (g_hash_table_size (contain), ==, 0);
+
+ g_hash_table_unref (contain);
+ contain = NULL;
+
+ /* ...or set to NULL */
+ g_object_set (holy_moley, "contain", NULL, NULL);
+ g_object_get (holy_moley, "contain", &contain, NULL);
+
+ g_assert (contain == NULL);
+
+ g_object_unref (holy_moley);
+}
+
+static void
+test_structs_holy_moley_properties_bonks (void)
+{
+ TTestHolyMoley *holy_moley;
+ GHashTable *bonks = NULL;
+ GPtrArray *bonk_list = NULL;
+ TTestBonk *bonk = NULL;
+ gint type;
+ gchar *message;
+ GList *key_list;
+
+ holy_moley = g_object_new (T_TEST_TYPE_HOLY_MOLEY, NULL);
+
+ /* A HolyMoley's "bonks" member is initialized on construction */
+ g_object_get (holy_moley, "bonks", &bonks, NULL);
+
+ g_assert (bonks != NULL);
+ g_assert_cmpint (g_hash_table_size (bonks), ==, 0);
+
+ /* It can be modified... */
+ bonk = g_object_new (T_TEST_TYPE_BONK,
+ "type", 100,
+ "message", "Sample Bonk",
+ NULL);
+ bonk_list = g_ptr_array_new_with_free_func (g_object_unref);
+ g_ptr_array_add (bonk_list, bonk);
+ bonk = NULL;
+
+ g_hash_table_insert (bonks, g_strdup ("Sample Bonks"), bonk_list);
+ bonk_list = NULL;
+
+ g_hash_table_unref (bonks);
+ bonks = NULL;
+
+ g_object_get (holy_moley, "bonks", &bonks, NULL);
+
+ g_assert_cmpint (g_hash_table_size (bonks), ==, 1);
+
+ key_list = g_hash_table_get_keys (bonks);
+ bonk_list = g_hash_table_lookup (bonks, g_list_nth_data (key_list, 0));
+
+ g_assert_cmpint (bonk_list->len, ==, 1);
+
+ bonk = (g_ptr_array_index (bonk_list, 0));
+ g_object_get (bonk,
+ "type", &type,
+ "message", &message,
+ NULL);
+
+ g_assert_cmpint (type, ==, 100);
+ g_assert_cmpstr (message, ==, "Sample Bonk");
+
+ bonk = NULL;
+ g_free (message);
+ g_list_free (key_list);
+ g_hash_table_unref (bonks);
+ bonks = NULL;
+
+ /* ...replaced... */
+ bonks = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) g_ptr_array_unref);
+ g_object_set (holy_moley, "bonks", bonks, NULL);
+ g_hash_table_unref (bonks);
+ bonks = NULL;
+
+ g_object_get (holy_moley, "bonks", &bonks, NULL);
+
+ g_assert_cmpint (g_hash_table_size (bonks), ==, 0);
+
+ g_hash_table_unref (bonks);
+ bonks = NULL;
+
+ /* ...or set to NULL */
+ g_object_set (holy_moley, "bonks", NULL, NULL);
+ g_object_get (holy_moley, "bonks", &bonks, NULL);
+
+ g_assert (bonks == NULL);
+
+ g_object_unref (holy_moley);
+}
+
+static void
+test_structs_empty (void)
+{
+ GObject *object = NULL;
+ GParamSpec **properties;
+ guint property_count;
+
+ /* An Empty structure can be created */
+ object = g_object_new (T_TEST_TYPE_EMPTY, NULL);
+
+ g_assert (object != NULL);
+ g_assert (T_TEST_IS_EMPTY (object));
+
+ /* An Empty structure has no members and thus no properties */
+ properties = g_object_class_list_properties (G_OBJECT_GET_CLASS (object),
+ &property_count);
+ g_assert_cmpint (property_count, ==, 0);
+ g_free (properties);
+
+ /* An Empty structure can be destroyed */
+ g_object_unref (object);
+}
+
+static void
+test_structs_wrapper_create_and_destroy (void)
+{
+ GObject *object = NULL;
+
+ /* A Wrapper structure can be created... */
+ object = g_object_new (T_TEST_TYPE_EMPTY, NULL);
+
+ g_assert (object != NULL);
+ g_assert (T_TEST_IS_EMPTY (object));
+
+ /* ...and destroyed */
+ g_object_unref (object);
+}
+
+static void
+test_structs_wrapper_properties_foo (void) {
+ TTestWrapper *wrapper;
+ TTestEmpty *foo;
+
+ wrapper = g_object_new (T_TEST_TYPE_WRAPPER, NULL);
+
+ /* A Wrapper structure has one member, "foo", which is an Empty
+ structure initialized during construction */
+ g_object_get (wrapper, "foo", &foo, NULL);
+
+ g_assert (foo != NULL);
+ g_assert (T_TEST_IS_EMPTY (foo));
+
+ g_object_unref (foo);
+ foo = NULL;
+
+ /* A Wrapper's foo property can be replaced... */
+ foo = g_object_new (T_TEST_TYPE_EMPTY, NULL);
+ g_object_set (wrapper, "foo", foo, NULL);
+
+ g_object_unref (foo);
+ foo = NULL;
+
+ g_object_get (wrapper, "foo", &foo, NULL);
+ g_assert (foo != NULL);
+ g_assert (T_TEST_IS_EMPTY (foo));
+
+ g_object_unref (foo);
+ foo = NULL;
+
+ /* ...or set to NULL */
+ g_object_set (wrapper, "foo", NULL, NULL);
+ g_object_get (wrapper, "foo", &foo, NULL);
+
+ g_assert (foo == NULL);
+
+ g_object_unref (wrapper);
+}
+
+static void
+test_services_inherited (void)
+{
+ ThriftProtocol *protocol;
+ TTestInheritedClient *inherited_client;
+ GObject *input_protocol, *output_protocol;
+
+ protocol = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, NULL);
+ inherited_client = g_object_new (T_TEST_TYPE_INHERITED_CLIENT,
+ NULL);
+
+ /* TTestInheritedClient inherits from TTestSrvClient */
+ g_assert (g_type_is_a (T_TEST_TYPE_INHERITED_CLIENT,
+ T_TEST_TYPE_SRV_CLIENT));
+
+ /* TTestInheritedClient implements TTestSrvClient's interface */
+ g_assert (g_type_is_a (T_TEST_TYPE_INHERITED_CLIENT,
+ T_TEST_TYPE_SRV_IF));
+
+ /* TTestInheritedClient's inherited properties can be set and retrieved */
+ g_object_set (inherited_client,
+ "input_protocol", protocol,
+ "output_protocol", protocol,
+ NULL);
+
+ g_object_get (inherited_client,
+ "input_protocol", &input_protocol,
+ "output_protocol", &output_protocol,
+ NULL);
+
+ g_assert (input_protocol == G_OBJECT(protocol));
+ g_assert (output_protocol == G_OBJECT(protocol));
+
+ g_object_unref (output_protocol);
+ g_object_unref (input_protocol);
+ g_object_unref (inherited_client);
+ g_object_unref (protocol);
+}
+
+int
+main(int argc, char *argv[])
+{
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init ();
+#endif
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/Doubles/CreateAndDestroy",
+ test_structs_doubles_create_and_destroy);
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/Doubles/Initialize",
+ test_structs_doubles_initialize);
+
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/OneOfEach/CreateAndDestroy",
+ test_structs_one_of_each_create_and_destroy);
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/OneOfEach/Initialize/DefaultValues",
+ test_structs_one_of_each_initialize_default_values);
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/OneOfEach/Initialize/SpecifiedValues",
+ test_structs_one_of_each_initialize_specified_values);
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/OneOfEach/Properties/byte_list",
+ test_structs_one_of_each_properties_byte_list);
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/OneOfEach/Properties/i16_list",
+ test_structs_one_of_each_properties_i16_list);
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/OneOfEach/Properties/i64_list",
+ test_structs_one_of_each_properties_i64_list);
+
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/Nesting/CreateAndDestroy",
+ test_structs_nesting_create_and_destroy);
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/Nesting/Properties/my_bonk",
+ test_structs_nesting_properties_my_bonk);
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/Nesting/Properties/my_ooe",
+ test_structs_nesting_properties_my_ooe);
+
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/HolyMoley/CreateAndDestroy",
+ test_structs_holy_moley_create_and_destroy);
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/HolyMoley/Properties/big",
+ test_structs_holy_moley_properties_big);
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/HolyMoley/Properties/contain",
+ test_structs_holy_moley_properties_contain);
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/HolyMoley/Properties/bonks",
+ test_structs_holy_moley_properties_bonks);
+
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/Empty",
+ test_structs_empty);
+
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/Wrapper/CreateAndDestroy",
+ test_structs_wrapper_create_and_destroy);
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Structs/Wrapper/Properties/foo",
+ test_structs_wrapper_properties_foo);
+
+ g_test_add_func
+ ("/testdebugproto/DebugProto/Services/Inherited",
+ test_services_inherited);
+
+ return g_test_run ();
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/testfdtransport.c b/src/jaegertracing/thrift/lib/c_glib/test/testfdtransport.c
new file mode 100755
index 000000000..1ea89be78
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/testfdtransport.c
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/transport/thrift_fd_transport.h>
+
+static const gchar TEST_DATA[12] = "abcde01234!";
+
+static void
+test_create_and_destroy (void)
+{
+ GObject *object;
+ object = g_object_new (THRIFT_TYPE_FD_TRANSPORT, "fd", -1, NULL);
+ g_assert (object != NULL);
+ g_object_unref (object);
+}
+
+static void
+test_open_and_close (void)
+{
+ ThriftTransport *transport;
+ ThriftTransportClass *klass;
+ GError *error;
+ gint fd;
+ gchar *filename;
+
+ error = NULL;
+ filename = NULL;
+
+ fd = g_file_open_tmp (NULL, &filename, &error);
+ g_assert (fd >= 0);
+
+ transport = THRIFT_TRANSPORT (g_object_new (THRIFT_TYPE_FD_TRANSPORT,
+ "fd", fd,
+ NULL));
+ klass = THRIFT_TRANSPORT_GET_CLASS (transport);
+
+ /* open is no-op */
+ g_assert (klass->is_open (transport));
+ g_assert (klass->peek (transport, &error));
+ g_assert (klass->open (transport, &error));
+ g_assert (klass->is_open (transport));
+ g_assert (klass->peek (transport, &error));
+
+ g_assert (klass->close (transport, &error));
+ g_assert (! klass->open (transport, &error));
+ g_assert (! klass->is_open (transport));
+ g_assert (! klass->peek (transport, &error));
+
+ /* already closed */
+ g_assert (close (fd) != 0);
+ g_assert (errno == EBADF);
+
+ g_object_unref (transport);
+
+ g_remove (filename);
+ g_free (filename);
+
+ /* test bad fd */
+ transport = THRIFT_TRANSPORT (g_object_new (THRIFT_TYPE_FD_TRANSPORT,
+ "fd", -1,
+ NULL));
+ klass = THRIFT_TRANSPORT_GET_CLASS (transport);
+
+ g_assert (! klass->is_open (transport));
+ error = NULL;
+ g_assert (! klass->peek (transport, &error));
+ error = NULL;
+ g_assert (! klass->open (transport, &error));
+ error = NULL;
+ g_assert (! klass->close (transport, &error));
+
+ g_object_unref (transport);
+}
+
+static void
+test_read_and_write (void)
+{
+ gchar out_buf[8];
+ gchar *b;
+ gint want, got;
+ ThriftTransport *transport;
+ ThriftTransportClass *klass;
+ GError *error;
+ gint fd;
+ gchar *filename;
+
+ error = NULL;
+ filename = NULL;
+
+ fd = g_file_open_tmp (NULL, &filename, &error);
+ g_assert (fd >= 0);
+
+ /* write */
+ transport = THRIFT_TRANSPORT (g_object_new (THRIFT_TYPE_FD_TRANSPORT,
+ "fd", fd,
+ NULL));
+ klass = THRIFT_TRANSPORT_GET_CLASS (transport);
+ g_assert (klass->is_open (transport));
+ g_assert (klass->write (transport, (gpointer) TEST_DATA, 11, &error));
+ g_assert (klass->flush (transport, &error));
+ g_assert (klass->close (transport, &error));
+ g_object_unref (transport);
+
+ /* read */
+ fd = open(filename, O_RDONLY, S_IRUSR | S_IWUSR);
+ g_assert (fd >= 0);
+
+ transport = THRIFT_TRANSPORT (g_object_new (THRIFT_TYPE_FD_TRANSPORT,
+ "fd", fd,
+ NULL));
+ klass = THRIFT_TRANSPORT_GET_CLASS (transport);
+
+ memset(out_buf, 0, 8);
+ b = out_buf;
+ want = 7;
+ while (want > 0) {
+ got = klass->read (transport, (gpointer) b, want, &error);
+ g_assert (got > 0 && got <= want);
+ b += got;
+ want -= got;
+ }
+ g_assert (memcmp (out_buf, TEST_DATA, 7) == 0);
+
+ memset(out_buf, 0, 8);
+ b = out_buf;
+ want = 4;
+ while (want > 0) {
+ got = klass->read (transport, (gpointer) b, want, &error);
+ g_assert (got > 0 && got <= want);
+ b += got;
+ want -= got;
+ }
+ g_assert (memcmp (out_buf, TEST_DATA + 7, 4) == 0);
+
+ g_assert (klass->close (transport, &error));
+ g_object_unref (transport);
+
+ /* clean up */
+
+ g_remove (filename);
+ g_free (filename);
+}
+
+int
+main (int argc, char *argv[])
+{
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init ();
+#endif
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/testfdtransport/CreateAndDestroy", test_create_and_destroy);
+ g_test_add_func ("/testfdtransport/OpenAndClose", test_open_and_close);
+ g_test_add_func ("/testfdtransport/ReadAndWrite", test_read_and_write);
+
+ return g_test_run ();
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/testframedtransport.c b/src/jaegertracing/thrift/lib/c_glib/test/testframedtransport.c
new file mode 100755
index 000000000..008e61e40
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/testframedtransport.c
@@ -0,0 +1,323 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <netdb.h>
+#include <sys/wait.h>
+
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/transport/thrift_socket.h>
+#include <thrift/c_glib/transport/thrift_server_transport.h>
+#include <thrift/c_glib/transport/thrift_server_socket.h>
+
+#define TEST_DATA { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }
+
+#include "../src/thrift/c_glib/transport/thrift_framed_transport.c"
+
+static void thrift_server (const int port);
+static void thrift_socket_server_open (const int port, int times);
+
+/* test object creation and destruction */
+static void
+test_create_and_destroy(void)
+{
+ ThriftTransport *transport = NULL;
+ guint r_buf_size = 0;
+ guint w_buf_size = 0;
+
+ GObject *object = NULL;
+ object = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT, NULL);
+ g_assert (object != NULL);
+ g_object_get (G_OBJECT (object), "transport", &transport,
+ "r_buf_size", &r_buf_size,
+ "w_buf_size", &w_buf_size, NULL);
+ g_object_unref (object);
+}
+
+static void
+test_open_and_close(void)
+{
+ ThriftSocket *tsocket = NULL;
+ ThriftTransport *transport = NULL;
+ GError *err = NULL;
+ pid_t pid;
+ int port = 51199;
+ int status;
+
+ pid = fork ();
+ g_assert ( pid >= 0 );
+
+ if ( pid == 0 )
+ {
+ /* child listens */
+ thrift_socket_server_open (port,1);
+ exit (0);
+ } else {
+ /* parent connects, wait a bit for the socket to be created */
+ sleep (1);
+ /* create a ThriftSocket */
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
+ "port", port, NULL);
+
+ /* create a BufferedTransport wrapper of the Socket */
+ transport = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT,
+ "transport", THRIFT_TRANSPORT (tsocket), NULL);
+
+ /* this shouldn't work */
+ g_assert (thrift_framed_transport_open (transport, NULL) == TRUE);
+ g_assert (thrift_framed_transport_is_open (transport) == TRUE);
+ g_assert (thrift_framed_transport_close (transport, NULL) == TRUE);
+ g_object_unref (transport);
+ g_object_unref (tsocket);
+
+ /* try and underlying socket failure */
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost.broken",
+ NULL);
+
+ /* create a BufferedTransport wrapper of the Socket */
+ transport = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT,
+ "transport", THRIFT_TRANSPORT (tsocket), NULL);
+
+ g_assert (thrift_framed_transport_open (transport, &err) == FALSE);
+ g_object_unref (transport);
+ g_object_unref (tsocket);
+ g_error_free (err);
+ err = NULL;
+
+ g_assert ( wait (&status) == pid );
+ g_assert ( status == 0 );
+ }
+}
+
+static void
+test_read_and_write(void)
+{
+ int status;
+ pid_t pid;
+ ThriftSocket *tsocket = NULL;
+ ThriftTransport *transport = NULL;
+ int port = 51199;
+ guchar buf[10] = TEST_DATA; /* a buffer */
+
+ pid = fork ();
+ g_assert ( pid >= 0 );
+
+ if ( pid == 0 )
+ {
+ /* child listens */
+ thrift_server (port);
+ exit (0);
+ } else {
+ /* parent connects, wait a bit for the socket to be created */
+ sleep (1);
+
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
+ "port", port, NULL);
+ transport = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT,
+ "transport", THRIFT_TRANSPORT (tsocket),
+ "w_buf_size", 4, NULL);
+
+ g_assert (thrift_framed_transport_open (transport, NULL) == TRUE);
+ g_assert (thrift_framed_transport_is_open (transport));
+
+ /* write 10 bytes */
+ thrift_framed_transport_write (transport, buf, 10, NULL);
+ thrift_framed_transport_flush (transport, NULL);
+
+ thrift_framed_transport_write (transport, buf, 1, NULL);
+ thrift_framed_transport_flush (transport, NULL);
+
+ thrift_framed_transport_write (transport, buf, 10, NULL);
+ thrift_framed_transport_flush (transport, NULL);
+
+ thrift_framed_transport_write (transport, buf, 10, NULL);
+ thrift_framed_transport_flush (transport, NULL);
+
+ thrift_framed_transport_write_end (transport, NULL);
+ thrift_framed_transport_flush (transport, NULL);
+ thrift_framed_transport_close (transport, NULL);
+
+ g_object_unref (transport);
+ g_object_unref (tsocket);
+
+ g_assert ( wait (&status) == pid );
+ g_assert ( status == 0 );
+ }
+}
+
+/* test reading from the transport after the peer has unexpectedly
+ closed the connection */
+static void
+test_read_after_peer_close(void)
+{
+ int status;
+ pid_t pid;
+ int port = 51199;
+ GError *err = NULL;
+
+ pid = fork ();
+ g_assert (pid >= 0);
+
+ if (pid == 0)
+ {
+ ThriftServerTransport *server_transport = NULL;
+ ThriftTransport *client_transport = NULL;
+
+ /* child listens */
+ server_transport = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+ "port", port,
+ NULL);
+ g_assert (server_transport != NULL);
+
+ thrift_server_transport_listen (server_transport, &err);
+ g_assert (err == NULL);
+
+ /* wrap the client transport in a ThriftFramedTransport */
+ client_transport = g_object_new
+ (THRIFT_TYPE_FRAMED_TRANSPORT,
+ "transport", thrift_server_transport_accept (server_transport, &err),
+ "r_buf_size", 0,
+ NULL);
+ g_assert (err == NULL);
+ g_assert (client_transport != NULL);
+
+ /* close the connection immediately after the client connects */
+ thrift_transport_close (client_transport, NULL);
+
+ g_object_unref (client_transport);
+ g_object_unref (server_transport);
+
+ exit (0);
+ } else {
+ ThriftSocket *tsocket = NULL;
+ ThriftTransport *transport = NULL;
+ guchar buf[10]; /* a buffer */
+
+ /* parent connects, wait a bit for the socket to be created */
+ sleep (1);
+
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET,
+ "hostname", "localhost",
+ "port", port,
+ NULL);
+ transport = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT,
+ "transport", THRIFT_TRANSPORT (tsocket),
+ "w_buf_size", 0,
+ NULL);
+
+ g_assert (thrift_transport_open (transport, NULL) == TRUE);
+ g_assert (thrift_transport_is_open (transport));
+
+ /* attempting to read from the transport after the peer has closed
+ the connection fails gracefully without generating a critical
+ warning or segmentation fault */
+ thrift_transport_read (transport, buf, 10, &err);
+ g_assert (err != NULL);
+
+ g_error_free (err);
+ err = NULL;
+
+ thrift_transport_read_end (transport, &err);
+ g_assert (err == NULL);
+
+ thrift_transport_close (transport, &err);
+ g_assert (err == NULL);
+
+ g_object_unref (transport);
+ g_object_unref (tsocket);
+
+ g_assert (wait (&status) == pid);
+ g_assert (status == 0);
+ }
+}
+
+static void
+thrift_socket_server_open (const int port, int times)
+{
+ ThriftServerTransport *transport = NULL;
+ ThriftTransport *client = NULL;
+ int i;
+
+ ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+ "port", port, NULL);
+
+ transport = THRIFT_SERVER_TRANSPORT (tsocket);
+ thrift_server_transport_listen (transport, NULL);
+ for(i=0;i<times;i++){
+ client = thrift_server_transport_accept (transport, NULL);
+ g_assert (client != NULL);
+ thrift_socket_close (client, NULL);
+ g_object_unref (client);
+ }
+ g_object_unref (tsocket);
+}
+
+static void
+thrift_server (const int port)
+{
+ int bytes = 0;
+ ThriftServerTransport *transport = NULL;
+ ThriftTransport *client = NULL;
+ guchar buf[12]; /* a buffer */
+ guchar match[10] = TEST_DATA;
+
+ ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+ "port", port, NULL);
+
+ transport = THRIFT_SERVER_TRANSPORT (tsocket);
+ thrift_server_transport_listen (transport, NULL);
+
+ /* wrap the client in a BufferedTransport */
+ client = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT, "transport",
+ thrift_server_transport_accept (transport, NULL),
+ "r_buf_size", 5, NULL);
+ g_assert (client != NULL);
+
+ /* read 10 bytes */
+ bytes = thrift_framed_transport_read (client, buf, 10, NULL);
+ g_assert (bytes == 10); /* make sure we've read 10 bytes */
+ g_assert ( memcmp (buf, match, 10) == 0 ); /* make sure what we got matches */
+
+ bytes = thrift_framed_transport_read (client, buf, 6, NULL);
+ bytes = thrift_framed_transport_read (client, buf, 5, NULL);
+ bytes = thrift_framed_transport_read (client, buf, 1, NULL);
+
+ bytes = thrift_framed_transport_read (client, buf, 12, NULL);
+
+ thrift_framed_transport_read_end (client, NULL);
+ thrift_framed_transport_close (client, NULL);
+ g_object_unref (client);
+ g_object_unref (tsocket);
+}
+
+int
+main(int argc, char *argv[])
+{
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init();
+#endif
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/testframedtransport/CreateAndDestroy", test_create_and_destroy);
+ g_test_add_func ("/testframedtransport/OpenAndClose", test_open_and_close);
+ g_test_add_func ("/testframedtransport/ReadAndWrite", test_read_and_write);
+ g_test_add_func ("/testframedtransport/ReadAfterPeerClose", test_read_after_peer_close);
+
+ return g_test_run ();
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/testmemorybuffer.c b/src/jaegertracing/thrift/lib/c_glib/test/testmemorybuffer.c
new file mode 100755
index 000000000..9fb68b93d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/testmemorybuffer.c
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <netdb.h>
+
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/transport/thrift_socket.h>
+#include <thrift/c_glib/transport/thrift_server_transport.h>
+#include <thrift/c_glib/transport/thrift_server_socket.h>
+
+static const gchar TEST_DATA[11] = "abcdefghij";
+
+#include "../src/thrift/c_glib/transport/thrift_memory_buffer.c"
+
+/* test object creation and destruction */
+static void
+test_create_and_destroy (void)
+{
+ GObject *object = NULL;
+ object = g_object_new (THRIFT_TYPE_MEMORY_BUFFER,
+ "buf_size", 10,
+ NULL);
+ g_assert (object != NULL);
+ g_object_unref (object);
+}
+
+static void
+test_create_and_destroy_large (void)
+{
+ GObject *object = NULL;
+ object = g_object_new (THRIFT_TYPE_MEMORY_BUFFER,
+ "buf_size", 10 * 1024 * 1024,
+ NULL);
+ g_assert (object != NULL);
+ g_object_unref (object);
+}
+
+static void
+test_create_and_destroy_default (void)
+{
+ GObject *object = NULL;
+ object = g_object_new (THRIFT_TYPE_MEMORY_BUFFER, NULL);
+ g_assert (object != NULL);
+ g_object_unref (object);
+}
+
+static void
+test_create_and_destroy_external (void)
+{
+ GObject *object = NULL;
+ GByteArray *buf = g_byte_array_new ();
+ g_assert (buf != NULL);
+ object = g_object_new (THRIFT_TYPE_MEMORY_BUFFER,
+ "buf", buf,
+ NULL);
+ g_assert (object != NULL);
+ g_object_unref (object);
+}
+
+static void
+test_create_and_destroy_unowned (void)
+{
+ GObject *object = NULL;
+ GValue val = G_VALUE_INIT;
+ GByteArray *buf;
+
+ object = g_object_new (THRIFT_TYPE_MEMORY_BUFFER,
+ "owner", FALSE,
+ NULL);
+ g_assert (object != NULL);
+
+ g_value_init (&val, G_TYPE_POINTER);
+ g_object_get_property (object, "buf", &val);
+ buf = (GByteArray*) g_value_get_pointer (&val);
+ g_assert (buf != NULL);
+
+ g_byte_array_unref (buf);
+ g_value_unset (&val);
+ g_object_unref (object);
+}
+
+static void
+test_open_and_close (void)
+{
+ ThriftMemoryBuffer *tbuffer = NULL;
+
+ /* create a ThriftMemoryBuffer */
+ tbuffer = g_object_new (THRIFT_TYPE_MEMORY_BUFFER, NULL);
+
+ /* no-ops */
+ g_assert (thrift_memory_buffer_open (THRIFT_TRANSPORT (tbuffer), NULL) == TRUE);
+ g_assert (thrift_memory_buffer_is_open (THRIFT_TRANSPORT (tbuffer)) == TRUE);
+ g_assert (thrift_memory_buffer_close (THRIFT_TRANSPORT (tbuffer), NULL) == TRUE);
+
+ g_object_unref (tbuffer);
+}
+
+static void
+test_read_and_write (void)
+{
+ ThriftMemoryBuffer *tbuffer = NULL;
+ gint got, want;
+ gchar read[10];
+ gchar *b;
+ GError *error = NULL;
+
+ tbuffer = g_object_new (THRIFT_TYPE_MEMORY_BUFFER, "buf_size", 5, NULL);
+ g_assert (thrift_memory_buffer_write (THRIFT_TRANSPORT (tbuffer),
+ (gpointer) TEST_DATA,
+ 10, &error) == FALSE);
+ g_assert (error != NULL);
+ g_error_free (error);
+ error = NULL;
+ g_object_unref (tbuffer);
+
+ tbuffer = g_object_new (THRIFT_TYPE_MEMORY_BUFFER, "buf_size", 15, NULL);
+ g_assert (thrift_memory_buffer_write (THRIFT_TRANSPORT (tbuffer),
+ (gpointer) TEST_DATA, 10, &error) == TRUE);
+ g_assert (error == NULL);
+
+ memset(read, 0, 10);
+ b = read;
+ want = 10;
+ while (want > 0) {
+ got = thrift_memory_buffer_read (THRIFT_TRANSPORT (tbuffer),
+ (gpointer) b, want, &error);
+ g_assert (got > 0 && got <= want);
+ g_assert (error == NULL);
+ b += got;
+ want -= got;
+ }
+ g_assert (memcmp (read, TEST_DATA, 10) == 0);
+ g_object_unref (tbuffer);
+}
+
+static void
+test_read_and_write_default (void)
+{
+ ThriftMemoryBuffer *tbuffer = NULL;
+ gint got, want, i;
+ gchar read[10];
+ gchar *b;
+ GError *error = NULL;
+
+ tbuffer = g_object_new (THRIFT_TYPE_MEMORY_BUFFER, NULL);
+ for (i = 0; i < 100; ++i) {
+ g_assert (thrift_memory_buffer_write (THRIFT_TRANSPORT (tbuffer),
+ (gpointer) TEST_DATA, 10, &error) == TRUE);
+ g_assert (error == NULL);
+ }
+
+ for (i = 0; i < 100; ++i) {
+ memset(read, 0, 10);
+ b = read;
+ want = 10;
+ while (want > 0) {
+ got = thrift_memory_buffer_read (THRIFT_TRANSPORT (tbuffer),
+ (gpointer) b, want, &error);
+ g_assert (got > 0 && got <= want);
+ g_assert (error == NULL);
+ b += got;
+ want -= got;
+ }
+ g_assert (memcmp (read, TEST_DATA, 10) == 0);
+ }
+ g_object_unref (tbuffer);
+}
+
+static void
+test_read_and_write_external (void)
+{
+ ThriftMemoryBuffer *tbuffer = NULL;
+ gchar *b;
+ GError *error = NULL;
+ GByteArray *buf = g_byte_array_new ();
+ g_assert (buf != NULL);
+
+ tbuffer = g_object_new (THRIFT_TYPE_MEMORY_BUFFER, "buf", buf, NULL);
+ g_assert (thrift_memory_buffer_write (THRIFT_TRANSPORT (tbuffer),
+ (gpointer) TEST_DATA, 10, &error) == TRUE);
+ g_assert (error == NULL);
+
+ g_assert (memcmp (buf->data, TEST_DATA, 10) == 0);
+ g_object_unref (tbuffer);
+}
+
+int
+main(int argc, char *argv[])
+{
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init ();
+#endif
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/testmemorybuffer/CreateAndDestroy", test_create_and_destroy);
+ g_test_add_func ("/testmemorybuffer/CreateAndDestroyLarge", test_create_and_destroy_large);
+ g_test_add_func ("/testmemorybuffer/CreateAndDestroyUnlimited", test_create_and_destroy_default);
+ g_test_add_func ("/testmemorybuffer/CreateAndDestroyExternal", test_create_and_destroy_external);
+ g_test_add_func ("/testmemorybuffer/CreateAndDestroyUnowned", test_create_and_destroy_unowned);
+ g_test_add_func ("/testmemorybuffer/OpenAndClose", test_open_and_close);
+ g_test_add_func ("/testmemorybuffer/ReadAndWrite", test_read_and_write);
+ g_test_add_func ("/testmemorybuffer/ReadAndWriteUnlimited", test_read_and_write_default);
+ g_test_add_func ("/testmemorybuffer/ReadAndWriteExternal", test_read_and_write_external);
+
+ return g_test_run ();
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/testoptionalrequired.c b/src/jaegertracing/thrift/lib/c_glib/test/testoptionalrequired.c
new file mode 100755
index 000000000..636c36d69
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/testoptionalrequired.c
@@ -0,0 +1,227 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <glib.h>
+
+#include <thrift/c_glib/thrift_struct.h>
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/protocol/thrift_binary_protocol.h>
+#include <thrift/c_glib/transport/thrift_memory_buffer.h>
+#include "gen-c_glib/t_test_optional_required_test_types.h"
+
+#include "gen-c_glib/t_test_optional_required_test_types.c"
+
+static void
+write_to_read (ThriftStruct *w, ThriftStruct *r, GError **write_error,
+ GError **read_error)
+{
+ ThriftMemoryBuffer *tbuffer = NULL;
+ ThriftProtocol *protocol = NULL;
+
+ tbuffer = g_object_new (THRIFT_TYPE_MEMORY_BUFFER, NULL);
+ protocol = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport",
+ tbuffer, NULL);
+
+ thrift_struct_write (w, protocol, write_error);
+ thrift_struct_read (r, protocol, read_error);
+
+ g_object_unref (protocol);
+ g_object_unref (tbuffer);
+}
+
+static void
+test_old_school1 (void)
+{
+ TTestOldSchool *o = NULL;
+
+ o = g_object_new (T_TEST_TYPE_OLD_SCHOOL, NULL);
+ o->im_int = 10;
+ o->im_str = g_strdup ("test");
+ o->im_big = g_ptr_array_new ();
+ g_ptr_array_free (o->im_big, TRUE);
+ o->im_big = NULL;
+ g_free (o->im_str);
+ o->im_str = NULL;
+ g_object_unref (o);
+}
+
+/**
+ * Write to read with optional fields
+ */
+static void
+test_simple (void)
+{
+ TTestSimple *s1 = NULL, *s2 = NULL, *s3 = NULL;
+
+ s1 = g_object_new (T_TEST_TYPE_SIMPLE, NULL);
+ s2 = g_object_new (T_TEST_TYPE_SIMPLE, NULL);
+ s3 = g_object_new (T_TEST_TYPE_SIMPLE, NULL);
+
+ /* write-to-read with optional fields */
+ s1->im_optional = 10;
+ g_assert (s1->__isset_im_default == FALSE);
+ g_assert (s1->__isset_im_optional == FALSE);
+ write_to_read (THRIFT_STRUCT (s1), THRIFT_STRUCT (s2), NULL, NULL);
+ g_assert (s2->__isset_im_default == TRUE);
+ g_assert (s2->__isset_im_optional == FALSE);
+ g_assert (s2->im_optional == 0);
+
+ s1->__isset_im_optional = TRUE;
+ write_to_read (THRIFT_STRUCT (s1), THRIFT_STRUCT (s3), NULL, NULL);
+ g_assert (s3->__isset_im_default == TRUE);
+ g_assert (s3->__isset_im_optional == TRUE);
+ g_assert (s3->im_optional == 10);
+
+ g_object_unref (s1);
+ g_object_unref (s2);
+}
+
+/**
+ * Writing between optional and default
+ */
+static void
+test_tricky1 (void)
+{
+ TTestTricky1 *t1 = NULL;
+ TTestTricky2 *t2 = NULL;
+
+ t1 = g_object_new (T_TEST_TYPE_TRICKY1, NULL);
+ t2 = g_object_new (T_TEST_TYPE_TRICKY2, NULL);
+
+ t2->im_optional = 10;
+ write_to_read (THRIFT_STRUCT (t2), THRIFT_STRUCT (t1), NULL, NULL);
+ write_to_read (THRIFT_STRUCT (t1), THRIFT_STRUCT (t2), NULL, NULL);
+
+ g_assert (t1->__isset_im_default == FALSE);
+ g_assert (t2->__isset_im_optional == TRUE);
+ g_assert (t1->im_default == t2->im_optional);
+ g_assert (t1->im_default == 0);
+
+ g_object_unref (t1);
+ g_object_unref (t2);
+}
+
+/**
+ * Writing between default and required.
+ */
+static void
+test_tricky2 (void)
+{
+ TTestTricky1 *t1 = NULL;
+ TTestTricky3 *t3 = NULL;
+
+ t1 = g_object_new (T_TEST_TYPE_TRICKY1, NULL);
+ t3 = g_object_new (T_TEST_TYPE_TRICKY3, NULL);
+
+ write_to_read (THRIFT_STRUCT (t1), THRIFT_STRUCT (t3), NULL, NULL);
+ write_to_read (THRIFT_STRUCT (t3), THRIFT_STRUCT (t1), NULL, NULL);
+
+ g_assert (t1->__isset_im_default == TRUE);
+
+ g_object_unref (t1);
+ g_object_unref (t3);
+}
+
+/**
+ * Writing between optional and required.
+ */
+static void
+test_tricky3 (void)
+{
+ TTestTricky2 *t2 = NULL;
+ TTestTricky3 *t3 = NULL;
+
+ t2 = g_object_new (T_TEST_TYPE_TRICKY2, NULL);
+ t3 = g_object_new (T_TEST_TYPE_TRICKY3, NULL);
+
+ t2->__isset_im_optional = TRUE;
+
+ write_to_read (THRIFT_STRUCT (t2), THRIFT_STRUCT (t3), NULL, NULL);
+ write_to_read (THRIFT_STRUCT (t3), THRIFT_STRUCT (t2), NULL, NULL);
+
+ g_object_unref (t2);
+ g_object_unref (t3);
+}
+
+/**
+ * Catch an optional not set exception. To quote the
+ * C++ test, "Mu-hu-ha-ha-ha!"
+ */
+static void
+test_tricky4 (void)
+{
+ TTestTricky2 *t2 = NULL;
+ TTestTricky3 *t3 = NULL;
+ GError *read_error = NULL;
+
+ t2 = g_object_new (T_TEST_TYPE_TRICKY2, NULL);
+ t3 = g_object_new (T_TEST_TYPE_TRICKY3, NULL);
+
+ /* throws protocol exception */
+ write_to_read (THRIFT_STRUCT (t2), THRIFT_STRUCT (t3), NULL, &read_error);
+ g_assert (read_error != NULL);
+ g_error_free (read_error);
+
+ write_to_read (THRIFT_STRUCT (t3), THRIFT_STRUCT (t2), NULL, NULL);
+
+ g_assert (t2->__isset_im_optional);
+
+ g_object_unref (t2);
+ g_object_unref (t3);
+}
+
+static void
+test_non_set_binary (void)
+{
+ TTestBinaries *b1 = NULL;
+ TTestBinaries *b2 = NULL;
+ GError *error = NULL;
+
+ b1 = g_object_new (T_TEST_TYPE_BINARIES, NULL);
+ b2 = g_object_new (T_TEST_TYPE_BINARIES, NULL);
+
+ write_to_read (THRIFT_STRUCT (b1), THRIFT_STRUCT (b2), NULL, &error);
+ g_assert(!error);
+ write_to_read (THRIFT_STRUCT (b2), THRIFT_STRUCT (b1), NULL, &error);
+ g_assert(!error);
+ /* OK. No segfault */
+
+ g_object_unref (b1);
+ g_object_unref (b2);
+}
+
+int
+main(int argc, char *argv[])
+{
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init();
+#endif
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/testoptionalrequired/OldSchool", test_old_school1);
+ g_test_add_func ("/testoptionalrequired/Simple", test_simple);
+ g_test_add_func ("/testoptionalrequired/Tricky1", test_tricky1);
+ g_test_add_func ("/testoptionalrequired/Tricky2", test_tricky2);
+ g_test_add_func ("/testoptionalrequired/Tricky3", test_tricky3);
+ g_test_add_func ("/testoptionalrequired/Tricky4", test_tricky4);
+ g_test_add_func ("/testoptionalrequired/Binary", test_non_set_binary);
+
+ return g_test_run ();
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/testserialization.c b/src/jaegertracing/thrift/lib/c_glib/test/testserialization.c
new file mode 100644
index 000000000..67d411de1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/testserialization.c
@@ -0,0 +1,95 @@
+#include <thrift/c_glib/protocol/thrift_binary_protocol.h>
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/transport/thrift_memory_buffer.h>
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include "gen-c_glib/t_test_debug_proto_test_types.h"
+#include "gen-c_glib/t_test_enum_test_types.h"
+
+static void enum_constants_read_write() {
+ GError* error = NULL;
+ ThriftTransport* transport
+ = THRIFT_TRANSPORT(g_object_new(THRIFT_TYPE_MEMORY_BUFFER, "buf_size", 1024, NULL));
+ ThriftProtocol* protocol
+ = THRIFT_PROTOCOL(g_object_new(THRIFT_TYPE_BINARY_PROTOCOL, "transport", transport, NULL));
+ TTestEnumTestStruct* src = T_TEST_ENUM_TEST;
+ TTestEnumTestStruct* dst = g_object_new(T_TEST_TYPE_ENUM_TEST_STRUCT, NULL);
+ TTestEnumTestStructClass* cls = T_TEST_ENUM_TEST_STRUCT_GET_CLASS(src);
+ int write_len;
+ int read_len;
+
+ write_len = THRIFT_STRUCT_CLASS(cls)->write(THRIFT_STRUCT(src), protocol, &error);
+ g_assert(!error);
+ g_assert(write_len > 0);
+
+ read_len = THRIFT_STRUCT_CLASS(cls)->read(THRIFT_STRUCT(dst), protocol, &error);
+ g_assert(!error);
+ g_assert_cmpint(write_len, ==, read_len);
+
+ g_object_unref(dst);
+ g_object_unref(protocol);
+ g_object_unref(transport);
+}
+
+static void struct_constants_read_write() {
+ GError* error = NULL;
+ ThriftTransport* transport
+ = THRIFT_TRANSPORT(g_object_new(THRIFT_TYPE_MEMORY_BUFFER, "buf_size", 4096, NULL));
+ ThriftProtocol* protocol
+ = THRIFT_PROTOCOL(g_object_new(THRIFT_TYPE_BINARY_PROTOCOL, "transport", transport, NULL));
+ TTestCompactProtoTestStruct* src = T_TEST_COMPACT_TEST;
+ TTestCompactProtoTestStruct* dst = g_object_new(T_TEST_TYPE_COMPACT_PROTO_TEST_STRUCT, NULL);
+ TTestCompactProtoTestStructClass* cls = T_TEST_COMPACT_PROTO_TEST_STRUCT_GET_CLASS(src);
+ int write_len;
+ int read_len;
+
+ write_len = THRIFT_STRUCT_CLASS(cls)->write(THRIFT_STRUCT(src), protocol, &error);
+ g_assert(!error);
+ g_assert(write_len > 0);
+
+ read_len = THRIFT_STRUCT_CLASS(cls)->read(THRIFT_STRUCT(dst), protocol, &error);
+ g_assert(!error);
+ g_assert_cmpint(write_len, ==, read_len);
+
+ g_object_unref(dst);
+ g_object_unref(protocol);
+ g_object_unref(transport);
+}
+
+static void struct_read_write_length_should_equal() {
+ GError* error = NULL;
+ ThriftTransport* transport
+ = THRIFT_TRANSPORT(g_object_new(THRIFT_TYPE_MEMORY_BUFFER, "buf_size", 2048, NULL));
+ ThriftProtocol* protocol
+ = THRIFT_PROTOCOL(g_object_new(THRIFT_TYPE_BINARY_PROTOCOL, "transport", transport, NULL));
+ TTestBonk* src = g_object_new(T_TEST_TYPE_BONK, NULL);
+ TTestBonk* dst = g_object_new(T_TEST_TYPE_BONK, NULL);
+ TTestBonkClass* cls = T_TEST_BONK_GET_CLASS(src);
+ int write_len;
+ int read_len;
+
+ write_len = THRIFT_STRUCT_CLASS(cls)->write(THRIFT_STRUCT(src), protocol, &error);
+ g_assert(!error);
+ g_assert(write_len > 0);
+
+ read_len = THRIFT_STRUCT_CLASS(cls)->read(THRIFT_STRUCT(dst), protocol, &error);
+ g_assert(!error);
+ g_assert_cmpint(write_len, ==, read_len);
+
+ g_object_unref(dst);
+ g_object_unref(src);
+ g_object_unref(protocol);
+ g_object_unref(transport);
+}
+
+int main(int argc, char* argv[]) {
+#if (!GLIB_CHECK_VERSION(2, 36, 0))
+ g_type_init();
+#endif
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/testserialization/StructReadWriteLengthShouldEqual",
+ struct_read_write_length_should_equal);
+ g_test_add_func("/testserialization/StructConstants", struct_constants_read_write);
+ g_test_add_func("/testserialization/EnumConstants", enum_constants_read_write);
+ return g_test_run();
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/testsimpleserver.c b/src/jaegertracing/thrift/lib/c_glib/test/testsimpleserver.c
new file mode 100755
index 000000000..3c6f2e80d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/testsimpleserver.c
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <glib.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/processor/thrift_processor.h>
+#include <thrift/c_glib/transport/thrift_server_socket.h>
+
+#define TEST_PORT 51199
+
+#include <thrift/c_glib/server/thrift_simple_server.c>
+
+/* create a rudimentary processor */
+#define TEST_PROCESSOR_TYPE (test_processor_get_type ())
+
+struct _TestProcessor
+{
+ ThriftProcessor parent;
+};
+typedef struct _TestProcessor TestProcessor;
+
+struct _TestProcessorClass
+{
+ ThriftProcessorClass parent;
+};
+typedef struct _TestProcessorClass TestProcessorClass;
+
+G_DEFINE_TYPE(TestProcessor, test_processor, THRIFT_TYPE_PROCESSOR)
+
+gboolean
+test_processor_process (ThriftProcessor *processor, ThriftProtocol *in,
+ ThriftProtocol *out, GError **error)
+{
+ THRIFT_UNUSED_VAR (processor);
+ THRIFT_UNUSED_VAR (in);
+ THRIFT_UNUSED_VAR (out);
+ THRIFT_UNUSED_VAR (error);
+
+ return FALSE;
+}
+
+static void
+test_processor_init (TestProcessor *p)
+{
+ THRIFT_UNUSED_VAR (p);
+}
+
+static void
+test_processor_class_init (TestProcessorClass *proc)
+{
+ (THRIFT_PROCESSOR_CLASS(proc))->process = test_processor_process;
+}
+
+static void
+test_server (void)
+{
+ int status;
+ pid_t pid;
+ TestProcessor *p = NULL;
+ ThriftServerSocket *tss = NULL;
+ ThriftSimpleServer *ss = NULL;
+
+ p = g_object_new (TEST_PROCESSOR_TYPE, NULL);
+ tss = g_object_new (THRIFT_TYPE_SERVER_SOCKET, "port", TEST_PORT, NULL);
+ ss = g_object_new (THRIFT_TYPE_SIMPLE_SERVER, "processor", p,
+ "server_transport", THRIFT_SERVER_TRANSPORT (tss), NULL);
+
+ /* run the server in a child process */
+ pid = fork ();
+ g_assert (pid >= 0);
+
+ if (pid == 0)
+ {
+ THRIFT_SERVER_GET_CLASS (THRIFT_SERVER (ss))->serve (THRIFT_SERVER (ss),
+ NULL);
+ exit (0);
+ } else {
+ sleep (5);
+ kill (pid, SIGINT);
+
+ g_object_unref (ss);
+ g_object_unref (tss);
+ g_object_unref (p);
+ g_assert (wait (&status) == pid);
+ g_assert (status == SIGINT);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init();
+#endif
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/testsimpleserver/SimpleServer", test_server);
+
+ return g_test_run ();
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/teststruct.c b/src/jaegertracing/thrift/lib/c_glib/test/teststruct.c
new file mode 100755
index 000000000..d120cbcfa
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/teststruct.c
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <glib-object.h>
+
+#include "../src/thrift/c_glib/thrift_struct.c"
+
+/* tests to ensure we can extend a ThriftStruct */
+
+struct _ThriftTestStruct
+{
+ ThriftStruct parent;
+};
+typedef struct _ThriftTestStruct ThriftTestStruct;
+
+struct _ThriftTestStructClass
+{
+ ThriftStructClass parent;
+};
+typedef struct _ThriftTestStructClass ThriftTestStructClass;
+
+GType thrift_test_struct_get_type (void);
+
+#define THRIFT_TYPE_TEST_STRUCT (thrift_test_struct_get_type ())
+#define THRIFT_TEST_STRUCT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_TEST_STRUCT, ThriftTestStruct))
+#define THRIFT_TEST_STRUCT_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_TEST_STRUCT, ThriftTestStructClass))
+#define THRIFT_IS_TEST_STRUCT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_TEST_STRUCT))
+#define THRIFT_IS_TEST_STRUCT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_TEST_STRUCT))
+#define THRIFT_TEST_STRUCT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_TEST_STRUCT, ThriftTestStructClass))
+
+G_DEFINE_TYPE(ThriftTestStruct, thrift_test_struct, THRIFT_TYPE_STRUCT)
+
+gint32
+thrift_test_struct_read (ThriftStruct *object, ThriftProtocol *protocol,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (object);
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+
+ return 0;
+}
+
+gint32
+thrift_test_struct_write (ThriftStruct *object, ThriftProtocol *protocol,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (object);
+ THRIFT_UNUSED_VAR (protocol);
+ THRIFT_UNUSED_VAR (error);
+
+ return 0;
+}
+
+static void
+thrift_test_struct_class_init (ThriftTestStructClass *cls)
+{
+ ThriftStructClass *ts_cls = THRIFT_STRUCT_CLASS (cls);
+ ts_cls->read = thrift_test_struct_read;
+ ts_cls->write = thrift_test_struct_write;
+}
+
+static void
+thrift_test_struct_init (ThriftTestStruct *s)
+{
+ THRIFT_UNUSED_VAR (s);
+}
+
+static void
+test_initialize_object (void)
+{
+ ThriftTestStruct *t = NULL;
+
+ t = g_object_new (THRIFT_TYPE_TEST_STRUCT, NULL);
+ g_assert ( THRIFT_IS_STRUCT (t));
+ thrift_struct_read (THRIFT_STRUCT (t), NULL, NULL);
+ thrift_struct_write (THRIFT_STRUCT (t), NULL, NULL);
+ thrift_test_struct_read (THRIFT_STRUCT (t), NULL, NULL);
+ thrift_test_struct_write (THRIFT_STRUCT (t), NULL, NULL);
+ g_object_unref (t);
+}
+
+int
+main(int argc, char *argv[])
+{
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init();
+#endif
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/teststruct/InitializeObject", test_initialize_object);
+
+ return g_test_run ();
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/testthrifttest.c b/src/jaegertracing/thrift/lib/c_glib/test/testthrifttest.c
new file mode 100755
index 000000000..23a934db9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/testthrifttest.c
@@ -0,0 +1,112 @@
+#include <netdb.h>
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/transport/thrift_server_transport.h>
+#include <thrift/c_glib/transport/thrift_server_socket.h>
+
+#include "t_test_thrift_test_types.h"
+#include "thrift_test_handler.h"
+
+static const char TEST_ADDRESS[] = "localhost";
+static const int TEST_PORT = 64444;
+
+static void
+test_thrift_server (void)
+{
+ ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+ "port", TEST_PORT, NULL);
+
+ g_object_unref (tsocket);
+}
+
+static void
+set_indicator (gpointer data, GObject *where_the_object_was) {
+ THRIFT_UNUSED_VAR(where_the_object_was);
+
+ *(gboolean *) data = TRUE;
+}
+
+static void
+test_thrift_handler (void)
+{
+ GError *error;
+ GHashTable *_return;
+ TTestInsanity *argument;
+ gboolean indicator;
+
+ TTestXtruct *xtruct, *xtruct2;
+ TTestNumberz numberz;
+ TTestNumberz numberz2;
+ TTestUserId user_id, *user_id_ptr, *user_id_ptr2;
+ GHashTable *user_map;
+ GPtrArray *xtructs;
+
+ error = NULL;
+ indicator = FALSE;
+
+ user_map = NULL;
+ xtructs = NULL;
+
+ argument = g_object_new (T_TEST_TYPE_INSANITY, NULL);
+ g_object_get (argument,
+ "userMap", &user_map,
+ "xtructs", &xtructs,
+ NULL);
+
+ numberz = T_TEST_NUMBERZ_FIVE;
+ numberz2 = T_TEST_NUMBERZ_EIGHT;
+ user_id_ptr = g_malloc (sizeof *user_id_ptr);
+ *user_id_ptr = 5;
+ user_id_ptr2 = g_malloc (sizeof *user_id_ptr);
+ *user_id_ptr2 = 8;
+ g_hash_table_insert (user_map, (gpointer)numberz, user_id_ptr);
+ g_hash_table_insert (user_map, (gpointer)numberz2, user_id_ptr2);
+ g_hash_table_unref (user_map);
+
+ xtruct = g_object_new (T_TEST_TYPE_XTRUCT,
+ "string_thing", "Hello2",
+ "byte_thing", 2,
+ "i32_thing", 2,
+ "i64_thing", 2LL,
+ NULL);
+ xtruct2 = g_object_new (T_TEST_TYPE_XTRUCT,
+ "string_thing", "Goodbye4",
+ "byte_thing", 4,
+ "i32_thing", 4,
+ "i64_thing", 4LL,
+ NULL);
+ g_ptr_array_add (xtructs, xtruct2);
+ g_ptr_array_add (xtructs, xtruct);
+ g_ptr_array_unref (xtructs);
+
+ _return = g_hash_table_new_full (g_int64_hash,
+ g_int64_equal,
+ g_free,
+ (GDestroyNotify)g_hash_table_unref);
+
+ g_object_weak_ref (G_OBJECT (argument), set_indicator, (gpointer) &indicator);
+
+ g_assert (thrift_test_handler_test_insanity (NULL, &_return, argument, &error));
+ g_assert (! indicator);
+
+ g_hash_table_unref (_return);
+ g_assert (! indicator);
+
+ g_object_unref (argument);
+ g_assert (indicator);
+}
+
+int
+main(int argc, char *argv[])
+{
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init();
+#endif
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/testthrift/Server", test_thrift_server);
+ g_test_add_func ("/testthrift/Handler", test_thrift_handler);
+
+ return g_test_run ();
+}
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/testthrifttestclient.cpp b/src/jaegertracing/thrift/lib/c_glib/test/testthrifttestclient.cpp
new file mode 100644
index 000000000..20fbcdb03
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/testthrifttestclient.cpp
@@ -0,0 +1,639 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* test a C client with a C++ server (that makes sense...) */
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/protocol/TDebugProtocol.h>
+#include <thrift/server/TSimpleServer.h>
+#include <memory>
+#include <thrift/transport/TServerSocket.h>
+#include "ThriftTest.h"
+#include "ThriftTest_types.h"
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+using namespace apache::thrift;
+using namespace apache::thrift::concurrency;
+using namespace apache::thrift::protocol;
+using namespace apache::thrift::server;
+using namespace apache::thrift::transport;
+
+using namespace thrift::test;
+
+using std::cout;
+using std::endl;
+using std::fixed;
+using std::make_pair;
+using std::map;
+using std::set;
+using std::string;
+using std::vector;
+
+#define TEST_PORT 9980
+
+// Extra functions required for ThriftTest_types to work
+namespace thrift { namespace test {
+
+bool Insanity::operator<(thrift::test::Insanity const& other) const {
+ using apache::thrift::ThriftDebugString;
+ return ThriftDebugString(*this) < ThriftDebugString(other);
+}
+
+}}
+
+class TestHandler : public ThriftTestIf {
+ public:
+ TestHandler() = default;
+
+ void testVoid() override {
+ cout << "[C -> C++] testVoid()" << endl;
+ }
+
+ void testString(string& out, const string &thing) override {
+ cout << "[C -> C++] testString(\"" << thing << "\")" << endl;
+ out = thing;
+ }
+
+ bool testBool(const bool thing) override {
+ cout << "[C -> C++] testBool(" << (thing ? "true" : "false") << ")" << endl;
+ return thing;
+ }
+ int8_t testByte(const int8_t thing) override {
+ cout << "[C -> C++] testByte(" << (int)thing << ")" << endl;
+ return thing;
+ }
+ int32_t testI32(const int32_t thing) override {
+ cout << "[C -> C++] testI32(" << thing << ")" << endl;
+ return thing;
+ }
+
+ int64_t testI64(const int64_t thing) override {
+ cout << "[C -> C++] testI64(" << thing << ")" << endl;
+ return thing;
+ }
+
+ double testDouble(const double thing) override {
+ cout.precision(6);
+ cout << "[C -> C++] testDouble(" << fixed << thing << ")" << endl;
+ return thing;
+ }
+
+ void testBinary(string& out, const string &thing) override {
+ cout << "[C -> C++] testBinary(\"" << thing << "\")" << endl;
+ out = thing;
+ }
+
+ void testStruct(Xtruct& out, const Xtruct &thing) override {
+ cout << "[C -> C++] testStruct({\"" << thing.string_thing << "\", " << (int)thing.byte_thing << ", " << thing.i32_thing << ", " << thing.i64_thing << "})" << endl;
+ out = thing;
+ }
+
+ void testNest(Xtruct2& out, const Xtruct2& nest) override {
+ const Xtruct &thing = nest.struct_thing;
+ cout << "[C -> C++] testNest({" << (int)nest.byte_thing << ", {\"" << thing.string_thing << "\", " << (int)thing.byte_thing << ", " << thing.i32_thing << ", " << thing.i64_thing << "}, " << nest.i32_thing << "})" << endl;
+ out = nest;
+ }
+
+ void testMap(map<int32_t, int32_t> &out, const map<int32_t, int32_t> &thing) override {
+ cout << "[C -> C++] testMap({";
+ map<int32_t, int32_t>::const_iterator m_iter;
+ bool first = true;
+ for (m_iter = thing.begin(); m_iter != thing.end(); ++m_iter) {
+ if (first) {
+ first = false;
+ } else {
+ cout << ", ";
+ }
+ cout << m_iter->first << " => " << m_iter->second;
+ }
+ cout << "})" << endl;
+ out = thing;
+ }
+
+ void testStringMap(map<std::string, std::string> &out, const map<std::string, std::string> &thing) override {
+ cout << "[C -> C++] testStringMap({";
+ map<std::string, std::string>::const_iterator m_iter;
+ bool first = true;
+ for (m_iter = thing.begin(); m_iter != thing.end(); ++m_iter) {
+ if (first) {
+ first = false;
+ } else {
+ cout << ", ";
+ }
+ cout << "\"" << m_iter->first << "\" => \"" << m_iter->second << "\"";
+ }
+ cout << "})" << endl;
+ out = thing;
+ }
+
+
+ void testSet(set<int32_t> &out, const set<int32_t> &thing) override {
+ cout << "[C -> C++] testSet({";
+ set<int32_t>::const_iterator s_iter;
+ bool first = true;
+ for (s_iter = thing.begin(); s_iter != thing.end(); ++s_iter) {
+ if (first) {
+ first = false;
+ } else {
+ cout << ", ";
+ }
+ cout << *s_iter;
+ }
+ cout << "})" << endl;
+ out = thing;
+ }
+
+ void testList(vector<int32_t> &out, const vector<int32_t> &thing) override {
+ cout << "[C -> C++] testList({";
+ vector<int32_t>::const_iterator l_iter;
+ bool first = true;
+ for (l_iter = thing.begin(); l_iter != thing.end(); ++l_iter) {
+ if (first) {
+ first = false;
+ } else {
+ cout << ", ";
+ }
+ cout << *l_iter;
+ }
+ cout << "})" << endl;
+ out = thing;
+ }
+
+ Numberz::type testEnum(const Numberz::type thing) override {
+ cout << "[C -> C++] testEnum(" << thing << ")" << endl;
+ return thing;
+ }
+
+ UserId testTypedef(const UserId thing) override {
+ cout << "[C -> C++] testTypedef(" << thing << ")" << endl;
+ return thing; }
+
+ void testMapMap(map<int32_t, map<int32_t,int32_t> > &mapmap, const int32_t hello) override {
+ cout << "[C -> C++] testMapMap(" << hello << ")" << endl;
+
+ map<int32_t,int32_t> pos;
+ map<int32_t,int32_t> neg;
+ for (int i = 1; i < 5; i++) {
+ pos.insert(make_pair(i,i));
+ neg.insert(make_pair(-i,-i));
+ }
+
+ mapmap.insert(make_pair(4, pos));
+ mapmap.insert(make_pair(-4, neg));
+
+ }
+
+ void testInsanity(map<UserId, map<Numberz::type,Insanity> > &insane, const Insanity &argument) override {
+ THRIFT_UNUSED_VARIABLE (argument);
+
+ cout << "[C -> C++] testInsanity()" << endl;
+
+ Xtruct hello;
+ hello.string_thing = "Hello2";
+ hello.byte_thing = 2;
+ hello.i32_thing = 2;
+ hello.i64_thing = 2;
+
+ Xtruct goodbye;
+ goodbye.string_thing = "Goodbye4";
+ goodbye.byte_thing = 4;
+ goodbye.i32_thing = 4;
+ goodbye.i64_thing = 4;
+
+ Insanity crazy;
+ crazy.userMap.insert(make_pair(Numberz::EIGHT, 8));
+ crazy.xtructs.push_back(goodbye);
+
+ Insanity looney;
+ crazy.userMap.insert(make_pair(Numberz::FIVE, 5));
+ crazy.xtructs.push_back(hello);
+
+ map<Numberz::type, Insanity> first_map;
+ map<Numberz::type, Insanity> second_map;
+
+ first_map.insert(make_pair(Numberz::TWO, crazy));
+ first_map.insert(make_pair(Numberz::THREE, crazy));
+
+ second_map.insert(make_pair(Numberz::SIX, looney));
+
+ insane.insert(make_pair(1, first_map));
+ insane.insert(make_pair(2, second_map));
+
+ cout << "return = {";
+ map<UserId, map<Numberz::type,Insanity> >::const_iterator i_iter;
+ for (i_iter = insane.begin(); i_iter != insane.end(); ++i_iter) {
+ cout << i_iter->first << " => {";
+ map<Numberz::type,Insanity>::const_iterator i2_iter;
+ for (i2_iter = i_iter->second.begin();
+ i2_iter != i_iter->second.end();
+ ++i2_iter) {
+ cout << i2_iter->first << " => {";
+ map<Numberz::type, UserId> userMap = i2_iter->second.userMap;
+ map<Numberz::type, UserId>::const_iterator um;
+ cout << "{";
+ for (um = userMap.begin(); um != userMap.end(); ++um) {
+ cout << um->first << " => " << um->second << ", ";
+ }
+ cout << "}, ";
+
+ vector<Xtruct> xtructs = i2_iter->second.xtructs;
+ vector<Xtruct>::const_iterator x;
+ cout << "{";
+ for (x = xtructs.begin(); x != xtructs.end(); ++x) {
+ cout << "{\"" << x->string_thing << "\", " << (int)x->byte_thing << ", " << x->i32_thing << ", " << x->i64_thing << "}, ";
+ }
+ cout << "}";
+
+ cout << "}, ";
+ }
+ cout << "}, ";
+ }
+ cout << "}" << endl;
+
+
+ }
+
+ void testMulti(Xtruct &hello, const int8_t arg0, const int32_t arg1, const int64_t arg2, const std::map<int16_t, std::string> &arg3, const Numberz::type arg4, const UserId arg5) override {
+ THRIFT_UNUSED_VARIABLE (arg3);
+ THRIFT_UNUSED_VARIABLE (arg4);
+ THRIFT_UNUSED_VARIABLE (arg5);
+
+ cout << "[C -> C++] testMulti()" << endl;
+
+ hello.string_thing = "Hello2";
+ hello.byte_thing = arg0;
+ hello.i32_thing = arg1;
+ hello.i64_thing = (int64_t)arg2;
+ }
+
+ void testException(const std::string &arg)
+ throw(Xception, apache::thrift::TException) override
+ {
+ cout << "[C -> C++] testException(" << arg << ")" << endl;
+ if (arg.compare("Xception") == 0) {
+ Xception e;
+ e.errorCode = 1001;
+ e.message = arg;
+ throw e;
+ } else if (arg.compare("ApplicationException") == 0) {
+ apache::thrift::TException e;
+ throw e;
+ } else {
+ Xtruct result;
+ result.string_thing = arg;
+ return;
+ }
+ }
+
+ void testMultiException(Xtruct &result, const std::string &arg0, const std::string &arg1) throw(Xception, Xception2) override {
+
+ cout << "[C -> C++] testMultiException(" << arg0 << ", " << arg1 << ")" << endl;
+
+ if (arg0.compare("Xception") == 0) {
+ Xception e;
+ e.errorCode = 1001;
+ e.message = "This is an Xception";
+ throw e;
+ } else if (arg0.compare("Xception2") == 0) {
+ Xception2 e;
+ e.errorCode = 2002;
+ e.struct_thing.string_thing = "This is an Xception2";
+ throw e;
+ } else {
+ result.string_thing = arg1;
+ return;
+ }
+ }
+
+ void testOneway(int sleepFor) override {
+ cout << "testOneway(" << sleepFor << "): Sleeping..." << endl;
+ sleep(sleepFor);
+ cout << "testOneway(" << sleepFor << "): done sleeping!" << endl;
+ }
+};
+
+// C CLIENT
+extern "C" {
+
+#undef THRIFT_SOCKET /* from lib/cpp */
+
+#include "t_test_thrift_test.h"
+#include "t_test_thrift_test_types.h"
+#include <thrift/c_glib/transport/thrift_socket.h>
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/protocol/thrift_binary_protocol.h>
+
+static void
+test_thrift_client (void)
+{
+ ThriftSocket *tsocket = nullptr;
+ ThriftBinaryProtocol *protocol = nullptr;
+ TTestThriftTestClient *client = nullptr;
+ TTestThriftTestIf *iface = nullptr;
+ GError *error = nullptr;
+ gchar *string = nullptr;
+ gint8 byte = 0;
+ gint16 i16 = 0;
+ gint32 i32 = 0, another_i32 = 56789;
+ gint64 i64 = 0;
+ double dbl = 0.0;
+ TTestXtruct *xtruct_in, *xtruct_out;
+ TTestXtruct2 *xtruct2_in, *xtruct2_out;
+ GHashTable *map_in = nullptr, *map_out = nullptr;
+ GHashTable *set_in = nullptr, *set_out = nullptr;
+ GArray *list_in = nullptr, *list_out = nullptr;
+ TTestNumberz enum_in, enum_out;
+ TTestUserId user_id_in, user_id_out;
+ GHashTable *insanity_in = nullptr;
+ TTestXtruct *xtruct1, *xtruct2;
+ TTestInsanity *insanity_out = nullptr;
+ TTestXtruct *multi_in = nullptr;
+ GHashTable *multi_map_out = nullptr;
+ TTestXception *xception = nullptr;
+ TTestXception2 *xception2 = nullptr;
+
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ // initialize gobject
+ g_type_init ();
+#endif
+
+ // create a C client
+ tsocket = (ThriftSocket *) g_object_new (THRIFT_TYPE_SOCKET,
+ "hostname", "localhost",
+ "port", TEST_PORT, NULL);
+ protocol = (ThriftBinaryProtocol *) g_object_new (THRIFT_TYPE_BINARY_PROTOCOL,
+ "transport",
+ tsocket, NULL);
+ client = (TTestThriftTestClient *) g_object_new (T_TEST_TYPE_THRIFT_TEST_CLIENT, "input_protocol", protocol, "output_protocol", protocol, NULL);
+ iface = T_TEST_THRIFT_TEST_IF (client);
+
+ // open and send
+ thrift_transport_open (THRIFT_TRANSPORT(tsocket), nullptr);
+
+ assert (t_test_thrift_test_client_test_void (iface, &error) == TRUE);
+ assert (error == nullptr);
+
+ assert (t_test_thrift_test_client_test_string (iface, &string, "test123", &error) == TRUE);
+ assert (strcmp (string, "test123") == 0);
+ g_free (string);
+ assert (error == nullptr);
+
+ assert (t_test_thrift_test_client_test_byte (iface, &byte, (gint8) 5, &error) == TRUE);
+ assert (byte == 5);
+ assert (error == nullptr);
+
+ assert (t_test_thrift_test_client_test_i32 (iface, &i32, 123, &error) == TRUE);
+ assert (i32 == 123);
+ assert (error == nullptr);
+
+ assert (t_test_thrift_test_client_test_i64 (iface, &i64, 12345, &error) == TRUE);
+ assert (i64 == 12345);
+ assert (error == nullptr);
+
+ assert (t_test_thrift_test_client_test_double (iface, &dbl, 5.6, &error) == TRUE);
+ assert (dbl == 5.6);
+ assert (error == nullptr);
+
+ xtruct_out = (TTestXtruct *) g_object_new (T_TEST_TYPE_XTRUCT, nullptr);
+ xtruct_out->byte_thing = 1;
+ xtruct_out->__isset_byte_thing = TRUE;
+ xtruct_out->i32_thing = 15;
+ xtruct_out->__isset_i32_thing = TRUE;
+ xtruct_out->i64_thing = 151;
+ xtruct_out->__isset_i64_thing = TRUE;
+ xtruct_out->string_thing = g_strdup ("abc123");
+ xtruct_out->__isset_string_thing = TRUE;
+ xtruct_in = (TTestXtruct *) g_object_new(T_TEST_TYPE_XTRUCT, nullptr);
+ assert (t_test_thrift_test_client_test_struct (iface, &xtruct_in, xtruct_out, &error) == TRUE);
+ assert (error == nullptr);
+
+ xtruct2_out = (TTestXtruct2 *) g_object_new (T_TEST_TYPE_XTRUCT2, nullptr);
+ xtruct2_out->byte_thing = 1;
+ xtruct2_out->__isset_byte_thing = TRUE;
+ if (xtruct2_out->struct_thing != nullptr)
+ g_object_unref(xtruct2_out->struct_thing);
+ xtruct2_out->struct_thing = xtruct_out;
+ xtruct2_out->__isset_struct_thing = TRUE;
+ xtruct2_out->i32_thing = 123;
+ xtruct2_out->__isset_i32_thing = TRUE;
+ xtruct2_in = (TTestXtruct2 *) g_object_new (T_TEST_TYPE_XTRUCT2, nullptr);
+ assert (t_test_thrift_test_client_test_nest (iface, &xtruct2_in, xtruct2_out, &error) == TRUE);
+ assert (error == nullptr);
+
+ g_object_unref (xtruct2_out);
+ g_object_unref (xtruct2_in);
+ g_object_unref (xtruct_in);
+
+ map_out = g_hash_table_new (nullptr, nullptr);
+ map_in = g_hash_table_new (nullptr, nullptr); g_hash_table_insert (map_out, &i32, &i32);
+ assert (t_test_thrift_test_client_test_map (iface, &map_in, map_out, &error) == TRUE);
+ assert (error == nullptr);
+ g_hash_table_destroy (map_out);
+ g_hash_table_destroy (map_in);
+
+ map_out = g_hash_table_new (nullptr, nullptr);
+ map_in = g_hash_table_new (nullptr, nullptr);
+ g_hash_table_insert (map_out, g_strdup ("a"), g_strdup ("123"));
+ g_hash_table_insert (map_out, g_strdup ("a b"), g_strdup ("with spaces "));
+ g_hash_table_insert (map_out, g_strdup ("same"), g_strdup ("same"));
+ g_hash_table_insert (map_out, g_strdup ("0"), g_strdup ("numeric key"));
+ assert (t_test_thrift_test_client_test_string_map (iface, &map_in, map_out, &error) == TRUE);
+ assert (error == nullptr);
+ g_hash_table_destroy (map_out);
+ g_hash_table_destroy (map_in);
+
+ set_out = g_hash_table_new (nullptr, nullptr);
+ set_in = g_hash_table_new (nullptr, nullptr);
+ g_hash_table_insert (set_out, &i32, &i32);
+ assert (t_test_thrift_test_client_test_set (iface, &set_in, set_out, &error) == TRUE);
+ assert (error == nullptr);
+ g_hash_table_destroy (set_out);
+ g_hash_table_destroy (set_in);
+
+ list_out = g_array_new(TRUE, TRUE, sizeof(gint32));
+ list_in = g_array_new(TRUE, TRUE, sizeof(gint32));
+ another_i32 = 456;
+ g_array_append_val (list_out, i32);
+ g_array_append_val (list_out, another_i32);
+ assert (t_test_thrift_test_client_test_list (iface, &list_in, list_out, &error) == TRUE);
+ assert (error == nullptr);
+ g_array_free (list_out, TRUE);
+ g_array_free (list_in, TRUE);
+
+ enum_out = T_TEST_NUMBERZ_ONE;
+ assert (t_test_thrift_test_client_test_enum (iface, &enum_in, enum_out, &error) == TRUE);
+ assert (enum_in == enum_out);
+ assert (error == nullptr);
+
+ user_id_out = 12345;
+ assert (t_test_thrift_test_client_test_typedef (iface, &user_id_in, user_id_out, &error) == TRUE);
+ assert (user_id_in == user_id_out);
+ assert (error == nullptr);
+
+ map_in = g_hash_table_new (nullptr, nullptr);
+ assert (t_test_thrift_test_client_test_map_map (iface, &map_in, i32, &error) == TRUE);
+ assert (error == nullptr);
+ g_hash_table_destroy (map_in);
+
+ // insanity
+ insanity_out = (TTestInsanity *) g_object_new (T_TEST_TYPE_INSANITY, nullptr);
+ insanity_out->userMap = g_hash_table_new (nullptr, nullptr);
+ g_hash_table_insert (insanity_out->userMap, GINT_TO_POINTER (enum_out), &user_id_out);
+
+ xtruct1 = (TTestXtruct *) g_object_new (T_TEST_TYPE_XTRUCT, nullptr);
+ xtruct1->byte_thing = 1;
+ xtruct1->__isset_byte_thing = TRUE;
+ xtruct1->i32_thing = 15;
+ xtruct1->__isset_i32_thing = TRUE;
+ xtruct1->i64_thing = 151;
+ xtruct1->__isset_i64_thing = TRUE;
+ xtruct1->string_thing = g_strdup ("abc123");
+ xtruct1->__isset_string_thing = TRUE;
+ xtruct2 = (TTestXtruct *) g_object_new (T_TEST_TYPE_XTRUCT, nullptr);
+ xtruct2->byte_thing = 1;
+ xtruct2->__isset_byte_thing = TRUE;
+ xtruct2->i32_thing = 15;
+ xtruct2->__isset_i32_thing = TRUE;
+ xtruct2->i64_thing = 151;
+ xtruct2->__isset_i64_thing = TRUE;
+ xtruct2->string_thing = g_strdup ("abc123");
+ xtruct2->__isset_string_thing = TRUE;
+
+ insanity_in = g_hash_table_new (nullptr, nullptr);
+ g_ptr_array_add (insanity_out->xtructs, xtruct1);
+ g_ptr_array_add (insanity_out->xtructs, xtruct2);
+ assert (t_test_thrift_test_client_test_insanity (iface, &insanity_in, insanity_out, &error) == TRUE);
+
+ g_hash_table_unref (insanity_in);
+ g_ptr_array_free (insanity_out->xtructs, TRUE);
+
+ multi_map_out = g_hash_table_new (nullptr, nullptr);
+ string = g_strdup ("abc123");
+ g_hash_table_insert (multi_map_out, &i16, string);
+ multi_in = (TTestXtruct *) g_object_new (T_TEST_TYPE_XTRUCT, nullptr);
+ assert (t_test_thrift_test_client_test_multi (iface, &multi_in, byte, i32, i64, multi_map_out, enum_out, user_id_out, &error) == TRUE);
+ assert (multi_in->i32_thing == i32);
+ assert (multi_in->i64_thing == i64);
+ g_object_unref (multi_in);
+ g_hash_table_unref (multi_map_out);
+ g_free (string);
+
+ assert (t_test_thrift_test_client_test_exception (iface, "Xception", &xception, &error) == FALSE);
+ assert (xception->errorCode == 1001);
+ g_error_free (error);
+ error = nullptr;
+ g_object_unref (xception);
+ xception = nullptr;
+
+ assert (t_test_thrift_test_client_test_exception (iface, "ApplicationException", &xception, &error) == FALSE);
+ g_error_free (error);
+ error = nullptr;
+ assert (xception == nullptr);
+
+ assert (t_test_thrift_test_client_test_exception (iface, "Test", &xception, &error) == TRUE);
+ assert (error == nullptr);
+
+ multi_in = (TTestXtruct*) g_object_new (T_TEST_TYPE_XTRUCT, nullptr);
+ assert (t_test_thrift_test_client_test_multi_exception (iface, &multi_in, "Xception", nullptr, &xception, &xception2, &error) == FALSE);
+ assert (xception->errorCode == 1001);
+ assert (xception2 == nullptr);
+ g_error_free (error);
+ error = nullptr;
+ g_object_unref (xception);
+ g_object_unref (multi_in);
+ xception = nullptr;
+ multi_in = nullptr;
+
+ multi_in = (TTestXtruct*) g_object_new (T_TEST_TYPE_XTRUCT, nullptr);
+ assert (t_test_thrift_test_client_test_multi_exception (iface, &multi_in, "Xception2", nullptr, &xception, &xception2, &error) == FALSE);
+ assert (xception2->errorCode == 2002);
+ assert (xception == nullptr);
+ g_error_free (error);
+ error = nullptr;
+ g_object_unref (xception2);
+ g_object_unref (multi_in);
+ xception2 = nullptr;
+ multi_in = nullptr;
+
+ multi_in = (TTestXtruct*) g_object_new (T_TEST_TYPE_XTRUCT, nullptr);
+ assert (t_test_thrift_test_client_test_multi_exception (iface, &multi_in, nullptr , nullptr, &xception, &xception2, &error) == TRUE);
+ assert (error == nullptr);
+ g_object_unref(multi_in);
+ multi_in = nullptr;
+
+ assert (t_test_thrift_test_client_test_oneway (iface, 1, &error) == TRUE);
+ assert (error == nullptr);
+
+ /* sleep to let the oneway call go through */
+ sleep (5);
+
+ thrift_transport_close (THRIFT_TRANSPORT(tsocket), nullptr);
+ g_object_unref (client);
+ g_object_unref (protocol);
+ g_object_unref (tsocket);
+}
+
+
+} /* extern "C" */
+
+
+static void
+bailout (int signum)
+{
+ THRIFT_UNUSED_VARIABLE (signum);
+
+ exit (1);
+}
+
+int
+main (void)
+{
+ int status;
+ int pid = fork ();
+ assert (pid >= 0);
+
+ if (pid == 0) /* child */
+ {
+ std::shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
+ std::shared_ptr<TestHandler> testHandler(new TestHandler());
+ std::shared_ptr<ThriftTestProcessor> testProcessor(new ThriftTestProcessor(testHandler));
+ std::shared_ptr<TServerSocket> serverSocket(new TServerSocket(TEST_PORT));
+ std::shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
+ TSimpleServer simpleServer(testProcessor, serverSocket, transportFactory, protocolFactory);
+ signal (SIGALRM, bailout);
+ alarm (60);
+ simpleServer.serve();
+ } else {
+ sleep (1);
+ test_thrift_client ();
+ kill (pid, SIGINT);
+ assert (wait (&status) == pid);
+ }
+
+ return 0;
+}
+
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/testtransportsocket.c b/src/jaegertracing/thrift/lib/c_glib/test/testtransportsocket.c
new file mode 100755
index 000000000..89c61b910
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/testtransportsocket.c
@@ -0,0 +1,361 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <netdb.h>
+#include <sys/wait.h>
+
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/transport/thrift_buffered_transport.h>
+#include <thrift/c_glib/transport/thrift_server_transport.h>
+#include <thrift/c_glib/transport/thrift_server_socket.h>
+
+#define TEST_DATA { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }
+
+/* substituted functions to test failures of system and library calls */
+static int socket_error = 0;
+int
+my_socket(int domain, int type, int protocol)
+{
+ if (socket_error == 0)
+ {
+ return socket (domain, type, protocol);
+ }
+ return -1;
+}
+
+static int recv_error = 0;
+ssize_t
+my_recv(int socket, void *buffer, size_t length, int flags)
+{
+ if (recv_error == 0)
+ {
+ return recv (socket, buffer, length, flags);
+ }
+ return -1;
+}
+
+static int send_error = 0;
+ssize_t
+my_send(int socket, const void *buffer, size_t length, int flags)
+{
+ if (send_error == 0)
+ {
+ return send (socket, buffer, length, flags);
+ }
+ return -1;
+}
+
+#define socket my_socket
+#define recv my_recv
+#define send my_send
+#include "../src/thrift/c_glib/transport/thrift_socket.c"
+#undef socket
+#undef recv
+#undef send
+
+static void thrift_socket_server (const int port);
+static void thrift_socket_server_open (const int port, int times);
+/* test object creation and destruction */
+static void
+test_create_and_destroy(void)
+{
+ gchar *hostname = NULL;
+ guint port = 0;
+
+ GObject *object = NULL;
+ object = g_object_new (THRIFT_TYPE_SOCKET, NULL);
+ g_assert (object != NULL);
+ g_object_get (G_OBJECT(object), "hostname", &hostname, "port", &port, NULL);
+ g_free (hostname);
+
+ g_object_unref (object);
+}
+
+static void
+test_open_and_close(void)
+{
+ ThriftSocket *tsocket = NULL;
+ ThriftTransport *transport = NULL;
+ GError *err = NULL;
+ int port = 51199;
+ pid_t pid;
+ int status;
+
+ pid = fork ();
+ g_assert ( pid >= 0 );
+
+ if ( pid == 0 )
+ {
+ /* child listens */
+ thrift_socket_server_open (port, 1);
+ exit (0);
+ } else {
+ /* parent connects, wait a bit for the socket to be created */
+ sleep (1);
+
+ /* open a connection and close it */
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
+ "port", port, NULL);
+ transport = THRIFT_TRANSPORT (tsocket);
+ thrift_socket_open (transport, NULL);
+ g_assert (thrift_socket_is_open (transport) == TRUE);
+ thrift_socket_close (transport, NULL);
+ g_assert (thrift_socket_is_open (transport) == FALSE);
+
+ /* test close failure */
+ tsocket->sd = -1;
+ thrift_socket_close (transport, NULL);
+ g_object_unref (tsocket);
+
+ /* try a hostname lookup failure */
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost.broken",
+ NULL);
+ transport = THRIFT_TRANSPORT (tsocket);
+ g_assert (thrift_socket_open (transport, &err) == FALSE);
+ g_object_unref (tsocket);
+ g_error_free (err);
+ err = NULL;
+
+ /* try an error call to socket() */
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", NULL);
+ transport = THRIFT_TRANSPORT (tsocket);
+ socket_error = 1;
+ g_assert (thrift_socket_open (transport, &err) == FALSE);
+ socket_error = 0;
+ g_object_unref (tsocket);
+ g_error_free (err);
+ g_assert ( wait (&status) == pid );
+ g_assert ( status == 0 );
+ }
+}
+
+static void
+test_read_and_write(void)
+{
+ ThriftSocket *tsocket = NULL;
+ ThriftTransport *transport = NULL;
+ pid_t pid;
+ int port = 51199;
+ int status;
+ guchar buf[10] = TEST_DATA; /* a buffer */
+
+ pid = fork ();
+ g_assert ( pid >= 0 );
+
+ if ( pid == 0 )
+ {
+ /* child listens */
+ thrift_socket_server (port);
+ exit (0);
+ } else {
+ /* parent connects, wait a bit for the socket to be created */
+ sleep (1);
+
+ tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
+ "port", port, NULL);
+ transport = THRIFT_TRANSPORT (tsocket);
+ g_assert (thrift_socket_open (transport, NULL) == TRUE);
+ g_assert (thrift_socket_is_open (transport));
+ thrift_socket_write (transport, buf, 10, NULL);
+
+ /* write fail */
+ send_error = 1;
+ thrift_socket_write (transport, buf, 1, NULL);
+ send_error = 0;
+
+ thrift_socket_write_end (transport, NULL);
+ thrift_socket_flush (transport, NULL);
+ thrift_socket_close (transport, NULL);
+ g_object_unref (tsocket);
+
+ g_assert ( wait (&status) == pid );
+ g_assert ( status == 0 );
+ }
+}
+
+/* test ThriftSocket's peek() implementation */
+static void
+test_peek(void)
+{
+ gint status;
+ pid_t pid;
+ guint port = 51199;
+ gchar data = 'A';
+ ThriftTransport *client_transport;
+ GError *error = NULL;
+
+ client_transport = g_object_new (THRIFT_TYPE_SOCKET,
+ "hostname", "localhost",
+ "port", port,
+ NULL);
+
+ /* thrift_transport_peek returns FALSE when the socket is closed */
+ g_assert (thrift_transport_is_open (client_transport) == FALSE);
+ g_assert (thrift_transport_peek (client_transport, &error) == FALSE);
+ g_assert (error == NULL);
+
+ pid = fork ();
+ g_assert (pid >= 0);
+
+ if (pid == 0)
+ {
+ ThriftServerTransport *server_transport = NULL;
+
+ g_object_unref (client_transport);
+
+ /* child listens */
+ server_transport = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+ "port", port,
+ NULL);
+ g_assert (server_transport != NULL);
+
+ thrift_server_transport_listen (server_transport, &error);
+ g_assert (error == NULL);
+
+ client_transport = g_object_new
+ (THRIFT_TYPE_BUFFERED_TRANSPORT,
+ "transport", thrift_server_transport_accept (server_transport, &error),
+ "r_buf_size", 0,
+ "w_buf_size", sizeof data,
+ NULL);
+ g_assert (error == NULL);
+ g_assert (client_transport != NULL);
+
+ /* write exactly one character to the client */
+ g_assert (thrift_transport_write (client_transport,
+ &data,
+ sizeof data,
+ &error) == TRUE);
+
+ thrift_transport_flush (client_transport, &error);
+ thrift_transport_write_end (client_transport, &error);
+ thrift_transport_close (client_transport, &error);
+
+ g_object_unref (client_transport);
+ g_object_unref (server_transport);
+
+ exit (0);
+ }
+ else {
+ /* parent connects, wait a bit for the socket to be created */
+ sleep (1);
+
+ /* connect to the child */
+ thrift_transport_open (client_transport, &error);
+ g_assert (error == NULL);
+ g_assert (thrift_transport_is_open (client_transport) == TRUE);
+
+ /* thrift_transport_peek returns TRUE when the socket is open and there is
+ data available to be read */
+ g_assert (thrift_transport_peek (client_transport, &error) == TRUE);
+ g_assert (error == NULL);
+
+ /* read exactly one character from the server */
+ g_assert_cmpint (thrift_transport_read (client_transport,
+ &data,
+ sizeof data,
+ &error), ==, sizeof data);
+
+ /* thrift_transport_peek returns FALSE when the socket is open but there is
+ no (more) data available to be read */
+ g_assert (thrift_transport_is_open (client_transport) == TRUE);
+ g_assert (thrift_transport_peek (client_transport, &error) == FALSE);
+ g_assert (error == NULL);
+
+ thrift_transport_read_end (client_transport, &error);
+ thrift_transport_close (client_transport, &error);
+
+ g_object_unref (client_transport);
+
+ g_assert (wait (&status) == pid);
+ g_assert (status == 0);
+ }
+}
+
+static void
+thrift_socket_server_open (const int port, int times)
+{
+ ThriftServerTransport *transport = NULL;
+ ThriftTransport *client = NULL;
+ int i;
+ ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+ "port", port, NULL);
+
+ transport = THRIFT_SERVER_TRANSPORT (tsocket);
+ thrift_server_transport_listen (transport, NULL);
+ for(i=0;i<times;i++){
+ client = thrift_server_transport_accept (transport, NULL);
+ g_assert (client != NULL);
+ thrift_socket_close (client, NULL);
+ g_object_unref (client);
+ }
+ g_object_unref (tsocket);
+}
+
+
+static void
+thrift_socket_server (const int port)
+{
+ int bytes = 0;
+ ThriftServerTransport *transport = NULL;
+ ThriftTransport *client = NULL;
+ guchar buf[10]; /* a buffer */
+ guchar match[10] = TEST_DATA;
+
+ ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+ "port", port, NULL);
+
+ transport = THRIFT_SERVER_TRANSPORT (tsocket);
+ thrift_server_transport_listen (transport, NULL);
+ client = thrift_server_transport_accept (transport, NULL);
+ g_assert (client != NULL);
+
+ /* read 10 bytes */
+ bytes = thrift_socket_read (client, buf, 10, NULL);
+ g_assert (bytes == 10); /* make sure we've read 10 bytes */
+ g_assert ( memcmp(buf, match, 10) == 0 ); /* make sure what we got matches */
+
+ /* failed read */
+ recv_error = 1;
+ thrift_socket_read (client, buf, 1, NULL);
+ recv_error = 0;
+
+ thrift_socket_read_end (client, NULL);
+ thrift_socket_close (client, NULL);
+ g_object_unref (tsocket);
+ g_object_unref (client);
+}
+
+int
+main(int argc, char *argv[])
+{
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init();
+#endif
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/testtransportsocket/CreateAndDestroy", test_create_and_destroy);
+ g_test_add_func ("/testtransportsocket/OpenAndClose", test_open_and_close);
+ g_test_add_func ("/testtransportsocket/ReadAndWrite", test_read_and_write);
+ g_test_add_func ("/testtransportsocket/Peek", test_peek);
+
+ return g_test_run ();
+}
+
diff --git a/src/jaegertracing/thrift/lib/c_glib/test/testtransportsslsocket.c b/src/jaegertracing/thrift/lib/c_glib/test/testtransportsslsocket.c
new file mode 100644
index 000000000..3c2644d8d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/test/testtransportsslsocket.c
@@ -0,0 +1,542 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#define _POSIX_C_SOURCE 200112L /* https://stackoverflow.com/questions/37541985/storage-size-of-addrinfo-isnt-known */
+
+
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <thrift/c_glib/transport/thrift_transport.h>
+#include <thrift/c_glib/transport/thrift_buffered_transport.h>
+#include <thrift/c_glib/transport/thrift_server_transport.h>
+#include <thrift/c_glib/transport/thrift_server_socket.h>
+#include <thrift/c_glib/transport/thrift_ssl_socket.h>
+
+/* #define TEST_DATA { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' } */
+#define TEST_DATA { "GET / HTTP/1.1\n\n" }
+
+
+/* substituted functions to test failures of system and library calls */
+static int socket_error = 0;
+int
+my_socket(int domain, int type, int protocol)
+{
+ if (socket_error == 0)
+ {
+ return socket (domain, type, protocol);
+ }
+ return -1;
+}
+
+static int recv_error = 0;
+ssize_t
+my_recv(int socket, void *buffer, size_t length, int flags)
+{
+ if (recv_error == 0)
+ {
+ return recv (socket, buffer, length, flags);
+ }
+ return -1;
+}
+
+static int send_error = 0;
+ssize_t
+my_send(int socket, const void *buffer, size_t length, int flags)
+{
+ if (send_error == 0)
+ {
+ return send (socket, buffer, length, flags);
+ }
+ return -1;
+}
+
+#define socket my_socket
+#define recv my_recv
+#define send my_send
+#include "../src/thrift/c_glib/transport/thrift_ssl_socket.c"
+#undef socket
+#undef recv
+#undef send
+
+static void thrift_socket_server (const int port);
+
+/* test object creation and destruction */
+static void
+test_ssl_create_and_destroy(void)
+{
+ gchar *hostname = NULL;
+ guint port = 0;
+
+ GObject *object = NULL;
+ object = g_object_new (THRIFT_TYPE_SSL_SOCKET, NULL);
+ g_assert (object != NULL);
+ g_object_get (G_OBJECT(object), "hostname", &hostname, "port", &port, NULL);
+ g_free (hostname);
+ g_object_unref (object);
+}
+
+static void
+test_ssl_create_and_set_properties(void)
+{
+ gchar *hostname = NULL;
+ guint port = 0;
+ SSL_CTX* ssl_ctx= NULL;
+ GError *error=NULL;
+
+ GObject *object = NULL;
+ object = thrift_ssl_socket_new(SSLTLS, &error);
+ g_object_get (G_OBJECT(object), "hostname", &hostname, "port", &port, "ssl_context", &ssl_ctx, NULL);
+ g_assert (ssl_ctx!=NULL);
+
+ g_free (hostname);
+ g_object_unref (object);
+}
+
+static void
+test_ssl_open_and_close_non_ssl_server(void)
+{
+ ThriftSSLSocket *tSSLSocket = NULL;
+ ThriftTransport *transport = NULL;
+ GError *error=NULL;
+ pid_t pid;
+ int non_ssl_port = 51198;
+ char errormsg[255];
+
+
+ pid = fork ();
+ g_assert ( pid >= 0 );
+
+ if ( pid == 0 )
+ {
+ /* child listens */
+ /* This is a non SSL server */
+ thrift_socket_server (non_ssl_port);
+ exit (0);
+ } else {
+ /* parent connects, wait a bit for the socket to be created */
+ sleep (1);
+
+ /* open a connection and close it */
+ tSSLSocket = thrift_ssl_socket_new_with_host(SSLTLS, "localhost", non_ssl_port, &error);
+
+ transport = THRIFT_TRANSPORT (tSSLSocket);
+ g_assert (thrift_ssl_socket_open (transport, &error) == FALSE);
+ g_assert_cmpstr(error->message, == ,"Error while connect/bind: 68 -> Connection reset by peer");
+ g_clear_error (&error);
+ g_assert (thrift_ssl_socket_is_open (transport) == FALSE);
+ thrift_ssl_socket_close (transport, NULL);
+ g_assert (thrift_ssl_socket_is_open (transport) == FALSE);
+
+ /* test close failure */
+ THRIFT_SOCKET(tSSLSocket)->sd = -1;
+ thrift_ssl_socket_close (transport, NULL);
+ g_object_unref (tSSLSocket);
+
+ /* try a hostname lookup failure */
+ tSSLSocket = thrift_ssl_socket_new_with_host(SSLTLS, "localhost.broken", non_ssl_port, &error);
+ transport = THRIFT_TRANSPORT (tSSLSocket);
+ g_assert (thrift_ssl_socket_open (transport, &error) == FALSE);
+ snprintf(errormsg, 255, "host lookup failed for localhost.broken:%d - Unknown host", non_ssl_port);
+ g_assert_cmpstr(error->message, ==, errormsg);
+ g_clear_error (&error);
+ g_object_unref (tSSLSocket);
+ error = NULL;
+
+ /* try an error call to socket() */
+ /*
+ tSSLSocket = thrift_ssl_socket_new_with_host(SSLTLS, "localhost", port, &error);
+ transport = THRIFT_TRANSPORT (tSSLSocket);
+ socket_error = 1;
+ assert (thrift_ssl_socket_open (transport, &error) == FALSE);
+ socket_error = 0;
+ g_object_unref (tSSLSocket);
+ g_error_free (error);
+ */
+ }
+}
+
+static void
+test_ssl_write_invalid_socket(void)
+{
+ ThriftSSLSocket *tSSLSocket = NULL;
+ ThriftTransport *transport = NULL;
+ GError *error=NULL;
+ char buffer[] = "this must not break";
+
+ /* open a connection and close it */
+ tSSLSocket = thrift_ssl_socket_new_with_host(SSLTLS, "localhost", 51188+1, &error);
+
+ transport = THRIFT_TRANSPORT (tSSLSocket);
+ g_assert (thrift_ssl_socket_open (transport, NULL) == FALSE);
+ g_assert (thrift_ssl_socket_is_open (transport) == FALSE);
+
+ /* FIXME This must be tested but since the assertion inside thrift_ssl_socket_write breaks the test unit
+ it's disabled. They idea is to disable trap/coredump during this test
+ g_assert (thrift_ssl_socket_write(transport, buffer, sizeof(buffer), &error) == FALSE);
+ g_message ("write_failed_with_error: %s",
+ error != NULL ? error->message : "No");
+ g_clear_error (&error);
+ */
+ thrift_ssl_socket_close (transport, NULL);
+ g_assert (thrift_ssl_socket_is_open (transport) == FALSE);
+
+ /* test close failure */
+ THRIFT_SOCKET(tSSLSocket)->sd = -1;
+ thrift_ssl_socket_close (transport, NULL);
+ g_object_unref (tSSLSocket);
+}
+
+
+
+/**
+ * Print the common name of certificate
+ */
+unsigned char * get_cn_name(X509_NAME* const name)
+{
+ int idx = -1;
+ unsigned char *utf8 = NULL;
+
+ do
+ {
+ if(!name) break; /* failed */
+
+ idx = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
+ if(!(idx > -1)) break; /* failed */
+
+ X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, idx);
+ if(!entry) break; /* failed */
+
+ ASN1_STRING* data = X509_NAME_ENTRY_get_data(entry);
+ if(!data) break; /* failed */
+
+ int length = ASN1_STRING_to_UTF8(&utf8, data);
+ if(!utf8 || !(length > 0)) break; /* failed */
+
+ } while (0);
+ return utf8;
+}
+
+/*
+ * Handle IPV4 and IPV6 addr
+ */
+void *get_in_addr(struct sockaddr *sa)
+{
+ if (sa->sa_family == AF_INET)
+ return &(((struct sockaddr_in*)sa)->sin_addr);
+ return &(((struct sockaddr_in6*)sa)->sin6_addr);
+}
+
+int verify_ip(char * hostname, struct sockaddr_storage *addr)
+{
+ struct addrinfo *addr_info,*p;
+ struct addrinfo hints;
+ int res;
+ int retval = 0;
+
+
+ memset(&hints, 0, sizeof (struct addrinfo));
+ hints.ai_family = AF_UNSPEC; /* use AF_INET6 to force IPv6 */
+ hints.ai_socktype = SOCK_STREAM;
+
+
+ if ( (res = getaddrinfo(hostname, NULL, &hints, &addr_info) ) != 0)
+ {
+ /* get the host info */
+ g_error("Cannot get the host address");
+ return retval;
+ }
+ /* loop through all the results and connect to the first we can */
+ char dnshost[INET6_ADDRSTRLEN]; /* bigger addr supported IPV6 */
+ char socket_ip[INET6_ADDRSTRLEN];
+ if(inet_ntop(addr->ss_family, get_in_addr(addr), socket_ip, INET6_ADDRSTRLEN)==socket_ip){
+ g_debug("We are connected to host %s checking against certificate...", socket_ip);
+ int sizeip = socket_ip!=NULL ? strlen(socket_ip) : 0;
+ for(p = addr_info; p != NULL; p = p->ai_next) {
+ if(inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), dnshost, INET6_ADDRSTRLEN)==dnshost){
+ if(dnshost!=NULL){
+ g_info("DNS address [%i -> %s]", p->ai_addr, dnshost);
+ if(!strncmp(dnshost, socket_ip, sizeip)){
+ retval=1;
+ break; /* if we get here, we must have connected successfully */
+ }
+ }
+ }
+ }
+ }
+
+ if(addr_info)
+ freeaddrinfo(addr_info);
+
+ return retval;
+}
+
+static void
+read_from_file(char *buffer, long size, const char *file_name)
+{
+ char ch;
+ long index=0;
+ FILE *fp;
+
+ fp = fopen(file_name,"r"); /* read mode */
+
+ if( fp == NULL )
+ {
+ perror("Error while opening the file.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ printf("The contents of %s file are :\n", file_name);
+
+ while(index<size && ( ch = fgetc(fp) ) != EOF ){
+ buffer[index++] = ch;
+ }
+
+ fclose(fp);
+}
+
+#define ISSUER_CN_PINNING "The Apache Software Foundation"
+#define SUBJECT_CN_PINNING "localhost"
+#define CERT_SERIAL_NUMBER "1"
+
+gboolean verify_certificate_sn(X509 *cert, const unsigned char *serial_number)
+{
+ gboolean retval = FALSE;
+
+ ASN1_INTEGER *serial = X509_get_serialNumber(cert);
+
+ BIGNUM *bn = ASN1_INTEGER_to_BN(serial, NULL);
+ if (!bn) {
+ fprintf(stderr, "unable to convert ASN1INTEGER to BN\n");
+ return EXIT_FAILURE;
+ }
+ char *tmp = BN_bn2dec(bn);
+ if (!tmp) {
+ g_warning(stderr, "unable to convert BN to decimal string.\n");
+ BN_free(bn);
+ return EXIT_FAILURE;
+ }
+ /*
+ if (strlen(tmp) >= len) {
+ g_warn(stderr, "buffer length shorter than serial number\n");
+ BN_free(bn);
+ OPENSSL_free(tmp);
+ return EXIT_FAILURE;
+ }
+ */
+ if(!strncmp(serial_number, tmp, strlen(serial_number))){
+ retval=TRUE;
+ }else{
+ g_warning("Serial number is not valid");
+ }
+
+ BN_free(bn);
+ OPENSSL_free(tmp);
+ return retval;
+}
+
+gboolean my_access_manager(ThriftTransport * transport, X509 *cert, struct sockaddr_storage *addr, GError **error)
+{
+ ThriftSSLSocket *sslSocket = THRIFT_SSL_SOCKET (transport);
+
+ g_info("Processing access to the server");
+ X509_NAME* iname = cert ? X509_get_issuer_name(cert) : NULL;
+ X509_NAME* sname = cert ? X509_get_subject_name(cert) : NULL;
+
+ /* Issuer is the authority we trust that warrants nothing useful */
+ const unsigned char * issuer = get_cn_name(iname);
+ if(issuer){
+ gboolean valid = TRUE;
+ g_info("Issuer (cn) %s", issuer);
+
+ /* Issuer pinning */
+ if(strncmp(ISSUER_CN_PINNING, issuer, strlen(ISSUER_CN_PINNING))){
+ g_warning("The Issuer of the certificate is not valid");
+ valid=FALSE;
+ }
+ OPENSSL_free(issuer);
+ if(!valid)
+ return valid;
+ }
+
+
+ /* Subject is who the certificate is issued to by the authority */
+ const unsigned char * subject = get_cn_name(sname);
+ if(subject){
+ g_info("Subject (cn) %s", subject);
+ gboolean valid = TRUE;
+
+ /* Subject pinning */
+ if(strncmp(SUBJECT_CN_PINNING, subject, strlen(SUBJECT_CN_PINNING))){
+ g_warning("The subject of the certificate is not valid");
+ valid=FALSE;
+ }
+
+ if(!valid)
+ return valid;
+
+ /* Host pinning */
+ if(verify_ip(subject, addr)){
+ g_info("Verified subject");
+ }else{
+ g_info("Cannot verify subject");
+ valid=FALSE;
+ }
+ OPENSSL_free(subject);
+
+ if(!valid)
+ return valid;
+ }
+
+ if(!verify_certificate_sn(cert, CERT_SERIAL_NUMBER)){
+ return FALSE;
+ }else{
+ g_info("Verified serial number");
+ }
+
+ return TRUE;
+
+}
+
+
+
+
+#ifdef BUILD_SERVER
+static void
+test_ssl_authorization_manager(void)
+{
+ int status=0;
+ pid_t pid;
+ ThriftSSLSocket *tSSLsocket = NULL;
+ ThriftTransport *transport = NULL;
+ /* int port = 51199; */
+ int port = 443;
+ GError *error=NULL;
+
+ guchar buf[17] = TEST_DATA; /* a buffer */
+
+/*
+ pid = fork ();
+ g_assert ( pid >= 0 );
+
+ if ( pid == 0 )
+ {
+ thrift_ssl_socket_server (port);
+ exit (0);
+ } else {
+ */
+ /* parent connects, wait a bit for the socket to be created */
+ sleep (1);
+
+ /* Test against level2 owncloud certificate */
+ tSSLsocket = thrift_ssl_socket_new_with_host(SSLTLS, "localhost", port, &error);
+ thrift_ssl_socket_set_manager(tSSLsocket, my_access_manager); /* Install pinning manager */
+ /* thrift_ssl_load_cert_from_file(tSSLsocket, "./owncloud.level2crm.pem"); */
+ unsigned char cert_buffer[65534];
+ read_from_file(cert_buffer, 65534, "../../keys/client.pem");
+ if(!thrift_ssl_load_cert_from_buffer(tSSLsocket, cert_buffer)){
+ g_warning("Certificates cannot be loaded!");
+ }
+
+ transport = THRIFT_TRANSPORT (tSSLsocket);
+ g_assert (thrift_ssl_socket_open (transport, NULL) == TRUE);
+ g_assert (thrift_ssl_socket_is_open (transport));
+
+ thrift_ssl_socket_write (transport, buf, 17, NULL);
+
+ /* write fail */
+ send_error = 1;
+ /*
+ thrift_ssl_socket_write (transport, buf, 1, NULL);
+ send_error = 0;
+ thrift_ssl_socket_write_end (transport, NULL);
+ thrift_ssl_socket_flush (transport, NULL);
+ */
+ thrift_ssl_socket_close (transport, NULL);
+ g_object_unref (tSSLsocket);
+
+ /* g_assert ( wait (&status) == pid ); */
+ g_assert ( status == 0 );
+ /* } */
+}
+#endif
+
+
+static void
+thrift_socket_server (const int port)
+{
+ int bytes = 0;
+ ThriftServerTransport *transport = NULL;
+ ThriftTransport *client = NULL;
+ guchar buf[10]; /* a buffer */
+ guchar match[10] = TEST_DATA;
+
+ ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+ "port", port, NULL);
+
+ transport = THRIFT_SERVER_TRANSPORT (tsocket);
+ thrift_server_transport_listen (transport, NULL);
+ client = thrift_server_transport_accept (transport, NULL);
+ g_assert (client != NULL);
+
+ /* read 10 bytes */
+ bytes = thrift_ssl_socket_read (client, buf, 10, NULL);
+ g_assert (bytes == 10); /* make sure we've read 10 bytes */
+ g_assert ( memcmp(buf, match, 10) == 0 ); /* make sure what we got matches */
+
+ /* failed read */
+ recv_error = 1;
+ thrift_ssl_socket_read (client, buf, 1, NULL);
+ recv_error = 0;
+
+ thrift_ssl_socket_read_end (client, NULL);
+ thrift_ssl_socket_close (client, NULL);
+ g_object_unref (tsocket);
+ g_object_unref (client);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int retval;
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init();
+#endif
+
+ g_test_init (&argc, &argv, NULL);
+
+ thrift_ssl_socket_initialize_openssl();
+
+ g_test_add_func ("/testtransportsslsocket/CreateAndDestroy", test_ssl_create_and_destroy);
+ g_test_add_func ("/testtransportsslsocket/CreateAndSetProperties", test_ssl_create_and_set_properties);
+ g_test_add_func ("/testtransportsslsocket/OpenAndCloseNonSSLServer", test_ssl_open_and_close_non_ssl_server);
+ g_test_add_func ("/testtransportsslsocket/OpenAndWriteInvalidSocket", test_ssl_write_invalid_socket);
+
+
+
+
+ retval = g_test_run ();
+
+ thrift_ssl_socket_finalize_openssl();
+
+ return retval;
+}
+
diff --git a/src/jaegertracing/thrift/lib/c_glib/thrift_c_glib.pc.in b/src/jaegertracing/thrift/lib/c_glib/thrift_c_glib.pc.in
new file mode 100644
index 000000000..568c7a25d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/c_glib/thrift_c_glib.pc.in
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Thrift
+Description: Thrift C API
+Version: @VERSION@
+Requires: glib-2.0 gobject-2.0
+Libs: -L${libdir} -lthrift_c_glib
+Cflags: -I${includedir}
diff --git a/src/jaegertracing/thrift/lib/cl/Makefile.am b/src/jaegertracing/thrift/lib/cl/Makefile.am
new file mode 100644
index 000000000..34b38861d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cl/Makefile.am
@@ -0,0 +1,40 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+all-local:
+ bash ensure-externals.sh
+
+run-tests: test/make-test-binary.lisp
+ $(SBCL) --script test/make-test-binary.lisp
+
+check-local: run-tests
+ ./run-tests
+
+clean-local:
+ $(RM) run-tests quicklisp.lisp backport-update.zip
+ $(RM) -rf lib externals quicklisp
+
+EXTRA_DIST = \
+ README.md \
+ READMES \
+ load-locally.lisp \
+ test \
+ ensure-externals.sh
diff --git a/src/jaegertracing/thrift/lib/cl/README.md b/src/jaegertracing/thrift/lib/cl/README.md
new file mode 100644
index 000000000..1d6eafbd7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cl/README.md
@@ -0,0 +1,253 @@
+Thrift Common Lisp Library
+
+License
+=======
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+
+
+Using Thrift with Common Lisp
+============================
+
+ Thrift is a protocol and library for language-independent communication between cooperating
+ processes. The communication takes the form of request and response messages, of which the forms
+ are specified in advance throufh a shared interface definition. A Thrift definition file is translated
+ into Lisp source files, which comprise several definitions:
+
+ * Three packages, one for the namespace of the implementation operators, and one each for request and
+ response operators.
+ * Various type definitions as implementations for Thrift typedef and enum definitions.
+ * DEF-STRUCT and DEF-EXCEPTION forms for Thrift struct and exception definitions.
+ * DEF-SERVICE forms for thrift service definitions.
+
+ Each service definition expands in a collection of generic function definitions. For each `op`
+ in the service definition, two functions are defined
+
+ * `op`-request is defined for use by a client. It accepts an additional initial `protocol` argument,
+ to act as the client proxy for the operation and mediate the interaction with a remote process
+ through a Thrift-encoded transport stream.
+ * `op`-response is defined for use by a server. It accepts a single `protocol` argument. A server
+ uses it to decode the request message, invoke the base `op` function with the message arguments,
+ encode and send the the result as a response, and handles exceptions.
+
+ The client interface is one operator
+
+ * `with-client (variable location) . body` : creates a connection in a dynamic context and closes it
+ upon exit. The variable is bound to a client proxy stream/protocol instance, which wraps the
+ base i/o stream - socket, file, etc, with an operators which implement the Thrift protocol
+ and transport mechanisms.
+
+ The server interface combines server and service objects
+
+ * `serve (location service)` : accepts connections on the designated port and responds to
+ requests of the service's operations.
+
+
+Building
+--------
+
+The Thrift Common Lisp library is packaged as the ASDF[[1]] system `thrift`.
+It depends on the systems
+
+* puri[[2]] : for the thrift uri class
+* closer-mop[[3]] : for class metadata
+* trivial-utf-8[[4]] : for string codecs
+* usocket[[5]] : for the socket transport
+* ieee-floats[[6]] : for conversion between ints and floats
+* trivial-gray-streams[[7]] : an abstraction layer for gray streams
+* alexandria[[8]] : handy utilities
+
+The dependencies are bundled for local builds of tests and tutorial binaries -
+it is possible to use those bundles to load the library, too.
+
+In order to build it, register those systems with ASDF and evaluate:
+
+ (asdf:load-system :thrift)
+
+This will compile and load the Lisp compiler for Thrift definition files, the
+transport and protocol implementations, and the client and server interface
+functions. In order to use Thrift in an application, one must also author and/or
+load the interface definitions for the remote service.[[9]] If one is implementing a service,
+one must also define the actual functions to which Thrift is to act as the proxy
+interface. The remainder of this document follows the Thrift tutorial to illustrate how
+to perform the steps
+
+ * implement the service
+ * translate the Thrift IDL
+ * load the Lisp service interfaces
+ * run a server for the service
+ * use a client to access the service remotely
+
+Note that, if one is to implement a new service, one will also need to author the
+IDL files, as there is no facility to generate them from a service implementation.
+
+
+Implement the Service
+---------------------
+
+The tutorial comprises serveral functions: `add`, `ping`, `zip`, and `calculate`.
+Each translated IDL file generates three packages for every service. In the case of
+the tutorial file, the relevant packages are:
+
+ * tutorial.calculator
+ * tutorial.calculator-implementation
+ * tutorial.calculator-response
+
+This is to separate the request (generated), response (generated) and implementation
+(meant to be implemented by the programmer) functions for defined Thrift methods.
+
+It is suggested to work in the `tutorial-implementation` package while implementing
+the services - it imports the `common-lisp` package, while the service-specific ones
+don't (to avoid conflicts between Thrift method names and function names in `common-lisp`).
+
+ ;; define the base operations
+
+ (in-package :tutorial-implementation)
+
+ (defun tutorial.calculator-implementation:add (num1 num2)
+ (format t "~&Asked to add ~A and ~A." num1 num2)
+ (+ num1 num2))
+
+ (defun tutorial.calculator-implementation:ping ()
+ (print :ping))
+
+ (defun tutorial.calculator-implementation:zip ()
+ (print :zip))
+
+ (defun tutorial.calculator-implementation:calculate (logid task)
+ (calculate-op (work-op task) (work-num1 task) (work-num2 task)))
+
+ (defgeneric calculate-op (op arg1 arg2)
+ (:method :around (op arg1 arg2)
+ (let ((result (call-next-method)))
+ (format t "~&Asked to calculate: ~d on ~A and ~A = ~d." op arg1 arg2 result)
+ result))
+
+ (:method ((op (eql operation.add)) arg1 arg2)
+ (+ arg1 arg2))
+ (:method ((op (eql operation.subtract)) arg1 arg2)
+ (- arg1 arg2))
+ (:method ((op (eql operation.multiply)) arg1 arg2)
+ (* arg1 arg2))
+ (:method ((op (eql operation.divide)) arg1 arg2)
+ (/ arg1 arg2)))
+
+ (defun zip () (print 'zip))
+
+
+Translate the Thrift IDL
+------------------------
+
+IDL files employ the file extension `thrift`. In this case, there are two files to translate
+ * `tutorial.thrift`
+ * `shared.thrift`
+As the former includes the latter, one uses it to generate the interfaces:
+
+ $THRIFT/bin/thrift -r --gen cl $THRIFT/tutorial/tutorial.thrift
+
+`-r` stands for recursion, while `--gen` lets one choose the language to translate to.
+
+
+Load the Lisp translated service interfaces
+-------------------------------------------
+
+The translator generates three files for each IDL file. For example `tutorial-types.lisp`,
+`tutorial-vars.lisp` and an `.asd` file that can be used to load them both and pull in
+other includes (like `shared` within the tutorial) as dependencies.
+
+
+Run a Server for the Service
+----------------------------
+
+The actual service name, as specified in the `def-service` form in `tutorial.lisp`, is `calculator`.
+Each service definition defines a global variable with the service name and binds it to a
+service instance whch describes the operations.
+
+In order to start a service, specify a location and the service instance.
+
+ (in-package :tutorial)
+ (serve #u"thrift://127.0.0.1:9091" calculator)
+
+
+Use a Client to Access the Service Remotely
+-------------------------------------------
+
+
+[in some other process] run the client
+
+ (in-package :cl-user)
+
+ (macrolet ((show (form)
+ `(format *trace-output* "~%~s =>~{ ~s~}"
+ ',form
+ (multiple-value-list (ignore-errors ,form)))))
+ (with-client (protocol #u"thrift://127.0.0.1:9091")
+ (show (tutorial.calculator:ping protocol))
+ (show (tutorial.calculator:add protocol 1 2))
+ (show (tutorial.calculator:add protocol 1 4))
+
+ (let ((task (make-instance 'tutorial:work
+ :op operation.subtract :num1 15 :num2 10)))
+ (show (tutorial.calculator:calculate protocol 1 task))
+
+ (setf (tutorial:work-op task) operation.divide
+ (tutorial:work-num1 task) 1
+ (tutorial:work-num2 task) 0)
+ (show (tutorial.calculator:calculate protocol 1 task)))
+
+ (show (shared.shared-service:get-struct protocol 1))
+
+ (show (zip protocol))))
+
+Issues
+------
+
+### optional fields
+ Where the IDL declares a field options, the def-struct form includes no
+ initform for the slot and the encoding operator skips an unbound slot. This leave some ambiguity
+ with bool fields.
+
+### instantiation protocol :
+ struct classes are standard classes and exception classes are
+ whatever the implementation prescribes. decoders apply make-struct to an initargs list.
+ particularly at the service end, there are advantages to resourcing structs and decoding
+ with direct side-effects on slot-values
+
+### maps:
+ Maps are now represented as hash tables. As data through the call/reply interface is all statically
+ typed, it is not necessary for the objects to themselves indicate the coding form. Association lists
+ would be sufficient. As the key type is arbitrary, property lists offer no additional convenience:
+ as `getf` operates with `eq` a new access interface would be necessary and they would not be
+ available for function application.
+
+
+ [1]: www.common-lisp.net/asdf
+ [2]: http://github.com/lisp/com.b9.puri.ppcre
+ [3]: www.common-lisp.net/closer-mop
+ [4]: trivial-utf-8
+ [5]: https://github.com/usocket/usocket
+ [6]: https://github.com/marijnh/ieee-floats
+ [7]: https://github.com/trivial-gray-streams/trivial-gray-streams
+ [8]: https://gitlab.common-lisp.net/alexandria/alexandria
+ [9]: http://wiki.apache.org/thrift/ThriftGeneration
+
+* usocket[[5]] : for the socket transport
+* ieee-floats[[6]] : for conversion between ints and floats
+* trivial-gray-streams[[7]] : an abstraction layer for gray streams
+* alexandria[[8]] : handy utilities
diff --git a/src/jaegertracing/thrift/lib/cl/READMES/readme-cassandra.lisp b/src/jaegertracing/thrift/lib/cl/READMES/readme-cassandra.lisp
new file mode 100644
index 000000000..72744ea99
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cl/READMES/readme-cassandra.lisp
@@ -0,0 +1,64 @@
+(in-package :cl-user)
+
+#+(or ccl sbcl) /development/source/library/
+(load "build-init.lisp")
+
+;;; ! first, select the api version in the cassandra system definition
+;;; as only one should be loaded at a time.
+(asdf:load-system :de.setf.cassandra)
+
+(in-package :de.setf.cassandra)
+
+(defparameter *c-location*
+ ;; remote
+ ;; #u"thrift://ec2-174-129-66-148.compute-1.amazonaws.com:9160"
+ ;; local
+ #u"thrift://127.0.0.1:9160"
+ "A cassandra service location - either the local one or a remote service
+ - always a 'thrift' uri.")
+
+(defparameter *c* (thrift:client *c-location*))
+
+
+(cassandra:describe-keyspaces *c*)
+;; => ("Keyspace1" "system")
+
+(cassandra:describe-cluster-name *c*)
+;; =>"Test Cluster"
+
+(cassandra:describe-version *c*)
+;; => "2.1.0"
+
+(loop for space in (cassandra:describe-keyspaces *c*)
+ collect (loop for key being each hash-key of (cassandra:describe-keyspace *c* space)
+ using (hash-value value)
+ collect (cons key
+ (loop for key being each hash-key of value
+ using (hash-value value)
+ collect (cons key value)))))
+
+
+(close *c*)
+
+(defun describe-cassandra (location &optional (stream *standard-output*))
+ "Print the first-order store metadata for a cassandra LOCATION."
+
+ (thrift:with-client (cassandra location)
+ (let* ((keyspace-names (cassandra:describe-keyspaces cassandra))
+ (cluster (cassandra:describe-cluster-name cassandra))
+ (version (cassandra:describe-version cassandra))
+ (keyspace-descriptions (loop for space in keyspace-names
+ collect (cons space
+ (loop for key being each hash-key
+ of (cassandra:describe-keyspace cassandra space)
+ using (hash-value value)
+ collect (cons key
+ (loop for key being each hash-key of value
+ using (hash-value value)
+ collect (cons key value))))))))
+ (format stream "~&connection to : ~a" cassandra)
+ (format stream "~&version : ~a" version)
+ (format stream "~&cluster : ~a" cluster)
+ (format stream "~&keyspaces~{~{~%~%space: ~a~@{~% ~{~a :~@{~20t~:w~^~%~}~}~}~}~}" keyspace-descriptions))))
+
+;;; (describe-cassandra *c-location*)
diff --git a/src/jaegertracing/thrift/lib/cl/ensure-externals.sh b/src/jaegertracing/thrift/lib/cl/ensure-externals.sh
new file mode 100755
index 000000000..0495f030c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cl/ensure-externals.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+set -e
+
+if [[ ! -e quicklisp.lisp ]]; then curl -O https://beta.quicklisp.org/quicklisp.lisp; fi
+sbcl --load quicklisp.lisp \
+ --eval "(ignore-errors (quicklisp-quickstart:install :path \"quicklisp/\"))" \
+ --eval "(load \"quicklisp/setup.lisp\")" \
+ --eval "(quicklisp:bundle-systems '(#:puri #:usocket #:closer-mop #:trivial-utf-8 #:ieee-floats #:trivial-gray-streams #:alexandria #:bordeaux-threads #:cl-ppcre #:fiasco #:net.didierverna.clon) :to \"externals/\")" \
+ --eval "(quit)" \
+ --no-userinit
+if [[ ! -e backport-update.zip ]]; then
+ curl -O -L https://github.com/TurtleWarePL/de.setf.thrift/archive/backport-update.zip;
+fi
+mkdir -p lib
+unzip -u backport-update.zip -d lib
diff --git a/src/jaegertracing/thrift/lib/cl/load-locally.lisp b/src/jaegertracing/thrift/lib/cl/load-locally.lisp
new file mode 100644
index 000000000..d12c70476
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cl/load-locally.lisp
@@ -0,0 +1,23 @@
+(in-package #:cl-user)
+
+;;;; Licensed under the Apache License, Version 2.0 (the "License");
+;;;; you may not use this file except in compliance with the License.
+;;;; You may obtain a copy of the License at
+;;;;
+;;;; http://www.apache.org/licenses/LICENSE-2.0
+;;;;
+;;;; Unless required by applicable law or agreed to in writing, software
+;;;; distributed under the License is distributed on an "AS IS" BASIS,
+;;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+;;;; See the License for the specific language governing permissions and
+;;;; limitations under the License.
+
+;;;; Just a script for loading the library itself, using bundled dependencies.
+;;;; This is here for when we want to build the self-test and cross-test
+;;;; binaries.
+
+(require "asdf")
+
+(load (merge-pathnames "externals/bundle.lisp" *load-truename*))
+(asdf:load-asd (merge-pathnames "lib/de.setf.thrift-backport-update/thrift.asd" *load-truename*))
+(asdf:load-system :thrift)
diff --git a/src/jaegertracing/thrift/lib/cl/test/make-test-binary.lisp b/src/jaegertracing/thrift/lib/cl/test/make-test-binary.lisp
new file mode 100644
index 000000000..4e7a58cc4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cl/test/make-test-binary.lisp
@@ -0,0 +1,31 @@
+;;;; Licensed under the Apache License, Version 2.0 (the "License");
+;;;; you may not use this file except in compliance with the License.
+;;;; You may obtain a copy of the License at
+;;;;
+;;;; http://www.apache.org/licenses/LICENSE-2.0
+;;;;
+;;;; Unless required by applicable law or agreed to in writing, software
+;;;; distributed under the License is distributed on an "AS IS" BASIS,
+;;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+;;;; See the License for the specific language governing permissions and
+;;;; limitations under the License.
+
+;;;; This file is used to build the binary that runs all self-tests. The
+;;;; binary is then meant to be hooked up to Thrift's `make check` facility,
+;;;; but can easily be run on its own as well.
+
+(in-package #:cl-user)
+
+(require "asdf")
+(load (merge-pathnames "../load-locally.lisp" *load-truename*))
+(asdf:load-asd (merge-pathnames "../lib/de.setf.thrift-backport-update/test/thrift-test.asd" *load-truename*))
+(asdf:load-system :thrift-test)
+(asdf:load-system :net.didierverna.clon)
+
+(net.didierverna.clon:nickname-package)
+
+(defun main ()
+ (let ((result (if (fiasco:run-tests 'thrift-test) 0 -1)))
+ (clon:exit result)))
+
+(clon:dump "run-tests" main)
diff --git a/src/jaegertracing/thrift/lib/cpp/3rdparty.props b/src/jaegertracing/thrift/lib/cpp/3rdparty.props
new file mode 100644
index 000000000..528d40a0b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/3rdparty.props
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ImportGroup Label="PropertySheets" />
+ <PropertyGroup Label="UserMacros">
+ <BOOST_ROOT>$(THIRD_PARTY)\boost\boost_1_47_0</BOOST_ROOT>
+ <OPENSSL_ROOT_DIR>$(THIRD_PARTY)\openssl\OpenSSL-Win32</OPENSSL_ROOT_DIR>
+ <LIBEVENT_ROOT>$(THIRD_PARTY)\libevent-2.0.21-stable</LIBEVENT_ROOT>
+ </PropertyGroup>
+ <PropertyGroup />
+ <ItemDefinitionGroup />
+ <ItemGroup>
+ <BuildMacro Include="BOOST_ROOT">
+ <Value>$(BOOST_ROOT)</Value>
+ <EnvironmentVariable>true</EnvironmentVariable>
+ </BuildMacro>
+ <BuildMacro Include="OPENSSL_ROOT_DIR">
+ <Value>$(OPENSSL_ROOT_DIR)</Value>
+ <EnvironmentVariable>true</EnvironmentVariable>
+ </BuildMacro>
+ <BuildMacro Include="LIBEVENT_ROOT">
+ <Value>$(LIBEVENT_ROOT)</Value>
+ <EnvironmentVariable>true</EnvironmentVariable>
+ </BuildMacro>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/cpp/CMakeLists.txt b/src/jaegertracing/thrift/lib/cpp/CMakeLists.txt
new file mode 100755
index 000000000..e92da606b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/CMakeLists.txt
@@ -0,0 +1,191 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Remove the following once lib/cpp no longer depends on boost headers:
+include(BoostMacros)
+REQUIRE_BOOST_HEADERS()
+
+include_directories(src)
+
+if(NOT BUILD_SHARED_LIBS)
+ add_definitions("-DTHRIFT_STATIC_DEFINE")
+endif()
+
+# SYSLIBS contains libraries that need to be linked to all lib targets
+set(SYSLIBS "")
+
+# Create the thrift C++ library
+set( thriftcpp_SOURCES
+ src/thrift/TApplicationException.cpp
+ src/thrift/TOutput.cpp
+ src/thrift/async/TAsyncChannel.cpp
+ src/thrift/async/TAsyncProtocolProcessor.cpp
+ src/thrift/async/TConcurrentClientSyncInfo.h
+ src/thrift/async/TConcurrentClientSyncInfo.cpp
+ src/thrift/concurrency/ThreadManager.cpp
+ src/thrift/concurrency/TimerManager.cpp
+ src/thrift/processor/PeekProcessor.cpp
+ src/thrift/protocol/TBase64Utils.cpp
+ src/thrift/protocol/TDebugProtocol.cpp
+ src/thrift/protocol/TJSONProtocol.cpp
+ src/thrift/protocol/TMultiplexedProtocol.cpp
+ src/thrift/protocol/TProtocol.cpp
+ src/thrift/transport/TTransportException.cpp
+ src/thrift/transport/TFDTransport.cpp
+ src/thrift/transport/TSimpleFileTransport.cpp
+ src/thrift/transport/THttpTransport.cpp
+ src/thrift/transport/THttpClient.cpp
+ src/thrift/transport/THttpServer.cpp
+ src/thrift/transport/TSocket.cpp
+ src/thrift/transport/TSocketPool.cpp
+ src/thrift/transport/TServerSocket.cpp
+ src/thrift/transport/TTransportUtils.cpp
+ src/thrift/transport/TBufferTransports.cpp
+ src/thrift/server/TConnectedClient.cpp
+ src/thrift/server/TServerFramework.cpp
+ src/thrift/server/TSimpleServer.cpp
+ src/thrift/server/TThreadPoolServer.cpp
+ src/thrift/server/TThreadedServer.cpp
+)
+
+# These files don't work on Windows CE as there is no pipe support
+# TODO: These files won't work with UNICODE support on windows. If fixed this can be re-added.
+if (NOT WINCE)
+ list(APPEND thriftcpp_SOURCES
+ src/thrift/transport/TPipe.cpp
+ src/thrift/transport/TPipeServer.cpp
+ src/thrift/transport/TFileTransport.cpp
+ )
+endif()
+
+
+if (WIN32)
+ list(APPEND thriftcpp_SOURCES
+ src/thrift/windows/TWinsockSingleton.cpp
+ src/thrift/windows/SocketPair.cpp
+ src/thrift/windows/GetTimeOfDay.cpp
+ src/thrift/windows/WinFcntl.cpp
+ )
+ if(NOT WINCE)
+ # This file uses pipes so it currently won't work on Windows CE
+ list(APPEND thriftcpp_SOURCES
+ src/thrift/windows/OverlappedSubmissionThread.cpp
+ )
+ endif()
+else()
+ # These files evaluate to nothing on Windows, so omit them from the
+ # Windows build
+ list(APPEND thriftcpp_SOURCES
+ src/thrift/VirtualProfiling.cpp
+ src/thrift/server/TServer.cpp
+ )
+endif()
+
+# If OpenSSL is not found or disabled just ignore the OpenSSL stuff
+if(OPENSSL_FOUND AND WITH_OPENSSL)
+ list( APPEND thriftcpp_SOURCES
+ src/thrift/transport/TSSLSocket.cpp
+ src/thrift/transport/TSSLServerSocket.cpp
+ )
+ include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}")
+ list(APPEND SYSLIBS "${OPENSSL_LIBRARIES}")
+endif()
+
+if(UNIX)
+ if(ANDROID)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
+ else()
+ list(APPEND SYSLIBS pthread)
+ endif()
+endif()
+set( thriftcpp_threads_SOURCES
+ src/thrift/concurrency/ThreadFactory.cpp
+ src/thrift/concurrency/Thread.cpp
+ src/thrift/concurrency/Monitor.cpp
+ src/thrift/concurrency/Mutex.cpp
+)
+
+# Thrift non blocking server
+set( thriftcppnb_SOURCES
+ src/thrift/server/TNonblockingServer.cpp
+ src/thrift/transport/TNonblockingServerSocket.cpp
+ src/thrift/transport/TNonblockingSSLServerSocket.cpp
+ src/thrift/async/TEvhttpServer.cpp
+ src/thrift/async/TEvhttpClientChannel.cpp
+)
+
+# Thrift zlib transport
+set( thriftcppz_SOURCES
+ src/thrift/transport/TZlibTransport.cpp
+ src/thrift/protocol/THeaderProtocol.cpp
+ src/thrift/transport/THeaderTransport.cpp
+ src/thrift/protocol/THeaderProtocol.cpp
+ src/thrift/transport/THeaderTransport.cpp
+)
+
+# Contains the thrift specific ADD_LIBRARY_THRIFT and TARGET_LINK_LIBRARIES_THRIFT
+include(ThriftMacros)
+
+ADD_LIBRARY_THRIFT(thrift ${thriftcpp_SOURCES} ${thriftcpp_threads_SOURCES})
+if(WIN32)
+ TARGET_LINK_LIBRARIES_THRIFT(thrift ${SYSLIBS} ws2_32)
+else()
+ TARGET_LINK_LIBRARIES_THRIFT(thrift ${SYSLIBS})
+endif()
+ADD_PKGCONFIG_THRIFT(thrift)
+
+if(WITH_LIBEVENT)
+ find_package(Libevent REQUIRED) # Libevent comes with CMake support form upstream
+ include_directories(SYSTEM ${LIBEVENT_INCLUDE_DIRS})
+
+ ADD_LIBRARY_THRIFT(thriftnb ${thriftcppnb_SOURCES})
+ LINK_AGAINST_THRIFT_LIBRARY(thriftnb thrift)
+ TARGET_LINK_LIBRARIES_THRIFT(thriftnb ${SYSLIBS} ${LIBEVENT_LIBRARIES})
+ ADD_PKGCONFIG_THRIFT(thrift-nb)
+endif()
+
+if(WITH_ZLIB)
+ find_package(ZLIB REQUIRED)
+ include_directories(SYSTEM ${ZLIB_INCLUDE_DIRS})
+
+ ADD_LIBRARY_THRIFT(thriftz ${thriftcppz_SOURCES})
+ TARGET_LINK_LIBRARIES_THRIFT(thriftz ${SYSLIBS} ${ZLIB_LIBRARIES})
+ TARGET_LINK_LIBRARIES_THRIFT_AGAINST_THRIFT_LIBRARY(thriftz thrift)
+ ADD_PKGCONFIG_THRIFT(thrift-z)
+endif()
+
+if(WITH_QT5)
+ add_subdirectory(src/thrift/qt)
+ ADD_PKGCONFIG_THRIFT(thrift-qt5)
+endif()
+
+if(MSVC)
+ add_definitions("-DUNICODE -D_UNICODE")
+endif()
+
+# Install the headers
+install(DIRECTORY "src/thrift" DESTINATION "${INCLUDE_INSTALL_DIR}"
+ FILES_MATCHING PATTERN "*.h" PATTERN "*.tcc")
+# Copy config.h file
+install(DIRECTORY "${CMAKE_BINARY_DIR}/thrift" DESTINATION "${INCLUDE_INSTALL_DIR}"
+ FILES_MATCHING PATTERN "*.h")
+
+if(BUILD_TESTING)
+ add_subdirectory(test)
+endif()
diff --git a/src/jaegertracing/thrift/lib/cpp/Makefile.am b/src/jaegertracing/thrift/lib/cpp/Makefile.am
new file mode 100755
index 000000000..9b5fb4c12
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/Makefile.am
@@ -0,0 +1,256 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+AUTOMAKE_OPTIONS = subdir-objects nostdinc
+
+moc__%.cpp: %.h
+ $(QT5_MOC) $(QT5_CFLAGS) $< -o $@
+
+SUBDIRS = .
+
+if WITH_TESTS
+SUBDIRS += test
+endif
+
+pkgconfigdir = $(libdir)/pkgconfig
+
+lib_LTLIBRARIES = libthrift.la
+pkgconfig_DATA = thrift.pc
+libthrift_la_LDFLAGS = -release $(VERSION)
+libthrift_la_LIBADD = $(BOOST_LDFLAGS) $(OPENSSL_LDFLAGS) $(OPENSSL_LIBS)
+
+## We only build the extra libraries if we have the dependencies,
+## but we install all of the headers unconditionally.
+if AMX_HAVE_LIBEVENT
+lib_LTLIBRARIES += libthriftnb.la
+pkgconfig_DATA += thrift-nb.pc
+endif
+if AMX_HAVE_ZLIB
+lib_LTLIBRARIES += libthriftz.la
+pkgconfig_DATA += thrift-z.pc
+endif
+if AMX_HAVE_QT5
+lib_LTLIBRARIES += libthriftqt5.la
+pkgconfig_DATA += thrift-qt5.pc
+endif
+
+AM_CXXFLAGS = -Wall -Wextra -pedantic
+AM_CPPFLAGS = $(BOOST_CPPFLAGS) $(OPENSSL_INCLUDES) -I$(srcdir)/src -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
+
+# Define the source files for the module
+
+libthrift_la_SOURCES = src/thrift/TApplicationException.cpp \
+ src/thrift/TOutput.cpp \
+ src/thrift/VirtualProfiling.cpp \
+ src/thrift/async/TAsyncChannel.cpp \
+ src/thrift/async/TAsyncProtocolProcessor.cpp \
+ src/thrift/async/TConcurrentClientSyncInfo.cpp \
+ src/thrift/concurrency/ThreadManager.cpp \
+ src/thrift/concurrency/TimerManager.cpp \
+ src/thrift/processor/PeekProcessor.cpp \
+ src/thrift/protocol/TDebugProtocol.cpp \
+ src/thrift/protocol/TJSONProtocol.cpp \
+ src/thrift/protocol/TBase64Utils.cpp \
+ src/thrift/protocol/TMultiplexedProtocol.cpp \
+ src/thrift/protocol/TProtocol.cpp \
+ src/thrift/transport/TTransportException.cpp \
+ src/thrift/transport/TFDTransport.cpp \
+ src/thrift/transport/TFileTransport.cpp \
+ src/thrift/transport/TSimpleFileTransport.cpp \
+ src/thrift/transport/THttpTransport.cpp \
+ src/thrift/transport/THttpClient.cpp \
+ src/thrift/transport/THttpServer.cpp \
+ src/thrift/transport/TSocket.cpp \
+ src/thrift/transport/TPipe.cpp \
+ src/thrift/transport/TPipeServer.cpp \
+ src/thrift/transport/TSSLSocket.cpp \
+ src/thrift/transport/TSocketPool.cpp \
+ src/thrift/transport/TServerSocket.cpp \
+ src/thrift/transport/TSSLServerSocket.cpp \
+ src/thrift/transport/TNonblockingServerSocket.cpp \
+ src/thrift/transport/TNonblockingSSLServerSocket.cpp \
+ src/thrift/transport/TTransportUtils.cpp \
+ src/thrift/transport/TBufferTransports.cpp \
+ src/thrift/server/TConnectedClient.cpp \
+ src/thrift/server/TServer.cpp \
+ src/thrift/server/TServerFramework.cpp \
+ src/thrift/server/TSimpleServer.cpp \
+ src/thrift/server/TThreadPoolServer.cpp \
+ src/thrift/server/TThreadedServer.cpp
+
+libthrift_la_SOURCES += src/thrift/concurrency/Mutex.cpp \
+ src/thrift/concurrency/ThreadFactory.cpp \
+ src/thrift/concurrency/Thread.cpp \
+ src/thrift/concurrency/Monitor.cpp
+
+libthriftnb_la_SOURCES = src/thrift/server/TNonblockingServer.cpp \
+ src/thrift/async/TEvhttpServer.cpp \
+ src/thrift/async/TEvhttpClientChannel.cpp
+
+libthriftz_la_SOURCES = src/thrift/transport/TZlibTransport.cpp \
+ src/thrift/transport/THeaderTransport.cpp \
+ src/thrift/protocol/THeaderProtocol.cpp
+
+
+libthriftqt5_la_MOC = src/thrift/qt/moc__TQTcpServer.cpp
+nodist_libthriftqt5_la_SOURCES = $(libthriftqt5_la_MOC)
+libthriftqt5_la_SOURCES = src/thrift/qt/TQIODeviceTransport.cpp \
+ src/thrift/qt/TQTcpServer.cpp
+CLEANFILES = $(libthriftqt5_la_MOC)
+
+# Flags for the various libraries
+libthriftnb_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBEVENT_CPPFLAGS)
+libthriftz_la_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CPPFLAGS)
+libthriftqt5_la_CPPFLAGS = $(AM_CPPFLAGS) $(QT5_CFLAGS)
+if QT5_REDUCE_RELOCATIONS
+libthriftqt5_la_CPPFLAGS += -fPIC
+endif
+libthriftnb_la_CXXFLAGS = $(AM_CXXFLAGS)
+libthriftz_la_CXXFLAGS = $(AM_CXXFLAGS)
+libthriftqt5_la_CXXFLAGS = $(AM_CXXFLAGS)
+libthriftnb_la_LDFLAGS = -release $(VERSION) $(BOOST_LDFLAGS)
+libthriftz_la_LDFLAGS = -release $(VERSION) $(BOOST_LDFLAGS) $(ZLIB_LIBS)
+libthriftqt5_la_LDFLAGS = -release $(VERSION) $(BOOST_LDFLAGS) $(QT5_LIBS)
+
+include_thriftdir = $(includedir)/thrift
+include_thrift_HEADERS = \
+ $(top_builddir)/config.h \
+ src/thrift/thrift-config.h \
+ src/thrift/thrift_export.h \
+ src/thrift/TDispatchProcessor.h \
+ src/thrift/Thrift.h \
+ src/thrift/TOutput.h \
+ src/thrift/TProcessor.h \
+ src/thrift/TApplicationException.h \
+ src/thrift/TLogging.h \
+ src/thrift/TToString.h \
+ src/thrift/TBase.h
+
+include_concurrencydir = $(include_thriftdir)/concurrency
+include_concurrency_HEADERS = \
+ src/thrift/concurrency/Exception.h \
+ src/thrift/concurrency/Mutex.h \
+ src/thrift/concurrency/Monitor.h \
+ src/thrift/concurrency/ThreadFactory.h \
+ src/thrift/concurrency/Thread.h \
+ src/thrift/concurrency/ThreadManager.h \
+ src/thrift/concurrency/TimerManager.h \
+ src/thrift/concurrency/FunctionRunner.h
+
+include_protocoldir = $(include_thriftdir)/protocol
+include_protocol_HEADERS = \
+ src/thrift/protocol/TBinaryProtocol.h \
+ src/thrift/protocol/TBinaryProtocol.tcc \
+ src/thrift/protocol/TCompactProtocol.h \
+ src/thrift/protocol/TCompactProtocol.tcc \
+ src/thrift/protocol/TDebugProtocol.h \
+ src/thrift/protocol/THeaderProtocol.h \
+ src/thrift/protocol/TBase64Utils.h \
+ src/thrift/protocol/TJSONProtocol.h \
+ src/thrift/protocol/TMultiplexedProtocol.h \
+ src/thrift/protocol/TProtocolDecorator.h \
+ src/thrift/protocol/TProtocolTap.h \
+ src/thrift/protocol/TProtocolTypes.h \
+ src/thrift/protocol/TProtocolException.h \
+ src/thrift/protocol/TVirtualProtocol.h \
+ src/thrift/protocol/TProtocol.h
+
+include_transportdir = $(include_thriftdir)/transport
+include_transport_HEADERS = \
+ src/thrift/transport/PlatformSocket.h \
+ src/thrift/transport/TFDTransport.h \
+ src/thrift/transport/TFileTransport.h \
+ src/thrift/transport/THeaderTransport.h \
+ src/thrift/transport/TSimpleFileTransport.h \
+ src/thrift/transport/TServerSocket.h \
+ src/thrift/transport/TSSLServerSocket.h \
+ src/thrift/transport/TServerTransport.h \
+ src/thrift/transport/TNonblockingServerTransport.h \
+ src/thrift/transport/TNonblockingServerSocket.h \
+ src/thrift/transport/TNonblockingSSLServerSocket.h \
+ src/thrift/transport/THttpTransport.h \
+ src/thrift/transport/THttpClient.h \
+ src/thrift/transport/THttpServer.h \
+ src/thrift/transport/TSocket.h \
+ src/thrift/transport/TPipe.h \
+ src/thrift/transport/TPipeServer.h \
+ src/thrift/transport/TSSLSocket.h \
+ src/thrift/transport/TSocketPool.h \
+ src/thrift/transport/TVirtualTransport.h \
+ src/thrift/transport/TTransport.h \
+ src/thrift/transport/TTransportException.h \
+ src/thrift/transport/TTransportUtils.h \
+ src/thrift/transport/TBufferTransports.h \
+ src/thrift/transport/TShortReadTransport.h \
+ src/thrift/transport/TZlibTransport.h
+
+include_serverdir = $(include_thriftdir)/server
+include_server_HEADERS = \
+ src/thrift/server/TConnectedClient.h \
+ src/thrift/server/TServer.h \
+ src/thrift/server/TServerFramework.h \
+ src/thrift/server/TSimpleServer.h \
+ src/thrift/server/TThreadPoolServer.h \
+ src/thrift/server/TThreadedServer.h \
+ src/thrift/server/TNonblockingServer.h
+
+include_processordir = $(include_thriftdir)/processor
+include_processor_HEADERS = \
+ src/thrift/processor/PeekProcessor.h \
+ src/thrift/processor/StatsProcessor.h \
+ src/thrift/processor/TMultiplexedProcessor.h
+
+include_asyncdir = $(include_thriftdir)/async
+include_async_HEADERS = \
+ src/thrift/async/TAsyncChannel.h \
+ src/thrift/async/TAsyncDispatchProcessor.h \
+ src/thrift/async/TAsyncProcessor.h \
+ src/thrift/async/TAsyncBufferProcessor.h \
+ src/thrift/async/TAsyncProtocolProcessor.h \
+ src/thrift/async/TConcurrentClientSyncInfo.h \
+ src/thrift/async/TEvhttpClientChannel.h \
+ src/thrift/async/TEvhttpServer.h
+
+include_qtdir = $(include_thriftdir)/qt
+include_qt_HEADERS = \
+ src/thrift/qt/TQIODeviceTransport.h \
+ src/thrift/qt/TQTcpServer.h
+
+WINDOWS_DIST = \
+ src/thrift/windows \
+ thrift.sln \
+ libthrift.vcxproj \
+ libthrift.vcxproj.filters \
+ libthriftnb.vcxproj \
+ libthriftnb.vcxproj.filters \
+ 3rdparty.props
+
+EXTRA_DIST = \
+ CMakeLists.txt \
+ coding_standards.md \
+ README.md \
+ thrift-nb.pc.in \
+ thrift.pc.in \
+ thrift-z.pc.in \
+ thrift-qt5.pc.in \
+ src/thrift/qt/CMakeLists.txt \
+ $(WINDOWS_DIST)
+
+style-local:
+ $(CPPSTYLE_CMD)
diff --git a/src/jaegertracing/thrift/lib/cpp/README.md b/src/jaegertracing/thrift/lib/cpp/README.md
new file mode 100755
index 000000000..8a897d1ef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/README.md
@@ -0,0 +1,278 @@
+Thrift C++ Software Library
+
+# License
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+# Using Thrift with C++
+
+The Thrift C++ libraries are built using the GNU tools. Follow the instructions
+in the top-level README.md
+
+In case you do not want to open another README.md file, do this thrift src:
+
+ ./bootstrap.sh
+ ./configure (--with-boost=/usr/local)
+ make
+ sudo make install
+
+Thrift is divided into two libraries.
+
+* libthrift - The core Thrift library contains all the core Thrift code. This requires
+ openssl, pthreads, and librt.
+
+* libthriftnb - This library contains the Thrift nonblocking server, which uses libevent.
+ To link this library you will also need to link libevent.
+
+## Linking Against Thrift
+
+After you build and install Thrift the libraries are installed to
+/usr/local/lib by default. Make sure this is in your LDPATH.
+
+On Linux, the best way to do this is to ensure that /usr/local/lib is in
+your /etc/ld.so.conf and then run /sbin/ldconfig.
+
+Depending upon whether you are linking dynamically or statically and how
+your build environment it set up, you may need to include additional
+libraries when linking against thrift, such as librt and/or libpthread. If
+you are using libthriftnb you will also need libevent.
+
+## Dependencies
+
+C++11 is required at a minimum. C++03/C++98 are not supported after version 0.12.0.
+
+Boost is required to run the C++ unit tests. It is not necessary to link against
+the runtime library.
+
+libevent (for libthriftnb only) - most linux distributions have dev packages for this:
+http://monkey.org/~provos/libevent/
+
+# Using Thrift with C++ on Windows
+
+Both the autoconf and cmake build systems are able to automatically detect many
+system configurations without the need to specify library locations, however if
+you run into problems or want to redirect thrift to build and link against your
+own provided third party libraries:
+
+BOOST_ROOT : For boost, e.g. D:\boost_1_55_0
+OPENSSL_ROOT_DIR : For OpenSSL, e.g. D:\OpenSSL-Win32
+
+only required by libthriftnb:
+
+LIBEVENT_ROOT_DIR : For Libevent e.g. D:\libevent-2.0.21-stable
+
+See /3rdparty.user for more details.
+
+The same linking guidelines described above for libthriftnb apply to windows as well.
+
+## Linking Against Thrift
+
+You need to link your project that uses thrift against all the thrift
+dependencies; in the case of libthrift, openssl, pthreads, and librt and for
+libthriftnb, libevent.
+
+In the project properties you must also set HAVE_CONFIG_H as force include
+the config header: "windows/config.h"
+
+## Dependencies
+
+libevent (for libthriftnb only)
+http://monkey.org/~provos/libevent/
+
+## Windows version compatibility
+
+The Thrift library targets Windows 7 or latter versions. The supports for windows XP and Vista are avaiable until 0.12.0.
+
+## Named Pipes
+
+Named Pipe transport has been added in the TPipe and TPipeServer classes. This
+is currently Windows-only. Named pipe transport for *NIX has not been
+implemented. Domain sockets are a better choice for local IPC under non-Windows
+OS's. *NIX named pipes only support 1:1 client-server connection.
+
+# Thrift/SSL
+
+## Scope
+
+This SSL only supports blocking mode socket I/O. It can only be used with
+TSimpleServer, TThreadedServer, and TThreadPoolServer.
+
+## Implementation
+
+There are two main classes TSSLSocketFactory and TSSLSocket. Instances of
+TSSLSocket are always created from TSSLSocketFactory.
+
+## How to use SSL APIs
+
+See the TestClient.cpp and TestServer.cpp files for examples.
+
+### AccessManager (certificate validation)
+
+An example of certificate validation can be found in TestServer.cpp.
+
+AccessManager defines a callback interface. It has three callback methods:
+
+(a) Decision verify(const sockaddr_storage& sa);
+
+(b) Decision verify(const string& host, const char* name, int size);
+
+(c) Decision verify(const sockaddr_storage& sa, const char* data, int size);
+
+After SSL handshake completes, additional checks are conducted. Application
+is given the chance to decide whether or not to continue the conversation
+with the remote. Application is queried through the above three "verify"
+method. They are called at different points of the verification process.
+
+Decisions can be one of ALLOW, DENY, and SKIP. ALLOW and DENY means the
+conversation should be continued or disconnected, respectively. ALLOW and
+DENY decision stops the verification process. SKIP means there's no decision
+based on the given input, continue the verification process.
+
+First, (a) is called with the remote IP. It is called once at the beginning.
+"sa" is the IP address of the remote peer.
+
+Then, the certificate of remote peer is loaded. SubjectAltName extensions
+are extracted and sent to application for verification. When a DNS
+subjectAltName field is extracted, (b) is called. When an IP subjectAltName
+field is extracted, (c) is called.
+
+The "host" in (b) is the value from TSocket::getHost() if this is a client
+side socket, or TSocket::getPeerHost() if this is a server side socket. The
+reason is client side socket initiates the connection. TSocket::getHost()
+is the remote host name. On server side, the remote host name is unknown
+unless it's retrieved through TSocket::getPeerHost(). Either way, "host"
+should be the remote host name. Keep in mind, if TSocket::getPeerHost()
+failed, it would return the remote host name in numeric format.
+
+If all subjectAltName extensions were "skipped", the common name field would
+be checked. It is sent to application through (c), where "sa" is the remote
+IP address. "data" is the IP address extracted from subjectAltName IP
+extension, and "size" is the length of the extension data.
+
+If any of the above "verify" methods returned a decision ALLOW or DENY, the
+verification process would be stopped.
+
+If any of the above "verify" methods returned SKIP, that decision would be
+ignored and the verification process would move on till the last item is
+examined. At that point, if there's still no decision, the connection is
+terminated.
+
+Thread safety, an access manager should not store state information if it's
+to be used by many SSL sockets.
+
+## SIGPIPE signal
+
+Applications running OpenSSL over network connections may crash if SIGPIPE
+is not ignored. This happens when they receive a connection reset by remote
+peer exception, which somehow triggers a SIGPIPE signal. If not handled,
+this signal would kill the application.
+
+## How to run test client/server in SSL mode
+
+The server and client expects the followings from the directory /test/
+
+- keys/server.crt
+- keys/server.key
+- keys/CA.pem
+
+The file names are hard coded in the source code. You need to create these
+certificates before you can run the test code in SSL mode. Make sure at least
+one of the followings is included in "keys/server.crt",
+
+- subjectAltName, DNS localhost
+- subjectAltName, IP 127.0.0.1
+- common name, localhost
+
+Run within /test/ folder,
+
+ ./cpp/TestServer --ssl &
+ ./cpp/TestClient --ssl
+
+If "-h <host>" is used to run client, the above "localhost" in the above
+keys/server.crt has to be replaced with that host name.
+
+## TSSLSocketFactory::randomize()
+
+The default implementation of OpenSSLSocketFactory::randomize() simply calls
+OpenSSL's RAND_poll() when OpenSSL library is first initialized.
+
+The PRNG seed is key to the application security. This method should be
+overridden if it's not strong enough for you.
+
+# Deprecations
+
+## 0.12.0
+
+Support for C++03/C++98 was deprecated.
+Support for Boost at runtime was deprecated.
+
+# Breaking Changes
+
+## 1.0.0
+
+THRIFT-4720:
+The classes Monitor and TimerManager now use std::chrono::milliseconds for timeout, the methods and functions involving THRIFT_TIMESPEC and timeval have been removed, the related tests have been modified.
+
+Support for Windows XP/Vista has been dropped.
+
+Support for C++03/C++98 has been dropped. Use version 0.12.0 to support that
+language level. As a consequence, boost is no longer required as a runtime
+library depenedency, but is is still required to build the runtime library
+and to run the unit tests. We will work towards removing boost as a
+build dependency for folks who just want to build the runtime and not
+run the tests. This means the header thrift/stdcxx.h has been removed and
+anything that relied on it has been changed to directly use C++11 concepts.
+
+THRIFT-4730:
+The classes BoostThreadFactory, PosixThreadFactory, StdThreadFactory, and
+PlatformThreadFactory have been removed, and we will use a ThreadFactory
+based on C++11 (essentially StdThreadFactory was renamed ThreadFactory).
+
+THRIFT-4732:
+The CMake build options WITH_SHARED_LIBS and WITH_STATIC_LIBS are deprecated.
+The project no longer performs a side-by-side static and shared build; you
+tell CMake through BUILD_SHARED_LIBS whether to make shared or static
+libraries now. This is CMake standard behavior.
+
+THRIFT-4735:
+Qt4 support was removed.
+
+THRIFT-4762:
+Added `const` specifier to `TTransport::getOrigin()`. This changes its function signature.
+It's recommended to add the `override` specifier in implementations derived from `TTransport`.
+
+## 0.11.0
+
+Older versions of thrift depended on the <boost/smart_ptr.hpp> classes which
+were used in thrift headers to define interfaces. Thrift now detects C++11
+at build time and will prefer to use <memory> classes from C++11 instead.
+You can force the library to build with boost memory classes by defining the
+preprocessor macro `FORCE_BOOST_SMART_PTR`. (THRIFT-2221)
+
+In the pthread mutex implementation, the contention profiling code was enabled
+by default in all builds. This changed to be disabled by default. (THRIFT-4151)
+
+In older releases, if a TSSLSocketFactory's lifetime was not at least as long
+as the TSSLSockets it created, we silently reverted openssl to unsafe multithread behavior
+and so the results were undefined. Changes were made in 0.11.0 that cause either an
+assertion or a core instead of undefined behavior. The lifetime of a TSSLSocketFactory
+*must* be longer than any TSSLSocket that it creates, otherwise openssl will be cleaned
+up too early. If the static boolean is set to disable openssl initialization and
+cleanup and leave it up to the consuming application, this requirement is not needed.
+(THRIFT-4164)
+
diff --git a/src/jaegertracing/thrift/lib/cpp/coding_standards.md b/src/jaegertracing/thrift/lib/cpp/coding_standards.md
new file mode 100644
index 000000000..8018c774e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/coding_standards.md
@@ -0,0 +1,4 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
+
+ * see .clang-format in root dir for settings of accepted format
+ * clang-format (3.5 or newer) can be used to automaticaly reformat code ('make style' command)
diff --git a/src/jaegertracing/thrift/lib/cpp/libthrift.vcxproj b/src/jaegertracing/thrift/lib/cpp/libthrift.vcxproj
new file mode 100644
index 000000000..d1097ecd3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/libthrift.vcxproj
@@ -0,0 +1,366 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug-mt|Win32">
+ <Configuration>Debug-mt</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-mt|x64">
+ <Configuration>Debug-mt</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-mt|Win32">
+ <Configuration>Release-mt</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-mt|x64">
+ <Configuration>Release-mt</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="src\thrift\async\TAsyncChannel.cpp"/>
+ <ClCompile Include="src\thrift\async\TConcurrentClientSyncInfo.cpp"/>
+ <ClCompile Include="src\thrift\concurrency\BoostMonitor.cpp" />
+ <ClCompile Include="src\thrift\concurrency\BoostMutex.cpp" />
+ <ClCompile Include="src\thrift\concurrency\BoostThreadFactory.cpp" />
+ <ClCompile Include="src\thrift\concurrency\StdThreadFactory.cpp" />
+ <ClCompile Include="src\thrift\concurrency\ThreadManager.cpp"/>
+ <ClCompile Include="src\thrift\concurrency\TimerManager.cpp"/>
+ <ClCompile Include="src\thrift\concurrency\Util.cpp"/>
+ <ClCompile Include="src\thrift\processor\PeekProcessor.cpp"/>
+ <ClCompile Include="src\thrift\protocol\TBase64Utils.cpp" />
+ <ClCompile Include="src\thrift\protocol\TDebugProtocol.cpp"/>
+ <ClCompile Include="src\thrift\protocol\TJSONProtocol.cpp"/>
+ <ClCompile Include="src\thrift\protocol\TProtocol.cpp"/>
+ <ClCompile Include="src\thrift\protocol\TMultiplexedProtocol.cpp"/>
+ <ClCompile Include="src\thrift\server\TSimpleServer.cpp"/>
+ <ClCompile Include="src\thrift\server\TThreadPoolServer.cpp"/>
+ <ClCompile Include="src\thrift\server\TThreadedServer.cpp"/>
+ <ClCompile Include="src\thrift\server\TConnectedClient.cpp"/>
+ <ClCompile Include="src\thrift\server\TNonblockingServer.cpp"/>
+ <ClCompile Include="src\thrift\server\TServerFramework.cpp"/>
+ <ClCompile Include="src\thrift\TApplicationException.cpp"/>
+ <ClCompile Include="src\thrift\TOutput.cpp"/>
+ <ClCompile Include="src\thrift\transport\TBufferTransports.cpp"/>
+ <ClCompile Include="src\thrift\transport\TFDTransport.cpp" />
+ <ClCompile Include="src\thrift\transport\THttpClient.cpp" />
+ <ClCompile Include="src\thrift\transport\THttpServer.cpp" />
+ <ClCompile Include="src\thrift\transport\THttpTransport.cpp"/>
+ <ClCompile Include="src\thrift\transport\TPipe.cpp" />
+ <ClCompile Include="src\thrift\transport\TPipeServer.cpp" />
+ <ClCompile Include="src\thrift\transport\TServerSocket.cpp"/>
+ <ClCompile Include="src\thrift\transport\TSimpleFileTransport.cpp" />
+ <ClCompile Include="src\thrift\transport\TFileTransport.cpp" />
+ <ClCompile Include="src\thrift\transport\TSocket.cpp"/>
+ <ClCompile Include="src\thrift\transport\TSSLSocket.cpp"/>
+ <ClCompile Include="src\thrift\transport\TTransportException.cpp"/>
+ <ClCompile Include="src\thrift\transport\TTransportUtils.cpp"/>
+ <ClCompile Include="src\thrift\windows\GetTimeOfDay.cpp" />
+ <ClCompile Include="src\thrift\windows\OverlappedSubmissionThread.cpp" />
+ <ClCompile Include="src\thrift\windows\SocketPair.cpp" />
+ <ClCompile Include="src\thrift\windows\TWinsockSingleton.cpp" />
+ <ClCompile Include="src\thrift\windows\WinFcntl.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="src\thrift\async\TAsyncChannel.h" />
+ <ClInclude Include="src\thrift\async\TConcurrentClientSyncInfo.h" />
+ <ClInclude Include="src\thrift\concurrency\BoostThreadFactory.h" />
+ <ClInclude Include="src\thrift\concurrency\StdThreadFactory.h" />
+ <ClInclude Include="src\thrift\concurrency\Exception.h" />
+ <ClInclude Include="src\thrift\concurrency\PlatformThreadFactory.h" />
+ <ClInclude Include="src\thrift\processor\PeekProcessor.h" />
+ <ClInclude Include="src\thrift\processor\TMultiplexedProcessor.h" />
+ <ClInclude Include="src\thrift\protocol\TBinaryProtocol.h" />
+ <ClInclude Include="src\thrift\protocol\TDebugProtocol.h" />
+ <ClInclude Include="src\thrift\protocol\TJSONProtocol.h" />
+ <ClInclude Include="src\thrift\protocol\TMultiplexedProtocol.h" />
+ <ClInclude Include="src\thrift\protocol\TProtocol.h" />
+ <ClInclude Include="src\thrift\protocol\TVirtualProtocol.h" />
+ <ClInclude Include="src\thrift\server\TServer.h" />
+ <ClInclude Include="src\thrift\server\TSimpleServer.h" />
+ <ClInclude Include="src\thrift\server\TThreadPoolServer.h" />
+ <ClInclude Include="src\thrift\server\TThreadedServer.h" />
+ <ClInclude Include="src\thrift\TApplicationException.h" />
+ <ClInclude Include="src\thrift\Thrift.h" />
+ <ClInclude Include="src\thrift\TOutput.h" />
+ <ClInclude Include="src\thrift\TProcessor.h" />
+ <ClInclude Include="src\thrift\transport\TBufferTransports.h" />
+ <ClInclude Include="src\thrift\transport\TFDTransport.h" />
+ <ClInclude Include="src\thrift\transport\TFileTransport.h" />
+ <ClInclude Include="src\thrift\transport\THttpClient.h" />
+ <ClInclude Include="src\thrift\transport\THttpServer.h" />
+ <ClInclude Include="src\thrift\transport\TPipe.h" />
+ <ClInclude Include="src\thrift\transport\TPipeServer.h" />
+ <ClInclude Include="src\thrift\transport\TServerSocket.h" />
+ <ClInclude Include="src\thrift\transport\TServerTransport.h" />
+ <ClInclude Include="src\thrift\transport\TSimpleFileTransport.h" />
+ <ClInclude Include="src\thrift\transport\TSocket.h" />
+ <ClInclude Include="src\thrift\transport\TSSLSocket.h" />
+ <ClInclude Include="src\thrift\transport\TTransport.h" />
+ <ClInclude Include="src\thrift\transport\TTransportException.h" />
+ <ClInclude Include="src\thrift\transport\TTransportUtils.h" />
+ <ClInclude Include="src\thrift\transport\TVirtualTransport.h" />
+ <ClInclude Include="src\thrift\windows\config.h" />
+ <ClInclude Include="src\thrift\windows\GetTimeOfDay.h" />
+ <ClInclude Include="src\thrift\windows\Operators.h" />
+ <ClInclude Include="src\thrift\windows\OverlappedSubmissionThread.h" />
+ <ClInclude Include="src\thrift\windows\SocketPair.h" />
+ <ClInclude Include="src\thrift\windows\TWinsockSingleton.h" />
+ <ClInclude Include="src\thrift\windows\WinFcntl.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="src\thrift\protocol\TBinaryProtocol.tcc" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{DD26F57E-60F2-4F37-A616-D219A9BF338F}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>thrift</RootNamespace>
+ <ProjectName>libthrift</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-mt|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-mt|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-mt|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-mt|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="3rdparty.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug-mt|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="3rdparty.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="3rdparty.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug-mt|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="3rdparty.props" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="3rdparty.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release-mt|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="3rdparty.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="3rdparty.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release-mt|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="3rdparty.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\thrift\windows\;$(BOOST_ROOT)\include;$(BOOST_ROOT)\;$(OPENSSL_ROOT_DIR)\include\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-mt|Win32'">
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\thrift\windows\;$(BOOST_ROOT)\include;$(BOOST_ROOT)\;$(OPENSSL_ROOT_DIR)\include\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\thrift\windows\;$(BOOST_ROOT)\include;$(BOOST_ROOT)\;$(OPENSSL_ROOT_DIR)\include\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-mt|x64'">
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\thrift\windows\;$(BOOST_ROOT)\include;$(BOOST_ROOT)\;$(OPENSSL_ROOT_DIR)\include\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\thrift\windows\;$(BOOST_ROOT)\include;$(BOOST_ROOT)\;$(OPENSSL_ROOT_DIR)\include\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-mt|Win32'">
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\thrift\windows\;$(BOOST_ROOT)\include;$(BOOST_ROOT)\;$(OPENSSL_ROOT_DIR)\include\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\thrift\windows\;$(BOOST_ROOT)\include;$(BOOST_ROOT)\;$(OPENSSL_ROOT_DIR)\include\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-mt|x64'">
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\thrift\windows\;$(BOOST_ROOT)\include;$(BOOST_ROOT)\;$(OPENSSL_ROOT_DIR)\include\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>HAVE_CONFIG_H=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ProgramDataBaseFileName>$(IntDir)libthrift.pdb</ProgramDataBaseFileName>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug-mt|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>HAVE_CONFIG_H=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ProgramDataBaseFileName>$(IntDir)libthrift.pdb</ProgramDataBaseFileName>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>HAVE_CONFIG_H=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug-mt|x64'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>HAVE_CONFIG_H=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ProgramDataBaseFileName>$(IntDir)libthrift.pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release-mt|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ProgramDataBaseFileName>$(IntDir)libthrift.pdb</ProgramDataBaseFileName>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release-mt|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/src/jaegertracing/thrift/lib/cpp/libthrift.vcxproj.filters b/src/jaegertracing/thrift/lib/cpp/libthrift.vcxproj.filters
new file mode 100644
index 000000000..5f64f78cb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/libthrift.vcxproj.filters
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="src\thrift\transport\TBufferTransports.cpp">
+ <Filter>transport</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\TOutput.cpp" />
+ <ClCompile Include="src\thrift\TApplicationException.cpp" />
+ <ClCompile Include="src\thrift\windows\StdAfx.cpp">
+ <Filter>windows</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\transport\TTransportException.cpp">
+ <Filter>transport</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\windows\GetTimeOfDay.cpp">
+ <Filter>windows</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\concurrency\ThreadManager.cpp">
+ <Filter>concurrency</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\concurrency\TimerManager.cpp">
+ <Filter>concurrency</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\concurrency\Util.cpp">
+ <Filter>concurrency</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\protocol\TDebugProtocol.cpp">
+ <Filter>protocol</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\protocol\TBase64Utils.cpp">
+ <Filter>protocol</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\protocol\TJSONProtocol.cpp">
+ <Filter>protocol</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\protocol\TMultiplexedProtocol.cpp">
+ <Filter>protocol</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\transport\TFDTransport.cpp">
+ <Filter>transport</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\transport\TFileTransport.cpp">
+ <Filter>transport</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\transport\TSimpleFileTransport.cpp">
+ <Filter>transport</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\transport\THttpTransport.cpp">
+ <Filter>transport</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\transport\THttpClient.cpp">
+ <Filter>transport</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\transport\THttpServer.cpp">
+ <Filter>transport</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\transport\TSSLSocket.cpp">
+ <Filter>transport</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\transport\TTransportUtils.cpp">
+ <Filter>transport</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\server\TSimpleServer.cpp">
+ <Filter>server</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\server\TThreadPoolServer.cpp">
+ <Filter>server</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\server\TThreadedServer.cpp">
+ <Filter>server</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\async\TAsyncChannel.cpp">
+ <Filter>async</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\async\TConcurrentClientSyncInfo.cpp">
+ <Filter>async</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\processor\PeekProcessor.cpp">
+ <Filter>processor</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\transport\TServerSocket.cpp">
+ <Filter>transport</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\transport\TSocket.cpp">
+ <Filter>transport</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\windows\TWinsockSingleton.cpp">
+ <Filter>windows</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\windows\SocketPair.cpp">
+ <Filter>windows</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\concurrency\BoostMonitor.cpp">
+ <Filter>concurrency</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\concurrency\BoostMutex.cpp">
+ <Filter>concurrency</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\concurrency\BoostThreadFactory.cpp">
+ <Filter>concurrency</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\concurrency\StdThreadFactory.cpp">
+ <Filter>concurrency</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\windows\WinFcntl.cpp">
+ <Filter>windows</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\transport\TPipe.cpp">
+ <Filter>transport</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\transport\TPipeServer.cpp">
+ <Filter>transport</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="src\thrift\transport\TBufferTransports.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\TSocket.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\protocol\TBinaryProtocol.h">
+ <Filter>protocol</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\Thrift.h" />
+ <ClInclude Include="src\thrift\TProcessor.h" />
+ <ClInclude Include="src\thrift\TApplicationException.h" />
+ <ClInclude Include="src\thrift\windows\StdAfx.h">
+ <Filter>windows</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\windows\TargetVersion.h">
+ <Filter>windows</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\concurrency\Exception.h">
+ <Filter>concurrency</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\TVirtualTransport.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\TTransport.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\TTransportException.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\windows\GetTimeOfDay.h">
+ <Filter>windows</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\TServerTransport.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\windows\config.h">
+ <Filter>windows</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\protocol\TProtocol.h">
+ <Filter>protocol</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\protocol\TVirtualProtocol.h">
+ <Filter>protocol</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\server\TServer.h">
+ <Filter>server</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\server\TSimpleServer.h">
+ <Filter>server</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\server\TThreadPoolServer.h">
+ <Filter>server</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\server\TThreadedServer.h">
+ <Filter>server</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\async\TAsyncChannel.h">
+ <Filter>async</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\async\TConcurrentClientSyncInfo.h">
+ <Filter>async</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\processor\PeekProcessor.h">
+ <Filter>processor</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\processor\TMultiplexedProcessor.h">
+ <Filter>processor</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\TFDTransport.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\TFileTransport.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\THttpClient.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\THttpServer.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\TSSLSocket.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\TTransportUtils.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\TSimpleFileTransport.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\protocol\TJSONProtocol.h">
+ <Filter>protocol</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\protocol\TMultiplexedProtocol.h">
+ <Filter>protocol</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\protocol\TDebugProtocol.h">
+ <Filter>protocol</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\TServerSocket.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\windows\Operators.h">
+ <Filter>windows</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\windows\TWinsockSingleton.h">
+ <Filter>windows</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\windows\SocketPair.h">
+ <Filter>windows</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\windows\force_inc.h">
+ <Filter>windows</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\concurrency\BoostThreadFactory.h">
+ <Filter>concurrency</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\concurrency\StdThreadFactory.h">
+ <Filter>concurrency</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\concurrency\PlatformThreadFactory.h">
+ <Filter>concurrency</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\windows\WinFcntl.h">
+ <Filter>windows</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\TPipe.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\TPipeServer.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="protocol">
+ <UniqueIdentifier>{07ced19b-b72a-4105-9ffb-6d2bcf64497e}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="transport">
+ <UniqueIdentifier>{e9f61404-1148-4103-bd6f-e5869d37fa79}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="windows">
+ <UniqueIdentifier>{2814002a-3c68-427e-b0eb-33acd2f406ae}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="concurrency">
+ <UniqueIdentifier>{addd4707-dbaa-4d0c-bef6-fff8be7b495a}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="server">
+ <UniqueIdentifier>{f55a8e9b-6959-487f-a396-c31b4d6c61d6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="async">
+ <UniqueIdentifier>{d526885b-1b3e-4ee3-8027-e694fe98ad63}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="processor">
+ <UniqueIdentifier>{8f428da8-5a83-44fb-9578-de935fb415e1}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="windows\tr1">
+ <UniqueIdentifier>{eea10406-3380-4f2d-9365-c26fa2875dae}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="src\thrift\protocol\TBinaryProtocol.tcc">
+ <Filter>protocol</Filter>
+ </None>
+ <None Include="src\thrift\windows\tr1\functional">
+ <Filter>windows\tr1</Filter>
+ </None>
+ </ItemGroup>
+</Project>
diff --git a/src/jaegertracing/thrift/lib/cpp/libthriftnb.vcxproj b/src/jaegertracing/thrift/lib/cpp/libthriftnb.vcxproj
new file mode 100755
index 000000000..9a6ffe60d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/libthriftnb.vcxproj
@@ -0,0 +1,298 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug-mt|Win32">
+ <Configuration>Debug-mt</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-mt|x64">
+ <Configuration>Debug-mt</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-mt|Win32">
+ <Configuration>Release-mt</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-mt|x64">
+ <Configuration>Release-mt</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="src\thrift\async\TAsyncProtocolProcessor.cpp" />
+ <ClCompile Include="src\thrift\async\TEvhttpClientChannel.cpp" />
+ <ClCompile Include="src\thrift\async\TEvhttpServer.cpp" />
+ <ClCompile Include="src\thrift\server\TNonblockingServer.cpp" />
+ <ClCompile Include="src\thrift\transport\TNonblockingServerSocket.cpp" />
+ <ClCompile Include="src\thrift\transport\TNonblockingSSLServerSocket.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="src\thrift\async\TAsyncProtocolProcessor.h" />
+ <ClInclude Include="src\thrift\async\TEvhttpClientChannel.h" />
+ <ClInclude Include="src\thrift\async\TEvhttpServer.h" />
+ <ClInclude Include="src\thrift\server\TNonblockingServer.h" />
+ <ClInclude Include="src\thrift\transport\TNonblockingServerSocket.h" />
+ <ClInclude Include="src\thrift\transport\TNonblockingServerTransport.h" />
+ <ClInclude Include="src\thrift\transport\TNonblockingSSLServerSocket.h" />
+ <ClInclude Include="src\thrift\windows\config.h" />
+ <ClInclude Include="src\thrift\windows\force_inc.h" />
+ <ClInclude Include="src\thrift\windows\TargetVersion.h" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{D8696CCE-7D46-4659-B432-91754A41DEB0}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>libthriftnb</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-mt|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-mt|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-mt|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-mt|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="3rdparty.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug-mt|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="3rdparty.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="3rdparty.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug-mt|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="3rdparty.props" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="3rdparty.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release-mt|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="3rdparty.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="3rdparty.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release-mt|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="3rdparty.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\thrift\windows\;$(BOOST_ROOT)\include;$(BOOST_ROOT)\;$(LIBEVENT_ROOT)\WIN32-Code\;$(LIBEVENT_ROOT)\include;$(LIBEVENT_ROOT)\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-mt|Win32'">
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\thrift\windows\;$(BOOST_ROOT)\include;$(BOOST_ROOT)\;$(LIBEVENT_ROOT)\WIN32-Code\;$(LIBEVENT_ROOT)\include;$(LIBEVENT_ROOT)\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\thrift\windows\;$(BOOST_ROOT)\include;$(BOOST_ROOT)\;$(LIBEVENT_ROOT)\WIN32-Code\;$(LIBEVENT_ROOT)\include;$(LIBEVENT_ROOT)\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-mt|x64'">
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\thrift\windows\;$(BOOST_ROOT)\include;$(BOOST_ROOT)\;$(LIBEVENT_ROOT)\WIN32-Code\;$(LIBEVENT_ROOT)\include;$(LIBEVENT_ROOT)\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\thrift\windows\;$(BOOST_ROOT)\include;$(BOOST_ROOT)\;$(LIBEVENT_ROOT)\WIN32-Code\;$(LIBEVENT_ROOT)\include;$(LIBEVENT_ROOT)\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-mt|Win32'">
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\thrift\windows\;$(BOOST_ROOT)\include;$(BOOST_ROOT)\;$(LIBEVENT_ROOT)\WIN32-Code\;$(LIBEVENT_ROOT)\include;$(LIBEVENT_ROOT)\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\thrift\windows\;$(BOOST_ROOT)\include;$(BOOST_ROOT)\;$(LIBEVENT_ROOT)\WIN32-Code\;$(LIBEVENT_ROOT)\include;$(LIBEVENT_ROOT)\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-mt|x64'">
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\thrift\windows\;$(BOOST_ROOT)\include;$(BOOST_ROOT)\;$(LIBEVENT_ROOT)\WIN32-Code\;$(LIBEVENT_ROOT)\include;$(LIBEVENT_ROOT)\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>HAVE_CONFIG_H=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ProgramDataBaseFileName>$(IntDir)libthriftnb.pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug-mt|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>HAVE_CONFIG_H=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ProgramDataBaseFileName>$(IntDir)libthriftnb.pdb</ProgramDataBaseFileName>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>HAVE_CONFIG_H=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug-mt|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>HAVE_CONFIG_H=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ProgramDataBaseFileName>$(IntDir)libthriftnb.pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release-mt|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ProgramDataBaseFileName>$(IntDir)libthriftnb.pdb</ProgramDataBaseFileName>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release-mt|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/cpp/libthriftnb.vcxproj.filters b/src/jaegertracing/thrift/lib/cpp/libthriftnb.vcxproj.filters
new file mode 100644
index 000000000..85703dd12
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/libthriftnb.vcxproj.filters
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="server">
+ <UniqueIdentifier>{bf449d92-4be8-4f6f-a010-c536f57c6f13}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="async">
+ <UniqueIdentifier>{0294d0a6-ce46-4be8-a659-826d6e98ae41}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="windows">
+ <UniqueIdentifier>{60fc9e5e-0866-4aba-8662-439bb4a461d3}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="transport">
+ <UniqueIdentifier>{23fe2fde-a7c9-43ec-a409-7f53df5eee64}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="src\thrift\server\TNonblockingServer.cpp">
+ <Filter>server</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\async\TEvhttpClientChannel.cpp">
+ <Filter>async</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\async\TEvhttpServer.cpp">
+ <Filter>async</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\async\TAsyncProtocolProcessor.cpp">
+ <Filter>async</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\windows\StdAfx.cpp">
+ <Filter>windows</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\transport\TNonblockingServerSocket.cpp">
+ <Filter>transport</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thrift\transport\TNonblockingSSLServerSocket.cpp">
+ <Filter>transport</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="src\thrift\server\TNonblockingServer.h">
+ <Filter>server</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\async\TEvhttpClientChannel.h">
+ <Filter>async</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\async\TEvhttpServer.h">
+ <Filter>async</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\async\TAsyncProtocolProcessor.h">
+ <Filter>async</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\windows\config.h">
+ <Filter>windows</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\windows\StdAfx.h">
+ <Filter>windows</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\windows\TargetVersion.h">
+ <Filter>windows</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\windows\force_inc.h">
+ <Filter>windows</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\TNonblockingServerSocket.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\TNonblockingServerTransport.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ <ClInclude Include="src\thrift\transport\TNonblockingSSLServerSocket.h">
+ <Filter>transport</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project>
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/TApplicationException.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/TApplicationException.cpp
new file mode 100644
index 000000000..2f14653ab
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/TApplicationException.cpp
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/TApplicationException.h>
+#include <thrift/protocol/TProtocol.h>
+
+namespace apache {
+namespace thrift {
+
+uint32_t TApplicationException::read(apache::thrift::protocol::TProtocol* iprot) {
+ uint32_t xfer = 0;
+ std::string fname;
+ apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ while (true) {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid) {
+ case 1:
+ if (ftype == apache::thrift::protocol::T_STRING) {
+ xfer += iprot->readString(message_);
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 2:
+ if (ftype == apache::thrift::protocol::T_I32) {
+ int32_t type;
+ xfer += iprot->readI32(type);
+ type_ = (TApplicationExceptionType)type;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+ return xfer;
+}
+
+uint32_t TApplicationException::write(apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ xfer += oprot->writeStructBegin("TApplicationException");
+ xfer += oprot->writeFieldBegin("message", apache::thrift::protocol::T_STRING, 1);
+ xfer += oprot->writeString(message_);
+ xfer += oprot->writeFieldEnd();
+ xfer += oprot->writeFieldBegin("type", apache::thrift::protocol::T_I32, 2);
+ xfer += oprot->writeI32(type_);
+ xfer += oprot->writeFieldEnd();
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+}
+} // apache::thrift
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/TApplicationException.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/TApplicationException.h
new file mode 100644
index 000000000..cd1b3e7c0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/TApplicationException.h
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TAPPLICATIONEXCEPTION_H_
+#define _THRIFT_TAPPLICATIONEXCEPTION_H_ 1
+
+#include <thrift/Thrift.h>
+
+namespace apache {
+namespace thrift {
+
+namespace protocol {
+class TProtocol;
+}
+
+class TApplicationException : public TException {
+public:
+ /**
+ * Error codes for the various types of exceptions.
+ */
+ enum TApplicationExceptionType {
+ UNKNOWN = 0,
+ UNKNOWN_METHOD = 1,
+ INVALID_MESSAGE_TYPE = 2,
+ WRONG_METHOD_NAME = 3,
+ BAD_SEQUENCE_ID = 4,
+ MISSING_RESULT = 5,
+ INTERNAL_ERROR = 6,
+ PROTOCOL_ERROR = 7,
+ INVALID_TRANSFORM = 8,
+ INVALID_PROTOCOL = 9,
+ UNSUPPORTED_CLIENT_TYPE = 10
+ };
+
+ TApplicationException() : TException(), type_(UNKNOWN) {}
+
+ TApplicationException(TApplicationExceptionType type) : TException(), type_(type) {}
+
+ TApplicationException(const std::string& message) : TException(message), type_(UNKNOWN) {}
+
+ TApplicationException(TApplicationExceptionType type, const std::string& message)
+ : TException(message), type_(type) {}
+
+ ~TApplicationException() noexcept override = default;
+
+ /**
+ * Returns an error code that provides information about the type of error
+ * that has occurred.
+ *
+ * @return Error code
+ */
+ TApplicationExceptionType getType() const { return type_; }
+
+ const char* what() const noexcept override {
+ if (message_.empty()) {
+ switch (type_) {
+ case UNKNOWN:
+ return "TApplicationException: Unknown application exception";
+ case UNKNOWN_METHOD:
+ return "TApplicationException: Unknown method";
+ case INVALID_MESSAGE_TYPE:
+ return "TApplicationException: Invalid message type";
+ case WRONG_METHOD_NAME:
+ return "TApplicationException: Wrong method name";
+ case BAD_SEQUENCE_ID:
+ return "TApplicationException: Bad sequence identifier";
+ case MISSING_RESULT:
+ return "TApplicationException: Missing result";
+ case INTERNAL_ERROR:
+ return "TApplicationException: Internal error";
+ case PROTOCOL_ERROR:
+ return "TApplicationException: Protocol error";
+ case INVALID_TRANSFORM:
+ return "TApplicationException: Invalid transform";
+ case INVALID_PROTOCOL:
+ return "TApplicationException: Invalid protocol";
+ case UNSUPPORTED_CLIENT_TYPE:
+ return "TApplicationException: Unsupported client type";
+ default:
+ return "TApplicationException: (Invalid exception type)";
+ };
+ } else {
+ return message_.c_str();
+ }
+ }
+
+ uint32_t read(protocol::TProtocol* iprot);
+ uint32_t write(protocol::TProtocol* oprot) const;
+
+protected:
+ /**
+ * Error code
+ */
+ TApplicationExceptionType type_;
+};
+}
+} // apache::thrift
+
+#endif // #ifndef _THRIFT_TAPPLICATIONEXCEPTION_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/TBase.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/TBase.h
new file mode 100644
index 000000000..e2e78e725
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/TBase.h
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TBASE_H_
+#define _THRIFT_TBASE_H_ 1
+
+#include <thrift/Thrift.h>
+#include <thrift/protocol/TProtocol.h>
+
+namespace apache {
+namespace thrift {
+
+class TBase {
+public:
+ virtual ~TBase() = default;
+ virtual uint32_t read(protocol::TProtocol* iprot) = 0;
+ virtual uint32_t write(protocol::TProtocol* oprot) const = 0;
+};
+}
+} // apache::thrift
+
+#endif // #ifndef _THRIFT_TBASE_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/TDispatchProcessor.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/TDispatchProcessor.h
new file mode 100644
index 000000000..ae522b2d8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/TDispatchProcessor.h
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef _THRIFT_TDISPATCHPROCESSOR_H_
+#define _THRIFT_TDISPATCHPROCESSOR_H_ 1
+
+#include <thrift/TProcessor.h>
+
+namespace apache {
+namespace thrift {
+
+/**
+ * TDispatchProcessor is a helper class to parse the message header then call
+ * another function to dispatch based on the function name.
+ *
+ * Subclasses must implement dispatchCall() to dispatch on the function name.
+ */
+template <class Protocol_>
+class TDispatchProcessorT : public TProcessor {
+public:
+ bool process(std::shared_ptr<protocol::TProtocol> in,
+ std::shared_ptr<protocol::TProtocol> out,
+ void* connectionContext) override {
+ protocol::TProtocol* inRaw = in.get();
+ protocol::TProtocol* outRaw = out.get();
+
+ // Try to dynamic cast to the template protocol type
+ auto* specificIn = dynamic_cast<Protocol_*>(inRaw);
+ auto* specificOut = dynamic_cast<Protocol_*>(outRaw);
+ if (specificIn && specificOut) {
+ return processFast(specificIn, specificOut, connectionContext);
+ }
+
+ // Log the fact that we have to use the slow path
+ T_GENERIC_PROTOCOL(this, inRaw, specificIn);
+ T_GENERIC_PROTOCOL(this, outRaw, specificOut);
+
+ std::string fname;
+ protocol::TMessageType mtype;
+ int32_t seqid;
+ inRaw->readMessageBegin(fname, mtype, seqid);
+
+ // If this doesn't look like a valid call, log an error and return false so
+ // that the server will close the connection.
+ //
+ // (The old generated processor code used to try to skip a T_STRUCT and
+ // continue. However, that seems unsafe.)
+ if (mtype != protocol::T_CALL && mtype != protocol::T_ONEWAY) {
+ GlobalOutput.printf("received invalid message type %d from client", mtype);
+ return false;
+ }
+
+ return this->dispatchCall(inRaw, outRaw, fname, seqid, connectionContext);
+ }
+
+protected:
+ bool processFast(Protocol_* in, Protocol_* out, void* connectionContext) {
+ std::string fname;
+ protocol::TMessageType mtype;
+ int32_t seqid;
+ in->readMessageBegin(fname, mtype, seqid);
+
+ if (mtype != protocol::T_CALL && mtype != protocol::T_ONEWAY) {
+ GlobalOutput.printf("received invalid message type %d from client", mtype);
+ return false;
+ }
+
+ return this->dispatchCallTemplated(in, out, fname, seqid, connectionContext);
+ }
+
+ /**
+ * dispatchCall() methods must be implemented by subclasses
+ */
+ virtual bool dispatchCall(apache::thrift::protocol::TProtocol* in,
+ apache::thrift::protocol::TProtocol* out,
+ const std::string& fname,
+ int32_t seqid,
+ void* callContext) = 0;
+
+ virtual bool dispatchCallTemplated(Protocol_* in,
+ Protocol_* out,
+ const std::string& fname,
+ int32_t seqid,
+ void* callContext) = 0;
+};
+
+/**
+ * Non-templatized version of TDispatchProcessor, that doesn't bother trying to
+ * perform a dynamic_cast.
+ */
+class TDispatchProcessor : public TProcessor {
+public:
+ bool process(std::shared_ptr<protocol::TProtocol> in,
+ std::shared_ptr<protocol::TProtocol> out,
+ void* connectionContext) override {
+ std::string fname;
+ protocol::TMessageType mtype;
+ int32_t seqid;
+ in->readMessageBegin(fname, mtype, seqid);
+
+ if (mtype != protocol::T_CALL && mtype != protocol::T_ONEWAY) {
+ GlobalOutput.printf("received invalid message type %d from client", mtype);
+ return false;
+ }
+
+ return dispatchCall(in.get(), out.get(), fname, seqid, connectionContext);
+ }
+
+protected:
+ virtual bool dispatchCall(apache::thrift::protocol::TProtocol* in,
+ apache::thrift::protocol::TProtocol* out,
+ const std::string& fname,
+ int32_t seqid,
+ void* callContext) = 0;
+};
+
+// Specialize TDispatchProcessorT for TProtocol and TDummyProtocol just to use
+// the generic TDispatchProcessor.
+template <>
+class TDispatchProcessorT<protocol::TDummyProtocol> : public TDispatchProcessor {};
+template <>
+class TDispatchProcessorT<protocol::TProtocol> : public TDispatchProcessor {};
+}
+} // apache::thrift
+
+#endif // _THRIFT_TDISPATCHPROCESSOR_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/TLogging.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/TLogging.h
new file mode 100644
index 000000000..07ff030f7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/TLogging.h
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TLOGGING_H_
+#define _THRIFT_TLOGGING_H_ 1
+
+#include <thrift/thrift-config.h>
+
+/**
+ * Contains utility macros for debugging and logging.
+ *
+ */
+
+#include <time.h>
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+/**
+ * T_GLOBAL_DEBUGGING_LEVEL = 0: all debugging turned off, debug macros undefined
+ * T_GLOBAL_DEBUGGING_LEVEL = 1: all debugging turned on
+ */
+#define T_GLOBAL_DEBUGGING_LEVEL 0
+
+/**
+ * T_GLOBAL_LOGGING_LEVEL = 0: all logging turned off, logging macros undefined
+ * T_GLOBAL_LOGGING_LEVEL = 1: all logging turned on
+ */
+#define T_GLOBAL_LOGGING_LEVEL 1
+
+/**
+ * Standard wrapper around fprintf what will prefix the file name and line
+ * number to the line. Uses T_GLOBAL_DEBUGGING_LEVEL to control whether it is
+ * turned on or off.
+ *
+ * @param format_string
+ */
+#if T_GLOBAL_DEBUGGING_LEVEL > 0
+#define T_DEBUG(format_string, ...) \
+ if (T_GLOBAL_DEBUGGING_LEVEL > 0) { \
+ fprintf(stderr, "[%s,%d] " format_string " \n", __FILE__, __LINE__, ##__VA_ARGS__); \
+ }
+#else
+#define T_DEBUG(format_string, ...)
+#endif
+
+/**
+ * analogous to T_DEBUG but also prints the time
+ *
+ * @param string format_string input: printf style format string
+ */
+#if T_GLOBAL_DEBUGGING_LEVEL > 0
+#define T_DEBUG_T(format_string, ...) \
+ { \
+ if (T_GLOBAL_DEBUGGING_LEVEL > 0) { \
+ time_t now; \
+ char dbgtime[26]; \
+ time(&now); \
+ THRIFT_CTIME_R(&now, dbgtime); \
+ dbgtime[24] = '\0'; \
+ fprintf(stderr, \
+ "[%s,%d] [%s] " format_string " \n", \
+ __FILE__, \
+ __LINE__, \
+ dbgtime, \
+ ##__VA_ARGS__); \
+ } \
+ }
+#else
+#define T_DEBUG_T(format_string, ...)
+#endif
+
+/**
+ * analogous to T_DEBUG but uses input level to determine whether or not the string
+ * should be logged.
+ *
+ * @param int level: specified debug level
+ * @param string format_string input: format string
+ */
+#define T_DEBUG_L(level, format_string, ...) \
+ if ((level) > 0) { \
+ fprintf(stderr, "[%s,%d] " format_string " \n", __FILE__, __LINE__, ##__VA_ARGS__); \
+ }
+
+/**
+ * Explicit error logging. Prints time, file name and line number
+ *
+ * @param string format_string input: printf style format string
+ */
+#define T_ERROR(format_string, ...) \
+ { \
+ time_t now; \
+ char dbgtime[26]; \
+ time(&now); \
+ THRIFT_CTIME_R(&now, dbgtime); \
+ dbgtime[24] = '\0'; \
+ fprintf(stderr, \
+ "[%s,%d] [%s] ERROR: " format_string " \n", \
+ __FILE__, \
+ __LINE__, \
+ dbgtime, \
+ ##__VA_ARGS__); \
+ }
+
+/**
+ * Analogous to T_ERROR, additionally aborting the process.
+ * WARNING: macro calls abort(), ending program execution
+ *
+ * @param string format_string input: printf style format string
+ */
+#define T_ERROR_ABORT(format_string, ...) \
+ { \
+ time_t now; \
+ char dbgtime[26]; \
+ time(&now); \
+ THRIFT_CTIME_R(&now, dbgtime); \
+ dbgtime[24] = '\0'; \
+ fprintf(stderr, \
+ "[%s,%d] [%s] ERROR: Going to abort " format_string " \n", \
+ __FILE__, \
+ __LINE__, \
+ dbgtime, \
+ ##__VA_ARGS__); \
+ exit(1); \
+ }
+
+/**
+ * Log input message
+ *
+ * @param string format_string input: printf style format string
+ */
+#if T_GLOBAL_LOGGING_LEVEL > 0
+#define T_LOG_OPER(format_string, ...) \
+ { \
+ if (T_GLOBAL_LOGGING_LEVEL > 0) { \
+ time_t now; \
+ char dbgtime[26]; \
+ time(&now); \
+ THRIFT_CTIME_R(&now, dbgtime); \
+ dbgtime[24] = '\0'; \
+ fprintf(stderr, "[%s] " format_string " \n", dbgtime, ##__VA_ARGS__); \
+ } \
+ }
+#else
+#define T_LOG_OPER(format_string, ...)
+#endif
+
+/**
+ * T_GLOBAL_DEBUG_VIRTUAL = 0 or unset: normal operation,
+ * virtual call debug messages disabled
+ * T_GLOBAL_DEBUG_VIRTUAL = 1: log a debug messages whenever an
+ * avoidable virtual call is made
+ * T_GLOBAL_DEBUG_VIRTUAL = 2: record detailed info that can be
+ * printed by calling
+ * apache::thrift::profile_print_info()
+ */
+#if T_GLOBAL_DEBUG_VIRTUAL > 1
+#define T_VIRTUAL_CALL() ::apache::thrift::profile_virtual_call(typeid(*this))
+#define T_GENERIC_PROTOCOL(template_class, generic_prot, specific_prot) \
+ do { \
+ if (!(specific_prot)) { \
+ ::apache::thrift::profile_generic_protocol(typeid(*template_class), typeid(*generic_prot)); \
+ } \
+ } while (0)
+#elif T_GLOBAL_DEBUG_VIRTUAL == 1
+#define T_VIRTUAL_CALL() fprintf(stderr, "[%s,%d] virtual call\n", __FILE__, __LINE__)
+#define T_GENERIC_PROTOCOL(template_class, generic_prot, specific_prot) \
+ do { \
+ if (!(specific_prot)) { \
+ fprintf(stderr, "[%s,%d] failed to cast to specific protocol type\n", __FILE__, __LINE__); \
+ } \
+ } while (0)
+#else
+#define T_VIRTUAL_CALL()
+#define T_GENERIC_PROTOCOL(template_class, generic_prot, specific_prot)
+#endif
+
+#endif // #ifndef _THRIFT_TLOGGING_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/TOutput.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/TOutput.cpp
new file mode 100644
index 000000000..8d163a941
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/TOutput.cpp
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/Thrift.h>
+#include <thrift/TToString.h>
+#include <cstring>
+#include <cstdlib>
+#include <stdarg.h>
+#include <stdio.h>
+
+namespace apache {
+namespace thrift {
+
+THRIFT_EXPORT TOutput GlobalOutput;
+
+TOutput::TOutput() : f_(&errorTimeWrapper) {}
+
+void TOutput::printf(const char* message, ...) {
+#ifndef THRIFT_SQUELCH_CONSOLE_OUTPUT
+ // Try to reduce heap usage, even if printf is called rarely.
+ static const int STACK_BUF_SIZE = 256;
+ char stack_buf[STACK_BUF_SIZE];
+ va_list ap;
+
+#ifdef _MSC_VER
+ va_start(ap, message);
+ int need = _vscprintf(message, ap);
+ va_end(ap);
+
+ if (need < STACK_BUF_SIZE) {
+ va_start(ap, message);
+ vsnprintf_s(stack_buf, STACK_BUF_SIZE, _TRUNCATE, message, ap);
+ va_end(ap);
+ f_(stack_buf);
+ return;
+ }
+#else
+ va_start(ap, message);
+ int need = vsnprintf(stack_buf, STACK_BUF_SIZE, message, ap);
+ va_end(ap);
+
+ if (need < STACK_BUF_SIZE) {
+ f_(stack_buf);
+ return;
+ }
+#endif
+
+ char* heap_buf = (char*)malloc((need + 1) * sizeof(char));
+ if (heap_buf == nullptr) {
+#ifdef _MSC_VER
+ va_start(ap, message);
+ vsnprintf_s(stack_buf, STACK_BUF_SIZE, _TRUNCATE, message, ap);
+ va_end(ap);
+#endif
+ // Malloc failed. We might as well print the stack buffer.
+ f_(stack_buf);
+ return;
+ }
+
+ va_start(ap, message);
+ int rval = vsnprintf(heap_buf, need + 1, message, ap);
+ va_end(ap);
+ // TODO(shigin): inform user
+ if (rval != -1) {
+ f_(heap_buf);
+ }
+ free(heap_buf);
+#endif
+}
+
+void TOutput::errorTimeWrapper(const char* msg) {
+#ifndef THRIFT_SQUELCH_CONSOLE_OUTPUT
+ time_t now;
+ char dbgtime[26];
+ time(&now);
+ THRIFT_CTIME_R(&now, dbgtime);
+ dbgtime[24] = 0;
+ fprintf(stderr, "Thrift: %s %s\n", dbgtime, msg);
+#endif
+}
+
+void TOutput::perror(const char* message, int errno_copy) {
+ std::string out = message + std::string(": ") + strerror_s(errno_copy);
+ f_(out.c_str());
+}
+
+std::string TOutput::strerror_s(int errno_copy) {
+#ifndef HAVE_STRERROR_R
+ return "errno = " + to_string(errno_copy);
+#else // HAVE_STRERROR_R
+
+ char b_errbuf[1024] = {'\0'};
+#ifdef STRERROR_R_CHAR_P
+ char* b_error = strerror_r(errno_copy, b_errbuf, sizeof(b_errbuf));
+#else
+ char* b_error = b_errbuf;
+ int rv = strerror_r(errno_copy, b_errbuf, sizeof(b_errbuf));
+ if (rv == -1) {
+ // strerror_r failed. omgwtfbbq.
+ return "XSI-compliant strerror_r() failed with errno = "
+ + to_string(errno_copy);
+ }
+#endif
+ // Can anyone prove that explicit cast is probably not necessary
+ // to ensure that the string object is constructed before
+ // b_error becomes invalid?
+ return std::string(b_error);
+
+#endif // HAVE_STRERROR_R
+}
+}
+} // apache::thrift
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/TOutput.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/TOutput.h
new file mode 100644
index 000000000..26c9a563a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/TOutput.h
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_OUTPUT_H_
+#define _THRIFT_OUTPUT_H_ 1
+
+#include <thrift/thrift_export.h>
+
+namespace apache {
+namespace thrift {
+
+class TOutput {
+public:
+ TOutput();
+
+ inline void setOutputFunction(void (*function)(const char*)) { f_ = function; }
+
+ inline void operator()(const char* message) { f_(message); }
+
+ // It is important to have a const char* overload here instead of
+ // just the string version, otherwise errno could be corrupted
+ // if there is some problem allocating memory when constructing
+ // the string.
+ void perror(const char* message, int errno_copy);
+ inline void perror(const std::string& message, int errno_copy) {
+ perror(message.c_str(), errno_copy);
+ }
+
+ void printf(const char* message, ...);
+
+ static void errorTimeWrapper(const char* msg);
+
+ /** Just like strerror_r but returns a C++ string object. */
+ static std::string strerror_s(int errno_copy);
+
+private:
+ void (*f_)(const char*);
+};
+
+THRIFT_EXPORT extern TOutput GlobalOutput;
+}
+} // namespace apache::thrift
+
+#endif //_THRIFT_OUTPUT_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/TProcessor.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/TProcessor.h
new file mode 100644
index 000000000..65bf3d4a7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/TProcessor.h
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TPROCESSOR_H_
+#define _THRIFT_TPROCESSOR_H_ 1
+
+#include <string>
+#include <thrift/protocol/TProtocol.h>
+
+namespace apache {
+namespace thrift {
+
+/**
+ * Virtual interface class that can handle events from the processor. To
+ * use this you should subclass it and implement the methods that you care
+ * about. Your subclass can also store local data that you may care about,
+ * such as additional "arguments" to these methods (stored in the object
+ * instance's state).
+ */
+class TProcessorEventHandler {
+public:
+ virtual ~TProcessorEventHandler() = default;
+
+ /**
+ * Called before calling other callback methods.
+ * Expected to return some sort of context object.
+ * The return value is passed to all other callbacks
+ * for that function invocation.
+ */
+ virtual void* getContext(const char* fn_name, void* serverContext) {
+ (void)fn_name;
+ (void)serverContext;
+ return nullptr;
+ }
+
+ /**
+ * Expected to free resources associated with a context.
+ */
+ virtual void freeContext(void* ctx, const char* fn_name) {
+ (void)ctx;
+ (void)fn_name;
+ }
+
+ /**
+ * Called before reading arguments.
+ */
+ virtual void preRead(void* ctx, const char* fn_name) {
+ (void)ctx;
+ (void)fn_name;
+ }
+
+ /**
+ * Called between reading arguments and calling the handler.
+ */
+ virtual void postRead(void* ctx, const char* fn_name, uint32_t bytes) {
+ (void)ctx;
+ (void)fn_name;
+ (void)bytes;
+ }
+
+ /**
+ * Called between calling the handler and writing the response.
+ */
+ virtual void preWrite(void* ctx, const char* fn_name) {
+ (void)ctx;
+ (void)fn_name;
+ }
+
+ /**
+ * Called after writing the response.
+ */
+ virtual void postWrite(void* ctx, const char* fn_name, uint32_t bytes) {
+ (void)ctx;
+ (void)fn_name;
+ (void)bytes;
+ }
+
+ /**
+ * Called when an async function call completes successfully.
+ */
+ virtual void asyncComplete(void* ctx, const char* fn_name) {
+ (void)ctx;
+ (void)fn_name;
+ }
+
+ /**
+ * Called if the handler throws an undeclared exception.
+ */
+ virtual void handlerError(void* ctx, const char* fn_name) {
+ (void)ctx;
+ (void)fn_name;
+ }
+
+protected:
+ TProcessorEventHandler() = default;
+};
+
+/**
+ * A helper class used by the generated code to free each context.
+ */
+class TProcessorContextFreer {
+public:
+ TProcessorContextFreer(TProcessorEventHandler* handler, void* context, const char* method)
+ : handler_(handler), context_(context), method_(method) {}
+ ~TProcessorContextFreer() {
+ if (handler_ != nullptr)
+ handler_->freeContext(context_, method_);
+ }
+ void unregister() { handler_ = nullptr; }
+
+private:
+ apache::thrift::TProcessorEventHandler* handler_;
+ void* context_;
+ const char* method_;
+};
+
+/**
+ * A processor is a generic object that acts upon two streams of data, one
+ * an input and the other an output. The definition of this object is loose,
+ * though the typical case is for some sort of server that either generates
+ * responses to an input stream or forwards data from one pipe onto another.
+ *
+ */
+class TProcessor {
+public:
+ virtual ~TProcessor() = default;
+
+ virtual bool process(std::shared_ptr<protocol::TProtocol> in,
+ std::shared_ptr<protocol::TProtocol> out,
+ void* connectionContext) = 0;
+
+ bool process(std::shared_ptr<apache::thrift::protocol::TProtocol> io, void* connectionContext) {
+ return process(io, io, connectionContext);
+ }
+
+ std::shared_ptr<TProcessorEventHandler> getEventHandler() const { return eventHandler_; }
+
+ void setEventHandler(std::shared_ptr<TProcessorEventHandler> eventHandler) {
+ eventHandler_ = eventHandler;
+ }
+
+protected:
+ TProcessor() = default;
+
+ std::shared_ptr<TProcessorEventHandler> eventHandler_;
+};
+
+/**
+ * This is a helper class to allow std::shared_ptr to be used with handler
+ * pointers returned by the generated handler factories.
+ *
+ * The handler factory classes generated by the thrift compiler return raw
+ * pointers, and factory->releaseHandler() must be called when the handler is
+ * no longer needed.
+ *
+ * A ReleaseHandler object can be instantiated and passed as the second
+ * parameter to a shared_ptr, so that factory->releaseHandler() will be called
+ * when the object is no longer needed, instead of deleting the pointer.
+ */
+template <typename HandlerFactory_>
+class ReleaseHandler {
+public:
+ ReleaseHandler(const std::shared_ptr<HandlerFactory_>& handlerFactory)
+ : handlerFactory_(handlerFactory) {}
+
+ void operator()(typename HandlerFactory_::Handler* handler) {
+ if (handler) {
+ handlerFactory_->releaseHandler(handler);
+ }
+ }
+
+private:
+ std::shared_ptr<HandlerFactory_> handlerFactory_;
+};
+
+struct TConnectionInfo {
+ // The input and output protocols
+ std::shared_ptr<protocol::TProtocol> input;
+ std::shared_ptr<protocol::TProtocol> output;
+ // The underlying transport used for the connection
+ // This is the transport that was returned by TServerTransport::accept(),
+ // and it may be different than the transport pointed to by the input and
+ // output protocols.
+ std::shared_ptr<transport::TTransport> transport;
+};
+
+class TProcessorFactory {
+public:
+ virtual ~TProcessorFactory() = default;
+
+ /**
+ * Get the TProcessor to use for a particular connection.
+ *
+ * This method is always invoked in the same thread that the connection was
+ * accepted on. This generally means that this call does not need to be
+ * thread safe, as it will always be invoked from a single thread.
+ */
+ virtual std::shared_ptr<TProcessor> getProcessor(const TConnectionInfo& connInfo) = 0;
+};
+
+class TSingletonProcessorFactory : public TProcessorFactory {
+public:
+ TSingletonProcessorFactory(std::shared_ptr<TProcessor> processor) : processor_(processor) {}
+
+ std::shared_ptr<TProcessor> getProcessor(const TConnectionInfo&) override { return processor_; }
+
+private:
+ std::shared_ptr<TProcessor> processor_;
+};
+}
+} // apache::thrift
+
+#endif // #ifndef _THRIFT_TPROCESSOR_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/TToString.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/TToString.h
new file mode 100644
index 000000000..25780f9d2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/TToString.h
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TOSTRING_H_
+#define _THRIFT_TOSTRING_H_ 1
+
+#include <cmath>
+#include <limits>
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace apache {
+namespace thrift {
+
+template <typename T>
+std::string to_string(const T& t) {
+ std::ostringstream o;
+ o << t;
+ return o.str();
+}
+
+// TODO: replace the computations below with std::numeric_limits::max_digits10 once C++11
+// is enabled.
+inline std::string to_string(const float& t) {
+ std::ostringstream o;
+ o.precision(static_cast<std::streamsize>(std::ceil(static_cast<double>(std::numeric_limits<float>::digits * std::log10(2.0f) + 1))));
+ o << t;
+ return o.str();
+}
+
+inline std::string to_string(const double& t) {
+ std::ostringstream o;
+ o.precision(static_cast<std::streamsize>(std::ceil(static_cast<double>(std::numeric_limits<double>::digits * std::log10(2.0f) + 1))));
+ o << t;
+ return o.str();
+}
+
+inline std::string to_string(const long double& t) {
+ std::ostringstream o;
+ o.precision(static_cast<std::streamsize>(std::ceil(static_cast<double>(std::numeric_limits<long double>::digits * std::log10(2.0f) + 1))));
+ o << t;
+ return o.str();
+}
+
+template <typename K, typename V>
+std::string to_string(const std::map<K, V>& m);
+
+template <typename T>
+std::string to_string(const std::set<T>& s);
+
+template <typename T>
+std::string to_string(const std::vector<T>& t);
+
+template <typename K, typename V>
+std::string to_string(const typename std::pair<K, V>& v) {
+ std::ostringstream o;
+ o << to_string(v.first) << ": " << to_string(v.second);
+ return o.str();
+}
+
+template <typename T>
+std::string to_string(const T& beg, const T& end) {
+ std::ostringstream o;
+ for (T it = beg; it != end; ++it) {
+ if (it != beg)
+ o << ", ";
+ o << to_string(*it);
+ }
+ return o.str();
+}
+
+template <typename T>
+std::string to_string(const std::vector<T>& t) {
+ std::ostringstream o;
+ o << "[" << to_string(t.begin(), t.end()) << "]";
+ return o.str();
+}
+
+template <typename K, typename V>
+std::string to_string(const std::map<K, V>& m) {
+ std::ostringstream o;
+ o << "{" << to_string(m.begin(), m.end()) << "}";
+ return o.str();
+}
+
+template <typename T>
+std::string to_string(const std::set<T>& s) {
+ std::ostringstream o;
+ o << "{" << to_string(s.begin(), s.end()) << "}";
+ return o.str();
+}
+}
+} // apache::thrift
+
+#endif // _THRIFT_TOSTRING_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/Thrift.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/Thrift.h
new file mode 100644
index 000000000..6cb24e660
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/Thrift.h
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_THRIFT_H_
+#define _THRIFT_THRIFT_H_ 1
+
+#include <thrift/transport/PlatformSocket.h>
+
+#include <thrift/thrift-config.h>
+
+#include <stdio.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+#include <string>
+#include <map>
+#include <list>
+#include <set>
+#include <vector>
+#include <exception>
+#include <typeinfo>
+
+#include <thrift/TLogging.h>
+#include <thrift/TOutput.h>
+
+#define THRIFT_UNUSED_VARIABLE(x) ((void)(x))
+
+namespace apache {
+namespace thrift {
+
+class TEnumIterator
+ : public std::iterator<std::forward_iterator_tag, std::pair<int, const char*> > {
+public:
+ TEnumIterator(int n, int* enums, const char** names)
+ : ii_(0), n_(n), enums_(enums), names_(names) {}
+
+ int operator++() { return ++ii_; }
+
+ bool operator!=(const TEnumIterator& end) {
+ THRIFT_UNUSED_VARIABLE(end);
+ assert(end.n_ == -1);
+ return (ii_ != n_);
+ }
+
+ std::pair<int, const char*> operator*() const { return std::make_pair(enums_[ii_], names_[ii_]); }
+
+private:
+ int ii_;
+ const int n_;
+ int* enums_;
+ const char** names_;
+};
+
+class TException : public std::exception {
+public:
+ TException() : message_() {}
+
+ TException(const std::string& message) : message_(message) {}
+
+ ~TException() noexcept override = default;
+
+ const char* what() const noexcept override {
+ if (message_.empty()) {
+ return "Default TException.";
+ } else {
+ return message_.c_str();
+ }
+ }
+
+protected:
+ std::string message_;
+};
+
+class TDelayedException {
+public:
+ template <class E>
+ static TDelayedException* delayException(const E& e);
+ virtual void throw_it() = 0;
+ virtual ~TDelayedException() = default;
+};
+
+template <class E>
+class TExceptionWrapper : public TDelayedException {
+public:
+ TExceptionWrapper(const E& e) : e_(e) {}
+ void throw_it() override {
+ E temp(e_);
+ delete this;
+ throw temp;
+ }
+
+private:
+ E e_;
+};
+
+template <class E>
+TDelayedException* TDelayedException::delayException(const E& e) {
+ return new TExceptionWrapper<E>(e);
+}
+
+#if T_GLOBAL_DEBUG_VIRTUAL > 1
+void profile_virtual_call(const std::type_info& info);
+void profile_generic_protocol(const std::type_info& template_type, const std::type_info& prot_type);
+void profile_print_info(FILE* f);
+void profile_print_info();
+void profile_write_pprof(FILE* gen_calls_f, FILE* virtual_calls_f);
+#endif
+}
+} // apache::thrift
+
+#endif // #ifndef _THRIFT_THRIFT_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/VirtualProfiling.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/VirtualProfiling.cpp
new file mode 100644
index 000000000..6ce346b82
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/VirtualProfiling.cpp
@@ -0,0 +1,425 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/Thrift.h>
+
+// Do nothing if virtual call profiling is not enabled
+#if T_GLOBAL_DEBUG_VIRTUAL > 1
+
+// TODO: This code only works with g++ (since we rely on the fact
+// that all std::type_info instances referring to a particular type
+// always return the exact same pointer value from name().)
+#ifndef __GNUG__
+#error "Thrift virtual function profiling currently only works with gcc"
+#endif // !__GNUG__
+
+// TODO: We also require glibc for the backtrace() and backtrace_symbols()
+// functions.
+#ifndef __GLIBC__
+#error "Thrift virtual function profiling currently requires glibc"
+#endif // !__GLIBC__
+
+#include <thrift/concurrency/Mutex.h>
+
+#include <ext/hash_map>
+#include <execinfo.h>
+#include <stdio.h>
+
+namespace apache {
+namespace thrift {
+
+using ::apache::thrift::concurrency::Mutex;
+using ::apache::thrift::concurrency::Guard;
+
+static const unsigned int MAX_STACK_DEPTH = 15;
+
+/**
+ * A stack trace
+ */
+class Backtrace {
+public:
+ Backtrace(int skip = 0);
+ Backtrace(Backtrace const& bt);
+
+ void operator=(Backtrace const& bt) {
+ numCallers_ = bt.numCallers_;
+ if (numCallers_ >= 0) {
+ memcpy(callers_, bt.callers_, numCallers_ * sizeof(void*));
+ }
+ }
+
+ bool operator==(Backtrace const& bt) const { return (cmp(bt) == 0); }
+
+ size_t hash() const {
+ intptr_t ret = 0;
+ for (int n = 0; n < numCallers_; ++n) {
+ ret ^= reinterpret_cast<intptr_t>(callers_[n]);
+ }
+ return static_cast<size_t>(ret);
+ }
+
+ int cmp(Backtrace const& bt) const {
+ int depth_diff = (numCallers_ - bt.numCallers_);
+ if (depth_diff != 0) {
+ return depth_diff;
+ }
+
+ for (int n = 0; n < numCallers_; ++n) {
+ int diff = reinterpret_cast<intptr_t>(callers_[n])
+ - reinterpret_cast<intptr_t>(bt.callers_[n]);
+ if (diff != 0) {
+ return diff;
+ }
+ }
+
+ return 0;
+ }
+
+ void print(FILE* f, int indent = 0, int start = 0) const {
+ char** strings = backtrace_symbols(callers_, numCallers_);
+ if (strings) {
+ start += skip_;
+ if (start < 0) {
+ start = 0;
+ }
+ for (int n = start; n < numCallers_; ++n) {
+ fprintf(f, "%*s#%-2d %s\n", indent, "", n, strings[n]);
+ }
+ free(strings);
+ } else {
+ fprintf(f, "%*s<failed to determine symbols>\n", indent, "");
+ }
+ }
+
+ int getDepth() const { return numCallers_ - skip_; }
+
+ void* getFrame(int index) const {
+ int adjusted_index = index + skip_;
+ if (adjusted_index < 0 || adjusted_index >= numCallers_) {
+ return NULL;
+ }
+ return callers_[adjusted_index];
+ }
+
+private:
+ void* callers_[MAX_STACK_DEPTH];
+ int numCallers_;
+ int skip_;
+};
+
+// Define the constructors non-inline, so they consistently add a single
+// frame to the stack trace, regardless of whether optimization is enabled
+Backtrace::Backtrace(int skip)
+ : skip_(skip + 1) // ignore the constructor itself
+{
+ numCallers_ = backtrace(callers_, MAX_STACK_DEPTH);
+ if (skip_ > numCallers_) {
+ skip_ = numCallers_;
+ }
+}
+
+Backtrace::Backtrace(Backtrace const& bt) : numCallers_(bt.numCallers_), skip_(bt.skip_) {
+ if (numCallers_ >= 0) {
+ memcpy(callers_, bt.callers_, numCallers_ * sizeof(void*));
+ }
+}
+
+/**
+ * A backtrace, plus one or two type names
+ */
+class Key {
+public:
+ class Hash {
+ public:
+ size_t operator()(Key const& k) const { return k.hash(); }
+ };
+
+ Key(const Backtrace* bt, const std::type_info& type_info)
+ : backtrace_(bt), typeName1_(type_info.name()), typeName2_(NULL) {}
+
+ Key(const Backtrace* bt, const std::type_info& type_info1, const std::type_info& type_info2)
+ : backtrace_(bt), typeName1_(type_info1.name()), typeName2_(type_info2.name()) {}
+
+ Key(const Key& k)
+ : backtrace_(k.backtrace_), typeName1_(k.typeName1_), typeName2_(k.typeName2_) {}
+
+ void operator=(const Key& k) {
+ backtrace_ = k.backtrace_;
+ typeName1_ = k.typeName1_;
+ typeName2_ = k.typeName2_;
+ }
+
+ const Backtrace* getBacktrace() const { return backtrace_; }
+
+ const char* getTypeName() const { return typeName1_; }
+
+ const char* getTypeName2() const { return typeName2_; }
+
+ void makePersistent() {
+ // Copy the Backtrace object
+ backtrace_ = new Backtrace(*backtrace_);
+
+ // NOTE: We don't copy the type name.
+ // The GNU libstdc++ implementation of type_info::name() returns a value
+ // that will be valid for the lifetime of the program. (Although the C++
+ // standard doesn't guarantee this will be true on all implementations.)
+ }
+
+ /**
+ * Clean up memory allocated by makePersistent()
+ *
+ * Should only be invoked if makePersistent() has previously been called.
+ * The Key should no longer be used after cleanup() is called.
+ */
+ void cleanup() {
+ delete backtrace_;
+ backtrace_ = NULL;
+ }
+
+ int cmp(const Key& k) const {
+ int ret = backtrace_->cmp(*k.backtrace_);
+ if (ret != 0) {
+ return ret;
+ }
+
+ // NOTE: We compare just the name pointers.
+ // With GNU libstdc++, every type_info object for the same type points to
+ // exactly the same name string. (Although this isn't guaranteed by the
+ // C++ standard.)
+ ret = k.typeName1_ - typeName1_;
+ if (ret != 0) {
+ return ret;
+ }
+ return k.typeName2_ - typeName2_;
+ }
+
+ bool operator==(const Key& k) const { return cmp(k) == 0; }
+
+ size_t hash() const {
+ // NOTE: As above, we just use the name pointer value.
+ // Works with GNU libstdc++, but not guaranteed to be correct on all
+ // implementations.
+ return backtrace_->hash() ^ reinterpret_cast<size_t>(typeName1_)
+ ^ reinterpret_cast<size_t>(typeName2_);
+ }
+
+private:
+ const Backtrace* backtrace_;
+ const char* typeName1_;
+ const char* typeName2_;
+};
+
+/**
+ * A functor that determines which of two BacktraceMap entries
+ * has a higher count.
+ */
+class CountGreater {
+public:
+ bool operator()(std::pair<Key, size_t> bt1, std::pair<Key, size_t> bt2) const {
+ return bt1.second > bt2.second;
+ }
+};
+
+typedef __gnu_cxx::hash_map<Key, size_t, Key::Hash> BacktraceMap;
+
+/**
+ * A map describing how many times T_VIRTUAL_CALL() has been invoked.
+ */
+BacktraceMap virtual_calls;
+Mutex virtual_calls_mutex;
+
+/**
+ * A map describing how many times T_GENERIC_PROTOCOL() has been invoked.
+ */
+BacktraceMap generic_calls;
+Mutex generic_calls_mutex;
+
+void _record_backtrace(BacktraceMap* map, const Mutex& mutex, Key* k) {
+ Guard guard(mutex);
+
+ BacktraceMap::iterator it = map->find(*k);
+ if (it == map->end()) {
+ k->makePersistent();
+ map->insert(std::make_pair(*k, 1));
+ } else {
+ // increment the count
+ // NOTE: we could assert if it->second is 0 afterwards, since that would
+ // mean we've wrapped.
+ ++(it->second);
+ }
+}
+
+/**
+ * Record an unnecessary virtual function call.
+ *
+ * This method is invoked by the T_VIRTUAL_CALL() macro.
+ */
+void profile_virtual_call(const std::type_info& type) {
+ int const skip = 1; // ignore this frame
+ Backtrace bt(skip);
+ Key k(&bt, type);
+ _record_backtrace(&virtual_calls, virtual_calls_mutex, &k);
+}
+
+/**
+ * Record a call to a template processor with a protocol that is not the one
+ * specified in the template parameter.
+ *
+ * This method is invoked by the T_GENERIC_PROTOCOL() macro.
+ */
+void profile_generic_protocol(const std::type_info& template_type,
+ const std::type_info& prot_type) {
+ int const skip = 1; // ignore this frame
+ Backtrace bt(skip);
+ Key k(&bt, template_type, prot_type);
+ _record_backtrace(&generic_calls, generic_calls_mutex, &k);
+}
+
+/**
+ * Print the recorded profiling information to the specified file.
+ */
+void profile_print_info(FILE* f) {
+ typedef std::vector<std::pair<Key, size_t> > BacktraceVector;
+
+ CountGreater is_greater;
+
+ // Grab both locks for the duration of the print operation,
+ // to ensure the output is a consistent snapshot of a single point in time
+ Guard generic_calls_guard(generic_calls_mutex);
+ Guard virtual_calls_guard(virtual_calls_mutex);
+
+ // print the info from generic_calls, sorted by frequency
+ //
+ // We print the generic_calls info ahead of virtual_calls, since it is more
+ // useful in some cases. All T_GENERIC_PROTOCOL calls can be eliminated
+ // from most programs. Not all T_VIRTUAL_CALLs will be eliminated by
+ // converting to templates.
+ BacktraceVector gp_sorted(generic_calls.begin(), generic_calls.end());
+ std::sort(gp_sorted.begin(), gp_sorted.end(), is_greater);
+
+ for (BacktraceVector::const_iterator it = gp_sorted.begin(); it != gp_sorted.end(); ++it) {
+ Key const& key = it->first;
+ size_t const count = it->second;
+ fprintf(f,
+ "T_GENERIC_PROTOCOL: %zu calls to %s with a %s:\n",
+ count,
+ key.getTypeName(),
+ key.getTypeName2());
+ key.getBacktrace()->print(f, 2);
+ fprintf(f, "\n");
+ }
+
+ // print the info from virtual_calls, sorted by frequency
+ BacktraceVector vc_sorted(virtual_calls.begin(), virtual_calls.end());
+ std::sort(vc_sorted.begin(), vc_sorted.end(), is_greater);
+
+ for (BacktraceVector::const_iterator it = vc_sorted.begin(); it != vc_sorted.end(); ++it) {
+ Key const& key = it->first;
+ size_t const count = it->second;
+ fprintf(f, "T_VIRTUAL_CALL: %zu calls on %s:\n", count, key.getTypeName());
+ key.getBacktrace()->print(f, 2);
+ fprintf(f, "\n");
+ }
+}
+
+/**
+ * Print the recorded profiling information to stdout.
+ */
+void profile_print_info() {
+ profile_print_info(stdout);
+}
+
+/**
+ * Write a BacktraceMap as Google CPU profiler binary data.
+ */
+static void profile_write_pprof_file(FILE* f, BacktraceMap const& map) {
+ // Write the header
+ uintptr_t header[5] = {0, 3, 0, 0, 0};
+ fwrite(&header, sizeof(header), 1, f);
+
+ // Write the profile records
+ for (BacktraceMap::const_iterator it = map.begin(); it != map.end(); ++it) {
+ uintptr_t count = it->second;
+ fwrite(&count, sizeof(count), 1, f);
+
+ Backtrace const* bt = it->first.getBacktrace();
+ uintptr_t num_pcs = bt->getDepth();
+ fwrite(&num_pcs, sizeof(num_pcs), 1, f);
+
+ for (uintptr_t n = 0; n < num_pcs; ++n) {
+ void* pc = bt->getFrame(n);
+ fwrite(&pc, sizeof(pc), 1, f);
+ }
+ }
+
+ // Write the trailer
+ uintptr_t trailer[3] = {0, 1, 0};
+ fwrite(&trailer, sizeof(trailer), 1, f);
+
+ // Write /proc/self/maps
+ // TODO(simpkins): This only works on linux
+ FILE* proc_maps = fopen("/proc/self/maps", "r");
+ if (proc_maps) {
+ uint8_t buf[4096];
+ while (true) {
+ size_t bytes_read = fread(buf, 1, sizeof(buf), proc_maps);
+ if (bytes_read == 0) {
+ break;
+ }
+ fwrite(buf, 1, bytes_read, f);
+ }
+ fclose(proc_maps);
+ }
+}
+
+/**
+ * Write the recorded profiling information as pprof files.
+ *
+ * This writes the information using the Google CPU profiler binary data
+ * format, so it can be analyzed with pprof. Note that information about the
+ * protocol/transport data types cannot be stored in this file format.
+ *
+ * See http://code.google.com/p/google-perftools/ for more details.
+ *
+ * @param gen_calls_f The information about calls to
+ * profile_generic_protocol() will be written to this
+ * file.
+ * @param virtual_calls_f The information about calls to
+ * profile_virtual_call() will be written to this file.
+ */
+void profile_write_pprof(FILE* gen_calls_f, FILE* virtual_calls_f) {
+ typedef std::vector<std::pair<Key, size_t> > BacktraceVector;
+
+ CountGreater is_greater;
+
+ // Grab both locks for the duration of the print operation,
+ // to ensure the output is a consistent snapshot of a single point in time
+ Guard generic_calls_guard(generic_calls_mutex);
+ Guard virtual_calls_guard(virtual_calls_mutex);
+
+ // write the info from generic_calls
+ profile_write_pprof_file(gen_calls_f, generic_calls);
+
+ // write the info from virtual_calls
+ profile_write_pprof_file(virtual_calls_f, virtual_calls);
+}
+}
+} // apache::thrift
+
+#endif // T_GLOBAL_PROFILE_VIRTUAL > 0
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncBufferProcessor.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncBufferProcessor.h
new file mode 100644
index 000000000..e3c3597c2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncBufferProcessor.h
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TASYNC_BUFFER_PROCESSOR_H_
+#define _THRIFT_TASYNC_BUFFER_PROCESSOR_H_ 1
+
+#include <memory>
+#include <thrift/transport/TBufferTransports.h>
+
+namespace apache {
+namespace thrift {
+namespace async {
+
+class TAsyncBufferProcessor {
+public:
+ // Process data in "in", putting the result in "out".
+ // Call _return(true) when done, or _return(false) to
+ // forcefully close the connection (if applicable).
+ // "in" and "out" should be TMemoryBuffer or similar,
+ // not a wrapper around a socket.
+ virtual void process(std::function<void(bool healthy)> _return,
+ std::shared_ptr<transport::TBufferBase> ibuf,
+ std::shared_ptr<transport::TBufferBase> obuf) = 0;
+ virtual ~TAsyncBufferProcessor() = default;
+};
+}
+}
+} // apache::thrift::async
+
+#endif // #ifndef _THRIFT_TASYNC_BUFFER_PROCESSOR_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncChannel.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncChannel.cpp
new file mode 100644
index 000000000..01b91131f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncChannel.cpp
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/async/TAsyncChannel.h>
+
+namespace apache {
+namespace thrift {
+namespace async {
+
+void TAsyncChannel::sendAndRecvMessage(const VoidCallback& cob,
+ TMemoryBuffer* sendBuf,
+ TMemoryBuffer* recvBuf) {
+ std::function<void()> send_done
+ = std::bind(&TAsyncChannel::recvMessage, this, cob, recvBuf);
+
+ sendMessage(send_done, sendBuf);
+}
+}
+}
+} // apache::thrift::async
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncChannel.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncChannel.h
new file mode 100644
index 000000000..22cf38388
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncChannel.h
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_ASYNC_TASYNCCHANNEL_H_
+#define _THRIFT_ASYNC_TASYNCCHANNEL_H_ 1
+
+#include <functional>
+#include <memory>
+#include <thrift/Thrift.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+class TMemoryBuffer;
+}
+}
+}
+
+namespace apache {
+namespace thrift {
+namespace async {
+using apache::thrift::transport::TMemoryBuffer;
+
+class TAsyncChannel {
+public:
+ typedef std::function<void()> VoidCallback;
+
+ virtual ~TAsyncChannel() = default;
+
+ // is the channel in a good state?
+ virtual bool good() const = 0;
+ virtual bool error() const = 0;
+ virtual bool timedOut() const = 0;
+
+ /**
+ * Send a message over the channel.
+ */
+ virtual void sendMessage(const VoidCallback& cob,
+ apache::thrift::transport::TMemoryBuffer* message) = 0;
+
+ /**
+ * Receive a message from the channel.
+ */
+ virtual void recvMessage(const VoidCallback& cob,
+ apache::thrift::transport::TMemoryBuffer* message) = 0;
+
+ /**
+ * Send a message over the channel and receive a response.
+ */
+ virtual void sendAndRecvMessage(const VoidCallback& cob,
+ apache::thrift::transport::TMemoryBuffer* sendBuf,
+ apache::thrift::transport::TMemoryBuffer* recvBuf);
+};
+}
+}
+} // apache::thrift::async
+
+#endif // #ifndef _THRIFT_ASYNC_TASYNCCHANNEL_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncDispatchProcessor.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncDispatchProcessor.h
new file mode 100644
index 000000000..2a694ac53
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncDispatchProcessor.h
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef _THRIFT_ASYNC_TASYNCDISPATCHPROCESSOR_H_
+#define _THRIFT_ASYNC_TASYNCDISPATCHPROCESSOR_H_ 1
+
+#include <thrift/async/TAsyncProcessor.h>
+
+namespace apache {
+namespace thrift {
+namespace async {
+
+/**
+ * TAsyncDispatchProcessor is a helper class to parse the message header then
+ * call another function to dispatch based on the function name.
+ *
+ * Subclasses must implement dispatchCall() to dispatch on the function name.
+ */
+template <class Protocol_>
+class TAsyncDispatchProcessorT : public TAsyncProcessor {
+public:
+ void process(std::function<void(bool success)> _return,
+ std::shared_ptr<protocol::TProtocol> in,
+ std::shared_ptr<protocol::TProtocol> out) override {
+ protocol::TProtocol* inRaw = in.get();
+ protocol::TProtocol* outRaw = out.get();
+
+ // Try to dynamic cast to the template protocol type
+ auto* specificIn = dynamic_cast<Protocol_*>(inRaw);
+ auto* specificOut = dynamic_cast<Protocol_*>(outRaw);
+ if (specificIn && specificOut) {
+ return processFast(_return, specificIn, specificOut);
+ }
+
+ // Log the fact that we have to use the slow path
+ T_GENERIC_PROTOCOL(this, inRaw, specificIn);
+ T_GENERIC_PROTOCOL(this, outRaw, specificOut);
+
+ std::string fname;
+ protocol::TMessageType mtype;
+ int32_t seqid;
+ inRaw->readMessageBegin(fname, mtype, seqid);
+
+ // If this doesn't look like a valid call, log an error and return false so
+ // that the server will close the connection.
+ //
+ // (The old generated processor code used to try to skip a T_STRUCT and
+ // continue. However, that seems unsafe.)
+ if (mtype != protocol::T_CALL && mtype != protocol::T_ONEWAY) {
+ GlobalOutput.printf("received invalid message type %d from client", mtype);
+ _return(false);
+ return;
+ }
+
+ return this->dispatchCall(_return, inRaw, outRaw, fname, seqid);
+ }
+
+ void processFast(std::function<void(bool success)> _return,
+ Protocol_* in,
+ Protocol_* out) {
+ std::string fname;
+ protocol::TMessageType mtype;
+ int32_t seqid;
+ in->readMessageBegin(fname, mtype, seqid);
+
+ if (mtype != protocol::T_CALL && mtype != protocol::T_ONEWAY) {
+ GlobalOutput.printf("received invalid message type %d from client", mtype);
+ _return(false);
+ return;
+ }
+
+ return this->dispatchCallTemplated(_return, in, out, fname, seqid);
+ }
+
+ virtual void dispatchCall(std::function<void(bool ok)> _return,
+ apache::thrift::protocol::TProtocol* in,
+ apache::thrift::protocol::TProtocol* out,
+ const std::string& fname,
+ int32_t seqid) = 0;
+
+ virtual void dispatchCallTemplated(std::function<void(bool ok)> _return,
+ Protocol_* in,
+ Protocol_* out,
+ const std::string& fname,
+ int32_t seqid) = 0;
+};
+
+/**
+ * Non-templatized version of TAsyncDispatchProcessor,
+ * that doesn't bother trying to perform a dynamic_cast.
+ */
+class TAsyncDispatchProcessor : public TAsyncProcessor {
+public:
+ void process(std::function<void(bool success)> _return,
+ std::shared_ptr<protocol::TProtocol> in,
+ std::shared_ptr<protocol::TProtocol> out) override {
+ protocol::TProtocol* inRaw = in.get();
+ protocol::TProtocol* outRaw = out.get();
+
+ std::string fname;
+ protocol::TMessageType mtype;
+ int32_t seqid;
+ inRaw->readMessageBegin(fname, mtype, seqid);
+
+ // If this doesn't look like a valid call, log an error and return false so
+ // that the server will close the connection.
+ //
+ // (The old generated processor code used to try to skip a T_STRUCT and
+ // continue. However, that seems unsafe.)
+ if (mtype != protocol::T_CALL && mtype != protocol::T_ONEWAY) {
+ GlobalOutput.printf("received invalid message type %d from client", mtype);
+ _return(false);
+ return;
+ }
+
+ return dispatchCall(_return, inRaw, outRaw, fname, seqid);
+ }
+
+ virtual void dispatchCall(std::function<void(bool ok)> _return,
+ apache::thrift::protocol::TProtocol* in,
+ apache::thrift::protocol::TProtocol* out,
+ const std::string& fname,
+ int32_t seqid) = 0;
+};
+
+// Specialize TAsyncDispatchProcessorT for TProtocol and TDummyProtocol just to
+// use the generic TDispatchProcessor.
+template <>
+class TAsyncDispatchProcessorT<protocol::TDummyProtocol> : public TAsyncDispatchProcessor {};
+template <>
+class TAsyncDispatchProcessorT<protocol::TProtocol> : public TAsyncDispatchProcessor {};
+}
+}
+} // apache::thrift::async
+
+#endif // _THRIFT_ASYNC_TASYNCDISPATCHPROCESSOR_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncProcessor.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncProcessor.h
new file mode 100644
index 000000000..019233945
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncProcessor.h
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TASYNCPROCESSOR_H_
+#define _THRIFT_TASYNCPROCESSOR_H_ 1
+
+#include <thrift/protocol/TProtocol.h>
+#include <memory>
+#include <thrift/TProcessor.h>
+
+namespace apache {
+namespace thrift {
+namespace async {
+
+/**
+ * Async version of a TProcessor. It is not expected to complete by the time
+ * the call to process returns. Instead, it calls a cob to signal completion.
+ */
+
+class TAsyncProcessor {
+public:
+ virtual ~TAsyncProcessor() = default;
+
+ virtual void process(std::function<void(bool success)> _return,
+ std::shared_ptr<protocol::TProtocol> in,
+ std::shared_ptr<protocol::TProtocol> out) = 0;
+
+ void process(std::function<void(bool success)> _return,
+ std::shared_ptr<protocol::TProtocol> io) {
+ return process(_return, io, io);
+ }
+
+ std::shared_ptr<TProcessorEventHandler> getEventHandler() const { return eventHandler_; }
+
+ void setEventHandler(std::shared_ptr<TProcessorEventHandler> eventHandler) {
+ eventHandler_ = eventHandler;
+ }
+
+protected:
+ TAsyncProcessor() = default;
+
+ std::shared_ptr<TProcessorEventHandler> eventHandler_;
+};
+
+class TAsyncProcessorFactory {
+public:
+ virtual ~TAsyncProcessorFactory() = default;
+
+ /**
+ * Get the TAsyncProcessor to use for a particular connection.
+ *
+ * This method is always invoked in the same thread that the connection was
+ * accepted on. This generally means that this call does not need to be
+ * thread safe, as it will always be invoked from a single thread.
+ */
+ virtual std::shared_ptr<TAsyncProcessor> getProcessor(const TConnectionInfo& connInfo) = 0;
+};
+}
+}
+} // apache::thrift::async
+
+namespace apache {
+namespace thrift {
+ using apache::thrift::async::TAsyncProcessor;
+}
+}
+
+#endif // #ifndef _THRIFT_TASYNCPROCESSOR_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncProtocolProcessor.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncProtocolProcessor.cpp
new file mode 100644
index 000000000..cb5201bf6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncProtocolProcessor.cpp
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/async/TAsyncProtocolProcessor.h>
+
+using apache::thrift::transport::TBufferBase;
+using apache::thrift::protocol::TProtocol;
+
+namespace apache {
+namespace thrift {
+namespace async {
+
+void TAsyncProtocolProcessor::process(std::function<void(bool healthy)> _return,
+ std::shared_ptr<TBufferBase> ibuf,
+ std::shared_ptr<TBufferBase> obuf) {
+ std::shared_ptr<TProtocol> iprot(pfact_->getProtocol(ibuf));
+ std::shared_ptr<TProtocol> oprot(pfact_->getProtocol(obuf));
+ return underlying_
+ ->process(std::bind(&TAsyncProtocolProcessor::finish,
+ _return,
+ oprot,
+ std::placeholders::_1),
+ iprot,
+ oprot);
+}
+
+/* static */ void TAsyncProtocolProcessor::finish(
+ std::function<void(bool healthy)> _return,
+ std::shared_ptr<TProtocol> oprot,
+ bool healthy) {
+ (void)oprot;
+ // This is a stub function to hold a reference to oprot.
+ return _return(healthy);
+}
+}
+}
+} // apache::thrift::async
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncProtocolProcessor.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncProtocolProcessor.h
new file mode 100644
index 000000000..ace72b6dc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TAsyncProtocolProcessor.h
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TNAME_ME_H_
+#define _THRIFT_TNAME_ME_H_ 1
+
+#include <thrift/async/TAsyncProcessor.h>
+#include <thrift/async/TAsyncBufferProcessor.h>
+#include <thrift/protocol/TProtocol.h>
+
+namespace apache {
+namespace thrift {
+namespace async {
+
+class TAsyncProtocolProcessor : public TAsyncBufferProcessor {
+public:
+ TAsyncProtocolProcessor(std::shared_ptr<TAsyncProcessor> underlying,
+ std::shared_ptr<apache::thrift::protocol::TProtocolFactory> pfact)
+ : underlying_(underlying), pfact_(pfact) {}
+
+ void process(std::function<void(bool healthy)> _return,
+ std::shared_ptr<apache::thrift::transport::TBufferBase> ibuf,
+ std::shared_ptr<apache::thrift::transport::TBufferBase> obuf) override;
+
+ ~TAsyncProtocolProcessor() override = default;
+
+private:
+ static void finish(std::function<void(bool healthy)> _return,
+ std::shared_ptr<apache::thrift::protocol::TProtocol> oprot,
+ bool healthy);
+
+ std::shared_ptr<TAsyncProcessor> underlying_;
+ std::shared_ptr<apache::thrift::protocol::TProtocolFactory> pfact_;
+};
+}
+}
+} // apache::thrift::async
+
+#endif // #ifndef _THRIFT_TNAME_ME_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TConcurrentClientSyncInfo.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TConcurrentClientSyncInfo.cpp
new file mode 100644
index 000000000..0dac52458
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TConcurrentClientSyncInfo.cpp
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <limits>
+#include <memory>
+#include <thrift/TApplicationException.h>
+#include <thrift/async/TConcurrentClientSyncInfo.h>
+#include <thrift/transport/TTransportException.h>
+
+namespace apache { namespace thrift { namespace async {
+
+using namespace ::apache::thrift::concurrency;
+
+TConcurrentClientSyncInfo::TConcurrentClientSyncInfo() :
+ stop_(false),
+ seqidMutex_(),
+ // test rollover all the time
+ nextseqid_((std::numeric_limits<int32_t>::max)()-10),
+ seqidToMonitorMap_(),
+ freeMonitors_(),
+ writeMutex_(),
+ readMutex_(),
+ recvPending_(false),
+ wakeupSomeone_(false),
+ seqidPending_(0),
+ fnamePending_(),
+ mtypePending_(::apache::thrift::protocol::T_CALL)
+{
+ freeMonitors_.reserve(MONITOR_CACHE_SIZE);
+}
+
+bool TConcurrentClientSyncInfo::getPending(
+ std::string &fname,
+ ::apache::thrift::protocol::TMessageType &mtype,
+ int32_t &rseqid)
+{
+ if(stop_)
+ throwDeadConnection_();
+ wakeupSomeone_ = false;
+ if(recvPending_)
+ {
+ recvPending_ = false;
+ rseqid = seqidPending_;
+ fname = fnamePending_;
+ mtype = mtypePending_;
+ return true;
+ }
+ return false;
+}
+
+void TConcurrentClientSyncInfo::updatePending(
+ const std::string &fname,
+ ::apache::thrift::protocol::TMessageType mtype,
+ int32_t rseqid)
+{
+ recvPending_ = true;
+ seqidPending_ = rseqid;
+ fnamePending_ = fname;
+ mtypePending_ = mtype;
+ MonitorPtr monitor;
+ {
+ Guard seqidGuard(seqidMutex_);
+ auto i = seqidToMonitorMap_.find(rseqid);
+ if(i == seqidToMonitorMap_.end())
+ throwBadSeqId_();
+ monitor = i->second;
+ }
+ monitor->notify();
+}
+
+void TConcurrentClientSyncInfo::waitForWork(int32_t seqid)
+{
+ MonitorPtr m;
+ {
+ Guard seqidGuard(seqidMutex_);
+ m = seqidToMonitorMap_[seqid];
+ }
+ while(true)
+ {
+ // be very careful about setting state in this loop that affects waking up. You may exit
+ // this function, attempt to grab some work, and someone else could have beaten you (or not
+ // left) the read mutex, and that will put you right back in this loop, with the mangled
+ // state you left behind.
+ if(stop_)
+ throwDeadConnection_();
+ if(wakeupSomeone_)
+ return;
+ if(recvPending_ && seqidPending_ == seqid)
+ return;
+ m->waitForever();
+ }
+}
+
+void TConcurrentClientSyncInfo::throwBadSeqId_()
+{
+ throw apache::thrift::TApplicationException(
+ TApplicationException::BAD_SEQUENCE_ID,
+ "server sent a bad seqid");
+}
+
+void TConcurrentClientSyncInfo::throwDeadConnection_()
+{
+ throw apache::thrift::transport::TTransportException(
+ apache::thrift::transport::TTransportException::NOT_OPEN,
+ "this client died on another thread, and is now in an unusable state");
+}
+
+void TConcurrentClientSyncInfo::wakeupAnyone_(const Guard &)
+{
+ wakeupSomeone_ = true;
+ if(!seqidToMonitorMap_.empty())
+ {
+ // The monitor map maps integers to monitors. Larger integers are more recent
+ // messages. Since this is ordered, it means that the last element is the most recent.
+ // We are trying to guess which thread will have its message complete next, so we are picking
+ // the most recent. The oldest message is likely to be some polling, long lived message.
+ // If we guess right, the thread we wake up will handle the message that comes in.
+ // If we guess wrong, the thread we wake up will hand off the work to the correct thread,
+ // costing us an extra context switch.
+ seqidToMonitorMap_.rbegin()->second->notify();
+ }
+}
+
+void TConcurrentClientSyncInfo::markBad_(const Guard &)
+{
+ wakeupSomeone_ = true;
+ stop_ = true;
+ for(auto & i : seqidToMonitorMap_)
+ i.second->notify();
+}
+
+TConcurrentClientSyncInfo::MonitorPtr
+TConcurrentClientSyncInfo::newMonitor_(const Guard &)
+{
+ if(freeMonitors_.empty())
+ return std::make_shared<Monitor>(&readMutex_);
+ MonitorPtr retval;
+ //swapping to avoid an atomic operation
+ retval.swap(freeMonitors_.back());
+ freeMonitors_.pop_back();
+ return retval;
+}
+
+void TConcurrentClientSyncInfo::deleteMonitor_(
+ const Guard &,
+ TConcurrentClientSyncInfo::MonitorPtr &m) /*noexcept*/
+{
+ if(freeMonitors_.size() > MONITOR_CACHE_SIZE)
+ {
+ m.reset();
+ return;
+ }
+ //freeMonitors_ was reserved up to MONITOR_CACHE_SIZE in the ctor,
+ //so this shouldn't throw
+ freeMonitors_.push_back(TConcurrentClientSyncInfo::MonitorPtr());
+ //swapping to avoid an atomic operation
+ m.swap(freeMonitors_.back());
+}
+
+int32_t TConcurrentClientSyncInfo::generateSeqId()
+{
+ Guard seqidGuard(seqidMutex_);
+ if(stop_)
+ throwDeadConnection_();
+
+ if(!seqidToMonitorMap_.empty())
+ if(nextseqid_ == seqidToMonitorMap_.begin()->first)
+ throw apache::thrift::TApplicationException(
+ TApplicationException::BAD_SEQUENCE_ID,
+ "about to repeat a seqid");
+ int32_t newSeqId = nextseqid_++;
+ seqidToMonitorMap_[newSeqId] = newMonitor_(seqidGuard);
+ return newSeqId;
+}
+
+TConcurrentRecvSentry::TConcurrentRecvSentry(TConcurrentClientSyncInfo *sync, int32_t seqid) :
+ sync_(*sync),
+ seqid_(seqid),
+ committed_(false)
+{
+ sync_.getReadMutex().lock();
+}
+
+TConcurrentRecvSentry::~TConcurrentRecvSentry()
+{
+ {
+ Guard seqidGuard(sync_.seqidMutex_);
+ sync_.deleteMonitor_(seqidGuard, sync_.seqidToMonitorMap_[seqid_]);
+
+ sync_.seqidToMonitorMap_.erase(seqid_);
+ if(committed_)
+ sync_.wakeupAnyone_(seqidGuard);
+ else
+ sync_.markBad_(seqidGuard);
+ }
+ sync_.getReadMutex().unlock();
+}
+
+void TConcurrentRecvSentry::commit()
+{
+ committed_ = true;
+}
+
+TConcurrentSendSentry::TConcurrentSendSentry(TConcurrentClientSyncInfo *sync) :
+ sync_(*sync),
+ committed_(false)
+{
+ sync_.getWriteMutex().lock();
+}
+
+TConcurrentSendSentry::~TConcurrentSendSentry()
+{
+ if(!committed_)
+ {
+ Guard seqidGuard(sync_.seqidMutex_);
+ sync_.markBad_(seqidGuard);
+ }
+ sync_.getWriteMutex().unlock();
+}
+
+void TConcurrentSendSentry::commit()
+{
+ committed_ = true;
+}
+
+
+}}} // apache::thrift::async
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TConcurrentClientSyncInfo.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TConcurrentClientSyncInfo.h
new file mode 100644
index 000000000..0bc5eb565
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TConcurrentClientSyncInfo.h
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef _THRIFT_TCONCURRENTCLIENTSYNCINFO_H_
+#define _THRIFT_TCONCURRENTCLIENTSYNCINFO_H_ 1
+
+#include <thrift/protocol/TProtocol.h>
+#include <thrift/concurrency/Mutex.h>
+#include <thrift/concurrency/Monitor.h>
+#include <memory>
+#include <vector>
+#include <string>
+#include <map>
+
+namespace apache {
+namespace thrift {
+namespace async {
+
+class TConcurrentClientSyncInfo;
+
+class TConcurrentSendSentry {
+public:
+ explicit TConcurrentSendSentry(TConcurrentClientSyncInfo* sync);
+ ~TConcurrentSendSentry();
+
+ void commit();
+
+private:
+ TConcurrentClientSyncInfo& sync_;
+ bool committed_;
+};
+
+class TConcurrentRecvSentry {
+public:
+ TConcurrentRecvSentry(TConcurrentClientSyncInfo* sync, int32_t seqid);
+ ~TConcurrentRecvSentry();
+
+ void commit();
+
+private:
+ TConcurrentClientSyncInfo& sync_;
+ int32_t seqid_;
+ bool committed_;
+};
+
+class TConcurrentClientSyncInfo {
+private: // typedefs
+ typedef std::shared_ptr< ::apache::thrift::concurrency::Monitor> MonitorPtr;
+ typedef std::map<int32_t, MonitorPtr> MonitorMap;
+
+public:
+ TConcurrentClientSyncInfo();
+
+ int32_t generateSeqId();
+
+ bool getPending(std::string& fname,
+ ::apache::thrift::protocol::TMessageType& mtype,
+ int32_t& rseqid); /* requires readMutex_ */
+
+ void updatePending(const std::string& fname,
+ ::apache::thrift::protocol::TMessageType mtype,
+ int32_t rseqid); /* requires readMutex_ */
+
+ void waitForWork(int32_t seqid); /* requires readMutex_ */
+
+ ::apache::thrift::concurrency::Mutex& getReadMutex() { return readMutex_; }
+ ::apache::thrift::concurrency::Mutex& getWriteMutex() { return writeMutex_; }
+
+private: // constants
+ enum { MONITOR_CACHE_SIZE = 10 };
+
+private: // functions
+ MonitorPtr newMonitor_(
+ const ::apache::thrift::concurrency::Guard& seqidGuard); /* requires seqidMutex_ */
+ void deleteMonitor_(const ::apache::thrift::concurrency::Guard& seqidGuard, MonitorPtr& m);
+ /*noexcept*/ /* requires seqidMutex_ */
+ void wakeupAnyone_(
+ const ::apache::thrift::concurrency::Guard& seqidGuard); /* requires seqidMutex_ */
+ void markBad_(const ::apache::thrift::concurrency::Guard& seqidGuard); /* requires seqidMutex_ */
+ void throwBadSeqId_();
+ void throwDeadConnection_();
+
+private: // data members
+ volatile bool stop_;
+
+ ::apache::thrift::concurrency::Mutex seqidMutex_;
+ // begin seqidMutex_ protected members
+ int32_t nextseqid_;
+ MonitorMap seqidToMonitorMap_;
+ std::vector<MonitorPtr> freeMonitors_;
+ // end seqidMutex_ protected members
+
+ ::apache::thrift::concurrency::Mutex writeMutex_;
+
+ ::apache::thrift::concurrency::Mutex readMutex_;
+ // begin readMutex_ protected members
+ bool recvPending_;
+ bool wakeupSomeone_;
+ int32_t seqidPending_;
+ std::string fnamePending_;
+ ::apache::thrift::protocol::TMessageType mtypePending_;
+ // end readMutex_ protected members
+
+ friend class TConcurrentSendSentry;
+ friend class TConcurrentRecvSentry;
+};
+}
+}
+} // apache::thrift::async
+
+#endif // _THRIFT_TCONCURRENTCLIENTSYNCINFO_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TEvhttpClientChannel.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TEvhttpClientChannel.cpp
new file mode 100644
index 000000000..765659685
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TEvhttpClientChannel.cpp
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/async/TEvhttpClientChannel.h>
+#include <evhttp.h>
+#include <event2/buffer.h>
+#include <event2/buffer_compat.h>
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/protocol/TProtocolException.h>
+
+#include <iostream>
+#include <sstream>
+
+using namespace apache::thrift::protocol;
+using apache::thrift::transport::TTransportException;
+
+namespace apache {
+namespace thrift {
+namespace async {
+
+TEvhttpClientChannel::TEvhttpClientChannel(const std::string& host,
+ const std::string& path,
+ const char* address,
+ int port,
+ struct event_base* eb,
+ struct evdns_base* dnsbase)
+
+ : host_(host), path_(path), conn_(nullptr) {
+ conn_ = evhttp_connection_base_new(eb, dnsbase, address, port);
+ if (conn_ == nullptr) {
+ throw TException("evhttp_connection_new failed");
+ }
+}
+
+TEvhttpClientChannel::~TEvhttpClientChannel() {
+ if (conn_ != nullptr) {
+ evhttp_connection_free(conn_);
+ }
+}
+
+void TEvhttpClientChannel::sendAndRecvMessage(const VoidCallback& cob,
+ apache::thrift::transport::TMemoryBuffer* sendBuf,
+ apache::thrift::transport::TMemoryBuffer* recvBuf) {
+ struct evhttp_request* req = evhttp_request_new(response, this);
+ if (req == nullptr) {
+ throw TException("evhttp_request_new failed");
+ }
+
+ int rv;
+
+ rv = evhttp_add_header(req->output_headers, "Host", host_.c_str());
+ if (rv != 0) {
+ throw TException("evhttp_add_header failed");
+ }
+
+ rv = evhttp_add_header(req->output_headers, "Content-Type", "application/x-thrift");
+ if (rv != 0) {
+ throw TException("evhttp_add_header failed");
+ }
+
+ uint8_t* obuf;
+ uint32_t sz;
+ sendBuf->getBuffer(&obuf, &sz);
+ rv = evbuffer_add(req->output_buffer, obuf, sz);
+ if (rv != 0) {
+ throw TException("evbuffer_add failed");
+ }
+
+ rv = evhttp_make_request(conn_, req, EVHTTP_REQ_POST, path_.c_str());
+ if (rv != 0) {
+ throw TException("evhttp_make_request failed");
+ }
+
+ completionQueue_.push(Completion(cob, recvBuf));
+}
+
+void TEvhttpClientChannel::sendMessage(const VoidCallback& cob,
+ apache::thrift::transport::TMemoryBuffer* message) {
+ (void)cob;
+ (void)message;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "Unexpected call to TEvhttpClientChannel::sendMessage");
+}
+
+void TEvhttpClientChannel::recvMessage(const VoidCallback& cob,
+ apache::thrift::transport::TMemoryBuffer* message) {
+ (void)cob;
+ (void)message;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "Unexpected call to TEvhttpClientChannel::recvMessage");
+}
+
+void TEvhttpClientChannel::finish(struct evhttp_request* req) {
+ assert(!completionQueue_.empty());
+ Completion completion = completionQueue_.front();
+ completionQueue_.pop();
+ if (req == nullptr) {
+ try {
+ completion.first();
+ } catch (const TTransportException& e) {
+ if (e.getType() == TTransportException::END_OF_FILE)
+ throw TException("connect failed");
+ else
+ throw;
+ }
+ return;
+ } else if (req->response_code != 200) {
+ try {
+ completion.first();
+ } catch (const TTransportException& e) {
+ std::stringstream ss;
+ ss << "server returned code " << req->response_code;
+ if (req->response_code_line)
+ ss << ": " << req->response_code_line;
+ if (e.getType() == TTransportException::END_OF_FILE)
+ throw TException(ss.str());
+ else
+ throw;
+ }
+ return;
+ }
+ completion.second->resetBuffer(EVBUFFER_DATA(req->input_buffer),
+ static_cast<uint32_t>(EVBUFFER_LENGTH(req->input_buffer)));
+ completion.first();
+ return;
+}
+
+/* static */ void TEvhttpClientChannel::response(struct evhttp_request* req, void* arg) {
+ auto* self = (TEvhttpClientChannel*)arg;
+ try {
+ self->finish(req);
+ } catch (std::exception& e) {
+ // don't propagate a C++ exception in C code (e.g. libevent)
+ std::cerr << "TEvhttpClientChannel::response exception thrown (ignored): " << e.what()
+ << std::endl;
+ }
+}
+}
+}
+} // apache::thrift::async
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TEvhttpClientChannel.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TEvhttpClientChannel.h
new file mode 100644
index 000000000..f74272665
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TEvhttpClientChannel.h
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TEVHTTP_CLIENT_CHANNEL_H_
+#define _THRIFT_TEVHTTP_CLIENT_CHANNEL_H_ 1
+
+#include <queue>
+#include <string>
+#include <utility>
+#include <memory>
+#include <thrift/async/TAsyncChannel.h>
+
+struct event_base;
+struct evdns_base;
+struct evhttp_connection;
+struct evhttp_request;
+
+namespace apache {
+namespace thrift {
+namespace transport {
+class TMemoryBuffer;
+}
+}
+}
+
+namespace apache {
+namespace thrift {
+namespace async {
+
+class TEvhttpClientChannel : public TAsyncChannel {
+public:
+ using TAsyncChannel::VoidCallback;
+
+ TEvhttpClientChannel(const std::string& host,
+ const std::string& path,
+ const char* address,
+ int port,
+ struct event_base* eb,
+ struct evdns_base *dnsbase = nullptr);
+ ~TEvhttpClientChannel() override;
+
+ void sendAndRecvMessage(const VoidCallback& cob,
+ apache::thrift::transport::TMemoryBuffer* sendBuf,
+ apache::thrift::transport::TMemoryBuffer* recvBuf) override;
+
+ void sendMessage(const VoidCallback& cob,
+ apache::thrift::transport::TMemoryBuffer* message) override;
+ void recvMessage(const VoidCallback& cob,
+ apache::thrift::transport::TMemoryBuffer* message) override;
+
+ void finish(struct evhttp_request* req);
+
+ // XXX
+ bool good() const override { return true; }
+ bool error() const override { return false; }
+ bool timedOut() const override { return false; }
+
+private:
+ static void response(struct evhttp_request* req, void* arg);
+
+ std::string host_;
+ std::string path_;
+ typedef std::pair<VoidCallback, apache::thrift::transport::TMemoryBuffer*> Completion;
+ typedef std::queue<Completion> CompletionQueue;
+ CompletionQueue completionQueue_;
+ struct evhttp_connection* conn_;
+};
+}
+}
+} // apache::thrift::async
+
+#endif // #ifndef _THRIFT_TEVHTTP_CLIENT_CHANNEL_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TEvhttpServer.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TEvhttpServer.cpp
new file mode 100644
index 000000000..7d2cf21c0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TEvhttpServer.cpp
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/async/TEvhttpServer.h>
+#include <thrift/async/TAsyncBufferProcessor.h>
+#include <thrift/transport/TBufferTransports.h>
+#include <memory>
+#include <evhttp.h>
+#include <event2/buffer.h>
+#include <event2/buffer_compat.h>
+#include <iostream>
+
+#ifndef HTTP_INTERNAL // libevent < 2
+#define HTTP_INTERNAL 500
+#endif
+
+using apache::thrift::transport::TMemoryBuffer;
+using std::shared_ptr;
+
+namespace apache {
+namespace thrift {
+namespace async {
+
+struct TEvhttpServer::RequestContext {
+ struct evhttp_request* req;
+ std::shared_ptr<apache::thrift::transport::TMemoryBuffer> ibuf;
+ std::shared_ptr<apache::thrift::transport::TMemoryBuffer> obuf;
+
+ RequestContext(struct evhttp_request* req);
+};
+
+TEvhttpServer::TEvhttpServer(std::shared_ptr<TAsyncBufferProcessor> processor)
+ : processor_(processor), eb_(nullptr), eh_(nullptr) {
+}
+
+TEvhttpServer::TEvhttpServer(std::shared_ptr<TAsyncBufferProcessor> processor, int port)
+ : processor_(processor), eb_(nullptr), eh_(nullptr) {
+ // Create event_base and evhttp.
+ eb_ = event_base_new();
+ if (eb_ == nullptr) {
+ throw TException("event_base_new failed");
+ }
+ eh_ = evhttp_new(eb_);
+ if (eh_ == nullptr) {
+ event_base_free(eb_);
+ throw TException("evhttp_new failed");
+ }
+
+ // Bind to port.
+ int ret = evhttp_bind_socket(eh_, nullptr, port);
+ if (ret < 0) {
+ evhttp_free(eh_);
+ event_base_free(eb_);
+ throw TException("evhttp_bind_socket failed");
+ }
+
+ // Register a handler. If you use the other constructor,
+ // you will want to do this yourself.
+ // Don't forget to unregister before destorying this TEvhttpServer.
+ evhttp_set_cb(eh_, "/", request, (void*)this);
+}
+
+TEvhttpServer::~TEvhttpServer() {
+ if (eh_ != nullptr) {
+ evhttp_free(eh_);
+ }
+ if (eb_ != nullptr) {
+ event_base_free(eb_);
+ }
+}
+
+int TEvhttpServer::serve() {
+ if (eb_ == nullptr) {
+ throw TException("Unexpected call to TEvhttpServer::serve");
+ }
+ return event_base_dispatch(eb_);
+}
+
+TEvhttpServer::RequestContext::RequestContext(struct evhttp_request* req)
+ : req(req),
+ ibuf(new TMemoryBuffer(EVBUFFER_DATA(req->input_buffer),
+ static_cast<uint32_t>(EVBUFFER_LENGTH(req->input_buffer)))),
+ obuf(new TMemoryBuffer()) {
+}
+
+void TEvhttpServer::request(struct evhttp_request* req, void* self) {
+ try {
+ static_cast<TEvhttpServer*>(self)->process(req);
+ } catch (std::exception& e) {
+ evhttp_send_reply(req, HTTP_INTERNAL, e.what(), nullptr);
+ }
+}
+
+void TEvhttpServer::process(struct evhttp_request* req) {
+ auto* ctx = new RequestContext(req);
+ return processor_->process(std::bind(&TEvhttpServer::complete,
+ this,
+ ctx,
+ std::placeholders::_1),
+ ctx->ibuf,
+ ctx->obuf);
+}
+
+void TEvhttpServer::complete(RequestContext* ctx, bool success) {
+ (void)success;
+ std::unique_ptr<RequestContext> ptr(ctx);
+
+ int code = success ? 200 : 400;
+ const char* reason = success ? "OK" : "Bad Request";
+
+ int rv = evhttp_add_header(ctx->req->output_headers, "Content-Type", "application/x-thrift");
+ if (rv != 0) {
+ // TODO: Log an error.
+ std::cerr << "evhttp_add_header failed " << __FILE__ << ":" << __LINE__ << std::endl;
+ }
+
+ struct evbuffer* buf = evbuffer_new();
+ if (buf == nullptr) {
+ // TODO: Log an error.
+ std::cerr << "evbuffer_new failed " << __FILE__ << ":" << __LINE__ << std::endl;
+ } else {
+ uint8_t* obuf;
+ uint32_t sz;
+ ctx->obuf->getBuffer(&obuf, &sz);
+ int ret = evbuffer_add(buf, obuf, sz);
+ if (ret != 0) {
+ // TODO: Log an error.
+ std::cerr << "evhttp_add failed with " << ret << " " << __FILE__ << ":" << __LINE__
+ << std::endl;
+ }
+ }
+
+ evhttp_send_reply(ctx->req, code, reason, buf);
+ if (buf != nullptr) {
+ evbuffer_free(buf);
+ }
+}
+
+struct event_base* TEvhttpServer::getEventBase() {
+ return eb_;
+}
+}
+}
+} // apache::thrift::async
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TEvhttpServer.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TEvhttpServer.h
new file mode 100644
index 000000000..c5bf3b6ee
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/async/TEvhttpServer.h
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TEVHTTP_SERVER_H_
+#define _THRIFT_TEVHTTP_SERVER_H_ 1
+
+#include <memory>
+
+struct event_base;
+struct evhttp;
+struct evhttp_request;
+
+namespace apache {
+namespace thrift {
+namespace async {
+
+class TAsyncBufferProcessor;
+
+class TEvhttpServer {
+public:
+ /**
+ * Create a TEvhttpServer for use with an external evhttp instance.
+ * Must be manually installed with evhttp_set_cb, using
+ * TEvhttpServer::request as the callback and the
+ * address of the server as the extra arg.
+ * Do not call "serve" on this server.
+ */
+ TEvhttpServer(std::shared_ptr<TAsyncBufferProcessor> processor);
+
+ /**
+ * Create a TEvhttpServer with an embedded event_base and evhttp,
+ * listening on port and responding on the endpoint "/".
+ * Call "serve" on this server to serve forever.
+ */
+ TEvhttpServer(std::shared_ptr<TAsyncBufferProcessor> processor, int port);
+
+ ~TEvhttpServer();
+
+ static void request(struct evhttp_request* req, void* self);
+ int serve();
+
+ struct event_base* getEventBase();
+
+private:
+ struct RequestContext;
+
+ void process(struct evhttp_request* req);
+ void complete(RequestContext* ctx, bool success);
+
+ std::shared_ptr<TAsyncBufferProcessor> processor_;
+ struct event_base* eb_;
+ struct evhttp* eh_;
+};
+}
+}
+} // apache::thrift::async
+
+#endif // #ifndef _THRIFT_TEVHTTP_SERVER_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Exception.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Exception.h
new file mode 100644
index 000000000..947fc9f04
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Exception.h
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_CONCURRENCY_EXCEPTION_H_
+#define _THRIFT_CONCURRENCY_EXCEPTION_H_ 1
+
+#include <exception>
+#include <thrift/Thrift.h>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+
+class NoSuchTaskException : public apache::thrift::TException {};
+
+class UncancellableTaskException : public apache::thrift::TException {};
+
+class InvalidArgumentException : public apache::thrift::TException {};
+
+class IllegalStateException : public apache::thrift::TException {
+public:
+ IllegalStateException() = default;
+ IllegalStateException(const std::string& message) : TException(message) {}
+};
+
+class TimedOutException : public apache::thrift::TException {
+public:
+ TimedOutException() : TException("TimedOutException"){};
+ TimedOutException(const std::string& message) : TException(message) {}
+};
+
+class TooManyPendingTasksException : public apache::thrift::TException {
+public:
+ TooManyPendingTasksException() : TException("TooManyPendingTasksException"){};
+ TooManyPendingTasksException(const std::string& message) : TException(message) {}
+};
+
+class SystemResourceException : public apache::thrift::TException {
+public:
+ SystemResourceException() = default;
+
+ SystemResourceException(const std::string& message) : TException(message) {}
+};
+}
+}
+} // apache::thrift::concurrency
+
+#endif // #ifndef _THRIFT_CONCURRENCY_EXCEPTION_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/FunctionRunner.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/FunctionRunner.h
new file mode 100644
index 000000000..468834416
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/FunctionRunner.h
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_CONCURRENCY_FUNCTION_RUNNER_H
+#define _THRIFT_CONCURRENCY_FUNCTION_RUNNER_H 1
+
+#include <thrift/concurrency/Thread.h>
+#include <memory>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+
+/**
+ * Convenient implementation of Runnable that will execute arbitrary callbacks.
+ * Interfaces are provided to accept both a generic 'void(void)' callback, and
+ * a 'void* (void*)' pthread_create-style callback.
+ *
+ * Example use:
+ * void* my_thread_main(void* arg);
+ * shared_ptr<ThreadFactory> factory = ...;
+ * // To create a thread that executes my_thread_main once:
+ * shared_ptr<Thread> thread = factory->newThread(
+ * FunctionRunner::create(my_thread_main, some_argument));
+ * thread->start();
+ *
+ * bool A::foo();
+ * A* a = new A();
+ * // To create a thread that executes a.foo() every 100 milliseconds:
+ * factory->newThread(FunctionRunner::create(
+ * std::bind(&A::foo, a), 100))->start();
+ *
+ */
+
+class FunctionRunner : public Runnable {
+public:
+ // This is the type of callback 'pthread_create()' expects.
+ typedef void* (*PthreadFuncPtr)(void* arg);
+ // This a fully-generic void(void) callback for custom bindings.
+ typedef std::function<void()> VoidFunc;
+
+ typedef std::function<bool()> BoolFunc;
+
+ /**
+ * Syntactic sugar to make it easier to create new FunctionRunner
+ * objects wrapped in shared_ptr.
+ */
+ static std::shared_ptr<FunctionRunner> create(const VoidFunc& cob) {
+ return std::shared_ptr<FunctionRunner>(new FunctionRunner(cob));
+ }
+
+ static std::shared_ptr<FunctionRunner> create(PthreadFuncPtr func, void* arg) {
+ return std::shared_ptr<FunctionRunner>(new FunctionRunner(func, arg));
+ }
+
+private:
+ static void pthread_func_wrapper(PthreadFuncPtr func, void* arg) {
+ // discard return value
+ func(arg);
+ }
+
+public:
+ /**
+ * Given a 'pthread_create' style callback, this FunctionRunner will
+ * execute the given callback. Note that the 'void*' return value is ignored.
+ */
+ FunctionRunner(PthreadFuncPtr func, void* arg)
+ : func_(std::bind(pthread_func_wrapper, func, arg)), intervalMs_(-1) {}
+
+ /**
+ * Given a generic callback, this FunctionRunner will execute it.
+ */
+ FunctionRunner(const VoidFunc& cob) : func_(cob), intervalMs_(-1) {}
+
+ /**
+ * Given a bool foo(...) type callback, FunctionRunner will execute
+ * the callback repeatedly with 'intervalMs' milliseconds between the calls,
+ * until it returns false. Note that the actual interval between calls will
+ * be intervalMs plus execution time of the callback.
+ */
+ FunctionRunner(const BoolFunc& cob, int intervalMs) : repFunc_(cob), intervalMs_(intervalMs) {}
+
+ void run() override {
+ if (repFunc_) {
+ while (repFunc_()) {
+ THRIFT_SLEEP_USEC(intervalMs_ * 1000);
+ }
+ } else {
+ func_();
+ }
+ }
+
+private:
+ VoidFunc func_;
+ BoolFunc repFunc_;
+ int intervalMs_;
+};
+}
+}
+} // apache::thrift::concurrency
+
+#endif // #ifndef _THRIFT_CONCURRENCY_FUNCTION_RUNNER_H
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Monitor.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Monitor.cpp
new file mode 100644
index 000000000..dc92efd6c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Monitor.cpp
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+
+#include <thrift/concurrency/Monitor.h>
+#include <thrift/concurrency/Exception.h>
+#include <thrift/transport/PlatformSocket.h>
+#include <assert.h>
+
+#include <condition_variable>
+#include <chrono>
+#include <thread>
+#include <mutex>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+
+/**
+ * Monitor implementation using the std thread library
+ *
+ * @version $Id:$
+ */
+class Monitor::Impl {
+
+public:
+ Impl() : ownedMutex_(new Mutex()), conditionVariable_(), mutex_(nullptr) { init(ownedMutex_.get()); }
+
+ Impl(Mutex* mutex) : ownedMutex_(), conditionVariable_(), mutex_(nullptr) { init(mutex); }
+
+ Impl(Monitor* monitor) : ownedMutex_(), conditionVariable_(), mutex_(nullptr) {
+ init(&(monitor->mutex()));
+ }
+
+ Mutex& mutex() { return *mutex_; }
+ void lock() { mutex_->lock(); }
+ void unlock() { mutex_->unlock(); }
+
+ /**
+ * Exception-throwing version of waitForTimeRelative(), called simply
+ * wait(int64) for historical reasons. Timeout is in milliseconds.
+ *
+ * If the condition occurs, this function returns cleanly; on timeout or
+ * error an exception is thrown.
+ */
+ void wait(const std::chrono::milliseconds &timeout) {
+ int result = waitForTimeRelative(timeout);
+ if (result == THRIFT_ETIMEDOUT) {
+ throw TimedOutException();
+ } else if (result != 0) {
+ throw TException("Monitor::wait() failed");
+ }
+ }
+
+ /**
+ * Waits until the specified timeout in milliseconds for the condition to
+ * occur, or waits forever if timeout is zero.
+ *
+ * Returns 0 if condition occurs, THRIFT_ETIMEDOUT on timeout, or an error code.
+ */
+ int waitForTimeRelative(const std::chrono::milliseconds &timeout) {
+ if (timeout.count() == 0) {
+ return waitForever();
+ }
+
+ assert(mutex_);
+ auto* mutexImpl = static_cast<std::timed_mutex*>(mutex_->getUnderlyingImpl());
+ assert(mutexImpl);
+
+ std::unique_lock<std::timed_mutex> lock(*mutexImpl, std::adopt_lock);
+ bool timedout = (conditionVariable_.wait_for(lock, timeout)
+ == std::cv_status::timeout);
+ lock.release();
+ return (timedout ? THRIFT_ETIMEDOUT : 0);
+ }
+
+ /**
+ * Waits until the absolute time specified by abstime.
+ * Returns 0 if condition occurs, THRIFT_ETIMEDOUT on timeout, or an error code.
+ */
+ int waitForTime(const std::chrono::time_point<std::chrono::steady_clock>& abstime) {
+ assert(mutex_);
+ auto* mutexImpl = static_cast<std::timed_mutex*>(mutex_->getUnderlyingImpl());
+ assert(mutexImpl);
+
+ std::unique_lock<std::timed_mutex> lock(*mutexImpl, std::adopt_lock);
+ bool timedout = (conditionVariable_.wait_until(lock, abstime)
+ == std::cv_status::timeout);
+ lock.release();
+ return (timedout ? THRIFT_ETIMEDOUT : 0);
+ }
+
+ /**
+ * Waits forever until the condition occurs.
+ * Returns 0 if condition occurs, or an error code otherwise.
+ */
+ int waitForever() {
+ assert(mutex_);
+ auto* mutexImpl = static_cast<std::timed_mutex*>(mutex_->getUnderlyingImpl());
+ assert(mutexImpl);
+
+ std::unique_lock<std::timed_mutex> lock(*mutexImpl, std::adopt_lock);
+ conditionVariable_.wait(lock);
+ lock.release();
+ return 0;
+ }
+
+ void notify() { conditionVariable_.notify_one(); }
+
+ void notifyAll() { conditionVariable_.notify_all(); }
+
+private:
+ void init(Mutex* mutex) { mutex_ = mutex; }
+
+ const std::unique_ptr<Mutex> ownedMutex_;
+ std::condition_variable_any conditionVariable_;
+ Mutex* mutex_;
+};
+
+Monitor::Monitor() : impl_(new Monitor::Impl()) {
+}
+Monitor::Monitor(Mutex* mutex) : impl_(new Monitor::Impl(mutex)) {
+}
+Monitor::Monitor(Monitor* monitor) : impl_(new Monitor::Impl(monitor)) {
+}
+
+Monitor::~Monitor() {
+ delete impl_;
+}
+
+Mutex& Monitor::mutex() const {
+ return const_cast<Monitor::Impl*>(impl_)->mutex();
+}
+
+void Monitor::lock() const {
+ const_cast<Monitor::Impl*>(impl_)->lock();
+}
+
+void Monitor::unlock() const {
+ const_cast<Monitor::Impl*>(impl_)->unlock();
+}
+
+void Monitor::wait(const std::chrono::milliseconds &timeout) const {
+ const_cast<Monitor::Impl*>(impl_)->wait(timeout);
+}
+
+int Monitor::waitForTime(const std::chrono::time_point<std::chrono::steady_clock>& abstime) const {
+ return const_cast<Monitor::Impl*>(impl_)->waitForTime(abstime);
+}
+
+int Monitor::waitForTimeRelative(const std::chrono::milliseconds &timeout) const {
+ return const_cast<Monitor::Impl*>(impl_)->waitForTimeRelative(timeout);
+}
+
+int Monitor::waitForever() const {
+ return const_cast<Monitor::Impl*>(impl_)->waitForever();
+}
+
+void Monitor::notify() const {
+ const_cast<Monitor::Impl*>(impl_)->notify();
+}
+
+void Monitor::notifyAll() const {
+ const_cast<Monitor::Impl*>(impl_)->notifyAll();
+}
+}
+}
+} // apache::thrift::concurrency
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Monitor.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Monitor.h
new file mode 100644
index 000000000..b3939cb01
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Monitor.h
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_CONCURRENCY_MONITOR_H_
+#define _THRIFT_CONCURRENCY_MONITOR_H_ 1
+
+#include <chrono>
+#include <thrift/concurrency/Exception.h>
+#include <thrift/concurrency/Mutex.h>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+
+/**
+ * A monitor is a combination mutex and condition-event. Waiting and
+ * notifying condition events requires that the caller own the mutex. Mutex
+ * lock and unlock operations can be performed independently of condition
+ * events. This is more or less analogous to java.lang.Object multi-thread
+ * operations.
+ *
+ * Note the Monitor can create a new, internal mutex; alternatively, a
+ * separate Mutex can be passed in and the Monitor will re-use it without
+ * taking ownership. It's the user's responsibility to make sure that the
+ * Mutex is not deallocated before the Monitor.
+ *
+ * Note that all methods are const. Monitors implement logical constness, not
+ * bit constness. This allows const methods to call monitor methods without
+ * needing to cast away constness or change to non-const signatures.
+ *
+ * @version $Id:$
+ */
+class Monitor : boost::noncopyable {
+public:
+ /** Creates a new mutex, and takes ownership of it. */
+ Monitor();
+
+ /** Uses the provided mutex without taking ownership. */
+ explicit Monitor(Mutex* mutex);
+
+ /** Uses the mutex inside the provided Monitor without taking ownership. */
+ explicit Monitor(Monitor* monitor);
+
+ /** Deallocates the mutex only if we own it. */
+ virtual ~Monitor();
+
+ Mutex& mutex() const;
+
+ virtual void lock() const;
+
+ virtual void unlock() const;
+
+ /**
+ * Waits a maximum of the specified timeout in milliseconds for the condition
+ * to occur, or waits forever if timeout is zero.
+ *
+ * Returns 0 if condition occurs, THRIFT_ETIMEDOUT on timeout, or an error code.
+ */
+ int waitForTimeRelative(const std::chrono::milliseconds &timeout) const;
+
+ int waitForTimeRelative(uint64_t timeout_ms) const { return waitForTimeRelative(std::chrono::milliseconds(timeout_ms)); }
+
+ /**
+ * Waits until the absolute time specified by abstime.
+ * Returns 0 if condition occurs, THRIFT_ETIMEDOUT on timeout, or an error code.
+ */
+ int waitForTime(const std::chrono::time_point<std::chrono::steady_clock>& abstime) const;
+
+ /**
+ * Waits forever until the condition occurs.
+ * Returns 0 if condition occurs, or an error code otherwise.
+ */
+ int waitForever() const;
+
+ /**
+ * Exception-throwing version of waitForTimeRelative(), called simply
+ * wait(std::chrono::milliseconds) for historical reasons. Timeout is in milliseconds.
+ *
+ * If the condition occurs, this function returns cleanly; on timeout or
+ * error an exception is thrown.
+ */
+ void wait(const std::chrono::milliseconds &timeout) const;
+
+ void wait(uint64_t timeout_ms = 0ULL) const { this->wait(std::chrono::milliseconds(timeout_ms)); }
+
+ /** Wakes up one thread waiting on this monitor. */
+ virtual void notify() const;
+
+ /** Wakes up all waiting threads on this monitor. */
+ virtual void notifyAll() const;
+
+private:
+ class Impl;
+
+ Impl* impl_;
+};
+
+class Synchronized {
+public:
+ Synchronized(const Monitor* monitor) : g(monitor->mutex()) {}
+ Synchronized(const Monitor& monitor) : g(monitor.mutex()) {}
+
+private:
+ Guard g;
+};
+}
+}
+} // apache::thrift::concurrency
+
+#endif // #ifndef _THRIFT_CONCURRENCY_MONITOR_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Mutex.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Mutex.cpp
new file mode 100644
index 000000000..75802835d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Mutex.cpp
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/concurrency/Mutex.h>
+
+#include <chrono>
+#include <mutex>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+
+/**
+ * Implementation of Mutex class using C++11 std::timed_mutex
+ *
+ * Methods throw std::system_error on error.
+ *
+ * @version $Id:$
+ */
+class Mutex::impl : public std::timed_mutex {};
+
+Mutex::Mutex() : impl_(new Mutex::impl()) {
+}
+
+void* Mutex::getUnderlyingImpl() const {
+ return impl_.get();
+}
+
+void Mutex::lock() const {
+ impl_->lock();
+}
+
+bool Mutex::trylock() const {
+ return impl_->try_lock();
+}
+
+bool Mutex::timedlock(int64_t ms) const {
+ return impl_->try_lock_for(std::chrono::milliseconds(ms));
+}
+
+void Mutex::unlock() const {
+ impl_->unlock();
+}
+
+}
+}
+} // apache::thrift::concurrency
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Mutex.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Mutex.h
new file mode 100644
index 000000000..27e386ed4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Mutex.h
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_CONCURRENCY_MUTEX_H_
+#define _THRIFT_CONCURRENCY_MUTEX_H_ 1
+
+#include <memory>
+#include <boost/noncopyable.hpp>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+
+/**
+ * NOTE: All mutex implementations throw an exception on failure. See each
+ * specific implementation to understand the exception type(s) used.
+ */
+
+/**
+ * A simple mutex class
+ *
+ * @version $Id:$
+ */
+class Mutex {
+public:
+ Mutex();
+ virtual ~Mutex() = default;
+
+ virtual void lock() const;
+ virtual bool trylock() const;
+ virtual bool timedlock(int64_t milliseconds) const;
+ virtual void unlock() const;
+
+ void* getUnderlyingImpl() const;
+
+private:
+ class impl;
+ std::shared_ptr<impl> impl_;
+};
+
+
+class Guard : boost::noncopyable {
+public:
+ Guard(const Mutex& value, int64_t timeout = 0) : mutex_(&value) {
+ if (timeout == 0) {
+ value.lock();
+ } else if (timeout < 0) {
+ if (!value.trylock()) {
+ mutex_ = nullptr;
+ }
+ } else {
+ if (!value.timedlock(timeout)) {
+ mutex_ = nullptr;
+ }
+ }
+ }
+ ~Guard() {
+ if (mutex_) {
+ mutex_->unlock();
+ }
+ }
+
+ operator bool() const { return (mutex_ != nullptr); }
+
+private:
+ const Mutex* mutex_;
+};
+
+}
+}
+} // apache::thrift::concurrency
+
+#endif // #ifndef _THRIFT_CONCURRENCY_MUTEX_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Thread.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Thread.cpp
new file mode 100644
index 000000000..a2bb1270f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Thread.cpp
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/concurrency/Thread.h>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+
+void Thread::threadMain(std::shared_ptr<Thread> thread) {
+ thread->setState(started);
+ thread->runnable()->run();
+
+ if (thread->getState() != stopping && thread->getState() != stopped) {
+ thread->setState(stopping);
+ }
+}
+
+}
+}
+} // apache::thrift::concurrency
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Thread.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Thread.h
new file mode 100644
index 000000000..e803a82ce
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/Thread.h
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_CONCURRENCY_THREAD_H_
+#define _THRIFT_CONCURRENCY_THREAD_H_ 1
+
+#include <memory>
+#include <thread>
+
+#include <thrift/concurrency/Monitor.h>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+
+class Thread;
+
+/**
+ * Minimal runnable class. More or less analogous to java.lang.Runnable.
+ *
+ * @version $Id:$
+ */
+class Runnable {
+
+public:
+ virtual ~Runnable() = default;
+ virtual void run() = 0;
+
+ /**
+ * Gets the thread object that is hosting this runnable object - can return
+ * an empty boost::shared pointer if no references remain on that thread object
+ */
+ virtual std::shared_ptr<Thread> thread() { return thread_.lock(); }
+
+ /**
+ * Sets the thread that is executing this object. This is only meant for
+ * use by concrete implementations of Thread.
+ */
+ virtual void thread(std::shared_ptr<Thread> value) { thread_ = value; }
+
+private:
+ std::weak_ptr<Thread> thread_;
+};
+
+/**
+ * Minimal thread class. Returned by thread factory bound to a Runnable object
+ * and ready to start execution. More or less analogous to java.lang.Thread
+ * (minus all the thread group, priority, mode and other baggage, since that
+ * is difficult to abstract across platforms and is left for platform-specific
+ * ThreadFactory implemtations to deal with
+ *
+ * @see apache::thrift::concurrency::ThreadFactory)
+ */
+class Thread final : public std::enable_shared_from_this<Thread> {
+
+public:
+ typedef std::thread::id id_t;
+
+ enum STATE { uninitialized, starting, started, stopping, stopped };
+
+ static void threadMain(std::shared_ptr<Thread> thread);
+
+ static inline bool is_current(id_t t) { return t == std::this_thread::get_id(); }
+ static inline id_t get_current() { return std::this_thread::get_id(); }
+
+ Thread(bool detached, std::shared_ptr<Runnable> runnable)
+ : state_(uninitialized), detached_(detached) {
+ this->_runnable = runnable;
+ }
+
+ ~Thread() {
+ if (!detached_ && thread_->joinable()) {
+ try {
+ join();
+ } catch (...) {
+ // We're really hosed.
+ }
+ }
+ }
+
+ STATE getState() const
+ {
+ Synchronized sync(monitor_);
+ return state_;
+ }
+
+ void setState(STATE newState)
+ {
+ Synchronized sync(monitor_);
+ state_ = newState;
+
+ // unblock start() with the knowledge that the thread has actually
+ // started running, which avoids a race in detached threads.
+ if (newState == started) {
+ monitor_.notify();
+ }
+ }
+
+ /**
+ * Starts the thread. Does platform specific thread creation and
+ * configuration then invokes the run method of the Runnable object bound
+ * to this thread.
+ */
+ void start() {
+ if (getState() != uninitialized) {
+ return;
+ }
+
+ std::shared_ptr<Thread> selfRef = shared_from_this();
+ setState(starting);
+
+ Synchronized sync(monitor_);
+ thread_ = std::unique_ptr<std::thread>(new std::thread(threadMain, selfRef));
+
+ if (detached_)
+ thread_->detach();
+
+ // Wait for the thread to start and get far enough to grab everything
+ // that it needs from the calling context, thus absolving the caller
+ // from being required to hold on to runnable indefinitely.
+ monitor_.wait();
+ }
+
+ /**
+ * Join this thread. If this thread is joinable, the calling thread blocks
+ * until this thread completes. If the target thread is not joinable, then
+ * nothing happens.
+ */
+ void join() {
+ if (!detached_ && state_ != uninitialized) {
+ thread_->join();
+ }
+ }
+
+ /**
+ * Gets the thread's platform-specific ID
+ */
+ Thread::id_t getId() const { return thread_.get() ? thread_->get_id() : std::thread::id(); }
+
+ /**
+ * Gets the runnable object this thread is hosting
+ */
+ std::shared_ptr<Runnable> runnable() const { return _runnable; }
+
+private:
+ std::shared_ptr<Runnable> _runnable;
+ std::unique_ptr<std::thread> thread_;
+ Monitor monitor_;
+ STATE state_;
+ bool detached_;
+};
+
+
+}
+}
+} // apache::thrift::concurrency
+
+#endif // #ifndef _THRIFT_CONCURRENCY_THREAD_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/ThreadFactory.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/ThreadFactory.cpp
new file mode 100644
index 000000000..becb3b244
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/ThreadFactory.cpp
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+
+#include <thrift/concurrency/ThreadFactory.h>
+#include <memory>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+
+std::shared_ptr<Thread> ThreadFactory::newThread(std::shared_ptr<Runnable> runnable) const {
+ std::shared_ptr<Thread> result = std::make_shared<Thread>(isDetached(), runnable);
+ runnable->thread(result);
+ return result;
+}
+
+Thread::id_t ThreadFactory::getCurrentThreadId() const {
+ return std::this_thread::get_id();
+}
+}
+}
+} // apache::thrift::concurrency
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/ThreadFactory.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/ThreadFactory.h
new file mode 100644
index 000000000..a1547a6e0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/ThreadFactory.h
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_CONCURRENCY_THREADFACTORY_H_
+#define _THRIFT_CONCURRENCY_THREADFACTORY_H_ 1
+
+#include <thrift/concurrency/Thread.h>
+
+#include <memory>
+namespace apache {
+namespace thrift {
+namespace concurrency {
+
+/**
+ * Factory to create thread object and bind them to Runnable
+ * object for execution
+ */
+class ThreadFactory final {
+public:
+ /**
+ * All threads created by a factory are reference-counted
+ * via std::shared_ptr. The factory guarantees that threads and the Runnable tasks
+ * they host will be properly cleaned up once the last strong reference
+ * to both is given up.
+ *
+ * By default threads are not joinable.
+ */
+ ThreadFactory(bool detached = true) : detached_(detached) { }
+
+ ~ThreadFactory() = default;
+
+ /**
+ * Gets current detached mode
+ */
+ bool isDetached() const { return detached_; }
+
+ /**
+ * Sets the detached disposition of newly created threads.
+ */
+ void setDetached(bool detached) { detached_ = detached; }
+
+ /**
+ * Create a new thread.
+ */
+ std::shared_ptr<Thread> newThread(std::shared_ptr<Runnable> runnable) const;
+
+ /**
+ * Gets the current thread id or unknown_thread_id if the current thread is not a thrift thread
+ */
+ Thread::id_t getCurrentThreadId() const;
+
+private:
+ bool detached_;
+};
+
+}
+}
+} // apache::thrift::concurrency
+
+#endif // #ifndef _THRIFT_CONCURRENCY_THREADFACTORY_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/ThreadManager.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/ThreadManager.cpp
new file mode 100644
index 000000000..25b838aef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/ThreadManager.cpp
@@ -0,0 +1,583 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+
+#include <thrift/concurrency/ThreadManager.h>
+#include <thrift/concurrency/Exception.h>
+#include <thrift/concurrency/Monitor.h>
+
+#include <memory>
+
+#include <stdexcept>
+#include <deque>
+#include <set>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+
+using std::shared_ptr;
+using std::unique_ptr;
+using std::dynamic_pointer_cast;
+
+/**
+ * ThreadManager class
+ *
+ * This class manages a pool of threads. It uses a ThreadFactory to create
+ * threads. It never actually creates or destroys worker threads, rather
+ * it maintains statistics on number of idle threads, number of active threads,
+ * task backlog, and average wait and service times.
+ *
+ * There are three different monitors used for signaling different conditions
+ * however they all share the same mutex_.
+ *
+ * @version $Id:$
+ */
+class ThreadManager::Impl : public ThreadManager {
+
+public:
+ Impl()
+ : workerCount_(0),
+ workerMaxCount_(0),
+ idleCount_(0),
+ pendingTaskCountMax_(0),
+ expiredCount_(0),
+ state_(ThreadManager::UNINITIALIZED),
+ monitor_(&mutex_),
+ maxMonitor_(&mutex_),
+ workerMonitor_(&mutex_) {}
+
+ ~Impl() override { stop(); }
+
+ void start() override;
+ void stop() override;
+
+ ThreadManager::STATE state() const override { return state_; }
+
+ shared_ptr<ThreadFactory> threadFactory() const override {
+ Guard g(mutex_);
+ return threadFactory_;
+ }
+
+ void threadFactory(shared_ptr<ThreadFactory> value) override {
+ Guard g(mutex_);
+ if (threadFactory_ && threadFactory_->isDetached() != value->isDetached()) {
+ throw InvalidArgumentException();
+ }
+ threadFactory_ = value;
+ }
+
+ void addWorker(size_t value) override;
+
+ void removeWorker(size_t value) override;
+
+ size_t idleWorkerCount() const override { return idleCount_; }
+
+ size_t workerCount() const override {
+ Guard g(mutex_);
+ return workerCount_;
+ }
+
+ size_t pendingTaskCount() const override {
+ Guard g(mutex_);
+ return tasks_.size();
+ }
+
+ size_t totalTaskCount() const override {
+ Guard g(mutex_);
+ return tasks_.size() + workerCount_ - idleCount_;
+ }
+
+ size_t pendingTaskCountMax() const override {
+ Guard g(mutex_);
+ return pendingTaskCountMax_;
+ }
+
+ size_t expiredTaskCount() const override {
+ Guard g(mutex_);
+ return expiredCount_;
+ }
+
+ void pendingTaskCountMax(const size_t value) {
+ Guard g(mutex_);
+ pendingTaskCountMax_ = value;
+ }
+
+ void add(shared_ptr<Runnable> value, int64_t timeout, int64_t expiration) override;
+
+ void remove(shared_ptr<Runnable> task) override;
+
+ shared_ptr<Runnable> removeNextPending() override;
+
+ void removeExpiredTasks() override {
+ removeExpired(false);
+ }
+
+ void setExpireCallback(ExpireCallback expireCallback) override;
+
+private:
+ /**
+ * Remove one or more expired tasks.
+ * \param[in] justOne if true, try to remove just one task and return
+ */
+ void removeExpired(bool justOne);
+
+ /**
+ * \returns whether it is acceptable to block, depending on the current thread id
+ */
+ bool canSleep() const;
+
+ /**
+ * Lowers the maximum worker count and blocks until enough worker threads complete
+ * to get to the new maximum worker limit. The caller is responsible for acquiring
+ * a lock on the class mutex_.
+ */
+ void removeWorkersUnderLock(size_t value);
+
+ size_t workerCount_;
+ size_t workerMaxCount_;
+ size_t idleCount_;
+ size_t pendingTaskCountMax_;
+ size_t expiredCount_;
+ ExpireCallback expireCallback_;
+
+ ThreadManager::STATE state_;
+ shared_ptr<ThreadFactory> threadFactory_;
+
+ friend class ThreadManager::Task;
+ typedef std::deque<shared_ptr<Task> > TaskQueue;
+ TaskQueue tasks_;
+ Mutex mutex_;
+ Monitor monitor_;
+ Monitor maxMonitor_;
+ Monitor workerMonitor_; // used to synchronize changes in worker count
+
+ friend class ThreadManager::Worker;
+ std::set<shared_ptr<Thread> > workers_;
+ std::set<shared_ptr<Thread> > deadWorkers_;
+ std::map<const Thread::id_t, shared_ptr<Thread> > idMap_;
+};
+
+class ThreadManager::Task : public Runnable {
+
+public:
+ enum STATE { WAITING, EXECUTING, TIMEDOUT, COMPLETE };
+
+ Task(shared_ptr<Runnable> runnable, uint64_t expiration = 0ULL)
+ : runnable_(runnable),
+ state_(WAITING) {
+ if (expiration != 0ULL) {
+ expireTime_.reset(new std::chrono::steady_clock::time_point(std::chrono::steady_clock::now() + std::chrono::milliseconds(expiration)));
+ }
+ }
+
+ ~Task() override = default;
+
+ void run() override {
+ if (state_ == EXECUTING) {
+ runnable_->run();
+ state_ = COMPLETE;
+ }
+ }
+
+ shared_ptr<Runnable> getRunnable() { return runnable_; }
+
+ const unique_ptr<std::chrono::steady_clock::time_point> & getExpireTime() const { return expireTime_; }
+
+private:
+ shared_ptr<Runnable> runnable_;
+ friend class ThreadManager::Worker;
+ STATE state_;
+ unique_ptr<std::chrono::steady_clock::time_point> expireTime_;
+};
+
+class ThreadManager::Worker : public Runnable {
+ enum STATE { UNINITIALIZED, STARTING, STARTED, STOPPING, STOPPED };
+
+public:
+ Worker(ThreadManager::Impl* manager) : manager_(manager), state_(UNINITIALIZED) {}
+
+ ~Worker() override = default;
+
+private:
+ bool isActive() const {
+ return (manager_->workerCount_ <= manager_->workerMaxCount_)
+ || (manager_->state_ == JOINING && !manager_->tasks_.empty());
+ }
+
+public:
+ /**
+ * Worker entry point
+ *
+ * As long as worker thread is running, pull tasks off the task queue and
+ * execute.
+ */
+ void run() override {
+ Guard g(manager_->mutex_);
+
+ /**
+ * This method has three parts; one is to check for and account for
+ * admitting a task which happens under a lock. Then the lock is released
+ * and the task itself is executed. Finally we do some accounting
+ * under lock again when the task completes.
+ */
+
+ /**
+ * Admitting
+ */
+
+ /**
+ * Increment worker semaphore and notify manager if worker count reached
+ * desired max
+ */
+ bool active = manager_->workerCount_ < manager_->workerMaxCount_;
+ if (active) {
+ if (++manager_->workerCount_ == manager_->workerMaxCount_) {
+ manager_->workerMonitor_.notify();
+ }
+ }
+
+ while (active) {
+ /**
+ * While holding manager monitor block for non-empty task queue (Also
+ * check that the thread hasn't been requested to stop). Once the queue
+ * is non-empty, dequeue a task, release monitor, and execute. If the
+ * worker max count has been decremented such that we exceed it, mark
+ * ourself inactive, decrement the worker count and notify the manager
+ * (technically we're notifying the next blocked thread but eventually
+ * the manager will see it.
+ */
+ active = isActive();
+
+ while (active && manager_->tasks_.empty()) {
+ manager_->idleCount_++;
+ manager_->monitor_.wait();
+ active = isActive();
+ manager_->idleCount_--;
+ }
+
+ shared_ptr<ThreadManager::Task> task;
+
+ if (active) {
+ if (!manager_->tasks_.empty()) {
+ task = manager_->tasks_.front();
+ manager_->tasks_.pop_front();
+ if (task->state_ == ThreadManager::Task::WAITING) {
+ // If the state is changed to anything other than EXECUTING or TIMEDOUT here
+ // then the execution loop needs to be changed below.
+ task->state_ =
+ (task->getExpireTime() && *(task->getExpireTime()) < std::chrono::steady_clock::now()) ?
+ ThreadManager::Task::TIMEDOUT :
+ ThreadManager::Task::EXECUTING;
+ }
+ }
+
+ /* If we have a pending task max and we just dropped below it, wakeup any
+ thread that might be blocked on add. */
+ if (manager_->pendingTaskCountMax_ != 0
+ && manager_->tasks_.size() <= manager_->pendingTaskCountMax_ - 1) {
+ manager_->maxMonitor_.notify();
+ }
+ }
+
+ /**
+ * Execution - not holding a lock
+ */
+ if (task) {
+ if (task->state_ == ThreadManager::Task::EXECUTING) {
+
+ // Release the lock so we can run the task without blocking the thread manager
+ manager_->mutex_.unlock();
+
+ try {
+ task->run();
+ } catch (const std::exception& e) {
+ GlobalOutput.printf("[ERROR] task->run() raised an exception: %s", e.what());
+ } catch (...) {
+ GlobalOutput.printf("[ERROR] task->run() raised an unknown exception");
+ }
+
+ // Re-acquire the lock to proceed in the thread manager
+ manager_->mutex_.lock();
+
+ } else if (manager_->expireCallback_) {
+ // The only other state the task could have been in is TIMEDOUT (see above)
+ manager_->expireCallback_(task->getRunnable());
+ manager_->expiredCount_++;
+ }
+ }
+ }
+
+ /**
+ * Final accounting for the worker thread that is done working
+ */
+ manager_->deadWorkers_.insert(this->thread());
+ if (--manager_->workerCount_ == manager_->workerMaxCount_) {
+ manager_->workerMonitor_.notify();
+ }
+ }
+
+private:
+ ThreadManager::Impl* manager_;
+ friend class ThreadManager::Impl;
+ STATE state_;
+};
+
+void ThreadManager::Impl::addWorker(size_t value) {
+ std::set<shared_ptr<Thread> > newThreads;
+ for (size_t ix = 0; ix < value; ix++) {
+ shared_ptr<ThreadManager::Worker> worker
+ = std::make_shared<ThreadManager::Worker>(this);
+ newThreads.insert(threadFactory_->newThread(worker));
+ }
+
+ Guard g(mutex_);
+ workerMaxCount_ += value;
+ workers_.insert(newThreads.begin(), newThreads.end());
+
+ for (const auto & newThread : newThreads) {
+ shared_ptr<ThreadManager::Worker> worker
+ = dynamic_pointer_cast<ThreadManager::Worker, Runnable>(newThread->runnable());
+ worker->state_ = ThreadManager::Worker::STARTING;
+ newThread->start();
+ idMap_.insert(std::pair<const Thread::id_t, shared_ptr<Thread> >(newThread->getId(), newThread));
+ }
+
+ while (workerCount_ != workerMaxCount_) {
+ workerMonitor_.wait();
+ }
+}
+
+void ThreadManager::Impl::start() {
+ Guard g(mutex_);
+ if (state_ == ThreadManager::STOPPED) {
+ return;
+ }
+
+ if (state_ == ThreadManager::UNINITIALIZED) {
+ if (!threadFactory_) {
+ throw InvalidArgumentException();
+ }
+ state_ = ThreadManager::STARTED;
+ monitor_.notifyAll();
+ }
+
+ while (state_ == STARTING) {
+ monitor_.wait();
+ }
+}
+
+void ThreadManager::Impl::stop() {
+ Guard g(mutex_);
+ bool doStop = false;
+
+ if (state_ != ThreadManager::STOPPING && state_ != ThreadManager::JOINING
+ && state_ != ThreadManager::STOPPED) {
+ doStop = true;
+ state_ = ThreadManager::JOINING;
+ }
+
+ if (doStop) {
+ removeWorkersUnderLock(workerCount_);
+ }
+
+ state_ = ThreadManager::STOPPED;
+}
+
+void ThreadManager::Impl::removeWorker(size_t value) {
+ Guard g(mutex_);
+ removeWorkersUnderLock(value);
+}
+
+void ThreadManager::Impl::removeWorkersUnderLock(size_t value) {
+ if (value > workerMaxCount_) {
+ throw InvalidArgumentException();
+ }
+
+ workerMaxCount_ -= value;
+
+ if (idleCount_ > value) {
+ // There are more idle workers than we need to remove,
+ // so notify enough of them so they can terminate.
+ for (size_t ix = 0; ix < value; ix++) {
+ monitor_.notify();
+ }
+ } else {
+ // There are as many or less idle workers than we need to remove,
+ // so just notify them all so they can terminate.
+ monitor_.notifyAll();
+ }
+
+ while (workerCount_ != workerMaxCount_) {
+ workerMonitor_.wait();
+ }
+
+ for (const auto & deadWorker : deadWorkers_) {
+
+ // when used with a joinable thread factory, we join the threads as we remove them
+ if (!threadFactory_->isDetached()) {
+ deadWorker->join();
+ }
+
+ idMap_.erase(deadWorker->getId());
+ workers_.erase(deadWorker);
+ }
+
+ deadWorkers_.clear();
+}
+
+bool ThreadManager::Impl::canSleep() const {
+ const Thread::id_t id = threadFactory_->getCurrentThreadId();
+ return idMap_.find(id) == idMap_.end();
+}
+
+void ThreadManager::Impl::add(shared_ptr<Runnable> value, int64_t timeout, int64_t expiration) {
+ Guard g(mutex_, timeout);
+
+ if (!g) {
+ throw TimedOutException();
+ }
+
+ if (state_ != ThreadManager::STARTED) {
+ throw IllegalStateException(
+ "ThreadManager::Impl::add ThreadManager "
+ "not started");
+ }
+
+ // if we're at a limit, remove an expired task to see if the limit clears
+ if (pendingTaskCountMax_ > 0 && (tasks_.size() >= pendingTaskCountMax_)) {
+ removeExpired(true);
+ }
+
+ if (pendingTaskCountMax_ > 0 && (tasks_.size() >= pendingTaskCountMax_)) {
+ if (canSleep() && timeout >= 0) {
+ while (pendingTaskCountMax_ > 0 && tasks_.size() >= pendingTaskCountMax_) {
+ // This is thread safe because the mutex is shared between monitors.
+ maxMonitor_.wait(timeout);
+ }
+ } else {
+ throw TooManyPendingTasksException();
+ }
+ }
+
+ tasks_.push_back(std::make_shared<ThreadManager::Task>(value, expiration));
+
+ // If idle thread is available notify it, otherwise all worker threads are
+ // running and will get around to this task in time.
+ if (idleCount_ > 0) {
+ monitor_.notify();
+ }
+}
+
+void ThreadManager::Impl::remove(shared_ptr<Runnable> task) {
+ Guard g(mutex_);
+ if (state_ != ThreadManager::STARTED) {
+ throw IllegalStateException(
+ "ThreadManager::Impl::remove ThreadManager not "
+ "started");
+ }
+
+ for (auto it = tasks_.begin(); it != tasks_.end(); ++it)
+ {
+ if ((*it)->getRunnable() == task)
+ {
+ tasks_.erase(it);
+ return;
+ }
+ }
+}
+
+std::shared_ptr<Runnable> ThreadManager::Impl::removeNextPending() {
+ Guard g(mutex_);
+ if (state_ != ThreadManager::STARTED) {
+ throw IllegalStateException(
+ "ThreadManager::Impl::removeNextPending "
+ "ThreadManager not started");
+ }
+
+ if (tasks_.empty()) {
+ return std::shared_ptr<Runnable>();
+ }
+
+ shared_ptr<ThreadManager::Task> task = tasks_.front();
+ tasks_.pop_front();
+
+ return task->getRunnable();
+}
+
+void ThreadManager::Impl::removeExpired(bool justOne) {
+ // this is always called under a lock
+ if (tasks_.empty()) {
+ return;
+ }
+ auto now = std::chrono::steady_clock::now();
+
+ for (auto it = tasks_.begin(); it != tasks_.end(); )
+ {
+ if ((*it)->getExpireTime() && *((*it)->getExpireTime()) < now) {
+ if (expireCallback_) {
+ expireCallback_((*it)->getRunnable());
+ }
+ it = tasks_.erase(it);
+ ++expiredCount_;
+ if (justOne) {
+ return;
+ }
+ }
+ else
+ {
+ ++it;
+ }
+ }
+}
+
+void ThreadManager::Impl::setExpireCallback(ExpireCallback expireCallback) {
+ Guard g(mutex_);
+ expireCallback_ = expireCallback;
+}
+
+class SimpleThreadManager : public ThreadManager::Impl {
+
+public:
+ SimpleThreadManager(size_t workerCount = 4, size_t pendingTaskCountMax = 0)
+ : workerCount_(workerCount), pendingTaskCountMax_(pendingTaskCountMax) {}
+
+ void start() override {
+ ThreadManager::Impl::pendingTaskCountMax(pendingTaskCountMax_);
+ ThreadManager::Impl::start();
+ addWorker(workerCount_);
+ }
+
+private:
+ const size_t workerCount_;
+ const size_t pendingTaskCountMax_;
+};
+
+shared_ptr<ThreadManager> ThreadManager::newThreadManager() {
+ return shared_ptr<ThreadManager>(new ThreadManager::Impl());
+}
+
+shared_ptr<ThreadManager> ThreadManager::newSimpleThreadManager(size_t count,
+ size_t pendingTaskCountMax) {
+ return shared_ptr<ThreadManager>(new SimpleThreadManager(count, pendingTaskCountMax));
+}
+}
+}
+} // apache::thrift::concurrency
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/ThreadManager.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/ThreadManager.h
new file mode 100644
index 000000000..7b202ca6a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/ThreadManager.h
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_CONCURRENCY_THREADMANAGER_H_
+#define _THRIFT_CONCURRENCY_THREADMANAGER_H_ 1
+
+#include <functional>
+#include <memory>
+#include <thrift/concurrency/ThreadFactory.h>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+
+/**
+ * Thread Pool Manager and related classes
+ *
+ * @version $Id:$
+ */
+class ThreadManager;
+
+/**
+ * ThreadManager class
+ *
+ * This class manages a pool of threads. It uses a ThreadFactory to create
+ * threads. It never actually creates or destroys worker threads, rather
+ * it maintains statistics on number of idle threads, number of active threads,
+ * task backlog, and average wait and service times and informs the PoolPolicy
+ * object bound to instances of this manager of interesting transitions. It is
+ * then up the PoolPolicy object to decide if the thread pool size needs to be
+ * adjusted and call this object addWorker and removeWorker methods to make
+ * changes.
+ *
+ * This design allows different policy implementations to use this code to
+ * handle basic worker thread management and worker task execution and focus on
+ * policy issues. The simplest policy, StaticPolicy, does nothing other than
+ * create a fixed number of threads.
+ */
+class ThreadManager {
+
+protected:
+ ThreadManager() = default;
+
+public:
+ typedef std::function<void(std::shared_ptr<Runnable>)> ExpireCallback;
+
+ virtual ~ThreadManager() = default;
+
+ /**
+ * Starts the thread manager. Verifies all attributes have been properly
+ * initialized, then allocates necessary resources to begin operation
+ */
+ virtual void start() = 0;
+
+ /**
+ * Stops the thread manager. Aborts all remaining unprocessed task, shuts
+ * down all created worker threads, and releases all allocated resources.
+ * This method blocks for all worker threads to complete, thus it can
+ * potentially block forever if a worker thread is running a task that
+ * won't terminate.
+ *
+ * Worker threads will be joined depending on the threadFactory's detached
+ * disposition.
+ */
+ virtual void stop() = 0;
+
+ enum STATE { UNINITIALIZED, STARTING, STARTED, JOINING, STOPPING, STOPPED };
+
+ virtual STATE state() const = 0;
+
+ /**
+ * \returns the current thread factory
+ */
+ virtual std::shared_ptr<ThreadFactory> threadFactory() const = 0;
+
+ /**
+ * Set the thread factory.
+ * \throws InvalidArgumentException if the new thread factory has a different
+ * detached disposition than the one replacing it
+ */
+ virtual void threadFactory(std::shared_ptr<ThreadFactory> value) = 0;
+
+ /**
+ * Adds worker thread(s).
+ */
+ virtual void addWorker(size_t value = 1) = 0;
+
+ /**
+ * Removes worker thread(s).
+ * Threads are joined if the thread factory detached disposition allows it.
+ * Blocks until the number of worker threads reaches the new limit.
+ * \param[in] value the number to remove
+ * \throws InvalidArgumentException if the value is greater than the number
+ * of workers
+ */
+ virtual void removeWorker(size_t value = 1) = 0;
+
+ /**
+ * Gets the current number of idle worker threads
+ */
+ virtual size_t idleWorkerCount() const = 0;
+
+ /**
+ * Gets the current number of total worker threads
+ */
+ virtual size_t workerCount() const = 0;
+
+ /**
+ * Gets the current number of pending tasks
+ */
+ virtual size_t pendingTaskCount() const = 0;
+
+ /**
+ * Gets the current number of pending and executing tasks
+ */
+ virtual size_t totalTaskCount() const = 0;
+
+ /**
+ * Gets the maximum pending task count. 0 indicates no maximum
+ */
+ virtual size_t pendingTaskCountMax() const = 0;
+
+ /**
+ * Gets the number of tasks which have been expired without being run
+ * since start() was called.
+ */
+ virtual size_t expiredTaskCount() const = 0;
+
+ /**
+ * Adds a task to be executed at some time in the future by a worker thread.
+ *
+ * This method will block if pendingTaskCountMax() in not zero and pendingTaskCount()
+ * is greater than or equalt to pendingTaskCountMax(). If this method is called in the
+ * context of a ThreadManager worker thread it will throw a
+ * TooManyPendingTasksException
+ *
+ * @param task The task to queue for execution
+ *
+ * @param timeout Time to wait in milliseconds to add a task when a pending-task-count
+ * is specified. Specific cases:
+ * timeout = 0 : Wait forever to queue task.
+ * timeout = -1 : Return immediately if pending task count exceeds specified max
+ * @param expiration when nonzero, the number of milliseconds the task is valid
+ * to be run; if exceeded, the task will be dropped off the queue and not run.
+ *
+ * @throws TooManyPendingTasksException Pending task count exceeds max pending task count
+ */
+ virtual void add(std::shared_ptr<Runnable> task,
+ int64_t timeout = 0LL,
+ int64_t expiration = 0LL) = 0;
+
+ /**
+ * Removes a pending task
+ */
+ virtual void remove(std::shared_ptr<Runnable> task) = 0;
+
+ /**
+ * Remove the next pending task which would be run.
+ *
+ * @return the task removed.
+ */
+ virtual std::shared_ptr<Runnable> removeNextPending() = 0;
+
+ /**
+ * Remove tasks from front of task queue that have expired.
+ */
+ virtual void removeExpiredTasks() = 0;
+
+ /**
+ * Set a callback to be called when a task is expired and not run.
+ *
+ * @param expireCallback a function called with the shared_ptr<Runnable> for
+ * the expired task.
+ */
+ virtual void setExpireCallback(ExpireCallback expireCallback) = 0;
+
+ static std::shared_ptr<ThreadManager> newThreadManager();
+
+ /**
+ * Creates a simple thread manager the uses count number of worker threads and has
+ * a pendingTaskCountMax maximum pending tasks. The default, 0, specified no limit
+ * on pending tasks
+ */
+ static std::shared_ptr<ThreadManager> newSimpleThreadManager(size_t count = 4,
+ size_t pendingTaskCountMax = 0);
+
+ class Task;
+
+ class Worker;
+
+ class Impl;
+};
+}
+}
+} // apache::thrift::concurrency
+
+#endif // #ifndef _THRIFT_CONCURRENCY_THREADMANAGER_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/TimerManager.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/TimerManager.cpp
new file mode 100644
index 000000000..703c19ed1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/TimerManager.cpp
@@ -0,0 +1,321 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/concurrency/TimerManager.h>
+#include <thrift/concurrency/Exception.h>
+
+#include <assert.h>
+#include <iostream>
+#include <memory>
+#include <set>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+
+using std::shared_ptr;
+using std::weak_ptr;
+
+/**
+ * TimerManager class
+ *
+ * @version $Id:$
+ */
+class TimerManager::Task : public Runnable {
+
+public:
+ enum STATE { WAITING, EXECUTING, CANCELLED, COMPLETE };
+
+ Task(shared_ptr<Runnable> runnable) : runnable_(runnable), state_(WAITING) {}
+
+ ~Task() override = default;
+
+ void run() override {
+ if (state_ == EXECUTING) {
+ runnable_->run();
+ state_ = COMPLETE;
+ }
+ }
+
+ bool operator==(const shared_ptr<Runnable> & runnable) const { return runnable_ == runnable; }
+
+ task_iterator it_;
+
+private:
+ shared_ptr<Runnable> runnable_;
+ friend class TimerManager::Dispatcher;
+ STATE state_;
+};
+
+class TimerManager::Dispatcher : public Runnable {
+
+public:
+ Dispatcher(TimerManager* manager) : manager_(manager) {}
+
+ ~Dispatcher() override = default;
+
+ /**
+ * Dispatcher entry point
+ *
+ * As long as dispatcher thread is running, pull tasks off the task taskMap_
+ * and execute.
+ */
+ void run() override {
+ {
+ Synchronized s(manager_->monitor_);
+ if (manager_->state_ == TimerManager::STARTING) {
+ manager_->state_ = TimerManager::STARTED;
+ manager_->monitor_.notifyAll();
+ }
+ }
+
+ do {
+ std::set<shared_ptr<TimerManager::Task> > expiredTasks;
+ {
+ Synchronized s(manager_->monitor_);
+ task_iterator expiredTaskEnd;
+ auto now = std::chrono::steady_clock::now();
+ while (manager_->state_ == TimerManager::STARTED
+ && (expiredTaskEnd = manager_->taskMap_.upper_bound(now))
+ == manager_->taskMap_.begin()) {
+ std::chrono::milliseconds timeout(0);
+ if (!manager_->taskMap_.empty()) {
+ timeout = std::chrono::duration_cast<std::chrono::milliseconds>(manager_->taskMap_.begin()->first - now);
+ //because the unit of steady_clock is smaller than millisecond,timeout may be 0.
+ if (timeout.count() == 0) {
+ timeout = std::chrono::milliseconds(1);
+ }
+ manager_->monitor_.waitForTimeRelative(timeout);
+ } else {
+ manager_->monitor_.waitForTimeRelative(0);
+ }
+ now = std::chrono::steady_clock::now();
+ }
+
+ if (manager_->state_ == TimerManager::STARTED) {
+ for (auto ix = manager_->taskMap_.begin(); ix != expiredTaskEnd; ix++) {
+ shared_ptr<TimerManager::Task> task = ix->second;
+ expiredTasks.insert(task);
+ task->it_ = manager_->taskMap_.end();
+ if (task->state_ == TimerManager::Task::WAITING) {
+ task->state_ = TimerManager::Task::EXECUTING;
+ }
+ manager_->taskCount_--;
+ }
+ manager_->taskMap_.erase(manager_->taskMap_.begin(), expiredTaskEnd);
+ }
+ }
+
+ for (const auto & expiredTask : expiredTasks) {
+ expiredTask->run();
+ }
+
+ } while (manager_->state_ == TimerManager::STARTED);
+
+ {
+ Synchronized s(manager_->monitor_);
+ if (manager_->state_ == TimerManager::STOPPING) {
+ manager_->state_ = TimerManager::STOPPED;
+ manager_->monitor_.notifyAll();
+ }
+ }
+ return;
+ }
+
+private:
+ TimerManager* manager_;
+ friend class TimerManager;
+};
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4355) // 'this' used in base member initializer list
+#endif
+
+TimerManager::TimerManager()
+ : taskCount_(0),
+ state_(TimerManager::UNINITIALIZED),
+ dispatcher_(std::make_shared<Dispatcher>(this)) {
+}
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+TimerManager::~TimerManager() {
+
+ // If we haven't been explicitly stopped, do so now. We don't need to grab
+ // the monitor here, since stop already takes care of reentrancy.
+
+ if (state_ != STOPPED) {
+ try {
+ stop();
+ } catch (...) {
+ // We're really hosed.
+ }
+ }
+}
+
+void TimerManager::start() {
+ bool doStart = false;
+ {
+ Synchronized s(monitor_);
+ if (!threadFactory_) {
+ throw InvalidArgumentException();
+ }
+ if (state_ == TimerManager::UNINITIALIZED) {
+ state_ = TimerManager::STARTING;
+ doStart = true;
+ }
+ }
+
+ if (doStart) {
+ dispatcherThread_ = threadFactory_->newThread(dispatcher_);
+ dispatcherThread_->start();
+ }
+
+ {
+ Synchronized s(monitor_);
+ while (state_ == TimerManager::STARTING) {
+ monitor_.wait();
+ }
+ assert(state_ != TimerManager::STARTING);
+ }
+}
+
+void TimerManager::stop() {
+ bool doStop = false;
+ {
+ Synchronized s(monitor_);
+ if (state_ == TimerManager::UNINITIALIZED) {
+ state_ = TimerManager::STOPPED;
+ } else if (state_ != STOPPING && state_ != STOPPED) {
+ doStop = true;
+ state_ = STOPPING;
+ monitor_.notifyAll();
+ }
+ while (state_ != STOPPED) {
+ monitor_.wait();
+ }
+ }
+
+ if (doStop) {
+ // Clean up any outstanding tasks
+ taskMap_.clear();
+
+ // Remove dispatcher's reference to us.
+ dispatcher_->manager_ = nullptr;
+ }
+}
+
+shared_ptr<const ThreadFactory> TimerManager::threadFactory() const {
+ Synchronized s(monitor_);
+ return threadFactory_;
+}
+
+void TimerManager::threadFactory(shared_ptr<const ThreadFactory> value) {
+ Synchronized s(monitor_);
+ threadFactory_ = value;
+}
+
+size_t TimerManager::taskCount() const {
+ return taskCount_;
+}
+
+TimerManager::Timer TimerManager::add(shared_ptr<Runnable> task, const std::chrono::milliseconds &timeout) {
+ return add(task, std::chrono::steady_clock::now() + timeout);
+}
+
+TimerManager::Timer TimerManager::add(shared_ptr<Runnable> task,
+ const std::chrono::time_point<std::chrono::steady_clock>& abstime) {
+ auto now = std::chrono::steady_clock::now();
+
+ if (abstime < now) {
+ throw InvalidArgumentException();
+ }
+ Synchronized s(monitor_);
+ if (state_ != TimerManager::STARTED) {
+ throw IllegalStateException();
+ }
+
+ // If the task map is empty, we will kick the dispatcher for sure. Otherwise, we kick him
+ // if the expiration time is shorter than the current value. Need to test before we insert,
+ // because the new task might insert at the front.
+ bool notifyRequired = (taskCount_ == 0) ? true : abstime < taskMap_.begin()->first;
+
+ shared_ptr<Task> timer(new Task(task));
+ taskCount_++;
+ timer->it_ = taskMap_.emplace(abstime, timer);
+
+ // If the task map was empty, or if we have an expiration that is earlier
+ // than any previously seen, kick the dispatcher so it can update its
+ // timeout
+ if (notifyRequired) {
+ monitor_.notify();
+ }
+
+ return timer;
+}
+
+void TimerManager::remove(shared_ptr<Runnable> task) {
+ Synchronized s(monitor_);
+ if (state_ != TimerManager::STARTED) {
+ throw IllegalStateException();
+ }
+ bool found = false;
+ for (auto ix = taskMap_.begin(); ix != taskMap_.end();) {
+ if (*ix->second == task) {
+ found = true;
+ taskCount_--;
+ taskMap_.erase(ix++);
+ } else {
+ ++ix;
+ }
+ }
+ if (!found) {
+ throw NoSuchTaskException();
+ }
+}
+
+void TimerManager::remove(Timer handle) {
+ Synchronized s(monitor_);
+ if (state_ != TimerManager::STARTED) {
+ throw IllegalStateException();
+ }
+
+ shared_ptr<Task> task = handle.lock();
+ if (!task) {
+ throw NoSuchTaskException();
+ }
+
+ if (task->it_ == taskMap_.end()) {
+ // Task is being executed
+ throw UncancellableTaskException();
+ }
+
+ taskMap_.erase(task->it_);
+ taskCount_--;
+}
+
+TimerManager::STATE TimerManager::state() const {
+ return state_;
+}
+}
+}
+} // apache::thrift::concurrency
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/TimerManager.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/TimerManager.h
new file mode 100644
index 000000000..44d4738d5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/concurrency/TimerManager.h
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_CONCURRENCY_TIMERMANAGER_H_
+#define _THRIFT_CONCURRENCY_TIMERMANAGER_H_ 1
+
+#include <thrift/concurrency/Monitor.h>
+#include <thrift/concurrency/ThreadFactory.h>
+
+#include <memory>
+#include <map>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+
+/**
+ * Timer Manager
+ *
+ * This class dispatches timer tasks when they fall due.
+ *
+ * @version $Id:$
+ */
+class TimerManager {
+
+public:
+ class Task;
+ typedef std::weak_ptr<Task> Timer;
+
+ TimerManager();
+
+ virtual ~TimerManager();
+
+ virtual std::shared_ptr<const ThreadFactory> threadFactory() const;
+
+ virtual void threadFactory(std::shared_ptr<const ThreadFactory> value);
+
+ /**
+ * Starts the timer manager service
+ *
+ * @throws IllegalArgumentException Missing thread factory attribute
+ */
+ virtual void start();
+
+ /**
+ * Stops the timer manager service
+ */
+ virtual void stop();
+
+ virtual size_t taskCount() const;
+
+ /**
+ * Adds a task to be executed at some time in the future by a worker thread.
+ *
+ * @param task The task to execute
+ * @param timeout Time in milliseconds to delay before executing task
+ * @return Handle of the timer, which can be used to remove the timer.
+ */
+ virtual Timer add(std::shared_ptr<Runnable> task, const std::chrono::milliseconds &timeout);
+ Timer add(std::shared_ptr<Runnable> task, uint64_t timeout) { return add(task,std::chrono::milliseconds(timeout)); }
+
+ /**
+ * Adds a task to be executed at some time in the future by a worker thread.
+ *
+ * @param task The task to execute
+ * @param abstime Absolute time in the future to execute task.
+ * @return Handle of the timer, which can be used to remove the timer.
+ */
+ virtual Timer add(std::shared_ptr<Runnable> task, const std::chrono::time_point<std::chrono::steady_clock>& abstime);
+
+ /**
+ * Removes a pending task
+ *
+ * @param task The task to remove. All timers which execute this task will
+ * be removed.
+ * @throws NoSuchTaskException Specified task doesn't exist. It was either
+ * processed already or this call was made for a
+ * task that was never added to this timer
+ *
+ * @throws UncancellableTaskException Specified task is already being
+ * executed or has completed execution.
+ */
+ virtual void remove(std::shared_ptr<Runnable> task);
+
+ /**
+ * Removes a single pending task
+ *
+ * @param timer The timer to remove. The timer is returned when calling the
+ * add() method.
+ * @throws NoSuchTaskException Specified task doesn't exist. It was either
+ * processed already or this call was made for a
+ * task that was never added to this timer
+ *
+ * @throws UncancellableTaskException Specified task is already being
+ * executed or has completed execution.
+ */
+ virtual void remove(Timer timer);
+
+ enum STATE { UNINITIALIZED, STARTING, STARTED, STOPPING, STOPPED };
+
+ virtual STATE state() const;
+
+private:
+ std::shared_ptr<const ThreadFactory> threadFactory_;
+ friend class Task;
+ std::multimap<std::chrono::time_point<std::chrono::steady_clock>, std::shared_ptr<Task> > taskMap_;
+ size_t taskCount_;
+ Monitor monitor_;
+ STATE state_;
+ class Dispatcher;
+ friend class Dispatcher;
+ std::shared_ptr<Dispatcher> dispatcher_;
+ std::shared_ptr<Thread> dispatcherThread_;
+ using task_iterator = decltype(taskMap_)::iterator;
+ typedef std::pair<task_iterator, task_iterator> task_range;
+};
+}
+}
+} // apache::thrift::concurrency
+
+#endif // #ifndef _THRIFT_CONCURRENCY_TIMERMANAGER_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/processor/PeekProcessor.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/processor/PeekProcessor.cpp
new file mode 100644
index 000000000..4cd58b8db
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/processor/PeekProcessor.cpp
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/processor/PeekProcessor.h>
+
+using namespace apache::thrift::transport;
+using namespace apache::thrift::protocol;
+using namespace apache::thrift;
+
+namespace apache {
+namespace thrift {
+namespace processor {
+
+PeekProcessor::PeekProcessor() {
+ memoryBuffer_.reset(new TMemoryBuffer());
+ targetTransport_ = memoryBuffer_;
+}
+PeekProcessor::~PeekProcessor() = default;
+
+void PeekProcessor::initialize(std::shared_ptr<TProcessor> actualProcessor,
+ std::shared_ptr<TProtocolFactory> protocolFactory,
+ std::shared_ptr<TPipedTransportFactory> transportFactory) {
+ actualProcessor_ = actualProcessor;
+ pipedProtocol_ = protocolFactory->getProtocol(targetTransport_);
+ transportFactory_ = transportFactory;
+ transportFactory_->initializeTargetTransport(targetTransport_);
+}
+
+std::shared_ptr<TTransport> PeekProcessor::getPipedTransport(std::shared_ptr<TTransport> in) {
+ return transportFactory_->getTransport(in);
+}
+
+void PeekProcessor::setTargetTransport(std::shared_ptr<TTransport> targetTransport) {
+ targetTransport_ = targetTransport;
+ if (std::dynamic_pointer_cast<TMemoryBuffer>(targetTransport_)) {
+ memoryBuffer_ = std::dynamic_pointer_cast<TMemoryBuffer>(targetTransport);
+ } else if (std::dynamic_pointer_cast<TPipedTransport>(targetTransport_)) {
+ memoryBuffer_ = std::dynamic_pointer_cast<TMemoryBuffer>(
+ std::dynamic_pointer_cast<TPipedTransport>(targetTransport_)->getTargetTransport());
+ }
+
+ if (!memoryBuffer_) {
+ throw TException(
+ "Target transport must be a TMemoryBuffer or a TPipedTransport with TMemoryBuffer");
+ }
+}
+
+bool PeekProcessor::process(std::shared_ptr<TProtocol> in,
+ std::shared_ptr<TProtocol> out,
+ void* connectionContext) {
+
+ std::string fname;
+ TMessageType mtype;
+ int32_t seqid;
+ in->readMessageBegin(fname, mtype, seqid);
+
+ if (mtype != T_CALL && mtype != T_ONEWAY) {
+ throw TException("Unexpected message type");
+ }
+
+ // Peek at the name
+ peekName(fname);
+
+ TType ftype;
+ int16_t fid;
+ while (true) {
+ in->readFieldBegin(fname, ftype, fid);
+ if (ftype == T_STOP) {
+ break;
+ }
+
+ // Peek at the variable
+ peek(in, ftype, fid);
+ in->readFieldEnd();
+ }
+ in->readMessageEnd();
+ in->getTransport()->readEnd();
+
+ //
+ // All the data is now in memoryBuffer_ and ready to be processed
+ //
+
+ // Let's first take a peek at the full data in memory
+ uint8_t* buffer;
+ uint32_t size;
+ memoryBuffer_->getBuffer(&buffer, &size);
+ peekBuffer(buffer, size);
+
+ // Done peeking at variables
+ peekEnd();
+
+ bool ret = actualProcessor_->process(pipedProtocol_, out, connectionContext);
+ memoryBuffer_->resetBuffer();
+ return ret;
+}
+
+void PeekProcessor::peekName(const std::string& fname) {
+ (void)fname;
+}
+
+void PeekProcessor::peekBuffer(uint8_t* buffer, uint32_t size) {
+ (void)buffer;
+ (void)size;
+}
+
+void PeekProcessor::peek(std::shared_ptr<TProtocol> in, TType ftype, int16_t fid) {
+ (void)fid;
+ in->skip(ftype);
+}
+
+void PeekProcessor::peekEnd() {
+}
+}
+}
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/processor/PeekProcessor.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/processor/PeekProcessor.h
new file mode 100644
index 000000000..ae565fc4b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/processor/PeekProcessor.h
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef PEEKPROCESSOR_H
+#define PEEKPROCESSOR_H
+
+#include <string>
+#include <thrift/TProcessor.h>
+#include <thrift/transport/TTransport.h>
+#include <thrift/transport/TTransportUtils.h>
+#include <thrift/transport/TBufferTransports.h>
+#include <memory>
+
+namespace apache {
+namespace thrift {
+namespace processor {
+
+/*
+ * Class for peeking at the raw data that is being processed by another processor
+ * and gives the derived class a chance to change behavior accordingly
+ *
+ */
+class PeekProcessor : public apache::thrift::TProcessor {
+
+public:
+ PeekProcessor();
+ ~PeekProcessor() override;
+
+ // Input here: actualProcessor - the underlying processor
+ // protocolFactory - the protocol factory used to wrap the memory buffer
+ // transportFactory - this TPipedTransportFactory is used to wrap the source transport
+ // via a call to getPipedTransport
+ void initialize(
+ std::shared_ptr<apache::thrift::TProcessor> actualProcessor,
+ std::shared_ptr<apache::thrift::protocol::TProtocolFactory> protocolFactory,
+ std::shared_ptr<apache::thrift::transport::TPipedTransportFactory> transportFactory);
+
+ std::shared_ptr<apache::thrift::transport::TTransport> getPipedTransport(
+ std::shared_ptr<apache::thrift::transport::TTransport> in);
+
+ void setTargetTransport(std::shared_ptr<apache::thrift::transport::TTransport> targetTransport);
+
+ bool process(std::shared_ptr<apache::thrift::protocol::TProtocol> in,
+ std::shared_ptr<apache::thrift::protocol::TProtocol> out,
+ void* connectionContext) override;
+
+ // The following three functions can be overloaded by child classes to
+ // achieve desired peeking behavior
+ virtual void peekName(const std::string& fname);
+ virtual void peekBuffer(uint8_t* buffer, uint32_t size);
+ virtual void peek(std::shared_ptr<apache::thrift::protocol::TProtocol> in,
+ apache::thrift::protocol::TType ftype,
+ int16_t fid);
+ virtual void peekEnd();
+
+private:
+ std::shared_ptr<apache::thrift::TProcessor> actualProcessor_;
+ std::shared_ptr<apache::thrift::protocol::TProtocol> pipedProtocol_;
+ std::shared_ptr<apache::thrift::transport::TPipedTransportFactory> transportFactory_;
+ std::shared_ptr<apache::thrift::transport::TMemoryBuffer> memoryBuffer_;
+ std::shared_ptr<apache::thrift::transport::TTransport> targetTransport_;
+};
+}
+}
+} // apache::thrift::processor
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/processor/StatsProcessor.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/processor/StatsProcessor.h
new file mode 100644
index 000000000..e98efb82c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/processor/StatsProcessor.h
@@ -0,0 +1,242 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef STATSPROCESSOR_H
+#define STATSPROCESSOR_H
+
+#include <memory>
+#include <thrift/transport/TTransport.h>
+#include <thrift/protocol/TProtocol.h>
+#include <TProcessor.h>
+
+namespace apache {
+namespace thrift {
+namespace processor {
+
+/*
+ * Class for keeping track of function call statistics and printing them if desired
+ *
+ */
+class StatsProcessor : public apache::thrift::TProcessor {
+public:
+ StatsProcessor(bool print, bool frequency) : print_(print), frequency_(frequency) {}
+ virtual ~StatsProcessor(){};
+
+ virtual bool process(std::shared_ptr<apache::thrift::protocol::TProtocol> piprot,
+ std::shared_ptr<apache::thrift::protocol::TProtocol> poprot,
+ void* serverContext) {
+
+ piprot_ = piprot;
+
+ std::string fname;
+ apache::thrift::protocol::TMessageType mtype;
+ int32_t seqid;
+
+ piprot_->readMessageBegin(fname, mtype, seqid);
+ if (mtype != apache::thrift::protocol::T_CALL && mtype != apache::thrift::protocol::T_ONEWAY) {
+ if (print_) {
+ printf("Unknown message type\n");
+ }
+ throw apache::thrift::TException("Unexpected message type");
+ }
+ if (print_) {
+ printf("%s (", fname.c_str());
+ }
+ if (frequency_) {
+ if (frequency_map_.find(fname) != frequency_map_.end()) {
+ frequency_map_[fname]++;
+ } else {
+ frequency_map_[fname] = 1;
+ }
+ }
+
+ apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ while (true) {
+ piprot_->readFieldBegin(fname, ftype, fid);
+ if (ftype == apache::thrift::protocol::T_STOP) {
+ break;
+ }
+
+ printAndPassToBuffer(ftype);
+ if (print_) {
+ printf(", ");
+ }
+ }
+
+ if (print_) {
+ printf("\b\b)\n");
+ }
+ return true;
+ }
+
+ const std::map<std::string, int64_t>& get_frequency_map() { return frequency_map_; }
+
+protected:
+ void printAndPassToBuffer(apache::thrift::protocol::TType ftype) {
+ switch (ftype) {
+ case apache::thrift::protocol::T_BOOL: {
+ bool boolv;
+ piprot_->readBool(boolv);
+ if (print_) {
+ printf("%d", boolv);
+ }
+ } break;
+ case apache::thrift::protocol::T_BYTE: {
+ int8_t bytev;
+ piprot_->readByte(bytev);
+ if (print_) {
+ printf("%d", bytev);
+ }
+ } break;
+ case apache::thrift::protocol::T_I16: {
+ int16_t i16;
+ piprot_->readI16(i16);
+ if (print_) {
+ printf("%d", i16);
+ }
+ } break;
+ case apache::thrift::protocol::T_I32: {
+ int32_t i32;
+ piprot_->readI32(i32);
+ if (print_) {
+ printf("%d", i32);
+ }
+ } break;
+ case apache::thrift::protocol::T_I64: {
+ int64_t i64;
+ piprot_->readI64(i64);
+ if (print_) {
+ printf("%ld", i64);
+ }
+ } break;
+ case apache::thrift::protocol::T_DOUBLE: {
+ double dub;
+ piprot_->readDouble(dub);
+ if (print_) {
+ printf("%f", dub);
+ }
+ } break;
+ case apache::thrift::protocol::T_STRING: {
+ std::string str;
+ piprot_->readString(str);
+ if (print_) {
+ printf("%s", str.c_str());
+ }
+ } break;
+ case apache::thrift::protocol::T_STRUCT: {
+ std::string name;
+ int16_t fid;
+ apache::thrift::protocol::TType ftype;
+ piprot_->readStructBegin(name);
+ if (print_) {
+ printf("<");
+ }
+ while (true) {
+ piprot_->readFieldBegin(name, ftype, fid);
+ if (ftype == apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ printAndPassToBuffer(ftype);
+ if (print_) {
+ printf(",");
+ }
+ piprot_->readFieldEnd();
+ }
+ piprot_->readStructEnd();
+ if (print_) {
+ printf("\b>");
+ }
+ } break;
+ case apache::thrift::protocol::T_MAP: {
+ apache::thrift::protocol::TType keyType;
+ apache::thrift::protocol::TType valType;
+ uint32_t i, size;
+ piprot_->readMapBegin(keyType, valType, size);
+ if (print_) {
+ printf("{");
+ }
+ for (i = 0; i < size; i++) {
+ printAndPassToBuffer(keyType);
+ if (print_) {
+ printf("=>");
+ }
+ printAndPassToBuffer(valType);
+ if (print_) {
+ printf(",");
+ }
+ }
+ piprot_->readMapEnd();
+ if (print_) {
+ printf("\b}");
+ }
+ } break;
+ case apache::thrift::protocol::T_SET: {
+ apache::thrift::protocol::TType elemType;
+ uint32_t i, size;
+ piprot_->readSetBegin(elemType, size);
+ if (print_) {
+ printf("{");
+ }
+ for (i = 0; i < size; i++) {
+ printAndPassToBuffer(elemType);
+ if (print_) {
+ printf(",");
+ }
+ }
+ piprot_->readSetEnd();
+ if (print_) {
+ printf("\b}");
+ }
+ } break;
+ case apache::thrift::protocol::T_LIST: {
+ apache::thrift::protocol::TType elemType;
+ uint32_t i, size;
+ piprot_->readListBegin(elemType, size);
+ if (print_) {
+ printf("[");
+ }
+ for (i = 0; i < size; i++) {
+ printAndPassToBuffer(elemType);
+ if (print_) {
+ printf(",");
+ }
+ }
+ piprot_->readListEnd();
+ if (print_) {
+ printf("\b]");
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+
+ std::shared_ptr<apache::thrift::protocol::TProtocol> piprot_;
+ std::map<std::string, int64_t> frequency_map_;
+
+ bool print_;
+ bool frequency_;
+};
+}
+}
+} // apache::thrift::processor
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/processor/TMultiplexedProcessor.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/processor/TMultiplexedProcessor.h
new file mode 100644
index 000000000..85c0affc2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/processor/TMultiplexedProcessor.h
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef THRIFT_TMULTIPLEXEDPROCESSOR_H_
+#define THRIFT_TMULTIPLEXEDPROCESSOR_H_ 1
+
+#include <thrift/protocol/TProtocolDecorator.h>
+#include <thrift/TApplicationException.h>
+#include <thrift/TProcessor.h>
+#include <boost/tokenizer.hpp>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+/**
+ * To be able to work with any protocol, we needed
+ * to allow them to call readMessageBegin() and get a TMessage in exactly
+ * the standard format, without the service name prepended to TMessage.name.
+ */
+class StoredMessageProtocol : public TProtocolDecorator {
+public:
+ StoredMessageProtocol(std::shared_ptr<protocol::TProtocol> _protocol,
+ const std::string& _name,
+ const TMessageType _type,
+ const int32_t _seqid)
+ : TProtocolDecorator(_protocol), name(_name), type(_type), seqid(_seqid) {}
+
+ uint32_t readMessageBegin_virt(std::string& _name, TMessageType& _type, int32_t& _seqid) override {
+
+ _name = name;
+ _type = type;
+ _seqid = seqid;
+
+ return 0; // (Normal TProtocol read functions return number of bytes read)
+ }
+
+ std::string name;
+ TMessageType type;
+ int32_t seqid;
+};
+} // namespace protocol
+
+/**
+ * <code>TMultiplexedProcessor</code> is a <code>TProcessor</code> allowing
+ * a single <code>TServer</code> to provide multiple services.
+ *
+ * <p>To do so, you instantiate the processor and then register additional
+ * processors with it, as shown in the following example:</p>
+ *
+ * <blockquote><code>
+ * std::shared_ptr<TMultiplexedProcessor> processor(new TMultiplexedProcessor());
+ *
+ * processor->registerProcessor(
+ * "Calculator",
+ * std::shared_ptr<TProcessor>( new CalculatorProcessor(
+ * std::shared_ptr<CalculatorHandler>( new CalculatorHandler()))));
+ *
+ * processor->registerProcessor(
+ * "WeatherReport",
+ * std::shared_ptr<TProcessor>( new WeatherReportProcessor(
+ * std::shared_ptr<WeatherReportHandler>( new WeatherReportHandler()))));
+ *
+ * std::shared_ptr<TServerTransport> transport(new TServerSocket(9090));
+ * TSimpleServer server(processor, transport);
+ *
+ * server.serve();
+ * </code></blockquote>
+ */
+class TMultiplexedProcessor : public TProcessor {
+public:
+ typedef std::map<std::string, std::shared_ptr<TProcessor> > services_t;
+
+ /**
+ * 'Register' a service with this <code>TMultiplexedProcessor</code>. This
+ * allows us to broker requests to individual services by using the service
+ * name to select them at request time.
+ *
+ * \param [in] serviceName Name of a service, has to be identical to the name
+ * declared in the Thrift IDL, e.g. "WeatherReport".
+ * \param [in] processor Implementation of a service, usually referred to
+ * as "handlers", e.g. WeatherReportHandler,
+ * implementing WeatherReportIf interface.
+ */
+ void registerProcessor(const std::string& serviceName, std::shared_ptr<TProcessor> processor) {
+ services[serviceName] = processor;
+ }
+
+ /**
+ * Register a service to be called to process queries without service name
+ * \param [in] processor Implementation of a service.
+ */
+ void registerDefault(const std::shared_ptr<TProcessor>& processor) {
+ defaultProcessor = processor;
+ }
+
+ /**
+ * Chew up invalid input and return an exception to throw.
+ */
+ TException protocol_error(std::shared_ptr<protocol::TProtocol> in,
+ std::shared_ptr<protocol::TProtocol> out,
+ const std::string& name,
+ int32_t seqid,
+ const std::string& msg) const {
+ in->skip(::apache::thrift::protocol::T_STRUCT);
+ in->readMessageEnd();
+ in->getTransport()->readEnd();
+ ::apache::thrift::TApplicationException
+ x(::apache::thrift::TApplicationException::PROTOCOL_ERROR,
+ "TMultiplexedProcessor: " + msg);
+ out->writeMessageBegin(name, ::apache::thrift::protocol::T_EXCEPTION, seqid);
+ x.write(out.get());
+ out->writeMessageEnd();
+ out->getTransport()->writeEnd();
+ out->getTransport()->flush();
+ return TException(msg);
+}
+
+ /**
+ * This implementation of <code>process</code> performs the following steps:
+ *
+ * <ol>
+ * <li>Read the beginning of the message.</li>
+ * <li>Extract the service name from the message.</li>
+ * <li>Using the service name to locate the appropriate processor.</li>
+ * <li>Dispatch to the processor, with a decorated instance of TProtocol
+ * that allows readMessageBegin() to return the original TMessage.</li>
+ * </ol>
+ *
+ * \throws TException If the message type is not T_CALL or T_ONEWAY, if
+ * the service name was not found in the message, or if the service
+ * name was not found in the service map.
+ */
+ bool process(std::shared_ptr<protocol::TProtocol> in,
+ std::shared_ptr<protocol::TProtocol> out,
+ void* connectionContext) override {
+ std::string name;
+ protocol::TMessageType type;
+ int32_t seqid;
+
+ // Use the actual underlying protocol (e.g. TBinaryProtocol) to read the
+ // message header. This pulls the message "off the wire", which we'll
+ // deal with at the end of this method.
+ in->readMessageBegin(name, type, seqid);
+
+ if (type != protocol::T_CALL && type != protocol::T_ONEWAY) {
+ // Unexpected message type.
+ throw protocol_error(in, out, name, seqid, "Unexpected message type");
+ }
+
+ // Extract the service name
+ boost::tokenizer<boost::char_separator<char> > tok(name, boost::char_separator<char>(":"));
+
+ std::vector<std::string> tokens;
+ std::copy(tok.begin(), tok.end(), std::back_inserter(tokens));
+
+ // A valid message should consist of two tokens: the service
+ // name and the name of the method to call.
+ if (tokens.size() == 2) {
+ // Search for a processor associated with this service name.
+ auto it = services.find(tokens[0]);
+
+ if (it != services.end()) {
+ std::shared_ptr<TProcessor> processor = it->second;
+ // Let the processor registered for this service name
+ // process the message.
+ return processor
+ ->process(std::shared_ptr<protocol::TProtocol>(
+ new protocol::StoredMessageProtocol(in, tokens[1], type, seqid)),
+ out,
+ connectionContext);
+ } else {
+ // Unknown service.
+ throw protocol_error(in, out, name, seqid,
+ "Unknown service: " + tokens[0] +
+ ". Did you forget to call registerProcessor()?");
+ }
+ } else if (tokens.size() == 1) {
+ if (defaultProcessor) {
+ // non-multiplexed client forwards to default processor
+ return defaultProcessor
+ ->process(std::shared_ptr<protocol::TProtocol>(
+ new protocol::StoredMessageProtocol(in, tokens[0], type, seqid)),
+ out,
+ connectionContext);
+ } else {
+ throw protocol_error(in, out, name, seqid,
+ "Non-multiplexed client request dropped. "
+ "Did you forget to call defaultProcessor()?");
+ }
+ } else {
+ throw protocol_error(in, out, name, seqid,
+ "Wrong number of tokens.");
+ }
+ }
+
+private:
+ /** Map of service processor objects, indexed by service names. */
+ services_t services;
+
+ //! If a non-multi client requests something, it goes to the
+ //! default processor (if one is defined) for backwards compatibility.
+ std::shared_ptr<TProcessor> defaultProcessor;
+};
+}
+}
+
+#endif // THRIFT_TMULTIPLEXEDPROCESSOR_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TBase64Utils.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TBase64Utils.cpp
new file mode 100644
index 000000000..7474f5af8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TBase64Utils.cpp
@@ -0,0 +1,315 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/protocol/TBase64Utils.h>
+
+using std::string;
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+static const uint8_t* kBase64EncodeTable
+ = (const uint8_t*)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+void base64_encode(const uint8_t* in, uint32_t len, uint8_t* buf) {
+ buf[0] = kBase64EncodeTable[(in[0] >> 2) & 0x3f];
+ if (len == 3) {
+ buf[1] = kBase64EncodeTable[((in[0] << 4) & 0x30) | ((in[1] >> 4) & 0x0f)];
+ buf[2] = kBase64EncodeTable[((in[1] << 2) & 0x3c) | ((in[2] >> 6) & 0x03)];
+ buf[3] = kBase64EncodeTable[in[2] & 0x3f];
+ } else if (len == 2) {
+ buf[1] = kBase64EncodeTable[((in[0] << 4) & 0x30) | ((in[1] >> 4) & 0x0f)];
+ buf[2] = kBase64EncodeTable[(in[1] << 2) & 0x3c];
+ } else { // len == 1
+ buf[1] = kBase64EncodeTable[(in[0] << 4) & 0x30];
+ }
+}
+
+static const uint8_t kBase64DecodeTable[256] = {
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0x3e,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0x3f,
+ 0x34,
+ 0x35,
+ 0x36,
+ 0x37,
+ 0x38,
+ 0x39,
+ 0x3a,
+ 0x3b,
+ 0x3c,
+ 0x3d,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0x00,
+ 0x01,
+ 0x02,
+ 0x03,
+ 0x04,
+ 0x05,
+ 0x06,
+ 0x07,
+ 0x08,
+ 0x09,
+ 0x0a,
+ 0x0b,
+ 0x0c,
+ 0x0d,
+ 0x0e,
+ 0x0f,
+ 0x10,
+ 0x11,
+ 0x12,
+ 0x13,
+ 0x14,
+ 0x15,
+ 0x16,
+ 0x17,
+ 0x18,
+ 0x19,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0x1a,
+ 0x1b,
+ 0x1c,
+ 0x1d,
+ 0x1e,
+ 0x1f,
+ 0x20,
+ 0x21,
+ 0x22,
+ 0x23,
+ 0x24,
+ 0x25,
+ 0x26,
+ 0x27,
+ 0x28,
+ 0x29,
+ 0x2a,
+ 0x2b,
+ 0x2c,
+ 0x2d,
+ 0x2e,
+ 0x2f,
+ 0x30,
+ 0x31,
+ 0x32,
+ 0x33,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+};
+
+void base64_decode(uint8_t* buf, uint32_t len) {
+ buf[0] = (kBase64DecodeTable[buf[0]] << 2) | (kBase64DecodeTable[buf[1]] >> 4);
+ if (len > 2) {
+ buf[1] = ((kBase64DecodeTable[buf[1]] << 4) & 0xf0) | (kBase64DecodeTable[buf[2]] >> 2);
+ if (len > 3) {
+ buf[2] = ((kBase64DecodeTable[buf[2]] << 6) & 0xc0) | (kBase64DecodeTable[buf[3]]);
+ }
+ }
+}
+}
+}
+} // apache::thrift::protocol
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TBase64Utils.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TBase64Utils.h
new file mode 100644
index 000000000..1ea67440e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TBase64Utils.h
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_PROTOCOL_TBASE64UTILS_H_
+#define _THRIFT_PROTOCOL_TBASE64UTILS_H_
+
+#include <stdint.h>
+#include <string>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+// in must be at least len bytes
+// len must be 1, 2, or 3
+// buf must be a buffer of at least 4 bytes and may not overlap in
+// the data is not padded with '='; the caller can do this if desired
+void base64_encode(const uint8_t* in, uint32_t len, uint8_t* buf);
+
+// buf must be a buffer of at least 4 bytes and contain base64 encoded values
+// buf will be changed to contain output bytes
+// len is number of bytes to consume from input (must be 2, 3, or 4)
+// no '=' padding should be included in the input
+void base64_decode(uint8_t* buf, uint32_t len);
+}
+}
+} // apache::thrift::protocol
+
+#endif // #define _THRIFT_PROTOCOL_TBASE64UTILS_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TBinaryProtocol.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TBinaryProtocol.h
new file mode 100644
index 000000000..6bd5fb830
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TBinaryProtocol.h
@@ -0,0 +1,250 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_PROTOCOL_TBINARYPROTOCOL_H_
+#define _THRIFT_PROTOCOL_TBINARYPROTOCOL_H_ 1
+
+#include <thrift/protocol/TProtocol.h>
+#include <thrift/protocol/TVirtualProtocol.h>
+
+#include <memory>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+/**
+ * The default binary protocol for thrift. Writes all data in a very basic
+ * binary format, essentially just spitting out the raw bytes.
+ *
+ */
+template <class Transport_, class ByteOrder_ = TNetworkBigEndian>
+class TBinaryProtocolT : public TVirtualProtocol<TBinaryProtocolT<Transport_, ByteOrder_> > {
+public:
+ static const int32_t VERSION_MASK = ((int32_t)0xffff0000);
+ static const int32_t VERSION_1 = ((int32_t)0x80010000);
+ // VERSION_2 (0x80020000) was taken by TDenseProtocol (which has since been removed)
+
+ TBinaryProtocolT(std::shared_ptr<Transport_> trans)
+ : TVirtualProtocol<TBinaryProtocolT<Transport_, ByteOrder_> >(trans),
+ trans_(trans.get()),
+ string_limit_(0),
+ container_limit_(0),
+ strict_read_(false),
+ strict_write_(true) {}
+
+ TBinaryProtocolT(std::shared_ptr<Transport_> trans,
+ int32_t string_limit,
+ int32_t container_limit,
+ bool strict_read,
+ bool strict_write)
+ : TVirtualProtocol<TBinaryProtocolT<Transport_, ByteOrder_> >(trans),
+ trans_(trans.get()),
+ string_limit_(string_limit),
+ container_limit_(container_limit),
+ strict_read_(strict_read),
+ strict_write_(strict_write) {}
+
+ void setStringSizeLimit(int32_t string_limit) { string_limit_ = string_limit; }
+
+ void setContainerSizeLimit(int32_t container_limit) { container_limit_ = container_limit; }
+
+ void setStrict(bool strict_read, bool strict_write) {
+ strict_read_ = strict_read;
+ strict_write_ = strict_write;
+ }
+
+ /**
+ * Writing functions.
+ */
+
+ /*ol*/ uint32_t writeMessageBegin(const std::string& name,
+ const TMessageType messageType,
+ const int32_t seqid);
+
+ /*ol*/ uint32_t writeMessageEnd();
+
+ inline uint32_t writeStructBegin(const char* name);
+
+ inline uint32_t writeStructEnd();
+
+ inline uint32_t writeFieldBegin(const char* name, const TType fieldType, const int16_t fieldId);
+
+ inline uint32_t writeFieldEnd();
+
+ inline uint32_t writeFieldStop();
+
+ inline uint32_t writeMapBegin(const TType keyType, const TType valType, const uint32_t size);
+
+ inline uint32_t writeMapEnd();
+
+ inline uint32_t writeListBegin(const TType elemType, const uint32_t size);
+
+ inline uint32_t writeListEnd();
+
+ inline uint32_t writeSetBegin(const TType elemType, const uint32_t size);
+
+ inline uint32_t writeSetEnd();
+
+ inline uint32_t writeBool(const bool value);
+
+ inline uint32_t writeByte(const int8_t byte);
+
+ inline uint32_t writeI16(const int16_t i16);
+
+ inline uint32_t writeI32(const int32_t i32);
+
+ inline uint32_t writeI64(const int64_t i64);
+
+ inline uint32_t writeDouble(const double dub);
+
+ template <typename StrType>
+ inline uint32_t writeString(const StrType& str);
+
+ inline uint32_t writeBinary(const std::string& str);
+
+ /**
+ * Reading functions
+ */
+
+ /*ol*/ uint32_t readMessageBegin(std::string& name, TMessageType& messageType, int32_t& seqid);
+
+ /*ol*/ uint32_t readMessageEnd();
+
+ inline uint32_t readStructBegin(std::string& name);
+
+ inline uint32_t readStructEnd();
+
+ inline uint32_t readFieldBegin(std::string& name, TType& fieldType, int16_t& fieldId);
+
+ inline uint32_t readFieldEnd();
+
+ inline uint32_t readMapBegin(TType& keyType, TType& valType, uint32_t& size);
+
+ inline uint32_t readMapEnd();
+
+ inline uint32_t readListBegin(TType& elemType, uint32_t& size);
+
+ inline uint32_t readListEnd();
+
+ inline uint32_t readSetBegin(TType& elemType, uint32_t& size);
+
+ inline uint32_t readSetEnd();
+
+ inline uint32_t readBool(bool& value);
+ // Provide the default readBool() implementation for std::vector<bool>
+ using TVirtualProtocol<TBinaryProtocolT<Transport_, ByteOrder_> >::readBool;
+
+ inline uint32_t readByte(int8_t& byte);
+
+ inline uint32_t readI16(int16_t& i16);
+
+ inline uint32_t readI32(int32_t& i32);
+
+ inline uint32_t readI64(int64_t& i64);
+
+ inline uint32_t readDouble(double& dub);
+
+ template <typename StrType>
+ inline uint32_t readString(StrType& str);
+
+ inline uint32_t readBinary(std::string& str);
+
+protected:
+ template <typename StrType>
+ uint32_t readStringBody(StrType& str, int32_t sz);
+
+ Transport_* trans_;
+
+ int32_t string_limit_;
+ int32_t container_limit_;
+
+ // Enforce presence of version identifier
+ bool strict_read_;
+ bool strict_write_;
+};
+
+typedef TBinaryProtocolT<TTransport> TBinaryProtocol;
+typedef TBinaryProtocolT<TTransport, TNetworkLittleEndian> TLEBinaryProtocol;
+
+/**
+ * Constructs binary protocol handlers
+ */
+template <class Transport_, class ByteOrder_ = TNetworkBigEndian>
+class TBinaryProtocolFactoryT : public TProtocolFactory {
+public:
+ TBinaryProtocolFactoryT()
+ : string_limit_(0), container_limit_(0), strict_read_(false), strict_write_(true) {}
+
+ TBinaryProtocolFactoryT(int32_t string_limit,
+ int32_t container_limit,
+ bool strict_read,
+ bool strict_write)
+ : string_limit_(string_limit),
+ container_limit_(container_limit),
+ strict_read_(strict_read),
+ strict_write_(strict_write) {}
+
+ ~TBinaryProtocolFactoryT() override = default;
+
+ void setStringSizeLimit(int32_t string_limit) { string_limit_ = string_limit; }
+
+ void setContainerSizeLimit(int32_t container_limit) { container_limit_ = container_limit; }
+
+ void setStrict(bool strict_read, bool strict_write) {
+ strict_read_ = strict_read;
+ strict_write_ = strict_write;
+ }
+
+ std::shared_ptr<TProtocol> getProtocol(std::shared_ptr<TTransport> trans) override {
+ std::shared_ptr<Transport_> specific_trans = std::dynamic_pointer_cast<Transport_>(trans);
+ TProtocol* prot;
+ if (specific_trans) {
+ prot = new TBinaryProtocolT<Transport_, ByteOrder_>(specific_trans,
+ string_limit_,
+ container_limit_,
+ strict_read_,
+ strict_write_);
+ } else {
+ prot = new TBinaryProtocolT<TTransport, ByteOrder_>(trans,
+ string_limit_,
+ container_limit_,
+ strict_read_,
+ strict_write_);
+ }
+
+ return std::shared_ptr<TProtocol>(prot);
+ }
+
+private:
+ int32_t string_limit_;
+ int32_t container_limit_;
+ bool strict_read_;
+ bool strict_write_;
+};
+
+typedef TBinaryProtocolFactoryT<TTransport> TBinaryProtocolFactory;
+typedef TBinaryProtocolFactoryT<TTransport, TNetworkLittleEndian> TLEBinaryProtocolFactory;
+}
+}
+} // apache::thrift::protocol
+
+#include <thrift/protocol/TBinaryProtocol.tcc>
+
+#endif // #ifndef _THRIFT_PROTOCOL_TBINARYPROTOCOL_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TBinaryProtocol.tcc b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TBinaryProtocol.tcc
new file mode 100644
index 000000000..2964f25d0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TBinaryProtocol.tcc
@@ -0,0 +1,454 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_PROTOCOL_TBINARYPROTOCOL_TCC_
+#define _THRIFT_PROTOCOL_TBINARYPROTOCOL_TCC_ 1
+
+#include <thrift/protocol/TBinaryProtocol.h>
+
+#include <limits>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeMessageBegin(const std::string& name,
+ const TMessageType messageType,
+ const int32_t seqid) {
+ if (this->strict_write_) {
+ int32_t version = (VERSION_1) | ((int32_t)messageType);
+ uint32_t wsize = 0;
+ wsize += writeI32(version);
+ wsize += writeString(name);
+ wsize += writeI32(seqid);
+ return wsize;
+ } else {
+ uint32_t wsize = 0;
+ wsize += writeString(name);
+ wsize += writeByte((int8_t)messageType);
+ wsize += writeI32(seqid);
+ return wsize;
+ }
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeMessageEnd() {
+ return 0;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeStructBegin(const char* name) {
+ (void)name;
+ return 0;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeStructEnd() {
+ return 0;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeFieldBegin(const char* name,
+ const TType fieldType,
+ const int16_t fieldId) {
+ (void)name;
+ uint32_t wsize = 0;
+ wsize += writeByte((int8_t)fieldType);
+ wsize += writeI16(fieldId);
+ return wsize;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeFieldEnd() {
+ return 0;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeFieldStop() {
+ return writeByte((int8_t)T_STOP);
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeMapBegin(const TType keyType,
+ const TType valType,
+ const uint32_t size) {
+ uint32_t wsize = 0;
+ wsize += writeByte((int8_t)keyType);
+ wsize += writeByte((int8_t)valType);
+ wsize += writeI32((int32_t)size);
+ return wsize;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeMapEnd() {
+ return 0;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeListBegin(const TType elemType,
+ const uint32_t size) {
+ uint32_t wsize = 0;
+ wsize += writeByte((int8_t)elemType);
+ wsize += writeI32((int32_t)size);
+ return wsize;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeListEnd() {
+ return 0;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeSetBegin(const TType elemType,
+ const uint32_t size) {
+ uint32_t wsize = 0;
+ wsize += writeByte((int8_t)elemType);
+ wsize += writeI32((int32_t)size);
+ return wsize;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeSetEnd() {
+ return 0;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeBool(const bool value) {
+ uint8_t tmp = value ? 1 : 0;
+ this->trans_->write(&tmp, 1);
+ return 1;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeByte(const int8_t byte) {
+ this->trans_->write((uint8_t*)&byte, 1);
+ return 1;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeI16(const int16_t i16) {
+ auto net = (int16_t)ByteOrder_::toWire16(i16);
+ this->trans_->write((uint8_t*)&net, 2);
+ return 2;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeI32(const int32_t i32) {
+ auto net = (int32_t)ByteOrder_::toWire32(i32);
+ this->trans_->write((uint8_t*)&net, 4);
+ return 4;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeI64(const int64_t i64) {
+ auto net = (int64_t)ByteOrder_::toWire64(i64);
+ this->trans_->write((uint8_t*)&net, 8);
+ return 8;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeDouble(const double dub) {
+ static_assert(sizeof(double) == sizeof(uint64_t), "sizeof(double) == sizeof(uint64_t)");
+ static_assert(std::numeric_limits<double>::is_iec559, "std::numeric_limits<double>::is_iec559");
+
+ auto bits = bitwise_cast<uint64_t>(dub);
+ bits = ByteOrder_::toWire64(bits);
+ this->trans_->write((uint8_t*)&bits, 8);
+ return 8;
+}
+
+template <class Transport_, class ByteOrder_>
+template <typename StrType>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeString(const StrType& str) {
+ if (str.size() > static_cast<size_t>((std::numeric_limits<int32_t>::max)()))
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ auto size = static_cast<uint32_t>(str.size());
+ uint32_t result = writeI32((int32_t)size);
+ if (size > 0) {
+ this->trans_->write((uint8_t*)str.data(), size);
+ }
+ return result + size;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeBinary(const std::string& str) {
+ return TBinaryProtocolT<Transport_, ByteOrder_>::writeString(str);
+}
+
+/**
+ * Reading functions
+ */
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readMessageBegin(std::string& name,
+ TMessageType& messageType,
+ int32_t& seqid) {
+ uint32_t result = 0;
+ int32_t sz;
+ result += readI32(sz);
+
+ if (sz < 0) {
+ // Check for correct version number
+ int32_t version = sz & VERSION_MASK;
+ if (version != VERSION_1) {
+ throw TProtocolException(TProtocolException::BAD_VERSION, "Bad version identifier");
+ }
+ messageType = (TMessageType)(sz & 0x000000ff);
+ result += readString(name);
+ result += readI32(seqid);
+ } else {
+ if (this->strict_read_) {
+ throw TProtocolException(TProtocolException::BAD_VERSION,
+ "No version identifier... old protocol client in strict mode?");
+ } else {
+ // Handle pre-versioned input
+ int8_t type;
+ result += readStringBody(name, sz);
+ result += readByte(type);
+ messageType = (TMessageType)type;
+ result += readI32(seqid);
+ }
+ }
+ return result;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readMessageEnd() {
+ return 0;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readStructBegin(std::string& name) {
+ name = "";
+ return 0;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readStructEnd() {
+ return 0;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readFieldBegin(std::string& name,
+ TType& fieldType,
+ int16_t& fieldId) {
+ (void)name;
+ uint32_t result = 0;
+ int8_t type;
+ result += readByte(type);
+ fieldType = (TType)type;
+ if (fieldType == T_STOP) {
+ fieldId = 0;
+ return result;
+ }
+ result += readI16(fieldId);
+ return result;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readFieldEnd() {
+ return 0;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readMapBegin(TType& keyType,
+ TType& valType,
+ uint32_t& size) {
+ int8_t k, v;
+ uint32_t result = 0;
+ int32_t sizei;
+ result += readByte(k);
+ keyType = (TType)k;
+ result += readByte(v);
+ valType = (TType)v;
+ result += readI32(sizei);
+ if (sizei < 0) {
+ throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
+ } else if (this->container_limit_ && sizei > this->container_limit_) {
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ }
+ size = (uint32_t)sizei;
+ return result;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readMapEnd() {
+ return 0;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readListBegin(TType& elemType, uint32_t& size) {
+ int8_t e;
+ uint32_t result = 0;
+ int32_t sizei;
+ result += readByte(e);
+ elemType = (TType)e;
+ result += readI32(sizei);
+ if (sizei < 0) {
+ throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
+ } else if (this->container_limit_ && sizei > this->container_limit_) {
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ }
+ size = (uint32_t)sizei;
+ return result;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readListEnd() {
+ return 0;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readSetBegin(TType& elemType, uint32_t& size) {
+ int8_t e;
+ uint32_t result = 0;
+ int32_t sizei;
+ result += readByte(e);
+ elemType = (TType)e;
+ result += readI32(sizei);
+ if (sizei < 0) {
+ throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
+ } else if (this->container_limit_ && sizei > this->container_limit_) {
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ }
+ size = (uint32_t)sizei;
+ return result;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readSetEnd() {
+ return 0;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readBool(bool& value) {
+ uint8_t b[1];
+ this->trans_->readAll(b, 1);
+ value = *(int8_t*)b != 0;
+ return 1;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readByte(int8_t& byte) {
+ uint8_t b[1];
+ this->trans_->readAll(b, 1);
+ byte = *(int8_t*)b;
+ return 1;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readI16(int16_t& i16) {
+ union bytes {
+ uint8_t b[2];
+ int16_t all;
+ } theBytes;
+ this->trans_->readAll(theBytes.b, 2);
+ i16 = (int16_t)ByteOrder_::fromWire16(theBytes.all);
+ return 2;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readI32(int32_t& i32) {
+ union bytes {
+ uint8_t b[4];
+ int32_t all;
+ } theBytes;
+ this->trans_->readAll(theBytes.b, 4);
+ i32 = (int32_t)ByteOrder_::fromWire32(theBytes.all);
+ return 4;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readI64(int64_t& i64) {
+ union bytes {
+ uint8_t b[8];
+ int64_t all;
+ } theBytes;
+ this->trans_->readAll(theBytes.b, 8);
+ i64 = (int64_t)ByteOrder_::fromWire64(theBytes.all);
+ return 8;
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readDouble(double& dub) {
+ static_assert(sizeof(double) == sizeof(uint64_t), "sizeof(double) == sizeof(uint64_t)");
+ static_assert(std::numeric_limits<double>::is_iec559, "std::numeric_limits<double>::is_iec559");
+
+ union bytes {
+ uint8_t b[8];
+ uint64_t all;
+ } theBytes;
+ this->trans_->readAll(theBytes.b, 8);
+ theBytes.all = ByteOrder_::fromWire64(theBytes.all);
+ dub = bitwise_cast<double>(theBytes.all);
+ return 8;
+}
+
+template <class Transport_, class ByteOrder_>
+template <typename StrType>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readString(StrType& str) {
+ uint32_t result;
+ int32_t size;
+ result = readI32(size);
+ return result + readStringBody(str, size);
+}
+
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readBinary(std::string& str) {
+ return TBinaryProtocolT<Transport_, ByteOrder_>::readString(str);
+}
+
+template <class Transport_, class ByteOrder_>
+template <typename StrType>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readStringBody(StrType& str, int32_t size) {
+ uint32_t result = 0;
+
+ // Catch error cases
+ if (size < 0) {
+ throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
+ }
+ if (this->string_limit_ > 0 && size > this->string_limit_) {
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ }
+
+ // Catch empty string case
+ if (size == 0) {
+ str.clear();
+ return result;
+ }
+
+ // Try to borrow first
+ const uint8_t* borrow_buf;
+ uint32_t got = size;
+ if ((borrow_buf = this->trans_->borrow(nullptr, &got))) {
+ str.assign((const char*)borrow_buf, size);
+ this->trans_->consume(size);
+ return size;
+ }
+
+ str.resize(size);
+ this->trans_->readAll(reinterpret_cast<uint8_t*>(&str[0]), size);
+ return (uint32_t)size;
+}
+}
+}
+} // apache::thrift::protocol
+
+#endif // #ifndef _THRIFT_PROTOCOL_TBINARYPROTOCOL_TCC_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TCompactProtocol.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TCompactProtocol.h
new file mode 100644
index 000000000..2930aba29
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TCompactProtocol.h
@@ -0,0 +1,266 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_H_
+#define _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_H_ 1
+
+#include <thrift/protocol/TVirtualProtocol.h>
+
+#include <stack>
+#include <memory>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+/**
+ * C++ Implementation of the Compact Protocol as described in THRIFT-110
+ */
+template <class Transport_>
+class TCompactProtocolT : public TVirtualProtocol<TCompactProtocolT<Transport_> > {
+public:
+ static const int8_t PROTOCOL_ID = (int8_t)0x82u;
+ static const int8_t VERSION_N = 1;
+ static const int8_t VERSION_MASK = 0x1f; // 0001 1111
+
+protected:
+ static const int8_t TYPE_MASK = (int8_t)0xE0u; // 1110 0000
+ static const int8_t TYPE_BITS = 0x07; // 0000 0111
+ static const int32_t TYPE_SHIFT_AMOUNT = 5;
+
+ Transport_* trans_;
+
+ /**
+ * (Writing) If we encounter a boolean field begin, save the TField here
+ * so it can have the value incorporated.
+ */
+ struct {
+ const char* name;
+ TType fieldType;
+ int16_t fieldId;
+ } booleanField_;
+
+ /**
+ * (Reading) If we read a field header, and it's a boolean field, save
+ * the boolean value here so that readBool can use it.
+ */
+ struct {
+ bool hasBoolValue;
+ bool boolValue;
+ } boolValue_;
+
+ /**
+ * Used to keep track of the last field for the current and previous structs,
+ * so we can do the delta stuff.
+ */
+
+ std::stack<int16_t> lastField_;
+ int16_t lastFieldId_;
+
+public:
+ TCompactProtocolT(std::shared_ptr<Transport_> trans)
+ : TVirtualProtocol<TCompactProtocolT<Transport_> >(trans),
+ trans_(trans.get()),
+ lastFieldId_(0),
+ string_limit_(0),
+ string_buf_(nullptr),
+ string_buf_size_(0),
+ container_limit_(0) {
+ booleanField_.name = nullptr;
+ boolValue_.hasBoolValue = false;
+ }
+
+ TCompactProtocolT(std::shared_ptr<Transport_> trans,
+ int32_t string_limit,
+ int32_t container_limit)
+ : TVirtualProtocol<TCompactProtocolT<Transport_> >(trans),
+ trans_(trans.get()),
+ lastFieldId_(0),
+ string_limit_(string_limit),
+ string_buf_(nullptr),
+ string_buf_size_(0),
+ container_limit_(container_limit) {
+ booleanField_.name = nullptr;
+ boolValue_.hasBoolValue = false;
+ }
+
+ ~TCompactProtocolT() override { free(string_buf_); }
+
+ /**
+ * Writing functions
+ */
+
+ virtual uint32_t writeMessageBegin(const std::string& name,
+ const TMessageType messageType,
+ const int32_t seqid);
+
+ uint32_t writeStructBegin(const char* name);
+
+ uint32_t writeStructEnd();
+
+ uint32_t writeFieldBegin(const char* name, const TType fieldType, const int16_t fieldId);
+
+ uint32_t writeFieldStop();
+
+ uint32_t writeListBegin(const TType elemType, const uint32_t size);
+
+ uint32_t writeSetBegin(const TType elemType, const uint32_t size);
+
+ virtual uint32_t writeMapBegin(const TType keyType, const TType valType, const uint32_t size);
+
+ uint32_t writeBool(const bool value);
+
+ uint32_t writeByte(const int8_t byte);
+
+ uint32_t writeI16(const int16_t i16);
+
+ uint32_t writeI32(const int32_t i32);
+
+ uint32_t writeI64(const int64_t i64);
+
+ uint32_t writeDouble(const double dub);
+
+ uint32_t writeString(const std::string& str);
+
+ uint32_t writeBinary(const std::string& str);
+
+ /**
+ * These methods are called by structs, but don't actually have any wired
+ * output or purpose
+ */
+ virtual uint32_t writeMessageEnd() { return 0; }
+ uint32_t writeMapEnd() { return 0; }
+ uint32_t writeListEnd() { return 0; }
+ uint32_t writeSetEnd() { return 0; }
+ uint32_t writeFieldEnd() { return 0; }
+
+protected:
+ int32_t writeFieldBeginInternal(const char* name,
+ const TType fieldType,
+ const int16_t fieldId,
+ int8_t typeOverride);
+ uint32_t writeCollectionBegin(const TType elemType, int32_t size);
+ uint32_t writeVarint32(uint32_t n);
+ uint32_t writeVarint64(uint64_t n);
+ uint64_t i64ToZigzag(const int64_t l);
+ uint32_t i32ToZigzag(const int32_t n);
+ inline int8_t getCompactType(const TType ttype);
+
+public:
+ uint32_t readMessageBegin(std::string& name, TMessageType& messageType, int32_t& seqid);
+
+ uint32_t readStructBegin(std::string& name);
+
+ uint32_t readStructEnd();
+
+ uint32_t readFieldBegin(std::string& name, TType& fieldType, int16_t& fieldId);
+
+ uint32_t readMapBegin(TType& keyType, TType& valType, uint32_t& size);
+
+ uint32_t readListBegin(TType& elemType, uint32_t& size);
+
+ uint32_t readSetBegin(TType& elemType, uint32_t& size);
+
+ uint32_t readBool(bool& value);
+ // Provide the default readBool() implementation for std::vector<bool>
+ using TVirtualProtocol<TCompactProtocolT<Transport_> >::readBool;
+
+ uint32_t readByte(int8_t& byte);
+
+ uint32_t readI16(int16_t& i16);
+
+ uint32_t readI32(int32_t& i32);
+
+ uint32_t readI64(int64_t& i64);
+
+ uint32_t readDouble(double& dub);
+
+ uint32_t readString(std::string& str);
+
+ uint32_t readBinary(std::string& str);
+
+ /*
+ *These methods are here for the struct to call, but don't have any wire
+ * encoding.
+ */
+ uint32_t readMessageEnd() { return 0; }
+ uint32_t readFieldEnd() { return 0; }
+ uint32_t readMapEnd() { return 0; }
+ uint32_t readListEnd() { return 0; }
+ uint32_t readSetEnd() { return 0; }
+
+protected:
+ uint32_t readVarint32(int32_t& i32);
+ uint32_t readVarint64(int64_t& i64);
+ int32_t zigzagToI32(uint32_t n);
+ int64_t zigzagToI64(uint64_t n);
+ TType getTType(int8_t type);
+
+ // Buffer for reading strings, save for the lifetime of the protocol to
+ // avoid memory churn allocating memory on every string read
+ int32_t string_limit_;
+ uint8_t* string_buf_;
+ int32_t string_buf_size_;
+ int32_t container_limit_;
+};
+
+typedef TCompactProtocolT<TTransport> TCompactProtocol;
+
+/**
+ * Constructs compact protocol handlers
+ */
+template <class Transport_>
+class TCompactProtocolFactoryT : public TProtocolFactory {
+public:
+ TCompactProtocolFactoryT() : string_limit_(0), container_limit_(0) {}
+
+ TCompactProtocolFactoryT(int32_t string_limit, int32_t container_limit)
+ : string_limit_(string_limit), container_limit_(container_limit) {}
+
+ ~TCompactProtocolFactoryT() override = default;
+
+ void setStringSizeLimit(int32_t string_limit) { string_limit_ = string_limit; }
+
+ void setContainerSizeLimit(int32_t container_limit) { container_limit_ = container_limit; }
+
+ std::shared_ptr<TProtocol> getProtocol(std::shared_ptr<TTransport> trans) override {
+ std::shared_ptr<Transport_> specific_trans = std::dynamic_pointer_cast<Transport_>(trans);
+ TProtocol* prot;
+ if (specific_trans) {
+ prot = new TCompactProtocolT<Transport_>(specific_trans, string_limit_, container_limit_);
+ } else {
+ prot = new TCompactProtocol(trans, string_limit_, container_limit_);
+ }
+
+ return std::shared_ptr<TProtocol>(prot);
+ }
+
+private:
+ int32_t string_limit_;
+ int32_t container_limit_;
+};
+
+typedef TCompactProtocolFactoryT<TTransport> TCompactProtocolFactory;
+}
+}
+} // apache::thrift::protocol
+
+#include <thrift/protocol/TCompactProtocol.tcc>
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TCompactProtocol.tcc b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TCompactProtocol.tcc
new file mode 100644
index 000000000..d1e342efd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TCompactProtocol.tcc
@@ -0,0 +1,826 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_TCC_
+#define _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_TCC_ 1
+
+#include <limits>
+
+#include "thrift/config.h"
+
+/*
+ * TCompactProtocol::i*ToZigzag depend on the fact that the right shift
+ * operator on a signed integer is an arithmetic (sign-extending) shift.
+ * If this is not the case, the current implementation will not work.
+ * If anyone encounters this error, we can try to figure out the best
+ * way to implement an arithmetic right shift on their platform.
+ */
+#if !defined(SIGNED_RIGHT_SHIFT_IS) || !defined(ARITHMETIC_RIGHT_SHIFT)
+# error "Unable to determine the behavior of a signed right shift"
+#endif
+#if SIGNED_RIGHT_SHIFT_IS != ARITHMETIC_RIGHT_SHIFT
+# error "TCompactProtocol currently only works if a signed right shift is arithmetic"
+#endif
+
+#ifdef __GNUC__
+#define UNLIKELY(val) (__builtin_expect((val), 0))
+#else
+#define UNLIKELY(val) (val)
+#endif
+
+namespace apache { namespace thrift { namespace protocol {
+
+namespace detail { namespace compact {
+
+enum Types {
+ CT_STOP = 0x00,
+ CT_BOOLEAN_TRUE = 0x01,
+ CT_BOOLEAN_FALSE = 0x02,
+ CT_BYTE = 0x03,
+ CT_I16 = 0x04,
+ CT_I32 = 0x05,
+ CT_I64 = 0x06,
+ CT_DOUBLE = 0x07,
+ CT_BINARY = 0x08,
+ CT_LIST = 0x09,
+ CT_SET = 0x0A,
+ CT_MAP = 0x0B,
+ CT_STRUCT = 0x0C
+};
+
+const int8_t TTypeToCType[16] = {
+ CT_STOP, // T_STOP
+ 0, // unused
+ CT_BOOLEAN_TRUE, // T_BOOL
+ CT_BYTE, // T_BYTE
+ CT_DOUBLE, // T_DOUBLE
+ 0, // unused
+ CT_I16, // T_I16
+ 0, // unused
+ CT_I32, // T_I32
+ 0, // unused
+ CT_I64, // T_I64
+ CT_BINARY, // T_STRING
+ CT_STRUCT, // T_STRUCT
+ CT_MAP, // T_MAP
+ CT_SET, // T_SET
+ CT_LIST, // T_LIST
+};
+
+}} // end detail::compact namespace
+
+
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeMessageBegin(
+ const std::string& name,
+ const TMessageType messageType,
+ const int32_t seqid) {
+ uint32_t wsize = 0;
+ wsize += writeByte(PROTOCOL_ID);
+ wsize += writeByte((VERSION_N & VERSION_MASK) | (((int32_t)messageType << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
+ wsize += writeVarint32(seqid);
+ wsize += writeString(name);
+ return wsize;
+}
+
+/**
+ * Write a field header containing the field id and field type. If the
+ * difference between the current field id and the last one is small (< 15),
+ * then the field id will be encoded in the 4 MSB as a delta. Otherwise, the
+ * field id will follow the type header as a zigzag varint.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeFieldBegin(const char* name,
+ const TType fieldType,
+ const int16_t fieldId) {
+ if (fieldType == T_BOOL) {
+ booleanField_.name = name;
+ booleanField_.fieldType = fieldType;
+ booleanField_.fieldId = fieldId;
+ } else {
+ return writeFieldBeginInternal(name, fieldType, fieldId, -1);
+ }
+ return 0;
+}
+
+/**
+ * Write the STOP symbol so we know there are no more fields in this struct.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeFieldStop() {
+ return writeByte(T_STOP);
+}
+
+/**
+ * Write a struct begin. This doesn't actually put anything on the wire. We
+ * use it as an opportunity to put special placeholder markers on the field
+ * stack so we can get the field id deltas correct.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeStructBegin(const char* name) {
+ (void) name;
+ lastField_.push(lastFieldId_);
+ lastFieldId_ = 0;
+ return 0;
+}
+
+/**
+ * Write a struct end. This doesn't actually put anything on the wire. We use
+ * this as an opportunity to pop the last field from the current struct off
+ * of the field stack.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeStructEnd() {
+ lastFieldId_ = lastField_.top();
+ lastField_.pop();
+ return 0;
+}
+
+/**
+ * Write a List header.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeListBegin(const TType elemType,
+ const uint32_t size) {
+ return writeCollectionBegin(elemType, size);
+}
+
+/**
+ * Write a set header.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeSetBegin(const TType elemType,
+ const uint32_t size) {
+ return writeCollectionBegin(elemType, size);
+}
+
+/**
+ * Write a map header. If the map is empty, omit the key and value type
+ * headers, as we don't need any additional information to skip it.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeMapBegin(const TType keyType,
+ const TType valType,
+ const uint32_t size) {
+ uint32_t wsize = 0;
+
+ if (size == 0) {
+ wsize += writeByte(0);
+ } else {
+ wsize += writeVarint32(size);
+ wsize += writeByte(getCompactType(keyType) << 4 | getCompactType(valType));
+ }
+ return wsize;
+}
+
+/**
+ * Write a boolean value. Potentially, this could be a boolean field, in
+ * which case the field header info isn't written yet. If so, decide what the
+ * right type header is for the value and then write the field header.
+ * Otherwise, write a single byte.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeBool(const bool value) {
+ uint32_t wsize = 0;
+
+ if (booleanField_.name != nullptr) {
+ // we haven't written the field header yet
+ wsize
+ += writeFieldBeginInternal(booleanField_.name,
+ booleanField_.fieldType,
+ booleanField_.fieldId,
+ static_cast<int8_t>(value
+ ? detail::compact::CT_BOOLEAN_TRUE
+ : detail::compact::CT_BOOLEAN_FALSE));
+ booleanField_.name = nullptr;
+ } else {
+ // we're not part of a field, so just write the value
+ wsize
+ += writeByte(static_cast<int8_t>(value
+ ? detail::compact::CT_BOOLEAN_TRUE
+ : detail::compact::CT_BOOLEAN_FALSE));
+ }
+ return wsize;
+}
+
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeByte(const int8_t byte) {
+ trans_->write((uint8_t*)&byte, 1);
+ return 1;
+}
+
+/**
+ * Write an i16 as a zigzag varint.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeI16(const int16_t i16) {
+ return writeVarint32(i32ToZigzag(i16));
+}
+
+/**
+ * Write an i32 as a zigzag varint.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeI32(const int32_t i32) {
+ return writeVarint32(i32ToZigzag(i32));
+}
+
+/**
+ * Write an i64 as a zigzag varint.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeI64(const int64_t i64) {
+ return writeVarint64(i64ToZigzag(i64));
+}
+
+/**
+ * Write a double to the wire as 8 bytes.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeDouble(const double dub) {
+ static_assert(sizeof(double) == sizeof(uint64_t), "sizeof(double) == sizeof(uint64_t)");
+ static_assert(std::numeric_limits<double>::is_iec559, "std::numeric_limits<double>::is_iec559");
+
+ auto bits = bitwise_cast<uint64_t>(dub);
+ bits = THRIFT_htolell(bits);
+ trans_->write((uint8_t*)&bits, 8);
+ return 8;
+}
+
+/**
+ * Write a string to the wire with a varint size preceding.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeString(const std::string& str) {
+ return writeBinary(str);
+}
+
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeBinary(const std::string& str) {
+ if(str.size() > (std::numeric_limits<uint32_t>::max)())
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ auto ssize = static_cast<uint32_t>(str.size());
+ uint32_t wsize = writeVarint32(ssize) ;
+ // checking ssize + wsize > uint_max, but we don't want to overflow while checking for overflows.
+ // transforming the check to ssize > uint_max - wsize
+ if(ssize > (std::numeric_limits<uint32_t>::max)() - wsize)
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ wsize += ssize;
+ trans_->write((uint8_t*)str.data(), ssize);
+ return wsize;
+}
+
+//
+// Internal Writing methods
+//
+
+/**
+ * The workhorse of writeFieldBegin. It has the option of doing a
+ * 'type override' of the type header. This is used specifically in the
+ * boolean field case.
+ */
+template <class Transport_>
+int32_t TCompactProtocolT<Transport_>::writeFieldBeginInternal(
+ const char* name,
+ const TType fieldType,
+ const int16_t fieldId,
+ int8_t typeOverride) {
+ (void) name;
+ uint32_t wsize = 0;
+
+ // if there's a type override, use that.
+ int8_t typeToWrite = (typeOverride == -1 ? getCompactType(fieldType) : typeOverride);
+
+ // check if we can use delta encoding for the field id
+ if (fieldId > lastFieldId_ && fieldId - lastFieldId_ <= 15) {
+ // write them together
+ wsize += writeByte(static_cast<int8_t>((fieldId - lastFieldId_)
+ << 4 | typeToWrite));
+ } else {
+ // write them separate
+ wsize += writeByte(typeToWrite);
+ wsize += writeI16(fieldId);
+ }
+
+ lastFieldId_ = fieldId;
+ return wsize;
+}
+
+/**
+ * Abstract method for writing the start of lists and sets. List and sets on
+ * the wire differ only by the type indicator.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeCollectionBegin(const TType elemType,
+ int32_t size) {
+ uint32_t wsize = 0;
+ if (size <= 14) {
+ wsize += writeByte(static_cast<int8_t>(size
+ << 4 | getCompactType(elemType)));
+ } else {
+ wsize += writeByte(0xf0 | getCompactType(elemType));
+ wsize += writeVarint32(size);
+ }
+ return wsize;
+}
+
+/**
+ * Write an i32 as a varint. Results in 1-5 bytes on the wire.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeVarint32(uint32_t n) {
+ uint8_t buf[5];
+ uint32_t wsize = 0;
+
+ while (true) {
+ if ((n & ~0x7F) == 0) {
+ buf[wsize++] = (int8_t)n;
+ break;
+ } else {
+ buf[wsize++] = (int8_t)((n & 0x7F) | 0x80);
+ n >>= 7;
+ }
+ }
+ trans_->write(buf, wsize);
+ return wsize;
+}
+
+/**
+ * Write an i64 as a varint. Results in 1-10 bytes on the wire.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::writeVarint64(uint64_t n) {
+ uint8_t buf[10];
+ uint32_t wsize = 0;
+
+ while (true) {
+ if ((n & ~0x7FL) == 0) {
+ buf[wsize++] = (int8_t)n;
+ break;
+ } else {
+ buf[wsize++] = (int8_t)((n & 0x7F) | 0x80);
+ n >>= 7;
+ }
+ }
+ trans_->write(buf, wsize);
+ return wsize;
+}
+
+/**
+ * Convert l into a zigzag long. This allows negative numbers to be
+ * represented compactly as a varint.
+ */
+template <class Transport_>
+uint64_t TCompactProtocolT<Transport_>::i64ToZigzag(const int64_t l) {
+ return (static_cast<uint64_t>(l) << 1) ^ (l >> 63);
+}
+
+/**
+ * Convert n into a zigzag int. This allows negative numbers to be
+ * represented compactly as a varint.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::i32ToZigzag(const int32_t n) {
+ return (static_cast<uint32_t>(n) << 1) ^ (n >> 31);
+}
+
+/**
+ * Given a TType value, find the appropriate detail::compact::Types value
+ */
+template <class Transport_>
+int8_t TCompactProtocolT<Transport_>::getCompactType(const TType ttype) {
+ return detail::compact::TTypeToCType[ttype];
+}
+
+//
+// Reading Methods
+//
+
+/**
+ * Read a message header.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::readMessageBegin(
+ std::string& name,
+ TMessageType& messageType,
+ int32_t& seqid) {
+ uint32_t rsize = 0;
+ int8_t protocolId;
+ int8_t versionAndType;
+ int8_t version;
+
+ rsize += readByte(protocolId);
+ if (protocolId != PROTOCOL_ID) {
+ throw TProtocolException(TProtocolException::BAD_VERSION, "Bad protocol identifier");
+ }
+
+ rsize += readByte(versionAndType);
+ version = (int8_t)(versionAndType & VERSION_MASK);
+ if (version != VERSION_N) {
+ throw TProtocolException(TProtocolException::BAD_VERSION, "Bad protocol version");
+ }
+
+ messageType = (TMessageType)((versionAndType >> TYPE_SHIFT_AMOUNT) & TYPE_BITS);
+ rsize += readVarint32(seqid);
+ rsize += readString(name);
+
+ return rsize;
+}
+
+/**
+ * Read a struct begin. There's nothing on the wire for this, but it is our
+ * opportunity to push a new struct begin marker on the field stack.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::readStructBegin(std::string& name) {
+ name = "";
+ lastField_.push(lastFieldId_);
+ lastFieldId_ = 0;
+ return 0;
+}
+
+/**
+ * Doesn't actually consume any wire data, just removes the last field for
+ * this struct from the field stack.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::readStructEnd() {
+ lastFieldId_ = lastField_.top();
+ lastField_.pop();
+ return 0;
+}
+
+/**
+ * Read a field header off the wire.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::readFieldBegin(std::string& name,
+ TType& fieldType,
+ int16_t& fieldId) {
+ (void) name;
+ uint32_t rsize = 0;
+ int8_t byte;
+ int8_t type;
+
+ rsize += readByte(byte);
+ type = (byte & 0x0f);
+
+ // if it's a stop, then we can return immediately, as the struct is over.
+ if (type == T_STOP) {
+ fieldType = T_STOP;
+ fieldId = 0;
+ return rsize;
+ }
+
+ // mask off the 4 MSB of the type header. it could contain a field id delta.
+ auto modifier = (int16_t)(((uint8_t)byte & 0xf0) >> 4);
+ if (modifier == 0) {
+ // not a delta, look ahead for the zigzag varint field id.
+ rsize += readI16(fieldId);
+ } else {
+ fieldId = (int16_t)(lastFieldId_ + modifier);
+ }
+ fieldType = getTType(type);
+
+ // if this happens to be a boolean field, the value is encoded in the type
+ if (type == detail::compact::CT_BOOLEAN_TRUE ||
+ type == detail::compact::CT_BOOLEAN_FALSE) {
+ // save the boolean value in a special instance variable.
+ boolValue_.hasBoolValue = true;
+ boolValue_.boolValue =
+ (type == detail::compact::CT_BOOLEAN_TRUE ? true : false);
+ }
+
+ // push the new field onto the field stack so we can keep the deltas going.
+ lastFieldId_ = fieldId;
+ return rsize;
+}
+
+/**
+ * Read a map header off the wire. If the size is zero, skip reading the key
+ * and value type. This means that 0-length maps will yield TMaps without the
+ * "correct" types.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::readMapBegin(TType& keyType,
+ TType& valType,
+ uint32_t& size) {
+ uint32_t rsize = 0;
+ int8_t kvType = 0;
+ int32_t msize = 0;
+
+ rsize += readVarint32(msize);
+ if (msize != 0)
+ rsize += readByte(kvType);
+
+ if (msize < 0) {
+ throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
+ } else if (container_limit_ && msize > container_limit_) {
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ }
+
+ keyType = getTType((int8_t)((uint8_t)kvType >> 4));
+ valType = getTType((int8_t)((uint8_t)kvType & 0xf));
+ size = (uint32_t)msize;
+
+ return rsize;
+}
+
+/**
+ * Read a list header off the wire. If the list size is 0-14, the size will
+ * be packed into the element type header. If it's a longer list, the 4 MSB
+ * of the element type header will be 0xF, and a varint will follow with the
+ * true size.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::readListBegin(TType& elemType,
+ uint32_t& size) {
+ int8_t size_and_type;
+ uint32_t rsize = 0;
+ int32_t lsize;
+
+ rsize += readByte(size_and_type);
+
+ lsize = ((uint8_t)size_and_type >> 4) & 0x0f;
+ if (lsize == 15) {
+ rsize += readVarint32(lsize);
+ }
+
+ if (lsize < 0) {
+ throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
+ } else if (container_limit_ && lsize > container_limit_) {
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ }
+
+ elemType = getTType((int8_t)(size_and_type & 0x0f));
+ size = (uint32_t)lsize;
+
+ return rsize;
+}
+
+/**
+ * Read a set header off the wire. If the set size is 0-14, the size will
+ * be packed into the element type header. If it's a longer set, the 4 MSB
+ * of the element type header will be 0xF, and a varint will follow with the
+ * true size.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::readSetBegin(TType& elemType,
+ uint32_t& size) {
+ return readListBegin(elemType, size);
+}
+
+/**
+ * Read a boolean off the wire. If this is a boolean field, the value should
+ * already have been read during readFieldBegin, so we'll just consume the
+ * pre-stored value. Otherwise, read a byte.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::readBool(bool& value) {
+ if (boolValue_.hasBoolValue == true) {
+ value = boolValue_.boolValue;
+ boolValue_.hasBoolValue = false;
+ return 0;
+ } else {
+ int8_t val;
+ readByte(val);
+ value = (val == detail::compact::CT_BOOLEAN_TRUE);
+ return 1;
+ }
+}
+
+/**
+ * Read a single byte off the wire. Nothing interesting here.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::readByte(int8_t& byte) {
+ uint8_t b[1];
+ trans_->readAll(b, 1);
+ byte = *(int8_t*)b;
+ return 1;
+}
+
+/**
+ * Read an i16 from the wire as a zigzag varint.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::readI16(int16_t& i16) {
+ int32_t value;
+ uint32_t rsize = readVarint32(value);
+ i16 = (int16_t)zigzagToI32(value);
+ return rsize;
+}
+
+/**
+ * Read an i32 from the wire as a zigzag varint.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::readI32(int32_t& i32) {
+ int32_t value;
+ uint32_t rsize = readVarint32(value);
+ i32 = zigzagToI32(value);
+ return rsize;
+}
+
+/**
+ * Read an i64 from the wire as a zigzag varint.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::readI64(int64_t& i64) {
+ int64_t value;
+ uint32_t rsize = readVarint64(value);
+ i64 = zigzagToI64(value);
+ return rsize;
+}
+
+/**
+ * No magic here - just read a double off the wire.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::readDouble(double& dub) {
+ static_assert(sizeof(double) == sizeof(uint64_t), "sizeof(double) == sizeof(uint64_t)");
+ static_assert(std::numeric_limits<double>::is_iec559, "std::numeric_limits<double>::is_iec559");
+
+ union {
+ uint64_t bits;
+ uint8_t b[8];
+ } u;
+ trans_->readAll(u.b, 8);
+ u.bits = THRIFT_letohll(u.bits);
+ dub = bitwise_cast<double>(u.bits);
+ return 8;
+}
+
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::readString(std::string& str) {
+ return readBinary(str);
+}
+
+/**
+ * Read a byte[] from the wire.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::readBinary(std::string& str) {
+ int32_t rsize = 0;
+ int32_t size;
+
+ rsize += readVarint32(size);
+ // Catch empty string case
+ if (size == 0) {
+ str = "";
+ return rsize;
+ }
+
+ // Catch error cases
+ if (size < 0) {
+ throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
+ }
+ if (string_limit_ > 0 && size > string_limit_) {
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ }
+
+ // Use the heap here to prevent stack overflow for v. large strings
+ if (size > string_buf_size_ || string_buf_ == nullptr) {
+ void* new_string_buf = std::realloc(string_buf_, (uint32_t)size);
+ if (new_string_buf == nullptr) {
+ throw std::bad_alloc();
+ }
+ string_buf_ = (uint8_t*)new_string_buf;
+ string_buf_size_ = size;
+ }
+ trans_->readAll(string_buf_, size);
+ str.assign((char*)string_buf_, size);
+
+ return rsize + (uint32_t)size;
+}
+
+/**
+ * Read an i32 from the wire as a varint. The MSB of each byte is set
+ * if there is another byte to follow. This can read up to 5 bytes.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::readVarint32(int32_t& i32) {
+ int64_t val;
+ uint32_t rsize = readVarint64(val);
+ i32 = (int32_t)val;
+ return rsize;
+}
+
+/**
+ * Read an i64 from the wire as a proper varint. The MSB of each byte is set
+ * if there is another byte to follow. This can read up to 10 bytes.
+ */
+template <class Transport_>
+uint32_t TCompactProtocolT<Transport_>::readVarint64(int64_t& i64) {
+ uint32_t rsize = 0;
+ uint64_t val = 0;
+ int shift = 0;
+ uint8_t buf[10]; // 64 bits / (7 bits/byte) = 10 bytes.
+ uint32_t buf_size = sizeof(buf);
+ const uint8_t* borrowed = trans_->borrow(buf, &buf_size);
+
+ // Fast path.
+ if (borrowed != nullptr) {
+ while (true) {
+ uint8_t byte = borrowed[rsize];
+ rsize++;
+ val |= (uint64_t)(byte & 0x7f) << shift;
+ shift += 7;
+ if (!(byte & 0x80)) {
+ i64 = val;
+ trans_->consume(rsize);
+ return rsize;
+ }
+ // Have to check for invalid data so we don't crash.
+ if (UNLIKELY(rsize == sizeof(buf))) {
+ throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes.");
+ }
+ }
+ }
+
+ // Slow path.
+ else {
+ while (true) {
+ uint8_t byte;
+ rsize += trans_->readAll(&byte, 1);
+ val |= (uint64_t)(byte & 0x7f) << shift;
+ shift += 7;
+ if (!(byte & 0x80)) {
+ i64 = val;
+ return rsize;
+ }
+ // Might as well check for invalid data on the slow path too.
+ if (UNLIKELY(rsize >= sizeof(buf))) {
+ throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes.");
+ }
+ }
+ }
+}
+
+/**
+ * Convert from zigzag int to int.
+ */
+template <class Transport_>
+int32_t TCompactProtocolT<Transport_>::zigzagToI32(uint32_t n) {
+ return (n >> 1) ^ static_cast<uint32_t>(-static_cast<int32_t>(n & 1));
+}
+
+/**
+ * Convert from zigzag long to long.
+ */
+template <class Transport_>
+int64_t TCompactProtocolT<Transport_>::zigzagToI64(uint64_t n) {
+ return (n >> 1) ^ static_cast<uint64_t>(-static_cast<int64_t>(n & 1));
+}
+
+template <class Transport_>
+TType TCompactProtocolT<Transport_>::getTType(int8_t type) {
+ switch (type) {
+ case T_STOP:
+ return T_STOP;
+ case detail::compact::CT_BOOLEAN_FALSE:
+ case detail::compact::CT_BOOLEAN_TRUE:
+ return T_BOOL;
+ case detail::compact::CT_BYTE:
+ return T_BYTE;
+ case detail::compact::CT_I16:
+ return T_I16;
+ case detail::compact::CT_I32:
+ return T_I32;
+ case detail::compact::CT_I64:
+ return T_I64;
+ case detail::compact::CT_DOUBLE:
+ return T_DOUBLE;
+ case detail::compact::CT_BINARY:
+ return T_STRING;
+ case detail::compact::CT_LIST:
+ return T_LIST;
+ case detail::compact::CT_SET:
+ return T_SET;
+ case detail::compact::CT_MAP:
+ return T_MAP;
+ case detail::compact::CT_STRUCT:
+ return T_STRUCT;
+ default:
+ throw TException(std::string("don't know what type: ") + (char)type);
+ }
+}
+
+}}} // apache::thrift::protocol
+
+#endif // _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_TCC_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TDebugProtocol.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TDebugProtocol.cpp
new file mode 100644
index 000000000..0e6d4a2aa
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TDebugProtocol.cpp
@@ -0,0 +1,393 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/protocol/TDebugProtocol.h>
+
+#include <thrift/TToString.h>
+#include <cassert>
+#include <cctype>
+#include <cstdio>
+#include <stdexcept>
+
+using std::string;
+
+static string byte_to_hex(const uint8_t byte) {
+ char buf[3];
+ int ret = std::sprintf(buf, "%02x", (int)byte);
+ THRIFT_UNUSED_VARIABLE(ret);
+ assert(ret == 2);
+ assert(buf[2] == '\0');
+ return buf;
+}
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+string TDebugProtocol::fieldTypeName(TType type) {
+ switch (type) {
+ case T_STOP:
+ return "stop";
+ case T_VOID:
+ return "void";
+ case T_BOOL:
+ return "bool";
+ case T_BYTE:
+ return "byte";
+ case T_I16:
+ return "i16";
+ case T_I32:
+ return "i32";
+ case T_U64:
+ return "u64";
+ case T_I64:
+ return "i64";
+ case T_DOUBLE:
+ return "double";
+ case T_STRING:
+ return "string";
+ case T_STRUCT:
+ return "struct";
+ case T_MAP:
+ return "map";
+ case T_SET:
+ return "set";
+ case T_LIST:
+ return "list";
+ case T_UTF8:
+ return "utf8";
+ case T_UTF16:
+ return "utf16";
+ default:
+ return "unknown";
+ }
+}
+
+void TDebugProtocol::indentUp() {
+ indent_str_ += string(indent_inc, ' ');
+}
+
+void TDebugProtocol::indentDown() {
+ if (indent_str_.length() < (string::size_type)indent_inc) {
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ }
+ indent_str_.erase(indent_str_.length() - indent_inc);
+}
+
+uint32_t TDebugProtocol::writePlain(const string& str) {
+ if (str.length() > (std::numeric_limits<uint32_t>::max)())
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ trans_->write((uint8_t*)str.data(), static_cast<uint32_t>(str.length()));
+ return static_cast<uint32_t>(str.length());
+}
+
+uint32_t TDebugProtocol::writeIndented(const string& str) {
+ if (str.length() > (std::numeric_limits<uint32_t>::max)())
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ if (indent_str_.length() > (std::numeric_limits<uint32_t>::max)())
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ uint64_t total_len = indent_str_.length() + str.length();
+ if (total_len > (std::numeric_limits<uint32_t>::max)())
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ trans_->write((uint8_t*)indent_str_.data(), static_cast<uint32_t>(indent_str_.length()));
+ trans_->write((uint8_t*)str.data(), static_cast<uint32_t>(str.length()));
+ return static_cast<uint32_t>(indent_str_.length() + str.length());
+}
+
+uint32_t TDebugProtocol::startItem() {
+ uint32_t size;
+
+ switch (write_state_.back()) {
+ case UNINIT:
+ // XXX figure out what to do here.
+ // throw TProtocolException(TProtocolException::INVALID_DATA);
+ // return writeIndented(str);
+ return 0;
+ case STRUCT:
+ return 0;
+ case SET:
+ return writeIndented("");
+ case MAP_KEY:
+ return writeIndented("");
+ case MAP_VALUE:
+ return writePlain(" -> ");
+ case LIST:
+ size = writeIndented("[" + to_string(list_idx_.back()) + "] = ");
+ list_idx_.back()++;
+ return size;
+ default:
+ throw std::logic_error("Invalid enum value.");
+ }
+}
+
+uint32_t TDebugProtocol::endItem() {
+ // uint32_t size;
+
+ switch (write_state_.back()) {
+ case UNINIT:
+ // XXX figure out what to do here.
+ // throw TProtocolException(TProtocolException::INVALID_DATA);
+ // return writeIndented(str);
+ return 0;
+ case STRUCT:
+ return writePlain(",\n");
+ case SET:
+ return writePlain(",\n");
+ case MAP_KEY:
+ write_state_.back() = MAP_VALUE;
+ return 0;
+ case MAP_VALUE:
+ write_state_.back() = MAP_KEY;
+ return writePlain(",\n");
+ case LIST:
+ return writePlain(",\n");
+ default:
+ throw std::logic_error("Invalid enum value.");
+ }
+}
+
+uint32_t TDebugProtocol::writeItem(const std::string& str) {
+ uint32_t size = 0;
+ size += startItem();
+ size += writePlain(str);
+ size += endItem();
+ return size;
+}
+
+uint32_t TDebugProtocol::writeMessageBegin(const std::string& name,
+ const TMessageType messageType,
+ const int32_t seqid) {
+ (void)seqid;
+ string mtype;
+ switch (messageType) {
+ case T_CALL:
+ mtype = "call";
+ break;
+ case T_REPLY:
+ mtype = "reply";
+ break;
+ case T_EXCEPTION:
+ mtype = "exn";
+ break;
+ case T_ONEWAY:
+ mtype = "oneway";
+ break;
+ }
+
+ uint32_t size = writeIndented("(" + mtype + ") " + name + "(");
+ indentUp();
+ return size;
+}
+
+uint32_t TDebugProtocol::writeMessageEnd() {
+ indentDown();
+ return writeIndented(")\n");
+}
+
+uint32_t TDebugProtocol::writeStructBegin(const char* name) {
+ uint32_t size = 0;
+ size += startItem();
+ size += writePlain(string(name) + " {\n");
+ indentUp();
+ write_state_.push_back(STRUCT);
+ return size;
+}
+
+uint32_t TDebugProtocol::writeStructEnd() {
+ indentDown();
+ write_state_.pop_back();
+ uint32_t size = 0;
+ size += writeIndented("}");
+ size += endItem();
+ return size;
+}
+
+uint32_t TDebugProtocol::writeFieldBegin(const char* name,
+ const TType fieldType,
+ const int16_t fieldId) {
+ // sprintf(id_str, "%02d", fieldId);
+ string id_str = to_string(fieldId);
+ if (id_str.length() == 1)
+ id_str = '0' + id_str;
+
+ return writeIndented(id_str + ": " + name + " (" + fieldTypeName(fieldType) + ") = ");
+}
+
+uint32_t TDebugProtocol::writeFieldEnd() {
+ assert(write_state_.back() == STRUCT);
+ return 0;
+}
+
+uint32_t TDebugProtocol::writeFieldStop() {
+ return 0;
+ // writeIndented("***STOP***\n");
+}
+
+uint32_t TDebugProtocol::writeMapBegin(const TType keyType,
+ const TType valType,
+ const uint32_t size) {
+ // TODO(dreiss): Optimize short maps?
+ uint32_t bsize = 0;
+ bsize += startItem();
+ bsize += writePlain(
+ "map<" + fieldTypeName(keyType) + "," + fieldTypeName(valType) + ">"
+ "[" + to_string(size) + "] {\n");
+ indentUp();
+ write_state_.push_back(MAP_KEY);
+ return bsize;
+}
+
+uint32_t TDebugProtocol::writeMapEnd() {
+ indentDown();
+ write_state_.pop_back();
+ uint32_t size = 0;
+ size += writeIndented("}");
+ size += endItem();
+ return size;
+}
+
+uint32_t TDebugProtocol::writeListBegin(const TType elemType, const uint32_t size) {
+ // TODO(dreiss): Optimize short arrays.
+ uint32_t bsize = 0;
+ bsize += startItem();
+ bsize += writePlain(
+ "list<" + fieldTypeName(elemType) + ">"
+ "[" + to_string(size) + "] {\n");
+ indentUp();
+ write_state_.push_back(LIST);
+ list_idx_.push_back(0);
+ return bsize;
+}
+
+uint32_t TDebugProtocol::writeListEnd() {
+ indentDown();
+ write_state_.pop_back();
+ list_idx_.pop_back();
+ uint32_t size = 0;
+ size += writeIndented("}");
+ size += endItem();
+ return size;
+}
+
+uint32_t TDebugProtocol::writeSetBegin(const TType elemType, const uint32_t size) {
+ // TODO(dreiss): Optimize short sets.
+ uint32_t bsize = 0;
+ bsize += startItem();
+ bsize += writePlain(
+ "set<" + fieldTypeName(elemType) + ">"
+ "[" + to_string(size) + "] {\n");
+ indentUp();
+ write_state_.push_back(SET);
+ return bsize;
+}
+
+uint32_t TDebugProtocol::writeSetEnd() {
+ indentDown();
+ write_state_.pop_back();
+ uint32_t size = 0;
+ size += writeIndented("}");
+ size += endItem();
+ return size;
+}
+
+uint32_t TDebugProtocol::writeBool(const bool value) {
+ return writeItem(value ? "true" : "false");
+}
+
+uint32_t TDebugProtocol::writeByte(const int8_t byte) {
+ return writeItem("0x" + byte_to_hex(byte));
+}
+
+uint32_t TDebugProtocol::writeI16(const int16_t i16) {
+ return writeItem(to_string(i16));
+}
+
+uint32_t TDebugProtocol::writeI32(const int32_t i32) {
+ return writeItem(to_string(i32));
+}
+
+uint32_t TDebugProtocol::writeI64(const int64_t i64) {
+ return writeItem(to_string(i64));
+}
+
+uint32_t TDebugProtocol::writeDouble(const double dub) {
+ return writeItem(to_string(dub));
+}
+
+uint32_t TDebugProtocol::writeString(const string& str) {
+ // XXX Raw/UTF-8?
+
+ string to_show = str;
+ if (to_show.length() > (string::size_type)string_limit_) {
+ to_show = str.substr(0, string_prefix_size_);
+ to_show += "[...](" + to_string(str.length()) + ")";
+ }
+
+ string output = "\"";
+
+ for (string::const_iterator it = to_show.begin(); it != to_show.end(); ++it) {
+ if (*it == '\\') {
+ output += "\\\\";
+ } else if (*it == '"') {
+ output += "\\\"";
+ // passing characters <0 to std::isprint causes asserts. isprint takes an
+ // int, so we need to be careful of sign extension
+ } else if (std::isprint((unsigned char)*it)) {
+ output += *it;
+ } else {
+ switch (*it) {
+ case '\a':
+ output += "\\a";
+ break;
+ case '\b':
+ output += "\\b";
+ break;
+ case '\f':
+ output += "\\f";
+ break;
+ case '\n':
+ output += "\\n";
+ break;
+ case '\r':
+ output += "\\r";
+ break;
+ case '\t':
+ output += "\\t";
+ break;
+ case '\v':
+ output += "\\v";
+ break;
+ default:
+ output += "\\x";
+ output += byte_to_hex(*it);
+ }
+ }
+ }
+
+ output += '\"';
+ return writeItem(output);
+}
+
+uint32_t TDebugProtocol::writeBinary(const string& str) {
+ // XXX Hex?
+ return TDebugProtocol::writeString(str);
+}
+}
+}
+} // apache::thrift::protocol
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TDebugProtocol.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TDebugProtocol.h
new file mode 100644
index 000000000..41bb0d4ec
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TDebugProtocol.h
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_PROTOCOL_TDEBUGPROTOCOL_H_
+#define _THRIFT_PROTOCOL_TDEBUGPROTOCOL_H_ 1
+
+#include <thrift/protocol/TVirtualProtocol.h>
+
+#include <memory>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+/*
+
+!!! EXPERIMENTAL CODE !!!
+
+This protocol is very much a work in progress.
+It doesn't handle many cases properly.
+It throws exceptions in many cases.
+It probably segfaults in many cases.
+Bug reports and feature requests are welcome.
+Complaints are not. :R
+
+*/
+
+/**
+ * Protocol that prints the payload in a nice human-readable format.
+ * Reading from this protocol is not supported.
+ *
+ */
+class TDebugProtocol : public TVirtualProtocol<TDebugProtocol> {
+private:
+ enum write_state_t { UNINIT, STRUCT, LIST, SET, MAP_KEY, MAP_VALUE };
+
+public:
+ TDebugProtocol(std::shared_ptr<TTransport> trans)
+ : TVirtualProtocol<TDebugProtocol>(trans),
+ trans_(trans.get()),
+ string_limit_(DEFAULT_STRING_LIMIT),
+ string_prefix_size_(DEFAULT_STRING_PREFIX_SIZE) {
+ write_state_.push_back(UNINIT);
+ }
+
+ static const int32_t DEFAULT_STRING_LIMIT = 256;
+ static const int32_t DEFAULT_STRING_PREFIX_SIZE = 16;
+
+ void setStringSizeLimit(int32_t string_limit) { string_limit_ = string_limit; }
+
+ void setStringPrefixSize(int32_t string_prefix_size) { string_prefix_size_ = string_prefix_size; }
+
+ uint32_t writeMessageBegin(const std::string& name,
+ const TMessageType messageType,
+ const int32_t seqid);
+
+ uint32_t writeMessageEnd();
+
+ uint32_t writeStructBegin(const char* name);
+
+ uint32_t writeStructEnd();
+
+ uint32_t writeFieldBegin(const char* name, const TType fieldType, const int16_t fieldId);
+
+ uint32_t writeFieldEnd();
+
+ uint32_t writeFieldStop();
+
+ uint32_t writeMapBegin(const TType keyType, const TType valType, const uint32_t size);
+
+ uint32_t writeMapEnd();
+
+ uint32_t writeListBegin(const TType elemType, const uint32_t size);
+
+ uint32_t writeListEnd();
+
+ uint32_t writeSetBegin(const TType elemType, const uint32_t size);
+
+ uint32_t writeSetEnd();
+
+ uint32_t writeBool(const bool value);
+
+ uint32_t writeByte(const int8_t byte);
+
+ uint32_t writeI16(const int16_t i16);
+
+ uint32_t writeI32(const int32_t i32);
+
+ uint32_t writeI64(const int64_t i64);
+
+ uint32_t writeDouble(const double dub);
+
+ uint32_t writeString(const std::string& str);
+
+ uint32_t writeBinary(const std::string& str);
+
+private:
+ void indentUp();
+ void indentDown();
+ uint32_t writePlain(const std::string& str);
+ uint32_t writeIndented(const std::string& str);
+ uint32_t startItem();
+ uint32_t endItem();
+ uint32_t writeItem(const std::string& str);
+
+ static std::string fieldTypeName(TType type);
+
+ TTransport* trans_;
+
+ int32_t string_limit_;
+ int32_t string_prefix_size_;
+
+ std::string indent_str_;
+ static const int indent_inc = 2;
+
+ std::vector<write_state_t> write_state_;
+ std::vector<int> list_idx_;
+};
+
+/**
+ * Constructs debug protocol handlers
+ */
+class TDebugProtocolFactory : public TProtocolFactory {
+public:
+ TDebugProtocolFactory() = default;
+ ~TDebugProtocolFactory() override = default;
+
+ std::shared_ptr<TProtocol> getProtocol(std::shared_ptr<TTransport> trans) override {
+ return std::shared_ptr<TProtocol>(new TDebugProtocol(trans));
+ }
+};
+}
+}
+} // apache::thrift::protocol
+
+// TODO(dreiss): Move (part of) ThriftDebugString into a .cpp file and remove this.
+#include <thrift/transport/TBufferTransports.h>
+
+namespace apache {
+namespace thrift {
+
+template <typename ThriftStruct>
+std::string ThriftDebugString(const ThriftStruct& ts) {
+ using namespace apache::thrift::transport;
+ using namespace apache::thrift::protocol;
+ auto* buffer = new TMemoryBuffer;
+ std::shared_ptr<TTransport> trans(buffer);
+ TDebugProtocol protocol(trans);
+
+ ts.write(&protocol);
+
+ uint8_t* buf;
+ uint32_t size;
+ buffer->getBuffer(&buf, &size);
+ return std::string((char*)buf, (unsigned int)size);
+}
+
+// TODO(dreiss): This is badly broken. Don't use it unless you are me.
+#if 0
+template<typename Object>
+std::string DebugString(const std::vector<Object>& vec) {
+ using namespace apache::thrift::transport;
+ using namespace apache::thrift::protocol;
+ TMemoryBuffer* buffer = new TMemoryBuffer;
+ std::shared_ptr<TTransport> trans(buffer);
+ TDebugProtocol protocol(trans);
+
+ // I am gross!
+ protocol.writeStructBegin("SomeRandomVector");
+
+ // TODO: Fix this with a trait.
+ protocol.writeListBegin((TType)99, vec.size());
+ typename std::vector<Object>::const_iterator it;
+ for (it = vec.begin(); it != vec.end(); ++it) {
+ it->write(&protocol);
+ }
+ protocol.writeListEnd();
+
+ uint8_t* buf;
+ uint32_t size;
+ buffer->getBuffer(&buf, &size);
+ return std::string((char*)buf, (unsigned int)size);
+}
+#endif // 0
+}
+} // apache::thrift
+
+#endif // #ifndef _THRIFT_PROTOCOL_TDEBUGPROTOCOL_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/THeaderProtocol.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/THeaderProtocol.cpp
new file mode 100644
index 000000000..6242e30b8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/THeaderProtocol.cpp
@@ -0,0 +1,253 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef THRIFT_PROTOCOL_THEADERPROTOCOL_CPP_
+#define THRIFT_PROTOCOL_THEADERPROTOCOL_CPP_ 1
+
+#include <thrift/protocol/THeaderProtocol.h>
+#include <thrift/protocol/TCompactProtocol.h>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/TApplicationException.h>
+
+#include <limits>
+
+#include <memory>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+void THeaderProtocol::resetProtocol() {
+ if (proto_ && protoId_ == trans_->getProtocolId()) {
+ return;
+ }
+
+ protoId_ = trans_->getProtocolId();
+
+ switch (protoId_) {
+ case T_BINARY_PROTOCOL:
+ proto_ = std::make_shared<TBinaryProtocolT<THeaderTransport> >(trans_);
+ break;
+
+ case T_COMPACT_PROTOCOL:
+ proto_ = std::make_shared<TCompactProtocolT<THeaderTransport> >(trans_);
+ break;
+
+ default:
+ throw TApplicationException(TApplicationException::INVALID_PROTOCOL,
+ "Unknown protocol requested");
+ }
+}
+
+uint32_t THeaderProtocol::writeMessageBegin(const std::string& name,
+ const TMessageType messageType,
+ const int32_t seqId) {
+ resetProtocol(); // Reset in case we changed protocols
+ trans_->setSequenceNumber(seqId);
+ return proto_->writeMessageBegin(name, messageType, seqId);
+}
+
+uint32_t THeaderProtocol::writeMessageEnd() {
+ return proto_->writeMessageEnd();
+}
+
+uint32_t THeaderProtocol::writeStructBegin(const char* name) {
+ return proto_->writeStructBegin(name);
+}
+
+uint32_t THeaderProtocol::writeStructEnd() {
+ return proto_->writeStructEnd();
+}
+
+uint32_t THeaderProtocol::writeFieldBegin(const char* name,
+ const TType fieldType,
+ const int16_t fieldId) {
+ return proto_->writeFieldBegin(name, fieldType, fieldId);
+}
+
+uint32_t THeaderProtocol::writeFieldEnd() {
+ return proto_->writeFieldEnd();
+}
+
+uint32_t THeaderProtocol::writeFieldStop() {
+ return proto_->writeFieldStop();
+}
+
+uint32_t THeaderProtocol::writeMapBegin(const TType keyType,
+ const TType valType,
+ const uint32_t size) {
+ return proto_->writeMapBegin(keyType, valType, size);
+}
+
+uint32_t THeaderProtocol::writeMapEnd() {
+ return proto_->writeMapEnd();
+}
+
+uint32_t THeaderProtocol::writeListBegin(const TType elemType, const uint32_t size) {
+ return proto_->writeListBegin(elemType, size);
+}
+
+uint32_t THeaderProtocol::writeListEnd() {
+ return proto_->writeListEnd();
+}
+
+uint32_t THeaderProtocol::writeSetBegin(const TType elemType, const uint32_t size) {
+ return proto_->writeSetBegin(elemType, size);
+}
+
+uint32_t THeaderProtocol::writeSetEnd() {
+ return proto_->writeSetEnd();
+}
+
+uint32_t THeaderProtocol::writeBool(const bool value) {
+ return proto_->writeBool(value);
+}
+
+uint32_t THeaderProtocol::writeByte(const int8_t byte) {
+ return proto_->writeByte(byte);
+}
+
+uint32_t THeaderProtocol::writeI16(const int16_t i16) {
+ return proto_->writeI16(i16);
+}
+
+uint32_t THeaderProtocol::writeI32(const int32_t i32) {
+ return proto_->writeI32(i32);
+}
+
+uint32_t THeaderProtocol::writeI64(const int64_t i64) {
+ return proto_->writeI64(i64);
+}
+
+uint32_t THeaderProtocol::writeDouble(const double dub) {
+ return proto_->writeDouble(dub);
+}
+
+uint32_t THeaderProtocol::writeString(const std::string& str) {
+ return proto_->writeString(str);
+}
+
+uint32_t THeaderProtocol::writeBinary(const std::string& str) {
+ return proto_->writeBinary(str);
+}
+
+/**
+ * Reading functions
+ */
+
+uint32_t THeaderProtocol::readMessageBegin(std::string& name,
+ TMessageType& messageType,
+ int32_t& seqId) {
+ // Read the next frame, and change protocols if needed
+ try {
+ trans_->resetProtocol();
+ resetProtocol();
+ } catch (const TApplicationException& ex) {
+ writeMessageBegin("", T_EXCEPTION, 0);
+ ex.write((TProtocol*)this);
+ writeMessageEnd();
+ trans_->flush();
+
+ // The framing is still good, but we don't know about this protocol.
+ // In the future, this could be made a client-side only error if
+ // connection pooling is used.
+ throw ex;
+ }
+ return proto_->readMessageBegin(name, messageType, seqId);
+}
+
+uint32_t THeaderProtocol::readMessageEnd() {
+ return proto_->readMessageEnd();
+}
+
+uint32_t THeaderProtocol::readStructBegin(std::string& name) {
+ return proto_->readStructBegin(name);
+}
+
+uint32_t THeaderProtocol::readStructEnd() {
+ return proto_->readStructEnd();
+}
+
+uint32_t THeaderProtocol::readFieldBegin(std::string& name, TType& fieldType, int16_t& fieldId) {
+ return proto_->readFieldBegin(name, fieldType, fieldId);
+}
+
+uint32_t THeaderProtocol::readFieldEnd() {
+ return proto_->readFieldEnd();
+}
+
+uint32_t THeaderProtocol::readMapBegin(TType& keyType, TType& valType, uint32_t& size) {
+ return proto_->readMapBegin(keyType, valType, size);
+}
+
+uint32_t THeaderProtocol::readMapEnd() {
+ return proto_->readMapEnd();
+}
+
+uint32_t THeaderProtocol::readListBegin(TType& elemType, uint32_t& size) {
+ return proto_->readListBegin(elemType, size);
+}
+
+uint32_t THeaderProtocol::readListEnd() {
+ return proto_->readListEnd();
+}
+
+uint32_t THeaderProtocol::readSetBegin(TType& elemType, uint32_t& size) {
+ return proto_->readSetBegin(elemType, size);
+}
+
+uint32_t THeaderProtocol::readSetEnd() {
+ return proto_->readSetEnd();
+}
+
+uint32_t THeaderProtocol::readBool(bool& value) {
+ return proto_->readBool(value);
+}
+
+uint32_t THeaderProtocol::readByte(int8_t& byte) {
+ return proto_->readByte(byte);
+}
+
+uint32_t THeaderProtocol::readI16(int16_t& i16) {
+ return proto_->readI16(i16);
+}
+
+uint32_t THeaderProtocol::readI32(int32_t& i32) {
+ return proto_->readI32(i32);
+}
+
+uint32_t THeaderProtocol::readI64(int64_t& i64) {
+ return proto_->readI64(i64);
+}
+
+uint32_t THeaderProtocol::readDouble(double& dub) {
+ return proto_->readDouble(dub);
+}
+
+uint32_t THeaderProtocol::readString(std::string& str) {
+ return proto_->readString(str);
+}
+
+uint32_t THeaderProtocol::readBinary(std::string& binary) {
+ return proto_->readBinary(binary);
+}
+}
+}
+} // apache::thrift::protocol
+
+#endif // #ifndef THRIFT_PROTOCOL_THEADERPROTOCOL_CPP_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/THeaderProtocol.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/THeaderProtocol.h
new file mode 100644
index 000000000..0d5018596
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/THeaderProtocol.h
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef THRIFT_PROTOCOL_THEADERPROTOCOL_H_
+#define THRIFT_PROTOCOL_THEADERPROTOCOL_H_ 1
+
+#include <thrift/protocol/TProtocol.h>
+#include <thrift/protocol/TProtocolTypes.h>
+#include <thrift/protocol/TVirtualProtocol.h>
+#include <thrift/transport/THeaderTransport.h>
+
+#include <memory>
+
+using apache::thrift::transport::THeaderTransport;
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+/**
+ * The header protocol for thrift. Reads unframed, framed, header format,
+ * and http
+ *
+ */
+class THeaderProtocol : public TVirtualProtocol<THeaderProtocol> {
+protected:
+public:
+ void resetProtocol();
+
+ explicit THeaderProtocol(const std::shared_ptr<TTransport>& trans,
+ uint16_t protoId = T_COMPACT_PROTOCOL)
+ : TVirtualProtocol<THeaderProtocol>(std::shared_ptr<TTransport>(new THeaderTransport(trans))),
+ trans_(std::dynamic_pointer_cast<THeaderTransport>(getTransport())),
+ protoId_(protoId) {
+ trans_->setProtocolId(protoId);
+ resetProtocol();
+ }
+
+ THeaderProtocol(const std::shared_ptr<TTransport>& inTrans,
+ const std::shared_ptr<TTransport>& outTrans,
+ uint16_t protoId = T_COMPACT_PROTOCOL)
+ : TVirtualProtocol<THeaderProtocol>(
+ std::shared_ptr<TTransport>(new THeaderTransport(inTrans, outTrans))),
+ trans_(std::dynamic_pointer_cast<THeaderTransport>(getTransport())),
+ protoId_(protoId) {
+ trans_->setProtocolId(protoId);
+ resetProtocol();
+ }
+
+ ~THeaderProtocol() override = default;
+
+ /**
+ * Functions to work with headers by calling into THeaderTransport
+ */
+ void setProtocolId(uint16_t protoId) {
+ trans_->setProtocolId(protoId);
+ resetProtocol();
+ }
+
+ typedef THeaderTransport::StringToStringMap StringToStringMap;
+
+ // these work with write headers
+ void setHeader(const std::string& key, const std::string& value) {
+ trans_->setHeader(key, value);
+ }
+
+ void clearHeaders() { trans_->clearHeaders(); }
+
+ StringToStringMap& getWriteHeaders() { return trans_->getWriteHeaders(); }
+
+ // these work with read headers
+ const StringToStringMap& getHeaders() const { return trans_->getHeaders(); }
+
+ /**
+ * Writing functions.
+ */
+
+ /*ol*/ uint32_t writeMessageBegin(const std::string& name,
+ const TMessageType messageType,
+ const int32_t seqId);
+
+ /*ol*/ uint32_t writeMessageEnd();
+
+ uint32_t writeStructBegin(const char* name);
+
+ uint32_t writeStructEnd();
+
+ uint32_t writeFieldBegin(const char* name, const TType fieldType, const int16_t fieldId);
+
+ uint32_t writeFieldEnd();
+
+ uint32_t writeFieldStop();
+
+ uint32_t writeMapBegin(const TType keyType, const TType valType, const uint32_t size);
+
+ uint32_t writeMapEnd();
+
+ uint32_t writeListBegin(const TType elemType, const uint32_t size);
+
+ uint32_t writeListEnd();
+
+ uint32_t writeSetBegin(const TType elemType, const uint32_t size);
+
+ uint32_t writeSetEnd();
+
+ uint32_t writeBool(const bool value);
+
+ uint32_t writeByte(const int8_t byte);
+
+ uint32_t writeI16(const int16_t i16);
+
+ uint32_t writeI32(const int32_t i32);
+
+ uint32_t writeI64(const int64_t i64);
+
+ uint32_t writeDouble(const double dub);
+
+ uint32_t writeString(const std::string& str);
+
+ uint32_t writeBinary(const std::string& str);
+
+ /**
+ * Reading functions
+ */
+
+ /*ol*/ uint32_t readMessageBegin(std::string& name, TMessageType& messageType, int32_t& seqId);
+
+ /*ol*/ uint32_t readMessageEnd();
+
+ uint32_t readStructBegin(std::string& name);
+
+ uint32_t readStructEnd();
+
+ uint32_t readFieldBegin(std::string& name, TType& fieldType, int16_t& fieldId);
+
+ uint32_t readFieldEnd();
+
+ uint32_t readMapBegin(TType& keyType, TType& valType, uint32_t& size);
+
+ uint32_t readMapEnd();
+
+ uint32_t readListBegin(TType& elemType, uint32_t& size);
+
+ uint32_t readListEnd();
+
+ uint32_t readSetBegin(TType& elemType, uint32_t& size);
+
+ uint32_t readSetEnd();
+
+ uint32_t readBool(bool& value);
+ // Provide the default readBool() implementation for std::vector<bool>
+ using TVirtualProtocol<THeaderProtocol>::readBool;
+
+ uint32_t readByte(int8_t& byte);
+
+ uint32_t readI16(int16_t& i16);
+
+ uint32_t readI32(int32_t& i32);
+
+ uint32_t readI64(int64_t& i64);
+
+ uint32_t readDouble(double& dub);
+
+ uint32_t readString(std::string& str);
+
+ uint32_t readBinary(std::string& binary);
+
+protected:
+ std::shared_ptr<THeaderTransport> trans_;
+
+ std::shared_ptr<TProtocol> proto_;
+ uint32_t protoId_;
+};
+
+class THeaderProtocolFactory : public TProtocolFactory {
+public:
+ std::shared_ptr<TProtocol> getProtocol(std::shared_ptr<transport::TTransport> trans) override {
+ auto* headerProtocol
+ = new THeaderProtocol(trans, trans, T_BINARY_PROTOCOL);
+ return std::shared_ptr<TProtocol>(headerProtocol);
+ }
+
+ std::shared_ptr<TProtocol> getProtocol(
+ std::shared_ptr<transport::TTransport> inTrans,
+ std::shared_ptr<transport::TTransport> outTrans) override {
+ auto* headerProtocol = new THeaderProtocol(inTrans, outTrans, T_BINARY_PROTOCOL);
+ return std::shared_ptr<TProtocol>(headerProtocol);
+ }
+};
+}
+}
+} // apache::thrift::protocol
+
+#endif // #ifndef THRIFT_PROTOCOL_THEADERPROTOCOL_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp
new file mode 100644
index 000000000..28d0da299
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp
@@ -0,0 +1,1098 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/protocol/TJSONProtocol.h>
+
+#include <boost/locale.hpp>
+
+#include <cmath>
+#include <limits>
+#include <locale>
+#include <sstream>
+#include <stdexcept>
+
+#include <thrift/protocol/TBase64Utils.h>
+#include <thrift/transport/TTransportException.h>
+#include <thrift/TToString.h>
+
+using namespace apache::thrift::transport;
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+// Static data
+
+static const uint8_t kJSONObjectStart = '{';
+static const uint8_t kJSONObjectEnd = '}';
+static const uint8_t kJSONArrayStart = '[';
+static const uint8_t kJSONArrayEnd = ']';
+static const uint8_t kJSONPairSeparator = ':';
+static const uint8_t kJSONElemSeparator = ',';
+static const uint8_t kJSONBackslash = '\\';
+static const uint8_t kJSONStringDelimiter = '"';
+static const uint8_t kJSONEscapeChar = 'u';
+
+static const std::string kJSONEscapePrefix("\\u00");
+
+static const uint32_t kThriftVersion1 = 1;
+
+static const std::string kThriftNan("NaN");
+static const std::string kThriftInfinity("Infinity");
+static const std::string kThriftNegativeInfinity("-Infinity");
+
+static const std::string kTypeNameBool("tf");
+static const std::string kTypeNameByte("i8");
+static const std::string kTypeNameI16("i16");
+static const std::string kTypeNameI32("i32");
+static const std::string kTypeNameI64("i64");
+static const std::string kTypeNameDouble("dbl");
+static const std::string kTypeNameStruct("rec");
+static const std::string kTypeNameString("str");
+static const std::string kTypeNameMap("map");
+static const std::string kTypeNameList("lst");
+static const std::string kTypeNameSet("set");
+
+static const std::string& getTypeNameForTypeID(TType typeID) {
+ switch (typeID) {
+ case T_BOOL:
+ return kTypeNameBool;
+ case T_BYTE:
+ return kTypeNameByte;
+ case T_I16:
+ return kTypeNameI16;
+ case T_I32:
+ return kTypeNameI32;
+ case T_I64:
+ return kTypeNameI64;
+ case T_DOUBLE:
+ return kTypeNameDouble;
+ case T_STRING:
+ return kTypeNameString;
+ case T_STRUCT:
+ return kTypeNameStruct;
+ case T_MAP:
+ return kTypeNameMap;
+ case T_SET:
+ return kTypeNameSet;
+ case T_LIST:
+ return kTypeNameList;
+ default:
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED, "Unrecognized type");
+ }
+}
+
+static TType getTypeIDForTypeName(const std::string& name) {
+ TType result = T_STOP; // Sentinel value
+ if (name.length() > 1) {
+ switch (name[0]) {
+ case 'd':
+ result = T_DOUBLE;
+ break;
+ case 'i':
+ switch (name[1]) {
+ case '8':
+ result = T_BYTE;
+ break;
+ case '1':
+ result = T_I16;
+ break;
+ case '3':
+ result = T_I32;
+ break;
+ case '6':
+ result = T_I64;
+ break;
+ }
+ break;
+ case 'l':
+ result = T_LIST;
+ break;
+ case 'm':
+ result = T_MAP;
+ break;
+ case 'r':
+ result = T_STRUCT;
+ break;
+ case 's':
+ if (name[1] == 't') {
+ result = T_STRING;
+ } else if (name[1] == 'e') {
+ result = T_SET;
+ }
+ break;
+ case 't':
+ result = T_BOOL;
+ break;
+ }
+ }
+ if (result == T_STOP) {
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED, "Unrecognized type");
+ }
+ return result;
+}
+
+// This table describes the handling for the first 0x30 characters
+// 0 : escape using "\u00xx" notation
+// 1 : just output index
+// <other> : escape using "\<other>" notation
+static const uint8_t kJSONCharTable[0x30] = {
+ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 'b',
+ 't',
+ 'n',
+ 0,
+ 'f',
+ 'r',
+ 0,
+ 0, // 0
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0, // 1
+ 1,
+ 1,
+ '"',
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1, // 2
+};
+
+// This string's characters must match up with the elements in kEscapeCharVals.
+// I don't have '/' on this list even though it appears on www.json.org --
+// it is not in the RFC
+const static std::string kEscapeChars("\"\\bfnrt");
+
+// The elements of this array must match up with the sequence of characters in
+// kEscapeChars
+const static uint8_t kEscapeCharVals[7] = {
+ '"',
+ '\\',
+ '\b',
+ '\f',
+ '\n',
+ '\r',
+ '\t',
+};
+
+// Static helper functions
+
+// Read 1 character from the transport trans and verify that it is the
+// expected character ch.
+// Throw a protocol exception if it is not.
+static uint32_t readSyntaxChar(TJSONProtocol::LookaheadReader& reader, uint8_t ch) {
+ uint8_t ch2 = reader.read();
+ if (ch2 != ch) {
+ throw TProtocolException(TProtocolException::INVALID_DATA,
+ "Expected \'" + std::string((char*)&ch, 1) + "\'; got \'"
+ + std::string((char*)&ch2, 1) + "\'.");
+ }
+ return 1;
+}
+
+// Return the integer value of a hex character ch.
+// Throw a protocol exception if the character is not [0-9a-f].
+static uint8_t hexVal(uint8_t ch) {
+ if ((ch >= '0') && (ch <= '9')) {
+ return ch - '0';
+ } else if ((ch >= 'a') && (ch <= 'f')) {
+ return ch - 'a' + 10;
+ } else {
+ throw TProtocolException(TProtocolException::INVALID_DATA,
+ "Expected hex val ([0-9a-f]); got \'" + std::string((char*)&ch, 1)
+ + "\'.");
+ }
+}
+
+// Return the hex character representing the integer val. The value is masked
+// to make sure it is in the correct range.
+static uint8_t hexChar(uint8_t val) {
+ val &= 0x0F;
+ if (val < 10) {
+ return val + '0';
+ } else {
+ return val - 10 + 'a';
+ }
+}
+
+// Return true if the character ch is in [-+0-9.Ee]; false otherwise
+static bool isJSONNumeric(uint8_t ch) {
+ switch (ch) {
+ case '+':
+ case '-':
+ case '.':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'E':
+ case 'e':
+ return true;
+ }
+ return false;
+}
+
+// Return true if the code unit is high surrogate
+static bool isHighSurrogate(uint16_t val) {
+ return val >= 0xD800 && val <= 0xDBFF;
+}
+
+// Return true if the code unit is low surrogate
+static bool isLowSurrogate(uint16_t val) {
+ return val >= 0xDC00 && val <= 0xDFFF;
+}
+
+/**
+ * Class to serve as base JSON context and as base class for other context
+ * implementations
+ */
+class TJSONContext {
+
+public:
+ TJSONContext() = default;
+
+ virtual ~TJSONContext() = default;
+
+ /**
+ * Write context data to the transport. Default is to do nothing.
+ */
+ virtual uint32_t write(TTransport& trans) {
+ (void)trans;
+ return 0;
+ };
+
+ /**
+ * Read context data from the transport. Default is to do nothing.
+ */
+ virtual uint32_t read(TJSONProtocol::LookaheadReader& reader) {
+ (void)reader;
+ return 0;
+ };
+
+ /**
+ * Return true if numbers need to be escaped as strings in this context.
+ * Default behavior is to return false.
+ */
+ virtual bool escapeNum() { return false; }
+};
+
+// Context class for object member key-value pairs
+class JSONPairContext : public TJSONContext {
+
+public:
+ JSONPairContext() : first_(true), colon_(true) {}
+
+ uint32_t write(TTransport& trans) override {
+ if (first_) {
+ first_ = false;
+ colon_ = true;
+ return 0;
+ } else {
+ trans.write(colon_ ? &kJSONPairSeparator : &kJSONElemSeparator, 1);
+ colon_ = !colon_;
+ return 1;
+ }
+ }
+
+ uint32_t read(TJSONProtocol::LookaheadReader& reader) override {
+ if (first_) {
+ first_ = false;
+ colon_ = true;
+ return 0;
+ } else {
+ uint8_t ch = (colon_ ? kJSONPairSeparator : kJSONElemSeparator);
+ colon_ = !colon_;
+ return readSyntaxChar(reader, ch);
+ }
+ }
+
+ // Numbers must be turned into strings if they are the key part of a pair
+ bool escapeNum() override { return colon_; }
+
+private:
+ bool first_;
+ bool colon_;
+};
+
+// Context class for lists
+class JSONListContext : public TJSONContext {
+
+public:
+ JSONListContext() : first_(true) {}
+
+ uint32_t write(TTransport& trans) override {
+ if (first_) {
+ first_ = false;
+ return 0;
+ } else {
+ trans.write(&kJSONElemSeparator, 1);
+ return 1;
+ }
+ }
+
+ uint32_t read(TJSONProtocol::LookaheadReader& reader) override {
+ if (first_) {
+ first_ = false;
+ return 0;
+ } else {
+ return readSyntaxChar(reader, kJSONElemSeparator);
+ }
+ }
+
+private:
+ bool first_;
+};
+
+TJSONProtocol::TJSONProtocol(std::shared_ptr<TTransport> ptrans)
+ : TVirtualProtocol<TJSONProtocol>(ptrans),
+ trans_(ptrans.get()),
+ context_(new TJSONContext()),
+ reader_(*ptrans) {
+}
+
+TJSONProtocol::~TJSONProtocol() = default;
+
+void TJSONProtocol::pushContext(std::shared_ptr<TJSONContext> c) {
+ contexts_.push(context_);
+ context_ = c;
+}
+
+void TJSONProtocol::popContext() {
+ context_ = contexts_.top();
+ contexts_.pop();
+}
+
+// Write the character ch as a JSON escape sequence ("\u00xx")
+uint32_t TJSONProtocol::writeJSONEscapeChar(uint8_t ch) {
+ trans_->write((const uint8_t*)kJSONEscapePrefix.c_str(),
+ static_cast<uint32_t>(kJSONEscapePrefix.length()));
+ uint8_t outCh = hexChar(ch >> 4);
+ trans_->write(&outCh, 1);
+ outCh = hexChar(ch);
+ trans_->write(&outCh, 1);
+ return 6;
+}
+
+// Write the character ch as part of a JSON string, escaping as appropriate.
+uint32_t TJSONProtocol::writeJSONChar(uint8_t ch) {
+ if (ch >= 0x30) {
+ if (ch == kJSONBackslash) { // Only special character >= 0x30 is '\'
+ trans_->write(&kJSONBackslash, 1);
+ trans_->write(&kJSONBackslash, 1);
+ return 2;
+ } else {
+ trans_->write(&ch, 1);
+ return 1;
+ }
+ } else {
+ uint8_t outCh = kJSONCharTable[ch];
+ // Check if regular character, backslash escaped, or JSON escaped
+ if (outCh == 1) {
+ trans_->write(&ch, 1);
+ return 1;
+ } else if (outCh > 1) {
+ trans_->write(&kJSONBackslash, 1);
+ trans_->write(&outCh, 1);
+ return 2;
+ } else {
+ return writeJSONEscapeChar(ch);
+ }
+ }
+}
+
+// Write out the contents of the string str as a JSON string, escaping
+// characters as appropriate.
+uint32_t TJSONProtocol::writeJSONString(const std::string& str) {
+ uint32_t result = context_->write(*trans_);
+ result += 2; // For quotes
+ trans_->write(&kJSONStringDelimiter, 1);
+ std::string::const_iterator iter(str.begin());
+ std::string::const_iterator end(str.end());
+ while (iter != end) {
+ result += writeJSONChar(*iter++);
+ }
+ trans_->write(&kJSONStringDelimiter, 1);
+ return result;
+}
+
+// Write out the contents of the string as JSON string, base64-encoding
+// the string's contents, and escaping as appropriate
+uint32_t TJSONProtocol::writeJSONBase64(const std::string& str) {
+ uint32_t result = context_->write(*trans_);
+ result += 2; // For quotes
+ trans_->write(&kJSONStringDelimiter, 1);
+ uint8_t b[4];
+ const auto* bytes = (const uint8_t*)str.c_str();
+ if (str.length() > (std::numeric_limits<uint32_t>::max)())
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ auto len = static_cast<uint32_t>(str.length());
+ while (len >= 3) {
+ // Encode 3 bytes at a time
+ base64_encode(bytes, 3, b);
+ trans_->write(b, 4);
+ result += 4;
+ bytes += 3;
+ len -= 3;
+ }
+ if (len) { // Handle remainder
+ base64_encode(bytes, len, b);
+ trans_->write(b, len + 1);
+ result += len + 1;
+ }
+ trans_->write(&kJSONStringDelimiter, 1);
+ return result;
+}
+
+// Convert the given integer type to a JSON number, or a string
+// if the context requires it (eg: key in a map pair).
+template <typename NumberType>
+uint32_t TJSONProtocol::writeJSONInteger(NumberType num) {
+ uint32_t result = context_->write(*trans_);
+ std::string val(to_string(num));
+ bool escapeNum = context_->escapeNum();
+ if (escapeNum) {
+ trans_->write(&kJSONStringDelimiter, 1);
+ result += 1;
+ }
+ if (val.length() > (std::numeric_limits<uint32_t>::max)())
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ trans_->write((const uint8_t*)val.c_str(), static_cast<uint32_t>(val.length()));
+ result += static_cast<uint32_t>(val.length());
+ if (escapeNum) {
+ trans_->write(&kJSONStringDelimiter, 1);
+ result += 1;
+ }
+ return result;
+}
+
+namespace {
+std::string doubleToString(double d) {
+ std::ostringstream str;
+ str.imbue(std::locale::classic());
+ const std::streamsize max_digits10 = 2 + std::numeric_limits<double>::digits10;
+ str.precision(max_digits10);
+ str << d;
+ return str.str();
+}
+}
+
+// Convert the given double to a JSON string, which is either the number,
+// "NaN" or "Infinity" or "-Infinity".
+uint32_t TJSONProtocol::writeJSONDouble(double num) {
+ uint32_t result = context_->write(*trans_);
+ std::string val;
+
+ bool special = false;
+ switch (std::fpclassify(num)) {
+ case FP_INFINITE:
+ if (std::signbit(num)) {
+ val = kThriftNegativeInfinity;
+ } else {
+ val = kThriftInfinity;
+ }
+ special = true;
+ break;
+ case FP_NAN:
+ val = kThriftNan;
+ special = true;
+ break;
+ default:
+ val = doubleToString(num);
+ break;
+ }
+
+ bool escapeNum = special || context_->escapeNum();
+ if (escapeNum) {
+ trans_->write(&kJSONStringDelimiter, 1);
+ result += 1;
+ }
+ if (val.length() > (std::numeric_limits<uint32_t>::max)())
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ trans_->write((const uint8_t*)val.c_str(), static_cast<uint32_t>(val.length()));
+ result += static_cast<uint32_t>(val.length());
+ if (escapeNum) {
+ trans_->write(&kJSONStringDelimiter, 1);
+ result += 1;
+ }
+ return result;
+}
+
+uint32_t TJSONProtocol::writeJSONObjectStart() {
+ uint32_t result = context_->write(*trans_);
+ trans_->write(&kJSONObjectStart, 1);
+ pushContext(std::shared_ptr<TJSONContext>(new JSONPairContext()));
+ return result + 1;
+}
+
+uint32_t TJSONProtocol::writeJSONObjectEnd() {
+ popContext();
+ trans_->write(&kJSONObjectEnd, 1);
+ return 1;
+}
+
+uint32_t TJSONProtocol::writeJSONArrayStart() {
+ uint32_t result = context_->write(*trans_);
+ trans_->write(&kJSONArrayStart, 1);
+ pushContext(std::shared_ptr<TJSONContext>(new JSONListContext()));
+ return result + 1;
+}
+
+uint32_t TJSONProtocol::writeJSONArrayEnd() {
+ popContext();
+ trans_->write(&kJSONArrayEnd, 1);
+ return 1;
+}
+
+uint32_t TJSONProtocol::writeMessageBegin(const std::string& name,
+ const TMessageType messageType,
+ const int32_t seqid) {
+ uint32_t result = writeJSONArrayStart();
+ result += writeJSONInteger(kThriftVersion1);
+ result += writeJSONString(name);
+ result += writeJSONInteger(messageType);
+ result += writeJSONInteger(seqid);
+ return result;
+}
+
+uint32_t TJSONProtocol::writeMessageEnd() {
+ return writeJSONArrayEnd();
+}
+
+uint32_t TJSONProtocol::writeStructBegin(const char* name) {
+ (void)name;
+ return writeJSONObjectStart();
+}
+
+uint32_t TJSONProtocol::writeStructEnd() {
+ return writeJSONObjectEnd();
+}
+
+uint32_t TJSONProtocol::writeFieldBegin(const char* name,
+ const TType fieldType,
+ const int16_t fieldId) {
+ (void)name;
+ uint32_t result = writeJSONInteger(fieldId);
+ result += writeJSONObjectStart();
+ result += writeJSONString(getTypeNameForTypeID(fieldType));
+ return result;
+}
+
+uint32_t TJSONProtocol::writeFieldEnd() {
+ return writeJSONObjectEnd();
+}
+
+uint32_t TJSONProtocol::writeFieldStop() {
+ return 0;
+}
+
+uint32_t TJSONProtocol::writeMapBegin(const TType keyType,
+ const TType valType,
+ const uint32_t size) {
+ uint32_t result = writeJSONArrayStart();
+ result += writeJSONString(getTypeNameForTypeID(keyType));
+ result += writeJSONString(getTypeNameForTypeID(valType));
+ result += writeJSONInteger((int64_t)size);
+ result += writeJSONObjectStart();
+ return result;
+}
+
+uint32_t TJSONProtocol::writeMapEnd() {
+ uint32_t result = writeJSONObjectEnd();
+ result += writeJSONArrayEnd();
+ return result;
+}
+
+uint32_t TJSONProtocol::writeListBegin(const TType elemType, const uint32_t size) {
+ uint32_t result = writeJSONArrayStart();
+ result += writeJSONString(getTypeNameForTypeID(elemType));
+ result += writeJSONInteger((int64_t)size);
+ return result;
+}
+
+uint32_t TJSONProtocol::writeListEnd() {
+ return writeJSONArrayEnd();
+}
+
+uint32_t TJSONProtocol::writeSetBegin(const TType elemType, const uint32_t size) {
+ uint32_t result = writeJSONArrayStart();
+ result += writeJSONString(getTypeNameForTypeID(elemType));
+ result += writeJSONInteger((int64_t)size);
+ return result;
+}
+
+uint32_t TJSONProtocol::writeSetEnd() {
+ return writeJSONArrayEnd();
+}
+
+uint32_t TJSONProtocol::writeBool(const bool value) {
+ return writeJSONInteger(value);
+}
+
+uint32_t TJSONProtocol::writeByte(const int8_t byte) {
+ // writeByte() must be handled specially because to_string sees
+ // int8_t as a text type instead of an integer type
+ return writeJSONInteger((int16_t)byte);
+}
+
+uint32_t TJSONProtocol::writeI16(const int16_t i16) {
+ return writeJSONInteger(i16);
+}
+
+uint32_t TJSONProtocol::writeI32(const int32_t i32) {
+ return writeJSONInteger(i32);
+}
+
+uint32_t TJSONProtocol::writeI64(const int64_t i64) {
+ return writeJSONInteger(i64);
+}
+
+uint32_t TJSONProtocol::writeDouble(const double dub) {
+ return writeJSONDouble(dub);
+}
+
+uint32_t TJSONProtocol::writeString(const std::string& str) {
+ return writeJSONString(str);
+}
+
+uint32_t TJSONProtocol::writeBinary(const std::string& str) {
+ return writeJSONBase64(str);
+}
+
+/**
+ * Reading functions
+ */
+
+// Reads 1 byte and verifies that it matches ch.
+uint32_t TJSONProtocol::readJSONSyntaxChar(uint8_t ch) {
+ return readSyntaxChar(reader_, ch);
+}
+
+// Decodes the four hex parts of a JSON escaped string character and returns
+// the UTF-16 code unit via out.
+uint32_t TJSONProtocol::readJSONEscapeChar(uint16_t* out) {
+ uint8_t b[4];
+ b[0] = reader_.read();
+ b[1] = reader_.read();
+ b[2] = reader_.read();
+ b[3] = reader_.read();
+
+ *out = (hexVal(b[0]) << 12)
+ + (hexVal(b[1]) << 8) + (hexVal(b[2]) << 4) + hexVal(b[3]);
+
+ return 4;
+}
+
+// Decodes a JSON string, including unescaping, and returns the string via str
+uint32_t TJSONProtocol::readJSONString(std::string& str, bool skipContext) {
+ uint32_t result = (skipContext ? 0 : context_->read(reader_));
+ result += readJSONSyntaxChar(kJSONStringDelimiter);
+ std::vector<uint16_t> codeunits;
+ uint8_t ch;
+ str.clear();
+ while (true) {
+ ch = reader_.read();
+ ++result;
+ if (ch == kJSONStringDelimiter) {
+ break;
+ }
+ if (ch == kJSONBackslash) {
+ ch = reader_.read();
+ ++result;
+ if (ch == kJSONEscapeChar) {
+ uint16_t cp;
+ result += readJSONEscapeChar(&cp);
+ if (isHighSurrogate(cp)) {
+ codeunits.push_back(cp);
+ } else {
+ if (isLowSurrogate(cp)
+ && codeunits.empty()) {
+ throw TProtocolException(TProtocolException::INVALID_DATA,
+ "Missing UTF-16 high surrogate pair.");
+ }
+ codeunits.push_back(cp);
+ codeunits.push_back(0);
+ str += boost::locale::conv::utf_to_utf<char>(codeunits.data());
+ codeunits.clear();
+ }
+ continue;
+ } else {
+ size_t pos = kEscapeChars.find(ch);
+ if (pos == kEscapeChars.npos) {
+ throw TProtocolException(TProtocolException::INVALID_DATA,
+ "Expected control char, got '" + std::string((const char*)&ch, 1)
+ + "'.");
+ }
+ ch = kEscapeCharVals[pos];
+ }
+ }
+ if (!codeunits.empty()) {
+ throw TProtocolException(TProtocolException::INVALID_DATA,
+ "Missing UTF-16 low surrogate pair.");
+ }
+ str += ch;
+ }
+
+ if (!codeunits.empty()) {
+ throw TProtocolException(TProtocolException::INVALID_DATA,
+ "Missing UTF-16 low surrogate pair.");
+ }
+ return result;
+}
+
+// Reads a block of base64 characters, decoding it, and returns via str
+uint32_t TJSONProtocol::readJSONBase64(std::string& str) {
+ std::string tmp;
+ uint32_t result = readJSONString(tmp);
+ auto* b = (uint8_t*)tmp.c_str();
+ if (tmp.length() > (std::numeric_limits<uint32_t>::max)())
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ auto len = static_cast<uint32_t>(tmp.length());
+ str.clear();
+ // Ignore padding
+ if (len >= 2) {
+ uint32_t bound = len - 2;
+ for (uint32_t i = len - 1; i >= bound && b[i] == '='; --i) {
+ --len;
+ }
+ }
+ while (len >= 4) {
+ base64_decode(b, 4);
+ str.append((const char*)b, 3);
+ b += 4;
+ len -= 4;
+ }
+ // Don't decode if we hit the end or got a single leftover byte (invalid
+ // base64 but legal for skip of regular string type)
+ if (len > 1) {
+ base64_decode(b, len);
+ str.append((const char*)b, len - 1);
+ }
+ return result;
+}
+
+// Reads a sequence of characters, stopping at the first one that is not
+// a valid JSON numeric character.
+uint32_t TJSONProtocol::readJSONNumericChars(std::string& str) {
+ uint32_t result = 0;
+ str.clear();
+ while (true) {
+ uint8_t ch = reader_.peek();
+ if (!isJSONNumeric(ch)) {
+ break;
+ }
+ reader_.read();
+ str += ch;
+ ++result;
+ }
+ return result;
+}
+
+namespace {
+template <typename T>
+T fromString(const std::string& s) {
+ T t;
+ std::istringstream str(s);
+ str.imbue(std::locale::classic());
+ str >> t;
+ if (str.bad() || !str.eof())
+ throw std::runtime_error(s);
+ return t;
+}
+}
+
+// Reads a sequence of characters and assembles them into a number,
+// returning them via num
+template <typename NumberType>
+uint32_t TJSONProtocol::readJSONInteger(NumberType& num) {
+ uint32_t result = context_->read(reader_);
+ if (context_->escapeNum()) {
+ result += readJSONSyntaxChar(kJSONStringDelimiter);
+ }
+ std::string str;
+ result += readJSONNumericChars(str);
+ try {
+ num = fromString<NumberType>(str);
+ } catch (const std::runtime_error&) {
+ throw TProtocolException(TProtocolException::INVALID_DATA,
+ "Expected numeric value; got \"" + str + "\"");
+ }
+ if (context_->escapeNum()) {
+ result += readJSONSyntaxChar(kJSONStringDelimiter);
+ }
+ return result;
+}
+
+// Reads a JSON number or string and interprets it as a double.
+uint32_t TJSONProtocol::readJSONDouble(double& num) {
+ uint32_t result = context_->read(reader_);
+ std::string str;
+ if (reader_.peek() == kJSONStringDelimiter) {
+ result += readJSONString(str, true);
+ // Check for NaN, Infinity and -Infinity
+ if (str == kThriftNan) {
+ num = HUGE_VAL / HUGE_VAL; // generates NaN
+ } else if (str == kThriftInfinity) {
+ num = HUGE_VAL;
+ } else if (str == kThriftNegativeInfinity) {
+ num = -HUGE_VAL;
+ } else {
+ if (!context_->escapeNum()) {
+ // Throw exception -- we should not be in a string in this case
+ throw TProtocolException(TProtocolException::INVALID_DATA,
+ "Numeric data unexpectedly quoted");
+ }
+ try {
+ num = fromString<double>(str);
+ } catch (const std::runtime_error&) {
+ throw TProtocolException(TProtocolException::INVALID_DATA,
+ "Expected numeric value; got \"" + str + "\"");
+ }
+ }
+ } else {
+ if (context_->escapeNum()) {
+ // This will throw - we should have had a quote if escapeNum == true
+ readJSONSyntaxChar(kJSONStringDelimiter);
+ }
+ result += readJSONNumericChars(str);
+ try {
+ num = fromString<double>(str);
+ } catch (const std::runtime_error&) {
+ throw TProtocolException(TProtocolException::INVALID_DATA,
+ "Expected numeric value; got \"" + str + "\"");
+ }
+ }
+ return result;
+}
+
+uint32_t TJSONProtocol::readJSONObjectStart() {
+ uint32_t result = context_->read(reader_);
+ result += readJSONSyntaxChar(kJSONObjectStart);
+ pushContext(std::shared_ptr<TJSONContext>(new JSONPairContext()));
+ return result;
+}
+
+uint32_t TJSONProtocol::readJSONObjectEnd() {
+ uint32_t result = readJSONSyntaxChar(kJSONObjectEnd);
+ popContext();
+ return result;
+}
+
+uint32_t TJSONProtocol::readJSONArrayStart() {
+ uint32_t result = context_->read(reader_);
+ result += readJSONSyntaxChar(kJSONArrayStart);
+ pushContext(std::shared_ptr<TJSONContext>(new JSONListContext()));
+ return result;
+}
+
+uint32_t TJSONProtocol::readJSONArrayEnd() {
+ uint32_t result = readJSONSyntaxChar(kJSONArrayEnd);
+ popContext();
+ return result;
+}
+
+uint32_t TJSONProtocol::readMessageBegin(std::string& name,
+ TMessageType& messageType,
+ int32_t& seqid) {
+ uint32_t result = readJSONArrayStart();
+ int64_t tmpVal = 0;
+ result += readJSONInteger(tmpVal);
+ if (tmpVal != kThriftVersion1) {
+ throw TProtocolException(TProtocolException::BAD_VERSION, "Message contained bad version.");
+ }
+ result += readJSONString(name);
+ result += readJSONInteger(tmpVal);
+ messageType = (TMessageType)tmpVal;
+ result += readJSONInteger(tmpVal);
+ if (tmpVal > (std::numeric_limits<int32_t>::max)() ||
+ tmpVal < (std::numeric_limits<int32_t>::min)())
+ throw TProtocolException(TProtocolException::INVALID_DATA, "sequence id is not int32_t");
+ seqid = static_cast<int32_t>(tmpVal);
+ return result;
+}
+
+uint32_t TJSONProtocol::readMessageEnd() {
+ return readJSONArrayEnd();
+}
+
+uint32_t TJSONProtocol::readStructBegin(std::string& name) {
+ (void)name;
+ return readJSONObjectStart();
+}
+
+uint32_t TJSONProtocol::readStructEnd() {
+ return readJSONObjectEnd();
+}
+
+uint32_t TJSONProtocol::readFieldBegin(std::string& name, TType& fieldType, int16_t& fieldId) {
+ (void)name;
+ uint32_t result = 0;
+ // Check if we hit the end of the list
+ uint8_t ch = reader_.peek();
+ if (ch == kJSONObjectEnd) {
+ fieldType = apache::thrift::protocol::T_STOP;
+ } else {
+ uint64_t tmpVal = 0;
+ std::string tmpStr;
+ result += readJSONInteger(tmpVal);
+ if (tmpVal > static_cast<uint32_t>((std::numeric_limits<int16_t>::max)()))
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ fieldId = static_cast<int16_t>(tmpVal);
+ result += readJSONObjectStart();
+ result += readJSONString(tmpStr);
+ fieldType = getTypeIDForTypeName(tmpStr);
+ }
+ return result;
+}
+
+uint32_t TJSONProtocol::readFieldEnd() {
+ return readJSONObjectEnd();
+}
+
+uint32_t TJSONProtocol::readMapBegin(TType& keyType, TType& valType, uint32_t& size) {
+ uint64_t tmpVal = 0;
+ std::string tmpStr;
+ uint32_t result = readJSONArrayStart();
+ result += readJSONString(tmpStr);
+ keyType = getTypeIDForTypeName(tmpStr);
+ result += readJSONString(tmpStr);
+ valType = getTypeIDForTypeName(tmpStr);
+ result += readJSONInteger(tmpVal);
+ if (tmpVal > (std::numeric_limits<uint32_t>::max)())
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ size = static_cast<uint32_t>(tmpVal);
+ result += readJSONObjectStart();
+ return result;
+}
+
+uint32_t TJSONProtocol::readMapEnd() {
+ uint32_t result = readJSONObjectEnd();
+ result += readJSONArrayEnd();
+ return result;
+}
+
+uint32_t TJSONProtocol::readListBegin(TType& elemType, uint32_t& size) {
+ uint64_t tmpVal = 0;
+ std::string tmpStr;
+ uint32_t result = readJSONArrayStart();
+ result += readJSONString(tmpStr);
+ elemType = getTypeIDForTypeName(tmpStr);
+ result += readJSONInteger(tmpVal);
+ if (tmpVal > (std::numeric_limits<uint32_t>::max)())
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ size = static_cast<uint32_t>(tmpVal);
+ return result;
+}
+
+uint32_t TJSONProtocol::readListEnd() {
+ return readJSONArrayEnd();
+}
+
+uint32_t TJSONProtocol::readSetBegin(TType& elemType, uint32_t& size) {
+ uint64_t tmpVal = 0;
+ std::string tmpStr;
+ uint32_t result = readJSONArrayStart();
+ result += readJSONString(tmpStr);
+ elemType = getTypeIDForTypeName(tmpStr);
+ result += readJSONInteger(tmpVal);
+ if (tmpVal > (std::numeric_limits<uint32_t>::max)())
+ throw TProtocolException(TProtocolException::SIZE_LIMIT);
+ size = static_cast<uint32_t>(tmpVal);
+ return result;
+}
+
+uint32_t TJSONProtocol::readSetEnd() {
+ return readJSONArrayEnd();
+}
+
+uint32_t TJSONProtocol::readBool(bool& value) {
+ return readJSONInteger(value);
+}
+
+// readByte() must be handled properly because boost::lexical cast sees int8_t
+// as a text type instead of an integer type
+uint32_t TJSONProtocol::readByte(int8_t& byte) {
+ auto tmp = (int16_t)byte;
+ uint32_t result = readJSONInteger(tmp);
+ assert(tmp < 256);
+ byte = (int8_t)tmp;
+ return result;
+}
+
+uint32_t TJSONProtocol::readI16(int16_t& i16) {
+ return readJSONInteger(i16);
+}
+
+uint32_t TJSONProtocol::readI32(int32_t& i32) {
+ return readJSONInteger(i32);
+}
+
+uint32_t TJSONProtocol::readI64(int64_t& i64) {
+ return readJSONInteger(i64);
+}
+
+uint32_t TJSONProtocol::readDouble(double& dub) {
+ return readJSONDouble(dub);
+}
+
+uint32_t TJSONProtocol::readString(std::string& str) {
+ return readJSONString(str);
+}
+
+uint32_t TJSONProtocol::readBinary(std::string& str) {
+ return readJSONBase64(str);
+}
+}
+}
+} // apache::thrift::protocol
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TJSONProtocol.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TJSONProtocol.h
new file mode 100644
index 000000000..420995ef3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TJSONProtocol.h
@@ -0,0 +1,325 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_PROTOCOL_TJSONPROTOCOL_H_
+#define _THRIFT_PROTOCOL_TJSONPROTOCOL_H_ 1
+
+#include <thrift/protocol/TVirtualProtocol.h>
+
+#include <stack>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+// Forward declaration
+class TJSONContext;
+
+/**
+ * JSON protocol for Thrift.
+ *
+ * Implements a protocol which uses JSON as the wire-format.
+ *
+ * Thrift types are represented as described below:
+ *
+ * 1. Every Thrift integer type is represented as a JSON number.
+ *
+ * 2. Thrift doubles are represented as JSON numbers. Some special values are
+ * represented as strings:
+ * a. "NaN" for not-a-number values
+ * b. "Infinity" for positive infinity
+ * c. "-Infinity" for negative infinity
+ *
+ * 3. Thrift string values are emitted as JSON strings, with appropriate
+ * escaping.
+ *
+ * 4. Thrift binary values are encoded into Base64 and emitted as JSON strings.
+ * The readBinary() method is written such that it will properly skip if
+ * called on a Thrift string (although it will decode garbage data).
+ *
+ * NOTE: Base64 padding is optional for Thrift binary value encoding. So
+ * the readBinary() method needs to decode both input strings with padding
+ * and those without one.
+ *
+ * 5. Thrift structs are represented as JSON objects, with the field ID as the
+ * key, and the field value represented as a JSON object with a single
+ * key-value pair. The key is a short string identifier for that type,
+ * followed by the value. The valid type identifiers are: "tf" for bool,
+ * "i8" for byte, "i16" for 16-bit integer, "i32" for 32-bit integer, "i64"
+ * for 64-bit integer, "dbl" for double-precision loating point, "str" for
+ * string (including binary), "rec" for struct ("records"), "map" for map,
+ * "lst" for list, "set" for set.
+ *
+ * 6. Thrift lists and sets are represented as JSON arrays, with the first
+ * element of the JSON array being the string identifier for the Thrift
+ * element type and the second element of the JSON array being the count of
+ * the Thrift elements. The Thrift elements then follow.
+ *
+ * 7. Thrift maps are represented as JSON arrays, with the first two elements
+ * of the JSON array being the string identifiers for the Thrift key type
+ * and value type, followed by the count of the Thrift pairs, followed by a
+ * JSON object containing the key-value pairs. Note that JSON keys can only
+ * be strings, which means that the key type of the Thrift map should be
+ * restricted to numeric or string types -- in the case of numerics, they
+ * are serialized as strings.
+ *
+ * 8. Thrift messages are represented as JSON arrays, with the protocol
+ * version #, the message name, the message type, and the sequence ID as
+ * the first 4 elements.
+ *
+ * More discussion of the double handling is probably warranted. The aim of
+ * the current implementation is to match as closely as possible the behavior
+ * of Java's Double.toString(), which has no precision loss. Implementors in
+ * other languages should strive to achieve that where possible. I have not
+ * yet verified whether std::istringstream::operator>>, which is doing that
+ * work for me in C++, loses any precision, but I am leaving this as a future
+ * improvement. I may try to provide a C component for this, so that other
+ * languages could bind to the same underlying implementation for maximum
+ * consistency.
+ *
+ */
+class TJSONProtocol : public TVirtualProtocol<TJSONProtocol> {
+public:
+ TJSONProtocol(std::shared_ptr<TTransport> ptrans);
+
+ ~TJSONProtocol() override;
+
+private:
+ void pushContext(std::shared_ptr<TJSONContext> c);
+
+ void popContext();
+
+ uint32_t writeJSONEscapeChar(uint8_t ch);
+
+ uint32_t writeJSONChar(uint8_t ch);
+
+ uint32_t writeJSONString(const std::string& str);
+
+ uint32_t writeJSONBase64(const std::string& str);
+
+ template <typename NumberType>
+ uint32_t writeJSONInteger(NumberType num);
+
+ uint32_t writeJSONDouble(double num);
+
+ uint32_t writeJSONObjectStart();
+
+ uint32_t writeJSONObjectEnd();
+
+ uint32_t writeJSONArrayStart();
+
+ uint32_t writeJSONArrayEnd();
+
+ uint32_t readJSONSyntaxChar(uint8_t ch);
+
+ uint32_t readJSONEscapeChar(uint16_t* out);
+
+ uint32_t readJSONString(std::string& str, bool skipContext = false);
+
+ uint32_t readJSONBase64(std::string& str);
+
+ uint32_t readJSONNumericChars(std::string& str);
+
+ template <typename NumberType>
+ uint32_t readJSONInteger(NumberType& num);
+
+ uint32_t readJSONDouble(double& num);
+
+ uint32_t readJSONObjectStart();
+
+ uint32_t readJSONObjectEnd();
+
+ uint32_t readJSONArrayStart();
+
+ uint32_t readJSONArrayEnd();
+
+public:
+ /**
+ * Writing functions.
+ */
+
+ uint32_t writeMessageBegin(const std::string& name,
+ const TMessageType messageType,
+ const int32_t seqid);
+
+ uint32_t writeMessageEnd();
+
+ uint32_t writeStructBegin(const char* name);
+
+ uint32_t writeStructEnd();
+
+ uint32_t writeFieldBegin(const char* name, const TType fieldType, const int16_t fieldId);
+
+ uint32_t writeFieldEnd();
+
+ uint32_t writeFieldStop();
+
+ uint32_t writeMapBegin(const TType keyType, const TType valType, const uint32_t size);
+
+ uint32_t writeMapEnd();
+
+ uint32_t writeListBegin(const TType elemType, const uint32_t size);
+
+ uint32_t writeListEnd();
+
+ uint32_t writeSetBegin(const TType elemType, const uint32_t size);
+
+ uint32_t writeSetEnd();
+
+ uint32_t writeBool(const bool value);
+
+ uint32_t writeByte(const int8_t byte);
+
+ uint32_t writeI16(const int16_t i16);
+
+ uint32_t writeI32(const int32_t i32);
+
+ uint32_t writeI64(const int64_t i64);
+
+ uint32_t writeDouble(const double dub);
+
+ uint32_t writeString(const std::string& str);
+
+ uint32_t writeBinary(const std::string& str);
+
+ /**
+ * Reading functions
+ */
+
+ uint32_t readMessageBegin(std::string& name, TMessageType& messageType, int32_t& seqid);
+
+ uint32_t readMessageEnd();
+
+ uint32_t readStructBegin(std::string& name);
+
+ uint32_t readStructEnd();
+
+ uint32_t readFieldBegin(std::string& name, TType& fieldType, int16_t& fieldId);
+
+ uint32_t readFieldEnd();
+
+ uint32_t readMapBegin(TType& keyType, TType& valType, uint32_t& size);
+
+ uint32_t readMapEnd();
+
+ uint32_t readListBegin(TType& elemType, uint32_t& size);
+
+ uint32_t readListEnd();
+
+ uint32_t readSetBegin(TType& elemType, uint32_t& size);
+
+ uint32_t readSetEnd();
+
+ uint32_t readBool(bool& value);
+
+ // Provide the default readBool() implementation for std::vector<bool>
+ using TVirtualProtocol<TJSONProtocol>::readBool;
+
+ uint32_t readByte(int8_t& byte);
+
+ uint32_t readI16(int16_t& i16);
+
+ uint32_t readI32(int32_t& i32);
+
+ uint32_t readI64(int64_t& i64);
+
+ uint32_t readDouble(double& dub);
+
+ uint32_t readString(std::string& str);
+
+ uint32_t readBinary(std::string& str);
+
+ class LookaheadReader {
+
+ public:
+ LookaheadReader(TTransport& trans) : trans_(&trans), hasData_(false), data_(0) {}
+
+ uint8_t read() {
+ if (hasData_) {
+ hasData_ = false;
+ } else {
+ trans_->readAll(&data_, 1);
+ }
+ return data_;
+ }
+
+ uint8_t peek() {
+ if (!hasData_) {
+ trans_->readAll(&data_, 1);
+ }
+ hasData_ = true;
+ return data_;
+ }
+
+ private:
+ TTransport* trans_;
+ bool hasData_;
+ uint8_t data_;
+ };
+
+private:
+ TTransport* trans_;
+
+ std::stack<std::shared_ptr<TJSONContext> > contexts_;
+ std::shared_ptr<TJSONContext> context_;
+ LookaheadReader reader_;
+};
+
+/**
+ * Constructs input and output protocol objects given transports.
+ */
+class TJSONProtocolFactory : public TProtocolFactory {
+public:
+ TJSONProtocolFactory() = default;
+
+ ~TJSONProtocolFactory() override = default;
+
+ std::shared_ptr<TProtocol> getProtocol(std::shared_ptr<TTransport> trans) override {
+ return std::shared_ptr<TProtocol>(new TJSONProtocol(trans));
+ }
+};
+}
+}
+} // apache::thrift::protocol
+
+// TODO(dreiss): Move part of ThriftJSONString into a .cpp file and remove this.
+#include <thrift/transport/TBufferTransports.h>
+
+namespace apache {
+namespace thrift {
+
+template <typename ThriftStruct>
+std::string ThriftJSONString(const ThriftStruct& ts) {
+ using namespace apache::thrift::transport;
+ using namespace apache::thrift::protocol;
+ auto* buffer = new TMemoryBuffer;
+ std::shared_ptr<TTransport> trans(buffer);
+ TJSONProtocol protocol(trans);
+
+ ts.write(&protocol);
+
+ uint8_t* buf;
+ uint32_t size;
+ buffer->getBuffer(&buf, &size);
+ return std::string((char*)buf, (unsigned int)size);
+}
+}
+} // apache::thrift
+
+#endif // #define _THRIFT_PROTOCOL_TJSONPROTOCOL_H_ 1
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TMultiplexedProtocol.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TMultiplexedProtocol.cpp
new file mode 100644
index 000000000..f0dc69e0d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TMultiplexedProtocol.cpp
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/protocol/TMultiplexedProtocol.h>
+#include <thrift/processor/TMultiplexedProcessor.h>
+#include <thrift/protocol/TProtocolDecorator.h>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+uint32_t TMultiplexedProtocol::writeMessageBegin_virt(const std::string& _name,
+ const TMessageType _type,
+ const int32_t _seqid) {
+ if (_type == T_CALL || _type == T_ONEWAY) {
+ return TProtocolDecorator::writeMessageBegin_virt(serviceName + separator + _name,
+ _type,
+ _seqid);
+ } else {
+ return TProtocolDecorator::writeMessageBegin_virt(_name, _type, _seqid);
+ }
+}
+}
+}
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TMultiplexedProtocol.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TMultiplexedProtocol.h
new file mode 100644
index 000000000..0dc960584
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TMultiplexedProtocol.h
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef THRIFT_TMULTIPLEXEDPROTOCOL_H_
+#define THRIFT_TMULTIPLEXEDPROTOCOL_H_ 1
+
+#include <thrift/protocol/TProtocolDecorator.h>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+using std::shared_ptr;
+
+/**
+ * <code>TMultiplexedProtocol</code> is a protocol-independent concrete decorator
+ * that allows a Thrift client to communicate with a multiplexing Thrift server,
+ * by prepending the service name to the function name during function calls.
+ *
+ * \note THIS IS NOT USED BY SERVERS. On the server, use
+ * {@link apache::thrift::TMultiplexedProcessor TMultiplexedProcessor} to handle requests
+ * from a multiplexing client.
+ *
+ * This example uses a single socket transport to invoke two services:
+ *
+ * <blockquote><code>
+ * shared_ptr<TSocket> transport(new TSocket("localhost", 9090));
+ * transport->open();
+ *
+ * shared_ptr<TBinaryProtocol> protocol(new TBinaryProtocol(transport));
+ *
+ * shared_ptr<TMultiplexedProtocol> mp1(new TMultiplexedProtocol(protocol, "Calculator"));
+ * shared_ptr<CalculatorClient> service1(new CalculatorClient(mp1));
+ *
+ * shared_ptr<TMultiplexedProtocol> mp2(new TMultiplexedProtocol(protocol, "WeatherReport"));
+ * shared_ptr<WeatherReportClient> service2(new WeatherReportClient(mp2));
+ *
+ * service1->add(2,2);
+ * int temp = service2->getTemperature();
+ * </code></blockquote>
+ *
+ * @see apache::thrift::protocol::TProtocolDecorator
+ */
+class TMultiplexedProtocol : public TProtocolDecorator {
+public:
+ /**
+ * Wrap the specified protocol, allowing it to be used to communicate with a
+ * multiplexing server. The <code>serviceName</code> is required as it is
+ * prepended to the message header so that the multiplexing server can broker
+ * the function call to the proper service.
+ *
+ * \param _protocol Your communication protocol of choice, e.g. <code>TBinaryProtocol</code>.
+ * \param _serviceName The service name of the service communicating via this protocol.
+ */
+ TMultiplexedProtocol(shared_ptr<TProtocol> _protocol, const std::string& _serviceName)
+ : TProtocolDecorator(_protocol), serviceName(_serviceName), separator(":") {}
+ ~TMultiplexedProtocol() override = default;
+
+ /**
+ * Prepends the service name to the function name, separated by TMultiplexedProtocol::SEPARATOR.
+ *
+ * \param [in] _name The name of the method to be called in the service.
+ * \param [in] _type The type of message
+ * \param [in] _name The sequential id of the message
+ *
+ * \throws TException Passed through from wrapped <code>TProtocol</code> instance.
+ */
+ uint32_t writeMessageBegin_virt(const std::string& _name,
+ const TMessageType _type,
+ const int32_t _seqid) override;
+
+private:
+ const std::string serviceName;
+ const std::string separator;
+};
+}
+}
+}
+
+#endif // THRIFT_TMULTIPLEXEDPROTOCOL_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocol.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocol.cpp
new file mode 100644
index 000000000..b460455ff
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocol.cpp
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/protocol/TProtocol.h>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+TProtocol::~TProtocol() = default;
+uint32_t TProtocol::skip_virt(TType type) {
+ return ::apache::thrift::protocol::skip(*this, type);
+}
+
+TProtocolFactory::~TProtocolFactory() = default;
+
+}}} // apache::thrift::protocol
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocol.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocol.h
new file mode 100644
index 000000000..df9c5c39b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocol.h
@@ -0,0 +1,762 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_PROTOCOL_TPROTOCOL_H_
+#define _THRIFT_PROTOCOL_TPROTOCOL_H_ 1
+
+#ifdef _WIN32
+// Need to come before any Windows.h includes
+#include <Winsock2.h>
+#endif
+
+#include <thrift/transport/TTransport.h>
+#include <thrift/protocol/TProtocolException.h>
+
+#include <memory>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#include <sys/types.h>
+#include <string>
+#include <map>
+#include <vector>
+#include <climits>
+
+// Use this to get around strict aliasing rules.
+// For example, uint64_t i = bitwise_cast<uint64_t>(returns_double());
+// The most obvious implementation is to just cast a pointer,
+// but that doesn't work.
+// For a pretty in-depth explanation of the problem, see
+// http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html
+template <typename To, typename From>
+static inline To bitwise_cast(From from) {
+ static_assert(sizeof(From) == sizeof(To), "sizeof(From) == sizeof(To)");
+
+ // BAD!!! These are all broken with -O2.
+ //return *reinterpret_cast<To*>(&from); // BAD!!!
+ //return *static_cast<To*>(static_cast<void*>(&from)); // BAD!!!
+ //return *(To*)(void*)&from; // BAD!!!
+
+ // Super clean and paritally blessed by section 3.9 of the standard.
+ //unsigned char c[sizeof(from)];
+ //memcpy(c, &from, sizeof(from));
+ //To to;
+ //memcpy(&to, c, sizeof(c));
+ //return to;
+
+ // Slightly more questionable.
+ // Same code emitted by GCC.
+ //To to;
+ //memcpy(&to, &from, sizeof(from));
+ //return to;
+
+ // Technically undefined, but almost universally supported,
+ // and the most efficient implementation.
+ union {
+ From f;
+ To t;
+ } u;
+ u.f = from;
+ return u.t;
+}
+
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifndef __THRIFT_BYTE_ORDER
+# if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
+# define __THRIFT_BYTE_ORDER BYTE_ORDER
+# define __THRIFT_LITTLE_ENDIAN LITTLE_ENDIAN
+# define __THRIFT_BIG_ENDIAN BIG_ENDIAN
+# else
+# include <boost/predef/other/endian.h>
+# if BOOST_ENDIAN_BIG_BYTE
+# define __THRIFT_BYTE_ORDER 4321
+# define __THRIFT_LITTLE_ENDIAN 0
+# define __THRIFT_BIG_ENDIAN __THRIFT_BYTE_ORDER
+# elif BOOST_ENDIAN_LITTLE_BYTE
+# define __THRIFT_BYTE_ORDER 1234
+# define __THRIFT_LITTLE_ENDIAN __THRIFT_BYTE_ORDER
+# define __THRIFT_BIG_ENDIAN 0
+# endif
+# ifdef BOOST_LITTLE_ENDIAN
+# else
+# endif
+# endif
+#endif
+
+#if __THRIFT_BYTE_ORDER == __THRIFT_BIG_ENDIAN
+# if !defined(THRIFT_ntohll)
+# define THRIFT_ntohll(n) (n)
+# define THRIFT_htonll(n) (n)
+# endif
+# if defined(__GNUC__) && defined(__GLIBC__)
+# include <byteswap.h>
+# define THRIFT_htolell(n) bswap_64(n)
+# define THRIFT_letohll(n) bswap_64(n)
+# define THRIFT_htolel(n) bswap_32(n)
+# define THRIFT_letohl(n) bswap_32(n)
+# define THRIFT_htoles(n) bswap_16(n)
+# define THRIFT_letohs(n) bswap_16(n)
+# else /* GNUC & GLIBC */
+# define bswap_64(n) \
+ ( (((n) & 0xff00000000000000ull) >> 56) \
+ | (((n) & 0x00ff000000000000ull) >> 40) \
+ | (((n) & 0x0000ff0000000000ull) >> 24) \
+ | (((n) & 0x000000ff00000000ull) >> 8) \
+ | (((n) & 0x00000000ff000000ull) << 8) \
+ | (((n) & 0x0000000000ff0000ull) << 24) \
+ | (((n) & 0x000000000000ff00ull) << 40) \
+ | (((n) & 0x00000000000000ffull) << 56) )
+# define bswap_32(n) \
+ ( (((n) & 0xff000000ul) >> 24) \
+ | (((n) & 0x00ff0000ul) >> 8) \
+ | (((n) & 0x0000ff00ul) << 8) \
+ | (((n) & 0x000000fful) << 24) )
+# define bswap_16(n) \
+ ( (((n) & ((unsigned short)0xff00ul)) >> 8) \
+ | (((n) & ((unsigned short)0x00fful)) << 8) )
+# define THRIFT_htolell(n) bswap_64(n)
+# define THRIFT_letohll(n) bswap_64(n)
+# define THRIFT_htolel(n) bswap_32(n)
+# define THRIFT_letohl(n) bswap_32(n)
+# define THRIFT_htoles(n) bswap_16(n)
+# define THRIFT_letohs(n) bswap_16(n)
+# endif /* GNUC & GLIBC */
+#elif __THRIFT_BYTE_ORDER == __THRIFT_LITTLE_ENDIAN
+# define THRIFT_htolell(n) (n)
+# define THRIFT_letohll(n) (n)
+# define THRIFT_htolel(n) (n)
+# define THRIFT_letohl(n) (n)
+# define THRIFT_htoles(n) (n)
+# define THRIFT_letohs(n) (n)
+# if defined(__GNUC__) && defined(__GLIBC__)
+# include <byteswap.h>
+# define THRIFT_ntohll(n) bswap_64(n)
+# define THRIFT_htonll(n) bswap_64(n)
+# elif defined(_MSC_VER) /* Microsoft Visual C++ */
+# define THRIFT_ntohll(n) ( _byteswap_uint64((uint64_t)n) )
+# define THRIFT_htonll(n) ( _byteswap_uint64((uint64_t)n) )
+# elif !defined(THRIFT_ntohll) /* Not GNUC/GLIBC or MSVC */
+# define THRIFT_ntohll(n) ( (((uint64_t)ntohl((uint32_t)n)) << 32) + ntohl((uint32_t)(n >> 32)) )
+# define THRIFT_htonll(n) ( (((uint64_t)htonl((uint32_t)n)) << 32) + htonl((uint32_t)(n >> 32)) )
+# endif /* GNUC/GLIBC or MSVC or something else */
+#else /* __THRIFT_BYTE_ORDER */
+# error "Can't define THRIFT_htonll or THRIFT_ntohll!"
+#endif
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+using apache::thrift::transport::TTransport;
+
+/**
+ * Enumerated definition of the types that the Thrift protocol supports.
+ * Take special note of the T_END type which is used specifically to mark
+ * the end of a sequence of fields.
+ */
+enum TType {
+ T_STOP = 0,
+ T_VOID = 1,
+ T_BOOL = 2,
+ T_BYTE = 3,
+ T_I08 = 3,
+ T_I16 = 6,
+ T_I32 = 8,
+ T_U64 = 9,
+ T_I64 = 10,
+ T_DOUBLE = 4,
+ T_STRING = 11,
+ T_UTF7 = 11,
+ T_STRUCT = 12,
+ T_MAP = 13,
+ T_SET = 14,
+ T_LIST = 15,
+ T_UTF8 = 16,
+ T_UTF16 = 17
+};
+
+/**
+ * Enumerated definition of the message types that the Thrift protocol
+ * supports.
+ */
+enum TMessageType {
+ T_CALL = 1,
+ T_REPLY = 2,
+ T_EXCEPTION = 3,
+ T_ONEWAY = 4
+};
+
+static const uint32_t DEFAULT_RECURSION_LIMIT = 64;
+
+/**
+ * Abstract class for a thrift protocol driver. These are all the methods that
+ * a protocol must implement. Essentially, there must be some way of reading
+ * and writing all the base types, plus a mechanism for writing out structs
+ * with indexed fields.
+ *
+ * TProtocol objects should not be shared across multiple encoding contexts,
+ * as they may need to maintain internal state in some protocols (i.e. XML).
+ * Note that is is acceptable for the TProtocol module to do its own internal
+ * buffered reads/writes to the underlying TTransport where appropriate (i.e.
+ * when parsing an input XML stream, reading should be batched rather than
+ * looking ahead character by character for a close tag).
+ *
+ */
+class TProtocol {
+public:
+ virtual ~TProtocol();
+
+ /**
+ * Writing functions.
+ */
+
+ virtual uint32_t writeMessageBegin_virt(const std::string& name,
+ const TMessageType messageType,
+ const int32_t seqid) = 0;
+
+ virtual uint32_t writeMessageEnd_virt() = 0;
+
+ virtual uint32_t writeStructBegin_virt(const char* name) = 0;
+
+ virtual uint32_t writeStructEnd_virt() = 0;
+
+ virtual uint32_t writeFieldBegin_virt(const char* name,
+ const TType fieldType,
+ const int16_t fieldId) = 0;
+
+ virtual uint32_t writeFieldEnd_virt() = 0;
+
+ virtual uint32_t writeFieldStop_virt() = 0;
+
+ virtual uint32_t writeMapBegin_virt(const TType keyType, const TType valType, const uint32_t size)
+ = 0;
+
+ virtual uint32_t writeMapEnd_virt() = 0;
+
+ virtual uint32_t writeListBegin_virt(const TType elemType, const uint32_t size) = 0;
+
+ virtual uint32_t writeListEnd_virt() = 0;
+
+ virtual uint32_t writeSetBegin_virt(const TType elemType, const uint32_t size) = 0;
+
+ virtual uint32_t writeSetEnd_virt() = 0;
+
+ virtual uint32_t writeBool_virt(const bool value) = 0;
+
+ virtual uint32_t writeByte_virt(const int8_t byte) = 0;
+
+ virtual uint32_t writeI16_virt(const int16_t i16) = 0;
+
+ virtual uint32_t writeI32_virt(const int32_t i32) = 0;
+
+ virtual uint32_t writeI64_virt(const int64_t i64) = 0;
+
+ virtual uint32_t writeDouble_virt(const double dub) = 0;
+
+ virtual uint32_t writeString_virt(const std::string& str) = 0;
+
+ virtual uint32_t writeBinary_virt(const std::string& str) = 0;
+
+ uint32_t writeMessageBegin(const std::string& name,
+ const TMessageType messageType,
+ const int32_t seqid) {
+ T_VIRTUAL_CALL();
+ return writeMessageBegin_virt(name, messageType, seqid);
+ }
+
+ uint32_t writeMessageEnd() {
+ T_VIRTUAL_CALL();
+ return writeMessageEnd_virt();
+ }
+
+ uint32_t writeStructBegin(const char* name) {
+ T_VIRTUAL_CALL();
+ return writeStructBegin_virt(name);
+ }
+
+ uint32_t writeStructEnd() {
+ T_VIRTUAL_CALL();
+ return writeStructEnd_virt();
+ }
+
+ uint32_t writeFieldBegin(const char* name, const TType fieldType, const int16_t fieldId) {
+ T_VIRTUAL_CALL();
+ return writeFieldBegin_virt(name, fieldType, fieldId);
+ }
+
+ uint32_t writeFieldEnd() {
+ T_VIRTUAL_CALL();
+ return writeFieldEnd_virt();
+ }
+
+ uint32_t writeFieldStop() {
+ T_VIRTUAL_CALL();
+ return writeFieldStop_virt();
+ }
+
+ uint32_t writeMapBegin(const TType keyType, const TType valType, const uint32_t size) {
+ T_VIRTUAL_CALL();
+ return writeMapBegin_virt(keyType, valType, size);
+ }
+
+ uint32_t writeMapEnd() {
+ T_VIRTUAL_CALL();
+ return writeMapEnd_virt();
+ }
+
+ uint32_t writeListBegin(const TType elemType, const uint32_t size) {
+ T_VIRTUAL_CALL();
+ return writeListBegin_virt(elemType, size);
+ }
+
+ uint32_t writeListEnd() {
+ T_VIRTUAL_CALL();
+ return writeListEnd_virt();
+ }
+
+ uint32_t writeSetBegin(const TType elemType, const uint32_t size) {
+ T_VIRTUAL_CALL();
+ return writeSetBegin_virt(elemType, size);
+ }
+
+ uint32_t writeSetEnd() {
+ T_VIRTUAL_CALL();
+ return writeSetEnd_virt();
+ }
+
+ uint32_t writeBool(const bool value) {
+ T_VIRTUAL_CALL();
+ return writeBool_virt(value);
+ }
+
+ uint32_t writeByte(const int8_t byte) {
+ T_VIRTUAL_CALL();
+ return writeByte_virt(byte);
+ }
+
+ uint32_t writeI16(const int16_t i16) {
+ T_VIRTUAL_CALL();
+ return writeI16_virt(i16);
+ }
+
+ uint32_t writeI32(const int32_t i32) {
+ T_VIRTUAL_CALL();
+ return writeI32_virt(i32);
+ }
+
+ uint32_t writeI64(const int64_t i64) {
+ T_VIRTUAL_CALL();
+ return writeI64_virt(i64);
+ }
+
+ uint32_t writeDouble(const double dub) {
+ T_VIRTUAL_CALL();
+ return writeDouble_virt(dub);
+ }
+
+ uint32_t writeString(const std::string& str) {
+ T_VIRTUAL_CALL();
+ return writeString_virt(str);
+ }
+
+ uint32_t writeBinary(const std::string& str) {
+ T_VIRTUAL_CALL();
+ return writeBinary_virt(str);
+ }
+
+ /**
+ * Reading functions
+ */
+
+ virtual uint32_t readMessageBegin_virt(std::string& name,
+ TMessageType& messageType,
+ int32_t& seqid) = 0;
+
+ virtual uint32_t readMessageEnd_virt() = 0;
+
+ virtual uint32_t readStructBegin_virt(std::string& name) = 0;
+
+ virtual uint32_t readStructEnd_virt() = 0;
+
+ virtual uint32_t readFieldBegin_virt(std::string& name, TType& fieldType, int16_t& fieldId) = 0;
+
+ virtual uint32_t readFieldEnd_virt() = 0;
+
+ virtual uint32_t readMapBegin_virt(TType& keyType, TType& valType, uint32_t& size) = 0;
+
+ virtual uint32_t readMapEnd_virt() = 0;
+
+ virtual uint32_t readListBegin_virt(TType& elemType, uint32_t& size) = 0;
+
+ virtual uint32_t readListEnd_virt() = 0;
+
+ virtual uint32_t readSetBegin_virt(TType& elemType, uint32_t& size) = 0;
+
+ virtual uint32_t readSetEnd_virt() = 0;
+
+ virtual uint32_t readBool_virt(bool& value) = 0;
+
+ virtual uint32_t readBool_virt(std::vector<bool>::reference value) = 0;
+
+ virtual uint32_t readByte_virt(int8_t& byte) = 0;
+
+ virtual uint32_t readI16_virt(int16_t& i16) = 0;
+
+ virtual uint32_t readI32_virt(int32_t& i32) = 0;
+
+ virtual uint32_t readI64_virt(int64_t& i64) = 0;
+
+ virtual uint32_t readDouble_virt(double& dub) = 0;
+
+ virtual uint32_t readString_virt(std::string& str) = 0;
+
+ virtual uint32_t readBinary_virt(std::string& str) = 0;
+
+ uint32_t readMessageBegin(std::string& name, TMessageType& messageType, int32_t& seqid) {
+ T_VIRTUAL_CALL();
+ return readMessageBegin_virt(name, messageType, seqid);
+ }
+
+ uint32_t readMessageEnd() {
+ T_VIRTUAL_CALL();
+ return readMessageEnd_virt();
+ }
+
+ uint32_t readStructBegin(std::string& name) {
+ T_VIRTUAL_CALL();
+ return readStructBegin_virt(name);
+ }
+
+ uint32_t readStructEnd() {
+ T_VIRTUAL_CALL();
+ return readStructEnd_virt();
+ }
+
+ uint32_t readFieldBegin(std::string& name, TType& fieldType, int16_t& fieldId) {
+ T_VIRTUAL_CALL();
+ return readFieldBegin_virt(name, fieldType, fieldId);
+ }
+
+ uint32_t readFieldEnd() {
+ T_VIRTUAL_CALL();
+ return readFieldEnd_virt();
+ }
+
+ uint32_t readMapBegin(TType& keyType, TType& valType, uint32_t& size) {
+ T_VIRTUAL_CALL();
+ return readMapBegin_virt(keyType, valType, size);
+ }
+
+ uint32_t readMapEnd() {
+ T_VIRTUAL_CALL();
+ return readMapEnd_virt();
+ }
+
+ uint32_t readListBegin(TType& elemType, uint32_t& size) {
+ T_VIRTUAL_CALL();
+ return readListBegin_virt(elemType, size);
+ }
+
+ uint32_t readListEnd() {
+ T_VIRTUAL_CALL();
+ return readListEnd_virt();
+ }
+
+ uint32_t readSetBegin(TType& elemType, uint32_t& size) {
+ T_VIRTUAL_CALL();
+ return readSetBegin_virt(elemType, size);
+ }
+
+ uint32_t readSetEnd() {
+ T_VIRTUAL_CALL();
+ return readSetEnd_virt();
+ }
+
+ uint32_t readBool(bool& value) {
+ T_VIRTUAL_CALL();
+ return readBool_virt(value);
+ }
+
+ uint32_t readByte(int8_t& byte) {
+ T_VIRTUAL_CALL();
+ return readByte_virt(byte);
+ }
+
+ uint32_t readI16(int16_t& i16) {
+ T_VIRTUAL_CALL();
+ return readI16_virt(i16);
+ }
+
+ uint32_t readI32(int32_t& i32) {
+ T_VIRTUAL_CALL();
+ return readI32_virt(i32);
+ }
+
+ uint32_t readI64(int64_t& i64) {
+ T_VIRTUAL_CALL();
+ return readI64_virt(i64);
+ }
+
+ uint32_t readDouble(double& dub) {
+ T_VIRTUAL_CALL();
+ return readDouble_virt(dub);
+ }
+
+ uint32_t readString(std::string& str) {
+ T_VIRTUAL_CALL();
+ return readString_virt(str);
+ }
+
+ uint32_t readBinary(std::string& str) {
+ T_VIRTUAL_CALL();
+ return readBinary_virt(str);
+ }
+
+ /*
+ * std::vector is specialized for bool, and its elements are individual bits
+ * rather than bools. We need to define a different version of readBool()
+ * to work with std::vector<bool>.
+ */
+ uint32_t readBool(std::vector<bool>::reference value) {
+ T_VIRTUAL_CALL();
+ return readBool_virt(value);
+ }
+
+ /**
+ * Method to arbitrarily skip over data.
+ */
+ uint32_t skip(TType type) {
+ T_VIRTUAL_CALL();
+ return skip_virt(type);
+ }
+ virtual uint32_t skip_virt(TType type);
+
+ inline std::shared_ptr<TTransport> getTransport() { return ptrans_; }
+
+ // TODO: remove these two calls, they are for backwards
+ // compatibility
+ inline std::shared_ptr<TTransport> getInputTransport() { return ptrans_; }
+ inline std::shared_ptr<TTransport> getOutputTransport() { return ptrans_; }
+
+ // input and output recursion depth are kept separate so that one protocol
+ // can be used concurrently for both input and output.
+ void incrementInputRecursionDepth() {
+ if (recursion_limit_ < ++input_recursion_depth_) {
+ throw TProtocolException(TProtocolException::DEPTH_LIMIT);
+ }
+ }
+ void decrementInputRecursionDepth() { --input_recursion_depth_; }
+
+ void incrementOutputRecursionDepth() {
+ if (recursion_limit_ < ++output_recursion_depth_) {
+ throw TProtocolException(TProtocolException::DEPTH_LIMIT);
+ }
+ }
+ void decrementOutputRecursionDepth() { --output_recursion_depth_; }
+
+ uint32_t getRecursionLimit() const {return recursion_limit_;}
+ void setRecurisionLimit(uint32_t depth) {recursion_limit_ = depth;}
+
+protected:
+ TProtocol(std::shared_ptr<TTransport> ptrans)
+ : ptrans_(ptrans), input_recursion_depth_(0), output_recursion_depth_(0), recursion_limit_(DEFAULT_RECURSION_LIMIT)
+ {}
+
+ std::shared_ptr<TTransport> ptrans_;
+
+private:
+ TProtocol() = default;
+ uint32_t input_recursion_depth_;
+ uint32_t output_recursion_depth_;
+ uint32_t recursion_limit_;
+};
+
+/**
+ * Constructs input and output protocol objects given transports.
+ */
+class TProtocolFactory {
+public:
+ TProtocolFactory() = default;
+
+ virtual ~TProtocolFactory();
+
+ virtual std::shared_ptr<TProtocol> getProtocol(std::shared_ptr<TTransport> trans) = 0;
+ virtual std::shared_ptr<TProtocol> getProtocol(std::shared_ptr<TTransport> inTrans,
+ std::shared_ptr<TTransport> outTrans) {
+ (void)outTrans;
+ return getProtocol(inTrans);
+ }
+};
+
+/**
+ * Dummy protocol class.
+ *
+ * This class does nothing, and should never be instantiated.
+ * It is used only by the generator code.
+ */
+class TDummyProtocol : public TProtocol {};
+
+// This is the default / legacy choice
+struct TNetworkBigEndian
+{
+ static uint16_t toWire16(uint16_t x) {return htons(x);}
+ static uint32_t toWire32(uint32_t x) {return htonl(x);}
+ static uint64_t toWire64(uint64_t x) {return THRIFT_htonll(x);}
+ static uint16_t fromWire16(uint16_t x) {return ntohs(x);}
+ static uint32_t fromWire32(uint32_t x) {return ntohl(x);}
+ static uint64_t fromWire64(uint64_t x) {return THRIFT_ntohll(x);}
+};
+
+// On most systems, this will be a bit faster than TNetworkBigEndian
+struct TNetworkLittleEndian
+{
+ static uint16_t toWire16(uint16_t x) {return THRIFT_htoles(x);}
+ static uint32_t toWire32(uint32_t x) {return THRIFT_htolel(x);}
+ static uint64_t toWire64(uint64_t x) {return THRIFT_htolell(x);}
+ static uint16_t fromWire16(uint16_t x) {return THRIFT_letohs(x);}
+ static uint32_t fromWire32(uint32_t x) {return THRIFT_letohl(x);}
+ static uint64_t fromWire64(uint64_t x) {return THRIFT_letohll(x);}
+};
+
+struct TOutputRecursionTracker {
+ TProtocol &prot_;
+ TOutputRecursionTracker(TProtocol &prot) : prot_(prot) {
+ prot_.incrementOutputRecursionDepth();
+ }
+ ~TOutputRecursionTracker() {
+ prot_.decrementOutputRecursionDepth();
+ }
+};
+
+struct TInputRecursionTracker {
+ TProtocol &prot_;
+ TInputRecursionTracker(TProtocol &prot) : prot_(prot) {
+ prot_.incrementInputRecursionDepth();
+ }
+ ~TInputRecursionTracker() {
+ prot_.decrementInputRecursionDepth();
+ }
+};
+
+/**
+ * Helper template for implementing TProtocol::skip().
+ *
+ * Templatized to avoid having to make virtual function calls.
+ */
+template <class Protocol_>
+uint32_t skip(Protocol_& prot, TType type) {
+ TInputRecursionTracker tracker(prot);
+
+ switch (type) {
+ case T_BOOL: {
+ bool boolv;
+ return prot.readBool(boolv);
+ }
+ case T_BYTE: {
+ int8_t bytev = 0;
+ return prot.readByte(bytev);
+ }
+ case T_I16: {
+ int16_t i16;
+ return prot.readI16(i16);
+ }
+ case T_I32: {
+ int32_t i32;
+ return prot.readI32(i32);
+ }
+ case T_I64: {
+ int64_t i64;
+ return prot.readI64(i64);
+ }
+ case T_DOUBLE: {
+ double dub;
+ return prot.readDouble(dub);
+ }
+ case T_STRING: {
+ std::string str;
+ return prot.readBinary(str);
+ }
+ case T_STRUCT: {
+ uint32_t result = 0;
+ std::string name;
+ int16_t fid;
+ TType ftype;
+ result += prot.readStructBegin(name);
+ while (true) {
+ result += prot.readFieldBegin(name, ftype, fid);
+ if (ftype == T_STOP) {
+ break;
+ }
+ result += skip(prot, ftype);
+ result += prot.readFieldEnd();
+ }
+ result += prot.readStructEnd();
+ return result;
+ }
+ case T_MAP: {
+ uint32_t result = 0;
+ TType keyType;
+ TType valType;
+ uint32_t i, size;
+ result += prot.readMapBegin(keyType, valType, size);
+ for (i = 0; i < size; i++) {
+ result += skip(prot, keyType);
+ result += skip(prot, valType);
+ }
+ result += prot.readMapEnd();
+ return result;
+ }
+ case T_SET: {
+ uint32_t result = 0;
+ TType elemType;
+ uint32_t i, size;
+ result += prot.readSetBegin(elemType, size);
+ for (i = 0; i < size; i++) {
+ result += skip(prot, elemType);
+ }
+ result += prot.readSetEnd();
+ return result;
+ }
+ case T_LIST: {
+ uint32_t result = 0;
+ TType elemType;
+ uint32_t i, size;
+ result += prot.readListBegin(elemType, size);
+ for (i = 0; i < size; i++) {
+ result += skip(prot, elemType);
+ }
+ result += prot.readListEnd();
+ return result;
+ }
+ default:
+ break;
+ }
+
+ throw TProtocolException(TProtocolException::INVALID_DATA,
+ "invalid TType");
+}
+
+}}} // apache::thrift::protocol
+
+#endif // #define _THRIFT_PROTOCOL_TPROTOCOL_H_ 1
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocolDecorator.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocolDecorator.h
new file mode 100644
index 000000000..5258159f1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocolDecorator.h
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef THRIFT_TPROTOCOLDECORATOR_H_
+#define THRIFT_TPROTOCOLDECORATOR_H_ 1
+
+#include <thrift/protocol/TProtocol.h>
+#include <memory>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+using std::shared_ptr;
+
+/**
+ * <code>TProtocolDecorator</code> forwards all requests to an enclosed
+ * <code>TProtocol</code> instance, providing a way to author concise
+ * concrete decorator subclasses.
+ *
+ * <p>See p.175 of Design Patterns (by Gamma et al.)</p>
+ *
+ * @see apache::thrift::protocol::TMultiplexedProtocol
+ */
+class TProtocolDecorator : public TProtocol {
+public:
+ ~TProtocolDecorator() override = default;
+
+ // Desc: Initializes the protocol decorator object.
+ TProtocolDecorator(shared_ptr<TProtocol> proto)
+ : TProtocol(proto->getTransport()), protocol(proto) {}
+
+ uint32_t writeMessageBegin_virt(const std::string& name,
+ const TMessageType messageType,
+ const int32_t seqid) override {
+ return protocol->writeMessageBegin(name, messageType, seqid);
+ }
+ uint32_t writeMessageEnd_virt() override { return protocol->writeMessageEnd(); }
+ uint32_t writeStructBegin_virt(const char* name) override {
+ return protocol->writeStructBegin(name);
+ }
+ uint32_t writeStructEnd_virt() override { return protocol->writeStructEnd(); }
+
+ uint32_t writeFieldBegin_virt(const char* name,
+ const TType fieldType,
+ const int16_t fieldId) override {
+ return protocol->writeFieldBegin(name, fieldType, fieldId);
+ }
+
+ uint32_t writeFieldEnd_virt() override { return protocol->writeFieldEnd(); }
+ uint32_t writeFieldStop_virt() override { return protocol->writeFieldStop(); }
+
+ uint32_t writeMapBegin_virt(const TType keyType,
+ const TType valType,
+ const uint32_t size) override {
+ return protocol->writeMapBegin(keyType, valType, size);
+ }
+
+ uint32_t writeMapEnd_virt() override { return protocol->writeMapEnd(); }
+
+ uint32_t writeListBegin_virt(const TType elemType, const uint32_t size) override {
+ return protocol->writeListBegin(elemType, size);
+ }
+ uint32_t writeListEnd_virt() override { return protocol->writeListEnd(); }
+
+ uint32_t writeSetBegin_virt(const TType elemType, const uint32_t size) override {
+ return protocol->writeSetBegin(elemType, size);
+ }
+ uint32_t writeSetEnd_virt() override { return protocol->writeSetEnd(); }
+
+ uint32_t writeBool_virt(const bool value) override { return protocol->writeBool(value); }
+ uint32_t writeByte_virt(const int8_t byte) override { return protocol->writeByte(byte); }
+ uint32_t writeI16_virt(const int16_t i16) override { return protocol->writeI16(i16); }
+ uint32_t writeI32_virt(const int32_t i32) override { return protocol->writeI32(i32); }
+ uint32_t writeI64_virt(const int64_t i64) override { return protocol->writeI64(i64); }
+
+ uint32_t writeDouble_virt(const double dub) override { return protocol->writeDouble(dub); }
+ uint32_t writeString_virt(const std::string& str) override { return protocol->writeString(str); }
+ uint32_t writeBinary_virt(const std::string& str) override { return protocol->writeBinary(str); }
+
+ uint32_t readMessageBegin_virt(std::string& name,
+ TMessageType& messageType,
+ int32_t& seqid) override {
+ return protocol->readMessageBegin(name, messageType, seqid);
+ }
+ uint32_t readMessageEnd_virt() override { return protocol->readMessageEnd(); }
+
+ uint32_t readStructBegin_virt(std::string& name) override {
+ return protocol->readStructBegin(name);
+ }
+ uint32_t readStructEnd_virt() override { return protocol->readStructEnd(); }
+
+ uint32_t readFieldBegin_virt(std::string& name, TType& fieldType, int16_t& fieldId) override {
+ return protocol->readFieldBegin(name, fieldType, fieldId);
+ }
+ uint32_t readFieldEnd_virt() override { return protocol->readFieldEnd(); }
+
+ uint32_t readMapBegin_virt(TType& keyType, TType& valType, uint32_t& size) override {
+ return protocol->readMapBegin(keyType, valType, size);
+ }
+ uint32_t readMapEnd_virt() override { return protocol->readMapEnd(); }
+
+ uint32_t readListBegin_virt(TType& elemType, uint32_t& size) override {
+ return protocol->readListBegin(elemType, size);
+ }
+ uint32_t readListEnd_virt() override { return protocol->readListEnd(); }
+
+ uint32_t readSetBegin_virt(TType& elemType, uint32_t& size) override {
+ return protocol->readSetBegin(elemType, size);
+ }
+ uint32_t readSetEnd_virt() override { return protocol->readSetEnd(); }
+
+ uint32_t readBool_virt(bool& value) override { return protocol->readBool(value); }
+ uint32_t readBool_virt(std::vector<bool>::reference value) override {
+ return protocol->readBool(value);
+ }
+
+ uint32_t readByte_virt(int8_t& byte) override { return protocol->readByte(byte); }
+
+ uint32_t readI16_virt(int16_t& i16) override { return protocol->readI16(i16); }
+ uint32_t readI32_virt(int32_t& i32) override { return protocol->readI32(i32); }
+ uint32_t readI64_virt(int64_t& i64) override { return protocol->readI64(i64); }
+
+ uint32_t readDouble_virt(double& dub) override { return protocol->readDouble(dub); }
+
+ uint32_t readString_virt(std::string& str) override { return protocol->readString(str); }
+ uint32_t readBinary_virt(std::string& str) override { return protocol->readBinary(str); }
+
+private:
+ shared_ptr<TProtocol> protocol;
+};
+}
+}
+}
+
+#endif // THRIFT_TPROTOCOLDECORATOR_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocolException.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocolException.h
new file mode 100644
index 000000000..4ace9046e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocolException.h
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_PROTOCOL_TPROTOCOLEXCEPTION_H_
+#define _THRIFT_PROTOCOL_TPROTOCOLEXCEPTION_H_ 1
+
+#include <string>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+/**
+ * Class to encapsulate all the possible types of protocol errors that may
+ * occur in various protocol systems. This provides a sort of generic
+ * wrapper around the vague UNIX E_ error codes that lets a common code
+ * base of error handling to be used for various types of protocols, i.e.
+ * pipes etc.
+ *
+ */
+class TProtocolException : public apache::thrift::TException {
+public:
+ /**
+ * Error codes for the various types of exceptions.
+ */
+ enum TProtocolExceptionType {
+ UNKNOWN = 0,
+ INVALID_DATA = 1,
+ NEGATIVE_SIZE = 2,
+ SIZE_LIMIT = 3,
+ BAD_VERSION = 4,
+ NOT_IMPLEMENTED = 5,
+ DEPTH_LIMIT = 6
+ };
+
+ TProtocolException() : apache::thrift::TException(), type_(UNKNOWN) {}
+
+ TProtocolException(TProtocolExceptionType type) : apache::thrift::TException(), type_(type) {}
+
+ TProtocolException(const std::string& message)
+ : apache::thrift::TException(message), type_(UNKNOWN) {}
+
+ TProtocolException(TProtocolExceptionType type, const std::string& message)
+ : apache::thrift::TException(message), type_(type) {}
+
+ ~TProtocolException() noexcept override = default;
+
+ /**
+ * Returns an error code that provides information about the type of error
+ * that has occurred.
+ *
+ * @return Error code
+ */
+ TProtocolExceptionType getType() const { return type_; }
+
+ const char* what() const noexcept override {
+ if (message_.empty()) {
+ switch (type_) {
+ case UNKNOWN:
+ return "TProtocolException: Unknown protocol exception";
+ case INVALID_DATA:
+ return "TProtocolException: Invalid data";
+ case NEGATIVE_SIZE:
+ return "TProtocolException: Negative size";
+ case SIZE_LIMIT:
+ return "TProtocolException: Exceeded size limit";
+ case BAD_VERSION:
+ return "TProtocolException: Invalid version";
+ case NOT_IMPLEMENTED:
+ return "TProtocolException: Not implemented";
+ case DEPTH_LIMIT:
+ return "TProtocolException: Exceeded depth limit";
+ default:
+ return "TProtocolException: (Invalid exception type)";
+ }
+ } else {
+ return message_.c_str();
+ }
+ }
+
+protected:
+ /**
+ * Error code
+ */
+ TProtocolExceptionType type_;
+};
+}
+}
+} // apache::thrift::protocol
+
+#endif // #ifndef _THRIFT_PROTOCOL_TPROTOCOLEXCEPTION_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocolTap.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocolTap.h
new file mode 100644
index 000000000..d000ba61a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocolTap.h
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_PROTOCOL_TPROTOCOLTAP_H_
+#define _THRIFT_PROTOCOL_TPROTOCOLTAP_H_ 1
+
+#include <thrift/protocol/TVirtualProtocol.h>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+using apache::thrift::transport::TTransport;
+
+/**
+ * Puts a wiretap on a protocol object. Any reads to this class are passed
+ * through to an enclosed protocol object, but also mirrored as write to a
+ * second protocol object.
+ *
+ */
+class TProtocolTap : public TVirtualProtocol<TProtocolTap> {
+public:
+ TProtocolTap(std::shared_ptr<TProtocol> source, std::shared_ptr<TProtocol> sink)
+ : TVirtualProtocol<TProtocolTap>(source->getTransport()), source_(source), sink_(sink) {}
+
+ uint32_t readMessageBegin(std::string& name, TMessageType& messageType, int32_t& seqid) {
+ uint32_t rv = source_->readMessageBegin(name, messageType, seqid);
+ sink_->writeMessageBegin(name, messageType, seqid);
+ return rv;
+ }
+
+ uint32_t readMessageEnd() {
+ uint32_t rv = source_->readMessageEnd();
+ sink_->writeMessageEnd();
+ return rv;
+ }
+
+ uint32_t readStructBegin(std::string& name) {
+ uint32_t rv = source_->readStructBegin(name);
+ sink_->writeStructBegin(name.c_str());
+ return rv;
+ }
+
+ uint32_t readStructEnd() {
+ uint32_t rv = source_->readStructEnd();
+ sink_->writeStructEnd();
+ return rv;
+ }
+
+ uint32_t readFieldBegin(std::string& name, TType& fieldType, int16_t& fieldId) {
+ uint32_t rv = source_->readFieldBegin(name, fieldType, fieldId);
+ if (fieldType == T_STOP) {
+ sink_->writeFieldStop();
+ } else {
+ sink_->writeFieldBegin(name.c_str(), fieldType, fieldId);
+ }
+ return rv;
+ }
+
+ uint32_t readFieldEnd() {
+ uint32_t rv = source_->readFieldEnd();
+ sink_->writeFieldEnd();
+ return rv;
+ }
+
+ uint32_t readMapBegin(TType& keyType, TType& valType, uint32_t& size) {
+ uint32_t rv = source_->readMapBegin(keyType, valType, size);
+ sink_->writeMapBegin(keyType, valType, size);
+ return rv;
+ }
+
+ uint32_t readMapEnd() {
+ uint32_t rv = source_->readMapEnd();
+ sink_->writeMapEnd();
+ return rv;
+ }
+
+ uint32_t readListBegin(TType& elemType, uint32_t& size) {
+ uint32_t rv = source_->readListBegin(elemType, size);
+ sink_->writeListBegin(elemType, size);
+ return rv;
+ }
+
+ uint32_t readListEnd() {
+ uint32_t rv = source_->readListEnd();
+ sink_->writeListEnd();
+ return rv;
+ }
+
+ uint32_t readSetBegin(TType& elemType, uint32_t& size) {
+ uint32_t rv = source_->readSetBegin(elemType, size);
+ sink_->writeSetBegin(elemType, size);
+ return rv;
+ }
+
+ uint32_t readSetEnd() {
+ uint32_t rv = source_->readSetEnd();
+ sink_->writeSetEnd();
+ return rv;
+ }
+
+ uint32_t readBool(bool& value) {
+ uint32_t rv = source_->readBool(value);
+ sink_->writeBool(value);
+ return rv;
+ }
+
+ // Provide the default readBool() implementation for std::vector<bool>
+ using TVirtualProtocol<TProtocolTap>::readBool;
+
+ uint32_t readByte(int8_t& byte) {
+ uint32_t rv = source_->readByte(byte);
+ sink_->writeByte(byte);
+ return rv;
+ }
+
+ uint32_t readI16(int16_t& i16) {
+ uint32_t rv = source_->readI16(i16);
+ sink_->writeI16(i16);
+ return rv;
+ }
+
+ uint32_t readI32(int32_t& i32) {
+ uint32_t rv = source_->readI32(i32);
+ sink_->writeI32(i32);
+ return rv;
+ }
+
+ uint32_t readI64(int64_t& i64) {
+ uint32_t rv = source_->readI64(i64);
+ sink_->writeI64(i64);
+ return rv;
+ }
+
+ uint32_t readDouble(double& dub) {
+ uint32_t rv = source_->readDouble(dub);
+ sink_->writeDouble(dub);
+ return rv;
+ }
+
+ uint32_t readString(std::string& str) {
+ uint32_t rv = source_->readString(str);
+ sink_->writeString(str);
+ return rv;
+ }
+
+ uint32_t readBinary(std::string& str) {
+ uint32_t rv = source_->readBinary(str);
+ sink_->writeBinary(str);
+ return rv;
+ }
+
+private:
+ std::shared_ptr<TProtocol> source_;
+ std::shared_ptr<TProtocol> sink_;
+};
+}
+}
+} // apache::thrift::protocol
+
+#endif // #define _THRIFT_PROTOCOL_TPROTOCOLTAP_H_ 1
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocolTypes.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocolTypes.h
new file mode 100644
index 000000000..6898b24eb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TProtocolTypes.h
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef THRIFT_PROTOCOL_TPROTOCOLTYPES_H_
+#define THRIFT_PROTOCOL_TPROTOCOLTYPES_H_ 1
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+enum PROTOCOL_TYPES {
+ T_BINARY_PROTOCOL = 0,
+ T_JSON_PROTOCOL = 1,
+ T_COMPACT_PROTOCOL = 2,
+};
+}
+}
+} // apache::thrift::protocol
+
+#endif // #define _THRIFT_PROTOCOL_TPROTOCOLTYPES_H_ 1
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TVirtualProtocol.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TVirtualProtocol.h
new file mode 100644
index 000000000..b7fe929af
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/protocol/TVirtualProtocol.h
@@ -0,0 +1,513 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_PROTOCOL_TVIRTUALPROTOCOL_H_
+#define _THRIFT_PROTOCOL_TVIRTUALPROTOCOL_H_ 1
+
+#include <thrift/protocol/TProtocol.h>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+using apache::thrift::transport::TTransport;
+
+/**
+ * Helper class that provides default implementations of TProtocol methods.
+ *
+ * This class provides default implementations of the non-virtual TProtocol
+ * methods. It exists primarily so TVirtualProtocol can derive from it. It
+ * prevents TVirtualProtocol methods from causing infinite recursion if the
+ * non-virtual methods are not overridden by the TVirtualProtocol subclass.
+ *
+ * You probably don't want to use this class directly. Use TVirtualProtocol
+ * instead.
+ */
+class TProtocolDefaults : public TProtocol {
+public:
+ uint32_t readMessageBegin(std::string& name, TMessageType& messageType, int32_t& seqid) {
+ (void)name;
+ (void)messageType;
+ (void)seqid;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readMessageEnd() {
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readStructBegin(std::string& name) {
+ (void)name;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readStructEnd() {
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readFieldBegin(std::string& name, TType& fieldType, int16_t& fieldId) {
+ (void)name;
+ (void)fieldType;
+ (void)fieldId;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readFieldEnd() {
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readMapBegin(TType& keyType, TType& valType, uint32_t& size) {
+ (void)keyType;
+ (void)valType;
+ (void)size;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readMapEnd() {
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readListBegin(TType& elemType, uint32_t& size) {
+ (void)elemType;
+ (void)size;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readListEnd() {
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readSetBegin(TType& elemType, uint32_t& size) {
+ (void)elemType;
+ (void)size;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readSetEnd() {
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readBool(bool& value) {
+ (void)value;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readBool(std::vector<bool>::reference value) {
+ (void)value;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readByte(int8_t& byte) {
+ (void)byte;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readI16(int16_t& i16) {
+ (void)i16;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readI32(int32_t& i32) {
+ (void)i32;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readI64(int64_t& i64) {
+ (void)i64;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readDouble(double& dub) {
+ (void)dub;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readString(std::string& str) {
+ (void)str;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t readBinary(std::string& str) {
+ (void)str;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support reading (yet).");
+ }
+
+ uint32_t writeMessageBegin(const std::string& name,
+ const TMessageType messageType,
+ const int32_t seqid) {
+ (void)name;
+ (void)messageType;
+ (void)seqid;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeMessageEnd() {
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeStructBegin(const char* name) {
+ (void)name;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeStructEnd() {
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeFieldBegin(const char* name, const TType fieldType, const int16_t fieldId) {
+ (void)name;
+ (void)fieldType;
+ (void)fieldId;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeFieldEnd() {
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeFieldStop() {
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeMapBegin(const TType keyType, const TType valType, const uint32_t size) {
+ (void)keyType;
+ (void)valType;
+ (void)size;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeMapEnd() {
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeListBegin(const TType elemType, const uint32_t size) {
+ (void)elemType;
+ (void)size;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeListEnd() {
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeSetBegin(const TType elemType, const uint32_t size) {
+ (void)elemType;
+ (void)size;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeSetEnd() {
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeBool(const bool value) {
+ (void)value;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeByte(const int8_t byte) {
+ (void)byte;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeI16(const int16_t i16) {
+ (void)i16;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeI32(const int32_t i32) {
+ (void)i32;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeI64(const int64_t i64) {
+ (void)i64;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeDouble(const double dub) {
+ (void)dub;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeString(const std::string& str) {
+ (void)str;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t writeBinary(const std::string& str) {
+ (void)str;
+ throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+ "this protocol does not support writing (yet).");
+ }
+
+ uint32_t skip(TType type) { return ::apache::thrift::protocol::skip(*this, type); }
+
+protected:
+ TProtocolDefaults(std::shared_ptr<TTransport> ptrans) : TProtocol(ptrans) {}
+};
+
+/**
+ * Concrete TProtocol classes should inherit from TVirtualProtocol
+ * so they don't have to manually override virtual methods.
+ */
+template <class Protocol_, class Super_ = TProtocolDefaults>
+class TVirtualProtocol : public Super_ {
+public:
+ /**
+ * Writing functions.
+ */
+
+ uint32_t writeMessageBegin_virt(const std::string& name,
+ const TMessageType messageType,
+ const int32_t seqid) override {
+ return static_cast<Protocol_*>(this)->writeMessageBegin(name, messageType, seqid);
+ }
+
+ uint32_t writeMessageEnd_virt() override {
+ return static_cast<Protocol_*>(this)->writeMessageEnd();
+ }
+
+ uint32_t writeStructBegin_virt(const char* name) override {
+ return static_cast<Protocol_*>(this)->writeStructBegin(name);
+ }
+
+ uint32_t writeStructEnd_virt() override { return static_cast<Protocol_*>(this)->writeStructEnd(); }
+
+ uint32_t writeFieldBegin_virt(const char* name,
+ const TType fieldType,
+ const int16_t fieldId) override {
+ return static_cast<Protocol_*>(this)->writeFieldBegin(name, fieldType, fieldId);
+ }
+
+ uint32_t writeFieldEnd_virt() override { return static_cast<Protocol_*>(this)->writeFieldEnd(); }
+
+ uint32_t writeFieldStop_virt() override { return static_cast<Protocol_*>(this)->writeFieldStop(); }
+
+ uint32_t writeMapBegin_virt(const TType keyType,
+ const TType valType,
+ const uint32_t size) override {
+ return static_cast<Protocol_*>(this)->writeMapBegin(keyType, valType, size);
+ }
+
+ uint32_t writeMapEnd_virt() override { return static_cast<Protocol_*>(this)->writeMapEnd(); }
+
+ uint32_t writeListBegin_virt(const TType elemType, const uint32_t size) override {
+ return static_cast<Protocol_*>(this)->writeListBegin(elemType, size);
+ }
+
+ uint32_t writeListEnd_virt() override { return static_cast<Protocol_*>(this)->writeListEnd(); }
+
+ uint32_t writeSetBegin_virt(const TType elemType, const uint32_t size) override {
+ return static_cast<Protocol_*>(this)->writeSetBegin(elemType, size);
+ }
+
+ uint32_t writeSetEnd_virt() override { return static_cast<Protocol_*>(this)->writeSetEnd(); }
+
+ uint32_t writeBool_virt(const bool value) override {
+ return static_cast<Protocol_*>(this)->writeBool(value);
+ }
+
+ uint32_t writeByte_virt(const int8_t byte) override {
+ return static_cast<Protocol_*>(this)->writeByte(byte);
+ }
+
+ uint32_t writeI16_virt(const int16_t i16) override {
+ return static_cast<Protocol_*>(this)->writeI16(i16);
+ }
+
+ uint32_t writeI32_virt(const int32_t i32) override {
+ return static_cast<Protocol_*>(this)->writeI32(i32);
+ }
+
+ uint32_t writeI64_virt(const int64_t i64) override {
+ return static_cast<Protocol_*>(this)->writeI64(i64);
+ }
+
+ uint32_t writeDouble_virt(const double dub) override {
+ return static_cast<Protocol_*>(this)->writeDouble(dub);
+ }
+
+ uint32_t writeString_virt(const std::string& str) override {
+ return static_cast<Protocol_*>(this)->writeString(str);
+ }
+
+ uint32_t writeBinary_virt(const std::string& str) override {
+ return static_cast<Protocol_*>(this)->writeBinary(str);
+ }
+
+ /**
+ * Reading functions
+ */
+
+ uint32_t readMessageBegin_virt(std::string& name,
+ TMessageType& messageType,
+ int32_t& seqid) override {
+ return static_cast<Protocol_*>(this)->readMessageBegin(name, messageType, seqid);
+ }
+
+ uint32_t readMessageEnd_virt() override { return static_cast<Protocol_*>(this)->readMessageEnd(); }
+
+ uint32_t readStructBegin_virt(std::string& name) override {
+ return static_cast<Protocol_*>(this)->readStructBegin(name);
+ }
+
+ uint32_t readStructEnd_virt() override { return static_cast<Protocol_*>(this)->readStructEnd(); }
+
+ uint32_t readFieldBegin_virt(std::string& name, TType& fieldType, int16_t& fieldId) override {
+ return static_cast<Protocol_*>(this)->readFieldBegin(name, fieldType, fieldId);
+ }
+
+ uint32_t readFieldEnd_virt() override { return static_cast<Protocol_*>(this)->readFieldEnd(); }
+
+ uint32_t readMapBegin_virt(TType& keyType, TType& valType, uint32_t& size) override {
+ return static_cast<Protocol_*>(this)->readMapBegin(keyType, valType, size);
+ }
+
+ uint32_t readMapEnd_virt() override { return static_cast<Protocol_*>(this)->readMapEnd(); }
+
+ uint32_t readListBegin_virt(TType& elemType, uint32_t& size) override {
+ return static_cast<Protocol_*>(this)->readListBegin(elemType, size);
+ }
+
+ uint32_t readListEnd_virt() override { return static_cast<Protocol_*>(this)->readListEnd(); }
+
+ uint32_t readSetBegin_virt(TType& elemType, uint32_t& size) override {
+ return static_cast<Protocol_*>(this)->readSetBegin(elemType, size);
+ }
+
+ uint32_t readSetEnd_virt() override { return static_cast<Protocol_*>(this)->readSetEnd(); }
+
+ uint32_t readBool_virt(bool& value) override {
+ return static_cast<Protocol_*>(this)->readBool(value);
+ }
+
+ uint32_t readBool_virt(std::vector<bool>::reference value) override {
+ return static_cast<Protocol_*>(this)->readBool(value);
+ }
+
+ uint32_t readByte_virt(int8_t& byte) override {
+ return static_cast<Protocol_*>(this)->readByte(byte);
+ }
+
+ uint32_t readI16_virt(int16_t& i16) override {
+ return static_cast<Protocol_*>(this)->readI16(i16);
+ }
+
+ uint32_t readI32_virt(int32_t& i32) override {
+ return static_cast<Protocol_*>(this)->readI32(i32);
+ }
+
+ uint32_t readI64_virt(int64_t& i64) override {
+ return static_cast<Protocol_*>(this)->readI64(i64);
+ }
+
+ uint32_t readDouble_virt(double& dub) override {
+ return static_cast<Protocol_*>(this)->readDouble(dub);
+ }
+
+ uint32_t readString_virt(std::string& str) override {
+ return static_cast<Protocol_*>(this)->readString(str);
+ }
+
+ uint32_t readBinary_virt(std::string& str) override {
+ return static_cast<Protocol_*>(this)->readBinary(str);
+ }
+
+ uint32_t skip_virt(TType type) override { return static_cast<Protocol_*>(this)->skip(type); }
+
+ /*
+ * Provide a default skip() implementation that uses non-virtual read
+ * methods.
+ *
+ * Note: subclasses that use TVirtualProtocol to derive from another protocol
+ * implementation (i.e., not TProtocolDefaults) should beware that this may
+ * override any non-default skip() implementation provided by the parent
+ * transport class. They may need to explicitly redefine skip() to call the
+ * correct parent implementation, if desired.
+ */
+ uint32_t skip(TType type) {
+ auto* const prot = static_cast<Protocol_*>(this);
+ return ::apache::thrift::protocol::skip(*prot, type);
+ }
+
+ /*
+ * Provide a default readBool() implementation for use with
+ * std::vector<bool>, that behaves the same as reading into a normal bool.
+ *
+ * Subclasses can override this if desired, but there normally shouldn't
+ * be a need to.
+ */
+ uint32_t readBool(std::vector<bool>::reference value) {
+ bool b = false;
+ uint32_t ret = static_cast<Protocol_*>(this)->readBool(b);
+ value = b;
+ return ret;
+ }
+ using Super_::readBool; // so we don't hide readBool(bool&)
+
+protected:
+ TVirtualProtocol(std::shared_ptr<TTransport> ptrans) : Super_(ptrans) {}
+};
+}
+}
+} // apache::thrift::protocol
+
+#endif // #define _THRIFT_PROTOCOL_TVIRTUALPROTOCOL_H_ 1
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/qt/CMakeLists.txt b/src/jaegertracing/thrift/lib/cpp/src/thrift/qt/CMakeLists.txt
new file mode 100644
index 000000000..04a9a316e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/qt/CMakeLists.txt
@@ -0,0 +1,28 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+set( thriftcppqt5_SOURCES
+ TQIODeviceTransport.cpp
+ TQTcpServer.cpp
+)
+set(CMAKE_AUTOMOC ON)
+find_package(Qt5 REQUIRED COMPONENTS Core Network)
+ADD_LIBRARY_THRIFT(thriftqt5 ${thriftcppqt5_SOURCES})
+TARGET_LINK_LIBRARIES_THRIFT(thriftqt5 Qt5::Core Qt5::Network)
+TARGET_LINK_LIBRARIES_THRIFT_AGAINST_THRIFT_LIBRARY(thriftqt5 thrift)
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/qt/TQIODeviceTransport.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/qt/TQIODeviceTransport.cpp
new file mode 100644
index 000000000..1537fc60d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/qt/TQIODeviceTransport.cpp
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/qt/TQIODeviceTransport.h>
+
+#include <QAbstractSocket>
+#include <QIODevice>
+
+#include <thrift/transport/TBufferTransports.h>
+#include <memory>
+
+namespace apache {
+namespace thrift {
+
+using std::shared_ptr;
+
+namespace transport {
+
+TQIODeviceTransport::TQIODeviceTransport(shared_ptr<QIODevice> dev) : dev_(dev) {
+}
+
+TQIODeviceTransport::~TQIODeviceTransport() {
+ dev_->close();
+}
+
+void TQIODeviceTransport::open() {
+ if (!isOpen()) {
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "open(): underlying QIODevice isn't open");
+ }
+}
+
+bool TQIODeviceTransport::isOpen() const {
+ return dev_->isOpen();
+}
+
+bool TQIODeviceTransport::peek() {
+ return dev_->bytesAvailable() > 0;
+}
+
+void TQIODeviceTransport::close() {
+ dev_->close();
+}
+
+uint32_t TQIODeviceTransport::readAll(uint8_t* buf, uint32_t len) {
+ uint32_t requestLen = len;
+ while (len) {
+ uint32_t readSize;
+ try {
+ readSize = read(buf, len);
+ } catch (...) {
+ if (len != requestLen) {
+ // something read already
+ return requestLen - len;
+ }
+ // error but nothing read yet
+ throw;
+ }
+ if (readSize == 0) {
+ dev_->waitForReadyRead(50);
+ } else {
+ buf += readSize;
+ len -= readSize;
+ }
+ }
+ return requestLen;
+}
+
+uint32_t TQIODeviceTransport::read(uint8_t* buf, uint32_t len) {
+ uint32_t actualSize;
+ qint64 readSize;
+
+ if (!dev_->isOpen()) {
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "read(): underlying QIODevice is not open");
+ }
+
+ actualSize = (uint32_t)(std::min)((qint64)len, dev_->bytesAvailable());
+ readSize = dev_->read(reinterpret_cast<char*>(buf), actualSize);
+
+ if (readSize < 0) {
+ QAbstractSocket* socket;
+ if ((socket = qobject_cast<QAbstractSocket*>(dev_.get()))) {
+ throw TTransportException(TTransportException::UNKNOWN,
+ "Failed to read() from QAbstractSocket",
+ socket->error());
+ }
+ throw TTransportException(TTransportException::UNKNOWN, "Failed to read from from QIODevice");
+ }
+
+ return (uint32_t)readSize;
+}
+
+void TQIODeviceTransport::write(const uint8_t* buf, uint32_t len) {
+ while (len) {
+ uint32_t written = write_partial(buf, len);
+ len -= written;
+ dev_->waitForBytesWritten(50);
+ }
+}
+
+uint32_t TQIODeviceTransport::write_partial(const uint8_t* buf, uint32_t len) {
+ qint64 written;
+
+ if (!dev_->isOpen()) {
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "write_partial(): underlying QIODevice is not open");
+ }
+
+ written = dev_->write(reinterpret_cast<const char*>(buf), len);
+ if (written < 0) {
+ QAbstractSocket* socket;
+ if ((socket = qobject_cast<QAbstractSocket*>(dev_.get()))) {
+ throw TTransportException(TTransportException::UNKNOWN,
+ "write_partial(): failed to write to QAbstractSocket",
+ socket->error());
+ }
+
+ throw TTransportException(TTransportException::UNKNOWN,
+ "write_partial(): failed to write to underlying QIODevice");
+ }
+
+ return (uint32_t)written;
+}
+
+void TQIODeviceTransport::flush() {
+ if (!dev_->isOpen()) {
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "flush(): underlying QIODevice is not open");
+ }
+
+ QAbstractSocket* socket;
+
+ if ((socket = qobject_cast<QAbstractSocket*>(dev_.get()))) {
+ socket->flush();
+ } else {
+ dev_->waitForBytesWritten(1);
+ }
+}
+
+uint8_t* TQIODeviceTransport::borrow(uint8_t* buf, uint32_t* len) {
+ (void)buf;
+ (void)len;
+ return nullptr;
+}
+
+void TQIODeviceTransport::consume(uint32_t len) {
+ (void)len;
+ throw TTransportException(TTransportException::UNKNOWN);
+}
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/qt/TQIODeviceTransport.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/qt/TQIODeviceTransport.h
new file mode 100644
index 000000000..a3b511def
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/qt/TQIODeviceTransport.h
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_ASYNC_TQIODEVICE_TRANSPORT_H_
+#define _THRIFT_ASYNC_TQIODEVICE_TRANSPORT_H_ 1
+
+#include <memory>
+
+#include <thrift/transport/TVirtualTransport.h>
+
+class QIODevice;
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * Transport that operates on a QIODevice (socket, file, etc).
+ */
+class TQIODeviceTransport
+ : public apache::thrift::transport::TVirtualTransport<TQIODeviceTransport> {
+public:
+ explicit TQIODeviceTransport(std::shared_ptr<QIODevice> dev);
+ ~TQIODeviceTransport() override;
+
+ void open() override;
+ bool isOpen() const override;
+ bool peek() override;
+ void close() override;
+
+ uint32_t readAll(uint8_t* buf, uint32_t len);
+ uint32_t read(uint8_t* buf, uint32_t len);
+
+ void write(const uint8_t* buf, uint32_t len);
+ uint32_t write_partial(const uint8_t* buf, uint32_t len);
+
+ void flush() override;
+
+ uint8_t* borrow(uint8_t* buf, uint32_t* len);
+ void consume(uint32_t len);
+
+private:
+ TQIODeviceTransport(const TQIODeviceTransport&);
+ TQIODeviceTransport& operator=(const TQIODeviceTransport&);
+
+ std::shared_ptr<QIODevice> dev_;
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_ASYNC_TQIODEVICE_TRANSPORT_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/qt/TQTcpServer.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/qt/TQTcpServer.cpp
new file mode 100644
index 000000000..04044823e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/qt/TQTcpServer.cpp
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <functional>
+#include <memory>
+
+#include <thrift/qt/TQTcpServer.h>
+#include <thrift/qt/TQIODeviceTransport.h>
+
+#include <QMetaType>
+#include <QTcpSocket>
+
+#include <thrift/protocol/TProtocol.h>
+#include <thrift/async/TAsyncProcessor.h>
+
+using apache::thrift::protocol::TProtocol;
+using apache::thrift::protocol::TProtocolFactory;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using apache::thrift::transport::TQIODeviceTransport;
+using std::bind;
+using std::function;
+using std::placeholders::_1;
+using std::shared_ptr;
+
+QT_USE_NAMESPACE
+
+namespace apache {
+namespace thrift {
+namespace async {
+
+struct TQTcpServer::ConnectionContext {
+ shared_ptr<QTcpSocket> connection_;
+ shared_ptr<TTransport> transport_;
+ shared_ptr<TProtocol> iprot_;
+ shared_ptr<TProtocol> oprot_;
+
+ explicit ConnectionContext(shared_ptr<QTcpSocket> connection,
+ shared_ptr<TTransport> transport,
+ shared_ptr<TProtocol> iprot,
+ shared_ptr<TProtocol> oprot)
+ : connection_(connection), transport_(transport), iprot_(iprot), oprot_(oprot) {}
+};
+
+TQTcpServer::TQTcpServer(shared_ptr<QTcpServer> server,
+ shared_ptr<TAsyncProcessor> processor,
+ shared_ptr<TProtocolFactory> pfact,
+ QObject* parent)
+ : QObject(parent), server_(server), processor_(processor), pfact_(pfact) {
+ qRegisterMetaType<QTcpSocket*>("QTcpSocket*");
+ connect(server.get(), SIGNAL(newConnection()), SLOT(processIncoming()));
+}
+
+TQTcpServer::~TQTcpServer() = default;
+
+void TQTcpServer::processIncoming() {
+ while (server_->hasPendingConnections()) {
+ // take ownership of the QTcpSocket; technically it could be deleted
+ // when the QTcpServer is destroyed, but any real app should delete this
+ // class before deleting the QTcpServer that we are using
+ shared_ptr<QTcpSocket> connection(server_->nextPendingConnection());
+
+ shared_ptr<TTransport> transport;
+ shared_ptr<TProtocol> iprot;
+ shared_ptr<TProtocol> oprot;
+
+ try {
+ transport = shared_ptr<TTransport>(new TQIODeviceTransport(connection));
+ iprot = shared_ptr<TProtocol>(pfact_->getProtocol(transport));
+ oprot = shared_ptr<TProtocol>(pfact_->getProtocol(transport));
+ } catch (...) {
+ qWarning("[TQTcpServer] Failed to initialize transports/protocols");
+ continue;
+ }
+
+ ctxMap_[connection.get()]
+ = std::make_shared<ConnectionContext>(connection, transport, iprot, oprot);
+
+ connect(connection.get(), SIGNAL(readyRead()), SLOT(beginDecode()));
+
+ connect(connection.get(), SIGNAL(disconnected()), SLOT(socketClosed()));
+ }
+}
+
+void TQTcpServer::beginDecode() {
+ auto* connection(qobject_cast<QTcpSocket*>(sender()));
+ Q_ASSERT(connection);
+
+ if (ctxMap_.find(connection) == ctxMap_.end()) {
+ qWarning("[TQTcpServer] Got data on an unknown QTcpSocket");
+ return;
+ }
+
+ shared_ptr<ConnectionContext> ctx = ctxMap_[connection];
+
+ try {
+ processor_
+ ->process(bind(&TQTcpServer::finish, this, ctx, _1),
+ ctx->iprot_,
+ ctx->oprot_);
+ } catch (const TTransportException& ex) {
+ qWarning("[TQTcpServer] TTransportException during processing: '%s'", ex.what());
+ scheduleDeleteConnectionContext(connection);
+ } catch (...) {
+ qWarning("[TQTcpServer] Unknown processor exception");
+ scheduleDeleteConnectionContext(connection);
+ }
+}
+
+void TQTcpServer::socketClosed() {
+ auto* connection(qobject_cast<QTcpSocket*>(sender()));
+ Q_ASSERT(connection);
+ scheduleDeleteConnectionContext(connection);
+}
+
+void TQTcpServer::deleteConnectionContext(QTcpSocket* connection) {
+ const ConnectionContextMap::size_type deleted = ctxMap_.erase(connection);
+ if (0 == deleted) {
+ qWarning("[TQTcpServer] Unknown QTcpSocket");
+ }
+}
+
+void TQTcpServer::scheduleDeleteConnectionContext(QTcpSocket* connection) {
+ QMetaObject::invokeMethod(this, "deleteConnectionContext", Qt::QueuedConnection, Q_ARG(QTcpSocket*, connection));
+}
+
+void TQTcpServer::finish(shared_ptr<ConnectionContext> ctx, bool healthy) {
+ if (!healthy) {
+ qWarning("[TQTcpServer] Processor failed to process data successfully");
+ deleteConnectionContext(ctx->connection_.get());
+ }
+}
+}
+}
+} // apache::thrift::async
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/qt/TQTcpServer.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/qt/TQTcpServer.h
new file mode 100644
index 000000000..25994ab8d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/qt/TQTcpServer.h
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TASYNC_QTCP_SERVER_H_
+#define _THRIFT_TASYNC_QTCP_SERVER_H_
+
+#include <QObject>
+#include <QTcpServer>
+
+#include <memory>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+class TProtocolFactory;
+}
+}
+} // apache::thrift::protocol
+
+namespace apache {
+namespace thrift {
+namespace async {
+
+class TAsyncProcessor;
+
+/**
+ * Server that uses Qt to listen for connections.
+ * Simply give it a QTcpServer that is listening, along with an async
+ * processor and a protocol factory, and then run the Qt event loop.
+ */
+class TQTcpServer : public QObject {
+ Q_OBJECT
+public:
+ TQTcpServer(std::shared_ptr<QTcpServer> server,
+ std::shared_ptr<TAsyncProcessor> processor,
+ std::shared_ptr<apache::thrift::protocol::TProtocolFactory> protocolFactory,
+ QObject* parent = nullptr);
+ ~TQTcpServer() override;
+
+private Q_SLOTS:
+ void processIncoming();
+ void beginDecode();
+ void socketClosed();
+ void deleteConnectionContext(QTcpSocket* connection);
+
+private:
+ Q_DISABLE_COPY(TQTcpServer)
+
+ struct ConnectionContext;
+
+ void scheduleDeleteConnectionContext(QTcpSocket* connection);
+ void finish(std::shared_ptr<ConnectionContext> ctx, bool healthy);
+
+ std::shared_ptr<QTcpServer> server_;
+ std::shared_ptr<TAsyncProcessor> processor_;
+ std::shared_ptr<apache::thrift::protocol::TProtocolFactory> pfact_;
+
+ typedef std::map<QTcpSocket*, std::shared_ptr<ConnectionContext> > ConnectionContextMap;
+ ConnectionContextMap ctxMap_;
+};
+}
+}
+} // apache::thrift::async
+
+#endif // #ifndef _THRIFT_TASYNC_QTCP_SERVER_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TConnectedClient.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TConnectedClient.cpp
new file mode 100644
index 000000000..9a78e3e9b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TConnectedClient.cpp
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/server/TConnectedClient.h>
+
+namespace apache {
+namespace thrift {
+namespace server {
+
+using apache::thrift::TProcessor;
+using apache::thrift::protocol::TProtocol;
+using apache::thrift::server::TServerEventHandler;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using std::shared_ptr;
+using std::string;
+
+TConnectedClient::TConnectedClient(const shared_ptr<TProcessor>& processor,
+ const shared_ptr<TProtocol>& inputProtocol,
+ const shared_ptr<TProtocol>& outputProtocol,
+ const shared_ptr<TServerEventHandler>& eventHandler,
+ const shared_ptr<TTransport>& client)
+
+ : processor_(processor),
+ inputProtocol_(inputProtocol),
+ outputProtocol_(outputProtocol),
+ eventHandler_(eventHandler),
+ client_(client),
+ opaqueContext_(nullptr) {
+}
+
+TConnectedClient::~TConnectedClient() = default;
+
+void TConnectedClient::run() {
+ if (eventHandler_) {
+ opaqueContext_ = eventHandler_->createContext(inputProtocol_, outputProtocol_);
+ }
+
+ for (bool done = false; !done;) {
+ if (eventHandler_) {
+ eventHandler_->processContext(opaqueContext_, client_);
+ }
+
+ try {
+ if (!processor_->process(inputProtocol_, outputProtocol_, opaqueContext_)) {
+ break;
+ }
+ } catch (const TTransportException& ttx) {
+ switch (ttx.getType()) {
+ case TTransportException::END_OF_FILE:
+ case TTransportException::INTERRUPTED:
+ case TTransportException::TIMED_OUT:
+ // Client disconnected or was interrupted or did not respond within the receive timeout.
+ // No logging needed. Done.
+ done = true;
+ break;
+
+ default: {
+ // All other transport exceptions are logged.
+ // State of connection is unknown. Done.
+ string errStr = string("TConnectedClient died: ") + ttx.what();
+ GlobalOutput(errStr.c_str());
+ done = true;
+ break;
+ }
+ }
+ } catch (const TException& tex) {
+ string errStr = string("TConnectedClient processing exception: ") + tex.what();
+ GlobalOutput(errStr.c_str());
+ // Disconnect from client, because we could not process the message.
+ done = true;
+ }
+ }
+
+ cleanup();
+}
+
+void TConnectedClient::cleanup() {
+ if (eventHandler_) {
+ eventHandler_->deleteContext(opaqueContext_, inputProtocol_, outputProtocol_);
+ }
+
+ try {
+ inputProtocol_->getTransport()->close();
+ } catch (const TTransportException& ttx) {
+ string errStr = string("TConnectedClient input close failed: ") + ttx.what();
+ GlobalOutput(errStr.c_str());
+ }
+
+ try {
+ outputProtocol_->getTransport()->close();
+ } catch (const TTransportException& ttx) {
+ string errStr = string("TConnectedClient output close failed: ") + ttx.what();
+ GlobalOutput(errStr.c_str());
+ }
+
+ try {
+ client_->close();
+ } catch (const TTransportException& ttx) {
+ string errStr = string("TConnectedClient client close failed: ") + ttx.what();
+ GlobalOutput(errStr.c_str());
+ }
+}
+}
+}
+} // apache::thrift::server
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TConnectedClient.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TConnectedClient.h
new file mode 100644
index 000000000..071571a88
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TConnectedClient.h
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_SERVER_TCONNECTEDCLIENT_H_
+#define _THRIFT_SERVER_TCONNECTEDCLIENT_H_ 1
+
+#include <memory>
+#include <thrift/TProcessor.h>
+#include <thrift/protocol/TProtocol.h>
+#include <thrift/server/TServer.h>
+#include <thrift/transport/TTransport.h>
+
+namespace apache {
+namespace thrift {
+namespace server {
+
+/**
+ * This represents a client connected to a TServer. The
+ * processing loop for a client must provide some required
+ * functionality common to all implementations so it is
+ * encapsulated here.
+ */
+
+class TConnectedClient : public apache::thrift::concurrency::Runnable {
+public:
+ /**
+ * Constructor.
+ *
+ * @param[in] processor the TProcessor
+ * @param[in] inputProtocol the input TProtocol
+ * @param[in] outputProtocol the output TProtocol
+ * @param[in] eventHandler the server event handler
+ * @param[in] client the TTransport representing the client
+ */
+ TConnectedClient(
+ const std::shared_ptr<apache::thrift::TProcessor>& processor,
+ const std::shared_ptr<apache::thrift::protocol::TProtocol>& inputProtocol,
+ const std::shared_ptr<apache::thrift::protocol::TProtocol>& outputProtocol,
+ const std::shared_ptr<apache::thrift::server::TServerEventHandler>& eventHandler,
+ const std::shared_ptr<apache::thrift::transport::TTransport>& client);
+
+ /**
+ * Destructor.
+ */
+ ~TConnectedClient() override;
+
+ /**
+ * Drive the client until it is done.
+ * The client processing loop is:
+ *
+ * [optional] call eventHandler->createContext once
+ * [optional] call eventHandler->processContext per request
+ * call processor->process per request
+ * handle expected transport exceptions:
+ * END_OF_FILE means the client is gone
+ * INTERRUPTED means the client was interrupted
+ * by TServerTransport::interruptChildren()
+ * handle unexpected transport exceptions by logging
+ * handle standard exceptions by logging
+ * handle unexpected exceptions by logging
+ * cleanup()
+ */
+ void run() override /* override */;
+
+protected:
+ /**
+ * Cleanup after a client. This happens if the client disconnects,
+ * or if the server is stopped, or if an exception occurs.
+ *
+ * The cleanup processing is:
+ * [optional] call eventHandler->deleteContext once
+ * close the inputProtocol's TTransport
+ * close the outputProtocol's TTransport
+ * close the client
+ */
+ virtual void cleanup();
+
+private:
+ std::shared_ptr<apache::thrift::TProcessor> processor_;
+ std::shared_ptr<apache::thrift::protocol::TProtocol> inputProtocol_;
+ std::shared_ptr<apache::thrift::protocol::TProtocol> outputProtocol_;
+ std::shared_ptr<apache::thrift::server::TServerEventHandler> eventHandler_;
+ std::shared_ptr<apache::thrift::transport::TTransport> client_;
+
+ /**
+ * Context acquired from the eventHandler_ if one exists.
+ */
+ void* opaqueContext_;
+};
+}
+}
+}
+
+#endif // #ifndef _THRIFT_SERVER_TCONNECTEDCLIENT_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TNonblockingServer.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TNonblockingServer.cpp
new file mode 100644
index 000000000..eea0427d2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TNonblockingServer.cpp
@@ -0,0 +1,1518 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+
+#include <thrift/server/TNonblockingServer.h>
+#include <thrift/concurrency/Exception.h>
+#include <thrift/transport/TSocket.h>
+#include <thrift/concurrency/ThreadFactory.h>
+#include <thrift/transport/PlatformSocket.h>
+
+#include <algorithm>
+#include <iostream>
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#elif HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#elif HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <assert.h>
+
+#ifdef HAVE_SCHED_H
+#include <sched.h>
+#endif
+
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+namespace apache {
+namespace thrift {
+namespace server {
+
+using namespace apache::thrift::protocol;
+using namespace apache::thrift::transport;
+using namespace apache::thrift::concurrency;
+using apache::thrift::transport::TSocket;
+using apache::thrift::transport::TTransportException;
+using std::shared_ptr;
+
+/// Three states for sockets: recv frame size, recv data, and send mode
+enum TSocketState { SOCKET_RECV_FRAMING, SOCKET_RECV, SOCKET_SEND };
+
+/**
+ * Five states for the nonblocking server:
+ * 1) initialize
+ * 2) read 4 byte frame size
+ * 3) read frame of data
+ * 4) send back data (if any)
+ * 5) force immediate connection close
+ */
+enum TAppState {
+ APP_INIT,
+ APP_READ_FRAME_SIZE,
+ APP_READ_REQUEST,
+ APP_WAIT_TASK,
+ APP_SEND_RESULT,
+ APP_CLOSE_CONNECTION
+};
+
+/**
+ * Represents a connection that is handled via libevent. This connection
+ * essentially encapsulates a socket that has some associated libevent state.
+ */
+class TNonblockingServer::TConnection {
+private:
+ /// Server IO Thread handling this connection
+ TNonblockingIOThread* ioThread_;
+
+ /// Server handle
+ TNonblockingServer* server_;
+
+ /// TProcessor
+ std::shared_ptr<TProcessor> processor_;
+
+ /// Object wrapping network socket
+ std::shared_ptr<TSocket> tSocket_;
+
+ /// Libevent object
+ struct event event_;
+
+ /// Libevent flags
+ short eventFlags_;
+
+ /// Socket mode
+ TSocketState socketState_;
+
+ /// Application state
+ TAppState appState_;
+
+ /// How much data needed to read
+ uint32_t readWant_;
+
+ /// Where in the read buffer are we
+ uint32_t readBufferPos_;
+
+ /// Read buffer
+ uint8_t* readBuffer_;
+
+ /// Read buffer size
+ uint32_t readBufferSize_;
+
+ /// Write buffer
+ uint8_t* writeBuffer_;
+
+ /// Write buffer size
+ uint32_t writeBufferSize_;
+
+ /// How far through writing are we?
+ uint32_t writeBufferPos_;
+
+ /// Largest size of write buffer seen since buffer was constructed
+ size_t largestWriteBufferSize_;
+
+ /// Count of the number of calls for use with getResizeBufferEveryN().
+ int32_t callsForResize_;
+
+ /// Transport to read from
+ std::shared_ptr<TMemoryBuffer> inputTransport_;
+
+ /// Transport that processor writes to
+ std::shared_ptr<TMemoryBuffer> outputTransport_;
+
+ /// extra transport generated by transport factory (e.g. BufferedRouterTransport)
+ std::shared_ptr<TTransport> factoryInputTransport_;
+ std::shared_ptr<TTransport> factoryOutputTransport_;
+
+ /// Protocol decoder
+ std::shared_ptr<TProtocol> inputProtocol_;
+
+ /// Protocol encoder
+ std::shared_ptr<TProtocol> outputProtocol_;
+
+ /// Server event handler, if any
+ std::shared_ptr<TServerEventHandler> serverEventHandler_;
+
+ /// Thrift call context, if any
+ void* connectionContext_;
+
+ /// Go into read mode
+ void setRead() { setFlags(EV_READ | EV_PERSIST); }
+
+ /// Go into write mode
+ void setWrite() { setFlags(EV_WRITE | EV_PERSIST); }
+
+ /// Set socket idle
+ void setIdle() { setFlags(0); }
+
+ /**
+ * Set event flags for this connection.
+ *
+ * @param eventFlags flags we pass to libevent for the connection.
+ */
+ void setFlags(short eventFlags);
+
+ /**
+ * Libevent handler called (via our static wrapper) when the connection
+ * socket had something happen. Rather than use the flags libevent passed,
+ * we use the connection state to determine whether we need to read or
+ * write the socket.
+ */
+ void workSocket();
+
+public:
+ class Task;
+
+ /// Constructor
+ TConnection(std::shared_ptr<TSocket> socket,
+ TNonblockingIOThread* ioThread) {
+ readBuffer_ = nullptr;
+ readBufferSize_ = 0;
+
+ ioThread_ = ioThread;
+ server_ = ioThread->getServer();
+
+ // Allocate input and output transports these only need to be allocated
+ // once per TConnection (they don't need to be reallocated on init() call)
+ inputTransport_.reset(new TMemoryBuffer(readBuffer_, readBufferSize_));
+ outputTransport_.reset(
+ new TMemoryBuffer(static_cast<uint32_t>(server_->getWriteBufferDefaultSize())));
+
+ tSocket_ = socket;
+
+ init(ioThread);
+ }
+
+ ~TConnection() { std::free(readBuffer_); }
+
+ /// Close this connection and free or reset its resources.
+ void close();
+
+ /**
+ * Check buffers against any size limits and shrink it if exceeded.
+ *
+ * @param readLimit we reduce read buffer size to this (if nonzero).
+ * @param writeLimit if nonzero and write buffer is larger, replace it.
+ */
+ void checkIdleBufferMemLimit(size_t readLimit, size_t writeLimit);
+
+ /// Initialize
+ void init(TNonblockingIOThread* ioThread);
+
+ /// set socket for connection
+ void setSocket(std::shared_ptr<TSocket> socket);
+
+ /**
+ * This is called when the application transitions from one state into
+ * another. This means that it has finished writing the data that it needed
+ * to, or finished receiving the data that it needed to.
+ */
+ void transition();
+
+ /**
+ * C-callable event handler for connection events. Provides a callback
+ * that libevent can understand which invokes connection_->workSocket().
+ *
+ * @param fd the descriptor the event occurred on.
+ * @param which the flags associated with the event.
+ * @param v void* callback arg where we placed TConnection's "this".
+ */
+ static void eventHandler(evutil_socket_t fd, short /* which */, void* v) {
+ assert(fd == static_cast<evutil_socket_t>(((TConnection*)v)->getTSocket()->getSocketFD()));
+ ((TConnection*)v)->workSocket();
+ }
+
+ /**
+ * Notification to server that processing has ended on this request.
+ * Can be called either when processing is completed or when a waiting
+ * task has been preemptively terminated (on overload).
+ *
+ * Don't call this from the IO thread itself.
+ *
+ * @return true if successful, false if unable to notify (check THRIFT_GET_SOCKET_ERROR).
+ */
+ bool notifyIOThread() { return ioThread_->notify(this); }
+
+ /*
+ * Returns the number of this connection's currently assigned IO
+ * thread.
+ */
+ int getIOThreadNumber() const { return ioThread_->getThreadNumber(); }
+
+ /// Force connection shutdown for this connection.
+ void forceClose() {
+ appState_ = APP_CLOSE_CONNECTION;
+ if (!notifyIOThread()) {
+ server_->decrementActiveProcessors();
+ close();
+ throw TException("TConnection::forceClose: failed write on notify pipe");
+ }
+ }
+
+ /// return the server this connection was initialized for.
+ TNonblockingServer* getServer() const { return server_; }
+
+ /// get state of connection.
+ TAppState getState() const { return appState_; }
+
+ /// return the TSocket transport wrapping this network connection
+ std::shared_ptr<TSocket> getTSocket() const { return tSocket_; }
+
+ /// return the server event handler if any
+ std::shared_ptr<TServerEventHandler> getServerEventHandler() { return serverEventHandler_; }
+
+ /// return the Thrift connection context if any
+ void* getConnectionContext() { return connectionContext_; }
+};
+
+class TNonblockingServer::TConnection::Task : public Runnable {
+public:
+ Task(std::shared_ptr<TProcessor> processor,
+ std::shared_ptr<TProtocol> input,
+ std::shared_ptr<TProtocol> output,
+ TConnection* connection)
+ : processor_(processor),
+ input_(input),
+ output_(output),
+ connection_(connection),
+ serverEventHandler_(connection_->getServerEventHandler()),
+ connectionContext_(connection_->getConnectionContext()) {}
+
+ void run() override {
+ try {
+ for (;;) {
+ if (serverEventHandler_) {
+ serverEventHandler_->processContext(connectionContext_, connection_->getTSocket());
+ }
+ if (!processor_->process(input_, output_, connectionContext_)
+ || !input_->getTransport()->peek()) {
+ break;
+ }
+ }
+ } catch (const TTransportException& ttx) {
+ GlobalOutput.printf("TNonblockingServer: client died: %s", ttx.what());
+ } catch (const std::bad_alloc&) {
+ GlobalOutput("TNonblockingServer: caught bad_alloc exception.");
+ exit(1);
+ } catch (const std::exception& x) {
+ GlobalOutput.printf("TNonblockingServer: process() exception: %s: %s",
+ typeid(x).name(),
+ x.what());
+ } catch (...) {
+ GlobalOutput.printf("TNonblockingServer: unknown exception while processing.");
+ }
+
+ // Signal completion back to the libevent thread via a pipe
+ if (!connection_->notifyIOThread()) {
+ GlobalOutput.printf("TNonblockingServer: failed to notifyIOThread, closing.");
+ connection_->server_->decrementActiveProcessors();
+ connection_->close();
+ throw TException("TNonblockingServer::Task::run: failed write on notify pipe");
+ }
+ }
+
+ TConnection* getTConnection() { return connection_; }
+
+private:
+ std::shared_ptr<TProcessor> processor_;
+ std::shared_ptr<TProtocol> input_;
+ std::shared_ptr<TProtocol> output_;
+ TConnection* connection_;
+ std::shared_ptr<TServerEventHandler> serverEventHandler_;
+ void* connectionContext_;
+};
+
+void TNonblockingServer::TConnection::init(TNonblockingIOThread* ioThread) {
+ ioThread_ = ioThread;
+ server_ = ioThread->getServer();
+ appState_ = APP_INIT;
+ eventFlags_ = 0;
+
+ readBufferPos_ = 0;
+ readWant_ = 0;
+
+ writeBuffer_ = nullptr;
+ writeBufferSize_ = 0;
+ writeBufferPos_ = 0;
+ largestWriteBufferSize_ = 0;
+
+ socketState_ = SOCKET_RECV_FRAMING;
+ callsForResize_ = 0;
+
+ // get input/transports
+ factoryInputTransport_ = server_->getInputTransportFactory()->getTransport(inputTransport_);
+ factoryOutputTransport_ = server_->getOutputTransportFactory()->getTransport(outputTransport_);
+
+ // Create protocol
+ if (server_->getHeaderTransport()) {
+ inputProtocol_ = server_->getInputProtocolFactory()->getProtocol(factoryInputTransport_,
+ factoryOutputTransport_);
+ outputProtocol_ = inputProtocol_;
+ } else {
+ inputProtocol_ = server_->getInputProtocolFactory()->getProtocol(factoryInputTransport_);
+ outputProtocol_ = server_->getOutputProtocolFactory()->getProtocol(factoryOutputTransport_);
+ }
+
+ // Set up for any server event handler
+ serverEventHandler_ = server_->getEventHandler();
+ if (serverEventHandler_) {
+ connectionContext_ = serverEventHandler_->createContext(inputProtocol_, outputProtocol_);
+ } else {
+ connectionContext_ = nullptr;
+ }
+
+ // Get the processor
+ processor_ = server_->getProcessor(inputProtocol_, outputProtocol_, tSocket_);
+}
+
+void TNonblockingServer::TConnection::setSocket(std::shared_ptr<TSocket> socket) {
+ tSocket_ = socket;
+}
+
+void TNonblockingServer::TConnection::workSocket() {
+ int got = 0, left = 0, sent = 0;
+ uint32_t fetch = 0;
+
+ switch (socketState_) {
+ case SOCKET_RECV_FRAMING:
+ union {
+ uint8_t buf[sizeof(uint32_t)];
+ uint32_t size;
+ } framing;
+
+ // if we've already received some bytes we kept them here
+ framing.size = readWant_;
+ // determine size of this frame
+ try {
+ // Read from the socket
+ fetch = tSocket_->read(&framing.buf[readBufferPos_],
+ uint32_t(sizeof(framing.size) - readBufferPos_));
+ if (fetch == 0) {
+ // Whenever we get here it means a remote disconnect
+ close();
+ return;
+ }
+ readBufferPos_ += fetch;
+ } catch (TTransportException& te) {
+ //In Nonblocking SSLSocket some operations need to be retried again.
+ //Current approach is parsing exception message, but a better solution needs to be investigated.
+ if(!strstr(te.what(), "retry")) {
+ GlobalOutput.printf("TConnection::workSocket(): %s", te.what());
+ close();
+
+ return;
+ }
+ }
+
+ if (readBufferPos_ < sizeof(framing.size)) {
+ // more needed before frame size is known -- save what we have so far
+ readWant_ = framing.size;
+ return;
+ }
+
+ readWant_ = ntohl(framing.size);
+ if (readWant_ > server_->getMaxFrameSize()) {
+ // Don't allow giant frame sizes. This prevents bad clients from
+ // causing us to try and allocate a giant buffer.
+ GlobalOutput.printf(
+ "TNonblockingServer: frame size too large "
+ "(%" PRIu32 " > %" PRIu64
+ ") from client %s. "
+ "Remote side not using TFramedTransport?",
+ readWant_,
+ (uint64_t)server_->getMaxFrameSize(),
+ tSocket_->getSocketInfo().c_str());
+ close();
+ return;
+ }
+ // size known; now get the rest of the frame
+ transition();
+
+ // If the socket has more data than the frame header, continue to work on it. This is not strictly necessary for
+ // regular sockets, because if there is more data, libevent will fire the event handler registered for read
+ // readiness, which will in turn call workSocket(). However, some socket types (such as TSSLSocket) may have the
+ // data sitting in their internal buffers and from libevent's perspective, there is no further data available. In
+ // that case, not having this workSocket() call here would result in a hang as we will never get to work the socket,
+ // despite having more data.
+ if (tSocket_->hasPendingDataToRead())
+ {
+ workSocket();
+ }
+
+ return;
+
+ case SOCKET_RECV:
+ // It is an error to be in this state if we already have all the data
+ assert(readBufferPos_ < readWant_);
+
+ try {
+ // Read from the socket
+ fetch = readWant_ - readBufferPos_;
+ got = tSocket_->read(readBuffer_ + readBufferPos_, fetch);
+ } catch (TTransportException& te) {
+ //In Nonblocking SSLSocket some operations need to be retried again.
+ //Current approach is parsing exception message, but a better solution needs to be investigated.
+ if(!strstr(te.what(), "retry")) {
+ GlobalOutput.printf("TConnection::workSocket(): %s", te.what());
+ close();
+ }
+
+ return;
+ }
+
+ if (got > 0) {
+ // Move along in the buffer
+ readBufferPos_ += got;
+
+ // Check that we did not overdo it
+ assert(readBufferPos_ <= readWant_);
+
+ // We are done reading, move onto the next state
+ if (readBufferPos_ == readWant_) {
+ transition();
+ }
+ return;
+ }
+
+ // Whenever we get down here it means a remote disconnect
+ close();
+
+ return;
+
+ case SOCKET_SEND:
+ // Should never have position past size
+ assert(writeBufferPos_ <= writeBufferSize_);
+
+ // If there is no data to send, then let us move on
+ if (writeBufferPos_ == writeBufferSize_) {
+ GlobalOutput("WARNING: Send state with no data to send");
+ transition();
+ return;
+ }
+
+ try {
+ left = writeBufferSize_ - writeBufferPos_;
+ sent = tSocket_->write_partial(writeBuffer_ + writeBufferPos_, left);
+ } catch (TTransportException& te) {
+ GlobalOutput.printf("TConnection::workSocket(): %s ", te.what());
+ close();
+ return;
+ }
+
+ writeBufferPos_ += sent;
+
+ // Did we overdo it?
+ assert(writeBufferPos_ <= writeBufferSize_);
+
+ // We are done!
+ if (writeBufferPos_ == writeBufferSize_) {
+ transition();
+ }
+
+ return;
+
+ default:
+ GlobalOutput.printf("Unexpected Socket State %d", socketState_);
+ assert(0);
+ }
+}
+
+bool TNonblockingServer::getHeaderTransport() {
+ // Currently if there is no output protocol factory,
+ // we assume header transport (without having to create
+ // a new transport and check)
+ return getOutputProtocolFactory() == nullptr;
+}
+
+/**
+ * This is called when the application transitions from one state into
+ * another. This means that it has finished writing the data that it needed
+ * to, or finished receiving the data that it needed to.
+ */
+void TNonblockingServer::TConnection::transition() {
+ // ensure this connection is active right now
+ assert(ioThread_);
+ assert(server_);
+
+ // Switch upon the state that we are currently in and move to a new state
+ switch (appState_) {
+
+ case APP_READ_REQUEST:
+ // We are done reading the request, package the read buffer into transport
+ // and get back some data from the dispatch function
+ if (server_->getHeaderTransport()) {
+ inputTransport_->resetBuffer(readBuffer_, readBufferPos_);
+ outputTransport_->resetBuffer();
+ } else {
+ // We saved room for the framing size in case header transport needed it,
+ // but just skip it for the non-header case
+ inputTransport_->resetBuffer(readBuffer_ + 4, readBufferPos_ - 4);
+ outputTransport_->resetBuffer();
+
+ // Prepend four bytes of blank space to the buffer so we can
+ // write the frame size there later.
+ outputTransport_->getWritePtr(4);
+ outputTransport_->wroteBytes(4);
+ }
+
+ server_->incrementActiveProcessors();
+
+ if (server_->isThreadPoolProcessing()) {
+ // We are setting up a Task to do this work and we will wait on it
+
+ // Create task and dispatch to the thread manager
+ std::shared_ptr<Runnable> task = std::shared_ptr<Runnable>(
+ new Task(processor_, inputProtocol_, outputProtocol_, this));
+ // The application is now waiting on the task to finish
+ appState_ = APP_WAIT_TASK;
+
+ // Set this connection idle so that libevent doesn't process more
+ // data on it while we're still waiting for the threadmanager to
+ // finish this task
+ setIdle();
+
+ try {
+ server_->addTask(task);
+ } catch (IllegalStateException& ise) {
+ // The ThreadManager is not ready to handle any more tasks (it's probably shutting down).
+ GlobalOutput.printf("IllegalStateException: Server::process() %s", ise.what());
+ server_->decrementActiveProcessors();
+ close();
+ } catch (TimedOutException& to) {
+ GlobalOutput.printf("[ERROR] TimedOutException: Server::process() %s", to.what());
+ server_->decrementActiveProcessors();
+ close();
+ }
+
+ return;
+ } else {
+ try {
+ if (serverEventHandler_) {
+ serverEventHandler_->processContext(connectionContext_, getTSocket());
+ }
+ // Invoke the processor
+ processor_->process(inputProtocol_, outputProtocol_, connectionContext_);
+ } catch (const TTransportException& ttx) {
+ GlobalOutput.printf(
+ "TNonblockingServer transport error in "
+ "process(): %s",
+ ttx.what());
+ server_->decrementActiveProcessors();
+ close();
+ return;
+ } catch (const std::exception& x) {
+ GlobalOutput.printf("Server::process() uncaught exception: %s: %s",
+ typeid(x).name(),
+ x.what());
+ server_->decrementActiveProcessors();
+ close();
+ return;
+ } catch (...) {
+ GlobalOutput.printf("Server::process() unknown exception");
+ server_->decrementActiveProcessors();
+ close();
+ return;
+ }
+ }
+ // fallthrough
+
+ // Intentionally fall through here, the call to process has written into
+ // the writeBuffer_
+
+ case APP_WAIT_TASK:
+ // We have now finished processing a task and the result has been written
+ // into the outputTransport_, so we grab its contents and place them into
+ // the writeBuffer_ for actual writing by the libevent thread
+
+ server_->decrementActiveProcessors();
+ // Get the result of the operation
+ outputTransport_->getBuffer(&writeBuffer_, &writeBufferSize_);
+
+ // If the function call generated return data, then move into the send
+ // state and get going
+ // 4 bytes were reserved for frame size
+ if (writeBufferSize_ > 4) {
+
+ // Move into write state
+ writeBufferPos_ = 0;
+ socketState_ = SOCKET_SEND;
+
+ // Put the frame size into the write buffer
+ auto frameSize = (int32_t)htonl(writeBufferSize_ - 4);
+ memcpy(writeBuffer_, &frameSize, 4);
+
+ // Socket into write mode
+ appState_ = APP_SEND_RESULT;
+ setWrite();
+
+ return;
+ }
+
+ // In this case, the request was oneway and we should fall through
+ // right back into the read frame header state
+ goto LABEL_APP_INIT;
+
+ case APP_SEND_RESULT:
+ // it's now safe to perform buffer size housekeeping.
+ if (writeBufferSize_ > largestWriteBufferSize_) {
+ largestWriteBufferSize_ = writeBufferSize_;
+ }
+ if (server_->getResizeBufferEveryN() > 0
+ && ++callsForResize_ >= server_->getResizeBufferEveryN()) {
+ checkIdleBufferMemLimit(server_->getIdleReadBufferLimit(),
+ server_->getIdleWriteBufferLimit());
+ callsForResize_ = 0;
+ }
+ // fallthrough
+
+ // N.B.: We also intentionally fall through here into the INIT state!
+
+ LABEL_APP_INIT:
+ case APP_INIT:
+
+ // Clear write buffer variables
+ writeBuffer_ = nullptr;
+ writeBufferPos_ = 0;
+ writeBufferSize_ = 0;
+
+ // Into read4 state we go
+ socketState_ = SOCKET_RECV_FRAMING;
+ appState_ = APP_READ_FRAME_SIZE;
+
+ readBufferPos_ = 0;
+
+ // Register read event
+ setRead();
+
+ return;
+
+ case APP_READ_FRAME_SIZE:
+ readWant_ += 4;
+
+ // We just read the request length
+ // Double the buffer size until it is big enough
+ if (readWant_ > readBufferSize_) {
+ if (readBufferSize_ == 0) {
+ readBufferSize_ = 1;
+ }
+ uint32_t newSize = readBufferSize_;
+ while (readWant_ > newSize) {
+ newSize *= 2;
+ }
+
+ auto* newBuffer = (uint8_t*)std::realloc(readBuffer_, newSize);
+ if (newBuffer == nullptr) {
+ // nothing else to be done...
+ throw std::bad_alloc();
+ }
+ readBuffer_ = newBuffer;
+ readBufferSize_ = newSize;
+ }
+
+ readBufferPos_ = 4;
+ *((uint32_t*)readBuffer_) = htonl(readWant_ - 4);
+
+ // Move into read request state
+ socketState_ = SOCKET_RECV;
+ appState_ = APP_READ_REQUEST;
+
+ return;
+
+ case APP_CLOSE_CONNECTION:
+ server_->decrementActiveProcessors();
+ close();
+ return;
+
+ default:
+ GlobalOutput.printf("Unexpected Application State %d", appState_);
+ assert(0);
+ }
+}
+
+void TNonblockingServer::TConnection::setFlags(short eventFlags) {
+ // Catch the do nothing case
+ if (eventFlags_ == eventFlags) {
+ return;
+ }
+
+ // Delete a previously existing event
+ if (eventFlags_ && event_del(&event_) == -1) {
+ GlobalOutput.perror("TConnection::setFlags() event_del", THRIFT_GET_SOCKET_ERROR);
+ return;
+ }
+
+ // Update in memory structure
+ eventFlags_ = eventFlags;
+
+ // Do not call event_set if there are no flags
+ if (!eventFlags_) {
+ return;
+ }
+
+ /*
+ * event_set:
+ *
+ * Prepares the event structure &event to be used in future calls to
+ * event_add() and event_del(). The event will be prepared to call the
+ * eventHandler using the 'sock' file descriptor to monitor events.
+ *
+ * The events can be either EV_READ, EV_WRITE, or both, indicating
+ * that an application can read or write from the file respectively without
+ * blocking.
+ *
+ * The eventHandler will be called with the file descriptor that triggered
+ * the event and the type of event which will be one of: EV_TIMEOUT,
+ * EV_SIGNAL, EV_READ, EV_WRITE.
+ *
+ * The additional flag EV_PERSIST makes an event_add() persistent until
+ * event_del() has been called.
+ *
+ * Once initialized, the &event struct can be used repeatedly with
+ * event_add() and event_del() and does not need to be reinitialized unless
+ * the eventHandler and/or the argument to it are to be changed. However,
+ * when an ev structure has been added to libevent using event_add() the
+ * structure must persist until the event occurs (assuming EV_PERSIST
+ * is not set) or is removed using event_del(). You may not reuse the same
+ * ev structure for multiple monitored descriptors; each descriptor needs
+ * its own ev.
+ */
+ event_set(&event_, tSocket_->getSocketFD(), eventFlags_, TConnection::eventHandler, this);
+ event_base_set(ioThread_->getEventBase(), &event_);
+
+ // Add the event
+ if (event_add(&event_, nullptr) == -1) {
+ GlobalOutput.perror("TConnection::setFlags(): could not event_add", THRIFT_GET_SOCKET_ERROR);
+ }
+}
+
+/**
+ * Closes a connection
+ */
+void TNonblockingServer::TConnection::close() {
+ setIdle();
+
+ if (serverEventHandler_) {
+ serverEventHandler_->deleteContext(connectionContext_, inputProtocol_, outputProtocol_);
+ }
+ ioThread_ = nullptr;
+
+ // Close the socket
+ tSocket_->close();
+
+ // close any factory produced transports
+ factoryInputTransport_->close();
+ factoryOutputTransport_->close();
+
+ // release processor and handler
+ processor_.reset();
+
+ // Give this object back to the server that owns it
+ server_->returnConnection(this);
+}
+
+void TNonblockingServer::TConnection::checkIdleBufferMemLimit(size_t readLimit, size_t writeLimit) {
+ if (readLimit > 0 && readBufferSize_ > readLimit) {
+ free(readBuffer_);
+ readBuffer_ = nullptr;
+ readBufferSize_ = 0;
+ }
+
+ if (writeLimit > 0 && largestWriteBufferSize_ > writeLimit) {
+ // just start over
+ outputTransport_->resetBuffer(static_cast<uint32_t>(server_->getWriteBufferDefaultSize()));
+ largestWriteBufferSize_ = 0;
+ }
+}
+
+TNonblockingServer::~TNonblockingServer() {
+ // Close any active connections (moves them to the idle connection stack)
+ while (activeConnections_.size()) {
+ activeConnections_.front()->close();
+ }
+ // Clean up unused TConnection objects in connectionStack_
+ while (!connectionStack_.empty()) {
+ TConnection* connection = connectionStack_.top();
+ connectionStack_.pop();
+ delete connection;
+ }
+ // The TNonblockingIOThread objects have shared_ptrs to the Thread
+ // objects and the Thread objects have shared_ptrs to the TNonblockingIOThread
+ // objects (as runnable) so these objects will never deallocate without help.
+ while (!ioThreads_.empty()) {
+ std::shared_ptr<TNonblockingIOThread> iot = ioThreads_.back();
+ ioThreads_.pop_back();
+ iot->setThread(std::shared_ptr<Thread>());
+ }
+}
+
+/**
+ * Creates a new connection either by reusing an object off the stack or
+ * by allocating a new one entirely
+ */
+TNonblockingServer::TConnection* TNonblockingServer::createConnection(std::shared_ptr<TSocket> socket) {
+ // Check the stack
+ Guard g(connMutex_);
+
+ // pick an IO thread to handle this connection -- currently round robin
+ assert(nextIOThread_ < ioThreads_.size());
+ int selectedThreadIdx = nextIOThread_;
+ nextIOThread_ = static_cast<uint32_t>((nextIOThread_ + 1) % ioThreads_.size());
+
+ TNonblockingIOThread* ioThread = ioThreads_[selectedThreadIdx].get();
+
+ // Check the connection stack to see if we can re-use
+ TConnection* result = nullptr;
+ if (connectionStack_.empty()) {
+ result = new TConnection(socket, ioThread);
+ ++numTConnections_;
+ } else {
+ result = connectionStack_.top();
+ connectionStack_.pop();
+ result->setSocket(socket);
+ result->init(ioThread);
+ }
+ activeConnections_.push_back(result);
+ return result;
+}
+
+/**
+ * Returns a connection to the stack
+ */
+void TNonblockingServer::returnConnection(TConnection* connection) {
+ Guard g(connMutex_);
+
+ activeConnections_.erase(std::remove(activeConnections_.begin(),
+ activeConnections_.end(),
+ connection),
+ activeConnections_.end());
+
+ if (connectionStackLimit_ && (connectionStack_.size() >= connectionStackLimit_)) {
+ delete connection;
+ --numTConnections_;
+ } else {
+ connection->checkIdleBufferMemLimit(idleReadBufferLimit_, idleWriteBufferLimit_);
+ connectionStack_.push(connection);
+ }
+}
+
+/**
+ * Server socket had something happen. We accept all waiting client
+ * connections on fd and assign TConnection objects to handle those requests.
+ */
+void TNonblockingServer::handleEvent(THRIFT_SOCKET fd, short which) {
+ (void)which;
+ // Make sure that libevent didn't mess up the socket handles
+ assert(fd == serverSocket_);
+
+ // Going to accept a new client socket
+ std::shared_ptr<TSocket> clientSocket;
+
+ clientSocket = serverTransport_->accept();
+ if (clientSocket) {
+ // If we're overloaded, take action here
+ if (overloadAction_ != T_OVERLOAD_NO_ACTION && serverOverloaded()) {
+ Guard g(connMutex_);
+ nConnectionsDropped_++;
+ nTotalConnectionsDropped_++;
+ if (overloadAction_ == T_OVERLOAD_CLOSE_ON_ACCEPT) {
+ clientSocket->close();
+ return;
+ } else if (overloadAction_ == T_OVERLOAD_DRAIN_TASK_QUEUE) {
+ if (!drainPendingTask()) {
+ // Nothing left to discard, so we drop connection instead.
+ clientSocket->close();
+ return;
+ }
+ }
+ }
+
+ // Create a new TConnection for this client socket.
+ TConnection* clientConnection = createConnection(clientSocket);
+
+ // Fail fast if we could not create a TConnection object
+ if (clientConnection == nullptr) {
+ GlobalOutput.printf("thriftServerEventHandler: failed TConnection factory");
+ clientSocket->close();
+ return;
+ }
+
+ /*
+ * Either notify the ioThread that is assigned this connection to
+ * start processing, or if it is us, we'll just ask this
+ * connection to do its initial state change here.
+ *
+ * (We need to avoid writing to our own notification pipe, to
+ * avoid possible deadlocks if the pipe is full.)
+ *
+ * The IO thread #0 is the only one that handles these listen
+ * events, so unless the connection has been assigned to thread #0
+ * we know it's not on our thread.
+ */
+ if (clientConnection->getIOThreadNumber() == 0) {
+ clientConnection->transition();
+ } else {
+ if (!clientConnection->notifyIOThread()) {
+ GlobalOutput.perror("[ERROR] notifyIOThread failed on fresh connection, closing", errno);
+ clientConnection->close();
+ }
+ }
+ }
+}
+
+/**
+ * Creates a socket to listen on and binds it to the local port.
+ */
+void TNonblockingServer::createAndListenOnSocket() {
+ serverTransport_->listen();
+ serverSocket_ = serverTransport_->getSocketFD();
+}
+
+
+void TNonblockingServer::setThreadManager(std::shared_ptr<ThreadManager> threadManager) {
+ threadManager_ = threadManager;
+ if (threadManager) {
+ threadManager->setExpireCallback(
+ std::bind(&TNonblockingServer::expireClose,
+ this,
+ std::placeholders::_1));
+ threadPoolProcessing_ = true;
+ } else {
+ threadPoolProcessing_ = false;
+ }
+}
+
+bool TNonblockingServer::serverOverloaded() {
+ size_t activeConnections = numTConnections_ - connectionStack_.size();
+ if (numActiveProcessors_ > maxActiveProcessors_ || activeConnections > maxConnections_) {
+ if (!overloaded_) {
+ GlobalOutput.printf("TNonblockingServer: overload condition begun.");
+ overloaded_ = true;
+ }
+ } else {
+ if (overloaded_ && (numActiveProcessors_ <= overloadHysteresis_ * maxActiveProcessors_)
+ && (activeConnections <= overloadHysteresis_ * maxConnections_)) {
+ GlobalOutput.printf(
+ "TNonblockingServer: overload ended; "
+ "%u dropped (%llu total)",
+ nConnectionsDropped_,
+ nTotalConnectionsDropped_);
+ nConnectionsDropped_ = 0;
+ overloaded_ = false;
+ }
+ }
+
+ return overloaded_;
+}
+
+bool TNonblockingServer::drainPendingTask() {
+ if (threadManager_) {
+ std::shared_ptr<Runnable> task = threadManager_->removeNextPending();
+ if (task) {
+ TConnection* connection = static_cast<TConnection::Task*>(task.get())->getTConnection();
+ assert(connection && connection->getServer() && connection->getState() == APP_WAIT_TASK);
+ connection->forceClose();
+ return true;
+ }
+ }
+ return false;
+}
+
+void TNonblockingServer::expireClose(std::shared_ptr<Runnable> task) {
+ TConnection* connection = static_cast<TConnection::Task*>(task.get())->getTConnection();
+ assert(connection && connection->getServer() && connection->getState() == APP_WAIT_TASK);
+ connection->forceClose();
+}
+
+void TNonblockingServer::stop() {
+ // Breaks the event loop in all threads so that they end ASAP.
+ for (auto & ioThread : ioThreads_) {
+ ioThread->stop();
+ }
+}
+
+void TNonblockingServer::registerEvents(event_base* user_event_base) {
+ userEventBase_ = user_event_base;
+
+ // init listen socket
+ if (serverSocket_ == THRIFT_INVALID_SOCKET)
+ createAndListenOnSocket();
+
+ // set up the IO threads
+ assert(ioThreads_.empty());
+ if (!numIOThreads_) {
+ numIOThreads_ = DEFAULT_IO_THREADS;
+ }
+ // User-provided event-base doesn't works for multi-threaded servers
+ assert(numIOThreads_ == 1 || !userEventBase_);
+
+ for (uint32_t id = 0; id < numIOThreads_; ++id) {
+ // the first IO thread also does the listening on server socket
+ THRIFT_SOCKET listenFd = (id == 0 ? serverSocket_ : THRIFT_INVALID_SOCKET);
+
+ shared_ptr<TNonblockingIOThread> thread(
+ new TNonblockingIOThread(this, id, listenFd, useHighPriorityIOThreads_));
+ ioThreads_.push_back(thread);
+ }
+
+ // Notify handler of the preServe event
+ if (eventHandler_) {
+ eventHandler_->preServe();
+ }
+
+ // Start all of our helper IO threads. Note that the threads run forever,
+ // only terminating if stop() is called.
+ assert(ioThreads_.size() == numIOThreads_);
+ assert(ioThreads_.size() > 0);
+
+ GlobalOutput.printf("TNonblockingServer: Serving with %d io threads.",
+ ioThreads_.size());
+
+ // Launch all the secondary IO threads in separate threads
+ if (ioThreads_.size() > 1) {
+ ioThreadFactory_.reset(new ThreadFactory(
+ false // detached
+ ));
+
+ assert(ioThreadFactory_.get());
+
+ // intentionally starting at thread 1, not 0
+ for (uint32_t i = 1; i < ioThreads_.size(); ++i) {
+ shared_ptr<Thread> thread = ioThreadFactory_->newThread(ioThreads_[i]);
+ ioThreads_[i]->setThread(thread);
+ thread->start();
+ }
+ }
+
+ // Register the events for the primary (listener) IO thread
+ ioThreads_[0]->registerEvents();
+}
+
+/**
+ * Main workhorse function, starts up the server listening on a port and
+ * loops over the libevent handler.
+ */
+void TNonblockingServer::serve() {
+
+ if (ioThreads_.empty())
+ registerEvents(nullptr);
+
+ // Run the primary (listener) IO thread loop in our main thread; this will
+ // only return when the server is shutting down.
+ ioThreads_[0]->run();
+
+ // Ensure all threads are finished before exiting serve()
+ for (uint32_t i = 0; i < ioThreads_.size(); ++i) {
+ ioThreads_[i]->join();
+ GlobalOutput.printf("TNonblocking: join done for IO thread #%d", i);
+ }
+}
+
+TNonblockingIOThread::TNonblockingIOThread(TNonblockingServer* server,
+ int number,
+ THRIFT_SOCKET listenSocket,
+ bool useHighPriority)
+ : server_(server),
+ number_(number),
+ threadId_{},
+ listenSocket_(listenSocket),
+ useHighPriority_(useHighPriority),
+ eventBase_(nullptr),
+ ownEventBase_(false),
+ serverEvent_{},
+ notificationEvent_{} {
+ notificationPipeFDs_[0] = -1;
+ notificationPipeFDs_[1] = -1;
+}
+
+TNonblockingIOThread::~TNonblockingIOThread() {
+ // make sure our associated thread is fully finished
+ join();
+
+ if (eventBase_ && ownEventBase_) {
+ event_base_free(eventBase_);
+ ownEventBase_ = false;
+ }
+
+ if (listenSocket_ != THRIFT_INVALID_SOCKET) {
+ if (0 != ::THRIFT_CLOSESOCKET(listenSocket_)) {
+ GlobalOutput.perror("TNonblockingIOThread listenSocket_ close(): ", THRIFT_GET_SOCKET_ERROR);
+ }
+ listenSocket_ = THRIFT_INVALID_SOCKET;
+ }
+
+ for (auto notificationPipeFD : notificationPipeFDs_) {
+ if (notificationPipeFD >= 0) {
+ if (0 != ::THRIFT_CLOSESOCKET(notificationPipeFD)) {
+ GlobalOutput.perror("TNonblockingIOThread notificationPipe close(): ",
+ THRIFT_GET_SOCKET_ERROR);
+ }
+ notificationPipeFD = THRIFT_INVALID_SOCKET;
+ }
+ }
+}
+
+void TNonblockingIOThread::createNotificationPipe() {
+ if (evutil_socketpair(AF_LOCAL, SOCK_STREAM, 0, notificationPipeFDs_) == -1) {
+ GlobalOutput.perror("TNonblockingServer::createNotificationPipe ", EVUTIL_SOCKET_ERROR());
+ throw TException("can't create notification pipe");
+ }
+ if (evutil_make_socket_nonblocking(notificationPipeFDs_[0]) < 0
+ || evutil_make_socket_nonblocking(notificationPipeFDs_[1]) < 0) {
+ ::THRIFT_CLOSESOCKET(notificationPipeFDs_[0]);
+ ::THRIFT_CLOSESOCKET(notificationPipeFDs_[1]);
+ throw TException("TNonblockingServer::createNotificationPipe() THRIFT_O_NONBLOCK");
+ }
+ for (auto notificationPipeFD : notificationPipeFDs_) {
+#if LIBEVENT_VERSION_NUMBER < 0x02000000
+ int flags;
+ if ((flags = THRIFT_FCNTL(notificationPipeFD, F_GETFD, 0)) < 0
+ || THRIFT_FCNTL(notificationPipeFD, F_SETFD, flags | FD_CLOEXEC) < 0) {
+#else
+ if (evutil_make_socket_closeonexec(notificationPipeFD) < 0) {
+#endif
+ ::THRIFT_CLOSESOCKET(notificationPipeFDs_[0]);
+ ::THRIFT_CLOSESOCKET(notificationPipeFDs_[1]);
+ throw TException(
+ "TNonblockingServer::createNotificationPipe() "
+ "FD_CLOEXEC");
+ }
+ }
+}
+
+/**
+ * Register the core libevent events onto the proper base.
+ */
+void TNonblockingIOThread::registerEvents() {
+ threadId_ = Thread::get_current();
+
+ assert(eventBase_ == nullptr);
+ eventBase_ = getServer()->getUserEventBase();
+ if (eventBase_ == nullptr) {
+ eventBase_ = event_base_new();
+ ownEventBase_ = true;
+ }
+
+ // Print some libevent stats
+ if (number_ == 0) {
+ GlobalOutput.printf("TNonblockingServer: using libevent %s method %s",
+ event_get_version(),
+ event_base_get_method(eventBase_));
+ }
+
+ if (listenSocket_ != THRIFT_INVALID_SOCKET) {
+ // Register the server event
+ event_set(&serverEvent_,
+ listenSocket_,
+ EV_READ | EV_PERSIST,
+ TNonblockingIOThread::listenHandler,
+ server_);
+ event_base_set(eventBase_, &serverEvent_);
+
+ // Add the event and start up the server
+ if (-1 == event_add(&serverEvent_, nullptr)) {
+ throw TException(
+ "TNonblockingServer::serve(): "
+ "event_add() failed on server listen event");
+ }
+ GlobalOutput.printf("TNonblocking: IO thread #%d registered for listen.", number_);
+ }
+
+ createNotificationPipe();
+
+ // Create an event to be notified when a task finishes
+ event_set(&notificationEvent_,
+ getNotificationRecvFD(),
+ EV_READ | EV_PERSIST,
+ TNonblockingIOThread::notifyHandler,
+ this);
+
+ // Attach to the base
+ event_base_set(eventBase_, &notificationEvent_);
+
+ // Add the event and start up the server
+ if (-1 == event_add(&notificationEvent_, nullptr)) {
+ throw TException(
+ "TNonblockingServer::serve(): "
+ "event_add() failed on task-done notification event");
+ }
+ GlobalOutput.printf("TNonblocking: IO thread #%d registered for notify.", number_);
+}
+
+bool TNonblockingIOThread::notify(TNonblockingServer::TConnection* conn) {
+ auto fd = getNotificationSendFD();
+ if (fd < 0) {
+ return false;
+ }
+
+ int ret = -1;
+ long kSize = sizeof(conn);
+ const char * pos = (const char *)const_cast_sockopt(&conn);
+
+#if defined(HAVE_POLL_H) || defined(HAVE_SYS_POLL_H)
+ struct pollfd pfd = {fd, POLLOUT, 0};
+
+ while (kSize > 0) {
+ pfd.revents = 0;
+ ret = poll(&pfd, 1, -1);
+ if (ret < 0) {
+ return false;
+ } else if (ret == 0) {
+ continue;
+ }
+
+ if (pfd.revents & POLLHUP || pfd.revents & POLLERR) {
+ ::THRIFT_CLOSESOCKET(fd);
+ return false;
+ }
+
+ if (pfd.revents & POLLOUT) {
+ ret = send(fd, pos, kSize, 0);
+ if (ret < 0) {
+ if (errno == EAGAIN) {
+ continue;
+ }
+
+ ::THRIFT_CLOSESOCKET(fd);
+ return false;
+ }
+
+ kSize -= ret;
+ pos += ret;
+ }
+ }
+#else
+ fd_set wfds, efds;
+
+ while (kSize > 0) {
+ FD_ZERO(&wfds);
+ FD_ZERO(&efds);
+ FD_SET(fd, &wfds);
+ FD_SET(fd, &efds);
+ ret = select(static_cast<int>(fd + 1), NULL, &wfds, &efds, NULL);
+ if (ret < 0) {
+ return false;
+ } else if (ret == 0) {
+ continue;
+ }
+
+ if (FD_ISSET(fd, &efds)) {
+ ::THRIFT_CLOSESOCKET(fd);
+ return false;
+ }
+
+ if (FD_ISSET(fd, &wfds)) {
+ ret = send(fd, pos, kSize, 0);
+ if (ret < 0) {
+ if (errno == EAGAIN) {
+ continue;
+ }
+
+ ::THRIFT_CLOSESOCKET(fd);
+ return false;
+ }
+
+ kSize -= ret;
+ pos += ret;
+ }
+ }
+#endif
+
+ return true;
+}
+
+/* static */
+void TNonblockingIOThread::notifyHandler(evutil_socket_t fd, short which, void* v) {
+ auto* ioThread = (TNonblockingIOThread*)v;
+ assert(ioThread);
+ (void)which;
+
+ while (true) {
+ TNonblockingServer::TConnection* connection = nullptr;
+ const int kSize = sizeof(connection);
+ long nBytes = recv(fd, cast_sockopt(&connection), kSize, 0);
+ if (nBytes == kSize) {
+ if (connection == nullptr) {
+ // this is the command to stop our thread, exit the handler!
+ ioThread->breakLoop(false);
+ return;
+ }
+ connection->transition();
+ } else if (nBytes > 0) {
+ // throw away these bytes and hope that next time we get a solid read
+ GlobalOutput.printf("notifyHandler: Bad read of %d bytes, wanted %d", nBytes, kSize);
+ ioThread->breakLoop(true);
+ return;
+ } else if (nBytes == 0) {
+ GlobalOutput.printf("notifyHandler: Notify socket closed!");
+ ioThread->breakLoop(false);
+ // exit the loop
+ break;
+ } else { // nBytes < 0
+ if (THRIFT_GET_SOCKET_ERROR != THRIFT_EWOULDBLOCK
+ && THRIFT_GET_SOCKET_ERROR != THRIFT_EAGAIN) {
+ GlobalOutput.perror("TNonblocking: notifyHandler read() failed: ", THRIFT_GET_SOCKET_ERROR);
+ ioThread->breakLoop(true);
+ return;
+ }
+ // exit the loop
+ break;
+ }
+ }
+}
+
+void TNonblockingIOThread::breakLoop(bool error) {
+ if (error) {
+ GlobalOutput.printf("TNonblockingServer: IO thread #%d exiting with error.", number_);
+ // TODO: figure out something better to do here, but for now kill the
+ // whole process.
+ GlobalOutput.printf("TNonblockingServer: aborting process.");
+ ::abort();
+ }
+
+ // If we're running in the same thread, we can't use the notify(0)
+ // mechanism to stop the thread, but happily if we're running in the
+ // same thread, this means the thread can't be blocking in the event
+ // loop either.
+ if (!Thread::is_current(threadId_)) {
+ notify(nullptr);
+ } else {
+ // cause the loop to stop ASAP - even if it has things to do in it
+ event_base_loopbreak(eventBase_);
+ }
+}
+
+void TNonblockingIOThread::setCurrentThreadHighPriority(bool value) {
+#ifdef HAVE_SCHED_H
+ // Start out with a standard, low-priority setup for the sched params.
+ struct sched_param sp;
+ bzero((void*)&sp, sizeof(sp));
+ int policy = SCHED_OTHER;
+
+ // If desired, set up high-priority sched params structure.
+ if (value) {
+ // FIFO scheduler, ranked above default SCHED_OTHER queue
+ policy = SCHED_FIFO;
+ // The priority only compares us to other SCHED_FIFO threads, so we
+ // just pick a random priority halfway between min & max.
+ const int priority = (sched_get_priority_max(policy) + sched_get_priority_min(policy)) / 2;
+
+ sp.sched_priority = priority;
+ }
+
+ // Actually set the sched params for the current thread.
+ if (0 == pthread_setschedparam(pthread_self(), policy, &sp)) {
+ GlobalOutput.printf("TNonblocking: IO Thread #%d using high-priority scheduler!", number_);
+ } else {
+ GlobalOutput.perror("TNonblocking: pthread_setschedparam(): ", THRIFT_GET_SOCKET_ERROR);
+ }
+#else
+ THRIFT_UNUSED_VARIABLE(value);
+#endif
+}
+
+void TNonblockingIOThread::run() {
+ if (eventBase_ == nullptr) {
+ registerEvents();
+ }
+ if (useHighPriority_) {
+ setCurrentThreadHighPriority(true);
+ }
+
+ if (eventBase_ != nullptr)
+ {
+ GlobalOutput.printf("TNonblockingServer: IO thread #%d entering loop...", number_);
+ // Run libevent engine, never returns, invokes calls to eventHandler
+ event_base_loop(eventBase_, 0);
+
+ if (useHighPriority_) {
+ setCurrentThreadHighPriority(false);
+ }
+
+ // cleans up our registered events
+ cleanupEvents();
+ }
+
+ GlobalOutput.printf("TNonblockingServer: IO thread #%d run() done!", number_);
+}
+
+void TNonblockingIOThread::cleanupEvents() {
+ // stop the listen socket, if any
+ if (listenSocket_ != THRIFT_INVALID_SOCKET) {
+ if (event_del(&serverEvent_) == -1) {
+ GlobalOutput.perror("TNonblockingIOThread::stop() event_del: ", THRIFT_GET_SOCKET_ERROR);
+ }
+ }
+
+ event_del(&notificationEvent_);
+}
+
+void TNonblockingIOThread::stop() {
+ // This should cause the thread to fall out of its event loop ASAP.
+ breakLoop(false);
+}
+
+void TNonblockingIOThread::join() {
+ // If this was a thread created by a factory (not the thread that called
+ // serve()), we join() it to make sure we shut down fully.
+ if (thread_) {
+ try {
+ // Note that it is safe to both join() ourselves twice, as well as join
+ // the current thread as the pthread implementation checks for deadlock.
+ thread_->join();
+ } catch (...) {
+ // swallow everything
+ }
+ }
+}
+}
+}
+} // apache::thrift::server
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TNonblockingServer.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TNonblockingServer.h
new file mode 100644
index 000000000..82bc375bd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TNonblockingServer.h
@@ -0,0 +1,860 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_SERVER_TNONBLOCKINGSERVER_H_
+#define _THRIFT_SERVER_TNONBLOCKINGSERVER_H_ 1
+
+#include <thrift/Thrift.h>
+#include <memory>
+#include <thrift/server/TServer.h>
+#include <thrift/transport/PlatformSocket.h>
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/transport/TSocket.h>
+#include <thrift/transport/TNonblockingServerTransport.h>
+#include <thrift/concurrency/ThreadManager.h>
+#include <climits>
+#include <thrift/concurrency/Thread.h>
+#include <thrift/concurrency/ThreadFactory.h>
+#include <thrift/concurrency/Mutex.h>
+#include <stack>
+#include <vector>
+#include <string>
+#include <cstdlib>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <event.h>
+#include <event2/event_compat.h>
+#include <event2/event_struct.h>
+
+namespace apache {
+namespace thrift {
+namespace server {
+
+using apache::thrift::transport::TMemoryBuffer;
+using apache::thrift::transport::TSocket;
+using apache::thrift::transport::TNonblockingServerTransport;
+using apache::thrift::protocol::TProtocol;
+using apache::thrift::concurrency::Runnable;
+using apache::thrift::concurrency::ThreadManager;
+using apache::thrift::concurrency::ThreadFactory;
+using apache::thrift::concurrency::ThreadFactory;
+using apache::thrift::concurrency::Thread;
+using apache::thrift::concurrency::Mutex;
+using apache::thrift::concurrency::Guard;
+
+#ifdef LIBEVENT_VERSION_NUMBER
+#define LIBEVENT_VERSION_MAJOR (LIBEVENT_VERSION_NUMBER >> 24)
+#define LIBEVENT_VERSION_MINOR ((LIBEVENT_VERSION_NUMBER >> 16) & 0xFF)
+#define LIBEVENT_VERSION_REL ((LIBEVENT_VERSION_NUMBER >> 8) & 0xFF)
+#else
+// assume latest version 1 series
+#define LIBEVENT_VERSION_MAJOR 1
+#define LIBEVENT_VERSION_MINOR 14
+#define LIBEVENT_VERSION_REL 13
+#define LIBEVENT_VERSION_NUMBER \
+ ((LIBEVENT_VERSION_MAJOR << 24) | (LIBEVENT_VERSION_MINOR << 16) | (LIBEVENT_VERSION_REL << 8))
+#endif
+
+#if LIBEVENT_VERSION_NUMBER < 0x02000000
+typedef THRIFT_SOCKET evutil_socket_t;
+#endif
+
+#ifndef SOCKOPT_CAST_T
+#ifndef _WIN32
+#define SOCKOPT_CAST_T void
+#else
+#define SOCKOPT_CAST_T char
+#endif // _WIN32
+#endif
+
+template <class T>
+inline const SOCKOPT_CAST_T* const_cast_sockopt(const T* v) {
+ return reinterpret_cast<const SOCKOPT_CAST_T*>(v);
+}
+
+template <class T>
+inline SOCKOPT_CAST_T* cast_sockopt(T* v) {
+ return reinterpret_cast<SOCKOPT_CAST_T*>(v);
+}
+
+/**
+ * This is a non-blocking server in C++ for high performance that
+ * operates a set of IO threads (by default only one). It assumes that
+ * all incoming requests are framed with a 4 byte length indicator and
+ * writes out responses using the same framing.
+ */
+
+/// Overload condition actions.
+enum TOverloadAction {
+ T_OVERLOAD_NO_ACTION, ///< Don't handle overload */
+ T_OVERLOAD_CLOSE_ON_ACCEPT, ///< Drop new connections immediately */
+ T_OVERLOAD_DRAIN_TASK_QUEUE ///< Drop some tasks from head of task queue */
+};
+
+class TNonblockingIOThread;
+
+class TNonblockingServer : public TServer {
+private:
+ class TConnection;
+
+ friend class TNonblockingIOThread;
+
+private:
+ /// Listen backlog
+ static const int LISTEN_BACKLOG = 1024;
+
+ /// Default limit on size of idle connection pool
+ static const size_t CONNECTION_STACK_LIMIT = 1024;
+
+ /// Default limit on frame size
+ static const int MAX_FRAME_SIZE = 256 * 1024 * 1024;
+
+ /// Default limit on total number of connected sockets
+ static const int MAX_CONNECTIONS = INT_MAX;
+
+ /// Default limit on connections in handler/task processing
+ static const int MAX_ACTIVE_PROCESSORS = INT_MAX;
+
+ /// Default size of write buffer
+ static const int WRITE_BUFFER_DEFAULT_SIZE = 1024;
+
+ /// Maximum size of read buffer allocated to idle connection (0 = unlimited)
+ static const int IDLE_READ_BUFFER_LIMIT = 1024;
+
+ /// Maximum size of write buffer allocated to idle connection (0 = unlimited)
+ static const int IDLE_WRITE_BUFFER_LIMIT = 1024;
+
+ /// # of calls before resizing oversized buffers (0 = check only on close)
+ static const int RESIZE_BUFFER_EVERY_N = 512;
+
+ /// # of IO threads to use by default
+ static const int DEFAULT_IO_THREADS = 1;
+
+ /// # of IO threads this server will use
+ size_t numIOThreads_;
+
+ /// Whether to set high scheduling priority for IO threads
+ bool useHighPriorityIOThreads_;
+
+ /// Server socket file descriptor
+ THRIFT_SOCKET serverSocket_;
+
+ /// The optional user-provided event-base (for single-thread servers)
+ event_base* userEventBase_;
+
+ /// For processing via thread pool, may be NULL
+ std::shared_ptr<ThreadManager> threadManager_;
+
+ /// Is thread pool processing?
+ bool threadPoolProcessing_;
+
+ // Factory to create the IO threads
+ std::shared_ptr<ThreadFactory> ioThreadFactory_;
+
+ // Vector of IOThread objects that will handle our IO
+ std::vector<std::shared_ptr<TNonblockingIOThread> > ioThreads_;
+
+ // Index of next IO Thread to be used (for round-robin)
+ uint32_t nextIOThread_;
+
+ // Synchronizes access to connection stack and similar data
+ Mutex connMutex_;
+
+ /// Number of TConnection object we've created
+ size_t numTConnections_;
+
+ /// Number of Connections processing or waiting to process
+ size_t numActiveProcessors_;
+
+ /// Limit for how many TConnection objects to cache
+ size_t connectionStackLimit_;
+
+ /// Limit for number of connections processing or waiting to process
+ size_t maxActiveProcessors_;
+
+ /// Limit for number of open connections
+ size_t maxConnections_;
+
+ /// Limit for frame size
+ size_t maxFrameSize_;
+
+ /// Time in milliseconds before an unperformed task expires (0 == infinite).
+ int64_t taskExpireTime_;
+
+ /**
+ * Hysteresis for overload state. This is the fraction of the overload
+ * value that needs to be reached before the overload state is cleared;
+ * must be <= 1.0.
+ */
+ double overloadHysteresis_;
+
+ /// Action to take when we're overloaded.
+ TOverloadAction overloadAction_;
+
+ /**
+ * The write buffer is initialized (and when idleWriteBufferLimit_ is checked
+ * and found to be exceeded, reinitialized) to this size.
+ */
+ size_t writeBufferDefaultSize_;
+
+ /**
+ * Max read buffer size for an idle TConnection. When we place an idle
+ * TConnection into connectionStack_ or on every resizeBufferEveryN_ calls,
+ * we will free the buffer (such that it will be reinitialized by the next
+ * received frame) if it has exceeded this limit. 0 disables this check.
+ */
+ size_t idleReadBufferLimit_;
+
+ /**
+ * Max write buffer size for an idle connection. When we place an idle
+ * TConnection into connectionStack_ or on every resizeBufferEveryN_ calls,
+ * we insure that its write buffer is <= to this size; otherwise we
+ * replace it with a new one of writeBufferDefaultSize_ bytes to insure that
+ * idle connections don't hog memory. 0 disables this check.
+ */
+ size_t idleWriteBufferLimit_;
+
+ /**
+ * Every N calls we check the buffer size limits on a connected TConnection.
+ * 0 disables (i.e. the checks are only done when a connection closes).
+ */
+ int32_t resizeBufferEveryN_;
+
+ /// Set if we are currently in an overloaded state.
+ bool overloaded_;
+
+ /// Count of connections dropped since overload started
+ uint32_t nConnectionsDropped_;
+
+ /// Count of connections dropped on overload since server started
+ uint64_t nTotalConnectionsDropped_;
+
+ /**
+ * This is a stack of all the objects that have been created but that
+ * are NOT currently in use. When we close a connection, we place it on this
+ * stack so that the object can be reused later, rather than freeing the
+ * memory and reallocating a new object later.
+ */
+ std::stack<TConnection*> connectionStack_;
+
+ /**
+ * This container holds pointers to all active connections. This container
+ * allows the server to clean up unlcosed connection objects at destruction,
+ * which in turn allows their transports, protocols, processors and handlers
+ * to deallocate and clean up correctly.
+ */
+ std::vector<TConnection*> activeConnections_;
+
+ /*
+ */
+ std::shared_ptr<TNonblockingServerTransport> serverTransport_;
+
+ /**
+ * Called when server socket had something happen. We accept all waiting
+ * client connections on listen socket fd and assign TConnection objects
+ * to handle those requests.
+ *
+ * @param which the event flag that triggered the handler.
+ */
+ void handleEvent(THRIFT_SOCKET fd, short which);
+
+ void init() {
+ serverSocket_ = THRIFT_INVALID_SOCKET;
+ numIOThreads_ = DEFAULT_IO_THREADS;
+ nextIOThread_ = 0;
+ useHighPriorityIOThreads_ = false;
+ userEventBase_ = nullptr;
+ threadPoolProcessing_ = false;
+ numTConnections_ = 0;
+ numActiveProcessors_ = 0;
+ connectionStackLimit_ = CONNECTION_STACK_LIMIT;
+ maxActiveProcessors_ = MAX_ACTIVE_PROCESSORS;
+ maxConnections_ = MAX_CONNECTIONS;
+ maxFrameSize_ = MAX_FRAME_SIZE;
+ taskExpireTime_ = 0;
+ overloadHysteresis_ = 0.8;
+ overloadAction_ = T_OVERLOAD_NO_ACTION;
+ writeBufferDefaultSize_ = WRITE_BUFFER_DEFAULT_SIZE;
+ idleReadBufferLimit_ = IDLE_READ_BUFFER_LIMIT;
+ idleWriteBufferLimit_ = IDLE_WRITE_BUFFER_LIMIT;
+ resizeBufferEveryN_ = RESIZE_BUFFER_EVERY_N;
+ overloaded_ = false;
+ nConnectionsDropped_ = 0;
+ nTotalConnectionsDropped_ = 0;
+ }
+
+public:
+ TNonblockingServer(const std::shared_ptr<TProcessorFactory>& processorFactory,
+ const std::shared_ptr<apache::thrift::transport::TNonblockingServerTransport>& serverTransport)
+ : TServer(processorFactory), serverTransport_(serverTransport) {
+ init();
+ }
+
+ TNonblockingServer(const std::shared_ptr<TProcessor>& processor,
+ const std::shared_ptr<apache::thrift::transport::TNonblockingServerTransport>& serverTransport)
+ : TServer(processor), serverTransport_(serverTransport) {
+ init();
+ }
+
+
+ TNonblockingServer(const std::shared_ptr<TProcessorFactory>& processorFactory,
+ const std::shared_ptr<TProtocolFactory>& protocolFactory,
+ const std::shared_ptr<apache::thrift::transport::TNonblockingServerTransport>& serverTransport,
+ const std::shared_ptr<ThreadManager>& threadManager
+ = std::shared_ptr<ThreadManager>())
+ : TServer(processorFactory), serverTransport_(serverTransport) {
+ init();
+
+ setInputProtocolFactory(protocolFactory);
+ setOutputProtocolFactory(protocolFactory);
+ setThreadManager(threadManager);
+ }
+
+ TNonblockingServer(const std::shared_ptr<TProcessor>& processor,
+ const std::shared_ptr<TProtocolFactory>& protocolFactory,
+ const std::shared_ptr<apache::thrift::transport::TNonblockingServerTransport>& serverTransport,
+ const std::shared_ptr<ThreadManager>& threadManager
+ = std::shared_ptr<ThreadManager>())
+ : TServer(processor), serverTransport_(serverTransport) {
+ init();
+
+ setInputProtocolFactory(protocolFactory);
+ setOutputProtocolFactory(protocolFactory);
+ setThreadManager(threadManager);
+ }
+
+ TNonblockingServer(const std::shared_ptr<TProcessorFactory>& processorFactory,
+ const std::shared_ptr<TTransportFactory>& inputTransportFactory,
+ const std::shared_ptr<TTransportFactory>& outputTransportFactory,
+ const std::shared_ptr<TProtocolFactory>& inputProtocolFactory,
+ const std::shared_ptr<TProtocolFactory>& outputProtocolFactory,
+ const std::shared_ptr<apache::thrift::transport::TNonblockingServerTransport>& serverTransport,
+ const std::shared_ptr<ThreadManager>& threadManager
+ = std::shared_ptr<ThreadManager>())
+ : TServer(processorFactory), serverTransport_(serverTransport) {
+ init();
+
+ setInputTransportFactory(inputTransportFactory);
+ setOutputTransportFactory(outputTransportFactory);
+ setInputProtocolFactory(inputProtocolFactory);
+ setOutputProtocolFactory(outputProtocolFactory);
+ setThreadManager(threadManager);
+ }
+
+ TNonblockingServer(const std::shared_ptr<TProcessor>& processor,
+ const std::shared_ptr<TTransportFactory>& inputTransportFactory,
+ const std::shared_ptr<TTransportFactory>& outputTransportFactory,
+ const std::shared_ptr<TProtocolFactory>& inputProtocolFactory,
+ const std::shared_ptr<TProtocolFactory>& outputProtocolFactory,
+ const std::shared_ptr<apache::thrift::transport::TNonblockingServerTransport>& serverTransport,
+ const std::shared_ptr<ThreadManager>& threadManager
+ = std::shared_ptr<ThreadManager>())
+ : TServer(processor), serverTransport_(serverTransport) {
+ init();
+
+ setInputTransportFactory(inputTransportFactory);
+ setOutputTransportFactory(outputTransportFactory);
+ setInputProtocolFactory(inputProtocolFactory);
+ setOutputProtocolFactory(outputProtocolFactory);
+ setThreadManager(threadManager);
+ }
+
+ ~TNonblockingServer() override;
+
+ void setThreadManager(std::shared_ptr<ThreadManager> threadManager);
+
+ int getListenPort() { return serverTransport_->getListenPort(); }
+
+ std::shared_ptr<ThreadManager> getThreadManager() { return threadManager_; }
+
+ /**
+ * Sets the number of IO threads used by this server. Can only be used before
+ * the call to serve() and has no effect afterwards.
+ */
+ void setNumIOThreads(size_t numThreads) {
+ numIOThreads_ = numThreads;
+ // User-provided event-base doesn't works for multi-threaded servers
+ assert(numIOThreads_ <= 1 || !userEventBase_);
+ }
+
+ /** Return whether the IO threads will get high scheduling priority */
+ bool useHighPriorityIOThreads() const { return useHighPriorityIOThreads_; }
+
+ /** Set whether the IO threads will get high scheduling priority. */
+ void setUseHighPriorityIOThreads(bool val) { useHighPriorityIOThreads_ = val; }
+
+ /** Return the number of IO threads used by this server. */
+ size_t getNumIOThreads() const { return numIOThreads_; }
+
+ /**
+ * Get the maximum number of unused TConnection we will hold in reserve.
+ *
+ * @return the current limit on TConnection pool size.
+ */
+ size_t getConnectionStackLimit() const { return connectionStackLimit_; }
+
+ /**
+ * Set the maximum number of unused TConnection we will hold in reserve.
+ *
+ * @param sz the new limit for TConnection pool size.
+ */
+ void setConnectionStackLimit(size_t sz) { connectionStackLimit_ = sz; }
+
+ bool isThreadPoolProcessing() const { return threadPoolProcessing_; }
+
+ void addTask(std::shared_ptr<Runnable> task) {
+ threadManager_->add(task, 0LL, taskExpireTime_);
+ }
+
+ /**
+ * Return the count of sockets currently connected to.
+ *
+ * @return count of connected sockets.
+ */
+ size_t getNumConnections() const { return numTConnections_; }
+
+ /**
+ * Return the count of sockets currently connected to.
+ *
+ * @return count of connected sockets.
+ */
+ size_t getNumActiveConnections() const { return getNumConnections() - getNumIdleConnections(); }
+
+ /**
+ * Return the count of connection objects allocated but not in use.
+ *
+ * @return count of idle connection objects.
+ */
+ size_t getNumIdleConnections() const { return connectionStack_.size(); }
+
+ /**
+ * Return count of number of connections which are currently processing.
+ * This is defined as a connection where all data has been received and
+ * either assigned a task (when threading) or passed to a handler (when
+ * not threading), and where the handler has not yet returned.
+ *
+ * @return # of connections currently processing.
+ */
+ size_t getNumActiveProcessors() const { return numActiveProcessors_; }
+
+ /// Increment the count of connections currently processing.
+ void incrementActiveProcessors() {
+ Guard g(connMutex_);
+ ++numActiveProcessors_;
+ }
+
+ /// Decrement the count of connections currently processing.
+ void decrementActiveProcessors() {
+ Guard g(connMutex_);
+ if (numActiveProcessors_ > 0) {
+ --numActiveProcessors_;
+ }
+ }
+
+ /**
+ * Get the maximum # of connections allowed before overload.
+ *
+ * @return current setting.
+ */
+ size_t getMaxConnections() const { return maxConnections_; }
+
+ /**
+ * Set the maximum # of connections allowed before overload.
+ *
+ * @param maxConnections new setting for maximum # of connections.
+ */
+ void setMaxConnections(size_t maxConnections) { maxConnections_ = maxConnections; }
+
+ /**
+ * Get the maximum # of connections waiting in handler/task before overload.
+ *
+ * @return current setting.
+ */
+ size_t getMaxActiveProcessors() const { return maxActiveProcessors_; }
+
+ /**
+ * Set the maximum # of connections waiting in handler/task before overload.
+ *
+ * @param maxActiveProcessors new setting for maximum # of active processes.
+ */
+ void setMaxActiveProcessors(size_t maxActiveProcessors) {
+ maxActiveProcessors_ = maxActiveProcessors;
+ }
+
+ /**
+ * Get the maximum allowed frame size.
+ *
+ * If a client tries to send a message larger than this limit,
+ * its connection will be closed.
+ *
+ * @return Maxium frame size, in bytes.
+ */
+ size_t getMaxFrameSize() const { return maxFrameSize_; }
+
+ /**
+ * Set the maximum allowed frame size.
+ *
+ * @param maxFrameSize The new maximum frame size.
+ */
+ void setMaxFrameSize(size_t maxFrameSize) { maxFrameSize_ = maxFrameSize; }
+
+ /**
+ * Get fraction of maximum limits before an overload condition is cleared.
+ *
+ * @return hysteresis fraction
+ */
+ double getOverloadHysteresis() const { return overloadHysteresis_; }
+
+ /**
+ * Set fraction of maximum limits before an overload condition is cleared.
+ * A good value would probably be between 0.5 and 0.9.
+ *
+ * @param hysteresisFraction fraction <= 1.0.
+ */
+ void setOverloadHysteresis(double hysteresisFraction) {
+ if (hysteresisFraction <= 1.0 && hysteresisFraction > 0.0) {
+ overloadHysteresis_ = hysteresisFraction;
+ }
+ }
+
+ /**
+ * Get the action the server will take on overload.
+ *
+ * @return a TOverloadAction enum value for the currently set action.
+ */
+ TOverloadAction getOverloadAction() const { return overloadAction_; }
+
+ /**
+ * Set the action the server is to take on overload.
+ *
+ * @param overloadAction a TOverloadAction enum value for the action.
+ */
+ void setOverloadAction(TOverloadAction overloadAction) { overloadAction_ = overloadAction; }
+
+ /**
+ * Get the time in milliseconds after which a task expires (0 == infinite).
+ *
+ * @return a 64-bit time in milliseconds.
+ */
+ int64_t getTaskExpireTime() const { return taskExpireTime_; }
+
+ /**
+ * Set the time in milliseconds after which a task expires (0 == infinite).
+ *
+ * @param taskExpireTime a 64-bit time in milliseconds.
+ */
+ void setTaskExpireTime(int64_t taskExpireTime) { taskExpireTime_ = taskExpireTime; }
+
+ /**
+ * Determine if the server is currently overloaded.
+ * This function checks the maximums for open connections and connections
+ * currently in processing, and sets an overload condition if they are
+ * exceeded. The overload will persist until both values are below the
+ * current hysteresis fraction of their maximums.
+ *
+ * @return true if an overload condition exists, false if not.
+ */
+ bool serverOverloaded();
+
+ /** Pop and discard next task on threadpool wait queue.
+ *
+ * @return true if a task was discarded, false if the wait queue was empty.
+ */
+ bool drainPendingTask();
+
+ /**
+ * Get the starting size of a TConnection object's write buffer.
+ *
+ * @return # bytes we initialize a TConnection object's write buffer to.
+ */
+ size_t getWriteBufferDefaultSize() const { return writeBufferDefaultSize_; }
+
+ /**
+ * Set the starting size of a TConnection object's write buffer.
+ *
+ * @param size # bytes we initialize a TConnection object's write buffer to.
+ */
+ void setWriteBufferDefaultSize(size_t size) { writeBufferDefaultSize_ = size; }
+
+ /**
+ * Get the maximum size of read buffer allocated to idle TConnection objects.
+ *
+ * @return # bytes beyond which we will dealloc idle buffer.
+ */
+ size_t getIdleReadBufferLimit() const { return idleReadBufferLimit_; }
+
+ /**
+ * [NOTE: This is for backwards compatibility, use getIdleReadBufferLimit().]
+ * Get the maximum size of read buffer allocated to idle TConnection objects.
+ *
+ * @return # bytes beyond which we will dealloc idle buffer.
+ */
+ size_t getIdleBufferMemLimit() const { return idleReadBufferLimit_; }
+
+ /**
+ * Set the maximum size read buffer allocated to idle TConnection objects.
+ * If a TConnection object is found (either on connection close or between
+ * calls when resizeBufferEveryN_ is set) with more than this much memory
+ * allocated to its read buffer, we free it and allow it to be reinitialized
+ * on the next received frame.
+ *
+ * @param limit of bytes beyond which we will shrink buffers when checked.
+ */
+ void setIdleReadBufferLimit(size_t limit) { idleReadBufferLimit_ = limit; }
+
+ /**
+ * [NOTE: This is for backwards compatibility, use setIdleReadBufferLimit().]
+ * Set the maximum size read buffer allocated to idle TConnection objects.
+ * If a TConnection object is found (either on connection close or between
+ * calls when resizeBufferEveryN_ is set) with more than this much memory
+ * allocated to its read buffer, we free it and allow it to be reinitialized
+ * on the next received frame.
+ *
+ * @param limit of bytes beyond which we will shrink buffers when checked.
+ */
+ void setIdleBufferMemLimit(size_t limit) { idleReadBufferLimit_ = limit; }
+
+ /**
+ * Get the maximum size of write buffer allocated to idle TConnection objects.
+ *
+ * @return # bytes beyond which we will reallocate buffers when checked.
+ */
+ size_t getIdleWriteBufferLimit() const { return idleWriteBufferLimit_; }
+
+ /**
+ * Set the maximum size write buffer allocated to idle TConnection objects.
+ * If a TConnection object is found (either on connection close or between
+ * calls when resizeBufferEveryN_ is set) with more than this much memory
+ * allocated to its write buffer, we destroy and construct that buffer with
+ * writeBufferDefaultSize_ bytes.
+ *
+ * @param limit of bytes beyond which we will shrink buffers when idle.
+ */
+ void setIdleWriteBufferLimit(size_t limit) { idleWriteBufferLimit_ = limit; }
+
+ /**
+ * Get # of calls made between buffer size checks. 0 means disabled.
+ *
+ * @return # of calls between buffer size checks.
+ */
+ int32_t getResizeBufferEveryN() const { return resizeBufferEveryN_; }
+
+ /**
+ * Check buffer sizes every "count" calls. This allows buffer limits
+ * to be enforced for persistent connections with a controllable degree
+ * of overhead. 0 disables checks except at connection close.
+ *
+ * @param count the number of calls between checks, or 0 to disable
+ */
+ void setResizeBufferEveryN(int32_t count) { resizeBufferEveryN_ = count; }
+
+ /**
+ * Main workhorse function, starts up the server listening on a port and
+ * loops over the libevent handler.
+ */
+ void serve() override;
+
+ /**
+ * Causes the server to terminate gracefully (can be called from any thread).
+ */
+ void stop() override;
+
+ /// Creates a socket to listen on and binds it to the local port.
+ void createAndListenOnSocket();
+
+ /**
+ * Register the optional user-provided event-base (for single-thread servers)
+ *
+ * This method should be used when the server is running in a single-thread
+ * mode, and the event base is provided by the user (i.e., the caller).
+ *
+ * @param user_event_base the user-provided event-base. The user is
+ * responsible for freeing the event base memory.
+ */
+ void registerEvents(event_base* user_event_base);
+
+ /**
+ * Returns the optional user-provided event-base (for single-thread servers).
+ */
+ event_base* getUserEventBase() const { return userEventBase_; }
+
+ /** Some transports, like THeaderTransport, require passing through
+ * the framing size instead of stripping it.
+ */
+ bool getHeaderTransport();
+
+private:
+ /**
+ * Callback function that the threadmanager calls when a task reaches
+ * its expiration time. It is needed to clean up the expired connection.
+ *
+ * @param task the runnable associated with the expired task.
+ */
+ void expireClose(std::shared_ptr<Runnable> task);
+
+ /**
+ * Return an initialized connection object. Creates or recovers from
+ * pool a TConnection and initializes it with the provided socket FD
+ * and flags.
+ *
+ * @param socket FD of socket associated with this connection.
+ * @param addr the sockaddr of the client
+ * @param addrLen the length of addr
+ * @return pointer to initialized TConnection object.
+ */
+ TConnection* createConnection(std::shared_ptr<TSocket> socket);
+
+ /**
+ * Returns a connection to pool or deletion. If the connection pool
+ * (a stack) isn't full, place the connection object on it, otherwise
+ * just delete it.
+ *
+ * @param connection the TConection being returned.
+ */
+ void returnConnection(TConnection* connection);
+};
+
+class TNonblockingIOThread : public Runnable {
+public:
+ // Creates an IO thread and sets up the event base. The listenSocket should
+ // be a valid FD on which listen() has already been called. If the
+ // listenSocket is < 0, accepting will not be done.
+ TNonblockingIOThread(TNonblockingServer* server,
+ int number,
+ THRIFT_SOCKET listenSocket,
+ bool useHighPriority);
+
+ ~TNonblockingIOThread() override;
+
+ // Returns the event-base for this thread.
+ event_base* getEventBase() const { return eventBase_; }
+
+ // Returns the server for this thread.
+ TNonblockingServer* getServer() const { return server_; }
+
+ // Returns the number of this IO thread.
+ int getThreadNumber() const { return number_; }
+
+ // Returns the thread id associated with this object. This should
+ // only be called after the thread has been started.
+ Thread::id_t getThreadId() const { return threadId_; }
+
+ // Returns the send-fd for task complete notifications.
+ evutil_socket_t getNotificationSendFD() const { return notificationPipeFDs_[1]; }
+
+ // Returns the read-fd for task complete notifications.
+ evutil_socket_t getNotificationRecvFD() const { return notificationPipeFDs_[0]; }
+
+ // Returns the actual thread object associated with this IO thread.
+ std::shared_ptr<Thread> getThread() const { return thread_; }
+
+ // Sets the actual thread object associated with this IO thread.
+ void setThread(const std::shared_ptr<Thread>& t) { thread_ = t; }
+
+ // Used by TConnection objects to indicate processing has finished.
+ bool notify(TNonblockingServer::TConnection* conn);
+
+ // Enters the event loop and does not return until a call to stop().
+ void run() override;
+
+ // Exits the event loop as soon as possible.
+ void stop();
+
+ // Ensures that the event-loop thread is fully finished and shut down.
+ void join();
+
+ /// Registers the events for the notification & listen sockets
+ void registerEvents();
+
+private:
+ /**
+ * C-callable event handler for signaling task completion. Provides a
+ * callback that libevent can understand that will read a connection
+ * object's address from a pipe and call connection->transition() for
+ * that object.
+ *
+ * @param fd the descriptor the event occurred on.
+ */
+ static void notifyHandler(evutil_socket_t fd, short which, void* v);
+
+ /**
+ * C-callable event handler for listener events. Provides a callback
+ * that libevent can understand which invokes server->handleEvent().
+ *
+ * @param fd the descriptor the event occurred on.
+ * @param which the flags associated with the event.
+ * @param v void* callback arg where we placed TNonblockingServer's "this".
+ */
+ static void listenHandler(evutil_socket_t fd, short which, void* v) {
+ ((TNonblockingServer*)v)->handleEvent(fd, which);
+ }
+
+ /// Exits the loop ASAP in case of shutdown or error.
+ void breakLoop(bool error);
+
+ /// Create the pipe used to notify I/O process of task completion.
+ void createNotificationPipe();
+
+ /// Unregisters our events for notification and listen sockets.
+ void cleanupEvents();
+
+ /// Sets (or clears) high priority scheduling status for the current thread.
+ void setCurrentThreadHighPriority(bool value);
+
+private:
+ /// associated server
+ TNonblockingServer* server_;
+
+ /// thread number (for debugging).
+ const int number_;
+
+ /// The actual physical thread id.
+ Thread::id_t threadId_;
+
+ /// If listenSocket_ >= 0, adds an event on the event_base to accept conns
+ THRIFT_SOCKET listenSocket_;
+
+ /// Sets a high scheduling priority when running
+ bool useHighPriority_;
+
+ /// pointer to eventbase to be used for looping
+ event_base* eventBase_;
+
+ /// Set to true if this class is responsible for freeing the event base
+ /// memory.
+ bool ownEventBase_;
+
+ /// Used with eventBase_ for connection events (only in listener thread)
+ struct event serverEvent_;
+
+ /// Used with eventBase_ for task completion notification
+ struct event notificationEvent_;
+
+ /// File descriptors for pipe used for task completion notification.
+ evutil_socket_t notificationPipeFDs_[2];
+
+ /// Actual IO Thread
+ std::shared_ptr<Thread> thread_;
+};
+}
+}
+} // apache::thrift::server
+
+#endif // #ifndef _THRIFT_SERVER_TNONBLOCKINGSERVER_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TServer.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TServer.cpp
new file mode 100644
index 000000000..df731c2f0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TServer.cpp
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+namespace apache {
+namespace thrift {
+namespace server {
+
+#ifdef HAVE_SYS_RESOURCE_H
+int increase_max_fds(int max_fds = (1 << 24)) {
+ struct rlimit fdmaxrl;
+
+ for (fdmaxrl.rlim_cur = max_fds, fdmaxrl.rlim_max = max_fds;
+ max_fds && (setrlimit(RLIMIT_NOFILE, &fdmaxrl) < 0);
+ fdmaxrl.rlim_cur = max_fds, fdmaxrl.rlim_max = max_fds) {
+ max_fds /= 2;
+ }
+
+ return static_cast<int>(fdmaxrl.rlim_cur);
+}
+#endif
+}
+}
+} // apache::thrift::server
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TServer.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TServer.h
new file mode 100644
index 000000000..d2eabde12
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TServer.h
@@ -0,0 +1,273 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_SERVER_TSERVER_H_
+#define _THRIFT_SERVER_TSERVER_H_ 1
+
+#include <thrift/TProcessor.h>
+#include <thrift/transport/TServerTransport.h>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/concurrency/Thread.h>
+
+#include <memory>
+
+namespace apache {
+namespace thrift {
+namespace server {
+
+using apache::thrift::TProcessor;
+using apache::thrift::protocol::TBinaryProtocolFactory;
+using apache::thrift::protocol::TProtocol;
+using apache::thrift::protocol::TProtocolFactory;
+using apache::thrift::transport::TServerTransport;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportFactory;
+
+/**
+ * Virtual interface class that can handle events from the server core. To
+ * use this you should subclass it and implement the methods that you care
+ * about. Your subclass can also store local data that you may care about,
+ * such as additional "arguments" to these methods (stored in the object
+ * instance's state).
+ */
+class TServerEventHandler {
+public:
+ virtual ~TServerEventHandler() = default;
+
+ /**
+ * Called before the server begins.
+ */
+ virtual void preServe() {}
+
+ /**
+ * Called when a new client has connected and is about to being processing.
+ */
+ virtual void* createContext(std::shared_ptr<TProtocol> input,
+ std::shared_ptr<TProtocol> output) {
+ (void)input;
+ (void)output;
+ return nullptr;
+ }
+
+ /**
+ * Called when a client has finished request-handling to delete server
+ * context.
+ */
+ virtual void deleteContext(void* serverContext,
+ std::shared_ptr<TProtocol> input,
+ std::shared_ptr<TProtocol> output) {
+ (void)serverContext;
+ (void)input;
+ (void)output;
+ }
+
+ /**
+ * Called when a client is about to call the processor.
+ */
+ virtual void processContext(void* serverContext, std::shared_ptr<TTransport> transport) {
+ (void)serverContext;
+ (void)transport;
+ }
+
+protected:
+ /**
+ * Prevent direct instantiation.
+ */
+ TServerEventHandler() = default;
+};
+
+/**
+ * Thrift server.
+ *
+ */
+class TServer : public concurrency::Runnable {
+public:
+ ~TServer() override = default;
+
+ virtual void serve() = 0;
+
+ virtual void stop() {}
+
+ // Allows running the server as a Runnable thread
+ void run() override { serve(); }
+
+ std::shared_ptr<TProcessorFactory> getProcessorFactory() { return processorFactory_; }
+
+ std::shared_ptr<TServerTransport> getServerTransport() { return serverTransport_; }
+
+ std::shared_ptr<TTransportFactory> getInputTransportFactory() { return inputTransportFactory_; }
+
+ std::shared_ptr<TTransportFactory> getOutputTransportFactory() {
+ return outputTransportFactory_;
+ }
+
+ std::shared_ptr<TProtocolFactory> getInputProtocolFactory() { return inputProtocolFactory_; }
+
+ std::shared_ptr<TProtocolFactory> getOutputProtocolFactory() { return outputProtocolFactory_; }
+
+ std::shared_ptr<TServerEventHandler> getEventHandler() { return eventHandler_; }
+
+protected:
+ TServer(const std::shared_ptr<TProcessorFactory>& processorFactory)
+ : processorFactory_(processorFactory) {
+ setInputTransportFactory(std::shared_ptr<TTransportFactory>(new TTransportFactory()));
+ setOutputTransportFactory(std::shared_ptr<TTransportFactory>(new TTransportFactory()));
+ setInputProtocolFactory(std::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory()));
+ setOutputProtocolFactory(std::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory()));
+ }
+
+ TServer(const std::shared_ptr<TProcessor>& processor)
+ : processorFactory_(new TSingletonProcessorFactory(processor)) {
+ setInputTransportFactory(std::shared_ptr<TTransportFactory>(new TTransportFactory()));
+ setOutputTransportFactory(std::shared_ptr<TTransportFactory>(new TTransportFactory()));
+ setInputProtocolFactory(std::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory()));
+ setOutputProtocolFactory(std::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory()));
+ }
+
+ TServer(const std::shared_ptr<TProcessorFactory>& processorFactory,
+ const std::shared_ptr<TServerTransport>& serverTransport)
+ : processorFactory_(processorFactory), serverTransport_(serverTransport) {
+ setInputTransportFactory(std::shared_ptr<TTransportFactory>(new TTransportFactory()));
+ setOutputTransportFactory(std::shared_ptr<TTransportFactory>(new TTransportFactory()));
+ setInputProtocolFactory(std::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory()));
+ setOutputProtocolFactory(std::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory()));
+ }
+
+ TServer(const std::shared_ptr<TProcessor>& processor,
+ const std::shared_ptr<TServerTransport>& serverTransport)
+ : processorFactory_(new TSingletonProcessorFactory(processor)),
+ serverTransport_(serverTransport) {
+ setInputTransportFactory(std::shared_ptr<TTransportFactory>(new TTransportFactory()));
+ setOutputTransportFactory(std::shared_ptr<TTransportFactory>(new TTransportFactory()));
+ setInputProtocolFactory(std::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory()));
+ setOutputProtocolFactory(std::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory()));
+ }
+
+ TServer(const std::shared_ptr<TProcessorFactory>& processorFactory,
+ const std::shared_ptr<TServerTransport>& serverTransport,
+ const std::shared_ptr<TTransportFactory>& transportFactory,
+ const std::shared_ptr<TProtocolFactory>& protocolFactory)
+ : processorFactory_(processorFactory),
+ serverTransport_(serverTransport),
+ inputTransportFactory_(transportFactory),
+ outputTransportFactory_(transportFactory),
+ inputProtocolFactory_(protocolFactory),
+ outputProtocolFactory_(protocolFactory) {}
+
+ TServer(const std::shared_ptr<TProcessor>& processor,
+ const std::shared_ptr<TServerTransport>& serverTransport,
+ const std::shared_ptr<TTransportFactory>& transportFactory,
+ const std::shared_ptr<TProtocolFactory>& protocolFactory)
+ : processorFactory_(new TSingletonProcessorFactory(processor)),
+ serverTransport_(serverTransport),
+ inputTransportFactory_(transportFactory),
+ outputTransportFactory_(transportFactory),
+ inputProtocolFactory_(protocolFactory),
+ outputProtocolFactory_(protocolFactory) {}
+
+ TServer(const std::shared_ptr<TProcessorFactory>& processorFactory,
+ const std::shared_ptr<TServerTransport>& serverTransport,
+ const std::shared_ptr<TTransportFactory>& inputTransportFactory,
+ const std::shared_ptr<TTransportFactory>& outputTransportFactory,
+ const std::shared_ptr<TProtocolFactory>& inputProtocolFactory,
+ const std::shared_ptr<TProtocolFactory>& outputProtocolFactory)
+ : processorFactory_(processorFactory),
+ serverTransport_(serverTransport),
+ inputTransportFactory_(inputTransportFactory),
+ outputTransportFactory_(outputTransportFactory),
+ inputProtocolFactory_(inputProtocolFactory),
+ outputProtocolFactory_(outputProtocolFactory) {}
+
+ TServer(const std::shared_ptr<TProcessor>& processor,
+ const std::shared_ptr<TServerTransport>& serverTransport,
+ const std::shared_ptr<TTransportFactory>& inputTransportFactory,
+ const std::shared_ptr<TTransportFactory>& outputTransportFactory,
+ const std::shared_ptr<TProtocolFactory>& inputProtocolFactory,
+ const std::shared_ptr<TProtocolFactory>& outputProtocolFactory)
+ : processorFactory_(new TSingletonProcessorFactory(processor)),
+ serverTransport_(serverTransport),
+ inputTransportFactory_(inputTransportFactory),
+ outputTransportFactory_(outputTransportFactory),
+ inputProtocolFactory_(inputProtocolFactory),
+ outputProtocolFactory_(outputProtocolFactory) {}
+
+ /**
+ * Get a TProcessor to handle calls on a particular connection.
+ *
+ * This method should only be called once per connection (never once per
+ * call). This allows the TProcessorFactory to return a different processor
+ * for each connection if it desires.
+ */
+ std::shared_ptr<TProcessor> getProcessor(std::shared_ptr<TProtocol> inputProtocol,
+ std::shared_ptr<TProtocol> outputProtocol,
+ std::shared_ptr<TTransport> transport) {
+ TConnectionInfo connInfo;
+ connInfo.input = inputProtocol;
+ connInfo.output = outputProtocol;
+ connInfo.transport = transport;
+ return processorFactory_->getProcessor(connInfo);
+ }
+
+ // Class variables
+ std::shared_ptr<TProcessorFactory> processorFactory_;
+ std::shared_ptr<TServerTransport> serverTransport_;
+
+ std::shared_ptr<TTransportFactory> inputTransportFactory_;
+ std::shared_ptr<TTransportFactory> outputTransportFactory_;
+
+ std::shared_ptr<TProtocolFactory> inputProtocolFactory_;
+ std::shared_ptr<TProtocolFactory> outputProtocolFactory_;
+
+ std::shared_ptr<TServerEventHandler> eventHandler_;
+
+public:
+ void setInputTransportFactory(std::shared_ptr<TTransportFactory> inputTransportFactory) {
+ inputTransportFactory_ = inputTransportFactory;
+ }
+
+ void setOutputTransportFactory(std::shared_ptr<TTransportFactory> outputTransportFactory) {
+ outputTransportFactory_ = outputTransportFactory;
+ }
+
+ void setInputProtocolFactory(std::shared_ptr<TProtocolFactory> inputProtocolFactory) {
+ inputProtocolFactory_ = inputProtocolFactory;
+ }
+
+ void setOutputProtocolFactory(std::shared_ptr<TProtocolFactory> outputProtocolFactory) {
+ outputProtocolFactory_ = outputProtocolFactory;
+ }
+
+ void setServerEventHandler(std::shared_ptr<TServerEventHandler> eventHandler) {
+ eventHandler_ = eventHandler;
+ }
+};
+
+/**
+ * Helper function to increase the max file descriptors limit
+ * for the current process and all of its children.
+ * By default, tries to increase it to as much as 2^24.
+ */
+#ifdef HAVE_SYS_RESOURCE_H
+int increase_max_fds(int max_fds = (1 << 24));
+#endif
+}
+}
+} // apache::thrift::server
+
+#endif // #ifndef _THRIFT_SERVER_TSERVER_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TServerFramework.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TServerFramework.cpp
new file mode 100644
index 000000000..35f3b254d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TServerFramework.cpp
@@ -0,0 +1,244 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <algorithm>
+#include <stdexcept>
+#include <stdint.h>
+#include <thrift/server/TServerFramework.h>
+
+namespace apache {
+namespace thrift {
+namespace server {
+
+using apache::thrift::concurrency::Synchronized;
+using apache::thrift::protocol::TProtocol;
+using apache::thrift::protocol::TProtocolFactory;
+using std::bind;
+using std::shared_ptr;
+using apache::thrift::transport::TServerTransport;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using apache::thrift::transport::TTransportFactory;
+using std::string;
+
+TServerFramework::TServerFramework(const shared_ptr<TProcessorFactory>& processorFactory,
+ const shared_ptr<TServerTransport>& serverTransport,
+ const shared_ptr<TTransportFactory>& transportFactory,
+ const shared_ptr<TProtocolFactory>& protocolFactory)
+ : TServer(processorFactory, serverTransport, transportFactory, protocolFactory),
+ clients_(0),
+ hwm_(0),
+ limit_(INT64_MAX) {
+}
+
+TServerFramework::TServerFramework(const shared_ptr<TProcessor>& processor,
+ const shared_ptr<TServerTransport>& serverTransport,
+ const shared_ptr<TTransportFactory>& transportFactory,
+ const shared_ptr<TProtocolFactory>& protocolFactory)
+ : TServer(processor, serverTransport, transportFactory, protocolFactory),
+ clients_(0),
+ hwm_(0),
+ limit_(INT64_MAX) {
+}
+
+TServerFramework::TServerFramework(const shared_ptr<TProcessorFactory>& processorFactory,
+ const shared_ptr<TServerTransport>& serverTransport,
+ const shared_ptr<TTransportFactory>& inputTransportFactory,
+ const shared_ptr<TTransportFactory>& outputTransportFactory,
+ const shared_ptr<TProtocolFactory>& inputProtocolFactory,
+ const shared_ptr<TProtocolFactory>& outputProtocolFactory)
+ : TServer(processorFactory,
+ serverTransport,
+ inputTransportFactory,
+ outputTransportFactory,
+ inputProtocolFactory,
+ outputProtocolFactory),
+ clients_(0),
+ hwm_(0),
+ limit_(INT64_MAX) {
+}
+
+TServerFramework::TServerFramework(const shared_ptr<TProcessor>& processor,
+ const shared_ptr<TServerTransport>& serverTransport,
+ const shared_ptr<TTransportFactory>& inputTransportFactory,
+ const shared_ptr<TTransportFactory>& outputTransportFactory,
+ const shared_ptr<TProtocolFactory>& inputProtocolFactory,
+ const shared_ptr<TProtocolFactory>& outputProtocolFactory)
+ : TServer(processor,
+ serverTransport,
+ inputTransportFactory,
+ outputTransportFactory,
+ inputProtocolFactory,
+ outputProtocolFactory),
+ clients_(0),
+ hwm_(0),
+ limit_(INT64_MAX) {
+}
+
+TServerFramework::~TServerFramework() = default;
+
+template <typename T>
+static void releaseOneDescriptor(const string& name, T& pTransport) {
+ if (pTransport) {
+ try {
+ pTransport->close();
+ } catch (const TTransportException& ttx) {
+ string errStr = string("TServerFramework " + name + " close failed: ") + ttx.what();
+ GlobalOutput(errStr.c_str());
+ }
+ }
+}
+
+void TServerFramework::serve() {
+ shared_ptr<TTransport> client;
+ shared_ptr<TTransport> inputTransport;
+ shared_ptr<TTransport> outputTransport;
+ shared_ptr<TProtocol> inputProtocol;
+ shared_ptr<TProtocol> outputProtocol;
+
+ // Start the server listening
+ serverTransport_->listen();
+
+ // Run the preServe event to indicate server is now listening
+ // and that it is safe to connect.
+ if (eventHandler_) {
+ eventHandler_->preServe();
+ }
+
+ // Fetch client from server
+ for (;;) {
+ try {
+ // Dereference any resources from any previous client creation
+ // such that a blocking accept does not hold them indefinitely.
+ outputProtocol.reset();
+ inputProtocol.reset();
+ outputTransport.reset();
+ inputTransport.reset();
+ client.reset();
+
+ // If we have reached the limit on the number of concurrent
+ // clients allowed, wait for one or more clients to drain before
+ // accepting another.
+ {
+ Synchronized sync(mon_);
+ while (clients_ >= limit_) {
+ mon_.wait();
+ }
+ }
+
+ client = serverTransport_->accept();
+
+ inputTransport = inputTransportFactory_->getTransport(client);
+ outputTransport = outputTransportFactory_->getTransport(client);
+ if (!outputProtocolFactory_) {
+ inputProtocol = inputProtocolFactory_->getProtocol(inputTransport, outputTransport);
+ outputProtocol = inputProtocol;
+ } else {
+ inputProtocol = inputProtocolFactory_->getProtocol(inputTransport);
+ outputProtocol = outputProtocolFactory_->getProtocol(outputTransport);
+ }
+
+ newlyConnectedClient(shared_ptr<TConnectedClient>(
+ new TConnectedClient(getProcessor(inputProtocol, outputProtocol, client),
+ inputProtocol,
+ outputProtocol,
+ eventHandler_,
+ client),
+ bind(&TServerFramework::disposeConnectedClient, this, std::placeholders::_1)));
+
+ } catch (TTransportException& ttx) {
+ releaseOneDescriptor("inputTransport", inputTransport);
+ releaseOneDescriptor("outputTransport", outputTransport);
+ releaseOneDescriptor("client", client);
+ if (ttx.getType() == TTransportException::TIMED_OUT) {
+ // Accept timeout - continue processing.
+ continue;
+ } else if (ttx.getType() == TTransportException::END_OF_FILE
+ || ttx.getType() == TTransportException::INTERRUPTED) {
+ // Server was interrupted. This only happens when stopping.
+ break;
+ } else {
+ // All other transport exceptions are logged.
+ // State of connection is unknown. Done.
+ string errStr = string("TServerTransport died: ") + ttx.what();
+ GlobalOutput(errStr.c_str());
+ break;
+ }
+ }
+ }
+
+ releaseOneDescriptor("serverTransport", serverTransport_);
+}
+
+int64_t TServerFramework::getConcurrentClientLimit() const {
+ Synchronized sync(mon_);
+ return limit_;
+}
+
+int64_t TServerFramework::getConcurrentClientCount() const {
+ Synchronized sync(mon_);
+ return clients_;
+}
+
+int64_t TServerFramework::getConcurrentClientCountHWM() const {
+ Synchronized sync(mon_);
+ return hwm_;
+}
+
+void TServerFramework::setConcurrentClientLimit(int64_t newLimit) {
+ if (newLimit < 1) {
+ throw std::invalid_argument("newLimit must be greater than zero");
+ }
+ Synchronized sync(mon_);
+ limit_ = newLimit;
+ if (limit_ - clients_ > 0) {
+ mon_.notify();
+ }
+}
+
+void TServerFramework::stop() {
+ // Order is important because serve() releases serverTransport_ when it is
+ // interrupted, which closes the socket that interruptChildren uses.
+ serverTransport_->interruptChildren();
+ serverTransport_->interrupt();
+}
+
+void TServerFramework::newlyConnectedClient(const shared_ptr<TConnectedClient>& pClient) {
+ {
+ Synchronized sync(mon_);
+ ++clients_;
+ hwm_ = (std::max)(hwm_, clients_);
+ }
+
+ onClientConnected(pClient);
+}
+
+void TServerFramework::disposeConnectedClient(TConnectedClient* pClient) {
+ onClientDisconnected(pClient);
+ delete pClient;
+
+ Synchronized sync(mon_);
+ if (limit_ - --clients_ > 0) {
+ mon_.notify();
+ }
+}
+
+}
+}
+} // apache::thrift::server
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TServerFramework.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TServerFramework.h
new file mode 100644
index 000000000..dac79ef59
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TServerFramework.h
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_SERVER_TSERVERFRAMEWORK_H_
+#define _THRIFT_SERVER_TSERVERFRAMEWORK_H_ 1
+
+#include <memory>
+#include <stdint.h>
+#include <thrift/TProcessor.h>
+#include <thrift/concurrency/Monitor.h>
+#include <thrift/server/TConnectedClient.h>
+#include <thrift/server/TServer.h>
+#include <thrift/transport/TServerTransport.h>
+#include <thrift/transport/TTransport.h>
+
+namespace apache {
+namespace thrift {
+namespace server {
+
+/**
+ * TServerFramework provides a single consolidated processing loop for
+ * servers. By having a single processing loop, behavior between servers
+ * is more predictable and maintenance cost is lowered. Implementations
+ * of TServerFramework must provide a method to deal with a client that
+ * connects and one that disconnects.
+ *
+ * While this functionality could be rolled directly into TServer, and
+ * probably should be, it would break the TServer interface contract so
+ * to maintain backwards compatibility for third party servers, no TServers
+ * were harmed in the making of this class.
+ */
+class TServerFramework : public TServer {
+public:
+ TServerFramework(
+ const std::shared_ptr<apache::thrift::TProcessorFactory>& processorFactory,
+ const std::shared_ptr<apache::thrift::transport::TServerTransport>& serverTransport,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& transportFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& protocolFactory);
+
+ TServerFramework(
+ const std::shared_ptr<apache::thrift::TProcessor>& processor,
+ const std::shared_ptr<apache::thrift::transport::TServerTransport>& serverTransport,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& transportFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& protocolFactory);
+
+ TServerFramework(
+ const std::shared_ptr<apache::thrift::TProcessorFactory>& processorFactory,
+ const std::shared_ptr<apache::thrift::transport::TServerTransport>& serverTransport,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& inputTransportFactory,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& outputTransportFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& inputProtocolFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& outputProtocolFactory);
+
+ TServerFramework(
+ const std::shared_ptr<apache::thrift::TProcessor>& processor,
+ const std::shared_ptr<apache::thrift::transport::TServerTransport>& serverTransport,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& inputTransportFactory,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& outputTransportFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& inputProtocolFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& outputProtocolFactory);
+
+ ~TServerFramework() override;
+
+ /**
+ * Accept clients from the TServerTransport and add them for processing.
+ * Call stop() on another thread to interrupt processing
+ * and return control to the caller.
+ * Post-conditions (return guarantees):
+ * The serverTransport will be closed.
+ */
+ void serve() override;
+
+ /**
+ * Interrupt serve() so that it meets post-conditions and returns.
+ */
+ void stop() override;
+
+ /**
+ * Get the concurrent client limit.
+ * \returns the concurrent client limit
+ */
+ virtual int64_t getConcurrentClientLimit() const;
+
+ /**
+ * Get the number of currently connected clients.
+ * \returns the number of currently connected clients
+ */
+ virtual int64_t getConcurrentClientCount() const;
+
+ /**
+ * Get the highest number of concurrent clients.
+ * \returns the highest number of concurrent clients
+ */
+ virtual int64_t getConcurrentClientCountHWM() const;
+
+ /**
+ * Set the concurrent client limit. This can be changed while
+ * the server is serving however it will not necessarily be
+ * enforced until the next client is accepted and added. If the
+ * limit is lowered below the number of connected clients, no
+ * action is taken to disconnect the clients.
+ * The default value used if this is not called is INT64_MAX.
+ * \param[in] newLimit the new limit of concurrent clients
+ * \throws std::invalid_argument if newLimit is less than 1
+ */
+ virtual void setConcurrentClientLimit(int64_t newLimit);
+
+protected:
+ /**
+ * A client has connected. The implementation is responsible for managing the
+ * lifetime of the client object. This is called during the serve() thread,
+ * therefore a failure to return quickly will result in new client connection
+ * delays.
+ *
+ * \param[in] pClient the newly connected client
+ */
+ virtual void onClientConnected(const std::shared_ptr<TConnectedClient>& pClient) = 0;
+
+ /**
+ * A client has disconnected.
+ * When called:
+ * The server no longer tracks the client.
+ * The client TTransport has already been closed.
+ * The implementation must not delete the pointer.
+ *
+ * \param[in] pClient the disconnected client
+ */
+ virtual void onClientDisconnected(TConnectedClient* pClient) = 0;
+
+private:
+ /**
+ * Common handling for new connected clients. Implements concurrent
+ * client rate limiting after onClientConnected returns by blocking the
+ * serve() thread if the limit has been reached.
+ */
+ void newlyConnectedClient(const std::shared_ptr<TConnectedClient>& pClient);
+
+ /**
+ * Smart pointer client deletion.
+ * Calls onClientDisconnected and then deletes pClient.
+ */
+ void disposeConnectedClient(TConnectedClient* pClient);
+
+ /**
+ * Monitor for limiting the number of concurrent clients.
+ */
+ apache::thrift::concurrency::Monitor mon_;
+
+ /**
+ * The number of concurrent clients.
+ */
+ int64_t clients_;
+
+ /**
+ * The high water mark of concurrent clients.
+ */
+ int64_t hwm_;
+
+ /**
+ * The limit on the number of concurrent clients.
+ */
+ int64_t limit_;
+};
+}
+}
+} // apache::thrift::server
+
+#endif // #ifndef _THRIFT_SERVER_TSERVERFRAMEWORK_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TSimpleServer.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TSimpleServer.cpp
new file mode 100644
index 000000000..ba7a183db
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TSimpleServer.cpp
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/server/TSimpleServer.h>
+
+namespace apache {
+namespace thrift {
+namespace server {
+
+using apache::thrift::protocol::TProtocol;
+using apache::thrift::protocol::TProtocolFactory;
+using apache::thrift::transport::TServerTransport;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using apache::thrift::transport::TTransportFactory;
+using std::shared_ptr;
+using std::string;
+
+TSimpleServer::TSimpleServer(const shared_ptr<TProcessorFactory>& processorFactory,
+ const shared_ptr<TServerTransport>& serverTransport,
+ const shared_ptr<TTransportFactory>& transportFactory,
+ const shared_ptr<TProtocolFactory>& protocolFactory)
+ : TServerFramework(processorFactory, serverTransport, transportFactory, protocolFactory) {
+ TServerFramework::setConcurrentClientLimit(1);
+}
+
+TSimpleServer::TSimpleServer(const shared_ptr<TProcessor>& processor,
+ const shared_ptr<TServerTransport>& serverTransport,
+ const shared_ptr<TTransportFactory>& transportFactory,
+ const shared_ptr<TProtocolFactory>& protocolFactory)
+ : TServerFramework(processor, serverTransport, transportFactory, protocolFactory) {
+ TServerFramework::setConcurrentClientLimit(1);
+}
+
+TSimpleServer::TSimpleServer(const shared_ptr<TProcessorFactory>& processorFactory,
+ const shared_ptr<TServerTransport>& serverTransport,
+ const shared_ptr<TTransportFactory>& inputTransportFactory,
+ const shared_ptr<TTransportFactory>& outputTransportFactory,
+ const shared_ptr<TProtocolFactory>& inputProtocolFactory,
+ const shared_ptr<TProtocolFactory>& outputProtocolFactory)
+ : TServerFramework(processorFactory,
+ serverTransport,
+ inputTransportFactory,
+ outputTransportFactory,
+ inputProtocolFactory,
+ outputProtocolFactory) {
+ TServerFramework::setConcurrentClientLimit(1);
+}
+
+TSimpleServer::TSimpleServer(const shared_ptr<TProcessor>& processor,
+ const shared_ptr<TServerTransport>& serverTransport,
+ const shared_ptr<TTransportFactory>& inputTransportFactory,
+ const shared_ptr<TTransportFactory>& outputTransportFactory,
+ const shared_ptr<TProtocolFactory>& inputProtocolFactory,
+ const shared_ptr<TProtocolFactory>& outputProtocolFactory)
+ : TServerFramework(processor,
+ serverTransport,
+ inputTransportFactory,
+ outputTransportFactory,
+ inputProtocolFactory,
+ outputProtocolFactory) {
+ TServerFramework::setConcurrentClientLimit(1);
+}
+
+TSimpleServer::~TSimpleServer() = default;
+
+/**
+ * The main body of customized implementation for TSimpleServer is quite simple:
+ * When a client connects, use the serve() thread to drive it to completion thus
+ * blocking new connections.
+ */
+void TSimpleServer::onClientConnected(const shared_ptr<TConnectedClient>& pClient) {
+ pClient->run();
+}
+
+/**
+ * TSimpleServer does not track clients so there is nothing to do here.
+ */
+void TSimpleServer::onClientDisconnected(TConnectedClient*) {
+}
+
+/**
+ * This makes little sense to the simple server because it is not capable
+ * of having more than one client at a time, so we hide it.
+ */
+void TSimpleServer::setConcurrentClientLimit(int64_t) {
+}
+}
+}
+} // apache::thrift::server
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TSimpleServer.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TSimpleServer.h
new file mode 100644
index 000000000..3afeb79d5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TSimpleServer.h
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_SERVER_TSIMPLESERVER_H_
+#define _THRIFT_SERVER_TSIMPLESERVER_H_ 1
+
+#include <thrift/server/TServerFramework.h>
+
+namespace apache {
+namespace thrift {
+namespace server {
+
+/**
+ * This is the most basic simple server. It is single-threaded and runs a
+ * continuous loop of accepting a single connection, processing requests on
+ * that connection until it closes, and then repeating.
+ */
+class TSimpleServer : public TServerFramework {
+public:
+ TSimpleServer(
+ const std::shared_ptr<apache::thrift::TProcessorFactory>& processorFactory,
+ const std::shared_ptr<apache::thrift::transport::TServerTransport>& serverTransport,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& transportFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& protocolFactory);
+
+ TSimpleServer(
+ const std::shared_ptr<apache::thrift::TProcessor>& processor,
+ const std::shared_ptr<apache::thrift::transport::TServerTransport>& serverTransport,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& transportFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& protocolFactory);
+
+ TSimpleServer(
+ const std::shared_ptr<apache::thrift::TProcessorFactory>& processorFactory,
+ const std::shared_ptr<apache::thrift::transport::TServerTransport>& serverTransport,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& inputTransportFactory,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& outputTransportFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& inputProtocolFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& outputProtocolFactory);
+
+ TSimpleServer(
+ const std::shared_ptr<apache::thrift::TProcessor>& processor,
+ const std::shared_ptr<apache::thrift::transport::TServerTransport>& serverTransport,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& inputTransportFactory,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& outputTransportFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& inputProtocolFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& outputProtocolFactory);
+
+ ~TSimpleServer() override;
+
+protected:
+ void onClientConnected(const std::shared_ptr<TConnectedClient>& pClient) override /* override */;
+ void onClientDisconnected(TConnectedClient* pClient) override /* override */;
+
+private:
+ void setConcurrentClientLimit(int64_t newLimit) override; // hide
+};
+}
+}
+} // apache::thrift::server
+
+#endif // #ifndef _THRIFT_SERVER_TSIMPLESERVER_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TThreadPoolServer.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TThreadPoolServer.cpp
new file mode 100644
index 000000000..121dde3ea
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TThreadPoolServer.cpp
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/server/TThreadPoolServer.h>
+
+namespace apache {
+namespace thrift {
+namespace server {
+
+using apache::thrift::concurrency::ThreadManager;
+using apache::thrift::protocol::TProtocol;
+using apache::thrift::protocol::TProtocolFactory;
+using apache::thrift::transport::TServerTransport;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using apache::thrift::transport::TTransportFactory;
+using std::shared_ptr;
+using std::string;
+
+TThreadPoolServer::TThreadPoolServer(const shared_ptr<TProcessorFactory>& processorFactory,
+ const shared_ptr<TServerTransport>& serverTransport,
+ const shared_ptr<TTransportFactory>& transportFactory,
+ const shared_ptr<TProtocolFactory>& protocolFactory,
+ const shared_ptr<ThreadManager>& threadManager)
+ : TServerFramework(processorFactory, serverTransport, transportFactory, protocolFactory),
+ threadManager_(threadManager),
+ timeout_(0),
+ taskExpiration_(0) {
+}
+
+TThreadPoolServer::TThreadPoolServer(const shared_ptr<TProcessor>& processor,
+ const shared_ptr<TServerTransport>& serverTransport,
+ const shared_ptr<TTransportFactory>& transportFactory,
+ const shared_ptr<TProtocolFactory>& protocolFactory,
+ const shared_ptr<ThreadManager>& threadManager)
+ : TServerFramework(processor, serverTransport, transportFactory, protocolFactory),
+ threadManager_(threadManager),
+ timeout_(0),
+ taskExpiration_(0) {
+}
+
+TThreadPoolServer::TThreadPoolServer(const shared_ptr<TProcessorFactory>& processorFactory,
+ const shared_ptr<TServerTransport>& serverTransport,
+ const shared_ptr<TTransportFactory>& inputTransportFactory,
+ const shared_ptr<TTransportFactory>& outputTransportFactory,
+ const shared_ptr<TProtocolFactory>& inputProtocolFactory,
+ const shared_ptr<TProtocolFactory>& outputProtocolFactory,
+ const shared_ptr<ThreadManager>& threadManager)
+ : TServerFramework(processorFactory,
+ serverTransport,
+ inputTransportFactory,
+ outputTransportFactory,
+ inputProtocolFactory,
+ outputProtocolFactory),
+ threadManager_(threadManager),
+ timeout_(0),
+ taskExpiration_(0) {
+}
+
+TThreadPoolServer::TThreadPoolServer(const shared_ptr<TProcessor>& processor,
+ const shared_ptr<TServerTransport>& serverTransport,
+ const shared_ptr<TTransportFactory>& inputTransportFactory,
+ const shared_ptr<TTransportFactory>& outputTransportFactory,
+ const shared_ptr<TProtocolFactory>& inputProtocolFactory,
+ const shared_ptr<TProtocolFactory>& outputProtocolFactory,
+ const shared_ptr<ThreadManager>& threadManager)
+ : TServerFramework(processor,
+ serverTransport,
+ inputTransportFactory,
+ outputTransportFactory,
+ inputProtocolFactory,
+ outputProtocolFactory),
+ threadManager_(threadManager),
+ timeout_(0),
+ taskExpiration_(0) {
+}
+
+TThreadPoolServer::~TThreadPoolServer() = default;
+
+void TThreadPoolServer::serve() {
+ TServerFramework::serve();
+ threadManager_->stop();
+}
+
+int64_t TThreadPoolServer::getTimeout() const {
+ return timeout_;
+}
+
+void TThreadPoolServer::setTimeout(int64_t value) {
+ timeout_ = value;
+}
+
+int64_t TThreadPoolServer::getTaskExpiration() const {
+ return taskExpiration_;
+}
+
+void TThreadPoolServer::setTaskExpiration(int64_t value) {
+ taskExpiration_ = value;
+}
+
+std::shared_ptr<apache::thrift::concurrency::ThreadManager>
+TThreadPoolServer::getThreadManager() const {
+ return threadManager_;
+}
+
+void TThreadPoolServer::onClientConnected(const shared_ptr<TConnectedClient>& pClient) {
+ threadManager_->add(pClient, getTimeout(), getTaskExpiration());
+}
+
+void TThreadPoolServer::onClientDisconnected(TConnectedClient*) {
+}
+
+}
+}
+} // apache::thrift::server
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TThreadPoolServer.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TThreadPoolServer.h
new file mode 100644
index 000000000..a9411b86c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TThreadPoolServer.h
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_SERVER_TTHREADPOOLSERVER_H_
+#define _THRIFT_SERVER_TTHREADPOOLSERVER_H_ 1
+
+#include <atomic>
+#include <thrift/concurrency/ThreadManager.h>
+#include <thrift/server/TServerFramework.h>
+
+namespace apache {
+namespace thrift {
+namespace server {
+
+/**
+ * Manage clients using a thread pool.
+ */
+class TThreadPoolServer : public TServerFramework {
+public:
+ TThreadPoolServer(
+ const std::shared_ptr<apache::thrift::TProcessorFactory>& processorFactory,
+ const std::shared_ptr<apache::thrift::transport::TServerTransport>& serverTransport,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& transportFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& protocolFactory,
+ const std::shared_ptr<apache::thrift::concurrency::ThreadManager>& threadManager
+ = apache::thrift::concurrency::ThreadManager::newSimpleThreadManager());
+
+ TThreadPoolServer(
+ const std::shared_ptr<apache::thrift::TProcessor>& processor,
+ const std::shared_ptr<apache::thrift::transport::TServerTransport>& serverTransport,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& transportFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& protocolFactory,
+ const std::shared_ptr<apache::thrift::concurrency::ThreadManager>& threadManager
+ = apache::thrift::concurrency::ThreadManager::newSimpleThreadManager());
+
+ TThreadPoolServer(
+ const std::shared_ptr<apache::thrift::TProcessorFactory>& processorFactory,
+ const std::shared_ptr<apache::thrift::transport::TServerTransport>& serverTransport,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& inputTransportFactory,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& outputTransportFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& inputProtocolFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& outputProtocolFactory,
+ const std::shared_ptr<apache::thrift::concurrency::ThreadManager>& threadManager
+ = apache::thrift::concurrency::ThreadManager::newSimpleThreadManager());
+
+ TThreadPoolServer(
+ const std::shared_ptr<apache::thrift::TProcessor>& processor,
+ const std::shared_ptr<apache::thrift::transport::TServerTransport>& serverTransport,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& inputTransportFactory,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& outputTransportFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& inputProtocolFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& outputProtocolFactory,
+ const std::shared_ptr<apache::thrift::concurrency::ThreadManager>& threadManager
+ = apache::thrift::concurrency::ThreadManager::newSimpleThreadManager());
+
+ ~TThreadPoolServer() override;
+
+ /**
+ * Post-conditions (return guarantees):
+ * There will be no clients connected.
+ */
+ void serve() override;
+
+ virtual int64_t getTimeout() const;
+ virtual void setTimeout(int64_t value);
+
+ virtual int64_t getTaskExpiration() const;
+ virtual void setTaskExpiration(int64_t value);
+
+ virtual std::shared_ptr<apache::thrift::concurrency::ThreadManager> getThreadManager() const;
+
+protected:
+ void onClientConnected(const std::shared_ptr<TConnectedClient>& pClient) override /* override */;
+ void onClientDisconnected(TConnectedClient* pClient) override /* override */;
+
+ std::shared_ptr<apache::thrift::concurrency::ThreadManager> threadManager_;
+ std::atomic<int64_t> timeout_;
+ std::atomic<int64_t> taskExpiration_;
+};
+
+}
+}
+} // apache::thrift::server
+
+#endif // #ifndef _THRIFT_SERVER_TTHREADPOOLSERVER_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TThreadedServer.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TThreadedServer.cpp
new file mode 100644
index 000000000..79dcc70f7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TThreadedServer.cpp
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <string>
+#include <memory>
+#include <thrift/concurrency/ThreadFactory.h>
+#include <thrift/server/TThreadedServer.h>
+
+namespace apache {
+namespace thrift {
+namespace server {
+
+using apache::thrift::concurrency::Runnable;
+using apache::thrift::concurrency::Synchronized;
+using apache::thrift::concurrency::Thread;
+using apache::thrift::concurrency::ThreadFactory;
+using apache::thrift::protocol::TProtocol;
+using apache::thrift::protocol::TProtocolFactory;
+using std::make_shared;
+using std::shared_ptr;
+using apache::thrift::transport::TServerTransport;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using apache::thrift::transport::TTransportFactory;
+
+TThreadedServer::TThreadedServer(const shared_ptr<TProcessorFactory>& processorFactory,
+ const shared_ptr<TServerTransport>& serverTransport,
+ const shared_ptr<TTransportFactory>& transportFactory,
+ const shared_ptr<TProtocolFactory>& protocolFactory,
+ const shared_ptr<ThreadFactory>& threadFactory)
+ : TServerFramework(processorFactory, serverTransport, transportFactory, protocolFactory),
+ threadFactory_(threadFactory) {
+}
+
+TThreadedServer::TThreadedServer(const shared_ptr<TProcessor>& processor,
+ const shared_ptr<TServerTransport>& serverTransport,
+ const shared_ptr<TTransportFactory>& transportFactory,
+ const shared_ptr<TProtocolFactory>& protocolFactory,
+ const shared_ptr<ThreadFactory>& threadFactory)
+ : TServerFramework(processor, serverTransport, transportFactory, protocolFactory),
+ threadFactory_(threadFactory) {
+}
+
+TThreadedServer::TThreadedServer(const shared_ptr<TProcessorFactory>& processorFactory,
+ const shared_ptr<TServerTransport>& serverTransport,
+ const shared_ptr<TTransportFactory>& inputTransportFactory,
+ const shared_ptr<TTransportFactory>& outputTransportFactory,
+ const shared_ptr<TProtocolFactory>& inputProtocolFactory,
+ const shared_ptr<TProtocolFactory>& outputProtocolFactory,
+ const shared_ptr<ThreadFactory>& threadFactory)
+ : TServerFramework(processorFactory,
+ serverTransport,
+ inputTransportFactory,
+ outputTransportFactory,
+ inputProtocolFactory,
+ outputProtocolFactory),
+ threadFactory_(threadFactory) {
+}
+
+TThreadedServer::TThreadedServer(const shared_ptr<TProcessor>& processor,
+ const shared_ptr<TServerTransport>& serverTransport,
+ const shared_ptr<TTransportFactory>& inputTransportFactory,
+ const shared_ptr<TTransportFactory>& outputTransportFactory,
+ const shared_ptr<TProtocolFactory>& inputProtocolFactory,
+ const shared_ptr<TProtocolFactory>& outputProtocolFactory,
+ const shared_ptr<ThreadFactory>& threadFactory)
+ : TServerFramework(processor,
+ serverTransport,
+ inputTransportFactory,
+ outputTransportFactory,
+ inputProtocolFactory,
+ outputProtocolFactory),
+ threadFactory_(threadFactory) {
+}
+
+TThreadedServer::~TThreadedServer() = default;
+
+void TThreadedServer::serve() {
+ TServerFramework::serve();
+
+ // Ensure post-condition of no active clients
+ Synchronized s(clientMonitor_);
+ while (!activeClientMap_.empty()) {
+ clientMonitor_.wait();
+ }
+
+ drainDeadClients();
+}
+
+void TThreadedServer::drainDeadClients() {
+ // we're in a monitor here
+ while (!deadClientMap_.empty()) {
+ auto it = deadClientMap_.begin();
+ it->second->join();
+ deadClientMap_.erase(it);
+ }
+}
+
+void TThreadedServer::onClientConnected(const shared_ptr<TConnectedClient>& pClient) {
+ Synchronized sync(clientMonitor_);
+ shared_ptr<TConnectedClientRunner> pRunnable = make_shared<TConnectedClientRunner>(pClient);
+ shared_ptr<Thread> pThread = threadFactory_->newThread(pRunnable);
+ pRunnable->thread(pThread);
+ activeClientMap_.insert(ClientMap::value_type(pClient.get(), pThread));
+ pThread->start();
+}
+
+void TThreadedServer::onClientDisconnected(TConnectedClient* pClient) {
+ Synchronized sync(clientMonitor_);
+ drainDeadClients(); // use the outgoing thread to do some maintenance on our dead client backlog
+ auto it = activeClientMap_.find(pClient);
+ if (it != activeClientMap_.end()) {
+ auto end = it;
+ deadClientMap_.insert(it, ++end);
+ activeClientMap_.erase(it);
+ }
+ if (activeClientMap_.empty()) {
+ clientMonitor_.notify();
+ }
+}
+
+TThreadedServer::TConnectedClientRunner::TConnectedClientRunner(const shared_ptr<TConnectedClient>& pClient)
+ : pClient_(pClient) {
+}
+
+TThreadedServer::TConnectedClientRunner::~TConnectedClientRunner() = default;
+
+void TThreadedServer::TConnectedClientRunner::run() /* override */ {
+ pClient_->run(); // Run the client
+ pClient_.reset(); // The client is done - release it here rather than in the destructor for safety
+}
+
+}
+}
+} // apache::thrift::server
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TThreadedServer.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TThreadedServer.h
new file mode 100644
index 000000000..756e5a063
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/server/TThreadedServer.h
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_SERVER_TTHREADEDSERVER_H_
+#define _THRIFT_SERVER_TTHREADEDSERVER_H_ 1
+
+#include <map>
+#include <thrift/concurrency/Monitor.h>
+#include <thrift/concurrency/ThreadFactory.h>
+#include <thrift/concurrency/Thread.h>
+#include <thrift/server/TServerFramework.h>
+
+namespace apache {
+namespace thrift {
+namespace server {
+
+/**
+ * Manage clients using threads - threads are created one for each client and are
+ * released when the client disconnects. This server is used to make a dynamically
+ * scalable server up to the concurrent connection limit.
+ */
+class TThreadedServer : public TServerFramework {
+public:
+ TThreadedServer(
+ const std::shared_ptr<apache::thrift::TProcessorFactory>& processorFactory,
+ const std::shared_ptr<apache::thrift::transport::TServerTransport>& serverTransport,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& transportFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& protocolFactory,
+ const std::shared_ptr<apache::thrift::concurrency::ThreadFactory>& threadFactory
+ = std::shared_ptr<apache::thrift::concurrency::ThreadFactory>(
+ new apache::thrift::concurrency::ThreadFactory(false)));
+
+ TThreadedServer(
+ const std::shared_ptr<apache::thrift::TProcessor>& processor,
+ const std::shared_ptr<apache::thrift::transport::TServerTransport>& serverTransport,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& transportFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& protocolFactory,
+ const std::shared_ptr<apache::thrift::concurrency::ThreadFactory>& threadFactory
+ = std::shared_ptr<apache::thrift::concurrency::ThreadFactory>(
+ new apache::thrift::concurrency::ThreadFactory(false)));
+
+ TThreadedServer(
+ const std::shared_ptr<apache::thrift::TProcessorFactory>& processorFactory,
+ const std::shared_ptr<apache::thrift::transport::TServerTransport>& serverTransport,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& inputTransportFactory,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& outputTransportFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& inputProtocolFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& outputProtocolFactory,
+ const std::shared_ptr<apache::thrift::concurrency::ThreadFactory>& threadFactory
+ = std::shared_ptr<apache::thrift::concurrency::ThreadFactory>(
+ new apache::thrift::concurrency::ThreadFactory(false)));
+
+ TThreadedServer(
+ const std::shared_ptr<apache::thrift::TProcessor>& processor,
+ const std::shared_ptr<apache::thrift::transport::TServerTransport>& serverTransport,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& inputTransportFactory,
+ const std::shared_ptr<apache::thrift::transport::TTransportFactory>& outputTransportFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& inputProtocolFactory,
+ const std::shared_ptr<apache::thrift::protocol::TProtocolFactory>& outputProtocolFactory,
+ const std::shared_ptr<apache::thrift::concurrency::ThreadFactory>& threadFactory
+ = std::shared_ptr<apache::thrift::concurrency::ThreadFactory>(
+ new apache::thrift::concurrency::ThreadFactory(false)));
+
+ ~TThreadedServer() override;
+
+ /**
+ * Post-conditions (return guarantees):
+ * There will be no clients connected.
+ */
+ void serve() override;
+
+protected:
+ /**
+ * Drain recently connected clients by joining their threads - this is done lazily because
+ * we cannot do it inside the thread context that is disconnecting.
+ */
+ virtual void drainDeadClients();
+
+ /**
+ * Implementation of TServerFramework::onClientConnected
+ */
+ void onClientConnected(const std::shared_ptr<TConnectedClient>& pClient) override /* override */;
+
+ /**
+ * Implementation of TServerFramework::onClientDisconnected
+ */
+ void onClientDisconnected(TConnectedClient *pClient) override /* override */;
+
+ std::shared_ptr<apache::thrift::concurrency::ThreadFactory> threadFactory_;
+
+ /**
+ * A helper wrapper used to wrap the client in something we can use to maintain
+ * the lifetime of the connected client within a detached thread. We cannot simply
+ * track the threads because a shared_ptr<Thread> hangs on to the Runnable it is
+ * passed, and TServerFramework requires the runnable (TConnectedClient) to be
+ * destroyed in order to work properly.
+ */
+ class TConnectedClientRunner : public apache::thrift::concurrency::Runnable
+ {
+ public:
+ TConnectedClientRunner(const std::shared_ptr<TConnectedClient>& pClient);
+ ~TConnectedClientRunner() override;
+ void run() override /* override */;
+ private:
+ std::shared_ptr<TConnectedClient> pClient_;
+ };
+
+ apache::thrift::concurrency::Monitor clientMonitor_;
+
+ typedef std::map<TConnectedClient *, std::shared_ptr<apache::thrift::concurrency::Thread> > ClientMap;
+
+ /**
+ * A map of active clients
+ */
+ ClientMap activeClientMap_;
+
+ /**
+ * A map of clients that have disconnected but their threads have not been joined
+ */
+ ClientMap deadClientMap_;
+};
+
+}
+}
+} // apache::thrift::server
+
+#endif // #ifndef _THRIFT_SERVER_TTHREADEDSERVER_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/thrift-config.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/thrift-config.h
new file mode 100644
index 000000000..d648706c7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/thrift-config.h
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifdef _WIN32
+#include <thrift/windows/config.h>
+#else
+#include <thrift/config.h>
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/thrift_export.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/thrift_export.h
new file mode 100644
index 000000000..f5c059fb7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/thrift_export.h
@@ -0,0 +1,20 @@
+#ifndef THRIFT_EXPORT_H
+#define THRIFT_EXPORT_H
+
+#ifdef THRIFT_STATIC_DEFINE
+# define THRIFT_EXPORT
+#elif defined(_MSC_VER )
+# ifndef THRIFT_EXPORT
+# ifdef thrift_EXPORTS
+ /* We are building this library */
+# define THRIFT_EXPORT __declspec(dllexport)
+# else
+ /* We are using this library */
+# define THRIFT_EXPORT __declspec(dllimport)
+# endif
+# endif
+#else
+# define THRIFT_EXPORT
+#endif
+
+#endif /* THRIFT_EXPORT_H */
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/PlatformSocket.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/PlatformSocket.h
new file mode 100644
index 000000000..959105806
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/PlatformSocket.h
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// clang-format off
+
+#ifndef _THRIFT_TRANSPORT_PLATFORM_SOCKET_H_
+# define _THRIFT_TRANSPORT_PLATFORM_SOCKET_H_
+
+#ifdef _WIN32
+# include <winsock2.h>
+# define THRIFT_GET_SOCKET_ERROR ::WSAGetLastError()
+# define THRIFT_ERRNO (*_errno())
+# define THRIFT_EINPROGRESS WSAEINPROGRESS
+# define THRIFT_EAGAIN WSAEWOULDBLOCK
+# define THRIFT_EINTR WSAEINTR
+# define THRIFT_ECONNRESET WSAECONNRESET
+# define THRIFT_ENOTCONN WSAENOTCONN
+# define THRIFT_ETIMEDOUT WSAETIMEDOUT
+# define THRIFT_EWOULDBLOCK WSAEWOULDBLOCK
+# define THRIFT_EPIPE WSAECONNRESET
+# define THRIFT_NO_SOCKET_CACHING SO_EXCLUSIVEADDRUSE
+# define THRIFT_SOCKET SOCKET
+# define THRIFT_INVALID_SOCKET INVALID_SOCKET
+# define THRIFT_SOCKETPAIR thrift_socketpair
+# define THRIFT_FCNTL thrift_fcntl
+# define THRIFT_O_NONBLOCK 1
+# define THRIFT_F_GETFL 0
+# define THRIFT_F_SETFL 1
+# define THRIFT_GETTIMEOFDAY thrift_gettimeofday
+# define THRIFT_CLOSESOCKET closesocket
+# define THRIFT_CLOSE _close
+# define THRIFT_OPEN _open
+# define THRIFT_FTRUNCATE _chsize_s
+# define THRIFT_FSYNC _commit
+# define THRIFT_LSEEK _lseek
+# define THRIFT_WRITE _write
+# define THRIFT_READ _read
+# define THRIFT_IOCTL_SOCKET ioctlsocket
+# define THRIFT_IOCTL_SOCKET_NUM_BYTES_TYPE u_long
+# define THRIFT_FSTAT _fstat
+# define THRIFT_STAT _stat
+# ifdef _WIN32_WCE
+# define THRIFT_GAI_STRERROR(...) thrift_wstr2str(gai_strerrorW(__VA_ARGS__))
+# else
+# define THRIFT_GAI_STRERROR gai_strerrorA
+# endif
+# define THRIFT_SSIZET ptrdiff_t
+# if (_MSC_VER < 1900)
+# define THRIFT_SNPRINTF _snprintf
+# else
+# define THRIFT_SNPRINTF snprintf
+# endif
+# define THRIFT_SLEEP_SEC thrift_sleep
+# define THRIFT_SLEEP_USEC thrift_usleep
+# define THRIFT_TIMESPEC thrift_timespec
+# define THRIFT_CTIME_R thrift_ctime_r
+# define THRIFT_POLL thrift_poll
+# if WINVER <= 0x0502 //XP, Server2003
+# define THRIFT_POLLFD thrift_pollfd
+# define THRIFT_POLLIN 0x0300
+# define THRIFT_POLLOUT 0x0010
+# else //Vista, Win7...
+# define THRIFT_POLLFD pollfd
+# define THRIFT_POLLIN POLLIN
+# define THRIFT_POLLOUT POLLOUT
+# endif //WINVER
+# define THRIFT_SHUT_RDWR SD_BOTH
+# if !defined(AI_ADDRCONFIG)
+# define AI_ADDRCONFIG 0x00000400
+# endif
+#else //not _WIN32
+# include <errno.h>
+# define THRIFT_GET_SOCKET_ERROR errno
+# define THRIFT_ERRNO errno
+# define THRIFT_EINTR EINTR
+# define THRIFT_EINPROGRESS EINPROGRESS
+# define THRIFT_ECONNRESET ECONNRESET
+# define THRIFT_ENOTCONN ENOTCONN
+# define THRIFT_ETIMEDOUT ETIMEDOUT
+# define THRIFT_EWOULDBLOCK EWOULDBLOCK
+# define THRIFT_EAGAIN EAGAIN
+# define THRIFT_EPIPE EPIPE
+# define THRIFT_NO_SOCKET_CACHING SO_REUSEADDR
+# define THRIFT_SOCKET int
+# define THRIFT_INVALID_SOCKET (-1)
+# define THRIFT_SOCKETPAIR socketpair
+# define THRIFT_FCNTL fcntl
+# define THRIFT_O_NONBLOCK O_NONBLOCK
+# define THRIFT_F_GETFL F_GETFL
+# define THRIFT_F_SETFL F_SETFL
+# define THRIFT_GETTIMEOFDAY gettimeofday
+# define THRIFT_CLOSESOCKET close
+# define THRIFT_CLOSE close
+# define THRIFT_OPEN open
+# define THRIFT_FTRUNCATE ftruncate
+# define THRIFT_FSYNC fsync
+# define THRIFT_LSEEK lseek
+# define THRIFT_WRITE write
+# define THRIFT_READ read
+# define THRIFT_IOCTL_SOCKET ioctl
+# define THRIFT_IOCTL_SOCKET_NUM_BYTES_TYPE int
+# define THRIFT_STAT stat
+# define THRIFT_FSTAT fstat
+# define THRIFT_GAI_STRERROR gai_strerror
+# define THRIFT_SSIZET ssize_t
+# define THRIFT_SNPRINTF snprintf
+# define THRIFT_SLEEP_SEC sleep
+# define THRIFT_SLEEP_USEC usleep
+# define THRIFT_TIMESPEC timespec
+# define THRIFT_CTIME_R ctime_r
+# define THRIFT_POLL poll
+# define THRIFT_POLLFD pollfd
+# define THRIFT_POLLIN POLLIN
+# define THRIFT_POLLOUT POLLOUT
+# define THRIFT_SHUT_RDWR SHUT_RDWR
+#endif
+
+#endif // _THRIFT_TRANSPORT_PLATFORM_SOCKET_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TBufferTransports.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TBufferTransports.cpp
new file mode 100644
index 000000000..4bb8713de
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TBufferTransports.cpp
@@ -0,0 +1,415 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <cassert>
+#include <algorithm>
+
+#include <thrift/transport/TBufferTransports.h>
+
+using std::string;
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+uint32_t TBufferedTransport::readSlow(uint8_t* buf, uint32_t len) {
+ auto have = static_cast<uint32_t>(rBound_ - rBase_);
+
+ // We should only take the slow path if we can't satisfy the read
+ // with the data already in the buffer.
+ assert(have < len);
+
+ // If we have some data in the buffer, copy it out and return it.
+ // We have to return it without attempting to read more, since we aren't
+ // guaranteed that the underlying transport actually has more data, so
+ // attempting to read from it could block.
+ if (have > 0) {
+ memcpy(buf, rBase_, have);
+ setReadBuffer(rBuf_.get(), 0);
+ return have;
+ }
+
+ // No data is available in our buffer.
+ // Get more from underlying transport up to buffer size.
+ // Note that this makes a lot of sense if len < rBufSize_
+ // and almost no sense otherwise. TODO(dreiss): Fix that
+ // case (possibly including some readv hotness).
+ setReadBuffer(rBuf_.get(), transport_->read(rBuf_.get(), rBufSize_));
+
+ // Hand over whatever we have.
+ uint32_t give = (std::min)(len, static_cast<uint32_t>(rBound_ - rBase_));
+ memcpy(buf, rBase_, give);
+ rBase_ += give;
+
+ return give;
+}
+
+void TBufferedTransport::writeSlow(const uint8_t* buf, uint32_t len) {
+ auto have_bytes = static_cast<uint32_t>(wBase_ - wBuf_.get());
+ auto space = static_cast<uint32_t>(wBound_ - wBase_);
+ // We should only take the slow path if we can't accommodate the write
+ // with the free space already in the buffer.
+ assert(wBound_ - wBase_ < static_cast<ptrdiff_t>(len));
+
+ // Now here's the tricky question: should we copy data from buf into our
+ // internal buffer and write it from there, or should we just write out
+ // the current internal buffer in one syscall and write out buf in another.
+ // If our currently buffered data plus buf is at least double our buffer
+ // size, we will have to do two syscalls no matter what (except in the
+ // degenerate case when our buffer is empty), so there is no use copying.
+ // Otherwise, there is sort of a sliding scale. If we have N-1 bytes
+ // buffered and need to write 2, it would be crazy to do two syscalls.
+ // On the other hand, if we have 2 bytes buffered and are writing 2N-3,
+ // we can save a syscall in the short term by loading up our buffer, writing
+ // it out, and copying the rest of the bytes into our buffer. Of course,
+ // if we get another 2-byte write, we haven't saved any syscalls at all,
+ // and have just copied nearly 2N bytes for nothing. Finding a perfect
+ // policy would require predicting the size of future writes, so we're just
+ // going to always eschew syscalls if we have less than 2N bytes to write.
+
+ // The case where we have to do two syscalls.
+ // This case also covers the case where the buffer is empty,
+ // but it is clearer (I think) to think of it as two separate cases.
+ if ((have_bytes + len >= 2 * wBufSize_) || (have_bytes == 0)) {
+ // TODO(dreiss): writev
+ if (have_bytes > 0) {
+ transport_->write(wBuf_.get(), have_bytes);
+ }
+ transport_->write(buf, len);
+ wBase_ = wBuf_.get();
+ return;
+ }
+
+ // Fill up our internal buffer for a write.
+ memcpy(wBase_, buf, space);
+ buf += space;
+ len -= space;
+ transport_->write(wBuf_.get(), wBufSize_);
+
+ // Copy the rest into our buffer.
+ assert(len < wBufSize_);
+ memcpy(wBuf_.get(), buf, len);
+ wBase_ = wBuf_.get() + len;
+ return;
+}
+
+const uint8_t* TBufferedTransport::borrowSlow(uint8_t* buf, uint32_t* len) {
+ (void)buf;
+ (void)len;
+ // Simply return NULL. We don't know if there is actually data available on
+ // the underlying transport, so calling read() might block.
+ return nullptr;
+}
+
+void TBufferedTransport::flush() {
+ // Write out any data waiting in the write buffer.
+ auto have_bytes = static_cast<uint32_t>(wBase_ - wBuf_.get());
+ if (have_bytes > 0) {
+ // Note that we reset wBase_ prior to the underlying write
+ // to ensure we're in a sane state (i.e. internal buffer cleaned)
+ // if the underlying write throws up an exception
+ wBase_ = wBuf_.get();
+ transport_->write(wBuf_.get(), have_bytes);
+ }
+
+ // Flush the underlying transport.
+ transport_->flush();
+}
+
+uint32_t TFramedTransport::readSlow(uint8_t* buf, uint32_t len) {
+ uint32_t want = len;
+ auto have = static_cast<uint32_t>(rBound_ - rBase_);
+
+ // We should only take the slow path if we can't satisfy the read
+ // with the data already in the buffer.
+ assert(have < want);
+
+ // If we have some data in the buffer, copy it out and return it.
+ // We have to return it without attempting to read more, since we aren't
+ // guaranteed that the underlying transport actually has more data, so
+ // attempting to read from it could block.
+ if (have > 0) {
+ memcpy(buf, rBase_, have);
+ setReadBuffer(rBuf_.get(), 0);
+ return have;
+ }
+
+ // Read another frame.
+ if (!readFrame()) {
+ // EOF. No frame available.
+ return 0;
+ }
+
+ // TODO(dreiss): Should we warn when reads cross frames?
+
+ // Hand over whatever we have.
+ uint32_t give = (std::min)(want, static_cast<uint32_t>(rBound_ - rBase_));
+ memcpy(buf, rBase_, give);
+ rBase_ += give;
+ want -= give;
+
+ return (len - want);
+}
+
+bool TFramedTransport::readFrame() {
+ // TODO(dreiss): Think about using readv here, even though it would
+ // result in (gasp) read-ahead.
+
+ // Read the size of the next frame.
+ // We can't use readAll(&sz, sizeof(sz)), since that always throws an
+ // exception on EOF. We want to throw an exception only if EOF occurs after
+ // partial size data.
+ int32_t sz = -1;
+ uint32_t size_bytes_read = 0;
+ while (size_bytes_read < sizeof(sz)) {
+ uint8_t* szp = reinterpret_cast<uint8_t*>(&sz) + size_bytes_read;
+ uint32_t bytes_read
+ = transport_->read(szp, static_cast<uint32_t>(sizeof(sz)) - size_bytes_read);
+ if (bytes_read == 0) {
+ if (size_bytes_read == 0) {
+ // EOF before any data was read.
+ return false;
+ } else {
+ // EOF after a partial frame header. Raise an exception.
+ throw TTransportException(TTransportException::END_OF_FILE,
+ "No more data to read after "
+ "partial frame header.");
+ }
+ }
+ size_bytes_read += bytes_read;
+ }
+
+ sz = ntohl(sz);
+
+ if (sz < 0) {
+ throw TTransportException("Frame size has negative value");
+ }
+
+ // Check for oversized frame
+ if (sz > static_cast<int32_t>(maxFrameSize_))
+ throw TTransportException(TTransportException::CORRUPTED_DATA, "Received an oversized frame");
+
+ // Read the frame payload, and reset markers.
+ if (sz > static_cast<int32_t>(rBufSize_)) {
+ rBuf_.reset(new uint8_t[sz]);
+ rBufSize_ = sz;
+ }
+ transport_->readAll(rBuf_.get(), sz);
+ setReadBuffer(rBuf_.get(), sz);
+ return true;
+}
+
+void TFramedTransport::writeSlow(const uint8_t* buf, uint32_t len) {
+ // Double buffer size until sufficient.
+ auto have = static_cast<uint32_t>(wBase_ - wBuf_.get());
+ uint32_t new_size = wBufSize_;
+ if (len + have < have /* overflow */ || len + have > 0x7fffffff) {
+ throw TTransportException(TTransportException::BAD_ARGS,
+ "Attempted to write over 2 GB to TFramedTransport.");
+ }
+ while (new_size < len + have) {
+ new_size = new_size > 0 ? new_size * 2 : 1;
+ }
+
+ // TODO(dreiss): Consider modifying this class to use malloc/free
+ // so we can use realloc here.
+
+ // Allocate new buffer.
+ auto* new_buf = new uint8_t[new_size];
+
+ // Copy the old buffer to the new one.
+ memcpy(new_buf, wBuf_.get(), have);
+
+ // Now point buf to the new one.
+ wBuf_.reset(new_buf);
+ wBufSize_ = new_size;
+ wBase_ = wBuf_.get() + have;
+ wBound_ = wBuf_.get() + wBufSize_;
+
+ // Copy the data into the new buffer.
+ memcpy(wBase_, buf, len);
+ wBase_ += len;
+}
+
+void TFramedTransport::flush() {
+ int32_t sz_hbo, sz_nbo;
+ assert(wBufSize_ > sizeof(sz_nbo));
+
+ // Slip the frame size into the start of the buffer.
+ sz_hbo = static_cast<uint32_t>(wBase_ - (wBuf_.get() + sizeof(sz_nbo)));
+ sz_nbo = (int32_t)htonl((uint32_t)(sz_hbo));
+ memcpy(wBuf_.get(), (uint8_t*)&sz_nbo, sizeof(sz_nbo));
+
+ if (sz_hbo > 0) {
+ // Note that we reset wBase_ (with a pad for the frame size)
+ // prior to the underlying write to ensure we're in a sane state
+ // (i.e. internal buffer cleaned) if the underlying write throws
+ // up an exception
+ wBase_ = wBuf_.get() + sizeof(sz_nbo);
+
+ // Write size and frame body.
+ transport_->write(wBuf_.get(), static_cast<uint32_t>(sizeof(sz_nbo)) + sz_hbo);
+ }
+
+ // Flush the underlying transport.
+ transport_->flush();
+
+ // reclaim write buffer
+ if (wBufSize_ > bufReclaimThresh_) {
+ wBufSize_ = DEFAULT_BUFFER_SIZE;
+ wBuf_.reset(new uint8_t[wBufSize_]);
+ setWriteBuffer(wBuf_.get(), wBufSize_);
+
+ // reset wBase_ with a pad for the frame size
+ int32_t pad = 0;
+ wBase_ = wBuf_.get() + sizeof(pad);
+ }
+}
+
+uint32_t TFramedTransport::writeEnd() {
+ return static_cast<uint32_t>(wBase_ - wBuf_.get());
+}
+
+const uint8_t* TFramedTransport::borrowSlow(uint8_t* buf, uint32_t* len) {
+ (void)buf;
+ (void)len;
+ // Don't try to be clever with shifting buffers.
+ // If the fast path failed let the protocol use its slow path.
+ // Besides, who is going to try to borrow across messages?
+ return nullptr;
+}
+
+uint32_t TFramedTransport::readEnd() {
+ // include framing bytes
+ auto bytes_read = static_cast<uint32_t>(rBound_ - rBuf_.get() + sizeof(uint32_t));
+
+ if (rBufSize_ > bufReclaimThresh_) {
+ rBufSize_ = 0;
+ rBuf_.reset();
+ setReadBuffer(rBuf_.get(), rBufSize_);
+ }
+
+ return bytes_read;
+}
+
+void TMemoryBuffer::computeRead(uint32_t len, uint8_t** out_start, uint32_t* out_give) {
+ // Correct rBound_ so we can use the fast path in the future.
+ rBound_ = wBase_;
+
+ // Decide how much to give.
+ uint32_t give = (std::min)(len, available_read());
+
+ *out_start = rBase_;
+ *out_give = give;
+
+ // Preincrement rBase_ so the caller doesn't have to.
+ rBase_ += give;
+}
+
+uint32_t TMemoryBuffer::readSlow(uint8_t* buf, uint32_t len) {
+ uint8_t* start;
+ uint32_t give;
+ computeRead(len, &start, &give);
+
+ // Copy into the provided buffer.
+ memcpy(buf, start, give);
+
+ return give;
+}
+
+uint32_t TMemoryBuffer::readAppendToString(std::string& str, uint32_t len) {
+ // Don't get some stupid assertion failure.
+ if (buffer_ == nullptr) {
+ return 0;
+ }
+
+ uint8_t* start;
+ uint32_t give;
+ computeRead(len, &start, &give);
+
+ // Append to the provided string.
+ str.append((char*)start, give);
+
+ return give;
+}
+
+void TMemoryBuffer::ensureCanWrite(uint32_t len) {
+ // Check available space
+ uint32_t avail = available_write();
+ if (len <= avail) {
+ return;
+ }
+
+ if (!owner_) {
+ throw TTransportException("Insufficient space in external MemoryBuffer");
+ }
+
+ // Grow the buffer as necessary.
+ uint64_t new_size = bufferSize_;
+ while (len > avail) {
+ new_size = new_size > 0 ? new_size * 2 : 1;
+ if (new_size > maxBufferSize_) {
+ throw TTransportException(TTransportException::BAD_ARGS,
+ "Internal buffer size overflow");
+ }
+ avail = available_write() + (static_cast<uint32_t>(new_size) - bufferSize_);
+ }
+
+ // Allocate into a new pointer so we don't bork ours if it fails.
+ auto* new_buffer = static_cast<uint8_t*>(std::realloc(buffer_, new_size));
+ if (new_buffer == nullptr) {
+ throw std::bad_alloc();
+ }
+
+ rBase_ = new_buffer + (rBase_ - buffer_);
+ rBound_ = new_buffer + (rBound_ - buffer_);
+ wBase_ = new_buffer + (wBase_ - buffer_);
+ wBound_ = new_buffer + new_size;
+ buffer_ = new_buffer;
+ bufferSize_ = static_cast<uint32_t>(new_size);
+}
+
+void TMemoryBuffer::writeSlow(const uint8_t* buf, uint32_t len) {
+ ensureCanWrite(len);
+
+ // Copy into the buffer and increment wBase_.
+ memcpy(wBase_, buf, len);
+ wBase_ += len;
+}
+
+void TMemoryBuffer::wroteBytes(uint32_t len) {
+ uint32_t avail = available_write();
+ if (len > avail) {
+ throw TTransportException("Client wrote more bytes than size of buffer.");
+ }
+ wBase_ += len;
+}
+
+const uint8_t* TMemoryBuffer::borrowSlow(uint8_t* buf, uint32_t* len) {
+ (void)buf;
+ rBound_ = wBase_;
+ if (available_read() >= *len) {
+ *len = available_read();
+ return rBase_;
+ }
+ return nullptr;
+}
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TBufferTransports.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TBufferTransports.h
new file mode 100644
index 000000000..df06586bd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TBufferTransports.h
@@ -0,0 +1,747 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TBUFFERTRANSPORTS_H_
+#define _THRIFT_TRANSPORT_TBUFFERTRANSPORTS_H_ 1
+
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include <boost/scoped_array.hpp>
+
+#include <thrift/transport/TTransport.h>
+#include <thrift/transport/TVirtualTransport.h>
+
+#ifdef __GNUC__
+#define TDB_LIKELY(val) (__builtin_expect((val), 1))
+#define TDB_UNLIKELY(val) (__builtin_expect((val), 0))
+#else
+#define TDB_LIKELY(val) (val)
+#define TDB_UNLIKELY(val) (val)
+#endif
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * Base class for all transports that use read/write buffers for performance.
+ *
+ * TBufferBase is designed to implement the fast-path "memcpy" style
+ * operations that work in the common case. It does so with small and
+ * (eventually) nonvirtual, inlinable methods. TBufferBase is an abstract
+ * class. Subclasses are expected to define the "slow path" operations
+ * that have to be done when the buffers are full or empty.
+ *
+ */
+class TBufferBase : public TVirtualTransport<TBufferBase> {
+
+public:
+ /**
+ * Fast-path read.
+ *
+ * When we have enough data buffered to fulfill the read, we can satisfy it
+ * with a single memcpy, then adjust our internal pointers. If the buffer
+ * is empty, we call out to our slow path, implemented by a subclass.
+ * This method is meant to eventually be nonvirtual and inlinable.
+ */
+ uint32_t read(uint8_t* buf, uint32_t len) {
+ uint8_t* new_rBase = rBase_ + len;
+ if (TDB_LIKELY(new_rBase <= rBound_)) {
+ std::memcpy(buf, rBase_, len);
+ rBase_ = new_rBase;
+ return len;
+ }
+ return readSlow(buf, len);
+ }
+
+ /**
+ * Shortcutted version of readAll.
+ */
+ uint32_t readAll(uint8_t* buf, uint32_t len) {
+ uint8_t* new_rBase = rBase_ + len;
+ if (TDB_LIKELY(new_rBase <= rBound_)) {
+ std::memcpy(buf, rBase_, len);
+ rBase_ = new_rBase;
+ return len;
+ }
+ return apache::thrift::transport::readAll(*this, buf, len);
+ }
+
+ /**
+ * Fast-path write.
+ *
+ * When we have enough empty space in our buffer to accommodate the write, we
+ * can satisfy it with a single memcpy, then adjust our internal pointers.
+ * If the buffer is full, we call out to our slow path, implemented by a
+ * subclass. This method is meant to eventually be nonvirtual and
+ * inlinable.
+ */
+ void write(const uint8_t* buf, uint32_t len) {
+ uint8_t* new_wBase = wBase_ + len;
+ if (TDB_LIKELY(new_wBase <= wBound_)) {
+ std::memcpy(wBase_, buf, len);
+ wBase_ = new_wBase;
+ return;
+ }
+ writeSlow(buf, len);
+ }
+
+ /**
+ * Fast-path borrow. A lot like the fast-path read.
+ */
+ const uint8_t* borrow(uint8_t* buf, uint32_t* len) {
+ if (TDB_LIKELY(static_cast<ptrdiff_t>(*len) <= rBound_ - rBase_)) {
+ // With strict aliasing, writing to len shouldn't force us to
+ // refetch rBase_ from memory. TODO(dreiss): Verify this.
+ *len = static_cast<uint32_t>(rBound_ - rBase_);
+ return rBase_;
+ }
+ return borrowSlow(buf, len);
+ }
+
+ /**
+ * Consume doesn't require a slow path.
+ */
+ void consume(uint32_t len) {
+ if (TDB_LIKELY(static_cast<ptrdiff_t>(len) <= rBound_ - rBase_)) {
+ rBase_ += len;
+ } else {
+ throw TTransportException(TTransportException::BAD_ARGS, "consume did not follow a borrow.");
+ }
+ }
+
+protected:
+ /// Slow path read.
+ virtual uint32_t readSlow(uint8_t* buf, uint32_t len) = 0;
+
+ /// Slow path write.
+ virtual void writeSlow(const uint8_t* buf, uint32_t len) = 0;
+
+ /**
+ * Slow path borrow.
+ *
+ * POSTCONDITION: return == NULL || rBound_ - rBase_ >= *len
+ */
+ virtual const uint8_t* borrowSlow(uint8_t* buf, uint32_t* len) = 0;
+
+ /**
+ * Trivial constructor.
+ *
+ * Initialize pointers safely. Constructing is not a very
+ * performance-sensitive operation, so it is okay to just leave it to
+ * the concrete class to set up pointers correctly.
+ */
+ TBufferBase() : rBase_(nullptr), rBound_(nullptr), wBase_(nullptr), wBound_(nullptr) {}
+
+ /// Convenience mutator for setting the read buffer.
+ void setReadBuffer(uint8_t* buf, uint32_t len) {
+ rBase_ = buf;
+ rBound_ = buf + len;
+ }
+
+ /// Convenience mutator for setting the write buffer.
+ void setWriteBuffer(uint8_t* buf, uint32_t len) {
+ wBase_ = buf;
+ wBound_ = buf + len;
+ }
+
+ ~TBufferBase() override = default;
+
+ /// Reads begin here.
+ uint8_t* rBase_;
+ /// Reads may extend to just before here.
+ uint8_t* rBound_;
+
+ /// Writes begin here.
+ uint8_t* wBase_;
+ /// Writes may extend to just before here.
+ uint8_t* wBound_;
+};
+
+/**
+ * Buffered transport. For reads it will read more data than is requested
+ * and will serve future data out of a local buffer. For writes, data is
+ * stored to an in memory buffer before being written out.
+ *
+ */
+class TBufferedTransport : public TVirtualTransport<TBufferedTransport, TBufferBase> {
+public:
+ static const int DEFAULT_BUFFER_SIZE = 512;
+
+ /// Use default buffer sizes.
+ TBufferedTransport(std::shared_ptr<TTransport> transport)
+ : transport_(transport),
+ rBufSize_(DEFAULT_BUFFER_SIZE),
+ wBufSize_(DEFAULT_BUFFER_SIZE),
+ rBuf_(new uint8_t[rBufSize_]),
+ wBuf_(new uint8_t[wBufSize_]) {
+ initPointers();
+ }
+
+ /// Use specified buffer sizes.
+ TBufferedTransport(std::shared_ptr<TTransport> transport, uint32_t sz)
+ : transport_(transport),
+ rBufSize_(sz),
+ wBufSize_(sz),
+ rBuf_(new uint8_t[rBufSize_]),
+ wBuf_(new uint8_t[wBufSize_]) {
+ initPointers();
+ }
+
+ /// Use specified read and write buffer sizes.
+ TBufferedTransport(std::shared_ptr<TTransport> transport, uint32_t rsz, uint32_t wsz)
+ : transport_(transport),
+ rBufSize_(rsz),
+ wBufSize_(wsz),
+ rBuf_(new uint8_t[rBufSize_]),
+ wBuf_(new uint8_t[wBufSize_]) {
+ initPointers();
+ }
+
+ void open() override { transport_->open(); }
+
+ bool isOpen() const override { return transport_->isOpen(); }
+
+ bool peek() override {
+ if (rBase_ == rBound_) {
+ setReadBuffer(rBuf_.get(), transport_->read(rBuf_.get(), rBufSize_));
+ }
+ return (rBound_ > rBase_);
+ }
+
+ void close() override {
+ flush();
+ transport_->close();
+ }
+
+ uint32_t readSlow(uint8_t* buf, uint32_t len) override;
+
+ void writeSlow(const uint8_t* buf, uint32_t len) override;
+
+ void flush() override;
+
+ /**
+ * Returns the origin of the underlying transport
+ */
+ const std::string getOrigin() const override { return transport_->getOrigin(); }
+
+ /**
+ * The following behavior is currently implemented by TBufferedTransport,
+ * but that may change in a future version:
+ * 1/ If len is at most rBufSize_, borrow will never return NULL.
+ * Depending on the underlying transport, it could throw an exception
+ * or hang forever.
+ * 2/ Some borrow requests may copy bytes internally. However,
+ * if len is at most rBufSize_/2, none of the copied bytes
+ * will ever have to be copied again. For optimial performance,
+ * stay under this limit.
+ */
+ const uint8_t* borrowSlow(uint8_t* buf, uint32_t* len) override;
+
+ std::shared_ptr<TTransport> getUnderlyingTransport() { return transport_; }
+
+ /*
+ * TVirtualTransport provides a default implementation of readAll().
+ * We want to use the TBufferBase version instead.
+ */
+ uint32_t readAll(uint8_t* buf, uint32_t len) { return TBufferBase::readAll(buf, len); }
+
+protected:
+ void initPointers() {
+ setReadBuffer(rBuf_.get(), 0);
+ setWriteBuffer(wBuf_.get(), wBufSize_);
+ // Write size never changes.
+ }
+
+ std::shared_ptr<TTransport> transport_;
+
+ uint32_t rBufSize_;
+ uint32_t wBufSize_;
+ boost::scoped_array<uint8_t> rBuf_;
+ boost::scoped_array<uint8_t> wBuf_;
+};
+
+/**
+ * Wraps a transport into a buffered one.
+ *
+ */
+class TBufferedTransportFactory : public TTransportFactory {
+public:
+ TBufferedTransportFactory() = default;
+
+ ~TBufferedTransportFactory() override = default;
+
+ /**
+ * Wraps the transport into a buffered one.
+ */
+ std::shared_ptr<TTransport> getTransport(std::shared_ptr<TTransport> trans) override {
+ return std::shared_ptr<TTransport>(new TBufferedTransport(trans));
+ }
+};
+
+/**
+ * Framed transport. All writes go into an in-memory buffer until flush is
+ * called, at which point the transport writes the length of the entire
+ * binary chunk followed by the data payload. This allows the receiver on the
+ * other end to always do fixed-length reads.
+ *
+ */
+class TFramedTransport : public TVirtualTransport<TFramedTransport, TBufferBase> {
+public:
+ static const int DEFAULT_BUFFER_SIZE = 512;
+ static const int DEFAULT_MAX_FRAME_SIZE = 256 * 1024 * 1024;
+
+ /// Use default buffer sizes.
+ TFramedTransport()
+ : transport_(),
+ rBufSize_(0),
+ wBufSize_(DEFAULT_BUFFER_SIZE),
+ rBuf_(),
+ wBuf_(new uint8_t[wBufSize_]),
+ bufReclaimThresh_((std::numeric_limits<uint32_t>::max)()) {
+ initPointers();
+ }
+
+ TFramedTransport(std::shared_ptr<TTransport> transport)
+ : transport_(transport),
+ rBufSize_(0),
+ wBufSize_(DEFAULT_BUFFER_SIZE),
+ rBuf_(),
+ wBuf_(new uint8_t[wBufSize_]),
+ bufReclaimThresh_((std::numeric_limits<uint32_t>::max)()),
+ maxFrameSize_(DEFAULT_MAX_FRAME_SIZE) {
+ initPointers();
+ }
+
+ TFramedTransport(std::shared_ptr<TTransport> transport,
+ uint32_t sz,
+ uint32_t bufReclaimThresh = (std::numeric_limits<uint32_t>::max)())
+ : transport_(transport),
+ rBufSize_(0),
+ wBufSize_(sz),
+ rBuf_(),
+ wBuf_(new uint8_t[wBufSize_]),
+ bufReclaimThresh_(bufReclaimThresh),
+ maxFrameSize_(DEFAULT_MAX_FRAME_SIZE) {
+ initPointers();
+ }
+
+ void open() override { transport_->open(); }
+
+ bool isOpen() const override { return transport_->isOpen(); }
+
+ bool peek() override { return (rBase_ < rBound_) || transport_->peek(); }
+
+ void close() override {
+ flush();
+ transport_->close();
+ }
+
+ uint32_t readSlow(uint8_t* buf, uint32_t len) override;
+
+ void writeSlow(const uint8_t* buf, uint32_t len) override;
+
+ void flush() override;
+
+ uint32_t readEnd() override;
+
+ uint32_t writeEnd() override;
+
+ const uint8_t* borrowSlow(uint8_t* buf, uint32_t* len) override;
+
+ std::shared_ptr<TTransport> getUnderlyingTransport() { return transport_; }
+
+ /*
+ * TVirtualTransport provides a default implementation of readAll().
+ * We want to use the TBufferBase version instead.
+ */
+ using TBufferBase::readAll;
+
+ /**
+ * Returns the origin of the underlying transport
+ */
+ const std::string getOrigin() const override { return transport_->getOrigin(); }
+
+ /**
+ * Set the maximum size of the frame at read
+ */
+ void setMaxFrameSize(uint32_t maxFrameSize) { maxFrameSize_ = maxFrameSize; }
+
+ /**
+ * Get the maximum size of the frame at read
+ */
+ uint32_t getMaxFrameSize() { return maxFrameSize_; }
+
+protected:
+ /**
+ * Reads a frame of input from the underlying stream.
+ *
+ * Returns true if a frame was read successfully, or false on EOF.
+ * (Raises a TTransportException if EOF occurs after a partial frame.)
+ */
+ virtual bool readFrame();
+
+ void initPointers() {
+ setReadBuffer(nullptr, 0);
+ setWriteBuffer(wBuf_.get(), wBufSize_);
+
+ // Pad the buffer so we can insert the size later.
+ int32_t pad = 0;
+ this->write((uint8_t*)&pad, sizeof(pad));
+ }
+
+ std::shared_ptr<TTransport> transport_;
+
+ uint32_t rBufSize_;
+ uint32_t wBufSize_;
+ boost::scoped_array<uint8_t> rBuf_;
+ boost::scoped_array<uint8_t> wBuf_;
+ uint32_t bufReclaimThresh_;
+ uint32_t maxFrameSize_;
+};
+
+/**
+ * Wraps a transport into a framed one.
+ *
+ */
+class TFramedTransportFactory : public TTransportFactory {
+public:
+ TFramedTransportFactory() = default;
+
+ ~TFramedTransportFactory() override = default;
+
+ /**
+ * Wraps the transport into a framed one.
+ */
+ std::shared_ptr<TTransport> getTransport(std::shared_ptr<TTransport> trans) override {
+ return std::shared_ptr<TTransport>(new TFramedTransport(trans));
+ }
+};
+
+/**
+ * A memory buffer is a tranpsort that simply reads from and writes to an
+ * in memory buffer. Anytime you call write on it, the data is simply placed
+ * into a buffer, and anytime you call read, data is read from that buffer.
+ *
+ * The buffers are allocated using C constructs malloc,realloc, and the size
+ * doubles as necessary. We've considered using scoped
+ *
+ */
+class TMemoryBuffer : public TVirtualTransport<TMemoryBuffer, TBufferBase> {
+private:
+ // Common initialization done by all constructors.
+ void initCommon(uint8_t* buf, uint32_t size, bool owner, uint32_t wPos) {
+
+ maxBufferSize_ = (std::numeric_limits<uint32_t>::max)();
+
+ if (buf == nullptr && size != 0) {
+ assert(owner);
+ buf = (uint8_t*)std::malloc(size);
+ if (buf == nullptr) {
+ throw std::bad_alloc();
+ }
+ }
+
+ buffer_ = buf;
+ bufferSize_ = size;
+
+ rBase_ = buffer_;
+ rBound_ = buffer_ + wPos;
+ // TODO(dreiss): Investigate NULL-ing this if !owner.
+ wBase_ = buffer_ + wPos;
+ wBound_ = buffer_ + bufferSize_;
+
+ owner_ = owner;
+
+ // rBound_ is really an artifact. In principle, it should always be
+ // equal to wBase_. We update it in a few places (computeRead, etc.).
+ }
+
+public:
+ static const uint32_t defaultSize = 1024;
+
+ /**
+ * This enum specifies how a TMemoryBuffer should treat
+ * memory passed to it via constructors or resetBuffer.
+ *
+ * OBSERVE:
+ * TMemoryBuffer will simply store a pointer to the memory.
+ * It is the callers responsibility to ensure that the pointer
+ * remains valid for the lifetime of the TMemoryBuffer,
+ * and that it is properly cleaned up.
+ * Note that no data can be written to observed buffers.
+ *
+ * COPY:
+ * TMemoryBuffer will make an internal copy of the buffer.
+ * The caller has no responsibilities.
+ *
+ * TAKE_OWNERSHIP:
+ * TMemoryBuffer will become the "owner" of the buffer,
+ * and will be responsible for freeing it.
+ * The membory must have been allocated with malloc.
+ */
+ enum MemoryPolicy { OBSERVE = 1, COPY = 2, TAKE_OWNERSHIP = 3 };
+
+ /**
+ * Construct a TMemoryBuffer with a default-sized buffer,
+ * owned by the TMemoryBuffer object.
+ */
+ TMemoryBuffer() { initCommon(nullptr, defaultSize, true, 0); }
+
+ /**
+ * Construct a TMemoryBuffer with a buffer of a specified size,
+ * owned by the TMemoryBuffer object.
+ *
+ * @param sz The initial size of the buffer.
+ */
+ TMemoryBuffer(uint32_t sz) { initCommon(nullptr, sz, true, 0); }
+
+ /**
+ * Construct a TMemoryBuffer with buf as its initial contents.
+ *
+ * @param buf The initial contents of the buffer.
+ * Note that, while buf is a non-const pointer,
+ * TMemoryBuffer will not write to it if policy == OBSERVE,
+ * so it is safe to const_cast<uint8_t*>(whatever).
+ * @param sz The size of @c buf.
+ * @param policy See @link MemoryPolicy @endlink .
+ */
+ TMemoryBuffer(uint8_t* buf, uint32_t sz, MemoryPolicy policy = OBSERVE) {
+ if (buf == nullptr && sz != 0) {
+ throw TTransportException(TTransportException::BAD_ARGS,
+ "TMemoryBuffer given null buffer with non-zero size.");
+ }
+
+ switch (policy) {
+ case OBSERVE:
+ case TAKE_OWNERSHIP:
+ initCommon(buf, sz, policy == TAKE_OWNERSHIP, sz);
+ break;
+ case COPY:
+ initCommon(nullptr, sz, true, 0);
+ this->write(buf, sz);
+ break;
+ default:
+ throw TTransportException(TTransportException::BAD_ARGS,
+ "Invalid MemoryPolicy for TMemoryBuffer");
+ }
+ }
+
+ ~TMemoryBuffer() override {
+ if (owner_) {
+ std::free(buffer_);
+ }
+ }
+
+ bool isOpen() const override { return true; }
+
+ bool peek() override { return (rBase_ < wBase_); }
+
+ void open() override {}
+
+ void close() override {}
+
+ // TODO(dreiss): Make bufPtr const.
+ void getBuffer(uint8_t** bufPtr, uint32_t* sz) {
+ *bufPtr = rBase_;
+ *sz = static_cast<uint32_t>(wBase_ - rBase_);
+ }
+
+ std::string getBufferAsString() {
+ if (buffer_ == nullptr) {
+ return "";
+ }
+ uint8_t* buf;
+ uint32_t sz;
+ getBuffer(&buf, &sz);
+ return std::string((char*)buf, (std::string::size_type)sz);
+ }
+
+ void appendBufferToString(std::string& str) {
+ if (buffer_ == nullptr) {
+ return;
+ }
+ uint8_t* buf;
+ uint32_t sz;
+ getBuffer(&buf, &sz);
+ str.append((char*)buf, sz);
+ }
+
+ void resetBuffer() {
+ rBase_ = buffer_;
+ rBound_ = buffer_;
+ wBase_ = buffer_;
+ // It isn't safe to write into a buffer we don't own.
+ if (!owner_) {
+ wBound_ = wBase_;
+ bufferSize_ = 0;
+ }
+ }
+
+ /// See constructor documentation.
+ void resetBuffer(uint8_t* buf, uint32_t sz, MemoryPolicy policy = OBSERVE) {
+ // Use a variant of the copy-and-swap trick for assignment operators.
+ // This is sub-optimal in terms of performance for two reasons:
+ // 1/ The constructing and swapping of the (small) values
+ // in the temporary object takes some time, and is not necessary.
+ // 2/ If policy == COPY, we allocate the new buffer before
+ // freeing the old one, precluding the possibility of
+ // reusing that memory.
+ // I doubt that either of these problems could be optimized away,
+ // but the second is probably no a common case, and the first is minor.
+ // I don't expect resetBuffer to be a common operation, so I'm willing to
+ // bite the performance bullet to make the method this simple.
+
+ // Construct the new buffer.
+ TMemoryBuffer new_buffer(buf, sz, policy);
+ // Move it into ourself.
+ this->swap(new_buffer);
+ // Our old self gets destroyed.
+ }
+
+ /// See constructor documentation.
+ void resetBuffer(uint32_t sz) {
+ // Construct the new buffer.
+ TMemoryBuffer new_buffer(sz);
+ // Move it into ourself.
+ this->swap(new_buffer);
+ // Our old self gets destroyed.
+ }
+
+ std::string readAsString(uint32_t len) {
+ std::string str;
+ (void)readAppendToString(str, len);
+ return str;
+ }
+
+ uint32_t readAppendToString(std::string& str, uint32_t len);
+
+ // return number of bytes read
+ uint32_t readEnd() override {
+ // This cast should be safe, because buffer_'s size is a uint32_t
+ auto bytes = static_cast<uint32_t>(rBase_ - buffer_);
+ if (rBase_ == wBase_) {
+ resetBuffer();
+ }
+ return bytes;
+ }
+
+ // Return number of bytes written
+ uint32_t writeEnd() override {
+ // This cast should be safe, because buffer_'s size is a uint32_t
+ return static_cast<uint32_t>(wBase_ - buffer_);
+ }
+
+ uint32_t available_read() const {
+ // Remember, wBase_ is the real rBound_.
+ return static_cast<uint32_t>(wBase_ - rBase_);
+ }
+
+ uint32_t available_write() const { return static_cast<uint32_t>(wBound_ - wBase_); }
+
+ // Returns a pointer to where the client can write data to append to
+ // the TMemoryBuffer, and ensures the buffer is big enough to accommodate a
+ // write of the provided length. The returned pointer is very convenient for
+ // passing to read(), recv(), or similar. You must call wroteBytes() as soon
+ // as data is written or the buffer will not be aware that data has changed.
+ uint8_t* getWritePtr(uint32_t len) {
+ ensureCanWrite(len);
+ return wBase_;
+ }
+
+ // Informs the buffer that the client has written 'len' bytes into storage
+ // that had been provided by getWritePtr().
+ void wroteBytes(uint32_t len);
+
+ /*
+ * TVirtualTransport provides a default implementation of readAll().
+ * We want to use the TBufferBase version instead.
+ */
+ uint32_t readAll(uint8_t* buf, uint32_t len) { return TBufferBase::readAll(buf, len); }
+
+ //! \brief Get the current buffer size
+ //! \returns the current buffer size
+ uint32_t getBufferSize() const {
+ return bufferSize_;
+ }
+
+ //! \brief Get the current maximum buffer size
+ //! \returns the current maximum buffer size
+ uint32_t getMaxBufferSize() const {
+ return maxBufferSize_;
+ }
+
+ //! \brief Change the maximum buffer size
+ //! \param[in] maxSize the new maximum buffer size allowed to grow to
+ //! \throws TTransportException(BAD_ARGS) if maxSize is less than the current buffer size
+ void setMaxBufferSize(uint32_t maxSize) {
+ if (maxSize < bufferSize_) {
+ throw TTransportException(TTransportException::BAD_ARGS,
+ "Maximum buffer size would be less than current buffer size");
+ }
+ maxBufferSize_ = maxSize;
+ }
+
+protected:
+ void swap(TMemoryBuffer& that) {
+ using std::swap;
+ swap(buffer_, that.buffer_);
+ swap(bufferSize_, that.bufferSize_);
+
+ swap(rBase_, that.rBase_);
+ swap(rBound_, that.rBound_);
+ swap(wBase_, that.wBase_);
+ swap(wBound_, that.wBound_);
+
+ swap(owner_, that.owner_);
+ }
+
+ // Make sure there's at least 'len' bytes available for writing.
+ void ensureCanWrite(uint32_t len);
+
+ // Compute the position and available data for reading.
+ void computeRead(uint32_t len, uint8_t** out_start, uint32_t* out_give);
+
+ uint32_t readSlow(uint8_t* buf, uint32_t len) override;
+
+ void writeSlow(const uint8_t* buf, uint32_t len) override;
+
+ const uint8_t* borrowSlow(uint8_t* buf, uint32_t* len) override;
+
+ // Data buffer
+ uint8_t* buffer_;
+
+ // Allocated buffer size
+ uint32_t bufferSize_;
+
+ // Maximum allowed size
+ uint32_t maxBufferSize_;
+
+ // Is this object the owner of the buffer?
+ bool owner_;
+
+ // Don't forget to update constrctors, initCommon, and swap if
+ // you add new members.
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_TBUFFERTRANSPORTS_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TFDTransport.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TFDTransport.cpp
new file mode 100644
index 000000000..93dd10021
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TFDTransport.cpp
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <cerrno>
+#include <exception>
+
+#include <thrift/transport/TFDTransport.h>
+#include <thrift/transport/PlatformSocket.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef _WIN32
+#include <io.h>
+#endif
+
+using std::string;
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+void TFDTransport::close() {
+ if (!isOpen()) {
+ return;
+ }
+
+ int rv = ::THRIFT_CLOSE(fd_);
+ int errno_copy = THRIFT_ERRNO;
+ fd_ = -1;
+ // Have to check uncaught_exception because this is called in the destructor.
+ if (rv < 0 && !std::uncaught_exception()) {
+ throw TTransportException(TTransportException::UNKNOWN, "TFDTransport::close()", errno_copy);
+ }
+}
+
+uint32_t TFDTransport::read(uint8_t* buf, uint32_t len) {
+ unsigned int maxRetries = 5; // same as the TSocket default
+ unsigned int retries = 0;
+ while (true) {
+ THRIFT_SSIZET rv = ::THRIFT_READ(fd_, buf, len);
+ if (rv < 0) {
+ if (THRIFT_ERRNO == THRIFT_EINTR && retries < maxRetries) {
+ // If interrupted, try again
+ ++retries;
+ continue;
+ }
+ int errno_copy = THRIFT_ERRNO;
+ throw TTransportException(TTransportException::UNKNOWN, "TFDTransport::read()", errno_copy);
+ }
+ // this should be fine, since we already checked for negative values,
+ // and ::read should only return a 32-bit value since len is 32-bit.
+ return static_cast<uint32_t>(rv);
+ }
+}
+
+void TFDTransport::write(const uint8_t* buf, uint32_t len) {
+ while (len > 0) {
+ THRIFT_SSIZET rv = ::THRIFT_WRITE(fd_, buf, len);
+
+ if (rv < 0) {
+ int errno_copy = THRIFT_ERRNO;
+ throw TTransportException(TTransportException::UNKNOWN, "TFDTransport::write()", errno_copy);
+ } else if (rv == 0) {
+ throw TTransportException(TTransportException::END_OF_FILE, "TFDTransport::write()");
+ }
+
+ buf += rv;
+ // this should be fine, as we've already checked for negative values, and
+ //::write shouldn't return more than a uint32_t since len is a uint32_t
+ len -= static_cast<uint32_t>(rv);
+ }
+}
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TFDTransport.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TFDTransport.h
new file mode 100644
index 000000000..a3cf51948
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TFDTransport.h
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TFDTRANSPORT_H_
+#define _THRIFT_TRANSPORT_TFDTRANSPORT_H_ 1
+
+#include <string>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <thrift/transport/TTransport.h>
+#include <thrift/transport/TVirtualTransport.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * Dead-simple wrapper around a file descriptor.
+ *
+ */
+class TFDTransport : public TVirtualTransport<TFDTransport> {
+public:
+ enum ClosePolicy { NO_CLOSE_ON_DESTROY = 0, CLOSE_ON_DESTROY = 1 };
+
+ TFDTransport(int fd, ClosePolicy close_policy = NO_CLOSE_ON_DESTROY)
+ : fd_(fd), close_policy_(close_policy) {}
+
+ ~TFDTransport() override {
+ if (close_policy_ == CLOSE_ON_DESTROY) {
+ try {
+ close();
+ } catch (TTransportException& ex) {
+ GlobalOutput.printf("~TFDTransport TTransportException: '%s'", ex.what());
+ }
+ }
+ }
+
+ bool isOpen() const override { return fd_ >= 0; }
+
+ void open() override {}
+
+ void close() override;
+
+ uint32_t read(uint8_t* buf, uint32_t len);
+
+ void write(const uint8_t* buf, uint32_t len);
+
+ void setFD(int fd) { fd_ = fd; }
+ int getFD() { return fd_; }
+
+protected:
+ int fd_;
+ ClosePolicy close_policy_;
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_TFDTRANSPORT_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TFileTransport.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TFileTransport.cpp
new file mode 100644
index 000000000..eaf2bc365
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TFileTransport.cpp
@@ -0,0 +1,1068 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+
+#include <thrift/transport/TFileTransport.h>
+#include <thrift/transport/TTransportUtils.h>
+#include <thrift/transport/PlatformSocket.h>
+#include <thrift/concurrency/FunctionRunner.h>
+
+#include <boost/version.hpp>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <limits>
+#include <memory>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef _WIN32
+#include <io.h>
+#endif
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+using std::shared_ptr;
+using std::cerr;
+using std::cout;
+using std::endl;
+using std::string;
+using namespace apache::thrift::protocol;
+using namespace apache::thrift::concurrency;
+
+TFileTransport::TFileTransport(string path, bool readOnly)
+ : readState_(),
+ readBuff_(nullptr),
+ currentEvent_(nullptr),
+ readBuffSize_(DEFAULT_READ_BUFF_SIZE),
+ readTimeout_(NO_TAIL_READ_TIMEOUT),
+ chunkSize_(DEFAULT_CHUNK_SIZE),
+ eventBufferSize_(DEFAULT_EVENT_BUFFER_SIZE),
+ flushMaxUs_(DEFAULT_FLUSH_MAX_US),
+ flushMaxBytes_(DEFAULT_FLUSH_MAX_BYTES),
+ maxEventSize_(DEFAULT_MAX_EVENT_SIZE),
+ maxCorruptedEvents_(DEFAULT_MAX_CORRUPTED_EVENTS),
+ eofSleepTime_(DEFAULT_EOF_SLEEP_TIME_US),
+ corruptedEventSleepTime_(DEFAULT_CORRUPTED_SLEEP_TIME_US),
+ writerThreadIOErrorSleepTime_(DEFAULT_WRITER_THREAD_SLEEP_TIME_US),
+ dequeueBuffer_(nullptr),
+ enqueueBuffer_(nullptr),
+ notFull_(&mutex_),
+ notEmpty_(&mutex_),
+ closing_(false),
+ flushed_(&mutex_),
+ forceFlush_(false),
+ filename_(path),
+ fd_(0),
+ bufferAndThreadInitialized_(false),
+ offset_(0),
+ lastBadChunk_(0),
+ numCorruptedEventsInChunk_(0),
+ readOnly_(readOnly) {
+ threadFactory_.setDetached(false);
+ openLogFile();
+}
+
+void TFileTransport::resetOutputFile(int fd, string filename, off_t offset) {
+ filename_ = filename;
+ offset_ = offset;
+
+ // check if current file is still open
+ if (fd_ > 0) {
+ // flush any events in the queue
+ flush();
+ GlobalOutput.printf("error, current file (%s) not closed", filename_.c_str());
+ if (-1 == ::THRIFT_CLOSE(fd_)) {
+ int errno_copy = THRIFT_ERRNO;
+ GlobalOutput.perror("TFileTransport: resetOutputFile() ::close() ", errno_copy);
+ throw TTransportException(TTransportException::UNKNOWN,
+ "TFileTransport: error in file close",
+ errno_copy);
+ } else {
+ // successfully closed fd
+ fd_ = 0;
+ }
+ }
+
+ if (fd) {
+ fd_ = fd;
+ } else {
+ // open file if the input fd is 0
+ openLogFile();
+ }
+}
+
+TFileTransport::~TFileTransport() {
+ // flush the buffer if a writer thread is active
+ if (writerThread_.get()) {
+ // set state to closing
+ closing_ = true;
+
+ // wake up the writer thread
+ // Since closing_ is true, it will attempt to flush all data, then exit.
+ notEmpty_.notify();
+
+ writerThread_->join();
+ writerThread_.reset();
+ }
+
+ if (dequeueBuffer_) {
+ delete dequeueBuffer_;
+ dequeueBuffer_ = nullptr;
+ }
+
+ if (enqueueBuffer_) {
+ delete enqueueBuffer_;
+ enqueueBuffer_ = nullptr;
+ }
+
+ if (readBuff_) {
+ delete[] readBuff_;
+ readBuff_ = nullptr;
+ }
+
+ if (currentEvent_) {
+ delete currentEvent_;
+ currentEvent_ = nullptr;
+ }
+
+ // close logfile
+ if (fd_ > 0) {
+ if (-1 == ::THRIFT_CLOSE(fd_)) {
+ GlobalOutput.perror("TFileTransport: ~TFileTransport() ::close() ", THRIFT_ERRNO);
+ } else {
+ // successfully closed fd
+ fd_ = 0;
+ }
+ }
+}
+
+bool TFileTransport::initBufferAndWriteThread() {
+ if (bufferAndThreadInitialized_) {
+ T_ERROR("%s", "Trying to double-init TFileTransport");
+ return false;
+ }
+
+ if (!writerThread_.get()) {
+ writerThread_ = threadFactory_.newThread(
+ apache::thrift::concurrency::FunctionRunner::create(startWriterThread, this));
+ writerThread_->start();
+ }
+
+ dequeueBuffer_ = new TFileTransportBuffer(eventBufferSize_);
+ enqueueBuffer_ = new TFileTransportBuffer(eventBufferSize_);
+ bufferAndThreadInitialized_ = true;
+
+ return true;
+}
+
+void TFileTransport::write(const uint8_t* buf, uint32_t len) {
+ if (readOnly_) {
+ throw TTransportException("TFileTransport: attempting to write to file opened readonly");
+ }
+
+ enqueueEvent(buf, len);
+}
+
+template <class _T>
+struct uniqueDeleter
+{
+ void operator()(_T *ptr) const { delete ptr; }
+};
+
+void TFileTransport::enqueueEvent(const uint8_t* buf, uint32_t eventLen) {
+ // can't enqueue more events if file is going to close
+ if (closing_) {
+ return;
+ }
+
+ // make sure that event size is valid
+ if ((maxEventSize_ > 0) && (eventLen > maxEventSize_)) {
+ T_ERROR("msg size is greater than max event size: %u > %u\n", eventLen, maxEventSize_);
+ return;
+ }
+
+ if (eventLen == 0) {
+ T_ERROR("%s", "cannot enqueue an empty event");
+ return;
+ }
+
+ std::unique_ptr<eventInfo, uniqueDeleter<eventInfo> > toEnqueue(new eventInfo());
+ toEnqueue->eventBuff_ = new uint8_t[(sizeof(uint8_t) * eventLen) + 4];
+
+ // first 4 bytes is the event length
+ memcpy(toEnqueue->eventBuff_, (void*)(&eventLen), 4);
+ // actual event contents
+ memcpy(toEnqueue->eventBuff_ + 4, buf, eventLen);
+ toEnqueue->eventSize_ = eventLen + 4;
+
+ // lock mutex
+ Guard g(mutex_);
+
+ // make sure that enqueue buffer is initialized and writer thread is running
+ if (!bufferAndThreadInitialized_) {
+ if (!initBufferAndWriteThread()) {
+ return;
+ }
+ }
+
+ // Can't enqueue while buffer is full
+ while (enqueueBuffer_->isFull()) {
+ notFull_.wait();
+ }
+
+ // We shouldn't be trying to enqueue new data while a forced flush is
+ // requested. (Otherwise the writer thread might not ever be able to finish
+ // the flush if more data keeps being enqueued.)
+ assert(!forceFlush_);
+
+ // add to the buffer
+ eventInfo* pEvent = toEnqueue.release();
+ if (!enqueueBuffer_->addEvent(pEvent)) {
+ delete pEvent;
+ return;
+ }
+
+ // signal anybody who's waiting for the buffer to be non-empty
+ notEmpty_.notify();
+
+ // this really should be a loop where it makes sure it got flushed
+ // because condition variables can get triggered by the os for no reason
+ // it is probably a non-factor for the time being
+}
+
+bool TFileTransport::swapEventBuffers(const std::chrono::time_point<std::chrono::steady_clock> *deadline) {
+ bool swap;
+ Guard g(mutex_);
+
+ if (!enqueueBuffer_->isEmpty()) {
+ swap = true;
+ } else if (closing_) {
+ // even though there is no data to write,
+ // return immediately if the transport is closing
+ swap = false;
+ } else {
+ if (deadline != nullptr) {
+ // if we were handed a deadline time struct, do a timed wait
+ notEmpty_.waitForTime(*deadline);
+ } else {
+ // just wait until the buffer gets an item
+ notEmpty_.wait();
+ }
+
+ // could be empty if we timed out
+ swap = enqueueBuffer_->isEmpty();
+ }
+
+ if (swap) {
+ TFileTransportBuffer* temp = enqueueBuffer_;
+ enqueueBuffer_ = dequeueBuffer_;
+ dequeueBuffer_ = temp;
+ }
+
+ if (swap) {
+ notFull_.notify();
+ }
+
+ return swap;
+}
+
+void TFileTransport::writerThread() {
+ bool hasIOError = false;
+
+ // open file if it is not open
+ if (!fd_) {
+ try {
+ openLogFile();
+ } catch (...) {
+ int errno_copy = THRIFT_ERRNO;
+ GlobalOutput.perror("TFileTransport: writerThread() openLogFile() ", errno_copy);
+ fd_ = 0;
+ hasIOError = true;
+ }
+ }
+
+ // set the offset to the correct value (EOF)
+ if (!hasIOError) {
+ try {
+ seekToEnd();
+ // throw away any partial events
+ offset_ += readState_.lastDispatchPtr_;
+ if (0 == THRIFT_FTRUNCATE(fd_, offset_)) {
+ readState_.resetAllValues();
+ } else {
+ int errno_copy = THRIFT_ERRNO;
+ GlobalOutput.perror("TFileTransport: writerThread() truncate ", errno_copy);
+ hasIOError = true;
+ }
+ } catch (...) {
+ int errno_copy = THRIFT_ERRNO;
+ GlobalOutput.perror("TFileTransport: writerThread() initialization ", errno_copy);
+ hasIOError = true;
+ }
+ }
+
+ // Figure out the next time by which a flush must take place
+ auto ts_next_flush = getNextFlushTime();
+ uint32_t unflushed = 0;
+
+ while (1) {
+ // this will only be true when the destructor is being invoked
+ if (closing_) {
+ if (hasIOError) {
+ return;
+ }
+
+ // Try to empty buffers before exit
+ if (enqueueBuffer_->isEmpty() && dequeueBuffer_->isEmpty()) {
+ ::THRIFT_FSYNC(fd_);
+ if (-1 == ::THRIFT_CLOSE(fd_)) {
+ int errno_copy = THRIFT_ERRNO;
+ GlobalOutput.perror("TFileTransport: writerThread() ::close() ", errno_copy);
+ } else {
+ // fd successfully closed
+ fd_ = 0;
+ }
+ return;
+ }
+ }
+
+ if (swapEventBuffers(&ts_next_flush)) {
+ eventInfo* outEvent;
+ while (nullptr != (outEvent = dequeueBuffer_->getNext())) {
+ // Remove an event from the buffer and write it out to disk. If there is any IO error, for
+ // instance,
+ // the output file is unmounted or deleted, then this event is dropped. However, the writer
+ // thread
+ // will: (1) sleep for a short while; (2) try to reopen the file; (3) if successful then
+ // start writing
+ // from the end.
+
+ while (hasIOError) {
+ T_ERROR(
+ "TFileTransport: writer thread going to sleep for %u microseconds due to IO errors",
+ writerThreadIOErrorSleepTime_);
+ THRIFT_SLEEP_USEC(writerThreadIOErrorSleepTime_);
+ if (closing_) {
+ return;
+ }
+ if (!fd_) {
+ ::THRIFT_CLOSE(fd_);
+ fd_ = 0;
+ }
+ try {
+ openLogFile();
+ seekToEnd();
+ unflushed = 0;
+ hasIOError = false;
+ T_LOG_OPER(
+ "TFileTransport: log file %s reopened by writer thread during error recovery",
+ filename_.c_str());
+ } catch (...) {
+ T_ERROR("TFileTransport: unable to reopen log file %s during error recovery",
+ filename_.c_str());
+ }
+ }
+
+ // sanity check on event
+ if ((maxEventSize_ > 0) && (outEvent->eventSize_ > maxEventSize_)) {
+ T_ERROR("msg size is greater than max event size: %u > %u\n",
+ outEvent->eventSize_,
+ maxEventSize_);
+ continue;
+ }
+
+ // If chunking is required, then make sure that msg does not cross chunk boundary
+ if ((outEvent->eventSize_ > 0) && (chunkSize_ != 0)) {
+ // event size must be less than chunk size
+ if (outEvent->eventSize_ > chunkSize_) {
+ T_ERROR("TFileTransport: event size(%u) > chunk size(%u): skipping event",
+ outEvent->eventSize_,
+ chunkSize_);
+ continue;
+ }
+
+ int64_t chunk1 = offset_ / chunkSize_;
+ int64_t chunk2 = (offset_ + outEvent->eventSize_ - 1) / chunkSize_;
+
+ // if adding this event will cross a chunk boundary, pad the chunk with zeros
+ if (chunk1 != chunk2) {
+ // refetch the offset to keep in sync
+ offset_ = THRIFT_LSEEK(fd_, 0, SEEK_CUR);
+ auto padding = (int32_t)((offset_ / chunkSize_ + 1) * chunkSize_ - offset_);
+
+ auto* zeros = new uint8_t[padding];
+ memset(zeros, '\0', padding);
+ boost::scoped_array<uint8_t> array(zeros);
+ if (-1 == ::THRIFT_WRITE(fd_, zeros, padding)) {
+ int errno_copy = THRIFT_ERRNO;
+ GlobalOutput.perror("TFileTransport: writerThread() error while padding zeros ",
+ errno_copy);
+ hasIOError = true;
+ continue;
+ }
+ unflushed += padding;
+ offset_ += padding;
+ }
+ }
+
+ // write the dequeued event to the file
+ if (outEvent->eventSize_ > 0) {
+ if (-1 == ::THRIFT_WRITE(fd_, outEvent->eventBuff_, outEvent->eventSize_)) {
+ int errno_copy = THRIFT_ERRNO;
+ GlobalOutput.perror("TFileTransport: error while writing event ", errno_copy);
+ hasIOError = true;
+ continue;
+ }
+ unflushed += outEvent->eventSize_;
+ offset_ += outEvent->eventSize_;
+ }
+ }
+ dequeueBuffer_->reset();
+ }
+
+ if (hasIOError) {
+ continue;
+ }
+
+ // Local variable to cache the state of forceFlush_.
+ //
+ // We only want to check the value of forceFlush_ once each time around the
+ // loop. If we check it more than once without holding the lock the entire
+ // time, it could have changed state in between. This will result in us
+ // making inconsistent decisions.
+ bool forced_flush = false;
+ {
+ Guard g(mutex_);
+ if (forceFlush_) {
+ if (!enqueueBuffer_->isEmpty()) {
+ // If forceFlush_ is true, we need to flush all available data.
+ // If enqueueBuffer_ is not empty, go back to the start of the loop to
+ // write it out.
+ //
+ // We know the main thread is waiting on forceFlush_ to be cleared,
+ // so no new events will be added to enqueueBuffer_ until we clear
+ // forceFlush_. Therefore the next time around the loop enqueueBuffer_
+ // is guaranteed to be empty. (I.e., we're guaranteed to make progress
+ // and clear forceFlush_ the next time around the loop.)
+ continue;
+ }
+ forced_flush = true;
+ }
+ }
+
+ // determine if we need to perform an fsync
+ bool flush = false;
+ if (forced_flush || unflushed > flushMaxBytes_) {
+ flush = true;
+ } else {
+ if (std::chrono::steady_clock::now() > ts_next_flush) {
+ if (unflushed > 0) {
+ flush = true;
+ } else {
+ // If there is no new data since the last fsync,
+ // don't perform the fsync, but do reset the timer.
+ ts_next_flush = getNextFlushTime();
+ }
+ }
+ }
+
+ if (flush) {
+ // sync (force flush) file to disk
+ THRIFT_FSYNC(fd_);
+ unflushed = 0;
+ ts_next_flush = getNextFlushTime();
+
+ // notify anybody waiting for flush completion
+ if (forced_flush) {
+ Guard g(mutex_);
+ forceFlush_ = false;
+ assert(enqueueBuffer_->isEmpty());
+ assert(dequeueBuffer_->isEmpty());
+ flushed_.notifyAll();
+ }
+ }
+ }
+}
+
+void TFileTransport::flush() {
+ // file must be open for writing for any flushing to take place
+ if (!writerThread_.get()) {
+ return;
+ }
+ // wait for flush to take place
+ Guard g(mutex_);
+
+ // Indicate that we are requesting a flush
+ forceFlush_ = true;
+ // Wake up the writer thread so it will perform the flush immediately
+ notEmpty_.notify();
+
+ while (forceFlush_) {
+ flushed_.wait();
+ }
+}
+
+uint32_t TFileTransport::readAll(uint8_t* buf, uint32_t len) {
+ uint32_t have = 0;
+ uint32_t get = 0;
+
+ while (have < len) {
+ get = read(buf + have, len - have);
+ if (get <= 0) {
+ throw TEOFException();
+ }
+ have += get;
+ }
+
+ return have;
+}
+
+bool TFileTransport::peek() {
+ // check if there is an event ready to be read
+ if (!currentEvent_) {
+ currentEvent_ = readEvent();
+ }
+
+ // did not manage to read an event from the file. This could have happened
+ // if the timeout expired or there was some other error
+ if (!currentEvent_) {
+ return false;
+ }
+
+ // check if there is anything to read
+ return (currentEvent_->eventSize_ - currentEvent_->eventBuffPos_) > 0;
+}
+
+uint32_t TFileTransport::read(uint8_t* buf, uint32_t len) {
+ // check if there an event is ready to be read
+ if (!currentEvent_) {
+ currentEvent_ = readEvent();
+ }
+
+ // did not manage to read an event from the file. This could have happened
+ // if the timeout expired or there was some other error
+ if (!currentEvent_) {
+ return 0;
+ }
+
+ // read as much of the current event as possible
+ int32_t remaining = currentEvent_->eventSize_ - currentEvent_->eventBuffPos_;
+ if (remaining <= (int32_t)len) {
+ // copy over anything thats remaining
+ if (remaining > 0) {
+ memcpy(buf, currentEvent_->eventBuff_ + currentEvent_->eventBuffPos_, remaining);
+ }
+ delete (currentEvent_);
+ currentEvent_ = nullptr;
+ return remaining;
+ }
+
+ // read as much as possible
+ memcpy(buf, currentEvent_->eventBuff_ + currentEvent_->eventBuffPos_, len);
+ currentEvent_->eventBuffPos_ += len;
+ return len;
+}
+
+// note caller is responsible for freeing returned events
+eventInfo* TFileTransport::readEvent() {
+ int readTries = 0;
+
+ if (!readBuff_) {
+ readBuff_ = new uint8_t[readBuffSize_];
+ }
+
+ while (1) {
+ // read from the file if read buffer is exhausted
+ if (readState_.bufferPtr_ == readState_.bufferLen_) {
+ // advance the offset pointer
+ offset_ += readState_.bufferLen_;
+ readState_.bufferLen_ = static_cast<uint32_t>(::THRIFT_READ(fd_, readBuff_, readBuffSize_));
+ // if (readState_.bufferLen_) {
+ // T_DEBUG_L(1, "Amount read: %u (offset: %lu)", readState_.bufferLen_, offset_);
+ // }
+ readState_.bufferPtr_ = 0;
+ readState_.lastDispatchPtr_ = 0;
+
+ // read error
+ if (readState_.bufferLen_ == -1) {
+ readState_.resetAllValues();
+ GlobalOutput("TFileTransport: error while reading from file");
+ throw TTransportException("TFileTransport: error while reading from file");
+ } else if (readState_.bufferLen_ == 0) { // EOF
+ // wait indefinitely if there is no timeout
+ if (readTimeout_ == TAIL_READ_TIMEOUT) {
+ THRIFT_SLEEP_USEC(eofSleepTime_);
+ continue;
+ } else if (readTimeout_ == NO_TAIL_READ_TIMEOUT) {
+ // reset state
+ readState_.resetState(0);
+ return nullptr;
+ } else if (readTimeout_ > 0) {
+ // timeout already expired once
+ if (readTries > 0) {
+ readState_.resetState(0);
+ return nullptr;
+ } else {
+ THRIFT_SLEEP_USEC(readTimeout_ * 1000);
+ readTries++;
+ continue;
+ }
+ }
+ }
+ }
+
+ readTries = 0;
+
+ // attempt to read an event from the buffer
+ while (readState_.bufferPtr_ < readState_.bufferLen_) {
+ if (readState_.readingSize_) {
+ if (readState_.eventSizeBuffPos_ == 0) {
+ if ((offset_ + readState_.bufferPtr_) / chunkSize_
+ != ((offset_ + readState_.bufferPtr_ + 3) / chunkSize_)) {
+ // skip one byte towards chunk boundary
+ // T_DEBUG_L(1, "Skipping a byte");
+ readState_.bufferPtr_++;
+ continue;
+ }
+ }
+
+ readState_.eventSizeBuff_[readState_.eventSizeBuffPos_++]
+ = readBuff_[readState_.bufferPtr_++];
+
+ if (readState_.eventSizeBuffPos_ == 4) {
+ if (readState_.getEventSize() == 0) {
+ // 0 length event indicates padding
+ // T_DEBUG_L(1, "Got padding");
+ readState_.resetState(readState_.lastDispatchPtr_);
+ continue;
+ }
+ // got a valid event
+ readState_.readingSize_ = false;
+ if (readState_.event_) {
+ delete (readState_.event_);
+ }
+ readState_.event_ = new eventInfo();
+ readState_.event_->eventSize_ = readState_.getEventSize();
+
+ // check if the event is corrupted and perform recovery if required
+ if (isEventCorrupted()) {
+ performRecovery();
+ // start from the top
+ break;
+ }
+ }
+ } else {
+ if (!readState_.event_->eventBuff_) {
+ readState_.event_->eventBuff_ = new uint8_t[readState_.event_->eventSize_];
+ readState_.event_->eventBuffPos_ = 0;
+ }
+ // take either the entire event or the remaining bytes in the buffer
+ int reclaimBuffer = (std::min)((uint32_t)(readState_.bufferLen_ - readState_.bufferPtr_),
+ readState_.event_->eventSize_ - readState_.event_->eventBuffPos_);
+
+ // copy data from read buffer into event buffer
+ memcpy(readState_.event_->eventBuff_ + readState_.event_->eventBuffPos_,
+ readBuff_ + readState_.bufferPtr_,
+ reclaimBuffer);
+
+ // increment position ptrs
+ readState_.event_->eventBuffPos_ += reclaimBuffer;
+ readState_.bufferPtr_ += reclaimBuffer;
+
+ // check if the event has been read in full
+ if (readState_.event_->eventBuffPos_ == readState_.event_->eventSize_) {
+ // set the completed event to the current event
+ eventInfo* completeEvent = readState_.event_;
+ completeEvent->eventBuffPos_ = 0;
+
+ readState_.event_ = nullptr;
+ readState_.resetState(readState_.bufferPtr_);
+
+ // exit criteria
+ return completeEvent;
+ }
+ }
+ }
+ }
+}
+
+bool TFileTransport::isEventCorrupted() {
+ // an error is triggered if:
+ if ((maxEventSize_ > 0) && (readState_.event_->eventSize_ > maxEventSize_)) {
+ // 1. Event size is larger than user-speficied max-event size
+ T_ERROR("Read corrupt event. Event size(%u) greater than max event size (%u)",
+ readState_.event_->eventSize_,
+ maxEventSize_);
+ return true;
+ } else if (readState_.event_->eventSize_ > chunkSize_) {
+ // 2. Event size is larger than chunk size
+ T_ERROR("Read corrupt event. Event size(%u) greater than chunk size (%u)",
+ readState_.event_->eventSize_,
+ chunkSize_);
+ return true;
+ } else if (((offset_ + readState_.bufferPtr_ - 4) / chunkSize_)
+ != ((offset_ + readState_.bufferPtr_ + readState_.event_->eventSize_ - 1)
+ / chunkSize_)) {
+ // 3. size indicates that event crosses chunk boundary
+ T_ERROR("Read corrupt event. Event crosses chunk boundary. Event size:%u Offset:%lu",
+ readState_.event_->eventSize_,
+ static_cast<unsigned long>(offset_ + readState_.bufferPtr_ + 4));
+
+ return true;
+ }
+
+ return false;
+}
+
+void TFileTransport::performRecovery() {
+ // perform some kickass recovery
+ uint32_t curChunk = getCurChunk();
+ if (lastBadChunk_ == curChunk) {
+ numCorruptedEventsInChunk_++;
+ } else {
+ lastBadChunk_ = curChunk;
+ numCorruptedEventsInChunk_ = 1;
+ }
+
+ if (numCorruptedEventsInChunk_ < maxCorruptedEvents_) {
+ // maybe there was an error in reading the file from disk
+ // seek to the beginning of chunk and try again
+ seekToChunk(curChunk);
+ } else {
+
+ // just skip ahead to the next chunk if we not already at the last chunk
+ if (curChunk != (getNumChunks() - 1)) {
+ seekToChunk(curChunk + 1);
+ } else if (readTimeout_ == TAIL_READ_TIMEOUT) {
+ // if tailing the file, wait until there is enough data to start
+ // the next chunk
+ while (curChunk == (getNumChunks() - 1)) {
+ THRIFT_SLEEP_USEC(corruptedEventSleepTime_);
+ }
+ seekToChunk(curChunk + 1);
+ } else {
+ // pretty hosed at this stage, rewind the file back to the last successful
+ // point and punt on the error
+ readState_.resetState(readState_.lastDispatchPtr_);
+ currentEvent_ = nullptr;
+ char errorMsg[1024];
+ sprintf(errorMsg,
+ "TFileTransport: log file corrupted at offset: %lu",
+ static_cast<unsigned long>(offset_ + readState_.lastDispatchPtr_));
+
+ GlobalOutput(errorMsg);
+ throw TTransportException(errorMsg);
+ }
+ }
+}
+
+void TFileTransport::seekToChunk(int32_t chunk) {
+ if (fd_ <= 0) {
+ throw TTransportException("File not open");
+ }
+
+ int32_t numChunks = getNumChunks();
+
+ // file is empty, seeking to chunk is pointless
+ if (numChunks == 0) {
+ return;
+ }
+
+ // negative indicates reverse seek (from the end)
+ if (chunk < 0) {
+ chunk += numChunks;
+ }
+
+ // too large a value for reverse seek, just seek to beginning
+ if (chunk < 0) {
+ T_DEBUG("%s", "Incorrect value for reverse seek. Seeking to beginning...");
+ chunk = 0;
+ }
+
+ // cannot seek past EOF
+ bool seekToEnd = false;
+ off_t minEndOffset = 0;
+ if (chunk >= numChunks) {
+ T_DEBUG("%s", "Trying to seek past EOF. Seeking to EOF instead...");
+ seekToEnd = true;
+ chunk = numChunks - 1;
+ // this is the min offset to process events till
+ minEndOffset = ::THRIFT_LSEEK(fd_, 0, SEEK_END);
+ }
+
+ off_t newOffset = off_t(chunk) * chunkSize_;
+ offset_ = ::THRIFT_LSEEK(fd_, newOffset, SEEK_SET);
+ readState_.resetAllValues();
+ currentEvent_ = nullptr;
+ if (offset_ == -1) {
+ GlobalOutput("TFileTransport: lseek error in seekToChunk");
+ throw TTransportException("TFileTransport: lseek error in seekToChunk");
+ }
+
+ // seek to EOF if user wanted to go to last chunk
+ if (seekToEnd) {
+ uint32_t oldReadTimeout = getReadTimeout();
+ setReadTimeout(NO_TAIL_READ_TIMEOUT);
+ // keep on reading unti the last event at point of seekChunk call
+ shared_ptr<eventInfo> event;
+ while ((offset_ + readState_.bufferPtr_) < minEndOffset) {
+ event.reset(readEvent());
+ if (event.get() == nullptr) {
+ break;
+ }
+ }
+ setReadTimeout(oldReadTimeout);
+ }
+}
+
+void TFileTransport::seekToEnd() {
+ seekToChunk(getNumChunks());
+}
+
+uint32_t TFileTransport::getNumChunks() {
+ if (fd_ <= 0) {
+ return 0;
+ }
+
+ struct THRIFT_STAT f_info;
+ int rv = ::THRIFT_FSTAT(fd_, &f_info);
+
+ if (rv < 0) {
+ int errno_copy = THRIFT_ERRNO;
+ throw TTransportException(TTransportException::UNKNOWN,
+ "TFileTransport::getNumChunks() (fstat)",
+ errno_copy);
+ }
+
+ if (f_info.st_size > 0) {
+ size_t numChunks = ((f_info.st_size) / chunkSize_) + 1;
+ if (numChunks > (std::numeric_limits<uint32_t>::max)())
+ throw TTransportException("Too many chunks");
+ return static_cast<uint32_t>(numChunks);
+ }
+
+ // empty file has no chunks
+ return 0;
+}
+
+uint32_t TFileTransport::getCurChunk() {
+ return static_cast<uint32_t>(offset_ / chunkSize_);
+}
+
+// Utility Functions
+void TFileTransport::openLogFile() {
+#ifndef _WIN32
+ mode_t mode = readOnly_ ? S_IRUSR | S_IRGRP | S_IROTH : S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ int flags = readOnly_ ? O_RDONLY : O_RDWR | O_CREAT | O_APPEND;
+#else
+ int mode = readOnly_ ? _S_IREAD : _S_IREAD | _S_IWRITE;
+ int flags = readOnly_ ? _O_RDONLY : _O_RDWR | _O_CREAT | _O_APPEND;
+#endif
+ fd_ = ::THRIFT_OPEN(filename_.c_str(), flags, mode);
+ offset_ = 0;
+
+ // make sure open call was successful
+ if (fd_ == -1) {
+ int errno_copy = THRIFT_ERRNO;
+ GlobalOutput.perror("TFileTransport: openLogFile() ::open() file: " + filename_, errno_copy);
+ throw TTransportException(TTransportException::NOT_OPEN, filename_, errno_copy);
+ }
+}
+
+std::chrono::time_point<std::chrono::steady_clock> TFileTransport::getNextFlushTime() {
+ return std::chrono::steady_clock::now() + std::chrono::microseconds(flushMaxUs_);
+}
+
+TFileTransportBuffer::TFileTransportBuffer(uint32_t size)
+ : bufferMode_(WRITE), writePoint_(0), readPoint_(0), size_(size) {
+ buffer_ = new eventInfo* [size];
+}
+
+TFileTransportBuffer::~TFileTransportBuffer() {
+ if (buffer_) {
+ for (uint32_t i = 0; i < writePoint_; i++) {
+ delete buffer_[i];
+ }
+ delete[] buffer_;
+ buffer_ = nullptr;
+ }
+}
+
+bool TFileTransportBuffer::addEvent(eventInfo* event) {
+ if (bufferMode_ == READ) {
+ GlobalOutput("Trying to write to a buffer in read mode");
+ }
+ if (writePoint_ < size_) {
+ buffer_[writePoint_++] = event;
+ return true;
+ } else {
+ // buffer is full
+ return false;
+ }
+}
+
+eventInfo* TFileTransportBuffer::getNext() {
+ if (bufferMode_ == WRITE) {
+ bufferMode_ = READ;
+ }
+ if (readPoint_ < writePoint_) {
+ return buffer_[readPoint_++];
+ } else {
+ // no more entries
+ return nullptr;
+ }
+}
+
+void TFileTransportBuffer::reset() {
+ if (bufferMode_ == WRITE || writePoint_ > readPoint_) {
+ T_DEBUG("%s", "Resetting a buffer with unread entries");
+ }
+ // Clean up the old entries
+ for (uint32_t i = 0; i < writePoint_; i++) {
+ delete buffer_[i];
+ }
+ bufferMode_ = WRITE;
+ writePoint_ = 0;
+ readPoint_ = 0;
+}
+
+bool TFileTransportBuffer::isFull() {
+ return writePoint_ == size_;
+}
+
+bool TFileTransportBuffer::isEmpty() {
+ return writePoint_ == 0;
+}
+
+TFileProcessor::TFileProcessor(shared_ptr<TProcessor> processor,
+ shared_ptr<TProtocolFactory> protocolFactory,
+ shared_ptr<TFileReaderTransport> inputTransport)
+ : processor_(processor),
+ inputProtocolFactory_(protocolFactory),
+ outputProtocolFactory_(protocolFactory),
+ inputTransport_(inputTransport) {
+
+ // default the output transport to a null transport (common case)
+ outputTransport_ = std::make_shared<TNullTransport>();
+}
+
+TFileProcessor::TFileProcessor(shared_ptr<TProcessor> processor,
+ shared_ptr<TProtocolFactory> inputProtocolFactory,
+ shared_ptr<TProtocolFactory> outputProtocolFactory,
+ shared_ptr<TFileReaderTransport> inputTransport)
+ : processor_(processor),
+ inputProtocolFactory_(inputProtocolFactory),
+ outputProtocolFactory_(outputProtocolFactory),
+ inputTransport_(inputTransport) {
+
+ // default the output transport to a null transport (common case)
+ outputTransport_ = std::make_shared<TNullTransport>();
+}
+
+TFileProcessor::TFileProcessor(shared_ptr<TProcessor> processor,
+ shared_ptr<TProtocolFactory> protocolFactory,
+ shared_ptr<TFileReaderTransport> inputTransport,
+ shared_ptr<TTransport> outputTransport)
+ : processor_(processor),
+ inputProtocolFactory_(protocolFactory),
+ outputProtocolFactory_(protocolFactory),
+ inputTransport_(inputTransport),
+ outputTransport_(outputTransport) {
+}
+
+void TFileProcessor::process(uint32_t numEvents, bool tail) {
+ shared_ptr<TProtocol> inputProtocol = inputProtocolFactory_->getProtocol(inputTransport_);
+ shared_ptr<TProtocol> outputProtocol = outputProtocolFactory_->getProtocol(outputTransport_);
+
+ // set the read timeout to 0 if tailing is required
+ int32_t oldReadTimeout = inputTransport_->getReadTimeout();
+ if (tail) {
+ // save old read timeout so it can be restored
+ inputTransport_->setReadTimeout(TFileTransport::TAIL_READ_TIMEOUT);
+ }
+
+ uint32_t numProcessed = 0;
+ while (1) {
+ // bad form to use exceptions for flow control but there is really
+ // no other way around it
+ try {
+ processor_->process(inputProtocol, outputProtocol, nullptr);
+ numProcessed++;
+ if ((numEvents > 0) && (numProcessed == numEvents)) {
+ return;
+ }
+ } catch (TEOFException&) {
+ if (!tail) {
+ break;
+ }
+ } catch (TException& te) {
+ cerr << te.what() << endl;
+ break;
+ }
+ }
+
+ // restore old read timeout
+ if (tail) {
+ inputTransport_->setReadTimeout(oldReadTimeout);
+ }
+}
+
+void TFileProcessor::processChunk() {
+ shared_ptr<TProtocol> inputProtocol = inputProtocolFactory_->getProtocol(inputTransport_);
+ shared_ptr<TProtocol> outputProtocol = outputProtocolFactory_->getProtocol(outputTransport_);
+
+ uint32_t curChunk = inputTransport_->getCurChunk();
+
+ while (1) {
+ // bad form to use exceptions for flow control but there is really
+ // no other way around it
+ try {
+ processor_->process(inputProtocol, outputProtocol, nullptr);
+ if (curChunk != inputTransport_->getCurChunk()) {
+ break;
+ }
+ } catch (TEOFException&) {
+ break;
+ } catch (TException& te) {
+ cerr << te.what() << endl;
+ break;
+ }
+ }
+}
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TFileTransport.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TFileTransport.h
new file mode 100644
index 000000000..0df5cf909
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TFileTransport.h
@@ -0,0 +1,439 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TFILETRANSPORT_H_
+#define _THRIFT_TRANSPORT_TFILETRANSPORT_H_ 1
+
+#include <thrift/transport/TTransport.h>
+#include <thrift/Thrift.h>
+#include <thrift/TProcessor.h>
+
+#include <atomic>
+#include <string>
+#include <stdio.h>
+
+#include <thrift/concurrency/Mutex.h>
+#include <thrift/concurrency/Monitor.h>
+#include <thrift/concurrency/ThreadFactory.h>
+#include <thrift/concurrency/Thread.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+using apache::thrift::TProcessor;
+using apache::thrift::protocol::TProtocolFactory;
+using apache::thrift::concurrency::Mutex;
+using apache::thrift::concurrency::Monitor;
+
+// Data pertaining to a single event
+typedef struct eventInfo {
+ uint8_t* eventBuff_;
+ uint32_t eventSize_;
+ uint32_t eventBuffPos_;
+
+ eventInfo() : eventBuff_(nullptr), eventSize_(0), eventBuffPos_(0){};
+ ~eventInfo() {
+ if (eventBuff_) {
+ delete[] eventBuff_;
+ }
+ }
+} eventInfo;
+
+// information about current read state
+typedef struct readState {
+ eventInfo* event_;
+
+ // keep track of event size
+ uint8_t eventSizeBuff_[4];
+ uint8_t eventSizeBuffPos_;
+ bool readingSize_;
+
+ // read buffer variables
+ int32_t bufferPtr_;
+ int32_t bufferLen_;
+
+ // last successful dispatch point
+ int32_t lastDispatchPtr_;
+
+ void resetState(uint32_t lastDispatchPtr) {
+ readingSize_ = true;
+ eventSizeBuffPos_ = 0;
+ lastDispatchPtr_ = lastDispatchPtr;
+ }
+
+ void resetAllValues() {
+ resetState(0);
+ bufferPtr_ = 0;
+ bufferLen_ = 0;
+ if (event_) {
+ delete (event_);
+ }
+ event_ = nullptr;
+ }
+
+ inline uint32_t getEventSize() {
+ const void* buffer = reinterpret_cast<const void*>(eventSizeBuff_);
+ return *reinterpret_cast<const uint32_t*>(buffer);
+ }
+
+ readState() {
+ event_ = nullptr;
+ resetAllValues();
+ }
+
+ ~readState() {
+ if (event_) {
+ delete (event_);
+ }
+ }
+
+} readState;
+
+/**
+ * TFileTransportBuffer - buffer class used by TFileTransport for queueing up events
+ * to be written to disk. Should be used in the following way:
+ * 1) Buffer created
+ * 2) Buffer written to (addEvent)
+ * 3) Buffer read from (getNext)
+ * 4) Buffer reset (reset)
+ * 5) Go back to 2, or destroy buffer
+ *
+ * The buffer should never be written to after it is read from, unless it is reset first.
+ * Note: The above rules are enforced mainly for debugging its sole client TFileTransport
+ * which uses the buffer in this way.
+ *
+ */
+class TFileTransportBuffer {
+public:
+ TFileTransportBuffer(uint32_t size);
+ ~TFileTransportBuffer();
+
+ bool addEvent(eventInfo* event);
+ eventInfo* getNext();
+ void reset();
+ bool isFull();
+ bool isEmpty();
+
+private:
+ TFileTransportBuffer(); // should not be used
+
+ enum mode { WRITE, READ };
+ mode bufferMode_;
+
+ uint32_t writePoint_;
+ uint32_t readPoint_;
+ uint32_t size_;
+ eventInfo** buffer_;
+};
+
+/**
+ * Abstract interface for transports used to read files
+ */
+class TFileReaderTransport : virtual public TTransport {
+public:
+ virtual int32_t getReadTimeout() = 0;
+ virtual void setReadTimeout(int32_t readTimeout) = 0;
+
+ virtual uint32_t getNumChunks() = 0;
+ virtual uint32_t getCurChunk() = 0;
+ virtual void seekToChunk(int32_t chunk) = 0;
+ virtual void seekToEnd() = 0;
+};
+
+/**
+ * Abstract interface for transports used to write files
+ */
+class TFileWriterTransport : virtual public TTransport {
+public:
+ virtual uint32_t getChunkSize() = 0;
+ virtual void setChunkSize(uint32_t chunkSize) = 0;
+};
+
+/**
+ * File implementation of a transport. Reads and writes are done to a
+ * file on disk.
+ *
+ */
+class TFileTransport : public TFileReaderTransport, public TFileWriterTransport {
+public:
+ TFileTransport(std::string path, bool readOnly = false);
+ ~TFileTransport() override;
+
+ // TODO: what is the correct behaviour for this?
+ // the log file is generally always open
+ bool isOpen() const override { return true; }
+
+ void write(const uint8_t* buf, uint32_t len);
+ void flush() override;
+
+ uint32_t readAll(uint8_t* buf, uint32_t len);
+ uint32_t read(uint8_t* buf, uint32_t len);
+ bool peek() override;
+
+ // log-file specific functions
+ void seekToChunk(int32_t chunk) override;
+ void seekToEnd() override;
+ uint32_t getNumChunks() override;
+ uint32_t getCurChunk() override;
+
+ // for changing the output file
+ void resetOutputFile(int fd, std::string filename, off_t offset);
+
+ // Setter/Getter functions for user-controllable options
+ void setReadBuffSize(uint32_t readBuffSize) {
+ if (readBuffSize) {
+ readBuffSize_ = readBuffSize;
+ }
+ }
+ uint32_t getReadBuffSize() { return readBuffSize_; }
+
+ static const int32_t TAIL_READ_TIMEOUT = -1;
+ static const int32_t NO_TAIL_READ_TIMEOUT = 0;
+ void setReadTimeout(int32_t readTimeout) override { readTimeout_ = readTimeout; }
+ int32_t getReadTimeout() override { return readTimeout_; }
+
+ void setChunkSize(uint32_t chunkSize) override {
+ if (chunkSize) {
+ chunkSize_ = chunkSize;
+ }
+ }
+ uint32_t getChunkSize() override { return chunkSize_; }
+
+ void setEventBufferSize(uint32_t bufferSize) {
+ if (bufferAndThreadInitialized_) {
+ GlobalOutput("Cannot change the buffer size after writer thread started");
+ return;
+ }
+ eventBufferSize_ = bufferSize;
+ }
+
+ uint32_t getEventBufferSize() { return eventBufferSize_; }
+
+ void setFlushMaxUs(uint32_t flushMaxUs) {
+ if (flushMaxUs) {
+ flushMaxUs_ = flushMaxUs;
+ }
+ }
+ uint32_t getFlushMaxUs() { return flushMaxUs_; }
+
+ void setFlushMaxBytes(uint32_t flushMaxBytes) {
+ if (flushMaxBytes) {
+ flushMaxBytes_ = flushMaxBytes;
+ }
+ }
+ uint32_t getFlushMaxBytes() { return flushMaxBytes_; }
+
+ void setMaxEventSize(uint32_t maxEventSize) { maxEventSize_ = maxEventSize; }
+ uint32_t getMaxEventSize() { return maxEventSize_; }
+
+ void setMaxCorruptedEvents(uint32_t maxCorruptedEvents) {
+ maxCorruptedEvents_ = maxCorruptedEvents;
+ }
+ uint32_t getMaxCorruptedEvents() { return maxCorruptedEvents_; }
+
+ void setEofSleepTimeUs(uint32_t eofSleepTime) {
+ if (eofSleepTime) {
+ eofSleepTime_ = eofSleepTime;
+ }
+ }
+ uint32_t getEofSleepTimeUs() { return eofSleepTime_; }
+
+ /*
+ * Override TTransport *_virt() functions to invoke our implementations.
+ * We cannot use TVirtualTransport to provide these, since we need to inherit
+ * virtually from TTransport.
+ */
+ uint32_t read_virt(uint8_t* buf, uint32_t len) override { return this->read(buf, len); }
+ uint32_t readAll_virt(uint8_t* buf, uint32_t len) override { return this->readAll(buf, len); }
+ void write_virt(const uint8_t* buf, uint32_t len) override { this->write(buf, len); }
+
+private:
+ // helper functions for writing to a file
+ void enqueueEvent(const uint8_t* buf, uint32_t eventLen);
+ bool swapEventBuffers(const std::chrono::time_point<std::chrono::steady_clock> *deadline);
+ bool initBufferAndWriteThread();
+
+ // control for writer thread
+ static void* startWriterThread(void* ptr) {
+ static_cast<TFileTransport*>(ptr)->writerThread();
+ return nullptr;
+ }
+ void writerThread();
+
+ // helper functions for reading from a file
+ eventInfo* readEvent();
+
+ // event corruption-related functions
+ bool isEventCorrupted();
+ void performRecovery();
+
+ // Utility functions
+ void openLogFile();
+ std::chrono::time_point<std::chrono::steady_clock> getNextFlushTime();
+
+ // Class variables
+ readState readState_;
+ uint8_t* readBuff_;
+ eventInfo* currentEvent_;
+
+ uint32_t readBuffSize_;
+ static const uint32_t DEFAULT_READ_BUFF_SIZE = 1 * 1024 * 1024;
+
+ int32_t readTimeout_;
+ static const int32_t DEFAULT_READ_TIMEOUT_MS = 200;
+
+ // size of chunks that file will be split up into
+ uint32_t chunkSize_;
+ static const uint32_t DEFAULT_CHUNK_SIZE = 16 * 1024 * 1024;
+
+ // size of event buffers
+ uint32_t eventBufferSize_;
+ static const uint32_t DEFAULT_EVENT_BUFFER_SIZE = 10000;
+
+ // max number of microseconds that can pass without flushing
+ uint32_t flushMaxUs_;
+ static const uint32_t DEFAULT_FLUSH_MAX_US = 3000000;
+
+ // max number of bytes that can be written without flushing
+ uint32_t flushMaxBytes_;
+ static const uint32_t DEFAULT_FLUSH_MAX_BYTES = 1000 * 1024;
+
+ // max event size
+ uint32_t maxEventSize_;
+ static const uint32_t DEFAULT_MAX_EVENT_SIZE = 0;
+
+ // max number of corrupted events per chunk
+ uint32_t maxCorruptedEvents_;
+ static const uint32_t DEFAULT_MAX_CORRUPTED_EVENTS = 0;
+
+ // sleep duration when EOF is hit
+ uint32_t eofSleepTime_;
+ static const uint32_t DEFAULT_EOF_SLEEP_TIME_US = 500 * 1000;
+
+ // sleep duration when a corrupted event is encountered
+ uint32_t corruptedEventSleepTime_;
+ static const uint32_t DEFAULT_CORRUPTED_SLEEP_TIME_US = 1 * 1000 * 1000;
+
+ // sleep duration in seconds when an IO error is encountered in the writer thread
+ uint32_t writerThreadIOErrorSleepTime_;
+ static const uint32_t DEFAULT_WRITER_THREAD_SLEEP_TIME_US = 60 * 1000 * 1000;
+
+ // writer thread
+ apache::thrift::concurrency::ThreadFactory threadFactory_;
+ std::shared_ptr<apache::thrift::concurrency::Thread> writerThread_;
+
+ // buffers to hold data before it is flushed. Each element of the buffer stores a msg that
+ // needs to be written to the file. The buffers are swapped by the writer thread.
+ TFileTransportBuffer* dequeueBuffer_;
+ TFileTransportBuffer* enqueueBuffer_;
+
+ // conditions used to block when the buffer is full or empty
+ Monitor notFull_, notEmpty_;
+ std::atomic<bool> closing_;
+
+ // To keep track of whether the buffer has been flushed
+ Monitor flushed_;
+ std::atomic<bool> forceFlush_;
+
+ // Mutex that is grabbed when enqueueing and swapping the read/write buffers
+ Mutex mutex_;
+
+ // File information
+ std::string filename_;
+ int fd_;
+
+ // Whether the writer thread and buffers have been initialized
+ bool bufferAndThreadInitialized_;
+
+ // Offset within the file
+ off_t offset_;
+
+ // event corruption information
+ uint32_t lastBadChunk_;
+ uint32_t numCorruptedEventsInChunk_;
+
+ bool readOnly_;
+};
+
+// Exception thrown when EOF is hit
+class TEOFException : public TTransportException {
+public:
+ TEOFException() : TTransportException(TTransportException::END_OF_FILE){};
+};
+
+// wrapper class to process events from a file containing thrift events
+class TFileProcessor {
+public:
+ /**
+ * Constructor that defaults output transport to null transport
+ *
+ * @param processor processes log-file events
+ * @param protocolFactory protocol factory
+ * @param inputTransport file transport
+ */
+ TFileProcessor(std::shared_ptr<TProcessor> processor,
+ std::shared_ptr<TProtocolFactory> protocolFactory,
+ std::shared_ptr<TFileReaderTransport> inputTransport);
+
+ TFileProcessor(std::shared_ptr<TProcessor> processor,
+ std::shared_ptr<TProtocolFactory> inputProtocolFactory,
+ std::shared_ptr<TProtocolFactory> outputProtocolFactory,
+ std::shared_ptr<TFileReaderTransport> inputTransport);
+
+ /**
+ * Constructor
+ *
+ * @param processor processes log-file events
+ * @param protocolFactory protocol factory
+ * @param inputTransport input file transport
+ * @param output output transport
+ */
+ TFileProcessor(std::shared_ptr<TProcessor> processor,
+ std::shared_ptr<TProtocolFactory> protocolFactory,
+ std::shared_ptr<TFileReaderTransport> inputTransport,
+ std::shared_ptr<TTransport> outputTransport);
+
+ /**
+ * processes events from the file
+ *
+ * @param numEvents number of events to process (0 for unlimited)
+ * @param tail tails the file if true
+ */
+ void process(uint32_t numEvents, bool tail);
+
+ /**
+ * process events until the end of the chunk
+ *
+ */
+ void processChunk();
+
+private:
+ std::shared_ptr<TProcessor> processor_;
+ std::shared_ptr<TProtocolFactory> inputProtocolFactory_;
+ std::shared_ptr<TProtocolFactory> outputProtocolFactory_;
+ std::shared_ptr<TFileReaderTransport> inputTransport_;
+ std::shared_ptr<TTransport> outputTransport_;
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // _THRIFT_TRANSPORT_TFILETRANSPORT_H_
+
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THeaderTransport.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THeaderTransport.cpp
new file mode 100644
index 000000000..b582d8da7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THeaderTransport.cpp
@@ -0,0 +1,611 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/transport/THeaderTransport.h>
+#include <thrift/TApplicationException.h>
+#include <thrift/protocol/TProtocolTypes.h>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/protocol/TCompactProtocol.h>
+
+#include <limits>
+#include <utility>
+#include <string>
+#include <string.h>
+#include <zlib.h>
+
+using std::map;
+using std::string;
+using std::vector;
+
+namespace apache {
+namespace thrift {
+
+using std::shared_ptr;
+
+namespace transport {
+
+using namespace apache::thrift::protocol;
+using apache::thrift::protocol::TBinaryProtocol;
+
+uint32_t THeaderTransport::readSlow(uint8_t* buf, uint32_t len) {
+ if (clientType == THRIFT_UNFRAMED_BINARY || clientType == THRIFT_UNFRAMED_COMPACT) {
+ return transport_->read(buf, len);
+ }
+
+ return TFramedTransport::readSlow(buf, len);
+}
+
+uint16_t THeaderTransport::getProtocolId() const {
+ if (clientType == THRIFT_HEADER_CLIENT_TYPE) {
+ return protoId;
+ } else if (clientType == THRIFT_UNFRAMED_COMPACT || clientType == THRIFT_FRAMED_COMPACT) {
+ return T_COMPACT_PROTOCOL;
+ } else {
+ return T_BINARY_PROTOCOL; // Assume other transports use TBinary
+ }
+}
+
+void THeaderTransport::ensureReadBuffer(uint32_t sz) {
+ if (sz > rBufSize_) {
+ rBuf_.reset(new uint8_t[sz]);
+ rBufSize_ = sz;
+ }
+}
+
+bool THeaderTransport::readFrame() {
+ // szN is network byte order of sz
+ uint32_t szN;
+ uint32_t sz;
+
+ // Read the size of the next frame.
+ // We can't use readAll(&sz, sizeof(sz)), since that always throws an
+ // exception on EOF. We want to throw an exception only if EOF occurs after
+ // partial size data.
+ uint32_t sizeBytesRead = 0;
+ while (sizeBytesRead < sizeof(szN)) {
+ uint8_t* szp = reinterpret_cast<uint8_t*>(&szN) + sizeBytesRead;
+ uint32_t bytesRead = transport_->read(szp, sizeof(szN) - sizeBytesRead);
+ if (bytesRead == 0) {
+ if (sizeBytesRead == 0) {
+ // EOF before any data was read.
+ return false;
+ } else {
+ // EOF after a partial frame header. Raise an exception.
+ throw TTransportException(TTransportException::END_OF_FILE,
+ "No more data to read after "
+ "partial frame header.");
+ }
+ }
+ sizeBytesRead += bytesRead;
+ }
+
+ sz = ntohl(szN);
+
+ ensureReadBuffer(4);
+
+ if ((sz & TBinaryProtocol::VERSION_MASK) == (uint32_t)TBinaryProtocol::VERSION_1) {
+ // unframed
+ clientType = THRIFT_UNFRAMED_BINARY;
+ memcpy(rBuf_.get(), &szN, sizeof(szN));
+ setReadBuffer(rBuf_.get(), 4);
+ } else if (static_cast<int8_t>(sz >> 24) == TCompactProtocol::PROTOCOL_ID
+ && (static_cast<int8_t>(sz >> 16) & TCompactProtocol::VERSION_MASK)
+ == TCompactProtocol::VERSION_N) {
+ clientType = THRIFT_UNFRAMED_COMPACT;
+ memcpy(rBuf_.get(), &szN, sizeof(szN));
+ setReadBuffer(rBuf_.get(), 4);
+ } else {
+ // Could be header format or framed. Check next uint32
+ uint32_t magic_n;
+ uint32_t magic;
+
+ if (sz > MAX_FRAME_SIZE) {
+ throw TTransportException(TTransportException::CORRUPTED_DATA,
+ "Header transport frame is too large");
+ }
+
+ ensureReadBuffer(sz);
+
+ // We can use readAll here, because it would be an invalid frame otherwise
+ transport_->readAll(reinterpret_cast<uint8_t*>(&magic_n), sizeof(magic_n));
+ memcpy(rBuf_.get(), &magic_n, sizeof(magic_n));
+ magic = ntohl(magic_n);
+
+ if ((magic & TBinaryProtocol::VERSION_MASK) == (uint32_t)TBinaryProtocol::VERSION_1) {
+ // framed
+ clientType = THRIFT_FRAMED_BINARY;
+ transport_->readAll(rBuf_.get() + 4, sz - 4);
+ setReadBuffer(rBuf_.get(), sz);
+ } else if (static_cast<int8_t>(magic >> 24) == TCompactProtocol::PROTOCOL_ID
+ && (static_cast<int8_t>(magic >> 16) & TCompactProtocol::VERSION_MASK)
+ == TCompactProtocol::VERSION_N) {
+ clientType = THRIFT_FRAMED_COMPACT;
+ transport_->readAll(rBuf_.get() + 4, sz - 4);
+ setReadBuffer(rBuf_.get(), sz);
+ } else if (HEADER_MAGIC == (magic & HEADER_MASK)) {
+ if (sz < 10) {
+ throw TTransportException(TTransportException::CORRUPTED_DATA,
+ "Header transport frame is too small");
+ }
+
+ transport_->readAll(rBuf_.get() + 4, sz - 4);
+
+ // header format
+ clientType = THRIFT_HEADER_CLIENT_TYPE;
+ // flags
+ flags = magic & FLAGS_MASK;
+ // seqId
+ uint32_t seqId_n;
+ memcpy(&seqId_n, rBuf_.get() + 4, sizeof(seqId_n));
+ seqId = ntohl(seqId_n);
+ // header size
+ uint16_t headerSize_n;
+ memcpy(&headerSize_n, rBuf_.get() + 8, sizeof(headerSize_n));
+ uint16_t headerSize = ntohs(headerSize_n);
+ setReadBuffer(rBuf_.get(), sz);
+ readHeaderFormat(headerSize, sz);
+ } else {
+ clientType = THRIFT_UNKNOWN_CLIENT_TYPE;
+ throw TTransportException(TTransportException::BAD_ARGS,
+ "Could not detect client transport type");
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Reads a string from ptr, taking care not to reach headerBoundary
+ * Advances ptr on success
+ *
+ * @param str output string
+ * @throws CORRUPTED_DATA if size of string exceeds boundary
+ */
+void THeaderTransport::readString(uint8_t*& ptr,
+ /* out */ string& str,
+ uint8_t const* headerBoundary) {
+ int32_t strLen;
+
+ uint32_t bytes = readVarint32(ptr, &strLen, headerBoundary);
+ if (strLen > headerBoundary - ptr) {
+ throw TTransportException(TTransportException::CORRUPTED_DATA,
+ "Info header length exceeds header size");
+ }
+ ptr += bytes;
+ str.assign(reinterpret_cast<const char*>(ptr), strLen);
+ ptr += strLen;
+}
+
+void THeaderTransport::readHeaderFormat(uint16_t headerSize, uint32_t sz) {
+ readTrans_.clear(); // Clear out any previous transforms.
+ readHeaders_.clear(); // Clear out any previous headers.
+
+ // skip over already processed magic(4), seqId(4), headerSize(2)
+ auto* ptr = reinterpret_cast<uint8_t*>(rBuf_.get() + 10);
+
+ // Catch integer overflow, check for reasonable header size
+ if (headerSize >= 16384) {
+ throw TTransportException(TTransportException::CORRUPTED_DATA,
+ "Header size is unreasonable");
+ }
+ headerSize *= 4;
+ const uint8_t* const headerBoundary = ptr + headerSize;
+ if (headerSize > sz) {
+ throw TTransportException(TTransportException::CORRUPTED_DATA,
+ "Header size is larger than frame");
+ }
+ uint8_t* data = ptr + headerSize;
+ ptr += readVarint16(ptr, &protoId, headerBoundary);
+ int16_t numTransforms;
+ ptr += readVarint16(ptr, &numTransforms, headerBoundary);
+
+ // For now all transforms consist of only the ID, not data.
+ for (int i = 0; i < numTransforms; i++) {
+ int32_t transId;
+ ptr += readVarint32(ptr, &transId, headerBoundary);
+
+ readTrans_.push_back(transId);
+ }
+
+ // Info headers
+ while (ptr < headerBoundary) {
+ int32_t infoId;
+ ptr += readVarint32(ptr, &infoId, headerBoundary);
+
+ if (infoId == 0) {
+ // header padding
+ break;
+ }
+ if (infoId >= infoIdType::END) {
+ // cannot handle infoId
+ break;
+ }
+ switch (infoId) {
+ case infoIdType::KEYVALUE:
+ // Process key-value headers
+ uint32_t numKVHeaders;
+ ptr += readVarint32(ptr, (int32_t*)&numKVHeaders, headerBoundary);
+ // continue until we reach (padded) end of packet
+ while (numKVHeaders-- && ptr < headerBoundary) {
+ // format: key; value
+ // both: length (varint32); value (string)
+ string key, value;
+ readString(ptr, key, headerBoundary);
+ // value
+ readString(ptr, value, headerBoundary);
+ // save to headers
+ readHeaders_[key] = value;
+ }
+ break;
+ }
+ }
+
+ // Untransform the data section. rBuf will contain result.
+ untransform(data, safe_numeric_cast<uint32_t>(static_cast<ptrdiff_t>(sz) - (data - rBuf_.get())));
+}
+
+void THeaderTransport::untransform(uint8_t* ptr, uint32_t sz) {
+ // Update the transform buffer size if needed
+ resizeTransformBuffer();
+
+ for (vector<uint16_t>::const_iterator it = readTrans_.begin(); it != readTrans_.end(); ++it) {
+ const uint16_t transId = *it;
+
+ if (transId == ZLIB_TRANSFORM) {
+ z_stream stream;
+ int err;
+
+ stream.next_in = ptr;
+ stream.avail_in = sz;
+
+ // Setting these to 0 means use the default free/alloc functions
+ stream.zalloc = (alloc_func)nullptr;
+ stream.zfree = (free_func)nullptr;
+ stream.opaque = (voidpf)nullptr;
+ err = inflateInit(&stream);
+ if (err != Z_OK) {
+ throw TApplicationException(TApplicationException::MISSING_RESULT,
+ "Error while zlib deflateInit");
+ }
+ stream.next_out = tBuf_.get();
+ stream.avail_out = tBufSize_;
+ err = inflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END || stream.avail_out == 0) {
+ throw TApplicationException(TApplicationException::MISSING_RESULT,
+ "Error while zlib deflate");
+ }
+ sz = stream.total_out;
+
+ err = inflateEnd(&stream);
+ if (err != Z_OK) {
+ throw TApplicationException(TApplicationException::MISSING_RESULT,
+ "Error while zlib deflateEnd");
+ }
+
+ memcpy(ptr, tBuf_.get(), sz);
+ } else {
+ throw TApplicationException(TApplicationException::MISSING_RESULT, "Unknown transform");
+ }
+ }
+
+ setReadBuffer(ptr, sz);
+}
+
+/**
+ * We may have updated the wBuf size, update the tBuf size to match.
+ * Should be called in transform.
+ *
+ * The buffer should be slightly larger than write buffer size due to
+ * compression transforms (that may slightly grow on small frame sizes)
+ */
+void THeaderTransport::resizeTransformBuffer(uint32_t additionalSize) {
+ if (tBufSize_ < wBufSize_ + DEFAULT_BUFFER_SIZE) {
+ uint32_t new_size = wBufSize_ + DEFAULT_BUFFER_SIZE + additionalSize;
+ auto* new_buf = new uint8_t[new_size];
+ tBuf_.reset(new_buf);
+ tBufSize_ = new_size;
+ }
+}
+
+void THeaderTransport::transform(uint8_t* ptr, uint32_t sz) {
+ // Update the transform buffer size if needed
+ resizeTransformBuffer();
+
+ for (vector<uint16_t>::const_iterator it = writeTrans_.begin(); it != writeTrans_.end(); ++it) {
+ const uint16_t transId = *it;
+
+ if (transId == ZLIB_TRANSFORM) {
+ z_stream stream;
+ int err;
+
+ stream.next_in = ptr;
+ stream.avail_in = sz;
+
+ stream.zalloc = (alloc_func)nullptr;
+ stream.zfree = (free_func)nullptr;
+ stream.opaque = (voidpf)nullptr;
+ err = deflateInit(&stream, Z_DEFAULT_COMPRESSION);
+ if (err != Z_OK) {
+ throw TTransportException(TTransportException::CORRUPTED_DATA,
+ "Error while zlib deflateInit");
+ }
+ uint32_t tbuf_size = 0;
+ while (err == Z_OK) {
+ resizeTransformBuffer(tbuf_size);
+
+ stream.next_out = tBuf_.get();
+ stream.avail_out = tBufSize_;
+ err = deflate(&stream, Z_FINISH);
+ tbuf_size += DEFAULT_BUFFER_SIZE;
+ }
+ sz = stream.total_out;
+
+ err = deflateEnd(&stream);
+ if (err != Z_OK) {
+ throw TTransportException(TTransportException::CORRUPTED_DATA,
+ "Error while zlib deflateEnd");
+ }
+
+ memcpy(ptr, tBuf_.get(), sz);
+ } else {
+ throw TTransportException(TTransportException::CORRUPTED_DATA, "Unknown transform");
+ }
+ }
+
+ wBase_ = wBuf_.get() + sz;
+}
+
+void THeaderTransport::resetProtocol() {
+ // Set to anything except HTTP type so we don't flush again
+ clientType = THRIFT_HEADER_CLIENT_TYPE;
+
+ // Read the header and decide which protocol to go with
+ readFrame();
+}
+
+uint32_t THeaderTransport::getWriteBytes() {
+ return safe_numeric_cast<uint32_t>(wBase_ - wBuf_.get());
+}
+
+/**
+ * Writes a string to a byte buffer, as size (varint32) + string (non-null
+ * terminated)
+ * Automatically advances ptr to after the written portion
+ */
+void THeaderTransport::writeString(uint8_t*& ptr, const string& str) {
+ auto strLen = safe_numeric_cast<int32_t>(str.length());
+ ptr += writeVarint32(strLen, ptr);
+ memcpy(ptr, str.c_str(), strLen); // no need to write \0
+ ptr += strLen;
+}
+
+void THeaderTransport::setHeader(const string& key, const string& value) {
+ writeHeaders_[key] = value;
+}
+
+uint32_t THeaderTransport::getMaxWriteHeadersSize() const {
+ size_t maxWriteHeadersSize = 0;
+ THeaderTransport::StringToStringMap::const_iterator it;
+ for (it = writeHeaders_.begin(); it != writeHeaders_.end(); ++it) {
+ // add sizes of key and value to maxWriteHeadersSize
+ // 2 varints32 + the strings themselves
+ maxWriteHeadersSize += 5 + 5 + (it->first).length() + (it->second).length();
+ }
+ return safe_numeric_cast<uint32_t>(maxWriteHeadersSize);
+}
+
+void THeaderTransport::clearHeaders() {
+ writeHeaders_.clear();
+}
+
+void THeaderTransport::flush() {
+ // Write out any data waiting in the write buffer.
+ uint32_t haveBytes = getWriteBytes();
+
+ if (clientType == THRIFT_HEADER_CLIENT_TYPE) {
+ transform(wBuf_.get(), haveBytes);
+ haveBytes = getWriteBytes(); // transform may have changed the size
+ }
+
+ // Note that we reset wBase_ prior to the underlying write
+ // to ensure we're in a sane state (i.e. internal buffer cleaned)
+ // if the underlying write throws up an exception
+ wBase_ = wBuf_.get();
+
+ if (haveBytes > MAX_FRAME_SIZE) {
+ throw TTransportException(TTransportException::CORRUPTED_DATA,
+ "Attempting to send frame that is too large");
+ }
+
+ if (clientType == THRIFT_HEADER_CLIENT_TYPE) {
+ // header size will need to be updated at the end because of varints.
+ // Make it big enough here for max varint size, plus 4 for padding.
+ uint32_t headerSize = (2 + getNumTransforms()) * THRIFT_MAX_VARINT32_BYTES + 4;
+ // add approximate size of info headers
+ headerSize += getMaxWriteHeadersSize();
+
+ // Pkt size
+ uint32_t maxSzHbo = headerSize + haveBytes // thrift header + payload
+ + 10; // common header section
+ uint8_t* pkt = tBuf_.get();
+ uint8_t* headerStart;
+ uint8_t* headerSizePtr;
+ uint8_t* pktStart = pkt;
+
+ if (maxSzHbo > tBufSize_) {
+ throw TTransportException(TTransportException::CORRUPTED_DATA,
+ "Attempting to header frame that is too large");
+ }
+
+ uint32_t szHbo;
+ uint32_t szNbo;
+ uint16_t headerSizeN;
+
+ // Fixup szHbo later
+ pkt += sizeof(szNbo);
+ uint16_t headerN = htons(HEADER_MAGIC >> 16);
+ memcpy(pkt, &headerN, sizeof(headerN));
+ pkt += sizeof(headerN);
+ uint16_t flagsN = htons(flags);
+ memcpy(pkt, &flagsN, sizeof(flagsN));
+ pkt += sizeof(flagsN);
+ uint32_t seqIdN = htonl(seqId);
+ memcpy(pkt, &seqIdN, sizeof(seqIdN));
+ pkt += sizeof(seqIdN);
+ headerSizePtr = pkt;
+ // Fixup headerSizeN later
+ pkt += sizeof(headerSizeN);
+ headerStart = pkt;
+
+ pkt += writeVarint32(protoId, pkt);
+ pkt += writeVarint32(getNumTransforms(), pkt);
+
+ // For now, each transform is only the ID, no following data.
+ for (vector<uint16_t>::const_iterator it = writeTrans_.begin(); it != writeTrans_.end(); ++it) {
+ pkt += writeVarint32(*it, pkt);
+ }
+
+ // write info headers
+
+ // for now only write kv-headers
+ auto headerCount = safe_numeric_cast<int32_t>(writeHeaders_.size());
+ if (headerCount > 0) {
+ pkt += writeVarint32(infoIdType::KEYVALUE, pkt);
+ // Write key-value headers count
+ pkt += writeVarint32(static_cast<int32_t>(headerCount), pkt);
+ // Write info headers
+ map<string, string>::const_iterator it;
+ for (it = writeHeaders_.begin(); it != writeHeaders_.end(); ++it) {
+ writeString(pkt, it->first); // key
+ writeString(pkt, it->second); // value
+ }
+ writeHeaders_.clear();
+ }
+
+ // Fixups after varint size calculations
+ headerSize = safe_numeric_cast<uint32_t>(pkt - headerStart);
+ uint8_t padding = 4 - (headerSize % 4);
+ headerSize += padding;
+
+ // Pad out pkt with 0x00
+ for (int i = 0; i < padding; i++) {
+ *(pkt++) = 0x00;
+ }
+
+ // Pkt size
+ ptrdiff_t szHbp = (headerStart - pktStart - 4);
+ if (static_cast<uint64_t>(szHbp) > static_cast<uint64_t>((std::numeric_limits<uint32_t>().max)()) - (headerSize + haveBytes)) {
+ throw TTransportException(TTransportException::CORRUPTED_DATA,
+ "Header section size is unreasonable");
+ }
+ szHbo = headerSize + haveBytes // thrift header + payload
+ + static_cast<uint32_t>(szHbp); // common header section
+ headerSizeN = htons(headerSize / 4);
+ memcpy(headerSizePtr, &headerSizeN, sizeof(headerSizeN));
+
+ // Set framing size.
+ szNbo = htonl(szHbo);
+ memcpy(pktStart, &szNbo, sizeof(szNbo));
+
+ outTransport_->write(pktStart, szHbo - haveBytes + 4);
+ outTransport_->write(wBuf_.get(), haveBytes);
+ } else if (clientType == THRIFT_FRAMED_BINARY || clientType == THRIFT_FRAMED_COMPACT) {
+ auto szHbo = (uint32_t)haveBytes;
+ uint32_t szNbo = htonl(szHbo);
+
+ outTransport_->write(reinterpret_cast<uint8_t*>(&szNbo), 4);
+ outTransport_->write(wBuf_.get(), haveBytes);
+ } else if (clientType == THRIFT_UNFRAMED_BINARY || clientType == THRIFT_UNFRAMED_COMPACT) {
+ outTransport_->write(wBuf_.get(), haveBytes);
+ } else {
+ throw TTransportException(TTransportException::BAD_ARGS, "Unknown client type");
+ }
+
+ // Flush the underlying transport.
+ outTransport_->flush();
+}
+
+/**
+ * Read an i16 from the wire as a varint. The MSB of each byte is set
+ * if there is another byte to follow. This can read up to 3 bytes.
+ */
+uint32_t THeaderTransport::readVarint16(uint8_t const* ptr, int16_t* i16, uint8_t const* boundary) {
+ int32_t val;
+ uint32_t rsize = readVarint32(ptr, &val, boundary);
+ *i16 = (int16_t)val;
+ return rsize;
+}
+
+/**
+ * Read an i32 from the wire as a varint. The MSB of each byte is set
+ * if there is another byte to follow. This can read up to 5 bytes.
+ */
+uint32_t THeaderTransport::readVarint32(uint8_t const* ptr, int32_t* i32, uint8_t const* boundary) {
+
+ uint32_t rsize = 0;
+ uint32_t val = 0;
+ int shift = 0;
+
+ while (true) {
+ if (ptr == boundary) {
+ throw TApplicationException(TApplicationException::INVALID_MESSAGE_TYPE,
+ "Trying to read past header boundary");
+ }
+ uint8_t byte = *(ptr++);
+ rsize++;
+ val |= (uint64_t)(byte & 0x7f) << shift;
+ shift += 7;
+ if (!(byte & 0x80)) {
+ *i32 = val;
+ return rsize;
+ }
+ }
+}
+
+/**
+ * Write an i32 as a varint. Results in 1-5 bytes on the wire.
+ */
+uint32_t THeaderTransport::writeVarint32(int32_t n, uint8_t* pkt) {
+ uint8_t buf[5];
+ uint32_t wsize = 0;
+
+ while (true) {
+ if ((n & ~0x7F) == 0) {
+ buf[wsize++] = (int8_t)n;
+ break;
+ } else {
+ buf[wsize++] = (int8_t)((n & 0x7F) | 0x80);
+ n >>= 7;
+ }
+ }
+
+ // Caller will advance pkt.
+ for (uint32_t i = 0; i < wsize; i++) {
+ pkt[i] = buf[i];
+ }
+
+ return wsize;
+}
+
+uint32_t THeaderTransport::writeVarint16(int16_t n, uint8_t* pkt) {
+ return writeVarint32(n, pkt);
+}
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THeaderTransport.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THeaderTransport.h
new file mode 100644
index 000000000..d1e9d4339
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THeaderTransport.h
@@ -0,0 +1,275 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef THRIFT_TRANSPORT_THEADERTRANSPORT_H_
+#define THRIFT_TRANSPORT_THEADERTRANSPORT_H_ 1
+
+#include <bitset>
+#include <limits>
+#include <vector>
+#include <stdexcept>
+#include <string>
+#include <map>
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#elif HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#include <boost/scoped_array.hpp>
+
+#include <thrift/protocol/TProtocolTypes.h>
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/transport/TTransport.h>
+#include <thrift/transport/TVirtualTransport.h>
+
+enum CLIENT_TYPE {
+ THRIFT_HEADER_CLIENT_TYPE = 0,
+ THRIFT_FRAMED_BINARY = 1,
+ THRIFT_UNFRAMED_BINARY = 2,
+ THRIFT_FRAMED_COMPACT = 3,
+ THRIFT_UNFRAMED_COMPACT = 4,
+ THRIFT_UNKNOWN_CLIENT_TYPE = 5,
+};
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+using apache::thrift::protocol::T_COMPACT_PROTOCOL;
+
+/**
+ * Header transport. All writes go into an in-memory buffer until flush is
+ * called, at which point the transport writes the length of the entire
+ * binary chunk followed by the data payload. This allows the receiver on the
+ * other end to always do fixed-length reads.
+ *
+ * Subclass TFramedTransport because most of the read/write methods are similar
+ * and need similar buffers. Major changes are readFrame & flush.
+ *
+ * Header Transport *must* be the same transport for both input and
+ * output when used on the server side - client responses should be
+ * the same protocol as those in the request.
+ */
+class THeaderTransport : public TVirtualTransport<THeaderTransport, TFramedTransport> {
+public:
+ static const int DEFAULT_BUFFER_SIZE = 512u;
+ static const int THRIFT_MAX_VARINT32_BYTES = 5;
+
+ /// Use default buffer sizes.
+ explicit THeaderTransport(const std::shared_ptr<TTransport>& transport)
+ : TVirtualTransport(transport),
+ outTransport_(transport),
+ protoId(T_COMPACT_PROTOCOL),
+ clientType(THRIFT_HEADER_CLIENT_TYPE),
+ seqId(0),
+ flags(0),
+ tBufSize_(0),
+ tBuf_(nullptr) {
+ if (!transport_) throw std::invalid_argument("transport is empty");
+ initBuffers();
+ }
+
+ THeaderTransport(const std::shared_ptr<TTransport> inTransport,
+ const std::shared_ptr<TTransport> outTransport)
+ : TVirtualTransport(inTransport),
+ outTransport_(outTransport),
+ protoId(T_COMPACT_PROTOCOL),
+ clientType(THRIFT_HEADER_CLIENT_TYPE),
+ seqId(0),
+ flags(0),
+ tBufSize_(0),
+ tBuf_(nullptr) {
+ if (!transport_) throw std::invalid_argument("inTransport is empty");
+ if (!outTransport_) throw std::invalid_argument("outTransport is empty");
+ initBuffers();
+ }
+
+ uint32_t readSlow(uint8_t* buf, uint32_t len) override;
+ void flush() override;
+
+ void resizeTransformBuffer(uint32_t additionalSize = 0);
+
+ uint16_t getProtocolId() const;
+ void setProtocolId(uint16_t protoId) { this->protoId = protoId; }
+
+ void resetProtocol();
+
+ /**
+ * We know we got a packet in header format here, try to parse the header
+ *
+ * @param headerSize size of the header portion
+ * @param sz Size of the whole message, including header
+ */
+ void readHeaderFormat(uint16_t headerSize, uint32_t sz);
+
+ /**
+ * Untransform the data based on the received header flags
+ * On conclusion of function, setReadBuffer is called with the
+ * untransformed data.
+ *
+ * @param ptr ptr to data
+ * @param size of data
+ */
+ void untransform(uint8_t* ptr, uint32_t sz);
+
+ /**
+ * Transform the data based on our write transform flags
+ * At conclusion of function the write buffer is set to the
+ * transformed data.
+ *
+ * @param ptr Ptr to data to transform
+ * @param sz Size of data buffer
+ */
+ void transform(uint8_t* ptr, uint32_t sz);
+
+ uint16_t getNumTransforms() const {
+ return safe_numeric_cast<uint16_t>(writeTrans_.size());
+ }
+
+ void setTransform(uint16_t transId) { writeTrans_.push_back(transId); }
+
+ // Info headers
+
+ typedef std::map<std::string, std::string> StringToStringMap;
+
+ // these work with write headers
+ void setHeader(const std::string& key, const std::string& value);
+
+ void clearHeaders();
+
+ StringToStringMap& getWriteHeaders() { return writeHeaders_; }
+
+ // these work with read headers
+ const StringToStringMap& getHeaders() const { return readHeaders_; }
+
+ // accessors for seqId
+ int32_t getSequenceNumber() const { return seqId; }
+ void setSequenceNumber(int32_t seqId) { this->seqId = seqId; }
+
+ enum TRANSFORMS {
+ ZLIB_TRANSFORM = 0x01,
+ };
+
+protected:
+ /**
+ * Reads a frame of input from the underlying stream.
+ *
+ * Returns true if a frame was read successfully, or false on EOF.
+ * (Raises a TTransportException if EOF occurs after a partial frame.)
+ */
+ bool readFrame() override;
+
+ void ensureReadBuffer(uint32_t sz);
+ uint32_t getWriteBytes();
+
+ void initBuffers() {
+ setReadBuffer(nullptr, 0);
+ setWriteBuffer(wBuf_.get(), wBufSize_);
+ }
+
+ std::shared_ptr<TTransport> outTransport_;
+
+ // 0 and 16th bits must be 0 to differentiate from framed & unframed
+ static const uint32_t HEADER_MAGIC = 0x0FFF0000;
+ static const uint32_t HEADER_MASK = 0xFFFF0000;
+ static const uint32_t FLAGS_MASK = 0x0000FFFF;
+
+ static const uint32_t MAX_FRAME_SIZE = 0x3FFFFFFF;
+
+ int16_t protoId;
+ uint16_t clientType;
+ uint32_t seqId;
+ uint16_t flags;
+
+ std::vector<uint16_t> readTrans_;
+ std::vector<uint16_t> writeTrans_;
+
+ // Map to use for headers
+ StringToStringMap readHeaders_;
+ StringToStringMap writeHeaders_;
+
+ /**
+ * Returns the maximum number of bytes that write k/v headers can take
+ */
+ uint32_t getMaxWriteHeadersSize() const;
+
+ struct infoIdType {
+ enum idType {
+ // start at 1 to avoid confusing header padding for an infoId
+ KEYVALUE = 1,
+ END // signal the end of infoIds we can handle
+ };
+ };
+
+ // Buffers to use for transform processing
+ uint32_t tBufSize_;
+ boost::scoped_array<uint8_t> tBuf_;
+
+ void readString(uint8_t*& ptr, /* out */ std::string& str, uint8_t const* headerBoundary);
+
+ void writeString(uint8_t*& ptr, const std::string& str);
+
+ // Varint utils
+ /**
+ * Read an i16 from the wire as a varint. The MSB of each byte is set
+ * if there is another byte to follow. This can read up to 3 bytes.
+ */
+ uint32_t readVarint16(uint8_t const* ptr, int16_t* i16, uint8_t const* boundary);
+
+ /**
+ * Read an i32 from the wire as a varint. The MSB of each byte is set
+ * if there is another byte to follow. This can read up to 5 bytes.
+ */
+ uint32_t readVarint32(uint8_t const* ptr, int32_t* i32, uint8_t const* boundary);
+
+ /**
+ * Write an i32 as a varint. Results in 1-5 bytes on the wire.
+ */
+ uint32_t writeVarint32(int32_t n, uint8_t* pkt);
+
+ /**
+ * Write an i16 as a varint. Results in 1-3 bytes on the wire.
+ */
+ uint32_t writeVarint16(int16_t n, uint8_t* pkt);
+};
+
+/**
+ * Wraps a transport into a header one.
+ *
+ */
+class THeaderTransportFactory : public TTransportFactory {
+public:
+ THeaderTransportFactory() = default;
+
+ ~THeaderTransportFactory() override = default;
+
+ /**
+ * Wraps the transport into a header one.
+ */
+ std::shared_ptr<TTransport> getTransport(std::shared_ptr<TTransport> trans) override {
+ return std::shared_ptr<TTransport>(new THeaderTransport(trans));
+ }
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef THRIFT_TRANSPORT_THEADERTRANSPORT_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpClient.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpClient.cpp
new file mode 100644
index 000000000..04566c991
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpClient.cpp
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <limits>
+#include <cstdlib>
+#include <sstream>
+#include <boost/algorithm/string.hpp>
+
+#include <thrift/config.h>
+#include <thrift/transport/THttpClient.h>
+#include <thrift/transport/TSocket.h>
+
+using std::string;
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+THttpClient::THttpClient(std::shared_ptr<TTransport> transport,
+ std::string host,
+ std::string path)
+ : THttpTransport(transport), host_(host), path_(path) {
+}
+
+THttpClient::THttpClient(string host, int port, string path)
+ : THttpTransport(std::shared_ptr<TTransport>(new TSocket(host, port))),
+ host_(host),
+ path_(path) {
+}
+
+THttpClient::~THttpClient() = default;
+
+void THttpClient::parseHeader(char* header) {
+ char* colon = strchr(header, ':');
+ if (colon == nullptr) {
+ return;
+ }
+ char* value = colon + 1;
+
+ if (boost::istarts_with(header, "Transfer-Encoding")) {
+ if (boost::iends_with(value, "chunked")) {
+ chunked_ = true;
+ }
+ } else if (boost::istarts_with(header, "Content-Length")) {
+ chunked_ = false;
+ contentLength_ = atoi(value);
+ }
+}
+
+bool THttpClient::parseStatusLine(char* status) {
+ char* http = status;
+
+ char* code = strchr(http, ' ');
+ if (code == nullptr) {
+ throw TTransportException(string("Bad Status: ") + status);
+ }
+
+ *code = '\0';
+ while (*(code++) == ' ') {
+ };
+
+ char* msg = strchr(code, ' ');
+ if (msg == nullptr) {
+ throw TTransportException(string("Bad Status: ") + status);
+ }
+ *msg = '\0';
+
+ if (strcmp(code, "200") == 0) {
+ // HTTP 200 = OK, we got the response
+ return true;
+ } else if (strcmp(code, "100") == 0) {
+ // HTTP 100 = continue, just keep reading
+ return false;
+ } else {
+ throw TTransportException(string("Bad Status: ") + status);
+ }
+}
+
+void THttpClient::flush() {
+ // Fetch the contents of the write buffer
+ uint8_t* buf;
+ uint32_t len;
+ writeBuffer_.getBuffer(&buf, &len);
+
+ // Construct the HTTP header
+ std::ostringstream h;
+ h << "POST " << path_ << " HTTP/1.1" << CRLF << "Host: " << host_ << CRLF
+ << "Content-Type: application/x-thrift" << CRLF << "Content-Length: " << len << CRLF
+ << "Accept: application/x-thrift" << CRLF << "User-Agent: Thrift/" << PACKAGE_VERSION
+ << " (C++/THttpClient)" << CRLF << CRLF;
+ string header = h.str();
+
+ if (header.size() > (std::numeric_limits<uint32_t>::max)())
+ throw TTransportException("Header too big");
+ // Write the header, then the data, then flush
+ transport_->write((const uint8_t*)header.c_str(), static_cast<uint32_t>(header.size()));
+ transport_->write(buf, len);
+ transport_->flush();
+
+ // Reset the buffer and header variables
+ writeBuffer_.resetBuffer();
+ readHeaders_ = true;
+}
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpClient.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpClient.h
new file mode 100644
index 000000000..fdca505c8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpClient.h
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_THTTPCLIENT_H_
+#define _THRIFT_TRANSPORT_THTTPCLIENT_H_ 1
+
+#include <thrift/transport/THttpTransport.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+class THttpClient : public THttpTransport {
+public:
+ THttpClient(std::shared_ptr<TTransport> transport, std::string host, std::string path = "");
+
+ THttpClient(std::string host, int port, std::string path = "");
+
+ ~THttpClient() override;
+
+ void flush() override;
+
+protected:
+ std::string host_;
+ std::string path_;
+
+ void parseHeader(char* header) override;
+ bool parseStatusLine(char* status) override;
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_THTTPCLIENT_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpServer.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpServer.cpp
new file mode 100644
index 000000000..94ac681e7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpServer.cpp
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <cstdlib>
+#include <sstream>
+#include <iostream>
+
+#include <thrift/config.h>
+#include <thrift/transport/THttpServer.h>
+#include <thrift/transport/TSocket.h>
+#if defined(_MSC_VER) || defined(__MINGW32__)
+ #include <Shlwapi.h>
+#endif
+
+using std::string;
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+THttpServer::THttpServer(std::shared_ptr<TTransport> transport) : THttpTransport(transport) {
+}
+
+THttpServer::~THttpServer() = default;
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+ #define THRIFT_GMTIME(TM, TIME) gmtime_s(&TM, &TIME)
+ #define THRIFT_strncasecmp(str1, str2, len) _strnicmp(str1, str2, len)
+ #define THRIFT_strcasestr(haystack, needle) StrStrIA(haystack, needle)
+#else
+ #define THRIFT_GMTIME(TM, TIME) gmtime_r(&TIME, &TM)
+ #define THRIFT_strncasecmp(str1, str2, len) strncasecmp(str1, str2, len)
+ #define THRIFT_strcasestr(haystack, needle) strcasestr(haystack, needle)
+#endif
+
+void THttpServer::parseHeader(char* header) {
+ char* colon = strchr(header, ':');
+ if (colon == nullptr) {
+ return;
+ }
+ size_t sz = colon - header;
+ char* value = colon + 1;
+
+ if (THRIFT_strncasecmp(header, "Transfer-Encoding", sz) == 0) {
+ if (THRIFT_strcasestr(value, "chunked") != nullptr) {
+ chunked_ = true;
+ }
+ } else if (THRIFT_strncasecmp(header, "Content-length", sz) == 0) {
+ chunked_ = false;
+ contentLength_ = atoi(value);
+ } else if (strncmp(header, "X-Forwarded-For", sz) == 0) {
+ origin_ = value;
+ }
+}
+
+bool THttpServer::parseStatusLine(char* status) {
+ char* method = status;
+
+ char* path = strchr(method, ' ');
+ if (path == nullptr) {
+ throw TTransportException(string("Bad Status: ") + status);
+ }
+
+ *path = '\0';
+ while (*(++path) == ' ') {
+ };
+
+ char* http = strchr(path, ' ');
+ if (http == nullptr) {
+ throw TTransportException(string("Bad Status: ") + status);
+ }
+ *http = '\0';
+
+ if (strcmp(method, "POST") == 0) {
+ // POST method ok, looking for content.
+ return true;
+ } else if (strcmp(method, "OPTIONS") == 0) {
+ // preflight OPTIONS method, we don't need further content.
+ // how to graciously close connection?
+ uint8_t* buf;
+ uint32_t len;
+ writeBuffer_.getBuffer(&buf, &len);
+
+ // Construct the HTTP header
+ std::ostringstream h;
+ h << "HTTP/1.1 200 OK" << CRLF << "Date: " << getTimeRFC1123() << CRLF
+ << "Access-Control-Allow-Origin: *" << CRLF << "Access-Control-Allow-Methods: POST, OPTIONS"
+ << CRLF << "Access-Control-Allow-Headers: Content-Type" << CRLF << CRLF;
+ string header = h.str();
+
+ // Write the header, then the data, then flush
+ transport_->write((const uint8_t*)header.c_str(), static_cast<uint32_t>(header.size()));
+ transport_->write(buf, len);
+ transport_->flush();
+
+ // Reset the buffer and header variables
+ writeBuffer_.resetBuffer();
+ readHeaders_ = true;
+ return true;
+ }
+ throw TTransportException(string("Bad Status (unsupported method): ") + status);
+}
+
+void THttpServer::flush() {
+ // Fetch the contents of the write buffer
+ uint8_t* buf;
+ uint32_t len;
+ writeBuffer_.getBuffer(&buf, &len);
+
+ // Construct the HTTP header
+ std::ostringstream h;
+ h << "HTTP/1.1 200 OK" << CRLF << "Date: " << getTimeRFC1123() << CRLF << "Server: Thrift/"
+ << PACKAGE_VERSION << CRLF << "Access-Control-Allow-Origin: *" << CRLF
+ << "Content-Type: application/x-thrift" << CRLF << "Content-Length: " << len << CRLF
+ << "Connection: Keep-Alive" << CRLF << CRLF;
+ string header = h.str();
+
+ // Write the header, then the data, then flush
+ // cast should be fine, because none of "header" is under attacker control
+ transport_->write((const uint8_t*)header.c_str(), static_cast<uint32_t>(header.size()));
+ transport_->write(buf, len);
+ transport_->flush();
+
+ // Reset the buffer and header variables
+ writeBuffer_.resetBuffer();
+ readHeaders_ = true;
+}
+
+std::string THttpServer::getTimeRFC1123() {
+ static const char* Days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+ static const char* Months[]
+ = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+ char buff[128];
+
+ time_t t = time(nullptr);
+ struct tm tmb;
+ THRIFT_GMTIME(tmb, t);
+
+ sprintf(buff,
+ "%s, %d %s %d %d:%d:%d GMT",
+ Days[tmb.tm_wday],
+ tmb.tm_mday,
+ Months[tmb.tm_mon],
+ tmb.tm_year + 1900,
+ tmb.tm_hour,
+ tmb.tm_min,
+ tmb.tm_sec);
+ return std::string(buff);
+}
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpServer.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpServer.h
new file mode 100644
index 000000000..0e83399d1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpServer.h
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_THTTPSERVER_H_
+#define _THRIFT_TRANSPORT_THTTPSERVER_H_ 1
+
+#include <thrift/transport/THttpTransport.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+class THttpServer : public THttpTransport {
+public:
+ THttpServer(std::shared_ptr<TTransport> transport);
+
+ ~THttpServer() override;
+
+ void flush() override;
+
+protected:
+ void readHeaders();
+ void parseHeader(char* header) override;
+ bool parseStatusLine(char* status) override;
+ std::string getTimeRFC1123();
+};
+
+/**
+ * Wraps a transport into HTTP protocol
+ */
+class THttpServerTransportFactory : public TTransportFactory {
+public:
+ THttpServerTransportFactory() = default;
+
+ ~THttpServerTransportFactory() override = default;
+
+ /**
+ * Wraps the transport into a buffered one.
+ */
+ std::shared_ptr<TTransport> getTransport(std::shared_ptr<TTransport> trans) override {
+ return std::shared_ptr<TTransport>(new THttpServer(trans));
+ }
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_THTTPSERVER_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpTransport.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpTransport.cpp
new file mode 100644
index 000000000..aea2b2847
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpTransport.cpp
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <sstream>
+
+#include <thrift/transport/THttpTransport.h>
+
+using std::string;
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+// Yeah, yeah, hacky to put these here, I know.
+const char* THttpTransport::CRLF = "\r\n";
+const int THttpTransport::CRLF_LEN = 2;
+
+THttpTransport::THttpTransport(std::shared_ptr<TTransport> transport)
+ : transport_(transport),
+ origin_(""),
+ readHeaders_(true),
+ chunked_(false),
+ chunkedDone_(false),
+ chunkSize_(0),
+ contentLength_(0),
+ httpBuf_(nullptr),
+ httpPos_(0),
+ httpBufLen_(0),
+ httpBufSize_(1024) {
+ init();
+}
+
+void THttpTransport::init() {
+ httpBuf_ = (char*)std::malloc(httpBufSize_ + 1);
+ if (httpBuf_ == nullptr) {
+ throw std::bad_alloc();
+ }
+ httpBuf_[httpBufLen_] = '\0';
+}
+
+THttpTransport::~THttpTransport() {
+ if (httpBuf_ != nullptr) {
+ std::free(httpBuf_);
+ }
+}
+
+uint32_t THttpTransport::read(uint8_t* buf, uint32_t len) {
+ if (readBuffer_.available_read() == 0) {
+ readBuffer_.resetBuffer();
+ uint32_t got = readMoreData();
+ if (got == 0) {
+ return 0;
+ }
+ }
+ return readBuffer_.read(buf, len);
+}
+
+uint32_t THttpTransport::readEnd() {
+ // Read any pending chunked data (footers etc.)
+ if (chunked_) {
+ while (!chunkedDone_) {
+ readChunked();
+ }
+ }
+ return 0;
+}
+
+uint32_t THttpTransport::readMoreData() {
+ uint32_t size;
+
+ if (httpPos_ == httpBufLen_) {
+ // Get more data!
+ refill();
+ }
+
+ if (readHeaders_) {
+ readHeaders();
+ }
+
+ if (chunked_) {
+ size = readChunked();
+ } else {
+ size = readContent(contentLength_);
+ readHeaders_ = true;
+ }
+
+ return size;
+}
+
+uint32_t THttpTransport::readChunked() {
+ uint32_t length = 0;
+
+ char* line = readLine();
+ uint32_t chunkSize = parseChunkSize(line);
+ if (chunkSize == 0) {
+ readChunkedFooters();
+ } else {
+ // Read data content
+ length += readContent(chunkSize);
+ // Read trailing CRLF after content
+ readLine();
+ }
+ return length;
+}
+
+void THttpTransport::readChunkedFooters() {
+ // End of data, read footer lines until a blank one appears
+ while (true) {
+ char* line = readLine();
+ if (strlen(line) == 0) {
+ chunkedDone_ = true;
+ break;
+ }
+ }
+}
+
+uint32_t THttpTransport::parseChunkSize(char* line) {
+ char* semi = strchr(line, ';');
+ if (semi != nullptr) {
+ *semi = '\0';
+ }
+ uint32_t size = 0;
+ sscanf(line, "%x", &size);
+ return size;
+}
+
+uint32_t THttpTransport::readContent(uint32_t size) {
+ uint32_t need = size;
+ while (need > 0) {
+ uint32_t avail = httpBufLen_ - httpPos_;
+ if (avail == 0) {
+ // We have given all the data, reset position to head of the buffer
+ httpPos_ = 0;
+ httpBufLen_ = 0;
+ refill();
+
+ // Now have available however much we read
+ avail = httpBufLen_;
+ }
+ uint32_t give = avail;
+ if (need < give) {
+ give = need;
+ }
+ readBuffer_.write((uint8_t*)(httpBuf_ + httpPos_), give);
+ httpPos_ += give;
+ need -= give;
+ }
+ return size;
+}
+
+char* THttpTransport::readLine() {
+ while (true) {
+ char* eol = nullptr;
+
+ eol = strstr(httpBuf_ + httpPos_, CRLF);
+
+ // No CRLF yet?
+ if (eol == nullptr) {
+ // Shift whatever we have now to front and refill
+ shift();
+ refill();
+ } else {
+ // Return pointer to next line
+ *eol = '\0';
+ char* line = httpBuf_ + httpPos_;
+ httpPos_ = static_cast<uint32_t>((eol - httpBuf_) + CRLF_LEN);
+ return line;
+ }
+ }
+}
+
+void THttpTransport::shift() {
+ if (httpBufLen_ > httpPos_) {
+ // Shift down remaining data and read more
+ uint32_t length = httpBufLen_ - httpPos_;
+ memmove(httpBuf_, httpBuf_ + httpPos_, length);
+ httpBufLen_ = length;
+ } else {
+ httpBufLen_ = 0;
+ }
+ httpPos_ = 0;
+ httpBuf_[httpBufLen_] = '\0';
+}
+
+void THttpTransport::refill() {
+ uint32_t avail = httpBufSize_ - httpBufLen_;
+ if (avail <= (httpBufSize_ / 4)) {
+ httpBufSize_ *= 2;
+ char* tmpBuf = (char*)std::realloc(httpBuf_, httpBufSize_ + 1);
+ if (tmpBuf == nullptr) {
+ throw std::bad_alloc();
+ }
+ httpBuf_ = tmpBuf;
+ }
+
+ // Read more data
+ uint32_t got = transport_->read((uint8_t*)(httpBuf_ + httpBufLen_), httpBufSize_ - httpBufLen_);
+ httpBufLen_ += got;
+ httpBuf_[httpBufLen_] = '\0';
+
+ if (got == 0) {
+ throw TTransportException(TTransportException::END_OF_FILE, "Could not refill buffer");
+ }
+}
+
+void THttpTransport::readHeaders() {
+ // Initialize headers state variables
+ contentLength_ = 0;
+ chunked_ = false;
+ chunkedDone_ = false;
+ chunkSize_ = 0;
+
+ // Control state flow
+ bool statusLine = true;
+ bool finished = false;
+
+ // Loop until headers are finished
+ while (true) {
+ char* line = readLine();
+
+ if (strlen(line) == 0) {
+ if (finished) {
+ readHeaders_ = false;
+ return;
+ } else {
+ // Must have been an HTTP 100, keep going for another status line
+ statusLine = true;
+ }
+ } else {
+ if (statusLine) {
+ statusLine = false;
+ finished = parseStatusLine(line);
+ } else {
+ parseHeader(line);
+ }
+ }
+ }
+}
+
+void THttpTransport::write(const uint8_t* buf, uint32_t len) {
+ writeBuffer_.write(buf, len);
+}
+
+const std::string THttpTransport::getOrigin() const {
+ std::ostringstream oss;
+ if (!origin_.empty()) {
+ oss << origin_ << ", ";
+ }
+ oss << transport_->getOrigin();
+ return oss.str();
+}
+}
+}
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpTransport.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpTransport.h
new file mode 100644
index 000000000..75f0d8c07
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/THttpTransport.h
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_THTTPTRANSPORT_H_
+#define _THRIFT_TRANSPORT_THTTPTRANSPORT_H_ 1
+
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/transport/TVirtualTransport.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * HTTP implementation of the thrift transport. This was irritating
+ * to write, but the alternatives in C++ land are daunting. Linking CURL
+ * requires 23 dynamic libraries last time I checked (WTF?!?). All we have
+ * here is a VERY basic HTTP/1.1 client which supports HTTP 100 Continue,
+ * chunked transfer encoding, keepalive, etc. Tested against Apache.
+ */
+class THttpTransport : public TVirtualTransport<THttpTransport> {
+public:
+ THttpTransport(std::shared_ptr<TTransport> transport);
+
+ ~THttpTransport() override;
+
+ void open() override { transport_->open(); }
+
+ bool isOpen() const override { return transport_->isOpen(); }
+
+ bool peek() override { return transport_->peek(); }
+
+ void close() override { transport_->close(); }
+
+ uint32_t read(uint8_t* buf, uint32_t len);
+
+ uint32_t readEnd() override;
+
+ void write(const uint8_t* buf, uint32_t len);
+
+ void flush() override = 0;
+
+ const std::string getOrigin() const override;
+
+protected:
+ std::shared_ptr<TTransport> transport_;
+ std::string origin_;
+
+ TMemoryBuffer writeBuffer_;
+ TMemoryBuffer readBuffer_;
+
+ bool readHeaders_;
+ bool chunked_;
+ bool chunkedDone_;
+ uint32_t chunkSize_;
+ uint32_t contentLength_;
+
+ char* httpBuf_;
+ uint32_t httpPos_;
+ uint32_t httpBufLen_;
+ uint32_t httpBufSize_;
+
+ virtual void init();
+
+ uint32_t readMoreData();
+ char* readLine();
+
+ void readHeaders();
+ virtual void parseHeader(char* header) = 0;
+ virtual bool parseStatusLine(char* status) = 0;
+
+ uint32_t readChunked();
+ void readChunkedFooters();
+ uint32_t parseChunkSize(char* line);
+
+ uint32_t readContent(uint32_t size);
+
+ void refill();
+ void shift();
+
+ static const char* CRLF;
+ static const int CRLF_LEN;
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_THTTPCLIENT_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingSSLServerSocket.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingSSLServerSocket.cpp
new file mode 100644
index 000000000..adec5d0f9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingSSLServerSocket.cpp
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/transport/TNonblockingSSLServerSocket.h>
+#include <thrift/transport/TSSLSocket.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * Nonblocking SSL server socket implementation.
+ */
+TNonblockingSSLServerSocket::TNonblockingSSLServerSocket(int port, std::shared_ptr<TSSLSocketFactory> factory)
+ : TNonblockingServerSocket(port), factory_(factory) {
+ factory_->server(true);
+}
+
+TNonblockingSSLServerSocket::TNonblockingSSLServerSocket(const std::string& address,
+ int port,
+ std::shared_ptr<TSSLSocketFactory> factory)
+ : TNonblockingServerSocket(address, port), factory_(factory) {
+ factory_->server(true);
+}
+
+TNonblockingSSLServerSocket::TNonblockingSSLServerSocket(int port,
+ int sendTimeout,
+ int recvTimeout,
+ std::shared_ptr<TSSLSocketFactory> factory)
+ : TNonblockingServerSocket(port, sendTimeout, recvTimeout), factory_(factory) {
+ factory_->server(true);
+}
+
+std::shared_ptr<TSocket> TNonblockingSSLServerSocket::createSocket(THRIFT_SOCKET client) {
+ std::shared_ptr<TSSLSocket> tSSLSocket;
+ tSSLSocket = factory_->createSocket(client);
+ tSSLSocket->setLibeventSafe();
+ return tSSLSocket;
+}
+}
+}
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingSSLServerSocket.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingSSLServerSocket.h
new file mode 100644
index 000000000..a38bf1266
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingSSLServerSocket.h
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TNONBLOCKINGSSLSERVERSOCKET_H_
+#define _THRIFT_TRANSPORT_TNONBLOCKINGSSLSERVERSOCKET_H_ 1
+
+#include <thrift/transport/TNonblockingServerSocket.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+class TSSLSocketFactory;
+
+/**
+ * Nonblocking Server socket that accepts SSL connections.
+ */
+class TNonblockingSSLServerSocket : public TNonblockingServerSocket {
+public:
+ /**
+ * Constructor. Binds to all interfaces.
+ *
+ * @param port Listening port
+ * @param factory SSL socket factory implementation
+ */
+ TNonblockingSSLServerSocket(int port, std::shared_ptr<TSSLSocketFactory> factory);
+
+ /**
+ * Constructor. Binds to the specified address.
+ *
+ * @param address Address to bind to
+ * @param port Listening port
+ * @param factory SSL socket factory implementation
+ */
+ TNonblockingSSLServerSocket(const std::string& address,
+ int port,
+ std::shared_ptr<TSSLSocketFactory> factory);
+
+ /**
+ * Constructor. Binds to all interfaces.
+ *
+ * @param port Listening port
+ * @param sendTimeout Socket send timeout
+ * @param recvTimeout Socket receive timeout
+ * @param factory SSL socket factory implementation
+ */
+ TNonblockingSSLServerSocket(int port,
+ int sendTimeout,
+ int recvTimeout,
+ std::shared_ptr<TSSLSocketFactory> factory);
+
+protected:
+ std::shared_ptr<TSocket> createSocket(THRIFT_SOCKET socket) override;
+ std::shared_ptr<TSSLSocketFactory> factory_;
+};
+}
+}
+}
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingServerSocket.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingServerSocket.cpp
new file mode 100644
index 000000000..9902b9063
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingServerSocket.cpp
@@ -0,0 +1,549 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+
+#include <cstring>
+#include <memory>
+#include <stdexcept>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <thrift/transport/TSocket.h>
+#include <thrift/transport/TNonblockingServerSocket.h>
+#include <thrift/transport/PlatformSocket.h>
+
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+
+#ifndef SOCKOPT_CAST_T
+#ifndef _WIN32
+#define SOCKOPT_CAST_T void
+#else
+#define SOCKOPT_CAST_T char
+#endif // _WIN32
+#endif
+
+template <class T>
+inline const SOCKOPT_CAST_T* const_cast_sockopt(const T* v) {
+ return reinterpret_cast<const SOCKOPT_CAST_T*>(v);
+}
+
+template <class T>
+inline SOCKOPT_CAST_T* cast_sockopt(T* v) {
+ return reinterpret_cast<SOCKOPT_CAST_T*>(v);
+}
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+using std::string;
+using std::shared_ptr;
+
+TNonblockingServerSocket::TNonblockingServerSocket(int port)
+ : port_(port),
+ listenPort_(port),
+ serverSocket_(THRIFT_INVALID_SOCKET),
+ acceptBacklog_(DEFAULT_BACKLOG),
+ sendTimeout_(0),
+ recvTimeout_(0),
+ retryLimit_(0),
+ retryDelay_(0),
+ tcpSendBuffer_(0),
+ tcpRecvBuffer_(0),
+ keepAlive_(false),
+ listening_(false) {
+}
+
+TNonblockingServerSocket::TNonblockingServerSocket(int port, int sendTimeout, int recvTimeout)
+ : port_(port),
+ listenPort_(port),
+ serverSocket_(THRIFT_INVALID_SOCKET),
+ acceptBacklog_(DEFAULT_BACKLOG),
+ sendTimeout_(sendTimeout),
+ recvTimeout_(recvTimeout),
+ retryLimit_(0),
+ retryDelay_(0),
+ tcpSendBuffer_(0),
+ tcpRecvBuffer_(0),
+ keepAlive_(false),
+ listening_(false) {
+}
+
+TNonblockingServerSocket::TNonblockingServerSocket(const string& address, int port)
+ : port_(port),
+ listenPort_(port),
+ address_(address),
+ serverSocket_(THRIFT_INVALID_SOCKET),
+ acceptBacklog_(DEFAULT_BACKLOG),
+ sendTimeout_(0),
+ recvTimeout_(0),
+ retryLimit_(0),
+ retryDelay_(0),
+ tcpSendBuffer_(0),
+ tcpRecvBuffer_(0),
+ keepAlive_(false),
+ listening_(false) {
+}
+
+TNonblockingServerSocket::TNonblockingServerSocket(const string& path)
+ : port_(0),
+ listenPort_(0),
+ path_(path),
+ serverSocket_(THRIFT_INVALID_SOCKET),
+ acceptBacklog_(DEFAULT_BACKLOG),
+ sendTimeout_(0),
+ recvTimeout_(0),
+ retryLimit_(0),
+ retryDelay_(0),
+ tcpSendBuffer_(0),
+ tcpRecvBuffer_(0),
+ keepAlive_(false),
+ listening_(false) {
+}
+
+TNonblockingServerSocket::~TNonblockingServerSocket() {
+ close();
+}
+
+void TNonblockingServerSocket::setSendTimeout(int sendTimeout) {
+ sendTimeout_ = sendTimeout;
+}
+
+void TNonblockingServerSocket::setRecvTimeout(int recvTimeout) {
+ recvTimeout_ = recvTimeout;
+}
+
+void TNonblockingServerSocket::setAcceptBacklog(int accBacklog) {
+ acceptBacklog_ = accBacklog;
+}
+
+void TNonblockingServerSocket::setRetryLimit(int retryLimit) {
+ retryLimit_ = retryLimit;
+}
+
+void TNonblockingServerSocket::setRetryDelay(int retryDelay) {
+ retryDelay_ = retryDelay;
+}
+
+void TNonblockingServerSocket::setTcpSendBuffer(int tcpSendBuffer) {
+ tcpSendBuffer_ = tcpSendBuffer;
+}
+
+void TNonblockingServerSocket::setTcpRecvBuffer(int tcpRecvBuffer) {
+ tcpRecvBuffer_ = tcpRecvBuffer;
+}
+
+void TNonblockingServerSocket::listen() {
+ listening_ = true;
+#ifdef _WIN32
+ TWinsockSingleton::create();
+#endif // _WIN32
+
+ // Validate port number
+ if (port_ < 0 || port_ > 0xFFFF) {
+ throw TTransportException(TTransportException::BAD_ARGS, "Specified port is invalid");
+ }
+
+ const struct addrinfo *res;
+ int error;
+ char port[sizeof("65535")];
+ THRIFT_SNPRINTF(port, sizeof(port), "%d", port_);
+
+ struct addrinfo hints;
+ std::memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+
+ // If address is not specified use wildcard address (NULL)
+ TGetAddrInfoWrapper info(address_.empty() ? nullptr : &address_[0], port, &hints);
+
+ error = info.init();
+ if (error) {
+ GlobalOutput.printf("getaddrinfo %d: %s", error, THRIFT_GAI_STRERROR(error));
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not resolve host for server socket.");
+ }
+
+ // Pick the ipv6 address first since ipv4 addresses can be mapped
+ // into ipv6 space.
+ for (res = info.res(); res; res = res->ai_next) {
+ if (res->ai_family == AF_INET6 || res->ai_next == nullptr)
+ break;
+ }
+
+ if (!path_.empty()) {
+ serverSocket_ = socket(PF_UNIX, SOCK_STREAM, IPPROTO_IP);
+ } else if (res != nullptr) {
+ serverSocket_ = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ }
+
+ if (serverSocket_ == THRIFT_INVALID_SOCKET) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TNonblockingServerSocket::listen() socket() ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not create server socket.",
+ errno_copy);
+ }
+
+ // Set THRIFT_NO_SOCKET_CACHING to prevent 2MSL delay on accept
+ int one = 1;
+ if (-1 == setsockopt(serverSocket_,
+ SOL_SOCKET,
+ THRIFT_NO_SOCKET_CACHING,
+ cast_sockopt(&one),
+ sizeof(one))) {
+// ignore errors coming out of this setsockopt on Windows. This is because
+// SO_EXCLUSIVEADDRUSE requires admin privileges on WinXP, but we don't
+// want to force servers to be an admin.
+#ifndef _WIN32
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TNonblockingServerSocket::listen() setsockopt() THRIFT_NO_SOCKET_CACHING ",
+ errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not set THRIFT_NO_SOCKET_CACHING",
+ errno_copy);
+#endif
+ }
+
+ // Set TCP buffer sizes
+ if (tcpSendBuffer_ > 0) {
+ if (-1 == setsockopt(serverSocket_,
+ SOL_SOCKET,
+ SO_SNDBUF,
+ cast_sockopt(&tcpSendBuffer_),
+ sizeof(tcpSendBuffer_))) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TNonblockingServerSocket::listen() setsockopt() SO_SNDBUF ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not set SO_SNDBUF",
+ errno_copy);
+ }
+ }
+
+ if (tcpRecvBuffer_ > 0) {
+ if (-1 == setsockopt(serverSocket_,
+ SOL_SOCKET,
+ SO_RCVBUF,
+ cast_sockopt(&tcpRecvBuffer_),
+ sizeof(tcpRecvBuffer_))) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TNonblockingServerSocket::listen() setsockopt() SO_RCVBUF ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not set SO_RCVBUF",
+ errno_copy);
+ }
+ }
+
+#ifdef IPV6_V6ONLY
+ if (res->ai_family == AF_INET6 && path_.empty()) {
+ int zero = 0;
+ if (-1 == setsockopt(serverSocket_,
+ IPPROTO_IPV6,
+ IPV6_V6ONLY,
+ cast_sockopt(&zero),
+ sizeof(zero))) {
+ GlobalOutput.perror("TNonblockingServerSocket::listen() IPV6_V6ONLY ", THRIFT_GET_SOCKET_ERROR);
+ }
+ }
+#endif // #ifdef IPV6_V6ONLY
+
+ // Turn linger off, don't want to block on calls to close
+ struct linger ling = {0, 0};
+ if (-1 == setsockopt(serverSocket_, SOL_SOCKET, SO_LINGER, cast_sockopt(&ling), sizeof(ling))) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TNonblockingServerSocket::listen() setsockopt() SO_LINGER ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN, "Could not set SO_LINGER", errno_copy);
+ }
+
+ // Keepalive to ensure full result flushing
+ if (-1 == setsockopt(serverSocket_, SOL_SOCKET, SO_KEEPALIVE, const_cast_sockopt(&one), sizeof(one))) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TNonblockingServerSocket::listen() setsockopt() SO_KEEPALIVE ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not set TCP_NODELAY",
+ errno_copy);
+ }
+
+ // Set TCP nodelay if available, MAC OS X Hack
+ // See http://lists.danga.com/pipermail/memcached/2005-March/001240.html
+#ifndef TCP_NOPUSH
+ // Unix Sockets do not need that
+ if (path_.empty()) {
+ // TCP Nodelay, speed over bandwidth
+ if (-1
+ == setsockopt(serverSocket_, IPPROTO_TCP, TCP_NODELAY, cast_sockopt(&one), sizeof(one))) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TNonblockingServerSocket::listen() setsockopt() TCP_NODELAY ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not set TCP_NODELAY",
+ errno_copy);
+ }
+ }
+#endif
+
+ // Set NONBLOCK on the accept socket
+ int flags = THRIFT_FCNTL(serverSocket_, THRIFT_F_GETFL, 0);
+ if (flags == -1) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TNonblockingServerSocket::listen() THRIFT_FCNTL() THRIFT_F_GETFL ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "THRIFT_FCNTL() THRIFT_F_GETFL failed",
+ errno_copy);
+ }
+
+ if (-1 == THRIFT_FCNTL(serverSocket_, THRIFT_F_SETFL, flags | THRIFT_O_NONBLOCK)) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TNonblockingServerSocket::listen() THRIFT_FCNTL() THRIFT_O_NONBLOCK ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "THRIFT_FCNTL() THRIFT_F_SETFL THRIFT_O_NONBLOCK failed",
+ errno_copy);
+ }
+
+#ifdef TCP_LOW_MIN_RTO
+ if (TSocket::getUseLowMinRto()) {
+ if (-1 == setsockopt(s, IPPROTO_TCP, TCP_LOW_MIN_RTO, const_cast_sockopt(&one), sizeof(one))) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TNonblockingServerSocket::listen() setsockopt() TCP_LOW_MIN_RTO ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not set TCP_NODELAY",
+ errno_copy);
+ }
+ }
+#endif
+
+ // prepare the port information
+ // we may want to try to bind more than once, since THRIFT_NO_SOCKET_CACHING doesn't
+ // always seem to work. The client can configure the retry variables.
+ int retries = 0;
+ int errno_copy = 0;
+
+ if (!path_.empty()) {
+
+#ifndef _WIN32
+
+ // Unix Domain Socket
+ size_t len = path_.size() + 1;
+ if (len > sizeof(((sockaddr_un*)nullptr)->sun_path)) {
+ errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TSocket::listen() Unix Domain socket path too long", errno_copy);
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Unix Domain socket path too long",
+ errno_copy);
+ }
+
+ struct sockaddr_un address;
+ address.sun_family = AF_UNIX;
+ memcpy(address.sun_path, path_.c_str(), len);
+
+ auto structlen = static_cast<socklen_t>(sizeof(address));
+
+ if (!address.sun_path[0]) { // abstract namespace socket
+#ifdef __linux__
+ // sun_path is not null-terminated in this case and structlen determines its length
+ structlen -= sizeof(address.sun_path) - len;
+#else
+ GlobalOutput.perror("TSocket::open() Abstract Namespace Domain sockets only supported on linux: ", -99);
+ throw TTransportException(TTransportException::NOT_OPEN,
+ " Abstract Namespace Domain socket path not supported");
+#endif
+ }
+
+ do {
+ if (0 == ::bind(serverSocket_, (struct sockaddr*)&address, structlen)) {
+ break;
+ }
+ errno_copy = THRIFT_GET_SOCKET_ERROR;
+ // use short circuit evaluation here to only sleep if we need to
+ } while ((retries++ < retryLimit_) && (THRIFT_SLEEP_SEC(retryDelay_) == 0));
+#else
+ GlobalOutput.perror("TSocket::open() Unix Domain socket path not supported on windows", -99);
+ throw TTransportException(TTransportException::NOT_OPEN,
+ " Unix Domain socket path not supported");
+#endif
+ } else {
+ do {
+ if (0 == ::bind(serverSocket_, res->ai_addr, static_cast<int>(res->ai_addrlen))) {
+ break;
+ }
+ errno_copy = THRIFT_GET_SOCKET_ERROR;
+ // use short circuit evaluation here to only sleep if we need to
+ } while ((retries++ < retryLimit_) && (THRIFT_SLEEP_SEC(retryDelay_) == 0));
+
+ // retrieve bind info
+ if (port_ == 0 && retries <= retryLimit_) {
+ struct sockaddr_storage sa;
+ socklen_t len = sizeof(sa);
+ std::memset(&sa, 0, len);
+ if (::getsockname(serverSocket_, reinterpret_cast<struct sockaddr*>(&sa), &len) < 0) {
+ errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TNonblockingServerSocket::getPort() getsockname() ", errno_copy);
+ } else {
+ if (sa.ss_family == AF_INET6) {
+ const auto* sin = reinterpret_cast<const struct sockaddr_in6*>(&sa);
+ listenPort_ = ntohs(sin->sin6_port);
+ } else {
+ const auto* sin = reinterpret_cast<const struct sockaddr_in*>(&sa);
+ listenPort_ = ntohs(sin->sin_port);
+ }
+ }
+ }
+ }
+
+ // throw an error if we failed to bind properly
+ if (retries > retryLimit_) {
+ char errbuf[1024];
+ if (!path_.empty()) {
+ THRIFT_SNPRINTF(errbuf, sizeof(errbuf), "TNonblockingServerSocket::listen() PATH %s", path_.c_str());
+ } else {
+ THRIFT_SNPRINTF(errbuf, sizeof(errbuf), "TNonblockingServerSocket::listen() BIND %d", port_);
+ }
+ GlobalOutput(errbuf);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not bind",
+ errno_copy);
+ }
+
+ if (listenCallback_)
+ listenCallback_(serverSocket_);
+
+ // Call listen
+ if (-1 == ::listen(serverSocket_, acceptBacklog_)) {
+ errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TNonblockingServerSocket::listen() listen() ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN, "Could not listen", errno_copy);
+ }
+
+ // The socket is now listening!
+}
+
+int TNonblockingServerSocket::getPort() {
+ return port_;
+}
+
+int TNonblockingServerSocket::getListenPort() {
+ return listenPort_;
+}
+
+shared_ptr<TSocket> TNonblockingServerSocket::acceptImpl() {
+ if (serverSocket_ == THRIFT_INVALID_SOCKET) {
+ throw TTransportException(TTransportException::NOT_OPEN, "TNonblockingServerSocket not listening");
+ }
+
+ struct sockaddr_storage clientAddress;
+ int size = sizeof(clientAddress);
+ THRIFT_SOCKET clientSocket
+ = ::accept(serverSocket_, (struct sockaddr*)&clientAddress, (socklen_t*)&size);
+
+ if (clientSocket == THRIFT_INVALID_SOCKET) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TNonblockingServerSocket::acceptImpl() ::accept() ", errno_copy);
+ throw TTransportException(TTransportException::UNKNOWN, "accept()", errno_copy);
+ }
+
+ // Explicitly set this socket to NONBLOCK mode
+ int flags = THRIFT_FCNTL(clientSocket, THRIFT_F_GETFL, 0);
+ if (flags == -1) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ ::THRIFT_CLOSESOCKET(clientSocket);
+ GlobalOutput.perror("TNonblockingServerSocket::acceptImpl() THRIFT_FCNTL() THRIFT_F_GETFL ", errno_copy);
+ throw TTransportException(TTransportException::UNKNOWN,
+ "THRIFT_FCNTL(THRIFT_F_GETFL)",
+ errno_copy);
+ }
+
+ if (-1 == THRIFT_FCNTL(clientSocket, THRIFT_F_SETFL, flags | THRIFT_O_NONBLOCK)) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ ::THRIFT_CLOSESOCKET(clientSocket);
+ GlobalOutput
+ .perror("TNonblockingServerSocket::acceptImpl() THRIFT_FCNTL() THRIFT_F_SETFL ~THRIFT_O_NONBLOCK ",
+ errno_copy);
+ throw TTransportException(TTransportException::UNKNOWN,
+ "THRIFT_FCNTL(THRIFT_F_SETFL)",
+ errno_copy);
+ }
+
+ shared_ptr<TSocket> client = createSocket(clientSocket);
+ if (sendTimeout_ > 0) {
+ client->setSendTimeout(sendTimeout_);
+ }
+ if (recvTimeout_ > 0) {
+ client->setRecvTimeout(recvTimeout_);
+ }
+ if (keepAlive_) {
+ client->setKeepAlive(keepAlive_);
+ }
+ client->setCachedAddress((sockaddr*)&clientAddress, size);
+
+ if (acceptCallback_)
+ acceptCallback_(clientSocket);
+
+ return client;
+}
+
+shared_ptr<TSocket> TNonblockingServerSocket::createSocket(THRIFT_SOCKET clientSocket) {
+ return std::make_shared<TSocket>(clientSocket);
+}
+
+void TNonblockingServerSocket::close() {
+ if (serverSocket_ != THRIFT_INVALID_SOCKET) {
+ shutdown(serverSocket_, THRIFT_SHUT_RDWR);
+ ::THRIFT_CLOSESOCKET(serverSocket_);
+ }
+ serverSocket_ = THRIFT_INVALID_SOCKET;
+ listening_ = false;
+}
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingServerSocket.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingServerSocket.h
new file mode 100644
index 000000000..a68c28d22
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingServerSocket.h
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TNONBLOCKINGSERVERSOCKET_H_
+#define _THRIFT_TRANSPORT_TNONBLOCKINGSERVERSOCKET_H_ 1
+
+#include <thrift/transport/TNonblockingServerTransport.h>
+#include <thrift/transport/PlatformSocket.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+class TSocket;
+
+/**
+ * Nonblocking Server socket implementation of TNonblockingServerTransport. Wrapper around a unix
+ * socket listen and accept calls.
+ *
+ */
+class TNonblockingServerSocket : public TNonblockingServerTransport {
+public:
+ typedef std::function<void(THRIFT_SOCKET fd)> socket_func_t;
+
+ const static int DEFAULT_BACKLOG = 1024;
+
+ /**
+ * Constructor.
+ *
+ * @param port Port number to bind to
+ */
+ TNonblockingServerSocket(int port);
+
+ /**
+ * Constructor.
+ *
+ * @param port Port number to bind to
+ * @param sendTimeout Socket send timeout
+ * @param recvTimeout Socket receive timeout
+ */
+ TNonblockingServerSocket(int port, int sendTimeout, int recvTimeout);
+
+ /**
+ * Constructor.
+ *
+ * @param address Address to bind to
+ * @param port Port number to bind to
+ */
+ TNonblockingServerSocket(const std::string& address, int port);
+
+ /**
+ * Constructor used for unix sockets.
+ *
+ * @param path Pathname for unix socket.
+ */
+ TNonblockingServerSocket(const std::string& path);
+
+ ~TNonblockingServerSocket() override;
+
+ void setSendTimeout(int sendTimeout);
+ void setRecvTimeout(int recvTimeout);
+
+ void setAcceptBacklog(int accBacklog);
+
+ void setRetryLimit(int retryLimit);
+ void setRetryDelay(int retryDelay);
+
+ void setKeepAlive(bool keepAlive) { keepAlive_ = keepAlive; }
+
+ void setTcpSendBuffer(int tcpSendBuffer);
+ void setTcpRecvBuffer(int tcpRecvBuffer);
+
+ // listenCallback gets called just before listen, and after all Thrift
+ // setsockopt calls have been made. If you have custom setsockopt
+ // things that need to happen on the listening socket, this is the place to do it.
+ void setListenCallback(const socket_func_t& listenCallback) { listenCallback_ = listenCallback; }
+
+ // acceptCallback gets called after each accept call, on the newly created socket.
+ // It is called after all Thrift setsockopt calls have been made. If you have
+ // custom setsockopt things that need to happen on the accepted
+ // socket, this is the place to do it.
+ void setAcceptCallback(const socket_func_t& acceptCallback) { acceptCallback_ = acceptCallback; }
+
+ THRIFT_SOCKET getSocketFD() override { return serverSocket_; }
+
+ int getPort() override;
+
+ int getListenPort() override;
+
+ void listen() override;
+ void close() override;
+
+protected:
+ std::shared_ptr<TSocket> acceptImpl() override;
+ virtual std::shared_ptr<TSocket> createSocket(THRIFT_SOCKET client);
+
+private:
+ int port_;
+ int listenPort_;
+ std::string address_;
+ std::string path_;
+ THRIFT_SOCKET serverSocket_;
+ int acceptBacklog_;
+ int sendTimeout_;
+ int recvTimeout_;
+ int retryLimit_;
+ int retryDelay_;
+ int tcpSendBuffer_;
+ int tcpRecvBuffer_;
+ bool keepAlive_;
+ bool listening_;
+
+ socket_func_t listenCallback_;
+ socket_func_t acceptCallback_;
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_TNONBLOCKINGSERVERSOCKET_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingServerTransport.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingServerTransport.h
new file mode 100644
index 000000000..f81132869
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TNonblockingServerTransport.h
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TNONBLOCKINGSERVERTRANSPORT_H_
+#define _THRIFT_TRANSPORT_TNONBLOCKINGSERVERTRANSPORT_H_ 1
+
+#include <thrift/transport/TSocket.h>
+#include <thrift/transport/TTransportException.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * Server transport framework. A server needs to have some facility for
+ * creating base transports to read/write from. The server is expected
+ * to keep track of TTransport children that it creates for purposes of
+ * controlling their lifetime.
+ */
+class TNonblockingServerTransport {
+public:
+ virtual ~TNonblockingServerTransport() = default;
+
+ /**
+ * Starts the server transport listening for new connections. Prior to this
+ * call most transports will not return anything when accept is called.
+ *
+ * @throws TTransportException if we were unable to listen
+ */
+ virtual void listen() {}
+
+ /**
+ * Gets a new dynamically allocated transport object and passes it to the
+ * caller. Note that it is the explicit duty of the caller to free the
+ * allocated object. The returned TTransport object must always be in the
+ * opened state. NULL should never be returned, instead an Exception should
+ * always be thrown.
+ *
+ * @return A new TTransport object
+ * @throws TTransportException if there is an error
+ */
+ std::shared_ptr<TSocket> accept() {
+ std::shared_ptr<TSocket> result = acceptImpl();
+ if (!result) {
+ throw TTransportException("accept() may not return NULL");
+ }
+ return result;
+ }
+
+ /**
+ * Utility method
+ *
+ * @return server socket file descriptor
+ * @throw TTransportException If an error occurs
+ */
+
+ virtual THRIFT_SOCKET getSocketFD() = 0;
+
+ virtual int getPort() = 0;
+
+ virtual int getListenPort() = 0;
+
+ /**
+ * Closes this transport such that future calls to accept will do nothing.
+ */
+ virtual void close() = 0;
+
+protected:
+ TNonblockingServerTransport() = default;
+
+ /**
+ * Subclasses should implement this function for accept.
+ *
+ * @return A newly allocated TTransport object
+ * @throw TTransportException If an error occurs
+ */
+ virtual std::shared_ptr<TSocket> acceptImpl() = 0;
+
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_TNONBLOCKINGSERVERTRANSPORT_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TPipe.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TPipe.cpp
new file mode 100644
index 000000000..72af4fcd6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TPipe.cpp
@@ -0,0 +1,398 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+#include <thrift/transport/TTransportException.h>
+#include <thrift/transport/TPipe.h>
+#ifdef _WIN32
+#include <thrift/windows/OverlappedSubmissionThread.h>
+#include <thrift/windows/Sync.h>
+#endif
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+* TPipe implementation.
+*/
+
+#ifdef _WIN32
+
+uint32_t pipe_read(HANDLE pipe, uint8_t* buf, uint32_t len);
+void pipe_write(HANDLE pipe, const uint8_t* buf, uint32_t len);
+
+uint32_t pseudo_sync_read(HANDLE pipe, HANDLE event, uint8_t* buf, uint32_t len);
+void pseudo_sync_write(HANDLE pipe, HANDLE event, const uint8_t* buf, uint32_t len);
+
+class TPipeImpl : boost::noncopyable {
+public:
+ TPipeImpl() {}
+ virtual ~TPipeImpl() {}
+ virtual uint32_t read(uint8_t* buf, uint32_t len) = 0;
+ virtual void write(const uint8_t* buf, uint32_t len) = 0;
+ virtual HANDLE getPipeHandle() = 0; // doubles as the read handle for anon pipe
+ virtual void setPipeHandle(HANDLE pipehandle) = 0;
+ virtual HANDLE getWrtPipeHandle() { return INVALID_HANDLE_VALUE; }
+ virtual void setWrtPipeHandle(HANDLE) {}
+ virtual bool isBufferedDataAvailable() { return false; }
+ virtual HANDLE getNativeWaitHandle() { return INVALID_HANDLE_VALUE; }
+};
+
+class TNamedPipeImpl : public TPipeImpl {
+public:
+ explicit TNamedPipeImpl(TAutoHandle &pipehandle) : Pipe_(pipehandle.release()) {}
+ virtual ~TNamedPipeImpl() {}
+ virtual uint32_t read(uint8_t* buf, uint32_t len) {
+ return pseudo_sync_read(Pipe_.h, read_event_.h, buf, len);
+ }
+ virtual void write(const uint8_t* buf, uint32_t len) {
+ pseudo_sync_write(Pipe_.h, write_event_.h, buf, len);
+ }
+
+ virtual HANDLE getPipeHandle() { return Pipe_.h; }
+ virtual void setPipeHandle(HANDLE pipehandle) { Pipe_.reset(pipehandle); }
+
+private:
+ TManualResetEvent read_event_;
+ TManualResetEvent write_event_;
+ TAutoHandle Pipe_;
+};
+
+class TAnonPipeImpl : public TPipeImpl {
+public:
+ TAnonPipeImpl(HANDLE PipeRd, HANDLE PipeWrt) : PipeRd_(PipeRd), PipeWrt_(PipeWrt) {}
+ virtual ~TAnonPipeImpl() {}
+ virtual uint32_t read(uint8_t* buf, uint32_t len) { return pipe_read(PipeRd_.h, buf, len); }
+ virtual void write(const uint8_t* buf, uint32_t len) { pipe_write(PipeWrt_.h, buf, len); }
+
+ virtual HANDLE getPipeHandle() { return PipeRd_.h; }
+ virtual void setPipeHandle(HANDLE PipeRd) { PipeRd_.reset(PipeRd); }
+ virtual HANDLE getWrtPipeHandle() { return PipeWrt_.h; }
+ virtual void setWrtPipeHandle(HANDLE PipeWrt) { PipeWrt_.reset(PipeWrt); }
+
+private:
+ TAutoHandle PipeRd_;
+ TAutoHandle PipeWrt_;
+};
+
+// If you want a select-like loop to work, use this subclass. Be warned...
+// the read implementation has several context switches, so this is slower
+// than using the regular named pipe implementation
+class TWaitableNamedPipeImpl : public TPipeImpl {
+public:
+ explicit TWaitableNamedPipeImpl(TAutoHandle &pipehandle)
+ : begin_unread_idx_(0), end_unread_idx_(0) {
+ readOverlap_.action = TOverlappedWorkItem::READ;
+ readOverlap_.h = pipehandle.h;
+ cancelOverlap_.action = TOverlappedWorkItem::CANCELIO;
+ cancelOverlap_.h = pipehandle.h;
+ buffer_.resize(1024 /*arbitrary buffer size*/, '\0');
+ beginAsyncRead(&buffer_[0], static_cast<uint32_t>(buffer_.size()));
+ Pipe_.reset(pipehandle.release());
+ }
+ virtual ~TWaitableNamedPipeImpl() {
+ // see if there is an outstanding read request
+ if (begin_unread_idx_ == end_unread_idx_) {
+ // if so, cancel it, and wait for the dead completion
+ thread_->addWorkItem(&cancelOverlap_);
+ readOverlap_.overlappedResults(false /*ignore errors*/);
+ }
+ }
+ virtual uint32_t read(uint8_t* buf, uint32_t len);
+ virtual void write(const uint8_t* buf, uint32_t len) {
+ pseudo_sync_write(Pipe_.h, write_event_.h, buf, len);
+ }
+
+ virtual HANDLE getPipeHandle() { return Pipe_.h; }
+ virtual void setPipeHandle(HANDLE pipehandle) { Pipe_.reset(pipehandle); }
+ virtual bool isBufferedDataAvailable() { return begin_unread_idx_ < end_unread_idx_; }
+ virtual HANDLE getNativeWaitHandle() { return ready_event_.h; }
+
+private:
+ void beginAsyncRead(uint8_t* buf, uint32_t len);
+ uint32_t endAsyncRead();
+
+ TAutoOverlapThread thread_;
+ TAutoHandle Pipe_;
+ TOverlappedWorkItem readOverlap_;
+ TOverlappedWorkItem cancelOverlap_;
+ TManualResetEvent ready_event_;
+ TManualResetEvent write_event_;
+ std::vector<uint8_t> buffer_;
+ uint32_t begin_unread_idx_;
+ uint32_t end_unread_idx_;
+};
+
+void TWaitableNamedPipeImpl::beginAsyncRead(uint8_t* buf, uint32_t len) {
+ begin_unread_idx_ = end_unread_idx_ = 0;
+ readOverlap_.reset(buf, len, ready_event_.h);
+ thread_->addWorkItem(&readOverlap_);
+ if (readOverlap_.success == FALSE && readOverlap_.last_error != ERROR_IO_PENDING) {
+ GlobalOutput.perror("TPipe ::ReadFile errored GLE=", readOverlap_.last_error);
+ throw TTransportException(TTransportException::UNKNOWN, "TPipe: ReadFile failed");
+ }
+}
+
+uint32_t TWaitableNamedPipeImpl::endAsyncRead() {
+ return readOverlap_.overlappedResults();
+}
+
+uint32_t TWaitableNamedPipeImpl::read(uint8_t* buf, uint32_t len) {
+ if (begin_unread_idx_ == end_unread_idx_) {
+ end_unread_idx_ = endAsyncRead();
+ }
+
+ uint32_t __idxsize = end_unread_idx_ - begin_unread_idx_;
+ uint32_t bytes_to_copy = (len < __idxsize) ? len : __idxsize;
+ memcpy(buf, &buffer_[begin_unread_idx_], bytes_to_copy);
+ begin_unread_idx_ += bytes_to_copy;
+ if (begin_unread_idx_ != end_unread_idx_) {
+ assert(len == bytes_to_copy);
+ // we were able to fulfill the read with just the bytes in our
+ // buffer, and we still have buffer left
+ return bytes_to_copy;
+ }
+ uint32_t bytes_copied = bytes_to_copy;
+
+ // all of the requested data has been read. Kick off an async read for the next round.
+ beginAsyncRead(&buffer_[0], static_cast<uint32_t>(buffer_.size()));
+
+ return bytes_copied;
+}
+
+void pseudo_sync_write(HANDLE pipe, HANDLE event, const uint8_t* buf, uint32_t len) {
+ OVERLAPPED tempOverlap;
+ memset(&tempOverlap, 0, sizeof(tempOverlap));
+ tempOverlap.hEvent = event;
+
+ uint32_t written = 0;
+ while (written < len) {
+ BOOL result = ::WriteFile(pipe, buf + written, len - written, NULL, &tempOverlap);
+
+ if (result == FALSE && ::GetLastError() != ERROR_IO_PENDING) {
+ GlobalOutput.perror("TPipe ::WriteFile errored GLE=", ::GetLastError());
+ throw TTransportException(TTransportException::UNKNOWN, "TPipe: write failed");
+ }
+
+ DWORD bytes = 0;
+ result = ::GetOverlappedResult(pipe, &tempOverlap, &bytes, TRUE);
+ if (!result) {
+ GlobalOutput.perror("TPipe ::GetOverlappedResult errored GLE=", ::GetLastError());
+ throw TTransportException(TTransportException::UNKNOWN, "TPipe: GetOverlappedResult failed");
+ }
+ written += bytes;
+ }
+}
+
+uint32_t pseudo_sync_read(HANDLE pipe, HANDLE event, uint8_t* buf, uint32_t len) {
+ OVERLAPPED tempOverlap;
+ memset(&tempOverlap, 0, sizeof(tempOverlap));
+ tempOverlap.hEvent = event;
+
+ BOOL result = ::ReadFile(pipe, buf, len, NULL, &tempOverlap);
+
+ if (result == FALSE && ::GetLastError() != ERROR_IO_PENDING) {
+ GlobalOutput.perror("TPipe ::ReadFile errored GLE=", ::GetLastError());
+ throw TTransportException(TTransportException::UNKNOWN, "TPipe: read failed");
+ }
+
+ DWORD bytes = 0;
+ result = ::GetOverlappedResult(pipe, &tempOverlap, &bytes, TRUE);
+ if (!result) {
+ GlobalOutput.perror("TPipe ::GetOverlappedResult errored GLE=", ::GetLastError());
+ throw TTransportException(TTransportException::UNKNOWN, "TPipe: GetOverlappedResult failed");
+ }
+ return bytes;
+}
+
+//---- Constructors ----
+TPipe::TPipe(TAutoHandle &Pipe)
+ : impl_(new TWaitableNamedPipeImpl(Pipe)), TimeoutSeconds_(3), isAnonymous_(false) {
+}
+
+TPipe::TPipe(HANDLE Pipe)
+ : TimeoutSeconds_(3), isAnonymous_(false)
+{
+ TAutoHandle pipeHandle(Pipe);
+ impl_.reset(new TWaitableNamedPipeImpl(pipeHandle));
+}
+
+TPipe::TPipe(const char* pipename) : TimeoutSeconds_(3), isAnonymous_(false) {
+ setPipename(pipename);
+}
+
+TPipe::TPipe(const std::string& pipename) : TimeoutSeconds_(3), isAnonymous_(false) {
+ setPipename(pipename);
+}
+
+TPipe::TPipe(HANDLE PipeRd, HANDLE PipeWrt)
+ : impl_(new TAnonPipeImpl(PipeRd, PipeWrt)), TimeoutSeconds_(3), isAnonymous_(true) {
+}
+
+TPipe::TPipe() : TimeoutSeconds_(3), isAnonymous_(false) {
+}
+
+TPipe::~TPipe() {
+}
+
+//---------------------------------------------------------
+// Transport callbacks
+//---------------------------------------------------------
+bool TPipe::isOpen() const {
+ return impl_.get() != NULL;
+}
+
+bool TPipe::peek() {
+ return isOpen();
+}
+
+void TPipe::open() {
+ if (isOpen())
+ return;
+
+ TAutoHandle hPipe;
+ do {
+ DWORD flags = FILE_FLAG_OVERLAPPED; // async mode, so we can do reads at the same time as writes
+ hPipe.reset(CreateFileA(pipename_.c_str(),
+ GENERIC_READ | GENERIC_WRITE,
+ 0, // no sharing
+ NULL, // default security attributes
+ OPEN_EXISTING, // opens existing pipe
+ flags,
+ NULL)); // no template file
+
+ if (hPipe.h != INVALID_HANDLE_VALUE)
+ break; // success!
+
+ if (::GetLastError() != ERROR_PIPE_BUSY) {
+ GlobalOutput.perror("TPipe::open ::CreateFile errored GLE=", ::GetLastError());
+ throw TTransportException(TTransportException::NOT_OPEN, "Unable to open pipe");
+ }
+ } while (::WaitNamedPipeA(pipename_.c_str(), TimeoutSeconds_ * 1000));
+
+ if (hPipe.h == INVALID_HANDLE_VALUE) {
+ GlobalOutput.perror("TPipe::open ::CreateFile errored GLE=", ::GetLastError());
+ throw TTransportException(TTransportException::NOT_OPEN, "Unable to open pipe");
+ }
+
+ impl_.reset(new TNamedPipeImpl(hPipe));
+}
+
+void TPipe::close() {
+ impl_.reset();
+}
+
+uint32_t TPipe::read(uint8_t* buf, uint32_t len) {
+ if (!isOpen())
+ throw TTransportException(TTransportException::NOT_OPEN, "Called read on non-open pipe");
+ return impl_->read(buf, len);
+}
+
+uint32_t pipe_read(HANDLE pipe, uint8_t* buf, uint32_t len) {
+ DWORD cbRead;
+ int fSuccess = ReadFile(pipe, // pipe handle
+ buf, // buffer to receive reply
+ len, // size of buffer
+ &cbRead, // number of bytes read
+ NULL); // not overlapped
+
+ if (!fSuccess && GetLastError() != ERROR_MORE_DATA)
+ return 0; // No more data, possibly because client disconnected.
+
+ return cbRead;
+}
+
+void TPipe::write(const uint8_t* buf, uint32_t len) {
+ if (!isOpen())
+ throw TTransportException(TTransportException::NOT_OPEN, "Called write on non-open pipe");
+ impl_->write(buf, len);
+}
+
+void pipe_write(HANDLE pipe, const uint8_t* buf, uint32_t len) {
+ DWORD cbWritten;
+ int fSuccess = WriteFile(pipe, // pipe handle
+ buf, // message
+ len, // message length
+ &cbWritten, // bytes written
+ NULL); // not overlapped
+
+ if (!fSuccess)
+ throw TTransportException(TTransportException::NOT_OPEN, "Write to pipe failed");
+}
+
+//---------------------------------------------------------
+// Accessors
+//---------------------------------------------------------
+
+std::string TPipe::getPipename() {
+ return pipename_;
+}
+
+void TPipe::setPipename(const std::string& pipename) {
+ if (pipename.find("\\\\") == std::string::npos)
+ pipename_ = "\\\\.\\pipe\\" + pipename;
+ else
+ pipename_ = pipename;
+}
+
+HANDLE TPipe::getPipeHandle() {
+ if (impl_)
+ return impl_->getPipeHandle();
+ return INVALID_HANDLE_VALUE;
+}
+
+void TPipe::setPipeHandle(HANDLE pipehandle) {
+ if (isAnonymous_)
+ impl_->setPipeHandle(pipehandle);
+ else
+ {
+ TAutoHandle pipe(pipehandle);
+ impl_.reset(new TNamedPipeImpl(pipe));
+ }
+}
+
+HANDLE TPipe::getWrtPipeHandle() {
+ if (impl_)
+ return impl_->getWrtPipeHandle();
+ return INVALID_HANDLE_VALUE;
+}
+
+void TPipe::setWrtPipeHandle(HANDLE pipehandle) {
+ if (impl_)
+ impl_->setWrtPipeHandle(pipehandle);
+}
+
+HANDLE TPipe::getNativeWaitHandle() {
+ if (impl_)
+ return impl_->getNativeWaitHandle();
+ return INVALID_HANDLE_VALUE;
+}
+
+long TPipe::getConnTimeout() {
+ return TimeoutSeconds_;
+}
+
+void TPipe::setConnTimeout(long seconds) {
+ TimeoutSeconds_ = seconds;
+}
+
+#endif //_WIN32
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TPipe.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TPipe.h
new file mode 100644
index 000000000..ba149b109
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TPipe.h
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TPIPE_H_
+#define _THRIFT_TRANSPORT_TPIPE_H_ 1
+
+#include <thrift/transport/TTransport.h>
+#include <thrift/transport/TVirtualTransport.h>
+#ifndef _WIN32
+#include <thrift/transport/TSocket.h>
+#endif
+#ifdef _WIN32
+#include <thrift/windows/Sync.h>
+#endif
+#include <boost/noncopyable.hpp>
+#ifdef _WIN32
+#include <thrift/windows/Sync.h>
+#endif
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * Windows Pipes implementation of the TTransport interface.
+ * Don't destroy a TPipe at global scope, as that will cause a thread join
+ * during DLLMain. That also means that client objects using TPipe shouldn't be at global
+ * scope.
+ */
+#ifdef _WIN32
+class TPipeImpl;
+
+class TPipe : public TVirtualTransport<TPipe> {
+public:
+ // Constructs a new pipe object.
+ TPipe();
+ // Named pipe constructors -
+ explicit TPipe(HANDLE Pipe); // HANDLE is a void*
+ explicit TPipe(TAutoHandle& Pipe); // this ctor will clear out / move from Pipe
+ // need a const char * overload so string literals don't go to the HANDLE overload
+ explicit TPipe(const char* pipename);
+ explicit TPipe(const std::string& pipename);
+ // Anonymous pipe -
+ TPipe(HANDLE PipeRd, HANDLE PipeWrt);
+
+ // Destroys the pipe object, closing it if necessary.
+ virtual ~TPipe();
+
+ // Returns whether the pipe is open & valid.
+ bool isOpen() const override;
+
+ // Checks whether more data is available in the pipe.
+ bool peek() override;
+
+ // Creates and opens the named/anonymous pipe.
+ void open() override;
+
+ // Shuts down communications on the pipe.
+ void close() override;
+
+ // Reads from the pipe.
+ virtual uint32_t read(uint8_t* buf, uint32_t len);
+
+ // Writes to the pipe.
+ virtual void write(const uint8_t* buf, uint32_t len);
+
+ // Accessors
+ std::string getPipename();
+ void setPipename(const std::string& pipename);
+ HANDLE getPipeHandle(); // doubles as the read handle for anon pipe
+ void setPipeHandle(HANDLE pipehandle);
+ HANDLE getWrtPipeHandle();
+ void setWrtPipeHandle(HANDLE pipehandle);
+ long getConnTimeout();
+ void setConnTimeout(long seconds);
+
+ // this function is intended to be used in generic / template situations,
+ // so its name needs to be the same as TPipeServer's
+ HANDLE getNativeWaitHandle();
+
+private:
+ std::shared_ptr<TPipeImpl> impl_;
+
+ std::string pipename_;
+
+ long TimeoutSeconds_;
+ bool isAnonymous_;
+};
+
+#else
+typedef TSocket TPipe;
+#endif
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_TPIPE_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TPipeServer.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TPipeServer.cpp
new file mode 100644
index 000000000..47d882251
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TPipeServer.cpp
@@ -0,0 +1,481 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+#include <cstring>
+
+#include <thrift/transport/TPipe.h>
+#include <thrift/transport/TPipeServer.h>
+#include <boost/noncopyable.hpp>
+
+#ifdef _WIN32
+#include <thrift/windows/OverlappedSubmissionThread.h>
+#include <AccCtrl.h>
+#include <Aclapi.h>
+#endif //_WIN32
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+#ifdef _WIN32
+
+using std::shared_ptr;
+
+class TPipeServerImpl : boost::noncopyable {
+public:
+ TPipeServerImpl() {}
+ virtual ~TPipeServerImpl() {}
+ virtual void interrupt() = 0;
+ virtual std::shared_ptr<TTransport> acceptImpl() = 0;
+
+ virtual HANDLE getPipeHandle() = 0;
+ virtual HANDLE getWrtPipeHandle() = 0;
+ virtual HANDLE getClientRdPipeHandle() = 0;
+ virtual HANDLE getClientWrtPipeHandle() = 0;
+ virtual HANDLE getNativeWaitHandle() { return NULL; }
+};
+
+class TAnonPipeServer : public TPipeServerImpl {
+public:
+ TAnonPipeServer() {
+ // The anonymous pipe needs to be created first so that the server can
+ // pass the handles on to the client before the serve (acceptImpl)
+ // blocking call.
+ if (!createAnonPipe()) {
+ GlobalOutput.perror("TPipeServer Create(Anon)Pipe failed, GLE=", GetLastError());
+ throw TTransportException(TTransportException::NOT_OPEN,
+ " TPipeServer Create(Anon)Pipe failed");
+ }
+ }
+
+ virtual ~TAnonPipeServer() {
+ PipeR_.reset();
+ PipeW_.reset();
+ ClientAnonRead_.reset();
+ ClientAnonWrite_.reset();
+ }
+
+ virtual void interrupt() {} // not currently implemented
+
+ virtual std::shared_ptr<TTransport> acceptImpl();
+
+ virtual HANDLE getPipeHandle() { return PipeR_.h; }
+ virtual HANDLE getWrtPipeHandle() { return PipeW_.h; }
+ virtual HANDLE getClientRdPipeHandle() { return ClientAnonRead_.h; }
+ virtual HANDLE getClientWrtPipeHandle() { return ClientAnonWrite_.h; }
+
+private:
+ bool createAnonPipe();
+
+ TAutoHandle PipeR_; // Anonymous Pipe (R)
+ TAutoHandle PipeW_; // Anonymous Pipe (W)
+
+ // Client side anonymous pipe handles
+ //? Do we need duplicates to send to client?
+ TAutoHandle ClientAnonRead_;
+ TAutoHandle ClientAnonWrite_;
+};
+
+class TNamedPipeServer : public TPipeServerImpl {
+public:
+ TNamedPipeServer(const std::string& pipename, uint32_t bufsize, uint32_t maxconnections)
+ : stopping_(false), pipename_(pipename), bufsize_(bufsize), maxconns_(maxconnections)
+ {
+ connectOverlap_.action = TOverlappedWorkItem::CONNECT;
+ cancelOverlap_.action = TOverlappedWorkItem::CANCELIO;
+ TAutoCrit lock(pipe_protect_);
+ initiateNamedConnect(lock);
+ }
+ virtual ~TNamedPipeServer() {}
+
+ virtual void interrupt() {
+ TAutoCrit lock(pipe_protect_);
+ cached_client_.reset();
+ if (Pipe_.h != INVALID_HANDLE_VALUE) {
+ stopping_ = true;
+ cancelOverlap_.h = Pipe_.h;
+ // This should wake up GetOverlappedResult
+ thread_->addWorkItem(&cancelOverlap_);
+ }
+ }
+
+ virtual std::shared_ptr<TTransport> acceptImpl();
+
+ virtual HANDLE getPipeHandle() { return Pipe_.h; }
+ virtual HANDLE getWrtPipeHandle() { return INVALID_HANDLE_VALUE; }
+ virtual HANDLE getClientRdPipeHandle() { return INVALID_HANDLE_VALUE; }
+ virtual HANDLE getClientWrtPipeHandle() { return INVALID_HANDLE_VALUE; }
+ virtual HANDLE getNativeWaitHandle() { return listen_event_.h; }
+
+private:
+ bool createNamedPipe(const TAutoCrit &lockProof);
+ void initiateNamedConnect(const TAutoCrit &lockProof);
+
+ TAutoOverlapThread thread_;
+ TOverlappedWorkItem connectOverlap_;
+ TOverlappedWorkItem cancelOverlap_;
+
+ bool stopping_;
+ std::string pipename_;
+ uint32_t bufsize_;
+ uint32_t maxconns_;
+ TManualResetEvent listen_event_;
+
+ TCriticalSection pipe_protect_;
+ // only read or write these variables underneath a locked pipe_protect_
+ std::shared_ptr<TPipe> cached_client_;
+ TAutoHandle Pipe_;
+};
+
+HANDLE TPipeServer::getNativeWaitHandle() {
+ if (impl_)
+ return impl_->getNativeWaitHandle();
+ return NULL;
+}
+
+//---- Constructors ----
+TPipeServer::TPipeServer(const std::string& pipename, uint32_t bufsize)
+ : bufsize_(bufsize), isAnonymous_(false) {
+ setMaxConnections(TPIPE_SERVER_MAX_CONNS_DEFAULT);
+ setPipename(pipename);
+}
+
+TPipeServer::TPipeServer(const std::string& pipename, uint32_t bufsize, uint32_t maxconnections)
+ : bufsize_(bufsize), isAnonymous_(false) {
+ setMaxConnections(maxconnections);
+ setPipename(pipename);
+}
+
+TPipeServer::TPipeServer(const std::string& pipename) : bufsize_(1024), isAnonymous_(false) {
+ setMaxConnections(TPIPE_SERVER_MAX_CONNS_DEFAULT);
+ setPipename(pipename);
+}
+
+TPipeServer::TPipeServer(int bufsize) : bufsize_(bufsize), isAnonymous_(true) {
+ setMaxConnections(1);
+ impl_.reset(new TAnonPipeServer);
+}
+
+TPipeServer::TPipeServer() : bufsize_(1024), isAnonymous_(true) {
+ setMaxConnections(1);
+ impl_.reset(new TAnonPipeServer);
+}
+
+//---- Destructor ----
+TPipeServer::~TPipeServer() {}
+
+//---------------------------------------------------------
+// Transport callbacks
+//---------------------------------------------------------
+void TPipeServer::listen() {
+ if (isAnonymous_)
+ return;
+ impl_.reset(new TNamedPipeServer(pipename_, bufsize_, maxconns_));
+}
+
+shared_ptr<TTransport> TPipeServer::acceptImpl() {
+ return impl_->acceptImpl();
+}
+
+shared_ptr<TTransport> TAnonPipeServer::acceptImpl() {
+ // This 0-byte read serves merely as a blocking call.
+ byte buf;
+ DWORD br;
+ int fSuccess = ReadFile(PipeR_.h, // pipe handle
+ &buf, // buffer to receive reply
+ 0, // size of buffer
+ &br, // number of bytes read
+ NULL); // not overlapped
+
+ if (!fSuccess && GetLastError() != ERROR_MORE_DATA) {
+ GlobalOutput.perror("TPipeServer unable to initiate pipe comms, GLE=", GetLastError());
+ throw TTransportException(TTransportException::NOT_OPEN,
+ " TPipeServer unable to initiate pipe comms");
+ }
+ shared_ptr<TPipe> client(new TPipe(PipeR_.h, PipeW_.h));
+ return client;
+}
+
+void TNamedPipeServer::initiateNamedConnect(const TAutoCrit &lockProof) {
+ if (stopping_)
+ return;
+ if (!createNamedPipe(lockProof)) {
+ GlobalOutput.perror("TPipeServer CreateNamedPipe failed, GLE=", GetLastError());
+ throw TTransportException(TTransportException::NOT_OPEN, " TPipeServer CreateNamedPipe failed");
+ }
+
+ // The prior connection has been handled, so close the gate
+ ResetEvent(listen_event_.h);
+ connectOverlap_.reset(NULL, 0, listen_event_.h);
+ connectOverlap_.h = Pipe_.h;
+ thread_->addWorkItem(&connectOverlap_);
+
+ // Wait for the client to connect; if it succeeds, the
+ // function returns a nonzero value. If the function returns
+ // zero, GetLastError should return ERROR_PIPE_CONNECTED.
+ if (connectOverlap_.success) {
+ GlobalOutput.printf("Client connected.");
+ cached_client_.reset(new TPipe(Pipe_));
+ // make sure people know that a connection is ready
+ SetEvent(listen_event_.h);
+ return;
+ }
+
+ DWORD dwErr = connectOverlap_.last_error;
+ switch (dwErr) {
+ case ERROR_PIPE_CONNECTED:
+ GlobalOutput.printf("Client connected.");
+ cached_client_.reset(new TPipe(Pipe_));
+ // make sure people know that a connection is ready
+ SetEvent(listen_event_.h);
+ return;
+ case ERROR_IO_PENDING:
+ return; // acceptImpl will do the appropriate WaitForMultipleObjects
+ default:
+ GlobalOutput.perror("TPipeServer ConnectNamedPipe failed, GLE=", dwErr);
+ throw TTransportException(TTransportException::NOT_OPEN,
+ " TPipeServer ConnectNamedPipe failed");
+ }
+}
+
+shared_ptr<TTransport> TNamedPipeServer::acceptImpl() {
+ {
+ TAutoCrit lock(pipe_protect_);
+ if (cached_client_.get() != NULL) {
+ shared_ptr<TPipe> client;
+ // zero out cached_client, since we are about to return it.
+ client.swap(cached_client_);
+
+ // kick off the next connection before returning
+ initiateNamedConnect(lock);
+ return client; // success!
+ }
+ }
+
+ if (Pipe_.h == INVALID_HANDLE_VALUE) {
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "TNamedPipeServer: someone called accept on a closed pipe server");
+ }
+
+ DWORD dwDummy = 0;
+
+ // For the most part, Pipe_ should be protected with pipe_protect_. We can't
+ // reasonably do that here though without breaking interruptability. However,
+ // this should be safe, though I'm not happy about it. We only need to ensure
+ // that no one writes / modifies Pipe_.h while we are reading it. Well, the
+ // only two things that should be modifying Pipe_ are acceptImpl, the
+ // functions it calls, and the destructor. Those things shouldn't be run
+ // concurrently anyway. So this call is 'really' just a read that may happen
+ // concurrently with interrupt, and that should be fine.
+ if (GetOverlappedResult(Pipe_.h, &connectOverlap_.overlap, &dwDummy, TRUE)) {
+ TAutoCrit lock(pipe_protect_);
+ GlobalOutput.printf("Client connected.");
+ shared_ptr<TPipe> client(new TPipe(Pipe_));
+ // kick off the next connection before returning
+ initiateNamedConnect(lock);
+ return client; // success!
+ }
+ // if we got here, then we are in an error / shutdown case
+ DWORD gle = GetLastError(); // save error before doing cleanup
+ GlobalOutput.perror("TPipeServer ConnectNamedPipe GLE=", gle);
+ if(gle == ERROR_OPERATION_ABORTED) {
+ TAutoCrit lock(pipe_protect_); // Needed to insure concurrent thread to be out of interrupt.
+ throw TTransportException(TTransportException::INTERRUPTED, "TPipeServer: server interupted");
+ }
+ throw TTransportException(TTransportException::NOT_OPEN, "TPipeServer: client connection failed");
+}
+
+void TPipeServer::interrupt() {
+ if (impl_)
+ impl_->interrupt();
+}
+
+void TPipeServer::close() {
+ impl_.reset();
+}
+
+bool TNamedPipeServer::createNamedPipe(const TAutoCrit & /*lockProof*/) {
+
+ // Windows - set security to allow non-elevated apps
+ // to access pipes created by elevated apps.
+ SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
+ PSID everyone_sid = NULL;
+ AllocateAndInitializeSid(
+ &SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &everyone_sid);
+
+ EXPLICIT_ACCESS ea;
+ ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
+ ea.grfAccessPermissions = SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL;
+ ea.grfAccessMode = SET_ACCESS;
+ ea.grfInheritance = NO_INHERITANCE;
+ ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
+ ea.Trustee.ptstrName = static_cast<LPTSTR>(everyone_sid);
+
+ PACL acl = NULL;
+ SetEntriesInAcl(1, &ea, NULL, &acl);
+
+ PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
+ if (!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) {
+ auto lastError = GetLastError();
+ LocalFree(sd);
+ LocalFree(acl);
+ GlobalOutput.perror("TPipeServer::InitializeSecurityDescriptor() GLE=", lastError);
+ throw TTransportException(TTransportException::NOT_OPEN, "InitializeSecurityDescriptor() failed",
+ lastError);
+ }
+ if (!SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE)) {
+ auto lastError = GetLastError();
+ LocalFree(sd);
+ LocalFree(acl);
+ GlobalOutput.perror("TPipeServer::SetSecurityDescriptorDacl() GLE=", lastError);
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "SetSecurityDescriptorDacl() failed", lastError);
+ }
+
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor = sd;
+ sa.bInheritHandle = FALSE;
+
+ // Create an instance of the named pipe
+ TAutoHandle hPipe(CreateNamedPipeA(pipename_.c_str(), // pipe name
+ PIPE_ACCESS_DUPLEX | // read/write access
+ FILE_FLAG_OVERLAPPED, // async mode
+ PIPE_TYPE_BYTE | // byte type pipe
+ PIPE_READMODE_BYTE, // byte read mode
+ maxconns_, // max. instances
+ bufsize_, // output buffer size
+ bufsize_, // input buffer size
+ 0, // client time-out
+ &sa)); // security attributes
+
+ auto lastError = GetLastError();
+ LocalFree(sd);
+ LocalFree(acl);
+ FreeSid(everyone_sid);
+
+ if (hPipe.h == INVALID_HANDLE_VALUE) {
+ Pipe_.reset();
+ GlobalOutput.perror("TPipeServer::TCreateNamedPipe() GLE=", lastError);
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "TCreateNamedPipe() failed",
+ lastError);
+ }
+
+ Pipe_.reset(hPipe.release());
+ return true;
+}
+
+bool TAnonPipeServer::createAnonPipe() {
+ SECURITY_ATTRIBUTES sa;
+ SECURITY_DESCRIPTOR sd; // security information for pipes
+
+ if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
+ {
+ GlobalOutput.perror("TPipeServer InitializeSecurityDescriptor (anon) failed, GLE=", GetLastError());
+ return false;
+ }
+ if (!SetSecurityDescriptorDacl(&sd, true, NULL, false))
+ {
+ GlobalOutput.perror("TPipeServer SetSecurityDescriptorDacl (anon) failed, GLE=",
+ GetLastError());
+ return false;
+ }
+ sa.lpSecurityDescriptor = &sd;
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = true; // allow passing handle to child
+
+ HANDLE ClientAnonReadH, PipeW_H, ClientAnonWriteH, Pipe_H;
+ if (!CreatePipe(&ClientAnonReadH, &PipeW_H, &sa, 0)) // create stdin pipe
+ {
+ GlobalOutput.perror("TPipeServer CreatePipe (anon) failed, GLE=", GetLastError());
+ return false;
+ }
+ if (!CreatePipe(&Pipe_H, &ClientAnonWriteH, &sa, 0)) // create stdout pipe
+ {
+ GlobalOutput.perror("TPipeServer CreatePipe (anon) failed, GLE=", GetLastError());
+ CloseHandle(ClientAnonReadH);
+ CloseHandle(PipeW_H);
+ return false;
+ }
+
+ ClientAnonRead_.reset(ClientAnonReadH);
+ ClientAnonWrite_.reset(ClientAnonWriteH);
+ PipeR_.reset(Pipe_H);
+ PipeW_.reset(PipeW_H);
+
+ return true;
+}
+
+//---------------------------------------------------------
+// Accessors
+//---------------------------------------------------------
+std::string TPipeServer::getPipename() {
+ return pipename_;
+}
+
+void TPipeServer::setPipename(const std::string& pipename) {
+ if (pipename.find("\\\\") == std::string::npos)
+ pipename_ = "\\\\.\\pipe\\" + pipename;
+ else
+ pipename_ = pipename;
+}
+
+int TPipeServer::getBufferSize() {
+ return bufsize_;
+}
+void TPipeServer::setBufferSize(int bufsize) {
+ bufsize_ = bufsize;
+}
+
+HANDLE TPipeServer::getPipeHandle() {
+ return impl_ ? impl_->getPipeHandle() : INVALID_HANDLE_VALUE;
+}
+HANDLE TPipeServer::getWrtPipeHandle() {
+ return impl_ ? impl_->getWrtPipeHandle() : INVALID_HANDLE_VALUE;
+}
+HANDLE TPipeServer::getClientRdPipeHandle() {
+ return impl_ ? impl_->getClientRdPipeHandle() : INVALID_HANDLE_VALUE;
+}
+HANDLE TPipeServer::getClientWrtPipeHandle() {
+ return impl_ ? impl_->getClientWrtPipeHandle() : INVALID_HANDLE_VALUE;
+}
+
+bool TPipeServer::getAnonymous() {
+ return isAnonymous_;
+}
+void TPipeServer::setAnonymous(bool anon) {
+ isAnonymous_ = anon;
+}
+
+void TPipeServer::setMaxConnections(uint32_t maxconnections) {
+ if (maxconnections == 0)
+ maxconns_ = 1;
+ else if (maxconnections > PIPE_UNLIMITED_INSTANCES)
+ maxconns_ = PIPE_UNLIMITED_INSTANCES;
+ else
+ maxconns_ = maxconnections;
+}
+
+#endif //_WIN32
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TPipeServer.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TPipeServer.h
new file mode 100644
index 000000000..871b6afab
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TPipeServer.h
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TSERVERWINPIPES_H_
+#define _THRIFT_TRANSPORT_TSERVERWINPIPES_H_ 1
+
+#include <memory>
+#include <thrift/transport/TServerTransport.h>
+#ifndef _WIN32
+#include <thrift/transport/TServerSocket.h>
+#endif
+#ifdef _WIN32
+#include <thrift/windows/Sync.h>
+#endif
+
+#define TPIPE_SERVER_MAX_CONNS_DEFAULT PIPE_UNLIMITED_INSTANCES
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * Windows Pipes implementation of TServerTransport.
+ * Don't destroy a TPipeServer at global scope, as that will cause a thread join
+ * during DLLMain. That also means that TServer's using TPipeServer shouldn't be at global
+ * scope.
+ */
+#ifdef _WIN32
+class TPipeServerImpl;
+class TPipe;
+
+class TPipeServer : public TServerTransport {
+public:
+ // Constructors
+ // Named Pipe -
+ TPipeServer(const std::string& pipename, uint32_t bufsize);
+ TPipeServer(const std::string& pipename, uint32_t bufsize, uint32_t maxconnections);
+ TPipeServer(const std::string& pipename);
+ // Anonymous pipe -
+ TPipeServer(int bufsize);
+ TPipeServer();
+
+ // Destructor
+ virtual ~TPipeServer();
+
+ // Standard transport callbacks
+ void interrupt() override;
+ void close() override;
+ void listen() override;
+
+ // Accessors
+ std::string getPipename();
+ void setPipename(const std::string& pipename);
+ int getBufferSize();
+ void setBufferSize(int bufsize);
+ HANDLE getPipeHandle(); // Named Pipe R/W -or- Anonymous pipe Read handle
+ HANDLE getWrtPipeHandle();
+ HANDLE getClientRdPipeHandle();
+ HANDLE getClientWrtPipeHandle();
+ bool getAnonymous();
+ void setAnonymous(bool anon);
+ void setMaxConnections(uint32_t maxconnections);
+
+ // this function is intended to be used in generic / template situations,
+ // so its name needs to be the same as TPipe's
+ HANDLE getNativeWaitHandle();
+
+protected:
+ virtual std::shared_ptr<TTransport> acceptImpl();
+
+private:
+ std::shared_ptr<TPipeServerImpl> impl_;
+
+ std::string pipename_;
+ uint32_t bufsize_;
+ uint32_t maxconns_;
+ bool isAnonymous_;
+};
+#else //_WIN32
+//*NIX named pipe implementation uses domain socket
+typedef TServerSocket TPipeServer;
+#endif
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_TSERVERWINPIPES_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSSLServerSocket.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSSLServerSocket.cpp
new file mode 100644
index 000000000..b20c17408
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSSLServerSocket.cpp
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift_export.h>
+#include <thrift/transport/TSSLServerSocket.h>
+#include <thrift/transport/TSSLSocket.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * SSL server socket implementation.
+ */
+TSSLServerSocket::TSSLServerSocket(int port, std::shared_ptr<TSSLSocketFactory> factory)
+ : TServerSocket(port), factory_(factory) {
+ factory_->server(true);
+}
+
+TSSLServerSocket::TSSLServerSocket(const std::string& address,
+ int port,
+ std::shared_ptr<TSSLSocketFactory> factory)
+ : TServerSocket(address, port), factory_(factory) {
+ factory_->server(true);
+}
+
+TSSLServerSocket::TSSLServerSocket(int port,
+ int sendTimeout,
+ int recvTimeout,
+ std::shared_ptr<TSSLSocketFactory> factory)
+ : TServerSocket(port, sendTimeout, recvTimeout), factory_(factory) {
+ factory_->server(true);
+}
+
+std::shared_ptr<TSocket> TSSLServerSocket::createSocket(THRIFT_SOCKET client) {
+ if (interruptableChildren_) {
+ return factory_->createSocket(client, pChildInterruptSockReader_);
+
+ } else {
+ return factory_->createSocket(client);
+ }
+}
+}
+}
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSSLServerSocket.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSSLServerSocket.h
new file mode 100644
index 000000000..44df43276
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSSLServerSocket.h
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TSSLSERVERSOCKET_H_
+#define _THRIFT_TRANSPORT_TSSLSERVERSOCKET_H_ 1
+
+#include <thrift/transport/TServerSocket.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+class TSSLSocketFactory;
+
+/**
+ * Server socket that accepts SSL connections.
+ */
+class TSSLServerSocket : public TServerSocket {
+public:
+ /**
+ * Constructor. Binds to all interfaces.
+ *
+ * @param port Listening port
+ * @param factory SSL socket factory implementation
+ */
+ TSSLServerSocket(int port, std::shared_ptr<TSSLSocketFactory> factory);
+
+ /**
+ * Constructor. Binds to the specified address.
+ *
+ * @param address Address to bind to
+ * @param port Listening port
+ * @param factory SSL socket factory implementation
+ */
+ TSSLServerSocket(const std::string& address,
+ int port,
+ std::shared_ptr<TSSLSocketFactory> factory);
+
+ /**
+ * Constructor. Binds to all interfaces.
+ *
+ * @param port Listening port
+ * @param sendTimeout Socket send timeout
+ * @param recvTimeout Socket receive timeout
+ * @param factory SSL socket factory implementation
+ */
+ TSSLServerSocket(int port,
+ int sendTimeout,
+ int recvTimeout,
+ std::shared_ptr<TSSLSocketFactory> factory);
+
+protected:
+ std::shared_ptr<TSocket> createSocket(THRIFT_SOCKET socket) override;
+ std::shared_ptr<TSSLSocketFactory> factory_;
+};
+}
+}
+}
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSSLSocket.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSSLSocket.cpp
new file mode 100644
index 000000000..b413002f1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSSLSocket.cpp
@@ -0,0 +1,1120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+
+#include <cstring>
+#include <errno.h>
+#include <memory>
+#include <string>
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#define OPENSSL_VERSION_NO_THREAD_ID_BEFORE 0x10000000L
+#define OPENSSL_ENGINE_CLEANUP_REQUIRED_BEFORE 0x10100000L
+
+#include <boost/shared_array.hpp>
+#include <openssl/opensslv.h>
+#if (OPENSSL_VERSION_NUMBER < OPENSSL_ENGINE_CLEANUP_REQUIRED_BEFORE)
+#include <openssl/engine.h>
+#endif
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+#include <thrift/concurrency/Mutex.h>
+#include <thrift/transport/TSSLSocket.h>
+#include <thrift/transport/PlatformSocket.h>
+#include <thrift/TToString.h>
+
+using namespace apache::thrift::concurrency;
+using std::string;
+
+struct CRYPTO_dynlock_value {
+ Mutex mutex;
+};
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+// OpenSSL initialization/cleanup
+
+static bool openSSLInitialized = false;
+static boost::shared_array<Mutex> mutexes;
+
+static void callbackLocking(int mode, int n, const char*, int) {
+ if (mode & CRYPTO_LOCK) {
+ // assertion of (px != 0) here typically means that a TSSLSocket's lifetime
+ // exceeded the lifetime of the TSSLSocketFactory that created it, and the
+ // TSSLSocketFactory already ran cleanupOpenSSL(), which deleted "mutexes".
+ mutexes[n].lock();
+ } else {
+ mutexes[n].unlock();
+ }
+}
+
+#if (OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_NO_THREAD_ID_BEFORE)
+static unsigned long callbackThreadID() {
+#ifdef _WIN32
+ return (unsigned long)GetCurrentThreadId();
+#else
+ return (unsigned long)pthread_self();
+#endif
+}
+#endif
+
+static CRYPTO_dynlock_value* dyn_create(const char*, int) {
+ return new CRYPTO_dynlock_value;
+}
+
+static void dyn_lock(int mode, struct CRYPTO_dynlock_value* lock, const char*, int) {
+ if (lock != nullptr) {
+ if (mode & CRYPTO_LOCK) {
+ lock->mutex.lock();
+ } else {
+ lock->mutex.unlock();
+ }
+ }
+}
+
+static void dyn_destroy(struct CRYPTO_dynlock_value* lock, const char*, int) {
+ delete lock;
+}
+
+void initializeOpenSSL() {
+ if (openSSLInitialized) {
+ return;
+ }
+ openSSLInitialized = true;
+ SSL_library_init();
+ SSL_load_error_strings();
+ ERR_load_crypto_strings();
+
+ // static locking
+ // newer versions of OpenSSL changed CRYPTO_num_locks - see THRIFT-3878
+#ifdef CRYPTO_num_locks
+ mutexes = boost::shared_array<Mutex>(new Mutex[CRYPTO_num_locks()]);
+#else
+ mutexes = boost::shared_array<Mutex>(new Mutex[ ::CRYPTO_num_locks()]);
+#endif
+
+#if (OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_NO_THREAD_ID_BEFORE)
+ CRYPTO_set_id_callback(callbackThreadID);
+#endif
+
+ CRYPTO_set_locking_callback(callbackLocking);
+
+ // dynamic locking
+ CRYPTO_set_dynlock_create_callback(dyn_create);
+ CRYPTO_set_dynlock_lock_callback(dyn_lock);
+ CRYPTO_set_dynlock_destroy_callback(dyn_destroy);
+}
+
+void cleanupOpenSSL() {
+ if (!openSSLInitialized) {
+ return;
+ }
+ openSSLInitialized = false;
+
+ // https://wiki.openssl.org/index.php/Library_Initialization#Cleanup
+ // we purposefully do NOT call FIPS_mode_set(0) and leave it up to the enclosing application to manage FIPS entirely
+#if (OPENSSL_VERSION_NUMBER < OPENSSL_ENGINE_CLEANUP_REQUIRED_BEFORE)
+ ENGINE_cleanup(); // https://www.openssl.org/docs/man1.1.0/crypto/ENGINE_cleanup.html - cleanup call is needed before 1.1.0
+#endif
+ CONF_modules_unload(1);
+ EVP_cleanup();
+ CRYPTO_cleanup_all_ex_data();
+ ERR_remove_state(0);
+ ERR_free_strings();
+
+ mutexes.reset();
+}
+
+static void buildErrors(string& message, int errno_copy = 0, int sslerrno = 0);
+static bool matchName(const char* host, const char* pattern, int size);
+static char uppercase(char c);
+
+// SSLContext implementation
+SSLContext::SSLContext(const SSLProtocol& protocol) {
+ if (protocol == SSLTLS) {
+ ctx_ = SSL_CTX_new(SSLv23_method());
+#ifndef OPENSSL_NO_SSL3
+ } else if (protocol == SSLv3) {
+ ctx_ = SSL_CTX_new(SSLv3_method());
+#endif
+ } else if (protocol == TLSv1_0) {
+ ctx_ = SSL_CTX_new(TLSv1_method());
+ } else if (protocol == TLSv1_1) {
+ ctx_ = SSL_CTX_new(TLSv1_1_method());
+ } else if (protocol == TLSv1_2) {
+ ctx_ = SSL_CTX_new(TLSv1_2_method());
+ } else {
+ /// UNKNOWN PROTOCOL!
+ throw TSSLException("SSL_CTX_new: Unknown protocol");
+ }
+
+ if (ctx_ == nullptr) {
+ string errors;
+ buildErrors(errors);
+ throw TSSLException("SSL_CTX_new: " + errors);
+ }
+ SSL_CTX_set_mode(ctx_, SSL_MODE_AUTO_RETRY);
+
+ // Disable horribly insecure SSLv2 and SSLv3 protocols but allow a handshake
+ // with older clients so they get a graceful denial.
+ if (protocol == SSLTLS) {
+ SSL_CTX_set_options(ctx_, SSL_OP_NO_SSLv2);
+ SSL_CTX_set_options(ctx_, SSL_OP_NO_SSLv3); // THRIFT-3164
+ }
+}
+
+SSLContext::~SSLContext() {
+ if (ctx_ != nullptr) {
+ SSL_CTX_free(ctx_);
+ ctx_ = nullptr;
+ }
+}
+
+SSL* SSLContext::createSSL() {
+ SSL* ssl = SSL_new(ctx_);
+ if (ssl == nullptr) {
+ string errors;
+ buildErrors(errors);
+ throw TSSLException("SSL_new: " + errors);
+ }
+ return ssl;
+}
+
+// TSSLSocket implementation
+TSSLSocket::TSSLSocket(std::shared_ptr<SSLContext> ctx)
+ : TSocket(), server_(false), ssl_(nullptr), ctx_(ctx) {
+ init();
+}
+
+TSSLSocket::TSSLSocket(std::shared_ptr<SSLContext> ctx, std::shared_ptr<THRIFT_SOCKET> interruptListener)
+ : TSocket(), server_(false), ssl_(nullptr), ctx_(ctx) {
+ init();
+ interruptListener_ = interruptListener;
+}
+
+TSSLSocket::TSSLSocket(std::shared_ptr<SSLContext> ctx, THRIFT_SOCKET socket)
+ : TSocket(socket), server_(false), ssl_(nullptr), ctx_(ctx) {
+ init();
+}
+
+TSSLSocket::TSSLSocket(std::shared_ptr<SSLContext> ctx, THRIFT_SOCKET socket, std::shared_ptr<THRIFT_SOCKET> interruptListener)
+ : TSocket(socket, interruptListener), server_(false), ssl_(nullptr), ctx_(ctx) {
+ init();
+}
+
+TSSLSocket::TSSLSocket(std::shared_ptr<SSLContext> ctx, string host, int port)
+ : TSocket(host, port), server_(false), ssl_(nullptr), ctx_(ctx) {
+ init();
+}
+
+TSSLSocket::TSSLSocket(std::shared_ptr<SSLContext> ctx, string host, int port, std::shared_ptr<THRIFT_SOCKET> interruptListener)
+ : TSocket(host, port), server_(false), ssl_(nullptr), ctx_(ctx) {
+ init();
+ interruptListener_ = interruptListener;
+}
+
+TSSLSocket::~TSSLSocket() {
+ close();
+}
+
+bool TSSLSocket::hasPendingDataToRead() {
+ if (!isOpen()) {
+ return false;
+ }
+ initializeHandshake();
+ if (!checkHandshake())
+ throw TSSLException("TSSLSocket::hasPendingDataToRead: Handshake is not completed");
+ // data may be available in SSL buffers (note: SSL_pending does not have a failure mode)
+ return SSL_pending(ssl_) > 0 || TSocket::hasPendingDataToRead();
+}
+
+void TSSLSocket::init() {
+ handshakeCompleted_ = false;
+ readRetryCount_ = 0;
+ eventSafe_ = false;
+}
+
+bool TSSLSocket::isOpen() const {
+ if (ssl_ == nullptr || !TSocket::isOpen()) {
+ return false;
+ }
+ int shutdown = SSL_get_shutdown(ssl_);
+ // "!!" is squelching C4800 "forcing bool -> true or false" performance warning
+ bool shutdownReceived = !!(shutdown & SSL_RECEIVED_SHUTDOWN);
+ bool shutdownSent = !!(shutdown & SSL_SENT_SHUTDOWN);
+ if (shutdownReceived && shutdownSent) {
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Note: This method is not libevent safe.
+*/
+bool TSSLSocket::peek() {
+ if (!isOpen()) {
+ return false;
+ }
+ initializeHandshake();
+ if (!checkHandshake())
+ throw TSSLException("SSL_peek: Handshake is not completed");
+ int rc;
+ do {
+ uint8_t byte;
+ rc = SSL_peek(ssl_, &byte, 1);
+ if (rc < 0) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ int error = SSL_get_error(ssl_, rc);
+ switch (error) {
+ case SSL_ERROR_SYSCALL:
+ if ((errno_copy != THRIFT_EINTR)
+ && (errno_copy != THRIFT_EAGAIN)) {
+ break;
+ }
+ // fallthrough
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ // in the case of SSL_ERROR_SYSCALL we want to wait for an read event again
+ waitForEvent(error != SSL_ERROR_WANT_WRITE);
+ continue;
+ default:;// do nothing
+ }
+ string errors;
+ buildErrors(errors, errno_copy, error);
+ throw TSSLException("SSL_peek: " + errors);
+ } else if (rc == 0) {
+ ERR_clear_error();
+ break;
+ } else {
+ break;
+ }
+ } while (true);
+ return (rc > 0);
+}
+
+void TSSLSocket::open() {
+ if (isOpen() || server()) {
+ throw TTransportException(TTransportException::BAD_ARGS);
+ }
+ TSocket::open();
+}
+
+/*
+ * Note: This method is not libevent safe.
+*/
+void TSSLSocket::close() {
+ if (ssl_ != nullptr) {
+ try {
+ int rc;
+ int errno_copy = 0;
+ int error = 0;
+
+ do {
+ rc = SSL_shutdown(ssl_);
+ if (rc <= 0) {
+ errno_copy = THRIFT_GET_SOCKET_ERROR;
+ error = SSL_get_error(ssl_, rc);
+ switch (error) {
+ case SSL_ERROR_SYSCALL:
+ if ((errno_copy != THRIFT_EINTR)
+ && (errno_copy != THRIFT_EAGAIN)) {
+ break;
+ }
+ // fallthrough
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ // in the case of SSL_ERROR_SYSCALL we want to wait for an write/read event again
+ waitForEvent(error == SSL_ERROR_WANT_READ);
+ rc = 2;
+ default:;// do nothing
+ }
+ }
+ } while (rc == 2);
+
+ if (rc < 0) {
+ string errors;
+ buildErrors(errors, errno_copy, error);
+ GlobalOutput(("SSL_shutdown: " + errors).c_str());
+ }
+ } catch (TTransportException& te) {
+ // Don't emit an exception because this method is called by the
+ // destructor. There's also not much that a user can do to recover, so
+ // just clean up as much as possible without throwing, similar to the rc
+ // < 0 case above.
+ GlobalOutput.printf("SSL_shutdown: %s", te.what());
+ }
+ SSL_free(ssl_);
+ ssl_ = nullptr;
+ handshakeCompleted_ = false;
+ ERR_remove_state(0);
+ }
+ TSocket::close();
+}
+
+/*
+ * Returns number of bytes read in SSL Socket.
+ * If eventSafe is set, and it may returns 0 bytes then read method
+ * needs to be called again until it is successfull or it throws
+ * exception incase of failure.
+*/
+uint32_t TSSLSocket::read(uint8_t* buf, uint32_t len) {
+ initializeHandshake();
+ if (!checkHandshake())
+ throw TTransportException(TTransportException::UNKNOWN, "retry again");
+ int32_t bytes = 0;
+ while (readRetryCount_ < maxRecvRetries_) {
+ bytes = SSL_read(ssl_, buf, len);
+ int32_t errno_copy = THRIFT_GET_SOCKET_ERROR;
+ int32_t error = SSL_get_error(ssl_, bytes);
+ readRetryCount_++;
+ if (error == SSL_ERROR_NONE) {
+ readRetryCount_ = 0;
+ break;
+ }
+ unsigned int waitEventReturn;
+ bool breakout = false;
+ switch (error) {
+ case SSL_ERROR_ZERO_RETURN:
+ throw TTransportException(TTransportException::END_OF_FILE, "client disconnected");
+
+ case SSL_ERROR_SYSCALL:
+ if (errno_copy == 0 && ERR_peek_error() == 0) {
+ breakout = true;
+ break;
+ }
+ if ((errno_copy != THRIFT_EINTR)
+ && (errno_copy != THRIFT_EAGAIN)) {
+ break;
+ }
+ if (readRetryCount_ >= maxRecvRetries_) {
+ // THRIFT_EINTR needs to be handled manually and we can tolerate
+ // a certain number
+ break;
+ }
+ // fallthrough
+
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ if (isLibeventSafe()) {
+ if (readRetryCount_ < maxRecvRetries_) {
+ // THRIFT_EINTR needs to be handled manually and we can tolerate
+ // a certain number
+ throw TTransportException(TTransportException::UNKNOWN, "retry again");
+ }
+ throw TTransportException(TTransportException::INTERNAL_ERROR, "too much recv retries");
+ }
+ // in the case of SSL_ERROR_SYSCALL we want to wait for an read event again
+ else if ((waitEventReturn = waitForEvent(error != SSL_ERROR_WANT_WRITE)) == TSSL_EINTR ) {
+ // repeat operation
+ if (readRetryCount_ < maxRecvRetries_) {
+ // THRIFT_EINTR needs to be handled manually and we can tolerate
+ // a certain number
+ continue;
+ }
+ throw TTransportException(TTransportException::INTERNAL_ERROR, "too much recv retries");
+ }
+ else if (waitEventReturn == TSSL_DATA) {
+ // in case of SSL and huge thrift packets, there may be a number of
+ // socket operations, before any data becomes available by SSL_read().
+ // Therefore the number of retries should not be increased and
+ // the operation should be repeated.
+ readRetryCount_--;
+ continue;
+ }
+ throw TTransportException(TTransportException::INTERNAL_ERROR, "unkown waitForEvent return value");
+ default:;// do nothing
+ }
+ if (breakout) {
+ break;
+ }
+ string errors;
+ buildErrors(errors, errno_copy, error);
+ throw TSSLException("SSL_read: " + errors);
+ }
+ return bytes;
+}
+
+void TSSLSocket::write(const uint8_t* buf, uint32_t len) {
+ initializeHandshake();
+ if (!checkHandshake())
+ return;
+ // loop in case SSL_MODE_ENABLE_PARTIAL_WRITE is set in SSL_CTX.
+ uint32_t written = 0;
+ while (written < len) {
+ ERR_clear_error();
+ int32_t bytes = SSL_write(ssl_, &buf[written], len - written);
+ if (bytes <= 0) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ int error = SSL_get_error(ssl_, bytes);
+ switch (error) {
+ case SSL_ERROR_SYSCALL:
+ if ((errno_copy != THRIFT_EINTR)
+ && (errno_copy != THRIFT_EAGAIN)) {
+ break;
+ }
+ // fallthrough
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ if (isLibeventSafe()) {
+ return;
+ }
+ else {
+ // in the case of SSL_ERROR_SYSCALL we want to wait for an write event again
+ waitForEvent(error == SSL_ERROR_WANT_READ);
+ continue;
+ }
+ default:;// do nothing
+ }
+ string errors;
+ buildErrors(errors, errno_copy, error);
+ throw TSSLException("SSL_write: " + errors);
+ }
+ written += bytes;
+ }
+}
+
+/*
+ * Returns number of bytes written in SSL Socket.
+ * If eventSafe is set, and it may returns 0 bytes then write method
+ * needs to be called again until it is successfull or it throws
+ * exception incase of failure.
+*/
+uint32_t TSSLSocket::write_partial(const uint8_t* buf, uint32_t len) {
+ initializeHandshake();
+ if (!checkHandshake())
+ return 0;
+ // loop in case SSL_MODE_ENABLE_PARTIAL_WRITE is set in SSL_CTX.
+ uint32_t written = 0;
+ while (written < len) {
+ ERR_clear_error();
+ int32_t bytes = SSL_write(ssl_, &buf[written], len - written);
+ if (bytes <= 0) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ int error = SSL_get_error(ssl_, bytes);
+ switch (error) {
+ case SSL_ERROR_SYSCALL:
+ if ((errno_copy != THRIFT_EINTR)
+ && (errno_copy != THRIFT_EAGAIN)) {
+ break;
+ }
+ // fallthrough
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ if (isLibeventSafe()) {
+ return 0;
+ }
+ else {
+ // in the case of SSL_ERROR_SYSCALL we want to wait for an write event again
+ waitForEvent(error == SSL_ERROR_WANT_READ);
+ continue;
+ }
+ default:;// do nothing
+ }
+ string errors;
+ buildErrors(errors, errno_copy, error);
+ throw TSSLException("SSL_write: " + errors);
+ }
+ written += bytes;
+ }
+ return written;
+}
+
+void TSSLSocket::flush() {
+ // Don't throw exception if not open. Thrift servers close socket twice.
+ if (ssl_ == nullptr) {
+ return;
+ }
+ initializeHandshake();
+ if (!checkHandshake())
+ throw TSSLException("BIO_flush: Handshake is not completed");
+ BIO* bio = SSL_get_wbio(ssl_);
+ if (bio == nullptr) {
+ throw TSSLException("SSL_get_wbio returns NULL");
+ }
+ if (BIO_flush(bio) != 1) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ string errors;
+ buildErrors(errors, errno_copy);
+ throw TSSLException("BIO_flush: " + errors);
+ }
+}
+
+void TSSLSocket::initializeHandshakeParams() {
+ // set underlying socket to non-blocking
+ int flags;
+ if ((flags = THRIFT_FCNTL(socket_, THRIFT_F_GETFL, 0)) < 0
+ || THRIFT_FCNTL(socket_, THRIFT_F_SETFL, flags | THRIFT_O_NONBLOCK) < 0) {
+ GlobalOutput.perror("thriftServerEventHandler: set THRIFT_O_NONBLOCK (THRIFT_FCNTL) ",
+ THRIFT_GET_SOCKET_ERROR);
+ ::THRIFT_CLOSESOCKET(socket_);
+ return;
+ }
+ ssl_ = ctx_->createSSL();
+
+ SSL_set_fd(ssl_, static_cast<int>(socket_));
+}
+
+bool TSSLSocket::checkHandshake() {
+ return handshakeCompleted_;
+}
+
+void TSSLSocket::initializeHandshake() {
+ if (!TSocket::isOpen()) {
+ throw TTransportException(TTransportException::NOT_OPEN);
+ }
+ if (checkHandshake()) {
+ return;
+ }
+
+ if (ssl_ == nullptr) {
+ initializeHandshakeParams();
+ }
+
+ int rc;
+ int errno_copy = 0;
+ int error = 0;
+ if (server()) {
+ do {
+ rc = SSL_accept(ssl_);
+ if (rc <= 0) {
+ errno_copy = THRIFT_GET_SOCKET_ERROR;
+ error = SSL_get_error(ssl_, rc);
+ switch (error) {
+ case SSL_ERROR_SYSCALL:
+ if ((errno_copy != THRIFT_EINTR)
+ && (errno_copy != THRIFT_EAGAIN)) {
+ break;
+ }
+ // fallthrough
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ if (isLibeventSafe()) {
+ return;
+ }
+ else {
+ // repeat operation
+ // in the case of SSL_ERROR_SYSCALL we want to wait for an write/read event again
+ waitForEvent(error == SSL_ERROR_WANT_READ);
+ rc = 2;
+ }
+ default:;// do nothing
+ }
+ }
+ } while (rc == 2);
+ } else {
+ // OpenSSL < 0.9.8f does not have SSL_set_tlsext_host_name()
+ #if defined(SSL_set_tlsext_host_name)
+ // set the SNI hostname
+ SSL_set_tlsext_host_name(ssl_, getHost().c_str());
+ #endif
+ do {
+ rc = SSL_connect(ssl_);
+ if (rc <= 0) {
+ errno_copy = THRIFT_GET_SOCKET_ERROR;
+ error = SSL_get_error(ssl_, rc);
+ switch (error) {
+ case SSL_ERROR_SYSCALL:
+ if ((errno_copy != THRIFT_EINTR)
+ && (errno_copy != THRIFT_EAGAIN)) {
+ break;
+ }
+ // fallthrough
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ if (isLibeventSafe()) {
+ return;
+ }
+ else {
+ // repeat operation
+ // in the case of SSL_ERROR_SYSCALL we want to wait for an write/read event again
+ waitForEvent(error == SSL_ERROR_WANT_READ);
+ rc = 2;
+ }
+ default:;// do nothing
+ }
+ }
+ } while (rc == 2);
+ }
+ if (rc <= 0) {
+ string fname(server() ? "SSL_accept" : "SSL_connect");
+ string errors;
+ buildErrors(errors, errno_copy, error);
+ throw TSSLException(fname + ": " + errors);
+ }
+ authorize();
+ handshakeCompleted_ = true;
+}
+
+void TSSLSocket::authorize() {
+ int rc = SSL_get_verify_result(ssl_);
+ if (rc != X509_V_OK) { // verify authentication result
+ throw TSSLException(string("SSL_get_verify_result(), ") + X509_verify_cert_error_string(rc));
+ }
+
+ X509* cert = SSL_get_peer_certificate(ssl_);
+ if (cert == nullptr) {
+ // certificate is not present
+ if (SSL_get_verify_mode(ssl_) & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+ throw TSSLException("authorize: required certificate not present");
+ }
+ // certificate was optional: didn't intend to authorize remote
+ if (server() && access_ != nullptr) {
+ throw TSSLException("authorize: certificate required for authorization");
+ }
+ return;
+ }
+ // certificate is present
+ if (access_ == nullptr) {
+ X509_free(cert);
+ return;
+ }
+ // both certificate and access manager are present
+
+ string host;
+ sockaddr_storage sa;
+ socklen_t saLength = sizeof(sa);
+
+ if (getpeername(socket_, (sockaddr*)&sa, &saLength) != 0) {
+ sa.ss_family = AF_UNSPEC;
+ }
+
+ AccessManager::Decision decision = access_->verify(sa);
+
+ if (decision != AccessManager::SKIP) {
+ X509_free(cert);
+ if (decision != AccessManager::ALLOW) {
+ throw TSSLException("authorize: access denied based on remote IP");
+ }
+ return;
+ }
+
+ // extract subjectAlternativeName
+ auto* alternatives
+ = (STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr);
+ if (alternatives != nullptr) {
+ const int count = sk_GENERAL_NAME_num(alternatives);
+ for (int i = 0; decision == AccessManager::SKIP && i < count; i++) {
+ const GENERAL_NAME* name = sk_GENERAL_NAME_value(alternatives, i);
+ if (name == nullptr) {
+ continue;
+ }
+ char* data = (char*)ASN1_STRING_data(name->d.ia5);
+ int length = ASN1_STRING_length(name->d.ia5);
+ switch (name->type) {
+ case GEN_DNS:
+ if (host.empty()) {
+ host = (server() ? getPeerHost() : getHost());
+ }
+ decision = access_->verify(host, data, length);
+ break;
+ case GEN_IPADD:
+ decision = access_->verify(sa, data, length);
+ break;
+ }
+ }
+ sk_GENERAL_NAME_pop_free(alternatives, GENERAL_NAME_free);
+ }
+
+ if (decision != AccessManager::SKIP) {
+ X509_free(cert);
+ if (decision != AccessManager::ALLOW) {
+ throw TSSLException("authorize: access denied");
+ }
+ return;
+ }
+
+ // extract commonName
+ X509_NAME* name = X509_get_subject_name(cert);
+ if (name != nullptr) {
+ X509_NAME_ENTRY* entry;
+ unsigned char* utf8;
+ int last = -1;
+ while (decision == AccessManager::SKIP) {
+ last = X509_NAME_get_index_by_NID(name, NID_commonName, last);
+ if (last == -1)
+ break;
+ entry = X509_NAME_get_entry(name, last);
+ if (entry == nullptr)
+ continue;
+ ASN1_STRING* common = X509_NAME_ENTRY_get_data(entry);
+ int size = ASN1_STRING_to_UTF8(&utf8, common);
+ if (host.empty()) {
+ host = (server() ? getPeerHost() : getHost());
+ }
+ decision = access_->verify(host, (char*)utf8, size);
+ OPENSSL_free(utf8);
+ }
+ }
+ X509_free(cert);
+ if (decision != AccessManager::ALLOW) {
+ throw TSSLException("authorize: cannot authorize peer");
+ }
+}
+
+/*
+ * Note: This method is not libevent safe.
+*/
+unsigned int TSSLSocket::waitForEvent(bool wantRead) {
+ int fdSocket;
+ BIO* bio;
+
+ if (wantRead) {
+ bio = SSL_get_rbio(ssl_);
+ } else {
+ bio = SSL_get_wbio(ssl_);
+ }
+
+ if (bio == nullptr) {
+ throw TSSLException("SSL_get_?bio returned NULL");
+ }
+
+ if (BIO_get_fd(bio, &fdSocket) <= 0) {
+ throw TSSLException("BIO_get_fd failed");
+ }
+
+ struct THRIFT_POLLFD fds[2];
+ memset(fds, 0, sizeof(fds));
+ fds[0].fd = fdSocket;
+ // use POLLIN also on write operations too, this is needed for operations
+ // which requires read and write on the socket.
+ fds[0].events = wantRead ? THRIFT_POLLIN : THRIFT_POLLIN | THRIFT_POLLOUT;
+
+ if (interruptListener_) {
+ fds[1].fd = *(interruptListener_.get());
+ fds[1].events = THRIFT_POLLIN;
+ }
+
+ int timeout = -1;
+ if (wantRead && recvTimeout_) {
+ timeout = recvTimeout_;
+ }
+ if (!wantRead && sendTimeout_) {
+ timeout = sendTimeout_;
+ }
+
+ int ret = THRIFT_POLL(fds, interruptListener_ ? 2 : 1, timeout);
+
+ if (ret < 0) {
+ // error cases
+ if (THRIFT_GET_SOCKET_ERROR == THRIFT_EINTR) {
+ return TSSL_EINTR; // repeat operation
+ }
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TSSLSocket::read THRIFT_POLL() ", errno_copy);
+ throw TTransportException(TTransportException::UNKNOWN, "Unknown", errno_copy);
+ } else if (ret > 0){
+ if (fds[1].revents & THRIFT_POLLIN) {
+ throw TTransportException(TTransportException::INTERRUPTED, "Interrupted");
+ }
+ return TSSL_DATA;
+ } else {
+ throw TTransportException(TTransportException::TIMED_OUT, "THRIFT_POLL (timed out)");
+ }
+}
+
+// TSSLSocketFactory implementation
+uint64_t TSSLSocketFactory::count_ = 0;
+Mutex TSSLSocketFactory::mutex_;
+bool TSSLSocketFactory::manualOpenSSLInitialization_ = false;
+
+TSSLSocketFactory::TSSLSocketFactory(SSLProtocol protocol) : server_(false) {
+ Guard guard(mutex_);
+ if (count_ == 0) {
+ if (!manualOpenSSLInitialization_) {
+ initializeOpenSSL();
+ }
+ randomize();
+ }
+ count_++;
+ ctx_ = std::make_shared<SSLContext>(protocol);
+}
+
+TSSLSocketFactory::~TSSLSocketFactory() {
+ Guard guard(mutex_);
+ ctx_.reset();
+ count_--;
+ if (count_ == 0 && !manualOpenSSLInitialization_) {
+ cleanupOpenSSL();
+ }
+}
+
+std::shared_ptr<TSSLSocket> TSSLSocketFactory::createSocket() {
+ std::shared_ptr<TSSLSocket> ssl(new TSSLSocket(ctx_));
+ setup(ssl);
+ return ssl;
+}
+
+std::shared_ptr<TSSLSocket> TSSLSocketFactory::createSocket(std::shared_ptr<THRIFT_SOCKET> interruptListener) {
+ std::shared_ptr<TSSLSocket> ssl(new TSSLSocket(ctx_, interruptListener));
+ setup(ssl);
+ return ssl;
+}
+
+std::shared_ptr<TSSLSocket> TSSLSocketFactory::createSocket(THRIFT_SOCKET socket) {
+ std::shared_ptr<TSSLSocket> ssl(new TSSLSocket(ctx_, socket));
+ setup(ssl);
+ return ssl;
+}
+
+std::shared_ptr<TSSLSocket> TSSLSocketFactory::createSocket(THRIFT_SOCKET socket, std::shared_ptr<THRIFT_SOCKET> interruptListener) {
+ std::shared_ptr<TSSLSocket> ssl(new TSSLSocket(ctx_, socket, interruptListener));
+ setup(ssl);
+ return ssl;
+}
+
+std::shared_ptr<TSSLSocket> TSSLSocketFactory::createSocket(const string& host, int port) {
+ std::shared_ptr<TSSLSocket> ssl(new TSSLSocket(ctx_, host, port));
+ setup(ssl);
+ return ssl;
+}
+
+std::shared_ptr<TSSLSocket> TSSLSocketFactory::createSocket(const string& host, int port, std::shared_ptr<THRIFT_SOCKET> interruptListener) {
+ std::shared_ptr<TSSLSocket> ssl(new TSSLSocket(ctx_, host, port, interruptListener));
+ setup(ssl);
+ return ssl;
+}
+
+
+void TSSLSocketFactory::setup(std::shared_ptr<TSSLSocket> ssl) {
+ ssl->server(server());
+ if (access_ == nullptr && !server()) {
+ access_ = std::shared_ptr<AccessManager>(new DefaultClientAccessManager);
+ }
+ if (access_ != nullptr) {
+ ssl->access(access_);
+ }
+}
+
+void TSSLSocketFactory::ciphers(const string& enable) {
+ int rc = SSL_CTX_set_cipher_list(ctx_->get(), enable.c_str());
+ if (ERR_peek_error() != 0) {
+ string errors;
+ buildErrors(errors);
+ throw TSSLException("SSL_CTX_set_cipher_list: " + errors);
+ }
+ if (rc == 0) {
+ throw TSSLException("None of specified ciphers are supported");
+ }
+}
+
+void TSSLSocketFactory::authenticate(bool required) {
+ int mode;
+ if (required) {
+ mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE;
+ } else {
+ mode = SSL_VERIFY_NONE;
+ }
+ SSL_CTX_set_verify(ctx_->get(), mode, nullptr);
+}
+
+void TSSLSocketFactory::loadCertificate(const char* path, const char* format) {
+ if (path == nullptr || format == nullptr) {
+ throw TTransportException(TTransportException::BAD_ARGS,
+ "loadCertificateChain: either <path> or <format> is NULL");
+ }
+ if (strcmp(format, "PEM") == 0) {
+ if (SSL_CTX_use_certificate_chain_file(ctx_->get(), path) == 0) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ string errors;
+ buildErrors(errors, errno_copy);
+ throw TSSLException("SSL_CTX_use_certificate_chain_file: " + errors);
+ }
+ } else {
+ throw TSSLException("Unsupported certificate format: " + string(format));
+ }
+}
+
+void TSSLSocketFactory::loadPrivateKey(const char* path, const char* format) {
+ if (path == nullptr || format == nullptr) {
+ throw TTransportException(TTransportException::BAD_ARGS,
+ "loadPrivateKey: either <path> or <format> is NULL");
+ }
+ if (strcmp(format, "PEM") == 0) {
+ if (SSL_CTX_use_PrivateKey_file(ctx_->get(), path, SSL_FILETYPE_PEM) == 0) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ string errors;
+ buildErrors(errors, errno_copy);
+ throw TSSLException("SSL_CTX_use_PrivateKey_file: " + errors);
+ }
+ }
+}
+
+void TSSLSocketFactory::loadTrustedCertificates(const char* path, const char* capath) {
+ if (path == nullptr) {
+ throw TTransportException(TTransportException::BAD_ARGS,
+ "loadTrustedCertificates: <path> is NULL");
+ }
+ if (SSL_CTX_load_verify_locations(ctx_->get(), path, capath) == 0) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ string errors;
+ buildErrors(errors, errno_copy);
+ throw TSSLException("SSL_CTX_load_verify_locations: " + errors);
+ }
+}
+
+void TSSLSocketFactory::randomize() {
+ RAND_poll();
+}
+
+void TSSLSocketFactory::overrideDefaultPasswordCallback() {
+ SSL_CTX_set_default_passwd_cb(ctx_->get(), passwordCallback);
+ SSL_CTX_set_default_passwd_cb_userdata(ctx_->get(), this);
+}
+
+int TSSLSocketFactory::passwordCallback(char* password, int size, int, void* data) {
+ auto* factory = (TSSLSocketFactory*)data;
+ string userPassword;
+ factory->getPassword(userPassword, size);
+ int length = static_cast<int>(userPassword.size());
+ if (length > size) {
+ length = size;
+ }
+ strncpy(password, userPassword.c_str(), length);
+ userPassword.assign(userPassword.size(), '*');
+ return length;
+}
+
+// extract error messages from error queue
+void buildErrors(string& errors, int errno_copy, int sslerrno) {
+ unsigned long errorCode;
+ char message[256];
+
+ errors.reserve(512);
+ while ((errorCode = ERR_get_error()) != 0) {
+ if (!errors.empty()) {
+ errors += "; ";
+ }
+ const char* reason = ERR_reason_error_string(errorCode);
+ if (reason == nullptr) {
+ THRIFT_SNPRINTF(message, sizeof(message) - 1, "SSL error # %lu", errorCode);
+ reason = message;
+ }
+ errors += reason;
+ }
+ if (errors.empty()) {
+ if (errno_copy != 0) {
+ errors += TOutput::strerror_s(errno_copy);
+ }
+ }
+ if (errors.empty()) {
+ errors = "error code: " + to_string(errno_copy);
+ }
+ if (sslerrno) {
+ errors += " (SSL_error_code = " + to_string(sslerrno) + ")";
+ if (sslerrno == SSL_ERROR_SYSCALL) {
+ char buf[4096];
+ int err;
+ while ((err = ERR_get_error()) != 0) {
+ errors += " ";
+ errors += ERR_error_string(err, buf);
+ }
+ }
+ }
+}
+
+/**
+ * Default implementation of AccessManager
+ */
+Decision DefaultClientAccessManager::verify(const sockaddr_storage& sa) noexcept {
+ (void)sa;
+ return SKIP;
+}
+
+Decision DefaultClientAccessManager::verify(const string& host,
+ const char* name,
+ int size) noexcept {
+ if (host.empty() || name == nullptr || size <= 0) {
+ return SKIP;
+ }
+ return (matchName(host.c_str(), name, size) ? ALLOW : SKIP);
+}
+
+Decision DefaultClientAccessManager::verify(const sockaddr_storage& sa,
+ const char* data,
+ int size) noexcept {
+ bool match = false;
+ if (sa.ss_family == AF_INET && size == sizeof(in_addr)) {
+ match = (memcmp(&((sockaddr_in*)&sa)->sin_addr, data, size) == 0);
+ } else if (sa.ss_family == AF_INET6 && size == sizeof(in6_addr)) {
+ match = (memcmp(&((sockaddr_in6*)&sa)->sin6_addr, data, size) == 0);
+ }
+ return (match ? ALLOW : SKIP);
+}
+
+/**
+ * Match a name with a pattern. The pattern may include wildcard. A single
+ * wildcard "*" can match up to one component in the domain name.
+ *
+ * @param host Host name, typically the name of the remote host
+ * @param pattern Name retrieved from certificate
+ * @param size Size of "pattern"
+ * @return True, if "host" matches "pattern". False otherwise.
+ */
+bool matchName(const char* host, const char* pattern, int size) {
+ bool match = false;
+ int i = 0, j = 0;
+ while (i < size && host[j] != '\0') {
+ if (uppercase(pattern[i]) == uppercase(host[j])) {
+ i++;
+ j++;
+ continue;
+ }
+ if (pattern[i] == '*') {
+ while (host[j] != '.' && host[j] != '\0') {
+ j++;
+ }
+ i++;
+ continue;
+ }
+ break;
+ }
+ if (i == size && host[j] == '\0') {
+ match = true;
+ }
+ return match;
+}
+
+// This is to work around the Turkish locale issue, i.e.,
+// toupper('i') != toupper('I') if locale is "tr_TR"
+char uppercase(char c) {
+ if ('a' <= c && c <= 'z') {
+ return c + ('A' - 'a');
+ }
+ return c;
+}
+}
+}
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSSLSocket.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSSLSocket.h
new file mode 100644
index 000000000..87a960147
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSSLSocket.h
@@ -0,0 +1,436 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TSSLSOCKET_H_
+#define _THRIFT_TRANSPORT_TSSLSOCKET_H_ 1
+
+// Put this first to avoid WIN32 build failure
+#include <thrift/transport/TSocket.h>
+
+#include <openssl/ssl.h>
+#include <string>
+#include <thrift/concurrency/Mutex.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+class AccessManager;
+class SSLContext;
+
+enum SSLProtocol {
+ SSLTLS = 0, // Supports SSLv2 and SSLv3 handshake but only negotiates at TLSv1_0 or later.
+//SSLv2 = 1, // HORRIBLY INSECURE!
+ SSLv3 = 2, // Supports SSLv3 only - also horribly insecure!
+ TLSv1_0 = 3, // Supports TLSv1_0 or later.
+ TLSv1_1 = 4, // Supports TLSv1_1 or later.
+ TLSv1_2 = 5, // Supports TLSv1_2 or later.
+ LATEST = TLSv1_2
+};
+
+#define TSSL_EINTR 0
+#define TSSL_DATA 1
+
+/**
+ * Initialize OpenSSL library. This function, or some other
+ * equivalent function to initialize OpenSSL, must be called before
+ * TSSLSocket is used. If you set TSSLSocketFactory to use manual
+ * OpenSSL initialization, you should call this function or otherwise
+ * ensure OpenSSL is initialized yourself.
+ */
+void initializeOpenSSL();
+/**
+ * Cleanup OpenSSL library. This function should be called to clean
+ * up OpenSSL after use of OpenSSL functionality is finished. If you
+ * set TSSLSocketFactory to use manual OpenSSL initialization, you
+ * should call this function yourself or ensure that whatever
+ * initialized OpenSSL cleans it up too.
+ */
+void cleanupOpenSSL();
+
+/**
+ * OpenSSL implementation for SSL socket interface.
+ */
+class TSSLSocket : public TSocket {
+public:
+ ~TSSLSocket() override;
+ /**
+ * TTransport interface.
+ */
+ bool isOpen() const override;
+ bool peek() override;
+ void open() override;
+ void close() override;
+ bool hasPendingDataToRead() override;
+ uint32_t read(uint8_t* buf, uint32_t len) override;
+ void write(const uint8_t* buf, uint32_t len) override;
+ uint32_t write_partial(const uint8_t* buf, uint32_t len) override;
+ void flush() override;
+ /**
+ * Set whether to use client or server side SSL handshake protocol.
+ *
+ * @param flag Use server side handshake protocol if true.
+ */
+ void server(bool flag) { server_ = flag; }
+ /**
+ * Determine whether the SSL socket is server or client mode.
+ */
+ bool server() const { return server_; }
+ /**
+ * Set AccessManager.
+ *
+ * @param manager Instance of AccessManager
+ */
+ virtual void access(std::shared_ptr<AccessManager> manager) { access_ = manager; }
+ /**
+ * Set eventSafe flag if libevent is used.
+ */
+ void setLibeventSafe() { eventSafe_ = true; }
+ /**
+ * Determines whether SSL Socket is libevent safe or not.
+ */
+ bool isLibeventSafe() const { return eventSafe_; }
+
+protected:
+ /**
+ * Constructor.
+ */
+ TSSLSocket(std::shared_ptr<SSLContext> ctx);
+ /**
+ * Constructor with an interrupt signal.
+ */
+ TSSLSocket(std::shared_ptr<SSLContext> ctx, std::shared_ptr<THRIFT_SOCKET> interruptListener);
+ /**
+ * Constructor, create an instance of TSSLSocket given an existing socket.
+ *
+ * @param socket An existing socket
+ */
+ TSSLSocket(std::shared_ptr<SSLContext> ctx, THRIFT_SOCKET socket);
+ /**
+ * Constructor, create an instance of TSSLSocket given an existing socket that can be interrupted.
+ *
+ * @param socket An existing socket
+ */
+ TSSLSocket(std::shared_ptr<SSLContext> ctx, THRIFT_SOCKET socket, std::shared_ptr<THRIFT_SOCKET> interruptListener);
+ /**
+ * Constructor.
+ *
+ * @param host Remote host name
+ * @param port Remote port number
+ */
+ TSSLSocket(std::shared_ptr<SSLContext> ctx, std::string host, int port);
+ /**
+ * Constructor with an interrupt signal.
+ *
+ * @param host Remote host name
+ * @param port Remote port number
+ */
+ TSSLSocket(std::shared_ptr<SSLContext> ctx, std::string host, int port, std::shared_ptr<THRIFT_SOCKET> interruptListener);
+ /**
+ * Authorize peer access after SSL handshake completes.
+ */
+ virtual void authorize();
+ /**
+ * Initiate SSL handshake if not already initiated.
+ */
+ void initializeHandshake();
+ /**
+ * Initiate SSL handshake params.
+ */
+ void initializeHandshakeParams();
+ /**
+ * Check if SSL handshake is completed or not.
+ */
+ bool checkHandshake();
+ /**
+ * Waits for an socket or shutdown event.
+ *
+ * @throw TTransportException::INTERRUPTED if interrupted is signaled.
+ *
+ * @return TSSL_EINTR if EINTR happened on the underlying socket
+ * TSSL_DATA if data is available on the socket.
+ */
+ unsigned int waitForEvent(bool wantRead);
+
+ bool server_;
+ SSL* ssl_;
+ std::shared_ptr<SSLContext> ctx_;
+ std::shared_ptr<AccessManager> access_;
+ friend class TSSLSocketFactory;
+
+private:
+ bool handshakeCompleted_;
+ int readRetryCount_;
+ bool eventSafe_;
+
+ void init();
+};
+
+/**
+ * SSL socket factory. SSL sockets should be created via SSL factory.
+ * The factory will automatically initialize and cleanup openssl as long as
+ * there is a TSSLSocketFactory instantiated, and as long as the static
+ * boolean manualOpenSSLInitialization_ is set to false, the default.
+ *
+ * If you would like to initialize and cleanup openssl yourself, set
+ * manualOpenSSLInitialization_ to true and TSSLSocketFactory will no
+ * longer be responsible for openssl initialization and teardown.
+ *
+ * It is the responsibility of the code using TSSLSocketFactory to
+ * ensure that the factory lifetime exceeds the lifetime of any sockets
+ * it might create. If this is not guaranteed, a socket may call into
+ * openssl after the socket factory has cleaned up openssl! This
+ * guarantee is unnecessary if manualOpenSSLInitialization_ is true,
+ * however, since it would be up to the consuming application instead.
+ */
+class TSSLSocketFactory {
+public:
+ /**
+ * Constructor/Destructor
+ *
+ * @param protocol The SSL/TLS protocol to use.
+ */
+ TSSLSocketFactory(SSLProtocol protocol = SSLTLS);
+ virtual ~TSSLSocketFactory();
+ /**
+ * Create an instance of TSSLSocket with a fresh new socket.
+ */
+ virtual std::shared_ptr<TSSLSocket> createSocket();
+ /**
+ * Create an instance of TSSLSocket with a fresh new socket, which is interruptable.
+ */
+ virtual std::shared_ptr<TSSLSocket> createSocket(std::shared_ptr<THRIFT_SOCKET> interruptListener);
+ /**
+ * Create an instance of TSSLSocket with the given socket.
+ *
+ * @param socket An existing socket.
+ */
+ virtual std::shared_ptr<TSSLSocket> createSocket(THRIFT_SOCKET socket);
+ /**
+ * Create an instance of TSSLSocket with the given socket which is interruptable.
+ *
+ * @param socket An existing socket.
+ */
+ virtual std::shared_ptr<TSSLSocket> createSocket(THRIFT_SOCKET socket, std::shared_ptr<THRIFT_SOCKET> interruptListener);
+ /**
+ * Create an instance of TSSLSocket.
+ *
+ * @param host Remote host to be connected to
+ * @param port Remote port to be connected to
+ */
+ virtual std::shared_ptr<TSSLSocket> createSocket(const std::string& host, int port);
+ /**
+ * Create an instance of TSSLSocket.
+ *
+ * @param host Remote host to be connected to
+ * @param port Remote port to be connected to
+ */
+ virtual std::shared_ptr<TSSLSocket> createSocket(const std::string& host, int port, std::shared_ptr<THRIFT_SOCKET> interruptListener);
+ /**
+ * Set ciphers to be used in SSL handshake process.
+ *
+ * @param ciphers A list of ciphers
+ */
+ virtual void ciphers(const std::string& enable);
+ /**
+ * Enable/Disable authentication.
+ *
+ * @param required Require peer to present valid certificate if true
+ */
+ virtual void authenticate(bool required);
+ /**
+ * Load server certificate.
+ *
+ * @param path Path to the certificate file
+ * @param format Certificate file format
+ */
+ virtual void loadCertificate(const char* path, const char* format = "PEM");
+ /**
+ * Load private key.
+ *
+ * @param path Path to the private key file
+ * @param format Private key file format
+ */
+ virtual void loadPrivateKey(const char* path, const char* format = "PEM");
+ /**
+ * Load trusted certificates from specified file.
+ *
+ * @param path Path to trusted certificate file
+ */
+ virtual void loadTrustedCertificates(const char* path, const char* capath = nullptr);
+ /**
+ * Default randomize method.
+ */
+ virtual void randomize();
+ /**
+ * Override default OpenSSL password callback with getPassword().
+ */
+ void overrideDefaultPasswordCallback();
+ /**
+ * Set/Unset server mode.
+ *
+ * @param flag Server mode if true
+ */
+ virtual void server(bool flag) { server_ = flag; }
+ /**
+ * Determine whether the socket is in server or client mode.
+ *
+ * @return true, if server mode, or, false, if client mode
+ */
+ virtual bool server() const { return server_; }
+ /**
+ * Set AccessManager.
+ *
+ * @param manager The AccessManager instance
+ */
+ virtual void access(std::shared_ptr<AccessManager> manager) { access_ = manager; }
+ static void setManualOpenSSLInitialization(bool manualOpenSSLInitialization) {
+ manualOpenSSLInitialization_ = manualOpenSSLInitialization;
+ }
+
+protected:
+ std::shared_ptr<SSLContext> ctx_;
+
+ /**
+ * Override this method for custom password callback. It may be called
+ * multiple times at any time during a session as necessary.
+ *
+ * @param password Pass collected password to OpenSSL
+ * @param size Maximum length of password including NULL character
+ */
+ virtual void getPassword(std::string& /* password */, int /* size */) {}
+
+private:
+ bool server_;
+ std::shared_ptr<AccessManager> access_;
+ static concurrency::Mutex mutex_;
+ static uint64_t count_;
+ THRIFT_EXPORT static bool manualOpenSSLInitialization_;
+ void setup(std::shared_ptr<TSSLSocket> ssl);
+ static int passwordCallback(char* password, int size, int, void* data);
+};
+
+/**
+ * SSL exception.
+ */
+class TSSLException : public TTransportException {
+public:
+ TSSLException(const std::string& message)
+ : TTransportException(TTransportException::INTERNAL_ERROR, message) {}
+
+ const char* what() const noexcept override {
+ if (message_.empty()) {
+ return "TSSLException";
+ } else {
+ return message_.c_str();
+ }
+ }
+};
+
+/**
+ * Wrap OpenSSL SSL_CTX into a class.
+ */
+class SSLContext {
+public:
+ SSLContext(const SSLProtocol& protocol = SSLTLS);
+ virtual ~SSLContext();
+ SSL* createSSL();
+ SSL_CTX* get() { return ctx_; }
+
+private:
+ SSL_CTX* ctx_;
+};
+
+/**
+ * Callback interface for access control. It's meant to verify the remote host.
+ * It's constructed when application starts and set to TSSLSocketFactory
+ * instance. It's passed onto all TSSLSocket instances created by this factory
+ * object.
+ */
+class AccessManager {
+public:
+ enum Decision {
+ DENY = -1, // deny access
+ SKIP = 0, // cannot make decision, move on to next (if any)
+ ALLOW = 1 // allow access
+ };
+ /**
+ * Destructor
+ */
+ virtual ~AccessManager() = default;
+ /**
+ * Determine whether the peer should be granted access or not. It's called
+ * once after the SSL handshake completes successfully, before peer certificate
+ * is examined.
+ *
+ * If a valid decision (ALLOW or DENY) is returned, the peer certificate is
+ * not to be verified.
+ *
+ * @param sa Peer IP address
+ * @return True if the peer is trusted, false otherwise
+ */
+ virtual Decision verify(const sockaddr_storage& /* sa */) noexcept { return DENY; }
+ /**
+ * Determine whether the peer should be granted access or not. It's called
+ * every time a DNS subjectAltName/common name is extracted from peer's
+ * certificate.
+ *
+ * @param host Client mode: host name returned by TSocket::getHost()
+ * Server mode: host name returned by TSocket::getPeerHost()
+ * @param name SubjectAltName or common name extracted from peer certificate
+ * @param size Length of name
+ * @return True if the peer is trusted, false otherwise
+ *
+ * Note: The "name" parameter may be UTF8 encoded.
+ */
+ virtual Decision verify(const std::string& /* host */,
+ const char* /* name */,
+ int /* size */) noexcept {
+ return DENY;
+ }
+ /**
+ * Determine whether the peer should be granted access or not. It's called
+ * every time an IP subjectAltName is extracted from peer's certificate.
+ *
+ * @param sa Peer IP address retrieved from the underlying socket
+ * @param data IP address extracted from certificate
+ * @param size Length of the IP address
+ * @return True if the peer is trusted, false otherwise
+ */
+ virtual Decision verify(const sockaddr_storage& /* sa */,
+ const char* /* data */,
+ int /* size */) noexcept {
+ return DENY;
+ }
+};
+
+typedef AccessManager::Decision Decision;
+
+class DefaultClientAccessManager : public AccessManager {
+public:
+ // AccessManager interface
+ Decision verify(const sockaddr_storage& sa) noexcept override;
+ Decision verify(const std::string& host, const char* name, int size) noexcept override;
+ Decision verify(const sockaddr_storage& sa, const char* data, int size) noexcept override;
+};
+}
+}
+}
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TServerSocket.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TServerSocket.cpp
new file mode 100644
index 000000000..ece05445a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TServerSocket.cpp
@@ -0,0 +1,698 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+
+#include <cstring>
+#include <memory>
+#include <stdexcept>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <thrift/transport/TSocket.h>
+#include <thrift/transport/TServerSocket.h>
+#include <thrift/transport/PlatformSocket.h>
+
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+
+#ifndef SOCKOPT_CAST_T
+#ifndef _WIN32
+#define SOCKOPT_CAST_T void
+#else
+#define SOCKOPT_CAST_T char
+#endif // _WIN32
+#endif
+
+template <class T>
+inline const SOCKOPT_CAST_T* const_cast_sockopt(const T* v) {
+ return reinterpret_cast<const SOCKOPT_CAST_T*>(v);
+}
+
+template <class T>
+inline SOCKOPT_CAST_T* cast_sockopt(T* v) {
+ return reinterpret_cast<SOCKOPT_CAST_T*>(v);
+}
+
+void destroyer_of_fine_sockets(THRIFT_SOCKET* ssock) {
+ ::THRIFT_CLOSESOCKET(*ssock);
+ delete ssock;
+}
+
+using std::string;
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+using std::shared_ptr;
+
+TGetAddrInfoWrapper::TGetAddrInfoWrapper(const char* node,
+ const char* service,
+ const struct addrinfo* hints)
+ : node_(node), service_(service), hints_(hints), res_(nullptr) {}
+
+TGetAddrInfoWrapper::~TGetAddrInfoWrapper() {
+ if (this->res_ != nullptr)
+ freeaddrinfo(this->res_);
+}
+
+int TGetAddrInfoWrapper::init() {
+ if (this->res_ == nullptr)
+ return getaddrinfo(this->node_, this->service_, this->hints_, &(this->res_));
+ return 0;
+}
+
+const struct addrinfo* TGetAddrInfoWrapper::res() {
+ return this->res_;
+}
+
+TServerSocket::TServerSocket(int port)
+ : interruptableChildren_(true),
+ port_(port),
+ serverSocket_(THRIFT_INVALID_SOCKET),
+ acceptBacklog_(DEFAULT_BACKLOG),
+ sendTimeout_(0),
+ recvTimeout_(0),
+ accTimeout_(-1),
+ retryLimit_(0),
+ retryDelay_(0),
+ tcpSendBuffer_(0),
+ tcpRecvBuffer_(0),
+ keepAlive_(false),
+ listening_(false),
+ interruptSockWriter_(THRIFT_INVALID_SOCKET),
+ interruptSockReader_(THRIFT_INVALID_SOCKET),
+ childInterruptSockWriter_(THRIFT_INVALID_SOCKET) {
+}
+
+TServerSocket::TServerSocket(int port, int sendTimeout, int recvTimeout)
+ : interruptableChildren_(true),
+ port_(port),
+ serverSocket_(THRIFT_INVALID_SOCKET),
+ acceptBacklog_(DEFAULT_BACKLOG),
+ sendTimeout_(sendTimeout),
+ recvTimeout_(recvTimeout),
+ accTimeout_(-1),
+ retryLimit_(0),
+ retryDelay_(0),
+ tcpSendBuffer_(0),
+ tcpRecvBuffer_(0),
+ keepAlive_(false),
+ listening_(false),
+ interruptSockWriter_(THRIFT_INVALID_SOCKET),
+ interruptSockReader_(THRIFT_INVALID_SOCKET),
+ childInterruptSockWriter_(THRIFT_INVALID_SOCKET) {
+}
+
+TServerSocket::TServerSocket(const string& address, int port)
+ : interruptableChildren_(true),
+ port_(port),
+ address_(address),
+ serverSocket_(THRIFT_INVALID_SOCKET),
+ acceptBacklog_(DEFAULT_BACKLOG),
+ sendTimeout_(0),
+ recvTimeout_(0),
+ accTimeout_(-1),
+ retryLimit_(0),
+ retryDelay_(0),
+ tcpSendBuffer_(0),
+ tcpRecvBuffer_(0),
+ keepAlive_(false),
+ listening_(false),
+ interruptSockWriter_(THRIFT_INVALID_SOCKET),
+ interruptSockReader_(THRIFT_INVALID_SOCKET),
+ childInterruptSockWriter_(THRIFT_INVALID_SOCKET) {
+}
+
+TServerSocket::TServerSocket(const string& path)
+ : interruptableChildren_(true),
+ port_(0),
+ path_(path),
+ serverSocket_(THRIFT_INVALID_SOCKET),
+ acceptBacklog_(DEFAULT_BACKLOG),
+ sendTimeout_(0),
+ recvTimeout_(0),
+ accTimeout_(-1),
+ retryLimit_(0),
+ retryDelay_(0),
+ tcpSendBuffer_(0),
+ tcpRecvBuffer_(0),
+ keepAlive_(false),
+ listening_(false),
+ interruptSockWriter_(THRIFT_INVALID_SOCKET),
+ interruptSockReader_(THRIFT_INVALID_SOCKET),
+ childInterruptSockWriter_(THRIFT_INVALID_SOCKET) {
+}
+
+TServerSocket::~TServerSocket() {
+ close();
+}
+
+void TServerSocket::setSendTimeout(int sendTimeout) {
+ sendTimeout_ = sendTimeout;
+}
+
+void TServerSocket::setRecvTimeout(int recvTimeout) {
+ recvTimeout_ = recvTimeout;
+}
+
+void TServerSocket::setAcceptTimeout(int accTimeout) {
+ accTimeout_ = accTimeout;
+}
+
+void TServerSocket::setAcceptBacklog(int accBacklog) {
+ acceptBacklog_ = accBacklog;
+}
+
+void TServerSocket::setRetryLimit(int retryLimit) {
+ retryLimit_ = retryLimit;
+}
+
+void TServerSocket::setRetryDelay(int retryDelay) {
+ retryDelay_ = retryDelay;
+}
+
+void TServerSocket::setTcpSendBuffer(int tcpSendBuffer) {
+ tcpSendBuffer_ = tcpSendBuffer;
+}
+
+void TServerSocket::setTcpRecvBuffer(int tcpRecvBuffer) {
+ tcpRecvBuffer_ = tcpRecvBuffer;
+}
+
+void TServerSocket::setInterruptableChildren(bool enable) {
+ if (listening_) {
+ throw std::logic_error("setInterruptableChildren cannot be called after listen()");
+ }
+ interruptableChildren_ = enable;
+}
+
+void TServerSocket::listen() {
+ listening_ = true;
+#ifdef _WIN32
+ TWinsockSingleton::create();
+#endif // _WIN32
+ THRIFT_SOCKET sv[2];
+ // Create the socket pair used to interrupt
+ if (-1 == THRIFT_SOCKETPAIR(AF_LOCAL, SOCK_STREAM, 0, sv)) {
+ GlobalOutput.perror("TServerSocket::listen() socketpair() interrupt", THRIFT_GET_SOCKET_ERROR);
+ interruptSockWriter_ = THRIFT_INVALID_SOCKET;
+ interruptSockReader_ = THRIFT_INVALID_SOCKET;
+ } else {
+ interruptSockWriter_ = sv[1];
+ interruptSockReader_ = sv[0];
+ }
+
+ // Create the socket pair used to interrupt all clients
+ if (-1 == THRIFT_SOCKETPAIR(AF_LOCAL, SOCK_STREAM, 0, sv)) {
+ GlobalOutput.perror("TServerSocket::listen() socketpair() childInterrupt",
+ THRIFT_GET_SOCKET_ERROR);
+ childInterruptSockWriter_ = THRIFT_INVALID_SOCKET;
+ pChildInterruptSockReader_.reset();
+ } else {
+ childInterruptSockWriter_ = sv[1];
+ pChildInterruptSockReader_
+ = std::shared_ptr<THRIFT_SOCKET>(new THRIFT_SOCKET(sv[0]), destroyer_of_fine_sockets);
+ }
+
+ // Validate port number
+ if (port_ < 0 || port_ > 0xFFFF) {
+ throw TTransportException(TTransportException::BAD_ARGS, "Specified port is invalid");
+ }
+
+ const struct addrinfo *res;
+ int error;
+ char port[sizeof("65535")];
+ THRIFT_SNPRINTF(port, sizeof(port), "%d", port_);
+
+ struct addrinfo hints;
+ std::memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+
+ // If address is not specified use wildcard address (NULL)
+ TGetAddrInfoWrapper info(address_.empty() ? nullptr : &address_[0], port, &hints);
+
+ error = info.init();
+ if (error) {
+ GlobalOutput.printf("getaddrinfo %d: %s", error, THRIFT_GAI_STRERROR(error));
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not resolve host for server socket.");
+ }
+
+ // Pick the ipv6 address first since ipv4 addresses can be mapped
+ // into ipv6 space.
+ for (res = info.res(); res; res = res->ai_next) {
+ if (res->ai_family == AF_INET6 || res->ai_next == nullptr)
+ break;
+ }
+
+ if (!path_.empty()) {
+ serverSocket_ = socket(PF_UNIX, SOCK_STREAM, IPPROTO_IP);
+ } else if (res != nullptr) {
+ serverSocket_ = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ }
+
+ if (serverSocket_ == THRIFT_INVALID_SOCKET) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TServerSocket::listen() socket() ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not create server socket.",
+ errno_copy);
+ }
+
+ // Set THRIFT_NO_SOCKET_CACHING to prevent 2MSL delay on accept
+ int one = 1;
+ if (-1 == setsockopt(serverSocket_,
+ SOL_SOCKET,
+ THRIFT_NO_SOCKET_CACHING,
+ cast_sockopt(&one),
+ sizeof(one))) {
+// ignore errors coming out of this setsockopt on Windows. This is because
+// SO_EXCLUSIVEADDRUSE requires admin privileges on WinXP, but we don't
+// want to force servers to be an admin.
+#ifndef _WIN32
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TServerSocket::listen() setsockopt() THRIFT_NO_SOCKET_CACHING ",
+ errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not set THRIFT_NO_SOCKET_CACHING",
+ errno_copy);
+#endif
+ }
+
+ // Set TCP buffer sizes
+ if (tcpSendBuffer_ > 0) {
+ if (-1 == setsockopt(serverSocket_,
+ SOL_SOCKET,
+ SO_SNDBUF,
+ cast_sockopt(&tcpSendBuffer_),
+ sizeof(tcpSendBuffer_))) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TServerSocket::listen() setsockopt() SO_SNDBUF ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not set SO_SNDBUF",
+ errno_copy);
+ }
+ }
+
+ if (tcpRecvBuffer_ > 0) {
+ if (-1 == setsockopt(serverSocket_,
+ SOL_SOCKET,
+ SO_RCVBUF,
+ cast_sockopt(&tcpRecvBuffer_),
+ sizeof(tcpRecvBuffer_))) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TServerSocket::listen() setsockopt() SO_RCVBUF ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not set SO_RCVBUF",
+ errno_copy);
+ }
+ }
+
+// Defer accept
+#ifdef TCP_DEFER_ACCEPT
+ if (path_.empty()) {
+ if (-1 == setsockopt(serverSocket_, IPPROTO_TCP, TCP_DEFER_ACCEPT, &one, sizeof(one))) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TServerSocket::listen() setsockopt() TCP_DEFER_ACCEPT ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not set TCP_DEFER_ACCEPT",
+ errno_copy);
+ }
+ }
+#endif // #ifdef TCP_DEFER_ACCEPT
+
+#ifdef IPV6_V6ONLY
+ if (res->ai_family == AF_INET6 && path_.empty()) {
+ int zero = 0;
+ if (-1 == setsockopt(serverSocket_,
+ IPPROTO_IPV6,
+ IPV6_V6ONLY,
+ cast_sockopt(&zero),
+ sizeof(zero))) {
+ GlobalOutput.perror("TServerSocket::listen() IPV6_V6ONLY ", THRIFT_GET_SOCKET_ERROR);
+ }
+ }
+#endif // #ifdef IPV6_V6ONLY
+
+ // Turn linger off, don't want to block on calls to close
+ struct linger ling = {0, 0};
+ if (-1 == setsockopt(serverSocket_, SOL_SOCKET, SO_LINGER, cast_sockopt(&ling), sizeof(ling))) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TServerSocket::listen() setsockopt() SO_LINGER ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN, "Could not set SO_LINGER", errno_copy);
+ }
+
+ // Unix Sockets do not need that
+ if (path_.empty()) {
+ // TCP Nodelay, speed over bandwidth
+ if (-1
+ == setsockopt(serverSocket_, IPPROTO_TCP, TCP_NODELAY, cast_sockopt(&one), sizeof(one))) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TServerSocket::listen() setsockopt() TCP_NODELAY ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not set TCP_NODELAY",
+ errno_copy);
+ }
+ }
+
+ // Set NONBLOCK on the accept socket
+ int flags = THRIFT_FCNTL(serverSocket_, THRIFT_F_GETFL, 0);
+ if (flags == -1) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TServerSocket::listen() THRIFT_FCNTL() THRIFT_F_GETFL ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "THRIFT_FCNTL() THRIFT_F_GETFL failed",
+ errno_copy);
+ }
+
+ if (-1 == THRIFT_FCNTL(serverSocket_, THRIFT_F_SETFL, flags | THRIFT_O_NONBLOCK)) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TServerSocket::listen() THRIFT_FCNTL() THRIFT_O_NONBLOCK ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "THRIFT_FCNTL() THRIFT_F_SETFL THRIFT_O_NONBLOCK failed",
+ errno_copy);
+ }
+
+ // prepare the port information
+ // we may want to try to bind more than once, since THRIFT_NO_SOCKET_CACHING doesn't
+ // always seem to work. The client can configure the retry variables.
+ int retries = 0;
+ int errno_copy = 0;
+
+ if (!path_.empty()) {
+
+#ifndef _WIN32
+
+ // Unix Domain Socket
+ size_t len = path_.size() + 1;
+ if (len > sizeof(((sockaddr_un*)nullptr)->sun_path)) {
+ errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TSocket::listen() Unix Domain socket path too long", errno_copy);
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Unix Domain socket path too long",
+ errno_copy);
+ }
+
+ struct sockaddr_un address;
+ address.sun_family = AF_UNIX;
+ memcpy(address.sun_path, path_.c_str(), len);
+
+ auto structlen = static_cast<socklen_t>(sizeof(address));
+
+ if (!address.sun_path[0]) { // abstract namespace socket
+#ifdef __linux__
+ // sun_path is not null-terminated in this case and structlen determines its length
+ structlen -= sizeof(address.sun_path) - len;
+#else
+ GlobalOutput.perror("TSocket::open() Abstract Namespace Domain sockets only supported on linux: ", -99);
+ throw TTransportException(TTransportException::NOT_OPEN,
+ " Abstract Namespace Domain socket path not supported");
+#endif
+ }
+
+ do {
+ if (0 == ::bind(serverSocket_, (struct sockaddr*)&address, structlen)) {
+ break;
+ }
+ errno_copy = THRIFT_GET_SOCKET_ERROR;
+ // use short circuit evaluation here to only sleep if we need to
+ } while ((retries++ < retryLimit_) && (THRIFT_SLEEP_SEC(retryDelay_) == 0));
+#else
+ GlobalOutput.perror("TSocket::open() Unix Domain socket path not supported on windows", -99);
+ throw TTransportException(TTransportException::NOT_OPEN,
+ " Unix Domain socket path not supported");
+#endif
+ } else {
+ do {
+ if (0 == ::bind(serverSocket_, res->ai_addr, static_cast<int>(res->ai_addrlen))) {
+ break;
+ }
+ errno_copy = THRIFT_GET_SOCKET_ERROR;
+ // use short circuit evaluation here to only sleep if we need to
+ } while ((retries++ < retryLimit_) && (THRIFT_SLEEP_SEC(retryDelay_) == 0));
+
+ // retrieve bind info
+ if (port_ == 0 && retries <= retryLimit_) {
+ struct sockaddr_storage sa;
+ socklen_t len = sizeof(sa);
+ std::memset(&sa, 0, len);
+ if (::getsockname(serverSocket_, reinterpret_cast<struct sockaddr*>(&sa), &len) < 0) {
+ errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TServerSocket::getPort() getsockname() ", errno_copy);
+ } else {
+ if (sa.ss_family == AF_INET6) {
+ const auto* sin = reinterpret_cast<const struct sockaddr_in6*>(&sa);
+ port_ = ntohs(sin->sin6_port);
+ } else {
+ const auto* sin = reinterpret_cast<const struct sockaddr_in*>(&sa);
+ port_ = ntohs(sin->sin_port);
+ }
+ }
+ }
+ }
+
+ // throw an error if we failed to bind properly
+ if (retries > retryLimit_) {
+ char errbuf[1024];
+ if (!path_.empty()) {
+ THRIFT_SNPRINTF(errbuf, sizeof(errbuf), "TServerSocket::listen() PATH %s", path_.c_str());
+ } else {
+ THRIFT_SNPRINTF(errbuf, sizeof(errbuf), "TServerSocket::listen() BIND %d", port_);
+ }
+ GlobalOutput(errbuf);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not bind",
+ errno_copy);
+ }
+
+ if (listenCallback_)
+ listenCallback_(serverSocket_);
+
+ // Call listen
+ if (-1 == ::listen(serverSocket_, acceptBacklog_)) {
+ errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TServerSocket::listen() listen() ", errno_copy);
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN, "Could not listen", errno_copy);
+ }
+
+ // The socket is now listening!
+}
+
+int TServerSocket::getPort() {
+ return port_;
+}
+
+shared_ptr<TTransport> TServerSocket::acceptImpl() {
+ if (serverSocket_ == THRIFT_INVALID_SOCKET) {
+ throw TTransportException(TTransportException::NOT_OPEN, "TServerSocket not listening");
+ }
+
+ struct THRIFT_POLLFD fds[2];
+
+ int maxEintrs = 5;
+ int numEintrs = 0;
+
+ while (true) {
+ std::memset(fds, 0, sizeof(fds));
+ fds[0].fd = serverSocket_;
+ fds[0].events = THRIFT_POLLIN;
+ if (interruptSockReader_ != THRIFT_INVALID_SOCKET) {
+ fds[1].fd = interruptSockReader_;
+ fds[1].events = THRIFT_POLLIN;
+ }
+ /*
+ TODO: if THRIFT_EINTR is received, we'll restart the timeout.
+ To be accurate, we need to fix this in the future.
+ */
+ int ret = THRIFT_POLL(fds, 2, accTimeout_);
+
+ if (ret < 0) {
+ // error cases
+ if (THRIFT_GET_SOCKET_ERROR == THRIFT_EINTR && (numEintrs++ < maxEintrs)) {
+ // THRIFT_EINTR needs to be handled manually and we can tolerate
+ // a certain number
+ continue;
+ }
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TServerSocket::acceptImpl() THRIFT_POLL() ", errno_copy);
+ throw TTransportException(TTransportException::UNKNOWN, "Unknown", errno_copy);
+ } else if (ret > 0) {
+ // Check for an interrupt signal
+ if (interruptSockReader_ != THRIFT_INVALID_SOCKET && (fds[1].revents & THRIFT_POLLIN)) {
+ int8_t buf;
+ if (-1 == recv(interruptSockReader_, cast_sockopt(&buf), sizeof(int8_t), 0)) {
+ GlobalOutput.perror("TServerSocket::acceptImpl() recv() interrupt ",
+ THRIFT_GET_SOCKET_ERROR);
+ }
+ throw TTransportException(TTransportException::INTERRUPTED);
+ }
+
+ // Check for the actual server socket being ready
+ if (fds[0].revents & THRIFT_POLLIN) {
+ break;
+ }
+ } else {
+ GlobalOutput("TServerSocket::acceptImpl() THRIFT_POLL 0");
+ throw TTransportException(TTransportException::UNKNOWN);
+ }
+ }
+
+ struct sockaddr_storage clientAddress;
+ int size = sizeof(clientAddress);
+ THRIFT_SOCKET clientSocket
+ = ::accept(serverSocket_, (struct sockaddr*)&clientAddress, (socklen_t*)&size);
+
+ if (clientSocket == THRIFT_INVALID_SOCKET) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TServerSocket::acceptImpl() ::accept() ", errno_copy);
+ throw TTransportException(TTransportException::UNKNOWN, "accept()", errno_copy);
+ }
+
+ // Make sure client socket is blocking
+ int flags = THRIFT_FCNTL(clientSocket, THRIFT_F_GETFL, 0);
+ if (flags == -1) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ ::THRIFT_CLOSESOCKET(clientSocket);
+ GlobalOutput.perror("TServerSocket::acceptImpl() THRIFT_FCNTL() THRIFT_F_GETFL ", errno_copy);
+ throw TTransportException(TTransportException::UNKNOWN,
+ "THRIFT_FCNTL(THRIFT_F_GETFL)",
+ errno_copy);
+ }
+
+ if (-1 == THRIFT_FCNTL(clientSocket, THRIFT_F_SETFL, flags & ~THRIFT_O_NONBLOCK)) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ ::THRIFT_CLOSESOCKET(clientSocket);
+ GlobalOutput
+ .perror("TServerSocket::acceptImpl() THRIFT_FCNTL() THRIFT_F_SETFL ~THRIFT_O_NONBLOCK ",
+ errno_copy);
+ throw TTransportException(TTransportException::UNKNOWN,
+ "THRIFT_FCNTL(THRIFT_F_SETFL)",
+ errno_copy);
+ }
+
+ shared_ptr<TSocket> client = createSocket(clientSocket);
+ if (sendTimeout_ > 0) {
+ client->setSendTimeout(sendTimeout_);
+ }
+ if (recvTimeout_ > 0) {
+ client->setRecvTimeout(recvTimeout_);
+ }
+ if (keepAlive_) {
+ client->setKeepAlive(keepAlive_);
+ }
+ client->setCachedAddress((sockaddr*)&clientAddress, size);
+
+ if (acceptCallback_)
+ acceptCallback_(clientSocket);
+
+ return client;
+}
+
+shared_ptr<TSocket> TServerSocket::createSocket(THRIFT_SOCKET clientSocket) {
+ if (interruptableChildren_) {
+ return std::make_shared<TSocket>(clientSocket, pChildInterruptSockReader_);
+ } else {
+ return std::make_shared<TSocket>(clientSocket);
+ }
+}
+
+void TServerSocket::notify(THRIFT_SOCKET notifySocket) {
+ if (notifySocket != THRIFT_INVALID_SOCKET) {
+ int8_t byte = 0;
+ if (-1 == send(notifySocket, cast_sockopt(&byte), sizeof(int8_t), 0)) {
+ GlobalOutput.perror("TServerSocket::notify() send() ", THRIFT_GET_SOCKET_ERROR);
+ }
+ }
+}
+
+void TServerSocket::interrupt() {
+ concurrency::Guard g(rwMutex_);
+ if (interruptSockWriter_ != THRIFT_INVALID_SOCKET) {
+ notify(interruptSockWriter_);
+ }
+}
+
+void TServerSocket::interruptChildren() {
+ concurrency::Guard g(rwMutex_);
+ if (childInterruptSockWriter_ != THRIFT_INVALID_SOCKET) {
+ notify(childInterruptSockWriter_);
+ }
+}
+
+void TServerSocket::close() {
+ concurrency::Guard g(rwMutex_);
+ if (serverSocket_ != THRIFT_INVALID_SOCKET) {
+ shutdown(serverSocket_, THRIFT_SHUT_RDWR);
+ ::THRIFT_CLOSESOCKET(serverSocket_);
+ }
+ if (interruptSockWriter_ != THRIFT_INVALID_SOCKET) {
+ ::THRIFT_CLOSESOCKET(interruptSockWriter_);
+ }
+ if (interruptSockReader_ != THRIFT_INVALID_SOCKET) {
+ ::THRIFT_CLOSESOCKET(interruptSockReader_);
+ }
+ if (childInterruptSockWriter_ != THRIFT_INVALID_SOCKET) {
+ ::THRIFT_CLOSESOCKET(childInterruptSockWriter_);
+ }
+ serverSocket_ = THRIFT_INVALID_SOCKET;
+ interruptSockWriter_ = THRIFT_INVALID_SOCKET;
+ interruptSockReader_ = THRIFT_INVALID_SOCKET;
+ childInterruptSockWriter_ = THRIFT_INVALID_SOCKET;
+ pChildInterruptSockReader_.reset();
+ listening_ = false;
+}
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TServerSocket.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TServerSocket.h
new file mode 100644
index 000000000..d64096803
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TServerSocket.h
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TSERVERSOCKET_H_
+#define _THRIFT_TRANSPORT_TSERVERSOCKET_H_ 1
+
+#include <thrift/concurrency/Mutex.h>
+#include <thrift/transport/PlatformSocket.h>
+#include <thrift/transport/TServerTransport.h>
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+class TSocket;
+
+class TGetAddrInfoWrapper {
+public:
+ TGetAddrInfoWrapper(const char* node, const char* service, const struct addrinfo* hints);
+
+ virtual ~TGetAddrInfoWrapper();
+
+ int init();
+ const struct addrinfo* res();
+
+private:
+ const char* node_;
+ const char* service_;
+ const struct addrinfo* hints_;
+ struct addrinfo* res_;
+};
+
+/**
+ * Server socket implementation of TServerTransport. Wrapper around a unix
+ * socket listen and accept calls.
+ *
+ */
+class TServerSocket : public TServerTransport {
+public:
+ typedef std::function<void(THRIFT_SOCKET fd)> socket_func_t;
+
+ const static int DEFAULT_BACKLOG = 1024;
+
+ /**
+ * Constructor.
+ *
+ * @param port Port number to bind to
+ */
+ TServerSocket(int port);
+
+ /**
+ * Constructor.
+ *
+ * @param port Port number to bind to
+ * @param sendTimeout Socket send timeout
+ * @param recvTimeout Socket receive timeout
+ */
+ TServerSocket(int port, int sendTimeout, int recvTimeout);
+
+ /**
+ * Constructor.
+ *
+ * @param address Address to bind to
+ * @param port Port number to bind to
+ */
+ TServerSocket(const std::string& address, int port);
+
+ /**
+ * Constructor used for unix sockets.
+ *
+ * @param path Pathname for unix socket.
+ */
+ TServerSocket(const std::string& path);
+
+ ~TServerSocket() override;
+
+ void setSendTimeout(int sendTimeout);
+ void setRecvTimeout(int recvTimeout);
+
+ void setAcceptTimeout(int accTimeout);
+ void setAcceptBacklog(int accBacklog);
+
+ void setRetryLimit(int retryLimit);
+ void setRetryDelay(int retryDelay);
+
+ void setKeepAlive(bool keepAlive) { keepAlive_ = keepAlive; }
+
+ void setTcpSendBuffer(int tcpSendBuffer);
+ void setTcpRecvBuffer(int tcpRecvBuffer);
+
+ // listenCallback gets called just before listen, and after all Thrift
+ // setsockopt calls have been made. If you have custom setsockopt
+ // things that need to happen on the listening socket, this is the place to do it.
+ void setListenCallback(const socket_func_t& listenCallback) { listenCallback_ = listenCallback; }
+
+ // acceptCallback gets called after each accept call, on the newly created socket.
+ // It is called after all Thrift setsockopt calls have been made. If you have
+ // custom setsockopt things that need to happen on the accepted
+ // socket, this is the place to do it.
+ void setAcceptCallback(const socket_func_t& acceptCallback) { acceptCallback_ = acceptCallback; }
+
+ // When enabled (the default), new children TSockets will be constructed so
+ // they can be interrupted by TServerTransport::interruptChildren().
+ // This is more expensive in terms of system calls (poll + recv) however
+ // ensures a connected client cannot interfere with TServer::stop().
+ //
+ // When disabled, TSocket children do not incur an additional poll() call.
+ // Server-side reads are more efficient, however a client can interfere with
+ // the server's ability to shutdown properly by staying connected.
+ //
+ // Must be called before listen(); mode cannot be switched after that.
+ // \throws std::logic_error if listen() has been called
+ void setInterruptableChildren(bool enable);
+
+ THRIFT_SOCKET getSocketFD() override { return serverSocket_; }
+
+ int getPort();
+
+ void listen() override;
+ void interrupt() override;
+ void interruptChildren() override;
+ void close() override;
+
+protected:
+ std::shared_ptr<TTransport> acceptImpl() override;
+ virtual std::shared_ptr<TSocket> createSocket(THRIFT_SOCKET client);
+ bool interruptableChildren_;
+ std::shared_ptr<THRIFT_SOCKET> pChildInterruptSockReader_; // if interruptableChildren_ this is shared with child TSockets
+
+private:
+ void notify(THRIFT_SOCKET notifySock);
+
+ int port_;
+ std::string address_;
+ std::string path_;
+ THRIFT_SOCKET serverSocket_;
+ int acceptBacklog_;
+ int sendTimeout_;
+ int recvTimeout_;
+ int accTimeout_;
+ int retryLimit_;
+ int retryDelay_;
+ int tcpSendBuffer_;
+ int tcpRecvBuffer_;
+ bool keepAlive_;
+ bool listening_;
+
+ concurrency::Mutex rwMutex_; // thread-safe interrupt
+ THRIFT_SOCKET interruptSockWriter_; // is notified on interrupt()
+ THRIFT_SOCKET interruptSockReader_; // is used in select/poll with serverSocket_ for interruptability
+ THRIFT_SOCKET childInterruptSockWriter_; // is notified on interruptChildren()
+
+ socket_func_t listenCallback_;
+ socket_func_t acceptCallback_;
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_TSERVERSOCKET_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TServerTransport.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TServerTransport.h
new file mode 100644
index 000000000..f465bb38a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TServerTransport.h
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TSERVERTRANSPORT_H_
+#define _THRIFT_TRANSPORT_TSERVERTRANSPORT_H_ 1
+
+#include <thrift/transport/TTransport.h>
+#include <thrift/transport/TTransportException.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * Server transport framework. A server needs to have some facility for
+ * creating base transports to read/write from. The server is expected
+ * to keep track of TTransport children that it creates for purposes of
+ * controlling their lifetime.
+ */
+class TServerTransport {
+public:
+ virtual ~TServerTransport() = default;
+
+ /**
+ * Starts the server transport listening for new connections. Prior to this
+ * call most transports will not return anything when accept is called.
+ *
+ * @throws TTransportException if we were unable to listen
+ */
+ virtual void listen() {}
+
+ /**
+ * Gets a new dynamically allocated transport object and passes it to the
+ * caller. Note that it is the explicit duty of the caller to free the
+ * allocated object. The returned TTransport object must always be in the
+ * opened state. NULL should never be returned, instead an Exception should
+ * always be thrown.
+ *
+ * @return A new TTransport object
+ * @throws TTransportException if there is an error
+ */
+ std::shared_ptr<TTransport> accept() {
+ std::shared_ptr<TTransport> result = acceptImpl();
+ if (!result) {
+ throw TTransportException("accept() may not return NULL");
+ }
+ return result;
+ }
+
+ /**
+ * For "smart" TServerTransport implementations that work in a multi
+ * threaded context this can be used to break out of an accept() call.
+ * It is expected that the transport will throw a TTransportException
+ * with the INTERRUPTED error code.
+ *
+ * This will not make an attempt to interrupt any TTransport children.
+ */
+ virtual void interrupt() {}
+
+ /**
+ * This will interrupt the children created by the server transport.
+ * allowing them to break out of any blocking data reception call.
+ * It is expected that the children will throw a TTransportException
+ * with the INTERRUPTED error code.
+ */
+ virtual void interruptChildren() {}
+
+ /**
+ * Utility method
+ *
+ * @return server socket file descriptor
+ * @throw TTransportException If an error occurs
+ */
+
+ virtual THRIFT_SOCKET getSocketFD() { return -1; }
+
+ /**
+ * Closes this transport such that future calls to accept will do nothing.
+ */
+ virtual void close() = 0;
+
+protected:
+ TServerTransport() = default;
+
+ /**
+ * Subclasses should implement this function for accept.
+ *
+ * @return A newly allocated TTransport object
+ * @throw TTransportException If an error occurs
+ */
+ virtual std::shared_ptr<TTransport> acceptImpl() = 0;
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_TSERVERTRANSPORT_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TShortReadTransport.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TShortReadTransport.h
new file mode 100644
index 000000000..185c78dc7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TShortReadTransport.h
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TSHORTREADTRANSPORT_H_
+#define _THRIFT_TRANSPORT_TSHORTREADTRANSPORT_H_ 1
+
+#include <cstdlib>
+
+#include <thrift/transport/TTransport.h>
+#include <thrift/transport/TVirtualTransport.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+namespace test {
+
+/**
+ * This class is only meant for testing. It wraps another transport.
+ * Calls to read are passed through with some probability. Otherwise,
+ * the read amount is randomly reduced before being passed through.
+ *
+ */
+class TShortReadTransport : public TVirtualTransport<TShortReadTransport> {
+public:
+ TShortReadTransport(std::shared_ptr<TTransport> transport, double full_prob)
+ : transport_(transport), fullProb_(full_prob) {}
+
+ bool isOpen() const override { return transport_->isOpen(); }
+
+ bool peek() override { return transport_->peek(); }
+
+ void open() override { transport_->open(); }
+
+ void close() override { transport_->close(); }
+
+ uint32_t read(uint8_t* buf, uint32_t len) {
+ if (len == 0) {
+ return 0;
+ }
+
+ if (rand() / (double)RAND_MAX >= fullProb_) {
+ len = 1 + rand() % len;
+ }
+ return transport_->read(buf, len);
+ }
+
+ void write(const uint8_t* buf, uint32_t len) { transport_->write(buf, len); }
+
+ void flush() override { transport_->flush(); }
+
+ const uint8_t* borrow(uint8_t* buf, uint32_t* len) { return transport_->borrow(buf, len); }
+
+ void consume(uint32_t len) { return transport_->consume(len); }
+
+ std::shared_ptr<TTransport> getUnderlyingTransport() { return transport_; }
+
+protected:
+ std::shared_ptr<TTransport> transport_;
+ double fullProb_;
+};
+}
+}
+}
+} // apache::thrift::transport::test
+
+#endif // #ifndef _THRIFT_TRANSPORT_TSHORTREADTRANSPORT_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSimpleFileTransport.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSimpleFileTransport.cpp
new file mode 100644
index 000000000..4b1399e14
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSimpleFileTransport.cpp
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+
+#include <thrift/transport/TSimpleFileTransport.h>
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <fcntl.h>
+
+#ifdef _WIN32
+#include <io.h>
+#endif
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+TSimpleFileTransport::TSimpleFileTransport(const std::string& path, bool read, bool write)
+ : TFDTransport(-1, TFDTransport::CLOSE_ON_DESTROY) {
+ int flags = 0;
+ if (read && write) {
+ flags = O_RDWR;
+ } else if (read) {
+ flags = O_RDONLY;
+ } else if (write) {
+ flags = O_WRONLY;
+ } else {
+ throw TTransportException("Neither READ nor WRITE specified");
+ }
+ if (write) {
+ flags |= O_CREAT | O_APPEND;
+ }
+#ifndef _WIN32
+ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+#else
+ int mode = _S_IREAD | _S_IWRITE;
+#endif
+ int fd = ::THRIFT_OPEN(path.c_str(), flags, mode);
+ if (fd < 0) {
+ throw TTransportException("failed to open file for writing: " + path);
+ }
+ setFD(fd);
+ open();
+}
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSimpleFileTransport.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSimpleFileTransport.h
new file mode 100644
index 000000000..32e18974d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSimpleFileTransport.h
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TSIMPLEFILETRANSPORT_H_
+#define _THRIFT_TRANSPORT_TSIMPLEFILETRANSPORT_H_ 1
+
+#include <thrift/transport/TFDTransport.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * Dead-simple wrapper around a file.
+ *
+ * Writeable files are opened with O_CREAT and O_APPEND
+ */
+class TSimpleFileTransport : public TFDTransport {
+public:
+ TSimpleFileTransport(const std::string& path, bool read = true, bool write = false);
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // _THRIFT_TRANSPORT_TSIMPLEFILETRANSPORT_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSocket.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSocket.cpp
new file mode 100644
index 000000000..977834d48
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSocket.cpp
@@ -0,0 +1,959 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+
+#include <cstring>
+#include <sstream>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif
+#include <sys/types.h>
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+
+#include <thrift/concurrency/Monitor.h>
+#include <thrift/transport/TSocket.h>
+#include <thrift/transport/TTransportException.h>
+#include <thrift/transport/PlatformSocket.h>
+
+#ifndef SOCKOPT_CAST_T
+#ifndef _WIN32
+#define SOCKOPT_CAST_T void
+#else
+#define SOCKOPT_CAST_T char
+#endif // _WIN32
+#endif
+
+template <class T>
+inline const SOCKOPT_CAST_T* const_cast_sockopt(const T* v) {
+ return reinterpret_cast<const SOCKOPT_CAST_T*>(v);
+}
+
+template <class T>
+inline SOCKOPT_CAST_T* cast_sockopt(T* v) {
+ return reinterpret_cast<SOCKOPT_CAST_T*>(v);
+}
+
+using std::string;
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * TSocket implementation.
+ *
+ */
+
+TSocket::TSocket(const string& host, int port)
+ : host_(host),
+ port_(port),
+ socket_(THRIFT_INVALID_SOCKET),
+ peerPort_(0),
+ connTimeout_(0),
+ sendTimeout_(0),
+ recvTimeout_(0),
+ keepAlive_(false),
+ lingerOn_(1),
+ lingerVal_(0),
+ noDelay_(1),
+ maxRecvRetries_(5) {
+}
+
+TSocket::TSocket(const string& path)
+ : port_(0),
+ path_(path),
+ socket_(THRIFT_INVALID_SOCKET),
+ peerPort_(0),
+ connTimeout_(0),
+ sendTimeout_(0),
+ recvTimeout_(0),
+ keepAlive_(false),
+ lingerOn_(1),
+ lingerVal_(0),
+ noDelay_(1),
+ maxRecvRetries_(5) {
+ cachedPeerAddr_.ipv4.sin_family = AF_UNSPEC;
+}
+
+TSocket::TSocket()
+ : port_(0),
+ socket_(THRIFT_INVALID_SOCKET),
+ peerPort_(0),
+ connTimeout_(0),
+ sendTimeout_(0),
+ recvTimeout_(0),
+ keepAlive_(false),
+ lingerOn_(1),
+ lingerVal_(0),
+ noDelay_(1),
+ maxRecvRetries_(5) {
+ cachedPeerAddr_.ipv4.sin_family = AF_UNSPEC;
+}
+
+TSocket::TSocket(THRIFT_SOCKET socket)
+ : port_(0),
+ socket_(socket),
+ peerPort_(0),
+ connTimeout_(0),
+ sendTimeout_(0),
+ recvTimeout_(0),
+ keepAlive_(false),
+ lingerOn_(1),
+ lingerVal_(0),
+ noDelay_(1),
+ maxRecvRetries_(5) {
+ cachedPeerAddr_.ipv4.sin_family = AF_UNSPEC;
+#ifdef SO_NOSIGPIPE
+ {
+ int one = 1;
+ setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one));
+ }
+#endif
+}
+
+TSocket::TSocket(THRIFT_SOCKET socket, std::shared_ptr<THRIFT_SOCKET> interruptListener)
+ : port_(0),
+ socket_(socket),
+ peerPort_(0),
+ interruptListener_(interruptListener),
+ connTimeout_(0),
+ sendTimeout_(0),
+ recvTimeout_(0),
+ keepAlive_(false),
+ lingerOn_(1),
+ lingerVal_(0),
+ noDelay_(1),
+ maxRecvRetries_(5) {
+ cachedPeerAddr_.ipv4.sin_family = AF_UNSPEC;
+#ifdef SO_NOSIGPIPE
+ {
+ int one = 1;
+ setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one));
+ }
+#endif
+}
+
+TSocket::~TSocket() {
+ close();
+}
+
+bool TSocket::hasPendingDataToRead() {
+ if (!isOpen()) {
+ return false;
+ }
+
+ int32_t retries = 0;
+ THRIFT_IOCTL_SOCKET_NUM_BYTES_TYPE numBytesAvailable;
+try_again:
+ int r = THRIFT_IOCTL_SOCKET(socket_, FIONREAD, &numBytesAvailable);
+ if (r == -1) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ if (errno_copy == THRIFT_EINTR && (retries++ < maxRecvRetries_)) {
+ goto try_again;
+ }
+ GlobalOutput.perror("TSocket::hasPendingDataToRead() THRIFT_IOCTL_SOCKET() " + getSocketInfo(), errno_copy);
+ throw TTransportException(TTransportException::UNKNOWN, "Unknown", errno_copy);
+ }
+ return numBytesAvailable > 0;
+}
+
+bool TSocket::isOpen() const {
+ return (socket_ != THRIFT_INVALID_SOCKET);
+}
+
+bool TSocket::peek() {
+ if (!isOpen()) {
+ return false;
+ }
+ if (interruptListener_) {
+ for (int retries = 0;;) {
+ struct THRIFT_POLLFD fds[2];
+ std::memset(fds, 0, sizeof(fds));
+ fds[0].fd = socket_;
+ fds[0].events = THRIFT_POLLIN;
+ fds[1].fd = *(interruptListener_.get());
+ fds[1].events = THRIFT_POLLIN;
+ int ret = THRIFT_POLL(fds, 2, (recvTimeout_ == 0) ? -1 : recvTimeout_);
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ if (ret < 0) {
+ // error cases
+ if (errno_copy == THRIFT_EINTR && (retries++ < maxRecvRetries_)) {
+ continue;
+ }
+ GlobalOutput.perror("TSocket::peek() THRIFT_POLL() ", errno_copy);
+ throw TTransportException(TTransportException::UNKNOWN, "Unknown", errno_copy);
+ } else if (ret > 0) {
+ // Check the interruptListener
+ if (fds[1].revents & THRIFT_POLLIN) {
+ return false;
+ }
+ // There must be data or a disconnection, fall through to the PEEK
+ break;
+ } else {
+ // timeout
+ return false;
+ }
+ }
+ }
+
+ // Check to see if data is available or if the remote side closed
+ uint8_t buf;
+ int r = static_cast<int>(recv(socket_, cast_sockopt(&buf), 1, MSG_PEEK));
+ if (r == -1) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+#if defined __FreeBSD__ || defined __MACH__
+ /* shigin:
+ * freebsd returns -1 and THRIFT_ECONNRESET if socket was closed by
+ * the other side
+ */
+ if (errno_copy == THRIFT_ECONNRESET) {
+ return false;
+ }
+#endif
+ GlobalOutput.perror("TSocket::peek() recv() " + getSocketInfo(), errno_copy);
+ throw TTransportException(TTransportException::UNKNOWN, "recv()", errno_copy);
+ }
+ return (r > 0);
+}
+
+void TSocket::openConnection(struct addrinfo* res) {
+
+ if (isOpen()) {
+ return;
+ }
+
+ if (!path_.empty()) {
+ socket_ = socket(PF_UNIX, SOCK_STREAM, IPPROTO_IP);
+ } else {
+ socket_ = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ }
+
+ if (socket_ == THRIFT_INVALID_SOCKET) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TSocket::open() socket() " + getSocketInfo(), errno_copy);
+ throw TTransportException(TTransportException::NOT_OPEN, "socket()", errno_copy);
+ }
+
+ // Send timeout
+ if (sendTimeout_ > 0) {
+ setSendTimeout(sendTimeout_);
+ }
+
+ // Recv timeout
+ if (recvTimeout_ > 0) {
+ setRecvTimeout(recvTimeout_);
+ }
+
+ if (keepAlive_) {
+ setKeepAlive(keepAlive_);
+ }
+
+ // Linger
+ setLinger(lingerOn_, lingerVal_);
+
+ // No delay
+ setNoDelay(noDelay_);
+
+#ifdef SO_NOSIGPIPE
+ {
+ int one = 1;
+ setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one));
+ }
+#endif
+
+// Uses a low min RTO if asked to.
+#ifdef TCP_LOW_MIN_RTO
+ if (getUseLowMinRto()) {
+ int one = 1;
+ setsockopt(socket_, IPPROTO_TCP, TCP_LOW_MIN_RTO, &one, sizeof(one));
+ }
+#endif
+
+ // Set the socket to be non blocking for connect if a timeout exists
+ int flags = THRIFT_FCNTL(socket_, THRIFT_F_GETFL, 0);
+ if (connTimeout_ > 0) {
+ if (-1 == THRIFT_FCNTL(socket_, THRIFT_F_SETFL, flags | THRIFT_O_NONBLOCK)) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TSocket::open() THRIFT_FCNTL() " + getSocketInfo(), errno_copy);
+ throw TTransportException(TTransportException::NOT_OPEN, "THRIFT_FCNTL() failed", errno_copy);
+ }
+ } else {
+ if (-1 == THRIFT_FCNTL(socket_, THRIFT_F_SETFL, flags & ~THRIFT_O_NONBLOCK)) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TSocket::open() THRIFT_FCNTL " + getSocketInfo(), errno_copy);
+ throw TTransportException(TTransportException::NOT_OPEN, "THRIFT_FCNTL() failed", errno_copy);
+ }
+ }
+
+ // Connect the socket
+ int ret;
+ if (!path_.empty()) {
+
+#ifndef _WIN32
+ size_t len = path_.size() + 1;
+ if (len > sizeof(((sockaddr_un*)nullptr)->sun_path)) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TSocket::open() Unix Domain socket path too long", errno_copy);
+ throw TTransportException(TTransportException::NOT_OPEN, " Unix Domain socket path too long");
+ }
+
+ struct sockaddr_un address;
+ address.sun_family = AF_UNIX;
+ memcpy(address.sun_path, path_.c_str(), len);
+
+ auto structlen = static_cast<socklen_t>(sizeof(address));
+
+ if (!address.sun_path[0]) { // abstract namespace socket
+#ifdef __linux__
+ // sun_path is not null-terminated in this case and structlen determines its length
+ structlen -= sizeof(address.sun_path) - len;
+#else
+ GlobalOutput.perror("TSocket::open() Abstract Namespace Domain sockets only supported on linux: ", -99);
+ throw TTransportException(TTransportException::NOT_OPEN,
+ " Abstract Namespace Domain socket path not supported");
+#endif
+ }
+
+ ret = connect(socket_, (struct sockaddr*)&address, structlen);
+#else
+ GlobalOutput.perror("TSocket::open() Unix Domain socket path not supported on windows", -99);
+ throw TTransportException(TTransportException::NOT_OPEN,
+ " Unix Domain socket path not supported");
+#endif
+
+ } else {
+ ret = connect(socket_, res->ai_addr, static_cast<int>(res->ai_addrlen));
+ }
+
+ // success case
+ if (ret == 0) {
+ goto done;
+ }
+
+ if ((THRIFT_GET_SOCKET_ERROR != THRIFT_EINPROGRESS)
+ && (THRIFT_GET_SOCKET_ERROR != THRIFT_EWOULDBLOCK)) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TSocket::open() connect() " + getSocketInfo(), errno_copy);
+ throw TTransportException(TTransportException::NOT_OPEN, "connect() failed", errno_copy);
+ }
+
+ struct THRIFT_POLLFD fds[1];
+ std::memset(fds, 0, sizeof(fds));
+ fds[0].fd = socket_;
+ fds[0].events = THRIFT_POLLOUT;
+ ret = THRIFT_POLL(fds, 1, connTimeout_);
+
+ if (ret > 0) {
+ // Ensure the socket is connected and that there are no errors set
+ int val;
+ socklen_t lon;
+ lon = sizeof(int);
+ int ret2 = getsockopt(socket_, SOL_SOCKET, SO_ERROR, cast_sockopt(&val), &lon);
+ if (ret2 == -1) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TSocket::open() getsockopt() " + getSocketInfo(), errno_copy);
+ throw TTransportException(TTransportException::NOT_OPEN, "getsockopt()", errno_copy);
+ }
+ // no errors on socket, go to town
+ if (val == 0) {
+ goto done;
+ }
+ GlobalOutput.perror("TSocket::open() error on socket (after THRIFT_POLL) " + getSocketInfo(),
+ val);
+ throw TTransportException(TTransportException::NOT_OPEN, "socket open() error", val);
+ } else if (ret == 0) {
+ // socket timed out
+ string errStr = "TSocket::open() timed out " + getSocketInfo();
+ GlobalOutput(errStr.c_str());
+ throw TTransportException(TTransportException::NOT_OPEN, "open() timed out");
+ } else {
+ // error on THRIFT_POLL()
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TSocket::open() THRIFT_POLL() " + getSocketInfo(), errno_copy);
+ throw TTransportException(TTransportException::NOT_OPEN, "THRIFT_POLL() failed", errno_copy);
+ }
+
+done:
+ // Set socket back to normal mode (blocking)
+ if (-1 == THRIFT_FCNTL(socket_, THRIFT_F_SETFL, flags)) {
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TSocket::open() THRIFT_FCNTL " + getSocketInfo(), errno_copy);
+ throw TTransportException(TTransportException::NOT_OPEN, "THRIFT_FCNTL() failed", errno_copy);
+ }
+
+ if (path_.empty()) {
+ setCachedAddress(res->ai_addr, static_cast<socklen_t>(res->ai_addrlen));
+ }
+}
+
+void TSocket::open() {
+ if (isOpen()) {
+ return;
+ }
+ if (!path_.empty()) {
+ unix_open();
+ } else {
+ local_open();
+ }
+}
+
+void TSocket::unix_open() {
+ if (!path_.empty()) {
+ // Unix Domain SOcket does not need addrinfo struct, so we pass NULL
+ openConnection(nullptr);
+ }
+}
+
+void TSocket::local_open() {
+
+#ifdef _WIN32
+ TWinsockSingleton::create();
+#endif // _WIN32
+
+ if (isOpen()) {
+ return;
+ }
+
+ // Validate port number
+ if (port_ < 0 || port_ > 0xFFFF) {
+ throw TTransportException(TTransportException::BAD_ARGS, "Specified port is invalid");
+ }
+
+ struct addrinfo hints, *res, *res0;
+ res = nullptr;
+ res0 = nullptr;
+ int error;
+ char port[sizeof("65535")];
+ std::memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+ sprintf(port, "%d", port_);
+
+ error = getaddrinfo(host_.c_str(), port, &hints, &res0);
+
+#ifdef _WIN32
+ if (error == WSANO_DATA) {
+ hints.ai_flags &= ~AI_ADDRCONFIG;
+ error = getaddrinfo(host_.c_str(), port, &hints, &res0);
+ }
+#endif
+
+ if (error) {
+ string errStr = "TSocket::open() getaddrinfo() " + getSocketInfo()
+ + string(THRIFT_GAI_STRERROR(error));
+ GlobalOutput(errStr.c_str());
+ close();
+ throw TTransportException(TTransportException::NOT_OPEN,
+ "Could not resolve host for client socket.");
+ }
+
+ // Cycle through all the returned addresses until one
+ // connects or push the exception up.
+ for (res = res0; res; res = res->ai_next) {
+ try {
+ openConnection(res);
+ break;
+ } catch (TTransportException&) {
+ if (res->ai_next) {
+ close();
+ } else {
+ close();
+ freeaddrinfo(res0); // cleanup on failure
+ throw;
+ }
+ }
+ }
+
+ // Free address structure memory
+ freeaddrinfo(res0);
+}
+
+void TSocket::close() {
+ if (socket_ != THRIFT_INVALID_SOCKET) {
+ shutdown(socket_, THRIFT_SHUT_RDWR);
+ ::THRIFT_CLOSESOCKET(socket_);
+ }
+ socket_ = THRIFT_INVALID_SOCKET;
+}
+
+void TSocket::setSocketFD(THRIFT_SOCKET socket) {
+ if (socket_ != THRIFT_INVALID_SOCKET) {
+ close();
+ }
+ socket_ = socket;
+}
+
+uint32_t TSocket::read(uint8_t* buf, uint32_t len) {
+ if (socket_ == THRIFT_INVALID_SOCKET) {
+ throw TTransportException(TTransportException::NOT_OPEN, "Called read on non-open socket");
+ }
+
+ int32_t retries = 0;
+
+ // THRIFT_EAGAIN can be signalled both when a timeout has occurred and when
+ // the system is out of resources (an awesome undocumented feature).
+ // The following is an approximation of the time interval under which
+ // THRIFT_EAGAIN is taken to indicate an out of resources error.
+ uint32_t eagainThresholdMicros = 0;
+ if (recvTimeout_) {
+ // if a readTimeout is specified along with a max number of recv retries, then
+ // the threshold will ensure that the read timeout is not exceeded even in the
+ // case of resource errors
+ eagainThresholdMicros = (recvTimeout_ * 1000) / ((maxRecvRetries_ > 0) ? maxRecvRetries_ : 2);
+ }
+
+try_again:
+ // Read from the socket
+ struct timeval begin;
+ if (recvTimeout_ > 0) {
+ THRIFT_GETTIMEOFDAY(&begin, nullptr);
+ } else {
+ // if there is no read timeout we don't need the TOD to determine whether
+ // an THRIFT_EAGAIN is due to a timeout or an out-of-resource condition.
+ begin.tv_sec = begin.tv_usec = 0;
+ }
+
+ int got = 0;
+
+ if (interruptListener_) {
+ struct THRIFT_POLLFD fds[2];
+ std::memset(fds, 0, sizeof(fds));
+ fds[0].fd = socket_;
+ fds[0].events = THRIFT_POLLIN;
+ fds[1].fd = *(interruptListener_.get());
+ fds[1].events = THRIFT_POLLIN;
+
+ int ret = THRIFT_POLL(fds, 2, (recvTimeout_ == 0) ? -1 : recvTimeout_);
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ if (ret < 0) {
+ // error cases
+ if (errno_copy == THRIFT_EINTR && (retries++ < maxRecvRetries_)) {
+ goto try_again;
+ }
+ GlobalOutput.perror("TSocket::read() THRIFT_POLL() ", errno_copy);
+ throw TTransportException(TTransportException::UNKNOWN, "Unknown", errno_copy);
+ } else if (ret > 0) {
+ // Check the interruptListener
+ if (fds[1].revents & THRIFT_POLLIN) {
+ throw TTransportException(TTransportException::INTERRUPTED, "Interrupted");
+ }
+ } else /* ret == 0 */ {
+ throw TTransportException(TTransportException::TIMED_OUT, "THRIFT_EAGAIN (timed out)");
+ }
+
+ // falling through means there is something to recv and it cannot block
+ }
+
+ got = static_cast<int>(recv(socket_, cast_sockopt(buf), len, 0));
+ // THRIFT_GETTIMEOFDAY can change THRIFT_GET_SOCKET_ERROR
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+
+ // Check for error on read
+ if (got < 0) {
+ if (errno_copy == THRIFT_EAGAIN) {
+ // if no timeout we can assume that resource exhaustion has occurred.
+ if (recvTimeout_ == 0) {
+ throw TTransportException(TTransportException::TIMED_OUT,
+ "THRIFT_EAGAIN (unavailable resources)");
+ }
+ // check if this is the lack of resources or timeout case
+ struct timeval end;
+ THRIFT_GETTIMEOFDAY(&end, nullptr);
+ auto readElapsedMicros = static_cast<uint32_t>(((end.tv_sec - begin.tv_sec) * 1000 * 1000)
+ + (end.tv_usec - begin.tv_usec));
+
+ if (!eagainThresholdMicros || (readElapsedMicros < eagainThresholdMicros)) {
+ if (retries++ < maxRecvRetries_) {
+ THRIFT_SLEEP_USEC(50);
+ goto try_again;
+ } else {
+ throw TTransportException(TTransportException::TIMED_OUT,
+ "THRIFT_EAGAIN (unavailable resources)");
+ }
+ } else {
+ // infer that timeout has been hit
+ throw TTransportException(TTransportException::TIMED_OUT, "THRIFT_EAGAIN (timed out)");
+ }
+ }
+
+ // If interrupted, try again
+ if (errno_copy == THRIFT_EINTR && retries++ < maxRecvRetries_) {
+ goto try_again;
+ }
+
+ if (errno_copy == THRIFT_ECONNRESET) {
+ return 0;
+ }
+
+ // This ish isn't open
+ if (errno_copy == THRIFT_ENOTCONN) {
+ throw TTransportException(TTransportException::NOT_OPEN, "THRIFT_ENOTCONN");
+ }
+
+ // Timed out!
+ if (errno_copy == THRIFT_ETIMEDOUT) {
+ throw TTransportException(TTransportException::TIMED_OUT, "THRIFT_ETIMEDOUT");
+ }
+
+ // Now it's not a try again case, but a real probblez
+ GlobalOutput.perror("TSocket::read() recv() " + getSocketInfo(), errno_copy);
+
+ // Some other error, whatevz
+ throw TTransportException(TTransportException::UNKNOWN, "Unknown", errno_copy);
+ }
+
+ return got;
+}
+
+void TSocket::write(const uint8_t* buf, uint32_t len) {
+ uint32_t sent = 0;
+
+ while (sent < len) {
+ uint32_t b = write_partial(buf + sent, len - sent);
+ if (b == 0) {
+ // This should only happen if the timeout set with SO_SNDTIMEO expired.
+ // Raise an exception.
+ throw TTransportException(TTransportException::TIMED_OUT, "send timeout expired");
+ }
+ sent += b;
+ }
+}
+
+uint32_t TSocket::write_partial(const uint8_t* buf, uint32_t len) {
+ if (socket_ == THRIFT_INVALID_SOCKET) {
+ throw TTransportException(TTransportException::NOT_OPEN, "Called write on non-open socket");
+ }
+
+ uint32_t sent = 0;
+
+ int flags = 0;
+#ifdef MSG_NOSIGNAL
+ // Note the use of MSG_NOSIGNAL to suppress SIGPIPE errors, instead we
+ // check for the THRIFT_EPIPE return condition and close the socket in that case
+ flags |= MSG_NOSIGNAL;
+#endif // ifdef MSG_NOSIGNAL
+
+ int b = static_cast<int>(send(socket_, const_cast_sockopt(buf + sent), len - sent, flags));
+
+ if (b < 0) {
+ if (THRIFT_GET_SOCKET_ERROR == THRIFT_EWOULDBLOCK || THRIFT_GET_SOCKET_ERROR == THRIFT_EAGAIN) {
+ return 0;
+ }
+ // Fail on a send error
+ int errno_copy = THRIFT_GET_SOCKET_ERROR;
+ GlobalOutput.perror("TSocket::write_partial() send() " + getSocketInfo(), errno_copy);
+
+ if (errno_copy == THRIFT_EPIPE || errno_copy == THRIFT_ECONNRESET
+ || errno_copy == THRIFT_ENOTCONN) {
+ throw TTransportException(TTransportException::NOT_OPEN, "write() send()", errno_copy);
+ }
+
+ throw TTransportException(TTransportException::UNKNOWN, "write() send()", errno_copy);
+ }
+
+ // Fail on blocked send
+ if (b == 0) {
+ throw TTransportException(TTransportException::NOT_OPEN, "Socket send returned 0.");
+ }
+ return b;
+}
+
+std::string TSocket::getHost() {
+ return host_;
+}
+
+int TSocket::getPort() {
+ return port_;
+}
+
+void TSocket::setHost(string host) {
+ host_ = host;
+}
+
+void TSocket::setPort(int port) {
+ port_ = port;
+}
+
+void TSocket::setLinger(bool on, int linger) {
+ lingerOn_ = on;
+ lingerVal_ = linger;
+ if (socket_ == THRIFT_INVALID_SOCKET) {
+ return;
+ }
+
+#ifndef _WIN32
+ struct linger l = {(lingerOn_ ? 1 : 0), lingerVal_};
+#else
+ struct linger l = {static_cast<u_short>(lingerOn_ ? 1 : 0), static_cast<u_short>(lingerVal_)};
+#endif
+
+ int ret = setsockopt(socket_, SOL_SOCKET, SO_LINGER, cast_sockopt(&l), sizeof(l));
+ if (ret == -1) {
+ int errno_copy
+ = THRIFT_GET_SOCKET_ERROR; // Copy THRIFT_GET_SOCKET_ERROR because we're allocating memory.
+ GlobalOutput.perror("TSocket::setLinger() setsockopt() " + getSocketInfo(), errno_copy);
+ }
+}
+
+void TSocket::setNoDelay(bool noDelay) {
+ noDelay_ = noDelay;
+ if (socket_ == THRIFT_INVALID_SOCKET || !path_.empty()) {
+ return;
+ }
+
+ // Set socket to NODELAY
+ int v = noDelay_ ? 1 : 0;
+ int ret = setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, cast_sockopt(&v), sizeof(v));
+ if (ret == -1) {
+ int errno_copy
+ = THRIFT_GET_SOCKET_ERROR; // Copy THRIFT_GET_SOCKET_ERROR because we're allocating memory.
+ GlobalOutput.perror("TSocket::setNoDelay() setsockopt() " + getSocketInfo(), errno_copy);
+ }
+}
+
+void TSocket::setConnTimeout(int ms) {
+ connTimeout_ = ms;
+}
+
+void setGenericTimeout(THRIFT_SOCKET s, int timeout_ms, int optname) {
+ if (timeout_ms < 0) {
+ char errBuf[512];
+ sprintf(errBuf, "TSocket::setGenericTimeout with negative input: %d\n", timeout_ms);
+ GlobalOutput(errBuf);
+ return;
+ }
+
+ if (s == THRIFT_INVALID_SOCKET) {
+ return;
+ }
+
+#ifdef _WIN32
+ DWORD platform_time = static_cast<DWORD>(timeout_ms);
+#else
+ struct timeval platform_time = {(int)(timeout_ms / 1000), (int)((timeout_ms % 1000) * 1000)};
+#endif
+
+ int ret = setsockopt(s, SOL_SOCKET, optname, cast_sockopt(&platform_time), sizeof(platform_time));
+ if (ret == -1) {
+ int errno_copy
+ = THRIFT_GET_SOCKET_ERROR; // Copy THRIFT_GET_SOCKET_ERROR because we're allocating memory.
+ GlobalOutput.perror("TSocket::setGenericTimeout() setsockopt() ", errno_copy);
+ }
+}
+
+void TSocket::setRecvTimeout(int ms) {
+ setGenericTimeout(socket_, ms, SO_RCVTIMEO);
+ recvTimeout_ = ms;
+}
+
+void TSocket::setSendTimeout(int ms) {
+ setGenericTimeout(socket_, ms, SO_SNDTIMEO);
+ sendTimeout_ = ms;
+}
+
+void TSocket::setKeepAlive(bool keepAlive) {
+ keepAlive_ = keepAlive;
+
+ if (socket_ == THRIFT_INVALID_SOCKET) {
+ return;
+ }
+
+ int value = keepAlive_;
+ int ret
+ = setsockopt(socket_, SOL_SOCKET, SO_KEEPALIVE, const_cast_sockopt(&value), sizeof(value));
+
+ if (ret == -1) {
+ int errno_copy
+ = THRIFT_GET_SOCKET_ERROR; // Copy THRIFT_GET_SOCKET_ERROR because we're allocating memory.
+ GlobalOutput.perror("TSocket::setKeepAlive() setsockopt() " + getSocketInfo(), errno_copy);
+ }
+}
+
+void TSocket::setMaxRecvRetries(int maxRecvRetries) {
+ maxRecvRetries_ = maxRecvRetries;
+}
+
+string TSocket::getSocketInfo() const {
+ std::ostringstream oss;
+ if (path_.empty()) {
+ if (host_.empty() || port_ == 0) {
+ oss << "<Host: " << getPeerAddress();
+ oss << " Port: " << getPeerPort() << ">";
+ } else {
+ oss << "<Host: " << host_ << " Port: " << port_ << ">";
+ }
+ } else {
+ oss << "<Path: " << path_ << ">";
+ }
+ return oss.str();
+}
+
+std::string TSocket::getPeerHost() const {
+ if (peerHost_.empty() && path_.empty()) {
+ struct sockaddr_storage addr;
+ struct sockaddr* addrPtr;
+ socklen_t addrLen;
+
+ if (socket_ == THRIFT_INVALID_SOCKET) {
+ return host_;
+ }
+
+ addrPtr = getCachedAddress(&addrLen);
+
+ if (addrPtr == nullptr) {
+ addrLen = sizeof(addr);
+ if (getpeername(socket_, (sockaddr*)&addr, &addrLen) != 0) {
+ return peerHost_;
+ }
+ addrPtr = (sockaddr*)&addr;
+
+ const_cast<TSocket&>(*this).setCachedAddress(addrPtr, addrLen);
+ }
+
+ char clienthost[NI_MAXHOST];
+ char clientservice[NI_MAXSERV];
+
+ getnameinfo((sockaddr*)addrPtr,
+ addrLen,
+ clienthost,
+ sizeof(clienthost),
+ clientservice,
+ sizeof(clientservice),
+ 0);
+
+ peerHost_ = clienthost;
+ }
+ return peerHost_;
+}
+
+std::string TSocket::getPeerAddress() const {
+ if (peerAddress_.empty() && path_.empty()) {
+ struct sockaddr_storage addr;
+ struct sockaddr* addrPtr;
+ socklen_t addrLen;
+
+ if (socket_ == THRIFT_INVALID_SOCKET) {
+ return peerAddress_;
+ }
+
+ addrPtr = getCachedAddress(&addrLen);
+
+ if (addrPtr == nullptr) {
+ addrLen = sizeof(addr);
+ if (getpeername(socket_, (sockaddr*)&addr, &addrLen) != 0) {
+ return peerAddress_;
+ }
+ addrPtr = (sockaddr*)&addr;
+
+ const_cast<TSocket&>(*this).setCachedAddress(addrPtr, addrLen);
+ }
+
+ char clienthost[NI_MAXHOST];
+ char clientservice[NI_MAXSERV];
+
+ getnameinfo(addrPtr,
+ addrLen,
+ clienthost,
+ sizeof(clienthost),
+ clientservice,
+ sizeof(clientservice),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+
+ peerAddress_ = clienthost;
+ peerPort_ = std::atoi(clientservice);
+ }
+ return peerAddress_;
+}
+
+int TSocket::getPeerPort() const {
+ getPeerAddress();
+ return peerPort_;
+}
+
+void TSocket::setCachedAddress(const sockaddr* addr, socklen_t len) {
+ if (!path_.empty()) {
+ return;
+ }
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ if (len == sizeof(sockaddr_in)) {
+ memcpy((void*)&cachedPeerAddr_.ipv4, (void*)addr, len);
+ }
+ break;
+
+ case AF_INET6:
+ if (len == sizeof(sockaddr_in6)) {
+ memcpy((void*)&cachedPeerAddr_.ipv6, (void*)addr, len);
+ }
+ break;
+ }
+ peerAddress_.clear();
+ peerHost_.clear();
+}
+
+sockaddr* TSocket::getCachedAddress(socklen_t* len) const {
+ switch (cachedPeerAddr_.ipv4.sin_family) {
+ case AF_INET:
+ *len = sizeof(sockaddr_in);
+ return (sockaddr*)&cachedPeerAddr_.ipv4;
+
+ case AF_INET6:
+ *len = sizeof(sockaddr_in6);
+ return (sockaddr*)&cachedPeerAddr_.ipv6;
+
+ default:
+ return nullptr;
+ }
+}
+
+bool TSocket::useLowMinRto_ = false;
+void TSocket::setUseLowMinRto(bool useLowMinRto) {
+ useLowMinRto_ = useLowMinRto;
+}
+bool TSocket::getUseLowMinRto() {
+ return useLowMinRto_;
+}
+
+const std::string TSocket::getOrigin() const {
+ std::ostringstream oss;
+ oss << getPeerHost() << ":" << getPeerPort();
+ return oss.str();
+}
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSocket.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSocket.h
new file mode 100644
index 000000000..b0e8ade83
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSocket.h
@@ -0,0 +1,353 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TSOCKET_H_
+#define _THRIFT_TRANSPORT_TSOCKET_H_ 1
+
+#include <string>
+
+#include <thrift/transport/TTransport.h>
+#include <thrift/transport/TVirtualTransport.h>
+#include <thrift/transport/TServerSocket.h>
+#include <thrift/transport/PlatformSocket.h>
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * TCP Socket implementation of the TTransport interface.
+ *
+ */
+class TSocket : public TVirtualTransport<TSocket> {
+public:
+ /**
+ * Constructs a new socket. Note that this does NOT actually connect the
+ * socket.
+ *
+ */
+ TSocket();
+
+ /**
+ * Constructs a new socket. Note that this does NOT actually connect the
+ * socket.
+ *
+ * @param host An IP address or hostname to connect to
+ * @param port The port to connect on
+ */
+ TSocket(const std::string& host, int port);
+
+ /**
+ * Constructs a new Unix domain socket.
+ * Note that this does NOT actually connect the socket.
+ *
+ * @param path The Unix domain socket e.g. "/tmp/ThriftTest.binary.thrift"
+ */
+ TSocket(const std::string& path);
+
+ /**
+ * Destroyes the socket object, closing it if necessary.
+ */
+ ~TSocket() override;
+
+ /**
+ * Whether the socket is alive.
+ *
+ * @return Is the socket alive?
+ */
+ bool isOpen() const override;
+
+ /**
+ * Checks whether there is more data available in the socket to read.
+ *
+ * This call blocks until at least one byte is available or the socket is closed.
+ */
+ bool peek() override;
+
+ /**
+ * Creates and opens the UNIX socket.
+ *
+ * @throws TTransportException If the socket could not connect
+ */
+ void open() override;
+
+ /**
+ * Shuts down communications on the socket.
+ */
+ void close() override;
+
+ /**
+ * Determines whether there is pending data to read or not.
+ *
+ * This call does not block.
+ * \throws TTransportException of types:
+ * NOT_OPEN means the socket has been closed
+ * UNKNOWN means something unexpected happened
+ * \returns true if there is pending data to read, false otherwise
+ */
+ virtual bool hasPendingDataToRead();
+
+ /**
+ * Reads from the underlying socket.
+ * \returns the number of bytes read or 0 indicates EOF
+ * \throws TTransportException of types:
+ * INTERRUPTED means the socket was interrupted
+ * out of a blocking call
+ * NOT_OPEN means the socket has been closed
+ * TIMED_OUT means the receive timeout expired
+ * UNKNOWN means something unexpected happened
+ */
+ virtual uint32_t read(uint8_t* buf, uint32_t len);
+
+ /**
+ * Writes to the underlying socket. Loops until done or fail.
+ */
+ virtual void write(const uint8_t* buf, uint32_t len);
+
+ /**
+ * Writes to the underlying socket. Does single send() and returns result.
+ */
+ virtual uint32_t write_partial(const uint8_t* buf, uint32_t len);
+
+ /**
+ * Get the host that the socket is connected to
+ *
+ * @return string host identifier
+ */
+ std::string getHost();
+
+ /**
+ * Get the port that the socket is connected to
+ *
+ * @return int port number
+ */
+ int getPort();
+
+ /**
+ * Set the host that socket will connect to
+ *
+ * @param host host identifier
+ */
+ void setHost(std::string host);
+
+ /**
+ * Set the port that socket will connect to
+ *
+ * @param port port number
+ */
+ void setPort(int port);
+
+ /**
+ * Controls whether the linger option is set on the socket.
+ *
+ * @param on Whether SO_LINGER is on
+ * @param linger If linger is active, the number of seconds to linger for
+ */
+ void setLinger(bool on, int linger);
+
+ /**
+ * Whether to enable/disable Nagle's algorithm.
+ *
+ * @param noDelay Whether or not to disable the algorithm.
+ * @return
+ */
+ void setNoDelay(bool noDelay);
+
+ /**
+ * Set the connect timeout
+ */
+ void setConnTimeout(int ms);
+
+ /**
+ * Set the receive timeout
+ */
+ void setRecvTimeout(int ms);
+
+ /**
+ * Set the send timeout
+ */
+ void setSendTimeout(int ms);
+
+ /**
+ * Set the max number of recv retries in case of an THRIFT_EAGAIN
+ * error
+ */
+ void setMaxRecvRetries(int maxRecvRetries);
+
+ /**
+ * Set SO_KEEPALIVE
+ */
+ void setKeepAlive(bool keepAlive);
+
+ /**
+ * Get socket information formatted as a string <Host: x Port: x>
+ */
+ std::string getSocketInfo() const;
+
+ /**
+ * Returns the DNS name of the host to which the socket is connected
+ */
+ std::string getPeerHost() const;
+
+ /**
+ * Returns the address of the host to which the socket is connected
+ */
+ std::string getPeerAddress() const;
+
+ /**
+ * Returns the port of the host to which the socket is connected
+ **/
+ int getPeerPort() const;
+
+ /**
+ * Returns the underlying socket file descriptor.
+ */
+ THRIFT_SOCKET getSocketFD() { return socket_; }
+
+ /**
+ * (Re-)initialize a TSocket for the supplied descriptor. This is only
+ * intended for use by TNonblockingServer -- other use may result in
+ * unfortunate surprises.
+ *
+ * @param fd the descriptor for an already-connected socket
+ */
+ void setSocketFD(THRIFT_SOCKET fd);
+
+ /*
+ * Returns a cached copy of the peer address.
+ */
+ sockaddr* getCachedAddress(socklen_t* len) const;
+
+ /**
+ * Sets whether to use a low minimum TCP retransmission timeout.
+ */
+ static void setUseLowMinRto(bool useLowMinRto);
+
+ /**
+ * Gets whether to use a low minimum TCP retransmission timeout.
+ */
+ static bool getUseLowMinRto();
+
+ /**
+ * Get the origin the socket is connected to
+ *
+ * @return string peer host identifier and port
+ */
+ const std::string getOrigin() const override;
+
+ /**
+ * Constructor to create socket from file descriptor.
+ */
+ TSocket(THRIFT_SOCKET socket);
+
+ /**
+ * Constructor to create socket from file descriptor that
+ * can be interrupted safely.
+ */
+ TSocket(THRIFT_SOCKET socket, std::shared_ptr<THRIFT_SOCKET> interruptListener);
+
+ /**
+ * Set a cache of the peer address (used when trivially available: e.g.
+ * accept() or connect()). Only caches IPV4 and IPV6; unset for others.
+ */
+ void setCachedAddress(const sockaddr* addr, socklen_t len);
+
+protected:
+ /** connect, called by open */
+ void openConnection(struct addrinfo* res);
+
+ /** Host to connect to */
+ std::string host_;
+
+ /** Port number to connect on */
+ int port_;
+
+ /** UNIX domain socket path */
+ std::string path_;
+
+ /** Underlying socket handle */
+ THRIFT_SOCKET socket_;
+
+ /** Peer hostname */
+ mutable std::string peerHost_;
+
+ /** Peer address */
+ mutable std::string peerAddress_;
+
+ /** Peer port */
+ mutable int peerPort_;
+
+ /**
+ * A shared socket pointer that will interrupt a blocking read if data
+ * becomes available on it
+ */
+ std::shared_ptr<THRIFT_SOCKET> interruptListener_;
+
+ /** Connect timeout in ms */
+ int connTimeout_;
+
+ /** Send timeout in ms */
+ int sendTimeout_;
+
+ /** Recv timeout in ms */
+ int recvTimeout_;
+
+ /** Keep alive on */
+ bool keepAlive_;
+
+ /** Linger on */
+ bool lingerOn_;
+
+ /** Linger val */
+ int lingerVal_;
+
+ /** Nodelay */
+ bool noDelay_;
+
+ /** Recv EGAIN retries */
+ int maxRecvRetries_;
+
+ /** Cached peer address */
+ union {
+ sockaddr_in ipv4;
+ sockaddr_in6 ipv6;
+ } cachedPeerAddr_;
+
+ /** Whether to use low minimum TCP retransmission timeout */
+ static bool useLowMinRto_;
+
+private:
+ void unix_open();
+ void local_open();
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_TSOCKET_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSocketPool.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSocketPool.cpp
new file mode 100644
index 000000000..b6e79d3f6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSocketPool.cpp
@@ -0,0 +1,266 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+
+#include <algorithm>
+#include <iostream>
+#include <memory>
+#if __cplusplus >= 201703L
+#include <random>
+#endif
+
+#include <thrift/transport/TSocketPool.h>
+
+using std::pair;
+using std::string;
+using std::vector;
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+using std::shared_ptr;
+
+/**
+ * TSocketPoolServer implementation
+ *
+ */
+TSocketPoolServer::TSocketPoolServer()
+ : host_(""), port_(0), socket_(THRIFT_INVALID_SOCKET), lastFailTime_(0), consecutiveFailures_(0) {
+}
+
+/**
+ * Constructor for TSocketPool server
+ */
+TSocketPoolServer::TSocketPoolServer(const string& host, int port)
+ : host_(host),
+ port_(port),
+ socket_(THRIFT_INVALID_SOCKET),
+ lastFailTime_(0),
+ consecutiveFailures_(0) {
+}
+
+/**
+ * TSocketPool implementation.
+ *
+ */
+
+TSocketPool::TSocketPool()
+ : TSocket(),
+ numRetries_(1),
+ retryInterval_(60),
+ maxConsecutiveFailures_(1),
+ randomize_(true),
+ alwaysTryLast_(true) {
+}
+
+TSocketPool::TSocketPool(const vector<string>& hosts, const vector<int>& ports)
+ : TSocket(),
+ numRetries_(1),
+ retryInterval_(60),
+ maxConsecutiveFailures_(1),
+ randomize_(true),
+ alwaysTryLast_(true) {
+ if (hosts.size() != ports.size()) {
+ GlobalOutput("TSocketPool::TSocketPool: hosts.size != ports.size");
+ throw TTransportException(TTransportException::BAD_ARGS);
+ }
+
+ for (unsigned int i = 0; i < hosts.size(); ++i) {
+ addServer(hosts[i], ports[i]);
+ }
+}
+
+TSocketPool::TSocketPool(const vector<pair<string, int> >& servers)
+ : TSocket(),
+ numRetries_(1),
+ retryInterval_(60),
+ maxConsecutiveFailures_(1),
+ randomize_(true),
+ alwaysTryLast_(true) {
+ for (const auto & server : servers) {
+ addServer(server.first, server.second);
+ }
+}
+
+TSocketPool::TSocketPool(const vector<shared_ptr<TSocketPoolServer> >& servers)
+ : TSocket(),
+ servers_(servers),
+ numRetries_(1),
+ retryInterval_(60),
+ maxConsecutiveFailures_(1),
+ randomize_(true),
+ alwaysTryLast_(true) {
+}
+
+TSocketPool::TSocketPool(const string& host, int port)
+ : TSocket(),
+ numRetries_(1),
+ retryInterval_(60),
+ maxConsecutiveFailures_(1),
+ randomize_(true),
+ alwaysTryLast_(true) {
+ addServer(host, port);
+}
+
+TSocketPool::~TSocketPool() {
+ vector<shared_ptr<TSocketPoolServer> >::const_iterator iter = servers_.begin();
+ vector<shared_ptr<TSocketPoolServer> >::const_iterator iterEnd = servers_.end();
+ for (; iter != iterEnd; ++iter) {
+ setCurrentServer(*iter);
+ TSocketPool::close();
+ }
+}
+
+void TSocketPool::addServer(const string& host, int port) {
+ servers_.push_back(std::make_shared<TSocketPoolServer>(host, port));
+}
+
+void TSocketPool::addServer(shared_ptr<TSocketPoolServer>& server) {
+ if (server) {
+ servers_.push_back(server);
+ }
+}
+
+void TSocketPool::setServers(const vector<shared_ptr<TSocketPoolServer> >& servers) {
+ servers_ = servers;
+}
+
+void TSocketPool::getServers(vector<shared_ptr<TSocketPoolServer> >& servers) {
+ servers = servers_;
+}
+
+void TSocketPool::setNumRetries(int numRetries) {
+ numRetries_ = numRetries;
+}
+
+void TSocketPool::setRetryInterval(int retryInterval) {
+ retryInterval_ = retryInterval;
+}
+
+void TSocketPool::setMaxConsecutiveFailures(int maxConsecutiveFailures) {
+ maxConsecutiveFailures_ = maxConsecutiveFailures;
+}
+
+void TSocketPool::setRandomize(bool randomize) {
+ randomize_ = randomize;
+}
+
+void TSocketPool::setAlwaysTryLast(bool alwaysTryLast) {
+ alwaysTryLast_ = alwaysTryLast;
+}
+
+void TSocketPool::setCurrentServer(const shared_ptr<TSocketPoolServer>& server) {
+ currentServer_ = server;
+ host_ = server->host_;
+ port_ = server->port_;
+ socket_ = server->socket_;
+}
+
+/**
+ * This function throws an exception if socket open fails. When socket
+ * opens fails, the socket in the current server is reset.
+ */
+/* TODO: without apc we ignore a lot of functionality from the php version */
+void TSocketPool::open() {
+
+ size_t numServers = servers_.size();
+ if (numServers == 0) {
+ socket_ = THRIFT_INVALID_SOCKET;
+ throw TTransportException(TTransportException::NOT_OPEN);
+ }
+
+ if (isOpen()) {
+ return;
+ }
+
+ if (randomize_ && numServers > 1) {
+#if __cplusplus >= 201703L
+ std::random_device rng;
+ std::mt19937 urng(rng());
+ std::shuffle(servers_.begin(), servers_.end(), urng);
+#else
+ std::random_shuffle(servers_.begin(), servers_.end());
+#endif
+ }
+
+ for (size_t i = 0; i < numServers; ++i) {
+
+ shared_ptr<TSocketPoolServer>& server = servers_[i];
+ // Impersonate the server socket
+ setCurrentServer(server);
+
+ if (isOpen()) {
+ // already open means we're done
+ return;
+ }
+
+ bool retryIntervalPassed = (server->lastFailTime_ == 0);
+ bool isLastServer = alwaysTryLast_ ? (i == (numServers - 1)) : false;
+
+ if (server->lastFailTime_ > 0) {
+ // The server was marked as down, so check if enough time has elapsed to retry
+ time_t elapsedTime = time(nullptr) - server->lastFailTime_;
+ if (elapsedTime > retryInterval_) {
+ retryIntervalPassed = true;
+ }
+ }
+
+ if (retryIntervalPassed || isLastServer) {
+ for (int j = 0; j < numRetries_; ++j) {
+ try {
+ TSocket::open();
+ } catch (const TException &e) {
+ string errStr = "TSocketPool::open failed " + getSocketInfo() + ": " + e.what();
+ GlobalOutput(errStr.c_str());
+ socket_ = THRIFT_INVALID_SOCKET;
+ continue;
+ }
+
+ // Copy over the opened socket so that we can keep it persistent
+ server->socket_ = socket_;
+ // reset lastFailTime_ is required
+ server->lastFailTime_ = 0;
+ // success
+ return;
+ }
+
+ ++server->consecutiveFailures_;
+ if (server->consecutiveFailures_ > maxConsecutiveFailures_) {
+ // Mark server as down
+ server->consecutiveFailures_ = 0;
+ server->lastFailTime_ = time(nullptr);
+ }
+ }
+ }
+
+ GlobalOutput("TSocketPool::open: all connections failed");
+ throw TTransportException(TTransportException::NOT_OPEN);
+}
+
+void TSocketPool::close() {
+ TSocket::close();
+ if (currentServer_) {
+ currentServer_->socket_ = THRIFT_INVALID_SOCKET;
+ }
+}
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSocketPool.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSocketPool.h
new file mode 100644
index 000000000..97a2b9063
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TSocketPool.h
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TSOCKETPOOL_H_
+#define _THRIFT_TRANSPORT_TSOCKETPOOL_H_ 1
+
+#include <vector>
+#include <thrift/transport/TSocket.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * Class to hold server information for TSocketPool
+ *
+ */
+class TSocketPoolServer {
+
+public:
+ /**
+ * Default constructor for server info
+ */
+ TSocketPoolServer();
+
+ /**
+ * Constructor for TSocketPool server
+ */
+ TSocketPoolServer(const std::string& host, int port);
+
+ // Host name
+ std::string host_;
+
+ // Port to connect on
+ int port_;
+
+ // Socket for the server
+ THRIFT_SOCKET socket_;
+
+ // Last time connecting to this server failed
+ time_t lastFailTime_;
+
+ // Number of consecutive times connecting to this server failed
+ int consecutiveFailures_;
+};
+
+/**
+ * TCP Socket implementation of the TTransport interface.
+ *
+ */
+class TSocketPool : public TSocket {
+
+public:
+ /**
+ * Socket pool constructor
+ */
+ TSocketPool();
+
+ /**
+ * Socket pool constructor
+ *
+ * @param hosts list of host names
+ * @param ports list of port names
+ */
+ TSocketPool(const std::vector<std::string>& hosts, const std::vector<int>& ports);
+
+ /**
+ * Socket pool constructor
+ *
+ * @param servers list of pairs of host name and port
+ */
+ TSocketPool(const std::vector<std::pair<std::string, int> >& servers);
+
+ /**
+ * Socket pool constructor
+ *
+ * @param servers list of TSocketPoolServers
+ */
+ TSocketPool(const std::vector<std::shared_ptr<TSocketPoolServer> >& servers);
+
+ /**
+ * Socket pool constructor
+ *
+ * @param host single host
+ * @param port single port
+ */
+ TSocketPool(const std::string& host, int port);
+
+ /**
+ * Destroyes the socket object, closing it if necessary.
+ */
+ ~TSocketPool() override;
+
+ /**
+ * Add a server to the pool
+ */
+ void addServer(const std::string& host, int port);
+
+ /**
+ * Add a server to the pool
+ */
+ void addServer(std::shared_ptr<TSocketPoolServer>& server);
+
+ /**
+ * Set list of servers in this pool
+ */
+ void setServers(const std::vector<std::shared_ptr<TSocketPoolServer> >& servers);
+
+ /**
+ * Get list of servers in this pool
+ */
+ void getServers(std::vector<std::shared_ptr<TSocketPoolServer> >& servers);
+
+ /**
+ * Sets how many times to keep retrying a host in the connect function.
+ */
+ void setNumRetries(int numRetries);
+
+ /**
+ * Sets how long to wait until retrying a host if it was marked down
+ */
+ void setRetryInterval(int retryInterval);
+
+ /**
+ * Sets how many times to keep retrying a host before marking it as down.
+ */
+ void setMaxConsecutiveFailures(int maxConsecutiveFailures);
+
+ /**
+ * Turns randomization in connect order on or off.
+ */
+ void setRandomize(bool randomize);
+
+ /**
+ * Whether to always try the last server.
+ */
+ void setAlwaysTryLast(bool alwaysTryLast);
+
+ /**
+ * Creates and opens the UNIX socket.
+ */
+ void open() override;
+
+ /*
+ * Closes the UNIX socket
+ */
+ void close() override;
+
+protected:
+ void setCurrentServer(const std::shared_ptr<TSocketPoolServer>& server);
+
+ /** List of servers to connect to */
+ std::vector<std::shared_ptr<TSocketPoolServer> > servers_;
+
+ /** Current server */
+ std::shared_ptr<TSocketPoolServer> currentServer_;
+
+ /** How many times to retry each host in connect */
+ int numRetries_;
+
+ /** Retry interval in seconds, how long to not try a host if it has been
+ * marked as down.
+ */
+ time_t retryInterval_;
+
+ /** Max consecutive failures before marking a host down. */
+ int maxConsecutiveFailures_;
+
+ /** Try hosts in order? or Randomized? */
+ bool randomize_;
+
+ /** Always try last host, even if marked down? */
+ bool alwaysTryLast_;
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_TSOCKETPOOL_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransport.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransport.h
new file mode 100644
index 000000000..891bfe151
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransport.h
@@ -0,0 +1,271 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TTRANSPORT_H_
+#define _THRIFT_TRANSPORT_TTRANSPORT_H_ 1
+
+#include <thrift/Thrift.h>
+#include <thrift/transport/TTransportException.h>
+#include <memory>
+#include <string>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * Helper template to hoist readAll implementation out of TTransport
+ */
+template <class Transport_>
+uint32_t readAll(Transport_& trans, uint8_t* buf, uint32_t len) {
+ uint32_t have = 0;
+ uint32_t get = 0;
+
+ while (have < len) {
+ get = trans.read(buf + have, len - have);
+ if (get <= 0) {
+ throw TTransportException(TTransportException::END_OF_FILE, "No more data to read.");
+ }
+ have += get;
+ }
+
+ return have;
+}
+
+/**
+ * Generic interface for a method of transporting data. A TTransport may be
+ * capable of either reading or writing, but not necessarily both.
+ *
+ */
+class TTransport {
+public:
+ /**
+ * Virtual deconstructor.
+ */
+ virtual ~TTransport() = default;
+
+ /**
+ * Whether this transport is open.
+ */
+ virtual bool isOpen() const { return false; }
+
+ /**
+ * Tests whether there is more data to read or if the remote side is
+ * still open. By default this is true whenever the transport is open,
+ * but implementations should add logic to test for this condition where
+ * possible (i.e. on a socket).
+ * This is used by a server to check if it should listen for another
+ * request.
+ */
+ virtual bool peek() { return isOpen(); }
+
+ /**
+ * Opens the transport for communications.
+ *
+ * @return bool Whether the transport was successfully opened
+ * @throws TTransportException if opening failed
+ */
+ virtual void open() {
+ throw TTransportException(TTransportException::NOT_OPEN, "Cannot open base TTransport.");
+ }
+
+ /**
+ * Closes the transport.
+ */
+ virtual void close() {
+ throw TTransportException(TTransportException::NOT_OPEN, "Cannot close base TTransport.");
+ }
+
+ /**
+ * Attempt to read up to the specified number of bytes into the string.
+ *
+ * @param buf Reference to the location to write the data
+ * @param len How many bytes to read
+ * @return How many bytes were actually read
+ * @throws TTransportException If an error occurs
+ */
+ uint32_t read(uint8_t* buf, uint32_t len) {
+ T_VIRTUAL_CALL();
+ return read_virt(buf, len);
+ }
+ virtual uint32_t read_virt(uint8_t* /* buf */, uint32_t /* len */) {
+ throw TTransportException(TTransportException::NOT_OPEN, "Base TTransport cannot read.");
+ }
+
+ /**
+ * Reads the given amount of data in its entirety no matter what.
+ *
+ * @param s Reference to location for read data
+ * @param len How many bytes to read
+ * @return How many bytes read, which must be equal to size
+ * @throws TTransportException If insufficient data was read
+ */
+ uint32_t readAll(uint8_t* buf, uint32_t len) {
+ T_VIRTUAL_CALL();
+ return readAll_virt(buf, len);
+ }
+ virtual uint32_t readAll_virt(uint8_t* buf, uint32_t len) {
+ return apache::thrift::transport::readAll(*this, buf, len);
+ }
+
+ /**
+ * Called when read is completed.
+ * This can be over-ridden to perform a transport-specific action
+ * e.g. logging the request to a file
+ *
+ * @return number of bytes read if available, 0 otherwise.
+ */
+ virtual uint32_t readEnd() {
+ // default behaviour is to do nothing
+ return 0;
+ }
+
+ /**
+ * Writes the string in its entirety to the buffer.
+ *
+ * Note: You must call flush() to ensure the data is actually written,
+ * and available to be read back in the future. Destroying a TTransport
+ * object does not automatically flush pending data--if you destroy a
+ * TTransport object with written but unflushed data, that data may be
+ * discarded.
+ *
+ * @param buf The data to write out
+ * @throws TTransportException if an error occurs
+ */
+ void write(const uint8_t* buf, uint32_t len) {
+ T_VIRTUAL_CALL();
+ write_virt(buf, len);
+ }
+ virtual void write_virt(const uint8_t* /* buf */, uint32_t /* len */) {
+ throw TTransportException(TTransportException::NOT_OPEN, "Base TTransport cannot write.");
+ }
+
+ /**
+ * Called when write is completed.
+ * This can be over-ridden to perform a transport-specific action
+ * at the end of a request.
+ *
+ * @return number of bytes written if available, 0 otherwise
+ */
+ virtual uint32_t writeEnd() {
+ // default behaviour is to do nothing
+ return 0;
+ }
+
+ /**
+ * Flushes any pending data to be written. Typically used with buffered
+ * transport mechanisms.
+ *
+ * @throws TTransportException if an error occurs
+ */
+ virtual void flush() {
+ // default behaviour is to do nothing
+ }
+
+ /**
+ * Attempts to return a pointer to \c len bytes, possibly copied into \c buf.
+ * Does not consume the bytes read (i.e.: a later read will return the same
+ * data). This method is meant to support protocols that need to read
+ * variable-length fields. They can attempt to borrow the maximum amount of
+ * data that they will need, then consume (see next method) what they
+ * actually use. Some transports will not support this method and others
+ * will fail occasionally, so protocols must be prepared to use read if
+ * borrow fails.
+ *
+ * @oaram buf A buffer where the data can be stored if needed.
+ * If borrow doesn't return buf, then the contents of
+ * buf after the call are undefined. This parameter may be
+ * NULL to indicate that the caller is not supplying storage,
+ * but would like a pointer into an internal buffer, if
+ * available.
+ * @param len *len should initially contain the number of bytes to borrow.
+ * If borrow succeeds, *len will contain the number of bytes
+ * available in the returned pointer. This will be at least
+ * what was requested, but may be more if borrow returns
+ * a pointer to an internal buffer, rather than buf.
+ * If borrow fails, the contents of *len are undefined.
+ * @return If the borrow succeeds, return a pointer to the borrowed data.
+ * This might be equal to \c buf, or it might be a pointer into
+ * the transport's internal buffers.
+ * @throws TTransportException if an error occurs
+ */
+ const uint8_t* borrow(uint8_t* buf, uint32_t* len) {
+ T_VIRTUAL_CALL();
+ return borrow_virt(buf, len);
+ }
+ virtual const uint8_t* borrow_virt(uint8_t* /* buf */, uint32_t* /* len */) { return nullptr; }
+
+ /**
+ * Remove len bytes from the transport. This should always follow a borrow
+ * of at least len bytes, and should always succeed.
+ * TODO(dreiss): Is there any transport that could borrow but fail to
+ * consume, or that would require a buffer to dump the consumed data?
+ *
+ * @param len How many bytes to consume
+ * @throws TTransportException If an error occurs
+ */
+ void consume(uint32_t len) {
+ T_VIRTUAL_CALL();
+ consume_virt(len);
+ }
+ virtual void consume_virt(uint32_t /* len */) {
+ throw TTransportException(TTransportException::NOT_OPEN, "Base TTransport cannot consume.");
+ }
+
+ /**
+ * Returns the origin of the transports call. The value depends on the
+ * transport used. An IP based transport for example will return the
+ * IP address of the client making the request.
+ * If the transport doesn't know the origin Unknown is returned.
+ *
+ * The returned value can be used in a log message for example
+ */
+ virtual const std::string getOrigin() const { return "Unknown"; }
+
+protected:
+ /**
+ * Simple constructor.
+ */
+ TTransport() = default;
+};
+
+/**
+ * Generic factory class to make an input and output transport out of a
+ * source transport. Commonly used inside servers to make input and output
+ * streams out of raw clients.
+ *
+ */
+class TTransportFactory {
+public:
+ TTransportFactory() = default;
+
+ virtual ~TTransportFactory() = default;
+
+ /**
+ * Default implementation does nothing, just returns the transport given.
+ */
+ virtual std::shared_ptr<TTransport> getTransport(std::shared_ptr<TTransport> trans) {
+ return trans;
+ }
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_TTRANSPORT_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransportException.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransportException.cpp
new file mode 100644
index 000000000..a527317e0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransportException.cpp
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/transport/TTransportException.h>
+#include <cstring>
+
+#include <thrift/thrift-config.h>
+
+using std::string;
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+const char* TTransportException::what() const noexcept {
+ if (message_.empty()) {
+ switch (type_) {
+ case UNKNOWN:
+ return "TTransportException: Unknown transport exception";
+ case NOT_OPEN:
+ return "TTransportException: Transport not open";
+ case TIMED_OUT:
+ return "TTransportException: Timed out";
+ case END_OF_FILE:
+ return "TTransportException: End of file";
+ case INTERRUPTED:
+ return "TTransportException: Interrupted";
+ case BAD_ARGS:
+ return "TTransportException: Invalid arguments";
+ case CORRUPTED_DATA:
+ return "TTransportException: Corrupted Data";
+ case INTERNAL_ERROR:
+ return "TTransportException: Internal error";
+ default:
+ return "TTransportException: (Invalid exception type)";
+ }
+ } else {
+ return message_.c_str();
+ }
+}
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransportException.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransportException.h
new file mode 100644
index 000000000..38b75211f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransportException.h
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TTRANSPORTEXCEPTION_H_
+#define _THRIFT_TRANSPORT_TTRANSPORTEXCEPTION_H_ 1
+
+#include <boost/numeric/conversion/cast.hpp>
+#include <string>
+#include <thrift/Thrift.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * Class to encapsulate all the possible types of transport errors that may
+ * occur in various transport systems. This provides a sort of generic
+ * wrapper around the vague UNIX E_ error codes that lets a common code
+ * base of error handling to be used for various types of transports, i.e.
+ * pipes etc.
+ *
+ */
+class TTransportException : public apache::thrift::TException {
+public:
+ /**
+ * Error codes for the various types of exceptions.
+ */
+ enum TTransportExceptionType {
+ UNKNOWN = 0,
+ NOT_OPEN = 1,
+ TIMED_OUT = 2,
+ END_OF_FILE = 3,
+ INTERRUPTED = 4,
+ BAD_ARGS = 5,
+ CORRUPTED_DATA = 6,
+ INTERNAL_ERROR = 7
+ };
+
+ TTransportException() : apache::thrift::TException(), type_(UNKNOWN) {}
+
+ TTransportException(TTransportExceptionType type) : apache::thrift::TException(), type_(type) {}
+
+ TTransportException(const std::string& message)
+ : apache::thrift::TException(message), type_(UNKNOWN) {}
+
+ TTransportException(TTransportExceptionType type, const std::string& message)
+ : apache::thrift::TException(message), type_(type) {}
+
+ TTransportException(TTransportExceptionType type, const std::string& message, int errno_copy)
+ : apache::thrift::TException(message + ": " + TOutput::strerror_s(errno_copy)), type_(type) {}
+
+ ~TTransportException() noexcept override = default;
+
+ /**
+ * Returns an error code that provides information about the type of error
+ * that has occurred.
+ *
+ * @return Error code
+ */
+ TTransportExceptionType getType() const noexcept { return type_; }
+
+ const char* what() const noexcept override;
+
+protected:
+ /** Just like strerror_r but returns a C++ string object. */
+ std::string strerror_s(int errno_copy);
+
+ /** Error code */
+ TTransportExceptionType type_;
+};
+
+/**
+ * Legacy code in transport implementations have overflow issues
+ * that need to be enforced.
+ */
+template <typename To, typename From> To safe_numeric_cast(From i) {
+ try {
+ return boost::numeric_cast<To>(i);
+ }
+ catch (const std::bad_cast& bc) {
+ throw TTransportException(TTransportException::CORRUPTED_DATA,
+ bc.what());
+ }
+}
+
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_TTRANSPORTEXCEPTION_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransportUtils.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransportUtils.cpp
new file mode 100644
index 000000000..69372f3e2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransportUtils.cpp
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/transport/TTransportUtils.h>
+
+using std::string;
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+uint32_t TPipedTransport::read(uint8_t* buf, uint32_t len) {
+ uint32_t need = len;
+
+ // We don't have enough data yet
+ if (rLen_ - rPos_ < need) {
+ // Copy out whatever we have
+ if (rLen_ - rPos_ > 0) {
+ memcpy(buf, rBuf_ + rPos_, rLen_ - rPos_);
+ need -= rLen_ - rPos_;
+ buf += rLen_ - rPos_;
+ rPos_ = rLen_;
+ }
+
+ // Double the size of the underlying buffer if it is full
+ if (rLen_ == rBufSize_) {
+ rBufSize_ *= 2;
+ auto *tmpBuf = (uint8_t*)std::realloc(rBuf_, sizeof(uint8_t) * rBufSize_);
+ if (tmpBuf == nullptr) {
+ throw std::bad_alloc();
+ }
+ rBuf_ = tmpBuf;
+ }
+
+ // try to fill up the buffer
+ rLen_ += srcTrans_->read(rBuf_ + rPos_, rBufSize_ - rPos_);
+ }
+
+ // Hand over whatever we have
+ uint32_t give = need;
+ if (rLen_ - rPos_ < give) {
+ give = rLen_ - rPos_;
+ }
+ if (give > 0) {
+ memcpy(buf, rBuf_ + rPos_, give);
+ rPos_ += give;
+ need -= give;
+ }
+
+ return (len - need);
+}
+
+void TPipedTransport::write(const uint8_t* buf, uint32_t len) {
+ if (len == 0) {
+ return;
+ }
+
+ // Make the buffer as big as it needs to be
+ if ((len + wLen_) >= wBufSize_) {
+ uint32_t newBufSize = wBufSize_ * 2;
+ while ((len + wLen_) >= newBufSize) {
+ newBufSize *= 2;
+ }
+ auto *tmpBuf= (uint8_t*)std::realloc(wBuf_, sizeof(uint8_t) * newBufSize);
+ if (tmpBuf == nullptr) {
+ throw std::bad_alloc();
+ }
+ wBuf_ = tmpBuf;
+
+ wBufSize_ = newBufSize;
+ }
+
+ // Copy into the buffer
+ memcpy(wBuf_ + wLen_, buf, len);
+ wLen_ += len;
+}
+
+void TPipedTransport::flush() {
+ // Write out any data waiting in the write buffer
+ if (wLen_ > 0) {
+ srcTrans_->write(wBuf_, wLen_);
+ wLen_ = 0;
+ }
+
+ // Flush the underlying transport
+ srcTrans_->flush();
+}
+
+TPipedFileReaderTransport::TPipedFileReaderTransport(
+ std::shared_ptr<TFileReaderTransport> srcTrans,
+ std::shared_ptr<TTransport> dstTrans)
+ : TPipedTransport(srcTrans, dstTrans), srcTrans_(srcTrans) {
+}
+
+TPipedFileReaderTransport::~TPipedFileReaderTransport() = default;
+
+bool TPipedFileReaderTransport::isOpen() const {
+ return TPipedTransport::isOpen();
+}
+
+bool TPipedFileReaderTransport::peek() {
+ return TPipedTransport::peek();
+}
+
+void TPipedFileReaderTransport::open() {
+ TPipedTransport::open();
+}
+
+void TPipedFileReaderTransport::close() {
+ TPipedTransport::close();
+}
+
+uint32_t TPipedFileReaderTransport::read(uint8_t* buf, uint32_t len) {
+ return TPipedTransport::read(buf, len);
+}
+
+uint32_t TPipedFileReaderTransport::readAll(uint8_t* buf, uint32_t len) {
+ uint32_t have = 0;
+ uint32_t get = 0;
+
+ while (have < len) {
+ get = read(buf + have, len - have);
+ if (get <= 0) {
+ throw TEOFException();
+ }
+ have += get;
+ }
+
+ return have;
+}
+
+uint32_t TPipedFileReaderTransport::readEnd() {
+ return TPipedTransport::readEnd();
+}
+
+void TPipedFileReaderTransport::write(const uint8_t* buf, uint32_t len) {
+ TPipedTransport::write(buf, len);
+}
+
+uint32_t TPipedFileReaderTransport::writeEnd() {
+ return TPipedTransport::writeEnd();
+}
+
+void TPipedFileReaderTransport::flush() {
+ TPipedTransport::flush();
+}
+
+int32_t TPipedFileReaderTransport::getReadTimeout() {
+ return srcTrans_->getReadTimeout();
+}
+
+void TPipedFileReaderTransport::setReadTimeout(int32_t readTimeout) {
+ srcTrans_->setReadTimeout(readTimeout);
+}
+
+uint32_t TPipedFileReaderTransport::getNumChunks() {
+ return srcTrans_->getNumChunks();
+}
+
+uint32_t TPipedFileReaderTransport::getCurChunk() {
+ return srcTrans_->getCurChunk();
+}
+
+void TPipedFileReaderTransport::seekToChunk(int32_t chunk) {
+ srcTrans_->seekToChunk(chunk);
+}
+
+void TPipedFileReaderTransport::seekToEnd() {
+ srcTrans_->seekToEnd();
+}
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransportUtils.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransportUtils.h
new file mode 100644
index 000000000..28c93d2a1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TTransportUtils.h
@@ -0,0 +1,314 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TTRANSPORTUTILS_H_
+#define _THRIFT_TRANSPORT_TTRANSPORTUTILS_H_ 1
+
+#include <cstdlib>
+#include <cstring>
+#include <string>
+#include <algorithm>
+#include <thrift/transport/TTransport.h>
+// Include the buffered transports that used to be defined here.
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/transport/TFileTransport.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * The null transport is a dummy transport that doesn't actually do anything.
+ * It's sort of an analogy to /dev/null, you can never read anything from it
+ * and it will let you write anything you want to it, though it won't actually
+ * go anywhere.
+ *
+ */
+class TNullTransport : public TVirtualTransport<TNullTransport> {
+public:
+ TNullTransport() = default;
+
+ ~TNullTransport() override = default;
+
+ bool isOpen() const override { return true; }
+
+ void open() override {}
+
+ void write(const uint8_t* /* buf */, uint32_t /* len */) { return; }
+};
+
+/**
+ * TPipedTransport. This transport allows piping of a request from one
+ * transport to another either when readEnd() or writeEnd(). The typical
+ * use case for this is to log a request or a reply to disk.
+ * The underlying buffer expands to a keep a copy of the entire
+ * request/response.
+ *
+ */
+class TPipedTransport : virtual public TTransport {
+public:
+ TPipedTransport(std::shared_ptr<TTransport> srcTrans, std::shared_ptr<TTransport> dstTrans)
+ : srcTrans_(srcTrans),
+ dstTrans_(dstTrans),
+ rBufSize_(512),
+ rPos_(0),
+ rLen_(0),
+ wBufSize_(512),
+ wLen_(0) {
+
+ // default is to to pipe the request when readEnd() is called
+ pipeOnRead_ = true;
+ pipeOnWrite_ = false;
+
+ rBuf_ = (uint8_t*)std::malloc(sizeof(uint8_t) * rBufSize_);
+ if (rBuf_ == nullptr) {
+ throw std::bad_alloc();
+ }
+ wBuf_ = (uint8_t*)std::malloc(sizeof(uint8_t) * wBufSize_);
+ if (wBuf_ == nullptr) {
+ throw std::bad_alloc();
+ }
+ }
+
+ TPipedTransport(std::shared_ptr<TTransport> srcTrans,
+ std::shared_ptr<TTransport> dstTrans,
+ uint32_t sz)
+ : srcTrans_(srcTrans),
+ dstTrans_(dstTrans),
+ rBufSize_(512),
+ rPos_(0),
+ rLen_(0),
+ wBufSize_(sz),
+ wLen_(0) {
+
+ rBuf_ = (uint8_t*)std::malloc(sizeof(uint8_t) * rBufSize_);
+ if (rBuf_ == nullptr) {
+ throw std::bad_alloc();
+ }
+ wBuf_ = (uint8_t*)std::malloc(sizeof(uint8_t) * wBufSize_);
+ if (wBuf_ == nullptr) {
+ throw std::bad_alloc();
+ }
+ }
+
+ ~TPipedTransport() override {
+ std::free(rBuf_);
+ std::free(wBuf_);
+ }
+
+ bool isOpen() const override { return srcTrans_->isOpen(); }
+
+ bool peek() override {
+ if (rPos_ >= rLen_) {
+ // Double the size of the underlying buffer if it is full
+ if (rLen_ == rBufSize_) {
+ rBufSize_ *= 2;
+ auto * tmpBuf = (uint8_t*)std::realloc(rBuf_, sizeof(uint8_t) * rBufSize_);
+ if (tmpBuf == nullptr) {
+ throw std::bad_alloc();
+ }
+ rBuf_ = tmpBuf;
+ }
+
+ // try to fill up the buffer
+ rLen_ += srcTrans_->read(rBuf_ + rPos_, rBufSize_ - rPos_);
+ }
+ return (rLen_ > rPos_);
+ }
+
+ void open() override { srcTrans_->open(); }
+
+ void close() override { srcTrans_->close(); }
+
+ void setPipeOnRead(bool pipeVal) { pipeOnRead_ = pipeVal; }
+
+ void setPipeOnWrite(bool pipeVal) { pipeOnWrite_ = pipeVal; }
+
+ uint32_t read(uint8_t* buf, uint32_t len);
+
+ uint32_t readEnd() override {
+
+ if (pipeOnRead_) {
+ dstTrans_->write(rBuf_, rPos_);
+ dstTrans_->flush();
+ }
+
+ srcTrans_->readEnd();
+
+ // If requests are being pipelined, copy down our read-ahead data,
+ // then reset our state.
+ int read_ahead = rLen_ - rPos_;
+ uint32_t bytes = rPos_;
+ memcpy(rBuf_, rBuf_ + rPos_, read_ahead);
+ rPos_ = 0;
+ rLen_ = read_ahead;
+
+ return bytes;
+ }
+
+ void write(const uint8_t* buf, uint32_t len);
+
+ uint32_t writeEnd() override {
+ if (pipeOnWrite_) {
+ dstTrans_->write(wBuf_, wLen_);
+ dstTrans_->flush();
+ }
+ return wLen_;
+ }
+
+ void flush() override;
+
+ std::shared_ptr<TTransport> getTargetTransport() { return dstTrans_; }
+
+ /*
+ * Override TTransport *_virt() functions to invoke our implementations.
+ * We cannot use TVirtualTransport to provide these, since we need to inherit
+ * virtually from TTransport.
+ */
+ uint32_t read_virt(uint8_t* buf, uint32_t len) override { return this->read(buf, len); }
+ void write_virt(const uint8_t* buf, uint32_t len) override { this->write(buf, len); }
+
+protected:
+ std::shared_ptr<TTransport> srcTrans_;
+ std::shared_ptr<TTransport> dstTrans_;
+
+ uint8_t* rBuf_;
+ uint32_t rBufSize_;
+ uint32_t rPos_;
+ uint32_t rLen_;
+
+ uint8_t* wBuf_;
+ uint32_t wBufSize_;
+ uint32_t wLen_;
+
+ bool pipeOnRead_;
+ bool pipeOnWrite_;
+};
+
+/**
+ * Wraps a transport into a pipedTransport instance.
+ *
+ */
+class TPipedTransportFactory : public TTransportFactory {
+public:
+ TPipedTransportFactory() = default;
+ TPipedTransportFactory(std::shared_ptr<TTransport> dstTrans) {
+ initializeTargetTransport(dstTrans);
+ }
+ ~TPipedTransportFactory() override = default;
+
+ /**
+ * Wraps the base transport into a piped transport.
+ */
+ std::shared_ptr<TTransport> getTransport(std::shared_ptr<TTransport> srcTrans) override {
+ return std::shared_ptr<TTransport>(new TPipedTransport(srcTrans, dstTrans_));
+ }
+
+ virtual void initializeTargetTransport(std::shared_ptr<TTransport> dstTrans) {
+ if (dstTrans_.get() == nullptr) {
+ dstTrans_ = dstTrans;
+ } else {
+ throw TException("Target transport already initialized");
+ }
+ }
+
+protected:
+ std::shared_ptr<TTransport> dstTrans_;
+};
+
+/**
+ * TPipedFileTransport. This is just like a TTransport, except that
+ * it is a templatized class, so that clients who rely on a specific
+ * TTransport can still access the original transport.
+ *
+ */
+class TPipedFileReaderTransport : public TPipedTransport, public TFileReaderTransport {
+public:
+ TPipedFileReaderTransport(std::shared_ptr<TFileReaderTransport> srcTrans,
+ std::shared_ptr<TTransport> dstTrans);
+
+ ~TPipedFileReaderTransport() override;
+
+ // TTransport functions
+ bool isOpen() const override;
+ bool peek() override;
+ void open() override;
+ void close() override;
+ uint32_t read(uint8_t* buf, uint32_t len);
+ uint32_t readAll(uint8_t* buf, uint32_t len);
+ uint32_t readEnd() override;
+ void write(const uint8_t* buf, uint32_t len);
+ uint32_t writeEnd() override;
+ void flush() override;
+
+ // TFileReaderTransport functions
+ int32_t getReadTimeout() override;
+ void setReadTimeout(int32_t readTimeout) override;
+ uint32_t getNumChunks() override;
+ uint32_t getCurChunk() override;
+ void seekToChunk(int32_t chunk) override;
+ void seekToEnd() override;
+
+ /*
+ * Override TTransport *_virt() functions to invoke our implementations.
+ * We cannot use TVirtualTransport to provide these, since we need to inherit
+ * virtually from TTransport.
+ */
+ uint32_t read_virt(uint8_t* buf, uint32_t len) override { return this->read(buf, len); }
+ uint32_t readAll_virt(uint8_t* buf, uint32_t len) override { return this->readAll(buf, len); }
+ void write_virt(const uint8_t* buf, uint32_t len) override { this->write(buf, len); }
+
+protected:
+ // shouldn't be used
+ TPipedFileReaderTransport();
+ std::shared_ptr<TFileReaderTransport> srcTrans_;
+};
+
+/**
+ * Creates a TPipedFileReaderTransport from a filepath and a destination transport
+ *
+ */
+class TPipedFileReaderTransportFactory : public TPipedTransportFactory {
+public:
+ TPipedFileReaderTransportFactory() = default;
+ TPipedFileReaderTransportFactory(std::shared_ptr<TTransport> dstTrans)
+ : TPipedTransportFactory(dstTrans) {}
+ ~TPipedFileReaderTransportFactory() override = default;
+
+ std::shared_ptr<TTransport> getTransport(std::shared_ptr<TTransport> srcTrans) override {
+ std::shared_ptr<TFileReaderTransport> pFileReaderTransport
+ = std::dynamic_pointer_cast<TFileReaderTransport>(srcTrans);
+ if (pFileReaderTransport.get() != nullptr) {
+ return getFileReaderTransport(pFileReaderTransport);
+ } else {
+ return std::shared_ptr<TTransport>();
+ }
+ }
+
+ std::shared_ptr<TFileReaderTransport> getFileReaderTransport(
+ std::shared_ptr<TFileReaderTransport> srcTrans) {
+ return std::shared_ptr<TFileReaderTransport>(
+ new TPipedFileReaderTransport(srcTrans, dstTrans_));
+ }
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_TTRANSPORTUTILS_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TVirtualTransport.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TVirtualTransport.h
new file mode 100644
index 000000000..0a0485742
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TVirtualTransport.h
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TVIRTUALTRANSPORT_H_
+#define _THRIFT_TRANSPORT_TVIRTUALTRANSPORT_H_ 1
+
+#include <thrift/transport/TTransport.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * Helper class that provides default implementations of TTransport methods.
+ *
+ * This class provides default implementations of read(), readAll(), write(),
+ * borrow() and consume().
+ *
+ * In the TTransport base class, each of these methods simply invokes its
+ * virtual counterpart. This class overrides them to always perform the
+ * default behavior, without a virtual function call.
+ *
+ * The primary purpose of this class is to serve as a base class for
+ * TVirtualTransport, and prevent infinite recursion if one of its subclasses
+ * does not override the TTransport implementation of these methods. (Since
+ * TVirtualTransport::read_virt() calls read(), and TTransport::read() calls
+ * read_virt().)
+ */
+class TTransportDefaults : public TTransport {
+public:
+ /*
+ * TTransport *_virt() methods provide reasonable default implementations.
+ * Invoke them non-virtually.
+ */
+ uint32_t read(uint8_t* buf, uint32_t len) { return this->TTransport::read_virt(buf, len); }
+ uint32_t readAll(uint8_t* buf, uint32_t len) { return this->TTransport::readAll_virt(buf, len); }
+ void write(const uint8_t* buf, uint32_t len) { this->TTransport::write_virt(buf, len); }
+ const uint8_t* borrow(uint8_t* buf, uint32_t* len) {
+ return this->TTransport::borrow_virt(buf, len);
+ }
+ void consume(uint32_t len) { this->TTransport::consume_virt(len); }
+
+protected:
+ TTransportDefaults() = default;
+};
+
+/**
+ * Helper class to provide polymorphism for subclasses of TTransport.
+ *
+ * This class implements *_virt() methods of TTransport, to call the
+ * non-virtual versions of these functions in the proper subclass.
+ *
+ * To define your own transport class using TVirtualTransport:
+ * 1) Derive your subclass from TVirtualTransport<your class>
+ * e.g: class MyTransport : public TVirtualTransport<MyTransport> {
+ * 2) Provide your own implementations of read(), readAll(), etc.
+ * These methods should be non-virtual.
+ *
+ * Transport implementations that need to use virtual inheritance when
+ * inheriting from TTransport cannot use TVirtualTransport.
+ *
+ * @author Chad Walters <chad@powerset.com>
+ */
+template <class Transport_, class Super_ = TTransportDefaults>
+class TVirtualTransport : public Super_ {
+public:
+ /*
+ * Implementations of the *_virt() functions, to call the subclass's
+ * non-virtual implementation function.
+ */
+ uint32_t read_virt(uint8_t* buf, uint32_t len) override {
+ return static_cast<Transport_*>(this)->read(buf, len);
+ }
+
+ uint32_t readAll_virt(uint8_t* buf, uint32_t len) override {
+ return static_cast<Transport_*>(this)->readAll(buf, len);
+ }
+
+ void write_virt(const uint8_t* buf, uint32_t len) override {
+ static_cast<Transport_*>(this)->write(buf, len);
+ }
+
+ const uint8_t* borrow_virt(uint8_t* buf, uint32_t* len) override {
+ return static_cast<Transport_*>(this)->borrow(buf, len);
+ }
+
+ void consume_virt(uint32_t len) override { static_cast<Transport_*>(this)->consume(len); }
+
+ /*
+ * Provide a default readAll() implementation that invokes
+ * read() non-virtually.
+ *
+ * Note: subclasses that use TVirtualTransport to derive from another
+ * transport implementation (i.e., not TTransportDefaults) should beware that
+ * this may override any non-default readAll() implementation provided by
+ * the parent transport class. They may need to redefine readAll() to call
+ * the correct parent implementation, if desired.
+ */
+ uint32_t readAll(uint8_t* buf, uint32_t len) {
+ auto* trans = static_cast<Transport_*>(this);
+ return ::apache::thrift::transport::readAll(*trans, buf, len);
+ }
+
+protected:
+ TVirtualTransport() = default;
+
+ /*
+ * Templatized constructors, to allow arguments to be passed to the Super_
+ * constructor. Currently we only support 0, 1, or 2 arguments, but
+ * additional versions can be added as needed.
+ */
+ template <typename Arg_>
+ TVirtualTransport(Arg_ const& arg)
+ : Super_(arg) {}
+
+ template <typename Arg1_, typename Arg2_>
+ TVirtualTransport(Arg1_ const& a1, Arg2_ const& a2)
+ : Super_(a1, a2) {}
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_TVIRTUALTRANSPORT_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TZlibTransport.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TZlibTransport.cpp
new file mode 100644
index 000000000..437190b29
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TZlibTransport.cpp
@@ -0,0 +1,402 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <cassert>
+#include <cstring>
+#include <algorithm>
+#include <thrift/transport/TZlibTransport.h>
+
+using std::string;
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+// Don't call this outside of the constructor.
+void TZlibTransport::initZlib() {
+ int rv;
+ bool r_init = false;
+ try {
+ rstream_ = new z_stream;
+ wstream_ = new z_stream;
+
+ rstream_->zalloc = Z_NULL;
+ wstream_->zalloc = Z_NULL;
+ rstream_->zfree = Z_NULL;
+ wstream_->zfree = Z_NULL;
+ rstream_->opaque = Z_NULL;
+ wstream_->opaque = Z_NULL;
+
+ rstream_->next_in = crbuf_;
+ wstream_->next_in = uwbuf_;
+ rstream_->next_out = urbuf_;
+ wstream_->next_out = cwbuf_;
+ rstream_->avail_in = 0;
+ wstream_->avail_in = 0;
+ rstream_->avail_out = urbuf_size_;
+ wstream_->avail_out = cwbuf_size_;
+
+ rv = inflateInit(rstream_);
+ checkZlibRv(rv, rstream_->msg);
+
+ // Have to set this flag so we know whether to de-initialize.
+ r_init = true;
+
+ rv = deflateInit(wstream_, comp_level_);
+ checkZlibRv(rv, wstream_->msg);
+ }
+
+ catch (...) {
+ if (r_init) {
+ rv = inflateEnd(rstream_);
+ checkZlibRvNothrow(rv, rstream_->msg);
+ }
+ // There is no way we can get here if wstream_ was initialized.
+
+ throw;
+ }
+}
+
+inline void TZlibTransport::checkZlibRv(int status, const char* message) {
+ if (status != Z_OK) {
+ throw TZlibTransportException(status, message);
+ }
+}
+
+inline void TZlibTransport::checkZlibRvNothrow(int status, const char* message) {
+ if (status != Z_OK) {
+ string output = "TZlibTransport: zlib failure in destructor: "
+ + TZlibTransportException::errorMessage(status, message);
+ GlobalOutput(output.c_str());
+ }
+}
+
+TZlibTransport::~TZlibTransport() {
+ int rv;
+ rv = inflateEnd(rstream_);
+ checkZlibRvNothrow(rv, rstream_->msg);
+
+ rv = deflateEnd(wstream_);
+ // Z_DATA_ERROR may be returned if the caller has written data, but not
+ // called flush() to actually finish writing the data out to the underlying
+ // transport. The defined TTransport behavior in this case is that this data
+ // may be discarded, so we ignore the error and silently discard the data.
+ // For other erros, log a message.
+ if (rv != Z_DATA_ERROR) {
+ checkZlibRvNothrow(rv, wstream_->msg);
+ }
+
+ delete[] urbuf_;
+ delete[] crbuf_;
+ delete[] uwbuf_;
+ delete[] cwbuf_;
+ delete rstream_;
+ delete wstream_;
+}
+
+bool TZlibTransport::isOpen() const {
+ return (readAvail() > 0) || (rstream_->avail_in > 0) || transport_->isOpen();
+}
+
+bool TZlibTransport::peek() {
+ return (readAvail() > 0) || (rstream_->avail_in > 0) || transport_->peek();
+}
+
+// READING STRATEGY
+//
+// We have two buffers for reading: one containing the compressed data (crbuf_)
+// and one containing the uncompressed data (urbuf_). When read is called,
+// we repeat the following steps until we have satisfied the request:
+// - Copy data from urbuf_ into the caller's buffer.
+// - If we had enough, return.
+// - If urbuf_ is empty, read some data into it from the underlying transport.
+// - Inflate data from crbuf_ into urbuf_.
+//
+// In standalone objects, we set input_ended_ to true when inflate returns
+// Z_STREAM_END. This allows to make sure that a checksum was verified.
+
+inline int TZlibTransport::readAvail() const {
+ return urbuf_size_ - rstream_->avail_out - urpos_;
+}
+
+uint32_t TZlibTransport::read(uint8_t* buf, uint32_t len) {
+ uint32_t need = len;
+
+ // TODO(dreiss): Skip urbuf on big reads.
+
+ while (true) {
+ // Copy out whatever we have available, then give them the min of
+ // what we have and what they want, then advance indices.
+ int give = (std::min)((uint32_t)readAvail(), need);
+ memcpy(buf, urbuf_ + urpos_, give);
+ need -= give;
+ buf += give;
+ urpos_ += give;
+
+ // If they were satisfied, we are done.
+ if (need == 0) {
+ return len;
+ }
+
+ // If we will need to read from the underlying transport to get more data,
+ // but we already have some data available, return it now. Reading from
+ // the underlying transport may block, and read() is only allowed to block
+ // when no data is available.
+ if (need < len && rstream_->avail_in == 0) {
+ return len - need;
+ }
+
+ // If we get to this point, we need to get some more data.
+
+ // If zlib has reported the end of a stream, we can't really do any more.
+ if (input_ended_) {
+ return len - need;
+ }
+
+ // The uncompressed read buffer is empty, so reset the stream fields.
+ rstream_->next_out = urbuf_;
+ rstream_->avail_out = urbuf_size_;
+ urpos_ = 0;
+
+ // Call inflate() to uncompress some more data
+ if (!readFromZlib()) {
+ // no data available from underlying transport
+ return len - need;
+ }
+
+ // Okay. The read buffer should have whatever we can give it now.
+ // Loop back to the start and try to give some more.
+ }
+}
+
+bool TZlibTransport::readFromZlib() {
+ assert(!input_ended_);
+
+ // If we don't have any more compressed data available,
+ // read some from the underlying transport.
+ if (rstream_->avail_in == 0) {
+ uint32_t got = transport_->read(crbuf_, crbuf_size_);
+ if (got == 0) {
+ return false;
+ }
+ rstream_->next_in = crbuf_;
+ rstream_->avail_in = got;
+ }
+
+ // We have some compressed data now. Uncompress it.
+ int zlib_rv = inflate(rstream_, Z_SYNC_FLUSH);
+
+ if (zlib_rv == Z_STREAM_END) {
+ input_ended_ = true;
+ } else {
+ checkZlibRv(zlib_rv, rstream_->msg);
+ }
+
+ return true;
+}
+
+// WRITING STRATEGY
+//
+// We buffer up small writes before sending them to zlib, so our logic is:
+// - Is the write big?
+// - Send the buffer to zlib.
+// - Send this data to zlib.
+// - Is the write small?
+// - Is there insufficient space in the buffer for it?
+// - Send the buffer to zlib.
+// - Copy the data to the buffer.
+//
+// We have two buffers for writing also: the uncompressed buffer (mentioned
+// above) and the compressed buffer. When sending data to zlib we loop over
+// the following until the source (uncompressed buffer or big write) is empty:
+// - Is there no more space in the compressed buffer?
+// - Write the compressed buffer to the underlying transport.
+// - Deflate from the source into the compressed buffer.
+
+void TZlibTransport::write(const uint8_t* buf, uint32_t len) {
+ if (output_finished_) {
+ throw TTransportException(TTransportException::BAD_ARGS, "write() called after finish()");
+ }
+
+ // zlib's "deflate" function has enough logic in it that I think
+ // we're better off (performance-wise) buffering up small writes.
+ if (len > MIN_DIRECT_DEFLATE_SIZE) {
+ flushToZlib(uwbuf_, uwpos_, Z_NO_FLUSH);
+ uwpos_ = 0;
+ flushToZlib(buf, len, Z_NO_FLUSH);
+ } else if (len > 0) {
+ if (uwbuf_size_ - uwpos_ < len) {
+ flushToZlib(uwbuf_, uwpos_, Z_NO_FLUSH);
+ uwpos_ = 0;
+ }
+ memcpy(uwbuf_ + uwpos_, buf, len);
+ uwpos_ += len;
+ }
+}
+
+void TZlibTransport::flush() {
+ if (output_finished_) {
+ throw TTransportException(TTransportException::BAD_ARGS, "flush() called after finish()");
+ }
+
+ flushToZlib(uwbuf_, uwpos_, Z_BLOCK);
+ uwpos_ = 0;
+
+ if(wstream_->avail_out < 6){
+ transport_->write(cwbuf_, cwbuf_size_ - wstream_->avail_out);
+ wstream_->next_out = cwbuf_;
+ wstream_->avail_out = cwbuf_size_;
+ }
+
+ flushToTransport(Z_FULL_FLUSH);
+}
+
+void TZlibTransport::finish() {
+ if (output_finished_) {
+ throw TTransportException(TTransportException::BAD_ARGS, "finish() called more than once");
+ }
+
+ flushToTransport(Z_FINISH);
+}
+
+void TZlibTransport::flushToTransport(int flush) {
+ // write pending data in uwbuf_ to zlib
+ flushToZlib(uwbuf_, uwpos_, flush);
+ uwpos_ = 0;
+
+ // write all available data from zlib to the transport
+ transport_->write(cwbuf_, cwbuf_size_ - wstream_->avail_out);
+ wstream_->next_out = cwbuf_;
+ wstream_->avail_out = cwbuf_size_;
+
+ // flush the transport
+ transport_->flush();
+}
+
+void TZlibTransport::flushToZlib(const uint8_t* buf, int len, int flush) {
+ wstream_->next_in = const_cast<uint8_t*>(buf);
+ wstream_->avail_in = len;
+
+ while (true) {
+ if ((flush == Z_NO_FLUSH || flush == Z_BLOCK) && wstream_->avail_in == 0) {
+ break;
+ }
+
+ // If our ouput buffer is full, flush to the underlying transport.
+ if (wstream_->avail_out == 0) {
+ transport_->write(cwbuf_, cwbuf_size_);
+ wstream_->next_out = cwbuf_;
+ wstream_->avail_out = cwbuf_size_;
+ }
+
+ int zlib_rv = deflate(wstream_, flush);
+
+ if (flush == Z_FINISH && zlib_rv == Z_STREAM_END) {
+ assert(wstream_->avail_in == 0);
+ output_finished_ = true;
+ break;
+ }
+
+ checkZlibRv(zlib_rv, wstream_->msg);
+
+ if ((flush == Z_SYNC_FLUSH || flush == Z_FULL_FLUSH) && wstream_->avail_in == 0
+ && wstream_->avail_out != 0) {
+ break;
+ }
+ }
+}
+
+const uint8_t* TZlibTransport::borrow(uint8_t* buf, uint32_t* len) {
+ (void)buf;
+ // Don't try to be clever with shifting buffers.
+ // If we have enough data, give a pointer to it,
+ // otherwise let the protcol use its slow path.
+ if (readAvail() >= (int)*len) {
+ *len = (uint32_t)readAvail();
+ return urbuf_ + urpos_;
+ }
+ return nullptr;
+}
+
+void TZlibTransport::consume(uint32_t len) {
+ if (readAvail() >= (int)len) {
+ urpos_ += len;
+ } else {
+ throw TTransportException(TTransportException::BAD_ARGS, "consume did not follow a borrow.");
+ }
+}
+
+void TZlibTransport::verifyChecksum() {
+ // If zlib has already reported the end of the stream,
+ // it has verified the checksum.
+ if (input_ended_) {
+ return;
+ }
+
+ // This should only be called when reading is complete.
+ // If the caller still has unread data, throw an exception.
+ if (readAvail() > 0) {
+ throw TTransportException(TTransportException::CORRUPTED_DATA,
+ "verifyChecksum() called before end of zlib stream");
+ }
+
+ // Reset the rstream fields, in case avail_out is 0.
+ // (Since readAvail() is 0, we know there is no unread data in urbuf_)
+ rstream_->next_out = urbuf_;
+ rstream_->avail_out = urbuf_size_;
+ urpos_ = 0;
+
+ // Call inflate()
+ // This will throw an exception if the checksum is bad.
+ bool performed_inflate = readFromZlib();
+ if (!performed_inflate) {
+ // We needed to read from the underlying transport, and the read() call
+ // returned 0.
+ //
+ // Not all TTransport implementations behave the same way here, so we'll
+ // end up with different behavior depending on the underlying transport.
+ //
+ // For some transports (e.g., TFDTransport), read() blocks if no more data
+ // is available. They only return 0 if EOF has been reached, or if the
+ // remote endpoint has closed the connection. For those transports,
+ // verifyChecksum() will block until the checksum becomes available.
+ //
+ // Other transport types (e.g., TMemoryBuffer) always return 0 immediately
+ // if no more data is available. For those transport types, verifyChecksum
+ // will raise the following exception if the checksum is not available from
+ // the underlying transport yet.
+ throw TTransportException(TTransportException::CORRUPTED_DATA,
+ "checksum not available yet in "
+ "verifyChecksum()");
+ }
+
+ // If input_ended_ is true now, the checksum has been verified
+ if (input_ended_) {
+ return;
+ }
+
+ // The caller invoked us before the actual end of the data stream
+ assert(rstream_->avail_out < urbuf_size_);
+ throw TTransportException(TTransportException::CORRUPTED_DATA,
+ "verifyChecksum() called before end of "
+ "zlib stream");
+}
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TZlibTransport.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TZlibTransport.h
new file mode 100644
index 000000000..29afae0d4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/transport/TZlibTransport.h
@@ -0,0 +1,242 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_TZLIBTRANSPORT_H_
+#define _THRIFT_TRANSPORT_TZLIBTRANSPORT_H_ 1
+
+#include <thrift/transport/TTransport.h>
+#include <thrift/transport/TVirtualTransport.h>
+#include <thrift/TToString.h>
+#include <zlib.h>
+
+struct z_stream_s;
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+class TZlibTransportException : public TTransportException {
+public:
+ TZlibTransportException(int status, const char* msg)
+ : TTransportException(TTransportException::INTERNAL_ERROR, errorMessage(status, msg)),
+ zlib_status_(status),
+ zlib_msg_(msg == nullptr ? "(null)" : msg) {}
+
+ ~TZlibTransportException() noexcept override = default;
+
+ int getZlibStatus() { return zlib_status_; }
+ std::string getZlibMessage() { return zlib_msg_; }
+
+ static std::string errorMessage(int status, const char* msg) {
+ std::string rv = "zlib error: ";
+ if (msg) {
+ rv += msg;
+ } else {
+ rv += "(no message)";
+ }
+ rv += " (status = ";
+ rv += to_string(status);
+ rv += ")";
+ return rv;
+ }
+
+ int zlib_status_;
+ std::string zlib_msg_;
+};
+
+/**
+ * This transport uses zlib to compress on write and decompress on read
+ *
+ * TODO(dreiss): Don't do an extra copy of the compressed data if
+ * the underlying transport is TBuffered or TMemory.
+ *
+ */
+class TZlibTransport : public TVirtualTransport<TZlibTransport> {
+public:
+ /**
+ * @param transport The transport to read compressed data from
+ * and write compressed data to.
+ * @param urbuf_size Uncompressed buffer size for reading.
+ * @param crbuf_size Compressed buffer size for reading.
+ * @param uwbuf_size Uncompressed buffer size for writing.
+ * @param cwbuf_size Compressed buffer size for writing.
+ * @param comp_level Compression level (0=none[fast], 6=default, 9=max[slow]).
+ */
+ TZlibTransport(std::shared_ptr<TTransport> transport,
+ int urbuf_size = DEFAULT_URBUF_SIZE,
+ int crbuf_size = DEFAULT_CRBUF_SIZE,
+ int uwbuf_size = DEFAULT_UWBUF_SIZE,
+ int cwbuf_size = DEFAULT_CWBUF_SIZE,
+ int16_t comp_level = Z_DEFAULT_COMPRESSION)
+ : transport_(transport),
+ urpos_(0),
+ uwpos_(0),
+ input_ended_(false),
+ output_finished_(false),
+ urbuf_size_(urbuf_size),
+ crbuf_size_(crbuf_size),
+ uwbuf_size_(uwbuf_size),
+ cwbuf_size_(cwbuf_size),
+ urbuf_(nullptr),
+ crbuf_(nullptr),
+ uwbuf_(nullptr),
+ cwbuf_(nullptr),
+ rstream_(nullptr),
+ wstream_(nullptr),
+ comp_level_(comp_level) {
+ if (uwbuf_size_ < MIN_DIRECT_DEFLATE_SIZE) {
+ // Have to copy this into a local because of a linking issue.
+ int minimum = MIN_DIRECT_DEFLATE_SIZE;
+ throw TTransportException(TTransportException::BAD_ARGS,
+ "TZLibTransport: uncompressed write buffer must be at least"
+ + to_string(minimum) + ".");
+ }
+
+ try {
+ urbuf_ = new uint8_t[urbuf_size];
+ crbuf_ = new uint8_t[crbuf_size];
+ uwbuf_ = new uint8_t[uwbuf_size];
+ cwbuf_ = new uint8_t[cwbuf_size];
+
+ // Don't call this outside of the constructor.
+ initZlib();
+
+ } catch (...) {
+ delete[] urbuf_;
+ delete[] crbuf_;
+ delete[] uwbuf_;
+ delete[] cwbuf_;
+ throw;
+ }
+ }
+
+ // Don't call this outside of the constructor.
+ void initZlib();
+
+ /**
+ * TZlibTransport destructor.
+ *
+ * Warning: Destroying a TZlibTransport object may discard any written but
+ * unflushed data. You must explicitly call flush() or finish() to ensure
+ * that data is actually written and flushed to the underlying transport.
+ */
+ ~TZlibTransport() override;
+
+ bool isOpen() const override;
+ bool peek() override;
+
+ void open() override { transport_->open(); }
+
+ void close() override { transport_->close(); }
+
+ uint32_t read(uint8_t* buf, uint32_t len);
+
+ void write(const uint8_t* buf, uint32_t len);
+
+ void flush() override;
+
+ /**
+ * Finalize the zlib stream.
+ *
+ * This causes zlib to flush any pending write data and write end-of-stream
+ * information, including the checksum. Once finish() has been called, no
+ * new data can be written to the stream.
+ */
+ void finish();
+
+ const uint8_t* borrow(uint8_t* buf, uint32_t* len);
+
+ void consume(uint32_t len);
+
+ /**
+ * Verify the checksum at the end of the zlib stream.
+ *
+ * This may only be called after all data has been read.
+ * It verifies the checksum that was written by the finish() call.
+ */
+ void verifyChecksum();
+
+ /**
+ * TODO(someone_smart): Choose smart defaults.
+ */
+ static const int DEFAULT_URBUF_SIZE = 128;
+ static const int DEFAULT_CRBUF_SIZE = 1024;
+ static const int DEFAULT_UWBUF_SIZE = 128;
+ static const int DEFAULT_CWBUF_SIZE = 1024;
+
+ std::shared_ptr<TTransport> getUnderlyingTransport() const { return transport_; }
+
+protected:
+ inline void checkZlibRv(int status, const char* msg);
+ inline void checkZlibRvNothrow(int status, const char* msg);
+ inline int readAvail() const;
+ void flushToTransport(int flush);
+ void flushToZlib(const uint8_t* buf, int len, int flush);
+ bool readFromZlib();
+
+protected:
+ // Writes smaller than this are buffered up.
+ // Larger (or equal) writes are dumped straight to zlib.
+ static const uint32_t MIN_DIRECT_DEFLATE_SIZE = 32;
+
+ std::shared_ptr<TTransport> transport_;
+
+ int urpos_;
+ int uwpos_;
+
+ /// True iff zlib has reached the end of the input stream.
+ bool input_ended_;
+ /// True iff we have finished the output stream.
+ bool output_finished_;
+
+ uint32_t urbuf_size_;
+ uint32_t crbuf_size_;
+ uint32_t uwbuf_size_;
+ uint32_t cwbuf_size_;
+
+ uint8_t* urbuf_;
+ uint8_t* crbuf_;
+ uint8_t* uwbuf_;
+ uint8_t* cwbuf_;
+
+ struct z_stream_s* rstream_;
+ struct z_stream_s* wstream_;
+
+ const int comp_level_;
+};
+
+/**
+ * Wraps a transport into a zlibbed one.
+ *
+ */
+class TZlibTransportFactory : public TTransportFactory {
+public:
+ TZlibTransportFactory() = default;
+
+ ~TZlibTransportFactory() override = default;
+
+ std::shared_ptr<TTransport> getTransport(std::shared_ptr<TTransport> trans) override {
+ return std::shared_ptr<TTransport>(new TZlibTransport(trans));
+ }
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // #ifndef _THRIFT_TRANSPORT_TZLIBTRANSPORT_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/GetTimeOfDay.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/GetTimeOfDay.cpp
new file mode 100644
index 000000000..0a0292c7e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/GetTimeOfDay.cpp
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/windows/GetTimeOfDay.h>
+#include <thrift/thrift-config.h>
+
+// win32
+#if defined(__MINGW32__)
+ #include <sys/time.h>
+#endif
+
+#if !defined(__MINGW32__)
+struct timezone {
+ int tz_minuteswest; /* minutes W of Greenwich */
+ int tz_dsttime; /* type of dst correction */
+};
+#endif
+
+#if defined(__MINGW32__)
+int thrift_gettimeofday(struct timeval* tv, struct timezone* tz) {
+ return gettimeofday(tv,tz);
+}
+#else
+#define WIN32_LEAN_AND_MEAN
+#include <Winsock2.h>
+#include <cstdint>
+#include <sstream>
+#include <thrift/transport/TTransportException.h>
+
+// This code started from a "FREE implementation" posted to Stack Overflow at:
+// https://stackoverflow.com/questions/10905892/equivalent-of-gettimeday-for-windows
+// added: assert
+// added: error handling
+// added: C++ style casts
+int thrift_gettimeofday(struct timeval * tp, struct timezone * tzp)
+{
+ // We don't fill it in so prove nobody is looking for the data
+ assert(tzp == NULL);
+
+ // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's
+ // This magic number is the number of 100 nanosecond intervals since January 1, 1601 (UTC)
+ // until 00:00:00 January 1, 1970
+ static const uint64_t EPOCH = static_cast<uint64_t>(116444736000000000ULL);
+
+ SYSTEMTIME system_time;
+ FILETIME file_time;
+ uint64_t time;
+
+ GetSystemTime( &system_time );
+ if (!SystemTimeToFileTime( &system_time, &file_time )) {
+ DWORD lastError = GetLastError();
+ std::stringstream ss;
+ ss << "SystemTimeToFileTime failed: 0x" << std::hex << lastError;
+ using apache::thrift::transport::TTransportException;
+ throw TTransportException(TTransportException::INTERNAL_ERROR, ss.str());
+ }
+ time = static_cast<uint64_t>(file_time.dwLowDateTime ) ;
+ time += static_cast<uint64_t>(file_time.dwHighDateTime) << 32;
+
+ tp->tv_sec = static_cast<long>((time - EPOCH) / 10000000L);
+ tp->tv_usec = static_cast<long>(system_time.wMilliseconds * 1000);
+ return 0;
+}
+#endif
+
+int thrift_sleep(unsigned int seconds) {
+ ::Sleep(seconds * 1000);
+ return 0;
+}
+int thrift_usleep(unsigned int microseconds) {
+ unsigned int milliseconds = (microseconds + 999) / 1000;
+ ::Sleep(milliseconds);
+ return 0;
+}
+
+char* thrift_ctime_r(const time_t* _clock, char* _buf) {
+ strcpy(_buf, ctime(_clock));
+ return _buf;
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/GetTimeOfDay.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/GetTimeOfDay.h
new file mode 100644
index 000000000..762ac5e24
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/GetTimeOfDay.h
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_WINDOWS_GETTIMEOFDAY_H_
+#define _THRIFT_WINDOWS_GETTIMEOFDAY_H_
+
+#if defined(_MSC_VER) && (_MSC_VER > 1200)
+#pragma once
+#endif // _MSC_VER
+
+#ifndef _WIN32
+#error This is a MSVC header only.
+#endif
+
+#include <thrift/thrift-config.h>
+#include <time.h>
+
+struct thrift_timespec {
+ int64_t tv_sec;
+ int64_t tv_nsec;
+};
+
+int thrift_gettimeofday(struct timeval* tv, struct timezone* tz);
+int thrift_sleep(unsigned int seconds);
+int thrift_usleep(unsigned int micro_seconds);
+char* thrift_ctime_r(const time_t* _clock, char* _buf);
+
+#endif // _THRIFT_WINDOWS_GETTIMEOFDAY_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/Operators.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/Operators.h
new file mode 100644
index 000000000..9b8609680
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/Operators.h
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_WINDOWS_OPERATORS_H_
+#define _THRIFT_WINDOWS_OPERATORS_H_
+
+#if defined(_MSC_VER) && (_MSC_VER > 1200)
+#pragma once
+#endif // _MSC_VER
+
+namespace apache {
+namespace thrift {
+
+class TEnumIterator;
+
+inline bool operator==(const TEnumIterator&, const TEnumIterator&) {
+ // Not entirely sure what the test should be here. It is only to enable
+ // iterator debugging and is not used in release mode.
+ return true;
+}
+}
+} // apache::thrift
+
+#endif // _THRIFT_WINDOWS_OPERATORS_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/OverlappedSubmissionThread.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/OverlappedSubmissionThread.cpp
new file mode 100644
index 000000000..5ac6fe00b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/OverlappedSubmissionThread.cpp
@@ -0,0 +1,151 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+#include <thrift/windows/OverlappedSubmissionThread.h>
+#include <thrift/transport/TTransportException.h>
+#include <boost/noncopyable.hpp>
+#include <boost/scope_exit.hpp>
+#include <process.h>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+TOverlappedWorkItem::TOverlappedWorkItem()
+ : SLIST_ENTRY(),
+ action(UNKNOWN),
+ h(INVALID_HANDLE_VALUE),
+ buffer(NULL),
+ buffer_len(0),
+ overlap(),
+ last_error(0),
+ success(TRUE) {
+}
+
+void TOverlappedWorkItem::reset(uint8_t* buf, uint32_t len, HANDLE event) {
+ memset(&overlap, 0, sizeof(overlap));
+ overlap.hEvent = event;
+ buffer = buf;
+ buffer_len = len;
+ last_error = 0;
+ success = FALSE;
+}
+
+uint32_t TOverlappedWorkItem::overlappedResults(bool signal_failure) {
+ DWORD bytes = 0;
+ BOOL result = ::GetOverlappedResult(h, &overlap, &bytes, TRUE);
+ if (signal_failure && !result) // get overlapped error case
+ {
+ GlobalOutput.perror("TPipe ::GetOverlappedResult errored GLE=", ::GetLastError());
+ throw TTransportException(TTransportException::UNKNOWN, "TPipe: GetOverlappedResult failed");
+ }
+ return bytes;
+}
+
+bool TOverlappedWorkItem::process() {
+ BOOST_SCOPE_EXIT((&doneSubmittingEvent)) { SetEvent(doneSubmittingEvent.h); }
+ BOOST_SCOPE_EXIT_END
+
+ switch (action) {
+ case (CONNECT):
+ success = ::ConnectNamedPipe(h, &overlap);
+ if (success == FALSE)
+ last_error = ::GetLastError();
+ return true;
+ case (READ):
+ success = ::ReadFile(h, buffer, buffer_len, NULL, &overlap);
+ if (success == FALSE)
+ last_error = ::GetLastError();
+ return true;
+ case (CANCELIO):
+ success = ::CancelIo(h);
+ if (success == FALSE)
+ last_error = ::GetLastError();
+ return true;
+ case (STOP):
+ default:
+ return false;
+ }
+}
+
+void TOverlappedSubmissionThread::addWorkItem(TOverlappedWorkItem* item) {
+ InterlockedPushEntrySList(&workList_, item);
+ SetEvent(workAvailableEvent_.h);
+ WaitForSingleObject(item->doneSubmittingEvent.h, INFINITE);
+}
+
+TOverlappedSubmissionThread* TOverlappedSubmissionThread::acquire_instance() {
+ TAutoCrit lock(instanceGuard_);
+ if (instance_ == NULL) {
+ assert(instanceRefCount_ == 0);
+ instance_ = new TOverlappedSubmissionThread;
+ }
+ ++instanceRefCount_;
+ return instance_;
+}
+void TOverlappedSubmissionThread::release_instance() {
+ TAutoCrit lock(instanceGuard_);
+ if (--instanceRefCount_ == 0) {
+ delete instance_;
+ instance_ = NULL;
+ }
+}
+
+TOverlappedSubmissionThread::TOverlappedSubmissionThread() {
+ stopItem_.action = TOverlappedWorkItem::STOP;
+
+ InitializeSListHead(&workList_);
+ thread_ = (HANDLE)_beginthreadex(NULL, 0, thread_proc, this, 0, NULL);
+ if (thread_ == 0) {
+ GlobalOutput.perror("TOverlappedSubmissionThread unable to create thread, errno=", errno);
+ throw TTransportException(TTransportException::NOT_OPEN,
+ " TOverlappedSubmissionThread unable to create thread");
+ }
+}
+
+TOverlappedSubmissionThread::~TOverlappedSubmissionThread() {
+ addWorkItem(&stopItem_);
+ ::WaitForSingleObject(thread_, INFINITE);
+ CloseHandle(thread_);
+}
+
+void TOverlappedSubmissionThread::run() {
+ for (;;) {
+ WaitForSingleObject(workAvailableEvent_.h, INFINITE);
+ // todo check result
+ SLIST_ENTRY* entry = NULL;
+ while ((entry = InterlockedPopEntrySList(&workList_)) != NULL) {
+ TOverlappedWorkItem& item = *static_cast<TOverlappedWorkItem*>(entry);
+ if (!item.process())
+ return;
+ }
+ }
+}
+
+unsigned __stdcall TOverlappedSubmissionThread::thread_proc(void* addr) {
+ static_cast<TOverlappedSubmissionThread*>(addr)->run();
+ return 0;
+}
+
+TCriticalSection TOverlappedSubmissionThread::instanceGuard_;
+TOverlappedSubmissionThread* TOverlappedSubmissionThread::instance_;
+uint32_t TOverlappedSubmissionThread::instanceRefCount_ = 0;
+}
+}
+} // apach::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/OverlappedSubmissionThread.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/OverlappedSubmissionThread.h
new file mode 100644
index 000000000..dd0c5c957
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/OverlappedSubmissionThread.h
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_WINDOWS_OverlappedSubmissionThread_H_
+#define _THRIFT_WINDOWS_OverlappedSubmissionThread_H_ 1
+
+#ifndef _WIN32
+#error "OverlappedSubmissionThread.h is only usable on Windows"
+#endif
+
+#include <thrift/windows/Sync.h>
+#include <boost/noncopyable.hpp>
+#include <Windows.h>
+
+/*
+ *** Why does this class exist?
+ In short, because we want to enable something similar to a "select" loop, on Windows, with
+ named pipes. The core of the "select" loop is a call to WaitForMultipleObjects. So that means
+ we need a signalable object that indicates when data is available.
+
+ A pipe handle doesn't do that. A pipe handle is signaled when a read or write completes, and if
+ no one has called read or write, then the pipe handle is useless in WaitForMultipleObjects. So
+ instead, we use overlapped I/O. With overlapped I/O, you call read, and associate an event with
+ the read. When the read finishes, the event is signaled. This means that when you create a pipe,
+ you start a read. When the customer calls read on your transport object, you wait for the last
+ read to finish, and then kick off another.
+
+ There is one big caveat to this though. The thread that initiated the read must stay alive. If
+ the thread that initiated the read exits, then the read completes in an error state. To ensure
+ that the initiating thread stays alive, we create a singleton thread whose sole responsibility is
+ to manage this overlapped I/O requests. This introduces some overhead, but it is overhead that
+ is necessary for correct behavior.
+
+ This thread currently supports connect, read, and cancel io. So far, I haven't needed to put any
+ writes on this thread, but if needed, it could be done. The client write buffer would need to be
+ copied to ensure that it doesn't get invalidated.
+
+ *** How does one use this class?
+ Create a TOverlappedWorkItem, and fill in the action and "h", then call reset(). Your work item
+ is now ready to be submitted to the overlapped submission thread. Create a TAutoOverlapThread,
+ and call thread->addWorkItem with your work item. After addWorkItem completes, you may inspect
+ last_error and success. At some point in the future, call workItem.overlappedResults to wait
+ until the operation has completed.
+*/
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) TOverlappedWorkItem : public SLIST_ENTRY {
+ TOverlappedWorkItem();
+
+ enum action_t {
+ UNKNOWN = 3000,
+ CONNECT,
+ READ,
+ CANCELIO,
+ STOP,
+ };
+
+ TAutoResetEvent doneSubmittingEvent;
+ action_t action;
+ HANDLE h;
+ uint8_t* buffer;
+ uint32_t buffer_len;
+ OVERLAPPED overlap;
+
+ DWORD last_error;
+ BOOL success;
+
+ void reset(uint8_t* buf, uint32_t len, HANDLE event);
+ uint32_t overlappedResults(bool signal_failure = true);
+ bool process();
+};
+
+class TOverlappedSubmissionThread : boost::noncopyable {
+public:
+ void addWorkItem(TOverlappedWorkItem* item);
+
+ // singleton stuff
+public:
+ static TOverlappedSubmissionThread* acquire_instance();
+ static void release_instance();
+
+private:
+ static TCriticalSection instanceGuard_;
+ static TOverlappedSubmissionThread* instance_;
+ static uint32_t instanceRefCount_;
+
+ // thread details
+private:
+ TOverlappedSubmissionThread();
+ ~TOverlappedSubmissionThread();
+ void run();
+ static unsigned __stdcall thread_proc(void* addr);
+
+private:
+ DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) SLIST_HEADER workList_;
+ TOverlappedWorkItem stopItem_;
+ TAutoResetEvent workAvailableEvent_;
+ HANDLE thread_;
+};
+
+class TAutoOverlapThread : boost::noncopyable {
+private:
+ TOverlappedSubmissionThread* p;
+
+public:
+ TAutoOverlapThread() : p(TOverlappedSubmissionThread::acquire_instance()) {}
+ ~TAutoOverlapThread() { TOverlappedSubmissionThread::release_instance(); }
+ TOverlappedSubmissionThread* operator->() { return p; }
+};
+}
+}
+} // apache::thrift::transport
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/SocketPair.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/SocketPair.cpp
new file mode 100644
index 000000000..7228832ee
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/SocketPair.cpp
@@ -0,0 +1,100 @@
+/* socketpair.c
+ * Copyright 2007 by Nathan C. Myers <ncm@cantrip.org>; some rights reserved.
+ * This code is Free Software. It may be copied freely, in original or
+ * modified form, subject only to the restrictions that (1) the author is
+ * relieved from all responsibilities for any use for any purpose, and (2)
+ * this copyright notice must be retained, unchanged, in its entirety. If
+ * for any reason the author might be held responsible for any consequences
+ * of copying or use, license is withheld.
+ */
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/windows/SocketPair.h>
+#include <thrift/Thrift.h>
+
+// stl
+#include <string.h>
+
+// Win32
+#include <WS2tcpip.h>
+
+int thrift_socketpair(int d, int type, int protocol, THRIFT_SOCKET sv[2]) {
+ THRIFT_UNUSED_VARIABLE(protocol);
+ THRIFT_UNUSED_VARIABLE(type);
+ THRIFT_UNUSED_VARIABLE(d);
+
+ union {
+ struct sockaddr_in inaddr;
+ struct sockaddr addr;
+ } a;
+ THRIFT_SOCKET listener;
+ int e;
+ socklen_t addrlen = sizeof(a.inaddr);
+ DWORD flags = 0;
+ int reuse = 1;
+
+ if (sv == 0) {
+ WSASetLastError(WSAEINVAL);
+ return SOCKET_ERROR;
+ }
+
+ listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (listener == INVALID_SOCKET)
+ return SOCKET_ERROR;
+
+ memset(&a, 0, sizeof(a));
+ a.inaddr.sin_family = AF_INET;
+ a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ a.inaddr.sin_port = 0;
+
+ sv[0] = sv[1] = INVALID_SOCKET;
+ do {
+ // ignore errors coming out of this setsockopt. This is because
+ // SO_EXCLUSIVEADDRUSE requires admin privileges on WinXP, but we don't
+ // want to force socket pairs to be an admin.
+ setsockopt(listener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&reuse, (socklen_t)sizeof(reuse));
+ if (bind(listener, &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR)
+ break;
+ if (getsockname(listener, &a.addr, &addrlen) == SOCKET_ERROR)
+ break;
+ if (listen(listener, 1) == SOCKET_ERROR)
+ break;
+ sv[0] = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, flags);
+ if (sv[0] == INVALID_SOCKET)
+ break;
+ if (connect(sv[0], &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR)
+ break;
+ sv[1] = accept(listener, NULL, NULL);
+ if (sv[1] == INVALID_SOCKET)
+ break;
+
+ closesocket(listener);
+ return 0;
+
+ } while (0);
+
+ e = WSAGetLastError();
+ closesocket(listener);
+ closesocket(sv[0]);
+ closesocket(sv[1]);
+ WSASetLastError(e);
+ return SOCKET_ERROR;
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/SocketPair.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/SocketPair.h
new file mode 100644
index 000000000..86bf43150
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/SocketPair.h
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_WINDOWS_SOCKETPAIR_H_
+#define _THRIFT_WINDOWS_SOCKETPAIR_H_ 1
+
+#if defined(_MSC_VER) && (_MSC_VER > 1200)
+#pragma once
+#endif // _MSC_VER
+
+#ifndef _WIN32
+#error This is a MSVC header only.
+#endif
+
+// Win32
+#include <Winsock2.h>
+#include <thrift/thrift-config.h>
+
+int thrift_socketpair(int d, int type, int protocol, THRIFT_SOCKET sv[2]);
+
+#endif // _THRIFT_WINDOWS_SOCKETPAIR_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/Sync.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/Sync.h
new file mode 100644
index 000000000..5d321996b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/Sync.h
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_WINDOWS_Sync_H_
+#define _THRIFT_WINDOWS_Sync_H_ 1
+
+#ifndef _WIN32
+#error "windows/Sync.h is only usable on Windows"
+#endif
+
+#include <thrift/concurrency/Exception.h>
+#include <boost/noncopyable.hpp>
+#include <Windows.h>
+
+/*
+ Lightweight synchronization objects that only make sense on Windows. For cross-platform
+ code, use the classes found in the concurrency namespace
+*/
+
+namespace apache {
+namespace thrift {
+
+struct TCriticalSection : boost::noncopyable {
+ CRITICAL_SECTION cs;
+ TCriticalSection() { InitializeCriticalSection(&cs); }
+ ~TCriticalSection() { DeleteCriticalSection(&cs); }
+};
+
+class TAutoCrit : boost::noncopyable {
+private:
+ CRITICAL_SECTION* cs_;
+
+public:
+ explicit TAutoCrit(TCriticalSection& cs) : cs_(&cs.cs) { EnterCriticalSection(cs_); }
+ ~TAutoCrit() { LeaveCriticalSection(cs_); }
+};
+
+struct TAutoResetEvent : boost::noncopyable {
+ HANDLE h;
+
+ TAutoResetEvent() {
+ h = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (h == NULL) {
+ GlobalOutput.perror("TAutoResetEvent unable to create event, GLE=", GetLastError());
+ throw apache::thrift::concurrency::SystemResourceException("CreateEvent failed");
+ }
+ }
+ ~TAutoResetEvent() { CloseHandle(h); }
+};
+
+struct TManualResetEvent : boost::noncopyable {
+ HANDLE h;
+
+ TManualResetEvent() {
+ h = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (h == NULL) {
+ GlobalOutput.perror("TManualResetEvent unable to create event, GLE=", GetLastError());
+ throw apache::thrift::concurrency::SystemResourceException("CreateEvent failed");
+ }
+ }
+ ~TManualResetEvent() { CloseHandle(h); }
+};
+
+struct TAutoHandle : boost::noncopyable {
+ HANDLE h;
+ explicit TAutoHandle(HANDLE h_ = INVALID_HANDLE_VALUE) : h(h_) {}
+ ~TAutoHandle() {
+ if (h != INVALID_HANDLE_VALUE)
+ CloseHandle(h);
+ }
+
+ HANDLE release() {
+ HANDLE retval = h;
+ h = INVALID_HANDLE_VALUE;
+ return retval;
+ }
+ void reset(HANDLE h_ = INVALID_HANDLE_VALUE) {
+ if (h_ == h)
+ return;
+ if (h != INVALID_HANDLE_VALUE)
+ CloseHandle(h);
+ h = h_;
+ }
+};
+}
+} // apache::thrift
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/TWinsockSingleton.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/TWinsockSingleton.cpp
new file mode 100644
index 000000000..a502cbdc6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/TWinsockSingleton.cpp
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/windows/TWinsockSingleton.h>
+
+// boost
+#include <stdexcept>
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+TWinsockSingleton::instance_ptr TWinsockSingleton::instance_ptr_(NULL);
+std::once_flag TWinsockSingleton::flags_;
+
+//------------------------------------------------------------------------------
+TWinsockSingleton::TWinsockSingleton(void) {
+ WORD version(MAKEWORD(2, 2));
+ WSAData data = {0};
+
+ int error(WSAStartup(version, &data));
+ if (error != 0) {
+ throw std::runtime_error("Failed to initialise Winsock.");
+ }
+}
+
+//------------------------------------------------------------------------------
+TWinsockSingleton::~TWinsockSingleton(void) {
+ WSACleanup();
+}
+
+//------------------------------------------------------------------------------
+void TWinsockSingleton::create(void) {
+ std::call_once(flags_, init);
+}
+
+//------------------------------------------------------------------------------
+void TWinsockSingleton::init(void) {
+ instance_ptr_.reset(new TWinsockSingleton);
+}
+}
+}
+} // apache::thrift::transport
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/TWinsockSingleton.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/TWinsockSingleton.h
new file mode 100644
index 000000000..a30806b98
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/TWinsockSingleton.h
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TRANSPORT_WINDOWS_TWINSOCKSINGLETON_H_
+#define _THRIFT_TRANSPORT_WINDOWS_TWINSOCKSINGLETON_H_ 1
+
+#if defined(_MSC_VER) && (_MSC_VER > 1200)
+#pragma once
+#endif // _MSC_VER
+
+#ifndef _WIN32
+#error This is a MSVC header only.
+#endif
+
+#include <thrift/thrift-config.h>
+
+// boost
+#include <boost/noncopyable.hpp>
+
+#include <memory>
+#include <mutex>
+
+
+namespace apache {
+namespace thrift {
+namespace transport {
+
+/**
+ * Winsock2 must be intialised once only in order to create sockets. This class
+ * performs a one time initialisation when create is called.
+ */
+class TWinsockSingleton : private boost::noncopyable {
+
+public:
+ typedef std::shared_ptr<TWinsockSingleton> instance_ptr;
+
+private:
+ TWinsockSingleton(void);
+
+public:
+ ~TWinsockSingleton(void);
+
+public:
+ static void create(void);
+
+private:
+ static void init(void);
+
+private:
+ static instance_ptr instance_ptr_;
+ static std::once_flag flags_;
+};
+}
+}
+} // apache::thrift::transport
+
+#endif // _THRIFT_TRANSPORT_WINDOWS_TWINSOCKSINGLETON_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/WinFcntl.cpp b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/WinFcntl.cpp
new file mode 100644
index 000000000..c907e92f2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/WinFcntl.cpp
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/windows/WinFcntl.h>
+
+int thrift_fcntl(THRIFT_SOCKET fd, int cmd, int flags) {
+ if (cmd != THRIFT_F_GETFL && cmd != THRIFT_F_SETFL) {
+ return -1;
+ }
+
+ if (flags != THRIFT_O_NONBLOCK && flags != 0) {
+ return -1;
+ }
+
+ if (cmd == THRIFT_F_GETFL) {
+ return 0;
+ }
+
+ int res;
+ if (flags) {
+ res = ioctlsocket(fd, FIONBIO, reinterpret_cast<u_long*>(&(flags = 1)));
+ } else {
+ res = ioctlsocket(fd, FIONBIO, reinterpret_cast<u_long*>(&(flags = 0)));
+ }
+
+ return res;
+}
+
+#if WINVER <= 0x0502 // XP, Server2003
+int thrift_poll(THRIFT_POLLFD* fdArray, ULONG nfds, INT timeout) {
+ fd_set read_fds, write_fds;
+ fd_set* read_fds_ptr = NULL;
+ fd_set* write_fds_ptr = NULL;
+
+ FD_ZERO(&read_fds);
+ FD_ZERO(&write_fds);
+
+ for (ULONG i = 0; i < nfds; i++) {
+ // Read (in) socket
+ if ((fdArray[i].events & THRIFT_POLLIN) == THRIFT_POLLIN) {
+ read_fds_ptr = &read_fds;
+ FD_SET(fdArray[i].fd, &read_fds);
+ }
+ // Write (out) socket
+ else if ((fdArray[i].events & THRIFT_POLLOUT) == THRIFT_POLLOUT) {
+ write_fds_ptr = &write_fds;
+ FD_SET(fdArray[i].fd, &write_fds);
+ }
+ }
+
+ timeval time_out;
+ timeval* time_out_ptr = NULL;
+ if (timeout >= 0) {
+ time_out.tv_sec = timeout / 1000;
+ time_out.tv_usec = (timeout % 1000) * 1000;
+ time_out_ptr = &time_out;
+ } else { // to avoid compiler warnings
+ (void)time_out;
+ (void)timeout;
+ }
+
+ int sktready = select(1, read_fds_ptr, write_fds_ptr, NULL, time_out_ptr);
+ if (sktready > 0) {
+ for (ULONG i = 0; i < nfds; i++) {
+ fdArray[i].revents = 0;
+ if (FD_ISSET(fdArray[i].fd, &read_fds))
+ fdArray[i].revents |= THRIFT_POLLIN;
+ if (FD_ISSET(fdArray[i].fd, &write_fds))
+ fdArray[i].revents |= THRIFT_POLLOUT;
+ }
+ }
+ return sktready;
+}
+#else // Vista, Win7...
+int thrift_poll(THRIFT_POLLFD* fdArray, ULONG nfds, INT timeout) {
+ return WSAPoll(fdArray, nfds, timeout);
+}
+#endif // WINVER
+
+#ifdef _WIN32_WCE
+std::string thrift_wstr2str(std::wstring ws) {
+ std::string s(ws.begin(), ws.end());
+ return s;
+}
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/WinFcntl.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/WinFcntl.h
new file mode 100644
index 000000000..6c6be97ef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/WinFcntl.h
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_WINDOWS_FCNTL_H_
+#define _THRIFT_WINDOWS_FCNTL_H_ 1
+
+#if defined(_MSC_VER) && (_MSC_VER > 1200)
+#pragma once
+#endif // _MSC_VER
+
+#ifndef _WIN32
+#error This is a MSVC header only.
+#endif
+
+#ifdef _WIN32_WCE
+#include <string>
+#endif
+
+// Win32
+#include <Winsock2.h>
+#include <thrift/transport/PlatformSocket.h>
+
+#if WINVER <= 0x0502 // XP, Server2003
+struct thrift_pollfd {
+ THRIFT_SOCKET fd;
+ SHORT events;
+ SHORT revents;
+};
+#endif
+
+extern "C" {
+int thrift_fcntl(THRIFT_SOCKET fd, int cmd, int flags);
+int thrift_poll(THRIFT_POLLFD* fdArray, ULONG nfds, INT timeout);
+}
+
+#ifdef _WIN32_WCE
+std::string thrift_wstr2str(std::wstring ws);
+#endif
+
+#endif // _THRIFT_WINDOWS_FCNTL_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/config.h b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/config.h
new file mode 100644
index 000000000..063a92ad8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/src/thrift/windows/config.h
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_WINDOWS_CONFIG_H_
+#define _THRIFT_WINDOWS_CONFIG_H_ 1
+
+#if defined(_MSC_VER) && (_MSC_VER > 1200)
+#pragma once
+#endif // _MSC_VER
+
+#ifndef _WIN32
+#error "This is a Windows header only"
+#endif
+
+// Something that defines PRId64 is required to build
+#define HAVE_INTTYPES_H 1
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0601
+#endif
+
+#if defined(_M_IX86) || defined(_M_X64)
+#define ARITHMETIC_RIGHT_SHIFT 1
+#define SIGNED_RIGHT_SHIFT_IS 1
+#endif
+
+#ifndef __MINGW32__
+#pragma warning(disable : 4996) // Deprecated posix name.
+#endif
+
+#define HAVE_GETTIMEOFDAY 1
+#define HAVE_SYS_STAT_H 1
+
+#include <stdint.h>
+
+#include <thrift/transport/PlatformSocket.h>
+#include <thrift/windows/GetTimeOfDay.h>
+#include <thrift/windows/Operators.h>
+#include <thrift/windows/TWinsockSingleton.h>
+#include <thrift/windows/WinFcntl.h>
+#include <thrift/windows/SocketPair.h>
+
+// windows
+#include <Winsock2.h>
+#include <ws2tcpip.h>
+#ifndef __MINGW32__
+ #ifdef _WIN32_WCE
+ #pragma comment(lib, "Ws2.lib")
+ #else
+ #pragma comment(lib, "Ws2_32.lib")
+ #pragma comment(lib, "advapi32.lib") // For security APIs in TPipeServer
+ #pragma comment(lib, "Shlwapi.lib") // For StrStrIA in TPipeServer
+ #endif
+#endif // __MINGW32__
+
+#endif // _THRIFT_WINDOWS_CONFIG_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.cpp b/src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.cpp
new file mode 100644
index 000000000..6b5c7c436
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.cpp
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <stdio.h>
+
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/protocol/TCompactProtocol.h>
+#include <thrift/transport/TBufferTransports.h>
+
+#define BOOST_TEST_MODULE AllProtocolTests
+#include <boost/test/unit_test.hpp>
+
+#include "AllProtocolTests.tcc"
+
+using namespace apache::thrift;
+using namespace apache::thrift::protocol;
+using namespace apache::thrift::transport;
+
+char errorMessage[ERR_LEN];
+
+BOOST_AUTO_TEST_CASE(test_binary_protocol) {
+ testProtocol<TBinaryProtocol>("TBinaryProtocol");
+}
+
+BOOST_AUTO_TEST_CASE(test_little_binary_protocol) {
+ testProtocol<TLEBinaryProtocol>("TLEBinaryProtocol");
+}
+
+BOOST_AUTO_TEST_CASE(test_compact_protocol) {
+ testProtocol<TCompactProtocol>("TCompactProtocol");
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.tcc b/src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.tcc
new file mode 100644
index 000000000..80a4ea097
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.tcc
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TEST_GENERICPROTOCOLTEST_TCC_
+#define _THRIFT_TEST_GENERICPROTOCOLTEST_TCC_ 1
+
+#include <limits>
+
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/Thrift.h>
+
+#include "GenericHelpers.h"
+
+using std::shared_ptr;
+using namespace apache::thrift;
+using namespace apache::thrift::protocol;
+using namespace apache::thrift::transport;
+
+#define ERR_LEN 512
+extern char errorMessage[ERR_LEN];
+
+template <typename TProto, typename Val>
+void testNaked(Val val) {
+ shared_ptr<TTransport> transport(new TMemoryBuffer());
+ shared_ptr<TProtocol> protocol(new TProto(transport));
+
+ GenericIO::write(protocol, val);
+ Val out;
+ GenericIO::read(protocol, out);
+ if (out != val) {
+ THRIFT_SNPRINTF(errorMessage,
+ ERR_LEN,
+ "Invalid naked test (type: %s)",
+ ClassNames::getName<Val>());
+ throw TException(errorMessage);
+ }
+}
+
+template <typename TProto, TType type, typename Val>
+void testField(const Val val) {
+ shared_ptr<TTransport> transport(new TMemoryBuffer());
+ shared_ptr<TProtocol> protocol(new TProto(transport));
+
+ protocol->writeStructBegin("test_struct");
+ protocol->writeFieldBegin("test_field", type, (int16_t)15);
+
+ GenericIO::write(protocol, val);
+
+ protocol->writeFieldEnd();
+ protocol->writeStructEnd();
+
+ std::string name;
+ TType fieldType;
+ int16_t fieldId;
+
+ protocol->readStructBegin(name);
+ protocol->readFieldBegin(name, fieldType, fieldId);
+
+ if (fieldId != 15) {
+ THRIFT_SNPRINTF(errorMessage, ERR_LEN, "Invalid ID (type: %s)", typeid(val).name());
+ throw TException(errorMessage);
+ }
+ if (fieldType != type) {
+ THRIFT_SNPRINTF(errorMessage, ERR_LEN, "Invalid Field Type (type: %s)", typeid(val).name());
+ throw TException(errorMessage);
+ }
+
+ Val out;
+ GenericIO::read(protocol, out);
+
+ if (out != val) {
+ THRIFT_SNPRINTF(errorMessage, ERR_LEN, "Invalid value read (type: %s)", typeid(val).name());
+ throw TException(errorMessage);
+ }
+
+ protocol->readFieldEnd();
+ protocol->readStructEnd();
+}
+
+template <typename TProto>
+void testMessage() {
+ struct TMessage {
+ const char* name;
+ TMessageType type;
+ int32_t seqid;
+ } messages[] = {{"short message name", T_CALL, 0},
+ {"1", T_REPLY, 12345},
+ {"loooooooooooooooooooooooooooooooooong", T_EXCEPTION, 1 << 16},
+ {"one way push", T_ONEWAY, 12},
+ {"Janky", T_CALL, 0}};
+ const int messages_count = sizeof(messages) / sizeof(TMessage);
+
+ for (int i = 0; i < messages_count; i++) {
+ shared_ptr<TTransport> transport(new TMemoryBuffer());
+ shared_ptr<TProtocol> protocol(new TProto(transport));
+
+ protocol->writeMessageBegin(messages[i].name, messages[i].type, messages[i].seqid);
+ protocol->writeMessageEnd();
+
+ std::string name;
+ TMessageType type;
+ int32_t seqid;
+
+ protocol->readMessageBegin(name, type, seqid);
+ if (name != messages[i].name || type != messages[i].type || seqid != messages[i].seqid) {
+ throw TException("readMessageBegin failed.");
+ }
+ }
+}
+
+template <typename TProto>
+void testProtocol(const char* protoname) {
+ try {
+ testNaked<TProto, int8_t>((int8_t)123);
+
+ for (int32_t i = 0; i < 128; i++) {
+ testField<TProto, T_BYTE, int8_t>((int8_t)i);
+ testField<TProto, T_BYTE, int8_t>((int8_t)-i);
+ }
+
+ testNaked<TProto, int16_t>((int16_t)0);
+ testNaked<TProto, int16_t>((int16_t)1);
+ testNaked<TProto, int16_t>((int16_t)15000);
+ testNaked<TProto, int16_t>((int16_t)0x7fff);
+ testNaked<TProto, int16_t>((int16_t)-1);
+ testNaked<TProto, int16_t>((int16_t)-15000);
+ testNaked<TProto, int16_t>((int16_t)-0x7fff);
+ testNaked<TProto, int16_t>((std::numeric_limits<int16_t>::min)());
+ testNaked<TProto, int16_t>((std::numeric_limits<int16_t>::max)());
+
+ testField<TProto, T_I16, int16_t>((int16_t)0);
+ testField<TProto, T_I16, int16_t>((int16_t)1);
+ testField<TProto, T_I16, int16_t>((int16_t)7);
+ testField<TProto, T_I16, int16_t>((int16_t)150);
+ testField<TProto, T_I16, int16_t>((int16_t)15000);
+ testField<TProto, T_I16, int16_t>((int16_t)0x7fff);
+ testField<TProto, T_I16, int16_t>((int16_t)-1);
+ testField<TProto, T_I16, int16_t>((int16_t)-7);
+ testField<TProto, T_I16, int16_t>((int16_t)-150);
+ testField<TProto, T_I16, int16_t>((int16_t)-15000);
+ testField<TProto, T_I16, int16_t>((int16_t)-0x7fff);
+
+ testNaked<TProto, int32_t>(0);
+ testNaked<TProto, int32_t>(1);
+ testNaked<TProto, int32_t>(15000);
+ testNaked<TProto, int32_t>(0xffff);
+ testNaked<TProto, int32_t>(-1);
+ testNaked<TProto, int32_t>(-15000);
+ testNaked<TProto, int32_t>(-0xffff);
+ testNaked<TProto, int32_t>((std::numeric_limits<int32_t>::min)());
+ testNaked<TProto, int32_t>((std::numeric_limits<int32_t>::max)());
+
+ testField<TProto, T_I32, int32_t>(0);
+ testField<TProto, T_I32, int32_t>(1);
+ testField<TProto, T_I32, int32_t>(7);
+ testField<TProto, T_I32, int32_t>(150);
+ testField<TProto, T_I32, int32_t>(15000);
+ testField<TProto, T_I32, int32_t>(31337);
+ testField<TProto, T_I32, int32_t>(0xffff);
+ testField<TProto, T_I32, int32_t>(0xffffff);
+ testField<TProto, T_I32, int32_t>(-1);
+ testField<TProto, T_I32, int32_t>(-7);
+ testField<TProto, T_I32, int32_t>(-150);
+ testField<TProto, T_I32, int32_t>(-15000);
+ testField<TProto, T_I32, int32_t>(-0xffff);
+ testField<TProto, T_I32, int32_t>(-0xffffff);
+ testNaked<TProto, int64_t>((std::numeric_limits<int32_t>::min)());
+ testNaked<TProto, int64_t>((std::numeric_limits<int32_t>::max)());
+ testNaked<TProto, int64_t>((std::numeric_limits<int32_t>::min)() + 10);
+ testNaked<TProto, int64_t>((std::numeric_limits<int32_t>::max)() - 16);
+ testNaked<TProto, int64_t>((std::numeric_limits<int64_t>::min)());
+ testNaked<TProto, int64_t>((std::numeric_limits<int64_t>::max)());
+
+ testNaked<TProto, int64_t>(0);
+ for (int64_t i = 0; i < 62; i++) {
+ testNaked<TProto, int64_t>(1LL << i);
+ testNaked<TProto, int64_t>(-(1LL << i));
+ }
+
+ testField<TProto, T_I64, int64_t>(0);
+ for (int i = 0; i < 62; i++) {
+ testField<TProto, T_I64, int64_t>(1LL << i);
+ testField<TProto, T_I64, int64_t>(-(1LL << i));
+ }
+
+ testNaked<TProto, double>(123.456);
+
+ testNaked<TProto, std::string>("");
+ testNaked<TProto, std::string>("short");
+ testNaked<TProto, std::string>("borderlinetiny");
+ testNaked<TProto, std::string>("a bit longer than the smallest possible");
+ testNaked<TProto, std::string>("\x1\x2\x3\x4\x5\x6\x7\x8\x9\xA"); // kinda binary test
+
+ testField<TProto, T_STRING, std::string>("");
+ testField<TProto, T_STRING, std::string>("short");
+ testField<TProto, T_STRING, std::string>("borderlinetiny");
+ testField<TProto, T_STRING, std::string>("a bit longer than the smallest possible");
+
+ testMessage<TProto>();
+
+ printf("%s => OK\n", protoname);
+ } catch (const TException &e) {
+ THRIFT_SNPRINTF(errorMessage, ERR_LEN, "%s => Test FAILED: %s", protoname, e.what());
+ throw TException(errorMessage);
+ }
+}
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/test/AnnotationTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/AnnotationTest.cpp
new file mode 100644
index 000000000..2e18840e9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/AnnotationTest.cpp
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#define BOOST_TEST_MODULE AnnotationTest
+#include <boost/test/unit_test.hpp>
+#include "gen-cpp/AnnotationTest_types.h"
+#include <ostream>
+#include <sstream>
+
+// Normally thrift generates ostream operators, however
+// with the annotation "cpp.customostream" one can tell the
+// compiler they are going to provide their own, and not
+// emit operator << or printTo().
+
+std::ostream& operator<<(std::ostream& os, const ostr_custom& osc)
+{
+ os << "{ bar = " << osc.bar << "; }";
+ return os;
+}
+
+BOOST_AUTO_TEST_SUITE(BOOST_TEST_MODULE)
+
+BOOST_AUTO_TEST_CASE(test_cpp_compiler_generated_ostream_operator)
+{
+ ostr_default def;
+ def.__set_bar(10);
+
+ std::stringstream ssd;
+ ssd << def;
+ BOOST_CHECK_EQUAL(ssd.str(), "ostr_default(bar=10)");
+}
+
+BOOST_AUTO_TEST_CASE(test_cpp_customostream_uses_consuming_application_definition)
+{
+ ostr_custom cus;
+ cus.__set_bar(10);
+
+ std::stringstream csd;
+ csd << cus;
+ BOOST_CHECK_EQUAL(csd.str(), "{ bar = 10; }");
+}
+
+/**
+ * Disabled; see THRIFT-1567 - not sure what it is supposed to do
+BOOST_AUTO_TEST_CASE(test_cpp_type) {
+ // Check that the "cpp.type" annotation changes "struct foo" to "DenseFoo"
+ // This won't compile if the annotation is mishandled
+ DenseFoo foo;
+ foo.__set_bar(5);
+}
+ */
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/Base64Test.cpp b/src/jaegertracing/thrift/lib/cpp/test/Base64Test.cpp
new file mode 100644
index 000000000..7686e4e7b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/Base64Test.cpp
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <boost/test/auto_unit_test.hpp>
+#include <thrift/protocol/TBase64Utils.h>
+
+using apache::thrift::protocol::base64_encode;
+using apache::thrift::protocol::base64_decode;
+
+BOOST_AUTO_TEST_SUITE(Base64Test)
+
+void setupTestData(int i, uint8_t* data, int& len) {
+ len = 0;
+ do {
+ data[len] = (uint8_t)(i & 0xFF);
+ i >>= 8;
+ len++;
+ } while ((len < 3) && (i != 0));
+
+ BOOST_ASSERT(i == 0);
+}
+
+void checkEncoding(uint8_t* data, int len) {
+#ifdef NDEBUG
+ ((void)data);
+#endif
+
+ for (int i = 0; i < len; i++) {
+ BOOST_ASSERT(isalnum(data[i]) || data[i] == '/' || data[i] == '+');
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_Base64_Encode_Decode) {
+ int len;
+ uint8_t testInput[3];
+ uint8_t testOutput[4];
+
+ // Test all possible encoding / decoding cases given the
+ // three byte limit for base64_encode.
+
+ for (int i = 0xFFFFFF; i >= 0; i--) {
+
+ // fill testInput based on i
+ setupTestData(i, testInput, len);
+
+ // encode the test data, then decode it again
+ base64_encode(testInput, len, testOutput);
+
+ // verify each byte has a valid Base64 value (alphanumeric or either + or /)
+ checkEncoding(testOutput, len);
+
+ // decode output and check that it matches input
+ base64_decode(testOutput, len + 1);
+ BOOST_ASSERT(0 == memcmp(testInput, testOutput, len));
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/Benchmark.cpp b/src/jaegertracing/thrift/lib/cpp/test/Benchmark.cpp
new file mode 100644
index 000000000..56adac0b2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/Benchmark.cpp
@@ -0,0 +1,244 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <iostream>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <memory>
+#include "thrift/protocol/TBinaryProtocol.h"
+#include "thrift/transport/TBufferTransports.h"
+#include "gen-cpp/DebugProtoTest_types.h"
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+class Timer {
+public:
+ timeval vStart;
+
+ Timer() { THRIFT_GETTIMEOFDAY(&vStart, nullptr); }
+ void start() { THRIFT_GETTIMEOFDAY(&vStart, nullptr); }
+
+ double frame() {
+ timeval vEnd;
+ THRIFT_GETTIMEOFDAY(&vEnd, nullptr);
+ double dstart = vStart.tv_sec + ((double)vStart.tv_usec / 1000000.0);
+ double dend = vEnd.tv_sec + ((double)vEnd.tv_usec / 1000000.0);
+ return dend - dstart;
+ }
+};
+
+int main() {
+ using namespace thrift::test::debug;
+ using namespace apache::thrift::transport;
+ using namespace apache::thrift::protocol;
+ using std::cout;
+ using std::endl;
+
+ OneOfEach ooe;
+ ooe.im_true = true;
+ ooe.im_false = false;
+ ooe.a_bite = 0x7f;
+ ooe.integer16 = 27000;
+ ooe.integer32 = 1 << 24;
+ ooe.integer64 = (uint64_t)6000 * 1000 * 1000;
+ ooe.double_precision = M_PI;
+ ooe.some_characters = "JSON THIS! \"\1";
+ ooe.zomg_unicode = "\xd7\n\a\t";
+ ooe.base64 = "\1\2\3\255";
+
+ int num = 100000;
+ std::shared_ptr<TMemoryBuffer> buf(new TMemoryBuffer(num*1000));
+
+ uint8_t* data = nullptr;
+ uint32_t datasize = 0;
+
+ {
+ buf->resetBuffer();
+ TBinaryProtocolT<TMemoryBuffer> prot(buf);
+ double elapsed = 0.0;
+ Timer timer;
+
+ for (int i = 0; i < num; i++) {
+ ooe.write(&prot);
+ }
+ elapsed = timer.frame();
+ cout << "Write big endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ buf->getBuffer(&data, &datasize);
+
+ {
+ std::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
+ TBinaryProtocolT<TMemoryBuffer> prot(buf2);
+ OneOfEach ooe2;
+ double elapsed = 0.0;
+ Timer timer;
+
+ for (int i = 0; i < num; i++) {
+ ooe2.read(&prot);
+ }
+ elapsed = timer.frame();
+ cout << " Read big endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ {
+ buf->resetBuffer();
+ TBinaryProtocolT<TMemoryBuffer, TNetworkLittleEndian> prot(buf);
+ double elapsed = 0.0;
+ Timer timer;
+
+ for (int i = 0; i < num; i++) {
+ ooe.write(&prot);
+ }
+ elapsed = timer.frame();
+ cout << "Write little endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ {
+ OneOfEach ooe2;
+ std::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
+ TBinaryProtocolT<TMemoryBuffer, TNetworkLittleEndian> prot(buf2);
+ double elapsed = 0.0;
+ Timer timer;
+
+ for (int i = 0; i < num; i++) {
+ ooe2.read(&prot);
+ }
+ elapsed = timer.frame();
+ cout << " Read little endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ {
+ buf->resetBuffer();
+ TBinaryProtocolT<TMemoryBuffer> prot(buf);
+ double elapsed = 0.0;
+ Timer timer;
+
+ for (int i = 0; i < num; i++) {
+ ooe.write(&prot);
+ }
+ elapsed = timer.frame();
+ cout << "Write big endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ {
+ std::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
+ TBinaryProtocolT<TMemoryBuffer> prot(buf2);
+ OneOfEach ooe2;
+ double elapsed = 0.0;
+ Timer timer;
+
+ for (int i = 0; i < num; i++) {
+ ooe2.read(&prot);
+ }
+ elapsed = timer.frame();
+ cout << " Read big endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+
+ data = nullptr;
+ datasize = 0;
+ num = 10000000;
+
+ ListDoublePerf listDoublePerf;
+ listDoublePerf.field.reserve(num);
+ for (int x = 0; x < num; ++x)
+ listDoublePerf.field.push_back(double(x));
+
+ buf.reset(new TMemoryBuffer(num * 100));
+
+ {
+ buf->resetBuffer();
+ TBinaryProtocolT<TMemoryBuffer> prot(buf);
+ double elapsed = 0.0;
+ Timer timer;
+
+ listDoublePerf.write(&prot);
+ elapsed = timer.frame();
+ cout << "Double write big endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ buf->getBuffer(&data, &datasize);
+
+ {
+ std::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
+ TBinaryProtocolT<TMemoryBuffer> prot(buf2);
+ ListDoublePerf listDoublePerf2;
+ double elapsed = 0.0;
+ Timer timer;
+
+ listDoublePerf2.read(&prot);
+ elapsed = timer.frame();
+ cout << " Double read big endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ {
+ buf->resetBuffer();
+ TBinaryProtocolT<TMemoryBuffer, TNetworkLittleEndian> prot(buf);
+ double elapsed = 0.0;
+ Timer timer;
+
+ listDoublePerf.write(&prot);
+ elapsed = timer.frame();
+ cout << "Double write little endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ {
+ ListDoublePerf listDoublePerf2;
+ std::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
+ TBinaryProtocolT<TMemoryBuffer, TNetworkLittleEndian> prot(buf2);
+ double elapsed = 0.0;
+ Timer timer;
+
+ listDoublePerf2.read(&prot);
+ elapsed = timer.frame();
+ cout << " Double read little endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ {
+ buf->resetBuffer();
+ TBinaryProtocolT<TMemoryBuffer> prot(buf);
+ double elapsed = 0.0;
+ Timer timer;
+
+ listDoublePerf.write(&prot);
+ elapsed = timer.frame();
+ cout << "Double write big endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ {
+ std::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
+ TBinaryProtocolT<TMemoryBuffer> prot(buf2);
+ ListDoublePerf listDoublePerf2;
+ double elapsed = 0.0;
+ Timer timer;
+
+ listDoublePerf2.read(&prot);
+ elapsed = timer.frame();
+ cout << " Double read big endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+
+ return 0;
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/CMakeLists.txt b/src/jaegertracing/thrift/lib/cpp/test/CMakeLists.txt
new file mode 100644
index 000000000..ef08dbce2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/CMakeLists.txt
@@ -0,0 +1,390 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Unit tests still require boost
+include(BoostMacros)
+REQUIRE_BOOST_HEADERS()
+set(BOOST_COMPONENTS chrono date_time filesystem random thread unit_test_framework)
+REQUIRE_BOOST_LIBRARIES(BOOST_COMPONENTS)
+
+include(ThriftMacros)
+
+# Make sure gen-cpp files can be included
+include_directories("${CMAKE_CURRENT_BINARY_DIR}")
+
+# Create the thrift C++ test library
+set(testgencpp_SOURCES
+ gen-cpp/AnnotationTest_types.cpp
+ gen-cpp/AnnotationTest_types.h
+ gen-cpp/DebugProtoTest_types.cpp
+ gen-cpp/DebugProtoTest_types.h
+ gen-cpp/EnumTest_types.cpp
+ gen-cpp/EnumTest_types.h
+ gen-cpp/OptionalRequiredTest_types.cpp
+ gen-cpp/OptionalRequiredTest_types.h
+ gen-cpp/Recursive_types.cpp
+ gen-cpp/Recursive_types.h
+ gen-cpp/ThriftTest_types.cpp
+ gen-cpp/ThriftTest_types.h
+ gen-cpp/OneWayTest_types.cpp
+ gen-cpp/OneWayTest_types.h
+ gen-cpp/OneWayService.cpp
+ gen-cpp/OneWayService.h
+ gen-cpp/TypedefTest_types.cpp
+ gen-cpp/TypedefTest_types.h
+ ThriftTest_extras.cpp
+ DebugProtoTest_extras.cpp
+)
+
+add_library(testgencpp STATIC ${testgencpp_SOURCES})
+
+set(testgencpp_cob_SOURCES
+ gen-cpp/ChildService.cpp
+ gen-cpp/ChildService.h
+ gen-cpp/EmptyService.cpp
+ gen-cpp/EmptyService.h
+ gen-cpp/ParentService.cpp
+ gen-cpp/ParentService.h
+ gen-cpp/proc_types.cpp
+ gen-cpp/proc_types.h
+)
+add_library(testgencpp_cob STATIC ${testgencpp_cob_SOURCES})
+
+add_executable(Benchmark Benchmark.cpp)
+target_link_libraries(Benchmark testgencpp)
+LINK_AGAINST_THRIFT_LIBRARY(Benchmark thrift)
+add_test(NAME Benchmark COMMAND Benchmark)
+target_link_libraries(Benchmark testgencpp)
+
+set(UnitTest_SOURCES
+ UnitTestMain.cpp
+ OneWayHTTPTest.cpp
+ TMemoryBufferTest.cpp
+ TBufferBaseTest.cpp
+ Base64Test.cpp
+ ToStringTest.cpp
+ TypedefTest.cpp
+ TServerSocketTest.cpp
+ TServerTransportTest.cpp
+)
+
+add_executable(UnitTests ${UnitTest_SOURCES})
+target_link_libraries(UnitTests testgencpp ${Boost_LIBRARIES})
+LINK_AGAINST_THRIFT_LIBRARY(UnitTests thrift)
+add_test(NAME UnitTests COMMAND UnitTests)
+if ( MSVC )
+ # Disable C4503: decorated name length exceeded, name was truncated
+ # 'insanity' results in very long decorated names
+ set_property( TARGET UnitTests APPEND_STRING PROPERTY COMPILE_FLAGS /wd4503 )
+endif ( MSVC )
+
+
+set( TInterruptTest_SOURCES
+ TSocketInterruptTest.cpp
+ TSSLSocketInterruptTest.cpp
+)
+if (WIN32)
+ list(APPEND TInterruptTest_SOURCES
+ TPipeInterruptTest.cpp
+ )
+endif()
+add_executable(TInterruptTest ${TInterruptTest_SOURCES})
+target_link_libraries(TInterruptTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(TInterruptTest thrift)
+if (NOT MSVC AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT MINGW)
+target_link_libraries(TInterruptTest -lrt)
+endif ()
+add_test(NAME TInterruptTest COMMAND TInterruptTest -- "${CMAKE_CURRENT_SOURCE_DIR}/../../../test/keys")
+
+add_executable(TServerIntegrationTest TServerIntegrationTest.cpp)
+target_link_libraries(TServerIntegrationTest
+ testgencpp_cob
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(TServerIntegrationTest thrift)
+if (NOT MSVC AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT MINGW)
+target_link_libraries(TServerIntegrationTest -lrt)
+endif ()
+add_test(NAME TServerIntegrationTest COMMAND TServerIntegrationTest)
+
+if(WITH_ZLIB)
+include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}")
+add_executable(TransportTest TransportTest.cpp)
+target_link_libraries(TransportTest
+ testgencpp
+ ${Boost_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(TransportTest thrift)
+LINK_AGAINST_THRIFT_LIBRARY(TransportTest thriftz)
+add_test(NAME TransportTest COMMAND TransportTest)
+
+add_executable(ZlibTest ZlibTest.cpp)
+target_link_libraries(ZlibTest
+ testgencpp
+ ${Boost_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(ZlibTest thrift)
+LINK_AGAINST_THRIFT_LIBRARY(ZlibTest thriftz)
+add_test(NAME ZlibTest COMMAND ZlibTest)
+endif(WITH_ZLIB)
+
+add_executable(AnnotationTest AnnotationTest.cpp)
+target_link_libraries(AnnotationTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(AnnotationTest thrift)
+add_test(NAME AnnotationTest COMMAND AnnotationTest)
+
+add_executable(EnumTest EnumTest.cpp)
+target_link_libraries(EnumTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(EnumTest thrift)
+add_test(NAME EnumTest COMMAND EnumTest)
+
+if(HAVE_GETOPT_H)
+add_executable(TFileTransportTest TFileTransportTest.cpp)
+target_link_libraries(TFileTransportTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(TFileTransportTest thrift)
+add_test(NAME TFileTransportTest COMMAND TFileTransportTest)
+endif()
+
+add_executable(TFDTransportTest TFDTransportTest.cpp)
+target_link_libraries(TFDTransportTest
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(TFDTransportTest thrift)
+add_test(NAME TFDTransportTest COMMAND TFDTransportTest)
+
+add_executable(TPipedTransportTest TPipedTransportTest.cpp)
+target_link_libraries(TPipedTransportTest
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(TPipedTransportTest thrift)
+add_test(NAME TPipedTransportTest COMMAND TPipedTransportTest)
+
+set(AllProtocolsTest_SOURCES
+ AllProtocolTests.cpp
+ AllProtocolTests.tcc
+ GenericHelpers
+ )
+
+add_executable(AllProtocolsTest ${AllProtocolsTest_SOURCES})
+target_link_libraries(AllProtocolsTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(AllProtocolsTest thrift)
+add_test(NAME AllProtocolsTest COMMAND AllProtocolsTest)
+
+# The debug run-time in Windows asserts on isprint() with negative inputs
+if (NOT MSVC OR (MSVC AND CMAKE_BUILD_TYPE EQUAL "DEBUG"))
+add_executable(DebugProtoTest DebugProtoTest.cpp)
+target_link_libraries(DebugProtoTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(DebugProtoTest thrift)
+add_test(NAME DebugProtoTest COMMAND DebugProtoTest)
+endif()
+
+add_executable(JSONProtoTest JSONProtoTest.cpp)
+target_link_libraries(JSONProtoTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(JSONProtoTest thrift)
+add_test(NAME JSONProtoTest COMMAND JSONProtoTest)
+
+add_executable(OptionalRequiredTest OptionalRequiredTest.cpp)
+target_link_libraries(OptionalRequiredTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(OptionalRequiredTest thrift)
+add_test(NAME OptionalRequiredTest COMMAND OptionalRequiredTest)
+
+add_executable(RecursiveTest RecursiveTest.cpp)
+target_link_libraries(RecursiveTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(RecursiveTest thrift)
+add_test(NAME RecursiveTest COMMAND RecursiveTest)
+
+add_executable(SpecializationTest SpecializationTest.cpp)
+target_link_libraries(SpecializationTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(SpecializationTest thrift)
+add_test(NAME SpecializationTest COMMAND SpecializationTest)
+
+set(concurrency_test_SOURCES
+ concurrency/Tests.cpp
+ concurrency/ThreadFactoryTests.h
+ concurrency/ThreadManagerTests.h
+ concurrency/TimerManagerTests.h
+)
+add_executable(concurrency_test ${concurrency_test_SOURCES})
+LINK_AGAINST_THRIFT_LIBRARY(concurrency_test thrift)
+add_test(NAME concurrency_test COMMAND concurrency_test)
+
+set(link_test_SOURCES
+ link/LinkTest.cpp
+ gen-cpp/ParentService.h
+ link/TemplatedService1.cpp
+ link/TemplatedService2.cpp
+)
+
+add_executable(link_test ${link_test_SOURCES})
+target_link_libraries(link_test testgencpp_cob)
+LINK_AGAINST_THRIFT_LIBRARY(link_test thrift)
+target_link_libraries(link_test testgencpp)
+add_test(NAME link_test COMMAND link_test)
+
+if(WITH_LIBEVENT)
+set(processor_test_SOURCES
+ processor/ProcessorTest.cpp
+ processor/EventLog.cpp
+ processor/ServerThread.cpp
+ processor/EventLog.h
+ processor/Handlers.h
+ processor/ServerThread.h
+)
+add_executable(processor_test ${processor_test_SOURCES})
+target_link_libraries(processor_test
+ testgencpp_cob
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(processor_test thrift)
+LINK_AGAINST_THRIFT_LIBRARY(processor_test thriftnb)
+add_test(NAME processor_test COMMAND processor_test)
+
+set(TNonblockingServerTest_SOURCES TNonblockingServerTest.cpp)
+add_executable(TNonblockingServerTest ${TNonblockingServerTest_SOURCES})
+include_directories(${LIBEVENT_INCLUDE_DIRS})
+target_link_libraries(TNonblockingServerTest
+ testgencpp_cob
+ ${LIBEVENT_LIBRARIES}
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(TNonblockingServerTest thrift)
+LINK_AGAINST_THRIFT_LIBRARY(TNonblockingServerTest thriftnb)
+add_test(NAME TNonblockingServerTest COMMAND TNonblockingServerTest)
+
+if(OPENSSL_FOUND AND WITH_OPENSSL)
+ set(TNonblockingSSLServerTest_SOURCES TNonblockingSSLServerTest.cpp)
+ add_executable(TNonblockingSSLServerTest ${TNonblockingSSLServerTest_SOURCES})
+ include_directories(${LIBEVENT_INCLUDE_DIRS})
+ target_link_libraries(TNonblockingSSLServerTest
+ testgencpp_cob
+ ${LIBEVENT_LIBRARIES}
+ ${Boost_LIBRARIES}
+ )
+ LINK_AGAINST_THRIFT_LIBRARY(TNonblockingSSLServerTest thrift)
+ LINK_AGAINST_THRIFT_LIBRARY(TNonblockingSSLServerTest thriftnb)
+ add_test(NAME TNonblockingSSLServerTest COMMAND TNonblockingSSLServerTest -- "${CMAKE_CURRENT_SOURCE_DIR}/../../../test/keys")
+endif(OPENSSL_FOUND AND WITH_OPENSSL)
+endif(WITH_LIBEVENT)
+
+if(OPENSSL_FOUND AND WITH_OPENSSL)
+add_executable(OpenSSLManualInitTest OpenSSLManualInitTest.cpp)
+target_link_libraries(OpenSSLManualInitTest
+ ${OPENSSL_LIBRARIES}
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(OpenSSLManualInitTest thrift)
+add_test(NAME OpenSSLManualInitTest COMMAND OpenSSLManualInitTest)
+
+add_executable(SecurityTest SecurityTest.cpp)
+target_link_libraries(SecurityTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(SecurityTest thrift)
+if (NOT MSVC AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT MINGW)
+target_link_libraries(SecurityTest -lrt)
+endif ()
+add_test(NAME SecurityTest COMMAND SecurityTest -- "${CMAKE_CURRENT_SOURCE_DIR}/../../../test/keys")
+
+endif()
+
+if(WITH_QT5)
+add_subdirectory(qt)
+endif()
+
+#
+# Common thrift code generation rules
+#
+
+add_custom_command(OUTPUT gen-cpp/AnnotationTest_constants.cpp
+ gen-cpp/AnnotationTest_constants.h
+ gen-cpp/AnnotationTest_types.cpp
+ gen-cpp/AnnotationTest_types.h
+ gen-cpp/foo_service.cpp
+ gen-cpp/foo_service.h
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/AnnotationTest.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/DebugProtoTest_types.cpp gen-cpp/DebugProtoTest_types.h gen-cpp/EmptyService.cpp gen-cpp/EmptyService.h
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/DebugProtoTest.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/EnumTest_types.cpp gen-cpp/EnumTest_types.h
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/EnumTest.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/TypedefTest_types.cpp gen-cpp/TypedefTest_types.h
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/TypedefTest.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/OptionalRequiredTest_types.cpp gen-cpp/OptionalRequiredTest_types.h
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/OptionalRequiredTest.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/Recursive_types.cpp gen-cpp/Recursive_types.h
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/Recursive.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/Service.cpp gen-cpp/StressTest_types.cpp
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/StressTest.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/SecondService.cpp gen-cpp/ThriftTest_constants.cpp gen-cpp/ThriftTest.cpp gen-cpp/ThriftTest_types.cpp gen-cpp/ThriftTest_types.h
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/ThriftTest.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/OneWayService.cpp gen-cpp/OneWayTest_constants.cpp gen-cpp/OneWayTest_types.h gen-cpp/OneWayService.h gen-cpp/OneWayTest_constants.h gen-cpp/OneWayTest_types.cpp
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${CMAKE_CURRENT_SOURCE_DIR}/OneWayTest.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/ChildService.cpp gen-cpp/ChildService.h gen-cpp/ParentService.cpp gen-cpp/ParentService.h gen-cpp/proc_types.cpp gen-cpp/proc_types.h
+ COMMAND ${THRIFT_COMPILER} --gen cpp:templates,cob_style ${CMAKE_CURRENT_SOURCE_DIR}/processor/proc.thrift
+)
diff --git a/src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest.cpp
new file mode 100644
index 000000000..060f3547d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest.cpp
@@ -0,0 +1,310 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define _USE_MATH_DEFINES
+#include <cmath>
+#include "gen-cpp/DebugProtoTest_types.h"
+#include <thrift/protocol/TDebugProtocol.h>
+#include <memory>
+
+#define BOOST_TEST_MODULE DebugProtoTest
+#include <boost/test/unit_test.hpp>
+
+using namespace thrift::test::debug;
+
+static ::std::shared_ptr<OneOfEach> ooe;
+
+void testCaseSetup_1() {
+ ooe.reset(new OneOfEach);
+ ooe->im_true = true;
+ ooe->im_false = false;
+ ooe->a_bite = 0x7f;
+ ooe->integer16 = 27000;
+ ooe->integer32 = 1 << 24;
+ ooe->integer64 = (uint64_t)6000 * 1000 * 1000;
+ ooe->double_precision = M_PI;
+ ooe->some_characters = "Debug THIS!";
+ ooe->zomg_unicode = "\xd7\n\a\t";
+}
+
+BOOST_AUTO_TEST_CASE(test_debug_proto_1) {
+ testCaseSetup_1();
+
+ const std::string expected_result(
+ "OneOfEach {\n"
+ " 01: im_true (bool) = true,\n"
+ " 02: im_false (bool) = false,\n"
+ " 03: a_bite (byte) = 0x7f,\n"
+ " 04: integer16 (i16) = 27000,\n"
+ " 05: integer32 (i32) = 16777216,\n"
+ " 06: integer64 (i64) = 6000000000,\n"
+ " 07: double_precision (double) = 3.1415926535897931,\n"
+ " 08: some_characters (string) = \"Debug THIS!\",\n"
+ " 09: zomg_unicode (string) = \"\\xd7\\n\\a\\t\",\n"
+ " 10: what_who (bool) = false,\n"
+ " 11: base64 (string) = \"\",\n"
+ " 12: byte_list (list) = list<byte>[3] {\n"
+ " [0] = 0x01,\n"
+ " [1] = 0x02,\n"
+ " [2] = 0x03,\n"
+ " },\n"
+ " 13: i16_list (list) = list<i16>[3] {\n"
+ " [0] = 1,\n"
+ " [1] = 2,\n"
+ " [2] = 3,\n"
+ " },\n"
+ " 14: i64_list (list) = list<i64>[3] {\n"
+ " [0] = 1,\n"
+ " [1] = 2,\n"
+ " [2] = 3,\n"
+ " },\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(*ooe));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+static ::std::shared_ptr<Nesting> n;
+
+void testCaseSetup_2() {
+ testCaseSetup_1();
+
+ n.reset(new Nesting);
+ n->my_ooe = *ooe;
+ n->my_ooe.integer16 = 16;
+ n->my_ooe.integer32 = 32;
+ n->my_ooe.integer64 = 64;
+ n->my_ooe.double_precision = (std::sqrt(5.0) + 1) / 2;
+ n->my_ooe.some_characters = ":R (me going \"rrrr\")";
+ n->my_ooe.zomg_unicode = "\xd3\x80\xe2\x85\xae\xce\x9d\x20\xd0\x9d\xce"
+ "\xbf\xe2\x85\xbf\xd0\xbe\xc9\xa1\xd0\xb3\xd0"
+ "\xb0\xcf\x81\xe2\x84\x8e\x20\xce\x91\x74\x74"
+ "\xce\xb1\xe2\x85\xbd\xce\xba\xc7\x83\xe2\x80"
+ "\xbc";
+ n->my_bonk.type = 31337;
+ n->my_bonk.message = "I am a bonk... xor!";
+}
+
+BOOST_AUTO_TEST_CASE(test_debug_proto_2) {
+ testCaseSetup_2();
+
+ const std::string expected_result(
+ "Nesting {\n"
+ " 01: my_bonk (struct) = Bonk {\n"
+ " 01: type (i32) = 31337,\n"
+ " 02: message (string) = \"I am a bonk... xor!\",\n"
+ " },\n"
+ " 02: my_ooe (struct) = OneOfEach {\n"
+ " 01: im_true (bool) = true,\n"
+ " 02: im_false (bool) = false,\n"
+ " 03: a_bite (byte) = 0x7f,\n"
+ " 04: integer16 (i16) = 16,\n"
+ " 05: integer32 (i32) = 32,\n"
+ " 06: integer64 (i64) = 64,\n"
+ " 07: double_precision (double) = 1.6180339887498949,\n"
+ " 08: some_characters (string) = \":R (me going \\\"rrrr\\\")\",\n"
+ " 09: zomg_unicode (string) = \"\\xd3\\x80\\xe2\\x85\\xae\\xce\\x9d \\xd"
+ "0\\x9d\\xce\\xbf\\xe2\\x85\\xbf\\xd0\\xbe\\xc9\\xa1\\xd0\\xb3\\xd0\\xb0"
+ "\\xcf\\x81\\xe2\\x84\\x8e \\xce\\x91tt\\xce\\xb1\\xe2\\x85\\xbd\\xce\\xb"
+ "a\\xc7\\x83\\xe2\\x80\\xbc\",\n"
+ " 10: what_who (bool) = false,\n"
+ " 11: base64 (string) = \"\",\n"
+ " 12: byte_list (list) = list<byte>[3] {\n"
+ " [0] = 0x01,\n"
+ " [1] = 0x02,\n"
+ " [2] = 0x03,\n"
+ " },\n"
+ " 13: i16_list (list) = list<i16>[3] {\n"
+ " [0] = 1,\n"
+ " [1] = 2,\n"
+ " [2] = 3,\n"
+ " },\n"
+ " 14: i64_list (list) = list<i64>[3] {\n"
+ " [0] = 1,\n"
+ " [1] = 2,\n"
+ " [2] = 3,\n"
+ " },\n"
+ " },\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(*n));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+static ::std::shared_ptr<HolyMoley> hm;
+
+void testCaseSetup_3() {
+ testCaseSetup_2();
+
+ hm.reset(new HolyMoley);
+
+ hm->big.push_back(*ooe);
+ hm->big.push_back(n->my_ooe);
+ hm->big[0].a_bite = 0x22;
+ hm->big[1].a_bite = 0x33;
+
+ std::vector<std::string> stage1;
+ stage1.push_back("and a one");
+ stage1.push_back("and a two");
+ hm->contain.insert(stage1);
+ stage1.clear();
+ stage1.push_back("then a one, two");
+ stage1.push_back("three!");
+ stage1.push_back("FOUR!!");
+ hm->contain.insert(stage1);
+ stage1.clear();
+ hm->contain.insert(stage1);
+
+ std::vector<Bonk> stage2;
+ hm->bonks["nothing"] = stage2;
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 1;
+ stage2.back().message = "Wait.";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 2;
+ stage2.back().message = "What?";
+ hm->bonks["something"] = stage2;
+ stage2.clear();
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 3;
+ stage2.back().message = "quoth";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 4;
+ stage2.back().message = "the raven";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 5;
+ stage2.back().message = "nevermore";
+ hm->bonks["poe"] = stage2;
+}
+
+BOOST_AUTO_TEST_CASE(test_debug_proto_3) {
+ testCaseSetup_3();
+
+ const std::string expected_result(
+ "HolyMoley {\n"
+ " 01: big (list) = list<struct>[2] {\n"
+ " [0] = OneOfEach {\n"
+ " 01: im_true (bool) = true,\n"
+ " 02: im_false (bool) = false,\n"
+ " 03: a_bite (byte) = 0x22,\n"
+ " 04: integer16 (i16) = 27000,\n"
+ " 05: integer32 (i32) = 16777216,\n"
+ " 06: integer64 (i64) = 6000000000,\n"
+ " 07: double_precision (double) = 3.1415926535897931,\n"
+ " 08: some_characters (string) = \"Debug THIS!\",\n"
+ " 09: zomg_unicode (string) = \"\\xd7\\n\\a\\t\",\n"
+ " 10: what_who (bool) = false,\n"
+ " 11: base64 (string) = \"\",\n"
+ " 12: byte_list (list) = list<byte>[3] {\n"
+ " [0] = 0x01,\n"
+ " [1] = 0x02,\n"
+ " [2] = 0x03,\n"
+ " },\n"
+ " 13: i16_list (list) = list<i16>[3] {\n"
+ " [0] = 1,\n"
+ " [1] = 2,\n"
+ " [2] = 3,\n"
+ " },\n"
+ " 14: i64_list (list) = list<i64>[3] {\n"
+ " [0] = 1,\n"
+ " [1] = 2,\n"
+ " [2] = 3,\n"
+ " },\n"
+ " },\n"
+ " [1] = OneOfEach {\n"
+ " 01: im_true (bool) = true,\n"
+ " 02: im_false (bool) = false,\n"
+ " 03: a_bite (byte) = 0x33,\n"
+ " 04: integer16 (i16) = 16,\n"
+ " 05: integer32 (i32) = 32,\n"
+ " 06: integer64 (i64) = 64,\n"
+ " 07: double_precision (double) = 1.6180339887498949,\n"
+ " 08: some_characters (string) = \":R (me going \\\"rrrr\\\")\",\n"
+ " 09: zomg_unicode (string) = \"\\xd3\\x80\\xe2\\x85\\xae\\xce\\x9d \\"
+ "xd0\\x9d\\xce\\xbf\\xe2\\x85\\xbf\\xd0\\xbe\\xc9\\xa1\\xd0\\xb3\\xd0\\xb"
+ "0\\xcf\\x81\\xe2\\x84\\x8e \\xce\\x91tt\\xce\\xb1\\xe2\\x85\\xbd\\xce\\x"
+ "ba\\xc7\\x83\\xe2\\x80\\xbc\",\n"
+ " 10: what_who (bool) = false,\n"
+ " 11: base64 (string) = \"\",\n"
+ " 12: byte_list (list) = list<byte>[3] {\n"
+ " [0] = 0x01,\n"
+ " [1] = 0x02,\n"
+ " [2] = 0x03,\n"
+ " },\n"
+ " 13: i16_list (list) = list<i16>[3] {\n"
+ " [0] = 1,\n"
+ " [1] = 2,\n"
+ " [2] = 3,\n"
+ " },\n"
+ " 14: i64_list (list) = list<i64>[3] {\n"
+ " [0] = 1,\n"
+ " [1] = 2,\n"
+ " [2] = 3,\n"
+ " },\n"
+ " },\n"
+ " },\n"
+ " 02: contain (set) = set<list>[3] {\n"
+ " list<string>[0] {\n"
+ " },\n"
+ " list<string>[2] {\n"
+ " [0] = \"and a one\",\n"
+ " [1] = \"and a two\",\n"
+ " },\n"
+ " list<string>[3] {\n"
+ " [0] = \"then a one, two\",\n"
+ " [1] = \"three!\",\n"
+ " [2] = \"FOUR!!\",\n"
+ " },\n"
+ " },\n"
+ " 03: bonks (map) = map<string,list>[3] {\n"
+ " \"nothing\" -> list<struct>[0] {\n"
+ " },\n"
+ " \"poe\" -> list<struct>[3] {\n"
+ " [0] = Bonk {\n"
+ " 01: type (i32) = 3,\n"
+ " 02: message (string) = \"quoth\",\n"
+ " },\n"
+ " [1] = Bonk {\n"
+ " 01: type (i32) = 4,\n"
+ " 02: message (string) = \"the raven\",\n"
+ " },\n"
+ " [2] = Bonk {\n"
+ " 01: type (i32) = 5,\n"
+ " 02: message (string) = \"nevermore\",\n"
+ " },\n"
+ " },\n"
+ " \"something\" -> list<struct>[2] {\n"
+ " [0] = Bonk {\n"
+ " 01: type (i32) = 1,\n"
+ " 02: message (string) = \"Wait.\",\n"
+ " },\n"
+ " [1] = Bonk {\n"
+ " 01: type (i32) = 2,\n"
+ " 02: message (string) = \"What?\",\n"
+ " },\n"
+ " },\n"
+ " },\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(*hm));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest_extras.cpp b/src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest_extras.cpp
new file mode 100644
index 000000000..5c4fd35e7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest_extras.cpp
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Extra functions required for DebugProtoTest_types to work
+
+#include "gen-cpp/DebugProtoTest_types.h"
+
+namespace thrift {
+namespace test {
+namespace debug {
+
+bool Empty::operator<(Empty const& other) const {
+ (void)other;
+ // It is empty, so all are equal.
+ return false;
+}
+}
+}
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/EnumTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/EnumTest.cpp
new file mode 100644
index 000000000..388abb7e9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/EnumTest.cpp
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#define BOOST_TEST_MODULE EnumTest
+#include <boost/test/unit_test.hpp>
+#include "gen-cpp/EnumTest_types.h"
+
+std::ostream& operator <<(std::ostream& os, const MyEnumWithCustomOstream::type& val)
+{
+ os << "{" << (int)val << ":CUSTOM!" << "}";
+ return os;
+}
+
+std::string to_string(const MyEnumWithCustomOstream::type& val)
+{
+ std::ostringstream os;
+ os << val;
+ return os.str();
+}
+
+BOOST_AUTO_TEST_SUITE(EnumTest)
+
+BOOST_AUTO_TEST_CASE(test_enum_value) {
+ // Check that all the enum values match what we expect
+ BOOST_CHECK_EQUAL(MyEnum1::ME1_0, 0);
+ BOOST_CHECK_EQUAL(MyEnum1::ME1_1, 1);
+ BOOST_CHECK_EQUAL(MyEnum1::ME1_2, 2);
+ BOOST_CHECK_EQUAL(MyEnum1::ME1_3, 3);
+ BOOST_CHECK_EQUAL(MyEnum1::ME1_5, 5);
+ BOOST_CHECK_EQUAL(MyEnum1::ME1_6, 6);
+
+ BOOST_CHECK_EQUAL(MyEnum2::ME2_0, 0);
+ BOOST_CHECK_EQUAL(MyEnum2::ME2_1, 1);
+ BOOST_CHECK_EQUAL(MyEnum2::ME2_2, 2);
+
+ BOOST_CHECK_EQUAL(MyEnum3::ME3_0, 0);
+ BOOST_CHECK_EQUAL(MyEnum3::ME3_1, 1);
+ BOOST_CHECK_EQUAL(MyEnum3::ME3_N2, -2);
+ BOOST_CHECK_EQUAL(MyEnum3::ME3_N1, -1);
+ BOOST_CHECK_EQUAL(MyEnum3::ME3_D0, 0);
+ BOOST_CHECK_EQUAL(MyEnum3::ME3_D1, 1);
+ BOOST_CHECK_EQUAL(MyEnum3::ME3_9, 9);
+ BOOST_CHECK_EQUAL(MyEnum3::ME3_10, 10);
+
+ BOOST_CHECK_EQUAL(MyEnum4::ME4_A, 0x7ffffffd);
+ BOOST_CHECK_EQUAL(MyEnum4::ME4_B, 0x7ffffffe);
+ BOOST_CHECK_EQUAL(MyEnum4::ME4_C, 0x7fffffff);
+
+ BOOST_CHECK_EQUAL(MyEnum5::e1, 0);
+ BOOST_CHECK_EQUAL(MyEnum5::e2, 42);
+}
+
+template <class _T>
+std::string EnumToString(_T e)
+{
+ std::stringstream ss;
+ ss << e;
+ return ss.str();
+}
+
+BOOST_AUTO_TEST_CASE(test_enum_ostream)
+{
+ BOOST_CHECK_EQUAL(EnumToString(MyEnum1::ME1_0), "ME1_0");
+ BOOST_CHECK_EQUAL(EnumToString(MyEnum5::e2), "e2");
+ BOOST_CHECK_EQUAL(EnumToString(MyEnum3::ME3_N1), "ME3_N1");
+ BOOST_CHECK_EQUAL(EnumToString(MyEnumWithCustomOstream::CustoM2), "{2:CUSTOM!}");
+
+ // some invalid or unknown value
+ auto uut = static_cast<MyEnum5::type>(44);
+ BOOST_CHECK_EQUAL(EnumToString(uut), "44");
+}
+
+BOOST_AUTO_TEST_CASE(test_enum_to_string)
+{
+ BOOST_CHECK_EQUAL(::to_string(MyEnum1::ME1_0), "ME1_0");
+ BOOST_CHECK_EQUAL(::to_string(MyEnum5::e2), "e2");
+ BOOST_CHECK_EQUAL(::to_string(MyEnum3::ME3_N1), "ME3_N1");
+ BOOST_CHECK_EQUAL(::to_string(MyEnumWithCustomOstream::CustoM2), "{2:CUSTOM!}");
+
+ // some invalid or unknown value
+ auto uut = static_cast<MyEnum5::type>(44);
+ BOOST_CHECK_EQUAL(::to_string(uut), "44");
+}
+
+BOOST_AUTO_TEST_CASE(test_enum_constant)
+{
+ MyStruct ms;
+ BOOST_CHECK_EQUAL(ms.me2_2, 2);
+ BOOST_CHECK_EQUAL(ms.me3_n2, -2);
+ BOOST_CHECK_EQUAL(ms.me3_d1, 1);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/GenericHelpers.h b/src/jaegertracing/thrift/lib/cpp/test/GenericHelpers.h
new file mode 100644
index 000000000..bcef9f242
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/GenericHelpers.h
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TEST_GENERICHELPERS_H_
+#define _THRIFT_TEST_GENERICHELPERS_H_ 1
+
+#include <thrift/protocol/TProtocol.h>
+#include <memory>
+#include <thrift/Thrift.h>
+
+/* ClassName Helper for cleaner exceptions */
+class ClassNames {
+public:
+ template <typename T>
+ static const char* getName() {
+ return "Unknown type";
+ }
+};
+
+template <>
+const char* ClassNames::getName<int8_t>() {
+ return "byte";
+}
+template <>
+const char* ClassNames::getName<int16_t>() {
+ return "short";
+}
+template <>
+const char* ClassNames::getName<int32_t>() {
+ return "int";
+}
+template <>
+const char* ClassNames::getName<int64_t>() {
+ return "long";
+}
+template <>
+const char* ClassNames::getName<double>() {
+ return "double";
+}
+template <>
+const char* ClassNames::getName<std::string>() {
+ return "string";
+}
+
+/* Generic Protocol I/O function for tests */
+class GenericIO {
+public:
+ /* Write functions */
+
+ static uint32_t write(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, const int8_t& val) {
+ return proto->writeByte(val);
+ }
+
+ static uint32_t write(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, const int16_t& val) {
+ return proto->writeI16(val);
+ }
+
+ static uint32_t write(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, const int32_t& val) {
+ return proto->writeI32(val);
+ }
+
+ static uint32_t write(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, const double& val) {
+ return proto->writeDouble(val);
+ }
+
+ static uint32_t write(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, const int64_t& val) {
+ return proto->writeI64(val);
+ }
+
+ static uint32_t write(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, const std::string& val) {
+ return proto->writeString(val);
+ }
+
+ /* Read functions */
+
+ static uint32_t read(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, int8_t& val) { return proto->readByte(val); }
+
+ static uint32_t read(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, int16_t& val) { return proto->readI16(val); }
+
+ static uint32_t read(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, int32_t& val) { return proto->readI32(val); }
+
+ static uint32_t read(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, int64_t& val) { return proto->readI64(val); }
+
+ static uint32_t read(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, double& val) { return proto->readDouble(val); }
+
+ static uint32_t read(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, std::string& val) {
+ return proto->readString(val);
+ }
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/test/JSONProtoTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/JSONProtoTest.cpp
new file mode 100644
index 000000000..c2ad73e71
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/JSONProtoTest.cpp
@@ -0,0 +1,343 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define _USE_MATH_DEFINES
+#include <cmath>
+#include <iomanip>
+#include <sstream>
+#include <thrift/protocol/TJSONProtocol.h>
+#include <memory>
+#include <thrift/transport/TBufferTransports.h>
+#include "gen-cpp/DebugProtoTest_types.h"
+
+#define BOOST_TEST_MODULE JSONProtoTest
+#include <boost/test/unit_test.hpp>
+
+using namespace thrift::test::debug;
+using namespace apache::thrift;
+using apache::thrift::transport::TMemoryBuffer;
+using apache::thrift::protocol::TJSONProtocol;
+
+static std::shared_ptr<OneOfEach> ooe;
+
+void testCaseSetup_1() {
+ ooe.reset(new OneOfEach);
+ ooe->im_true = true;
+ ooe->im_false = false;
+ ooe->a_bite = 0x7f;
+ ooe->integer16 = 27000;
+ ooe->integer32 = 1 << 24;
+ ooe->integer64 = (uint64_t)6000 * 1000 * 1000;
+ ooe->double_precision = M_PI;
+ ooe->some_characters = "JSON THIS! \"\1";
+ ooe->zomg_unicode = "\xd7\n\a\t";
+ ooe->base64 = "\1\2\3\255";
+}
+
+BOOST_AUTO_TEST_CASE(test_json_proto_1) {
+ testCaseSetup_1();
+
+ const std::string expected_result(
+ "{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127},\"4\":{\"i16\":27000},"
+ "\"5\":{\"i32\":16777216},\"6\":{\"i64\":6000000000},\"7\":{\"dbl\":3.1415926"
+ "535897931},\"8\":{\"str\":\"JSON THIS! \\\"\\u0001\"},\"9\":{\"str\":\"\xd7\\"
+ "n\\u0007\\t\"},\"10\":{\"tf\":0},\"11\":{\"str\":\"AQIDrQ\"},\"12\":{\"lst\""
+ ":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64"
+ "\",3,1,2,3]}}");
+
+ const std::string result(apache::thrift::ThriftJSONString(*ooe));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+static std::shared_ptr<Nesting> n;
+
+void testCaseSetup_2() {
+ testCaseSetup_1();
+
+ n.reset(new Nesting);
+ n->my_ooe = *ooe;
+ n->my_ooe.integer16 = 16;
+ n->my_ooe.integer32 = 32;
+ n->my_ooe.integer64 = 64;
+ n->my_ooe.double_precision = (std::sqrt(5.0) + 1) / 2;
+ n->my_ooe.some_characters = ":R (me going \"rrrr\")";
+ n->my_ooe.zomg_unicode = "\xd3\x80\xe2\x85\xae\xce\x9d\x20\xd0\x9d\xce"
+ "\xbf\xe2\x85\xbf\xd0\xbe\xc9\xa1\xd0\xb3\xd0"
+ "\xb0\xcf\x81\xe2\x84\x8e\x20\xce\x91\x74\x74"
+ "\xce\xb1\xe2\x85\xbd\xce\xba\xc7\x83\xe2\x80"
+ "\xbc";
+ n->my_bonk.type = 31337;
+ n->my_bonk.message = "I am a bonk... xor!";
+}
+
+BOOST_AUTO_TEST_CASE(test_json_proto_2) {
+ testCaseSetup_2();
+
+ const std::string expected_result(
+ "{\"1\":{\"rec\":{\"1\":{\"i32\":31337},\"2\":{\"str\":\"I am a bonk... xor"
+ "!\"}}},\"2\":{\"rec\":{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127"
+ "},\"4\":{\"i16\":16},\"5\":{\"i32\":32},\"6\":{\"i64\":64},\"7\":{\"dbl\":"
+ "1.6180339887498949},\"8\":{\"str\":\":R (me going \\\"rrrr\\\")\"},\"9\":{"
+ "\"str\":\"ӀⅮΠÐοⅿоɡгаÏâ„Ž Αttαⅽκǃ‼\"},\"10\":{\"tf\":0},\"11\":{\"str\":\""
+ "AQIDrQ\"},\"12\":{\"lst\":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2"
+ ",3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]}}}}"
+ );
+
+ const std::string result(apache::thrift::ThriftJSONString(*n));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+static std::shared_ptr<HolyMoley> hm;
+
+void testCaseSetup_3() {
+ testCaseSetup_2();
+
+ hm.reset(new HolyMoley);
+
+ hm->big.push_back(*ooe);
+ hm->big.push_back(n->my_ooe);
+ hm->big[0].a_bite = 0x22;
+ hm->big[1].a_bite = 0x33;
+
+ std::vector<std::string> stage1;
+ stage1.push_back("and a one");
+ stage1.push_back("and a two");
+ hm->contain.insert(stage1);
+ stage1.clear();
+ stage1.push_back("then a one, two");
+ stage1.push_back("three!");
+ stage1.push_back("FOUR!!");
+ hm->contain.insert(stage1);
+ stage1.clear();
+ hm->contain.insert(stage1);
+
+ std::vector<Bonk> stage2;
+ hm->bonks["nothing"] = stage2;
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 1;
+ stage2.back().message = "Wait.";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 2;
+ stage2.back().message = "What?";
+ hm->bonks["something"] = stage2;
+ stage2.clear();
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 3;
+ stage2.back().message = "quoth";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 4;
+ stage2.back().message = "the raven";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 5;
+ stage2.back().message = "nevermore";
+ hm->bonks["poe"] = stage2;
+}
+
+BOOST_AUTO_TEST_CASE(test_json_proto_3) {
+ testCaseSetup_3();
+
+ const std::string expected_result(
+ "{\"1\":{\"lst\":[\"rec\",2,{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":"
+ "34},\"4\":{\"i16\":27000},\"5\":{\"i32\":16777216},\"6\":{\"i64\":6000000000"
+ "},\"7\":{\"dbl\":3.1415926535897931},\"8\":{\"str\":\"JSON THIS! \\\"\\u0001"
+ "\"},\"9\":{\"str\":\"\xd7\\n\\u0007\\t\"},\"10\":{\"tf\":0},\"11\":{\"str\":"
+ "\"AQIDrQ\"},\"12\":{\"lst\":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2"
+ ",3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]}},{\"1\":{\"tf\":1},\"2\":{\"tf\":0},"
+ "\"3\":{\"i8\":51},\"4\":{\"i16\":16},\"5\":{\"i32\":32},\"6\":{\"i64\":64},"
+ "\"7\":{\"dbl\":1.6180339887498949},\"8\":{\"str\":\":R (me going \\\"rrrr\\\""
+ ")\"},\"9\":{\"str\":\"ӀⅮΠÐοⅿоɡгаÏâ„Ž Αttαⅽκǃ‼\"},\"10\":{\"tf\":0},\"11\":{"
+ "\"str\":\"AQIDrQ\"},\"12\":{\"lst\":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16"
+ "\",3,1,2,3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]}}]},\"2\":{\"set\":[\"lst\",3"
+ ",[\"str\",0],[\"str\",2,\"and a one\",\"and a two\"],[\"str\",3,\"then a one"
+ ", two\",\"three!\",\"FOUR!!\"]]},\"3\":{\"map\":[\"str\",\"lst\",3,{\"nothin"
+ "g\":[\"rec\",0],\"poe\":[\"rec\",3,{\"1\":{\"i32\":3},\"2\":{\"str\":\"quoth"
+ "\"}},{\"1\":{\"i32\":4},\"2\":{\"str\":\"the raven\"}},{\"1\":{\"i32\":5},\""
+ "2\":{\"str\":\"nevermore\"}}],\"something\":[\"rec\",2,{\"1\":{\"i32\":1},\""
+ "2\":{\"str\":\"Wait.\"}},{\"1\":{\"i32\":2},\"2\":{\"str\":\"What?\"}}]}]}}"
+ );
+
+ const std::string result(apache::thrift::ThriftJSONString(*hm));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_json_proto_4) {
+ testCaseSetup_1();
+
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
+ std::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
+
+ ooe->write(proto.get());
+ OneOfEach ooe2;
+ ooe2.read(proto.get());
+
+ BOOST_CHECK(*ooe == ooe2);
+}
+
+BOOST_AUTO_TEST_CASE(test_json_proto_5) {
+ testCaseSetup_3();
+
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
+ std::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
+
+ hm->write(proto.get());
+ HolyMoley hm2;
+ hm2.read(proto.get());
+
+ BOOST_CHECK(*hm == hm2);
+
+ hm2.big[0].a_bite = 0x00;
+
+ BOOST_CHECK(*hm != hm2);
+}
+
+BOOST_AUTO_TEST_CASE(test_json_proto_6) {
+ Doubles dub;
+ dub.nan = HUGE_VAL / HUGE_VAL;
+ dub.inf = HUGE_VAL;
+ dub.neginf = -HUGE_VAL;
+ dub.repeating = 10.0 / 3.0;
+ dub.big = 1E+305;
+ dub.tiny = 1E-305;
+ dub.zero = 0.0;
+ dub.negzero = -0.0;
+
+ const std::string expected_result(
+ "{\"1\":{\"dbl\":\"NaN\"},\"2\":{\"dbl\":\"Infinity\"},\"3\":{\"dbl\":\"-Infi"
+ "nity\"},\"4\":{\"dbl\":3.3333333333333335},\"5\":{\"dbl\":9.9999999999999994e+"
+ "304},\"6\":{\"dbl\":1e-305},\"7\":{\"dbl\":0},\"8\":{\"dbl\":-0}}"
+ );
+
+ const std::string result(apache::thrift::ThriftJSONString(dub));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_json_proto_7) {
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
+ std::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
+
+ Base64 base;
+ base.a = 123;
+ base.b1 = "1";
+ base.b2 = "12";
+ base.b3 = "123";
+ base.b4 = "1234";
+ base.b5 = "12345";
+ base.b6 = "123456";
+
+ base.write(proto.get());
+ Base64 base2;
+ base2.read(proto.get());
+
+ BOOST_CHECK(base == base2);
+}
+
+BOOST_AUTO_TEST_CASE(test_json_proto_8) {
+ const char* json_string =
+ "{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127},\"4\":{\"i16\":27000},"
+ "\"5\":{\"i32\":16.77216},\"6\":{\"i64\":6000000000},\"7\":{\"dbl\":3.1415926"
+ "535897931},\"8\":{\"str\":\"JSON THIS! \\\"\\u0001\"},\"9\":{\"str\":\"\xd7\\"
+ "n\\u0007\\t\"},\"10\":{\"tf\":0},\"11\":{\"str\":\"AQIDrQ\"},\"12\":{\"lst\""
+ ":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64"
+ "\",3,1,2,3]}}";
+
+ const std::size_t bufSiz = strlen(json_string) * sizeof(char);
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(
+ (uint8_t*)(json_string), static_cast<uint32_t>(bufSiz)));
+ std::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
+
+ OneOfEach ooe2;
+
+ BOOST_CHECK_THROW(ooe2.read(proto.get()),
+ apache::thrift::protocol::TProtocolException);
+}
+
+static std::string toHexSequence(const std::string& str) {
+ std::stringstream ss;
+ ss << std::hex << std::setfill('0');
+ for (std::size_t i = 0; i < str.size(); i++) {
+ ss << "\\x" << int(uint8_t(str[i]));
+ }
+ return ss.str();
+}
+
+BOOST_AUTO_TEST_CASE(test_json_unicode_escaped) {
+ const char json_string[] =
+ "{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127},\"4\":{\"i16\":27000},"
+ "\"5\":{\"i32\":16},\"6\":{\"i64\":6000000000},\"7\":{\"dbl\":3.1415926"
+ "535897931},\"8\":{\"str\":\"JSON THIS!\"},\"9\":{\"str\":\"\\u0e01 \\ud835\\udd3e\"},"
+ "\"10\":{\"tf\":0},\"11\":{\"str\":\"000000\"},\"12\":{\"lst\""
+ ":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64"
+ "\",3,1,2,3]}}";
+ const char* expected_zomg_unicode = "\xe0\xb8\x81 \xf0\x9d\x94\xbe";
+
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(
+ (uint8_t*)(json_string), sizeof(json_string)));
+ std::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
+
+ OneOfEach ooe2;
+ ooe2.read(proto.get());
+ BOOST_CHECK_MESSAGE(!ooe2.zomg_unicode.compare(expected_zomg_unicode),
+ "Expected:\n" << toHexSequence(expected_zomg_unicode) << "\nGotten:\n"
+ << toHexSequence(ooe2.zomg_unicode));
+
+}
+
+BOOST_AUTO_TEST_CASE(test_json_unicode_escaped_missing_low_surrogate) {
+ const char json_string[] =
+ "{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127},\"4\":{\"i16\":27000},"
+ "\"5\":{\"i32\":16},\"6\":{\"i64\":6000000000},\"7\":{\"dbl\":3.1415926"
+ "535897931},\"8\":{\"str\":\"JSON THIS!\"},\"9\":{\"str\":\"\\ud835\"},"
+ "\"10\":{\"tf\":0},\"11\":{\"str\":\"000000\"},\"12\":{\"lst\""
+ ":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64"
+ "\",3,1,2,3]}}";
+
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(
+ (uint8_t*)(json_string), sizeof(json_string)));
+ std::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
+
+ OneOfEach ooe2;
+ BOOST_CHECK_THROW(ooe2.read(proto.get()),
+ apache::thrift::protocol::TProtocolException);
+}
+
+BOOST_AUTO_TEST_CASE(test_json_unicode_escaped_missing_hi_surrogate) {
+ const char json_string[] =
+ "{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127},\"4\":{\"i16\":27000},"
+ "\"5\":{\"i32\":16},\"6\":{\"i64\":6000000000},\"7\":{\"dbl\":3.1415926"
+ "535897931},\"8\":{\"str\":\"JSON THIS!\"},\"9\":{\"str\":\"\\udd3e\"},"
+ "\"10\":{\"tf\":0},\"11\":{\"str\":\"000000\"},\"12\":{\"lst\""
+ ":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64"
+ "\",3,1,2,3]}}";
+
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(
+ (uint8_t*)(json_string), sizeof(json_string)));
+ std::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
+
+ OneOfEach ooe2;
+ BOOST_CHECK_THROW(ooe2.read(proto.get()),
+ apache::thrift::protocol::TProtocolException);
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/Makefile.am b/src/jaegertracing/thrift/lib/cpp/test/Makefile.am
new file mode 100755
index 000000000..2a0b9e693
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/Makefile.am
@@ -0,0 +1,425 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+AUTOMAKE_OPTIONS = subdir-objects serial-tests nostdinc
+
+BUILT_SOURCES = gen-cpp/AnnotationTest_types.h \
+ gen-cpp/DebugProtoTest_types.h \
+ gen-cpp/EnumTest_types.h \
+ gen-cpp/OptionalRequiredTest_types.h \
+ gen-cpp/Recursive_types.h \
+ gen-cpp/ThriftTest_types.h \
+ gen-cpp/TypedefTest_types.h \
+ gen-cpp/ChildService.h \
+ gen-cpp/EmptyService.h \
+ gen-cpp/ParentService.h \
+ gen-cpp/OneWayTest_types.h \
+ gen-cpp/OneWayService.h \
+ gen-cpp/OneWayTest_constants.h \
+ gen-cpp/proc_types.h
+
+noinst_LTLIBRARIES = libtestgencpp.la libprocessortest.la
+nodist_libtestgencpp_la_SOURCES = \
+ gen-cpp/AnnotationTest_types.cpp \
+ gen-cpp/AnnotationTest_types.h \
+ gen-cpp/DebugProtoTest_types.cpp \
+ gen-cpp/DebugProtoTest_types.h \
+ gen-cpp/DoubleConstantsTest_constants.cpp \
+ gen-cpp/DoubleConstantsTest_constants.h \
+ gen-cpp/EnumTest_types.cpp \
+ gen-cpp/EnumTest_types.h \
+ gen-cpp/OptionalRequiredTest_types.cpp \
+ gen-cpp/OptionalRequiredTest_types.h \
+ gen-cpp/Recursive_types.cpp \
+ gen-cpp/Recursive_types.h \
+ gen-cpp/ThriftTest_types.cpp \
+ gen-cpp/ThriftTest_types.h \
+ gen-cpp/ThriftTest_constants.cpp \
+ gen-cpp/ThriftTest_constants.h \
+ gen-cpp/TypedefTest_types.cpp \
+ gen-cpp/TypedefTest_types.h \
+ gen-cpp/OneWayService.cpp \
+ gen-cpp/OneWayTest_constants.cpp \
+ gen-cpp/OneWayTest_types.h \
+ gen-cpp/OneWayService.h \
+ gen-cpp/OneWayTest_constants.h \
+ gen-cpp/OneWayTest_types.cpp \
+ ThriftTest_extras.cpp \
+ DebugProtoTest_extras.cpp
+
+nodist_libprocessortest_la_SOURCES = \
+ gen-cpp/ChildService.cpp \
+ gen-cpp/ChildService.h \
+ gen-cpp/EmptyService.cpp \
+ gen-cpp/EmptyService.h \
+ gen-cpp/ParentService.cpp \
+ gen-cpp/ParentService.h \
+ gen-cpp/proc_types.cpp \
+ gen-cpp/proc_types.h
+
+ThriftTest_extras.o: gen-cpp/ThriftTest_types.h
+DebugProtoTest_extras.o: gen-cpp/DebugProtoTest_types.h
+
+libtestgencpp_la_LIBADD = $(top_builddir)/lib/cpp/libthrift.la
+
+noinst_PROGRAMS = Benchmark \
+ concurrency_test
+
+Benchmark_SOURCES = \
+ Benchmark.cpp
+
+Benchmark_LDADD = libtestgencpp.la
+
+check_PROGRAMS = \
+ UnitTests \
+ TFDTransportTest \
+ TPipedTransportTest \
+ DebugProtoTest \
+ JSONProtoTest \
+ OptionalRequiredTest \
+ RecursiveTest \
+ SpecializationTest \
+ AllProtocolsTest \
+ TransportTest \
+ TInterruptTest \
+ TServerIntegrationTest \
+ SecurityTest \
+ ZlibTest \
+ TFileTransportTest \
+ link_test \
+ OpenSSLManualInitTest \
+ EnumTest \
+ RenderedDoubleConstantsTest \
+ AnnotationTest
+
+if AMX_HAVE_LIBEVENT
+noinst_PROGRAMS += \
+ processor_test
+check_PROGRAMS += \
+ TNonblockingServerTest \
+ TNonblockingSSLServerTest
+endif
+
+TESTS_ENVIRONMENT= \
+ BOOST_TEST_LOG_SINK=tests.xml \
+ BOOST_TEST_LOG_LEVEL=test_suite \
+ BOOST_TEST_LOG_FORMAT=XML
+
+TESTS = \
+ $(check_PROGRAMS)
+
+UnitTests_SOURCES = \
+ UnitTestMain.cpp \
+ OneWayHTTPTest.cpp \
+ TMemoryBufferTest.cpp \
+ TBufferBaseTest.cpp \
+ Base64Test.cpp \
+ ToStringTest.cpp \
+ TypedefTest.cpp \
+ TServerSocketTest.cpp \
+ TServerTransportTest.cpp \
+ TTransportCheckThrow.h
+
+UnitTests_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD) \
+ $(BOOST_SYSTEM_LDADD) \
+ $(BOOST_THREAD_LDADD)
+
+TInterruptTest_SOURCES = \
+ TSocketInterruptTest.cpp \
+ TSSLSocketInterruptTest.cpp
+
+TInterruptTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD) \
+ $(BOOST_FILESYSTEM_LDADD) \
+ $(BOOST_CHRONO_LDADD) \
+ $(BOOST_SYSTEM_LDADD) \
+ $(BOOST_THREAD_LDADD)
+
+TServerIntegrationTest_SOURCES = \
+ TServerIntegrationTest.cpp
+
+TServerIntegrationTest_LDADD = \
+ libtestgencpp.la \
+ libprocessortest.la \
+ $(BOOST_TEST_LDADD) \
+ $(BOOST_SYSTEM_LDADD) \
+ $(BOOST_THREAD_LDADD)
+
+SecurityTest_SOURCES = \
+ SecurityTest.cpp
+
+SecurityTest_LDADD = \
+ libtestgencpp.la \
+ libprocessortest.la \
+ $(BOOST_TEST_LDADD) \
+ $(BOOST_FILESYSTEM_LDADD) \
+ $(BOOST_SYSTEM_LDADD) \
+ $(BOOST_THREAD_LDADD)
+
+TransportTest_SOURCES = \
+ TransportTest.cpp
+
+TransportTest_LDADD = \
+ libtestgencpp.la \
+ $(top_builddir)/lib/cpp/libthriftz.la \
+ $(BOOST_TEST_LDADD) \
+ -lz
+
+ZlibTest_SOURCES = \
+ ZlibTest.cpp
+
+ZlibTest_LDADD = \
+ libtestgencpp.la \
+ $(top_builddir)/lib/cpp/libthriftz.la \
+ $(BOOST_TEST_LDADD) \
+ -lz
+
+EnumTest_SOURCES = \
+ EnumTest.cpp
+
+EnumTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+RenderedDoubleConstantsTest_SOURCES = RenderedDoubleConstantsTest.cpp
+
+RenderedDoubleConstantsTest_LDADD = libtestgencpp.la $(BOOST_TEST_LDADD)
+
+AnnotationTest_SOURCES = \
+ AnnotationTest.cpp
+
+AnnotationTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+TFileTransportTest_SOURCES = \
+ TFileTransportTest.cpp
+
+TFileTransportTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+#
+# TFDTransportTest
+#
+TFDTransportTest_SOURCES = \
+ TFDTransportTest.cpp
+
+TFDTransportTest_LDADD = \
+ $(top_builddir)/lib/cpp/libthrift.la \
+ $(BOOST_TEST_LDADD)
+
+
+#
+# TPipedTransportTest
+#
+TPipedTransportTest_SOURCES = \
+ TPipedTransportTest.cpp \
+ TPipeInterruptTest.cpp
+
+TPipedTransportTest_LDADD = \
+ libtestgencpp.la \
+ $(top_builddir)/lib/cpp/libthrift.la \
+ $(BOOST_TEST_LDADD) \
+ $(BOOST_SYSTEM_LDADD) \
+ $(BOOST_THREAD_LDADD)
+
+#
+# AllProtocolsTest
+#
+AllProtocolsTest_SOURCES = \
+ AllProtocolTests.cpp \
+ AllProtocolTests.tcc \
+ GenericHelpers.h
+
+AllProtocolsTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+#
+# DebugProtoTest
+#
+DebugProtoTest_SOURCES = \
+ DebugProtoTest.cpp
+
+DebugProtoTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+
+#
+# JSONProtoTest
+#
+JSONProtoTest_SOURCES = \
+ JSONProtoTest.cpp
+
+JSONProtoTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+#
+# TNonblockingServerTest
+#
+TNonblockingServerTest_SOURCES = TNonblockingServerTest.cpp
+
+TNonblockingServerTest_LDADD = libprocessortest.la \
+ $(top_builddir)/lib/cpp/libthrift.la \
+ $(top_builddir)/lib/cpp/libthriftnb.la \
+ $(BOOST_TEST_LDADD) \
+ $(BOOST_LDFLAGS) \
+ $(LIBEVENT_LIBS)
+#
+# TNonblockingSSLServerTest
+#
+TNonblockingSSLServerTest_SOURCES = TNonblockingSSLServerTest.cpp
+
+TNonblockingSSLServerTest_LDADD = libprocessortest.la \
+ $(top_builddir)/lib/cpp/libthrift.la \
+ $(top_builddir)/lib/cpp/libthriftnb.la \
+ $(BOOST_TEST_LDADD) \
+ $(BOOST_LDFLAGS) \
+ $(BOOST_FILESYSTEM_LDADD) \
+ $(BOOST_CHRONO_LDADD) \
+ $(BOOST_SYSTEM_LDADD) \
+ $(BOOST_THREAD_LDADD) \
+ $(LIBEVENT_LIBS)
+
+#
+# OptionalRequiredTest
+#
+OptionalRequiredTest_SOURCES = \
+ OptionalRequiredTest.cpp
+
+OptionalRequiredTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+#
+# OptionalRequiredTest
+#
+RecursiveTest_SOURCES = \
+ RecursiveTest.cpp
+
+RecursiveTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+#
+# SpecializationTest
+#
+SpecializationTest_SOURCES = \
+ SpecializationTest.cpp
+
+SpecializationTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+concurrency_test_SOURCES = \
+ concurrency/Tests.cpp \
+ concurrency/ThreadFactoryTests.h \
+ concurrency/ThreadManagerTests.h \
+ concurrency/TimerManagerTests.h
+
+concurrency_test_LDADD = \
+ $(top_builddir)/lib/cpp/libthrift.la
+
+link_test_SOURCES = \
+ link/LinkTest.cpp \
+ link/TemplatedService1.cpp \
+ link/TemplatedService2.cpp
+
+processor_test_SOURCES = \
+ processor/ProcessorTest.cpp \
+ processor/EventLog.cpp \
+ processor/ServerThread.cpp \
+ processor/EventLog.h \
+ processor/Handlers.h \
+ processor/ServerThread.h
+
+processor_test_LDADD = libprocessortest.la \
+ $(top_builddir)/lib/cpp/libthrift.la \
+ $(top_builddir)/lib/cpp/libthriftnb.la \
+ $(BOOST_TEST_LDADD) \
+ $(BOOST_LDFLAGS) \
+ $(LIBEVENT_LIBS)
+
+OpenSSLManualInitTest_SOURCES = \
+ OpenSSLManualInitTest.cpp
+
+OpenSSLManualInitTest_LDADD = \
+ $(top_builddir)/lib/cpp/libthrift.la \
+ $(BOOST_TEST_LDADD) \
+ $(OPENSSL_LDFLAGS) \
+ $(OPENSSL_LIBS)
+
+#
+# Common thrift code generation rules
+#
+
+gen-cpp/AnnotationTest_constants.cpp gen-cpp/AnnotationTest_constants.h gen-cpp/AnnotationTest_types.cpp gen-cpp/AnnotationTest_types.h: $(top_srcdir)/test/AnnotationTest.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/DebugProtoTest_types.cpp gen-cpp/DebugProtoTest_types.h gen-cpp/EmptyService.cpp gen-cpp/EmptyService.h: $(top_srcdir)/test/DebugProtoTest.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/DoubleConstantsTest_constants.cpp gen-cpp/DoubleConstantsTest_constants.h: $(top_srcdir)/test/DoubleConstantsTest.thrift
+ $(THRIFT) --gen cpp $<
+
+
+gen-cpp/EnumTest_types.cpp gen-cpp/EnumTest_types.h: $(top_srcdir)/test/EnumTest.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/TypedefTest_types.cpp gen-cpp/TypedefTest_types.h: $(top_srcdir)/test/TypedefTest.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/OptionalRequiredTest_types.cpp gen-cpp/OptionalRequiredTest_types.h: $(top_srcdir)/test/OptionalRequiredTest.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/Recursive_types.cpp gen-cpp/Recursive_types.h: $(top_srcdir)/test/Recursive.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/Service.cpp gen-cpp/StressTest_types.cpp: $(top_srcdir)/test/StressTest.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/SecondService.cpp gen-cpp/ThriftTest_constants.cpp gen-cpp/ThriftTest.cpp gen-cpp/ThriftTest_types.cpp gen-cpp/ThriftTest_types.h: $(top_srcdir)/test/ThriftTest.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/OneWayService.cpp gen-cpp/OneWayTest_constants.cpp gen-cpp/OneWayTest_types.h gen-cpp/OneWayService.h gen-cpp/OneWayTest_constants.h gen-cpp/OneWayTest_types.cpp: OneWayTest.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/ChildService.cpp gen-cpp/ChildService.h gen-cpp/ParentService.cpp gen-cpp/ParentService.h gen-cpp/proc_types.cpp gen-cpp/proc_types.h: processor/proc.thrift
+ $(THRIFT) --gen cpp:templates,cob_style $<
+
+AM_CPPFLAGS = $(BOOST_CPPFLAGS) -I$(top_srcdir)/lib/cpp/src -I$(top_srcdir)/lib/cpp/src/thrift -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I.
+AM_LDFLAGS = $(BOOST_LDFLAGS)
+AM_CXXFLAGS = -Wall -Wextra -pedantic
+
+clean-local:
+ $(RM) gen-cpp/*
+
+EXTRA_DIST = \
+ concurrency \
+ processor \
+ qt \
+ CMakeLists.txt \
+ DebugProtoTest_extras.cpp \
+ ThriftTest_extras.cpp \
+ OneWayTest.thrift
diff --git a/src/jaegertracing/thrift/lib/cpp/test/OneWayHTTPTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/OneWayHTTPTest.cpp
new file mode 100644
index 000000000..55d919bba
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/OneWayHTTPTest.cpp
@@ -0,0 +1,242 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <boost/test/auto_unit_test.hpp>
+#include <boost/thread.hpp>
+#include <iostream>
+#include <climits>
+#include <vector>
+#include <thrift/concurrency/Monitor.h>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/protocol/TJSONProtocol.h>
+#include <thrift/server/TThreadedServer.h>
+#include <thrift/transport/THttpServer.h>
+#include <thrift/transport/THttpClient.h>
+#include <thrift/transport/TServerSocket.h>
+#include <thrift/transport/TSocket.h>
+#include <memory>
+#include <thrift/transport/TBufferTransports.h>
+#include "gen-cpp/OneWayService.h"
+
+BOOST_AUTO_TEST_SUITE(OneWayHTTPTest)
+
+using namespace apache::thrift;
+using apache::thrift::protocol::TProtocol;
+using apache::thrift::protocol::TBinaryProtocol;
+using apache::thrift::protocol::TBinaryProtocolFactory;
+using apache::thrift::protocol::TJSONProtocol;
+using apache::thrift::protocol::TJSONProtocolFactory;
+using apache::thrift::server::TThreadedServer;
+using apache::thrift::server::TServerEventHandler;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::THttpServer;
+using apache::thrift::transport::THttpServerTransportFactory;
+using apache::thrift::transport::THttpClient;
+using apache::thrift::transport::TBufferedTransport;
+using apache::thrift::transport::TBufferedTransportFactory;
+using apache::thrift::transport::TMemoryBuffer;
+using apache::thrift::transport::TServerSocket;
+using apache::thrift::transport::TSocket;
+using apache::thrift::transport::TTransportException;
+using std::shared_ptr;
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::string;
+namespace utf = boost::unit_test;
+
+// Define this env var to enable some logging (in case you need to debug)
+#undef ENABLE_STDERR_LOGGING
+
+class OneWayServiceHandler : public onewaytest::OneWayServiceIf {
+public:
+ OneWayServiceHandler() = default;
+
+ void roundTripRPC() override {
+#ifdef ENABLE_STDERR_LOGGING
+ cerr << "roundTripRPC()" << endl;
+#endif
+ }
+ void oneWayRPC() override {
+#ifdef ENABLE_STDERR_LOGGING
+ cerr << "oneWayRPC()" << std::endl ;
+#endif
+ }
+};
+
+class OneWayServiceCloneFactory : virtual public onewaytest::OneWayServiceIfFactory {
+ public:
+ ~OneWayServiceCloneFactory() override = default;
+ onewaytest::OneWayServiceIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo) override
+ {
+ (void)connInfo ;
+ return new OneWayServiceHandler;
+ }
+ void releaseHandler( onewaytest::OneWayServiceIf* handler) override {
+ delete handler;
+ }
+};
+
+class RPC0ThreadClass {
+public:
+ RPC0ThreadClass(TThreadedServer& server) : server_(server) { } // Constructor
+~RPC0ThreadClass() = default; // Destructor
+
+void Run() {
+ server_.serve() ;
+}
+ TThreadedServer& server_ ;
+} ;
+
+using apache::thrift::concurrency::Monitor;
+using apache::thrift::concurrency::Mutex;
+using apache::thrift::concurrency::Synchronized;
+
+// copied from IntegrationTest
+class TServerReadyEventHandler : public TServerEventHandler, public Monitor {
+public:
+ TServerReadyEventHandler() : isListening_(false), accepted_(0) {}
+ ~TServerReadyEventHandler() override = default;
+ void preServe() override {
+ Synchronized sync(*this);
+ isListening_ = true;
+ notify();
+ }
+ void* createContext(shared_ptr<TProtocol> input,
+ shared_ptr<TProtocol> output) override {
+ Synchronized sync(*this);
+ ++accepted_;
+ notify();
+
+ (void)input;
+ (void)output;
+ return nullptr;
+ }
+ bool isListening() const { return isListening_; }
+ uint64_t acceptedCount() const { return accepted_; }
+
+private:
+ bool isListening_;
+ uint64_t accepted_;
+};
+
+class TBlockableBufferedTransport : public TBufferedTransport {
+ public:
+ TBlockableBufferedTransport(std::shared_ptr<TTransport> transport)
+ : TBufferedTransport(transport, 10240),
+ blocked_(false) {
+ }
+
+ uint32_t write_buffer_length() {
+ auto have_bytes = static_cast<uint32_t>(wBase_ - wBuf_.get());
+ return have_bytes ;
+ }
+
+ void block() {
+ blocked_ = true ;
+#ifdef ENABLE_STDERR_LOGGING
+ cerr << "block flushing\n" ;
+#endif
+ }
+ void unblock() {
+ blocked_ = false ;
+#ifdef ENABLE_STDERR_LOGGING
+ cerr << "unblock flushing, buffer is\n<<" << std::string((char *)wBuf_.get(), write_buffer_length()) << ">>\n" ;
+#endif
+ }
+
+ void flush() override {
+ if (blocked_) {
+#ifdef ENABLE_STDERR_LOGGING
+ cerr << "flush was blocked\n" ;
+#endif
+ return ;
+ }
+ TBufferedTransport::flush() ;
+ }
+
+ bool blocked_ ;
+} ;
+
+BOOST_AUTO_TEST_CASE( JSON_BufferedHTTP )
+{
+ std::shared_ptr<TServerSocket> ss = std::make_shared<TServerSocket>(0) ;
+ TThreadedServer server(
+ std::make_shared<onewaytest::OneWayServiceProcessorFactory>(std::make_shared<OneWayServiceCloneFactory>()),
+ ss, //port
+ std::make_shared<THttpServerTransportFactory>(),
+ std::make_shared<TJSONProtocolFactory>());
+
+ std::shared_ptr<TServerReadyEventHandler> pEventHandler(new TServerReadyEventHandler) ;
+ server.setServerEventHandler(pEventHandler);
+
+#ifdef ENABLE_STDERR_LOGGING
+ cerr << "Starting the server...\n";
+#endif
+ RPC0ThreadClass t(server) ;
+ boost::thread thread(&RPC0ThreadClass::Run, &t);
+
+ {
+ Synchronized sync(*(pEventHandler.get()));
+ while (!pEventHandler->isListening()) {
+ pEventHandler->wait();
+ }
+ }
+
+ int port = ss->getPort() ;
+#ifdef ENABLE_STDERR_LOGGING
+ cerr << "port " << port << endl ;
+#endif
+
+ {
+ std::shared_ptr<TSocket> socket(new TSocket("localhost", port));
+ socket->setRecvTimeout(10000) ; // 1000msec should be enough
+ std::shared_ptr<TBlockableBufferedTransport> blockable_transport(new TBlockableBufferedTransport(socket));
+ std::shared_ptr<TTransport> transport(new THttpClient(blockable_transport, "localhost", "/service"));
+ std::shared_ptr<TProtocol> protocol(new TJSONProtocol(transport));
+ onewaytest::OneWayServiceClient client(protocol);
+
+
+ transport->open();
+ client.roundTripRPC();
+ blockable_transport->block() ;
+ uint32_t size0 = blockable_transport->write_buffer_length() ;
+ client.send_oneWayRPC() ;
+ uint32_t size1 = blockable_transport->write_buffer_length() ;
+ client.send_oneWayRPC() ;
+ uint32_t size2 = blockable_transport->write_buffer_length() ;
+ BOOST_CHECK((size1 - size0) == (size2 - size1)) ;
+ blockable_transport->unblock() ;
+ client.send_roundTripRPC() ;
+ blockable_transport->flush() ;
+ try {
+ client.recv_roundTripRPC() ;
+ } catch (const TTransportException &e) {
+ BOOST_ERROR( "we should not get a transport exception -- this means we failed: " + std::string(e.what()) ) ;
+ }
+ transport->close();
+ }
+ server.stop();
+ thread.join() ;
+#ifdef ENABLE_STDERR_LOGGING
+ cerr << "finished.\n";
+#endif
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/OneWayTest.thrift b/src/jaegertracing/thrift/lib/cpp/test/OneWayTest.thrift
new file mode 100644
index 000000000..102cf26e5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/OneWayTest.thrift
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+namespace c_glib OneWayTest
+namespace java onewaytest
+namespace cpp onewaytest
+namespace rb Onewaytest
+namespace perl OneWayTest
+namespace csharp Onewaytest
+namespace js OneWayTest
+namespace st OneWayTest
+namespace py OneWayTest
+namespace py.twisted OneWayTest
+namespace go onewaytest
+namespace php OneWayTest
+namespace delphi Onewaytest
+namespace lua OneWayTest
+namespace xsd test (uri = 'http://thrift.apache.org/ns/OneWayTest')
+namespace netcore ThriftAsync.OneWayTest
+
+// a minimal Thrift service, for use in OneWayHTTPTtest.cpp
+service OneWayService {
+ void roundTripRPC(),
+ oneway void oneWayRPC()
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/OpenSSLManualInitTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/OpenSSLManualInitTest.cpp
new file mode 100644
index 000000000..a7518064e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/OpenSSLManualInitTest.cpp
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+// To show that this test actually tests something, you can change
+// MANUAL_OPENSSL_INIT to 0 to cause automatic OpenSSL init/cleanup,
+// which will cause the test to fail
+#define MANUAL_OPENSSL_INIT 1
+#ifdef _WIN32
+#include <WinSock2.h>
+#endif
+
+#include <boost/test/unit_test.hpp>
+#include <openssl/evp.h>
+#include <thrift/transport/TSSLSocket.h>
+
+using namespace apache::thrift::transport;
+
+void make_isolated_sslsocketfactory() {
+ // Here we create an isolated TSSLSocketFactory to ensure the
+ // constructor and destructor of TSSLSocketFactory get run. Thus
+ // without manual initialization normally OpenSSL would be
+ // uninitialized after this function.
+ TSSLSocketFactory factory;
+}
+
+void openssl_init() {
+#if MANUAL_OPENSSL_INIT
+ TSSLSocketFactory::setManualOpenSSLInitialization(true);
+ initializeOpenSSL();
+#endif
+}
+
+void openssl_cleanup() {
+#if MANUAL_OPENSSL_INIT
+ cleanupOpenSSL();
+#endif
+}
+
+void test_openssl_availability() {
+ // Check whether Thrift leaves OpenSSL functionality available after
+ // the last TSSLSocketFactory is destroyed when manual
+ // initialization is set
+ openssl_init();
+ make_isolated_sslsocketfactory();
+
+ // The following function is one that will fail if OpenSSL is
+ // uninitialized. It might also fail on very old versions of
+ // OpenSSL...
+ const EVP_MD* md = EVP_get_digestbyname("SHA256");
+ BOOST_CHECK(md != nullptr);
+ openssl_cleanup();
+}
+
+#ifdef BOOST_TEST_DYN_LINK
+bool init_unit_test_suite() {
+ boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
+ suite->p_name.value = "OpenSSLManualInit";
+
+ suite->add(BOOST_TEST_CASE(test_openssl_availability));
+
+ return true;
+}
+
+int main( int argc, char* argv[] ) {
+ return ::boost::unit_test::unit_test_main(&init_unit_test_suite,argc,argv);
+}
+#else
+boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
+ THRIFT_UNUSED_VARIABLE(argc);
+ THRIFT_UNUSED_VARIABLE(argv);
+ boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
+ suite->p_name.value = "OpenSSLManualInit";
+
+ suite->add(BOOST_TEST_CASE(test_openssl_availability));
+
+ return NULL;
+}
+#endif \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/cpp/test/OptionalRequiredTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/OptionalRequiredTest.cpp
new file mode 100644
index 000000000..4c435469e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/OptionalRequiredTest.cpp
@@ -0,0 +1,386 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+#include <map>
+#include <thrift/protocol/TDebugProtocol.h>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/transport/TBufferTransports.h>
+#include "gen-cpp/OptionalRequiredTest_types.h"
+
+#define BOOST_TEST_MODULE OptionalRequiredTest
+#include <boost/test/unit_test.hpp>
+
+using namespace thrift::test;
+using namespace apache::thrift;
+using namespace apache::thrift::transport;
+using namespace apache::thrift::protocol;
+
+/*
+template<typename Struct>
+void trywrite(const Struct& s, bool should_work) {
+ bool worked;
+ try {
+ TBinaryProtocol protocol(std::shared_ptr<TTransport>(new TMemoryBuffer));
+ s.write(&protocol);
+ worked = true;
+ } catch (TProtocolException & ex) {
+ worked = false;
+ }
+ BOOST_CHECK(worked == should_work);
+}
+*/
+
+template <typename Struct1, typename Struct2>
+void write_to_read(const Struct1& w, Struct2& r) {
+ TBinaryProtocol protocol(std::shared_ptr<TTransport>(new TMemoryBuffer));
+ w.write(&protocol);
+ r.read(&protocol);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_1) {
+ OldSchool o;
+
+ const std::string expected_result(
+ "OldSchool {\n"
+ " 01: im_int (i16) = 0,\n"
+ " 02: im_str (string) = \"\",\n"
+ " 03: im_big (list) = list<map>[0] {\n"
+ " },\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(o));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_2_1) {
+ Simple s;
+
+ const std::string expected_result(
+ "Simple {\n"
+ " 01: im_default (i16) = 0,\n"
+ " 02: im_required (i16) = 0,\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(s));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_2_2) {
+ Simple s;
+ s.im_optional = 10;
+
+ const std::string expected_result(
+ "Simple {\n"
+ " 01: im_default (i16) = 0,\n"
+ " 02: im_required (i16) = 0,\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(s));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_2_3) {
+ Simple s;
+ s.im_optional = 10;
+ s.__isset.im_optional = true;
+
+ const std::string expected_result(
+ "Simple {\n"
+ " 01: im_default (i16) = 0,\n"
+ " 02: im_required (i16) = 0,\n"
+ " 03: im_optional (i16) = 10,\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(s));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_2_4) {
+ Simple s;
+ s.__isset.im_optional = true;
+
+ const std::string expected_result(
+ "Simple {\n"
+ " 01: im_default (i16) = 0,\n"
+ " 02: im_required (i16) = 0,\n"
+ " 03: im_optional (i16) = 0,\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(s));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_2_5) {
+ Simple s;
+ s.__isset.im_optional = true;
+ s.im_optional = 10;
+
+ const std::string expected_result(
+ "Simple {\n"
+ " 01: im_default (i16) = 0,\n"
+ " 02: im_required (i16) = 0,\n"
+ " 03: im_optional (i16) = 10,\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(s));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_3) {
+ // assign/copy-construct with non-required fields
+
+ Simple s1, s2;
+ s1.__isset.im_default = true;
+ s1.__set_im_optional(10);
+ BOOST_CHECK(s1.__isset.im_default);
+ BOOST_CHECK(s1.__isset.im_optional);
+
+ s2 = s1;
+
+ BOOST_CHECK(s2.__isset.im_default);
+ BOOST_CHECK(s2.__isset.im_optional);
+
+ Simple s3(s1);
+
+ BOOST_CHECK(s3.__isset.im_default);
+ BOOST_CHECK(s3.__isset.im_optional);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_4) {
+ // Write-to-read with optional fields.
+
+ Simple s1, s2, s3;
+ s1.im_optional = 10;
+ BOOST_CHECK(!s1.__isset.im_default);
+ // BOOST_CHECK(!s1.__isset.im_required); // Compile error.
+ BOOST_CHECK(!s1.__isset.im_optional);
+
+ write_to_read(s1, s2);
+
+ BOOST_CHECK(s2.__isset.im_default);
+ // BOOST_CHECK( s2.__isset.im_required); // Compile error.
+ BOOST_CHECK(!s2.__isset.im_optional);
+ BOOST_CHECK(s3.im_optional == 0);
+
+ s1.__isset.im_optional = true;
+ write_to_read(s1, s3);
+
+ BOOST_CHECK(s3.__isset.im_default);
+ // BOOST_CHECK( s3.__isset.im_required); // Compile error.
+ BOOST_CHECK(s3.__isset.im_optional);
+ BOOST_CHECK(s3.im_optional == 10);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_5) {
+ // Writing between optional and default.
+
+ Tricky1 t1;
+ Tricky2 t2;
+
+ t2.im_optional = 10;
+ write_to_read(t2, t1);
+ write_to_read(t1, t2);
+ BOOST_CHECK(!t1.__isset.im_default);
+ BOOST_CHECK(t2.__isset.im_optional);
+ BOOST_CHECK(t1.im_default == t2.im_optional);
+ BOOST_CHECK(t1.im_default == 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_6) {
+ // Writing between default and required.
+
+ Tricky1 t1;
+ Tricky3 t3;
+ write_to_read(t1, t3);
+ write_to_read(t3, t1);
+ BOOST_CHECK(t1.__isset.im_default);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_7) {
+ // Writing between optional and required.
+
+ Tricky2 t2;
+ Tricky3 t3;
+ t2.__isset.im_optional = true;
+ write_to_read(t2, t3);
+ write_to_read(t3, t2);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_8) {
+ // Mu-hu-ha-ha-ha!
+
+ Tricky2 t2;
+ Tricky3 t3;
+ try {
+ write_to_read(t2, t3);
+ abort();
+ } catch (const TProtocolException&) {
+ }
+
+ write_to_read(t3, t2);
+ BOOST_CHECK(t2.__isset.im_optional);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_9) {
+ Complex c;
+
+ const std::string expected_result(
+ "Complex {\n"
+ " 01: cp_default (i16) = 0,\n"
+ " 02: cp_required (i16) = 0,\n"
+ " 04: the_map (map) = map<i16,struct>[0] {\n"
+ " },\n"
+ " 05: req_simp (struct) = Simple {\n"
+ " 01: im_default (i16) = 0,\n"
+ " 02: im_required (i16) = 0,\n"
+ " },\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(c));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_10) {
+ Tricky1 t1;
+ Tricky2 t2;
+ // Compile error.
+ //(void)(t1 == t2);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_11) {
+ OldSchool o1, o2, o3;
+ BOOST_CHECK(o1 == o2);
+ o1.im_int = o2.im_int = 10;
+ BOOST_CHECK(o1 == o2);
+ o1.__isset.im_int = true;
+ o2.__isset.im_int = false;
+ BOOST_CHECK(o1 == o2);
+ o1.im_int = 20;
+ o1.__isset.im_int = false;
+ BOOST_CHECK(o1 != o2);
+ o1.im_int = 10;
+ BOOST_CHECK(o1 == o2);
+ o1.im_str = o2.im_str = "foo";
+ BOOST_CHECK(o1 == o2);
+ o1.__isset.im_str = o2.__isset.im_str = true;
+ BOOST_CHECK(o1 == o2);
+ std::map<int32_t, std::string> mymap;
+ mymap[1] = "bar";
+ mymap[2] = "baz";
+ o1.im_big.push_back(std::map<int32_t, std::string>());
+ BOOST_CHECK(o1 != o2);
+ o2.im_big.push_back(std::map<int32_t, std::string>());
+ BOOST_CHECK(o1 == o2);
+ o2.im_big.push_back(mymap);
+ BOOST_CHECK(o1 != o2);
+ o1.im_big.push_back(mymap);
+ BOOST_CHECK(o1 == o2);
+
+ TBinaryProtocol protocol(std::shared_ptr<TTransport>(new TMemoryBuffer));
+ o1.write(&protocol);
+
+ o1.im_big.push_back(mymap);
+ mymap[3] = "qux";
+ o2.im_big.push_back(mymap);
+ BOOST_CHECK(o1 != o2);
+ o1.im_big.back()[3] = "qux";
+ BOOST_CHECK(o1 == o2);
+
+ o3.read(&protocol);
+ o3.im_big.push_back(mymap);
+ BOOST_CHECK(o1 == o3);
+
+ const std::string expected_result(
+ "OldSchool {\n"
+ " 01: im_int (i16) = 10,\n"
+ " 02: im_str (string) = \"foo\",\n"
+ " 03: im_big (list) = list<map>[3] {\n"
+ " [0] = map<i32,string>[0] {\n"
+ " },\n"
+ " [1] = map<i32,string>[2] {\n"
+ " 1 -> \"bar\",\n"
+ " 2 -> \"baz\",\n"
+ " },\n"
+ " [2] = map<i32,string>[3] {\n"
+ " 1 -> \"bar\",\n"
+ " 2 -> \"baz\",\n"
+ " 3 -> \"qux\",\n"
+ " },\n"
+ " },\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(o3));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_12) {
+ Tricky2 t1, t2;
+ BOOST_CHECK(t1.__isset.im_optional == false);
+ BOOST_CHECK(t2.__isset.im_optional == false);
+ BOOST_CHECK(t1 == t2);
+ t1.im_optional = 5;
+ BOOST_CHECK(t1 == t2);
+ t2.im_optional = 5;
+ BOOST_CHECK(t1 == t2);
+ t1.__isset.im_optional = true;
+ BOOST_CHECK(t1 != t2);
+ t2.__isset.im_optional = true;
+ BOOST_CHECK(t1 == t2);
+ t1.im_optional = 10;
+ BOOST_CHECK(t1 != t2);
+ t2.__isset.im_optional = false;
+ BOOST_CHECK(t1 != t2);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_13) {
+ OptionalDefault t1, t2;
+
+ BOOST_CHECK(t1.__isset.opt_int == true);
+ BOOST_CHECK(t1.__isset.opt_str == true);
+ BOOST_CHECK(t1.opt_int == t2.opt_int);
+ BOOST_CHECK(t1.opt_str == t2.opt_str);
+
+ write_to_read(t1, t2);
+ BOOST_CHECK(t2.__isset.opt_int == true);
+ BOOST_CHECK(t2.__isset.opt_str == true);
+ BOOST_CHECK(t1.opt_int == t2.opt_int);
+ BOOST_CHECK(t1.opt_str == t2.opt_str);
+
+ const std::string expected_result(
+ "OptionalDefault {\n"
+ " 01: opt_int (i16) = 1234,\n"
+ " 02: opt_str (string) = \"default\",\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(t2));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/RecursiveTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/RecursiveTest.cpp
new file mode 100644
index 000000000..ab2e46dd7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/RecursiveTest.cpp
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+#include "gen-cpp/Recursive_types.h"
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <memory>
+#include <thrift/transport/TBufferTransports.h>
+
+#define BOOST_TEST_MODULE RecursiveTest
+#include <boost/test/unit_test.hpp>
+
+using apache::thrift::transport::TMemoryBuffer;
+using apache::thrift::protocol::TBinaryProtocol;
+using std::shared_ptr;
+
+BOOST_AUTO_TEST_CASE(test_recursive_1) {
+ shared_ptr<TMemoryBuffer> buf(new TMemoryBuffer());
+ shared_ptr<TBinaryProtocol> prot(new TBinaryProtocol(buf));
+
+ RecTree tree;
+ RecTree child;
+ tree.children.push_back(child);
+
+ tree.write(prot.get());
+
+ RecTree result;
+ result.read(prot.get());
+ BOOST_CHECK(tree == result);
+}
+
+BOOST_AUTO_TEST_CASE(test_recursive_2) {
+ shared_ptr<TMemoryBuffer> buf(new TMemoryBuffer());
+ shared_ptr<TBinaryProtocol> prot(new TBinaryProtocol(buf));
+
+ RecList l;
+ shared_ptr<RecList> l2(new RecList);
+ l.nextitem = l2;
+
+ l.write(prot.get());
+
+ RecList resultlist;
+ resultlist.read(prot.get());
+ BOOST_CHECK(resultlist.nextitem != nullptr);
+ BOOST_CHECK(resultlist.nextitem->nextitem == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(test_recursive_3) {
+ shared_ptr<TMemoryBuffer> buf(new TMemoryBuffer());
+ shared_ptr<TBinaryProtocol> prot(new TBinaryProtocol(buf));
+
+ CoRec c;
+ shared_ptr<CoRec2> r(new CoRec2);
+ c.other = r;
+
+ c.write(prot.get());
+
+ c.read(prot.get());
+ BOOST_CHECK(c.other != nullptr);
+ BOOST_CHECK(c.other->other.other == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(test_recursive_4) {
+ shared_ptr<TMemoryBuffer> buf(new TMemoryBuffer());
+ shared_ptr<TBinaryProtocol> prot(new TBinaryProtocol(buf));
+
+ shared_ptr<RecList> depthLimit(new RecList);
+ depthLimit->nextitem = depthLimit;
+ BOOST_CHECK_THROW(depthLimit->write(prot.get()),
+ apache::thrift::protocol::TProtocolException);
+
+ depthLimit->nextitem.reset();
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/RenderedDoubleConstantsTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/RenderedDoubleConstantsTest.cpp
new file mode 100644
index 000000000..0ca042b73
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/RenderedDoubleConstantsTest.cpp
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#define EPSILON 0.0000001
+#include <typeindex>
+#include <typeinfo>
+#include <vector>
+
+#include "gen-cpp/DoubleConstantsTest_constants.h"
+using namespace thrift::test;
+
+#define BOOST_TEST_MODULE RenderedDoubleConstantsTest
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_SUITE(RenderedDoubleConstantsTest)
+
+BOOST_AUTO_TEST_CASE(test_rendered_double_constants) {
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_INT_CONSTANT = 1.0;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT = -100.0;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT = 9223372036854775807.0;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT = -9223372036854775807.0;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS = 3.14159265359;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE = 1000000.1;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE = -1000000.1;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE = 1.7e+308;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE = 9223372036854775816.43;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE = -1.7e+308;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE = -9223372036854775816.43;
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_INT_CONSTANT, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE, EPSILON);
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_INT_CONSTANT).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST)
+ .hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE).hash_code());
+}
+
+BOOST_AUTO_TEST_CASE(test_rendered_double_list) {
+ const std::vector<double> EXPECTED_DOUBLE_LIST{1.0,-100.0,100.0,9223372036854775807.0,-9223372036854775807.0,
+ 3.14159265359,1000000.1,-1000000.1,1.7e+308,-1.7e+308,9223372036854775816.43,-9223372036854775816.43};
+ BOOST_CHECK_EQUAL(g_DoubleConstantsTest_constants.DOUBLE_LIST_TEST.size(), EXPECTED_DOUBLE_LIST.size());
+ for (unsigned int i = 0; i < EXPECTED_DOUBLE_LIST.size(); ++i) {
+ BOOST_CHECK_CLOSE(g_DoubleConstantsTest_constants.DOUBLE_LIST_TEST[i], EXPECTED_DOUBLE_LIST[i], EPSILON);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/SecurityTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/SecurityTest.cpp
new file mode 100644
index 000000000..982a4f30c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/SecurityTest.cpp
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define BOOST_TEST_MODULE SecurityTest
+#include <boost/test/unit_test.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <boost/thread.hpp>
+#include <memory>
+#include <thrift/transport/TSSLServerSocket.h>
+#include <thrift/transport/TSSLSocket.h>
+#include <thrift/transport/TTransport.h>
+#include <vector>
+#ifdef __linux__
+#include <signal.h>
+#endif
+
+using apache::thrift::transport::TSSLServerSocket;
+using apache::thrift::transport::TServerTransport;
+using apache::thrift::transport::TSSLSocket;
+using apache::thrift::transport::TSSLSocketFactory;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using apache::thrift::transport::TTransportFactory;
+
+using std::bind;
+using std::shared_ptr;
+
+boost::filesystem::path keyDir;
+boost::filesystem::path certFile(const std::string& filename)
+{
+ return keyDir / filename;
+}
+boost::mutex gMutex;
+
+struct GlobalFixture
+{
+ GlobalFixture()
+ {
+ using namespace boost::unit_test::framework;
+ for (int i = 0; i < master_test_suite().argc; ++i)
+ {
+ BOOST_TEST_MESSAGE(boost::format("argv[%1%] = \"%2%\"") % i % master_test_suite().argv[i]);
+ }
+
+ #ifdef __linux__
+ // OpenSSL calls send() without MSG_NOSIGPIPE so writing to a socket that has
+ // disconnected can cause a SIGPIPE signal...
+ signal(SIGPIPE, SIG_IGN);
+ #endif
+
+ TSSLSocketFactory::setManualOpenSSLInitialization(true);
+ apache::thrift::transport::initializeOpenSSL();
+
+ keyDir = boost::filesystem::current_path().parent_path().parent_path().parent_path() / "test" / "keys";
+ if (!boost::filesystem::exists(certFile("server.crt")))
+ {
+ keyDir = boost::filesystem::path(master_test_suite().argv[master_test_suite().argc - 1]);
+ if (!boost::filesystem::exists(certFile("server.crt")))
+ {
+ throw std::invalid_argument("The last argument to this test must be the directory containing the test certificate(s).");
+ }
+ }
+ }
+
+ virtual ~GlobalFixture()
+ {
+ apache::thrift::transport::cleanupOpenSSL();
+#ifdef __linux__
+ signal(SIGPIPE, SIG_DFL);
+#endif
+ }
+};
+
+#if (BOOST_VERSION >= 105900)
+BOOST_GLOBAL_FIXTURE(GlobalFixture);
+#else
+BOOST_GLOBAL_FIXTURE(GlobalFixture)
+#endif
+
+struct SecurityFixture
+{
+ void server(apache::thrift::transport::SSLProtocol protocol)
+ {
+ try
+ {
+ boost::mutex::scoped_lock lock(mMutex);
+
+ shared_ptr<TSSLSocketFactory> pServerSocketFactory;
+ shared_ptr<TSSLServerSocket> pServerSocket;
+
+ pServerSocketFactory.reset(new TSSLSocketFactory(static_cast<apache::thrift::transport::SSLProtocol>(protocol)));
+ pServerSocketFactory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
+ pServerSocketFactory->loadCertificate(certFile("server.crt").string().c_str());
+ pServerSocketFactory->loadPrivateKey(certFile("server.key").string().c_str());
+ pServerSocketFactory->server(true);
+ pServerSocket.reset(new TSSLServerSocket("localhost", 0, pServerSocketFactory));
+ shared_ptr<TTransport> connectedClient;
+
+ try
+ {
+ pServerSocket->listen();
+ mPort = pServerSocket->getPort();
+ mCVar.notify_one();
+ lock.unlock();
+
+ connectedClient = pServerSocket->accept();
+ uint8_t buf[2];
+ buf[0] = 'O';
+ buf[1] = 'K';
+ connectedClient->write(&buf[0], 2);
+ connectedClient->flush();
+ }
+
+ catch (apache::thrift::transport::TTransportException& ex)
+ {
+ boost::mutex::scoped_lock lock(gMutex);
+ BOOST_TEST_MESSAGE(boost::format("SRV %1% Exception: %2%") % boost::this_thread::get_id() % ex.what());
+ }
+
+ if (connectedClient)
+ {
+ connectedClient->close();
+ connectedClient.reset();
+ }
+
+ pServerSocket->close();
+ pServerSocket.reset();
+ }
+ catch (std::exception& ex)
+ {
+ BOOST_FAIL(boost::format("%1%: %2%") % typeid(ex).name() % ex.what());
+ }
+ }
+
+ void client(apache::thrift::transport::SSLProtocol protocol)
+ {
+ try
+ {
+ shared_ptr<TSSLSocketFactory> pClientSocketFactory;
+ shared_ptr<TSSLSocket> pClientSocket;
+
+ try
+ {
+ pClientSocketFactory.reset(new TSSLSocketFactory(static_cast<apache::thrift::transport::SSLProtocol>(protocol)));
+ pClientSocketFactory->authenticate(true);
+ pClientSocketFactory->loadCertificate(certFile("client.crt").string().c_str());
+ pClientSocketFactory->loadPrivateKey(certFile("client.key").string().c_str());
+ pClientSocketFactory->loadTrustedCertificates(certFile("CA.pem").string().c_str());
+ pClientSocket = pClientSocketFactory->createSocket("localhost", mPort);
+ pClientSocket->open();
+
+ uint8_t buf[3];
+ buf[0] = 0;
+ buf[1] = 0;
+ BOOST_CHECK_EQUAL(2, pClientSocket->read(&buf[0], 2));
+ BOOST_CHECK_EQUAL(0, memcmp(&buf[0], "OK", 2));
+ mConnected = true;
+ }
+ catch (apache::thrift::transport::TTransportException& ex)
+ {
+ boost::mutex::scoped_lock lock(gMutex);
+ BOOST_TEST_MESSAGE(boost::format("CLI %1% Exception: %2%") % boost::this_thread::get_id() % ex.what());
+ }
+
+ if (pClientSocket)
+ {
+ pClientSocket->close();
+ pClientSocket.reset();
+ }
+ }
+ catch (std::exception& ex)
+ {
+ BOOST_FAIL(boost::format("%1%: %2%") % typeid(ex).name() % ex.what());
+ }
+ }
+
+ static const char *protocol2str(size_t protocol)
+ {
+ static const char *strings[apache::thrift::transport::LATEST + 1] =
+ {
+ "SSLTLS",
+ "SSLv2",
+ "SSLv3",
+ "TLSv1_0",
+ "TLSv1_1",
+ "TLSv1_2"
+ };
+ return strings[protocol];
+ }
+
+ boost::mutex mMutex;
+ boost::condition_variable mCVar;
+ int mPort;
+ bool mConnected;
+};
+
+BOOST_FIXTURE_TEST_SUITE(BOOST_TEST_MODULE, SecurityFixture)
+
+BOOST_AUTO_TEST_CASE(ssl_security_matrix)
+{
+ try
+ {
+ // matrix of connection success between client and server with different SSLProtocol selections
+ bool matrix[apache::thrift::transport::LATEST + 1][apache::thrift::transport::LATEST + 1] =
+ {
+ // server = SSLTLS SSLv2 SSLv3 TLSv1_0 TLSv1_1 TLSv1_2
+ // client
+ /* SSLTLS */ { true, false, false, true, true, true },
+ /* SSLv2 */ { false, false, false, false, false, false },
+ /* SSLv3 */ { false, false, true, false, false, false },
+ /* TLSv1_0 */ { true, false, false, true, false, false },
+ /* TLSv1_1 */ { true, false, false, false, true, false },
+ /* TLSv1_2 */ { true, false, false, false, false, true }
+ };
+
+ for (size_t si = 0; si <= apache::thrift::transport::LATEST; ++si)
+ {
+ for (size_t ci = 0; ci <= apache::thrift::transport::LATEST; ++ci)
+ {
+ if (si == 1 || ci == 1)
+ {
+ // Skip all SSLv2 cases - protocol not supported
+ continue;
+ }
+
+#ifdef OPENSSL_NO_SSL3
+ if (si == 2 || ci == 2)
+ {
+ // Skip all SSLv3 cases - protocol not supported
+ continue;
+ }
+#endif
+
+ boost::mutex::scoped_lock lock(mMutex);
+
+ BOOST_TEST_MESSAGE(boost::format("TEST: Server = %1%, Client = %2%")
+ % protocol2str(si) % protocol2str(ci));
+
+ mConnected = false;
+ // thread_group manages the thread lifetime - ignore the return value of create_thread
+ boost::thread_group threads;
+ (void)threads.create_thread(bind(&SecurityFixture::server, this, static_cast<apache::thrift::transport::SSLProtocol>(si)));
+ mCVar.wait(lock); // wait for listen() to succeed
+ lock.unlock();
+ (void)threads.create_thread(bind(&SecurityFixture::client, this, static_cast<apache::thrift::transport::SSLProtocol>(ci)));
+ threads.join_all();
+
+ BOOST_CHECK_MESSAGE(mConnected == matrix[ci][si],
+ boost::format(" Server = %1%, Client = %2% expected mConnected == %3% but was %4%")
+ % protocol2str(si) % protocol2str(ci) % matrix[ci][si] % mConnected);
+ }
+ }
+ }
+ catch (std::exception& ex)
+ {
+ BOOST_FAIL(boost::format("%1%: %2%") % typeid(ex).name() % ex.what());
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/SpecializationTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/SpecializationTest.cpp
new file mode 100644
index 000000000..008837d31
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/SpecializationTest.cpp
@@ -0,0 +1,103 @@
+#define _USE_MATH_DEFINES
+#include <cmath>
+#include <thrift/transport/TTransportUtils.h>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <gen-cpp/DebugProtoTest_types.h>
+
+using namespace thrift::test::debug;
+using namespace apache::thrift::transport;
+using namespace apache::thrift::protocol;
+
+#define BOOST_TEST_MODULE SpecializationTest
+#include <boost/test/unit_test.hpp>
+
+typedef TBinaryProtocolT<TMemoryBuffer> MyProtocol;
+// typedef TBinaryProtocolT<TTransport> MyProtocol;
+
+BOOST_AUTO_TEST_CASE(test_specialization_1) {
+ OneOfEach ooe;
+ ooe.im_true = true;
+ ooe.im_false = false;
+ ooe.a_bite = 0x7f;
+ ooe.integer16 = 27000;
+ ooe.integer32 = 1 << 24;
+ ooe.integer64 = (uint64_t)6000 * 1000 * 1000;
+ ooe.double_precision = M_PI;
+ ooe.some_characters = "JSON THIS! \"\1";
+ ooe.zomg_unicode = "\xd7\n\a\t";
+ ooe.base64 = "\1\2\3\255";
+
+ Nesting n;
+ n.my_ooe = ooe;
+ n.my_ooe.integer16 = 16;
+ n.my_ooe.integer32 = 32;
+ n.my_ooe.integer64 = 64;
+ n.my_ooe.double_precision = (std::sqrt(5.0) + 1) / 2;
+ n.my_ooe.some_characters = ":R (me going \"rrrr\")";
+ n.my_ooe.zomg_unicode = "\xd3\x80\xe2\x85\xae\xce\x9d\x20\xd0\x9d\xce"
+ "\xbf\xe2\x85\xbf\xd0\xbe\xc9\xa1\xd0\xb3\xd0"
+ "\xb0\xcf\x81\xe2\x84\x8e\x20\xce\x91\x74\x74"
+ "\xce\xb1\xe2\x85\xbd\xce\xba\xc7\x83\xe2\x80"
+ "\xbc";
+ n.my_bonk.type = 31337;
+ n.my_bonk.message = "I am a bonk... xor!";
+
+ HolyMoley hm;
+
+ hm.big.push_back(ooe);
+ hm.big.push_back(n.my_ooe);
+ hm.big[0].a_bite = 0x22;
+ hm.big[1].a_bite = 0x33;
+
+ std::vector<std::string> stage1;
+ stage1.push_back("and a one");
+ stage1.push_back("and a two");
+ hm.contain.insert(stage1);
+ stage1.clear();
+ stage1.push_back("then a one, two");
+ stage1.push_back("three!");
+ stage1.push_back("FOUR!!");
+ hm.contain.insert(stage1);
+ stage1.clear();
+ hm.contain.insert(stage1);
+
+ std::vector<Bonk> stage2;
+ hm.bonks["nothing"] = stage2;
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 1;
+ stage2.back().message = "Wait.";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 2;
+ stage2.back().message = "What?";
+ hm.bonks["something"] = stage2;
+ stage2.clear();
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 3;
+ stage2.back().message = "quoth";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 4;
+ stage2.back().message = "the raven";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 5;
+ stage2.back().message = "nevermore";
+ hm.bonks["poe"] = stage2;
+
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
+ std::shared_ptr<TProtocol> proto(new MyProtocol(buffer));
+
+ ooe.write(proto.get());
+ OneOfEach ooe2;
+ ooe2.read(proto.get());
+
+ BOOST_CHECK(ooe == ooe2);
+
+ hm.write(proto.get());
+ HolyMoley hm2;
+ hm2.read(proto.get());
+
+ BOOST_CHECK(hm == hm2);
+
+ hm2.big[0].a_bite = 0x00;
+
+ BOOST_CHECK(hm != hm2);
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TBufferBaseTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TBufferBaseTest.cpp
new file mode 100644
index 000000000..7203f829b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TBufferBaseTest.cpp
@@ -0,0 +1,639 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <algorithm>
+#include <boost/test/auto_unit_test.hpp>
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/transport/TShortReadTransport.h>
+#include <memory>
+
+using std::shared_ptr;
+using apache::thrift::transport::TMemoryBuffer;
+using apache::thrift::transport::TBufferedTransport;
+using apache::thrift::transport::TFramedTransport;
+using apache::thrift::transport::test::TShortReadTransport;
+using std::string;
+
+// Shamelessly copied from ZlibTransport. TODO: refactor.
+unsigned int dist[][5000] = {
+ { 1<<15 },
+
+ {
+ 5,13,9,1,8,9,11,13,18,48,24,13,21,13,5,11,35,2,4,20,17,72,27,14,15,4,7,26,
+ 12,1,14,9,2,16,29,41,7,24,4,27,14,4,1,4,25,3,6,34,10,8,50,2,14,13,55,29,3,
+ 43,53,49,14,4,10,32,27,48,1,3,1,11,5,17,16,51,17,30,15,11,9,2,2,11,52,12,2,
+ 13,94,1,19,1,38,2,8,43,8,33,7,30,8,17,22,2,15,14,12,34,2,12,6,37,29,74,3,
+ 165,16,11,17,5,14,3,10,7,37,11,24,7,1,3,12,37,8,9,34,17,12,8,21,13,37,1,4,
+ 30,14,78,4,15,2,40,37,17,12,36,82,14,4,1,4,7,17,11,16,88,77,2,3,15,3,34,11,
+ 5,79,22,34,8,4,4,40,22,24,28,9,13,3,34,27,9,16,39,16,39,13,2,4,3,41,26,10,4,
+ 33,4,7,12,5,6,3,10,30,8,21,16,58,19,9,0,47,7,13,11,19,15,7,53,57,2,13,28,22,
+ 3,16,9,25,33,12,40,7,12,64,7,14,24,44,9,2,14,11,2,58,1,26,30,11,9,5,24,7,9,
+ 94,2,10,21,5,5,4,5,6,179,9,18,2,7,13,31,41,17,4,36,3,21,6,26,8,15,18,44,27,
+ 11,9,25,7,0,14,2,12,20,23,13,2,163,9,5,15,65,2,14,6,8,98,11,15,14,34,2,3,10,
+ 22,9,92,7,10,32,67,13,3,4,35,8,2,1,5,0,26,381,7,27,8,2,16,93,4,19,5,8,25,9,
+ 31,14,4,21,5,3,9,22,56,4,18,3,11,18,6,4,3,40,12,16,110,8,35,14,1,18,40,9,12,
+ 14,3,11,7,57,13,18,116,53,19,22,7,16,11,5,8,21,16,1,75,21,20,1,28,2,6,1,7,
+ 19,38,5,6,9,9,4,1,7,55,36,62,5,4,4,24,15,1,12,35,48,20,5,17,1,5,26,15,4,54,
+ 13,5,5,15,5,19,32,29,31,7,6,40,7,80,11,18,8,128,48,6,12,84,13,4,7,2,13,9,16,
+ 17,3,254,1,4,181,8,44,7,6,24,27,9,23,14,34,16,22,25,10,3,3,4,4,12,2,12,6,7,
+ 13,58,13,6,11,19,53,11,66,18,19,10,4,13,2,5,49,58,1,67,7,21,64,14,11,14,8,3,
+ 26,33,91,31,20,7,9,42,39,4,3,55,11,10,0,7,4,75,8,12,0,27,3,8,9,0,12,12,23,
+ 28,23,20,4,13,30,2,22,20,19,30,6,22,2,6,4,24,7,19,55,86,5,33,2,161,6,7,1,62,
+ 13,3,72,12,12,9,7,12,10,5,10,29,1,5,22,13,13,5,2,12,3,7,14,18,2,3,46,21,17,
+ 15,19,3,27,5,16,45,31,10,8,17,18,18,3,7,24,6,55,9,3,6,12,10,12,8,91,9,4,4,4,
+ 27,29,16,5,7,22,43,28,11,14,8,11,28,109,55,71,40,3,8,22,26,15,44,3,25,29,5,
+ 3,32,17,12,3,29,27,25,15,11,8,40,39,38,17,3,9,11,2,32,11,6,20,48,75,27,3,7,
+ 54,12,95,12,7,24,23,2,13,8,15,16,5,12,4,17,7,19,88,2,6,13,115,45,12,21,2,86,
+ 74,9,7,5,16,32,16,2,21,18,6,34,5,18,260,7,12,16,44,19,92,31,7,8,2,9,0,0,15,
+ 8,38,4,8,20,18,2,83,3,3,4,9,5,3,10,3,5,29,15,7,11,8,48,17,23,2,17,4,11,22,
+ 21,64,8,8,4,19,95,0,17,28,9,11,20,71,5,11,18,12,13,45,49,4,1,33,32,23,13,5,
+ 52,2,2,16,3,4,7,12,2,1,12,6,24,1,22,155,21,3,45,4,12,44,26,5,40,36,9,9,8,20,
+ 35,31,3,2,32,50,10,8,37,2,75,35,22,15,192,8,11,23,1,4,29,6,8,8,5,12,18,32,4,
+ 7,12,2,0,0,9,5,48,11,35,3,1,123,6,29,8,11,8,23,51,16,6,63,12,2,5,4,14,2,15,
+ 7,14,3,2,7,17,32,8,8,10,1,23,62,2,49,6,49,47,23,3,20,7,11,39,10,24,6,15,5,5,
+ 11,8,16,36,8,13,20,3,10,44,7,52,7,10,36,6,15,10,5,11,4,14,19,17,10,12,3,6,
+ 23,4,13,94,70,7,36,7,38,7,28,8,4,15,3,19,4,33,39,21,109,4,80,6,40,4,432,4,4,
+ 7,8,3,31,8,28,37,34,10,2,21,5,22,0,7,36,14,12,6,24,1,21,5,9,2,29,20,54,113,
+ 13,31,39,27,6,0,27,4,5,2,43,7,8,57,8,62,7,9,12,22,90,30,6,19,7,10,20,6,5,58,
+ 32,30,41,4,10,25,13,3,8,7,10,2,9,6,151,44,16,12,16,20,8,3,18,11,17,4,10,45,
+ 15,8,56,38,52,25,40,14,4,17,15,8,2,19,7,8,26,30,2,3,180,8,26,17,38,35,5,16,
+ 28,5,15,56,13,14,18,9,15,83,27,3,9,4,11,8,27,27,44,10,12,8,3,48,14,7,9,4,4,
+ 8,4,5,9,122,8,14,12,19,17,21,4,29,63,21,17,10,12,18,47,10,10,53,4,18,16,4,8,
+ 118,9,5,12,9,11,9,3,12,32,3,23,2,15,3,3,30,3,17,235,15,22,9,299,14,17,1,5,
+ 16,8,3,7,3,13,2,7,6,4,8,66,2,13,6,15,16,47,3,36,5,7,10,24,1,9,9,8,13,16,26,
+ 12,7,24,21,18,49,23,39,10,41,4,13,4,27,11,12,12,19,4,147,8,10,9,40,21,2,83,
+ 10,5,6,11,25,9,50,57,40,12,12,21,1,3,24,23,9,3,9,13,2,3,12,57,8,11,13,15,26,
+ 15,10,47,36,4,25,1,5,8,5,4,0,12,49,5,19,4,6,16,14,6,10,69,10,33,29,7,8,61,
+ 12,4,0,3,7,6,3,16,29,27,38,4,21,0,24,3,2,1,19,16,22,2,8,138,11,7,7,3,12,22,
+ 3,16,5,7,3,53,9,10,32,14,5,7,3,6,22,9,59,26,8,7,58,5,16,11,55,7,4,11,146,91,
+ 8,13,18,14,6,8,8,31,26,22,6,11,30,11,30,15,18,31,3,48,17,7,6,4,9,2,25,3,35,
+ 13,13,7,8,4,31,10,8,10,4,3,45,10,23,2,7,259,17,21,13,14,3,26,3,8,27,4,18,9,
+ 66,7,12,5,8,17,4,23,55,41,51,2,32,26,66,4,21,14,12,65,16,22,17,5,14,2,29,24,
+ 7,3,36,2,43,53,86,5,28,4,58,13,49,121,6,2,73,2,1,47,4,2,27,10,35,28,27,10,
+ 17,10,56,7,10,14,28,20,24,40,7,4,7,3,10,11,32,6,6,3,15,11,54,573,2,3,6,2,3,
+ 14,64,4,16,12,16,42,10,26,4,6,11,69,18,27,2,2,17,22,9,13,22,11,6,1,15,49,3,
+ 14,1
+ },
+
+ {
+ 11,11,11,15,47,1,3,1,23,5,8,18,3,23,15,21,1,7,19,10,26,1,17,11,31,21,41,18,
+ 34,4,9,58,19,3,3,36,5,18,13,3,14,4,9,10,4,19,56,15,3,5,3,11,27,9,4,10,13,4,
+ 11,6,9,2,18,3,10,19,11,4,53,4,2,2,3,4,58,16,3,0,5,30,2,11,93,10,2,14,10,6,2,
+ 115,2,25,16,22,38,101,4,18,13,2,145,51,45,15,14,15,13,20,7,24,5,13,14,30,40,
+ 10,4,107,12,24,14,39,12,6,13,20,7,7,11,5,18,18,45,22,6,39,3,2,1,51,9,11,4,
+ 13,9,38,44,8,11,9,15,19,9,23,17,17,17,13,9,9,1,10,4,18,6,2,9,5,27,32,72,8,
+ 37,9,4,10,30,17,20,15,17,66,10,4,73,35,37,6,4,16,117,45,13,4,75,5,24,65,10,
+ 4,9,4,13,46,5,26,29,10,4,4,52,3,13,18,63,6,14,9,24,277,9,88,2,48,27,123,14,
+ 61,7,5,10,8,7,90,3,10,3,3,48,17,13,10,18,33,2,19,36,6,21,1,16,12,5,6,2,16,
+ 15,29,88,28,2,15,6,11,4,6,11,3,3,4,18,9,53,5,4,3,33,8,9,8,6,7,36,9,62,14,2,
+ 1,10,1,16,7,32,7,23,20,11,10,23,2,1,0,9,16,40,2,81,5,22,8,5,4,37,51,37,10,
+ 19,57,11,2,92,31,6,39,10,13,16,8,20,6,9,3,10,18,25,23,12,30,6,2,26,7,64,18,
+ 6,30,12,13,27,7,10,5,3,33,24,99,4,23,4,1,27,7,27,49,8,20,16,3,4,13,9,22,67,
+ 28,3,10,16,3,2,10,4,8,1,8,19,3,85,6,21,1,9,16,2,30,10,33,12,4,9,3,1,60,38,6,
+ 24,32,3,14,3,40,8,34,115,5,9,27,5,96,3,40,6,15,5,8,22,112,5,5,25,17,58,2,7,
+ 36,21,52,1,3,95,12,21,4,11,8,59,24,5,21,4,9,15,8,7,21,3,26,5,11,6,7,17,65,
+ 14,11,10,2,17,5,12,22,4,4,2,21,8,112,3,34,63,35,2,25,1,2,15,65,23,0,3,5,15,
+ 26,27,9,5,48,11,15,4,9,5,33,20,15,1,18,19,11,24,40,10,21,74,6,6,32,30,40,5,
+ 4,7,44,10,25,46,16,12,5,40,7,18,5,18,9,12,8,4,25,5,6,36,4,43,8,9,12,35,17,4,
+ 8,9,11,27,5,10,17,40,8,12,4,18,9,18,12,20,25,39,42,1,24,13,22,15,7,112,35,3,
+ 7,17,33,2,5,5,19,8,4,12,24,14,13,2,1,13,6,5,19,11,7,57,0,19,6,117,48,14,8,
+ 10,51,17,12,14,2,5,8,9,15,4,48,53,13,22,4,25,12,11,19,45,5,2,6,54,22,9,15,9,
+ 13,2,7,11,29,82,16,46,4,26,14,26,40,22,4,26,6,18,13,4,4,20,3,3,7,12,17,8,9,
+ 23,6,20,7,25,23,19,5,15,6,23,15,11,19,11,3,17,59,8,18,41,4,54,23,44,75,13,
+ 20,6,11,2,3,1,13,10,3,7,12,3,4,7,8,30,6,6,7,3,32,9,5,28,6,114,42,13,36,27,
+ 59,6,93,13,74,8,69,140,3,1,17,48,105,6,11,5,15,1,10,10,14,8,53,0,8,24,60,2,
+ 6,35,2,12,32,47,16,17,75,2,5,4,37,28,10,5,9,57,4,59,5,12,13,7,90,5,11,5,24,
+ 22,13,30,1,2,10,9,6,19,3,18,47,2,5,7,9,35,15,3,6,1,21,14,14,18,14,9,12,8,73,
+ 6,19,3,32,9,14,17,17,5,55,23,6,16,28,3,11,48,4,6,6,6,12,16,30,10,30,27,51,
+ 18,29,2,3,15,1,76,0,16,33,4,27,3,62,4,10,2,4,8,15,9,41,26,22,2,4,20,4,49,0,
+ 8,1,57,13,12,39,3,63,10,19,34,35,2,7,8,29,72,4,10,0,77,8,6,7,9,15,21,9,4,1,
+ 20,23,1,9,18,9,15,36,4,7,6,15,5,7,7,40,2,9,22,2,3,20,4,12,34,13,6,18,15,1,
+ 38,20,12,7,16,3,19,85,12,16,18,16,2,17,1,13,8,6,12,15,97,17,12,9,3,21,15,12,
+ 23,44,81,26,30,2,5,17,6,6,0,22,42,19,6,19,41,14,36,7,3,56,7,9,3,2,6,9,69,3,
+ 15,4,30,28,29,7,9,15,17,17,6,1,6,153,9,33,5,12,14,16,28,3,8,7,14,12,4,6,36,
+ 9,24,13,13,4,2,9,15,19,9,53,7,13,4,150,17,9,2,6,12,7,3,5,58,19,58,28,8,14,3,
+ 20,3,0,32,56,7,5,4,27,1,68,4,29,13,5,58,2,9,65,41,27,16,15,12,14,2,10,9,24,
+ 3,2,9,2,2,3,14,32,10,22,3,13,11,4,6,39,17,0,10,5,5,10,35,16,19,14,1,8,63,19,
+ 14,8,56,10,2,12,6,12,6,7,16,2,9,9,12,20,73,25,13,21,17,24,5,32,8,12,25,8,14,
+ 16,5,23,3,7,6,3,11,24,6,30,4,21,13,28,4,6,29,15,5,17,6,26,8,15,8,3,7,7,50,
+ 11,30,6,2,28,56,16,24,25,23,24,89,31,31,12,7,22,4,10,17,3,3,8,11,13,5,3,27,
+ 1,12,1,14,8,10,29,2,5,2,2,20,10,0,31,10,21,1,48,3,5,43,4,5,18,13,5,18,25,34,
+ 18,3,5,22,16,3,4,20,3,9,3,25,6,6,44,21,3,12,7,5,42,3,2,14,4,36,5,3,45,51,15,
+ 9,11,28,9,7,6,6,12,26,5,14,10,11,42,55,13,21,4,28,6,7,23,27,11,1,41,36,0,32,
+ 15,26,2,3,23,32,11,2,15,7,29,26,144,33,20,12,7,21,10,7,11,65,46,10,13,20,32,
+ 4,4,5,19,2,19,15,49,41,1,75,10,11,25,1,2,45,11,8,27,18,10,60,28,29,12,30,19,
+ 16,4,24,11,19,27,17,49,18,7,40,13,19,22,8,55,12,11,3,6,5,11,8,10,22,5,9,9,
+ 25,7,17,7,64,1,24,2,12,17,44,4,12,27,21,11,10,7,47,5,9,13,12,38,27,21,7,29,
+ 7,1,17,3,3,5,48,62,10,3,11,17,15,15,6,3,8,10,8,18,19,13,3,9,7,6,44,9,10,4,
+ 43,8,6,6,14,20,38,24,2,4,5,5,7,5,9,39,8,44,40,9,19,7,3,15,25,2,37,18,15,9,5,
+ 8,32,10,5,18,4,7,46,20,17,23,4,11,16,18,31,11,3,11,1,14,1,25,4,27,13,13,39,
+ 14,6,6,35,6,16,13,11,122,21,15,20,24,10,5,152,15,39,5,20,16,9,14,7,53,6,3,8,
+ 19,63,32,6,2,3,20,1,19,5,13,42,15,4,6,68,31,46,11,38,10,24,5,5,8,9,12,3,35,
+ 46,26,16,2,8,4,74,16,44,4,5,1,16,4,14,23,16,69,15,42,31,14,7,7,6,97,14,40,1,
+ 8,7,34,9,39,19,13,15,10,21,18,10,5,15,38,7,5,12,7,20,15,4,11,6,14,5,17,7,39,
+ 35,36,18,20,26,22,4,2,36,21,64,0,5,9,10,6,4,1,7,3,1,3,3,4,10,20,90,2,22,48,
+ 16,23,2,33,40,1,21,21,17,20,8,8,12,4,83,14,48,4,21,3,9,27,5,11,40,15,9,3,16,
+ 17,9,11,4,24,31,17,3,4,2,11,1,8,4,8,6,41,17,4,13,3,7,17,8,27,5,13,6,10,7,13,
+ 12,18,13,60,18,3,8,1,12,125,2,7,16,2,11,2,4,7,26,5,9,14,14,16,8,14,7,14,6,9,
+ 13,9,6,4,26,35,49,36,55,3,9,6,40,26,23,31,19,41,2,10,31,6,54,5,69,16,7,8,16,
+ 1,5,7,4,22,7,7,5,4,48,11,13,3,98,4,11,19,4,2,14,7,34,7,10,3,2,12,7,6,2,5,118
+ },
+};
+
+uint8_t data[1<<15];
+string data_str;
+void init_data() {
+ static bool initted = false;
+ if (initted) return;
+ initted = true;
+
+ // Repeatability. Kind of.
+ std::srand(42);
+ for (unsigned char & i : data) {
+ i = (uint8_t)rand();
+ }
+
+ data_str.assign((char*)data, sizeof(data));
+}
+
+
+BOOST_AUTO_TEST_SUITE( TBufferBaseTest )
+
+BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_GetBuffer ) {
+ init_data();
+
+ for (auto & d1 : dist) {
+ TMemoryBuffer buffer(16);
+ int offset = 0;
+ int index = 0;
+
+ while (offset < 1<<15) {
+ buffer.write(&data[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+
+ string output = buffer.getBufferAsString();
+ BOOST_CHECK_EQUAL(data_str, output);
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read ) {
+ init_data();
+
+ for (auto & d1 : dist) {
+ for (auto & d2 : dist) {
+ TMemoryBuffer buffer(16);
+ uint8_t data_out[1<<15];
+ int offset;
+ int index;
+
+ offset = 0;
+ index = 0;
+ while (offset < 1<<15) {
+ buffer.write(&data[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+
+ offset = 0;
+ index = 0;
+ while (offset < 1<<15) {
+ unsigned int got = buffer.read(&data_out[offset], d2[index]);
+ BOOST_CHECK_EQUAL(got, d2[index]);
+ offset += d2[index];
+ index++;
+ }
+
+ BOOST_CHECK(!memcmp(data, data_out, sizeof(data)));
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_ReadString ) {
+ init_data();
+
+ for (auto & d1 : dist) {
+ for (auto & d2 : dist) {
+ TMemoryBuffer buffer(16);
+ string output;
+ int offset;
+ int index;
+
+ offset = 0;
+ index = 0;
+ while (offset < 1<<15) {
+ buffer.write(&data[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+
+ offset = 0;
+ index = 0;
+ while (offset < 1<<15) {
+ unsigned int got = buffer.readAppendToString(output, d2[index]);
+ BOOST_CHECK_EQUAL(got, d2[index]);
+ offset += d2[index];
+ index++;
+ }
+
+ BOOST_CHECK_EQUAL(output, data_str);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read_Multi1 ) {
+ init_data();
+
+ // Do shorter writes and reads so we don't align to power-of-two boundaries.
+
+ for (auto & d1 : dist) {
+ for (auto & d2 : dist) {
+ TMemoryBuffer buffer(16);
+ uint8_t data_out[1<<15];
+ int offset;
+ int index;
+
+ for (int iter = 0; iter < 6; iter++) {
+ offset = 0;
+ index = 0;
+ while (offset < (1<<15)-42) {
+ buffer.write(&data[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+
+ offset = 0;
+ index = 0;
+ while (offset < (1<<15)-42) {
+ buffer.read(&data_out[offset], d2[index]);
+ offset += d2[index];
+ index++;
+ }
+
+ BOOST_CHECK(!memcmp(data, data_out, (1<<15)-42));
+
+ // Pull out the extra data.
+ buffer.read(data_out, 42);
+ }
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read_Multi2 ) {
+ init_data();
+
+ // Do shorter writes and reads so we don't align to power-of-two boundaries.
+ // Pull the buffer out of the loop so its state gets worked harder.
+ TMemoryBuffer buffer(16);
+
+ for (auto & d1 : dist) {
+ for (auto & d2 : dist) {
+ uint8_t data_out[1<<15];
+ int offset;
+ int index;
+
+ for (int iter = 0; iter < 6; iter++) {
+ offset = 0;
+ index = 0;
+ while (offset < (1<<15)-42) {
+ buffer.write(&data[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+
+ offset = 0;
+ index = 0;
+ while (offset < (1<<15)-42) {
+ buffer.read(&data_out[offset], d2[index]);
+ offset += d2[index];
+ index++;
+ }
+
+ BOOST_CHECK(!memcmp(data, data_out, (1<<15)-42));
+
+ // Pull out the extra data.
+ buffer.read(data_out, 42);
+ }
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read_Incomplete ) {
+ init_data();
+
+ // Do shorter writes and reads so we don't align to power-of-two boundaries.
+ // Pull the buffer out of the loop so its state gets worked harder.
+
+ for (auto & d1 : dist) {
+ for (auto & d2 : dist) {
+ TMemoryBuffer buffer(16);
+ uint8_t data_out[1<<13];
+
+ int write_offset = 0;
+ int write_index = 0;
+ unsigned int to_write = (1<<14)-42;
+ while (to_write > 0) {
+ int write_amt = (std::min)(d1[write_index], to_write);
+ buffer.write(&data[write_offset], write_amt);
+ write_offset += write_amt;
+ write_index++;
+ to_write -= write_amt;
+ }
+
+ int read_offset = 0;
+ int read_index = 0;
+ unsigned int to_read = (1<<13)-42;
+ while (to_read > 0) {
+ int read_amt = (std::min)(d2[read_index], to_read);
+ int got = buffer.read(&data_out[read_offset], read_amt);
+ BOOST_CHECK_EQUAL(got, read_amt);
+ read_offset += read_amt;
+ read_index++;
+ to_read -= read_amt;
+ }
+
+ BOOST_CHECK(!memcmp(data, data_out, (1<<13)-42));
+
+ int second_offset = write_offset;
+ int second_index = write_index-1;
+ unsigned int to_second = (1<<14)+42;
+ while (to_second > 0) {
+ int second_amt = (std::min)(d1[second_index], to_second);
+ //printf("%d\n", second_amt);
+ buffer.write(&data[second_offset], second_amt);
+ second_offset += second_amt;
+ second_index++;
+ to_second -= second_amt;
+ }
+
+ string output = buffer.getBufferAsString();
+ BOOST_CHECK_EQUAL(data_str.substr((1<<13)-42), output);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_BufferedTransport_Write ) {
+ init_data();
+
+ int sizes[] = {
+ 12, 15, 16, 17, 20,
+ 501, 512, 523,
+ 2000, 2048, 2096,
+ 1<<14, 1<<17,
+ };
+
+ for (int size : sizes) {
+ for (auto & d1 : dist) {
+ shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(16));
+ TBufferedTransport trans(buffer, size);
+
+ int offset = 0;
+ int index = 0;
+ while (offset < 1<<15) {
+ trans.write(&data[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+ trans.flush();
+
+ string output = buffer->getBufferAsString();
+ BOOST_CHECK_EQUAL(data_str, output);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_BufferedTransport_Read_Full ) {
+ init_data();
+
+ int sizes[] = {
+ 12, 15, 16, 17, 20,
+ 501, 512, 523,
+ 2000, 2048, 2096,
+ 1<<14, 1<<17,
+ };
+
+ for (int size : sizes) {
+ for (auto & d1 : dist) {
+ shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(data, sizeof(data)));
+ TBufferedTransport trans(buffer, size);
+ uint8_t data_out[1<<15];
+
+ int offset = 0;
+ int index = 0;
+ while (offset < 1<<15) {
+ // Note: this doesn't work with "read" because TBufferedTransport
+ // doesn't try loop over reads, so we get short reads. We don't
+ // check the return value, so that messes us up.
+ trans.readAll(&data_out[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+
+ BOOST_CHECK(!memcmp(data, data_out, sizeof(data)));
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_BufferedTransport_Read_Short ) {
+ init_data();
+
+ int sizes[] = {
+ 12, 15, 16, 17, 20,
+ 501, 512, 523,
+ 2000, 2048, 2096,
+ 1<<14, 1<<17,
+ };
+
+ for (int size : sizes) {
+ for (auto & d1 : dist) {
+ shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(data, sizeof(data)));
+ shared_ptr<TShortReadTransport> tshort(new TShortReadTransport(buffer, 0.125));
+ TBufferedTransport trans(buffer, size);
+ uint8_t data_out[1<<15];
+
+ int offset = 0;
+ int index = 0;
+ while (offset < 1<<15) {
+ // Note: this doesn't work with "read" because TBufferedTransport
+ // doesn't try loop over reads, so we get short reads. We don't
+ // check the return value, so that messes us up.
+ trans.readAll(&data_out[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+
+ BOOST_CHECK(!memcmp(data, data_out, sizeof(data)));
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_FramedTransport_Write ) {
+ init_data();
+
+ int sizes[] = {
+ 12, 15, 16, 17, 20,
+ 501, 512, 523,
+ 2000, 2048, 2096,
+ 1<<14, 1<<17,
+ };
+
+ for (int size : sizes) {
+ for (auto & d1 : dist) {
+ shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(16));
+ TFramedTransport trans(buffer, size);
+
+ int offset = 0;
+ int index = 0;
+ while (offset < 1<<15) {
+ trans.write(&data[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+ trans.flush();
+
+ int32_t frame_size = -1;
+ buffer->read(reinterpret_cast<uint8_t*>(&frame_size), sizeof(frame_size));
+ frame_size = (int32_t)ntohl((uint32_t)frame_size);
+ BOOST_CHECK_EQUAL(frame_size, 1<<15);
+ BOOST_CHECK_EQUAL(data_str.size(), (unsigned int)frame_size);
+ string output = buffer->getBufferAsString();
+ BOOST_CHECK_EQUAL(data_str, output);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_FramedTransport_Read ) {
+ init_data();
+
+ for (auto & d1 : dist) {
+ uint8_t data_out[1<<15];
+ shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
+ TFramedTransport trans(buffer);
+ int32_t length = sizeof(data);
+ length = (int32_t)htonl((uint32_t)length);
+ buffer->write(reinterpret_cast<uint8_t*>(&length), sizeof(length));
+ buffer->write(data, sizeof(data));
+
+ int offset = 0;
+ int index = 0;
+ while (offset < 1<<15) {
+ // This should work with read because we have one huge frame.
+ trans.read(&data_out[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+
+ BOOST_CHECK(!memcmp(data, data_out, sizeof(data)));
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_FramedTransport_Write_Read ) {
+ init_data();
+
+ int sizes[] = {
+ 12, 15, 16, 17, 20,
+ 501, 512, 523,
+ 2000, 2048, 2096,
+ 1<<14, 1<<17,
+ };
+
+ int probs[] = { 1, 2, 4, 8, 16, 32, };
+
+ for (int size : sizes) {
+ for (int prob : probs) {
+ for (auto & d1 : dist) {
+ shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(16));
+ TFramedTransport trans(buffer, size);
+ std::vector<uint8_t> data_out(1<<17, 0);
+ std::vector<int> flush_sizes;
+
+ int write_offset = 0;
+ int write_index = 0;
+ int flush_size = 0;
+ while (write_offset < 1<<15) {
+ trans.write(&data[write_offset], d1[write_index]);
+ write_offset += d1[write_index];
+ flush_size += d1[write_index];
+ write_index++;
+ if (flush_size > 0 && rand()%prob == 0) {
+ flush_sizes.push_back(flush_size);
+ flush_size = 0;
+ trans.flush();
+ }
+ }
+ if (flush_size != 0) {
+ flush_sizes.push_back(flush_size);
+ flush_size = 0;
+ trans.flush();
+ }
+
+ int read_offset = 0;
+ int read_index = 0;
+
+ for (int fsize : flush_sizes) {
+ // We are exploiting an implementation detail of TFramedTransport.
+ // The read buffer starts empty and it will never do more than one
+ // readFrame per read, so we should always get exactly one frame.
+ int got = trans.read(&data_out[read_offset], 1<<15);
+ BOOST_CHECK_EQUAL(got, fsize);
+ read_offset += got;
+ read_index++;
+ }
+
+ BOOST_CHECK_EQUAL((unsigned int)read_offset, sizeof(data));
+ BOOST_CHECK(!memcmp(data, &data_out[0], sizeof(data)));
+ }
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_FramedTransport_Empty_Flush ) {
+ init_data();
+
+ string output1("\x00\x00\x00\x01""a", 5);
+ string output2("\x00\x00\x00\x01""a\x00\x00\x00\x02""bc", 11);
+
+ shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
+ TFramedTransport trans(buffer);
+
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), "");
+ trans.flush();
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), "");
+ trans.flush();
+ trans.flush();
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), "");
+ trans.write((const uint8_t*)"a", 1);
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), "");
+ trans.flush();
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), output1);
+ trans.flush();
+ trans.flush();
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), output1);
+ trans.write((const uint8_t*)"bc", 2);
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), output1);
+ trans.flush();
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), output2);
+ trans.flush();
+ trans.flush();
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), output2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TFDTransportTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TFDTransportTest.cpp
new file mode 100644
index 000000000..0ba035a5b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TFDTransportTest.cpp
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <cstdlib>
+#include <stdexcept>
+#include <thrift/Thrift.h>
+#include <thrift/transport/TFDTransport.h>
+
+#define BOOST_TEST_MODULE TFDTransportTest
+#include <boost/test/unit_test.hpp>
+
+// Disabled on MSVC because the RTL asserts on an invalid file descriptor
+// in both debug and release mode; at least in MSVCR100 (Visual Studio 2010)
+#if !defined(WIN32)
+
+using apache::thrift::transport::TTransportException;
+using apache::thrift::transport::TFDTransport;
+
+BOOST_AUTO_TEST_CASE(test_tfdtransport_1) {
+ BOOST_CHECK_NO_THROW(TFDTransport t(256, TFDTransport::CLOSE_ON_DESTROY));
+}
+
+BOOST_AUTO_TEST_CASE(test_tfdtransport_2) {
+ TFDTransport t(256, TFDTransport::CLOSE_ON_DESTROY);
+ BOOST_CHECK_THROW(t.close(), TTransportException);
+}
+
+#else
+
+BOOST_AUTO_TEST_CASE(test_tfdtransport_dummy) {
+ BOOST_CHECK(true);
+}
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TFileTransportTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TFileTransportTest.cpp
new file mode 100644
index 000000000..21c1f3b33
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TFileTransportTest.cpp
@@ -0,0 +1,404 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE // needed for getopt_long
+#endif
+
+#include <thrift/thrift-config.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <getopt.h>
+#include <boost/test/unit_test.hpp>
+
+#include <thrift/transport/TFileTransport.h>
+
+#ifdef __MINGW32__
+ #include <io.h>
+ #include <unistd.h>
+ #include <sys/types.h>
+ #include <fcntl.h>
+ #include <sys\stat.h>
+#endif
+
+using namespace apache::thrift::transport;
+
+/**************************************************************************
+ * Global state
+ **************************************************************************/
+
+static const char* tmp_dir = "/tmp";
+
+class FsyncLog;
+FsyncLog* fsync_log;
+
+/**************************************************************************
+ * Helper code
+ **************************************************************************/
+
+/**
+ * Class to record calls to fsync
+ */
+class FsyncLog {
+public:
+ struct FsyncCall {
+ struct timeval time;
+ int fd;
+ };
+ typedef std::list<FsyncCall> CallList;
+
+ FsyncLog() = default;
+
+ void fsync(int fd) {
+ (void)fd;
+ FsyncCall call;
+ THRIFT_GETTIMEOFDAY(&call.time, nullptr);
+ calls_.push_back(call);
+ }
+
+ const CallList* getCalls() const { return &calls_; }
+
+private:
+ CallList calls_;
+};
+
+/**
+ * Helper class to clean up temporary files
+ */
+class TempFile {
+public:
+ TempFile(const char* directory, const char* prefix) {
+ #ifdef __MINGW32__
+ ((void)directory);
+ size_t path_len = strlen(prefix) + 8;
+ path_ = new char[path_len];
+ snprintf(path_, path_len, "%sXXXXXX", prefix);
+ if (_mktemp_s(path_,path_len) == 0) {
+ fd_ = open(path_,O_CREAT | O_RDWR | O_BINARY,S_IREAD | S_IWRITE);
+ if (fd_ < 0) {
+ throw apache::thrift::TException("_mktemp_s() failed");
+ }
+ } else {
+ throw apache::thrift::TException("_mktemp_s() failed");
+ }
+ #else
+ size_t path_len = strlen(directory) + strlen(prefix) + 8;
+ path_ = new char[path_len];
+ snprintf(path_, path_len, "%s/%sXXXXXX", directory, prefix);
+
+ fd_ = mkstemp(path_);
+ if (fd_ < 0) {
+ throw apache::thrift::TException("mkstemp() failed");
+ }
+ #endif
+ }
+
+ ~TempFile() {
+ unlink();
+ close();
+ }
+
+ const char* getPath() const { return path_; }
+
+ int getFD() const { return fd_; }
+
+ void unlink() {
+ if (path_) {
+ ::unlink(path_);
+ delete[] path_;
+ path_ = nullptr;
+ }
+ }
+
+ void close() {
+ if (fd_ < 0) {
+ return;
+ }
+
+ ::close(fd_);
+ fd_ = -1;
+ }
+
+private:
+ char* path_;
+ int fd_;
+};
+
+// Use our own version of fsync() for testing.
+// This returns immediately, so timing in test_destructor() isn't affected by
+// waiting on the actual filesystem.
+extern "C" int fsync(int fd) {
+ if (fsync_log) {
+ fsync_log->fsync(fd);
+ }
+ return 0;
+}
+
+int time_diff(const struct timeval* t1, const struct timeval* t2) {
+ return (t2->tv_usec - t1->tv_usec) + (t2->tv_sec - t1->tv_sec) * 1000000;
+}
+
+/**************************************************************************
+ * Test cases
+ **************************************************************************/
+
+/**
+ * Make sure the TFileTransport destructor exits "quickly".
+ *
+ * Previous versions had a bug causing the writer thread not to exit
+ * right away.
+ *
+ * It's kind of lame that we just check to see how long the destructor takes in
+ * wall-clock time. This could result in false failures on slower systems, or
+ * on heavily loaded machines.
+ */
+BOOST_AUTO_TEST_CASE(test_destructor) {
+ TempFile f(tmp_dir, "thrift.TFileTransportTest.");
+
+ unsigned int const NUM_ITERATIONS = 1000;
+
+ unsigned int num_over = 0;
+ for (unsigned int n = 0; n < NUM_ITERATIONS; ++n) {
+ BOOST_CHECK_EQUAL(0, ftruncate(f.getFD(), 0));
+
+ TFileTransport* transport = new TFileTransport(f.getPath());
+
+ // write something so that the writer thread gets started
+ transport->write(reinterpret_cast<const uint8_t*>("foo"), 3);
+
+ // Every other iteration, also call flush(), just in case that potentially
+ // has any effect on how the writer thread wakes up.
+ if (n & 0x1) {
+ transport->flush();
+ }
+
+ /*
+ * Time the call to the destructor
+ */
+ struct timeval start;
+ struct timeval end;
+
+ THRIFT_GETTIMEOFDAY(&start, nullptr);
+ delete transport;
+ THRIFT_GETTIMEOFDAY(&end, nullptr);
+
+ int delta = time_diff(&start, &end);
+
+ // If any attempt takes more than 500ms, treat that as a failure.
+ // Treat this as a fatal failure, so we'll return now instead of
+ // looping over a very slow operation.
+ BOOST_WARN( delta < 500000 );
+
+ // Normally, it takes less than 100ms on my dev box.
+ // However, if the box is heavily loaded, some of the test runs
+ // take longer, since we're just waiting for our turn on the CPU.
+ if (delta > 100000) {
+ ++num_over;
+ }
+ }
+
+ // Make sure fewer than 10% of the runs took longer than 1000us
+ BOOST_WARN(num_over < (NUM_ITERATIONS / 10));
+}
+
+/**
+ * Make sure setFlushMaxUs() is honored.
+ */
+void test_flush_max_us_impl(uint32_t flush_us, uint32_t write_us, uint32_t test_us) {
+ // TFileTransport only calls fsync() if data has been written,
+ // so make sure the write interval is smaller than the flush interval.
+ BOOST_WARN(write_us < flush_us);
+
+ TempFile f(tmp_dir, "thrift.TFileTransportTest.");
+
+ // Record calls to fsync()
+ FsyncLog log;
+ fsync_log = &log;
+
+ TFileTransport* transport = new TFileTransport(f.getPath());
+ // Don't flush because of # of bytes written
+ transport->setFlushMaxBytes(0xffffffff);
+ uint8_t buf[] = "a";
+ uint32_t buflen = sizeof(buf);
+
+ // Set the flush interval
+ transport->setFlushMaxUs(flush_us);
+
+ // Make one call to write, to start the writer thread now.
+ // (If we just let the thread get created during our test loop,
+ // the thread creation sometimes takes long enough to make the first
+ // fsync interval fail the check.)
+ transport->write(buf, buflen);
+
+ // Add one entry to the fsync log, just to mark the start time
+ log.fsync(-1);
+
+ // Loop doing write(), sleep(), ...
+ uint32_t total_time = 0;
+ while (true) {
+ transport->write(buf, buflen);
+ if (total_time > test_us) {
+ break;
+ }
+ usleep(write_us);
+ total_time += write_us;
+ }
+
+ delete transport;
+
+ // Stop logging new fsync() calls
+ fsync_log = nullptr;
+
+ // Examine the fsync() log
+ //
+ // TFileTransport uses pthread_cond_timedwait(), which only has millisecond
+ // resolution. In my testing, it normally wakes up about 1 millisecond late.
+ // However, sometimes it takes a bit longer. Allow 5ms leeway.
+ int max_allowed_delta = flush_us + 5000;
+
+ const FsyncLog::CallList* calls = log.getCalls();
+ // We added 1 fsync call above.
+ // Make sure TFileTransport called fsync at least once
+ BOOST_WARN_GE(calls->size(), static_cast<FsyncLog::CallList::size_type>(1));
+
+ const struct timeval* prev_time = nullptr;
+ for (const auto & call : *calls) {
+ if (prev_time) {
+ int delta = time_diff(prev_time, &call.time);
+ BOOST_WARN( delta < max_allowed_delta );
+ }
+ prev_time = &call.time;
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_flush_max_us1) {
+ // fsync every 10ms, write every 5ms, for 500ms
+ test_flush_max_us_impl(10000, 5000, 500000);
+}
+
+BOOST_AUTO_TEST_CASE(test_flush_max_us2) {
+ // fsync every 50ms, write every 20ms, for 500ms
+ test_flush_max_us_impl(50000, 20000, 500000);
+}
+
+BOOST_AUTO_TEST_CASE(test_flush_max_us3) {
+ // fsync every 400ms, write every 300ms, for 1s
+ test_flush_max_us_impl(400000, 300000, 1000000);
+}
+
+/**
+ * Make sure flush() is fast when there is nothing to do.
+ *
+ * TFileTransport used to have a bug where flush() would wait for the fsync
+ * timeout to expire.
+ */
+BOOST_AUTO_TEST_CASE(test_noop_flush) {
+ TempFile f(tmp_dir, "thrift.TFileTransportTest.");
+ TFileTransport transport(f.getPath());
+
+ // Write something to start the writer thread.
+ uint8_t buf[] = "a";
+ transport.write(buf, 1);
+
+ struct timeval start;
+ THRIFT_GETTIMEOFDAY(&start, nullptr);
+
+ for (unsigned int n = 0; n < 10; ++n) {
+ transport.flush();
+
+ struct timeval now;
+ THRIFT_GETTIMEOFDAY(&now, nullptr);
+
+ // Fail if at any point we've been running for longer than half a second.
+ // (With the buggy code, TFileTransport used to take 3 seconds per flush())
+ //
+ // Use a fatal fail so we break out early, rather than continuing to make
+ // many more slow flush() calls.
+ int delta = time_diff(&start, &now);
+ BOOST_WARN( delta < 2000000 );
+ }
+}
+
+/**************************************************************************
+ * General Initialization
+ **************************************************************************/
+
+void print_usage(FILE* f, const char* argv0) {
+ fprintf(f, "Usage: %s [boost_options] [options]\n", argv0);
+ fprintf(f, "Options:\n");
+ fprintf(f, " --tmp-dir=DIR, -t DIR\n");
+ fprintf(f, " --help\n");
+}
+
+void parse_args(int argc, char* argv[]) {
+ struct option long_opts[]
+ = {{"help", false, nullptr, 'h'}, {"tmp-dir", true, nullptr, 't'}, {nullptr, 0, nullptr, 0}};
+
+ while (true) {
+ optopt = 1;
+ int optchar = getopt_long(argc, argv, "ht:", long_opts, nullptr);
+ if (optchar == -1) {
+ break;
+ }
+
+ switch (optchar) {
+ case 't':
+ tmp_dir = optarg;
+ break;
+ case 'h':
+ print_usage(stdout, argv[0]);
+ exit(0);
+ case '?':
+ exit(1);
+ default:
+ // Only happens if someone adds another option to the optarg string,
+ // but doesn't update the switch statement to handle it.
+ fprintf(stderr, "unknown option \"-%c\"\n", optchar);
+ exit(1);
+ }
+ }
+}
+
+#ifdef BOOST_TEST_DYN_LINK
+static int myArgc = 0;
+static char **myArgv = nullptr;
+
+bool init_unit_test_suite() {
+ boost::unit_test::framework::master_test_suite().p_name.value = "TFileTransportTest";
+
+ // Parse arguments
+ parse_args(myArgc,myArgv);
+ return true;
+}
+
+int main( int argc, char* argv[] ) {
+ myArgc = argc;
+ myArgv = argv;
+ return ::boost::unit_test::unit_test_main(&init_unit_test_suite,argc,argv);
+}
+#else
+boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
+ boost::unit_test::framework::master_test_suite().p_name.value = "TFileTransportTest";
+
+ // Parse arguments
+ parse_args(argc, argv);
+ return NULL;
+}
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TMemoryBufferTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TMemoryBufferTest.cpp
new file mode 100644
index 000000000..42f97112b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TMemoryBufferTest.cpp
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <boost/test/auto_unit_test.hpp>
+#include <iostream>
+#include <climits>
+#include <vector>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <memory>
+#include <thrift/transport/TBufferTransports.h>
+#include "gen-cpp/ThriftTest_types.h"
+
+BOOST_AUTO_TEST_SUITE(TMemoryBufferTest)
+
+using apache::thrift::protocol::TBinaryProtocol;
+using apache::thrift::transport::TMemoryBuffer;
+using apache::thrift::transport::TTransportException;
+using std::shared_ptr;
+using std::cout;
+using std::endl;
+using std::string;
+
+BOOST_AUTO_TEST_CASE(test_read_write_grow) {
+ // Added to test the fix for THRIFT-1248
+ TMemoryBuffer uut;
+ const int maxSize = 65536;
+ uint8_t verify[maxSize];
+ std::vector<uint8_t> buf;
+ buf.resize(maxSize);
+
+ for (uint32_t i = 0; i < maxSize; ++i) {
+ buf[i] = static_cast<uint8_t>(i);
+ }
+
+ for (uint32_t i = 1; i < maxSize; i *= 2) {
+ uut.write(&buf[0], i);
+ }
+
+ for (uint32_t i = 1; i < maxSize; i *= 2) {
+ uut.read(verify, i);
+ BOOST_CHECK_EQUAL(0, ::memcmp(verify, &buf[0], i));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_roundtrip) {
+ shared_ptr<TMemoryBuffer> strBuffer(new TMemoryBuffer());
+ shared_ptr<TBinaryProtocol> binaryProtcol(new TBinaryProtocol(strBuffer));
+
+ thrift::test::Xtruct a;
+ a.i32_thing = 10;
+ a.i64_thing = 30;
+ a.string_thing = "holla back a";
+
+ a.write(binaryProtcol.get());
+ std::string serialized = strBuffer->getBufferAsString();
+
+ shared_ptr<TMemoryBuffer> strBuffer2(new TMemoryBuffer());
+ shared_ptr<TBinaryProtocol> binaryProtcol2(new TBinaryProtocol(strBuffer2));
+
+ strBuffer2->resetBuffer((uint8_t*)serialized.data(), static_cast<uint32_t>(serialized.length()));
+ thrift::test::Xtruct a2;
+ a2.read(binaryProtcol2.get());
+
+ BOOST_CHECK(a == a2);
+}
+
+BOOST_AUTO_TEST_CASE(test_readAppendToString) {
+ string str1 = "abcd1234";
+ TMemoryBuffer buf((uint8_t*)str1.data(),
+ static_cast<uint32_t>(str1.length()),
+ TMemoryBuffer::COPY);
+
+ string str3 = "wxyz", str4 = "6789";
+ buf.readAppendToString(str3, 4);
+ buf.readAppendToString(str4, INT_MAX);
+
+ BOOST_CHECK(str3 == "wxyzabcd");
+ BOOST_CHECK(str4 == "67891234");
+}
+
+BOOST_AUTO_TEST_CASE(test_exceptions) {
+ char data[] = "foo\0bar";
+
+ TMemoryBuffer buf1((uint8_t*)data, 7, TMemoryBuffer::OBSERVE);
+ string str = buf1.getBufferAsString();
+ BOOST_CHECK(str.length() == 7);
+
+ buf1.resetBuffer();
+
+ BOOST_CHECK_THROW(buf1.write((const uint8_t*)"foo", 3), TTransportException);
+
+ TMemoryBuffer buf2((uint8_t*)data, 7, TMemoryBuffer::COPY);
+ BOOST_CHECK_NO_THROW(buf2.write((const uint8_t*)"bar", 3));
+}
+
+BOOST_AUTO_TEST_CASE(test_default_maximum_buffer_size)
+{
+ BOOST_CHECK_EQUAL((std::numeric_limits<uint32_t>::max)(), TMemoryBuffer().getMaxBufferSize());
+}
+
+BOOST_AUTO_TEST_CASE(test_default_buffer_size)
+{
+ BOOST_CHECK_EQUAL(1024, TMemoryBuffer().getBufferSize());
+}
+
+BOOST_AUTO_TEST_CASE(test_error_set_max_buffer_size_too_small)
+{
+ TMemoryBuffer buf;
+ BOOST_CHECK_THROW(buf.setMaxBufferSize(buf.getBufferSize() - 1), TTransportException);
+}
+
+BOOST_AUTO_TEST_CASE(test_maximum_buffer_size)
+{
+ TMemoryBuffer buf;
+ buf.setMaxBufferSize(8192);
+ std::vector<uint8_t> small_buff(1);
+
+ for (size_t i = 0; i < 8192; ++i)
+ {
+ buf.write(&small_buff[0], 1);
+ }
+
+ BOOST_CHECK_THROW(buf.write(&small_buff[0], 1), TTransportException);
+}
+
+BOOST_AUTO_TEST_CASE(test_memory_buffer_to_get_sizeof_objects)
+{
+ // This is a demonstration of how to use TMemoryBuffer to determine
+ // the serialized size of a thrift object in the Binary protocol.
+ // See THRIFT-3480
+
+ shared_ptr<TMemoryBuffer> memBuffer(new TMemoryBuffer());
+ shared_ptr<TBinaryProtocol> binaryProtcol(new TBinaryProtocol(memBuffer));
+
+ thrift::test::Xtruct object;
+ object.i32_thing = 10;
+ object.i64_thing = 30;
+ object.string_thing = "who's your daddy?";
+
+ uint32_t size = object.write(binaryProtcol.get());
+ BOOST_CHECK_EQUAL(47, size);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TNonblockingSSLServerTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TNonblockingSSLServerTest.cpp
new file mode 100644
index 000000000..dc40c1257
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TNonblockingSSLServerTest.cpp
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define BOOST_TEST_MODULE TNonblockingSSLServerTest
+#include <boost/test/unit_test.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/format.hpp>
+
+#include "thrift/server/TNonblockingServer.h"
+#include "thrift/transport/TSSLSocket.h"
+#include "thrift/transport/TNonblockingSSLServerSocket.h"
+
+#include "gen-cpp/ParentService.h"
+
+#include <event.h>
+
+using namespace apache::thrift;
+using apache::thrift::concurrency::Guard;
+using apache::thrift::concurrency::Monitor;
+using apache::thrift::concurrency::Mutex;
+using apache::thrift::server::TServerEventHandler;
+using apache::thrift::transport::TSSLSocketFactory;
+using apache::thrift::transport::TSSLSocket;
+
+struct Handler : public test::ParentServiceIf {
+ void addString(const std::string& s) override { strings_.push_back(s); }
+ void getStrings(std::vector<std::string>& _return) override { _return = strings_; }
+ std::vector<std::string> strings_;
+
+ // dummy overrides not used in this test
+ int32_t incrementGeneration() override { return 0; }
+ int32_t getGeneration() override { return 0; }
+ void getDataWait(std::string&, const int32_t) override {}
+ void onewayWait() override {}
+ void exceptionWait(const std::string&) override {}
+ void unexpectedExceptionWait(const std::string&) override {}
+};
+
+boost::filesystem::path keyDir;
+boost::filesystem::path certFile(const std::string& filename)
+{
+ return keyDir / filename;
+}
+
+struct GlobalFixtureSSL
+{
+ GlobalFixtureSSL()
+ {
+ using namespace boost::unit_test::framework;
+ for (int i = 0; i < master_test_suite().argc; ++i)
+ {
+ BOOST_TEST_MESSAGE(boost::format("argv[%1%] = \"%2%\"") % i % master_test_suite().argv[i]);
+ }
+
+#ifdef __linux__
+ // OpenSSL calls send() without MSG_NOSIGPIPE so writing to a socket that has
+ // disconnected can cause a SIGPIPE signal...
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ TSSLSocketFactory::setManualOpenSSLInitialization(true);
+ apache::thrift::transport::initializeOpenSSL();
+
+ keyDir = boost::filesystem::current_path().parent_path().parent_path().parent_path() / "test" / "keys";
+ if (!boost::filesystem::exists(certFile("server.crt")))
+ {
+ keyDir = boost::filesystem::path(master_test_suite().argv[master_test_suite().argc - 1]);
+ if (!boost::filesystem::exists(certFile("server.crt")))
+ {
+ throw std::invalid_argument("The last argument to this test must be the directory containing the test certificate(s).");
+ }
+ }
+ }
+
+ virtual ~GlobalFixtureSSL()
+ {
+ apache::thrift::transport::cleanupOpenSSL();
+#ifdef __linux__
+ signal(SIGPIPE, SIG_DFL);
+#endif
+ }
+};
+
+#if (BOOST_VERSION >= 105900)
+BOOST_GLOBAL_FIXTURE(GlobalFixtureSSL);
+#else
+BOOST_GLOBAL_FIXTURE(GlobalFixtureSSL)
+#endif
+
+std::shared_ptr<TSSLSocketFactory> createServerSocketFactory() {
+ std::shared_ptr<TSSLSocketFactory> pServerSocketFactory;
+
+ pServerSocketFactory.reset(new TSSLSocketFactory());
+ pServerSocketFactory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
+ pServerSocketFactory->loadCertificate(certFile("server.crt").string().c_str());
+ pServerSocketFactory->loadPrivateKey(certFile("server.key").string().c_str());
+ pServerSocketFactory->server(true);
+ return pServerSocketFactory;
+}
+
+std::shared_ptr<TSSLSocketFactory> createClientSocketFactory() {
+ std::shared_ptr<TSSLSocketFactory> pClientSocketFactory;
+
+ pClientSocketFactory.reset(new TSSLSocketFactory());
+ pClientSocketFactory->authenticate(true);
+ pClientSocketFactory->loadCertificate(certFile("client.crt").string().c_str());
+ pClientSocketFactory->loadPrivateKey(certFile("client.key").string().c_str());
+ pClientSocketFactory->loadTrustedCertificates(certFile("CA.pem").string().c_str());
+ return pClientSocketFactory;
+}
+
+class Fixture {
+private:
+ struct ListenEventHandler : public TServerEventHandler {
+ public:
+ ListenEventHandler(Mutex* mutex) : listenMonitor_(mutex), ready_(false) {}
+
+ void preServe() override /* override */ {
+ Guard g(listenMonitor_.mutex());
+ ready_ = true;
+ listenMonitor_.notify();
+ }
+
+ Monitor listenMonitor_;
+ bool ready_;
+ };
+
+ struct Runner : public apache::thrift::concurrency::Runnable {
+ int port;
+ std::shared_ptr<event_base> userEventBase;
+ std::shared_ptr<TProcessor> processor;
+ std::shared_ptr<server::TNonblockingServer> server;
+ std::shared_ptr<ListenEventHandler> listenHandler;
+ std::shared_ptr<TSSLSocketFactory> pServerSocketFactory;
+ std::shared_ptr<transport::TNonblockingSSLServerSocket> socket;
+ Mutex mutex_;
+
+ Runner():port(0) {
+ listenHandler.reset(new ListenEventHandler(&mutex_));
+ }
+
+ void run() override {
+ // When binding to explicit port, allow retrying to workaround bind failures on ports in use
+ int retryCount = port ? 10 : 0;
+ pServerSocketFactory = createServerSocketFactory();
+ startServer(retryCount);
+ }
+
+ void readyBarrier() {
+ // block until server is listening and ready to accept connections
+ Guard g(mutex_);
+ while (!listenHandler->ready_) {
+ listenHandler->listenMonitor_.wait();
+ }
+ }
+ private:
+ void startServer(int retry_count) {
+ try {
+ socket.reset(new transport::TNonblockingSSLServerSocket(port, pServerSocketFactory));
+ server.reset(new server::TNonblockingServer(processor, socket));
+ server->setServerEventHandler(listenHandler);
+ server->setNumIOThreads(1);
+ if (userEventBase) {
+ server->registerEvents(userEventBase.get());
+ }
+ server->serve();
+ } catch (const transport::TTransportException&) {
+ if (retry_count > 0) {
+ ++port;
+ startServer(retry_count - 1);
+ } else {
+ throw;
+ }
+ }
+ }
+ };
+
+ struct EventDeleter {
+ void operator()(event_base* p) { event_base_free(p); }
+ };
+
+protected:
+ Fixture() : processor(new test::ParentServiceProcessor(std::make_shared<Handler>())) {}
+
+ ~Fixture() {
+ if (server) {
+ server->stop();
+ }
+ if (thread) {
+ thread->join();
+ }
+ }
+
+ void setEventBase(event_base* user_event_base) {
+ userEventBase_.reset(user_event_base, EventDeleter());
+ }
+
+ int startServer(int port) {
+ std::shared_ptr<Runner> runner(new Runner);
+ runner->port = port;
+ runner->processor = processor;
+ runner->userEventBase = userEventBase_;
+
+ std::unique_ptr<apache::thrift::concurrency::ThreadFactory> threadFactory(
+ new apache::thrift::concurrency::ThreadFactory(false));
+ thread = threadFactory->newThread(runner);
+ thread->start();
+ runner->readyBarrier();
+
+ server = runner->server;
+ return runner->port;
+ }
+
+ bool canCommunicate(int serverPort) {
+ std::shared_ptr<TSSLSocketFactory> pClientSocketFactory = createClientSocketFactory();
+ std::shared_ptr<TSSLSocket> socket = pClientSocketFactory->createSocket("localhost", serverPort);
+ socket->open();
+ test::ParentServiceClient client(std::make_shared<protocol::TBinaryProtocol>(
+ std::make_shared<transport::TFramedTransport>(socket)));
+ client.addString("foo");
+ std::vector<std::string> strings;
+ client.getStrings(strings);
+ return strings.size() == 1 && !(strings[0].compare("foo"));
+ }
+
+private:
+ std::shared_ptr<event_base> userEventBase_;
+ std::shared_ptr<test::ParentServiceProcessor> processor;
+protected:
+ std::shared_ptr<server::TNonblockingServer> server;
+private:
+ std::shared_ptr<apache::thrift::concurrency::Thread> thread;
+
+};
+
+BOOST_AUTO_TEST_SUITE(TNonblockingSSLServerTest)
+
+BOOST_FIXTURE_TEST_CASE(get_specified_port, Fixture) {
+ int specified_port = startServer(12345);
+ BOOST_REQUIRE_GE(specified_port, 12345);
+ BOOST_REQUIRE_EQUAL(server->getListenPort(), specified_port);
+ BOOST_CHECK(canCommunicate(specified_port));
+
+ server->stop();
+}
+
+BOOST_FIXTURE_TEST_CASE(get_assigned_port, Fixture) {
+ int specified_port = startServer(0);
+ BOOST_REQUIRE_EQUAL(specified_port, 0);
+ int assigned_port = server->getListenPort();
+ BOOST_REQUIRE_NE(assigned_port, 0);
+ BOOST_CHECK(canCommunicate(assigned_port));
+
+ server->stop();
+}
+
+BOOST_FIXTURE_TEST_CASE(provide_event_base, Fixture) {
+ event_base* eb = event_base_new();
+ setEventBase(eb);
+ startServer(0);
+
+ // assert that the server works
+ BOOST_CHECK(canCommunicate(server->getListenPort()));
+#if LIBEVENT_VERSION_NUMBER > 0x02010400
+ // also assert that the event_base is actually used when it's easy
+ BOOST_CHECK_GT(event_base_get_num_events(eb, EVENT_BASE_COUNT_ADDED), 0);
+#endif
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TNonblockingServerTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TNonblockingServerTest.cpp
new file mode 100644
index 000000000..f9aab4cc1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TNonblockingServerTest.cpp
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define BOOST_TEST_MODULE TNonblockingServerTest
+#include <boost/test/unit_test.hpp>
+#include <memory>
+
+#include "thrift/concurrency/Monitor.h"
+#include "thrift/concurrency/Thread.h"
+#include "thrift/server/TNonblockingServer.h"
+#include "thrift/transport/TNonblockingServerSocket.h"
+
+#include "gen-cpp/ParentService.h"
+
+#include <event.h>
+
+using apache::thrift::concurrency::Guard;
+using apache::thrift::concurrency::Monitor;
+using apache::thrift::concurrency::Mutex;
+using apache::thrift::concurrency::ThreadFactory;
+using apache::thrift::concurrency::Runnable;
+using apache::thrift::concurrency::Thread;
+using apache::thrift::concurrency::ThreadFactory;
+using apache::thrift::server::TServerEventHandler;
+using std::make_shared;
+using std::shared_ptr;
+
+using namespace apache::thrift;
+
+struct Handler : public test::ParentServiceIf {
+ void addString(const std::string& s) override { strings_.push_back(s); }
+ void getStrings(std::vector<std::string>& _return) override { _return = strings_; }
+ std::vector<std::string> strings_;
+
+ // dummy overrides not used in this test
+ int32_t incrementGeneration() override { return 0; }
+ int32_t getGeneration() override { return 0; }
+ void getDataWait(std::string&, const int32_t) override {}
+ void onewayWait() override {}
+ void exceptionWait(const std::string&) override {}
+ void unexpectedExceptionWait(const std::string&) override {}
+};
+
+class Fixture {
+private:
+ struct ListenEventHandler : public TServerEventHandler {
+ public:
+ ListenEventHandler(Mutex* mutex) : listenMonitor_(mutex), ready_(false) {}
+
+ void preServe() override /* override */ {
+ Guard g(listenMonitor_.mutex());
+ ready_ = true;
+ listenMonitor_.notify();
+ }
+
+ Monitor listenMonitor_;
+ bool ready_;
+ };
+
+ struct Runner : public Runnable {
+ int port;
+ shared_ptr<event_base> userEventBase;
+ shared_ptr<TProcessor> processor;
+ shared_ptr<server::TNonblockingServer> server;
+ shared_ptr<ListenEventHandler> listenHandler;
+ shared_ptr<transport::TNonblockingServerSocket> socket;
+ Mutex mutex_;
+
+ Runner() {
+ port = 0;
+ listenHandler.reset(new ListenEventHandler(&mutex_));
+ }
+
+ void run() override {
+ // When binding to explicit port, allow retrying to workaround bind failures on ports in use
+ int retryCount = port ? 10 : 0;
+ startServer(retryCount);
+ }
+
+ void readyBarrier() {
+ // block until server is listening and ready to accept connections
+ Guard g(mutex_);
+ while (!listenHandler->ready_) {
+ listenHandler->listenMonitor_.wait();
+ }
+ }
+ private:
+ void startServer(int retry_count) {
+ try {
+ socket.reset(new transport::TNonblockingServerSocket(port));
+ server.reset(new server::TNonblockingServer(processor, socket));
+ server->setServerEventHandler(listenHandler);
+ if (userEventBase) {
+ server->registerEvents(userEventBase.get());
+ }
+ server->serve();
+ } catch (const transport::TTransportException&) {
+ if (retry_count > 0) {
+ ++port;
+ startServer(retry_count - 1);
+ } else {
+ throw;
+ }
+ }
+ }
+ };
+
+ struct EventDeleter {
+ void operator()(event_base* p) { event_base_free(p); }
+ };
+
+protected:
+ Fixture() : processor(new test::ParentServiceProcessor(make_shared<Handler>())) {}
+
+ ~Fixture() {
+ if (server) {
+ server->stop();
+ }
+ if (thread) {
+ thread->join();
+ }
+ }
+
+ void setEventBase(event_base* user_event_base) {
+ userEventBase_.reset(user_event_base, EventDeleter());
+ }
+
+ int startServer(int port) {
+ shared_ptr<Runner> runner(new Runner);
+ runner->port = port;
+ runner->processor = processor;
+ runner->userEventBase = userEventBase_;
+
+ shared_ptr<ThreadFactory> threadFactory(
+ new ThreadFactory(false));
+ thread = threadFactory->newThread(runner);
+ thread->start();
+ runner->readyBarrier();
+
+ server = runner->server;
+ return runner->port;
+ }
+
+ bool canCommunicate(int serverPort) {
+ shared_ptr<transport::TSocket> socket(new transport::TSocket("localhost", serverPort));
+ socket->open();
+ test::ParentServiceClient client(make_shared<protocol::TBinaryProtocol>(
+ make_shared<transport::TFramedTransport>(socket)));
+ client.addString("foo");
+ std::vector<std::string> strings;
+ client.getStrings(strings);
+ return strings.size() == 1 && !(strings[0].compare("foo"));
+ }
+
+private:
+ shared_ptr<event_base> userEventBase_;
+ shared_ptr<test::ParentServiceProcessor> processor;
+protected:
+ shared_ptr<server::TNonblockingServer> server;
+private:
+ shared_ptr<apache::thrift::concurrency::Thread> thread;
+
+};
+
+BOOST_AUTO_TEST_SUITE(TNonblockingServerTest)
+
+BOOST_FIXTURE_TEST_CASE(get_specified_port, Fixture) {
+ int specified_port = startServer(12345);
+ BOOST_REQUIRE_GE(specified_port, 12345);
+ BOOST_REQUIRE_EQUAL(server->getListenPort(), specified_port);
+ BOOST_CHECK(canCommunicate(specified_port));
+
+ server->stop();
+}
+
+BOOST_FIXTURE_TEST_CASE(get_assigned_port, Fixture) {
+ int specified_port = startServer(0);
+ BOOST_REQUIRE_EQUAL(specified_port, 0);
+ int assigned_port = server->getListenPort();
+ BOOST_REQUIRE_NE(assigned_port, 0);
+ BOOST_CHECK(canCommunicate(assigned_port));
+
+ server->stop();
+}
+
+BOOST_FIXTURE_TEST_CASE(provide_event_base, Fixture) {
+ event_base* eb = event_base_new();
+ setEventBase(eb);
+ startServer(0);
+
+ // assert that the server works
+ BOOST_CHECK(canCommunicate(server->getListenPort()));
+#if LIBEVENT_VERSION_NUMBER > 0x02010400
+ // also assert that the event_base is actually used when it's easy
+ BOOST_CHECK_GT(event_base_get_num_events(eb, EVENT_BASE_COUNT_ADDED), 0);
+#endif
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TPipeInterruptTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TPipeInterruptTest.cpp
new file mode 100644
index 000000000..2423f5646
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TPipeInterruptTest.cpp
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifdef _WIN32
+
+#include <boost/test/test_tools.hpp>
+#include <boost/test/unit_test_suite.hpp>
+
+#include <boost/chrono/duration.hpp>
+#include <boost/date_time/posix_time/posix_time_duration.hpp>
+#include <boost/thread/thread.hpp>
+#include <thrift/transport/TPipe.h>
+#include <thrift/transport/TPipeServer.h>
+#include <memory>
+
+using apache::thrift::transport::TPipeServer;
+using apache::thrift::transport::TPipe;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using namespace apache::thrift;
+
+BOOST_AUTO_TEST_SUITE(TPipeInterruptTest)
+
+// TODO: duplicate the test cases in TSocketInterruptTest for pipes,
+// once pipes implement interruptChildren
+
+BOOST_AUTO_TEST_CASE(test_interrupt_before_accept) {
+ TPipeServer pipe1("TPipeInterruptTest");
+ pipe1.listen();
+ pipe1.interrupt();
+ BOOST_CHECK_THROW(pipe1.accept(), TTransportException);
+}
+
+static void acceptWorker(TPipeServer *pipe) {
+ try
+ {
+ for (;;)
+ {
+ std::shared_ptr<TTransport> temp = pipe->accept();
+ }
+ }
+ catch (...) {/*just want to make sure nothing crashes*/ }
+}
+
+static void interruptWorker(TPipeServer *pipe) {
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ pipe->interrupt();
+}
+
+BOOST_AUTO_TEST_CASE(stress_pipe_accept_interruption) {
+ int interruptIters = 10;
+
+ for (int i = 0; i < interruptIters; ++i)
+ {
+ TPipeServer pipeServer("TPipeInterruptTest");
+ pipeServer.listen();
+ boost::thread acceptThread(std::bind(acceptWorker, &pipeServer));
+ boost::thread interruptThread(std::bind(interruptWorker, &pipeServer));
+ try
+ {
+ for (;;)
+ {
+ TPipe client("TPipeInterruptTest");
+ client.setConnTimeout(1);
+ client.open();
+ }
+ } catch (...) { /*just testing for crashes*/ }
+ interruptThread.join();
+ acceptThread.join();
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TPipedTransportTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TPipedTransportTest.cpp
new file mode 100644
index 000000000..f3091a487
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TPipedTransportTest.cpp
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/Thrift.h>
+#include <memory>
+#include <thrift/transport/TTransportUtils.h>
+#include <thrift/transport/TBufferTransports.h>
+
+#define BOOST_TEST_MODULE TPipedTransportTest
+#include <boost/test/unit_test.hpp>
+
+using apache::thrift::transport::TTransportException;
+using apache::thrift::transport::TPipedTransport;
+using apache::thrift::transport::TMemoryBuffer;
+using namespace apache::thrift;
+
+BOOST_AUTO_TEST_CASE(test_read_write) {
+ std::shared_ptr<TMemoryBuffer> underlying(new TMemoryBuffer);
+ std::shared_ptr<TMemoryBuffer> pipe(new TMemoryBuffer);
+ std::shared_ptr<TPipedTransport> trans(new TPipedTransport(underlying, pipe));
+
+ uint8_t buffer[4];
+
+ underlying->write((uint8_t*)"abcd", 4);
+ trans->readAll(buffer, 2);
+ BOOST_CHECK(std::string((char*)buffer, 2) == "ab");
+ trans->readEnd();
+ BOOST_CHECK(pipe->getBufferAsString() == "ab");
+ pipe->resetBuffer();
+ underlying->write((uint8_t*)"ef", 2);
+ trans->readAll(buffer, 2);
+ BOOST_CHECK(std::string((char*)buffer, 2) == "cd");
+ trans->readAll(buffer, 2);
+ BOOST_CHECK(std::string((char*)buffer, 2) == "ef");
+ trans->readEnd();
+ BOOST_CHECK(pipe->getBufferAsString() == "cdef");
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TSSLSocketInterruptTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TSSLSocketInterruptTest.cpp
new file mode 100644
index 000000000..33f686cef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TSSLSocketInterruptTest.cpp
@@ -0,0 +1,282 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <boost/test/auto_unit_test.hpp>
+#include <boost/test/unit_test_suite.hpp>
+#include <boost/chrono/duration.hpp>
+#include <boost/date_time/posix_time/posix_time_duration.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/format.hpp>
+#include <memory>
+#include <thrift/transport/TSSLSocket.h>
+#include <thrift/transport/TSSLServerSocket.h>
+#ifdef __linux__
+#include <signal.h>
+#endif
+
+using apache::thrift::transport::TSSLServerSocket;
+using apache::thrift::transport::TSSLSocket;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using apache::thrift::transport::TSSLSocketFactory;
+
+using std::static_pointer_cast;
+using std::shared_ptr;
+
+BOOST_AUTO_TEST_SUITE(TSSLSocketInterruptTest)
+
+boost::filesystem::path keyDir;
+boost::filesystem::path certFile(const std::string& filename)
+{
+ return keyDir / filename;
+}
+boost::mutex gMutex;
+
+struct GlobalFixtureSSL
+{
+ GlobalFixtureSSL()
+ {
+ using namespace boost::unit_test::framework;
+ for (int i = 0; i < master_test_suite().argc; ++i)
+ {
+ BOOST_TEST_MESSAGE(boost::format("argv[%1%] = \"%2%\"") % i % master_test_suite().argv[i]);
+ }
+
+#ifdef __linux__
+ // OpenSSL calls send() without MSG_NOSIGPIPE so writing to a socket that has
+ // disconnected can cause a SIGPIPE signal...
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ TSSLSocketFactory::setManualOpenSSLInitialization(true);
+ apache::thrift::transport::initializeOpenSSL();
+
+ keyDir = boost::filesystem::current_path().parent_path().parent_path().parent_path() / "test" / "keys";
+ if (!boost::filesystem::exists(certFile("server.crt")))
+ {
+ keyDir = boost::filesystem::path(master_test_suite().argv[master_test_suite().argc - 1]);
+ if (!boost::filesystem::exists(certFile("server.crt")))
+ {
+ throw std::invalid_argument("The last argument to this test must be the directory containing the test certificate(s).");
+ }
+ }
+ }
+
+ virtual ~GlobalFixtureSSL()
+ {
+ apache::thrift::transport::cleanupOpenSSL();
+#ifdef __linux__
+ signal(SIGPIPE, SIG_DFL);
+#endif
+ }
+};
+
+#if (BOOST_VERSION >= 105900)
+BOOST_GLOBAL_FIXTURE(GlobalFixtureSSL);
+#else
+BOOST_GLOBAL_FIXTURE(GlobalFixtureSSL)
+#endif
+
+void readerWorker(shared_ptr<TTransport> tt, uint32_t expectedResult) {
+ uint8_t buf[4];
+ try {
+ tt->read(buf, 1);
+ BOOST_CHECK_EQUAL(expectedResult, tt->read(buf, 4));
+ } catch (const TTransportException& tx) {
+ BOOST_CHECK_EQUAL(TTransportException::TIMED_OUT, tx.getType());
+ }
+}
+
+void readerWorkerMustThrow(shared_ptr<TTransport> tt) {
+ try {
+ uint8_t buf[400];
+ tt->read(buf, 1);
+ tt->read(buf, 400);
+ BOOST_ERROR("should not have gotten here");
+ } catch (const TTransportException& tx) {
+ BOOST_CHECK_EQUAL(TTransportException::INTERRUPTED, tx.getType());
+ }
+}
+
+shared_ptr<TSSLSocketFactory> createServerSocketFactory() {
+ shared_ptr<TSSLSocketFactory> pServerSocketFactory;
+
+ pServerSocketFactory.reset(new TSSLSocketFactory());
+ pServerSocketFactory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
+ pServerSocketFactory->loadCertificate(certFile("server.crt").string().c_str());
+ pServerSocketFactory->loadPrivateKey(certFile("server.key").string().c_str());
+ pServerSocketFactory->server(true);
+ return pServerSocketFactory;
+}
+
+shared_ptr<TSSLSocketFactory> createClientSocketFactory() {
+ shared_ptr<TSSLSocketFactory> pClientSocketFactory;
+
+ pClientSocketFactory.reset(new TSSLSocketFactory());
+ pClientSocketFactory->authenticate(true);
+ pClientSocketFactory->loadCertificate(certFile("client.crt").string().c_str());
+ pClientSocketFactory->loadPrivateKey(certFile("client.key").string().c_str());
+ pClientSocketFactory->loadTrustedCertificates(certFile("CA.pem").string().c_str());
+ return pClientSocketFactory;
+}
+
+BOOST_AUTO_TEST_CASE(test_ssl_interruptable_child_read_while_handshaking) {
+ shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
+ TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
+ sock1.listen();
+ int port = sock1.getPort();
+ shared_ptr<TSSLSocketFactory> pClientSocketFactory = createClientSocketFactory();
+ shared_ptr<TSSLSocket> clientSock = pClientSocketFactory->createSocket("localhost", port);
+ clientSock->open();
+ shared_ptr<TTransport> accepted = sock1.accept();
+ boost::thread readThread(std::bind(readerWorkerMustThrow, accepted));
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // readThread is practically guaranteed to be blocking now
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(readThread.try_join_for(boost::chrono::milliseconds(20)),
+ "server socket interruptChildren did not interrupt child read");
+ clientSock->close();
+ accepted->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_ssl_interruptable_child_read) {
+ shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
+ TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
+ sock1.listen();
+ int port = sock1.getPort();
+ shared_ptr<TSSLSocketFactory> pClientSocketFactory = createClientSocketFactory();
+ shared_ptr<TSSLSocket> clientSock = pClientSocketFactory->createSocket("localhost", port);
+ clientSock->open();
+ shared_ptr<TTransport> accepted = sock1.accept();
+ boost::thread readThread(std::bind(readerWorkerMustThrow, accepted));
+ clientSock->write((const uint8_t*)"0", 1);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // readThread is practically guaranteed to be blocking now
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(readThread.try_join_for(boost::chrono::milliseconds(20)),
+ "server socket interruptChildren did not interrupt child read");
+ accepted->close();
+ clientSock->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_ssl_non_interruptable_child_read) {
+ shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
+ TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
+ sock1.setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior
+ sock1.listen();
+ int port = sock1.getPort();
+ shared_ptr<TSSLSocketFactory> pClientSocketFactory = createClientSocketFactory();
+ shared_ptr<TSSLSocket> clientSock = pClientSocketFactory->createSocket("localhost", port);
+ clientSock->open();
+ shared_ptr<TTransport> accepted = sock1.accept();
+ static_pointer_cast<TSSLSocket>(accepted)->setRecvTimeout(1000);
+ boost::thread readThread(std::bind(readerWorker, accepted, 0));
+ clientSock->write((const uint8_t*)"0", 1);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // readThread is practically guaranteed to be blocking here
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(!readThread.try_join_for(boost::chrono::milliseconds(200)),
+ "server socket interruptChildren interrupted child read");
+
+ // wait for receive timeout to kick in
+ readThread.join();
+ accepted->close();
+ clientSock->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_ssl_cannot_change_after_listen) {
+ shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
+ TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
+ sock1.listen();
+ BOOST_CHECK_THROW(sock1.setInterruptableChildren(false), std::logic_error);
+ sock1.close();
+}
+
+void peekerWorker(shared_ptr<TTransport> tt, bool expectedResult) {
+ uint8_t buf[400];
+ try {
+ tt->read(buf, 1);
+ BOOST_CHECK_EQUAL(expectedResult, tt->peek());
+ } catch (const TTransportException& tx) {
+ BOOST_CHECK_EQUAL(TTransportException::TIMED_OUT, tx.getType());
+ }
+}
+
+void peekerWorkerInterrupt(shared_ptr<TTransport> tt) {
+ uint8_t buf[400];
+ try {
+ tt->read(buf, 1);
+ tt->peek();
+ } catch (const TTransportException& tx) {
+ BOOST_CHECK_EQUAL(TTransportException::INTERRUPTED, tx.getType());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_ssl_interruptable_child_peek) {
+ shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
+ TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
+ sock1.listen();
+ int port = sock1.getPort();
+ shared_ptr<TSSLSocketFactory> pClientSocketFactory = createClientSocketFactory();
+ shared_ptr<TSSLSocket> clientSock = pClientSocketFactory->createSocket("localhost", port);
+ clientSock->open();
+ shared_ptr<TTransport> accepted = sock1.accept();
+ boost::thread peekThread(std::bind(peekerWorkerInterrupt, accepted));
+ clientSock->write((const uint8_t*)"0", 1);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // peekThread is practically guaranteed to be blocking now
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(peekThread.try_join_for(boost::chrono::milliseconds(200)),
+ "server socket interruptChildren did not interrupt child peek");
+ accepted->close();
+ clientSock->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_ssl_non_interruptable_child_peek) {
+ shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
+ TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
+ sock1.setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior
+ sock1.listen();
+ int port = sock1.getPort();
+ shared_ptr<TSSLSocketFactory> pClientSocketFactory = createClientSocketFactory();
+ shared_ptr<TSSLSocket> clientSock = pClientSocketFactory->createSocket("localhost", port);
+ clientSock->open();
+ shared_ptr<TTransport> accepted = sock1.accept();
+ static_pointer_cast<TSSLSocket>(accepted)->setRecvTimeout(1000);
+ boost::thread peekThread(std::bind(peekerWorker, accepted, false));
+ clientSock->write((const uint8_t*)"0", 1);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // peekThread is practically guaranteed to be blocking now
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(!peekThread.try_join_for(boost::chrono::milliseconds(200)),
+ "server socket interruptChildren interrupted child peek");
+
+ // wait for the receive timeout to kick in
+ peekThread.join();
+ accepted->close();
+ clientSock->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TServerIntegrationTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TServerIntegrationTest.cpp
new file mode 100644
index 000000000..b88c35bf4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TServerIntegrationTest.cpp
@@ -0,0 +1,536 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define BOOST_TEST_MODULE TServerIntegrationTest
+#include <atomic>
+#include <boost/test/auto_unit_test.hpp>
+#include <boost/date_time/posix_time/ptime.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <boost/thread.hpp>
+#include <thrift/server/TSimpleServer.h>
+#include <thrift/server/TThreadPoolServer.h>
+#include <thrift/server/TThreadedServer.h>
+#include <memory>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/transport/TServerSocket.h>
+#include <thrift/transport/TSocket.h>
+#include <thrift/transport/TTransport.h>
+#include "gen-cpp/ParentService.h"
+#include <string>
+#include <vector>
+
+using apache::thrift::concurrency::Guard;
+using apache::thrift::concurrency::Monitor;
+using apache::thrift::concurrency::Mutex;
+using apache::thrift::concurrency::Synchronized;
+using apache::thrift::protocol::TBinaryProtocol;
+using apache::thrift::protocol::TBinaryProtocolFactory;
+using apache::thrift::protocol::TProtocol;
+using apache::thrift::protocol::TProtocolFactory;
+using apache::thrift::transport::TServerSocket;
+using apache::thrift::transport::TServerTransport;
+using apache::thrift::transport::TSocket;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using apache::thrift::transport::TTransportFactory;
+using apache::thrift::server::TServer;
+using apache::thrift::server::TServerEventHandler;
+using apache::thrift::server::TSimpleServer;
+using apache::thrift::server::TThreadPoolServer;
+using apache::thrift::server::TThreadedServer;
+using std::dynamic_pointer_cast;
+using std::make_shared;
+using std::shared_ptr;
+using apache::thrift::test::ParentServiceClient;
+using apache::thrift::test::ParentServiceIf;
+using apache::thrift::test::ParentServiceIfFactory;
+using apache::thrift::test::ParentServiceIfSingletonFactory;
+using apache::thrift::test::ParentServiceProcessor;
+using apache::thrift::test::ParentServiceProcessorFactory;
+using apache::thrift::TProcessor;
+using apache::thrift::TProcessorFactory;
+using boost::posix_time::milliseconds;
+
+/**
+ * preServe runs after listen() is successful, when we can connect
+ */
+class TServerReadyEventHandler : public TServerEventHandler, public Monitor {
+public:
+ TServerReadyEventHandler() : isListening_(false), accepted_(0) {}
+ ~TServerReadyEventHandler() override = default;
+ void preServe() override {
+ Synchronized sync(*this);
+ isListening_ = true;
+ notify();
+ }
+ void* createContext(shared_ptr<TProtocol> input,
+ shared_ptr<TProtocol> output) override {
+ Synchronized sync(*this);
+ ++accepted_;
+ notify();
+
+ (void)input;
+ (void)output;
+ return nullptr;
+ }
+ bool isListening() const { return isListening_; }
+ uint64_t acceptedCount() const { return accepted_; }
+
+private:
+ bool isListening_;
+ uint64_t accepted_;
+};
+
+/**
+ * Reusing another generated test, just something to serve up
+ */
+class ParentHandler : public ParentServiceIf {
+public:
+ ParentHandler() : generation_(0) {}
+
+ int32_t incrementGeneration() override {
+ Guard g(mutex_);
+ return ++generation_;
+ }
+
+ int32_t getGeneration() override {
+ Guard g(mutex_);
+ return generation_;
+ }
+
+ void addString(const std::string& s) override {
+ Guard g(mutex_);
+ strings_.push_back(s);
+ }
+
+ void getStrings(std::vector<std::string>& _return) override {
+ Guard g(mutex_);
+ _return = strings_;
+ }
+
+ void getDataWait(std::string& _return, const int32_t length) override {
+ THRIFT_UNUSED_VARIABLE(_return);
+ THRIFT_UNUSED_VARIABLE(length);
+ }
+
+ void onewayWait() override {}
+
+ void exceptionWait(const std::string& message) override { THRIFT_UNUSED_VARIABLE(message); }
+
+ void unexpectedExceptionWait(const std::string& message) override { THRIFT_UNUSED_VARIABLE(message); }
+
+protected:
+ Mutex mutex_;
+ int32_t generation_;
+ std::vector<std::string> strings_;
+};
+
+void autoSocketCloser(TSocket* pSock) {
+ pSock->close();
+ delete pSock;
+}
+
+template <class TServerType>
+class TServerIntegrationTestFixture {
+public:
+ TServerIntegrationTestFixture(const shared_ptr<TProcessorFactory>& _processorFactory)
+ : pServer(new TServerType(_processorFactory,
+ shared_ptr<TServerTransport>(
+ new TServerSocket("localhost", 0)),
+ shared_ptr<TTransportFactory>(new TTransportFactory),
+ shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory))),
+ pEventHandler(shared_ptr<TServerReadyEventHandler>(new TServerReadyEventHandler)),
+ bStressDone(false),
+ bStressConnectionCount(0),
+ bStressRequestCount(0) {
+ pServer->setServerEventHandler(pEventHandler);
+ }
+
+ TServerIntegrationTestFixture(const shared_ptr<TProcessor>& _processor)
+ : pServer(
+ new TServerType(_processor,
+ shared_ptr<TServerTransport>(new TServerSocket("localhost", 0)),
+ shared_ptr<TTransportFactory>(new TTransportFactory),
+ shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory))),
+ pEventHandler(shared_ptr<TServerReadyEventHandler>(new TServerReadyEventHandler)),
+ bStressDone(false),
+ bStressConnectionCount(0),
+ bStressRequestCount(0) {
+ pServer->setServerEventHandler(pEventHandler);
+ }
+
+ void startServer() {
+ pServerThread.reset(new boost::thread(std::bind(&TServerType::serve, pServer.get())));
+
+ // block until listen() completes so clients will be able to connect
+ Synchronized sync(*(pEventHandler.get()));
+ while (!pEventHandler->isListening()) {
+ pEventHandler->wait();
+ }
+
+ BOOST_TEST_MESSAGE(" server is listening");
+ }
+
+ void blockUntilAccepted(uint64_t numAccepted) {
+ Synchronized sync(*(pEventHandler.get()));
+ while (pEventHandler->acceptedCount() < numAccepted) {
+ pEventHandler->wait();
+ }
+
+ BOOST_TEST_MESSAGE(boost::format(" server has accepted %1%") % numAccepted);
+ }
+
+ void stopServer() {
+ if (pServerThread) {
+ pServer->stop();
+ BOOST_TEST_MESSAGE(" server stop completed");
+
+ pServerThread->join();
+ BOOST_TEST_MESSAGE(" server thread joined");
+ pServerThread.reset();
+ }
+ }
+
+ ~TServerIntegrationTestFixture() { stopServer(); }
+
+ /**
+ * Performs a baseline test where some clients are opened and issue a single operation
+ * and then disconnect at different intervals.
+ * \param[in] numToMake the number of concurrent clients
+ * \param[in] expectedHWM the high water mark we expect of concurrency
+ * \param[in] purpose a description of the test for logging purposes
+ */
+ void baseline(int64_t numToMake, int64_t expectedHWM, const std::string& purpose) {
+ BOOST_TEST_MESSAGE(boost::format("Testing %1%: %2% with %3% clients, expect %4% HWM")
+ % typeid(TServerType).name() % purpose % numToMake % expectedHWM);
+
+ startServer();
+
+ std::vector<shared_ptr<TSocket> > holdSockets;
+ std::vector<shared_ptr<boost::thread> > holdThreads;
+
+ for (int64_t i = 0; i < numToMake; ++i) {
+ shared_ptr<TSocket> pClientSock(new TSocket("localhost", getServerPort()),
+ autoSocketCloser);
+ holdSockets.push_back(pClientSock);
+ shared_ptr<TProtocol> pClientProtocol(new TBinaryProtocol(pClientSock));
+ ParentServiceClient client(pClientProtocol);
+ pClientSock->open();
+ client.incrementGeneration();
+ holdThreads.push_back(shared_ptr<boost::thread>(
+ new boost::thread(std::bind(&TServerIntegrationTestFixture::delayClose,
+ this,
+ pClientSock,
+ milliseconds(10 * numToMake)))));
+ }
+
+ BOOST_CHECK_EQUAL(expectedHWM, pServer->getConcurrentClientCountHWM());
+
+ BOOST_FOREACH (shared_ptr<boost::thread> pThread, holdThreads) { pThread->join(); }
+ holdThreads.clear();
+ holdSockets.clear();
+
+ stopServer();
+ }
+
+ /**
+ * Helper method used to close a connection after a delay.
+ * \param[in] toClose the connection to close
+ * \param[in] after the delay to impose
+ */
+ void delayClose(shared_ptr<TTransport> toClose, boost::posix_time::time_duration after) {
+ boost::this_thread::sleep(after);
+ toClose->close();
+ }
+
+ /**
+ * \returns the server port number
+ */
+ int getServerPort() {
+ auto* pSock = dynamic_cast<TServerSocket*>(pServer->getServerTransport().get());
+ if (!pSock) { throw std::logic_error("how come?"); }
+ return pSock->getPort();
+ }
+
+ /**
+ * Performs a stress test by spawning threads that connect, do a number of operations
+ * and disconnect, then a random delay, then do it over again. This is done for a fixed
+ * period of time to test for concurrency correctness.
+ * \param[in] numToMake the number of concurrent clients
+ */
+ void stress(int64_t numToMake, const boost::posix_time::time_duration& duration) {
+ BOOST_TEST_MESSAGE(boost::format("Stress testing %1% with %2% clients for %3% seconds")
+ % typeid(TServerType).name() % numToMake % duration.total_seconds());
+
+ startServer();
+
+ std::vector<shared_ptr<boost::thread> > holdThreads;
+ for (int64_t i = 0; i < numToMake; ++i) {
+ holdThreads.push_back(shared_ptr<boost::thread>(
+ new boost::thread(std::bind(&TServerIntegrationTestFixture::stressor, this))));
+ }
+
+ boost::this_thread::sleep(duration);
+ bStressDone = true;
+
+ BOOST_TEST_MESSAGE(boost::format(" serviced %1% connections (HWM %2%) totaling %3% requests")
+ % bStressConnectionCount % pServer->getConcurrentClientCountHWM() % bStressRequestCount);
+
+ BOOST_FOREACH (shared_ptr<boost::thread> pThread, holdThreads) { pThread->join(); }
+ holdThreads.clear();
+
+ BOOST_CHECK(bStressRequestCount > 0);
+
+ stopServer();
+ }
+
+ /**
+ * Helper method to stress the system
+ */
+ void stressor() {
+ while (!bStressDone) {
+ shared_ptr<TSocket> pSocket(new TSocket("localhost", getServerPort()), autoSocketCloser);
+ shared_ptr<TProtocol> pProtocol(new TBinaryProtocol(pSocket));
+ ParentServiceClient client(pProtocol);
+ pSocket->open();
+ bStressConnectionCount.fetch_add(1, std::memory_order_relaxed);
+ for (int i = 0; i < rand() % 1000; ++i) {
+ client.incrementGeneration();
+ bStressRequestCount.fetch_add(1, std::memory_order_relaxed);
+ }
+ }
+ }
+
+ shared_ptr<TServerType> pServer;
+ shared_ptr<TServerReadyEventHandler> pEventHandler;
+ shared_ptr<boost::thread> pServerThread;
+ std::atomic<bool> bStressDone;
+ std::atomic<int64_t> bStressConnectionCount;
+ std::atomic<int64_t> bStressRequestCount;
+};
+
+template <class TServerType>
+class TServerIntegrationProcessorFactoryTestFixture
+ : public TServerIntegrationTestFixture<TServerType> {
+public:
+ TServerIntegrationProcessorFactoryTestFixture()
+ : TServerIntegrationTestFixture<TServerType>(make_shared<ParentServiceProcessorFactory>(
+ make_shared<ParentServiceIfSingletonFactory>(
+ make_shared<ParentHandler>()))) {}
+};
+
+template <class TServerType>
+class TServerIntegrationProcessorTestFixture : public TServerIntegrationTestFixture<TServerType> {
+public:
+ TServerIntegrationProcessorTestFixture()
+ : TServerIntegrationTestFixture<TServerType>(
+ make_shared<ParentServiceProcessor>(make_shared<ParentHandler>())) {}
+};
+
+BOOST_AUTO_TEST_SUITE(constructors)
+
+BOOST_FIXTURE_TEST_CASE(test_simple_factory,
+ TServerIntegrationProcessorFactoryTestFixture<TSimpleServer>) {
+ baseline(3, 1, "factory");
+}
+
+BOOST_FIXTURE_TEST_CASE(test_simple, TServerIntegrationProcessorTestFixture<TSimpleServer>) {
+ baseline(3, 1, "processor");
+}
+
+BOOST_FIXTURE_TEST_CASE(test_threaded_factory,
+ TServerIntegrationProcessorFactoryTestFixture<TThreadedServer>) {
+ baseline(10, 10, "factory");
+}
+
+BOOST_FIXTURE_TEST_CASE(test_threaded, TServerIntegrationProcessorTestFixture<TThreadedServer>) {
+ baseline(10, 10, "processor");
+}
+
+BOOST_FIXTURE_TEST_CASE(test_threaded_bound,
+ TServerIntegrationProcessorTestFixture<TThreadedServer>) {
+ pServer->setConcurrentClientLimit(4);
+ baseline(10, 4, "limit by server framework");
+}
+
+BOOST_FIXTURE_TEST_CASE(test_threaded_stress,
+ TServerIntegrationProcessorFactoryTestFixture<TThreadedServer>) {
+ stress(10, boost::posix_time::seconds(3));
+}
+
+BOOST_FIXTURE_TEST_CASE(test_threadpool_factory,
+ TServerIntegrationProcessorFactoryTestFixture<TThreadPoolServer>) {
+ pServer->getThreadManager()->threadFactory(
+ shared_ptr<apache::thrift::concurrency::ThreadFactory>(
+ new apache::thrift::concurrency::ThreadFactory));
+ pServer->getThreadManager()->start();
+
+ // thread factory has 4 threads as a default
+ // thread factory however is a bad way to limit concurrent clients
+ // as accept() will be called to grab a 5th client socket, in this case
+ // and then the thread factory will block adding the thread to manage
+ // that client.
+ baseline(10, 5, "limit by thread manager");
+}
+
+BOOST_FIXTURE_TEST_CASE(test_threadpool,
+ TServerIntegrationProcessorTestFixture<TThreadPoolServer>) {
+ pServer->getThreadManager()->threadFactory(
+ shared_ptr<apache::thrift::concurrency::ThreadFactory>(
+ new apache::thrift::concurrency::ThreadFactory));
+ pServer->getThreadManager()->start();
+
+ // thread factory has 4 threads as a default
+ // thread factory however is a bad way to limit concurrent clients
+ // as accept() will be called to grab a 5th client socket, in this case
+ // and then the thread factory will block adding the thread to manage
+ // that client.
+ baseline(10, 5, "limit by thread manager");
+}
+
+BOOST_FIXTURE_TEST_CASE(test_threadpool_bound,
+ TServerIntegrationProcessorTestFixture<TThreadPoolServer>) {
+ pServer->getThreadManager()->threadFactory(
+ shared_ptr<apache::thrift::concurrency::ThreadFactory>(
+ new apache::thrift::concurrency::ThreadFactory));
+ pServer->getThreadManager()->start();
+ pServer->setConcurrentClientLimit(4);
+
+ baseline(10, 4, "server framework connection limit");
+}
+
+BOOST_FIXTURE_TEST_CASE(test_threadpool_stress,
+ TServerIntegrationProcessorTestFixture<TThreadPoolServer>) {
+ pServer->getThreadManager()->threadFactory(
+ shared_ptr<apache::thrift::concurrency::ThreadFactory>(
+ new apache::thrift::concurrency::ThreadFactory));
+ pServer->getThreadManager()->start();
+
+ stress(10, boost::posix_time::seconds(3));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_FIXTURE_TEST_SUITE(TServerIntegrationTest,
+ TServerIntegrationProcessorTestFixture<TThreadedServer>)
+
+BOOST_AUTO_TEST_CASE(test_stop_with_interruptable_clients_connected) {
+ // This tests THRIFT-2441 new behavior: stopping the server disconnects clients
+ BOOST_TEST_MESSAGE("Testing stop with interruptable clients");
+
+ startServer();
+
+ shared_ptr<TSocket> pClientSock1(new TSocket("localhost", getServerPort()),
+ autoSocketCloser);
+ pClientSock1->open();
+
+ shared_ptr<TSocket> pClientSock2(new TSocket("localhost", getServerPort()),
+ autoSocketCloser);
+ pClientSock2->open();
+
+ // Ensure they have been accepted
+ blockUntilAccepted(2);
+
+ // The test fixture destructor will force the sockets to disconnect
+ // Prior to THRIFT-2441, pServer->stop() would hang until clients disconnected
+ stopServer();
+
+ // extra proof the server end disconnected the clients
+ uint8_t buf[1];
+ BOOST_CHECK_EQUAL(0, pClientSock1->read(&buf[0], 1)); // 0 = disconnected
+ BOOST_CHECK_EQUAL(0, pClientSock2->read(&buf[0], 1)); // 0 = disconnected
+}
+
+BOOST_AUTO_TEST_CASE(test_stop_with_uninterruptable_clients_connected) {
+ // This tests pre-THRIFT-2441 behavior: stopping the server blocks until clients
+ // disconnect.
+ BOOST_TEST_MESSAGE("Testing stop with uninterruptable clients");
+
+ dynamic_pointer_cast<TServerSocket>(pServer->getServerTransport())
+ ->setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior
+
+ startServer();
+
+ shared_ptr<TSocket> pClientSock1(new TSocket("localhost", getServerPort()),
+ autoSocketCloser);
+ pClientSock1->open();
+
+ shared_ptr<TSocket> pClientSock2(new TSocket("localhost", getServerPort()),
+ autoSocketCloser);
+ pClientSock2->open();
+
+ // Ensure they have been accepted
+ blockUntilAccepted(2);
+
+ boost::thread t1(std::bind(&TServerIntegrationTestFixture::delayClose,
+ this,
+ pClientSock1,
+ milliseconds(250)));
+ boost::thread t2(std::bind(&TServerIntegrationTestFixture::delayClose,
+ this,
+ pClientSock2,
+ milliseconds(250)));
+
+ // Once the clients disconnect the server will stop
+ stopServer();
+ BOOST_CHECK(pServer->getConcurrentClientCountHWM() > 0);
+ t1.join();
+ t2.join();
+}
+
+BOOST_AUTO_TEST_CASE(test_concurrent_client_limit) {
+ startServer();
+ BOOST_TEST_MESSAGE("Testing the concurrent client limit");
+
+ BOOST_CHECK_EQUAL(INT64_MAX, pServer->getConcurrentClientLimit());
+ pServer->setConcurrentClientLimit(2);
+ BOOST_CHECK_EQUAL(0, pServer->getConcurrentClientCount());
+ BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientLimit());
+
+ shared_ptr<TSocket> pClientSock1(new TSocket("localhost", getServerPort()),
+ autoSocketCloser);
+ pClientSock1->open();
+ blockUntilAccepted(1);
+ BOOST_CHECK_EQUAL(1, pServer->getConcurrentClientCount());
+
+ shared_ptr<TSocket> pClientSock2(new TSocket("localhost", getServerPort()),
+ autoSocketCloser);
+ pClientSock2->open();
+ blockUntilAccepted(2);
+ BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientCount());
+
+ // a third client cannot connect until one of the other two closes
+ boost::thread t2(std::bind(&TServerIntegrationTestFixture::delayClose,
+ this,
+ pClientSock2,
+ milliseconds(250)));
+ shared_ptr<TSocket> pClientSock3(new TSocket("localhost", getServerPort()),
+ autoSocketCloser);
+ pClientSock2->open();
+ blockUntilAccepted(2);
+ BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientCount());
+ BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientCountHWM());
+
+ stopServer();
+ BOOST_CHECK(pServer->getConcurrentClientCountHWM() > 0);
+ t2.join();
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TServerSocketTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TServerSocketTest.cpp
new file mode 100644
index 000000000..bec6d4756
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TServerSocketTest.cpp
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <boost/test/auto_unit_test.hpp>
+#include <thrift/transport/TSocket.h>
+#include <thrift/transport/TServerSocket.h>
+#include <memory>
+#include "TTransportCheckThrow.h"
+#include <iostream>
+
+using apache::thrift::transport::TServerSocket;
+using apache::thrift::transport::TSocket;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using std::shared_ptr;
+
+BOOST_AUTO_TEST_SUITE(TServerSocketTest)
+
+BOOST_AUTO_TEST_CASE(test_bind_to_address) {
+ TServerSocket sock1("localhost", 0);
+ sock1.listen();
+ int port = sock1.getPort();
+ TSocket clientSock("localhost", port);
+ clientSock.open();
+ shared_ptr<TTransport> accepted = sock1.accept();
+ accepted->close();
+ sock1.close();
+
+ std::cout << "An error message from getaddrinfo on the console is expected:" << std::endl;
+ TServerSocket sock2("257.258.259.260", 0);
+ BOOST_CHECK_THROW(sock2.listen(), TTransportException);
+ sock2.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_listen_valid_port) {
+ TServerSocket sock1(-1);
+ TTRANSPORT_CHECK_THROW(sock1.listen(), TTransportException::BAD_ARGS);
+
+ TServerSocket sock2(65536);
+ TTRANSPORT_CHECK_THROW(sock2.listen(), TTransportException::BAD_ARGS);
+}
+
+BOOST_AUTO_TEST_CASE(test_close_before_listen) {
+ TServerSocket sock1("localhost", 0);
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_get_port) {
+ TServerSocket sock1("localHost", 888);
+ BOOST_CHECK_EQUAL(888, sock1.getPort());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TServerTransportTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TServerTransportTest.cpp
new file mode 100644
index 000000000..18a393ee0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TServerTransportTest.cpp
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <boost/test/auto_unit_test.hpp>
+#include <thrift/transport/TSocket.h>
+#include <thrift/transport/TServerTransport.h>
+#include <memory>
+
+using apache::thrift::transport::TServerTransport;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using std::shared_ptr;
+
+BOOST_AUTO_TEST_SUITE(TServerTransportTest)
+
+class TestTTransport : public TTransport {};
+
+class TestTServerTransport : public TServerTransport {
+public:
+ TestTServerTransport() : valid_(true) {}
+ void close() override {}
+ bool valid_;
+
+protected:
+ shared_ptr<TTransport> acceptImpl() override {
+ return valid_ ? std::make_shared<TestTTransport>()
+ : shared_ptr<TestTTransport>();
+ }
+};
+
+BOOST_AUTO_TEST_CASE(test_positive_accept) {
+ TestTServerTransport uut;
+ BOOST_CHECK(uut.accept());
+}
+
+BOOST_AUTO_TEST_CASE(test_negative_accept) {
+ TestTServerTransport uut;
+ uut.valid_ = false;
+ BOOST_CHECK_THROW(uut.accept(), TTransportException);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TSocketInterruptTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TSocketInterruptTest.cpp
new file mode 100644
index 000000000..366242f7c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TSocketInterruptTest.cpp
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define BOOST_TEST_MODULE TSocketInterruptTest
+#include <boost/test/auto_unit_test.hpp>
+
+#include <boost/chrono/duration.hpp>
+#include <boost/date_time/posix_time/posix_time_duration.hpp>
+#include <boost/thread/thread.hpp>
+#include <thrift/transport/TSocket.h>
+#include <thrift/transport/TServerSocket.h>
+#include <memory>
+
+using apache::thrift::transport::TServerSocket;
+using apache::thrift::transport::TSocket;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using namespace apache::thrift;
+
+BOOST_AUTO_TEST_SUITE(TSocketInterruptTest)
+
+void readerWorker(std::shared_ptr<TTransport> tt, uint32_t expectedResult) {
+ uint8_t buf[4];
+ BOOST_CHECK_EQUAL(expectedResult, tt->read(buf, 4));
+}
+
+void readerWorkerMustThrow(std::shared_ptr<TTransport> tt) {
+ try {
+ uint8_t buf[4];
+ tt->read(buf, 4);
+ BOOST_ERROR("should not have gotten here");
+ } catch (const TTransportException& tx) {
+ BOOST_CHECK_EQUAL(TTransportException::INTERRUPTED, tx.getType());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_interruptable_child_read) {
+ TServerSocket sock1("localhost", 0);
+ sock1.listen();
+ int port = sock1.getPort();
+ TSocket clientSock("localhost", port);
+ clientSock.open();
+ std::shared_ptr<TTransport> accepted = sock1.accept();
+ boost::thread readThread(std::bind(readerWorkerMustThrow, accepted));
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // readThread is practically guaranteed to be blocking now
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(readThread.try_join_for(boost::chrono::milliseconds(200)),
+ "server socket interruptChildren did not interrupt child read");
+ clientSock.close();
+ accepted->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_non_interruptable_child_read) {
+ TServerSocket sock1("localhost", 0);
+ sock1.setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior
+ sock1.listen();
+ int port = sock1.getPort();
+ TSocket clientSock("localhost", port);
+ clientSock.open();
+ std::shared_ptr<TTransport> accepted = sock1.accept();
+ boost::thread readThread(std::bind(readerWorker, accepted, 0));
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // readThread is practically guaranteed to be blocking here
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(!readThread.try_join_for(boost::chrono::milliseconds(200)),
+ "server socket interruptChildren interrupted child read");
+
+ // only way to proceed is to have the client disconnect
+ clientSock.close();
+ readThread.join();
+ accepted->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_cannot_change_after_listen) {
+ TServerSocket sock1("localhost", 0);
+ sock1.listen();
+ BOOST_CHECK_THROW(sock1.setInterruptableChildren(false), std::logic_error);
+ sock1.close();
+}
+
+void peekerWorker(std::shared_ptr<TTransport> tt, bool expectedResult) {
+ BOOST_CHECK_EQUAL(expectedResult, tt->peek());
+}
+
+BOOST_AUTO_TEST_CASE(test_interruptable_child_peek) {
+ TServerSocket sock1("localhost", 0);
+ sock1.listen();
+ int port = sock1.getPort();
+ TSocket clientSock("localhost", port);
+ clientSock.open();
+ std::shared_ptr<TTransport> accepted = sock1.accept();
+ // peek() will return false if child is interrupted
+ boost::thread peekThread(std::bind(peekerWorker, accepted, false));
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // peekThread is practically guaranteed to be blocking now
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(peekThread.try_join_for(boost::chrono::milliseconds(200)),
+ "server socket interruptChildren did not interrupt child peek");
+ clientSock.close();
+ accepted->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_non_interruptable_child_peek) {
+ TServerSocket sock1("localhost", 0);
+ sock1.setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior
+ sock1.listen();
+ int port = sock1.getPort();
+ TSocket clientSock("localhost", port);
+ clientSock.open();
+ std::shared_ptr<TTransport> accepted = sock1.accept();
+ // peek() will return false when remote side is closed
+ boost::thread peekThread(std::bind(peekerWorker, accepted, false));
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // peekThread is practically guaranteed to be blocking now
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(!peekThread.try_join_for(boost::chrono::milliseconds(200)),
+ "server socket interruptChildren interrupted child peek");
+
+ // only way to proceed is to have the client disconnect
+ clientSock.close();
+ peekThread.join();
+ accepted->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TTransportCheckThrow.h b/src/jaegertracing/thrift/lib/cpp/test/TTransportCheckThrow.h
new file mode 100644
index 000000000..92277b480
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TTransportCheckThrow.h
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#define TTRANSPORT_CHECK_THROW(_CALL, _TYPE) \
+ { \
+ bool caught = false; \
+ try { \
+ (_CALL); \
+ } catch (TTransportException & ex) { \
+ BOOST_CHECK_EQUAL(ex.getType(), _TYPE); \
+ caught = true; \
+ } \
+ BOOST_CHECK_MESSAGE(caught, "expected TTransportException but nothing was thrown"); \
+ }
+
+#define TTRANSPORT_REQUIRE_THROW(_CALL, _TYPE) \
+ { \
+ bool caught = false; \
+ try { \
+ (_CALL); \
+ } catch (TTransportException & ex) { \
+ BOOST_REQUIRE_EQUAL(ex.getType(), _TYPE); \
+ caught = true; \
+ } \
+ BOOST_REQUIRE_MESSAGE(caught, "expected TTransportException but nothing was thrown"); \
+ }
diff --git a/src/jaegertracing/thrift/lib/cpp/test/ThriftTest_extras.cpp b/src/jaegertracing/thrift/lib/cpp/test/ThriftTest_extras.cpp
new file mode 100644
index 000000000..af5606efb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/ThriftTest_extras.cpp
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Extra functions required for ThriftTest_types to work
+
+#include <thrift/protocol/TDebugProtocol.h>
+#include "gen-cpp/ThriftTest_types.h"
+
+namespace thrift {
+namespace test {
+
+bool Insanity::operator<(thrift::test::Insanity const& other) const {
+ using apache::thrift::ThriftDebugString;
+ return ThriftDebugString(*this) < ThriftDebugString(other);
+}
+}
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/ToStringTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/ToStringTest.cpp
new file mode 100644
index 000000000..d204cb346
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/ToStringTest.cpp
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <vector>
+#include <map>
+
+#include <boost/test/auto_unit_test.hpp>
+
+#include <thrift/TToString.h>
+
+#include "gen-cpp/ThriftTest_types.h"
+#include "gen-cpp/OptionalRequiredTest_types.h"
+#include "gen-cpp/DebugProtoTest_types.h"
+
+using apache::thrift::to_string;
+
+BOOST_AUTO_TEST_SUITE(ToStringTest)
+
+BOOST_AUTO_TEST_CASE(base_types_to_string) {
+ BOOST_CHECK_EQUAL(to_string(10), "10");
+ BOOST_CHECK_EQUAL(to_string(true), "1");
+ BOOST_CHECK_EQUAL(to_string('a'), "a");
+ BOOST_CHECK_EQUAL(to_string(1.2), "1.2");
+ BOOST_CHECK_EQUAL(to_string("abc"), "abc");
+}
+
+BOOST_AUTO_TEST_CASE(empty_vector_to_string) {
+ std::vector<int> l;
+ BOOST_CHECK_EQUAL(to_string(l), "[]");
+}
+
+BOOST_AUTO_TEST_CASE(single_item_vector_to_string) {
+ std::vector<int> l;
+ l.push_back(100);
+ BOOST_CHECK_EQUAL(to_string(l), "[100]");
+}
+
+BOOST_AUTO_TEST_CASE(multiple_item_vector_to_string) {
+ std::vector<int> l;
+ l.push_back(100);
+ l.push_back(150);
+ BOOST_CHECK_EQUAL(to_string(l), "[100, 150]");
+}
+
+BOOST_AUTO_TEST_CASE(empty_map_to_string) {
+ std::map<int, std::string> m;
+ BOOST_CHECK_EQUAL(to_string(m), "{}");
+}
+
+BOOST_AUTO_TEST_CASE(single_item_map_to_string) {
+ std::map<int, std::string> m;
+ m[12] = "abc";
+ BOOST_CHECK_EQUAL(to_string(m), "{12: abc}");
+}
+
+BOOST_AUTO_TEST_CASE(multi_item_map_to_string) {
+ std::map<int, std::string> m;
+ m[12] = "abc";
+ m[31] = "xyz";
+ BOOST_CHECK_EQUAL(to_string(m), "{12: abc, 31: xyz}");
+}
+
+BOOST_AUTO_TEST_CASE(empty_set_to_string) {
+ std::set<char> s;
+ BOOST_CHECK_EQUAL(to_string(s), "{}");
+}
+
+BOOST_AUTO_TEST_CASE(single_item_set_to_string) {
+ std::set<char> s;
+ s.insert('c');
+ BOOST_CHECK_EQUAL(to_string(s), "{c}");
+}
+
+BOOST_AUTO_TEST_CASE(multi_item_set_to_string) {
+ std::set<char> s;
+ s.insert('a');
+ s.insert('z');
+ BOOST_CHECK_EQUAL(to_string(s), "{a, z}");
+}
+
+BOOST_AUTO_TEST_CASE(generated_empty_object_to_string) {
+ thrift::test::EmptyStruct e;
+ BOOST_CHECK_EQUAL(to_string(e), "EmptyStruct()");
+}
+
+BOOST_AUTO_TEST_CASE(generated_single_basic_field_object_to_string) {
+ thrift::test::StructA a;
+ a.__set_s("abcd");
+ BOOST_CHECK_EQUAL(to_string(a), "StructA(s=abcd)");
+}
+
+BOOST_AUTO_TEST_CASE(generated_two_basic_fields_object_to_string) {
+ thrift::test::Bonk a;
+ a.__set_message("abcd");
+ a.__set_type(1234);
+ BOOST_CHECK_EQUAL(to_string(a), "Bonk(message=abcd, type=1234)");
+}
+
+BOOST_AUTO_TEST_CASE(generated_optional_fields_object_to_string) {
+ thrift::test::Tricky2 a;
+ BOOST_CHECK_EQUAL(to_string(a), "Tricky2(im_optional=<null>)");
+ a.__set_im_optional(123);
+ BOOST_CHECK_EQUAL(to_string(a), "Tricky2(im_optional=123)");
+}
+
+BOOST_AUTO_TEST_CASE(generated_nested_object_to_string) {
+ thrift::test::OneField a;
+ BOOST_CHECK_EQUAL(to_string(a), "OneField(field=EmptyStruct())");
+}
+
+BOOST_AUTO_TEST_CASE(generated_nested_list_object_to_string) {
+ thrift::test::ListBonks l;
+ l.bonk.assign(2, thrift::test::Bonk());
+ l.bonk[0].__set_message("a");
+ l.bonk[1].__set_message("b");
+
+ BOOST_CHECK_EQUAL(to_string(l),
+ "ListBonks(bonk=[Bonk(message=a, type=0), Bonk(message=b, type=0)])");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TransportTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TransportTest.cpp
new file mode 100644
index 000000000..a890aa8ce
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TransportTest.cpp
@@ -0,0 +1,1089 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+
+#include <stdlib.h>
+#include <time.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <sstream>
+#include <fstream>
+
+#include <boost/mpl/list.hpp>
+#include <boost/shared_array.hpp>
+#include <boost/random.hpp>
+#include <boost/type_traits.hpp>
+#include <boost/test/unit_test.hpp>
+#include <boost/version.hpp>
+
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/transport/TFDTransport.h>
+#include <thrift/transport/TFileTransport.h>
+#include <thrift/transport/TZlibTransport.h>
+#include <thrift/transport/TSocket.h>
+
+#include <thrift/concurrency/FunctionRunner.h>
+#if _WIN32
+#include <thrift/transport/TPipe.h>
+#include <thrift/windows/TWinsockSingleton.h>
+#endif
+
+using namespace apache::thrift::transport;
+using namespace apache::thrift;
+
+static boost::mt19937 rng;
+
+void initrand(unsigned int seed) {
+ rng.seed(seed);
+}
+
+class SizeGenerator {
+public:
+ virtual ~SizeGenerator() = default;
+ virtual uint32_t nextSize() = 0;
+ virtual std::string describe() const = 0;
+};
+
+class ConstantSizeGenerator : public SizeGenerator {
+public:
+ ConstantSizeGenerator(uint32_t value) : value_(value) {}
+ uint32_t nextSize() override { return value_; }
+ std::string describe() const override {
+ std::ostringstream desc;
+ desc << value_;
+ return desc.str();
+ }
+
+private:
+ uint32_t value_;
+};
+
+class RandomSizeGenerator : public SizeGenerator {
+public:
+ RandomSizeGenerator(uint32_t min, uint32_t max)
+ : generator_(rng, boost::uniform_int<int>(min, max)) {}
+
+ uint32_t nextSize() override { return generator_(); }
+
+ std::string describe() const override {
+ std::ostringstream desc;
+ desc << "rand(" << getMin() << ", " << getMax() << ")";
+ return desc.str();
+ }
+
+ uint32_t getMin() const { return (generator_.distribution().min)(); }
+ uint32_t getMax() const { return (generator_.distribution().max)(); }
+
+private:
+ boost::variate_generator<boost::mt19937&, boost::uniform_int<int> > generator_;
+};
+
+/**
+ * This class exists solely to make the TEST_RW() macro easier to use.
+ * - it can be constructed implicitly from an integer
+ * - it can contain either a ConstantSizeGenerator or a RandomSizeGenerator
+ * (TEST_RW can't take a SizeGenerator pointer or reference, since it needs
+ * to make a copy of the generator to bind it to the test function.)
+ */
+class GenericSizeGenerator : public SizeGenerator {
+public:
+ GenericSizeGenerator(uint32_t value) : generator_(new ConstantSizeGenerator(value)) {}
+ GenericSizeGenerator(uint32_t min, uint32_t max)
+ : generator_(new RandomSizeGenerator(min, max)) {}
+
+ uint32_t nextSize() override { return generator_->nextSize(); }
+ std::string describe() const override { return generator_->describe(); }
+
+private:
+ std::shared_ptr<SizeGenerator> generator_;
+};
+
+/**************************************************************************
+ * Classes to set up coupled transports
+ **************************************************************************/
+
+/**
+ * Helper class to represent a coupled pair of transports.
+ *
+ * Data written to the out transport can be read from the in transport.
+ *
+ * This is used as the base class for the various coupled transport
+ * implementations. It shouldn't be instantiated directly.
+ */
+template <class Transport_>
+class CoupledTransports {
+public:
+ virtual ~CoupledTransports() = default;
+ typedef Transport_ TransportType;
+
+ CoupledTransports() : in(), out() {}
+
+ std::shared_ptr<Transport_> in;
+ std::shared_ptr<Transport_> out;
+
+private:
+ CoupledTransports(const CoupledTransports&) = delete;
+ CoupledTransports& operator=(const CoupledTransports&) = delete;
+};
+
+/**
+ * Coupled TMemoryBuffers
+ */
+class CoupledMemoryBuffers : public CoupledTransports<TMemoryBuffer> {
+public:
+ CoupledMemoryBuffers() : buf(new TMemoryBuffer) {
+ in = buf;
+ out = buf;
+ }
+
+ std::shared_ptr<TMemoryBuffer> buf;
+};
+
+/**
+ * Helper template class for creating coupled transports that wrap
+ * another transport.
+ */
+template <class WrapperTransport_, class InnerCoupledTransports_>
+class CoupledWrapperTransportsT : public CoupledTransports<WrapperTransport_> {
+public:
+ CoupledWrapperTransportsT() {
+ if (inner_.in) {
+ this->in.reset(new WrapperTransport_(inner_.in));
+ }
+ if (inner_.out) {
+ this->out.reset(new WrapperTransport_(inner_.out));
+ }
+ }
+
+ InnerCoupledTransports_ inner_;
+};
+
+/**
+ * Coupled TBufferedTransports.
+ */
+template <class InnerTransport_>
+class CoupledBufferedTransportsT
+ : public CoupledWrapperTransportsT<TBufferedTransport, InnerTransport_> {};
+
+typedef CoupledBufferedTransportsT<CoupledMemoryBuffers> CoupledBufferedTransports;
+
+/**
+ * Coupled TFramedTransports.
+ */
+template <class InnerTransport_>
+class CoupledFramedTransportsT
+ : public CoupledWrapperTransportsT<TFramedTransport, InnerTransport_> {};
+
+typedef CoupledFramedTransportsT<CoupledMemoryBuffers> CoupledFramedTransports;
+
+/**
+ * Coupled TZlibTransports.
+ */
+template <class InnerTransport_>
+class CoupledZlibTransportsT : public CoupledWrapperTransportsT<TZlibTransport, InnerTransport_> {};
+
+typedef CoupledZlibTransportsT<CoupledMemoryBuffers> CoupledZlibTransports;
+
+#ifndef _WIN32
+// FD transport doesn't make much sense on Windows.
+/**
+ * Coupled TFDTransports.
+ */
+class CoupledFDTransports : public CoupledTransports<TFDTransport> {
+public:
+ CoupledFDTransports() {
+ int pipes[2];
+
+ if (pipe(pipes) != 0) {
+ return;
+ }
+
+ in.reset(new TFDTransport(pipes[0], TFDTransport::CLOSE_ON_DESTROY));
+ out.reset(new TFDTransport(pipes[1], TFDTransport::CLOSE_ON_DESTROY));
+ }
+};
+#else
+/**
+ * Coupled pipe transports
+ */
+class CoupledPipeTransports : public CoupledTransports<TPipe> {
+public:
+ HANDLE hRead;
+ HANDLE hWrite;
+
+ CoupledPipeTransports() {
+ BOOST_REQUIRE(CreatePipe(&hRead, &hWrite, NULL, 1048576 * 2));
+ in.reset(new TPipe(hRead, hWrite));
+ in->open();
+ out = in;
+ }
+};
+#endif
+
+/**
+ * Coupled TSockets
+ */
+class CoupledSocketTransports : public CoupledTransports<TSocket> {
+public:
+ CoupledSocketTransports() {
+ THRIFT_SOCKET sockets[2] = {0};
+ if (THRIFT_SOCKETPAIR(PF_UNIX, SOCK_STREAM, 0, sockets) != 0) {
+ return;
+ }
+
+ in.reset(new TSocket(sockets[0]));
+ out.reset(new TSocket(sockets[1]));
+ out->setSendTimeout(100);
+ }
+};
+
+// These could be made to work on Windows, but I don't care enough to make it happen
+#ifndef _WIN32
+/**
+ * Coupled TFileTransports
+ */
+class CoupledFileTransports : public CoupledTransports<TFileTransport> {
+public:
+ CoupledFileTransports() {
+#ifndef _WIN32
+ const char* tmp_dir = "/tmp";
+#define FILENAME_SUFFIX "/thrift.transport_test"
+#else
+ const char* tmp_dir = getenv("TMP");
+#define FILENAME_SUFFIX "\\thrift.transport_test"
+#endif
+
+ // Create a temporary file to use
+ filename.resize(strlen(tmp_dir) + strlen(FILENAME_SUFFIX));
+ THRIFT_SNPRINTF(&filename[0], filename.size(), "%s" FILENAME_SUFFIX, tmp_dir);
+#undef FILENAME_SUFFIX
+
+ { std::ofstream dummy_creation(filename.c_str(), std::ofstream::trunc); }
+
+ in.reset(new TFileTransport(filename, true));
+ out.reset(new TFileTransport(filename));
+ }
+
+ ~CoupledFileTransports() override { remove(filename.c_str()); }
+
+ std::string filename;
+};
+#endif
+
+/**
+ * Wrapper around another CoupledTransports implementation that exposes the
+ * transports as TTransport pointers.
+ *
+ * This is used since accessing a transport via a "TTransport*" exercises a
+ * different code path than using the base pointer class. As part of the
+ * template code changes, most transport methods are no longer virtual.
+ */
+template <class CoupledTransports_>
+class CoupledTTransports : public CoupledTransports<TTransport> {
+public:
+ CoupledTTransports() : transports() {
+ in = transports.in;
+ out = transports.out;
+ }
+
+ CoupledTransports_ transports;
+};
+
+/**
+ * Wrapper around another CoupledTransports implementation that exposes the
+ * transports as TBufferBase pointers.
+ *
+ * This can only be instantiated with a transport type that is a subclass of
+ * TBufferBase.
+ */
+template <class CoupledTransports_>
+class CoupledBufferBases : public CoupledTransports<TBufferBase> {
+public:
+ CoupledBufferBases() : transports() {
+ in = transports.in;
+ out = transports.out;
+ }
+
+ CoupledTransports_ transports;
+};
+
+/**************************************************************************
+ * Alarm handling code for use in tests that check the transport blocking
+ * semantics.
+ *
+ * If the transport ends up blocking, we don't want to hang forever. We use
+ * SIGALRM to fire schedule signal to wake up and try to write data so the
+ * transport will unblock.
+ *
+ * It isn't really the safest thing in the world to be mucking around with
+ * complicated global data structures in a signal handler. It should probably
+ * be okay though, since we know the main thread should always be blocked in a
+ * read() request when the signal handler is running.
+ **************************************************************************/
+
+struct TriggerInfo {
+ TriggerInfo(int seconds, const std::shared_ptr<TTransport>& transport, uint32_t writeLength)
+ : timeoutSeconds(seconds), transport(transport), writeLength(writeLength), next(nullptr) {}
+
+ int timeoutSeconds;
+ std::shared_ptr<TTransport> transport;
+ uint32_t writeLength;
+ TriggerInfo* next;
+};
+
+apache::thrift::concurrency::Monitor g_alarm_monitor;
+TriggerInfo* g_triggerInfo;
+unsigned int g_numTriggersFired;
+bool g_teardown = false;
+
+void alarm_handler() {
+ TriggerInfo* info = nullptr;
+ {
+ apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
+ // The alarm timed out, which almost certainly means we're stuck
+ // on a transport that is incorrectly blocked.
+ ++g_numTriggersFired;
+
+ // Note: we print messages to stdout instead of stderr, since
+ // tools/test/runner only records stdout messages in the failure messages for
+ // boost tests. (boost prints its test info to stdout.)
+ printf("Timeout alarm expired; attempting to unblock transport\n");
+ if (g_triggerInfo == nullptr) {
+ printf(" trigger stack is empty!\n");
+ }
+
+ // Pop off the first TriggerInfo.
+ // If there is another one, schedule an alarm for it.
+ info = g_triggerInfo;
+ g_triggerInfo = info->next;
+ }
+
+ // Write some data to the transport to hopefully unblock it.
+ auto* buf = new uint8_t[info->writeLength];
+ memset(buf, 'b', info->writeLength);
+ boost::scoped_array<uint8_t> array(buf);
+ info->transport->write(buf, info->writeLength);
+ info->transport->flush();
+
+ delete info;
+}
+
+void alarm_handler_wrapper() {
+ int64_t timeout = 0; // timeout of 0 means wait forever
+ while (true) {
+ bool fireHandler = false;
+ {
+ apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
+ if (g_teardown)
+ return;
+ // calculate timeout
+ if (g_triggerInfo == nullptr) {
+ timeout = 0;
+ } else {
+ timeout = g_triggerInfo->timeoutSeconds * 1000;
+ }
+
+ int waitResult = g_alarm_monitor.waitForTimeRelative(timeout);
+ if (waitResult == THRIFT_ETIMEDOUT)
+ fireHandler = true;
+ }
+ if (fireHandler)
+ alarm_handler(); // calling outside the lock
+ }
+}
+
+/**
+ * Add a trigger to be scheduled "seconds" seconds after the
+ * last currently scheduled trigger.
+ *
+ * (Note that this is not "seconds" from now. That might be more logical, but
+ * would require slightly more complicated sorting, rather than just appending
+ * to the end.)
+ */
+void add_trigger(unsigned int seconds,
+ const std::shared_ptr<TTransport>& transport,
+ uint32_t write_len) {
+ auto* info = new TriggerInfo(seconds, transport, write_len);
+ {
+ apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
+ if (g_triggerInfo == nullptr) {
+ // This is the first trigger.
+ // Set g_triggerInfo, and schedule the alarm
+ g_triggerInfo = info;
+ g_alarm_monitor.notify();
+ } else {
+ // Add this trigger to the end of the list
+ TriggerInfo* prev = g_triggerInfo;
+ while (prev->next) {
+ prev = prev->next;
+ }
+ prev->next = info;
+ }
+ }
+}
+
+void clear_triggers() {
+ TriggerInfo* info = nullptr;
+
+ {
+ apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
+ info = g_triggerInfo;
+ g_triggerInfo = nullptr;
+ g_numTriggersFired = 0;
+ g_alarm_monitor.notify();
+ }
+
+ while (info != nullptr) {
+ TriggerInfo* next = info->next;
+ delete info;
+ info = next;
+ }
+}
+
+void set_trigger(unsigned int seconds,
+ const std::shared_ptr<TTransport>& transport,
+ uint32_t write_len) {
+ clear_triggers();
+ add_trigger(seconds, transport, write_len);
+}
+
+/**************************************************************************
+ * Test functions
+ **************************************************************************/
+
+/**
+ * Test interleaved write and read calls.
+ *
+ * Generates a buffer totalSize bytes long, then writes it to the transport,
+ * and verifies the written data can be read back correctly.
+ *
+ * Mode of operation:
+ * - call wChunkGenerator to figure out how large of a chunk to write
+ * - call wSizeGenerator to get the size for individual write() calls,
+ * and do this repeatedly until the entire chunk is written.
+ * - call rChunkGenerator to figure out how large of a chunk to read
+ * - call rSizeGenerator to get the size for individual read() calls,
+ * and do this repeatedly until the entire chunk is read.
+ * - repeat until the full buffer is written and read back,
+ * then compare the data read back against the original buffer
+ *
+ *
+ * - If any of the size generators return 0, this means to use the maximum
+ * possible size.
+ *
+ * - If maxOutstanding is non-zero, write chunk sizes will be chosen such that
+ * there are never more than maxOutstanding bytes waiting to be read back.
+ */
+template <class CoupledTransports>
+void test_rw(uint32_t totalSize,
+ SizeGenerator& wSizeGenerator,
+ SizeGenerator& rSizeGenerator,
+ SizeGenerator& wChunkGenerator,
+ SizeGenerator& rChunkGenerator,
+ uint32_t maxOutstanding) {
+ CoupledTransports transports;
+ BOOST_REQUIRE(transports.in != nullptr);
+ BOOST_REQUIRE(transports.out != nullptr);
+
+ boost::shared_array<uint8_t> wbuf = boost::shared_array<uint8_t>(new uint8_t[totalSize]);
+ boost::shared_array<uint8_t> rbuf = boost::shared_array<uint8_t>(new uint8_t[totalSize]);
+
+ // store some data in wbuf
+ for (uint32_t n = 0; n < totalSize; ++n) {
+ wbuf[n] = (n & 0xff);
+ }
+ // clear rbuf
+ memset(rbuf.get(), 0, totalSize);
+
+ uint32_t total_written = 0;
+ uint32_t total_read = 0;
+ while (total_read < totalSize) {
+ // Determine how large a chunk of data to write
+ uint32_t wchunk_size = wChunkGenerator.nextSize();
+ if (wchunk_size == 0 || wchunk_size > totalSize - total_written) {
+ wchunk_size = totalSize - total_written;
+ }
+
+ // Make sure (total_written - total_read) + wchunk_size
+ // is less than maxOutstanding
+ if (maxOutstanding > 0 && wchunk_size > maxOutstanding - (total_written - total_read)) {
+ wchunk_size = maxOutstanding - (total_written - total_read);
+ }
+
+ // Write the chunk
+ uint32_t chunk_written = 0;
+ while (chunk_written < wchunk_size) {
+ uint32_t write_size = wSizeGenerator.nextSize();
+ if (write_size == 0 || write_size > wchunk_size - chunk_written) {
+ write_size = wchunk_size - chunk_written;
+ }
+
+ try {
+ transports.out->write(wbuf.get() + total_written, write_size);
+ } catch (TTransportException& te) {
+ if (te.getType() == TTransportException::TIMED_OUT)
+ break;
+ throw te;
+ }
+ chunk_written += write_size;
+ total_written += write_size;
+ }
+
+ // Flush the data, so it will be available in the read transport
+ // Don't flush if wchunk_size is 0. (This should only happen if
+ // total_written == totalSize already, and we're only reading now.)
+ if (wchunk_size > 0) {
+ transports.out->flush();
+ }
+
+ // Determine how large a chunk of data to read back
+ uint32_t rchunk_size = rChunkGenerator.nextSize();
+ if (rchunk_size == 0 || rchunk_size > total_written - total_read) {
+ rchunk_size = total_written - total_read;
+ }
+
+ // Read the chunk
+ uint32_t chunk_read = 0;
+ while (chunk_read < rchunk_size) {
+ uint32_t read_size = rSizeGenerator.nextSize();
+ if (read_size == 0 || read_size > rchunk_size - chunk_read) {
+ read_size = rchunk_size - chunk_read;
+ }
+
+ int bytes_read = -1;
+ try {
+ bytes_read = transports.in->read(rbuf.get() + total_read, read_size);
+ } catch (TTransportException& e) {
+ BOOST_FAIL("read(pos=" << total_read << ", size=" << read_size << ") threw exception \""
+ << e.what() << "\"; written so far: " << total_written << " / "
+ << totalSize << " bytes");
+ }
+
+ BOOST_REQUIRE_MESSAGE(bytes_read > 0,
+ "read(pos=" << total_read << ", size=" << read_size << ") returned "
+ << bytes_read << "; written so far: " << total_written
+ << " / " << totalSize << " bytes");
+ chunk_read += bytes_read;
+ total_read += bytes_read;
+ }
+ }
+
+ // make sure the data read back is identical to the data written
+ BOOST_CHECK_EQUAL(memcmp(rbuf.get(), wbuf.get(), totalSize), 0);
+}
+
+template <class CoupledTransports>
+void test_read_part_available() {
+ CoupledTransports transports;
+ BOOST_REQUIRE(transports.in != nullptr);
+ BOOST_REQUIRE(transports.out != nullptr);
+
+ uint8_t write_buf[16];
+ uint8_t read_buf[16];
+ memset(write_buf, 'a', sizeof(write_buf));
+
+ // Attemping to read 10 bytes when only 9 are available should return 9
+ // immediately.
+ transports.out->write(write_buf, 9);
+ transports.out->flush();
+ set_trigger(3, transports.out, 1);
+ uint32_t bytes_read = transports.in->read(read_buf, 10);
+ BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
+ BOOST_CHECK_EQUAL(bytes_read, (uint32_t)9);
+
+ clear_triggers();
+}
+
+template <class CoupledTransports>
+void test_read_part_available_in_chunks() {
+ CoupledTransports transports;
+ BOOST_REQUIRE(transports.in != nullptr);
+ BOOST_REQUIRE(transports.out != nullptr);
+
+ uint8_t write_buf[16];
+ uint8_t read_buf[16];
+ memset(write_buf, 'a', sizeof(write_buf));
+
+ // Write 10 bytes (in a single frame, for transports that use framing)
+ transports.out->write(write_buf, 10);
+ transports.out->flush();
+
+ // Read 1 byte, to force the transport to read the frame
+ uint32_t bytes_read = transports.in->read(read_buf, 1);
+ BOOST_CHECK_EQUAL(bytes_read, 1u);
+
+ // Read more than what is remaining and verify the transport does not block
+ set_trigger(3, transports.out, 1);
+ bytes_read = transports.in->read(read_buf, 10);
+ BOOST_CHECK_EQUAL(g_numTriggersFired, 0u);
+ BOOST_CHECK_EQUAL(bytes_read, 9u);
+
+ clear_triggers();
+}
+
+template <class CoupledTransports>
+void test_read_partial_midframe() {
+ CoupledTransports transports;
+ BOOST_REQUIRE(transports.in != nullptr);
+ BOOST_REQUIRE(transports.out != nullptr);
+
+ uint8_t write_buf[16];
+ uint8_t read_buf[16];
+ memset(write_buf, 'a', sizeof(write_buf));
+
+ // Attempt to read 10 bytes, when only 9 are available, but after we have
+ // already read part of the data that is available. This exercises a
+ // different code path for several of the transports.
+ //
+ // For transports that add their own framing (e.g., TFramedTransport and
+ // TFileTransport), the two flush calls break up the data in to a 10 byte
+ // frame and a 3 byte frame. The first read then puts us partway through the
+ // first frame, and then we attempt to read past the end of that frame, and
+ // through the next frame, too.
+ //
+ // For buffered transports that perform read-ahead (e.g.,
+ // TBufferedTransport), the read-ahead will most likely see all 13 bytes
+ // written on the first read. The next read will then attempt to read past
+ // the end of the read-ahead buffer.
+ //
+ // Flush 10 bytes, then 3 bytes. This creates 2 separate frames for
+ // transports that track framing internally.
+ transports.out->write(write_buf, 10);
+ transports.out->flush();
+ transports.out->write(write_buf, 3);
+ transports.out->flush();
+
+ // Now read 4 bytes, so that we are partway through the written data.
+ uint32_t bytes_read = transports.in->read(read_buf, 4);
+ BOOST_CHECK_EQUAL(bytes_read, (uint32_t)4);
+
+ // Now attempt to read 10 bytes. Only 9 more are available.
+ //
+ // We should be able to get all 9 bytes, but it might take multiple read
+ // calls, since it is valid for read() to return fewer bytes than requested.
+ // (Most transports do immediately return 9 bytes, but the framing transports
+ // tend to only return to the end of the current frame, which is 6 bytes in
+ // this case.)
+ uint32_t total_read = 0;
+ while (total_read < 9) {
+ set_trigger(3, transports.out, 1);
+ bytes_read = transports.in->read(read_buf, 10);
+ BOOST_REQUIRE_EQUAL(g_numTriggersFired, (unsigned int)0);
+ BOOST_REQUIRE_GT(bytes_read, (uint32_t)0);
+ total_read += bytes_read;
+ BOOST_REQUIRE_LE(total_read, (uint32_t)9);
+ }
+
+ BOOST_CHECK_EQUAL(total_read, (uint32_t)9);
+
+ clear_triggers();
+}
+
+template <class CoupledTransports>
+void test_borrow_part_available() {
+ CoupledTransports transports;
+ BOOST_REQUIRE(transports.in != nullptr);
+ BOOST_REQUIRE(transports.out != nullptr);
+
+ uint8_t write_buf[16];
+ uint8_t read_buf[16];
+ memset(write_buf, 'a', sizeof(write_buf));
+
+ // Attemping to borrow 10 bytes when only 9 are available should return NULL
+ // immediately.
+ transports.out->write(write_buf, 9);
+ transports.out->flush();
+ set_trigger(3, transports.out, 1);
+ uint32_t borrow_len = 10;
+ const uint8_t* borrowed_buf = transports.in->borrow(read_buf, &borrow_len);
+ BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
+ BOOST_CHECK(borrowed_buf == nullptr);
+
+ clear_triggers();
+}
+
+template <class CoupledTransports>
+void test_read_none_available() {
+ CoupledTransports transports;
+ BOOST_REQUIRE(transports.in != nullptr);
+ BOOST_REQUIRE(transports.out != nullptr);
+
+ uint8_t read_buf[16];
+
+ // Attempting to read when no data is available should either block until
+ // some data is available, or fail immediately. (e.g., TSocket blocks,
+ // TMemoryBuffer just fails.)
+ //
+ // If the transport blocks, it should succeed once some data is available,
+ // even if less than the amount requested becomes available.
+ set_trigger(1, transports.out, 2);
+ add_trigger(1, transports.out, 8);
+ uint32_t bytes_read = transports.in->read(read_buf, 10);
+ if (bytes_read == 0) {
+ BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
+ clear_triggers();
+ } else {
+ BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)1);
+ BOOST_CHECK_EQUAL(bytes_read, (uint32_t)2);
+ }
+
+ clear_triggers();
+}
+
+template <class CoupledTransports>
+void test_borrow_none_available() {
+ CoupledTransports transports;
+ BOOST_REQUIRE(transports.in != nullptr);
+ BOOST_REQUIRE(transports.out != nullptr);
+
+ uint8_t write_buf[16];
+ memset(write_buf, 'a', sizeof(write_buf));
+
+ // Attempting to borrow when no data is available should fail immediately
+ set_trigger(1, transports.out, 10);
+ uint32_t borrow_len = 10;
+ const uint8_t* borrowed_buf = transports.in->borrow(nullptr, &borrow_len);
+ BOOST_CHECK(borrowed_buf == nullptr);
+ BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
+
+ clear_triggers();
+}
+
+/**************************************************************************
+ * Test case generation
+ *
+ * Pretty ugly and annoying. This would be much easier if we the unit test
+ * framework didn't force each test to be a separate function.
+ * - Writing a completely separate function definition for each of these would
+ * result in a lot of repetitive boilerplate code.
+ * - Combining many tests into a single function makes it more difficult to
+ * tell precisely which tests failed. It also means you can't get a progress
+ * update after each test, and the tests are already fairly slow.
+ * - Similar registration could be achieved with BOOST_TEST_CASE_TEMPLATE,
+ * but it requires a lot of awkward MPL code, and results in useless test
+ * case names. (The names are generated from std::type_info::name(), which
+ * is compiler-dependent. gcc returns mangled names.)
+ **************************************************************************/
+
+#define ADD_TEST_RW(CoupledTransports, totalSize, ...) \
+ addTestRW<CoupledTransports>(BOOST_STRINGIZE(CoupledTransports), totalSize, ##__VA_ARGS__);
+
+#define TEST_RW(CoupledTransports, totalSize, ...) \
+ do { \
+ /* Add the test as specified, to test the non-virtual function calls */ \
+ ADD_TEST_RW(CoupledTransports, totalSize, ##__VA_ARGS__); \
+ /* \
+ * Also test using the transport as a TTransport*, to test \
+ * the read_virt()/write_virt() calls \
+ */ \
+ ADD_TEST_RW(CoupledTTransports<CoupledTransports>, totalSize, ##__VA_ARGS__); \
+ /* Test wrapping the transport with TBufferedTransport */ \
+ ADD_TEST_RW(CoupledBufferedTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
+ /* Test wrapping the transport with TFramedTransports */ \
+ ADD_TEST_RW(CoupledFramedTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
+ /* Test wrapping the transport with TZlibTransport */ \
+ ADD_TEST_RW(CoupledZlibTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
+ } while (0)
+
+#define ADD_TEST_BLOCKING(CoupledTransports) \
+ addTestBlocking<CoupledTransports>(BOOST_STRINGIZE(CoupledTransports));
+
+#define TEST_BLOCKING_BEHAVIOR(CoupledTransports) \
+ ADD_TEST_BLOCKING(CoupledTransports); \
+ ADD_TEST_BLOCKING(CoupledTTransports<CoupledTransports>); \
+ ADD_TEST_BLOCKING(CoupledBufferedTransportsT<CoupledTransports>); \
+ ADD_TEST_BLOCKING(CoupledFramedTransportsT<CoupledTransports>); \
+ ADD_TEST_BLOCKING(CoupledZlibTransportsT<CoupledTransports>);
+
+class TransportTestGen {
+public:
+ TransportTestGen(boost::unit_test::test_suite* suite, float sizeMultiplier)
+ : suite_(suite), sizeMultiplier_(sizeMultiplier) {}
+
+ void generate() {
+ GenericSizeGenerator rand4k(1, 4096);
+
+ /*
+ * We do the basically the same set of tests for each transport type,
+ * although we tweak the parameters in some places.
+ */
+
+ // TMemoryBuffer tests
+ TEST_RW(CoupledMemoryBuffers, 1024 * 1024, 0, 0);
+ TEST_RW(CoupledMemoryBuffers, 1024 * 256, rand4k, rand4k);
+ TEST_RW(CoupledMemoryBuffers, 1024 * 256, 167, 163);
+ TEST_RW(CoupledMemoryBuffers, 1024 * 16, 1, 1);
+
+ TEST_RW(CoupledMemoryBuffers, 1024 * 256, 0, 0, rand4k, rand4k);
+ TEST_RW(CoupledMemoryBuffers, 1024 * 256, rand4k, rand4k, rand4k, rand4k);
+ TEST_RW(CoupledMemoryBuffers, 1024 * 256, 167, 163, rand4k, rand4k);
+ TEST_RW(CoupledMemoryBuffers, 1024 * 16, 1, 1, rand4k, rand4k);
+
+ TEST_BLOCKING_BEHAVIOR(CoupledMemoryBuffers);
+
+#ifndef _WIN32
+ // TFDTransport tests
+ // Since CoupledFDTransports tests with a pipe, writes will block
+ // if there is too much outstanding unread data in the pipe.
+ uint32_t fd_max_outstanding = 4096;
+ TEST_RW(CoupledFDTransports, 1024 * 1024, 0, 0, 0, 0, fd_max_outstanding);
+ TEST_RW(CoupledFDTransports, 1024 * 256, rand4k, rand4k, 0, 0, fd_max_outstanding);
+ TEST_RW(CoupledFDTransports, 1024 * 256, 167, 163, 0, 0, fd_max_outstanding);
+ TEST_RW(CoupledFDTransports, 1024 * 16, 1, 1, 0, 0, fd_max_outstanding);
+
+ TEST_RW(CoupledFDTransports, 1024 * 256, 0, 0, rand4k, rand4k, fd_max_outstanding);
+ TEST_RW(CoupledFDTransports, 1024 * 256, rand4k, rand4k, rand4k, rand4k, fd_max_outstanding);
+ TEST_RW(CoupledFDTransports, 1024 * 256, 167, 163, rand4k, rand4k, fd_max_outstanding);
+ TEST_RW(CoupledFDTransports, 1024 * 16, 1, 1, rand4k, rand4k, fd_max_outstanding);
+
+ TEST_BLOCKING_BEHAVIOR(CoupledFDTransports);
+#else
+ // TPipe tests (WIN32 only)
+ TEST_RW(CoupledPipeTransports, 1024 * 1024, 0, 0);
+ TEST_RW(CoupledPipeTransports, 1024 * 256, rand4k, rand4k);
+ TEST_RW(CoupledPipeTransports, 1024 * 256, 167, 163);
+ TEST_RW(CoupledPipeTransports, 1024 * 16, 1, 1);
+
+ TEST_RW(CoupledPipeTransports, 1024 * 256, 0, 0, rand4k, rand4k);
+ TEST_RW(CoupledPipeTransports, 1024 * 256, rand4k, rand4k, rand4k, rand4k);
+ TEST_RW(CoupledPipeTransports, 1024 * 256, 167, 163, rand4k, rand4k);
+ TEST_RW(CoupledPipeTransports, 1024 * 16, 1, 1, rand4k, rand4k);
+
+ TEST_BLOCKING_BEHAVIOR(CoupledPipeTransports);
+#endif //_WIN32
+
+ // TSocket tests
+ uint32_t socket_max_outstanding = 4096;
+ TEST_RW(CoupledSocketTransports, 1024 * 1024, 0, 0, 0, 0, socket_max_outstanding);
+ TEST_RW(CoupledSocketTransports, 1024 * 256, rand4k, rand4k, 0, 0, socket_max_outstanding);
+ TEST_RW(CoupledSocketTransports, 1024 * 256, 167, 163, 0, 0, socket_max_outstanding);
+ // Doh. Apparently writing to a socket has some additional overhead for
+ // each send() call. If we have more than ~400 outstanding 1-byte write
+ // requests, additional send() calls start blocking.
+ TEST_RW(CoupledSocketTransports, 1024 * 16, 1, 1, 0, 0, socket_max_outstanding);
+ TEST_RW(CoupledSocketTransports, 1024 * 256, 0, 0, rand4k, rand4k, socket_max_outstanding);
+ TEST_RW(CoupledSocketTransports,
+ 1024 * 256,
+ rand4k,
+ rand4k,
+ rand4k,
+ rand4k,
+ socket_max_outstanding);
+ TEST_RW(CoupledSocketTransports, 1024 * 256, 167, 163, rand4k, rand4k, socket_max_outstanding);
+ TEST_RW(CoupledSocketTransports, 1024 * 16, 1, 1, rand4k, rand4k, socket_max_outstanding);
+
+ TEST_BLOCKING_BEHAVIOR(CoupledSocketTransports);
+
+// These could be made to work on Windows, but I don't care enough to make it happen
+#ifndef _WIN32
+ // TFileTransport tests
+ // We use smaller buffer sizes here, since TFileTransport is fairly slow.
+ //
+ // TFileTransport can't write more than 16MB at once
+ uint32_t max_write_at_once = 1024 * 1024 * 16 - 4;
+ TEST_RW(CoupledFileTransports, 1024 * 1024, max_write_at_once, 0);
+ TEST_RW(CoupledFileTransports, 1024 * 128, rand4k, rand4k);
+ TEST_RW(CoupledFileTransports, 1024 * 128, 167, 163);
+ TEST_RW(CoupledFileTransports, 1024 * 2, 1, 1);
+
+ TEST_RW(CoupledFileTransports, 1024 * 64, 0, 0, rand4k, rand4k);
+ TEST_RW(CoupledFileTransports, 1024 * 64, rand4k, rand4k, rand4k, rand4k);
+ TEST_RW(CoupledFileTransports, 1024 * 64, 167, 163, rand4k, rand4k);
+ TEST_RW(CoupledFileTransports, 1024 * 2, 1, 1, rand4k, rand4k);
+
+ TEST_BLOCKING_BEHAVIOR(CoupledFileTransports);
+#endif
+
+ // Add some tests that access TBufferedTransport and TFramedTransport
+ // via TTransport pointers and TBufferBase pointers.
+ ADD_TEST_RW(CoupledTTransports<CoupledBufferedTransports>,
+ 1024 * 1024,
+ rand4k,
+ rand4k,
+ rand4k,
+ rand4k);
+ ADD_TEST_RW(CoupledBufferBases<CoupledBufferedTransports>,
+ 1024 * 1024,
+ rand4k,
+ rand4k,
+ rand4k,
+ rand4k);
+ ADD_TEST_RW(CoupledTTransports<CoupledFramedTransports>,
+ 1024 * 1024,
+ rand4k,
+ rand4k,
+ rand4k,
+ rand4k);
+ ADD_TEST_RW(CoupledBufferBases<CoupledFramedTransports>,
+ 1024 * 1024,
+ rand4k,
+ rand4k,
+ rand4k,
+ rand4k);
+
+ // Test using TZlibTransport via a TTransport pointer
+ ADD_TEST_RW(CoupledTTransports<CoupledZlibTransports>,
+ 1024 * 1024,
+ rand4k,
+ rand4k,
+ rand4k,
+ rand4k);
+ }
+
+#if (BOOST_VERSION >= 105900)
+#define MAKE_TEST_CASE(_FUNC, _NAME) boost::unit_test::make_test_case(_FUNC, _NAME, __FILE__, __LINE__)
+#else
+#define MAKE_TEST_CASE(_FUNC, _NAME) boost::unit_test::make_test_case(_FUNC, _NAME)
+#endif
+
+private:
+ template <class CoupledTransports>
+ void addTestRW(const char* transport_name,
+ uint32_t totalSize,
+ GenericSizeGenerator wSizeGen,
+ GenericSizeGenerator rSizeGen,
+ GenericSizeGenerator wChunkSizeGen = 0,
+ GenericSizeGenerator rChunkSizeGen = 0,
+ uint32_t maxOutstanding = 0,
+ uint32_t expectedFailures = 0) {
+ // adjust totalSize by the specified sizeMultiplier_ first
+ totalSize = static_cast<uint32_t>(totalSize * sizeMultiplier_);
+
+ std::ostringstream name;
+ name << transport_name << "::test_rw(" << totalSize << ", " << wSizeGen.describe() << ", "
+ << rSizeGen.describe() << ", " << wChunkSizeGen.describe() << ", "
+ << rChunkSizeGen.describe() << ", " << maxOutstanding << ")";
+
+#if (BOOST_VERSION >= 105900)
+ std::function<void ()> test_func
+#else
+ boost::unit_test::callback0<> test_func
+#endif
+ = std::bind(test_rw<CoupledTransports>,
+ totalSize,
+ wSizeGen,
+ rSizeGen,
+ wChunkSizeGen,
+ rChunkSizeGen,
+ maxOutstanding);
+ suite_->add(MAKE_TEST_CASE(test_func, name.str()), expectedFailures);
+ }
+
+ template <class CoupledTransports>
+ void addTestBlocking(const char* transportName, uint32_t expectedFailures = 0) {
+ char name[1024];
+
+ THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_part_available()", transportName);
+ suite_->add(MAKE_TEST_CASE(test_read_part_available<CoupledTransports>, name), expectedFailures);
+
+ THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_part_available_in_chunks()", transportName);
+ suite_->add(MAKE_TEST_CASE(test_read_part_available_in_chunks<CoupledTransports>, name), expectedFailures);
+
+ THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_partial_midframe()", transportName);
+ suite_->add(MAKE_TEST_CASE(test_read_partial_midframe<CoupledTransports>, name), expectedFailures);
+
+ THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_none_available()", transportName);
+ suite_->add(MAKE_TEST_CASE(test_read_none_available<CoupledTransports>, name), expectedFailures);
+
+ THRIFT_SNPRINTF(name, sizeof(name), "%s::test_borrow_part_available()", transportName);
+ suite_->add(MAKE_TEST_CASE(test_borrow_part_available<CoupledTransports>, name), expectedFailures);
+
+ THRIFT_SNPRINTF(name, sizeof(name), "%s::test_borrow_none_available()", transportName);
+ suite_->add(MAKE_TEST_CASE(test_borrow_none_available<CoupledTransports>, name), expectedFailures);
+ }
+
+ boost::unit_test::test_suite* suite_;
+ // sizeMultiplier_ is configurable via the command line, and allows the
+ // user to adjust between smaller buffers that can be tested quickly,
+ // or larger buffers that more thoroughly exercise the code, but take
+ // longer.
+ float sizeMultiplier_;
+};
+
+/**************************************************************************
+ * General Initialization
+ **************************************************************************/
+
+struct global_fixture {
+ std::shared_ptr<apache::thrift::concurrency::Thread> alarmThread_;
+ global_fixture() {
+#if _WIN32
+ apache::thrift::transport::TWinsockSingleton::create();
+#endif
+
+ apache::thrift::concurrency::ThreadFactory factory;
+ factory.setDetached(false);
+
+ alarmThread_ = factory.newThread(
+ apache::thrift::concurrency::FunctionRunner::create(alarm_handler_wrapper));
+ alarmThread_->start();
+ }
+ ~global_fixture() {
+ {
+ apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
+ g_teardown = true;
+ g_alarm_monitor.notify();
+ }
+ alarmThread_->join();
+ }
+};
+
+#if (BOOST_VERSION >= 105900)
+BOOST_GLOBAL_FIXTURE(global_fixture);
+#else
+BOOST_GLOBAL_FIXTURE(global_fixture)
+#endif
+
+#ifdef BOOST_TEST_DYN_LINK
+bool init_unit_test_suite() {
+ struct timeval tv;
+ THRIFT_GETTIMEOFDAY(&tv, nullptr);
+ int seed = tv.tv_sec ^ tv.tv_usec;
+
+ initrand(seed);
+
+ boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
+ suite->p_name.value = "TransportTest";
+ TransportTestGen transport_test_generator(suite, 1);
+ transport_test_generator.generate();
+ return true;
+}
+
+int main( int argc, char* argv[] ) {
+ return ::boost::unit_test::unit_test_main(&init_unit_test_suite,argc,argv);
+}
+#else
+boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
+ THRIFT_UNUSED_VARIABLE(argc);
+ THRIFT_UNUSED_VARIABLE(argv);
+ struct timeval tv;
+ THRIFT_GETTIMEOFDAY(&tv, NULL);
+ int seed = tv.tv_sec ^ tv.tv_usec;
+
+ initrand(seed);
+
+ boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
+ suite->p_name.value = "TransportTest";
+ TransportTestGen transport_test_generator(suite, 1);
+ transport_test_generator.generate();
+ return NULL;
+}
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TypedefTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TypedefTest.cpp
new file mode 100644
index 000000000..24e9265e6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TypedefTest.cpp
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <boost/type_traits/is_same.hpp>
+#include <boost/static_assert.hpp>
+
+#include "gen-cpp/TypedefTest_types.h"
+
+BOOST_STATIC_ASSERT((boost::is_same<int32_t, thrift::test::MyInt32>::value));
+BOOST_STATIC_ASSERT((boost::is_same<std::string, thrift::test::MyString>::value));
+BOOST_STATIC_ASSERT(
+ (boost::is_same<thrift::test::TypedefTestStruct, thrift::test::MyStruct>::value));
diff --git a/src/jaegertracing/thrift/lib/cpp/test/UnitTestMain.cpp b/src/jaegertracing/thrift/lib/cpp/test/UnitTestMain.cpp
new file mode 100644
index 000000000..f0ef1e4a6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/UnitTestMain.cpp
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define BOOST_TEST_MODULE thrift
+#include <boost/test/auto_unit_test.hpp>
diff --git a/src/jaegertracing/thrift/lib/cpp/test/ZlibTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/ZlibTest.cpp
new file mode 100644
index 000000000..3e2eb816c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/ZlibTest.cpp
@@ -0,0 +1,475 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE // needed for getopt_long
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER <= 1700)
+// polynomial and std::fill_t warning happens in MSVC 2010, 2013, maybe others
+// https://svn.boost.org/trac/boost/ticket/11426
+#pragma warning(disable:4996)
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+#include <cstddef>
+#include <fstream>
+#include <iostream>
+#include <memory>
+
+#include <boost/random.hpp>
+#include <boost/shared_array.hpp>
+#include <boost/test/unit_test.hpp>
+#include <boost/version.hpp>
+
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/transport/TZlibTransport.h>
+
+using namespace apache::thrift::transport;
+using std::shared_ptr;
+using std::string;
+
+boost::mt19937 rng;
+
+/*
+ * Utility code
+ */
+
+class SizeGenerator {
+public:
+ virtual ~SizeGenerator() = default;
+ virtual unsigned int getSize() = 0;
+};
+
+class ConstantSizeGenerator : public SizeGenerator {
+public:
+ ConstantSizeGenerator(unsigned int value) : value_(value) {}
+ unsigned int getSize() override { return value_; }
+
+private:
+ unsigned int value_;
+};
+
+class LogNormalSizeGenerator : public SizeGenerator {
+public:
+ LogNormalSizeGenerator(double mean, double std_dev)
+ : gen_(rng, boost::lognormal_distribution<double>(mean, std_dev)) {}
+
+ unsigned int getSize() override {
+ // Loop until we get a size of 1 or more
+ while (true) {
+ auto value = static_cast<unsigned int>(gen_());
+ if (value >= 1) {
+ return value;
+ }
+ }
+ }
+
+private:
+ boost::variate_generator<boost::mt19937, boost::lognormal_distribution<double> > gen_;
+};
+
+boost::shared_array<uint8_t> gen_uniform_buffer(uint32_t buf_len, uint8_t c) {
+ auto* buf = new uint8_t[buf_len];
+ memset(buf, c, buf_len);
+ return boost::shared_array<uint8_t>(buf);
+}
+
+boost::shared_array<uint8_t> gen_compressible_buffer(uint32_t buf_len) {
+ auto* buf = new uint8_t[buf_len];
+
+ // Generate small runs of alternately increasing and decreasing bytes
+ boost::uniform_smallint<uint32_t> run_length_distribution(1, 64);
+ boost::uniform_smallint<uint8_t> byte_distribution(0, UINT8_MAX);
+ boost::variate_generator<boost::mt19937, boost::uniform_smallint<uint8_t> >
+ byte_generator(rng, byte_distribution);
+ boost::variate_generator<boost::mt19937, boost::uniform_smallint<uint32_t> >
+ run_len_generator(rng, run_length_distribution);
+
+ uint32_t idx = 0;
+ int8_t step = 1;
+ while (idx < buf_len) {
+ uint32_t run_length = run_len_generator();
+ if (idx + run_length > buf_len) {
+ run_length = buf_len - idx;
+ }
+
+ uint8_t byte = byte_generator();
+ for (uint32_t n = 0; n < run_length; ++n) {
+ buf[idx] = byte;
+ ++idx;
+ byte += step;
+ }
+
+ step *= -1;
+ }
+
+ return boost::shared_array<uint8_t>(buf);
+}
+
+boost::shared_array<uint8_t> gen_random_buffer(uint32_t buf_len) {
+ auto* buf = new uint8_t[buf_len];
+
+ boost::uniform_smallint<uint8_t> distribution(0, UINT8_MAX);
+ boost::variate_generator<boost::mt19937, boost::uniform_smallint<uint8_t> >
+ generator(rng, distribution);
+
+ for (uint32_t n = 0; n < buf_len; ++n) {
+ buf[n] = generator();
+ }
+
+ return boost::shared_array<uint8_t>(buf);
+}
+
+/*
+ * Test functions
+ */
+
+void test_write_then_read(const boost::shared_array<uint8_t> buf, uint32_t buf_len) {
+ shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
+ shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
+ zlib_trans->write(buf.get(), buf_len);
+ zlib_trans->finish();
+
+ boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
+ uint32_t got = zlib_trans->readAll(mirror.get(), buf_len);
+ BOOST_REQUIRE_EQUAL(got, buf_len);
+ BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf.get(), buf_len), 0);
+ zlib_trans->verifyChecksum();
+}
+
+void test_separate_checksum(const boost::shared_array<uint8_t> buf, uint32_t buf_len) {
+ // This one is tricky. I separate the last byte of the stream out
+ // into a separate crbuf_. The last byte is part of the checksum,
+ // so the entire read goes fine, but when I go to verify the checksum
+ // it isn't there. The original implementation complained that
+ // the stream was not complete. I'm about to go fix that.
+ // It worked. Awesome.
+ shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
+ shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
+ zlib_trans->write(buf.get(), buf_len);
+ zlib_trans->finish();
+ string tmp_buf;
+ membuf->appendBufferToString(tmp_buf);
+ zlib_trans.reset(new TZlibTransport(membuf,
+ TZlibTransport::DEFAULT_URBUF_SIZE,
+ static_cast<uint32_t>(tmp_buf.length() - 1)));
+
+ boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
+ uint32_t got = zlib_trans->readAll(mirror.get(), buf_len);
+ BOOST_REQUIRE_EQUAL(got, buf_len);
+ BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf.get(), buf_len), 0);
+ zlib_trans->verifyChecksum();
+}
+
+void test_incomplete_checksum(const boost::shared_array<uint8_t> buf, uint32_t buf_len) {
+ // Make sure we still get that "not complete" error if
+ // it really isn't complete.
+ shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
+ shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
+ zlib_trans->write(buf.get(), buf_len);
+ zlib_trans->finish();
+ string tmp_buf;
+ membuf->appendBufferToString(tmp_buf);
+ tmp_buf.erase(tmp_buf.length() - 1);
+ membuf->resetBuffer(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(tmp_buf.data())),
+ static_cast<uint32_t>(tmp_buf.length()));
+
+ boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
+ uint32_t got = zlib_trans->readAll(mirror.get(), buf_len);
+ BOOST_REQUIRE_EQUAL(got, buf_len);
+ BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf.get(), buf_len), 0);
+ try {
+ zlib_trans->verifyChecksum();
+ BOOST_ERROR("verifyChecksum() did not report an error");
+ } catch (TTransportException& ex) {
+ BOOST_CHECK_EQUAL(ex.getType(), TTransportException::CORRUPTED_DATA);
+ }
+}
+
+void test_read_write_mix(const boost::shared_array<uint8_t> buf,
+ uint32_t buf_len,
+ const shared_ptr<SizeGenerator>& write_gen,
+ const shared_ptr<SizeGenerator>& read_gen) {
+ // Try it with a mix of read/write sizes.
+ shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
+ shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
+ unsigned int tot;
+
+ tot = 0;
+ while (tot < buf_len) {
+ uint32_t write_len = write_gen->getSize();
+ if (tot + write_len > buf_len) {
+ write_len = buf_len - tot;
+ }
+ zlib_trans->write(buf.get() + tot, write_len);
+ tot += write_len;
+ }
+
+ zlib_trans->finish();
+
+ tot = 0;
+ boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
+ while (tot < buf_len) {
+ uint32_t read_len = read_gen->getSize();
+ uint32_t expected_read_len = read_len;
+ if (tot + read_len > buf_len) {
+ expected_read_len = buf_len - tot;
+ }
+ uint32_t got = zlib_trans->read(mirror.get() + tot, read_len);
+ BOOST_REQUIRE_LE(got, expected_read_len);
+ BOOST_REQUIRE_NE(got, (uint32_t)0);
+ tot += got;
+ }
+
+ BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf.get(), buf_len), 0);
+ zlib_trans->verifyChecksum();
+}
+
+void test_invalid_checksum(const boost::shared_array<uint8_t> buf, uint32_t buf_len) {
+ // Verify checksum checking.
+ shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
+ shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
+ zlib_trans->write(buf.get(), buf_len);
+ zlib_trans->finish();
+ string tmp_buf;
+ membuf->appendBufferToString(tmp_buf);
+ // Modify a byte at the end of the buffer (part of the checksum).
+ // On rare occasions, modifying a byte in the middle of the buffer
+ // isn't caught by the checksum.
+ //
+ // (This happens especially often for the uniform buffer. The
+ // re-inflated data is correct, however. I suspect in this case that
+ // we're more likely to modify bytes that are part of zlib metadata
+ // instead of the actual compressed data.)
+ //
+ // I've also seen some failure scenarios where a checksum failure isn't
+ // reported, but zlib keeps trying to decode past the end of the data.
+ // (When this occurs, verifyChecksum() throws an exception indicating
+ // that the end of the data hasn't been reached.) I haven't seen this
+ // error when only modifying checksum bytes.
+ int index = static_cast<int>(tmp_buf.size() - 1);
+ tmp_buf[index]++;
+ membuf->resetBuffer(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(tmp_buf.data())),
+ static_cast<uint32_t>(tmp_buf.length()));
+
+ boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
+ try {
+ zlib_trans->readAll(mirror.get(), buf_len);
+ zlib_trans->verifyChecksum();
+ BOOST_ERROR("verifyChecksum() did not report an error");
+ } catch (TZlibTransportException& ex) {
+ BOOST_CHECK_EQUAL(ex.getType(), TTransportException::INTERNAL_ERROR);
+ }
+}
+
+void test_write_after_flush(const boost::shared_array<uint8_t> buf, uint32_t buf_len) {
+ // write some data
+ shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
+ shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
+ zlib_trans->write(buf.get(), buf_len);
+
+ // call finish()
+ zlib_trans->finish();
+
+ // make sure write() throws an error
+ try {
+ uint8_t write_buf[] = "a";
+ zlib_trans->write(write_buf, 1);
+ BOOST_ERROR("write() after finish() did not raise an exception");
+ } catch (TTransportException& ex) {
+ BOOST_CHECK_EQUAL(ex.getType(), TTransportException::BAD_ARGS);
+ }
+
+ // make sure flush() throws an error
+ try {
+ zlib_trans->flush();
+ BOOST_ERROR("flush() after finish() did not raise an exception");
+ } catch (TTransportException& ex) {
+ BOOST_CHECK_EQUAL(ex.getType(), TTransportException::BAD_ARGS);
+ }
+
+ // make sure finish() throws an error
+ try {
+ zlib_trans->finish();
+ BOOST_ERROR("finish() after finish() did not raise an exception");
+ } catch (TTransportException& ex) {
+ BOOST_CHECK_EQUAL(ex.getType(), TTransportException::BAD_ARGS);
+ }
+}
+
+void test_no_write() {
+ // Verify that no data is written to the underlying transport if we
+ // never write data to the TZlibTransport.
+ shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
+ {
+ // Create a TZlibTransport object, and immediately destroy it
+ // when it goes out of scope.
+ TZlibTransport w_zlib_trans(membuf);
+ }
+
+ BOOST_CHECK_EQUAL(membuf->available_read(), (uint32_t)0);
+}
+
+void test_get_underlying_transport() {
+ shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
+ shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
+ BOOST_CHECK_EQUAL(membuf.get(), zlib_trans->getUnderlyingTransport().get());
+}
+
+/*
+ * Initialization
+ */
+
+#if (BOOST_VERSION >= 105900)
+#define ADD_TEST_CASE(suite, name, _FUNC, ...) \
+ do { \
+ ::std::ostringstream name_ss; \
+ name_ss << name << "-" << BOOST_STRINGIZE(_FUNC); \
+ ::std::function<void ()> test_func = \
+ ::std::bind(_FUNC, ##__VA_ARGS__); \
+ ::boost::unit_test::test_case* tc \
+ = ::boost::unit_test::make_test_case(test_func, name_ss.str(), __FILE__, __LINE__); \
+ (suite)->add(tc); \
+ } while (0)
+#else
+#define ADD_TEST_CASE(suite, name, _FUNC, ...) \
+ do { \
+ ::std::ostringstream name_ss; \
+ name_ss << name << "-" << BOOST_STRINGIZE(_FUNC); \
+ ::boost::unit_test::test_case* tc \
+ = ::boost::unit_test::make_test_case(::std::bind(_FUNC, \
+ ##__VA_ARGS__), \
+ name_ss.str()); \
+ (suite)->add(tc); \
+ } while (0)
+#endif
+
+void add_tests(boost::unit_test::test_suite* suite,
+ const boost::shared_array<uint8_t>& buf,
+ uint32_t buf_len,
+ const char* name) {
+ ADD_TEST_CASE(suite, name, test_write_then_read, buf, buf_len);
+ ADD_TEST_CASE(suite, name, test_separate_checksum, buf, buf_len);
+ ADD_TEST_CASE(suite, name, test_incomplete_checksum, buf, buf_len);
+ ADD_TEST_CASE(suite, name, test_invalid_checksum, buf, buf_len);
+ ADD_TEST_CASE(suite, name, test_write_after_flush, buf, buf_len);
+
+ shared_ptr<SizeGenerator> size_32k(new ConstantSizeGenerator(1 << 15));
+ shared_ptr<SizeGenerator> size_lognormal(new LogNormalSizeGenerator(20, 30));
+ ADD_TEST_CASE(suite, name << "-constant", test_read_write_mix, buf, buf_len, size_32k, size_32k);
+ ADD_TEST_CASE(suite,
+ name << "-lognormal-write",
+ test_read_write_mix,
+ buf,
+ buf_len,
+ size_lognormal,
+ size_32k);
+ ADD_TEST_CASE(suite,
+ name << "-lognormal-read",
+ test_read_write_mix,
+ buf,
+ buf_len,
+ size_32k,
+ size_lognormal);
+ ADD_TEST_CASE(suite,
+ name << "-lognormal-both",
+ test_read_write_mix,
+ buf,
+ buf_len,
+ size_lognormal,
+ size_lognormal);
+
+ // Test with a random size distribution,
+ // but use the exact same distribution for reading as for writing.
+ //
+ // Because the SizeGenerator makes a copy of the random number generator,
+ // both SizeGenerators should return the exact same set of values, since they
+ // both start with random number generators in the same state.
+ shared_ptr<SizeGenerator> write_size_gen(new LogNormalSizeGenerator(20, 30));
+ shared_ptr<SizeGenerator> read_size_gen(new LogNormalSizeGenerator(20, 30));
+ ADD_TEST_CASE(suite,
+ name << "-lognormal-same-distribution",
+ test_read_write_mix,
+ buf,
+ buf_len,
+ write_size_gen,
+ read_size_gen);
+}
+
+void print_usage(FILE* f, const char* argv0) {
+ fprintf(f, "Usage: %s [boost_options] [options]\n", argv0);
+ fprintf(f, "Options:\n");
+ fprintf(f, " --seed=<N>, -s <N>\n");
+ fprintf(f, " --help\n");
+}
+
+#ifdef BOOST_TEST_DYN_LINK
+bool init_unit_test_suite() {
+ auto seed = static_cast<uint32_t>(time(nullptr));
+#ifdef HAVE_INTTYPES_H
+ printf("seed: %" PRIu32 "\n", seed);
+#endif
+ rng.seed(seed);
+
+ boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
+ suite->p_name.value = "ZlibTest";
+
+ uint32_t buf_len = 1024 * 32;
+ add_tests(suite, gen_uniform_buffer(buf_len, 'a'), buf_len, "uniform");
+ add_tests(suite, gen_compressible_buffer(buf_len), buf_len, "compressible");
+ add_tests(suite, gen_random_buffer(buf_len), buf_len, "random");
+
+ suite->add(BOOST_TEST_CASE(test_no_write));
+ suite->add(BOOST_TEST_CASE(test_get_underlying_transport));
+
+ return true;
+}
+
+int main( int argc, char* argv[] ) {
+ return ::boost::unit_test::unit_test_main(&init_unit_test_suite,argc,argv);
+}
+#else
+boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
+ THRIFT_UNUSED_VARIABLE(argc);
+ THRIFT_UNUSED_VARIABLE(argv);
+ uint32_t seed = static_cast<uint32_t>(time(NULL));
+#ifdef HAVE_INTTYPES_H
+ printf("seed: %" PRIu32 "\n", seed);
+#endif
+ rng.seed(seed);
+
+ boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
+ suite->p_name.value = "ZlibTest";
+
+ uint32_t buf_len = 1024 * 32;
+ add_tests(suite, gen_uniform_buffer(buf_len, 'a'), buf_len, "uniform");
+ add_tests(suite, gen_compressible_buffer(buf_len), buf_len, "compressible");
+ add_tests(suite, gen_random_buffer(buf_len), buf_len, "random");
+
+ suite->add(BOOST_TEST_CASE(test_no_write));
+
+ return NULL;
+}
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/test/concurrency/Tests.cpp b/src/jaegertracing/thrift/lib/cpp/test/concurrency/Tests.cpp
new file mode 100644
index 000000000..8c734c2d5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/concurrency/Tests.cpp
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <iostream>
+#include <vector>
+#include <string>
+
+#include "ThreadFactoryTests.h"
+#include "TimerManagerTests.h"
+#include "ThreadManagerTests.h"
+
+// The test weight, where 10 is 10 times more threads than baseline
+// and the baseline is optimized for running in valgrind
+static int WEIGHT = 10;
+
+int main(int argc, char** argv) {
+
+ std::vector<std::string> args(argc - 1 > 1 ? argc - 1 : 1);
+
+ args[0] = "all";
+
+ for (int ix = 1; ix < argc; ix++) {
+ args[ix - 1] = std::string(argv[ix]);
+ }
+
+ if (getenv("VALGRIND") != nullptr) {
+ // lower the scale of every test
+ WEIGHT = 1;
+ }
+
+ bool runAll = args[0].compare("all") == 0;
+
+ if (runAll || args[0].compare("thread-factory") == 0) {
+
+ ThreadFactoryTests threadFactoryTests;
+
+ std::cout << "ThreadFactory tests..." << std::endl;
+
+ int reapLoops = 2 * WEIGHT;
+ int reapCount = 100 * WEIGHT;
+ size_t floodLoops = 3;
+ size_t floodCount = 500 * WEIGHT;
+
+ std::cout << "\t\tThreadFactory reap N threads test: N = " << reapLoops << "x" << reapCount << std::endl;
+
+ if (!threadFactoryTests.reapNThreads(reapLoops, reapCount)) {
+ std::cerr << "\t\ttThreadFactory reap N threads FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tThreadFactory flood N threads test: N = " << floodLoops << "x" << floodCount << std::endl;
+
+ if (!threadFactoryTests.floodNTest(floodLoops, floodCount)) {
+ std::cerr << "\t\ttThreadFactory flood N threads FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tThreadFactory synchronous start test" << std::endl;
+
+ if (!threadFactoryTests.synchStartTest()) {
+ std::cerr << "\t\ttThreadFactory synchronous start FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tThreadFactory monitor timeout test" << std::endl;
+
+ if (!threadFactoryTests.monitorTimeoutTest()) {
+ std::cerr << "\t\ttThreadFactory monitor timeout FAILED" << std::endl;
+ return 1;
+ }
+ }
+
+ if (runAll || args[0].compare("util") == 0) {
+
+ std::cout << "Util tests..." << std::endl;
+
+ std::cout << "\t\tUtil minimum time" << std::endl;
+
+ int64_t time00 = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+ int64_t time01 = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+
+ std::cout << "\t\t\tMinimum time: " << time01 - time00 << "ms" << std::endl;
+
+ time00 = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+ time01 = time00;
+ size_t count = 0;
+
+ while (time01 < time00 + 10) {
+ count++;
+ time01 = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+ }
+
+ std::cout << "\t\t\tscall per ms: " << count / (time01 - time00) << std::endl;
+ }
+
+ if (runAll || args[0].compare("timer-manager") == 0) {
+
+ std::cout << "TimerManager tests..." << std::endl;
+
+ std::cout << "\t\tTimerManager test00" << std::endl;
+
+ TimerManagerTests timerManagerTests;
+
+ if (!timerManagerTests.test00()) {
+ std::cerr << "\t\tTimerManager tests FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tTimerManager test01" << std::endl;
+
+ if (!timerManagerTests.test01()) {
+ std::cerr << "\t\tTimerManager tests FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tTimerManager test02" << std::endl;
+
+ if (!timerManagerTests.test02()) {
+ std::cerr << "\t\tTimerManager tests FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tTimerManager test03" << std::endl;
+
+ if (!timerManagerTests.test03()) {
+ std::cerr << "\t\tTimerManager tests FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tTimerManager test04" << std::endl;
+
+ if (!timerManagerTests.test04()) {
+ std::cerr << "\t\tTimerManager tests FAILED" << std::endl;
+ return 1;
+ }
+ }
+
+ if (runAll || args[0].compare("thread-manager") == 0) {
+
+ std::cout << "ThreadManager tests..." << std::endl;
+
+ {
+ size_t workerCount = 10 * WEIGHT;
+ size_t taskCount = 500 * WEIGHT;
+ int64_t delay = 10LL;
+
+ ThreadManagerTests threadManagerTests;
+
+ std::cout << "\t\tThreadManager api test:" << std::endl;
+
+ if (!threadManagerTests.apiTest()) {
+ std::cerr << "\t\tThreadManager apiTest FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tThreadManager load test: worker count: " << workerCount
+ << " task count: " << taskCount << " delay: " << delay << std::endl;
+
+ if (!threadManagerTests.loadTest(taskCount, delay, workerCount)) {
+ std::cerr << "\t\tThreadManager loadTest FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tThreadManager block test: worker count: " << workerCount
+ << " delay: " << delay << std::endl;
+
+ if (!threadManagerTests.blockTest(delay, workerCount)) {
+ std::cerr << "\t\tThreadManager blockTest FAILED" << std::endl;
+ return 1;
+ }
+ }
+ }
+
+ if (runAll || args[0].compare("thread-manager-benchmark") == 0) {
+
+ std::cout << "ThreadManager benchmark tests..." << std::endl;
+
+ {
+
+ size_t minWorkerCount = 2;
+
+ size_t maxWorkerCount = 8;
+
+ size_t tasksPerWorker = 100 * WEIGHT;
+
+ int64_t delay = 5LL;
+
+ for (size_t workerCount = minWorkerCount; workerCount <= maxWorkerCount; workerCount *= 4) {
+
+ size_t taskCount = workerCount * tasksPerWorker;
+
+ std::cout << "\t\tThreadManager load test: worker count: " << workerCount
+ << " task count: " << taskCount << " delay: " << delay << std::endl;
+
+ ThreadManagerTests threadManagerTests;
+
+ if (!threadManagerTests.loadTest(taskCount, delay, workerCount))
+ {
+ std::cerr << "\t\tThreadManager loadTest FAILED" << std::endl;
+ return 1;
+ }
+ }
+ }
+ }
+
+ std::cout << "ALL TESTS PASSED" << std::endl;
+ return 0;
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadFactoryTests.h b/src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadFactoryTests.h
new file mode 100644
index 000000000..febe3f8b3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadFactoryTests.h
@@ -0,0 +1,308 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+#include <thrift/concurrency/Thread.h>
+#include <thrift/concurrency/ThreadFactory.h>
+#include <thrift/concurrency/Monitor.h>
+#include <thrift/concurrency/Mutex.h>
+
+#include <assert.h>
+#include <iostream>
+#include <vector>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+namespace test {
+
+using std::shared_ptr;
+using namespace apache::thrift::concurrency;
+
+/**
+ * ThreadManagerTests class
+ *
+ * @version $Id:$
+ */
+class ThreadFactoryTests {
+
+public:
+ /**
+ * Reap N threads
+ */
+ class ReapNTask : public Runnable {
+
+ public:
+ ReapNTask(Monitor& monitor, int& activeCount) : _monitor(monitor), _count(activeCount) {}
+
+ void run() override {
+ Synchronized s(_monitor);
+
+ if (--_count == 0) {
+ _monitor.notify();
+ }
+ }
+
+ Monitor& _monitor;
+ int& _count;
+ };
+
+ bool reapNThreads(int loop = 1, int count = 10) {
+
+ ThreadFactory threadFactory = ThreadFactory();
+ shared_ptr<Monitor> monitor(new Monitor);
+
+ for (int lix = 0; lix < loop; lix++) {
+
+ int activeCount = 0;
+
+ std::vector<shared_ptr<Thread> > threads;
+ int tix;
+
+ for (tix = 0; tix < count; tix++) {
+ try {
+ ++activeCount;
+ threads.push_back(
+ threadFactory.newThread(shared_ptr<Runnable>(new ReapNTask(*monitor, activeCount))));
+ } catch (SystemResourceException& e) {
+ std::cout << "\t\t\tfailed to create " << lix* count + tix << " thread " << e.what()
+ << std::endl;
+ throw e;
+ }
+ }
+
+ tix = 0;
+ for (std::vector<shared_ptr<Thread> >::const_iterator thread = threads.begin();
+ thread != threads.end();
+ tix++, ++thread) {
+
+ try {
+ (*thread)->start();
+ } catch (SystemResourceException& e) {
+ std::cout << "\t\t\tfailed to start " << lix* count + tix << " thread " << e.what()
+ << std::endl;
+ throw e;
+ }
+ }
+
+ {
+ Synchronized s(*monitor);
+ while (activeCount > 0) {
+ monitor->wait(1000);
+ }
+ }
+
+ std::cout << "\t\t\treaped " << lix* count << " threads" << std::endl;
+ }
+
+ std::cout << "\t\t\tSuccess!" << std::endl;
+ return true;
+ }
+
+ class SynchStartTask : public Runnable {
+
+ public:
+ enum STATE { UNINITIALIZED, STARTING, STARTED, STOPPING, STOPPED };
+
+ SynchStartTask(Monitor& monitor, volatile STATE& state) : _monitor(monitor), _state(state) {}
+
+ void run() override {
+ {
+ Synchronized s(_monitor);
+ if (_state == SynchStartTask::STARTING) {
+ _state = SynchStartTask::STARTED;
+ _monitor.notify();
+ }
+ }
+
+ {
+ Synchronized s(_monitor);
+ while (_state == SynchStartTask::STARTED) {
+ _monitor.wait();
+ }
+
+ if (_state == SynchStartTask::STOPPING) {
+ _state = SynchStartTask::STOPPED;
+ _monitor.notifyAll();
+ }
+ }
+ }
+
+ private:
+ Monitor& _monitor;
+ volatile STATE& _state;
+ };
+
+ bool synchStartTest() {
+
+ Monitor monitor;
+
+ SynchStartTask::STATE state = SynchStartTask::UNINITIALIZED;
+
+ shared_ptr<SynchStartTask> task
+ = shared_ptr<SynchStartTask>(new SynchStartTask(monitor, state));
+
+ ThreadFactory threadFactory = ThreadFactory();
+
+ shared_ptr<Thread> thread = threadFactory.newThread(task);
+
+ if (state == SynchStartTask::UNINITIALIZED) {
+
+ state = SynchStartTask::STARTING;
+
+ thread->start();
+ }
+
+ {
+ Synchronized s(monitor);
+ while (state == SynchStartTask::STARTING) {
+ monitor.wait();
+ }
+ }
+
+ assert(state != SynchStartTask::STARTING);
+
+ {
+ Synchronized s(monitor);
+
+ try {
+ monitor.wait(100);
+ } catch (TimedOutException&) {
+ }
+
+ if (state == SynchStartTask::STARTED) {
+
+ state = SynchStartTask::STOPPING;
+
+ monitor.notify();
+ }
+
+ while (state == SynchStartTask::STOPPING) {
+ monitor.wait();
+ }
+ }
+
+ assert(state == SynchStartTask::STOPPED);
+
+ bool success = true;
+
+ std::cout << "\t\t\t" << (success ? "Success" : "Failure") << "!" << std::endl;
+
+ return true;
+ }
+
+ /**
+ * The only guarantee a monitor timeout can give you is that
+ * it will take "at least" as long as the timeout, no less.
+ * There is absolutely no guarantee around regaining execution
+ * near the timeout. On a busy system (like inside a third party
+ * CI environment) it could take quite a bit longer than the
+ * requested timeout, and that's ok.
+ */
+
+ bool monitorTimeoutTest(int64_t count = 1000, int64_t timeout = 2) {
+
+ Monitor monitor;
+
+ int64_t startTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+
+ for (int64_t ix = 0; ix < count; ix++) {
+ {
+ Synchronized s(monitor);
+ try {
+ monitor.wait(timeout);
+ } catch (TimedOutException&) {
+ }
+ }
+ }
+
+ int64_t endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+
+ bool success = (endTime - startTime) >= (count * timeout);
+
+ std::cout << "\t\t\t" << (success ? "Success" : "Failure")
+ << ": minimum required time to elapse " << count * timeout
+ << "ms; actual elapsed time " << endTime - startTime << "ms"
+ << std::endl;
+
+ return success;
+ }
+
+ class FloodTask : public Runnable {
+ public:
+ FloodTask(const size_t id, Monitor& mon) : _id(id), _mon(mon) {}
+ ~FloodTask() override {
+ if (_id % 10000 == 0) {
+ Synchronized sync(_mon);
+ std::cout << "\t\tthread " << _id << " done" << std::endl;
+ }
+ }
+
+ void run() override {
+ if (_id % 10000 == 0) {
+ Synchronized sync(_mon);
+ std::cout << "\t\tthread " << _id << " started" << std::endl;
+ }
+ }
+ const size_t _id;
+ Monitor& _mon;
+ };
+
+ void foo(ThreadFactory* tf) { (void)tf; }
+
+ bool floodNTest(size_t loop = 1, size_t count = 100000) {
+
+ bool success = false;
+ Monitor mon;
+
+ for (size_t lix = 0; lix < loop; lix++) {
+
+ ThreadFactory threadFactory = ThreadFactory();
+ threadFactory.setDetached(true);
+
+ for (size_t tix = 0; tix < count; tix++) {
+
+ try {
+
+ shared_ptr<FloodTask> task(new FloodTask(lix * count + tix, mon));
+ shared_ptr<Thread> thread = threadFactory.newThread(task);
+ thread->start();
+
+ } catch (TException& e) {
+
+ std::cout << "\t\t\tfailed to start " << lix* count + tix << " thread " << e.what()
+ << std::endl;
+
+ return success;
+ }
+ }
+
+ Synchronized sync(mon);
+ std::cout << "\t\t\tflooded " << (lix + 1) * count << " threads" << std::endl;
+ success = true;
+ }
+
+ return success;
+ }
+};
+
+}
+}
+}
+} // apache::thrift::concurrency::test
diff --git a/src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadManagerTests.h b/src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadManagerTests.h
new file mode 100644
index 000000000..fee7c7c51
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadManagerTests.h
@@ -0,0 +1,639 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+#include <thrift/concurrency/ThreadManager.h>
+#include <thrift/concurrency/ThreadFactory.h>
+#include <thrift/concurrency/Monitor.h>
+
+#include <assert.h>
+#include <deque>
+#include <set>
+#include <iostream>
+#include <stdint.h>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+namespace test {
+
+using namespace apache::thrift::concurrency;
+
+static std::deque<std::shared_ptr<Runnable> > m_expired;
+static void expiredNotifier(std::shared_ptr<Runnable> runnable)
+{
+ m_expired.push_back(runnable);
+}
+
+static void sleep_(int64_t millisec) {
+ Monitor _sleep;
+ Synchronized s(_sleep);
+
+ try {
+ _sleep.wait(millisec);
+ } catch (TimedOutException&) {
+ ;
+ } catch (...) {
+ assert(0);
+ }
+}
+
+class ThreadManagerTests {
+
+public:
+ class Task : public Runnable {
+
+ public:
+ Task(Monitor& monitor, size_t& count, int64_t timeout)
+ : _monitor(monitor), _count(count), _timeout(timeout), _startTime(0), _endTime(0), _done(false) {}
+
+ void run() override {
+
+ _startTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+
+ sleep_(_timeout);
+
+ _endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+
+ _done = true;
+
+ {
+ Synchronized s(_monitor);
+
+ // std::cout << "Thread " << _count << " completed " << std::endl;
+
+ _count--;
+ if (_count % 10000 == 0) {
+ _monitor.notify();
+ }
+ }
+ }
+
+ Monitor& _monitor;
+ size_t& _count;
+ int64_t _timeout;
+ int64_t _startTime;
+ int64_t _endTime;
+ bool _done;
+ Monitor _sleep;
+ };
+
+ /**
+ * Dispatch count tasks, each of which blocks for timeout milliseconds then
+ * completes. Verify that all tasks completed and that thread manager cleans
+ * up properly on delete.
+ */
+ bool loadTest(size_t count = 100, int64_t timeout = 100LL, size_t workerCount = 4) {
+
+ Monitor monitor;
+
+ size_t activeCount = count;
+
+ shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(workerCount);
+
+ shared_ptr<ThreadFactory> threadFactory
+ = shared_ptr<ThreadFactory>(new ThreadFactory(false));
+
+ threadManager->threadFactory(threadFactory);
+
+ threadManager->start();
+
+ std::set<shared_ptr<ThreadManagerTests::Task> > tasks;
+
+ for (size_t ix = 0; ix < count; ix++) {
+
+ tasks.insert(shared_ptr<ThreadManagerTests::Task>(
+ new ThreadManagerTests::Task(monitor, activeCount, timeout)));
+ }
+
+ int64_t time00 = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+
+ for (auto ix = tasks.begin();
+ ix != tasks.end();
+ ix++) {
+
+ threadManager->add(*ix);
+ }
+
+ std::cout << "\t\t\t\tloaded " << count << " tasks to execute" << std::endl;
+
+ {
+ Synchronized s(monitor);
+
+ while (activeCount > 0) {
+ std::cout << "\t\t\t\tactiveCount = " << activeCount << std::endl;
+ monitor.wait();
+ }
+ }
+
+ int64_t time01 = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+
+ int64_t firstTime = 9223372036854775807LL;
+ int64_t lastTime = 0;
+
+ double averageTime = 0;
+ int64_t minTime = 9223372036854775807LL;
+ int64_t maxTime = 0;
+
+ for (auto ix = tasks.begin();
+ ix != tasks.end();
+ ix++) {
+
+ shared_ptr<ThreadManagerTests::Task> task = *ix;
+
+ int64_t delta = task->_endTime - task->_startTime;
+
+ assert(delta > 0);
+
+ if (task->_startTime < firstTime) {
+ firstTime = task->_startTime;
+ }
+
+ if (task->_endTime > lastTime) {
+ lastTime = task->_endTime;
+ }
+
+ if (delta < minTime) {
+ minTime = delta;
+ }
+
+ if (delta > maxTime) {
+ maxTime = delta;
+ }
+
+ averageTime += delta;
+ }
+
+ averageTime /= count;
+
+ std::cout << "\t\t\tfirst start: " << firstTime << " Last end: " << lastTime
+ << " min: " << minTime << "ms max: " << maxTime << "ms average: " << averageTime
+ << "ms" << std::endl;
+
+ bool success = (time01 - time00) >= ((int64_t)count * timeout) / (int64_t)workerCount;
+
+ std::cout << "\t\t\t" << (success ? "Success" : "Failure")
+ << "! expected time: " << ((int64_t)count * timeout) / (int64_t)workerCount << "ms elapsed time: " << time01 - time00
+ << "ms" << std::endl;
+
+ return success;
+ }
+
+ class BlockTask : public Runnable {
+
+ public:
+ BlockTask(Monitor& entryMonitor, Monitor& blockMonitor, bool& blocked, Monitor& doneMonitor, size_t& count)
+ : _entryMonitor(entryMonitor), _entered(false), _blockMonitor(blockMonitor), _blocked(blocked), _doneMonitor(doneMonitor), _count(count) {}
+
+ void run() override {
+ {
+ Synchronized s(_entryMonitor);
+ _entered = true;
+ _entryMonitor.notify();
+ }
+
+ {
+ Synchronized s(_blockMonitor);
+ while (_blocked) {
+ _blockMonitor.wait();
+ }
+ }
+
+ {
+ Synchronized s(_doneMonitor);
+ if (--_count == 0) {
+ _doneMonitor.notify();
+ }
+ }
+ }
+
+ Monitor& _entryMonitor;
+ bool _entered;
+ Monitor& _blockMonitor;
+ bool& _blocked;
+ Monitor& _doneMonitor;
+ size_t& _count;
+ };
+
+ /**
+ * Block test. Create pendingTaskCountMax tasks. Verify that we block adding the
+ * pendingTaskCountMax + 1th task. Verify that we unblock when a task completes */
+
+ bool blockTest(int64_t timeout = 100LL, size_t workerCount = 2) {
+ (void)timeout;
+ bool success = false;
+
+ try {
+
+ Monitor entryMonitor; // not used by this test
+ Monitor blockMonitor;
+ bool blocked[] = {true, true, true};
+ Monitor doneMonitor;
+
+ size_t pendingTaskMaxCount = workerCount;
+
+ size_t activeCounts[] = {workerCount, pendingTaskMaxCount, 1};
+
+ shared_ptr<ThreadManager> threadManager
+ = ThreadManager::newSimpleThreadManager(workerCount, pendingTaskMaxCount);
+
+ shared_ptr<ThreadFactory> threadFactory
+ = shared_ptr<ThreadFactory>(new ThreadFactory());
+
+ threadManager->threadFactory(threadFactory);
+
+ threadManager->start();
+
+ std::vector<shared_ptr<ThreadManagerTests::BlockTask> > tasks;
+ tasks.reserve(workerCount + pendingTaskMaxCount);
+
+ for (size_t ix = 0; ix < workerCount; ix++) {
+
+ tasks.push_back(shared_ptr<ThreadManagerTests::BlockTask>(
+ new ThreadManagerTests::BlockTask(entryMonitor, blockMonitor, blocked[0], doneMonitor, activeCounts[0])));
+ }
+
+ for (size_t ix = 0; ix < pendingTaskMaxCount; ix++) {
+
+ tasks.push_back(shared_ptr<ThreadManagerTests::BlockTask>(
+ new ThreadManagerTests::BlockTask(entryMonitor, blockMonitor, blocked[1], doneMonitor, activeCounts[1])));
+ }
+
+ for (auto ix = tasks.begin();
+ ix != tasks.end();
+ ix++) {
+ threadManager->add(*ix);
+ }
+
+ if (!(success = (threadManager->totalTaskCount() == pendingTaskMaxCount + workerCount))) {
+ throw TException("Unexpected pending task count");
+ }
+
+ shared_ptr<ThreadManagerTests::BlockTask> extraTask(
+ new ThreadManagerTests::BlockTask(entryMonitor, blockMonitor, blocked[2], doneMonitor, activeCounts[2]));
+
+ try {
+ threadManager->add(extraTask, 1);
+ throw TException("Unexpected success adding task in excess of pending task count");
+ } catch (TooManyPendingTasksException&) {
+ throw TException("Should have timed out adding task in excess of pending task count");
+ } catch (TimedOutException&) {
+ // Expected result
+ }
+
+ try {
+ threadManager->add(extraTask, -1);
+ throw TException("Unexpected success adding task in excess of pending task count");
+ } catch (TimedOutException&) {
+ throw TException("Unexpected timeout adding task in excess of pending task count");
+ } catch (TooManyPendingTasksException&) {
+ // Expected result
+ }
+
+ std::cout << "\t\t\t"
+ << "Pending tasks " << threadManager->pendingTaskCount() << std::endl;
+
+ {
+ Synchronized s(blockMonitor);
+ blocked[0] = false;
+ blockMonitor.notifyAll();
+ }
+
+ {
+ Synchronized s(doneMonitor);
+ while (activeCounts[0] != 0) {
+ doneMonitor.wait();
+ }
+ }
+
+ std::cout << "\t\t\t"
+ << "Pending tasks " << threadManager->pendingTaskCount() << std::endl;
+
+ try {
+ threadManager->add(extraTask, 1);
+ } catch (TimedOutException&) {
+ std::cout << "\t\t\t"
+ << "add timed out unexpectedly" << std::endl;
+ throw TException("Unexpected timeout adding task");
+
+ } catch (TooManyPendingTasksException&) {
+ std::cout << "\t\t\t"
+ << "add encountered too many pending exepctions" << std::endl;
+ throw TException("Unexpected timeout adding task");
+ }
+
+ // Wake up tasks that were pending before and wait for them to complete
+
+ {
+ Synchronized s(blockMonitor);
+ blocked[1] = false;
+ blockMonitor.notifyAll();
+ }
+
+ {
+ Synchronized s(doneMonitor);
+ while (activeCounts[1] != 0) {
+ doneMonitor.wait();
+ }
+ }
+
+ // Wake up the extra task and wait for it to complete
+
+ {
+ Synchronized s(blockMonitor);
+ blocked[2] = false;
+ blockMonitor.notifyAll();
+ }
+
+ {
+ Synchronized s(doneMonitor);
+ while (activeCounts[2] != 0) {
+ doneMonitor.wait();
+ }
+ }
+
+ threadManager->stop();
+
+ if (!(success = (threadManager->totalTaskCount() == 0))) {
+ throw TException("Unexpected total task count");
+ }
+
+ } catch (TException& e) {
+ std::cout << "ERROR: " << e.what() << std::endl;
+ }
+
+ std::cout << "\t\t\t" << (success ? "Success" : "Failure") << std::endl;
+ return success;
+ }
+
+
+ bool apiTest() {
+
+ // prove currentTime has milliseconds granularity since many other things depend on it
+ int64_t a = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+ sleep_(100);
+ int64_t b = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+ if (b - a < 50 || b - a > 150) {
+ std::cerr << "\t\t\texpected 100ms gap, found " << (b-a) << "ms gap instead." << std::endl;
+ return false;
+ }
+
+ return apiTestWithThreadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
+
+ }
+
+ bool apiTestWithThreadFactory(shared_ptr<ThreadFactory> threadFactory)
+ {
+ shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(1);
+ threadManager->threadFactory(threadFactory);
+
+ std::cout << "\t\t\t\tstarting.. " << std::endl;
+
+ threadManager->start();
+ threadManager->setExpireCallback(expiredNotifier); // std::bind(&ThreadManagerTests::expiredNotifier, this));
+
+#define EXPECT(FUNC, COUNT) { size_t c = FUNC; if (c != COUNT) { std::cerr << "expected " #FUNC" to be " #COUNT ", but was " << c << std::endl; return false; } }
+
+ EXPECT(threadManager->workerCount(), 1);
+ EXPECT(threadManager->idleWorkerCount(), 1);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+
+ std::cout << "\t\t\t\tadd 2nd worker.. " << std::endl;
+
+ threadManager->addWorker();
+
+ EXPECT(threadManager->workerCount(), 2);
+ EXPECT(threadManager->idleWorkerCount(), 2);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+
+ std::cout << "\t\t\t\tremove 2nd worker.. " << std::endl;
+
+ threadManager->removeWorker();
+
+ EXPECT(threadManager->workerCount(), 1);
+ EXPECT(threadManager->idleWorkerCount(), 1);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+
+ std::cout << "\t\t\t\tremove 1st worker.. " << std::endl;
+
+ threadManager->removeWorker();
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+
+ std::cout << "\t\t\t\tadd blocking task.. " << std::endl;
+
+ // We're going to throw a blocking task into the mix
+ Monitor entryMonitor; // signaled when task is running
+ Monitor blockMonitor; // to be signaled to unblock the task
+ bool blocked(true); // set to false before notifying
+ Monitor doneMonitor; // signaled when count reaches zero
+ size_t activeCount = 1;
+ shared_ptr<ThreadManagerTests::BlockTask> blockingTask(
+ new ThreadManagerTests::BlockTask(entryMonitor, blockMonitor, blocked, doneMonitor, activeCount));
+ threadManager->add(blockingTask);
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 1);
+
+ std::cout << "\t\t\t\tadd other task.. " << std::endl;
+
+ shared_ptr<ThreadManagerTests::Task> otherTask(
+ new ThreadManagerTests::Task(doneMonitor, activeCount, 0));
+
+ threadManager->add(otherTask);
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 2);
+
+ std::cout << "\t\t\t\tremove blocking task specifically.. " << std::endl;
+
+ threadManager->remove(blockingTask);
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 1);
+
+ std::cout << "\t\t\t\tremove next pending task.." << std::endl;
+
+ shared_ptr<Runnable> nextTask = threadManager->removeNextPending();
+ if (nextTask != otherTask) {
+ std::cerr << "\t\t\t\t\texpected removeNextPending to return otherTask" << std::endl;
+ return false;
+ }
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+
+ std::cout << "\t\t\t\tremove next pending task (none left).." << std::endl;
+
+ nextTask = threadManager->removeNextPending();
+ if (nextTask) {
+ std::cerr << "\t\t\t\t\texpected removeNextPending to return an empty Runnable" << std::endl;
+ return false;
+ }
+
+ std::cout << "\t\t\t\tadd 2 expired tasks and 1 not.." << std::endl;
+
+ shared_ptr<ThreadManagerTests::Task> expiredTask(
+ new ThreadManagerTests::Task(doneMonitor, activeCount, 0));
+
+ threadManager->add(expiredTask, 0, 1);
+ threadManager->add(blockingTask); // add one that hasn't expired to make sure it gets skipped
+ threadManager->add(expiredTask, 0, 1); // add a second expired to ensure removeExpiredTasks removes both
+
+ sleep_(50); // make sure enough time elapses for it to expire - the shortest expiration time is 1 millisecond
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 3);
+ EXPECT(threadManager->expiredTaskCount(), 0);
+
+ std::cout << "\t\t\t\tremove expired tasks.." << std::endl;
+
+ if (!m_expired.empty()) {
+ std::cerr << "\t\t\t\t\texpected m_expired to be empty" << std::endl;
+ return false;
+ }
+
+ threadManager->removeExpiredTasks();
+
+ if (m_expired.size() != 2) {
+ std::cerr << "\t\t\t\t\texpected m_expired to be set" << std::endl;
+ return false;
+ }
+
+ if (m_expired.front() != expiredTask) {
+ std::cerr << "\t\t\t\t\texpected m_expired[0] to be the expired task" << std::endl;
+ return false;
+ }
+ m_expired.pop_front();
+
+ if (m_expired.front() != expiredTask) {
+ std::cerr << "\t\t\t\t\texpected m_expired[1] to be the expired task" << std::endl;
+ return false;
+ }
+
+ m_expired.clear();
+
+ threadManager->remove(blockingTask);
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+ EXPECT(threadManager->expiredTaskCount(), 2);
+
+ std::cout << "\t\t\t\tadd expired task (again).." << std::endl;
+
+ threadManager->add(expiredTask, 0, 1); // expires in 1ms
+ sleep_(50); // make sure enough time elapses for it to expire - the shortest expiration time is 1ms
+
+ std::cout << "\t\t\t\tadd worker to consume expired task.." << std::endl;
+
+ threadManager->addWorker();
+ sleep_(100); // make sure it has time to spin up and expire the task
+
+ if (m_expired.empty()) {
+ std::cerr << "\t\t\t\t\texpected m_expired to be set" << std::endl;
+ return false;
+ }
+
+ if (m_expired.front() != expiredTask) {
+ std::cerr << "\t\t\t\t\texpected m_expired to be the expired task" << std::endl;
+ return false;
+ }
+
+ m_expired.clear();
+
+ EXPECT(threadManager->workerCount(), 1);
+ EXPECT(threadManager->idleWorkerCount(), 1);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+ EXPECT(threadManager->expiredTaskCount(), 3);
+
+ std::cout << "\t\t\t\ttry to remove too many workers" << std::endl;
+ try {
+ threadManager->removeWorker(2);
+ std::cerr << "\t\t\t\t\texpected InvalidArgumentException" << std::endl;
+ return false;
+ } catch (const InvalidArgumentException&) {
+ /* expected */
+ }
+
+ std::cout << "\t\t\t\tremove worker.. " << std::endl;
+
+ threadManager->removeWorker();
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+ EXPECT(threadManager->expiredTaskCount(), 3);
+
+ std::cout << "\t\t\t\tadd blocking task.. " << std::endl;
+
+ threadManager->add(blockingTask);
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 1);
+
+ std::cout << "\t\t\t\tadd worker.. " << std::endl;
+
+ threadManager->addWorker();
+ {
+ Synchronized s(entryMonitor);
+ while (!blockingTask->_entered) {
+ entryMonitor.wait();
+ }
+ }
+
+ EXPECT(threadManager->workerCount(), 1);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+
+ std::cout << "\t\t\t\tunblock task and remove worker.. " << std::endl;
+
+ {
+ Synchronized s(blockMonitor);
+ blocked = false;
+ blockMonitor.notifyAll();
+ }
+ threadManager->removeWorker();
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+
+ std::cout << "\t\t\t\tcleanup.. " << std::endl;
+
+ blockingTask.reset();
+ threadManager.reset();
+ return true;
+ }
+};
+
+}
+}
+}
+} // apache::thrift::concurrency
+
+using namespace apache::thrift::concurrency::test;
diff --git a/src/jaegertracing/thrift/lib/cpp/test/concurrency/TimerManagerTests.h b/src/jaegertracing/thrift/lib/cpp/test/concurrency/TimerManagerTests.h
new file mode 100644
index 000000000..2d1a2620a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/concurrency/TimerManagerTests.h
@@ -0,0 +1,273 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/concurrency/TimerManager.h>
+#include <thrift/concurrency/ThreadFactory.h>
+#include <thrift/concurrency/Monitor.h>
+
+#include <assert.h>
+#include <chrono>
+#include <thread>
+#include <iostream>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+namespace test {
+
+using namespace apache::thrift::concurrency;
+
+class TimerManagerTests {
+
+public:
+ class Task : public Runnable {
+ public:
+ Task(Monitor& monitor, uint64_t timeout)
+ : _timeout(timeout),
+ _startTime(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count()),
+ _endTime(0),
+ _monitor(monitor),
+ _success(false),
+ _done(false) {}
+
+ ~Task() override { std::cerr << this << std::endl; }
+
+ void run() override {
+
+ _endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+ _success = (_endTime - _startTime) >= _timeout;
+
+ {
+ Synchronized s(_monitor);
+ _done = true;
+ _monitor.notifyAll();
+ }
+ }
+
+ int64_t _timeout;
+ int64_t _startTime;
+ int64_t _endTime;
+ Monitor& _monitor;
+ bool _success;
+ bool _done;
+ };
+
+ /**
+ * This test creates two tasks and waits for the first to expire within 10%
+ * of the expected expiration time. It then verifies that the timer manager
+ * properly clean up itself and the remaining orphaned timeout task when the
+ * manager goes out of scope and its destructor is called.
+ */
+ bool test00(uint64_t timeout = 1000LL) {
+
+ shared_ptr<TimerManagerTests::Task> orphanTask
+ = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, 10 * timeout));
+
+ {
+ TimerManager timerManager;
+ timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
+ timerManager.start();
+ if (timerManager.state() != TimerManager::STARTED) {
+ std::cerr << "timerManager is not in the STARTED state, but should be" << std::endl;
+ return false;
+ }
+
+ // Don't create task yet, because its constructor sets the expected completion time, and we
+ // need to delay between inserting the two tasks into the run queue.
+ shared_ptr<TimerManagerTests::Task> task;
+
+ {
+ Synchronized s(_monitor);
+ timerManager.add(orphanTask, 10 * timeout);
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(timeout));
+
+ task.reset(new TimerManagerTests::Task(_monitor, timeout));
+ timerManager.add(task, timeout);
+ _monitor.wait();
+ }
+
+ if (!task->_done) {
+ std::cerr << "task is not done, but it should have executed" << std::endl;
+ return false;
+ }
+
+ std::cout << "\t\t\t" << (task->_success ? "Success" : "Failure") << "!" << std::endl;
+ }
+
+ if (orphanTask->_done) {
+ std::cerr << "orphan task is done, but it should not have executed" << std::endl;
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * This test creates two tasks, removes the first one then waits for the second one. It then
+ * verifies that the timer manager properly clean up itself and the remaining orphaned timeout
+ * task when the manager goes out of scope and its destructor is called.
+ */
+ bool test01(uint64_t timeout = 1000LL) {
+ TimerManager timerManager;
+ timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
+ timerManager.start();
+ assert(timerManager.state() == TimerManager::STARTED);
+
+ Synchronized s(_monitor);
+
+ // Setup the two tasks
+ shared_ptr<TimerManagerTests::Task> taskToRemove
+ = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout / 2));
+ timerManager.add(taskToRemove, taskToRemove->_timeout);
+
+ shared_ptr<TimerManagerTests::Task> task
+ = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout));
+ timerManager.add(task, task->_timeout);
+
+ // Remove one task and wait until the other has completed
+ timerManager.remove(taskToRemove);
+ _monitor.wait(timeout * 2);
+
+ assert(!taskToRemove->_done);
+ assert(task->_done);
+
+ return true;
+ }
+
+ /**
+ * This test creates two tasks with the same callback and another one, then removes the two
+ * duplicated then waits for the last one. It then verifies that the timer manager properly
+ * clean up itself and the remaining orphaned timeout task when the manager goes out of scope
+ * and its destructor is called.
+ */
+ bool test02(uint64_t timeout = 1000LL) {
+ TimerManager timerManager;
+ timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
+ timerManager.start();
+ assert(timerManager.state() == TimerManager::STARTED);
+
+ Synchronized s(_monitor);
+
+ // Setup the one tasks and add it twice
+ shared_ptr<TimerManagerTests::Task> taskToRemove
+ = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout / 3));
+ timerManager.add(taskToRemove, taskToRemove->_timeout);
+ timerManager.add(taskToRemove, taskToRemove->_timeout * 2);
+
+ shared_ptr<TimerManagerTests::Task> task
+ = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout));
+ timerManager.add(task, task->_timeout);
+
+ // Remove the first task (e.g. two timers) and wait until the other has completed
+ timerManager.remove(taskToRemove);
+ _monitor.wait(timeout * 2);
+
+ assert(!taskToRemove->_done);
+ assert(task->_done);
+
+ return true;
+ }
+
+ /**
+ * This test creates two tasks, removes the first one then waits for the second one. It then
+ * verifies that the timer manager properly clean up itself and the remaining orphaned timeout
+ * task when the manager goes out of scope and its destructor is called.
+ */
+ bool test03(uint64_t timeout = 1000LL) {
+ TimerManager timerManager;
+ timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
+ timerManager.start();
+ assert(timerManager.state() == TimerManager::STARTED);
+
+ Synchronized s(_monitor);
+
+ // Setup the two tasks
+ shared_ptr<TimerManagerTests::Task> taskToRemove
+ = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout / 2));
+ TimerManager::Timer timer = timerManager.add(taskToRemove, taskToRemove->_timeout);
+
+ shared_ptr<TimerManagerTests::Task> task
+ = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout));
+ timerManager.add(task, task->_timeout);
+
+ // Remove one task and wait until the other has completed
+ timerManager.remove(timer);
+ _monitor.wait(timeout * 2);
+
+ assert(!taskToRemove->_done);
+ assert(task->_done);
+
+ // Verify behavior when removing the removed task
+ try {
+ timerManager.remove(timer);
+ assert(nullptr == "ERROR: This remove should send a NoSuchTaskException exception.");
+ } catch (NoSuchTaskException&) {
+ }
+
+ return true;
+ }
+
+ /**
+ * This test creates one task, and tries to remove it after it has expired.
+ */
+ bool test04(uint64_t timeout = 1000LL) {
+ TimerManager timerManager;
+ timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
+ timerManager.start();
+ assert(timerManager.state() == TimerManager::STARTED);
+
+ Synchronized s(_monitor);
+
+ // Setup the task
+ shared_ptr<TimerManagerTests::Task> task
+ = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout / 10));
+ TimerManager::Timer timer = timerManager.add(task, task->_timeout);
+ task.reset();
+
+ // Wait until the task has completed
+ _monitor.wait(timeout);
+
+ // Verify behavior when removing the expired task
+ // notify is called inside the task so the task may still
+ // be running when we get here, so we need to loop...
+ for (;;) {
+ try {
+ timerManager.remove(timer);
+ assert(nullptr == "ERROR: This remove should throw NoSuchTaskException, or UncancellableTaskException.");
+ } catch (const NoSuchTaskException&) {
+ break;
+ } catch (const UncancellableTaskException&) {
+ // the thread was still exiting; try again...
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+ }
+
+ return true;
+ }
+
+ friend class TestTask;
+
+ Monitor _monitor;
+};
+
+}
+}
+}
+} // apache::thrift::concurrency
diff --git a/src/jaegertracing/thrift/lib/cpp/test/link/LinkTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/link/LinkTest.cpp
new file mode 100644
index 000000000..18e14d10c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/link/LinkTest.cpp
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+int main(int, char**) {
+ return 0;
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService1.cpp b/src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService1.cpp
new file mode 100644
index 000000000..da1790be2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService1.cpp
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * This file is a part of a link test that makes sure generated
+ * templated service headers can be included from multiple
+ * implementation files.
+ */
+
+#include "gen-cpp/ParentService.h"
diff --git a/src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService2.cpp b/src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService2.cpp
new file mode 100644
index 000000000..da1790be2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService2.cpp
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * This file is a part of a link test that makes sure generated
+ * templated service headers can be included from multiple
+ * implementation files.
+ */
+
+#include "gen-cpp/ParentService.h"
diff --git a/src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.cpp b/src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.cpp
new file mode 100644
index 000000000..c75955d27
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.cpp
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "EventLog.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+using namespace apache::thrift::concurrency;
+
+namespace {
+
+// Define environment variable DEBUG_EVENTLOG to enable debug logging
+// ex: $ DEBUG_EVENTLOG=1 processor_test
+static const char * DEBUG_EVENTLOG = getenv("DEBUG_EVENTLOG");
+
+void debug(const char* fmt, ...) {
+ if (DEBUG_EVENTLOG) {
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "\n");
+ }
+}
+}
+
+namespace apache {
+namespace thrift {
+namespace test {
+
+uint32_t EventLog::nextId_ = 0;
+
+#define EVENT_TYPE(value) EventType EventLog::value = #value
+EVENT_TYPE(ET_LOG_END);
+EVENT_TYPE(ET_CONN_CREATED);
+EVENT_TYPE(ET_CONN_DESTROYED);
+EVENT_TYPE(ET_CALL_STARTED);
+EVENT_TYPE(ET_CALL_FINISHED);
+EVENT_TYPE(ET_PROCESS);
+EVENT_TYPE(ET_PRE_READ);
+EVENT_TYPE(ET_POST_READ);
+EVENT_TYPE(ET_PRE_WRITE);
+EVENT_TYPE(ET_POST_WRITE);
+EVENT_TYPE(ET_ASYNC_COMPLETE);
+EVENT_TYPE(ET_HANDLER_ERROR);
+
+EVENT_TYPE(ET_CALL_INCREMENT_GENERATION);
+EVENT_TYPE(ET_CALL_GET_GENERATION);
+EVENT_TYPE(ET_CALL_ADD_STRING);
+EVENT_TYPE(ET_CALL_GET_STRINGS);
+EVENT_TYPE(ET_CALL_GET_DATA_WAIT);
+EVENT_TYPE(ET_CALL_ONEWAY_WAIT);
+EVENT_TYPE(ET_CALL_EXCEPTION_WAIT);
+EVENT_TYPE(ET_CALL_UNEXPECTED_EXCEPTION_WAIT);
+EVENT_TYPE(ET_CALL_SET_VALUE);
+EVENT_TYPE(ET_CALL_GET_VALUE);
+EVENT_TYPE(ET_WAIT_RETURN);
+
+EventLog::EventLog() {
+ id_ = nextId_++;
+ debug("New log: %d", id_);
+}
+
+void EventLog::append(EventType type,
+ uint32_t connectionId,
+ uint32_t callId,
+ const std::string& message) {
+ Synchronized s(monitor_);
+ debug("%d <-- %u, %u, %s \"%s\"", id_, connectionId, callId, type, message.c_str());
+
+ Event e(type, connectionId, callId, message);
+ events_.push_back(e);
+
+ monitor_.notify();
+}
+
+Event EventLog::waitForEvent(int64_t timeout) {
+ Synchronized s(monitor_);
+
+ try {
+ while (events_.empty()) {
+ monitor_.wait(timeout);
+ }
+ } catch (const TimedOutException &) {
+ return Event(ET_LOG_END, 0, 0, "");
+ }
+
+ Event event = events_.front();
+ events_.pop_front();
+ return event;
+}
+
+Event EventLog::waitForConnEvent(uint32_t connId, int64_t timeout) {
+ Synchronized s(monitor_);
+
+ auto it = events_.begin();
+ while (true) {
+ try {
+ // TODO: it would be nicer to honor timeout for the duration of this
+ // call, rather than restarting it for each call to wait(). It shouldn't
+ // be a big problem in practice, though.
+ while (it == events_.end()) {
+ monitor_.wait(timeout);
+ }
+ } catch (const TimedOutException &) {
+ return Event(ET_LOG_END, 0, 0, "");
+ }
+
+ if (it->connectionId == connId) {
+ Event event = *it;
+ events_.erase(it);
+ return event;
+ }
+ }
+}
+}
+}
+} // apache::thrift::test
diff --git a/src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.h b/src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.h
new file mode 100644
index 000000000..4f8275db9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.h
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef _THRIFT_TEST_EVENTLOG_H_
+#define _THRIFT_TEST_EVENTLOG_H_ 1
+
+#include <thrift/concurrency/Monitor.h>
+
+namespace apache {
+namespace thrift {
+namespace test {
+
+// Initially I made EventType an enum, but using char* results
+// in much more readable error messages when there is a mismatch.
+// It also lets users of EventLog easily define their own new types.
+// Comparing the literal pointer values should be safe, barring any strange
+// linking setup that results in duplicate symbols.
+typedef const char* EventType;
+
+struct Event {
+ Event(EventType type, uint32_t connectionId, uint32_t callId, const std::string& message)
+ : type(type), connectionId(connectionId), callId(callId), message(message) {}
+
+ EventType type;
+ uint32_t connectionId;
+ uint32_t callId;
+ std::string message;
+};
+
+class EventLog {
+public:
+ static EventType ET_LOG_END;
+ static EventType ET_CONN_CREATED;
+ static EventType ET_CONN_DESTROYED;
+ static EventType ET_CALL_STARTED;
+ static EventType ET_CALL_FINISHED;
+ static EventType ET_PROCESS;
+ static EventType ET_PRE_READ;
+ static EventType ET_POST_READ;
+ static EventType ET_PRE_WRITE;
+ static EventType ET_POST_WRITE;
+ static EventType ET_ASYNC_COMPLETE;
+ static EventType ET_HANDLER_ERROR;
+
+ static EventType ET_CALL_INCREMENT_GENERATION;
+ static EventType ET_CALL_GET_GENERATION;
+ static EventType ET_CALL_ADD_STRING;
+ static EventType ET_CALL_GET_STRINGS;
+ static EventType ET_CALL_GET_DATA_WAIT;
+ static EventType ET_CALL_ONEWAY_WAIT;
+ static EventType ET_CALL_UNEXPECTED_EXCEPTION_WAIT;
+ static EventType ET_CALL_EXCEPTION_WAIT;
+ static EventType ET_WAIT_RETURN;
+ static EventType ET_CALL_SET_VALUE;
+ static EventType ET_CALL_GET_VALUE;
+
+ EventLog();
+
+ void append(EventType type,
+ uint32_t connectionId,
+ uint32_t callId,
+ const std::string& message = "");
+
+ Event waitForEvent(int64_t timeout = 500);
+ Event waitForConnEvent(uint32_t connId, int64_t timeout = 500);
+
+protected:
+ typedef std::list<Event> EventList;
+
+ concurrency::Monitor monitor_;
+ EventList events_;
+ uint32_t id_;
+
+ static uint32_t nextId_;
+};
+}
+}
+} // apache::thrift::test
+
+#endif // _THRIFT_TEST_EVENTLOG_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/test/processor/Handlers.h b/src/jaegertracing/thrift/lib/cpp/test/processor/Handlers.h
new file mode 100644
index 000000000..05d19edd9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/processor/Handlers.h
@@ -0,0 +1,338 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef _THRIFT_PROCESSOR_TEST_HANDLERS_H_
+#define _THRIFT_PROCESSOR_TEST_HANDLERS_H_ 1
+
+#include "EventLog.h"
+#include "gen-cpp/ParentService.h"
+#include "gen-cpp/ChildService.h"
+
+namespace apache {
+namespace thrift {
+namespace test {
+
+class ParentHandler : virtual public ParentServiceIf {
+public:
+ ParentHandler(const std::shared_ptr<EventLog>& log)
+ : triggerMonitor(&mutex_), generation_(0), wait_(false), log_(log) {}
+
+ int32_t incrementGeneration() override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_INCREMENT_GENERATION, 0, 0);
+ return ++generation_;
+ }
+
+ int32_t getGeneration() override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_GET_GENERATION, 0, 0);
+ return generation_;
+ }
+
+ void addString(const std::string& s) override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_ADD_STRING, 0, 0);
+ strings_.push_back(s);
+ }
+
+ void getStrings(std::vector<std::string>& _return) override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_GET_STRINGS, 0, 0);
+ _return = strings_;
+ }
+
+ void getDataWait(std::string& _return, const int32_t length) override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_GET_DATA_WAIT, 0, 0);
+
+ blockUntilTriggered();
+
+ _return.append(length, 'a');
+ }
+
+ void onewayWait() override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_ONEWAY_WAIT, 0, 0);
+
+ blockUntilTriggered();
+ }
+
+ void exceptionWait(const std::string& message) override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_EXCEPTION_WAIT, 0, 0);
+
+ blockUntilTriggered();
+
+ MyError e;
+ e.message = message;
+ throw e;
+ }
+
+ void unexpectedExceptionWait(const std::string& message) override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_UNEXPECTED_EXCEPTION_WAIT, 0, 0);
+
+ blockUntilTriggered();
+
+ MyError e;
+ e.message = message;
+ throw e;
+ }
+
+ /**
+ * After prepareTriggeredCall() is invoked, calls to any of the *Wait()
+ * functions won't return until triggerPendingCalls() is invoked
+ *
+ * This has to be a separate function invoked by the main test thread
+ * in order to to avoid race conditions.
+ */
+ void prepareTriggeredCall() {
+ concurrency::Guard g(mutex_);
+ wait_ = true;
+ }
+
+ /**
+ * Wake up all calls waiting in blockUntilTriggered()
+ */
+ void triggerPendingCalls() {
+ concurrency::Guard g(mutex_);
+ wait_ = false;
+ triggerMonitor.notifyAll();
+ }
+
+protected:
+ /**
+ * blockUntilTriggered() won't return until triggerPendingCalls() is invoked
+ * in another thread.
+ *
+ * This should only be called when already holding mutex_.
+ */
+ void blockUntilTriggered() {
+ while (wait_) {
+ triggerMonitor.waitForever();
+ }
+
+ // Log an event when we return
+ log_->append(EventLog::ET_WAIT_RETURN, 0, 0);
+ }
+
+ concurrency::Mutex mutex_;
+ concurrency::Monitor triggerMonitor;
+ int32_t generation_;
+ bool wait_;
+ std::vector<std::string> strings_;
+ std::shared_ptr<EventLog> log_;
+};
+
+#ifdef _WIN32
+ #pragma warning( push )
+ #pragma warning (disable : 4250 ) //inheriting methods via dominance
+#endif
+
+class ChildHandler : public ParentHandler, virtual public ChildServiceIf {
+public:
+ ChildHandler(const std::shared_ptr<EventLog>& log) : ParentHandler(log), value_(0) {}
+
+ int32_t setValue(const int32_t value) override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_SET_VALUE, 0, 0);
+
+ int32_t oldValue = value_;
+ value_ = value;
+ return oldValue;
+ }
+
+ int32_t getValue() override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_GET_VALUE, 0, 0);
+
+ return value_;
+ }
+
+protected:
+ int32_t value_;
+};
+
+#ifdef _WIN32
+ #pragma warning( pop )
+#endif
+
+struct ConnContext {
+public:
+ ConnContext(std::shared_ptr<protocol::TProtocol> in,
+ std::shared_ptr<protocol::TProtocol> out,
+ uint32_t id)
+ : input(in), output(out), id(id) {}
+
+ std::shared_ptr<protocol::TProtocol> input;
+ std::shared_ptr<protocol::TProtocol> output;
+ uint32_t id;
+};
+
+struct CallContext {
+public:
+ CallContext(ConnContext* context, uint32_t id, const std::string& name)
+ : connContext(context), name(name), id(id) {}
+
+ ConnContext* connContext;
+ std::string name;
+ uint32_t id;
+};
+
+class ServerEventHandler : public server::TServerEventHandler {
+public:
+ ServerEventHandler(const std::shared_ptr<EventLog>& log) : nextId_(1), log_(log) {}
+
+ void preServe() override {}
+
+ void* createContext(std::shared_ptr<protocol::TProtocol> input,
+ std::shared_ptr<protocol::TProtocol> output) override {
+ ConnContext* context = new ConnContext(input, output, nextId_);
+ ++nextId_;
+ log_->append(EventLog::ET_CONN_CREATED, context->id, 0);
+ return context;
+ }
+
+ void deleteContext(void* serverContext,
+ std::shared_ptr<protocol::TProtocol> input,
+ std::shared_ptr<protocol::TProtocol> output) override {
+ auto* context = reinterpret_cast<ConnContext*>(serverContext);
+
+ if (input != context->input) {
+ abort();
+ }
+ if (output != context->output) {
+ abort();
+ }
+
+ log_->append(EventLog::ET_CONN_DESTROYED, context->id, 0);
+
+ delete context;
+ }
+
+ void processContext(void* serverContext,
+ std::shared_ptr<transport::TTransport> transport) override {
+// TODO: We currently don't test the behavior of the processContext()
+// calls. The various server implementations call processContext() at
+// slightly different times, and it is too annoying to try and account for
+// their various differences.
+//
+// TThreadedServer, TThreadPoolServer, and TSimpleServer usually wait until
+// they see the first byte of a request before calling processContext().
+// However, they don't wait for the first byte of the very first request,
+// and instead immediately call processContext() before any data is
+// received.
+//
+// TNonblockingServer always waits until receiving the full request before
+// calling processContext().
+#if 0
+ ConnContext* context = reinterpret_cast<ConnContext*>(serverContext);
+ log_->append(EventLog::ET_PROCESS, context->id, 0);
+#else
+ THRIFT_UNUSED_VARIABLE(serverContext);
+ THRIFT_UNUSED_VARIABLE(transport);
+#endif
+ }
+
+protected:
+ uint32_t nextId_;
+ std::shared_ptr<EventLog> log_;
+};
+
+class ProcessorEventHandler : public TProcessorEventHandler {
+public:
+ ProcessorEventHandler(const std::shared_ptr<EventLog>& log) : nextId_(1), log_(log) {}
+
+ void* getContext(const char* fnName, void* serverContext) override {
+ auto* connContext = reinterpret_cast<ConnContext*>(serverContext);
+
+ CallContext* context = new CallContext(connContext, nextId_, fnName);
+ ++nextId_;
+
+ log_->append(EventLog::ET_CALL_STARTED, connContext->id, context->id, fnName);
+ return context;
+ }
+
+ void freeContext(void* ctx, const char* fnName) override {
+ auto* context = reinterpret_cast<CallContext*>(ctx);
+ checkName(context, fnName);
+ log_->append(EventLog::ET_CALL_FINISHED, context->connContext->id, context->id, fnName);
+ delete context;
+ }
+
+ void preRead(void* ctx, const char* fnName) override {
+ auto* context = reinterpret_cast<CallContext*>(ctx);
+ checkName(context, fnName);
+ log_->append(EventLog::ET_PRE_READ, context->connContext->id, context->id, fnName);
+ }
+
+ void postRead(void* ctx, const char* fnName, uint32_t bytes) override {
+ THRIFT_UNUSED_VARIABLE(bytes);
+ auto* context = reinterpret_cast<CallContext*>(ctx);
+ checkName(context, fnName);
+ log_->append(EventLog::ET_POST_READ, context->connContext->id, context->id, fnName);
+ }
+
+ void preWrite(void* ctx, const char* fnName) override {
+ auto* context = reinterpret_cast<CallContext*>(ctx);
+ checkName(context, fnName);
+ log_->append(EventLog::ET_PRE_WRITE, context->connContext->id, context->id, fnName);
+ }
+
+ void postWrite(void* ctx, const char* fnName, uint32_t bytes) override {
+ THRIFT_UNUSED_VARIABLE(bytes);
+ auto* context = reinterpret_cast<CallContext*>(ctx);
+ checkName(context, fnName);
+ log_->append(EventLog::ET_POST_WRITE, context->connContext->id, context->id, fnName);
+ }
+
+ void asyncComplete(void* ctx, const char* fnName) override {
+ auto* context = reinterpret_cast<CallContext*>(ctx);
+ checkName(context, fnName);
+ log_->append(EventLog::ET_ASYNC_COMPLETE, context->connContext->id, context->id, fnName);
+ }
+
+ void handlerError(void* ctx, const char* fnName) override {
+ auto* context = reinterpret_cast<CallContext*>(ctx);
+ checkName(context, fnName);
+ log_->append(EventLog::ET_HANDLER_ERROR, context->connContext->id, context->id, fnName);
+ }
+
+protected:
+ void checkName(const CallContext* context, const char* fnName) {
+ // Note: we can't use BOOST_CHECK_EQUAL here, since the handler runs in a
+ // different thread from the test functions. Just abort if the names are
+ // different
+ if (context->name != fnName) {
+ fprintf(stderr,
+ "call context name mismatch: \"%s\" != \"%s\"\n",
+ context->name.c_str(),
+ fnName);
+ fflush(stderr);
+ abort();
+ }
+ }
+
+ uint32_t nextId_;
+ std::shared_ptr<EventLog> log_;
+};
+}
+}
+} // apache::thrift::test
+
+#endif // _THRIFT_PROCESSOR_TEST_HANDLERS_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/test/processor/ProcessorTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/processor/ProcessorTest.cpp
new file mode 100644
index 000000000..a36ef3eec
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/processor/ProcessorTest.cpp
@@ -0,0 +1,929 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * This file contains tests that ensure TProcessorEventHandler and
+ * TServerEventHandler are invoked properly by the various server
+ * implementations.
+ */
+
+#include <boost/test/unit_test.hpp>
+
+#include <thrift/concurrency/ThreadFactory.h>
+#include <thrift/concurrency/Monitor.h>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/server/TThreadedServer.h>
+#include <thrift/server/TThreadPoolServer.h>
+#include <thrift/server/TNonblockingServer.h>
+#include <thrift/server/TSimpleServer.h>
+#include <thrift/transport/TSocket.h>
+#include <thrift/transport/TNonblockingServerSocket.h>
+
+#include "EventLog.h"
+#include "ServerThread.h"
+#include "Handlers.h"
+#include "gen-cpp/ChildService.h"
+
+using namespace apache::thrift;
+using namespace apache::thrift::concurrency;
+using namespace apache::thrift::protocol;
+using namespace apache::thrift::server;
+using namespace apache::thrift::test;
+using namespace apache::thrift::transport;
+using std::string;
+using std::vector;
+
+/*
+ * Traits classes that encapsulate how to create various types of servers.
+ */
+
+class TSimpleServerTraits {
+public:
+ typedef TSimpleServer ServerType;
+
+ std::shared_ptr<TSimpleServer> createServer(
+ const std::shared_ptr<TProcessor>& processor,
+ uint16_t port,
+ const std::shared_ptr<TTransportFactory>& transportFactory,
+ const std::shared_ptr<TProtocolFactory>& protocolFactory) {
+ std::shared_ptr<TServerSocket> socket(new TServerSocket(port));
+ return std::shared_ptr<TSimpleServer>(
+ new TSimpleServer(processor, socket, transportFactory, protocolFactory));
+ }
+};
+
+class TThreadedServerTraits {
+public:
+ typedef TThreadedServer ServerType;
+
+ std::shared_ptr<TThreadedServer> createServer(
+ const std::shared_ptr<TProcessor>& processor,
+ uint16_t port,
+ const std::shared_ptr<TTransportFactory>& transportFactory,
+ const std::shared_ptr<TProtocolFactory>& protocolFactory) {
+ std::shared_ptr<TServerSocket> socket(new TServerSocket(port));
+ return std::shared_ptr<TThreadedServer>(
+ new TThreadedServer(processor, socket, transportFactory, protocolFactory));
+ }
+};
+
+class TThreadPoolServerTraits {
+public:
+ typedef TThreadPoolServer ServerType;
+
+ std::shared_ptr<TThreadPoolServer> createServer(
+ const std::shared_ptr<TProcessor>& processor,
+ uint16_t port,
+ const std::shared_ptr<TTransportFactory>& transportFactory,
+ const std::shared_ptr<TProtocolFactory>& protocolFactory) {
+ std::shared_ptr<TServerSocket> socket(new TServerSocket(port));
+
+ std::shared_ptr<ThreadFactory> threadFactory(new ThreadFactory);
+ std::shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(8);
+ threadManager->threadFactory(threadFactory);
+ threadManager->start();
+
+ return std::shared_ptr<TThreadPoolServer>(
+ new TThreadPoolServer(processor, socket, transportFactory, protocolFactory, threadManager));
+ }
+};
+
+class TNonblockingServerTraits {
+public:
+ typedef TNonblockingServer ServerType;
+
+ std::shared_ptr<TNonblockingServer> createServer(
+ const std::shared_ptr<TProcessor>& processor,
+ uint16_t port,
+ const std::shared_ptr<TTransportFactory>& transportFactory,
+ const std::shared_ptr<TProtocolFactory>& protocolFactory) {
+ // TNonblockingServer automatically uses TFramedTransport.
+ // Raise an exception if the supplied transport factory is not a
+ // TFramedTransportFactory
+ auto* framedFactory
+ = dynamic_cast<TFramedTransportFactory*>(transportFactory.get());
+ if (framedFactory == nullptr) {
+ throw TException("TNonblockingServer must use TFramedTransport");
+ }
+
+ std::shared_ptr<TNonblockingServerSocket> socket(new TNonblockingServerSocket(port));
+ std::shared_ptr<ThreadFactory> threadFactory(new ThreadFactory);
+ std::shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(8);
+ threadManager->threadFactory(threadFactory);
+ threadManager->start();
+
+ return std::shared_ptr<TNonblockingServer>(
+ new TNonblockingServer(processor, protocolFactory, socket, threadManager));
+ }
+};
+
+class TNonblockingServerNoThreadsTraits {
+public:
+ typedef TNonblockingServer ServerType;
+
+ std::shared_ptr<TNonblockingServer> createServer(
+ const std::shared_ptr<TProcessor>& processor,
+ uint16_t port,
+ const std::shared_ptr<TTransportFactory>& transportFactory,
+ const std::shared_ptr<TProtocolFactory>& protocolFactory) {
+ // TNonblockingServer automatically uses TFramedTransport.
+ // Raise an exception if the supplied transport factory is not a
+ // TFramedTransportFactory
+ auto* framedFactory
+ = dynamic_cast<TFramedTransportFactory*>(transportFactory.get());
+ if (framedFactory == nullptr) {
+ throw TException("TNonblockingServer must use TFramedTransport");
+ }
+
+ std::shared_ptr<TNonblockingServerSocket> socket(new TNonblockingServerSocket(port));
+ // Use a NULL ThreadManager
+ std::shared_ptr<ThreadManager> threadManager;
+ return std::shared_ptr<TNonblockingServer>(
+ new TNonblockingServer(processor, protocolFactory, socket, threadManager));
+ }
+};
+
+/*
+ * Traits classes for controlling if we instantiate templated or generic
+ * protocol factories, processors, clients, etc.
+ *
+ * The goal is to allow the outer test code to select which server type is
+ * being tested, and whether or not we are testing the templated classes, or
+ * the generic classes.
+ *
+ * Each specific test case can control whether we create a child or parent
+ * server, and whether we use TFramedTransport or TBufferedTransport.
+ */
+
+class UntemplatedTraits {
+public:
+ typedef TBinaryProtocolFactory ProtocolFactory;
+ typedef TBinaryProtocol Protocol;
+
+ typedef ParentServiceProcessor ParentProcessor;
+ typedef ChildServiceProcessor ChildProcessor;
+ typedef ParentServiceClient ParentClient;
+ typedef ChildServiceClient ChildClient;
+};
+
+class TemplatedTraits {
+public:
+ typedef TBinaryProtocolFactoryT<TBufferBase> ProtocolFactory;
+ typedef TBinaryProtocolT<TBufferBase> Protocol;
+
+ typedef ParentServiceProcessorT<Protocol> ParentProcessor;
+ typedef ChildServiceProcessorT<Protocol> ChildProcessor;
+ typedef ParentServiceClientT<Protocol> ParentClient;
+ typedef ChildServiceClientT<Protocol> ChildClient;
+};
+
+template <typename TemplateTraits_>
+class ParentServiceTraits {
+public:
+ typedef typename TemplateTraits_::ParentProcessor Processor;
+ typedef typename TemplateTraits_::ParentClient Client;
+ typedef ParentHandler Handler;
+
+ typedef typename TemplateTraits_::ProtocolFactory ProtocolFactory;
+ typedef typename TemplateTraits_::Protocol Protocol;
+};
+
+template <typename TemplateTraits_>
+class ChildServiceTraits {
+public:
+ typedef typename TemplateTraits_::ChildProcessor Processor;
+ typedef typename TemplateTraits_::ChildClient Client;
+ typedef ChildHandler Handler;
+
+ typedef typename TemplateTraits_::ProtocolFactory ProtocolFactory;
+ typedef typename TemplateTraits_::Protocol Protocol;
+};
+
+// TODO: It would be nicer if the TTransportFactory types defined a typedef,
+// to allow us to figure out the exact transport type without having to pass it
+// in as a separate template parameter here.
+//
+// It would also be niec if they used covariant return types. Unfortunately,
+// since they return shared_ptr instead of raw pointers, covariant return types
+// won't work.
+template <typename ServerTraits_,
+ typename ServiceTraits_,
+ typename TransportFactory_ = TFramedTransportFactory,
+ typename Transport_ = TFramedTransport>
+class ServiceState : public ServerState {
+public:
+ typedef typename ServiceTraits_::Processor Processor;
+ typedef typename ServiceTraits_::Client Client;
+ typedef typename ServiceTraits_::Handler Handler;
+
+ ServiceState()
+ : port_(0),
+ log_(new EventLog),
+ handler_(new Handler(log_)),
+ processor_(new Processor(handler_)),
+ transportFactory_(new TransportFactory_),
+ protocolFactory_(new typename ServiceTraits_::ProtocolFactory),
+ serverEventHandler_(new ServerEventHandler(log_)),
+ processorEventHandler_(new ProcessorEventHandler(log_)) {
+ processor_->setEventHandler(processorEventHandler_);
+ }
+
+ std::shared_ptr<TServer> createServer(uint16_t port) override {
+ ServerTraits_ serverTraits;
+ return serverTraits.createServer(processor_, port, transportFactory_, protocolFactory_);
+ }
+
+ std::shared_ptr<TServerEventHandler> getServerEventHandler() override { return serverEventHandler_; }
+
+ void bindSuccessful(uint16_t port) override { port_ = port; }
+
+ uint16_t getPort() const { return port_; }
+
+ const std::shared_ptr<EventLog>& getLog() const { return log_; }
+
+ const std::shared_ptr<Handler>& getHandler() const { return handler_; }
+
+ std::shared_ptr<Client> createClient() {
+ typedef typename ServiceTraits_::Protocol Protocol;
+
+ std::shared_ptr<TSocket> socket(new TSocket("127.0.0.1", port_));
+ std::shared_ptr<Transport_> transport(new Transport_(socket));
+ std::shared_ptr<Protocol> protocol(new Protocol(transport));
+ transport->open();
+
+ std::shared_ptr<Client> client(new Client(protocol));
+ return client;
+ }
+
+private:
+ uint16_t port_;
+ std::shared_ptr<EventLog> log_;
+ std::shared_ptr<Handler> handler_;
+ std::shared_ptr<Processor> processor_;
+ std::shared_ptr<TTransportFactory> transportFactory_;
+ std::shared_ptr<TProtocolFactory> protocolFactory_;
+ std::shared_ptr<TServerEventHandler> serverEventHandler_;
+ std::shared_ptr<TProcessorEventHandler> processorEventHandler_;
+};
+
+/**
+ * Check that there are no more events in the log
+ */
+void checkNoEvents(const std::shared_ptr<EventLog>& log) {
+ // Wait for an event with a very short timeout period. We don't expect
+ // anything to be present, so we will normally wait for the full timeout.
+ // On the other hand, a non-zero timeout is nice since it does give a short
+ // window for events to arrive in case there is a problem.
+ Event event = log->waitForEvent(10);
+ BOOST_CHECK_EQUAL(EventLog::ET_LOG_END, event.type);
+}
+
+/**
+ * Check for the events that should be logged when a new connection is created.
+ *
+ * Returns the connection ID allocated by the server.
+ */
+uint32_t checkNewConnEvents(const std::shared_ptr<EventLog>& log) {
+ // Check for an ET_CONN_CREATED event
+ Event event = log->waitForEvent(2500);
+ BOOST_CHECK_EQUAL(EventLog::ET_CONN_CREATED, event.type);
+
+ // Some servers call the processContext() hook immediately.
+ // Others (TNonblockingServer) only call it once a full request is received.
+ // We don't check for it yet, to allow either behavior.
+
+ return event.connectionId;
+}
+
+/**
+ * Check for the events that should be logged when a connection is closed.
+ */
+void checkCloseEvents(const std::shared_ptr<EventLog>& log, uint32_t connId) {
+ // Check for an ET_CONN_DESTROYED event
+ Event event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CONN_DESTROYED, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+
+ // Make sure there are no more events
+ checkNoEvents(log);
+}
+
+/**
+ * Check for the events that should be logged when a call is received
+ * and the handler is invoked.
+ *
+ * It does not check for anything after the handler invocation.
+ *
+ * Returns the call ID allocated by the server.
+ */
+uint32_t checkCallHandlerEvents(const std::shared_ptr<EventLog>& log,
+ uint32_t connId,
+ EventType callType,
+ const string& callName) {
+ // Call started
+ Event event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CALL_STARTED, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+ uint32_t callId = event.callId;
+
+ // Pre-read
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_PRE_READ, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ // Post-read
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_POST_READ, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ // Handler invocation
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(callType, event.type);
+ // The handler doesn't have any connection or call context,
+ // so the connectionId and callId in this event aren't valid
+
+ return callId;
+}
+
+/**
+ * Check for the events that should be after a handler returns.
+ */
+void checkCallPostHandlerEvents(const std::shared_ptr<EventLog>& log,
+ uint32_t connId,
+ uint32_t callId,
+ const string& callName) {
+ // Pre-write
+ Event event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_PRE_WRITE, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ // Post-write
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_POST_WRITE, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ // Call finished
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CALL_FINISHED, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ // It is acceptable for servers to call processContext() again immediately
+ // to start waiting on the next request. However, some servers wait before
+ // getting either a partial request or the full request before calling
+ // processContext(). We don't check for the next call to processContext()
+ // yet.
+}
+
+/**
+ * Check for the events that should be logged when a call is made.
+ *
+ * This just calls checkCallHandlerEvents() followed by
+ * checkCallPostHandlerEvents().
+ *
+ * Returns the call ID allocated by the server.
+ */
+uint32_t checkCallEvents(const std::shared_ptr<EventLog>& log,
+ uint32_t connId,
+ EventType callType,
+ const string& callName) {
+ uint32_t callId = checkCallHandlerEvents(log, connId, callType, callName);
+ checkCallPostHandlerEvents(log, connId, callId, callName);
+
+ return callId;
+}
+
+/*
+ * Test functions
+ */
+
+template <typename State_>
+void testParentService(const std::shared_ptr<State_>& state) {
+ std::shared_ptr<typename State_::Client> client = state->createClient();
+
+ int32_t gen = client->getGeneration();
+ int32_t newGen = client->incrementGeneration();
+ BOOST_CHECK_EQUAL(gen + 1, newGen);
+ newGen = client->getGeneration();
+ BOOST_CHECK_EQUAL(gen + 1, newGen);
+
+ client->addString("foo");
+ client->addString("bar");
+ client->addString("asdf");
+
+ vector<string> strings;
+ client->getStrings(strings);
+ BOOST_REQUIRE_EQUAL(3, strings.size());
+ BOOST_REQUIRE_EQUAL("foo", strings[0]);
+ BOOST_REQUIRE_EQUAL("bar", strings[1]);
+ BOOST_REQUIRE_EQUAL("asdf", strings[2]);
+}
+
+template <typename State_>
+void testChildService(const std::shared_ptr<State_>& state) {
+ std::shared_ptr<typename State_::Client> client = state->createClient();
+
+ // Test calling some of the parent methids via the a child client
+ int32_t gen = client->getGeneration();
+ int32_t newGen = client->incrementGeneration();
+ BOOST_CHECK_EQUAL(gen + 1, newGen);
+ newGen = client->getGeneration();
+ BOOST_CHECK_EQUAL(gen + 1, newGen);
+
+ // Test some of the child methods
+ client->setValue(10);
+ BOOST_CHECK_EQUAL(10, client->getValue());
+ BOOST_CHECK_EQUAL(10, client->setValue(99));
+ BOOST_CHECK_EQUAL(99, client->getValue());
+}
+
+template <typename ServerTraits, typename TemplateTraits>
+void testBasicService() {
+ typedef ServiceState<ServerTraits, ParentServiceTraits<TemplateTraits> > State;
+
+ // Start the server
+ std::shared_ptr<State> state(new State);
+ ServerThread serverThread(state, true);
+
+ testParentService(state);
+}
+
+template <typename ServerTraits, typename TemplateTraits>
+void testInheritedService() {
+ typedef ServiceState<ServerTraits, ChildServiceTraits<TemplateTraits> > State;
+
+ // Start the server
+ std::shared_ptr<State> state(new State);
+ ServerThread serverThread(state, true);
+
+ testParentService(state);
+ testChildService(state);
+}
+
+/**
+ * Test to make sure that the TServerEventHandler and TProcessorEventHandler
+ * methods are invoked in the correct order with the actual events.
+ */
+template <typename ServerTraits, typename TemplateTraits>
+void testEventSequencing() {
+ // We use TBufferedTransport for this test, instead of TFramedTransport.
+ // This way the server will start processing data as soon as it is received,
+ // instead of waiting for the full request. This is necessary so we can
+ // separate the preRead() and postRead() events.
+ typedef ServiceState<ServerTraits,
+ ChildServiceTraits<TemplateTraits>,
+ TBufferedTransportFactory,
+ TBufferedTransport> State;
+
+ // Start the server
+ std::shared_ptr<State> state(new State);
+ ServerThread serverThread(state, true);
+
+ const std::shared_ptr<EventLog>& log = state->getLog();
+
+ // Make sure we're at the end of the log
+ checkNoEvents(log);
+
+ state->getHandler()->prepareTriggeredCall();
+
+ // Make sure createContext() is called after a connection has been
+ // established. We open a plain socket instead of creating a client.
+ std::shared_ptr<TSocket> socket(new TSocket("127.0.0.1", state->getPort()));
+ socket->open();
+
+ // Make sure the proper events occurred after a new connection
+ uint32_t connId = checkNewConnEvents(log);
+
+ // Send a message header. We manually construct the request so that we
+ // can test the timing for the preRead() call.
+ string requestName = "getDataWait";
+ string eventName = "ParentService.getDataWait";
+ auto seqid = int32_t(time(nullptr));
+ TBinaryProtocol protocol(socket);
+ protocol.writeMessageBegin(requestName, T_CALL, seqid);
+ socket->flush();
+
+ // Make sure we saw the call started and pre-read events
+ Event event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CALL_STARTED, event.type);
+ BOOST_CHECK_EQUAL(eventName, event.message);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ uint32_t callId = event.callId;
+
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_PRE_READ, event.type);
+ BOOST_CHECK_EQUAL(eventName, event.message);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+
+ // Make sure there are no new events
+ checkNoEvents(log);
+
+ // Send the rest of the request
+ protocol.writeStructBegin("ParentService_getDataNotified_pargs");
+ protocol.writeFieldBegin("length", apache::thrift::protocol::T_I32, 1);
+ protocol.writeI32(8 * 1024 * 1024);
+ protocol.writeFieldEnd();
+ protocol.writeFieldStop();
+ protocol.writeStructEnd();
+ protocol.writeMessageEnd();
+ socket->writeEnd();
+ socket->flush();
+
+ // We should then see postRead()
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_POST_READ, event.type);
+ BOOST_CHECK_EQUAL(eventName, event.message);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+
+ // Then the handler should be invoked
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CALL_GET_DATA_WAIT, event.type);
+
+ // The handler won't respond until we notify it.
+ // Make sure there are no more events.
+ checkNoEvents(log);
+
+ // Notify the handler that it should return
+ // We just use a global lock for now, since it is easiest
+ state->getHandler()->triggerPendingCalls();
+
+ // The handler will log a separate event before it returns
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_WAIT_RETURN, event.type);
+
+ // We should then see preWrite()
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_PRE_WRITE, event.type);
+ BOOST_CHECK_EQUAL(eventName, event.message);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+
+ // We requested more data than can be buffered, and we aren't reading it,
+ // so the server shouldn't be able to finish its write yet.
+ // Make sure there are no more events.
+ checkNoEvents(log);
+
+ // Read the response header
+ string responseName;
+ int32_t responseSeqid = 0;
+ apache::thrift::protocol::TMessageType responseType;
+ protocol.readMessageBegin(responseName, responseType, responseSeqid);
+ BOOST_CHECK_EQUAL(responseSeqid, seqid);
+ BOOST_CHECK_EQUAL(requestName, responseName);
+ BOOST_CHECK_EQUAL(responseType, T_REPLY);
+ // Read the body. We just ignore it for now.
+ protocol.skip(T_STRUCT);
+
+ // Now that we have read, the server should have finished sending the data
+ // and called the postWrite() handler
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_POST_WRITE, event.type);
+ BOOST_CHECK_EQUAL(eventName, event.message);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+
+ // Call finished should be last
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CALL_FINISHED, event.type);
+ BOOST_CHECK_EQUAL(eventName, event.message);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+
+ // There should be no more events
+ checkNoEvents(log);
+
+ // Close the connection, and make sure we get a connection destroyed event
+ socket->close();
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CONN_DESTROYED, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+
+ // There should be no more events
+ checkNoEvents(log);
+}
+
+template <typename ServerTraits, typename TemplateTraits>
+void testSeparateConnections() {
+ typedef ServiceState<ServerTraits, ChildServiceTraits<TemplateTraits> > State;
+
+ // Start the server
+ std::shared_ptr<State> state(new State);
+ ServerThread serverThread(state, true);
+
+ const std::shared_ptr<EventLog>& log = state->getLog();
+
+ // Create a client
+ std::shared_ptr<typename State::Client> client1 = state->createClient();
+
+ // Make sure the expected events were logged
+ uint32_t client1Id = checkNewConnEvents(log);
+
+ // Create a second client
+ std::shared_ptr<typename State::Client> client2 = state->createClient();
+
+ // Make sure the expected events were logged
+ uint32_t client2Id = checkNewConnEvents(log);
+
+ // The two connections should have different IDs
+ BOOST_CHECK_NE(client1Id, client2Id);
+
+ // Make a call, and check for the proper events
+ int32_t value = 5;
+ client1->setValue(value);
+ uint32_t call1
+ = checkCallEvents(log, client1Id, EventLog::ET_CALL_SET_VALUE, "ChildService.setValue");
+
+ // Make a call with client2
+ int32_t v = client2->getValue();
+ BOOST_CHECK_EQUAL(value, v);
+ checkCallEvents(log, client2Id, EventLog::ET_CALL_GET_VALUE, "ChildService.getValue");
+
+ // Make another call with client1
+ v = client1->getValue();
+ BOOST_CHECK_EQUAL(value, v);
+ uint32_t call2
+ = checkCallEvents(log, client1Id, EventLog::ET_CALL_GET_VALUE, "ChildService.getValue");
+ BOOST_CHECK_NE(call1, call2);
+
+ // Close the second client, and check for the appropriate events
+ client2.reset();
+ checkCloseEvents(log, client2Id);
+}
+
+template <typename ServerTraits, typename TemplateTraits>
+void testOnewayCall() {
+ typedef ServiceState<ServerTraits, ChildServiceTraits<TemplateTraits> > State;
+
+ // Start the server
+ std::shared_ptr<State> state(new State);
+ ServerThread serverThread(state, true);
+
+ const std::shared_ptr<EventLog>& log = state->getLog();
+
+ // Create a client
+ std::shared_ptr<typename State::Client> client = state->createClient();
+ uint32_t connId = checkNewConnEvents(log);
+
+ // Make a oneway call
+ // It should return immediately, even though the server's handler
+ // won't return right away
+ state->getHandler()->prepareTriggeredCall();
+ client->onewayWait();
+ string callName = "ParentService.onewayWait";
+ uint32_t callId = checkCallHandlerEvents(log, connId, EventLog::ET_CALL_ONEWAY_WAIT, callName);
+
+ // There shouldn't be any more events
+ checkNoEvents(log);
+
+ // Trigger the handler to return
+ state->getHandler()->triggerPendingCalls();
+
+ // The handler will log an ET_WAIT_RETURN event when it wakes up
+ Event event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_WAIT_RETURN, event.type);
+
+ // Now we should see the async complete event, then call finished
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_ASYNC_COMPLETE, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CALL_FINISHED, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ // Destroy the client, and check for connection closed events
+ client.reset();
+ checkCloseEvents(log, connId);
+
+ checkNoEvents(log);
+}
+
+template <typename ServerTraits, typename TemplateTraits>
+void testExpectedError() {
+ typedef ServiceState<ServerTraits, ChildServiceTraits<TemplateTraits> > State;
+
+ // Start the server
+ std::shared_ptr<State> state(new State);
+ ServerThread serverThread(state, true);
+
+ const std::shared_ptr<EventLog>& log = state->getLog();
+
+ // Create a client
+ std::shared_ptr<typename State::Client> client = state->createClient();
+ uint32_t connId = checkNewConnEvents(log);
+
+ // Send the exceptionWait() call
+ state->getHandler()->prepareTriggeredCall();
+ string message = "test 1234 test";
+ client->send_exceptionWait(message);
+ string callName = "ParentService.exceptionWait";
+ uint32_t callId = checkCallHandlerEvents(log, connId, EventLog::ET_CALL_EXCEPTION_WAIT, callName);
+
+ // There shouldn't be any more events
+ checkNoEvents(log);
+
+ // Trigger the handler to return
+ state->getHandler()->triggerPendingCalls();
+
+ // The handler will log an ET_WAIT_RETURN event when it wakes up
+ Event event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_WAIT_RETURN, event.type);
+
+ // Now receive the response
+ try {
+ client->recv_exceptionWait();
+ BOOST_FAIL("expected MyError to be thrown");
+ } catch (const MyError& e) {
+ BOOST_CHECK_EQUAL(message, e.message);
+ // Check if std::exception::what() is handled properly
+ size_t message_pos = string(e.what()).find("TException - service has thrown: MyError");
+ BOOST_CHECK_NE(message_pos, string::npos);
+ }
+
+ // Now we should see the events for a normal call finish
+ checkCallPostHandlerEvents(log, connId, callId, callName);
+
+ // There shouldn't be any more events
+ checkNoEvents(log);
+
+ // Destroy the client, and check for connection closed events
+ client.reset();
+ checkCloseEvents(log, connId);
+
+ checkNoEvents(log);
+}
+
+template <typename ServerTraits, typename TemplateTraits>
+void testUnexpectedError() {
+ typedef ServiceState<ServerTraits, ChildServiceTraits<TemplateTraits> > State;
+
+ // Start the server
+ std::shared_ptr<State> state(new State);
+ ServerThread serverThread(state, true);
+
+ const std::shared_ptr<EventLog>& log = state->getLog();
+
+ // Create a client
+ std::shared_ptr<typename State::Client> client = state->createClient();
+ uint32_t connId = checkNewConnEvents(log);
+
+ // Send the unexpectedExceptionWait() call
+ state->getHandler()->prepareTriggeredCall();
+ string message = "1234 test 5678";
+ client->send_unexpectedExceptionWait(message);
+ string callName = "ParentService.unexpectedExceptionWait";
+ uint32_t callId
+ = checkCallHandlerEvents(log, connId, EventLog::ET_CALL_UNEXPECTED_EXCEPTION_WAIT, callName);
+
+ // There shouldn't be any more events
+ checkNoEvents(log);
+
+ // Trigger the handler to return
+ state->getHandler()->triggerPendingCalls();
+
+ // The handler will log an ET_WAIT_RETURN event when it wakes up
+ Event event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_WAIT_RETURN, event.type);
+
+ // Now receive the response
+ try {
+ client->recv_unexpectedExceptionWait();
+ BOOST_FAIL("expected TApplicationError to be thrown");
+ } catch (const TApplicationException&) {
+ }
+
+ // Now we should see a handler error event
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_HANDLER_ERROR, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ // pre-write and post-write events aren't generated after a handler error
+ // (Even for non-oneway calls where a response is written.)
+ //
+ // A call finished event is logged when the call context is destroyed
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CALL_FINISHED, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ // There shouldn't be any more events
+ checkNoEvents(log);
+
+ // Destroy the client, and check for connection closed events
+ client.reset();
+ checkCloseEvents(log, connId);
+
+ checkNoEvents(log);
+}
+
+// Macro to define simple tests that can be used with all server types
+#define DEFINE_SIMPLE_TESTS(Server, Template) \
+ BOOST_AUTO_TEST_CASE(Server##_##Template##_basicService) { \
+ testBasicService<Server##Traits, Template##Traits>(); \
+ } \
+ BOOST_AUTO_TEST_CASE(Server##_##Template##_inheritedService) { \
+ testInheritedService<Server##Traits, Template##Traits>(); \
+ } \
+ BOOST_AUTO_TEST_CASE(Server##_##Template##_oneway) { \
+ testOnewayCall<Server##Traits, Template##Traits>(); \
+ } \
+ BOOST_AUTO_TEST_CASE(Server##_##Template##_exception) { \
+ testExpectedError<Server##Traits, Template##Traits>(); \
+ } \
+ BOOST_AUTO_TEST_CASE(Server##_##Template##_unexpectedException) { \
+ testUnexpectedError<Server##Traits, Template##Traits>(); \
+ }
+
+// Tests that require the server to process multiple connections concurrently
+// (i.e., not TSimpleServer)
+#define DEFINE_CONCURRENT_SERVER_TESTS(Server, Template) \
+ BOOST_AUTO_TEST_CASE(Server##_##Template##_separateConnections) { \
+ testSeparateConnections<Server##Traits, Template##Traits>(); \
+ }
+
+// The testEventSequencing() test manually generates a request for the server,
+// and doesn't work with TFramedTransport. Therefore we can't test it with
+// TNonblockingServer.
+#define DEFINE_NOFRAME_TESTS(Server, Template) \
+ BOOST_AUTO_TEST_CASE(Server##_##Template##_eventSequencing) { \
+ testEventSequencing<Server##Traits, Template##Traits>(); \
+ }
+
+#define DEFINE_TNONBLOCKINGSERVER_TESTS(Server, Template) \
+ DEFINE_SIMPLE_TESTS(Server, Template) \
+ DEFINE_CONCURRENT_SERVER_TESTS(Server, Template)
+
+#define DEFINE_ALL_SERVER_TESTS(Server, Template) \
+ DEFINE_SIMPLE_TESTS(Server, Template) \
+ DEFINE_CONCURRENT_SERVER_TESTS(Server, Template) \
+ DEFINE_NOFRAME_TESTS(Server, Template)
+
+DEFINE_ALL_SERVER_TESTS(TThreadedServer, Templated)
+DEFINE_ALL_SERVER_TESTS(TThreadedServer, Untemplated)
+DEFINE_ALL_SERVER_TESTS(TThreadPoolServer, Templated)
+DEFINE_ALL_SERVER_TESTS(TThreadPoolServer, Untemplated)
+
+DEFINE_TNONBLOCKINGSERVER_TESTS(TNonblockingServer, Templated)
+DEFINE_TNONBLOCKINGSERVER_TESTS(TNonblockingServer, Untemplated)
+DEFINE_TNONBLOCKINGSERVER_TESTS(TNonblockingServerNoThreads, Templated)
+DEFINE_TNONBLOCKINGSERVER_TESTS(TNonblockingServerNoThreads, Untemplated)
+
+DEFINE_SIMPLE_TESTS(TSimpleServer, Templated)
+DEFINE_SIMPLE_TESTS(TSimpleServer, Untemplated)
+DEFINE_NOFRAME_TESTS(TSimpleServer, Templated)
+DEFINE_NOFRAME_TESTS(TSimpleServer, Untemplated)
+
+// TODO: We should test TEventServer in the future.
+// For now, it is known not to work correctly with TProcessorEventHandler.
+#ifdef BOOST_TEST_DYN_LINK
+bool init_unit_test_suite() {
+ ::boost::unit_test::framework::master_test_suite().p_name.value = "ProcessorTest";
+ return true;
+}
+
+int main( int argc, char* argv[] ) {
+ return ::boost::unit_test::unit_test_main(&init_unit_test_suite,argc,argv);
+}
+#else
+::boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
+ THRIFT_UNUSED_VARIABLE(argc);
+ THRIFT_UNUSED_VARIABLE(argv);
+ ::boost::unit_test::framework::master_test_suite().p_name.value = "ProcessorTest";
+ return NULL;
+}
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.cpp b/src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.cpp
new file mode 100644
index 000000000..b0505005b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.cpp
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef _THRIFT_TEST_SERVERTHREAD_TCC_
+#define _THRIFT_TEST_SERVERTHREAD_TCC_ 1
+
+#include "ServerThread.h"
+
+#include <thrift/concurrency/ThreadFactory.h>
+#include <thrift/concurrency/ThreadManager.h>
+#include <thrift/server/TThreadPoolServer.h>
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/transport/TServerSocket.h>
+
+namespace apache {
+namespace thrift {
+namespace test {
+
+void ServerThread::start() {
+ assert(!running_);
+ running_ = true;
+
+ helper_.reset(new Helper(this));
+
+ // Start the other thread
+ concurrency::ThreadFactory threadFactory;
+ threadFactory.setDetached(false);
+ thread_ = threadFactory.newThread(helper_);
+
+ thread_->start();
+
+ // Wait on the other thread to tell us that it has successfully
+ // bound to the port and started listening (or until an error occurs).
+ concurrency::Synchronized s(serverMonitor_);
+ while (!serving_ && !error_) {
+ serverMonitor_.waitForever();
+ }
+
+ if (error_) {
+ throw transport::TTransportException(transport::TTransportException::NOT_OPEN,
+ "failed to bind on server socket");
+ }
+}
+
+void ServerThread::stop() {
+ if (!running_) {
+ return;
+ }
+
+ // Tell the server to stop
+ server_->stop();
+ running_ = false;
+
+ // Wait for the server thread to exit
+ //
+ // Note: this only works if all client connections have closed. The servers
+ // generally wait for everything to be closed before exiting; there currently
+ // isn't a way to tell them to just exit now, and shut down existing
+ // connections.
+ thread_->join();
+}
+
+void ServerThread::run() {
+ /*
+ * Try binding to several ports, in case the one we want is already in use.
+ */
+ port_ = 12345;
+ unsigned int maxRetries = 10;
+ for (unsigned int n = 0; n < maxRetries; ++n) {
+ // Create the server
+ server_ = serverState_->createServer(port_);
+ // Install our helper as the server event handler, so that our
+ // preServe() method will be called once we've successfully bound to
+ // the port and are about to start listening.
+ server_->setServerEventHandler(helper_);
+
+ try {
+ // Try to serve requests
+ server_->serve();
+ } catch (const TException&) {
+ // TNonblockingServer throws a generic TException if it fails to bind.
+ // If we get a TException, we'll optimistically assume the bind failed.
+ ++port_;
+ continue;
+ }
+
+ // Seriously? serve() is pretty lame. If it fails to start serving it
+ // just returns rather than throwing an exception.
+ //
+ // We have to use our preServe() hook to tell if serve() successfully
+ // started serving and is returning because stop() is called, or if it just
+ // failed to start serving in the first place.
+ concurrency::Synchronized s(serverMonitor_);
+ if (serving_) {
+ // Oh good, we started serving and are exiting because
+ // we're trying to stop.
+ serving_ = false;
+ return;
+ } else {
+ // We never started serving, probably because we failed to bind to the
+ // port. Increment the port number and try again.
+ ++port_;
+ continue;
+ }
+ }
+
+ // We failed to bind on any port.
+ concurrency::Synchronized s(serverMonitor_);
+ error_ = true;
+ serverMonitor_.notify();
+}
+
+void ServerThread::preServe() {
+ // We bound to the port successfully, and are about to start serving requests
+ serverState_->bindSuccessful(port_);
+
+ // Set the real server event handler (replacing ourself)
+ std::shared_ptr<server::TServerEventHandler> serverEventHandler
+ = serverState_->getServerEventHandler();
+ server_->setServerEventHandler(serverEventHandler);
+
+ // Notify the main thread that we have successfully started serving requests
+ concurrency::Synchronized s(serverMonitor_);
+ serving_ = true;
+ serverMonitor_.notify();
+
+ // Invoke preServe() on the real event handler, since we ate
+ // the original preServe() event.
+ if (serverEventHandler) {
+ serverEventHandler->preServe();
+ }
+}
+}
+}
+} // apache::thrift::test
+
+#endif // _THRIFT_TEST_SERVERTHREAD_TCC_
diff --git a/src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.h b/src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.h
new file mode 100644
index 000000000..9cca2d600
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.h
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef _THRIFT_TEST_SERVERTHREAD_H_
+#define _THRIFT_TEST_SERVERTHREAD_H_ 1
+
+#include <thrift/TProcessor.h>
+#include <thrift/protocol/TProtocol.h>
+#include <thrift/server/TServer.h>
+#include <thrift/transport/TTransport.h>
+
+#include "EventLog.h"
+
+namespace apache {
+namespace thrift {
+namespace test {
+
+/**
+ * A helper class to tell ServerThread how to create the server
+ */
+class ServerState {
+public:
+ virtual ~ServerState() = default;
+
+ /**
+ * Create a server to listen on the specified port.
+ *
+ * If the server returned fails to bind to the specified port when serve() is
+ * called on it, createServer() may be called again on a different port.
+ */
+ virtual std::shared_ptr<server::TServer> createServer(uint16_t port) = 0;
+
+ /**
+ * Get the TServerEventHandler to set on the server.
+ *
+ * This is only called after the server successfully binds and is about to
+ * start serving traffic. It is invoked from the server thread, rather than
+ * the main thread.
+ */
+ virtual std::shared_ptr<server::TServerEventHandler> getServerEventHandler() {
+ return std::shared_ptr<server::TServerEventHandler>();
+ }
+
+ /**
+ * This method is called in the server thread after server binding succeeds.
+ *
+ * Subclasses may override this method if they wish to record the final
+ * port that was used for the server.
+ */
+ virtual void bindSuccessful(uint16_t /*port*/) {}
+};
+
+/**
+ * ServerThread starts a thrift server running in a separate thread.
+ */
+class ServerThread {
+public:
+ ServerThread(const std::shared_ptr<ServerState>& state, bool autoStart)
+ : port_(0),
+ running_(false),
+ serving_(false),
+ error_(false),
+ serverState_(state) {
+ if (autoStart) {
+ start();
+ }
+ }
+
+ void start();
+ void stop();
+
+ uint16_t getPort() const { return port_; }
+
+ ~ServerThread() {
+ if (running_) {
+ try {
+ stop();
+ } catch (...) {
+ GlobalOutput.printf("error shutting down server");
+ }
+ }
+ }
+
+protected:
+ // Annoying. thrift forces us to use shared_ptr, so we have to use
+ // a helper class that we can allocate on the heap and give to thrift.
+ // It would be simpler if we could just make Runnable and TServerEventHandler
+ // private base classes of ServerThread.
+ class Helper : public concurrency::Runnable, public server::TServerEventHandler {
+ public:
+ Helper(ServerThread* serverThread) : serverThread_(serverThread) {}
+
+ void run() override { serverThread_->run(); }
+
+ void preServe() override { serverThread_->preServe(); }
+
+ private:
+ ServerThread* serverThread_;
+ };
+
+ void run();
+ void preServe();
+
+ std::shared_ptr<Helper> helper_;
+
+ uint16_t port_;
+ bool running_;
+ bool serving_;
+ bool error_;
+ concurrency::Monitor serverMonitor_;
+
+ std::shared_ptr<ServerState> serverState_;
+ std::shared_ptr<server::TServer> server_;
+ std::shared_ptr<concurrency::Thread> thread_;
+};
+}
+}
+} // apache::thrift::test
+
+#endif // _THRIFT_TEST_SERVERTHREAD_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/test/processor/proc.thrift b/src/jaegertracing/thrift/lib/cpp/test/processor/proc.thrift
new file mode 100644
index 000000000..ac3c5f953
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/processor/proc.thrift
@@ -0,0 +1,22 @@
+namespace cpp apache.thrift.test
+
+exception MyError {
+ 1: string message
+}
+
+service ParentService {
+ i32 incrementGeneration()
+ i32 getGeneration()
+ void addString(1: string s)
+ list<string> getStrings()
+
+ binary getDataWait(1: i32 length)
+ oneway void onewayWait()
+ void exceptionWait(1: string message) throws (2: MyError error)
+ void unexpectedExceptionWait(1: string message)
+}
+
+service ChildService extends ParentService {
+ i32 setValue(1: i32 value)
+ i32 getValue()
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/qt/CMakeLists.txt b/src/jaegertracing/thrift/lib/cpp/test/qt/CMakeLists.txt
new file mode 100644
index 000000000..7f341cc4c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/qt/CMakeLists.txt
@@ -0,0 +1,32 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+set(CMAKE_AUTOMOC ON)
+find_package(Qt5 REQUIRED COMPONENTS Test Network)
+set(TQTcpServerTest_Qt5_SOURCES
+ TQTcpServerTest.cpp
+)
+add_executable(TQTcpServerTest_Qt5 ${TQTcpServerTest_Qt5_SOURCES})
+target_link_libraries(TQTcpServerTest_Qt5 testgencpp_cob)
+LINK_AGAINST_THRIFT_LIBRARY(TQTcpServerTest_Qt5 thriftqt5)
+LINK_AGAINST_THRIFT_LIBRARY(TQTcpServerTest_Qt5 thrift)
+target_link_libraries(TQTcpServerTest_Qt5 Qt5::Test Qt5::Network)
+
+add_test(NAME TQTcpServerTest_Qt5 COMMAND TQTcpServerTest_Qt5)
+
diff --git a/src/jaegertracing/thrift/lib/cpp/test/qt/TQTcpServerTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/qt/TQTcpServerTest.cpp
new file mode 100644
index 000000000..3371a9ae8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/qt/TQTcpServerTest.cpp
@@ -0,0 +1,113 @@
+#define BOOST_TEST_MODULE TQTcpServerTest
+#include <QTest>
+#include <iostream>
+
+#include <QTcpServer>
+#include <QTcpSocket>
+#include <QHostAddress>
+#include <QThread>
+
+#ifndef Q_MOC_RUN
+ #include "thrift/protocol/TBinaryProtocol.h"
+ #include "thrift/async/TAsyncProcessor.h"
+ #include "thrift/qt/TQTcpServer.h"
+ #include "thrift/qt/TQIODeviceTransport.h"
+
+ #include "gen-cpp/ParentService.h"
+#endif
+
+using namespace apache::thrift;
+
+struct AsyncHandler : public test::ParentServiceCobSvIf {
+ std::vector<std::string> strings;
+ void addString(std::function<void()> cob, const std::string& s) override {
+ strings.push_back(s);
+ cob();
+ }
+ void getStrings(std::function<void(std::vector<std::string> const& _return)> cob) override {
+ cob(strings);
+ }
+
+ // Overrides not used in this test
+ void incrementGeneration(std::function<void(int32_t const& _return)> cob) override {}
+ void getGeneration(std::function<void(int32_t const& _return)> cob) override {}
+ void getDataWait(std::function<void(std::string const& _return)> cob,
+ const int32_t length) override {}
+ void onewayWait(std::function<void()> cob) override {}
+ void exceptionWait(
+ std::function<void()> cob,
+ std::function<void(::apache::thrift::TDelayedException* _throw)> /* exn_cob */,
+ const std::string& message) override {}
+ void unexpectedExceptionWait(std::function<void()> cob, const std::string& message) override {}
+};
+
+class TQTcpServerTest : public QObject {
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void test_communicate();
+
+private:
+ std::shared_ptr<QThread> serverThread;
+ std::shared_ptr<async::TQTcpServer> server;
+ std::shared_ptr<test::ParentServiceClient> client;
+};
+
+void TQTcpServerTest::initTestCase() {
+ // setup server
+ std::shared_ptr<QTcpServer> serverSocket = std::make_shared<QTcpServer>();
+ server.reset(new async::TQTcpServer(serverSocket,
+ std::make_shared<test::ParentServiceAsyncProcessor>(
+ std::make_shared<AsyncHandler>()),
+ std::make_shared<protocol::TBinaryProtocolFactory>()));
+ QVERIFY(serverSocket->listen(QHostAddress::LocalHost));
+ int port = serverSocket->serverPort();
+ QVERIFY(port > 0);
+
+ //setup server thread and move server to it
+ serverThread.reset(new QThread());
+ serverSocket->moveToThread(serverThread.get());
+ server->moveToThread(serverThread.get());
+ serverThread->start();
+
+ // setup client
+ std::shared_ptr<QTcpSocket> socket = std::make_shared<QTcpSocket>();
+ client.reset(new test::ParentServiceClient(std::make_shared<protocol::TBinaryProtocol>(
+ std::make_shared<transport::TQIODeviceTransport>(socket))));
+ socket->connectToHost(QHostAddress::LocalHost, port);
+ QVERIFY(socket->waitForConnected());
+}
+
+void TQTcpServerTest::cleanupTestCase() {
+ //first, stop the thread which holds the server
+ serverThread->quit();
+ serverThread->wait();
+ // now, it is safe to delete the server
+ server.reset();
+ // delete thread now
+ serverThread.reset();
+
+ // cleanup client
+ client.reset();
+}
+
+void TQTcpServerTest::test_communicate() {
+ client->addString("foo");
+ client->addString("bar");
+
+ std::vector<std::string> reply;
+ client->getStrings(reply);
+ QCOMPARE(QString::fromStdString(reply[0]), QString("foo"));
+ QCOMPARE(QString::fromStdString(reply[1]), QString("bar"));
+}
+
+
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
+QTEST_GUILESS_MAIN(TQTcpServerTest);
+#else
+#undef QT_GUI_LIB
+QTEST_MAIN(TQTcpServerTest);
+#endif
+#include "TQTcpServerTest.moc"
diff --git a/src/jaegertracing/thrift/lib/cpp/thrift-nb.pc.in b/src/jaegertracing/thrift/lib/cpp/thrift-nb.pc.in
new file mode 100755
index 000000000..2c6a96973
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/thrift-nb.pc.in
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Thrift
+Description: Thrift Nonblocking API
+Version: @VERSION@
+Requires: thrift = @VERSION@
+Libs: -L${libdir} -lthriftnb
+Cflags: -I${includedir}
diff --git a/src/jaegertracing/thrift/lib/cpp/thrift-qt5.pc.in b/src/jaegertracing/thrift/lib/cpp/thrift-qt5.pc.in
new file mode 100755
index 000000000..a8b16663e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/thrift-qt5.pc.in
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Thrift
+Description: Thrift Qt5 API
+Version: @VERSION@
+Requires: thrift = @VERSION@
+Libs: -L${libdir} -lthriftqt5
+Cflags: -I${includedir}
diff --git a/src/jaegertracing/thrift/lib/cpp/thrift-z.pc.in b/src/jaegertracing/thrift/lib/cpp/thrift-z.pc.in
new file mode 100755
index 000000000..467d2e11c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/thrift-z.pc.in
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Thrift
+Description: Thrift Zlib API
+Version: @VERSION@
+Requires: thrift = @VERSION@
+Libs: -L${libdir} -lthriftz
+Cflags: -I${includedir}
diff --git a/src/jaegertracing/thrift/lib/cpp/thrift.pc.in b/src/jaegertracing/thrift/lib/cpp/thrift.pc.in
new file mode 100755
index 000000000..d11e6db29
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/thrift.pc.in
@@ -0,0 +1,29 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Thrift
+Description: Thrift C++ API
+Version: @VERSION@
+Libs: -L${libdir} -lthrift
+Cflags: -I${includedir}
diff --git a/src/jaegertracing/thrift/lib/cpp/thrift.sln b/src/jaegertracing/thrift/lib/cpp/thrift.sln
new file mode 100644
index 000000000..cb0f4559b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/thrift.sln
@@ -0,0 +1,61 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libthriftnb", "libthriftnb.vcxproj", "{D8696CCE-7D46-4659-B432-91754A41DEB0}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libthrift", "libthrift.vcxproj", "{DD26F57E-60F2-4F37-A616-D219A9BF338F}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{006984E0-7CC1-47E2-ACD2-A40FE4D38693}"
+ ProjectSection(SolutionItems) = preProject
+ README.md = README.md
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Debug-mt|Win32 = Debug-mt|Win32
+ Debug-mt|x64 = Debug-mt|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ Release-mt|Win32 = Release-mt|Win32
+ Release-mt|x64 = Release-mt|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D8696CCE-7D46-4659-B432-91754A41DEB0}.Debug|Win32.ActiveCfg = Debug|Win32
+ {D8696CCE-7D46-4659-B432-91754A41DEB0}.Debug|Win32.Build.0 = Debug|Win32
+ {D8696CCE-7D46-4659-B432-91754A41DEB0}.Debug|x64.ActiveCfg = Debug|x64
+ {D8696CCE-7D46-4659-B432-91754A41DEB0}.Debug|x64.Build.0 = Debug|x64
+ {D8696CCE-7D46-4659-B432-91754A41DEB0}.Debug-mt|Win32.ActiveCfg = Debug-mt|Win32
+ {D8696CCE-7D46-4659-B432-91754A41DEB0}.Debug-mt|Win32.Build.0 = Debug-mt|Win32
+ {D8696CCE-7D46-4659-B432-91754A41DEB0}.Debug-mt|x64.ActiveCfg = Debug-mt|x64
+ {D8696CCE-7D46-4659-B432-91754A41DEB0}.Debug-mt|x64.Build.0 = Debug-mt|x64
+ {D8696CCE-7D46-4659-B432-91754A41DEB0}.Release|Win32.ActiveCfg = Release|Win32
+ {D8696CCE-7D46-4659-B432-91754A41DEB0}.Release|Win32.Build.0 = Release|Win32
+ {D8696CCE-7D46-4659-B432-91754A41DEB0}.Release|x64.ActiveCfg = Release|x64
+ {D8696CCE-7D46-4659-B432-91754A41DEB0}.Release|x64.Build.0 = Release|x64
+ {D8696CCE-7D46-4659-B432-91754A41DEB0}.Release-mt|Win32.ActiveCfg = Release-mt|Win32
+ {D8696CCE-7D46-4659-B432-91754A41DEB0}.Release-mt|Win32.Build.0 = Release-mt|Win32
+ {D8696CCE-7D46-4659-B432-91754A41DEB0}.Release-mt|x64.ActiveCfg = Release-mt|x64
+ {D8696CCE-7D46-4659-B432-91754A41DEB0}.Release-mt|x64.Build.0 = Release-mt|x64
+ {DD26F57E-60F2-4F37-A616-D219A9BF338F}.Debug|Win32.ActiveCfg = Debug|Win32
+ {DD26F57E-60F2-4F37-A616-D219A9BF338F}.Debug|Win32.Build.0 = Debug|Win32
+ {DD26F57E-60F2-4F37-A616-D219A9BF338F}.Debug|x64.ActiveCfg = Debug|x64
+ {DD26F57E-60F2-4F37-A616-D219A9BF338F}.Debug|x64.Build.0 = Debug|x64
+ {DD26F57E-60F2-4F37-A616-D219A9BF338F}.Debug-mt|Win32.ActiveCfg = Debug-mt|Win32
+ {DD26F57E-60F2-4F37-A616-D219A9BF338F}.Debug-mt|Win32.Build.0 = Debug-mt|Win32
+ {DD26F57E-60F2-4F37-A616-D219A9BF338F}.Debug-mt|x64.ActiveCfg = Debug-mt|x64
+ {DD26F57E-60F2-4F37-A616-D219A9BF338F}.Debug-mt|x64.Build.0 = Debug-mt|x64
+ {DD26F57E-60F2-4F37-A616-D219A9BF338F}.Release|Win32.ActiveCfg = Release|Win32
+ {DD26F57E-60F2-4F37-A616-D219A9BF338F}.Release|Win32.Build.0 = Release|Win32
+ {DD26F57E-60F2-4F37-A616-D219A9BF338F}.Release|x64.ActiveCfg = Release|x64
+ {DD26F57E-60F2-4F37-A616-D219A9BF338F}.Release|x64.Build.0 = Release|x64
+ {DD26F57E-60F2-4F37-A616-D219A9BF338F}.Release-mt|Win32.ActiveCfg = Release-mt|Win32
+ {DD26F57E-60F2-4F37-A616-D219A9BF338F}.Release-mt|Win32.Build.0 = Release-mt|Win32
+ {DD26F57E-60F2-4F37-A616-D219A9BF338F}.Release-mt|x64.ActiveCfg = Release-mt|x64
+ {DD26F57E-60F2-4F37-A616-D219A9BF338F}.Release-mt|x64.Build.0 = Release-mt|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/jaegertracing/thrift/lib/csharp/Makefile.am b/src/jaegertracing/thrift/lib/csharp/Makefile.am
new file mode 100644
index 000000000..cc2bbc96c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/Makefile.am
@@ -0,0 +1,114 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+SUBDIRS = . test/Multiplex
+
+THRIFTCODE = \
+ src/Collections/THashSet.cs \
+ src/Collections/TCollections.cs \
+ src/Properties/AssemblyInfo.cs \
+ src/Protocol/TAbstractBase.cs \
+ src/Protocol/TBase.cs \
+ src/Protocol/TBase64Utils.cs \
+ src/Protocol/TJSONProtocol.cs \
+ src/Protocol/TProtocolException.cs \
+ src/Protocol/TProtocolFactory.cs \
+ src/Protocol/TList.cs \
+ src/Protocol/TSet.cs \
+ src/Protocol/TMap.cs \
+ src/Protocol/TProtocolUtil.cs \
+ src/Protocol/TMessageType.cs \
+ src/Protocol/TProtocol.cs \
+ src/Protocol/TProtocolDecorator.cs \
+ src/Protocol/TMultiplexedProtocol.cs \
+ src/Protocol/TMultiplexedProcessor.cs \
+ src/Protocol/TType.cs \
+ src/Protocol/TField.cs \
+ src/Protocol/TMessage.cs \
+ src/Protocol/TStruct.cs \
+ src/Protocol/TBinaryProtocol.cs \
+ src/Protocol/TCompactProtocol.cs \
+ src/Server/TThreadedServer.cs \
+ src/Server/TThreadPoolServer.cs \
+ src/Server/TSimpleServer.cs \
+ src/Server/TServer.cs \
+ src/Server/TServerEventHandler.cs \
+ src/Transport/TBufferedTransport.cs \
+ src/Transport/TTransport.cs \
+ src/Transport/TSocket.cs \
+ src/Transport/TSocketVersionizer.cs \
+ src/Transport/TTransportException.cs \
+ src/Transport/TStreamTransport.cs \
+ src/Transport/TFramedTransport.cs \
+ src/Transport/TServerTransport.cs \
+ src/Transport/TServerSocket.cs \
+ src/Transport/TTransportFactory.cs \
+ src/Transport/THttpClient.cs \
+ src/Transport/THttpHandler.cs \
+ src/Transport/TMemoryBuffer.cs \
+ src/Transport/TNamedPipeClientTransport.cs \
+ src/Transport/TNamedPipeServerTransport.cs \
+ src/Transport/TTLSSocket.cs \
+ src/Transport/TTLSServerSocket.cs \
+ src/TProcessor.cs \
+ src/TProcessorFactory.cs \
+ src/TSingletonProcessorFactory.cs \
+ src/TPrototypeProcessorFactory.cs \
+ src/TControllingHandler.cs \
+ src/TException.cs \
+ src/TApplicationException.cs
+
+if MONO_MCS
+export CSC = mcs
+else
+export CSC = gmcs
+endif
+
+if NET_2_0
+export CSC_DEFINES = -d:NET_2_0
+endif
+
+all-local: Thrift.dll Thrift.45.dll
+
+Thrift.dll: $(THRIFTCODE)
+ $(CSC) $(CSC_DEFINES) -out:$@ -target:library -reference:System.Web $(THRIFTCODE)
+
+Thrift.45.dll: $(THRIFTCODE)
+ $(CSC) $(CSC_DEFINES) -out:$@ -target:library -reference:System.Web $(THRIFTCODE)
+
+CLEANFILES = \
+ Thrift.dll \
+ Thrift.45.dll
+
+DISTCLEANFILES = \
+ Makefile.in
+
+EXTRA_DIST = \
+ $(THRIFTCODE) \
+ ThriftMSBuildTask \
+ src/Thrift.csproj \
+ src/Thrift.45.csproj \
+ src/Thrift.sln \
+ src/Net35/ExtensionsNet35.cs \
+ src/Transport/TSilverlightSocket.cs \
+ src/Transport/THttpTaskAsyncHandler.cs \
+ src/TAsyncProcessor.cs \
+ test \
+ coding_standards.md \
+ README.md
diff --git a/src/jaegertracing/thrift/lib/csharp/README.md b/src/jaegertracing/thrift/lib/csharp/README.md
new file mode 100644
index 000000000..5fc14cb3c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/README.md
@@ -0,0 +1,32 @@
+Thrift C# Software Library
+
+Deprecation notice
+=======
+
+Per [THRIFT-4723](https://issues.apache.org/jira/browse/THRIFT-4723), both CSharp and Netcore targets are deprecated
+and will be removed with the next release. Migrate to the [NetStd language target](../netstd/README.md) instead.
+
+License
+=======
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+Using Thrift with C#
+====================
+
+Thrift requires Mono >= 1.2.6 or .NET framework >= 3.5
diff --git a/src/jaegertracing/thrift/lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs b/src/jaegertracing/thrift/lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..6e99dd9fd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ThriftMSBuildTask")]
+[assembly: AssemblyDescription("MSBuild Task to generate csharp from .thrift files, and compile the code into a library: ThriftImpl.dll")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("The Apache Software Foundation")]
+[assembly: AssemblyProduct("ThriftMSBuildTask")]
+[assembly: AssemblyCopyright("The Apache Software Foundation")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+//@TODO where to put License information?
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("5095e09d-7b95-4be1-b250-e1c1db1c485e")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("0.13.0.*")]
+[assembly: AssemblyFileVersion("0.13.0.*")]
diff --git a/src/jaegertracing/thrift/lib/csharp/ThriftMSBuildTask/ThriftBuild.cs b/src/jaegertracing/thrift/lib/csharp/ThriftMSBuildTask/ThriftBuild.cs
new file mode 100644
index 000000000..4e6d30112
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/ThriftMSBuildTask/ThriftBuild.cs
@@ -0,0 +1,246 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using Microsoft.Build.Tasks;
+using System.IO;
+using System.Diagnostics;
+
+namespace ThriftMSBuildTask
+{
+ /// <summary>
+ /// MSBuild Task to generate csharp from .thrift files, and compile the code into a library: ThriftImpl.dll
+ /// </summary>
+ public class ThriftBuild : Task
+ {
+ /// <summary>
+ /// The full path to the thrift.exe compiler
+ /// </summary>
+ [Required]
+ public ITaskItem ThriftExecutable
+ {
+ get;
+ set;
+ }
+
+ /// <summary>
+ /// The full path to a thrift.dll C# library
+ /// </summary>
+ [Required]
+ public ITaskItem ThriftLibrary
+ {
+ get;
+ set;
+ }
+
+ /// <summary>
+ /// A direcotry containing .thrift files
+ /// </summary>
+ [Required]
+ public ITaskItem ThriftDefinitionDir
+ {
+ get;
+ set;
+ }
+
+ /// <summary>
+ /// The name of the auto-gen and compiled thrift library. It will placed in
+ /// the same directory as ThriftLibrary
+ /// </summary>
+ [Required]
+ public ITaskItem OutputName
+ {
+ get;
+ set;
+ }
+
+ /// <summary>
+ /// The full path to the compiled ThriftLibrary. This allows msbuild tasks to use this
+ /// output as a variable for use elsewhere.
+ /// </summary>
+ [Output]
+ public ITaskItem ThriftImplementation
+ {
+ get { return thriftImpl; }
+ }
+
+ private ITaskItem thriftImpl;
+ private const string lastCompilationName = "LAST_COMP_TIMESTAMP";
+
+ //use the Message Build Task to write something to build log
+ private void LogMessage(string text, MessageImportance importance)
+ {
+ Message m = new Message();
+ m.Text = text;
+ m.Importance = importance.ToString();
+ m.BuildEngine = this.BuildEngine;
+ m.Execute();
+ }
+
+ //recursively find .cs files in srcDir, paths should initially be non-null and empty
+ private void FindSourcesHelper(string srcDir, List<string> paths)
+ {
+ string[] files = Directory.GetFiles(srcDir, "*.cs");
+ foreach (string f in files)
+ {
+ paths.Add(f);
+ }
+ string[] dirs = Directory.GetDirectories(srcDir);
+ foreach (string dir in dirs)
+ {
+ FindSourcesHelper(dir, paths);
+ }
+ }
+
+ /// <summary>
+ /// Quote paths with spaces
+ /// </summary>
+ private string SafePath(string path)
+ {
+ if (path.Contains(' ') && !path.StartsWith("\""))
+ {
+ return "\"" + path + "\"";
+ }
+ return path;
+ }
+
+ private ITaskItem[] FindSources(string srcDir)
+ {
+ List<string> files = new List<string>();
+ FindSourcesHelper(srcDir, files);
+ ITaskItem[] items = new ITaskItem[files.Count];
+ for (int i = 0; i < items.Length; i++)
+ {
+ items[i] = new TaskItem(files[i]);
+ }
+ return items;
+ }
+
+ private string LastWriteTime(string defDir)
+ {
+ string[] files = Directory.GetFiles(defDir, "*.thrift");
+ DateTime d = (new DirectoryInfo(defDir)).LastWriteTime;
+ foreach(string file in files)
+ {
+ FileInfo f = new FileInfo(file);
+ DateTime curr = f.LastWriteTime;
+ if (DateTime.Compare(curr, d) > 0)
+ {
+ d = curr;
+ }
+ }
+ return d.ToFileTimeUtc().ToString();
+ }
+
+ public override bool Execute()
+ {
+ string defDir = SafePath(ThriftDefinitionDir.ItemSpec);
+ //look for last compilation timestamp
+ string lastBuildPath = Path.Combine(defDir, lastCompilationName);
+ DirectoryInfo defDirInfo = new DirectoryInfo(defDir);
+ string lastWrite = LastWriteTime(defDir);
+ if (File.Exists(lastBuildPath))
+ {
+ string lastComp = File.ReadAllText(lastBuildPath);
+ //don't recompile if the thrift library has been updated since lastComp
+ FileInfo f = new FileInfo(ThriftLibrary.ItemSpec);
+ string thriftLibTime = f.LastWriteTimeUtc.ToFileTimeUtc().ToString();
+ if (lastComp.CompareTo(thriftLibTime) < 0)
+ {
+ //new thrift library, do a compile
+ lastWrite = thriftLibTime;
+ }
+ else if (lastComp == lastWrite || (lastComp == thriftLibTime && lastComp.CompareTo(lastWrite) > 0))
+ {
+ //the .thrift dir hasn't been written to since last compilation, don't need to do anything
+ LogMessage("ThriftImpl up-to-date", MessageImportance.High);
+ return true;
+ }
+ }
+
+ //find the directory of the thriftlibrary (that's where output will go)
+ FileInfo thriftLibInfo = new FileInfo(SafePath(ThriftLibrary.ItemSpec));
+ string thriftDir = thriftLibInfo.Directory.FullName;
+
+ string genDir = Path.Combine(thriftDir, "gen-csharp");
+ if (Directory.Exists(genDir))
+ {
+ try
+ {
+ Directory.Delete(genDir, true);
+ }
+ catch { /*eh i tried, just over-write now*/}
+ }
+
+ //run the thrift executable to generate C#
+ foreach (string thriftFile in Directory.GetFiles(defDir, "*.thrift"))
+ {
+ LogMessage("Generating code for: " + thriftFile, MessageImportance.Normal);
+ Process p = new Process();
+ p.StartInfo.FileName = SafePath(ThriftExecutable.ItemSpec);
+ p.StartInfo.Arguments = "--gen csharp -o " + SafePath(thriftDir) + " -r " + thriftFile;
+ p.StartInfo.UseShellExecute = false;
+ p.StartInfo.CreateNoWindow = true;
+ p.StartInfo.RedirectStandardOutput = false;
+ p.Start();
+ p.WaitForExit();
+ if (p.ExitCode != 0)
+ {
+ LogMessage("thrift.exe failed to compile " + thriftFile, MessageImportance.High);
+ return false;
+ }
+ if (p.ExitCode != 0)
+ {
+ LogMessage("thrift.exe failed to compile " + thriftFile, MessageImportance.High);
+ return false;
+ }
+ }
+
+ Csc csc = new Csc();
+ csc.TargetType = "library";
+ csc.References = new ITaskItem[] { new TaskItem(ThriftLibrary.ItemSpec) };
+ csc.EmitDebugInformation = true;
+ string outputPath = Path.Combine(thriftDir, OutputName.ItemSpec);
+ csc.OutputAssembly = new TaskItem(outputPath);
+ csc.Sources = FindSources(Path.Combine(thriftDir, "gen-csharp"));
+ csc.BuildEngine = this.BuildEngine;
+ LogMessage("Compiling generated cs...", MessageImportance.Normal);
+ if (!csc.Execute())
+ {
+ return false;
+ }
+
+ //write file to defDir to indicate a build was successfully completed
+ File.WriteAllText(lastBuildPath, lastWrite);
+
+ thriftImpl = new TaskItem(outputPath);
+
+ return true;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj b/src/jaegertracing/thrift/lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj
new file mode 100644
index 000000000..f4a26de45
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{EC0A0231-66EA-4593-A792-C6CA3BB8668E}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>ThriftMSBuildTask</RootNamespace>
+ <AssemblyName>ThriftMSBuildTask</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <FileUpgradeFlags>
+ </FileUpgradeFlags>
+ <OldToolsVersion>3.5</OldToolsVersion>
+ <UpgradeBackupLocation />
+ <PublishUrl>publish\</PublishUrl>
+ <Install>true</Install>
+ <InstallFrom>Disk</InstallFrom>
+ <UpdateEnabled>false</UpdateEnabled>
+ <UpdateMode>Foreground</UpdateMode>
+ <UpdateInterval>7</UpdateInterval>
+ <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+ <UpdatePeriodically>false</UpdatePeriodically>
+ <UpdateRequired>false</UpdateRequired>
+ <MapFileExtensions>true</MapFileExtensions>
+ <ApplicationRevision>0</ApplicationRevision>
+ <ApplicationVersion>0.13.0.0</ApplicationVersion>
+ <IsWebBootstrapper>false</IsWebBootstrapper>
+ <UseApplicationTrust>false</UseApplicationTrust>
+ <BootstrapperEnabled>true</BootstrapperEnabled>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Microsoft.Build.Framework" />
+ <Reference Include="Microsoft.Build.Tasks" />
+ <Reference Include="Microsoft.Build.Utilities" />
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="ThriftBuild.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
+ <Visible>False</Visible>
+ <ProductName>Windows Installer 3.1</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
diff --git a/src/jaegertracing/thrift/lib/csharp/coding_standards.md b/src/jaegertracing/thrift/lib/csharp/coding_standards.md
new file mode 100644
index 000000000..dc87190bf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/coding_standards.md
@@ -0,0 +1,6 @@
+## C# Coding Standards
+
+Please follow:
+ * [Thrift General Coding Standards](/doc/coding_standards.md)
+ * [MSDN C# Coding Conventions](http://msdn.microsoft.com/en-us/library/ff926074.aspx)
+ * [C# Coding Guidelines](http://csharpguidelines.codeplex.com/)
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Collections/TCollections.cs b/src/jaegertracing/thrift/lib/csharp/src/Collections/TCollections.cs
new file mode 100644
index 000000000..84afb6a62
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Collections/TCollections.cs
@@ -0,0 +1,94 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+using System;
+using System.Collections;
+
+namespace Thrift.Collections
+{
+ public class TCollections
+ {
+ /// <summary>
+ /// This will return true if the two collections are value-wise the same.
+ /// If the collection contains a collection, the collections will be compared using this method.
+ /// </summary>
+ public static bool Equals (IEnumerable first, IEnumerable second)
+ {
+ if (first == null && second == null)
+ {
+ return true;
+ }
+ if (first == null || second == null)
+ {
+ return false;
+ }
+ IEnumerator fiter = first.GetEnumerator ();
+ IEnumerator siter = second.GetEnumerator ();
+
+ bool fnext = fiter.MoveNext ();
+ bool snext = siter.MoveNext ();
+ while (fnext && snext)
+ {
+ IEnumerable fenum = fiter.Current as IEnumerable;
+ IEnumerable senum = siter.Current as IEnumerable;
+ if (fenum != null && senum != null)
+ {
+ if (!Equals(fenum, senum))
+ {
+ return false;
+ }
+ }
+ else if (fenum == null ^ senum == null)
+ {
+ return false;
+ }
+ else if (!Equals(fiter.Current, siter.Current))
+ {
+ return false;
+ }
+ fnext = fiter.MoveNext();
+ snext = siter.MoveNext();
+ }
+
+ return fnext == snext;
+ }
+
+ /// <summary>
+ /// This returns a hashcode based on the value of the enumerable.
+ /// </summary>
+ public static int GetHashCode (IEnumerable enumerable)
+ {
+ if (enumerable == null)
+ {
+ return 0;
+ }
+
+ int hashcode = 0;
+ foreach (object obj in enumerable)
+ {
+ IEnumerable enum2 = obj as IEnumerable;
+ int objHash = enum2 == null ? obj.GetHashCode () : GetHashCode (enum2);
+ unchecked
+ {
+ hashcode = (hashcode * 397) ^ (objHash);
+ }
+ }
+ return hashcode;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Collections/THashSet.cs b/src/jaegertracing/thrift/lib/csharp/src/Collections/THashSet.cs
new file mode 100644
index 000000000..e29271a93
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Collections/THashSet.cs
@@ -0,0 +1,160 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+#if SILVERLIGHT
+using System.Runtime.Serialization;
+#endif
+
+namespace Thrift.Collections
+{
+#if SILVERLIGHT
+ [DataContract]
+#else
+ [Serializable]
+#endif
+ public class THashSet<T> : ICollection<T>
+ {
+#if NET_2_0 || SILVERLIGHT
+#if SILVERLIGHT
+ [DataMember]
+#endif
+ TDictSet<T> set = new TDictSet<T>();
+#else
+ HashSet<T> set = new HashSet<T>();
+#endif
+ public int Count
+ {
+ get { return set.Count; }
+ }
+
+ public bool IsReadOnly
+ {
+ get { return false; }
+ }
+
+ public void Add(T item)
+ {
+ set.Add(item);
+ }
+
+ public void Clear()
+ {
+ set.Clear();
+ }
+
+ public bool Contains(T item)
+ {
+ return set.Contains(item);
+ }
+
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ set.CopyTo(array, arrayIndex);
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return set.GetEnumerator();
+ }
+
+ IEnumerator<T> IEnumerable<T>.GetEnumerator()
+ {
+ return ((IEnumerable<T>)set).GetEnumerator();
+ }
+
+ public bool Remove(T item)
+ {
+ return set.Remove(item);
+ }
+
+#if NET_2_0 || SILVERLIGHT
+#if SILVERLIGHT
+ [DataContract]
+#endif
+ private class TDictSet<V> : ICollection<V>
+ {
+#if SILVERLIGHT
+ [DataMember]
+#endif
+ Dictionary<V, TDictSet<V>> dict = new Dictionary<V, TDictSet<V>>();
+
+ public int Count
+ {
+ get { return dict.Count; }
+ }
+
+ public bool IsReadOnly
+ {
+ get { return false; }
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return ((IEnumerable)dict.Keys).GetEnumerator();
+ }
+
+ IEnumerator<V> IEnumerable<V>.GetEnumerator()
+ {
+ return dict.Keys.GetEnumerator();
+ }
+
+ public bool Add(V item)
+ {
+ if (!dict.ContainsKey(item))
+ {
+ dict[item] = this;
+ return true;
+ }
+
+ return false;
+ }
+
+ void ICollection<V>.Add(V item)
+ {
+ Add(item);
+ }
+
+ public void Clear()
+ {
+ dict.Clear();
+ }
+
+ public bool Contains(V item)
+ {
+ return dict.ContainsKey(item);
+ }
+
+ public void CopyTo(V[] array, int arrayIndex)
+ {
+ dict.Keys.CopyTo(array, arrayIndex);
+ }
+
+ public bool Remove(V item)
+ {
+ return dict.Remove(item);
+ }
+ }
+#endif
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Net35/ExtensionsNet35.cs b/src/jaegertracing/thrift/lib/csharp/src/Net35/ExtensionsNet35.cs
new file mode 100644
index 000000000..73a423288
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Net35/ExtensionsNet35.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+#if (!NET45)
+namespace Thrift
+{
+ static class StreamExtensionsNet35
+ {
+ // CopyTo() has been added in 4.0
+ public static long CopyTo(this Stream source, Stream target)
+ {
+ byte[] buffer = new byte[8192]; // multiple of 4096
+ long nTotal = 0;
+ while (true)
+ {
+ int nRead = source.Read(buffer, 0, buffer.Length);
+ if (nRead <= 0) // done?
+ return nTotal;
+
+ target.Write(buffer, 0, nRead);
+ nTotal += nRead;
+ }
+ }
+ }
+
+}
+#endif
+
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Properties/AssemblyInfo.cs b/src/jaegertracing/thrift/lib/csharp/src/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..ace031045
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Thrift")]
+[assembly: AssemblyDescription("C# bindings for the Apache Thrift RPC system")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("The Apache Software Foundation")]
+[assembly: AssemblyProduct("Thrift")]
+[assembly: AssemblyCopyright("The Apache Software Foundation")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+//@TODO where to put License information?
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("df3f8ef0-e0a3-4c86-a65b-8ec84e016b1d")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("0.13.0.0")]
+[assembly: AssemblyFileVersion("0.13.0.0")]
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TAbstractBase.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TAbstractBase.cs
new file mode 100644
index 000000000..f5a61cd44
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TAbstractBase.cs
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Thrift.Protocol
+{
+ public interface TAbstractBase
+ {
+ /// <summary>
+ /// Writes the objects out to the protocol.
+ /// </summary>
+ void Write(TProtocol tProtocol);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TBase.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TBase.cs
new file mode 100644
index 000000000..411e4d95f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TBase.cs
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Thrift.Protocol
+{
+ public interface TBase : TAbstractBase
+ {
+ /// <summary>
+ /// Reads the TObject from the given input protocol.
+ /// </summary>
+ void Read(TProtocol tProtocol);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TBase64Utils.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TBase64Utils.cs
new file mode 100644
index 000000000..9eaaebdda
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TBase64Utils.cs
@@ -0,0 +1,100 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+
+namespace Thrift.Protocol
+{
+ internal static class TBase64Utils
+ {
+ internal const string ENCODE_TABLE =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ internal static void encode(byte[] src, int srcOff, int len, byte[] dst,
+ int dstOff)
+ {
+ dst[dstOff] = (byte)ENCODE_TABLE[(src[srcOff] >> 2) & 0x3F];
+ if (len == 3)
+ {
+ dst[dstOff + 1] =
+ (byte)ENCODE_TABLE[
+ ((src[srcOff] << 4) & 0x30) | ((src[srcOff + 1] >> 4) & 0x0F)];
+ dst[dstOff + 2] =
+ (byte)ENCODE_TABLE[
+ ((src[srcOff + 1] << 2) & 0x3C) | ((src[srcOff + 2] >> 6) & 0x03)];
+ dst[dstOff + 3] =
+ (byte)ENCODE_TABLE[src[srcOff + 2] & 0x3F];
+ }
+ else if (len == 2)
+ {
+ dst[dstOff + 1] =
+ (byte)ENCODE_TABLE[
+ ((src[srcOff] << 4) & 0x30) | ((src[srcOff + 1] >> 4) & 0x0F)];
+ dst[dstOff + 2] =
+ (byte)ENCODE_TABLE[(src[srcOff + 1] << 2) & 0x3C];
+
+ }
+ else
+ { // len == 1) {
+ dst[dstOff + 1] =
+ (byte)ENCODE_TABLE[(src[srcOff] << 4) & 0x30];
+ }
+ }
+
+ private static int[] DECODE_TABLE = {
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
+ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
+ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
+ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
+ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ };
+
+ internal static void decode(byte[] src, int srcOff, int len, byte[] dst,
+ int dstOff)
+ {
+ dst[dstOff] = (byte)
+ ((DECODE_TABLE[src[srcOff] & 0x0FF] << 2) |
+ (DECODE_TABLE[src[srcOff + 1] & 0x0FF] >> 4));
+ if (len > 2)
+ {
+ dst[dstOff + 1] = (byte)
+ (((DECODE_TABLE[src[srcOff + 1] & 0x0FF] << 4) & 0xF0) |
+ (DECODE_TABLE[src[srcOff + 2] & 0x0FF] >> 2));
+ if (len > 3)
+ {
+ dst[dstOff + 2] = (byte)
+ (((DECODE_TABLE[src[srcOff + 2] & 0x0FF] << 6) & 0xC0) |
+ DECODE_TABLE[src[srcOff + 3] & 0x0FF]);
+ }
+ }
+ }
+
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TBinaryProtocol.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TBinaryProtocol.cs
new file mode 100644
index 000000000..a4faa946f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TBinaryProtocol.cs
@@ -0,0 +1,395 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Text;
+using Thrift.Transport;
+
+namespace Thrift.Protocol
+{
+ public class TBinaryProtocol : TProtocol
+ {
+ protected const uint VERSION_MASK = 0xffff0000;
+ protected const uint VERSION_1 = 0x80010000;
+
+ protected bool strictRead_ = false;
+ protected bool strictWrite_ = true;
+
+ #region BinaryProtocol Factory
+
+ public class Factory : TProtocolFactory
+ {
+ protected bool strictRead_ = false;
+ protected bool strictWrite_ = true;
+
+ public Factory()
+ : this(false, true)
+ {
+ }
+
+ public Factory(bool strictRead, bool strictWrite)
+ {
+ strictRead_ = strictRead;
+ strictWrite_ = strictWrite;
+ }
+
+ public TProtocol GetProtocol(TTransport trans)
+ {
+ return new TBinaryProtocol(trans, strictRead_, strictWrite_);
+ }
+ }
+
+ #endregion
+
+ public TBinaryProtocol(TTransport trans)
+ : this(trans, false, true)
+ {
+ }
+
+ public TBinaryProtocol(TTransport trans, bool strictRead, bool strictWrite)
+ : base(trans)
+ {
+ strictRead_ = strictRead;
+ strictWrite_ = strictWrite;
+ }
+
+ #region Write Methods
+
+ public override void WriteMessageBegin(TMessage message)
+ {
+ if (strictWrite_)
+ {
+ uint version = VERSION_1 | (uint)(message.Type);
+ WriteI32((int)version);
+ WriteString(message.Name);
+ WriteI32(message.SeqID);
+ }
+ else
+ {
+ WriteString(message.Name);
+ WriteByte((sbyte)message.Type);
+ WriteI32(message.SeqID);
+ }
+ }
+
+ public override void WriteMessageEnd()
+ {
+ }
+
+ public override void WriteStructBegin(TStruct struc)
+ {
+ }
+
+ public override void WriteStructEnd()
+ {
+ }
+
+ public override void WriteFieldBegin(TField field)
+ {
+ WriteByte((sbyte)field.Type);
+ WriteI16(field.ID);
+ }
+
+ public override void WriteFieldEnd()
+ {
+ }
+
+ public override void WriteFieldStop()
+ {
+ WriteByte((sbyte)TType.Stop);
+ }
+
+ public override void WriteMapBegin(TMap map)
+ {
+ WriteByte((sbyte)map.KeyType);
+ WriteByte((sbyte)map.ValueType);
+ WriteI32(map.Count);
+ }
+
+ public override void WriteMapEnd()
+ {
+ }
+
+ public override void WriteListBegin(TList list)
+ {
+ WriteByte((sbyte)list.ElementType);
+ WriteI32(list.Count);
+ }
+
+ public override void WriteListEnd()
+ {
+ }
+
+ public override void WriteSetBegin(TSet set)
+ {
+ WriteByte((sbyte)set.ElementType);
+ WriteI32(set.Count);
+ }
+
+ public override void WriteSetEnd()
+ {
+ }
+
+ public override void WriteBool(bool b)
+ {
+ WriteByte(b ? (sbyte)1 : (sbyte)0);
+ }
+
+ private byte[] bout = new byte[1];
+ public override void WriteByte(sbyte b)
+ {
+ bout[0] = (byte)b;
+ trans.Write(bout, 0, 1);
+ }
+
+ private byte[] i16out = new byte[2];
+ public override void WriteI16(short s)
+ {
+ i16out[0] = (byte)(0xff & (s >> 8));
+ i16out[1] = (byte)(0xff & s);
+ trans.Write(i16out, 0, 2);
+ }
+
+ private byte[] i32out = new byte[4];
+ public override void WriteI32(int i32)
+ {
+ i32out[0] = (byte)(0xff & (i32 >> 24));
+ i32out[1] = (byte)(0xff & (i32 >> 16));
+ i32out[2] = (byte)(0xff & (i32 >> 8));
+ i32out[3] = (byte)(0xff & i32);
+ trans.Write(i32out, 0, 4);
+ }
+
+ private byte[] i64out = new byte[8];
+ public override void WriteI64(long i64)
+ {
+ i64out[0] = (byte)(0xff & (i64 >> 56));
+ i64out[1] = (byte)(0xff & (i64 >> 48));
+ i64out[2] = (byte)(0xff & (i64 >> 40));
+ i64out[3] = (byte)(0xff & (i64 >> 32));
+ i64out[4] = (byte)(0xff & (i64 >> 24));
+ i64out[5] = (byte)(0xff & (i64 >> 16));
+ i64out[6] = (byte)(0xff & (i64 >> 8));
+ i64out[7] = (byte)(0xff & i64);
+ trans.Write(i64out, 0, 8);
+ }
+
+ public override void WriteDouble(double d)
+ {
+#if !SILVERLIGHT
+ WriteI64(BitConverter.DoubleToInt64Bits(d));
+#else
+ var bytes = BitConverter.GetBytes(d);
+ WriteI64(BitConverter.ToInt64(bytes, 0));
+#endif
+ }
+
+ public override void WriteBinary(byte[] b)
+ {
+ WriteI32(b.Length);
+ trans.Write(b, 0, b.Length);
+ }
+
+ #endregion
+
+ #region ReadMethods
+
+ public override TMessage ReadMessageBegin()
+ {
+ TMessage message = new TMessage();
+ int size = ReadI32();
+ if (size < 0)
+ {
+ uint version = (uint)size & VERSION_MASK;
+ if (version != VERSION_1)
+ {
+ throw new TProtocolException(TProtocolException.BAD_VERSION, "Bad version in ReadMessageBegin: " + version);
+ }
+ message.Type = (TMessageType)(size & 0x000000ff);
+ message.Name = ReadString();
+ message.SeqID = ReadI32();
+ }
+ else
+ {
+ if (strictRead_)
+ {
+ throw new TProtocolException(TProtocolException.BAD_VERSION, "Missing version in readMessageBegin, old client?");
+ }
+ message.Name = ReadStringBody(size);
+ message.Type = (TMessageType)ReadByte();
+ message.SeqID = ReadI32();
+ }
+ return message;
+ }
+
+ public override void ReadMessageEnd()
+ {
+ }
+
+ public override TStruct ReadStructBegin()
+ {
+ return new TStruct();
+ }
+
+ public override void ReadStructEnd()
+ {
+ }
+
+ public override TField ReadFieldBegin()
+ {
+ TField field = new TField();
+ field.Type = (TType)ReadByte();
+
+ if (field.Type != TType.Stop)
+ {
+ field.ID = ReadI16();
+ }
+
+ return field;
+ }
+
+ public override void ReadFieldEnd()
+ {
+ }
+
+ public override TMap ReadMapBegin()
+ {
+ TMap map = new TMap();
+ map.KeyType = (TType)ReadByte();
+ map.ValueType = (TType)ReadByte();
+ map.Count = ReadI32();
+
+ return map;
+ }
+
+ public override void ReadMapEnd()
+ {
+ }
+
+ public override TList ReadListBegin()
+ {
+ TList list = new TList();
+ list.ElementType = (TType)ReadByte();
+ list.Count = ReadI32();
+
+ return list;
+ }
+
+ public override void ReadListEnd()
+ {
+ }
+
+ public override TSet ReadSetBegin()
+ {
+ TSet set = new TSet();
+ set.ElementType = (TType)ReadByte();
+ set.Count = ReadI32();
+
+ return set;
+ }
+
+ public override void ReadSetEnd()
+ {
+ }
+
+ public override bool ReadBool()
+ {
+ return ReadByte() == 1;
+ }
+
+ private byte[] bin = new byte[1];
+ public override sbyte ReadByte()
+ {
+ ReadAll(bin, 0, 1);
+ return (sbyte)bin[0];
+ }
+
+ private byte[] i16in = new byte[2];
+ public override short ReadI16()
+ {
+ ReadAll(i16in, 0, 2);
+ return (short)(((i16in[0] & 0xff) << 8) | ((i16in[1] & 0xff)));
+ }
+
+ private byte[] i32in = new byte[4];
+ public override int ReadI32()
+ {
+ ReadAll(i32in, 0, 4);
+ return (int)(((i32in[0] & 0xff) << 24) | ((i32in[1] & 0xff) << 16) | ((i32in[2] & 0xff) << 8) | ((i32in[3] & 0xff)));
+ }
+
+#pragma warning disable 675
+
+ private byte[] i64in = new byte[8];
+ public override long ReadI64()
+ {
+ ReadAll(i64in, 0, 8);
+ unchecked
+ {
+ return (long)(
+ ((long)(i64in[0] & 0xff) << 56) |
+ ((long)(i64in[1] & 0xff) << 48) |
+ ((long)(i64in[2] & 0xff) << 40) |
+ ((long)(i64in[3] & 0xff) << 32) |
+ ((long)(i64in[4] & 0xff) << 24) |
+ ((long)(i64in[5] & 0xff) << 16) |
+ ((long)(i64in[6] & 0xff) << 8) |
+ ((long)(i64in[7] & 0xff)));
+ }
+ }
+
+#pragma warning restore 675
+
+ public override double ReadDouble()
+ {
+#if !SILVERLIGHT
+ return BitConverter.Int64BitsToDouble(ReadI64());
+#else
+ var value = ReadI64();
+ var bytes = BitConverter.GetBytes(value);
+ return BitConverter.ToDouble(bytes, 0);
+#endif
+ }
+
+ public override byte[] ReadBinary()
+ {
+ int size = ReadI32();
+ byte[] buf = new byte[size];
+ trans.ReadAll(buf, 0, size);
+ return buf;
+ }
+ private string ReadStringBody(int size)
+ {
+ byte[] buf = new byte[size];
+ trans.ReadAll(buf, 0, size);
+ return Encoding.UTF8.GetString(buf, 0, buf.Length);
+ }
+
+ private int ReadAll(byte[] buf, int off, int len)
+ {
+ return trans.ReadAll(buf, off, len);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TCompactProtocol.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TCompactProtocol.cs
new file mode 100644
index 000000000..ff673975e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TCompactProtocol.cs
@@ -0,0 +1,849 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Text;
+using Thrift.Transport;
+using System.Collections;
+using System.IO;
+using System.Collections.Generic;
+
+namespace Thrift.Protocol
+{
+ public class TCompactProtocol : TProtocol
+ {
+ private static TStruct ANONYMOUS_STRUCT = new TStruct("");
+ private static TField TSTOP = new TField("", TType.Stop, (short)0);
+
+ private static byte[] ttypeToCompactType = new byte[16];
+
+ private const byte PROTOCOL_ID = 0x82;
+ private const byte VERSION = 1;
+ private const byte VERSION_MASK = 0x1f; // 0001 1111
+ private const byte TYPE_MASK = 0xE0; // 1110 0000
+ private const byte TYPE_BITS = 0x07; // 0000 0111
+ private const int TYPE_SHIFT_AMOUNT = 5;
+
+ /// <summary>
+ /// All of the on-wire type codes.
+ /// </summary>
+ private static class Types
+ {
+ public const byte STOP = 0x00;
+ public const byte BOOLEAN_TRUE = 0x01;
+ public const byte BOOLEAN_FALSE = 0x02;
+ public const byte BYTE = 0x03;
+ public const byte I16 = 0x04;
+ public const byte I32 = 0x05;
+ public const byte I64 = 0x06;
+ public const byte DOUBLE = 0x07;
+ public const byte BINARY = 0x08;
+ public const byte LIST = 0x09;
+ public const byte SET = 0x0A;
+ public const byte MAP = 0x0B;
+ public const byte STRUCT = 0x0C;
+ }
+
+ /// <summary>
+ /// Used to keep track of the last field for the current and previous structs,
+ /// so we can do the delta stuff.
+ /// </summary>
+ private Stack<short> lastField_ = new Stack<short>(15);
+
+ private short lastFieldId_ = 0;
+
+ /// <summary>
+ /// If we encounter a boolean field begin, save the TField here so it can
+ /// have the value incorporated.
+ /// </summary>
+ private Nullable<TField> booleanField_;
+
+ /// <summary>
+ /// If we Read a field header, and it's a boolean field, save the boolean
+ /// value here so that ReadBool can use it.
+ /// </summary>
+ private Nullable<Boolean> boolValue_;
+
+
+ #region CompactProtocol Factory
+
+ public class Factory : TProtocolFactory
+ {
+ public Factory() { }
+
+ public TProtocol GetProtocol(TTransport trans)
+ {
+ return new TCompactProtocol(trans);
+ }
+ }
+
+ #endregion
+
+ public TCompactProtocol(TTransport trans)
+ : base(trans)
+ {
+ ttypeToCompactType[(int)TType.Stop] = Types.STOP;
+ ttypeToCompactType[(int)TType.Bool] = Types.BOOLEAN_TRUE;
+ ttypeToCompactType[(int)TType.Byte] = Types.BYTE;
+ ttypeToCompactType[(int)TType.I16] = Types.I16;
+ ttypeToCompactType[(int)TType.I32] = Types.I32;
+ ttypeToCompactType[(int)TType.I64] = Types.I64;
+ ttypeToCompactType[(int)TType.Double] = Types.DOUBLE;
+ ttypeToCompactType[(int)TType.String] = Types.BINARY;
+ ttypeToCompactType[(int)TType.List] = Types.LIST;
+ ttypeToCompactType[(int)TType.Set] = Types.SET;
+ ttypeToCompactType[(int)TType.Map] = Types.MAP;
+ ttypeToCompactType[(int)TType.Struct] = Types.STRUCT;
+ }
+
+ public void reset()
+ {
+ lastField_.Clear();
+ lastFieldId_ = 0;
+ }
+
+ #region Write Methods
+
+ /// <summary>
+ /// Writes a byte without any possibility of all that field header nonsense.
+ /// Used internally by other writing methods that know they need to Write a byte.
+ /// </summary>
+ private byte[] byteDirectBuffer = new byte[1];
+
+ private void WriteByteDirect(byte b)
+ {
+ byteDirectBuffer[0] = b;
+ trans.Write(byteDirectBuffer);
+ }
+
+ /// <summary>
+ /// Writes a byte without any possibility of all that field header nonsense.
+ /// </summary>
+ private void WriteByteDirect(int n)
+ {
+ WriteByteDirect((byte)n);
+ }
+
+ /// <summary>
+ /// Write an i32 as a varint. Results in 1-5 bytes on the wire.
+ /// TODO: make a permanent buffer like WriteVarint64?
+ /// </summary>
+ byte[] i32buf = new byte[5];
+
+ private void WriteVarint32(uint n)
+ {
+ int idx = 0;
+ while (true)
+ {
+ if ((n & ~0x7F) == 0)
+ {
+ i32buf[idx++] = (byte)n;
+ // WriteByteDirect((byte)n);
+ break;
+ // return;
+ }
+ else
+ {
+ i32buf[idx++] = (byte)((n & 0x7F) | 0x80);
+ // WriteByteDirect((byte)((n & 0x7F) | 0x80));
+ n >>= 7;
+ }
+ }
+ trans.Write(i32buf, 0, idx);
+ }
+
+ /// <summary>
+ /// Write a message header to the wire. Compact Protocol messages contain the
+ /// protocol version so we can migrate forwards in the future if need be.
+ /// </summary>
+ public override void WriteMessageBegin(TMessage message)
+ {
+ WriteByteDirect(PROTOCOL_ID);
+ WriteByteDirect((byte)((VERSION & VERSION_MASK) | ((((uint)message.Type) << TYPE_SHIFT_AMOUNT) & TYPE_MASK)));
+ WriteVarint32((uint)message.SeqID);
+ WriteString(message.Name);
+ }
+
+ /// <summary>
+ /// Write a struct begin. This doesn't actually put anything on the wire. We
+ /// use it as an opportunity to put special placeholder markers on the field
+ /// stack so we can get the field id deltas correct.
+ /// </summary>
+ public override void WriteStructBegin(TStruct strct)
+ {
+ lastField_.Push(lastFieldId_);
+ lastFieldId_ = 0;
+ }
+
+ /// <summary>
+ /// Write a struct end. This doesn't actually put anything on the wire. We use
+ /// this as an opportunity to pop the last field from the current struct off
+ /// of the field stack.
+ /// </summary>
+ public override void WriteStructEnd()
+ {
+ lastFieldId_ = lastField_.Pop();
+ }
+
+ /// <summary>
+ /// Write a field header containing the field id and field type. If the
+ /// difference between the current field id and the last one is small (&lt; 15),
+ /// then the field id will be encoded in the 4 MSB as a delta. Otherwise, the
+ /// field id will follow the type header as a zigzag varint.
+ /// </summary>
+ public override void WriteFieldBegin(TField field)
+ {
+ if (field.Type == TType.Bool)
+ {
+ // we want to possibly include the value, so we'll wait.
+ booleanField_ = field;
+ }
+ else
+ {
+ WriteFieldBeginInternal(field, 0xFF);
+ }
+ }
+
+ /// <summary>
+ /// The workhorse of WriteFieldBegin. It has the option of doing a
+ /// 'type override' of the type header. This is used specifically in the
+ /// boolean field case.
+ /// </summary>
+ private void WriteFieldBeginInternal(TField field, byte typeOverride)
+ {
+ // short lastField = lastField_.Pop();
+
+ // if there's a type override, use that.
+ byte typeToWrite = typeOverride == 0xFF ? getCompactType(field.Type) : typeOverride;
+
+ // check if we can use delta encoding for the field id
+ if (field.ID > lastFieldId_ && field.ID - lastFieldId_ <= 15)
+ {
+ // Write them together
+ WriteByteDirect((field.ID - lastFieldId_) << 4 | typeToWrite);
+ }
+ else
+ {
+ // Write them separate
+ WriteByteDirect(typeToWrite);
+ WriteI16(field.ID);
+ }
+
+ lastFieldId_ = field.ID;
+ // lastField_.push(field.id);
+ }
+
+ /// <summary>
+ /// Write the STOP symbol so we know there are no more fields in this struct.
+ /// </summary>
+ public override void WriteFieldStop()
+ {
+ WriteByteDirect(Types.STOP);
+ }
+
+ /// <summary>
+ /// Write a map header. If the map is empty, omit the key and value type
+ /// headers, as we don't need any additional information to skip it.
+ /// </summary>
+ public override void WriteMapBegin(TMap map)
+ {
+ if (map.Count == 0)
+ {
+ WriteByteDirect(0);
+ }
+ else
+ {
+ WriteVarint32((uint)map.Count);
+ WriteByteDirect(getCompactType(map.KeyType) << 4 | getCompactType(map.ValueType));
+ }
+ }
+
+ /// <summary>
+ /// Write a list header.
+ /// </summary>
+ public override void WriteListBegin(TList list)
+ {
+ WriteCollectionBegin(list.ElementType, list.Count);
+ }
+
+ /// <summary>
+ /// Write a set header.
+ /// </summary>
+ public override void WriteSetBegin(TSet set)
+ {
+ WriteCollectionBegin(set.ElementType, set.Count);
+ }
+
+ /// <summary>
+ /// Write a boolean value. Potentially, this could be a boolean field, in
+ /// which case the field header info isn't written yet. If so, decide what the
+ /// right type header is for the value and then Write the field header.
+ /// Otherwise, Write a single byte.
+ /// </summary>
+ public override void WriteBool(Boolean b)
+ {
+ if (booleanField_ != null)
+ {
+ // we haven't written the field header yet
+ WriteFieldBeginInternal(booleanField_.Value, b ? Types.BOOLEAN_TRUE : Types.BOOLEAN_FALSE);
+ booleanField_ = null;
+ }
+ else
+ {
+ // we're not part of a field, so just Write the value.
+ WriteByteDirect(b ? Types.BOOLEAN_TRUE : Types.BOOLEAN_FALSE);
+ }
+ }
+
+ /// <summary>
+ /// Write a byte. Nothing to see here!
+ /// </summary>
+ public override void WriteByte(sbyte b)
+ {
+ WriteByteDirect((byte)b);
+ }
+
+ /// <summary>
+ /// Write an I16 as a zigzag varint.
+ /// </summary>
+ public override void WriteI16(short i16)
+ {
+ WriteVarint32(intToZigZag(i16));
+ }
+
+ /// <summary>
+ /// Write an i32 as a zigzag varint.
+ /// </summary>
+ public override void WriteI32(int i32)
+ {
+ WriteVarint32(intToZigZag(i32));
+ }
+
+ /// <summary>
+ /// Write an i64 as a zigzag varint.
+ /// </summary>
+ public override void WriteI64(long i64)
+ {
+ WriteVarint64(longToZigzag(i64));
+ }
+
+ /// <summary>
+ /// Write a double to the wire as 8 bytes.
+ /// </summary>
+ public override void WriteDouble(double dub)
+ {
+ byte[] data = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
+ fixedLongToBytes(BitConverter.DoubleToInt64Bits(dub), data, 0);
+ trans.Write(data);
+ }
+
+ /// <summary>
+ /// Write a string to the wire with a varint size preceding.
+ /// </summary>
+ public override void WriteString(string str)
+ {
+ byte[] bytes = UTF8Encoding.UTF8.GetBytes(str);
+ WriteBinary(bytes, 0, bytes.Length);
+ }
+
+ /// <summary>
+ /// Write a byte array, using a varint for the size.
+ /// </summary>
+ public override void WriteBinary(byte[] bin)
+ {
+ WriteBinary(bin, 0, bin.Length);
+ }
+
+ private void WriteBinary(byte[] buf, int offset, int length)
+ {
+ WriteVarint32((uint)length);
+ trans.Write(buf, offset, length);
+ }
+
+ //
+ // These methods are called by structs, but don't actually have any wire
+ // output or purpose.
+ //
+
+ public override void WriteMessageEnd() { }
+ public override void WriteMapEnd() { }
+ public override void WriteListEnd() { }
+ public override void WriteSetEnd() { }
+ public override void WriteFieldEnd() { }
+
+ //
+ // Internal writing methods
+ //
+
+ /// <summary>
+ /// Abstract method for writing the start of lists and sets. List and sets on
+ /// the wire differ only by the type indicator.
+ /// </summary>
+ protected void WriteCollectionBegin(TType elemType, int size)
+ {
+ if (size <= 14)
+ {
+ WriteByteDirect(size << 4 | getCompactType(elemType));
+ }
+ else
+ {
+ WriteByteDirect(0xf0 | getCompactType(elemType));
+ WriteVarint32((uint)size);
+ }
+ }
+
+ /// <summary>
+ /// Write an i64 as a varint. Results in 1-10 bytes on the wire.
+ /// </summary>
+ byte[] varint64out = new byte[10];
+ private void WriteVarint64(ulong n)
+ {
+ int idx = 0;
+ while (true)
+ {
+ if ((n & ~(ulong)0x7FL) == 0)
+ {
+ varint64out[idx++] = (byte)n;
+ break;
+ }
+ else
+ {
+ varint64out[idx++] = ((byte)((n & 0x7F) | 0x80));
+ n >>= 7;
+ }
+ }
+ trans.Write(varint64out, 0, idx);
+ }
+
+ /// <summary>
+ /// Convert l into a zigzag long. This allows negative numbers to be
+ /// represented compactly as a varint.
+ /// </summary>
+ private ulong longToZigzag(long n)
+ {
+ return (ulong)(n << 1) ^ (ulong)(n >> 63);
+ }
+
+ /// <summary>
+ /// Convert n into a zigzag int. This allows negative numbers to be
+ /// represented compactly as a varint.
+ /// </summary>
+ private uint intToZigZag(int n)
+ {
+ return (uint)(n << 1) ^ (uint)(n >> 31);
+ }
+
+ /// <summary>
+ /// Convert a long into little-endian bytes in buf starting at off and going
+ /// until off+7.
+ /// </summary>
+ private void fixedLongToBytes(long n, byte[] buf, int off)
+ {
+ buf[off + 0] = (byte)(n & 0xff);
+ buf[off + 1] = (byte)((n >> 8) & 0xff);
+ buf[off + 2] = (byte)((n >> 16) & 0xff);
+ buf[off + 3] = (byte)((n >> 24) & 0xff);
+ buf[off + 4] = (byte)((n >> 32) & 0xff);
+ buf[off + 5] = (byte)((n >> 40) & 0xff);
+ buf[off + 6] = (byte)((n >> 48) & 0xff);
+ buf[off + 7] = (byte)((n >> 56) & 0xff);
+ }
+
+ #endregion
+
+ #region ReadMethods
+
+ /// <summary>
+ /// Read a message header.
+ /// </summary>
+ public override TMessage ReadMessageBegin()
+ {
+ byte protocolId = (byte)ReadByte();
+ if (protocolId != PROTOCOL_ID)
+ {
+ throw new TProtocolException("Expected protocol id " + PROTOCOL_ID.ToString("X") + " but got " + protocolId.ToString("X"));
+ }
+ byte versionAndType = (byte)ReadByte();
+ byte version = (byte)(versionAndType & VERSION_MASK);
+ if (version != VERSION)
+ {
+ throw new TProtocolException("Expected version " + VERSION + " but got " + version);
+ }
+ byte type = (byte)((versionAndType >> TYPE_SHIFT_AMOUNT) & TYPE_BITS);
+ int seqid = (int)ReadVarint32();
+ string messageName = ReadString();
+ return new TMessage(messageName, (TMessageType)type, seqid);
+ }
+
+ /// <summary>
+ /// Read a struct begin. There's nothing on the wire for this, but it is our
+ /// opportunity to push a new struct begin marker onto the field stack.
+ /// </summary>
+ public override TStruct ReadStructBegin()
+ {
+ lastField_.Push(lastFieldId_);
+ lastFieldId_ = 0;
+ return ANONYMOUS_STRUCT;
+ }
+
+ /// <summary>
+ /// Doesn't actually consume any wire data, just removes the last field for
+ /// this struct from the field stack.
+ /// </summary>
+ public override void ReadStructEnd()
+ {
+ // consume the last field we Read off the wire.
+ lastFieldId_ = lastField_.Pop();
+ }
+
+ /// <summary>
+ /// Read a field header off the wire.
+ /// </summary>
+ public override TField ReadFieldBegin()
+ {
+ byte type = (byte)ReadByte();
+
+ // if it's a stop, then we can return immediately, as the struct is over.
+ if (type == Types.STOP)
+ {
+ return TSTOP;
+ }
+
+ short fieldId;
+
+ // mask off the 4 MSB of the type header. it could contain a field id delta.
+ short modifier = (short)((type & 0xf0) >> 4);
+ if (modifier == 0)
+ {
+ // not a delta. look ahead for the zigzag varint field id.
+ fieldId = ReadI16();
+ }
+ else
+ {
+ // has a delta. add the delta to the last Read field id.
+ fieldId = (short)(lastFieldId_ + modifier);
+ }
+
+ TField field = new TField("", getTType((byte)(type & 0x0f)), fieldId);
+
+ // if this happens to be a boolean field, the value is encoded in the type
+ if (isBoolType(type))
+ {
+ // save the boolean value in a special instance variable.
+ boolValue_ = (byte)(type & 0x0f) == Types.BOOLEAN_TRUE ? true : false;
+ }
+
+ // push the new field onto the field stack so we can keep the deltas going.
+ lastFieldId_ = field.ID;
+ return field;
+ }
+
+ /// <summary>
+ /// Read a map header off the wire. If the size is zero, skip Reading the key
+ /// and value type. This means that 0-length maps will yield TMaps without the
+ /// "correct" types.
+ /// </summary>
+ public override TMap ReadMapBegin()
+ {
+ int size = (int)ReadVarint32();
+ byte keyAndValueType = size == 0 ? (byte)0 : (byte)ReadByte();
+ return new TMap(getTType((byte)(keyAndValueType >> 4)), getTType((byte)(keyAndValueType & 0xf)), size);
+ }
+
+ /// <summary>
+ /// Read a list header off the wire. If the list size is 0-14, the size will
+ /// be packed into the element type header. If it's a longer list, the 4 MSB
+ /// of the element type header will be 0xF, and a varint will follow with the
+ /// true size.
+ /// </summary>
+ public override TList ReadListBegin()
+ {
+ byte size_and_type = (byte)ReadByte();
+ int size = (size_and_type >> 4) & 0x0f;
+ if (size == 15)
+ {
+ size = (int)ReadVarint32();
+ }
+ TType type = getTType(size_and_type);
+ return new TList(type, size);
+ }
+
+ /// <summary>
+ /// Read a set header off the wire. If the set size is 0-14, the size will
+ /// be packed into the element type header. If it's a longer set, the 4 MSB
+ /// of the element type header will be 0xF, and a varint will follow with the
+ /// true size.
+ /// </summary>
+ public override TSet ReadSetBegin()
+ {
+ return new TSet(ReadListBegin());
+ }
+
+ /// <summary>
+ /// Read a boolean off the wire. If this is a boolean field, the value should
+ /// already have been Read during ReadFieldBegin, so we'll just consume the
+ /// pre-stored value. Otherwise, Read a byte.
+ /// </summary>
+ public override Boolean ReadBool()
+ {
+ if (boolValue_ != null)
+ {
+ bool result = boolValue_.Value;
+ boolValue_ = null;
+ return result;
+ }
+ return ReadByte() == Types.BOOLEAN_TRUE;
+ }
+
+ byte[] byteRawBuf = new byte[1];
+ /// <summary>
+ /// Read a single byte off the wire. Nothing interesting here.
+ /// </summary>
+ public override sbyte ReadByte()
+ {
+ trans.ReadAll(byteRawBuf, 0, 1);
+ return (sbyte)byteRawBuf[0];
+ }
+
+ /// <summary>
+ /// Read an i16 from the wire as a zigzag varint.
+ /// </summary>
+ public override short ReadI16()
+ {
+ return (short)zigzagToInt(ReadVarint32());
+ }
+
+ /// <summary>
+ /// Read an i32 from the wire as a zigzag varint.
+ /// </summary>
+ public override int ReadI32()
+ {
+ return zigzagToInt(ReadVarint32());
+ }
+
+ /// <summary>
+ /// Read an i64 from the wire as a zigzag varint.
+ /// </summary>
+ public override long ReadI64()
+ {
+ return zigzagToLong(ReadVarint64());
+ }
+
+ /// <summary>
+ /// No magic here - just Read a double off the wire.
+ /// </summary>
+ public override double ReadDouble()
+ {
+ byte[] longBits = new byte[8];
+ trans.ReadAll(longBits, 0, 8);
+ return BitConverter.Int64BitsToDouble(bytesToLong(longBits));
+ }
+
+ /// <summary>
+ /// Reads a byte[] (via ReadBinary), and then UTF-8 decodes it.
+ /// </summary>
+ public override string ReadString()
+ {
+ int length = (int)ReadVarint32();
+
+ if (length == 0)
+ {
+ return "";
+ }
+
+ return Encoding.UTF8.GetString(ReadBinary(length));
+ }
+
+ /// <summary>
+ /// Read a byte[] from the wire.
+ /// </summary>
+ public override byte[] ReadBinary()
+ {
+ int length = (int)ReadVarint32();
+ if (length == 0) return new byte[0];
+
+ byte[] buf = new byte[length];
+ trans.ReadAll(buf, 0, length);
+ return buf;
+ }
+
+ /// <summary>
+ /// Read a byte[] of a known length from the wire.
+ /// </summary>
+ private byte[] ReadBinary(int length)
+ {
+ if (length == 0) return new byte[0];
+
+ byte[] buf = new byte[length];
+ trans.ReadAll(buf, 0, length);
+ return buf;
+ }
+
+ //
+ // These methods are here for the struct to call, but don't have any wire
+ // encoding.
+ //
+ public override void ReadMessageEnd() { }
+ public override void ReadFieldEnd() { }
+ public override void ReadMapEnd() { }
+ public override void ReadListEnd() { }
+ public override void ReadSetEnd() { }
+
+ //
+ // Internal Reading methods
+ //
+
+ /// <summary>
+ /// Read an i32 from the wire as a varint. The MSB of each byte is set
+ /// if there is another byte to follow. This can Read up to 5 bytes.
+ /// </summary>
+ private uint ReadVarint32()
+ {
+ uint result = 0;
+ int shift = 0;
+ while (true)
+ {
+ byte b = (byte)ReadByte();
+ result |= (uint)(b & 0x7f) << shift;
+ if ((b & 0x80) != 0x80) break;
+ shift += 7;
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Read an i64 from the wire as a proper varint. The MSB of each byte is set
+ /// if there is another byte to follow. This can Read up to 10 bytes.
+ /// </summary>
+ private ulong ReadVarint64()
+ {
+ int shift = 0;
+ ulong result = 0;
+ while (true)
+ {
+ byte b = (byte)ReadByte();
+ result |= (ulong)(b & 0x7f) << shift;
+ if ((b & 0x80) != 0x80) break;
+ shift += 7;
+ }
+
+ return result;
+ }
+
+ #endregion
+
+ //
+ // encoding helpers
+ //
+
+ /// <summary>
+ /// Convert from zigzag int to int.
+ /// </summary>
+ private int zigzagToInt(uint n)
+ {
+ return (int)(n >> 1) ^ (-(int)(n & 1));
+ }
+
+ /// <summary>
+ /// Convert from zigzag long to long.
+ /// </summary>
+ private long zigzagToLong(ulong n)
+ {
+ return (long)(n >> 1) ^ (-(long)(n & 1));
+ }
+
+ /// <summary>
+ /// Note that it's important that the mask bytes are long literals,
+ /// otherwise they'll default to ints, and when you shift an int left 56 bits,
+ /// you just get a messed up int.
+ /// </summary>
+ private long bytesToLong(byte[] bytes)
+ {
+ return
+ ((bytes[7] & 0xffL) << 56) |
+ ((bytes[6] & 0xffL) << 48) |
+ ((bytes[5] & 0xffL) << 40) |
+ ((bytes[4] & 0xffL) << 32) |
+ ((bytes[3] & 0xffL) << 24) |
+ ((bytes[2] & 0xffL) << 16) |
+ ((bytes[1] & 0xffL) << 8) |
+ ((bytes[0] & 0xffL));
+ }
+
+ //
+ // type testing and converting
+ //
+
+ private Boolean isBoolType(byte b)
+ {
+ int lowerNibble = b & 0x0f;
+ return lowerNibble == Types.BOOLEAN_TRUE || lowerNibble == Types.BOOLEAN_FALSE;
+ }
+
+ /// <summary>
+ /// Given a TCompactProtocol.Types constant, convert it to its corresponding
+ /// TType value.
+ /// </summary>
+ private TType getTType(byte type)
+ {
+ switch ((byte)(type & 0x0f))
+ {
+ case Types.STOP:
+ return TType.Stop;
+ case Types.BOOLEAN_FALSE:
+ case Types.BOOLEAN_TRUE:
+ return TType.Bool;
+ case Types.BYTE:
+ return TType.Byte;
+ case Types.I16:
+ return TType.I16;
+ case Types.I32:
+ return TType.I32;
+ case Types.I64:
+ return TType.I64;
+ case Types.DOUBLE:
+ return TType.Double;
+ case Types.BINARY:
+ return TType.String;
+ case Types.LIST:
+ return TType.List;
+ case Types.SET:
+ return TType.Set;
+ case Types.MAP:
+ return TType.Map;
+ case Types.STRUCT:
+ return TType.Struct;
+ default:
+ throw new TProtocolException("don't know what type: " + (byte)(type & 0x0f));
+ }
+ }
+
+ /// <summary>
+ /// Given a TType value, find the appropriate TCompactProtocol.Types constant.
+ /// </summary>
+ private byte getCompactType(TType ttype)
+ {
+ return ttypeToCompactType[(int)ttype];
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TField.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TField.cs
new file mode 100644
index 000000000..81795577e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TField.cs
@@ -0,0 +1,62 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Thrift.Protocol
+{
+ public struct TField
+ {
+ private string name;
+ private TType type;
+ private short id;
+
+ public TField(string name, TType type, short id)
+ :this()
+ {
+ this.name = name;
+ this.type = type;
+ this.id = id;
+ }
+
+ public string Name
+ {
+ get { return name; }
+ set { name = value; }
+ }
+
+ public TType Type
+ {
+ get { return type; }
+ set { type = value; }
+ }
+
+ public short ID
+ {
+ get { return id; }
+ set { id = value; }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TJSONProtocol.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TJSONProtocol.cs
new file mode 100644
index 000000000..9dbdea9aa
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TJSONProtocol.cs
@@ -0,0 +1,1124 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+
+using Thrift.Transport;
+using System.Globalization;
+
+namespace Thrift.Protocol
+{
+ /// <summary>
+ /// JSON protocol implementation for thrift.
+ /// <para/>
+ /// This is a full-featured protocol supporting Write and Read.
+ /// <para/>
+ /// Please see the C++ class header for a detailed description of the
+ /// protocol's wire format.
+ /// <para/>
+ /// Adapted from the Java version.
+ /// </summary>
+ public class TJSONProtocol : TProtocol
+ {
+ /// <summary>
+ /// Factory for JSON protocol objects.
+ /// </summary>
+ public class Factory : TProtocolFactory
+ {
+ public TProtocol GetProtocol(TTransport trans)
+ {
+ return new TJSONProtocol(trans);
+ }
+ }
+
+ private static byte[] COMMA = new byte[] { (byte)',' };
+ private static byte[] COLON = new byte[] { (byte)':' };
+ private static byte[] LBRACE = new byte[] { (byte)'{' };
+ private static byte[] RBRACE = new byte[] { (byte)'}' };
+ private static byte[] LBRACKET = new byte[] { (byte)'[' };
+ private static byte[] RBRACKET = new byte[] { (byte)']' };
+ private static byte[] QUOTE = new byte[] { (byte)'"' };
+ private static byte[] BACKSLASH = new byte[] { (byte)'\\' };
+
+ private byte[] ESCSEQ = new byte[] { (byte)'\\', (byte)'u', (byte)'0', (byte)'0' };
+
+ private const long VERSION = 1;
+ private byte[] JSON_CHAR_TABLE = {
+ 0, 0, 0, 0, 0, 0, 0, 0,(byte)'b',(byte)'t',(byte)'n', 0,(byte)'f',(byte)'r', 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1,(byte)'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ };
+
+ private char[] ESCAPE_CHARS = "\"\\/bfnrt".ToCharArray();
+
+ private byte[] ESCAPE_CHAR_VALS = {
+ (byte)'"', (byte)'\\', (byte)'/', (byte)'\b', (byte)'\f', (byte)'\n', (byte)'\r', (byte)'\t',
+ };
+
+ private const int DEF_STRING_SIZE = 16;
+
+ private static byte[] NAME_BOOL = new byte[] { (byte)'t', (byte)'f' };
+ private static byte[] NAME_BYTE = new byte[] { (byte)'i', (byte)'8' };
+ private static byte[] NAME_I16 = new byte[] { (byte)'i', (byte)'1', (byte)'6' };
+ private static byte[] NAME_I32 = new byte[] { (byte)'i', (byte)'3', (byte)'2' };
+ private static byte[] NAME_I64 = new byte[] { (byte)'i', (byte)'6', (byte)'4' };
+ private static byte[] NAME_DOUBLE = new byte[] { (byte)'d', (byte)'b', (byte)'l' };
+ private static byte[] NAME_STRUCT = new byte[] { (byte)'r', (byte)'e', (byte)'c' };
+ private static byte[] NAME_STRING = new byte[] { (byte)'s', (byte)'t', (byte)'r' };
+ private static byte[] NAME_MAP = new byte[] { (byte)'m', (byte)'a', (byte)'p' };
+ private static byte[] NAME_LIST = new byte[] { (byte)'l', (byte)'s', (byte)'t' };
+ private static byte[] NAME_SET = new byte[] { (byte)'s', (byte)'e', (byte)'t' };
+
+ private static byte[] GetTypeNameForTypeID(TType typeID)
+ {
+ switch (typeID)
+ {
+ case TType.Bool:
+ return NAME_BOOL;
+ case TType.Byte:
+ return NAME_BYTE;
+ case TType.I16:
+ return NAME_I16;
+ case TType.I32:
+ return NAME_I32;
+ case TType.I64:
+ return NAME_I64;
+ case TType.Double:
+ return NAME_DOUBLE;
+ case TType.String:
+ return NAME_STRING;
+ case TType.Struct:
+ return NAME_STRUCT;
+ case TType.Map:
+ return NAME_MAP;
+ case TType.Set:
+ return NAME_SET;
+ case TType.List:
+ return NAME_LIST;
+ default:
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
+ "Unrecognized type");
+ }
+ }
+
+ private static TType GetTypeIDForTypeName(byte[] name)
+ {
+ TType result = TType.Stop;
+ if (name.Length > 1)
+ {
+ switch (name[0])
+ {
+ case (byte)'d':
+ result = TType.Double;
+ break;
+ case (byte)'i':
+ switch (name[1])
+ {
+ case (byte)'8':
+ result = TType.Byte;
+ break;
+ case (byte)'1':
+ result = TType.I16;
+ break;
+ case (byte)'3':
+ result = TType.I32;
+ break;
+ case (byte)'6':
+ result = TType.I64;
+ break;
+ }
+ break;
+ case (byte)'l':
+ result = TType.List;
+ break;
+ case (byte)'m':
+ result = TType.Map;
+ break;
+ case (byte)'r':
+ result = TType.Struct;
+ break;
+ case (byte)'s':
+ if (name[1] == (byte)'t')
+ {
+ result = TType.String;
+ }
+ else if (name[1] == (byte)'e')
+ {
+ result = TType.Set;
+ }
+ break;
+ case (byte)'t':
+ result = TType.Bool;
+ break;
+ }
+ }
+ if (result == TType.Stop)
+ {
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
+ "Unrecognized type");
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Base class for tracking JSON contexts that may require
+ /// inserting/Reading additional JSON syntax characters
+ /// This base context does nothing.
+ /// </summary>
+ protected class JSONBaseContext
+ {
+ protected TJSONProtocol proto;
+
+ public JSONBaseContext(TJSONProtocol proto)
+ {
+ this.proto = proto;
+ }
+
+ public virtual void Write() { }
+
+ public virtual void Read() { }
+
+ public virtual bool EscapeNumbers() { return false; }
+ }
+
+ /// <summary>
+ /// Context for JSON lists. Will insert/Read commas before each item except
+ /// for the first one
+ /// </summary>
+ protected class JSONListContext : JSONBaseContext
+ {
+ public JSONListContext(TJSONProtocol protocol)
+ : base(protocol)
+ {
+
+ }
+
+ private bool first = true;
+
+ public override void Write()
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ proto.trans.Write(COMMA);
+ }
+ }
+
+ public override void Read()
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ proto.ReadJSONSyntaxChar(COMMA);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Context for JSON records. Will insert/Read colons before the value portion
+ /// of each record pair, and commas before each key except the first. In
+ /// addition, will indicate that numbers in the key position need to be
+ /// escaped in quotes (since JSON keys must be strings).
+ /// </summary>
+ protected class JSONPairContext : JSONBaseContext
+ {
+ public JSONPairContext(TJSONProtocol proto)
+ : base(proto)
+ {
+
+ }
+
+ private bool first = true;
+ private bool colon = true;
+
+ public override void Write()
+ {
+ if (first)
+ {
+ first = false;
+ colon = true;
+ }
+ else
+ {
+ proto.trans.Write(colon ? COLON : COMMA);
+ colon = !colon;
+ }
+ }
+
+ public override void Read()
+ {
+ if (first)
+ {
+ first = false;
+ colon = true;
+ }
+ else
+ {
+ proto.ReadJSONSyntaxChar(colon ? COLON : COMMA);
+ colon = !colon;
+ }
+ }
+
+ public override bool EscapeNumbers()
+ {
+ return colon;
+ }
+ }
+
+ /// <summary>
+ /// Holds up to one byte from the transport
+ /// </summary>
+ protected class LookaheadReader
+ {
+ protected TJSONProtocol proto;
+
+ public LookaheadReader(TJSONProtocol proto)
+ {
+ this.proto = proto;
+ }
+
+ private bool hasData;
+ private byte[] data = new byte[1];
+
+ /// <summary>
+ /// Return and consume the next byte to be Read, either taking it from the
+ /// data buffer if present or getting it from the transport otherwise.
+ /// </summary>
+ public byte Read()
+ {
+ if (hasData)
+ {
+ hasData = false;
+ }
+ else
+ {
+ proto.trans.ReadAll(data, 0, 1);
+ }
+ return data[0];
+ }
+
+ /// <summary>
+ /// Return the next byte to be Read without consuming, filling the data
+ /// buffer if it has not been filled alReady.
+ /// </summary>
+ public byte Peek()
+ {
+ if (!hasData)
+ {
+ proto.trans.ReadAll(data, 0, 1);
+ }
+ hasData = true;
+ return data[0];
+ }
+ }
+
+ // Default encoding
+ protected Encoding utf8Encoding = UTF8Encoding.UTF8;
+
+ // Stack of nested contexts that we may be in
+ protected Stack<JSONBaseContext> contextStack = new Stack<JSONBaseContext>();
+
+ // Current context that we are in
+ protected JSONBaseContext context;
+
+ // Reader that manages a 1-byte buffer
+ protected LookaheadReader reader;
+
+ /// <summary>
+ /// Push a new JSON context onto the stack.
+ /// </summary>
+ protected void PushContext(JSONBaseContext c)
+ {
+ contextStack.Push(context);
+ context = c;
+ }
+
+ /// <summary>
+ /// Pop the last JSON context off the stack
+ /// </summary>
+ protected void PopContext()
+ {
+ context = contextStack.Pop();
+ }
+
+ /// <summary>
+ /// TJSONProtocol Constructor
+ /// </summary>
+ public TJSONProtocol(TTransport trans)
+ : base(trans)
+ {
+ context = new JSONBaseContext(this);
+ reader = new LookaheadReader(this);
+ }
+
+ // Temporary buffer used by several methods
+ private byte[] tempBuffer = new byte[4];
+
+ /// <summary>
+ /// Read a byte that must match b[0]; otherwise an exception is thrown.
+ /// Marked protected to avoid synthetic accessor in JSONListContext.Read
+ /// and JSONPairContext.Read
+ /// </summary>
+ protected void ReadJSONSyntaxChar(byte[] b)
+ {
+ byte ch = reader.Read();
+ if (ch != b[0])
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Unexpected character:" + (char)ch);
+ }
+ }
+
+ /// <summary>
+ /// Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its
+ /// corresponding hex value
+ /// </summary>
+ private static byte HexVal(byte ch)
+ {
+ if ((ch >= '0') && (ch <= '9'))
+ {
+ return (byte)((char)ch - '0');
+ }
+ else if ((ch >= 'a') && (ch <= 'f'))
+ {
+ ch += 10;
+ return (byte)((char)ch - 'a');
+ }
+ else
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected hex character");
+ }
+ }
+
+ /// <summary>
+ /// Convert a byte containing a hex value to its corresponding hex character
+ /// </summary>
+ private static byte HexChar(byte val)
+ {
+ val &= 0x0F;
+ if (val < 10)
+ {
+ return (byte)((char)val + '0');
+ }
+ else
+ {
+ val -= 10;
+ return (byte)((char)val + 'a');
+ }
+ }
+
+ /// <summary>
+ /// Write the bytes in array buf as a JSON characters, escaping as needed
+ /// </summary>
+ private void WriteJSONString(byte[] b)
+ {
+ context.Write();
+ trans.Write(QUOTE);
+ int len = b.Length;
+ for (int i = 0; i < len; i++)
+ {
+ if ((b[i] & 0x00FF) >= 0x30)
+ {
+ if (b[i] == BACKSLASH[0])
+ {
+ trans.Write(BACKSLASH);
+ trans.Write(BACKSLASH);
+ }
+ else
+ {
+ trans.Write(b, i, 1);
+ }
+ }
+ else
+ {
+ tempBuffer[0] = JSON_CHAR_TABLE[b[i]];
+ if (tempBuffer[0] == 1)
+ {
+ trans.Write(b, i, 1);
+ }
+ else if (tempBuffer[0] > 1)
+ {
+ trans.Write(BACKSLASH);
+ trans.Write(tempBuffer, 0, 1);
+ }
+ else
+ {
+ trans.Write(ESCSEQ);
+ tempBuffer[0] = HexChar((byte)(b[i] >> 4));
+ tempBuffer[1] = HexChar(b[i]);
+ trans.Write(tempBuffer, 0, 2);
+ }
+ }
+ }
+ trans.Write(QUOTE);
+ }
+
+ /// <summary>
+ /// Write out number as a JSON value. If the context dictates so, it will be
+ /// wrapped in quotes to output as a JSON string.
+ /// </summary>
+ private void WriteJSONInteger(long num)
+ {
+ context.Write();
+ string str = num.ToString();
+
+ bool escapeNum = context.EscapeNumbers();
+ if (escapeNum)
+ trans.Write(QUOTE);
+
+ trans.Write(utf8Encoding.GetBytes(str));
+
+ if (escapeNum)
+ trans.Write(QUOTE);
+ }
+
+ /// <summary>
+ /// Write out a double as a JSON value. If it is NaN or infinity or if the
+ /// context dictates escaping, Write out as JSON string.
+ /// </summary>
+ private void WriteJSONDouble(double num)
+ {
+ context.Write();
+ string str = num.ToString("G17", CultureInfo.InvariantCulture);
+ bool special = false;
+
+ switch (str[0])
+ {
+ case 'N': // NaN
+ case 'I': // Infinity
+ special = true;
+ break;
+ case '-':
+ if (str[1] == 'I')
+ { // -Infinity
+ special = true;
+ }
+ break;
+ }
+
+ bool escapeNum = special || context.EscapeNumbers();
+
+ if (escapeNum)
+ trans.Write(QUOTE);
+
+ trans.Write(utf8Encoding.GetBytes(str));
+
+ if (escapeNum)
+ trans.Write(QUOTE);
+ }
+ /// <summary>
+ /// Write out contents of byte array b as a JSON string with base-64 encoded
+ /// data
+ /// </summary>
+ private void WriteJSONBase64(byte[] b)
+ {
+ context.Write();
+ trans.Write(QUOTE);
+
+ int len = b.Length;
+ int off = 0;
+
+ while (len >= 3)
+ {
+ // Encode 3 bytes at a time
+ TBase64Utils.encode(b, off, 3, tempBuffer, 0);
+ trans.Write(tempBuffer, 0, 4);
+ off += 3;
+ len -= 3;
+ }
+ if (len > 0)
+ {
+ // Encode remainder
+ TBase64Utils.encode(b, off, len, tempBuffer, 0);
+ trans.Write(tempBuffer, 0, len + 1);
+ }
+
+ trans.Write(QUOTE);
+ }
+
+ private void WriteJSONObjectStart()
+ {
+ context.Write();
+ trans.Write(LBRACE);
+ PushContext(new JSONPairContext(this));
+ }
+
+ private void WriteJSONObjectEnd()
+ {
+ PopContext();
+ trans.Write(RBRACE);
+ }
+
+ private void WriteJSONArrayStart()
+ {
+ context.Write();
+ trans.Write(LBRACKET);
+ PushContext(new JSONListContext(this));
+ }
+
+ private void WriteJSONArrayEnd()
+ {
+ PopContext();
+ trans.Write(RBRACKET);
+ }
+
+ public override void WriteMessageBegin(TMessage message)
+ {
+ WriteJSONArrayStart();
+ WriteJSONInteger(VERSION);
+
+ byte[] b = utf8Encoding.GetBytes(message.Name);
+ WriteJSONString(b);
+
+ WriteJSONInteger((long)message.Type);
+ WriteJSONInteger(message.SeqID);
+ }
+
+ public override void WriteMessageEnd()
+ {
+ WriteJSONArrayEnd();
+ }
+
+ public override void WriteStructBegin(TStruct str)
+ {
+ WriteJSONObjectStart();
+ }
+
+ public override void WriteStructEnd()
+ {
+ WriteJSONObjectEnd();
+ }
+
+ public override void WriteFieldBegin(TField field)
+ {
+ WriteJSONInteger(field.ID);
+ WriteJSONObjectStart();
+ WriteJSONString(GetTypeNameForTypeID(field.Type));
+ }
+
+ public override void WriteFieldEnd()
+ {
+ WriteJSONObjectEnd();
+ }
+
+ public override void WriteFieldStop() { }
+
+ public override void WriteMapBegin(TMap map)
+ {
+ WriteJSONArrayStart();
+ WriteJSONString(GetTypeNameForTypeID(map.KeyType));
+ WriteJSONString(GetTypeNameForTypeID(map.ValueType));
+ WriteJSONInteger(map.Count);
+ WriteJSONObjectStart();
+ }
+
+ public override void WriteMapEnd()
+ {
+ WriteJSONObjectEnd();
+ WriteJSONArrayEnd();
+ }
+
+ public override void WriteListBegin(TList list)
+ {
+ WriteJSONArrayStart();
+ WriteJSONString(GetTypeNameForTypeID(list.ElementType));
+ WriteJSONInteger(list.Count);
+ }
+
+ public override void WriteListEnd()
+ {
+ WriteJSONArrayEnd();
+ }
+
+ public override void WriteSetBegin(TSet set)
+ {
+ WriteJSONArrayStart();
+ WriteJSONString(GetTypeNameForTypeID(set.ElementType));
+ WriteJSONInteger(set.Count);
+ }
+
+ public override void WriteSetEnd()
+ {
+ WriteJSONArrayEnd();
+ }
+
+ public override void WriteBool(bool b)
+ {
+ WriteJSONInteger(b ? (long)1 : (long)0);
+ }
+
+ public override void WriteByte(sbyte b)
+ {
+ WriteJSONInteger((long)b);
+ }
+
+ public override void WriteI16(short i16)
+ {
+ WriteJSONInteger((long)i16);
+ }
+
+ public override void WriteI32(int i32)
+ {
+ WriteJSONInteger((long)i32);
+ }
+
+ public override void WriteI64(long i64)
+ {
+ WriteJSONInteger(i64);
+ }
+
+ public override void WriteDouble(double dub)
+ {
+ WriteJSONDouble(dub);
+ }
+
+ public override void WriteString(string str)
+ {
+ byte[] b = utf8Encoding.GetBytes(str);
+ WriteJSONString(b);
+ }
+
+ public override void WriteBinary(byte[] bin)
+ {
+ WriteJSONBase64(bin);
+ }
+
+ /**
+ * Reading methods.
+ */
+
+ /// <summary>
+ /// Read in a JSON string, unescaping as appropriate.. Skip Reading from the
+ /// context if skipContext is true.
+ /// </summary>
+ private byte[] ReadJSONString(bool skipContext)
+ {
+ MemoryStream buffer = new MemoryStream();
+ List<char> codeunits = new List<char>();
+
+
+ if (!skipContext)
+ {
+ context.Read();
+ }
+ ReadJSONSyntaxChar(QUOTE);
+ while (true)
+ {
+ byte ch = reader.Read();
+ if (ch == QUOTE[0])
+ {
+ break;
+ }
+
+ // escaped?
+ if (ch != ESCSEQ[0])
+ {
+ buffer.Write(new byte[] { (byte)ch }, 0, 1);
+ continue;
+ }
+
+ // distinguish between \uXXXX and \?
+ ch = reader.Read();
+ if (ch != ESCSEQ[1]) // control chars like \n
+ {
+ int off = Array.IndexOf(ESCAPE_CHARS, (char)ch);
+ if (off == -1)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected control char");
+ }
+ ch = ESCAPE_CHAR_VALS[off];
+ buffer.Write(new byte[] { (byte)ch }, 0, 1);
+ continue;
+ }
+
+
+ // it's \uXXXX
+ trans.ReadAll(tempBuffer, 0, 4);
+ var wch = (short)((HexVal((byte)tempBuffer[0]) << 12) +
+ (HexVal((byte)tempBuffer[1]) << 8) +
+ (HexVal((byte)tempBuffer[2]) << 4) +
+ HexVal(tempBuffer[3]));
+ if (Char.IsHighSurrogate((char)wch))
+ {
+ if (codeunits.Count > 0)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected low surrogate char");
+ }
+ codeunits.Add((char)wch);
+ }
+ else if (Char.IsLowSurrogate((char)wch))
+ {
+ if (codeunits.Count == 0)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected high surrogate char");
+ }
+ codeunits.Add((char)wch);
+ var tmp = utf8Encoding.GetBytes(codeunits.ToArray());
+ buffer.Write(tmp, 0, tmp.Length);
+ codeunits.Clear();
+ }
+ else
+ {
+ var tmp = utf8Encoding.GetBytes(new char[] { (char)wch });
+ buffer.Write(tmp, 0, tmp.Length);
+ }
+ }
+
+
+ if (codeunits.Count > 0)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected low surrogate char");
+ }
+
+ return buffer.ToArray();
+ }
+
+ /// <summary>
+ /// Return true if the given byte could be a valid part of a JSON number.
+ /// </summary>
+ private bool IsJSONNumeric(byte b)
+ {
+ switch (b)
+ {
+ case (byte)'+':
+ case (byte)'-':
+ case (byte)'.':
+ case (byte)'0':
+ case (byte)'1':
+ case (byte)'2':
+ case (byte)'3':
+ case (byte)'4':
+ case (byte)'5':
+ case (byte)'6':
+ case (byte)'7':
+ case (byte)'8':
+ case (byte)'9':
+ case (byte)'E':
+ case (byte)'e':
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Read in a sequence of characters that are all valid in JSON numbers. Does
+ /// not do a complete regex check to validate that this is actually a number.
+ /// </summary>
+ private string ReadJSONNumericChars()
+ {
+ StringBuilder strbld = new StringBuilder();
+ while (true)
+ {
+ byte ch = reader.Peek();
+ if (!IsJSONNumeric(ch))
+ {
+ break;
+ }
+ strbld.Append((char)reader.Read());
+ }
+ return strbld.ToString();
+ }
+
+ /// <summary>
+ /// Read in a JSON number. If the context dictates, Read in enclosing quotes.
+ /// </summary>
+ private long ReadJSONInteger()
+ {
+ context.Read();
+ if (context.EscapeNumbers())
+ {
+ ReadJSONSyntaxChar(QUOTE);
+ }
+
+ string str = ReadJSONNumericChars();
+ if (context.EscapeNumbers())
+ {
+ ReadJSONSyntaxChar(QUOTE);
+ }
+
+ try
+ {
+ return Int64.Parse(str);
+ }
+ catch (FormatException fex)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Bad data encounted in numeric data", fex);
+ }
+ }
+
+ /// <summary>
+ /// Read in a JSON double value. Throw if the value is not wrapped in quotes
+ /// when expected or if wrapped in quotes when not expected.
+ /// </summary>
+ private double ReadJSONDouble()
+ {
+ context.Read();
+ if (reader.Peek() == QUOTE[0])
+ {
+ byte[] arr = ReadJSONString(true);
+ double dub = Double.Parse(utf8Encoding.GetString(arr, 0, arr.Length), CultureInfo.InvariantCulture);
+
+ if (!context.EscapeNumbers() && !Double.IsNaN(dub) && !Double.IsInfinity(dub))
+ {
+ // Throw exception -- we should not be in a string in this case
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Numeric data unexpectedly quoted");
+ }
+ return dub;
+ }
+ else
+ {
+ if (context.EscapeNumbers())
+ {
+ // This will throw - we should have had a quote if escapeNum == true
+ ReadJSONSyntaxChar(QUOTE);
+ }
+ try
+ {
+ return Double.Parse(ReadJSONNumericChars(), CultureInfo.InvariantCulture);
+ }
+ catch (FormatException fex)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Bad data encounted in numeric data", fex);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Read in a JSON string containing base-64 encoded data and decode it.
+ /// </summary>
+ private byte[] ReadJSONBase64()
+ {
+ byte[] b = ReadJSONString(false);
+ int len = b.Length;
+ int off = 0;
+ int size = 0;
+ // reduce len to ignore fill bytes
+ while ((len > 0) && (b[len - 1] == '='))
+ {
+ --len;
+ }
+ // read & decode full byte triplets = 4 source bytes
+ while (len > 4)
+ {
+ // Decode 4 bytes at a time
+ TBase64Utils.decode(b, off, 4, b, size); // NB: decoded in place
+ off += 4;
+ len -= 4;
+ size += 3;
+ }
+ // Don't decode if we hit the end or got a single leftover byte (invalid
+ // base64 but legal for skip of regular string type)
+ if (len > 1)
+ {
+ // Decode remainder
+ TBase64Utils.decode(b, off, len, b, size); // NB: decoded in place
+ size += len - 1;
+ }
+ // Sadly we must copy the byte[] (any way around this?)
+ byte[] result = new byte[size];
+ Array.Copy(b, 0, result, 0, size);
+ return result;
+ }
+
+ private void ReadJSONObjectStart()
+ {
+ context.Read();
+ ReadJSONSyntaxChar(LBRACE);
+ PushContext(new JSONPairContext(this));
+ }
+
+ private void ReadJSONObjectEnd()
+ {
+ ReadJSONSyntaxChar(RBRACE);
+ PopContext();
+ }
+
+ private void ReadJSONArrayStart()
+ {
+ context.Read();
+ ReadJSONSyntaxChar(LBRACKET);
+ PushContext(new JSONListContext(this));
+ }
+
+ private void ReadJSONArrayEnd()
+ {
+ ReadJSONSyntaxChar(RBRACKET);
+ PopContext();
+ }
+
+ public override TMessage ReadMessageBegin()
+ {
+ TMessage message = new TMessage();
+ ReadJSONArrayStart();
+ if (ReadJSONInteger() != VERSION)
+ {
+ throw new TProtocolException(TProtocolException.BAD_VERSION,
+ "Message contained bad version.");
+ }
+
+ var buf = ReadJSONString(false);
+ message.Name = utf8Encoding.GetString(buf, 0, buf.Length);
+ message.Type = (TMessageType)ReadJSONInteger();
+ message.SeqID = (int)ReadJSONInteger();
+ return message;
+ }
+
+ public override void ReadMessageEnd()
+ {
+ ReadJSONArrayEnd();
+ }
+
+ public override TStruct ReadStructBegin()
+ {
+ ReadJSONObjectStart();
+ return new TStruct();
+ }
+
+ public override void ReadStructEnd()
+ {
+ ReadJSONObjectEnd();
+ }
+
+ public override TField ReadFieldBegin()
+ {
+ TField field = new TField();
+ byte ch = reader.Peek();
+ if (ch == RBRACE[0])
+ {
+ field.Type = TType.Stop;
+ }
+ else
+ {
+ field.ID = (short)ReadJSONInteger();
+ ReadJSONObjectStart();
+ field.Type = GetTypeIDForTypeName(ReadJSONString(false));
+ }
+ return field;
+ }
+
+ public override void ReadFieldEnd()
+ {
+ ReadJSONObjectEnd();
+ }
+
+ public override TMap ReadMapBegin()
+ {
+ TMap map = new TMap();
+ ReadJSONArrayStart();
+ map.KeyType = GetTypeIDForTypeName(ReadJSONString(false));
+ map.ValueType = GetTypeIDForTypeName(ReadJSONString(false));
+ map.Count = (int)ReadJSONInteger();
+ ReadJSONObjectStart();
+ return map;
+ }
+
+ public override void ReadMapEnd()
+ {
+ ReadJSONObjectEnd();
+ ReadJSONArrayEnd();
+ }
+
+ public override TList ReadListBegin()
+ {
+ TList list = new TList();
+ ReadJSONArrayStart();
+ list.ElementType = GetTypeIDForTypeName(ReadJSONString(false));
+ list.Count = (int)ReadJSONInteger();
+ return list;
+ }
+
+ public override void ReadListEnd()
+ {
+ ReadJSONArrayEnd();
+ }
+
+ public override TSet ReadSetBegin()
+ {
+ TSet set = new TSet();
+ ReadJSONArrayStart();
+ set.ElementType = GetTypeIDForTypeName(ReadJSONString(false));
+ set.Count = (int)ReadJSONInteger();
+ return set;
+ }
+
+ public override void ReadSetEnd()
+ {
+ ReadJSONArrayEnd();
+ }
+
+ public override bool ReadBool()
+ {
+ return (ReadJSONInteger() == 0 ? false : true);
+ }
+
+ public override sbyte ReadByte()
+ {
+ return (sbyte)ReadJSONInteger();
+ }
+
+ public override short ReadI16()
+ {
+ return (short)ReadJSONInteger();
+ }
+
+ public override int ReadI32()
+ {
+ return (int)ReadJSONInteger();
+ }
+
+ public override long ReadI64()
+ {
+ return (long)ReadJSONInteger();
+ }
+
+ public override double ReadDouble()
+ {
+ return ReadJSONDouble();
+ }
+
+ public override string ReadString()
+ {
+ var buf = ReadJSONString(false);
+ return utf8Encoding.GetString(buf, 0, buf.Length);
+ }
+
+ public override byte[] ReadBinary()
+ {
+ return ReadJSONBase64();
+ }
+
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TList.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TList.cs
new file mode 100644
index 000000000..0c8f2144a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TList.cs
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Thrift.Protocol
+{
+ public struct TList
+ {
+ private TType elementType;
+ private int count;
+
+ public TList(TType elementType, int count)
+ :this()
+ {
+ this.elementType = elementType;
+ this.count = count;
+ }
+
+ public TType ElementType
+ {
+ get { return elementType; }
+ set { elementType = value; }
+ }
+
+ public int Count
+ {
+ get { return count; }
+ set { count = value; }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TMap.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TMap.cs
new file mode 100644
index 000000000..aba9d3a95
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TMap.cs
@@ -0,0 +1,62 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Thrift.Protocol
+{
+ public struct TMap
+ {
+ private TType keyType;
+ private TType valueType;
+ private int count;
+
+ public TMap(TType keyType, TType valueType, int count)
+ :this()
+ {
+ this.keyType = keyType;
+ this.valueType = valueType;
+ this.count = count;
+ }
+
+ public TType KeyType
+ {
+ get { return keyType; }
+ set { keyType = value; }
+ }
+
+ public TType ValueType
+ {
+ get { return valueType; }
+ set { valueType = value; }
+ }
+
+ public int Count
+ {
+ get { return count; }
+ set { count = value; }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TMessage.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TMessage.cs
new file mode 100644
index 000000000..348263c37
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TMessage.cs
@@ -0,0 +1,62 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Thrift.Protocol
+{
+ public struct TMessage
+ {
+ private string name;
+ private TMessageType type;
+ private int seqID;
+
+ public TMessage(string name, TMessageType type, int seqid)
+ :this()
+ {
+ this.name = name;
+ this.type = type;
+ this.seqID = seqid;
+ }
+
+ public string Name
+ {
+ get { return name; }
+ set { name = value; }
+ }
+
+ public TMessageType Type
+ {
+ get { return type; }
+ set { type = value; }
+ }
+
+ public int SeqID
+ {
+ get { return seqID; }
+ set { seqID = value; }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TMessageType.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TMessageType.cs
new file mode 100644
index 000000000..c7091fede
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TMessageType.cs
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+
+namespace Thrift.Protocol
+{
+ public enum TMessageType
+ {
+ Call = 1,
+ Reply = 2,
+ Exception = 3,
+ Oneway = 4
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TMultiplexedProcessor.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TMultiplexedProcessor.cs
new file mode 100644
index 000000000..aa91c527f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TMultiplexedProcessor.cs
@@ -0,0 +1,183 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Text;
+using Thrift.Transport;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Thrift.Protocol
+{
+ /// <summary>
+ /// <see cref="TMultiplexedProcessor"/> is a <see cref="TProcessor"/> allowing a single <see cref="Thrift.Server.TServer"/>
+ /// to provide multiple services.
+ /// <para/>
+ /// To do so, you instantiate the processor and then register additional processors with it,
+ /// as shown in the following example:
+ /// <para/>
+ /// <code>
+ /// TMultiplexedProcessor processor = new TMultiplexedProcessor();
+ ///
+ /// processor.registerProcessor(
+ /// "Calculator",
+ /// new Calculator.Processor(new CalculatorHandler()));
+ ///
+ /// processor.registerProcessor(
+ /// "WeatherReport",
+ /// new WeatherReport.Processor(new WeatherReportHandler()));
+ ///
+ /// TServerTransport t = new TServerSocket(9090);
+ /// TSimpleServer server = new TSimpleServer(processor, t);
+ ///
+ /// server.serve();
+ /// </code>
+ /// </summary>
+ public class TMultiplexedProcessor : TProcessor
+ {
+ private Dictionary<string, TProcessor> ServiceProcessorMap = new Dictionary<string, TProcessor>();
+
+ /// <summary>
+ /// 'Register' a service with this TMultiplexedProcessor. This allows us to broker
+ /// requests to individual services by using the service name to select them at request time.
+ ///
+ /// Args:
+ /// - serviceName Name of a service, has to be identical to the name
+ /// declared in the Thrift IDL, e.g. "WeatherReport".
+ /// - processor Implementation of a service, usually referred to as "handlers",
+ /// e.g. WeatherReportHandler implementing WeatherReport.Iface.
+ /// </summary>
+ public void RegisterProcessor(string serviceName, TProcessor processor)
+ {
+ ServiceProcessorMap.Add(serviceName, processor);
+ }
+
+
+ private void Fail(TProtocol oprot, TMessage message, TApplicationException.ExceptionType extype, string etxt)
+ {
+ TApplicationException appex = new TApplicationException(extype, etxt);
+
+ TMessage newMessage = new TMessage(message.Name, TMessageType.Exception, message.SeqID);
+
+ oprot.WriteMessageBegin(newMessage);
+ appex.Write(oprot);
+ oprot.WriteMessageEnd();
+ oprot.Transport.Flush();
+ }
+
+
+ /// <summary>
+ /// This implementation of process performs the following steps:
+ ///
+ /// - Read the beginning of the message.
+ /// - Extract the service name from the message.
+ /// - Using the service name to locate the appropriate processor.
+ /// - Dispatch to the processor, with a decorated instance of TProtocol
+ /// that allows readMessageBegin() to return the original TMessage.
+ /// <para/>
+ /// Throws an exception if
+ /// - the message type is not CALL or ONEWAY,
+ /// - the service name was not found in the message, or
+ /// - the service name has not been RegisterProcessor()ed.
+ /// </summary>
+ public bool Process(TProtocol iprot, TProtocol oprot)
+ {
+ /* Use the actual underlying protocol (e.g. TBinaryProtocol) to read the
+ message header. This pulls the message "off the wire", which we'll
+ deal with at the end of this method. */
+
+ try
+ {
+ TMessage message = iprot.ReadMessageBegin();
+
+ if ((message.Type != TMessageType.Call) && (message.Type != TMessageType.Oneway))
+ {
+ Fail(oprot, message,
+ TApplicationException.ExceptionType.InvalidMessageType,
+ "Message type CALL or ONEWAY expected");
+ return false;
+ }
+
+ // Extract the service name
+ int index = message.Name.IndexOf(TMultiplexedProtocol.SEPARATOR);
+ if (index < 0)
+ {
+ Fail(oprot, message,
+ TApplicationException.ExceptionType.InvalidProtocol,
+ "Service name not found in message name: " + message.Name + ". " +
+ "Did you forget to use a TMultiplexProtocol in your client?");
+ return false;
+ }
+
+ // Create a new TMessage, something that can be consumed by any TProtocol
+ string serviceName = message.Name.Substring(0, index);
+ TProcessor actualProcessor;
+ if (!ServiceProcessorMap.TryGetValue(serviceName, out actualProcessor))
+ {
+ Fail(oprot, message,
+ TApplicationException.ExceptionType.InternalError,
+ "Service name not found: " + serviceName + ". " +
+ "Did you forget to call RegisterProcessor()?");
+ return false;
+ }
+
+ // Create a new TMessage, removing the service name
+ TMessage newMessage = new TMessage(
+ message.Name.Substring(serviceName.Length + TMultiplexedProtocol.SEPARATOR.Length),
+ message.Type,
+ message.SeqID);
+
+ // Dispatch processing to the stored processor
+ return actualProcessor.Process(new StoredMessageProtocol(iprot, newMessage), oprot);
+
+ }
+ catch (IOException)
+ {
+ return false; // similar to all other processors
+ }
+
+ }
+
+ /// <summary>
+ /// Our goal was to work with any protocol. In order to do that, we needed
+ /// to allow them to call readMessageBegin() and get a TMessage in exactly
+ /// the standard format, without the service name prepended to TMessage.name.
+ /// </summary>
+ private class StoredMessageProtocol : TProtocolDecorator
+ {
+ TMessage MsgBegin;
+
+ public StoredMessageProtocol(TProtocol protocol, TMessage messageBegin)
+ : base(protocol)
+ {
+ this.MsgBegin = messageBegin;
+ }
+
+ public override TMessage ReadMessageBegin()
+ {
+ return MsgBegin;
+ }
+ }
+
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TMultiplexedProtocol.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TMultiplexedProtocol.cs
new file mode 100644
index 000000000..1bd420fcc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TMultiplexedProtocol.cs
@@ -0,0 +1,103 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Text;
+using Thrift.Transport;
+using System.Collections.Generic;
+
+namespace Thrift.Protocol
+{
+
+ /// <summary>
+ /// TMultiplexedProtocol is a protocol-independent concrete decorator that allows a Thrift
+ /// client to communicate with a multiplexing Thrift server, by prepending the service name
+ /// to the function name during function calls.
+ /// <para/>
+ /// NOTE: THIS IS NOT TO BE USED BY SERVERS.
+ /// On the server, use TMultiplexedProcessor to handle requests from a multiplexing client.
+ /// <para/>
+ /// This example uses a single socket transport to invoke two services:
+ /// <code>
+ /// TSocket transport = new TSocket("localhost", 9090);
+ /// transport.open();
+ ///
+ /// TBinaryProtocol protocol = new TBinaryProtocol(transport);
+ ///
+ /// TMultiplexedProtocol mp = new TMultiplexedProtocol(protocol, "Calculator");
+ /// Calculator.Client service = new Calculator.Client(mp);
+ ///
+ /// TMultiplexedProtocol mp2 = new TMultiplexedProtocol(protocol, "WeatherReport");
+ /// WeatherReport.Client service2 = new WeatherReport.Client(mp2);
+ ///
+ /// System.out.println(service.add(2,2));
+ /// System.out.println(service2.getTemperature());
+ /// </code>
+ /// </summary>
+ public class TMultiplexedProtocol : TProtocolDecorator
+ {
+
+ /// <summary>
+ /// Used to delimit the service name from the function name.
+ /// </summary>
+ public static string SEPARATOR = ":";
+
+ private string ServiceName;
+
+ /// <summary>
+ /// Wrap the specified protocol, allowing it to be used to communicate with a
+ /// multiplexing server. The <paramref name="serviceName"/> is required as it is
+ /// prepended to the message header so that the multiplexing server can broker
+ /// the function call to the proper service.
+ /// </summary>
+ /// <param name="protocol">Your communication protocol of choice, e.g. <see cref="TBinaryProtocol"/>.</param>
+ /// <param name="serviceName">The service name of the service communicating via this protocol.</param>
+ public TMultiplexedProtocol(TProtocol protocol, string serviceName)
+ : base(protocol)
+ {
+ ServiceName = serviceName;
+ }
+
+ /// <summary>
+ /// Prepends the service name to the function name, separated by TMultiplexedProtocol.SEPARATOR.
+ /// </summary>
+ /// <param name="tMessage">The original message.</param>
+ public override void WriteMessageBegin(TMessage tMessage)
+ {
+ switch (tMessage.Type)
+ {
+ case TMessageType.Call:
+ case TMessageType.Oneway:
+ base.WriteMessageBegin(new TMessage(
+ ServiceName + SEPARATOR + tMessage.Name,
+ tMessage.Type,
+ tMessage.SeqID));
+ break;
+
+ default:
+ base.WriteMessageBegin(tMessage);
+ break;
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocol.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocol.cs
new file mode 100644
index 000000000..dd7a6e062
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocol.cs
@@ -0,0 +1,142 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Text;
+using Thrift.Transport;
+
+namespace Thrift.Protocol
+{
+ public abstract class TProtocol : IDisposable
+ {
+ private const int DEFAULT_RECURSION_DEPTH = 64;
+
+ protected TTransport trans;
+ protected int recursionLimit;
+ protected int recursionDepth;
+
+ protected TProtocol(TTransport trans)
+ {
+ this.trans = trans;
+ this.recursionLimit = DEFAULT_RECURSION_DEPTH;
+ this.recursionDepth = 0;
+ }
+
+ public TTransport Transport
+ {
+ get { return trans; }
+ }
+
+ public int RecursionLimit
+ {
+ get { return recursionLimit; }
+ set { recursionLimit = value; }
+ }
+
+ public void IncrementRecursionDepth()
+ {
+ if (recursionDepth < recursionLimit)
+ ++recursionDepth;
+ else
+ throw new TProtocolException(TProtocolException.DEPTH_LIMIT, "Depth limit exceeded");
+ }
+
+ public void DecrementRecursionDepth()
+ {
+ --recursionDepth;
+ }
+
+ #region " IDisposable Support "
+ private bool _IsDisposed;
+
+ // IDisposable
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_IsDisposed)
+ {
+ if (disposing)
+ {
+ if (trans is IDisposable)
+ (trans as IDisposable).Dispose();
+ }
+ }
+ _IsDisposed = true;
+ }
+ #endregion
+
+ public abstract void WriteMessageBegin(TMessage message);
+ public abstract void WriteMessageEnd();
+ public abstract void WriteStructBegin(TStruct struc);
+ public abstract void WriteStructEnd();
+ public abstract void WriteFieldBegin(TField field);
+ public abstract void WriteFieldEnd();
+ public abstract void WriteFieldStop();
+ public abstract void WriteMapBegin(TMap map);
+ public abstract void WriteMapEnd();
+ public abstract void WriteListBegin(TList list);
+ public abstract void WriteListEnd();
+ public abstract void WriteSetBegin(TSet set);
+ public abstract void WriteSetEnd();
+ public abstract void WriteBool(bool b);
+ public abstract void WriteByte(sbyte b);
+ public abstract void WriteI16(short i16);
+ public abstract void WriteI32(int i32);
+ public abstract void WriteI64(long i64);
+ public abstract void WriteDouble(double d);
+ public virtual void WriteString(string s)
+ {
+ WriteBinary(Encoding.UTF8.GetBytes(s));
+ }
+ public abstract void WriteBinary(byte[] b);
+
+ public abstract TMessage ReadMessageBegin();
+ public abstract void ReadMessageEnd();
+ public abstract TStruct ReadStructBegin();
+ public abstract void ReadStructEnd();
+ public abstract TField ReadFieldBegin();
+ public abstract void ReadFieldEnd();
+ public abstract TMap ReadMapBegin();
+ public abstract void ReadMapEnd();
+ public abstract TList ReadListBegin();
+ public abstract void ReadListEnd();
+ public abstract TSet ReadSetBegin();
+ public abstract void ReadSetEnd();
+ public abstract bool ReadBool();
+ public abstract sbyte ReadByte();
+ public abstract short ReadI16();
+ public abstract int ReadI32();
+ public abstract long ReadI64();
+ public abstract double ReadDouble();
+ public virtual string ReadString()
+ {
+ var buf = ReadBinary();
+ return Encoding.UTF8.GetString(buf, 0, buf.Length);
+ }
+ public abstract byte[] ReadBinary();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocolDecorator.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocolDecorator.cs
new file mode 100644
index 000000000..86000027f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocolDecorator.cs
@@ -0,0 +1,261 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Text;
+using Thrift.Transport;
+using System.Collections.Generic;
+
+namespace Thrift.Protocol
+{
+ /// <summary>
+ /// <see cref="TProtocolDecorator"/> forwards all requests to an enclosed <see cref="TProtocol"/> instance,
+ /// providing a way to author concise concrete decorator subclasses. While it has
+ /// no abstract methods, it is marked abstract as a reminder that by itself,
+ /// it does not modify the behaviour of the enclosed <see cref="TProtocol"/>.
+ /// <para/>
+ /// See p.175 of Design Patterns (by Gamma et al.)
+ /// </summary>
+ /// <seealso cref="TMultiplexedProtocol"/>
+ public abstract class TProtocolDecorator : TProtocol
+ {
+ private TProtocol WrappedProtocol;
+
+ /// <summary>
+ /// Encloses the specified protocol.
+ /// </summary>
+ /// <param name="protocol">All operations will be forward to this protocol. Must be non-null.</param>
+ public TProtocolDecorator(TProtocol protocol)
+ : base(protocol.Transport)
+ {
+
+ WrappedProtocol = protocol;
+ }
+
+ public override void WriteMessageBegin(TMessage tMessage)
+ {
+ WrappedProtocol.WriteMessageBegin(tMessage);
+ }
+
+ public override void WriteMessageEnd()
+ {
+ WrappedProtocol.WriteMessageEnd();
+ }
+
+ public override void WriteStructBegin(TStruct tStruct)
+ {
+ WrappedProtocol.WriteStructBegin(tStruct);
+ }
+
+ public override void WriteStructEnd()
+ {
+ WrappedProtocol.WriteStructEnd();
+ }
+
+ public override void WriteFieldBegin(TField tField)
+ {
+ WrappedProtocol.WriteFieldBegin(tField);
+ }
+
+ public override void WriteFieldEnd()
+ {
+ WrappedProtocol.WriteFieldEnd();
+ }
+
+ public override void WriteFieldStop()
+ {
+ WrappedProtocol.WriteFieldStop();
+ }
+
+ public override void WriteMapBegin(TMap tMap)
+ {
+ WrappedProtocol.WriteMapBegin(tMap);
+ }
+
+ public override void WriteMapEnd()
+ {
+ WrappedProtocol.WriteMapEnd();
+ }
+
+ public override void WriteListBegin(TList tList)
+ {
+ WrappedProtocol.WriteListBegin(tList);
+ }
+
+ public override void WriteListEnd()
+ {
+ WrappedProtocol.WriteListEnd();
+ }
+
+ public override void WriteSetBegin(TSet tSet)
+ {
+ WrappedProtocol.WriteSetBegin(tSet);
+ }
+
+ public override void WriteSetEnd()
+ {
+ WrappedProtocol.WriteSetEnd();
+ }
+
+ public override void WriteBool(bool b)
+ {
+ WrappedProtocol.WriteBool(b);
+ }
+
+ public override void WriteByte(sbyte b)
+ {
+ WrappedProtocol.WriteByte(b);
+ }
+
+ public override void WriteI16(short i)
+ {
+ WrappedProtocol.WriteI16(i);
+ }
+
+ public override void WriteI32(int i)
+ {
+ WrappedProtocol.WriteI32(i);
+ }
+
+ public override void WriteI64(long l)
+ {
+ WrappedProtocol.WriteI64(l);
+ }
+
+ public override void WriteDouble(double v)
+ {
+ WrappedProtocol.WriteDouble(v);
+ }
+
+ public override void WriteString(string s)
+ {
+ WrappedProtocol.WriteString(s);
+ }
+
+ public override void WriteBinary(byte[] bytes)
+ {
+ WrappedProtocol.WriteBinary(bytes);
+ }
+
+ public override TMessage ReadMessageBegin()
+ {
+ return WrappedProtocol.ReadMessageBegin();
+ }
+
+ public override void ReadMessageEnd()
+ {
+ WrappedProtocol.ReadMessageEnd();
+ }
+
+ public override TStruct ReadStructBegin()
+ {
+ return WrappedProtocol.ReadStructBegin();
+ }
+
+ public override void ReadStructEnd()
+ {
+ WrappedProtocol.ReadStructEnd();
+ }
+
+ public override TField ReadFieldBegin()
+ {
+ return WrappedProtocol.ReadFieldBegin();
+ }
+
+ public override void ReadFieldEnd()
+ {
+ WrappedProtocol.ReadFieldEnd();
+ }
+
+ public override TMap ReadMapBegin()
+ {
+ return WrappedProtocol.ReadMapBegin();
+ }
+
+ public override void ReadMapEnd()
+ {
+ WrappedProtocol.ReadMapEnd();
+ }
+
+ public override TList ReadListBegin()
+ {
+ return WrappedProtocol.ReadListBegin();
+ }
+
+ public override void ReadListEnd()
+ {
+ WrappedProtocol.ReadListEnd();
+ }
+
+ public override TSet ReadSetBegin()
+ {
+ return WrappedProtocol.ReadSetBegin();
+ }
+
+ public override void ReadSetEnd()
+ {
+ WrappedProtocol.ReadSetEnd();
+ }
+
+ public override bool ReadBool()
+ {
+ return WrappedProtocol.ReadBool();
+ }
+
+ public override sbyte ReadByte()
+ {
+ return WrappedProtocol.ReadByte();
+ }
+
+ public override short ReadI16()
+ {
+ return WrappedProtocol.ReadI16();
+ }
+
+ public override int ReadI32()
+ {
+ return WrappedProtocol.ReadI32();
+ }
+
+ public override long ReadI64()
+ {
+ return WrappedProtocol.ReadI64();
+ }
+
+ public override double ReadDouble()
+ {
+ return WrappedProtocol.ReadDouble();
+ }
+
+ public override string ReadString()
+ {
+ return WrappedProtocol.ReadString();
+ }
+
+ public override byte[] ReadBinary()
+ {
+ return WrappedProtocol.ReadBinary();
+ }
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocolException.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocolException.cs
new file mode 100644
index 000000000..7bef23685
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocolException.cs
@@ -0,0 +1,67 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+
+namespace Thrift.Protocol
+{
+ public class TProtocolException : TException
+ {
+ public const int UNKNOWN = 0;
+ public const int INVALID_DATA = 1;
+ public const int NEGATIVE_SIZE = 2;
+ public const int SIZE_LIMIT = 3;
+ public const int BAD_VERSION = 4;
+ public const int NOT_IMPLEMENTED = 5;
+ public const int DEPTH_LIMIT = 6;
+
+ protected int type_ = UNKNOWN;
+
+ public TProtocolException()
+ : base()
+ {
+ }
+
+ public TProtocolException(int type, Exception inner = null)
+ : base(string.Empty, inner)
+ {
+ type_ = type;
+ }
+
+ public TProtocolException(int type, string message, Exception inner = null)
+ : base(message, inner)
+ {
+ type_ = type;
+ }
+
+ public TProtocolException(string message, Exception inner = null)
+ : base(message, inner)
+ {
+ }
+
+ public int getType()
+ {
+ return type_;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocolFactory.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocolFactory.cs
new file mode 100644
index 000000000..71360a19a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocolFactory.cs
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using Thrift.Transport;
+
+namespace Thrift.Protocol
+{
+ public interface TProtocolFactory
+ {
+ TProtocol GetProtocol(TTransport trans);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocolUtil.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocolUtil.cs
new file mode 100644
index 000000000..d995c6ce7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TProtocolUtil.cs
@@ -0,0 +1,108 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+
+namespace Thrift.Protocol
+{
+ public static class TProtocolUtil
+ {
+ public static void Skip(TProtocol prot, TType type)
+ {
+ prot.IncrementRecursionDepth();
+ try
+ {
+ switch (type)
+ {
+ case TType.Bool:
+ prot.ReadBool();
+ break;
+ case TType.Byte:
+ prot.ReadByte();
+ break;
+ case TType.I16:
+ prot.ReadI16();
+ break;
+ case TType.I32:
+ prot.ReadI32();
+ break;
+ case TType.I64:
+ prot.ReadI64();
+ break;
+ case TType.Double:
+ prot.ReadDouble();
+ break;
+ case TType.String:
+ // Don't try to decode the string, just skip it.
+ prot.ReadBinary();
+ break;
+ case TType.Struct:
+ prot.ReadStructBegin();
+ while (true)
+ {
+ TField field = prot.ReadFieldBegin();
+ if (field.Type == TType.Stop)
+ {
+ break;
+ }
+ Skip(prot, field.Type);
+ prot.ReadFieldEnd();
+ }
+ prot.ReadStructEnd();
+ break;
+ case TType.Map:
+ TMap map = prot.ReadMapBegin();
+ for (int i = 0; i < map.Count; i++)
+ {
+ Skip(prot, map.KeyType);
+ Skip(prot, map.ValueType);
+ }
+ prot.ReadMapEnd();
+ break;
+ case TType.Set:
+ TSet set = prot.ReadSetBegin();
+ for (int i = 0; i < set.Count; i++)
+ {
+ Skip(prot, set.ElementType);
+ }
+ prot.ReadSetEnd();
+ break;
+ case TType.List:
+ TList list = prot.ReadListBegin();
+ for (int i = 0; i < list.Count; i++)
+ {
+ Skip(prot, list.ElementType);
+ }
+ prot.ReadListEnd();
+ break;
+ default:
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Unknown data type " + type.ToString("d"));
+ }
+ }
+ finally
+ {
+ prot.DecrementRecursionDepth();
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TSet.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TSet.cs
new file mode 100644
index 000000000..a918ab537
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TSet.cs
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Thrift.Protocol
+{
+ public struct TSet
+ {
+ private TType elementType;
+ private int count;
+
+ public TSet(TType elementType, int count)
+ :this()
+ {
+ this.elementType = elementType;
+ this.count = count;
+ }
+
+ public TSet(TList list)
+ : this(list.ElementType, list.Count)
+ {
+ }
+
+ public TType ElementType
+ {
+ get { return elementType; }
+ set { elementType = value; }
+ }
+
+ public int Count
+ {
+ get { return count; }
+ set { count = value; }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TStruct.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TStruct.cs
new file mode 100644
index 000000000..f4844a4c9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TStruct.cs
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Thrift.Protocol
+{
+ public struct TStruct
+ {
+ private string name;
+
+ public TStruct(string name)
+ :this()
+ {
+ this.name = name;
+ }
+
+ public string Name
+ {
+ get { return name; }
+ set { name = value; }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Protocol/TType.cs b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TType.cs
new file mode 100644
index 000000000..9ce915e08
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Protocol/TType.cs
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+
+namespace Thrift.Protocol
+{
+ public enum TType : byte
+ {
+ Stop = 0,
+ Void = 1,
+ Bool = 2,
+ Byte = 3,
+ Double = 4,
+ I16 = 6,
+ I32 = 8,
+ I64 = 10,
+ String = 11,
+ Struct = 12,
+ Map = 13,
+ Set = 14,
+ List = 15
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Server/TServer.cs b/src/jaegertracing/thrift/lib/csharp/src/Server/TServer.cs
new file mode 100644
index 000000000..2bc04f3a0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Server/TServer.cs
@@ -0,0 +1,155 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using Thrift.Protocol;
+using Thrift.Transport;
+using System.IO;
+
+namespace Thrift.Server
+{
+ public abstract class TServer
+ {
+ //Attributes
+ protected TProcessorFactory processorFactory;
+ protected TServerTransport serverTransport;
+ protected TTransportFactory inputTransportFactory;
+ protected TTransportFactory outputTransportFactory;
+ protected TProtocolFactory inputProtocolFactory;
+ protected TProtocolFactory outputProtocolFactory;
+ protected TServerEventHandler serverEventHandler = null;
+
+ //Methods
+ public void setEventHandler(TServerEventHandler seh)
+ {
+ serverEventHandler = seh;
+ }
+ public TServerEventHandler getEventHandler()
+ {
+ return serverEventHandler;
+ }
+
+ //Log delegation
+ public delegate void LogDelegate(string str);
+ private LogDelegate _logDelegate;
+ protected LogDelegate logDelegate
+ {
+ get { return _logDelegate; }
+ set { _logDelegate = (value != null) ? value : DefaultLogDelegate; }
+ }
+ protected static void DefaultLogDelegate(string s)
+ {
+ Console.Error.WriteLine(s);
+ }
+
+ //Construction
+ public TServer(TProcessor processor,
+ TServerTransport serverTransport)
+ : this(processor, serverTransport,
+ new TTransportFactory(),
+ new TTransportFactory(),
+ new TBinaryProtocol.Factory(),
+ new TBinaryProtocol.Factory(),
+ DefaultLogDelegate)
+ {
+ }
+
+ public TServer(TProcessor processor,
+ TServerTransport serverTransport,
+ LogDelegate logDelegate)
+ : this(processor,
+ serverTransport,
+ new TTransportFactory(),
+ new TTransportFactory(),
+ new TBinaryProtocol.Factory(),
+ new TBinaryProtocol.Factory(),
+ logDelegate)
+ {
+ }
+
+ public TServer(TProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory)
+ : this(processor,
+ serverTransport,
+ transportFactory,
+ transportFactory,
+ new TBinaryProtocol.Factory(),
+ new TBinaryProtocol.Factory(),
+ DefaultLogDelegate)
+ {
+ }
+
+ public TServer(TProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory)
+ : this(processor,
+ serverTransport,
+ transportFactory,
+ transportFactory,
+ protocolFactory,
+ protocolFactory,
+ DefaultLogDelegate)
+ {
+ }
+
+ public TServer(TProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ LogDelegate logDelegate)
+ {
+ this.processorFactory = new TSingletonProcessorFactory(processor);
+ this.serverTransport = serverTransport;
+ this.inputTransportFactory = inputTransportFactory;
+ this.outputTransportFactory = outputTransportFactory;
+ this.inputProtocolFactory = inputProtocolFactory;
+ this.outputProtocolFactory = outputProtocolFactory;
+ this.logDelegate = (logDelegate != null) ? logDelegate : DefaultLogDelegate;
+ }
+
+ public TServer(TProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ LogDelegate logDelegate)
+ {
+ this.processorFactory = processorFactory;
+ this.serverTransport = serverTransport;
+ this.inputTransportFactory = inputTransportFactory;
+ this.outputTransportFactory = outputTransportFactory;
+ this.inputProtocolFactory = inputProtocolFactory;
+ this.outputProtocolFactory = outputProtocolFactory;
+ this.logDelegate = (logDelegate != null) ? logDelegate : DefaultLogDelegate;
+ }
+
+ //Abstract Interface
+ public abstract void Serve();
+ public abstract void Stop();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Server/TServerEventHandler.cs b/src/jaegertracing/thrift/lib/csharp/src/Server/TServerEventHandler.cs
new file mode 100644
index 000000000..e81efc6af
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Server/TServerEventHandler.cs
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+
+namespace Thrift.Server
+{
+ /// <summary>
+ /// Interface implemented by server users to handle events from the server.
+ /// </summary>
+ public interface TServerEventHandler
+ {
+ /// <summary>
+ /// Called before the server begins.
+ /// </summary>
+ void preServe();
+
+ /// <summary>
+ /// Called when a new client has connected and is about to being processing.
+ /// </summary>
+ object createContext(Thrift.Protocol.TProtocol input, Thrift.Protocol.TProtocol output);
+
+ /// <summary>
+ /// Called when a client has finished request-handling to delete server context.
+ /// </summary>
+ void deleteContext(object serverContext, Thrift.Protocol.TProtocol input, Thrift.Protocol.TProtocol output);
+
+ /// <summary>
+ /// Called when a client is about to call the processor.
+ /// </summary>
+ void processContext(object serverContext, Thrift.Transport.TTransport transport);
+ };
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Server/TSimpleServer.cs b/src/jaegertracing/thrift/lib/csharp/src/Server/TSimpleServer.cs
new file mode 100644
index 000000000..4e7ea96f4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Server/TSimpleServer.cs
@@ -0,0 +1,180 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using Thrift.Transport;
+using Thrift.Protocol;
+
+namespace Thrift.Server
+{
+ /// <summary>
+ /// Simple single-threaded server for testing.
+ /// </summary>
+ public class TSimpleServer : TServer
+ {
+ private bool stop = false;
+
+ public TSimpleServer(TProcessor processor,
+ TServerTransport serverTransport)
+ : base(processor, serverTransport, new TTransportFactory(), new TTransportFactory(), new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(), DefaultLogDelegate)
+ {
+ }
+
+ public TSimpleServer(TProcessor processor,
+ TServerTransport serverTransport,
+ LogDelegate logDel)
+ : base(processor, serverTransport, new TTransportFactory(), new TTransportFactory(), new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(), logDel)
+ {
+ }
+
+ public TSimpleServer(TProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory)
+ : base(processor,
+ serverTransport,
+ transportFactory,
+ transportFactory,
+ new TBinaryProtocol.Factory(),
+ new TBinaryProtocol.Factory(),
+ DefaultLogDelegate)
+ {
+ }
+
+ public TSimpleServer(TProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory)
+ : base(processor,
+ serverTransport,
+ transportFactory,
+ transportFactory,
+ protocolFactory,
+ protocolFactory,
+ DefaultLogDelegate)
+ {
+ }
+
+ public TSimpleServer(TProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory)
+ : base(processorFactory,
+ serverTransport,
+ transportFactory,
+ transportFactory,
+ protocolFactory,
+ protocolFactory,
+ DefaultLogDelegate)
+ {
+ }
+
+ public override void Serve()
+ {
+ try
+ {
+ serverTransport.Listen();
+ }
+ catch (TTransportException ttx)
+ {
+ logDelegate(ttx.ToString());
+ return;
+ }
+
+ //Fire the preServe server event when server is up but before any client connections
+ if (serverEventHandler != null)
+ serverEventHandler.preServe();
+
+ while (!stop)
+ {
+ TProcessor processor = null;
+ TTransport client = null;
+ TTransport inputTransport = null;
+ TTransport outputTransport = null;
+ TProtocol inputProtocol = null;
+ TProtocol outputProtocol = null;
+ object connectionContext = null;
+ try
+ {
+ using (client = serverTransport.Accept())
+ {
+ processor = processorFactory.GetProcessor(client);
+ if (client != null)
+ {
+ using (inputTransport = inputTransportFactory.GetTransport(client))
+ {
+ using (outputTransport = outputTransportFactory.GetTransport(client))
+ {
+ inputProtocol = inputProtocolFactory.GetProtocol(inputTransport);
+ outputProtocol = outputProtocolFactory.GetProtocol(outputTransport);
+
+ //Recover event handler (if any) and fire createContext server event when a client connects
+ if (serverEventHandler != null)
+ connectionContext = serverEventHandler.createContext(inputProtocol, outputProtocol);
+
+ //Process client requests until client disconnects
+ while (!stop)
+ {
+ if (!inputTransport.Peek())
+ break;
+
+ //Fire processContext server event
+ //N.B. This is the pattern implemented in C++ and the event fires provisionally.
+ //That is to say it may be many minutes between the event firing and the client request
+ //actually arriving or the client may hang up without ever makeing a request.
+ if (serverEventHandler != null)
+ serverEventHandler.processContext(connectionContext, inputTransport);
+ //Process client request (blocks until transport is readable)
+ if (!processor.Process(inputProtocol, outputProtocol))
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (TTransportException ttx)
+ {
+ if (!stop || ttx.Type != TTransportException.ExceptionType.Interrupted)
+ {
+ logDelegate(ttx.ToString());
+ }
+ }
+ catch (Exception x)
+ {
+ //Unexpected
+ logDelegate(x.ToString());
+ }
+
+ //Fire deleteContext server event after client disconnects
+ if (serverEventHandler != null)
+ serverEventHandler.deleteContext(connectionContext, inputProtocol, outputProtocol);
+ }
+ }
+
+ public override void Stop()
+ {
+ stop = true;
+ serverTransport.Close();
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Server/TThreadPoolServer.cs b/src/jaegertracing/thrift/lib/csharp/src/Server/TThreadPoolServer.cs
new file mode 100644
index 000000000..a494ce7bd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Server/TThreadPoolServer.cs
@@ -0,0 +1,295 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Threading;
+using Thrift.Protocol;
+using Thrift.Transport;
+
+namespace Thrift.Server
+{
+ /// <summary>
+ /// Server that uses C# built-in ThreadPool to spawn threads when handling requests.
+ /// </summary>
+ public class TThreadPoolServer : TServer
+ {
+ private const int DEFAULT_MIN_THREADS = -1; // use .NET ThreadPool defaults
+ private const int DEFAULT_MAX_THREADS = -1; // use .NET ThreadPool defaults
+ private volatile bool stop = false;
+
+ public struct Configuration
+ {
+ public int MinWorkerThreads;
+ public int MaxWorkerThreads;
+ public int MinIOThreads;
+ public int MaxIOThreads;
+
+ public Configuration(int min = DEFAULT_MIN_THREADS, int max = DEFAULT_MAX_THREADS)
+ {
+ MinWorkerThreads = min;
+ MaxWorkerThreads = max;
+ MinIOThreads = min;
+ MaxIOThreads = max;
+ }
+
+ public Configuration(int minWork, int maxWork, int minIO, int maxIO)
+ {
+ MinWorkerThreads = minWork;
+ MaxWorkerThreads = maxWork;
+ MinIOThreads = minIO;
+ MaxIOThreads = maxIO;
+ }
+ }
+
+ public TThreadPoolServer(TProcessor processor, TServerTransport serverTransport)
+ : this(new TSingletonProcessorFactory(processor), serverTransport,
+ new TTransportFactory(), new TTransportFactory(),
+ new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(),
+ new Configuration(), DefaultLogDelegate)
+ {
+ }
+
+ public TThreadPoolServer(TProcessor processor, TServerTransport serverTransport, LogDelegate logDelegate)
+ : this(new TSingletonProcessorFactory(processor), serverTransport,
+ new TTransportFactory(), new TTransportFactory(),
+ new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(),
+ new Configuration(), logDelegate)
+ {
+ }
+
+ public TThreadPoolServer(TProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory)
+ : this(new TSingletonProcessorFactory(processor), serverTransport,
+ transportFactory, transportFactory,
+ protocolFactory, protocolFactory,
+ new Configuration(), DefaultLogDelegate)
+ {
+ }
+
+ public TThreadPoolServer(TProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory)
+ : this(processorFactory, serverTransport,
+ transportFactory, transportFactory,
+ protocolFactory, protocolFactory,
+ new Configuration(), DefaultLogDelegate)
+ {
+ }
+
+ public TThreadPoolServer(TProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ int minThreadPoolThreads, int maxThreadPoolThreads, LogDelegate logDel)
+ : this(processorFactory, serverTransport, inputTransportFactory, outputTransportFactory,
+ inputProtocolFactory, outputProtocolFactory,
+ new Configuration(minThreadPoolThreads, maxThreadPoolThreads),
+ logDel)
+ {
+ }
+
+ public TThreadPoolServer(TProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ Configuration threadConfig,
+ LogDelegate logDel)
+ : base(processorFactory, serverTransport, inputTransportFactory, outputTransportFactory,
+ inputProtocolFactory, outputProtocolFactory, logDel)
+ {
+ lock (typeof(TThreadPoolServer))
+ {
+ if ((threadConfig.MaxWorkerThreads > 0) || (threadConfig.MaxIOThreads > 0))
+ {
+ int work, comm;
+ ThreadPool.GetMaxThreads(out work, out comm);
+ if (threadConfig.MaxWorkerThreads > 0)
+ work = threadConfig.MaxWorkerThreads;
+ if (threadConfig.MaxIOThreads > 0)
+ comm = threadConfig.MaxIOThreads;
+ if (!ThreadPool.SetMaxThreads(work, comm))
+ throw new Exception("Error: could not SetMaxThreads in ThreadPool");
+ }
+
+ if ((threadConfig.MinWorkerThreads > 0) || (threadConfig.MinIOThreads > 0))
+ {
+ int work, comm;
+ ThreadPool.GetMinThreads(out work, out comm);
+ if (threadConfig.MinWorkerThreads > 0)
+ work = threadConfig.MinWorkerThreads;
+ if (threadConfig.MinIOThreads > 0)
+ comm = threadConfig.MinIOThreads;
+ if (!ThreadPool.SetMinThreads(work, comm))
+ throw new Exception("Error: could not SetMinThreads in ThreadPool");
+ }
+ }
+ }
+
+
+ /// <summary>
+ /// Use new ThreadPool thread for each new client connection.
+ /// </summary>
+ public override void Serve()
+ {
+ try
+ {
+ serverTransport.Listen();
+ }
+ catch (TTransportException ttx)
+ {
+ logDelegate("Error, could not listen on ServerTransport: " + ttx);
+ return;
+ }
+
+ //Fire the preServe server event when server is up but before any client connections
+ if (serverEventHandler != null)
+ serverEventHandler.preServe();
+
+ while (!stop)
+ {
+ int failureCount = 0;
+ try
+ {
+ TTransport client = serverTransport.Accept();
+ ThreadPool.QueueUserWorkItem(this.Execute, client);
+ }
+ catch (TTransportException ttx)
+ {
+ if (!stop || ttx.Type != TTransportException.ExceptionType.Interrupted)
+ {
+ ++failureCount;
+ logDelegate(ttx.ToString());
+ }
+
+ }
+ }
+
+ if (stop)
+ {
+ try
+ {
+ serverTransport.Close();
+ }
+ catch (TTransportException ttx)
+ {
+ logDelegate("TServerTransport failed on close: " + ttx.Message);
+ }
+ stop = false;
+ }
+ }
+
+ /// <summary>
+ /// Loops on processing a client forever
+ /// threadContext will be a TTransport instance
+ /// </summary>
+ /// <param name="threadContext"></param>
+ private void Execute(object threadContext)
+ {
+ using (TTransport client = (TTransport)threadContext)
+ {
+ TProcessor processor = processorFactory.GetProcessor(client, this);
+ TTransport inputTransport = null;
+ TTransport outputTransport = null;
+ TProtocol inputProtocol = null;
+ TProtocol outputProtocol = null;
+ object connectionContext = null;
+ try
+ {
+ try
+ {
+ inputTransport = inputTransportFactory.GetTransport(client);
+ outputTransport = outputTransportFactory.GetTransport(client);
+ inputProtocol = inputProtocolFactory.GetProtocol(inputTransport);
+ outputProtocol = outputProtocolFactory.GetProtocol(outputTransport);
+
+ //Recover event handler (if any) and fire createContext server event when a client connects
+ if (serverEventHandler != null)
+ connectionContext = serverEventHandler.createContext(inputProtocol, outputProtocol);
+
+ //Process client requests until client disconnects
+ while (!stop)
+ {
+ if (!inputTransport.Peek())
+ break;
+
+ //Fire processContext server event
+ //N.B. This is the pattern implemented in C++ and the event fires provisionally.
+ //That is to say it may be many minutes between the event firing and the client request
+ //actually arriving or the client may hang up without ever makeing a request.
+ if (serverEventHandler != null)
+ serverEventHandler.processContext(connectionContext, inputTransport);
+ //Process client request (blocks until transport is readable)
+ if (!processor.Process(inputProtocol, outputProtocol))
+ break;
+ }
+ }
+ catch (TTransportException)
+ {
+ //Usually a client disconnect, expected
+ }
+ catch (Exception x)
+ {
+ //Unexpected
+ logDelegate("Error: " + x);
+ }
+
+ //Fire deleteContext server event after client disconnects
+ if (serverEventHandler != null)
+ serverEventHandler.deleteContext(connectionContext, inputProtocol, outputProtocol);
+
+ }
+ finally
+ {
+ //Close transports
+ if (inputTransport != null)
+ inputTransport.Close();
+ if (outputTransport != null)
+ outputTransport.Close();
+
+ // disposable stuff should be disposed
+ if (inputProtocol != null)
+ inputProtocol.Dispose();
+ if (outputProtocol != null)
+ outputProtocol.Dispose();
+ if (inputTransport != null)
+ inputTransport.Dispose();
+ if (outputTransport != null)
+ outputTransport.Dispose();
+ }
+ }
+ }
+
+ public override void Stop()
+ {
+ stop = true;
+ serverTransport.Close();
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Server/TThreadedServer.cs b/src/jaegertracing/thrift/lib/csharp/src/Server/TThreadedServer.cs
new file mode 100644
index 000000000..cc051a33b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Server/TThreadedServer.cs
@@ -0,0 +1,282 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using Thrift.Collections;
+using Thrift.Protocol;
+using Thrift.Transport;
+
+namespace Thrift.Server
+{
+ /// <summary>
+ /// Server that uses C# threads (as opposed to the ThreadPool) when handling requests.
+ /// </summary>
+ public class TThreadedServer : TServer
+ {
+ private const int DEFAULT_MAX_THREADS = 100;
+ private volatile bool stop = false;
+ private readonly int maxThreads;
+
+ private Queue<TTransport> clientQueue;
+ private THashSet<Thread> clientThreads;
+ private object clientLock;
+ private Thread workerThread;
+
+ public int ClientThreadsCount
+ {
+ get { return clientThreads.Count; }
+ }
+
+ public TThreadedServer(TProcessor processor, TServerTransport serverTransport)
+ : this(new TSingletonProcessorFactory(processor), serverTransport,
+ new TTransportFactory(), new TTransportFactory(),
+ new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(),
+ DEFAULT_MAX_THREADS, DefaultLogDelegate)
+ {
+ }
+
+ public TThreadedServer(TProcessor processor, TServerTransport serverTransport, LogDelegate logDelegate)
+ : this(new TSingletonProcessorFactory(processor), serverTransport,
+ new TTransportFactory(), new TTransportFactory(),
+ new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(),
+ DEFAULT_MAX_THREADS, logDelegate)
+ {
+ }
+
+
+ public TThreadedServer(TProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory)
+ : this(new TSingletonProcessorFactory(processor), serverTransport,
+ transportFactory, transportFactory,
+ protocolFactory, protocolFactory,
+ DEFAULT_MAX_THREADS, DefaultLogDelegate)
+ {
+ }
+
+ public TThreadedServer(TProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory)
+ : this(processorFactory, serverTransport,
+ transportFactory, transportFactory,
+ protocolFactory, protocolFactory,
+ DEFAULT_MAX_THREADS, DefaultLogDelegate)
+ {
+ }
+ public TThreadedServer(TProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ int maxThreads, LogDelegate logDel)
+ : base(processorFactory, serverTransport, inputTransportFactory, outputTransportFactory,
+ inputProtocolFactory, outputProtocolFactory, logDel)
+ {
+ this.maxThreads = maxThreads;
+ clientQueue = new Queue<TTransport>();
+ clientLock = new object();
+ clientThreads = new THashSet<Thread>();
+ }
+
+ /// <summary>
+ /// Use new Thread for each new client connection. block until numConnections &lt; maxThreads.
+ /// </summary>
+ public override void Serve()
+ {
+ try
+ {
+ //start worker thread
+ workerThread = new Thread(new ThreadStart(Execute));
+ workerThread.Start();
+ serverTransport.Listen();
+ }
+ catch (TTransportException ttx)
+ {
+ logDelegate("Error, could not listen on ServerTransport: " + ttx);
+ return;
+ }
+
+ //Fire the preServe server event when server is up but before any client connections
+ if (serverEventHandler != null)
+ serverEventHandler.preServe();
+
+ while (!stop)
+ {
+ int failureCount = 0;
+ try
+ {
+ TTransport client = serverTransport.Accept();
+ lock (clientLock)
+ {
+ clientQueue.Enqueue(client);
+ Monitor.Pulse(clientLock);
+ }
+ }
+ catch (TTransportException ttx)
+ {
+ if (!stop || ttx.Type != TTransportException.ExceptionType.Interrupted)
+ {
+ ++failureCount;
+ logDelegate(ttx.ToString());
+ }
+
+ }
+ }
+
+ if (stop)
+ {
+ try
+ {
+ serverTransport.Close();
+ }
+ catch (TTransportException ttx)
+ {
+ logDelegate("TServeTransport failed on close: " + ttx.Message);
+ }
+ stop = false;
+ }
+ }
+
+ /// <summary>
+ /// Loops on processing a client forever
+ /// </summary>
+ private void Execute()
+ {
+ while (!stop)
+ {
+ TTransport client;
+ Thread t;
+ lock (clientLock)
+ {
+ //don't dequeue if too many connections
+ while (clientThreads.Count >= maxThreads)
+ {
+ Monitor.Wait(clientLock);
+ }
+
+ while (clientQueue.Count == 0)
+ {
+ Monitor.Wait(clientLock);
+ }
+
+ client = clientQueue.Dequeue();
+ t = new Thread(new ParameterizedThreadStart(ClientWorker));
+ clientThreads.Add(t);
+ }
+ //start processing requests from client on new thread
+ t.Start(client);
+ }
+ }
+
+ private void ClientWorker(object context)
+ {
+ using (TTransport client = (TTransport)context)
+ {
+ TProcessor processor = processorFactory.GetProcessor(client);
+ TTransport inputTransport = null;
+ TTransport outputTransport = null;
+ TProtocol inputProtocol = null;
+ TProtocol outputProtocol = null;
+ object connectionContext = null;
+ try
+ {
+ try
+ {
+ inputTransport = inputTransportFactory.GetTransport(client);
+ outputTransport = outputTransportFactory.GetTransport(client);
+ inputProtocol = inputProtocolFactory.GetProtocol(inputTransport);
+ outputProtocol = outputProtocolFactory.GetProtocol(outputTransport);
+
+ //Recover event handler (if any) and fire createContext server event when a client connects
+ if (serverEventHandler != null)
+ connectionContext = serverEventHandler.createContext(inputProtocol, outputProtocol);
+
+ //Process client requests until client disconnects
+ while (!stop)
+ {
+ if (!inputTransport.Peek())
+ break;
+
+ //Fire processContext server event
+ //N.B. This is the pattern implemented in C++ and the event fires provisionally.
+ //That is to say it may be many minutes between the event firing and the client request
+ //actually arriving or the client may hang up without ever makeing a request.
+ if (serverEventHandler != null)
+ serverEventHandler.processContext(connectionContext, inputTransport);
+ //Process client request (blocks until transport is readable)
+ if (!processor.Process(inputProtocol, outputProtocol))
+ break;
+ }
+ }
+ catch (TTransportException)
+ {
+ //Usually a client disconnect, expected
+ }
+ catch (Exception x)
+ {
+ //Unexpected
+ logDelegate("Error: " + x);
+ }
+
+ //Fire deleteContext server event after client disconnects
+ if (serverEventHandler != null)
+ serverEventHandler.deleteContext(connectionContext, inputProtocol, outputProtocol);
+
+ lock (clientLock)
+ {
+ clientThreads.Remove(Thread.CurrentThread);
+ Monitor.Pulse(clientLock);
+ }
+
+ }
+ finally
+ {
+ //Close transports
+ if (inputTransport != null)
+ inputTransport.Close();
+ if (outputTransport != null)
+ outputTransport.Close();
+
+ // disposable stuff should be disposed
+ if (inputProtocol != null)
+ inputProtocol.Dispose();
+ if (outputProtocol != null)
+ outputProtocol.Dispose();
+ }
+ }
+ }
+
+ public override void Stop()
+ {
+ stop = true;
+ serverTransport.Close();
+ //clean up all the threads myself
+ workerThread.Abort();
+ foreach (Thread t in clientThreads)
+ {
+ t.Abort();
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/TApplicationException.cs b/src/jaegertracing/thrift/lib/csharp/src/TApplicationException.cs
new file mode 100644
index 000000000..8dd7ae578
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/TApplicationException.cs
@@ -0,0 +1,146 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using Thrift.Protocol;
+
+namespace Thrift
+{
+ public class TApplicationException : TException
+ {
+ protected ExceptionType type;
+
+ public TApplicationException()
+ {
+ }
+
+ public TApplicationException(ExceptionType type)
+ {
+ this.type = type;
+ }
+
+ public TApplicationException(ExceptionType type, string message)
+ : base(message, null) // TApplicationException is serializable, but we never serialize InnerException
+ {
+ this.type = type;
+ }
+
+ public static TApplicationException Read(TProtocol iprot)
+ {
+ TField field;
+
+ string message = null;
+ ExceptionType type = ExceptionType.Unknown;
+
+ iprot.ReadStructBegin();
+ while (true)
+ {
+ field = iprot.ReadFieldBegin();
+ if (field.Type == TType.Stop)
+ {
+ break;
+ }
+
+ switch (field.ID)
+ {
+ case 1:
+ if (field.Type == TType.String)
+ {
+ message = iprot.ReadString();
+ }
+ else
+ {
+ TProtocolUtil.Skip(iprot, field.Type);
+ }
+ break;
+ case 2:
+ if (field.Type == TType.I32)
+ {
+ type = (ExceptionType)iprot.ReadI32();
+ }
+ else
+ {
+ TProtocolUtil.Skip(iprot, field.Type);
+ }
+ break;
+ default:
+ TProtocolUtil.Skip(iprot, field.Type);
+ break;
+ }
+
+ iprot.ReadFieldEnd();
+ }
+
+ iprot.ReadStructEnd();
+
+ return new TApplicationException(type, message);
+ }
+
+ public void Write(TProtocol oprot)
+ {
+ TStruct struc = new TStruct("TApplicationException");
+ TField field = new TField();
+
+ oprot.WriteStructBegin(struc);
+
+ if (!string.IsNullOrEmpty(Message))
+ {
+ field.Name = "message";
+ field.Type = TType.String;
+ field.ID = 1;
+ oprot.WriteFieldBegin(field);
+ oprot.WriteString(Message);
+ oprot.WriteFieldEnd();
+ }
+
+ field.Name = "type";
+ field.Type = TType.I32;
+ field.ID = 2;
+ oprot.WriteFieldBegin(field);
+ oprot.WriteI32((int)type);
+ oprot.WriteFieldEnd();
+ oprot.WriteFieldStop();
+ oprot.WriteStructEnd();
+ }
+
+ public enum ExceptionType
+ {
+ Unknown,
+ UnknownMethod,
+ InvalidMessageType,
+ WrongMethodName,
+ BadSequenceID,
+ MissingResult,
+ InternalError,
+ ProtocolError,
+ InvalidTransform,
+ InvalidProtocol,
+ UnsupportedClientType
+ }
+
+ public ExceptionType Type
+ {
+ get { return type; }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/TAsyncProcessor.cs b/src/jaegertracing/thrift/lib/csharp/src/TAsyncProcessor.cs
new file mode 100644
index 000000000..ab432255b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/TAsyncProcessor.cs
@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Threading.Tasks;
+using Thrift.Protocol;
+
+namespace Thrift
+{
+ /// <summary>
+ /// Processes a message asynchronously.
+ /// </summary>
+ public interface TAsyncProcessor
+ {
+ /// <summary>
+ /// Processes the next part of the message.
+ /// </summary>
+ /// <param name="iprot">The input protocol.</param>
+ /// <param name="oprot">The output protocol.</param>
+ /// <returns>true if there's more to process, false otherwise.</returns>
+ Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/TControllingHandler.cs b/src/jaegertracing/thrift/lib/csharp/src/TControllingHandler.cs
new file mode 100644
index 000000000..7b5203a5f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/TControllingHandler.cs
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using Thrift.Server;
+
+namespace Thrift
+{
+ public interface TControllingHandler
+ {
+ TServer server { get; set; }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/TException.cs b/src/jaegertracing/thrift/lib/csharp/src/TException.cs
new file mode 100644
index 000000000..b9fae6e9f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/TException.cs
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+
+namespace Thrift
+{
+ public class TException : Exception
+ {
+ public TException()
+ {
+ }
+
+ public TException(string message, Exception inner = null)
+ : base(message, inner)
+ {
+ }
+
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/TProcessor.cs b/src/jaegertracing/thrift/lib/csharp/src/TProcessor.cs
new file mode 100644
index 000000000..71ce75508
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/TProcessor.cs
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using Thrift.Protocol;
+
+namespace Thrift
+{
+ public interface TProcessor
+ {
+ bool Process(TProtocol iprot, TProtocol oprot);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/TProcessorFactory.cs b/src/jaegertracing/thrift/lib/csharp/src/TProcessorFactory.cs
new file mode 100644
index 000000000..fdf631bc7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/TProcessorFactory.cs
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using Thrift.Server;
+using Thrift.Transport;
+
+namespace Thrift
+{
+ public interface TProcessorFactory
+ {
+ TProcessor GetProcessor(TTransport trans, TServer server = null);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/TPrototypeProcessorFactory.cs b/src/jaegertracing/thrift/lib/csharp/src/TPrototypeProcessorFactory.cs
new file mode 100644
index 000000000..0b47261fb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/TPrototypeProcessorFactory.cs
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Thrift.Server;
+using Thrift.Transport;
+
+namespace Thrift
+{
+ public class TPrototypeProcessorFactory<P, H> : TProcessorFactory where P : TProcessor
+ {
+ object[] handlerArgs = null;
+
+ public TPrototypeProcessorFactory()
+ {
+ handlerArgs = new object[0];
+ }
+
+ public TPrototypeProcessorFactory(params object[] handlerArgs)
+ {
+ this.handlerArgs = handlerArgs;
+ }
+
+ public TProcessor GetProcessor(TTransport trans, TServer server = null)
+ {
+ H handler = (H)Activator.CreateInstance(typeof(H), handlerArgs);
+
+ TControllingHandler handlerServerRef = handler as TControllingHandler;
+ if (handlerServerRef != null)
+ {
+ handlerServerRef.server = server;
+ }
+ return Activator.CreateInstance(typeof(P), new object[] { handler }) as TProcessor;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/TSingletonProcessorFactory.cs b/src/jaegertracing/thrift/lib/csharp/src/TSingletonProcessorFactory.cs
new file mode 100644
index 000000000..ed2897ba3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/TSingletonProcessorFactory.cs
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Thrift.Server;
+using Thrift.Transport;
+
+namespace Thrift
+{
+ public class TSingletonProcessorFactory : TProcessorFactory
+ {
+ private readonly TProcessor processor_;
+
+ public TSingletonProcessorFactory(TProcessor processor)
+ {
+ processor_ = processor;
+ }
+
+ public TProcessor GetProcessor(TTransport trans, TServer server = null)
+ {
+ return processor_;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Thrift.45.csproj b/src/jaegertracing/thrift/lib/csharp/src/Thrift.45.csproj
new file mode 100644
index 000000000..146e7f8ed
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Thrift.45.csproj
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{EBCE35DA-CF6A-42BC-A357-A9C09B534299}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Thrift</RootNamespace>
+ <AssemblyName>Thrift45</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>portable</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>TRACE;DEBUG;NET45</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>portable</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE;NET45</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup>
+ <SignAssembly>true</SignAssembly>
+ </PropertyGroup>
+ <PropertyGroup>
+ <AssemblyOriginatorKeyFile>thrift.snk</AssemblyOriginatorKeyFile>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Web" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Collections\TCollections.cs" />
+ <Compile Include="Collections\THashSet.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Protocol\TAbstractBase.cs" />
+ <Compile Include="Protocol\TBase.cs" />
+ <Compile Include="Protocol\TBase64Utils.cs" />
+ <Compile Include="Protocol\TBinaryProtocol.cs" />
+ <Compile Include="Protocol\TCompactProtocol.cs" />
+ <Compile Include="Protocol\TField.cs" />
+ <Compile Include="Protocol\TJSONProtocol.cs" />
+ <Compile Include="Protocol\TList.cs" />
+ <Compile Include="Protocol\TMap.cs" />
+ <Compile Include="Protocol\TMessage.cs" />
+ <Compile Include="Protocol\TMessageType.cs" />
+ <Compile Include="Protocol\TMultiplexedProcessor.cs" />
+ <Compile Include="Protocol\TMultiplexedProtocol.cs" />
+ <Compile Include="Protocol\TProtocol.cs" />
+ <Compile Include="Protocol\TProtocolDecorator.cs" />
+ <Compile Include="Protocol\TProtocolException.cs" />
+ <Compile Include="Protocol\TProtocolFactory.cs" />
+ <Compile Include="Protocol\TProtocolUtil.cs" />
+ <Compile Include="Protocol\TSet.cs" />
+ <Compile Include="Protocol\TStruct.cs" />
+ <Compile Include="Protocol\TType.cs" />
+ <Compile Include="Server\TServer.cs" />
+ <Compile Include="Server\TServerEventHandler.cs" />
+ <Compile Include="Server\TSimpleServer.cs" />
+ <Compile Include="Server\TThreadedServer.cs" />
+ <Compile Include="Server\TThreadPoolServer.cs" />
+ <Compile Include="TApplicationException.cs" />
+ <Compile Include="TAsyncProcessor.cs" />
+ <Compile Include="TControllingHandler.cs" />
+ <Compile Include="TException.cs" />
+ <Compile Include="TProcessor.cs" />
+ <Compile Include="TProcessorFactory.cs" />
+ <Compile Include="TPrototypeProcessorFactory.cs" />
+ <Compile Include="Transport\TBufferedTransport.cs" />
+ <Compile Include="Transport\TFramedTransport.cs" />
+ <Compile Include="Transport\THttpClient.cs" />
+ <Compile Include="Transport\THttpHandler.cs" />
+ <Compile Include="Transport\THttpTaskAsyncHandler.cs" />
+ <Compile Include="Transport\TMemoryBuffer.cs" />
+ <Compile Include="Transport\TNamedPipeClientTransport.cs" />
+ <Compile Include="Transport\TNamedPipeServerTransport.cs" />
+ <Compile Include="Transport\TServerSocket.cs" />
+ <Compile Include="Transport\TServerTransport.cs" />
+ <Compile Include="Transport\TSilverlightSocket.cs" />
+ <Compile Include="Transport\TSocket.cs" />
+ <Compile Include="Transport\TSocketVersionizer.cs" />
+ <Compile Include="Transport\TStreamTransport.cs" />
+ <Compile Include="Transport\TTLSServerSocket.cs" />
+ <Compile Include="Transport\TTLSSocket.cs" />
+ <Compile Include="Transport\TTransport.cs" />
+ <Compile Include="Transport\TTransportException.cs" />
+ <Compile Include="Transport\TTransportFactory.cs" />
+ <Compile Include="TSingletonProcessorFactory.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Folder Include="Server\Collections\" />
+ <Folder Include="Server\Protocol\" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="thrift.snk" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Thrift.csproj b/src/jaegertracing/thrift/lib/csharp/src/Thrift.csproj
new file mode 100644
index 000000000..da69554d1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Thrift.csproj
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{499EB63C-D74C-47E8-AE48-A2FC94538E9D}</ProjectGuid>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <OutputType>Library</OutputType>
+ <NoStandardLibraries>false</NoStandardLibraries>
+ <AssemblyName>Thrift</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <RootNamespace>Thrift</RootNamespace>
+ <FileUpgradeFlags>
+ </FileUpgradeFlags>
+ <OldToolsVersion>3.5</OldToolsVersion>
+ <UpgradeBackupLocation />
+ <PublishUrl>publish\</PublishUrl>
+ <Install>true</Install>
+ <InstallFrom>Disk</InstallFrom>
+ <UpdateEnabled>false</UpdateEnabled>
+ <UpdateMode>Foreground</UpdateMode>
+ <UpdateInterval>7</UpdateInterval>
+ <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+ <UpdatePeriodically>false</UpdatePeriodically>
+ <UpdateRequired>false</UpdateRequired>
+ <MapFileExtensions>true</MapFileExtensions>
+ <ApplicationRevision>0</ApplicationRevision>
+ <ApplicationVersion>0.13.0.0</ApplicationVersion>
+ <IsWebBootstrapper>false</IsWebBootstrapper>
+ <UseApplicationTrust>false</UseApplicationTrust>
+ <BootstrapperEnabled>true</BootstrapperEnabled>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>portable</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>TRACE;DEBUG</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>portable</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>
+ </DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <PropertyGroup>
+ <SignAssembly>true</SignAssembly>
+ </PropertyGroup>
+ <PropertyGroup>
+ <AssemblyOriginatorKeyFile>thrift.snk</AssemblyOriginatorKeyFile>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Web" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Collections\TCollections.cs" />
+ <Compile Include="Collections\THashSet.cs" />
+ <Compile Include="Net35\ExtensionsNet35.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Protocol\TAbstractBase.cs" />
+ <Compile Include="Protocol\TBase.cs" />
+ <Compile Include="Protocol\TBase64Utils.cs" />
+ <Compile Include="Protocol\TBinaryProtocol.cs" />
+ <Compile Include="Protocol\TCompactProtocol.cs" />
+ <Compile Include="Protocol\TField.cs" />
+ <Compile Include="Protocol\TJSONProtocol.cs" />
+ <Compile Include="Protocol\TList.cs" />
+ <Compile Include="Protocol\TMap.cs" />
+ <Compile Include="Protocol\TMessage.cs" />
+ <Compile Include="Protocol\TMessageType.cs" />
+ <Compile Include="Protocol\TMultiplexedProcessor.cs" />
+ <Compile Include="Protocol\TMultiplexedProtocol.cs" />
+ <Compile Include="Protocol\TProtocol.cs" />
+ <Compile Include="Protocol\TProtocolDecorator.cs" />
+ <Compile Include="Protocol\TProtocolException.cs" />
+ <Compile Include="Protocol\TProtocolFactory.cs" />
+ <Compile Include="Protocol\TProtocolUtil.cs" />
+ <Compile Include="Protocol\TSet.cs" />
+ <Compile Include="Protocol\TStruct.cs" />
+ <Compile Include="Protocol\TType.cs" />
+ <Compile Include="Server\TServer.cs" />
+ <Compile Include="Server\TServerEventHandler.cs" />
+ <Compile Include="Server\TSimpleServer.cs" />
+ <Compile Include="Server\TThreadedServer.cs" />
+ <Compile Include="Server\TThreadPoolServer.cs" />
+ <Compile Include="TApplicationException.cs" />
+ <Compile Include="TControllingHandler.cs" />
+ <Compile Include="TException.cs" />
+ <Compile Include="TProcessor.cs" />
+ <Compile Include="TProcessorFactory.cs" />
+ <Compile Include="TPrototypeProcessorFactory.cs" />
+ <Compile Include="Transport\TBufferedTransport.cs" />
+ <Compile Include="Transport\TFramedTransport.cs" />
+ <Compile Include="Transport\THttpClient.cs" />
+ <Compile Include="Transport\THttpHandler.cs" />
+ <Compile Include="Transport\TMemoryBuffer.cs" />
+ <Compile Include="Transport\TNamedPipeClientTransport.cs" />
+ <Compile Include="Transport\TNamedPipeServerTransport.cs" />
+ <Compile Include="Transport\TServerSocket.cs" />
+ <Compile Include="Transport\TServerTransport.cs" />
+ <Compile Include="Transport\TSocket.cs" />
+ <Compile Include="Transport\TSocketVersionizer.cs" />
+ <Compile Include="Transport\TStreamTransport.cs" />
+ <Compile Include="Transport\TTLSServerSocket.cs" />
+ <Compile Include="Transport\TTLSSocket.cs" />
+ <Compile Include="Transport\TTransport.cs" />
+ <Compile Include="Transport\TTransportException.cs" />
+ <Compile Include="Transport\TTransportFactory.cs" />
+ <Compile Include="TSingletonProcessorFactory.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
+ <Visible>False</Visible>
+ <ProductName>Windows Installer 3.1</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="thrift.snk" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" />
+ <ProjectExtensions>
+ <VisualStudio AllowExistingFolder="true" />
+ </ProjectExtensions>
+</Project> \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Thrift.sln b/src/jaegertracing/thrift/lib/csharp/src/Thrift.sln
new file mode 100644
index 000000000..a29e46882
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Thrift.sln
@@ -0,0 +1,47 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Thrift", "Thrift.csproj", "{499EB63C-D74C-47E8-AE48-A2FC94538E9D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThriftTest", "..\test\ThriftTest\ThriftTest.csproj", "{48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThriftMSBuildTask", "..\ThriftMSBuildTask\ThriftMSBuildTask.csproj", "{EC0A0231-66EA-4593-A792-C6CA3BB8668E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Thrift.45", "Thrift.45.csproj", "{EBCE35DA-CF6A-42BC-A357-A9C09B534299}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThriftMVCTest", "..\test\ThriftMVCTest\ThriftMVCTest.csproj", "{891B4487-C7BA-427E-BBC8-4C596C229A10}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {499EB63C-D74C-47E8-AE48-A2FC94538E9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {499EB63C-D74C-47E8-AE48-A2FC94538E9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {499EB63C-D74C-47E8-AE48-A2FC94538E9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {499EB63C-D74C-47E8-AE48-A2FC94538E9D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EC0A0231-66EA-4593-A792-C6CA3BB8668E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EC0A0231-66EA-4593-A792-C6CA3BB8668E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EC0A0231-66EA-4593-A792-C6CA3BB8668E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EC0A0231-66EA-4593-A792-C6CA3BB8668E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EBCE35DA-CF6A-42BC-A357-A9C09B534299}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EBCE35DA-CF6A-42BC-A357-A9C09B534299}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EBCE35DA-CF6A-42BC-A357-A9C09B534299}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EBCE35DA-CF6A-42BC-A357-A9C09B534299}.Release|Any CPU.Build.0 = Release|Any CPU
+ {891B4487-C7BA-427E-BBC8-4C596C229A10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {891B4487-C7BA-427E-BBC8-4C596C229A10}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {891B4487-C7BA-427E-BBC8-4C596C229A10}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {891B4487-C7BA-427E-BBC8-4C596C229A10}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(MonoDevelopProperties) = preSolution
+ StartupItem = Thrift.csproj
+ EndGlobalSection
+EndGlobal
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/TBufferedTransport.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/TBufferedTransport.cs
new file mode 100644
index 000000000..887098810
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/TBufferedTransport.cs
@@ -0,0 +1,194 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using System.IO;
+
+namespace Thrift.Transport
+{
+ public class TBufferedTransport : TTransport, IDisposable
+ {
+ private readonly int bufSize;
+ private readonly MemoryStream inputBuffer = new MemoryStream(0);
+ private readonly MemoryStream outputBuffer = new MemoryStream(0);
+ private readonly TTransport transport;
+
+ public TBufferedTransport(TTransport transport, int bufSize = 1024)
+ {
+ if (transport == null)
+ throw new ArgumentNullException("transport");
+ if (bufSize <= 0)
+ throw new ArgumentException("bufSize", "Buffer size must be a positive number.");
+ this.transport = transport;
+ this.bufSize = bufSize;
+ }
+
+ public TTransport UnderlyingTransport
+ {
+ get
+ {
+ CheckNotDisposed();
+ return transport;
+ }
+ }
+
+ public override bool IsOpen
+ {
+ get
+ {
+ // We can legitimately throw here but be nice a bit.
+ // CheckNotDisposed();
+ return !_IsDisposed && transport.IsOpen;
+ }
+ }
+
+ public override void Open()
+ {
+ CheckNotDisposed();
+ transport.Open();
+ }
+
+ public override void Close()
+ {
+ CheckNotDisposed();
+ transport.Close();
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ CheckNotDisposed();
+ ValidateBufferArgs(buf, off, len);
+ if (!IsOpen)
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+
+ if (inputBuffer.Capacity < bufSize)
+ inputBuffer.Capacity = bufSize;
+
+ while (true)
+ {
+ int got = inputBuffer.Read(buf, off, len);
+ if (got > 0)
+ return got;
+
+ inputBuffer.Seek(0, SeekOrigin.Begin);
+ inputBuffer.SetLength(inputBuffer.Capacity);
+ int filled = transport.Read(inputBuffer.GetBuffer(), 0, (int)inputBuffer.Length);
+ inputBuffer.SetLength(filled);
+ if (filled == 0)
+ return 0;
+ }
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ CheckNotDisposed();
+ ValidateBufferArgs(buf, off, len);
+ if (!IsOpen)
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ // Relative offset from "off" argument
+ int offset = 0;
+ if (outputBuffer.Length > 0)
+ {
+ int capa = (int)(outputBuffer.Capacity - outputBuffer.Length);
+ int writeSize = capa <= len ? capa : len;
+ outputBuffer.Write(buf, off, writeSize);
+ offset += writeSize;
+ if (writeSize == capa)
+ {
+ transport.Write(outputBuffer.GetBuffer(), 0, (int)outputBuffer.Length);
+ outputBuffer.SetLength(0);
+ }
+ }
+ while (len - offset >= bufSize)
+ {
+ transport.Write(buf, off + offset, bufSize);
+ offset += bufSize;
+ }
+ int remain = len - offset;
+ if (remain > 0)
+ {
+ if (outputBuffer.Capacity < bufSize)
+ outputBuffer.Capacity = bufSize;
+ outputBuffer.Write(buf, off + offset, remain);
+ }
+ }
+
+ private void InternalFlush()
+ {
+ if (!IsOpen)
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ if (outputBuffer.Length > 0)
+ {
+ transport.Write(outputBuffer.GetBuffer(), 0, (int)outputBuffer.Length);
+ outputBuffer.SetLength(0);
+ }
+ }
+
+ public override void Flush()
+ {
+ CheckNotDisposed();
+ InternalFlush();
+
+ transport.Flush();
+ }
+
+ public override IAsyncResult BeginFlush(AsyncCallback callback, object state)
+ {
+ CheckNotDisposed();
+ InternalFlush();
+
+ return transport.BeginFlush( callback, state);
+ }
+
+ public override void EndFlush(IAsyncResult asyncResult)
+ {
+ transport.EndFlush( asyncResult);
+ }
+
+
+
+ protected void CheckNotDisposed()
+ {
+ if (_IsDisposed)
+ throw new ObjectDisposedException("TBufferedTransport");
+ }
+
+ #region " IDisposable Support "
+ protected bool _IsDisposed { get; private set; }
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_IsDisposed)
+ {
+ if (disposing)
+ {
+ if (inputBuffer != null)
+ inputBuffer.Dispose();
+ if (outputBuffer != null)
+ outputBuffer.Dispose();
+ if (transport != null)
+ transport.Dispose();
+ }
+ }
+ _IsDisposed = true;
+ }
+ #endregion
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/TFramedTransport.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/TFramedTransport.cs
new file mode 100644
index 000000000..a746a3223
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/TFramedTransport.cs
@@ -0,0 +1,205 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+using System;
+using System.IO;
+
+namespace Thrift.Transport
+{
+ public class TFramedTransport : TTransport, IDisposable
+ {
+ private readonly TTransport transport;
+ private readonly MemoryStream writeBuffer = new MemoryStream(1024);
+ private readonly MemoryStream readBuffer = new MemoryStream(1024);
+
+ private const int HeaderSize = 4;
+ private readonly byte[] headerBuf = new byte[HeaderSize];
+
+ public class Factory : TTransportFactory
+ {
+ public override TTransport GetTransport(TTransport trans)
+ {
+ return new TFramedTransport(trans);
+ }
+ }
+
+ public TFramedTransport(TTransport transport)
+ {
+ if (transport == null)
+ throw new ArgumentNullException("transport");
+ this.transport = transport;
+ InitWriteBuffer();
+ }
+
+ public override void Open()
+ {
+ CheckNotDisposed();
+ transport.Open();
+ }
+
+ public override bool IsOpen
+ {
+ get
+ {
+ // We can legitimately throw here but be nice a bit.
+ // CheckNotDisposed();
+ return !_IsDisposed && transport.IsOpen;
+ }
+ }
+
+ public override void Close()
+ {
+ CheckNotDisposed();
+ transport.Close();
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ CheckNotDisposed();
+ ValidateBufferArgs(buf, off, len);
+ if (!IsOpen)
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ int got = readBuffer.Read(buf, off, len);
+ if (got > 0)
+ {
+ return got;
+ }
+
+ // Read another frame of data
+ ReadFrame();
+
+ return readBuffer.Read(buf, off, len);
+ }
+
+ private void ReadFrame()
+ {
+ transport.ReadAll(headerBuf, 0, HeaderSize);
+ int size = DecodeFrameSize(headerBuf);
+
+ readBuffer.SetLength(size);
+ readBuffer.Seek(0, SeekOrigin.Begin);
+ byte[] buff = readBuffer.GetBuffer();
+ transport.ReadAll(buff, 0, size);
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ CheckNotDisposed();
+ ValidateBufferArgs(buf, off, len);
+ if (!IsOpen)
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ if (writeBuffer.Length + (long)len > (long)int.MaxValue)
+ Flush();
+ writeBuffer.Write(buf, off, len);
+ }
+
+ private void InternalFlush()
+ {
+ CheckNotDisposed();
+ if (!IsOpen)
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ byte[] buf = writeBuffer.GetBuffer();
+ int len = (int)writeBuffer.Length;
+ int data_len = len - HeaderSize;
+ if (data_len < 0)
+ throw new System.InvalidOperationException(); // logic error actually
+
+ // Inject message header into the reserved buffer space
+ EncodeFrameSize(data_len, buf);
+
+ // Send the entire message at once
+ transport.Write(buf, 0, len);
+
+ InitWriteBuffer();
+ }
+
+ public override void Flush()
+ {
+ CheckNotDisposed();
+ InternalFlush();
+
+ transport.Flush();
+ }
+
+ public override IAsyncResult BeginFlush(AsyncCallback callback, object state)
+ {
+ CheckNotDisposed();
+ InternalFlush();
+
+ return transport.BeginFlush( callback, state);
+ }
+
+ public override void EndFlush(IAsyncResult asyncResult)
+ {
+ transport.EndFlush( asyncResult);
+ }
+
+ private void InitWriteBuffer()
+ {
+ // Reserve space for message header to be put right before sending it out
+ writeBuffer.SetLength(HeaderSize);
+ writeBuffer.Seek(0, SeekOrigin.End);
+ }
+
+ private static void EncodeFrameSize(int frameSize, byte[] buf)
+ {
+ buf[0] = (byte)(0xff & (frameSize >> 24));
+ buf[1] = (byte)(0xff & (frameSize >> 16));
+ buf[2] = (byte)(0xff & (frameSize >> 8));
+ buf[3] = (byte)(0xff & (frameSize));
+ }
+
+ private static int DecodeFrameSize(byte[] buf)
+ {
+ return
+ ((buf[0] & 0xff) << 24) |
+ ((buf[1] & 0xff) << 16) |
+ ((buf[2] & 0xff) << 8) |
+ ((buf[3] & 0xff));
+ }
+
+
+ private void CheckNotDisposed()
+ {
+ if (_IsDisposed)
+ throw new ObjectDisposedException("TFramedTransport");
+ }
+
+ #region " IDisposable Support "
+ private bool _IsDisposed;
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_IsDisposed)
+ {
+ if (disposing)
+ {
+ if (readBuffer != null)
+ readBuffer.Dispose();
+ if (writeBuffer != null)
+ writeBuffer.Dispose();
+ if (transport != null)
+ transport.Dispose();
+ }
+ }
+ _IsDisposed = true;
+ }
+ #endregion
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/THttpClient.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/THttpClient.cs
new file mode 100644
index 000000000..986799cd2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/THttpClient.cs
@@ -0,0 +1,486 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Threading;
+using System.Linq;
+using System.Security.Cryptography.X509Certificates;
+using System.IO.Compression;
+
+namespace Thrift.Transport
+{
+ public class THttpClient : TTransport, IDisposable
+ {
+ private readonly Uri uri;
+ private readonly X509Certificate[] certificates;
+ private Stream inputStream;
+ private MemoryStream outputStream = new MemoryStream();
+
+ // Timeouts in milliseconds
+ private int connectTimeout = 30000;
+
+ private int readTimeout = 30000;
+
+ private IDictionary<string, string> customHeaders = new Dictionary<string, string>();
+ private string userAgent = "C#/THttpClient";
+
+#if !SILVERLIGHT
+ private IWebProxy proxy = WebRequest.DefaultWebProxy;
+#endif
+
+ public THttpClient(Uri u)
+ : this(u, Enumerable.Empty<X509Certificate>())
+ {
+ }
+ public THttpClient(Uri u, string userAgent)
+ : this(u, userAgent, Enumerable.Empty<X509Certificate>())
+ {
+ }
+
+ public THttpClient(Uri u, IEnumerable<X509Certificate> certificates)
+ {
+ uri = u;
+ this.certificates = (certificates ?? Enumerable.Empty<X509Certificate>()).ToArray();
+ }
+ public THttpClient(Uri u, string userAgent, IEnumerable<X509Certificate> certificates)
+ {
+ uri = u;
+ this.userAgent = userAgent;
+ this.certificates = (certificates ?? Enumerable.Empty<X509Certificate>()).ToArray();
+ }
+
+ public int ConnectTimeout
+ {
+ set
+ {
+ connectTimeout = value;
+ }
+ }
+
+ public int ReadTimeout
+ {
+ set
+ {
+ readTimeout = value;
+ }
+ }
+
+ public IDictionary<string, string> CustomHeaders
+ {
+ get
+ {
+ return customHeaders;
+ }
+ }
+
+#if !SILVERLIGHT
+ public IWebProxy Proxy
+ {
+ set
+ {
+ proxy = value;
+ }
+ }
+#endif
+
+ public override bool IsOpen
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public override void Open()
+ {
+ }
+
+ public override void Close()
+ {
+ if (inputStream != null)
+ {
+ inputStream.Close();
+ inputStream = null;
+ }
+ if (outputStream != null)
+ {
+ outputStream.Close();
+ outputStream = null;
+ }
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ if (inputStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No request has been sent");
+ }
+
+ try
+ {
+ int ret = inputStream.Read(buf, off, len);
+
+ if (ret == -1)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.EndOfFile, "No more data available");
+ }
+
+ return ret;
+ }
+ catch (IOException iox)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString(), iox);
+ }
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ outputStream.Write(buf, off, len);
+ }
+
+#if !SILVERLIGHT
+ public override void Flush()
+ {
+ try
+ {
+ SendRequest();
+ }
+ finally
+ {
+ outputStream = new MemoryStream();
+ }
+ }
+
+ private void SendRequest()
+ {
+ try
+ {
+ HttpWebRequest connection = CreateRequest();
+ connection.Headers.Add("Accept-Encoding", "gzip, deflate");
+
+ byte[] data = outputStream.ToArray();
+ connection.ContentLength = data.Length;
+
+ using (Stream requestStream = connection.GetRequestStream())
+ {
+ requestStream.Write(data, 0, data.Length);
+
+ // Resolve HTTP hang that can happens after successive calls by making sure
+ // that we release the response and response stream. To support this, we copy
+ // the response to a memory stream.
+ using (var response = connection.GetResponse())
+ {
+ using (var responseStream = response.GetResponseStream())
+ {
+ // Copy the response to a memory stream so that we can
+ // cleanly close the response and response stream.
+ inputStream = new MemoryStream();
+ byte[] buffer = new byte[8192]; // multiple of 4096
+ int bytesRead;
+ while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
+ {
+ inputStream.Write(buffer, 0, bytesRead);
+ }
+ inputStream.Seek(0, 0);
+ }
+
+ var encodings = response.Headers.GetValues("Content-Encoding");
+ if (encodings != null)
+ {
+ foreach (var encoding in encodings)
+ {
+ switch (encoding)
+ {
+ case "gzip":
+ DecompressGZipped(ref inputStream);
+ break;
+ case "deflate":
+ DecompressDeflated(ref inputStream);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (IOException iox)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString(), iox);
+ }
+ catch (WebException wx)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, "Couldn't connect to server: " + wx, wx);
+ }
+ }
+
+ private void DecompressDeflated(ref Stream inputStream)
+ {
+ var tmp = new MemoryStream();
+ using (var decomp = new DeflateStream(inputStream, CompressionMode.Decompress))
+ {
+ decomp.CopyTo(tmp);
+ }
+ inputStream.Dispose();
+ inputStream = tmp;
+ inputStream.Seek(0, 0);
+ }
+
+ private void DecompressGZipped(ref Stream inputStream)
+ {
+ var tmp = new MemoryStream();
+ using (var decomp = new GZipStream(inputStream, CompressionMode.Decompress))
+ {
+ decomp.CopyTo(tmp);
+ }
+ inputStream.Dispose();
+ inputStream = tmp;
+ inputStream.Seek(0, 0);
+ }
+#endif
+ private HttpWebRequest CreateRequest()
+ {
+ HttpWebRequest connection = (HttpWebRequest)WebRequest.Create(uri);
+
+
+#if !SILVERLIGHT
+ // Adding certificates through code is not supported with WP7 Silverlight
+ // see "Windows Phone 7 and Certificates_FINAL_121610.pdf"
+ connection.ClientCertificates.AddRange(certificates);
+
+ if (connectTimeout > 0)
+ {
+ connection.Timeout = connectTimeout;
+ }
+ if (readTimeout > 0)
+ {
+ connection.ReadWriteTimeout = readTimeout;
+ }
+#endif
+ // Make the request
+ connection.ContentType = "application/x-thrift";
+ connection.Accept = "application/x-thrift";
+ connection.UserAgent = userAgent;
+ connection.Method = "POST";
+#if !SILVERLIGHT
+ connection.ProtocolVersion = HttpVersion.Version10;
+#endif
+
+ //add custom headers here
+ foreach (KeyValuePair<string, string> item in customHeaders)
+ {
+#if !SILVERLIGHT
+ connection.Headers.Add(item.Key, item.Value);
+#else
+ connection.Headers[item.Key] = item.Value;
+#endif
+ }
+
+#if !SILVERLIGHT
+ connection.Proxy = proxy;
+#endif
+
+ return connection;
+ }
+
+ public override IAsyncResult BeginFlush(AsyncCallback callback, object state)
+ {
+ // Extract request and reset buffer
+ var data = outputStream.ToArray();
+
+ //requestBuffer_ = new MemoryStream();
+
+ try
+ {
+ // Create connection object
+ var flushAsyncResult = new FlushAsyncResult(callback, state);
+ flushAsyncResult.Connection = CreateRequest();
+
+ flushAsyncResult.Data = data;
+
+
+ flushAsyncResult.Connection.BeginGetRequestStream(GetRequestStreamCallback, flushAsyncResult);
+ return flushAsyncResult;
+
+ }
+ catch (IOException iox)
+ {
+ throw new TTransportException(iox.ToString(), iox);
+ }
+ }
+
+ public override void EndFlush(IAsyncResult asyncResult)
+ {
+ try
+ {
+ var flushAsyncResult = (FlushAsyncResult)asyncResult;
+
+ if (!flushAsyncResult.IsCompleted)
+ {
+ var waitHandle = flushAsyncResult.AsyncWaitHandle;
+ waitHandle.WaitOne(); // blocking INFINITEly
+ waitHandle.Close();
+ }
+
+ if (flushAsyncResult.AsyncException != null)
+ {
+ throw flushAsyncResult.AsyncException;
+ }
+ }
+ finally
+ {
+ outputStream = new MemoryStream();
+ }
+
+ }
+
+ private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
+ {
+ var flushAsyncResult = (FlushAsyncResult)asynchronousResult.AsyncState;
+ try
+ {
+ var reqStream = flushAsyncResult.Connection.EndGetRequestStream(asynchronousResult);
+ reqStream.Write(flushAsyncResult.Data, 0, flushAsyncResult.Data.Length);
+ reqStream.Flush();
+ reqStream.Close();
+
+ // Start the asynchronous operation to get the response
+ flushAsyncResult.Connection.BeginGetResponse(GetResponseCallback, flushAsyncResult);
+ }
+ catch (Exception exception)
+ {
+ flushAsyncResult.AsyncException = new TTransportException(exception.ToString(), exception);
+ flushAsyncResult.UpdateStatusToComplete();
+ flushAsyncResult.NotifyCallbackWhenAvailable();
+ }
+ }
+
+ private void GetResponseCallback(IAsyncResult asynchronousResult)
+ {
+ var flushAsyncResult = (FlushAsyncResult)asynchronousResult.AsyncState;
+ try
+ {
+ inputStream = flushAsyncResult.Connection.EndGetResponse(asynchronousResult).GetResponseStream();
+ }
+ catch (Exception exception)
+ {
+ flushAsyncResult.AsyncException = new TTransportException(exception.ToString(), exception);
+ }
+ flushAsyncResult.UpdateStatusToComplete();
+ flushAsyncResult.NotifyCallbackWhenAvailable();
+ }
+
+ // Based on http://msmvps.com/blogs/luisabreu/archive/2009/06/15/multithreading-implementing-the-iasyncresult-interface.aspx
+ class FlushAsyncResult : IAsyncResult
+ {
+ private volatile Boolean _isCompleted;
+ private ManualResetEvent _evt;
+ private readonly AsyncCallback _cbMethod;
+ private readonly object _state;
+
+ public FlushAsyncResult(AsyncCallback cbMethod, object state)
+ {
+ _cbMethod = cbMethod;
+ _state = state;
+ }
+
+ internal byte[] Data { get; set; }
+ internal HttpWebRequest Connection { get; set; }
+ internal TTransportException AsyncException { get; set; }
+
+ public object AsyncState
+ {
+ get { return _state; }
+ }
+ public WaitHandle AsyncWaitHandle
+ {
+ get { return GetEvtHandle(); }
+ }
+ public bool CompletedSynchronously
+ {
+ get { return false; }
+ }
+ public bool IsCompleted
+ {
+ get { return _isCompleted; }
+ }
+ private readonly object _locker = new object();
+ private ManualResetEvent GetEvtHandle()
+ {
+ lock (_locker)
+ {
+ if (_evt == null)
+ {
+ _evt = new ManualResetEvent(false);
+ }
+ if (_isCompleted)
+ {
+ _evt.Set();
+ }
+ }
+ return _evt;
+ }
+ internal void UpdateStatusToComplete()
+ {
+ _isCompleted = true; //1. set _iscompleted to true
+ lock (_locker)
+ {
+ if (_evt != null)
+ {
+ _evt.Set(); //2. set the event, when it exists
+ }
+ }
+ }
+
+ internal void NotifyCallbackWhenAvailable()
+ {
+ if (_cbMethod != null)
+ {
+ _cbMethod(this);
+ }
+ }
+ }
+
+ #region " IDisposable Support "
+ private bool _IsDisposed;
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_IsDisposed)
+ {
+ if (disposing)
+ {
+ if (inputStream != null)
+ inputStream.Dispose();
+ if (outputStream != null)
+ outputStream.Dispose();
+ }
+ }
+ _IsDisposed = true;
+ }
+ #endregion
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/THttpHandler.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/THttpHandler.cs
new file mode 100644
index 000000000..4115ef95a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/THttpHandler.cs
@@ -0,0 +1,102 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+
+using System;
+using System.Web;
+using System.Net;
+using System.IO;
+
+using Thrift.Protocol;
+
+namespace Thrift.Transport
+{
+ public class THttpHandler : IHttpHandler
+ {
+ protected TProcessor processor;
+
+ protected TProtocolFactory inputProtocolFactory;
+ protected TProtocolFactory outputProtocolFactory;
+
+ protected const string contentType = "application/x-thrift";
+ protected System.Text.Encoding encoding = System.Text.Encoding.UTF8;
+
+ public THttpHandler(TProcessor processor)
+ : this(processor, new TBinaryProtocol.Factory())
+ {
+
+ }
+
+ public THttpHandler(TProcessor processor, TProtocolFactory protocolFactory)
+ : this(processor, protocolFactory, protocolFactory)
+ {
+
+ }
+
+ public THttpHandler(TProcessor processor, TProtocolFactory inputProtocolFactory, TProtocolFactory outputProtocolFactory)
+ {
+ this.processor = processor;
+ this.inputProtocolFactory = inputProtocolFactory;
+ this.outputProtocolFactory = outputProtocolFactory;
+ }
+
+ public void ProcessRequest(HttpListenerContext context)
+ {
+ context.Response.ContentType = contentType;
+ context.Response.ContentEncoding = encoding;
+ ProcessRequest(context.Request.InputStream, context.Response.OutputStream);
+ }
+
+ public void ProcessRequest(HttpContext context)
+ {
+ context.Response.ContentType = contentType;
+ context.Response.ContentEncoding = encoding;
+ ProcessRequest(context.Request.InputStream, context.Response.OutputStream);
+ }
+
+ public void ProcessRequest(Stream input, Stream output)
+ {
+ TTransport transport = new TStreamTransport(input,output);
+
+ try
+ {
+ var inputProtocol = inputProtocolFactory.GetProtocol(transport);
+ var outputProtocol = outputProtocolFactory.GetProtocol(transport);
+
+ while (processor.Process(inputProtocol, outputProtocol))
+ {
+ }
+ }
+ catch (TTransportException)
+ {
+ // Client died, just move on
+ }
+ finally
+ {
+ transport.Close();
+ }
+ }
+
+ public bool IsReusable
+ {
+ get { return true; }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/THttpTaskAsyncHandler.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/THttpTaskAsyncHandler.cs
new file mode 100644
index 000000000..e491f32cb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/THttpTaskAsyncHandler.cs
@@ -0,0 +1,97 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System.Threading.Tasks;
+using System.Web;
+using Thrift.Protocol;
+
+namespace Thrift.Transport
+{
+ /// <summary>
+ /// An async task based HTTP handler for processing thrift services.
+ /// </summary>
+ public class THttpTaskAsyncHandler : HttpTaskAsyncHandler
+ {
+ private readonly TAsyncProcessor _processor;
+ private readonly TProtocolFactory _inputProtocolFactory;
+ private readonly TProtocolFactory _outputProtocolFactory;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="THttpTaskAsyncHandler"/> class
+ /// using the <see cref="TBinaryProtocol.Factory"/> for both input and output streams.
+ /// </summary>
+ /// <param name="processor">The async processor implementation.</param>
+ public THttpTaskAsyncHandler(TAsyncProcessor processor)
+ : this(processor, new TBinaryProtocol.Factory())
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="THttpTaskAsyncHandler"/> class
+ /// using <paramref name="protocolFactory"/> for both input and output streams.
+ /// </summary>
+ /// <param name="processor">The async processor implementation.</param>
+ /// <param name="protocolFactory">The protocol factory.</param>
+ public THttpTaskAsyncHandler(TAsyncProcessor processor, TProtocolFactory protocolFactory)
+ : this(processor, protocolFactory, protocolFactory)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="THttpTaskAsyncHandler"/> class.
+ /// </summary>
+ /// <param name="processor">The async processor implementation.</param>
+ /// <param name="inputProtocolFactory">The input protocol factory.</param>
+ /// <param name="outputProtocolFactory">The output protocol factory.</param>
+ public THttpTaskAsyncHandler(TAsyncProcessor processor, TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory)
+ {
+ _processor = processor;
+ _inputProtocolFactory = inputProtocolFactory;
+ _outputProtocolFactory = outputProtocolFactory;
+ }
+
+ public override async Task ProcessRequestAsync(HttpContext context)
+ {
+ var transport = new TStreamTransport(context.Request.InputStream, context.Response.OutputStream);
+
+ try
+ {
+ var input = _inputProtocolFactory.GetProtocol(transport);
+ var output = _outputProtocolFactory.GetProtocol(transport);
+
+ while (await _processor.ProcessAsync(input, output))
+ {
+ }
+ }
+ catch (TTransportException)
+ {
+ // Client died, just move on
+ }
+ finally
+ {
+ transport.Close();
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/TMemoryBuffer.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/TMemoryBuffer.cs
new file mode 100644
index 000000000..303d08329
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/TMemoryBuffer.cs
@@ -0,0 +1,117 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using System.IO;
+using System.Reflection;
+using Thrift.Protocol;
+
+namespace Thrift.Transport
+{
+ public class TMemoryBuffer : TTransport
+ {
+
+ private readonly MemoryStream byteStream;
+
+ public TMemoryBuffer()
+ {
+ byteStream = new MemoryStream();
+ }
+
+ public TMemoryBuffer(byte[] buf)
+ {
+ byteStream = new MemoryStream(buf);
+ }
+
+ public override void Open()
+ {
+ /** do nothing **/
+ }
+
+ public override void Close()
+ {
+ /** do nothing **/
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ return byteStream.Read(buf, off, len);
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ byteStream.Write(buf, off, len);
+ }
+
+ public byte[] GetBuffer()
+ {
+ return byteStream.ToArray();
+ }
+
+
+ public override bool IsOpen
+ {
+ get { return true; }
+ }
+
+ public static byte[] Serialize(TAbstractBase s)
+ {
+ var t = new TMemoryBuffer();
+ var p = new TBinaryProtocol(t);
+
+ s.Write(p);
+
+ return t.GetBuffer();
+ }
+
+ public static T DeSerialize<T>(byte[] buf) where T : TAbstractBase
+ {
+ var trans = new TMemoryBuffer(buf);
+ var p = new TBinaryProtocol(trans);
+ if (typeof(TBase).IsAssignableFrom(typeof(T)))
+ {
+ var method = typeof(T).GetMethod("Read", BindingFlags.Instance | BindingFlags.Public);
+ var t = Activator.CreateInstance<T>();
+ method.Invoke(t, new object[] { p });
+ return t;
+ }
+ else
+ {
+ var method = typeof(T).GetMethod("Read", BindingFlags.Static | BindingFlags.Public);
+ return (T)method.Invoke(null, new object[] { p });
+ }
+ }
+
+ private bool _IsDisposed;
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_IsDisposed)
+ {
+ if (disposing)
+ {
+ if (byteStream != null)
+ byteStream.Dispose();
+ }
+ }
+ _IsDisposed = true;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/TNamedPipeClientTransport.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/TNamedPipeClientTransport.cs
new file mode 100644
index 000000000..49a50aa5b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/TNamedPipeClientTransport.cs
@@ -0,0 +1,111 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.IO.Pipes;
+using System.Threading;
+
+namespace Thrift.Transport
+{
+ public class TNamedPipeClientTransport : TTransport
+ {
+ private NamedPipeClientStream client;
+ private string ServerName;
+ private string PipeName;
+ private int ConnectTimeout;
+
+ public TNamedPipeClientTransport(string pipe, int timeout = Timeout.Infinite)
+ {
+ ServerName = ".";
+ PipeName = pipe;
+ ConnectTimeout = timeout;
+ }
+
+ public TNamedPipeClientTransport(string server, string pipe, int timeout = Timeout.Infinite)
+ {
+ ServerName = (server != "") ? server : ".";
+ PipeName = pipe;
+ ConnectTimeout = timeout;
+ }
+
+ public override bool IsOpen
+ {
+ get { return client != null && client.IsConnected; }
+ }
+
+ public override void Open()
+ {
+ if (IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen);
+ }
+ client = new NamedPipeClientStream(ServerName, PipeName, PipeDirection.InOut, PipeOptions.None);
+ client.Connect(ConnectTimeout);
+ }
+
+ public override void Close()
+ {
+ if (client != null)
+ {
+ client.Close();
+ client = null;
+ }
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ if (client == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ return client.Read(buf, off, len);
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ if (client == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ // if necessary, send the data in chunks
+ // there's a system limit around 0x10000 bytes that we hit otherwise
+ // MSDN: "Pipe write operations across a network are limited to 65,535 bytes per write. For more information regarding pipes, see the Remarks section."
+ var nBytes = Math.Min(len, 15 * 4096); // 16 would exceed the limit
+ while (nBytes > 0)
+ {
+ client.Write(buf, off, nBytes);
+
+ off += nBytes;
+ len -= nBytes;
+ nBytes = Math.Min(len, nBytes);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ client.Dispose();
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/TNamedPipeServerTransport.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/TNamedPipeServerTransport.cs
new file mode 100644
index 000000000..32215cfc1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/TNamedPipeServerTransport.cs
@@ -0,0 +1,296 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.IO.Pipes;
+using System.Threading;
+using System.Security.Principal;
+
+namespace Thrift.Transport
+{
+ public class TNamedPipeServerTransport : TServerTransport
+ {
+ /// <summary>
+ /// This is the address of the Pipe on the localhost.
+ /// </summary>
+ private readonly string pipeAddress;
+ private NamedPipeServerStream stream = null;
+ private bool asyncMode = true;
+
+ public TNamedPipeServerTransport(string pipeAddress)
+ {
+ this.pipeAddress = pipeAddress;
+ }
+
+ public override void Listen()
+ {
+ // nothing to do here
+ }
+
+ public override void Close()
+ {
+ if (stream != null)
+ {
+ try
+ {
+ stream.Close();
+ stream.Dispose();
+ }
+ finally
+ {
+ stream = null;
+ }
+ }
+ }
+
+ private void EnsurePipeInstance()
+ {
+ if (stream == null)
+ {
+ var direction = PipeDirection.InOut;
+ var maxconn = NamedPipeServerStream.MaxAllowedServerInstances;
+ var mode = PipeTransmissionMode.Byte;
+ var options = asyncMode ? PipeOptions.Asynchronous : PipeOptions.None;
+ const int INBUF_SIZE = 4096;
+ const int OUTBUF_SIZE = 4096;
+
+ // security
+ var security = new PipeSecurity();
+ security.AddAccessRule(
+ new PipeAccessRule(
+ new SecurityIdentifier(WellKnownSidType.WorldSid, null),
+ PipeAccessRights.Read | PipeAccessRights.Write | PipeAccessRights.Synchronize | PipeAccessRights.CreateNewInstance,
+ System.Security.AccessControl.AccessControlType.Allow
+ )
+ );
+
+ try
+ {
+ stream = new NamedPipeServerStream(pipeAddress, direction, maxconn, mode, options, INBUF_SIZE, OUTBUF_SIZE, security);
+ }
+ catch (NotImplementedException) // Mono still does not support async, fallback to sync
+ {
+ if (asyncMode)
+ {
+ options &= (~PipeOptions.Asynchronous);
+ stream = new NamedPipeServerStream(pipeAddress, direction, maxconn, mode, options, INBUF_SIZE, OUTBUF_SIZE, security);
+ asyncMode = false;
+ }
+ else
+ {
+ throw;
+ }
+ }
+
+ }
+ }
+
+ protected override TTransport AcceptImpl()
+ {
+ try
+ {
+ EnsurePipeInstance();
+
+ if (asyncMode)
+ {
+ var evt = new ManualResetEvent(false);
+ Exception eOuter = null;
+
+ stream.BeginWaitForConnection(asyncResult =>
+ {
+ try
+ {
+ if (stream != null)
+ stream.EndWaitForConnection(asyncResult);
+ else
+ eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted);
+ }
+ catch (Exception e)
+ {
+ if (stream != null)
+ eOuter = e;
+ else
+ eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted, e.Message, e);
+ }
+ evt.Set();
+ }, null);
+
+ evt.WaitOne();
+
+ if (eOuter != null)
+ throw eOuter; // rethrow exception
+ }
+ else
+ {
+ stream.WaitForConnection();
+ }
+
+ var trans = new ServerTransport(stream,asyncMode);
+ stream = null; // pass ownership to ServerTransport
+ return trans;
+ }
+ catch (TTransportException)
+ {
+ Close();
+ throw;
+ }
+ catch (Exception e)
+ {
+ Close();
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, e.Message, e);
+ }
+ }
+
+ private class ServerTransport : TTransport
+ {
+ private NamedPipeServerStream stream;
+ private bool asyncMode;
+
+ public ServerTransport(NamedPipeServerStream stream, bool asyncMode)
+ {
+ this.stream = stream;
+ this.asyncMode = asyncMode;
+ }
+
+ public override bool IsOpen
+ {
+ get { return stream != null && stream.IsConnected; }
+ }
+
+ public override void Open()
+ {
+ }
+
+ public override void Close()
+ {
+ if (stream != null)
+ stream.Close();
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ if (stream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ if (asyncMode)
+ {
+ Exception eOuter = null;
+ var evt = new ManualResetEvent(false);
+ int retval = 0;
+
+ stream.BeginRead(buf, off, len, asyncResult =>
+ {
+ try
+ {
+ if (stream != null)
+ retval = stream.EndRead(asyncResult);
+ else
+ eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted);
+ }
+ catch (Exception e)
+ {
+ if (stream != null)
+ eOuter = e;
+ else
+ eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted, e.Message, e);
+ }
+ evt.Set();
+ }, null);
+
+ evt.WaitOne();
+
+ if (eOuter != null)
+ throw eOuter; // rethrow exception
+ else
+ return retval;
+ }
+ else
+ {
+ return stream.Read(buf, off, len);
+ }
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ if (stream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ // if necessary, send the data in chunks
+ // there's a system limit around 0x10000 bytes that we hit otherwise
+ // MSDN: "Pipe write operations across a network are limited to 65,535 bytes per write. For more information regarding pipes, see the Remarks section."
+ var nBytes = Math.Min(len, 15 * 4096); // 16 would exceed the limit
+ while (nBytes > 0)
+ {
+
+ if (asyncMode)
+ {
+ Exception eOuter = null;
+ var evt = new ManualResetEvent(false);
+
+ stream.BeginWrite(buf, off, nBytes, asyncResult =>
+ {
+ try
+ {
+ if (stream != null)
+ stream.EndWrite(asyncResult);
+ else
+ eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted);
+ }
+ catch (Exception e)
+ {
+ if (stream != null)
+ eOuter = e;
+ else
+ eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted, e.Message, e);
+ }
+ evt.Set();
+ }, null);
+
+ evt.WaitOne();
+
+ if (eOuter != null)
+ throw eOuter; // rethrow exception
+ }
+ else
+ {
+ stream.Write(buf, off, nBytes);
+ }
+
+ off += nBytes;
+ len -= nBytes;
+ nBytes = Math.Min(len, nBytes);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (stream != null)
+ stream.Dispose();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/TServerSocket.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/TServerSocket.cs
new file mode 100644
index 000000000..d8ec62ab3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/TServerSocket.cs
@@ -0,0 +1,176 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Net.Sockets;
+
+
+namespace Thrift.Transport
+{
+ public class TServerSocket : TServerTransport
+ {
+ /// <summary>
+ /// Underlying server with socket.
+ /// </summary>
+ private TcpListener server = null;
+
+ /// <summary>
+ /// Port to listen on.
+ /// </summary>
+ private int port = 0;
+
+ /// <summary>
+ /// Timeout for client sockets from accept.
+ /// </summary>
+ private int clientTimeout = 0;
+
+ /// <summary>
+ /// Whether or not to wrap new TSocket connections in buffers.
+ /// </summary>
+ private bool useBufferedSockets = false;
+
+ /// <summary>
+ /// Creates a server socket from underlying socket object.
+ /// </summary>
+ public TServerSocket(TcpListener listener)
+ : this(listener, 0)
+ {
+ }
+
+ /// <summary>
+ /// Creates a server socket from underlying socket object.
+ /// </summary>
+ public TServerSocket(TcpListener listener, int clientTimeout)
+ {
+ this.server = listener;
+ this.clientTimeout = clientTimeout;
+ }
+
+ /// <summary>
+ /// Creates just a port listening server socket.
+ /// </summary>
+ public TServerSocket(int port)
+ : this(port, 0)
+ {
+ }
+
+ /// <summary>
+ /// Creates just a port listening server socket.
+ /// </summary>
+ public TServerSocket(int port, int clientTimeout)
+ : this(port, clientTimeout, false)
+ {
+ }
+
+ public TServerSocket(int port, int clientTimeout, bool useBufferedSockets)
+ {
+ this.port = port;
+ this.clientTimeout = clientTimeout;
+ this.useBufferedSockets = useBufferedSockets;
+ try
+ {
+ // Make server socket
+ this.server = TSocketVersionizer.CreateTcpListener(this.port);
+ this.server.Server.NoDelay = true;
+ }
+ catch (Exception ex)
+ {
+ server = null;
+ throw new TTransportException("Could not create ServerSocket on port " + this.port + ".", ex);
+ }
+ }
+
+ public override void Listen()
+ {
+ // Make sure not to block on accept
+ if (server != null)
+ {
+ try
+ {
+ server.Start();
+ }
+ catch (SocketException sx)
+ {
+ throw new TTransportException("Could not accept on listening socket: " + sx.Message, sx);
+ }
+ }
+ }
+
+ protected override TTransport AcceptImpl()
+ {
+ if (server == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No underlying server socket.");
+ }
+ try
+ {
+ TSocket result2 = null;
+ TcpClient result = server.AcceptTcpClient();
+ try
+ {
+ result2 = new TSocket(result);
+ result2.Timeout = clientTimeout;
+ if (useBufferedSockets)
+ {
+ TBufferedTransport result3 = new TBufferedTransport(result2);
+ return result3;
+ }
+ else
+ {
+ return result2;
+ }
+ }
+ catch (System.Exception)
+ {
+ // If a TSocket was successfully created, then let
+ // it do proper cleanup of the TcpClient object.
+ if (result2 != null)
+ result2.Dispose();
+ else // Otherwise, clean it up ourselves.
+ ((IDisposable)result).Dispose();
+ throw;
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException(ex.ToString(), ex);
+ }
+ }
+
+ public override void Close()
+ {
+ if (server != null)
+ {
+ try
+ {
+ server.Stop();
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException("WARNING: Could not close server socket: " + ex, ex);
+ }
+ server = null;
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/TServerTransport.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/TServerTransport.cs
new file mode 100644
index 000000000..e63880be1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/TServerTransport.cs
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+
+namespace Thrift.Transport
+{
+ public abstract class TServerTransport
+ {
+ public abstract void Listen();
+ public abstract void Close();
+ protected abstract TTransport AcceptImpl();
+
+ public TTransport Accept()
+ {
+ TTransport transport = AcceptImpl();
+ if (transport == null)
+ {
+ throw new TTransportException("accept() may not return NULL");
+ }
+ return transport;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/TSilverlightSocket.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/TSilverlightSocket.cs
new file mode 100644
index 000000000..40469ab40
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/TSilverlightSocket.cs
@@ -0,0 +1,393 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+/* only for silverlight */
+#if SILVERLIGHT
+
+using System;
+using System.Net.Sockets;
+using System.IO;
+using System.Net;
+using System.Threading;
+
+namespace Thrift.Transport
+{
+ public class TSilverlightSocket : TTransport
+ {
+ Socket socket = null;
+ static ManualResetEvent readAsyncComplete = new ManualResetEvent(false);
+ public event EventHandler<SocketAsyncEventArgs> connectHandler = null;
+
+ // memory stream for write cache.
+ private MemoryStream outputStream = new MemoryStream();
+
+ private string host = null;
+ private int port = 0;
+ private int timeout = 0;
+
+ // constructor
+ public TSilverlightSocket(string host, int port)
+ : this(host, port, 0)
+ {
+ }
+
+ // constructor
+ public TSilverlightSocket(string host, int port, int timeout)
+ {
+ this.host = host;
+ this.port = port;
+ this.timeout = timeout;
+
+ InitSocket();
+ }
+
+ private void InitSocket()
+ {
+ // Create a stream-based, TCP socket using the InterNetwork Address Family.
+ socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+ socket.NoDelay = true;
+ }
+
+ public int Timeout
+ {
+ set
+ {
+ timeout = value;
+ }
+ }
+
+ public string Host
+ {
+ get
+ {
+ return host;
+ }
+ }
+
+ public int Port
+ {
+ get
+ {
+ return port;
+ }
+ }
+
+ public override bool IsOpen
+ {
+ get
+ {
+ if (socket == null)
+ {
+ return false;
+ }
+
+ return socket.Connected;
+ }
+ }
+
+ public override void Open()
+ {
+ if (IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen, "Socket already connected");
+ }
+
+ if (string.IsNullOrEmpty(host))
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open null host");
+ }
+
+ if (port <= 0)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open without port");
+ }
+
+ if (socket == null)
+ {
+ InitSocket();
+ }
+
+ if (timeout == 0) // no timeout -> infinite
+ {
+ timeout = 10000; // set a default timeout for WP.
+ }
+
+ {
+ // Create DnsEndPoint. The hostName and port are passed in to this method.
+ DnsEndPoint hostEntry = new DnsEndPoint(this.host, this.port);
+
+ // Create a SocketAsyncEventArgs object to be used in the connection request
+ SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
+ socketEventArg.RemoteEndPoint = hostEntry;
+
+ // Inline event handler for the Completed event.
+ // Note: This event handler was implemented inline in order to make this method self-contained.
+ socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
+ {
+ if (connectHandler != null)
+ {
+ connectHandler(this, e);
+ }
+ });
+
+ // Make an asynchronous Connect request over the socket
+ socket.ConnectAsync(socketEventArg);
+ }
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ bool _timeout = true;
+ string _error = null;
+ int _recvBytes = -1;
+
+ if (socket == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Socket is not open");
+ }
+
+ // Create SocketAsyncEventArgs context object
+ SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
+ socketEventArg.RemoteEndPoint = socket.RemoteEndPoint;
+
+ // Setup the buffer to receive the data
+ socketEventArg.SetBuffer(buf, off, len);
+
+ // Inline event handler for the Completed event.
+ // Note: This even handler was implemented inline in order to make
+ // this method self-contained.
+ socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
+ {
+ _timeout = false;
+
+ if (e.SocketError == SocketError.Success)
+ {
+ _recvBytes = e.BytesTransferred;
+ }
+ else
+ {
+ _error = e.SocketError.ToString();
+ }
+
+ readAsyncComplete.Set();
+ });
+
+ // Sets the state of the event to nonsignaled, causing threads to block
+ readAsyncComplete.Reset();
+
+ // Make an asynchronous Receive request over the socket
+ socket.ReceiveAsync(socketEventArg);
+
+ // Block the UI thread for a maximum of TIMEOUT_MILLISECONDS milliseconds.
+ // If no response comes back within this time then proceed
+ readAsyncComplete.WaitOne(this.timeout);
+
+ if (_timeout)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.TimedOut, "Socket recv timeout");
+ }
+
+ if (_error != null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, _error);
+ }
+
+ return _recvBytes;
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ outputStream.Write(buf, off, len);
+ }
+
+ private void beginFlush_Completed(object sender, SocketAsyncEventArgs e)
+ {
+ FlushAsyncResult flushAsyncResult = e.UserToken as FlushAsyncResult;
+ flushAsyncResult.UpdateStatusToComplete();
+ flushAsyncResult.NotifyCallbackWhenAvailable();
+
+ if (e.SocketError != SocketError.Success)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, e.SocketError.ToString());
+ }
+ }
+
+ public override IAsyncResult BeginFlush(AsyncCallback callback, object state)
+ {
+ // Extract request and reset buffer
+ byte[] data = outputStream.ToArray();
+
+ FlushAsyncResult flushAsyncResult = new FlushAsyncResult(callback, state);
+
+ SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
+ socketEventArg.RemoteEndPoint = socket.RemoteEndPoint;
+ socketEventArg.UserToken = flushAsyncResult;
+
+ socketEventArg.Completed += beginFlush_Completed;
+ socketEventArg.SetBuffer(data, 0, data.Length);
+
+ socket.SendAsync(socketEventArg);
+
+ return flushAsyncResult;
+ }
+
+ public override void EndFlush(IAsyncResult asyncResult)
+ {
+ try
+ {
+ var flushAsyncResult = (FlushAsyncResult)asyncResult;
+
+ if (!flushAsyncResult.IsCompleted)
+ {
+ var waitHandle = flushAsyncResult.AsyncWaitHandle;
+ waitHandle.WaitOne();
+ waitHandle.Close();
+ }
+
+ if (flushAsyncResult.AsyncException != null)
+ {
+ throw flushAsyncResult.AsyncException;
+ }
+ }
+ finally
+ {
+ outputStream = new MemoryStream();
+ }
+ }
+
+ // Copy from impl from THttpClient.cs
+ // Based on http://msmvps.com/blogs/luisabreu/archive/2009/06/15/multithreading-implementing-the-iasyncresult-interface.aspx
+ class FlushAsyncResult : IAsyncResult
+ {
+ private volatile Boolean _isCompleted;
+ private ManualResetEvent _evt;
+ private readonly AsyncCallback _cbMethod;
+ private readonly object _state;
+
+ public FlushAsyncResult(AsyncCallback cbMethod, object state)
+ {
+ _cbMethod = cbMethod;
+ _state = state;
+ }
+
+ internal byte[] Data { get; set; }
+ internal Socket Connection { get; set; }
+ internal TTransportException AsyncException { get; set; }
+
+ public object AsyncState
+ {
+ get { return _state; }
+ }
+
+ public WaitHandle AsyncWaitHandle
+ {
+ get { return GetEvtHandle(); }
+ }
+
+ public bool CompletedSynchronously
+ {
+ get { return false; }
+ }
+
+ public bool IsCompleted
+ {
+ get { return _isCompleted; }
+ }
+
+ private readonly object _locker = new object();
+
+ private ManualResetEvent GetEvtHandle()
+ {
+ lock (_locker)
+ {
+ if (_evt == null)
+ {
+ _evt = new ManualResetEvent(false);
+ }
+ if (_isCompleted)
+ {
+ _evt.Set();
+ }
+ }
+ return _evt;
+ }
+
+ internal void UpdateStatusToComplete()
+ {
+ _isCompleted = true; //1. set _iscompleted to true
+ lock (_locker)
+ {
+ if (_evt != null)
+ {
+ _evt.Set(); //2. set the event, when it exists
+ }
+ }
+ }
+
+ internal void NotifyCallbackWhenAvailable()
+ {
+ if (_cbMethod != null)
+ {
+ _cbMethod(this);
+ }
+ }
+ }
+
+ public override void Close()
+ {
+ if (socket != null)
+ {
+ socket.Close();
+ socket = null;
+ }
+ }
+
+#region " IDisposable Support "
+ private bool _IsDisposed;
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_IsDisposed)
+ {
+ if (disposing)
+ {
+ if (outputStream != null)
+ {
+ outputStream.Dispose();
+ }
+ outputStream = null;
+ if (socket != null)
+ {
+ ((IDisposable)socket).Dispose();
+ }
+ }
+ }
+ _IsDisposed = true;
+ }
+#endregion
+ }
+}
+
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/TSocket.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/TSocket.cs
new file mode 100644
index 000000000..d8fa335ad
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/TSocket.cs
@@ -0,0 +1,245 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Net.Sockets;
+
+namespace Thrift.Transport
+{
+ public class TSocket : TStreamTransport
+ {
+ private TcpClient client = null;
+ private string host = null;
+ private int port = 0;
+ private int timeout = 0;
+
+ public TSocket(TcpClient client)
+ {
+ this.client = client;
+
+ if (IsOpen)
+ {
+ inputStream = client.GetStream();
+ outputStream = client.GetStream();
+ }
+ }
+
+ public TSocket(string host, int port)
+ : this(host, port, 0)
+ {
+ }
+
+ public TSocket(string host, int port, int timeout)
+ {
+ this.host = host;
+ this.port = port;
+ this.timeout = timeout;
+
+ InitSocket();
+ }
+
+ private void InitSocket()
+ {
+ this.client = TSocketVersionizer.CreateTcpClient();
+ this.client.ReceiveTimeout = client.SendTimeout = timeout;
+ this.client.Client.NoDelay = true;
+ }
+
+ public int Timeout
+ {
+ set
+ {
+ client.ReceiveTimeout = client.SendTimeout = timeout = value;
+ }
+ }
+
+ public TcpClient TcpClient
+ {
+ get
+ {
+ return client;
+ }
+ }
+
+ public string Host
+ {
+ get
+ {
+ return host;
+ }
+ }
+
+ public int Port
+ {
+ get
+ {
+ return port;
+ }
+ }
+
+ public override bool IsOpen
+ {
+ get
+ {
+ if (client == null)
+ {
+ return false;
+ }
+
+ return client.Connected;
+ }
+ }
+
+ public override void Open()
+ {
+ if (IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen, "Socket already connected");
+ }
+
+ if (string.IsNullOrEmpty(host))
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open null host");
+ }
+
+ if (port <= 0)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open without port");
+ }
+
+ if (client == null)
+ {
+ InitSocket();
+ }
+
+ if (timeout == 0) // no timeout -> infinite
+ {
+ client.Connect(host, port);
+ }
+ else // we have a timeout -> use it
+ {
+ ConnectHelper hlp = new ConnectHelper(client);
+ IAsyncResult asyncres = client.BeginConnect(host, port, new AsyncCallback(ConnectCallback), hlp);
+ bool bConnected = asyncres.AsyncWaitHandle.WaitOne(timeout) && client.Connected;
+ if (!bConnected)
+ {
+ lock (hlp.Mutex)
+ {
+ if (hlp.CallbackDone)
+ {
+ asyncres.AsyncWaitHandle.Close();
+ client.Close();
+ }
+ else
+ {
+ hlp.DoCleanup = true;
+ client = null;
+ }
+ }
+ throw new TTransportException(TTransportException.ExceptionType.TimedOut, "Connect timed out");
+ }
+ }
+
+ inputStream = client.GetStream();
+ outputStream = client.GetStream();
+ }
+
+
+ static void ConnectCallback(IAsyncResult asyncres)
+ {
+ ConnectHelper hlp = asyncres.AsyncState as ConnectHelper;
+ lock (hlp.Mutex)
+ {
+ hlp.CallbackDone = true;
+
+ try
+ {
+ if (hlp.Client.Client != null)
+ hlp.Client.EndConnect(asyncres);
+ }
+ catch (Exception)
+ {
+ // catch that away
+ }
+
+ if (hlp.DoCleanup)
+ {
+ try
+ {
+ asyncres.AsyncWaitHandle.Close();
+ }
+ catch (Exception) { }
+
+ try
+ {
+ if (hlp.Client is IDisposable)
+ ((IDisposable)hlp.Client).Dispose();
+ }
+ catch (Exception) { }
+ hlp.Client = null;
+ }
+ }
+ }
+
+ private class ConnectHelper
+ {
+ public object Mutex = new object();
+ public bool DoCleanup = false;
+ public bool CallbackDone = false;
+ public TcpClient Client;
+ public ConnectHelper(TcpClient client)
+ {
+ Client = client;
+ }
+ }
+
+ public override void Close()
+ {
+ base.Close();
+ if (client != null)
+ {
+ client.Close();
+ client = null;
+ }
+ }
+
+ #region " IDisposable Support "
+ private bool _IsDisposed;
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_IsDisposed)
+ {
+ if (disposing)
+ {
+ if (client != null)
+ ((IDisposable)client).Dispose();
+ base.Dispose(disposing);
+ }
+ }
+ _IsDisposed = true;
+ }
+ #endregion
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/TSocketVersionizer.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/TSocketVersionizer.cs
new file mode 100644
index 000000000..8c2f8e995
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/TSocketVersionizer.cs
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Sockets;
+using System.Reflection;
+using System.Text;
+#if NET45
+using System.Threading.Tasks;
+#endif
+
+namespace Thrift.Transport
+{
+ /// <summary>
+ /// PropertyInfo for the DualMode property of the System.Net.Sockets.Socket class. Used to determine if the sockets are capable of
+ /// automatic IPv4 and IPv6 handling. If DualMode is present the sockets automatically handle IPv4 and IPv6 connections.
+ /// If the DualMode is not available the system configuration determines whether IPv4 or IPv6 is used.
+ /// </summary>
+ internal static class TSocketVersionizer
+ {
+ /// <summary>
+ /// Creates a TcpClient according to the capabilities of the used framework.
+ /// </summary>
+ internal static TcpClient CreateTcpClient()
+ {
+ TcpClient client = null;
+
+#if NET45
+ client = new TcpClient(AddressFamily.InterNetworkV6);
+ client.Client.DualMode = true;
+#else
+ client = new TcpClient(AddressFamily.InterNetwork);
+#endif
+
+ return client;
+ }
+
+ /// <summary>
+ /// Creates a TcpListener according to the capabilities of the used framework.
+ /// </summary>
+ internal static TcpListener CreateTcpListener(Int32 port)
+ {
+ TcpListener listener = null;
+
+#if NET45
+ listener = new TcpListener(System.Net.IPAddress.IPv6Any, port);
+ listener.Server.DualMode = true;
+#else
+
+ listener = new TcpListener(System.Net.IPAddress.Any, port);
+#endif
+
+ return listener;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/TStreamTransport.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/TStreamTransport.cs
new file mode 100644
index 000000000..304599faa
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/TStreamTransport.cs
@@ -0,0 +1,128 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.IO;
+
+namespace Thrift.Transport
+{
+ public class TStreamTransport : TTransport
+ {
+ protected Stream inputStream;
+ protected Stream outputStream;
+
+ protected TStreamTransport()
+ {
+ }
+
+ public TStreamTransport(Stream inputStream, Stream outputStream)
+ {
+ this.inputStream = inputStream;
+ this.outputStream = outputStream;
+ }
+
+ public Stream OutputStream
+ {
+ get { return outputStream; }
+ }
+
+ public Stream InputStream
+ {
+ get { return inputStream; }
+ }
+
+ public override bool IsOpen
+ {
+ get { return true; }
+ }
+
+ public override void Open()
+ {
+ }
+
+ public override void Close()
+ {
+ if (inputStream != null)
+ {
+ inputStream.Close();
+ inputStream = null;
+ }
+ if (outputStream != null)
+ {
+ outputStream.Close();
+ outputStream = null;
+ }
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ if (inputStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot read from null inputstream");
+ }
+
+ return inputStream.Read(buf, off, len);
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ if (outputStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot write to null outputstream");
+ }
+
+ outputStream.Write(buf, off, len);
+ }
+
+ public override void Flush()
+ {
+ if (outputStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot flush null outputstream");
+ }
+
+ outputStream.Flush();
+ }
+
+
+ #region " IDisposable Support "
+ private bool _IsDisposed;
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_IsDisposed)
+ {
+ if (disposing)
+ {
+ if (InputStream != null)
+ InputStream.Dispose();
+ if (OutputStream != null)
+ OutputStream.Dispose();
+ }
+ }
+ _IsDisposed = true;
+ }
+ #endregion
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/TTLSServerSocket.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/TTLSServerSocket.cs
new file mode 100644
index 000000000..716a97ca8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/TTLSServerSocket.cs
@@ -0,0 +1,223 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Thrift.Transport
+{
+ /// <summary>
+ /// SSL Server Socket Wrapper Class
+ /// </summary>
+ public class TTLSServerSocket : TServerTransport
+ {
+ /// <summary>
+ /// Underlying tcp server
+ /// </summary>
+ private TcpListener server = null;
+
+ /// <summary>
+ /// The port where the socket listen
+ /// </summary>
+ private int port = 0;
+
+ /// <summary>
+ /// Timeout for the created server socket
+ /// </summary>
+ private readonly int clientTimeout;
+
+ /// <summary>
+ /// Whether or not to wrap new TSocket connections in buffers
+ /// </summary>
+ private bool useBufferedSockets = false;
+
+ /// <summary>
+ /// The servercertificate with the private- and public-key
+ /// </summary>
+ private X509Certificate serverCertificate;
+
+ /// <summary>
+ /// The function to validate the client certificate.
+ /// </summary>
+ private RemoteCertificateValidationCallback clientCertValidator;
+
+ /// <summary>
+ /// The function to determine which certificate to use.
+ /// </summary>
+ private LocalCertificateSelectionCallback localCertificateSelectionCallback;
+
+ /// <summary>
+ /// The SslProtocols value that represents the protocol used for authentication.
+ /// </summary>
+ private readonly SslProtocols sslProtocols;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TTLSServerSocket" /> class.
+ /// </summary>
+ /// <param name="port">The port where the server runs.</param>
+ /// <param name="certificate">The certificate object.</param>
+ public TTLSServerSocket(int port, X509Certificate2 certificate)
+ : this(port, 0, certificate)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TTLSServerSocket" /> class.
+ /// </summary>
+ /// <param name="port">The port where the server runs.</param>
+ /// <param name="clientTimeout">Send/receive timeout.</param>
+ /// <param name="certificate">The certificate object.</param>
+ public TTLSServerSocket(int port, int clientTimeout, X509Certificate2 certificate)
+ : this(port, clientTimeout, false, certificate)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TTLSServerSocket" /> class.
+ /// </summary>
+ /// <param name="port">The port where the server runs.</param>
+ /// <param name="clientTimeout">Send/receive timeout.</param>
+ /// <param name="useBufferedSockets">If set to <c>true</c> [use buffered sockets].</param>
+ /// <param name="certificate">The certificate object.</param>
+ /// <param name="clientCertValidator">The certificate validator.</param>
+ /// <param name="localCertificateSelectionCallback">The callback to select which certificate to use.</param>
+ /// <param name="sslProtocols">The SslProtocols value that represents the protocol used for authentication.</param>
+ public TTLSServerSocket(
+ int port,
+ int clientTimeout,
+ bool useBufferedSockets,
+ X509Certificate2 certificate,
+ RemoteCertificateValidationCallback clientCertValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ // TODO: Enable Tls11 and Tls12 (TLS 1.1 and 1.2) by default once we start using .NET 4.5+.
+ SslProtocols sslProtocols = SslProtocols.Tls)
+ {
+ if (!certificate.HasPrivateKey)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, "Your server-certificate needs to have a private key");
+ }
+
+ this.port = port;
+ this.clientTimeout = clientTimeout;
+ this.serverCertificate = certificate;
+ this.useBufferedSockets = useBufferedSockets;
+ this.clientCertValidator = clientCertValidator;
+ this.localCertificateSelectionCallback = localCertificateSelectionCallback;
+ this.sslProtocols = sslProtocols;
+ try
+ {
+ // Create server socket
+ this.server = TSocketVersionizer.CreateTcpListener(this.port);
+ this.server.Server.NoDelay = true;
+ }
+ catch (Exception ex)
+ {
+ server = null;
+ throw new TTransportException("Could not create ServerSocket on port " + this.port + ".", ex);
+ }
+ }
+
+ /// <summary>
+ /// Starts the server.
+ /// </summary>
+ public override void Listen()
+ {
+ // Make sure accept is not blocking
+ if (this.server != null)
+ {
+ try
+ {
+ this.server.Start();
+ }
+ catch (SocketException sx)
+ {
+ throw new TTransportException("Could not accept on listening socket: " + sx.Message, sx);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Callback for Accept Implementation
+ /// </summary>
+ /// <returns>
+ /// TTransport-object.
+ /// </returns>
+ protected override TTransport AcceptImpl()
+ {
+ if (this.server == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No underlying server socket.");
+ }
+
+ try
+ {
+ TcpClient client = this.server.AcceptTcpClient();
+ client.SendTimeout = client.ReceiveTimeout = this.clientTimeout;
+
+ //wrap the client in an SSL Socket passing in the SSL cert
+ TTLSSocket socket = new TTLSSocket(
+ client,
+ this.serverCertificate,
+ true,
+ this.clientCertValidator,
+ this.localCertificateSelectionCallback,
+ this.sslProtocols);
+
+ socket.setupTLS();
+
+ if (useBufferedSockets)
+ {
+ TBufferedTransport trans = new TBufferedTransport(socket);
+ return trans;
+ }
+ else
+ {
+ return socket;
+ }
+
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException(ex.ToString(), ex);
+ }
+ }
+
+ /// <summary>
+ /// Stops the Server
+ /// </summary>
+ public override void Close()
+ {
+ if (this.server != null)
+ {
+ try
+ {
+ this.server.Stop();
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException("WARNING: Could not close server socket: " + ex, ex);
+ }
+ this.server = null;
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/TTLSSocket.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/TTLSSocket.cs
new file mode 100644
index 000000000..06286dc8b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/TTLSSocket.cs
@@ -0,0 +1,445 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Thrift.Transport
+{
+ /// <summary>
+ /// SSL Socket Wrapper class
+ /// </summary>
+ public class TTLSSocket : TStreamTransport
+ {
+ /// <summary>
+ /// Internal TCP Client
+ /// </summary>
+ private TcpClient client;
+
+ /// <summary>
+ /// The host
+ /// </summary>
+ private string host;
+
+ /// <summary>
+ /// The port
+ /// </summary>
+ private int port;
+
+ /// <summary>
+ /// The timeout for the connection
+ /// </summary>
+ private int timeout;
+
+ /// <summary>
+ /// Internal SSL Stream for IO
+ /// </summary>
+ private SslStream secureStream;
+
+ /// <summary>
+ /// Defines wheter or not this socket is a server socket<br/>
+ /// This is used for the TLS-authentication
+ /// </summary>
+ private bool isServer;
+
+ /// <summary>
+ /// The certificate
+ /// </summary>
+ private X509Certificate certificate;
+
+ /// <summary>
+ /// User defined certificate validator.
+ /// </summary>
+ private RemoteCertificateValidationCallback certValidator;
+
+ /// <summary>
+ /// The function to determine which certificate to use.
+ /// </summary>
+ private LocalCertificateSelectionCallback localCertificateSelectionCallback;
+
+ /// <summary>
+ /// The SslProtocols value that represents the protocol used for authentication.SSL protocols to be used.
+ /// </summary>
+ private readonly SslProtocols sslProtocols;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
+ /// </summary>
+ /// <param name="client">An already created TCP-client</param>
+ /// <param name="certificate">The certificate.</param>
+ /// <param name="isServer">if set to <c>true</c> [is server].</param>
+ /// <param name="certValidator">User defined cert validator.</param>
+ /// <param name="localCertificateSelectionCallback">The callback to select which certificate to use.</param>
+ /// <param name="sslProtocols">The SslProtocols value that represents the protocol used for authentication.</param>
+ public TTLSSocket(
+ TcpClient client,
+ X509Certificate certificate,
+ bool isServer = false,
+ RemoteCertificateValidationCallback certValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ // TODO: Enable Tls11 and Tls12 (TLS 1.1 and 1.2) by default once we start using .NET 4.5+.
+ SslProtocols sslProtocols = SslProtocols.Tls)
+ {
+ this.client = client;
+ this.certificate = certificate;
+ this.certValidator = certValidator;
+ this.localCertificateSelectionCallback = localCertificateSelectionCallback;
+ this.sslProtocols = sslProtocols;
+ this.isServer = isServer;
+ if (isServer && certificate == null)
+ {
+ throw new ArgumentException("TTLSSocket needs certificate to be used for server", "certificate");
+ }
+
+ if (IsOpen)
+ {
+ base.inputStream = client.GetStream();
+ base.outputStream = client.GetStream();
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
+ /// </summary>
+ /// <param name="host">The host, where the socket should connect to.</param>
+ /// <param name="port">The port.</param>
+ /// <param name="certificatePath">The certificate path.</param>
+ /// <param name="certValidator">User defined cert validator.</param>
+ /// <param name="localCertificateSelectionCallback">The callback to select which certificate to use.</param>
+ /// <param name="sslProtocols">The SslProtocols value that represents the protocol used for authentication.</param>
+ public TTLSSocket(
+ string host,
+ int port,
+ string certificatePath,
+ RemoteCertificateValidationCallback certValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls)
+ : this(host, port, 0, X509Certificate.CreateFromCertFile(certificatePath), certValidator, localCertificateSelectionCallback, sslProtocols)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
+ /// </summary>
+ /// <param name="host">The host, where the socket should connect to.</param>
+ /// <param name="port">The port.</param>
+ /// <param name="certificate">The certificate.</param>
+ /// <param name="certValidator">User defined cert validator.</param>
+ /// <param name="localCertificateSelectionCallback">The callback to select which certificate to use.</param>
+ /// <param name="sslProtocols">The SslProtocols value that represents the protocol used for authentication.</param>
+ public TTLSSocket(
+ string host,
+ int port,
+ X509Certificate certificate = null,
+ RemoteCertificateValidationCallback certValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls)
+ : this(host, port, 0, certificate, certValidator, localCertificateSelectionCallback, sslProtocols)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
+ /// </summary>
+ /// <param name="host">The host, where the socket should connect to.</param>
+ /// <param name="port">The port.</param>
+ /// <param name="timeout">The timeout.</param>
+ /// <param name="certificate">The certificate.</param>
+ /// <param name="certValidator">User defined cert validator.</param>
+ /// <param name="localCertificateSelectionCallback">The callback to select which certificate to use.</param>
+ /// <param name="sslProtocols">The SslProtocols value that represents the protocol used for authentication.</param>
+ public TTLSSocket(
+ string host,
+ int port,
+ int timeout,
+ X509Certificate certificate,
+ RemoteCertificateValidationCallback certValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls)
+ {
+ this.host = host;
+ this.port = port;
+ this.timeout = timeout;
+ this.certificate = certificate;
+ this.certValidator = certValidator;
+ this.localCertificateSelectionCallback = localCertificateSelectionCallback;
+ this.sslProtocols = sslProtocols;
+
+ InitSocket();
+ }
+
+ /// <summary>
+ /// Creates the TcpClient and sets the timeouts
+ /// </summary>
+ private void InitSocket()
+ {
+ client = TSocketVersionizer.CreateTcpClient();
+ client.ReceiveTimeout = client.SendTimeout = timeout;
+ client.Client.NoDelay = true;
+ }
+
+ /// <summary>
+ /// Sets Send / Recv Timeout for IO
+ /// </summary>
+ public int Timeout
+ {
+ set
+ {
+ this.client.ReceiveTimeout = this.client.SendTimeout = this.timeout = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets the TCP client.
+ /// </summary>
+ public TcpClient TcpClient
+ {
+ get
+ {
+ return client;
+ }
+ }
+
+ /// <summary>
+ /// Gets the host.
+ /// </summary>
+ public string Host
+ {
+ get
+ {
+ return host;
+ }
+ }
+
+ /// <summary>
+ /// Gets the port.
+ /// </summary>
+ public int Port
+ {
+ get
+ {
+ return port;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether TCP Client is Cpen
+ /// </summary>
+ public override bool IsOpen
+ {
+ get
+ {
+ if (this.client == null)
+ {
+ return false;
+ }
+
+ return this.client.Connected;
+ }
+ }
+
+ /// <summary>
+ /// Validates the certificates!<br/>
+ /// </summary>
+ /// <param name="sender">The sender-object.</param>
+ /// <param name="certificate">The used certificate.</param>
+ /// <param name="chain">The certificate chain.</param>
+ /// <param name="sslValidationErrors">An enum, which lists all the errors from the .NET certificate check.</param>
+ /// <returns></returns>
+ private bool DefaultCertificateValidator(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslValidationErrors)
+ {
+ return (sslValidationErrors == SslPolicyErrors.None);
+ }
+
+ /// <summary>
+ /// Connects to the host and starts the routine, which sets up the TLS
+ /// </summary>
+ public override void Open()
+ {
+ if (IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen, "Socket already connected");
+ }
+
+ if (string.IsNullOrEmpty(host))
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open null host");
+ }
+
+ if (port <= 0)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open without port");
+ }
+
+ if (client == null)
+ {
+ InitSocket();
+ }
+
+ if (timeout == 0) // no timeout -> infinite
+ {
+ client.Connect(host, port);
+ }
+ else // we have a timeout -> use it
+ {
+ ConnectHelper hlp = new ConnectHelper(client);
+ IAsyncResult asyncres = client.BeginConnect(host, port, new AsyncCallback(ConnectCallback), hlp);
+ bool bConnected = asyncres.AsyncWaitHandle.WaitOne(timeout) && client.Connected;
+ if (!bConnected)
+ {
+ lock (hlp.Mutex)
+ {
+ if (hlp.CallbackDone)
+ {
+ asyncres.AsyncWaitHandle.Close();
+ client.Close();
+ }
+ else
+ {
+ hlp.DoCleanup = true;
+ client = null;
+ }
+ }
+ throw new TTransportException(TTransportException.ExceptionType.TimedOut, "Connect timed out");
+ }
+ }
+
+ setupTLS();
+ }
+
+ /// <summary>
+ /// Creates a TLS-stream and lays it over the existing socket
+ /// </summary>
+ public void setupTLS()
+ {
+ RemoteCertificateValidationCallback validator = this.certValidator ?? DefaultCertificateValidator;
+
+ if (this.localCertificateSelectionCallback != null)
+ {
+ this.secureStream = new SslStream(
+ this.client.GetStream(),
+ false,
+ validator,
+ this.localCertificateSelectionCallback
+ );
+ }
+ else
+ {
+ this.secureStream = new SslStream(
+ this.client.GetStream(),
+ false,
+ validator
+ );
+ }
+
+ try
+ {
+ if (isServer)
+ {
+ // Server authentication
+ this.secureStream.AuthenticateAsServer(this.certificate, this.certValidator != null, sslProtocols, true);
+ }
+ else
+ {
+ // Client authentication
+ X509CertificateCollection certs = certificate != null ? new X509CertificateCollection { certificate } : new X509CertificateCollection();
+ this.secureStream.AuthenticateAsClient(host, certs, sslProtocols, true);
+ }
+ }
+ catch (Exception)
+ {
+ this.Close();
+ throw;
+ }
+
+ inputStream = this.secureStream;
+ outputStream = this.secureStream;
+ }
+
+ static void ConnectCallback(IAsyncResult asyncres)
+ {
+ ConnectHelper hlp = asyncres.AsyncState as ConnectHelper;
+ lock (hlp.Mutex)
+ {
+ hlp.CallbackDone = true;
+
+ try
+ {
+ if (hlp.Client.Client != null)
+ hlp.Client.EndConnect(asyncres);
+ }
+ catch (Exception)
+ {
+ // catch that away
+ }
+
+ if (hlp.DoCleanup)
+ {
+ try
+ {
+ asyncres.AsyncWaitHandle.Close();
+ }
+ catch (Exception) { }
+
+ try
+ {
+ if (hlp.Client is IDisposable)
+ ((IDisposable)hlp.Client).Dispose();
+ }
+ catch (Exception) { }
+ hlp.Client = null;
+ }
+ }
+ }
+
+ private class ConnectHelper
+ {
+ public object Mutex = new object();
+ public bool DoCleanup = false;
+ public bool CallbackDone = false;
+ public TcpClient Client;
+ public ConnectHelper(TcpClient client)
+ {
+ Client = client;
+ }
+ }
+
+ /// <summary>
+ /// Closes the SSL Socket
+ /// </summary>
+ public override void Close()
+ {
+ base.Close();
+ if (this.client != null)
+ {
+ this.client.Close();
+ this.client = null;
+ }
+
+ if (this.secureStream != null)
+ {
+ this.secureStream.Close();
+ this.secureStream = null;
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/TTransport.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/TTransport.cs
new file mode 100644
index 000000000..5e4ac22ea
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/TTransport.cs
@@ -0,0 +1,146 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.IO;
+
+namespace Thrift.Transport
+{
+ public abstract class TTransport : IDisposable
+ {
+ public abstract bool IsOpen
+ {
+ get;
+ }
+
+ private byte[] _peekBuffer = new byte[1];
+ private bool _hasPeekByte;
+
+ public bool Peek()
+ {
+ //If we already have a byte read but not consumed, do nothing.
+ if (_hasPeekByte)
+ return true;
+
+ //If transport closed we can't peek.
+ if (!IsOpen)
+ return false;
+
+ //Try to read one byte. If succeeds we will need to store it for the next read.
+ try
+ {
+ int bytes = Read(_peekBuffer, 0, 1);
+ if (bytes == 0)
+ return false;
+ }
+ catch (IOException)
+ {
+ return false;
+ }
+
+ _hasPeekByte = true;
+ return true;
+ }
+
+ public abstract void Open();
+
+ public abstract void Close();
+
+ protected static void ValidateBufferArgs(byte[] buf, int off, int len)
+ {
+ if (buf == null)
+ throw new ArgumentNullException("buf");
+ if (off < 0)
+ throw new ArgumentOutOfRangeException("Buffer offset is smaller than zero.");
+ if (len < 0)
+ throw new ArgumentOutOfRangeException("Buffer length is smaller than zero.");
+ if (off + len > buf.Length)
+ throw new ArgumentOutOfRangeException("Not enough data.");
+ }
+
+ public abstract int Read(byte[] buf, int off, int len);
+
+ public int ReadAll(byte[] buf, int off, int len)
+ {
+ ValidateBufferArgs(buf, off, len);
+ int got = 0;
+
+ //If we previously peeked a byte, we need to use that first.
+ if (_hasPeekByte)
+ {
+ buf[off + got++] = _peekBuffer[0];
+ _hasPeekByte = false;
+ }
+
+ while (got < len)
+ {
+ int ret = Read(buf, off + got, len - got);
+ if (ret <= 0)
+ {
+ throw new TTransportException(
+ TTransportException.ExceptionType.EndOfFile,
+ "Cannot read, Remote side has closed");
+ }
+ got += ret;
+ }
+ return got;
+ }
+
+ public virtual void Write(byte[] buf)
+ {
+ Write(buf, 0, buf.Length);
+ }
+
+ public abstract void Write(byte[] buf, int off, int len);
+
+ public virtual void Flush()
+ {
+ }
+
+ public virtual IAsyncResult BeginFlush(AsyncCallback callback, object state)
+ {
+ throw new TTransportException(
+ TTransportException.ExceptionType.Unknown,
+ "Asynchronous operations are not supported by this transport.");
+ }
+
+ public virtual void EndFlush(IAsyncResult asyncResult)
+ {
+ throw new TTransportException(
+ TTransportException.ExceptionType.Unknown,
+ "Asynchronous operations are not supported by this transport.");
+ }
+
+ #region " IDisposable Support "
+ // IDisposable
+ protected abstract void Dispose(bool disposing);
+
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/TTransportException.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/TTransportException.cs
new file mode 100644
index 000000000..7f6cc1889
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/TTransportException.cs
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+
+namespace Thrift.Transport
+{
+ public class TTransportException : TException
+ {
+ protected ExceptionType type;
+
+ public TTransportException()
+ : base()
+ {
+ }
+
+ public TTransportException(ExceptionType type)
+ : this()
+ {
+ this.type = type;
+ }
+
+ public TTransportException(ExceptionType type, string message, Exception inner = null)
+ : base(message, inner)
+ {
+ this.type = type;
+ }
+
+ public TTransportException(string message, Exception inner = null)
+ : base(message, inner)
+ {
+ }
+
+ public ExceptionType Type
+ {
+ get { return type; }
+ }
+
+ public enum ExceptionType
+ {
+ Unknown,
+ NotOpen,
+ AlreadyOpen,
+ TimedOut,
+ EndOfFile,
+ Interrupted
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/Transport/TTransportFactory.cs b/src/jaegertracing/thrift/lib/csharp/src/Transport/TTransportFactory.cs
new file mode 100644
index 000000000..47a0c6265
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/Transport/TTransportFactory.cs
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+
+namespace Thrift.Transport
+{
+ /// <summary>
+ /// From Mark Slee &amp; Aditya Agarwal of Facebook:
+ /// Factory class used to create wrapped instance of Transports.
+ /// This is used primarily in servers, which get Transports from
+ /// a ServerTransport and then may want to mutate them (i.e. create
+ /// a BufferedTransport from the underlying base transport)
+ /// </summary>
+ public class TTransportFactory
+ {
+ public virtual TTransport GetTransport(TTransport trans)
+ {
+ return trans;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/src/thrift.snk b/src/jaegertracing/thrift/lib/csharp/src/thrift.snk
new file mode 100644
index 000000000..97bc5812b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/src/thrift.snk
Binary files differ
diff --git a/src/jaegertracing/thrift/lib/csharp/test/JSON/JSONTest.csproj b/src/jaegertracing/thrift/lib/csharp/test/JSON/JSONTest.csproj
new file mode 100644
index 000000000..f07d43eec
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/JSON/JSONTest.csproj
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+ <ProductVersion>8.0.30703</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{E37A0034-DCBF-4886-A0DA-25A03D12D975}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>JSONTest</RootNamespace>
+ <AssemblyName>JSONTest</AssemblyName>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <TargetFrameworkProfile>
+ </TargetFrameworkProfile>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+ <PlatformTarget>x86</PlatformTarget>
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+ <PlatformTarget>x86</PlatformTarget>
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Program.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="app.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\src\Thrift.csproj">
+ <Project>{499EB63C-D74C-47E8-AE48-A2FC94538E9D}</Project>
+ <Name>Thrift</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/csharp/test/JSON/Program.cs b/src/jaegertracing/thrift/lib/csharp/test/JSON/Program.cs
new file mode 100644
index 000000000..f61388ae7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/JSON/Program.cs
@@ -0,0 +1,95 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Thrift.Protocol;
+using Thrift.Transport;
+
+namespace JSONTest
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ TestThrift2365(); // JSON binary decodes too much data
+ TestThrift2336(); // hex encoding using \uXXXX where 0xXXXX > 0xFF
+ TestThrift3403(); // JSON escaped unicode surrogate pair support.
+ }
+
+
+ public static void TestThrift2365()
+ {
+ var rnd = new Random();
+ for (var len = 0; len < 10; ++len)
+ {
+ byte[] dataWritten = new byte[len];
+ rnd.NextBytes(dataWritten);
+
+ Stream stm = new MemoryStream();
+ TTransport trans = new TStreamTransport(null, stm);
+ TProtocol prot = new TJSONProtocol(trans);
+ prot.WriteBinary(dataWritten);
+
+ stm.Position = 0;
+ trans = new TStreamTransport(stm, null);
+ prot = new TJSONProtocol(trans);
+ byte[] dataRead = prot.ReadBinary();
+
+ Debug.Assert(dataRead.Length == dataWritten.Length);
+ for (var i = 0; i < dataRead.Length; ++i)
+ Debug.Assert(dataRead[i] == dataWritten[i]);
+ }
+ }
+
+
+ public static void TestThrift2336()
+ {
+ const string RUSSIAN_TEXT = "\u0420\u0443\u0441\u0441\u043a\u043e\u0435 \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435";
+ const string RUSSIAN_JSON = "\"\\u0420\\u0443\\u0441\\u0441\\u043a\\u043e\\u0435 \\u041d\\u0430\\u0437\\u0432\\u0430\\u043d\\u0438\\u0435\"";
+
+ // prepare buffer with JSON data
+ byte[] rawBytes = new byte[RUSSIAN_JSON.Length];
+ for (var i = 0; i < RUSSIAN_JSON.Length; ++i)
+ rawBytes[i] = (byte)(RUSSIAN_JSON[i] & (char)0xFF); // only low bytes
+
+ // parse and check
+ var stm = new MemoryStream(rawBytes);
+ var trans = new TStreamTransport(stm, null);
+ var prot = new TJSONProtocol(trans);
+ Debug.Assert(prot.ReadString() == RUSSIAN_TEXT, "reading JSON with hex-encoded chars > 8 bit");
+ }
+
+ public static void TestThrift3403()
+ {
+ string GCLEF_TEXT = "\ud834\udd1e";
+ const string GCLEF_JSON = "\"\\ud834\\udd1e\"";
+
+ // parse and check
+ var stm = new MemoryStream(Encoding.UTF8.GetBytes(GCLEF_JSON));
+ var trans = new TStreamTransport(stm, null);
+ var prot = new TJSONProtocol(trans);
+ Debug.Assert(prot.ReadString() == GCLEF_TEXT, "reading JSON with surrogate pair hex-encoded chars");
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/test/JSON/Properties/AssemblyInfo.cs b/src/jaegertracing/thrift/lib/csharp/test/JSON/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..fdff4a1a5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/JSON/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// Allgemeine Informationen über eine Assembly werden über die folgenden
+// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+// die mit einer Assembly verknüpft sind.
+[assembly: AssemblyTitle("JSONTest")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("The Apache Software Foundation")]
+[assembly: AssemblyProduct("Thrift")]
+[assembly: AssemblyCopyright("The Apache Software Foundation")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
+// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
+// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
+[assembly: ComVisible(false)]
+
+// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
+[assembly: Guid("2b2e7d56-3e65-4368-92d7-e34d56b7105e")]
+
+// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
+//
+// Hauptversion
+// Nebenversion
+// Buildnummer
+// Revision
+//
+// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
+// übernehmen, indem Sie "*" eingeben:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/jaegertracing/thrift/lib/csharp/test/JSON/app.config b/src/jaegertracing/thrift/lib/csharp/test/JSON/app.config
new file mode 100644
index 000000000..9c1919d4f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/JSON/app.config
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<configuration>
+<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
diff --git a/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Client/Multiplex.Test.Client.cs b/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Client/Multiplex.Test.Client.cs
new file mode 100644
index 000000000..c810a0891
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Client/Multiplex.Test.Client.cs
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using Thrift.Collections;
+using Thrift.Transport;
+using Thrift.Protocol;
+using Thrift.Server;
+using Thrift;
+using Test.Multiplex;
+
+namespace Test.Multiplex.Client
+{
+ public class TestClient
+ {
+ static void Execute(int port)
+ {
+ try
+ {
+ TTransport trans;
+ trans = new TSocket("localhost", port);
+ trans = new TFramedTransport(trans);
+ trans.Open();
+
+ TProtocol Protocol = new TBinaryProtocol(trans, true, true);
+
+ TMultiplexedProtocol multiplex;
+
+ multiplex = new TMultiplexedProtocol(Protocol, Constants.NAME_BENCHMARKSERVICE);
+ BenchmarkService.Iface bench = new BenchmarkService.Client(multiplex);
+
+ multiplex = new TMultiplexedProtocol(Protocol, Constants.NAME_AGGR);
+ Aggr.Iface aggr = new Aggr.Client(multiplex);
+
+ for (sbyte i = 1; 10 >= i; ++i)
+ {
+ aggr.addValue(bench.fibonacci(i));
+ }
+
+ foreach (int k in aggr.getValues())
+ {
+ Console.Write(k.ToString() + " ");
+ Console.WriteLine("");
+ }
+ trans.Close();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e.Message);
+ }
+ }
+
+ static void Main(string[] args)
+ {
+ int port = 9090;
+ if (args.Length > 0)
+ {
+ port = ushort.Parse(args[0]);
+ }
+ Execute(port);
+ Console.WriteLine("done.");
+ }
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Client/MultiplexClient.csproj b/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Client/MultiplexClient.csproj
new file mode 100644
index 000000000..09d20f503
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Client/MultiplexClient.csproj
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{5E91DA17-E548-415F-8C9F-9E84EDF8EE06}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>MultiplexClient</RootNamespace>
+ <AssemblyName>MultiplexClient</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <IsWebBootstrapper>false</IsWebBootstrapper>
+ <FileUpgradeFlags>
+ </FileUpgradeFlags>
+ <OldToolsVersion>3.5</OldToolsVersion>
+ <UpgradeBackupLocation />
+ <PublishUrl>publish\</PublishUrl>
+ <Install>true</Install>
+ <InstallFrom>Disk</InstallFrom>
+ <UpdateEnabled>false</UpdateEnabled>
+ <UpdateMode>Foreground</UpdateMode>
+ <UpdateInterval>7</UpdateInterval>
+ <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+ <UpdatePeriodically>false</UpdatePeriodically>
+ <UpdateRequired>false</UpdateRequired>
+ <MapFileExtensions>true</MapFileExtensions>
+ <ApplicationRevision>0</ApplicationRevision>
+ <ApplicationVersion>0.13.0.0</ApplicationVersion>
+ <UseApplicationTrust>false</UseApplicationTrust>
+ <BootstrapperEnabled>true</BootstrapperEnabled>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>..\bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>..\bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="..\Multiplex.Test.Common.cs">
+ <Link>Multiplex.Test.Common.cs</Link>
+ </Compile>
+ <Compile Include="..\gen-csharp\Aggr.cs" />
+ <Compile Include="..\gen-csharp\BenchmarkService.cs" />
+ <Compile Include="..\gen-csharp\Error.cs" />
+ <Compile Include="Multiplex.Test.Client.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.2.0">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 2.0 %28x86%29</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.0">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.0 %28x86%29</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
+ <Visible>False</Visible>
+ <ProductName>Windows Installer 3.1</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\src\Thrift.csproj">
+ <Project>{499EB63C-D74C-47E8-AE48-A2FC94538E9D}</Project>
+ <Name>Thrift</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+ <PropertyGroup>
+ <PreBuildEvent>rmdir /s /q "$(ProjectDir)gen-csharp"
+del /f /q "$(ProjectDir)ThriftImpl.dll"
+SET OUTPUT_DIR=$(ProjectDir)
+
+SET THRIFT_FILE=$(ProjectDir)\..\..\..\..\..\contrib\async-test\aggr.thrift
+for %25%25I in ("%25OUTPUT_DIR%25") do set SHORT_DIR=%25%25~fsI
+for %25%25I in ("%25THRIFT_FILE%25") do set THRIFT_SHORT=%25%25~fsI
+"$(ProjectDir)\..\..\..\..\..\compiler\cpp\thrift.exe" --gen csharp -o %25SHORT_DIR%25 %25THRIFT_SHORT%25
+
+SET THRIFT_FILE=$(ProjectDir)\..\..\..\..\..\lib\rb\benchmark\Benchmark.thrift
+for %25%25I in ("%25OUTPUT_DIR%25") do set SHORT_DIR=%25%25~fsI
+for %25%25I in ("%25THRIFT_FILE%25") do set THRIFT_SHORT=%25%25~fsI
+"$(ProjectDir)\..\..\..\..\..\compiler\cpp\thrift.exe" --gen csharp -o %25SHORT_DIR%25 %25THRIFT_SHORT%25
+
+</PreBuildEvent>
+ </PropertyGroup>
+</Project>
diff --git a/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Client/Properties/AssemblyInfo.cs b/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Client/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..0b65c1e52
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Client/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("MultiplexClient")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("The Apache Software Foundation")]
+[assembly: AssemblyProduct("Thrift")]
+[assembly: AssemblyCopyright("The Apache Software Foundation")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("66FC61E5-420B-4b56-8012-D6D6CE22537F")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("0.13.0.0")]
+[assembly: AssemblyFileVersion("0.13.0.0")]
diff --git a/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Makefile.am b/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Makefile.am
new file mode 100644
index 000000000..9c1f1b8b7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Makefile.am
@@ -0,0 +1,63 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+GENERATED = \
+ gen-csharp/Aggr.cs \
+ gen-csharp/BenchmarkService.cs \
+ gen-csharp/Error.cs
+
+BUILT_SOURCES = $(GENERATED)
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+gen-csharp/Aggr.cs: $(top_srcdir)/contrib/async-test/aggr.thrift
+ $(THRIFT) --gen csharp $<
+
+gen-csharp/BenchmarkService.cs gen-csharp/Error.cs: $(top_srcdir)/lib/rb/benchmark/Benchmark.thrift
+ $(THRIFT) --gen csharp $<
+
+ThriftImpl.dll: Multiplex.Test.Common.cs $(GENERATED) ../../Thrift.dll
+ $(CSC) $(CSC_DEFINES) -t:library -out:./ThriftImpl.dll -reference:../../Thrift.dll $(GENERATED) $<
+
+MultiplexClient.exe: Client/Multiplex.Test.Client.cs ThriftImpl.dll
+ $(CSC) $(CSC_DEFINES) -out:$@ -reference:../../Thrift.dll -reference:ThriftImpl.dll $<
+
+MultiplexServer.exe: Server/Multiplex.Test.Server.cs ThriftImpl.dll
+ $(CSC) $(CSC_DEFINES) -out:$@ -reference:../../Thrift.dll -reference:ThriftImpl.dll $<
+
+CLEANFILES = \
+ MultiplexClient.exe \
+ MultiplexServer.exe \
+ ThriftImpl.dll
+
+DISTCLEANFILES = \
+ Makefile.in
+
+clean-local:
+ $(RM) -rf gen-csharp
+
+dist-hook:
+ $(RM) -r $(distdir)/gen-csharp/
+
+TESTPORT = 9501
+check-local: MultiplexServer.exe MultiplexClient.exe
+ echo $(TESTPORT)
+ MONO_PATH=../../ timeout 10 mono MultiplexServer.exe $(TESTPORT) &
+ sleep 1
+ MONO_PATH=../../ mono MultiplexClient.exe $(TESTPORT)
diff --git a/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Multiplex.Test.Common.cs b/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Multiplex.Test.Common.cs
new file mode 100644
index 000000000..a687852d9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Multiplex.Test.Common.cs
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Distributed under the Thrift Software License
+//
+// See accompanying file LICENSE or visit the Thrift site at:
+// http://developers.facebook.com/thrift/
+using System;
+using System.Collections.Generic;
+using Thrift.Collections;
+using Thrift.Transport;
+using Thrift.Protocol;
+using Thrift.Server;
+
+namespace Test.Multiplex
+{
+ public class Constants
+ {
+ public const string NAME_BENCHMARKSERVICE = "BenchmarkService";
+ public const string NAME_AGGR = "Aggr";
+ }
+}
+
+
diff --git a/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Server/Multiplex.Test.Server.cs b/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Server/Multiplex.Test.Server.cs
new file mode 100644
index 000000000..9786189bb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Server/Multiplex.Test.Server.cs
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using Thrift.Collections;
+using Thrift.Transport;
+using Thrift.Protocol;
+using Thrift.Server;
+using Thrift;
+using Test.Multiplex;
+
+namespace Test.Multiplex.Server
+{
+ public class TestServer
+ {
+ class BenchmarkServiceImpl : BenchmarkService.Iface
+ {
+ public int fibonacci(sbyte n)
+ {
+ int prev, next, result;
+ prev = 0;
+ result = 1;
+ while (n > 0)
+ {
+ next = result + prev;
+ prev = result;
+ result = next;
+ --n;
+ }
+ return result;
+ }
+ }
+
+ class AggrServiceImpl : Aggr.Iface
+ {
+ List<int> values = new List<int>();
+
+ public void addValue(int value)
+ {
+ values.Add(value);
+ }
+
+ public List<int> getValues()
+ {
+ return values;
+ }
+ }
+
+ static void Execute(int port)
+ {
+ try
+ {
+ // create protocol factory, default to BinaryProtocol
+ TProtocolFactory ProtocolFactory = new TBinaryProtocol.Factory(true,true);
+ TServerTransport servertrans = new TServerSocket(port, 0, false);
+ TTransportFactory TransportFactory = new TFramedTransport.Factory();
+
+ BenchmarkService.Iface benchHandler = new BenchmarkServiceImpl();
+ TProcessor benchProcessor = new BenchmarkService.Processor(benchHandler);
+
+ Aggr.Iface aggrHandler = new AggrServiceImpl();
+ TProcessor aggrProcessor = new Aggr.Processor(aggrHandler);
+
+ TMultiplexedProcessor multiplex = new TMultiplexedProcessor();
+ multiplex.RegisterProcessor(Constants.NAME_BENCHMARKSERVICE, benchProcessor);
+ multiplex.RegisterProcessor(Constants.NAME_AGGR, aggrProcessor);
+
+ TServer ServerEngine = new TSimpleServer(multiplex, servertrans, TransportFactory, ProtocolFactory);
+
+ Console.WriteLine("Starting the server ...");
+ ServerEngine.Serve();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e.Message);
+ }
+ }
+
+ static void Main(string[] args)
+ {
+ int port = 9090;
+ if (args.Length > 0)
+ {
+ port = ushort.Parse(args[0]);
+ }
+ Execute(port);
+ }
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Server/MultiplexServer.csproj b/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Server/MultiplexServer.csproj
new file mode 100644
index 000000000..23d325362
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Server/MultiplexServer.csproj
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{D592BDF3-0DCE-48FB-890F-E4AE1D9CE7CD}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>MultiplexServer</RootNamespace>
+ <AssemblyName>MultiplexServer</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <IsWebBootstrapper>false</IsWebBootstrapper>
+ <FileUpgradeFlags>
+ </FileUpgradeFlags>
+ <OldToolsVersion>3.5</OldToolsVersion>
+ <UpgradeBackupLocation />
+ <PublishUrl>publish\</PublishUrl>
+ <Install>true</Install>
+ <InstallFrom>Disk</InstallFrom>
+ <UpdateEnabled>false</UpdateEnabled>
+ <UpdateMode>Foreground</UpdateMode>
+ <UpdateInterval>7</UpdateInterval>
+ <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+ <UpdatePeriodically>false</UpdatePeriodically>
+ <UpdateRequired>false</UpdateRequired>
+ <MapFileExtensions>true</MapFileExtensions>
+ <ApplicationRevision>0</ApplicationRevision>
+ <ApplicationVersion>0.13.0.0</ApplicationVersion>
+ <UseApplicationTrust>false</UseApplicationTrust>
+ <BootstrapperEnabled>true</BootstrapperEnabled>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>..\bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>..\bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="..\Multiplex.Test.Common.cs">
+ <Link>Multiplex.Test.Common.cs</Link>
+ </Compile>
+ <Compile Include="..\gen-csharp\Aggr.cs" />
+ <Compile Include="..\gen-csharp\BenchmarkService.cs" />
+ <Compile Include="..\gen-csharp\Error.cs" />
+ <Compile Include="Multiplex.Test.Server.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.2.0">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 2.0 %28x86%29</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.0">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.0 %28x86%29</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
+ <Visible>False</Visible>
+ <ProductName>Windows Installer 3.1</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\src\Thrift.csproj">
+ <Project>{499EB63C-D74C-47E8-AE48-A2FC94538E9D}</Project>
+ <Name>Thrift</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+ <PropertyGroup>
+ <PreBuildEvent>rmdir /s /q "$(ProjectDir)gen-csharp"
+del /f /q "$(ProjectDir)ThriftImpl.dll"
+SET OUTPUT_DIR=$(ProjectDir)
+
+SET THRIFT_FILE=$(ProjectDir)\..\..\..\..\..\contrib\async-test\aggr.thrift
+for %25%25I in ("%25OUTPUT_DIR%25") do set SHORT_DIR=%25%25~fsI
+for %25%25I in ("%25THRIFT_FILE%25") do set THRIFT_SHORT=%25%25~fsI
+"$(ProjectDir)\..\..\..\..\..\compiler\cpp\thrift.exe" --gen csharp -o %25SHORT_DIR%25 %25THRIFT_SHORT%25
+
+SET THRIFT_FILE=$(ProjectDir)\..\..\..\..\..\lib\rb\benchmark\Benchmark.thrift
+for %25%25I in ("%25OUTPUT_DIR%25") do set SHORT_DIR=%25%25~fsI
+for %25%25I in ("%25THRIFT_FILE%25") do set THRIFT_SHORT=%25%25~fsI
+"$(ProjectDir)\..\..\..\..\..\compiler\cpp\thrift.exe" --gen csharp -o %25SHORT_DIR%25 %25THRIFT_SHORT%25
+
+</PreBuildEvent>
+ </PropertyGroup>
+</Project>
diff --git a/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Server/Properties/AssemblyInfo.cs b/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Server/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..2b8a6af23
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/Multiplex/Server/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("MultiplexServer")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("The Apache Software Foundation")]
+[assembly: AssemblyProduct("Thrift")]
+[assembly: AssemblyCopyright("The Apache Software Foundation")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("F2F436C1-3D4F-411a-ADC3-B98848476A8E")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("0.13.0.0")]
+[assembly: AssemblyFileVersion("0.13.0.0")]
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/App_Start/FilterConfig.cs b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/App_Start/FilterConfig.cs
new file mode 100644
index 000000000..855184fd2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/App_Start/FilterConfig.cs
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Web.Mvc;
+
+namespace ThriftMVCTest
+{
+ public static class FilterConfig
+ {
+ public static void RegisterGlobalFilters(GlobalFilterCollection filters)
+ {
+ filters.Add(new HandleErrorAttribute());
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/App_Start/RouteConfig.cs b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/App_Start/RouteConfig.cs
new file mode 100644
index 000000000..b4b6023d6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/App_Start/RouteConfig.cs
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Web.Mvc;
+using System.Web.Routing;
+
+namespace ThriftMVCTest
+{
+ public static class RouteConfig
+ {
+ public static void RegisterRoutes(RouteCollection routes)
+ {
+ routes.IgnoreRoute("{resource}.thrift");
+ routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
+
+ routes.MapRoute(
+ name: "Default",
+ url: "{controller}/{action}/{id}",
+ defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
+ );
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/AsyncHttpHandler.cs b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/AsyncHttpHandler.cs
new file mode 100644
index 000000000..7f26184fa
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/AsyncHttpHandler.cs
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using Thrift.Transport;
+
+namespace ThriftMVCTest
+{
+ public class AsyncHttpHandler : THttpTaskAsyncHandler
+ {
+ public AsyncHttpHandler()
+ : base(
+ new Thrift.Test.SecondService.AsyncProcessor(new SecondServiceImpl()))
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Controllers/HomeController.cs b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Controllers/HomeController.cs
new file mode 100644
index 000000000..c9a1ec43f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Controllers/HomeController.cs
@@ -0,0 +1,70 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using System.Threading.Tasks;
+using System.Web.Mvc;
+using Thrift.Protocol;
+using Thrift.Test;
+using Thrift.Transport;
+
+namespace ThriftMVCTest.Controllers
+{
+ public class HomeController : Controller
+ {
+ public ActionResult Index()
+ {
+ return View();
+ }
+
+ public async Task<ActionResult> TestThriftAsync()
+ {
+ var baseUri = new Uri(string.Format("{0}://{1}{2}", Request.Url.Scheme, Request.Url.Authority,
+ Url.Content("~")));
+
+ SecondService.IAsync asyncService =
+ new SecondService.Client(new TBinaryProtocol(new THttpClient(new Uri(baseUri, "Async.thrift"))));
+
+ var result = await asyncService.secondtestStringAsync("TestString");
+ if (result != "testString(\"TestString\")")
+ {
+ throw new Exception("The wrong result was returned");
+ }
+
+ return RedirectToAction("Index");
+ }
+
+ public ActionResult TestThriftSync()
+ {
+ var baseUri = new Uri(string.Format("{0}://{1}{2}", Request.Url.Scheme, Request.Url.Authority,
+ Url.Content("~")));
+
+ SecondService.ISync service =
+ new SecondService.Client(new TBinaryProtocol(new THttpClient(new Uri(baseUri, "Sync.thrift"))));
+
+ var result = service.secondtestString("TestString");
+ if (result != "testString(\"TestString\")")
+ {
+ throw new Exception("The wrong result was returned");
+ }
+
+ return RedirectToAction("Index");
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Global.asax b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Global.asax
new file mode 100644
index 000000000..7bb688c7a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Global.asax
@@ -0,0 +1,19 @@
+<%@ Application Codebehind="Global.asax.cs" Inherits="ThriftMVCTest.MvcApplication" Language="C#" %>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Global.asax.cs b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Global.asax.cs
new file mode 100644
index 000000000..59731efb3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Global.asax.cs
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Web.Mvc;
+using System.Web.Routing;
+
+namespace ThriftMVCTest
+{
+ public class MvcApplication : System.Web.HttpApplication
+ {
+ protected void Application_Start()
+ {
+ AreaRegistration.RegisterAllAreas();
+ FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
+ RouteConfig.RegisterRoutes(RouteTable.Routes);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Properties/AssemblyInfo.cs b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..b812aaf1d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Properties/AssemblyInfo.cs
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ThriftMVCTest")]
+[assembly: AssemblyDescription("A web project for testing the thrift ASP.NET features.")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("The Apache Software Foundation")]
+[assembly: AssemblyProduct("Thrift")]
+[assembly: AssemblyCopyright("The Apache Software Foundation")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("366f9bd0-3c0e-48aa-b2ca-61fd4a93e427")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("0.13.0.0")]
+[assembly: AssemblyFileVersion("0.13.0.0")]
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/SecondServiceImpl.cs b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/SecondServiceImpl.cs
new file mode 100644
index 000000000..fad301a35
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/SecondServiceImpl.cs
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Threading.Tasks;
+using Thrift.Test;
+
+namespace ThriftMVCTest
+{
+ public class SecondServiceImpl : SecondService.IAsync, SecondService.ISync
+ {
+ public Task<string> secondtestStringAsync(string thing)
+ {
+ return Task.FromResult(thing);
+ }
+
+ public string secondtestString(string thing)
+ {
+ return "testString(\"" + thing + "\")";
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/SyncHttpHandler.cs b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/SyncHttpHandler.cs
new file mode 100644
index 000000000..4fe26624a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/SyncHttpHandler.cs
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using Thrift.Transport;
+
+namespace ThriftMVCTest
+{
+ public class SyncHttpHandler : THttpHandler
+ {
+ public SyncHttpHandler()
+ : base(
+ new Thrift.Test.SecondService.Processor(new SecondServiceImpl()))
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/ThriftMVCTest.csproj b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/ThriftMVCTest.csproj
new file mode 100644
index 000000000..0eb969a04
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/ThriftMVCTest.csproj
@@ -0,0 +1,200 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>
+ </ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{891B4487-C7BA-427E-BBC8-4C596C229A10}</ProjectGuid>
+ <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>ThriftMVCTest</RootNamespace>
+ <AssemblyName>ThriftMVCTest</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <MvcBuildViews>false</MvcBuildViews>
+ <UseIISExpress>true</UseIISExpress>
+ <IISExpressSSLPort />
+ <IISExpressAnonymousAuthentication />
+ <IISExpressWindowsAuthentication />
+ <IISExpressUseClassicPipelineMode />
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Drawing" />
+ <Reference Include="System.Web.DynamicData" />
+ <Reference Include="System.Web.Entity" />
+ <Reference Include="System.Web.ApplicationServices" />
+ <Reference Include="System.ComponentModel.DataAnnotations" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Web" />
+ <Reference Include="System.Web.Extensions" />
+ <Reference Include="System.Web.Abstractions" />
+ <Reference Include="System.Web.Routing" />
+ <Reference Include="System.Xml" />
+ <Reference Include="System.Configuration" />
+ <Reference Include="System.Web.Services" />
+ <Reference Include="System.EnterpriseServices" />
+ <Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <Private>True</Private>
+ <HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Net.Http">
+ </Reference>
+ <Reference Include="System.Net.Http.WebRequest">
+ </Reference>
+ <Reference Include="System.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <Private>True</Private>
+ <HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <Private>True</Private>
+ <HintPath>..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Web.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <Private>True</Private>
+ <HintPath>..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <Private>True</Private>
+ <HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Web.WebPages.Deployment, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <Private>True</Private>
+ <HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Deployment.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <Private>True</Private>
+ <HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Razor.dll</HintPath>
+ </Reference>
+ <Reference Include="ThriftImpl">
+ <HintPath>.\ThriftImpl.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="App_Start\FilterConfig.cs" />
+ <Compile Include="App_Start\RouteConfig.cs" />
+ <Compile Include="SyncHttpHandler.cs" />
+ <Compile Include="AsyncHttpHandler.cs" />
+ <Compile Include="Controllers\HomeController.cs" />
+ <Compile Include="Global.asax.cs">
+ <DependentUpon>Global.asax</DependentUpon>
+ </Compile>
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="SecondServiceImpl.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="favicon.ico" />
+ <Content Include="Global.asax" />
+ <Content Include="Web.config" />
+ <Content Include="Web.Debug.config">
+ <DependentUpon>Web.config</DependentUpon>
+ </Content>
+ <Content Include="Web.Release.config">
+ <DependentUpon>Web.config</DependentUpon>
+ </Content>
+ <Content Include="Views\Web.config" />
+ <Content Include="Views\_ViewStart.cshtml" />
+ <Content Include="Views\Shared\_Layout.cshtml" />
+ <Content Include="Views\Home\Index.cshtml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Folder Include="App_Data\" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="packages.config">
+ <SubType>Designer</SubType>
+ </Content>
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\src\Thrift.45.csproj">
+ <Project>{ebce35da-cf6a-42bc-a357-a9c09b534299}</Project>
+ <Name>Thrift.45</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <PropertyGroup>
+ <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
+ <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+ </PropertyGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
+ <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
+ <Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
+ <AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
+ </Target>
+ <ProjectExtensions>
+ <VisualStudio>
+ <FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
+ <WebProjectProperties>
+ <UseIIS>True</UseIIS>
+ <AutoAssignPort>True</AutoAssignPort>
+ <DevelopmentServerPort>57482</DevelopmentServerPort>
+ <DevelopmentServerVPath>/</DevelopmentServerVPath>
+ <IISUrl>http://localhost:57482/</IISUrl>
+ <NTLMAuthentication>False</NTLMAuthentication>
+ <UseCustomServer>False</UseCustomServer>
+ <CustomServerUrl>
+ </CustomServerUrl>
+ <SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
+ </WebProjectProperties>
+ </FlavorProperties>
+ </VisualStudio>
+ </ProjectExtensions>
+ <PropertyGroup>
+ <PreBuildEvent>rmdir /s /q "$(ProjectDir)gen-csharp"
+del /f /q "$(ProjectDir)ThriftImpl.dll"
+SET OUTPUT_DIR=$(ProjectDir)
+SET THRIFT_FILE=$(ProjectDir)\..\..\..\..\test\ThriftTest.thrift
+for %25%25I in ("%25OUTPUT_DIR%25") do set SHORT_DIR=%25%25~fsI
+for %25%25I in ("%25THRIFT_FILE%25") do set THRIFT_SHORT=%25%25~fsI
+"$(ProjectDir)\..\..\..\..\compiler\cpp\Debug\thrift.exe" --gen csharp:async=true -o %25SHORT_DIR%25 %25THRIFT_SHORT%25
+"$(MSBuildToolsPath)\Csc.exe" /t:library /out:"$(ProjectDir)ThriftImpl.dll" /recurse:"$(ProjectDir)gen-csharp"\* /reference:"$(ProjectDir)..\..\src\bin\Debug\Thrift45.dll"</PreBuildEvent>
+ </PropertyGroup>
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target> -->
+</Project> \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Views/Home/Index.cshtml b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Views/Home/Index.cshtml
new file mode 100644
index 000000000..f0ca7da20
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Views/Home/Index.cshtml
@@ -0,0 +1,25 @@
+@*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*@
+
+@{
+ ViewBag.Title = "Home Page";
+}
+
+<p>@Html.ActionLink("Test Thrift Async Service", "TestThriftAsync")</p>
+<p>@Html.ActionLink("Test Thrift Sync Service", "TestThriftSync")</p>
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Views/Shared/_Layout.cshtml b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Views/Shared/_Layout.cshtml
new file mode 100644
index 000000000..b41c99a10
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Views/Shared/_Layout.cshtml
@@ -0,0 +1,30 @@
+@*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*@
+
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Thrift ASP.NET Test</title>
+</head>
+<body>
+ @RenderBody()
+</body>
+</html>
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Views/Web.config b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Views/Web.config
new file mode 100644
index 000000000..3c211387a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Views/Web.config
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<configuration>
+ <configSections>
+ <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
+ <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
+ <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
+ </sectionGroup>
+ </configSections>
+
+ <system.web.webPages.razor>
+ <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
+ <pages pageBaseType="System.Web.Mvc.WebViewPage">
+ <namespaces>
+ <add namespace="System.Web.Mvc" />
+ <add namespace="System.Web.Mvc.Ajax" />
+ <add namespace="System.Web.Mvc.Html" />
+ <add namespace="System.Web.Optimization"/>
+ <add namespace="System.Web.Routing" />
+ <add namespace="ThriftMVCTest" />
+ </namespaces>
+ </pages>
+ </system.web.webPages.razor>
+
+ <appSettings>
+ <add key="webpages:Enabled" value="false" />
+ </appSettings>
+
+ <system.webServer>
+ <handlers>
+ <remove name="BlockViewHandler"/>
+ <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
+ </handlers>
+ </system.webServer>
+
+ <system.web>
+ <compilation>
+ <assemblies>
+ <add assembly="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
+ </assemblies>
+ </compilation>
+ </system.web>
+</configuration>
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Views/_ViewStart.cshtml b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Views/_ViewStart.cshtml
new file mode 100644
index 000000000..8cde2eeb9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Views/_ViewStart.cshtml
@@ -0,0 +1,22 @@
+@*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*@
+
+@{
+ Layout = "~/Views/Shared/_Layout.cshtml";
+}
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Web.Debug.config b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Web.Debug.config
new file mode 100644
index 000000000..45d56d809
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Web.Debug.config
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
+ <!--
+ In the example below, the "SetAttributes" transform will change the value of
+ "connectionString" to use "ReleaseSQLServer" only when the "Match" locator
+ finds an attribute "name" that has a value of "MyDB".
+
+ <connectionStrings>
+ <add name="MyDB"
+ connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
+ xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
+ </connectionStrings>
+ -->
+ <system.web>
+ <!--
+ In the example below, the "Replace" transform will replace the entire
+ <customErrors> section of your Web.config file.
+ Note that because there is only one customErrors section under the
+ <system.web> node, there is no need to use the "xdt:Locator" attribute.
+
+ <customErrors defaultRedirect="GenericError.htm"
+ mode="RemoteOnly" xdt:Transform="Replace">
+ <error statusCode="500" redirect="InternalError.htm"/>
+ </customErrors>
+ -->
+ </system.web>
+</configuration>
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Web.Release.config b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Web.Release.config
new file mode 100644
index 000000000..157c340ca
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Web.Release.config
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
+ <!--
+ In the example below, the "SetAttributes" transform will change the value of
+ "connectionString" to use "ReleaseSQLServer" only when the "Match" locator
+ finds an attribute "name" that has a value of "MyDB".
+
+ <connectionStrings>
+ <add name="MyDB"
+ connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
+ xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
+ </connectionStrings>
+ -->
+ <system.web>
+ <compilation xdt:Transform="RemoveAttributes(debug)" />
+ <!--
+ In the example below, the "Replace" transform will replace the entire
+ <customErrors> section of your Web.config file.
+ Note that because there is only one customErrors section under the
+ <system.web> node, there is no need to use the "xdt:Locator" attribute.
+
+ <customErrors defaultRedirect="GenericError.htm"
+ mode="RemoteOnly" xdt:Transform="Replace">
+ <error statusCode="500" redirect="InternalError.htm"/>
+ </customErrors>
+ -->
+ </system.web>
+</configuration>
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Web.config b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Web.config
new file mode 100644
index 000000000..9c57d117c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/Web.config
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+<!--
+ For more information on how to configure your ASP.NET application, please visit
+ http://go.microsoft.com/fwlink/?LinkId=301880
+ -->
+<configuration>
+ <appSettings>
+ <add key="webpages:Version" value="3.0.0.0" />
+ <add key="webpages:Enabled" value="false" />
+ <add key="ClientValidationEnabled" value="true" />
+ <add key="UnobtrusiveJavaScriptEnabled" value="true" />
+ <add key="owin:AutomaticAppStartup" value="false" />
+ </appSettings>
+ <system.web>
+ <authentication mode="None" />
+ <compilation debug="true" targetFramework="4.5" />
+ <httpRuntime targetFramework="4.5" />
+ </system.web>
+ <system.webServer>
+ <modules>
+ <remove name="FormsAuthentication" />
+ </modules>
+ <handlers>
+ <add name="AsyncHttpHandler" verb="*" path="Async.thrift" type="ThriftMVCTest.AsyncHttpHandler, ThriftMVCTest" />
+ <add name="SyncHttpHandler" verb="*" path="Sync.thrift" type="ThriftMVCTest.SyncHttpHandler, ThriftMVCTest" />
+ </handlers>
+ </system.webServer>
+ <runtime>
+ <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+ <dependentAssembly>
+ <assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" />
+ <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
+ </dependentAssembly>
+ <dependentAssembly>
+ <assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" />
+ <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
+ </dependentAssembly>
+ <dependentAssembly>
+ <assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" />
+ <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
+ </dependentAssembly>
+ <dependentAssembly>
+ <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" />
+ <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
+ </dependentAssembly>
+ <dependentAssembly>
+ <assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed" />
+ <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
+ </dependentAssembly>
+ <dependentAssembly>
+ <assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" />
+ <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
+ </dependentAssembly>
+ <dependentAssembly>
+ <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
+ <bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" />
+ </dependentAssembly>
+ <dependentAssembly>
+ <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
+ <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
+ </dependentAssembly>
+ <dependentAssembly>
+ <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
+ <bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
+ </dependentAssembly>
+ <dependentAssembly>
+ <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
+ <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
+ </dependentAssembly>
+ </assemblyBinding>
+ </runtime>
+</configuration> \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/favicon.ico b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/favicon.ico
new file mode 100644
index 000000000..a3a799985
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/favicon.ico
Binary files differ
diff --git a/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/packages.config b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/packages.config
new file mode 100644
index 000000000..98c8416f5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/csharp/test/ThriftMVCTest/packages.config
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<packages>
+ <package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net45" />
+ <package id="Microsoft.AspNet.Razor" version="3.2.3" targetFramework="net45" />
+ <package id="Microsoft.AspNet.WebPages" version="3.2.3" targetFramework="net45" />
+ <package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
+</packages> \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/d/Makefile.am b/src/jaegertracing/thrift/lib/d/Makefile.am
new file mode 100644
index 000000000..4787e0a60
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/Makefile.am
@@ -0,0 +1,198 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+AUTOMAKE_OPTIONS = serial-tests
+
+SUBDIRS = .
+
+if WITH_TESTS
+SUBDIRS += test
+endif
+
+#
+# Enumeration of all the public and private modules.
+#
+# We unconditionally install all of them, even if libevent or OpenSSL are
+# not available, but build the respective libraries only if the Deimos headers
+# could be found.
+#
+d_thriftmodules = $(addprefix thrift/, base)
+d_thriftdir = $(D_IMPORT_PREFIX)/thrift
+d_thrift_DATA = $(addprefix src/, $(addsuffix .d, $(d_thriftmodules)))
+
+d_asyncmodules = $(addprefix thrift/async/, base libevent socket ssl)
+d_asyncdir = $(d_thriftdir)/async
+d_async_DATA = $(addprefix src/, $(addsuffix .d, $(d_asyncmodules)))
+
+d_codegenmodules = $(addprefix thrift/codegen/, async_client \
+ async_client_pool base client client_pool processor)
+#d_codegenmodules = $(addprefix thrift/codegen/, async_client \
+# async_client_pool base client client_pool idlgen processor)
+
+d_codegendir = $(d_thriftdir)/codegen
+d_codegen_DATA = $(addprefix src/, $(addsuffix .d, $(d_codegenmodules)))
+
+d_protocolmodules = $(addprefix thrift/protocol/, base binary compact json \
+ processor)
+d_protocoldir = $(d_thriftdir)/protocol
+d_protocol_DATA = $(addprefix src/, $(addsuffix .d, $(d_protocolmodules)))
+
+d_servermodules = $(addprefix thrift/server/, base simple nonblocking \
+ taskpool threaded)
+d_serverdir = $(d_thriftdir)/server
+d_server_DATA = $(addprefix src/, $(addsuffix .d, $(d_servermodules)))
+
+d_servertransportmodules = $(addprefix thrift/server/transport/, base socket ssl)
+d_servertransportdir = $(d_thriftdir)/server/transport
+d_servertransport_DATA = $(addprefix src/, $(addsuffix .d, \
+ $(d_servertransportmodules)))
+
+d_transportmodules = $(addprefix thrift/transport/, base buffered file \
+ framed http memory piped range socket ssl zlib)
+d_transportdir = $(d_thriftdir)/transport
+d_transport_DATA = $(addprefix src/, $(addsuffix .d, $(d_transportmodules)))
+
+d_utilmodules = $(addprefix thrift/util/, awaitable cancellation future \
+ hashset)
+d_utildir = $(d_thriftdir)/util
+d_util_DATA = $(addprefix src/, $(addsuffix .d, $(d_utilmodules)))
+
+d_internalmodules = $(addprefix thrift/internal/, algorithm codegen ctfe \
+ endian resource_pool socket ssl ssl_bio traits)
+d_internaldir = $(d_thriftdir)/internal
+d_internal_DATA = $(addprefix src/, $(addsuffix .d, $(d_internalmodules)))
+
+d_testmodules = $(addprefix thrift/internal/test/, protocol server)
+d_testdir = $(d_internaldir)/test
+d_test_DATA = $(addprefix src/, $(addsuffix .d, $(d_testmodules)))
+
+d_publicmodules = $(d_thriftmodules) $(d_asyncmodules) \
+ $(d_codegenmodules) $(d_protocolmodules) $(d_servermodules) \
+ $(d_servertransportmodules) $(d_transportmodules) $(d_utilmodules)
+d_publicsources = $(addprefix src/, $(addsuffix .d, $(d_publicmodules)))
+
+d_modules = $(d_publicmodules) $(d_internalmodules) $(d_testmodules)
+
+# List modules with external dependencies and remove them from the main list
+d_libevent_dependent_modules = thrift/async/libevent thrift/server/nonblocking
+d_openssl_dependent_modules = thrift/async/ssl thrift/internal/ssl \
+ thrift/internal/ssl_bio thrift/transport/ssl thrift/server/transport/ssl
+d_main_modules = $(filter-out $(d_libevent_dependent_modules) \
+ $(d_openssl_dependent_modules),$(d_modules))
+
+
+d_lib_flags = -w -wi -Isrc -lib
+all_targets =
+
+#
+# libevent-dependent modules.
+#
+if HAVE_DEIMOS_EVENT2
+$(D_EVENT_LIB_NAME): $(addprefix src/, $(addsuffix .d, $(d_libevent_dependent_modules)))
+ $(DMD) -of$(D_EVENT_LIB_NAME) $(d_lib_flags) $^
+all_targets += $(D_EVENT_LIB_NAME)
+endif
+
+#
+# OpenSSL-dependent modules.
+#
+if HAVE_DEIMOS_OPENSSL
+$(D_SSL_LIB_NAME): $(addprefix src/, $(addsuffix .d, $(d_openssl_dependent_modules)))
+ $(DMD) -of$(D_SSL_LIB_NAME) $(d_lib_flags) $^
+all_targets += $(D_SSL_LIB_NAME)
+endif
+
+#
+# Main library target.
+#
+$(D_LIB_NAME): $(addprefix src/, $(addsuffix .d, $(d_main_modules)))
+ $(DMD) -of$(D_LIB_NAME) $(d_lib_flags) $^
+all_targets += $(D_LIB_NAME)
+
+
+#
+# Documentation target (requires Dil).
+#
+docs: $(d_publicsources) src/thrift/index.d
+ dil ddoc docs -hl --kandil $^
+
+
+#
+# Hook custom library targets into the automake all/install targets.
+#
+all-local: $(all_targets)
+
+install-exec-local:
+ $(INSTALL_PROGRAM) $(all_targets) $(DESTDIR)$(libdir)
+
+clean-local:
+ $(RM) -r docs
+ $(RM) $(D_LIB_NAME)
+ $(RM) $(D_EVENT_LIB_NAME)
+ $(RM) $(D_SSL_LIB_NAME)
+ $(RM) -r test/gen-d
+ $(RM) -r unittest
+
+
+#
+# Unit tests (built both in debug and release mode).
+#
+d_test_flags = -unittest -w -wi -I$(top_srcdir)/lib/d/src
+
+# There just must be some way to reassign a variable without warnings in
+# Automake...
+d_test_modules__ = $(d_modules)
+
+if WITH_D_EVENT_TESTS
+d_test_flags += $(DMD_LIBEVENT_FLAGS)
+d_test_modules_ = $(d_test_modules__)
+else
+d_test_modules_ = $(filter-out $(d_libevent_dependent_modules), $(d_test_modules__))
+endif
+
+if WITH_D_SSL_TESTS
+d_test_flags += $(DMD_OPENSSL_FLAGS)
+d_test_modules = $(d_test_modules_)
+else
+d_test_modules = $(filter-out $(d_openssl_dependent_modules), $(d_test_modules_))
+endif
+
+unittest/emptymain.d: unittest/.directory
+ @echo 'void main(){}' >$@
+
+unittest/.directory:
+ mkdir -p unittest || exists unittest
+ touch $@
+
+unittest/debug/%: src/%.d $(all_targets) unittest/emptymain.d
+ $(DMD) -g -of$(subst /,$(DMD_OF_DIRSEP),$@) $(d_test_flags) $^
+
+unittest/release/%: src/%.d $(all_targets) unittest/emptymain.d
+ $(DMD) -O -release -of$(subst /,$(DMD_OF_DIRSEP),$@) $(d_test_flags) $^
+
+TESTS = $(addprefix unittest/debug/, $(d_test_modules)) \
+ $(addprefix unittest/release/, $(d_test_modules))
+
+precross: all-local
+ $(MAKE) -C test precross
+
+EXTRA_DIST = \
+ src \
+ test \
+ README.md
diff --git a/src/jaegertracing/thrift/lib/d/README.md b/src/jaegertracing/thrift/lib/d/README.md
new file mode 100644
index 000000000..9b188abf5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/README.md
@@ -0,0 +1,49 @@
+Thrift D Software Library
+=========================
+
+License
+-------
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+Testing
+-------
+
+D support in Thrift is covered by two sets of tests: first,
+the unit test blocks contained in the D source files, and
+second, the more extensive testing applications in the test/
+subdirectory, which also make use of the Thrift compiler.
+Both are built when running "make check", but only the
+unit tests are immediately run, however – the separate test
+cases typically run longer or require manual intervention.
+It might also be prudent to run the independent tests,
+which typically consist of a server and a client part,
+against the other language implementations.
+
+To build the unit tests on Windows, the easiest way might
+be to manually create a file containing an empty main() and
+invoke the compiler by running the following in the src/
+directory (PowerShell syntax):
+
+dmd -ofunittest -unittest -w $(dir -r -filter '*.d' -name)
+
+Async and SSL
+-------------
+Using SSL with async is experimental (always has been) and
+the unit test "async_test --ssl" hangs. Use at your own
+risk.
diff --git a/src/jaegertracing/thrift/lib/d/coding_standards.md b/src/jaegertracing/thrift/lib/d/coding_standards.md
new file mode 100644
index 000000000..fa0390bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/coding_standards.md
@@ -0,0 +1 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/async/base.d b/src/jaegertracing/thrift/lib/d/src/thrift/async/base.d
new file mode 100644
index 000000000..8debc3be0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/async/base.d
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Defines the interface used for client-side handling of asynchronous
+ * I/O operations, based on coroutines.
+ *
+ * The main piece of the »client side« (e.g. for TAsyncClient users) of the
+ * API is TFuture, which represents an asynchronously executed operation,
+ * which can have a return value, throw exceptions, and which can be waited
+ * upon.
+ *
+ * On the »implementation side«, the idea is that by using a TAsyncTransport
+ * instead of a normal TTransport and executing the work through a
+ * TAsyncManager, the same code as for synchronous I/O can be used for
+ * asynchronous operation as well, for example:
+ *
+ * ---
+ * auto socket = new TAsyncSocket(someTAsyncSocketManager(), host, port);
+ * // …
+ * socket.asyncManager.execute(socket, {
+ * SomeThriftStruct s;
+ *
+ * // Waiting for socket I/O will not block an entire thread but cause
+ * // the async manager to execute another task in the meantime, because
+ * // we are using TAsyncSocket instead of TSocket.
+ * s.read(socket);
+ *
+ * // Do something with s, e.g. set a TPromise result to it.
+ * writeln(s);
+ * });
+ * ---
+ */
+module thrift.async.base;
+
+import core.time : Duration, dur;
+import std.socket/+ : Socket+/; // DMD @@BUG314@@
+import thrift.base;
+import thrift.transport.base;
+import thrift.util.cancellation;
+
+/**
+ * Manages one or more asynchronous transport resources (e.g. sockets in the
+ * case of TAsyncSocketManager) and allows work items to be submitted for them.
+ *
+ * Implementations will typically run one or more background threads for
+ * executing the work, which is one of the reasons for a TAsyncManager to be
+ * used. Each work item is run in its own fiber and is expected to yield() away
+ * while waiting for time-consuming operations.
+ *
+ * The second important purpose of TAsyncManager is to serialize access to
+ * the transport resources – without taking care of that, e.g. issuing multiple
+ * RPC calls over the same connection in rapid succession would likely lead to
+ * more than one request being written at the same time, causing only garbage
+ * to arrive at the remote end.
+ *
+ * All methods are thread-safe.
+ */
+interface TAsyncManager {
+ /**
+ * Submits a work item to be executed asynchronously.
+ *
+ * Access to asnyc transports is serialized – if two work items associated
+ * with the same transport are submitted, the second delegate will not be
+ * invoked until the first has returned, even it the latter context-switches
+ * away (because it is waiting for I/O) and the async manager is idle
+ * otherwise.
+ *
+ * Optionally, a TCancellation instance can be specified. If present,
+ * triggering it will be considered a request to cancel the work item, if it
+ * is still waiting for the associated transport to become available.
+ * Delegates which are already being processed (i.e. waiting for I/O) are not
+ * affected because this would bring the connection into an undefined state
+ * (as probably half-written request or a half-read response would be left
+ * behind).
+ *
+ * Params:
+ * transport = The TAsyncTransport the work delegate will operate on. Must
+ * be associated with this TAsyncManager instance.
+ * work = The operations to execute on the given transport. Must never
+ * throw, errors should be handled in another way. nothrow semantics are
+ * difficult to enforce in combination with fibres though, so currently
+ * exceptions are just swallowed by TAsyncManager implementations.
+ * cancellation = If set, can be used to request cancellatinon of this work
+ * item if it is still waiting to be executed.
+ *
+ * Note: The work item will likely be executed in a different thread, so make
+ * sure the code it relies on is thread-safe. An exception are the async
+ * transports themselves, to which access is serialized as noted above.
+ */
+ void execute(TAsyncTransport transport, void delegate() work,
+ TCancellation cancellation = null
+ ) in {
+ assert(transport.asyncManager is this,
+ "The given transport must be associated with this TAsyncManager.");
+ }
+
+ /**
+ * Submits a delegate to be executed after a certain amount of time has
+ * passed.
+ *
+ * The actual amount of time elapsed can be higher if the async manager
+ * instance is busy and thus should not be relied on. The
+ *
+ * Params:
+ * duration = The amount of time to wait before starting to execute the
+ * work delegate.
+ * work = The code to execute after the specified amount of time has passed.
+ *
+ * Example:
+ * ---
+ * // A very basic example – usually, the actuall work item would enqueue
+ * // some async transport operation.
+ * auto asyncMangager = someAsyncManager();
+ *
+ * TFuture!int calculate() {
+ * // Create a promise and asynchronously set its value after three
+ * // seconds have passed.
+ * auto promise = new TPromise!int;
+ * asyncManager.delay(dur!"seconds"(3), {
+ * promise.succeed(42);
+ * });
+ *
+ * // Immediately return it to the caller.
+ * return promise;
+ * }
+ *
+ * // This will wait until the result is available and then print it.
+ * writeln(calculate().waitGet());
+ * ---
+ */
+ void delay(Duration duration, void delegate() work);
+
+ /**
+ * Shuts down all background threads or other facilities that might have
+ * been started in order to execute work items. This function is typically
+ * called during program shutdown.
+ *
+ * If there are still tasks to be executed when the timeout expires, any
+ * currently executed work items will never receive any notifications
+ * for async transports managed by this instance, queued work items will
+ * be silently dropped, and implementations are allowed to leak resources.
+ *
+ * Params:
+ * waitFinishTimeout = If positive, waits for all work items to be
+ * finished for the specified amount of time, if negative, waits for
+ * completion without ever timing out, if zero, immediately shuts down
+ * the background facilities.
+ */
+ bool stop(Duration waitFinishTimeout = dur!"hnsecs"(-1));
+}
+
+/**
+ * A TTransport which uses a TAsyncManager to schedule non-blocking operations.
+ *
+ * The actual type of device is not specified; typically, implementations will
+ * depend on an interface derived from TAsyncManager to be notified of changes
+ * in the transport state.
+ *
+ * The peeking, reading, writing and flushing methods must always be called
+ * from within the associated async manager.
+ */
+interface TAsyncTransport : TTransport {
+ /**
+ * The TAsyncManager associated with this transport.
+ */
+ TAsyncManager asyncManager() @property;
+}
+
+/**
+ * A TAsyncManager providing notificiations for socket events.
+ */
+interface TAsyncSocketManager : TAsyncManager {
+ /**
+ * Adds a listener that is triggered once when an event of the specified type
+ * occurs, and removed afterwards.
+ *
+ * Params:
+ * socket = The socket to listen for events at.
+ * eventType = The type of the event to listen for.
+ * timeout = The period of time after which the listener will be called
+ * with TAsyncEventReason.TIMED_OUT if no event happened.
+ * listener = The delegate to call when an event happened.
+ */
+ void addOneshotListener(Socket socket, TAsyncEventType eventType,
+ Duration timeout, TSocketEventListener listener);
+
+ /// Ditto
+ void addOneshotListener(Socket socket, TAsyncEventType eventType,
+ TSocketEventListener listener);
+}
+
+/**
+ * Types of events that can happen for an asynchronous transport.
+ */
+enum TAsyncEventType {
+ READ, /// New data became available to read.
+ WRITE /// The transport became ready to be written to.
+}
+
+/**
+ * The type of the delegates used to register socket event handlers.
+ */
+alias void delegate(TAsyncEventReason callReason) TSocketEventListener;
+
+/**
+ * The reason a listener was called.
+ */
+enum TAsyncEventReason : byte {
+ NORMAL, /// The event listened for was triggered normally.
+ TIMED_OUT /// A timeout for the event was set, and it expired.
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/async/libevent.d b/src/jaegertracing/thrift/lib/d/src/thrift/async/libevent.d
new file mode 100644
index 000000000..812e4a765
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/async/libevent.d
@@ -0,0 +1,461 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.async.libevent;
+
+import core.atomic;
+import core.time : Duration, dur;
+import core.exception : onOutOfMemoryError;
+import core.memory : GC;
+import core.thread : Fiber, Thread;
+import core.sync.condition;
+import core.sync.mutex;
+import core.stdc.stdlib : free, malloc;
+import deimos.event2.event;
+import std.array : empty, front, popFront;
+import std.conv : text, to;
+import std.exception : enforce;
+import std.socket : Socket, socketPair;
+import thrift.base;
+import thrift.async.base;
+import thrift.internal.socket;
+import thrift.internal.traits;
+import thrift.util.cancellation;
+
+// To avoid DMD @@BUG6395@@.
+import thrift.internal.algorithm;
+
+/**
+ * A TAsyncManager implementation based on libevent.
+ *
+ * The libevent loop for handling non-blocking sockets is run in a background
+ * thread, which is lazily spawned. The thread is not daemonized to avoid
+ * crashes on program shutdown, it is only stopped when the manager instance
+ * is destroyed. So, to ensure a clean program teardown, either make sure this
+ * instance gets destroyed (e.g. by using scope), or manually call stop() at
+ * the end.
+ */
+class TLibeventAsyncManager : TAsyncSocketManager {
+ this() {
+ eventBase_ = event_base_new();
+
+ // Set up the socket pair for transferring control messages to the event
+ // loop.
+ auto pair = socketPair();
+ controlSendSocket_ = pair[0];
+ controlReceiveSocket_ = pair[1];
+ controlReceiveSocket_.blocking = false;
+
+ // Register an event for receiving control messages.
+ controlReceiveEvent_ = event_new(eventBase_, controlReceiveSocket_.handle,
+ EV_READ | EV_PERSIST | EV_ET, assumeNothrow(&controlMsgReceiveCallback),
+ cast(void*)this);
+ event_add(controlReceiveEvent_, null);
+
+ queuedCountMutex_ = new Mutex;
+ zeroQueuedCondition_ = new Condition(queuedCountMutex_);
+ }
+
+ ~this() {
+ // stop() should be safe to call, because either we don't have a worker
+ // thread running and it is a no-op anyway, or it is guaranteed to be
+ // still running (blocked in event_base_loop), and thus guaranteed not to
+ // be garbage collected yet.
+ stop(dur!"hnsecs"(0));
+
+ event_free(controlReceiveEvent_);
+ event_base_free(eventBase_);
+ eventBase_ = null;
+ }
+
+ override void execute(TAsyncTransport transport, Work work,
+ TCancellation cancellation = null
+ ) {
+ if (cancellation && cancellation.triggered) return;
+
+ // Keep track that there is a new work item to be processed.
+ incrementQueuedCount();
+
+ ensureWorkerThreadRunning();
+
+ // We should be able to send the control message as a whole – we currently
+ // assume to be able to receive it at once as well. If this proves to be
+ // unstable (e.g. send could possibly return early if the receiving buffer
+ // is full and the blocking call gets interrupted by a signal), it could
+ // be changed to a more sophisticated scheme.
+
+ // Make sure the delegate context doesn't get GCd while the work item is
+ // on the wire.
+ GC.addRoot(work.ptr);
+
+ // Send work message.
+ sendControlMsg(ControlMsg(MsgType.WORK, work, transport));
+
+ if (cancellation) {
+ cancellation.triggering.addCallback({
+ sendControlMsg(ControlMsg(MsgType.CANCEL, work, transport));
+ });
+ }
+ }
+
+ override void delay(Duration duration, void delegate() work) {
+ incrementQueuedCount();
+
+ ensureWorkerThreadRunning();
+
+ const tv = toTimeval(duration);
+
+ // DMD @@BUG@@: Cannot deduce T to void delegate() here.
+ registerOneshotEvent!(void delegate())(
+ -1, 0, assumeNothrow(&delayCallback), &tv,
+ {
+ work();
+ decrementQueuedCount();
+ }
+ );
+ }
+
+ override bool stop(Duration waitFinishTimeout = dur!"hnsecs"(-1)) {
+ bool cleanExit = true;
+
+ synchronized (this) {
+ if (workerThread_) {
+ synchronized (queuedCountMutex_) {
+ if (waitFinishTimeout > dur!"hnsecs"(0)) {
+ if (queuedCount_ > 0) {
+ zeroQueuedCondition_.wait(waitFinishTimeout);
+ }
+ } else if (waitFinishTimeout < dur!"hnsecs"(0)) {
+ while (queuedCount_ > 0) zeroQueuedCondition_.wait();
+ } else {
+ // waitFinishTimeout is zero, immediately exit in all cases.
+ }
+ cleanExit = (queuedCount_ == 0);
+ }
+
+ event_base_loopbreak(eventBase_);
+ sendControlMsg(ControlMsg(MsgType.SHUTDOWN));
+ workerThread_.join();
+ workQueues_ = null;
+ // We have nuked all currently enqueued items, so set the count to
+ // zero. This is safe to do without locking, since the worker thread
+ // is down.
+ queuedCount_ = 0;
+ atomicStore(*(cast(shared)&workerThread_), cast(shared(Thread))null);
+ }
+ }
+
+ return cleanExit;
+ }
+
+ override void addOneshotListener(Socket socket, TAsyncEventType eventType,
+ TSocketEventListener listener
+ ) {
+ addOneshotListenerImpl(socket, eventType, null, listener);
+ }
+
+ override void addOneshotListener(Socket socket, TAsyncEventType eventType,
+ Duration timeout, TSocketEventListener listener
+ ) {
+ if (timeout <= dur!"hnsecs"(0)) {
+ addOneshotListenerImpl(socket, eventType, null, listener);
+ } else {
+ // This is not really documented well, but libevent does not require to
+ // keep the timeval around after the event was added.
+ auto tv = toTimeval(timeout);
+ addOneshotListenerImpl(socket, eventType, &tv, listener);
+ }
+ }
+
+private:
+ alias void delegate() Work;
+
+ void addOneshotListenerImpl(Socket socket, TAsyncEventType eventType,
+ const(timeval)* timeout, TSocketEventListener listener
+ ) {
+ registerOneshotEvent(socket.handle, libeventEventType(eventType),
+ assumeNothrow(&socketCallback), timeout, listener);
+ }
+
+ void registerOneshotEvent(T)(evutil_socket_t fd, short type,
+ event_callback_fn callback, const(timeval)* timeout, T payload
+ ) {
+ // Create a copy of the payload on the C heap.
+ auto payloadMem = malloc(payload.sizeof);
+ if (!payloadMem) onOutOfMemoryError();
+ (cast(T*)payloadMem)[0 .. 1] = payload;
+ GC.addRange(payloadMem, payload.sizeof);
+
+ auto result = event_base_once(eventBase_, fd, type, callback,
+ payloadMem, timeout);
+
+ // Assuming that we didn't get our arguments wrong above, the only other
+ // situation in which event_base_once can fail is when it can't allocate
+ // memory.
+ if (result != 0) onOutOfMemoryError();
+ }
+
+ enum MsgType : ubyte {
+ SHUTDOWN,
+ WORK,
+ CANCEL
+ }
+
+ struct ControlMsg {
+ MsgType type;
+ Work work;
+ TAsyncTransport transport;
+ }
+
+ /**
+ * Starts the worker thread if it is not already running.
+ */
+ void ensureWorkerThreadRunning() {
+ // Technically, only half barriers would be required here, but adding the
+ // argument seems to trigger a DMD template argument deduction @@BUG@@.
+ if (!atomicLoad(*(cast(shared)&workerThread_))) {
+ synchronized (this) {
+ if (!workerThread_) {
+ auto thread = new Thread({ event_base_loop(eventBase_, 0); });
+ thread.start();
+ atomicStore(*(cast(shared)&workerThread_), cast(shared)thread);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sends a control message to the worker thread.
+ */
+ void sendControlMsg(const(ControlMsg) msg) {
+ auto result = controlSendSocket_.send((&msg)[0 .. 1]);
+ enum size = msg.sizeof;
+ enforce(result == size, new TException(text(
+ "Sending control message of type ", msg.type, " failed (", result,
+ " bytes instead of ", size, " transmitted).")));
+ }
+
+ /**
+ * Receives messages from the control message socket and acts on them. Called
+ * from the worker thread.
+ */
+ void receiveControlMsg() {
+ // Read as many new work items off the socket as possible (at least one
+ // should be available, as we got notified by libevent).
+ ControlMsg msg;
+ ptrdiff_t bytesRead;
+ while (true) {
+ bytesRead = controlReceiveSocket_.receive(cast(ubyte[])((&msg)[0 .. 1]));
+
+ if (bytesRead < 0) {
+ auto errno = getSocketErrno();
+ if (errno != WOULD_BLOCK_ERRNO) {
+ logError("Reading control message, some work item will possibly " ~
+ "never be executed: %s", socketErrnoString(errno));
+ }
+ }
+ if (bytesRead != msg.sizeof) break;
+
+ // Everything went fine, we received a new control message.
+ final switch (msg.type) {
+ case MsgType.SHUTDOWN:
+ // The message was just intended to wake us up for shutdown.
+ break;
+
+ case MsgType.CANCEL:
+ // When processing a cancellation, we must not touch the first item,
+ // since it is already being processed.
+ auto queue = workQueues_[msg.transport];
+ if (queue.length > 0) {
+ workQueues_[msg.transport] = [queue[0]] ~
+ removeEqual(queue[1 .. $], msg.work);
+ }
+ break;
+
+ case MsgType.WORK:
+ // Now that the work item is back in the D world, we don't need the
+ // extra GC root for the context pointer anymore (see execute()).
+ GC.removeRoot(msg.work.ptr);
+
+ // Add the work item to the queue and execute it.
+ auto queue = msg.transport in workQueues_;
+ if (queue is null || (*queue).empty) {
+ // If the queue is empty, add the new work item to the queue as well,
+ // but immediately start executing it.
+ workQueues_[msg.transport] = [msg.work];
+ executeWork(msg.transport, msg.work);
+ } else {
+ (*queue) ~= msg.work;
+ }
+ break;
+ }
+ }
+
+ // If the last read was successful, but didn't read enough bytes, we got
+ // a problem.
+ if (bytesRead > 0) {
+ logError("Unexpected partial control message read (%s byte(s) " ~
+ "instead of %s), some work item will possibly never be executed.",
+ bytesRead, msg.sizeof);
+ }
+ }
+
+ /**
+ * Executes the given work item and all others enqueued for the same
+ * transport in a new fiber. Called from the worker thread.
+ */
+ void executeWork(TAsyncTransport transport, Work work) {
+ (new Fiber({
+ auto item = work;
+ while (true) {
+ try {
+ // Execute the actual work. It will possibly add listeners to the
+ // event loop and yield away if it has to wait for blocking
+ // operations. It is quite possible that another fiber will modify
+ // the work queue for the current transport.
+ item();
+ } catch (Exception e) {
+ // This should never happen, just to be sure the worker thread
+ // doesn't stop working in mysterious ways because of an unhandled
+ // exception.
+ logError("Exception thrown by work item: %s", e);
+ }
+
+ // Remove the item from the work queue.
+ // Note: Due to the value semantics of array slices, we have to
+ // re-lookup this on every iteration. This could be solved, but I'd
+ // rather replace this directly with a queue type once one becomes
+ // available in Phobos.
+ auto queue = workQueues_[transport];
+ assert(queue.front == item);
+ queue.popFront();
+ workQueues_[transport] = queue;
+
+ // Now that the work item is done, no longer count it as queued.
+ decrementQueuedCount();
+
+ if (queue.empty) break;
+
+ // If the queue is not empty, execute the next waiting item.
+ item = queue.front;
+ }
+ })).call();
+ }
+
+ /**
+ * Increments the amount of queued items.
+ */
+ void incrementQueuedCount() {
+ synchronized (queuedCountMutex_) {
+ ++queuedCount_;
+ }
+ }
+
+ /**
+ * Decrements the amount of queued items.
+ */
+ void decrementQueuedCount() {
+ synchronized (queuedCountMutex_) {
+ assert(queuedCount_ > 0);
+ --queuedCount_;
+ if (queuedCount_ == 0) {
+ zeroQueuedCondition_.notifyAll();
+ }
+ }
+ }
+
+ static extern(C) void controlMsgReceiveCallback(evutil_socket_t, short,
+ void *managerThis
+ ) {
+ (cast(TLibeventAsyncManager)managerThis).receiveControlMsg();
+ }
+
+ static extern(C) void socketCallback(evutil_socket_t, short flags,
+ void *arg
+ ) {
+ auto reason = (flags & EV_TIMEOUT) ? TAsyncEventReason.TIMED_OUT :
+ TAsyncEventReason.NORMAL;
+ (*(cast(TSocketEventListener*)arg))(reason);
+ GC.removeRange(arg);
+ destroy(arg);
+ free(arg);
+ }
+
+ static extern(C) void delayCallback(evutil_socket_t, short flags,
+ void *arg
+ ) {
+ assert(flags & EV_TIMEOUT);
+ (*(cast(void delegate()*)arg))();
+ GC.removeRange(arg);
+ destroy(arg);
+ free(arg);
+ }
+
+ Thread workerThread_;
+
+ event_base* eventBase_;
+
+ /// The socket used for receiving new work items in the event loop. Paired
+ /// with controlSendSocket_. Invalid (i.e. TAsyncWorkItem.init) items are
+ /// ignored and can be used to wake up the worker thread.
+ Socket controlReceiveSocket_;
+ event* controlReceiveEvent_;
+
+ /// The socket used to send new work items to the event loop. It is
+ /// expected that work items can always be read at once from it, i.e. that
+ /// there will never be short reads.
+ Socket controlSendSocket_;
+
+ /// Queued up work delegates for async transports. This also includes
+ /// currently active ones, they are removed from the queue on completion,
+ /// which is relied on by the control message receive fiber (the main one)
+ /// to decide whether to immediately start executing items or not.
+ // TODO: This should really be of some queue type, not an array slice, but
+ // std.container doesn't have anything.
+ Work[][TAsyncTransport] workQueues_;
+
+ /// The total number of work items not yet finished (queued and currently
+ /// executed) and delays not yet executed.
+ uint queuedCount_;
+
+ /// Protects queuedCount_.
+ Mutex queuedCountMutex_;
+
+ /// Triggered when queuedCount_ reaches zero, protected by queuedCountMutex_.
+ Condition zeroQueuedCondition_;
+}
+
+private {
+ timeval toTimeval(const(Duration) dur) {
+ timeval tv;
+ dur.split!("seconds", "usecs")(tv.tv_sec, tv.tv_usec);
+ return tv;
+ }
+
+ /**
+ * Returns the libevent flags combination to represent a given TAsyncEventType.
+ */
+ short libeventEventType(TAsyncEventType type) {
+ final switch (type) {
+ case TAsyncEventType.READ:
+ return EV_READ | EV_ET;
+ case TAsyncEventType.WRITE:
+ return EV_WRITE | EV_ET;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/async/socket.d b/src/jaegertracing/thrift/lib/d/src/thrift/async/socket.d
new file mode 100644
index 000000000..a08f51db0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/async/socket.d
@@ -0,0 +1,358 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.async.socket;
+
+import core.stdc.errno: ECONNRESET;
+import core.thread : Fiber;
+import core.time : dur, Duration;
+import std.array : empty;
+import std.conv : to;
+import std.exception : enforce;
+import std.socket;
+import thrift.base;
+import thrift.async.base;
+import thrift.transport.base;
+import thrift.transport.socket : TSocketBase;
+import thrift.internal.endian;
+import thrift.internal.socket;
+
+version (Windows) {
+ import std.c.windows.winsock : connect;
+} else version (Posix) {
+ import core.sys.posix.sys.socket : connect;
+} else static assert(0, "Don't know connect on this platform.");
+
+/**
+ * Non-blocking socket implementation of the TTransport interface.
+ *
+ * Whenever a socket operation would block, TAsyncSocket registers a callback
+ * with the specified TAsyncSocketManager and yields.
+ *
+ * As for thrift.transport.socket, due to the limitations of std.socket,
+ * currently only TCP/IP sockets are supported (i.e. Unix domain sockets are
+ * not).
+ */
+class TAsyncSocket : TSocketBase, TAsyncTransport {
+ /**
+ * Constructor that takes an already created, connected (!) socket.
+ *
+ * Params:
+ * asyncManager = The TAsyncSocketManager to use for non-blocking I/O.
+ * socket = Already created, connected socket object. Will be switched to
+ * non-blocking mode if it isn't already.
+ */
+ this(TAsyncSocketManager asyncManager, Socket socket) {
+ asyncManager_ = asyncManager;
+ socket.blocking = false;
+ super(socket);
+ }
+
+ /**
+ * Creates a new unconnected socket that will connect to the given host
+ * on the given port.
+ *
+ * Params:
+ * asyncManager = The TAsyncSocketManager to use for non-blocking I/O.
+ * host = Remote host.
+ * port = Remote port.
+ */
+ this(TAsyncSocketManager asyncManager, string host, ushort port) {
+ asyncManager_ = asyncManager;
+ super(host, port);
+ }
+
+ override TAsyncManager asyncManager() @property {
+ return asyncManager_;
+ }
+
+ /**
+ * Asynchronously connects the socket.
+ *
+ * Completes without blocking and defers further operations on the socket
+ * until the connection is established. If connecting fails, this is
+ * currently not indicated in any way other than every call to read/write
+ * failing.
+ */
+ override void open() {
+ if (isOpen) return;
+
+ enforce(!host_.empty, new TTransportException(
+ "Cannot open null host.", TTransportException.Type.NOT_OPEN));
+ enforce(port_ != 0, new TTransportException(
+ "Cannot open with null port.", TTransportException.Type.NOT_OPEN));
+
+
+ // Cannot use std.socket.Socket.connect here because it hides away
+ // EINPROGRESS/WSAWOULDBLOCK.
+ Address addr;
+ try {
+ // Currently, we just go with the first address returned, could be made
+ // more intelligent though – IPv6?
+ addr = getAddress(host_, port_)[0];
+ } catch (Exception e) {
+ throw new TTransportException(`Unable to resolve host "` ~ host_ ~ `".`,
+ TTransportException.Type.NOT_OPEN, __FILE__, __LINE__, e);
+ }
+
+ socket_ = new TcpSocket(addr.addressFamily);
+ socket_.blocking = false;
+ setSocketOpts();
+
+ auto errorCode = connect(socket_.handle, addr.name(), addr.nameLen());
+ if (errorCode == 0) {
+ // If the connection could be established immediately, just return. I
+ // don't know if this ever happens.
+ return;
+ }
+
+ auto errno = getSocketErrno();
+ if (errno != CONNECT_INPROGRESS_ERRNO) {
+ throw new TTransportException(`Could not establish connection to "` ~
+ host_ ~ `": ` ~ socketErrnoString(errno),
+ TTransportException.Type.NOT_OPEN);
+ }
+
+ // This is the expected case: connect() signalled that the connection
+ // is being established in the background. Queue up a work item with the
+ // async manager which just defers any other operations on this
+ // TAsyncSocket instance until the socket is ready.
+ asyncManager_.execute(this,
+ {
+ auto fiber = Fiber.getThis();
+ TAsyncEventReason reason = void;
+ asyncManager_.addOneshotListener(socket_, TAsyncEventType.WRITE,
+ connectTimeout,
+ scopedDelegate((TAsyncEventReason r){ reason = r; fiber.call(); })
+ );
+ Fiber.yield();
+
+ if (reason == TAsyncEventReason.TIMED_OUT) {
+ // Close the connection, so that subsequent work items fail immediately.
+ closeImmediately();
+ return;
+ }
+
+ int errorCode = void;
+ socket_.getOption(SocketOptionLevel.SOCKET, cast(SocketOption)SO_ERROR,
+ errorCode);
+
+ if (errorCode) {
+ logInfo("Could not connect TAsyncSocket: %s",
+ socketErrnoString(errorCode));
+
+ // Close the connection, so that subsequent work items fail immediately.
+ closeImmediately();
+ return;
+ }
+
+ }
+ );
+ }
+
+ /**
+ * Closes the socket.
+ *
+ * Will block until all currently active operations are finished before the
+ * socket is closed.
+ */
+ override void close() {
+ if (!isOpen) return;
+
+ import core.sync.condition;
+ import core.sync.mutex;
+
+ auto doneMutex = new Mutex;
+ auto doneCond = new Condition(doneMutex);
+ synchronized (doneMutex) {
+ asyncManager_.execute(this,
+ scopedDelegate(
+ {
+ closeImmediately();
+ synchronized (doneMutex) doneCond.notifyAll();
+ }
+ )
+ );
+ doneCond.wait();
+ }
+ }
+
+ override bool peek() {
+ if (!isOpen) return false;
+
+ ubyte buf;
+ auto r = socket_.receive((&buf)[0..1], SocketFlags.PEEK);
+ if (r == Socket.ERROR) {
+ auto lastErrno = getSocketErrno();
+ static if (connresetOnPeerShutdown) {
+ if (lastErrno == ECONNRESET) {
+ closeImmediately();
+ return false;
+ }
+ }
+ throw new TTransportException("Peeking into socket failed: " ~
+ socketErrnoString(lastErrno), TTransportException.Type.UNKNOWN);
+ }
+ return (r > 0);
+ }
+
+ override size_t read(ubyte[] buf) {
+ enforce(isOpen, new TTransportException(
+ "Cannot read if socket is not open.", TTransportException.Type.NOT_OPEN));
+
+ typeof(getSocketErrno()) lastErrno;
+
+ auto r = yieldOnBlock(socket_.receive(cast(void[])buf),
+ TAsyncEventType.READ);
+
+ // If recv went fine, immediately return.
+ if (r >= 0) return r;
+
+ // Something went wrong, find out how to handle it.
+ lastErrno = getSocketErrno();
+
+ static if (connresetOnPeerShutdown) {
+ // See top comment.
+ if (lastErrno == ECONNRESET) {
+ return 0;
+ }
+ }
+
+ throw new TTransportException("Receiving from socket failed: " ~
+ socketErrnoString(lastErrno), TTransportException.Type.UNKNOWN);
+ }
+
+ override void write(in ubyte[] buf) {
+ size_t sent;
+ while (sent < buf.length) {
+ sent += writeSome(buf[sent .. $]);
+ }
+ assert(sent == buf.length);
+ }
+
+ override size_t writeSome(in ubyte[] buf) {
+ enforce(isOpen, new TTransportException(
+ "Cannot write if socket is not open.", TTransportException.Type.NOT_OPEN));
+
+ auto r = yieldOnBlock(socket_.send(buf), TAsyncEventType.WRITE);
+
+ // Everything went well, just return the number of bytes written.
+ if (r > 0) return r;
+
+ // Handle error conditions.
+ if (r < 0) {
+ auto lastErrno = getSocketErrno();
+
+ auto type = TTransportException.Type.UNKNOWN;
+ if (isSocketCloseErrno(lastErrno)) {
+ type = TTransportException.Type.NOT_OPEN;
+ closeImmediately();
+ }
+
+ throw new TTransportException("Sending to socket failed: " ~
+ socketErrnoString(lastErrno), type);
+ }
+
+ // send() should never return 0.
+ throw new TTransportException("Sending to socket failed (0 bytes written).",
+ TTransportException.Type.UNKNOWN);
+ }
+
+ /// The amount of time in which a conncetion must be established before the
+ /// open() call times out.
+ Duration connectTimeout = dur!"seconds"(5);
+
+private:
+ void closeImmediately() {
+ socket_.close();
+ socket_ = null;
+ }
+
+ T yieldOnBlock(T)(lazy T call, TAsyncEventType eventType) {
+ while (true) {
+ auto result = call();
+ if (result != Socket.ERROR || getSocketErrno() != WOULD_BLOCK_ERRNO) return result;
+
+ // We got an EAGAIN result, register a callback to return here once some
+ // event happens and yield.
+
+ Duration timeout = void;
+ final switch (eventType) {
+ case TAsyncEventType.READ:
+ timeout = recvTimeout_;
+ break;
+ case TAsyncEventType.WRITE:
+ timeout = sendTimeout_;
+ break;
+ }
+
+ auto fiber = Fiber.getThis();
+ assert(fiber, "Current fiber null – not running in TAsyncManager?");
+ TAsyncEventReason eventReason = void;
+ asyncManager_.addOneshotListener(socket_, eventType, timeout,
+ scopedDelegate((TAsyncEventReason reason) {
+ eventReason = reason;
+ fiber.call();
+ })
+ );
+
+ // Yields execution back to the async manager, will return back here once
+ // the above listener is called.
+ Fiber.yield();
+
+ if (eventReason == TAsyncEventReason.TIMED_OUT) {
+ // If we are cancelling the request due to a timed out operation, the
+ // connection is in an undefined state, because the server could decide
+ // to send the requested data later, or we could have already been half-
+ // way into writing a request. Thus, we close the connection to make any
+ // possibly queued up work items fail immediately. Besides, the server
+ // is not very likely to immediately recover after a socket-level
+ // timeout has expired anyway.
+ closeImmediately();
+
+ throw new TTransportException("Timed out while waiting for socket " ~
+ "to get ready to " ~ to!string(eventType) ~ ".",
+ TTransportException.Type.TIMED_OUT);
+ }
+ }
+ }
+
+ /// The TAsyncSocketManager to use for non-blocking I/O.
+ TAsyncSocketManager asyncManager_;
+}
+
+private {
+ // std.socket doesn't include SO_ERROR for reasons unknown.
+ version (linux) {
+ enum SO_ERROR = 4;
+ } else version (OSX) {
+ enum SO_ERROR = 0x1007;
+ } else version (FreeBSD) {
+ enum SO_ERROR = 0x1007;
+ } else version (Win32) {
+ import std.c.windows.winsock : SO_ERROR;
+ } else static assert(false, "Don't know SO_ERROR on this platform.");
+
+ // This hack forces a delegate literal to be scoped, even if it is passed to
+ // a function accepting normal delegates as well. DMD likes to allocate the
+ // context on the heap anyway, but it seems to work for LDC.
+ import std.traits : isDelegate;
+ auto scopedDelegate(D)(scope D d) if (isDelegate!D) {
+ return d;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/async/ssl.d b/src/jaegertracing/thrift/lib/d/src/thrift/async/ssl.d
new file mode 100644
index 000000000..fe6242613
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/async/ssl.d
@@ -0,0 +1,292 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.async.ssl;
+
+import core.thread : Fiber;
+import core.time : Duration;
+import std.array : empty;
+import std.conv : to;
+import std.exception : enforce;
+import std.socket;
+import deimos.openssl.err;
+import deimos.openssl.ssl;
+import thrift.base;
+import thrift.async.base;
+import thrift.async.socket;
+import thrift.internal.ssl;
+import thrift.internal.ssl_bio;
+import thrift.transport.base;
+import thrift.transport.ssl;
+
+/**
+ * Provides SSL/TLS encryption for async sockets.
+ *
+ * This implementation should be considered experimental, as it context-switches
+ * between fibers from within OpenSSL calls, and the safety of this has not yet
+ * been verified.
+ *
+ * For obvious reasons (the SSL connection is stateful), more than one instance
+ * should never be used on a given socket at the same time.
+ */
+// Note: This could easily be extended to other transports in the future as well.
+// There are only two parts of the implementation which don't work with a generic
+// TTransport: 1) the certificate verification, for which peer name/address are
+// needed from the socket, and 2) the connection shutdown, where the associated
+// async manager is needed because close() is not usually called from within a
+// work item.
+final class TAsyncSSLSocket : TBaseTransport {
+ /**
+ * Constructor.
+ *
+ * Params:
+ * context = The SSL socket context to use. A reference to it is stored so
+ * that it does not get cleaned up while the socket is used.
+ * transport = The underlying async network transport to use for
+ * communication.
+ */
+ this(TAsyncSocket underlyingSocket, TSSLContext context) {
+ socket_ = underlyingSocket;
+ context_ = context;
+ serverSide_ = context.serverSide;
+ accessManager_ = context.accessManager;
+ }
+
+ override bool isOpen() @property {
+ if (ssl_ is null || !socket_.isOpen) return false;
+
+ auto shutdown = SSL_get_shutdown(ssl_);
+ bool shutdownReceived = (shutdown & SSL_RECEIVED_SHUTDOWN) != 0;
+ bool shutdownSent = (shutdown & SSL_SENT_SHUTDOWN) != 0;
+ return !(shutdownReceived && shutdownSent);
+ }
+
+ override bool peek() {
+ if (!isOpen) return false;
+ checkHandshake();
+
+ byte bt = void;
+ auto rc = SSL_peek(ssl_, &bt, bt.sizeof);
+ sslEnforce(rc >= 0, "SSL_peek");
+
+ if (rc == 0) {
+ ERR_clear_error();
+ }
+ return (rc > 0);
+ }
+
+ override void open() {
+ enforce(!serverSide_, "Cannot open a server-side SSL socket.");
+ if (isOpen) return;
+
+ if (ssl_) {
+ // If the underlying socket was automatically closed because of an error
+ // (i.e. close() was called from inside a socket method), we can land
+ // here with the SSL object still allocated; delete it here.
+ cleanupSSL();
+ }
+
+ socket_.open();
+ }
+
+ override void close() {
+ if (!isOpen) return;
+
+ if (ssl_ !is null) {
+ // SSL needs to send/receive data over the socket as part of the shutdown
+ // protocol, so we must execute the calls in the context of the associated
+ // async manager. On the other hand, TTransport clients expect the socket
+ // to be closed when close() returns, so we have to block until the
+ // shutdown work item has been executed.
+ import core.sync.condition;
+ import core.sync.mutex;
+
+ int rc = void;
+ auto doneMutex = new Mutex;
+ auto doneCond = new Condition(doneMutex);
+ synchronized (doneMutex) {
+ socket_.asyncManager.execute(socket_, {
+ rc = SSL_shutdown(ssl_);
+ if (rc == 0) {
+ rc = SSL_shutdown(ssl_);
+ }
+ synchronized (doneMutex) doneCond.notifyAll();
+ });
+ doneCond.wait();
+ }
+
+ if (rc < 0) {
+ // Do not throw an exception here as leaving the transport "open" will
+ // probably produce only more errors, and the chance we can do
+ // something about the error e.g. by retrying is very low.
+ logError("Error while shutting down SSL: %s", getSSLException());
+ }
+
+ cleanupSSL();
+ }
+
+ socket_.close();
+ }
+
+ override size_t read(ubyte[] buf) {
+ checkHandshake();
+ auto rc = SSL_read(ssl_, buf.ptr, cast(int)buf.length);
+ sslEnforce(rc >= 0, "SSL_read");
+ return rc;
+ }
+
+ override void write(in ubyte[] buf) {
+ checkHandshake();
+
+ // Loop in case SSL_MODE_ENABLE_PARTIAL_WRITE is set in SSL_CTX.
+ size_t written = 0;
+ while (written < buf.length) {
+ auto bytes = SSL_write(ssl_, buf.ptr + written,
+ cast(int)(buf.length - written));
+ sslEnforce(bytes > 0, "SSL_write");
+ written += bytes;
+ }
+ }
+
+ override void flush() {
+ checkHandshake();
+
+ auto bio = SSL_get_wbio(ssl_);
+ enforce(bio !is null, new TSSLException("SSL_get_wbio returned null"));
+
+ auto rc = BIO_flush(bio);
+ sslEnforce(rc == 1, "BIO_flush");
+ }
+
+ /**
+ * Whether to use client or server side SSL handshake protocol.
+ */
+ bool serverSide() @property const {
+ return serverSide_;
+ }
+
+ /// Ditto
+ void serverSide(bool value) @property {
+ serverSide_ = value;
+ }
+
+ /**
+ * The access manager to use.
+ */
+ void accessManager(TAccessManager value) @property {
+ accessManager_ = value;
+ }
+
+private:
+ /**
+ * If the condition is false, cleans up the SSL connection and throws the
+ * exception for the last SSL error.
+ */
+ void sslEnforce(bool condition, string location) {
+ if (!condition) {
+ // We need to fetch the error first, as the error stack will be cleaned
+ // when shutting down SSL.
+ auto e = getSSLException(location);
+ cleanupSSL();
+ throw e;
+ }
+ }
+
+ /**
+ * Frees the SSL connection object and clears the SSL error state.
+ */
+ void cleanupSSL() {
+ SSL_free(ssl_);
+ ssl_ = null;
+ ERR_remove_state(0);
+ }
+
+ /**
+ * Makes sure the SSL connection is up and running, and initializes it if not.
+ */
+ void checkHandshake() {
+ enforce(socket_.isOpen, new TTransportException(
+ TTransportException.Type.NOT_OPEN));
+
+ if (ssl_ !is null) return;
+ ssl_ = context_.createSSL();
+
+ auto bio = createTTransportBIO(socket_, false);
+ SSL_set_bio(ssl_, bio, bio);
+
+ int rc = void;
+ if (serverSide_) {
+ rc = SSL_accept(ssl_);
+ } else {
+ rc = SSL_connect(ssl_);
+ }
+ enforce(rc > 0, getSSLException());
+
+ auto addr = socket_.getPeerAddress();
+ authorize(ssl_, accessManager_, addr,
+ (serverSide_ ? addr.toHostNameString() : socket_.host));
+ }
+
+ TAsyncSocket socket_;
+ bool serverSide_;
+ SSL* ssl_;
+ TSSLContext context_;
+ TAccessManager accessManager_;
+}
+
+/**
+ * Wraps passed TAsyncSocket instances into TAsyncSSLSockets.
+ *
+ * Typically used with TAsyncClient. As an unfortunate consequence of the
+ * async client design, the passed transports cannot be statically verified to
+ * be of type TAsyncSocket. Instead, the type is verified at runtime – if a
+ * transport of an unexpected type is passed to getTransport(), it fails,
+ * throwing a TTransportException.
+ *
+ * Example:
+ * ---
+ * auto context = nwe TSSLContext();
+ * ... // Configure SSL context.
+ * auto factory = new TAsyncSSLSocketFactory(context);
+ *
+ * auto socket = new TAsyncSocket(someAsyncManager, host, port);
+ * socket.open();
+ *
+ * auto client = new TAsyncClient!Service(transport, factory,
+ * new TBinaryProtocolFactory!());
+ * ---
+ */
+class TAsyncSSLSocketFactory : TTransportFactory {
+ ///
+ this(TSSLContext context) {
+ context_ = context;
+ }
+
+ override TAsyncSSLSocket getTransport(TTransport transport) {
+ auto socket = cast(TAsyncSocket)transport;
+ enforce(socket, new TTransportException(
+ "TAsyncSSLSocketFactory requires a TAsyncSocket to work on, not a " ~
+ to!string(typeid(transport)) ~ ".",
+ TTransportException.Type.INTERNAL_ERROR
+ ));
+ return new TAsyncSSLSocket(socket, context_);
+ }
+
+private:
+ TSSLContext context_;
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/base.d b/src/jaegertracing/thrift/lib/d/src/thrift/base.d
new file mode 100644
index 000000000..150c3da8e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/base.d
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.base;
+
+/**
+ * Common base class for all Thrift exceptions.
+ */
+class TException : Exception {
+ ///
+ this(string msg = "", string file = __FILE__, size_t line = __LINE__,
+ Throwable next = null)
+ {
+ super(msg, file, line, next);
+ }
+}
+
+/**
+ * An operation failed because one or more sub-tasks failed.
+ */
+class TCompoundOperationException : TException {
+ ///
+ this(string msg, Exception[] exceptions, string file = __FILE__,
+ size_t line = __LINE__, Throwable next = null)
+ {
+ super(msg, file, line, next);
+ this.exceptions = exceptions;
+ }
+
+ /// The exceptions thrown by the children of the operation. If applicable,
+ /// the list is ordered in the same way the exceptions occurred.
+ Exception[] exceptions;
+}
+
+/// The Thrift version string, used for informative purposes.
+// Note: This is currently hardcoded, but will likely be filled in by the build
+// system in future versions.
+enum VERSION = "0.13.0";
+
+/**
+ * Functions used for logging inside Thrift.
+ *
+ * By default, the formatted messages are written to stdout/stderr, but this
+ * behavior can be overwritten by providing custom g_{Info, Error}LogSink
+ * handlers.
+ *
+ * Examples:
+ * ---
+ * logInfo("An informative message.");
+ * logError("Some error occurred: %s", e);
+ * ---
+ */
+alias logFormatted!g_infoLogSink logInfo;
+alias logFormatted!g_errorLogSink logError; /// Ditto
+
+/**
+ * Error and info log message sinks.
+ *
+ * These delegates are called with the log message passed as const(char)[]
+ * argument, and can be overwritten to hook the Thrift libraries up with a
+ * custom logging system. By default, they forward all output to stdout/stderr.
+ */
+__gshared void delegate(const(char)[]) g_infoLogSink;
+__gshared void delegate(const(char)[]) g_errorLogSink; /// Ditto
+
+shared static this() {
+ import std.stdio;
+
+ g_infoLogSink = (const(char)[] text) {
+ stdout.writeln(text);
+ };
+
+ g_errorLogSink = (const(char)[] text) {
+ stderr.writeln(text);
+ };
+}
+
+// This should be private, if it could still be used through the aliases then.
+template logFormatted(alias target) {
+ void logFormatted(string file = __FILE__, int line = __LINE__,
+ T...)(string fmt, T args) if (
+ __traits(compiles, { target(""); })
+ ) {
+ import std.format, std.stdio;
+ if (target !is null) {
+ scope(exit) g_formatBuffer.clear();
+
+ // Phobos @@BUG@@: If the empty string put() is removed, Appender.data
+ // stays empty.
+ g_formatBuffer.put("");
+
+ formattedWrite(g_formatBuffer, "%s:%s: ", file, line);
+
+ static if (T.length == 0) {
+ g_formatBuffer.put(fmt);
+ } else {
+ formattedWrite(g_formatBuffer, fmt, args);
+ }
+ target(g_formatBuffer.data);
+ }
+ }
+}
+
+private {
+ // Use a global, but thread-local buffer for constructing log messages.
+ import std.array : Appender;
+ Appender!(char[]) g_formatBuffer;
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/codegen/async_client.d b/src/jaegertracing/thrift/lib/d/src/thrift/codegen/async_client.d
new file mode 100644
index 000000000..e916dea15
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/codegen/async_client.d
@@ -0,0 +1,255 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.codegen.async_client;
+
+import std.conv : text, to;
+import std.traits : ParameterStorageClass, ParameterStorageClassTuple,
+ ParameterTypeTuple, ReturnType;
+import thrift.base;
+import thrift.async.base;
+import thrift.codegen.base;
+import thrift.codegen.client;
+import thrift.internal.codegen;
+import thrift.internal.ctfe;
+import thrift.protocol.base;
+import thrift.transport.base;
+import thrift.util.cancellation;
+import thrift.util.future;
+
+/**
+ * Asynchronous Thrift service client which returns the results as TFutures an
+ * uses a TAsyncManager to perform the actual work.
+ *
+ * TAsyncClientBase serves as a supertype for all TAsyncClients for the same
+ * service, which might be instantiated with different concrete protocol types
+ * (there is no covariance for template type parameters), and extends
+ * TFutureInterface!Interface. If Interface is derived from another service
+ * BaseInterface, it also extends TAsyncClientBase!BaseInterface.
+ *
+ * TAsyncClient implements TAsyncClientBase and offers two constructors with
+ * the following signatures:
+ * ---
+ * this(TAsyncTransport trans, TTransportFactory tf, TProtocolFactory pf);
+ * this(TAsyncTransport trans, TTransportFactory itf, TTransportFactory otf,
+ * TProtocolFactory ipf, TProtocolFactory opf);
+ * ---
+ *
+ * Again, if Interface represents a derived Thrift service,
+ * TAsyncClient!Interface is also derived from TAsyncClient!BaseInterface.
+ *
+ * TAsyncClient can exclusively be used with TAsyncTransports, as it needs to
+ * access the associated TAsyncManager. To set up any wrapper transports
+ * (e.g. buffered, framed) on top of it and to instanciate the protocols to use,
+ * TTransportFactory and TProtocolFactory instances are passed to the
+ * constructors – the three argument constructor is a shortcut if the same
+ * transport and protocol are to be used for both input and output, which is
+ * the most common case.
+ *
+ * If the same transport factory is passed for both input and output transports,
+ * only a single wrapper transport will be created and used for both directions.
+ * This allows easy implementation of protocols like SSL.
+ *
+ * Just as TClient does, TAsyncClient also takes two optional template
+ * arguments which can be used for specifying the actual TProtocol
+ * implementation used for optimization purposes, as virtual calls can
+ * completely be eliminated then. If the actual types of the protocols
+ * instantiated by the factories used does not match the ones statically
+ * specified in the template parameters, a TException is thrown during
+ * construction.
+ *
+ * Example:
+ * ---
+ * // A simple Thrift service.
+ * interface Foo { int foo(); }
+ *
+ * // Create a TAsyncSocketManager – thrift.async.libevent is used for this
+ * // example.
+ * auto manager = new TLibeventAsyncManager;
+ *
+ * // Set up an async transport to use.
+ * auto socket = new TAsyncSocket(manager, host, port);
+ *
+ * // Create a client instance.
+ * auto client = new TAsyncClient!Foo(
+ * socket,
+ * new TBufferedTransportFactory, // Wrap the socket in a TBufferedTransport.
+ * new TBinaryProtocolFactory!() // Use the Binary protocol.
+ * );
+ *
+ * // Call foo and use the returned future.
+ * auto result = client.foo();
+ * pragma(msg, typeof(result)); // TFuture!int
+ * int resultValue = result.waitGet(); // Waits until the result is available.
+ * ---
+ */
+interface TAsyncClientBase(Interface) if (isBaseService!Interface) :
+ TFutureInterface!Interface
+{
+ /**
+ * The underlying TAsyncTransport used by this client instance.
+ */
+ TAsyncTransport transport() @property;
+}
+
+/// Ditto
+interface TAsyncClientBase(Interface) if (isDerivedService!Interface) :
+ TAsyncClientBase!(BaseService!Interface), TFutureInterface!Interface
+{}
+
+/// Ditto
+template TAsyncClient(Interface, InputProtocol = TProtocol, OutputProtocol = void) if (
+ isService!Interface && isTProtocol!InputProtocol &&
+ (isTProtocol!OutputProtocol || is(OutputProtocol == void))
+) {
+ mixin({
+ static if (isDerivedService!Interface) {
+ string code = "class TAsyncClient : " ~
+ "TAsyncClient!(BaseService!Interface, InputProtocol, OutputProtocol), " ~
+ "TAsyncClientBase!Interface {\n";
+ code ~= q{
+ this(TAsyncTransport trans, TTransportFactory tf, TProtocolFactory pf) {
+ this(trans, tf, tf, pf, pf);
+ }
+
+ this(TAsyncTransport trans, TTransportFactory itf,
+ TTransportFactory otf, TProtocolFactory ipf, TProtocolFactory opf
+ ) {
+ super(trans, itf, otf, ipf, opf);
+ client_ = new typeof(client_)(iprot_, oprot_);
+ }
+
+ private TClient!(Interface, IProt, OProt) client_;
+ };
+ } else {
+ string code = "class TAsyncClient : TAsyncClientBase!Interface {";
+ code ~= q{
+ alias InputProtocol IProt;
+ static if (isTProtocol!OutputProtocol) {
+ alias OutputProtocol OProt;
+ } else {
+ static assert(is(OutputProtocol == void));
+ alias InputProtocol OProt;
+ }
+
+ this(TAsyncTransport trans, TTransportFactory tf, TProtocolFactory pf) {
+ this(trans, tf, tf, pf, pf);
+ }
+
+ this(TAsyncTransport trans, TTransportFactory itf,
+ TTransportFactory otf, TProtocolFactory ipf, TProtocolFactory opf
+ ) {
+ import std.exception;
+ transport_ = trans;
+
+ auto ip = itf.getTransport(trans);
+ TTransport op = void;
+ if (itf == otf) {
+ op = ip;
+ } else {
+ op = otf.getTransport(trans);
+ }
+
+ auto iprot = ipf.getProtocol(ip);
+ iprot_ = cast(IProt)iprot;
+ enforce(iprot_, new TException(text("Input protocol not of the " ~
+ "specified concrete type (", IProt.stringof, ").")));
+
+ auto oprot = opf.getProtocol(op);
+ oprot_ = cast(OProt)oprot;
+ enforce(oprot_, new TException(text("Output protocol not of the " ~
+ "specified concrete type (", OProt.stringof, ").")));
+
+ client_ = new typeof(client_)(iprot_, oprot_);
+ }
+
+ override TAsyncTransport transport() @property {
+ return transport_;
+ }
+
+ protected TAsyncTransport transport_;
+ protected IProt iprot_;
+ protected OProt oprot_;
+ private TClient!(Interface, IProt, OProt) client_;
+ };
+ }
+
+ foreach (methodName;
+ FilterMethodNames!(Interface, __traits(derivedMembers, Interface))
+ ) {
+ string[] paramList;
+ string[] paramNames;
+ foreach (i, _; ParameterTypeTuple!(mixin("Interface." ~ methodName))) {
+ immutable paramName = "param" ~ to!string(i + 1);
+ immutable storage = ParameterStorageClassTuple!(
+ mixin("Interface." ~ methodName))[i];
+
+ paramList ~= ((storage & ParameterStorageClass.ref_) ? "ref " : "") ~
+ "ParameterTypeTuple!(Interface." ~ methodName ~ ")[" ~
+ to!string(i) ~ "] " ~ paramName;
+ paramNames ~= paramName;
+ }
+ paramList ~= "TCancellation cancellation = null";
+
+ immutable returnTypeCode = "ReturnType!(Interface." ~ methodName ~ ")";
+ code ~= "TFuture!(" ~ returnTypeCode ~ ") " ~ methodName ~ "(" ~
+ ctfeJoin(paramList) ~ ") {\n";
+
+ // Create the future instance that will repesent the result.
+ code ~= "auto promise = new TPromise!(" ~ returnTypeCode ~ ");\n";
+
+ // Prepare delegate which executes the TClient method call.
+ code ~= "auto work = {\n";
+ code ~= "try {\n";
+ code ~= "static if (is(ReturnType!(Interface." ~ methodName ~
+ ") == void)) {\n";
+ code ~= "client_." ~ methodName ~ "(" ~ ctfeJoin(paramNames) ~ ");\n";
+ code ~= "promise.succeed();\n";
+ code ~= "} else {\n";
+ code ~= "auto result = client_." ~ methodName ~ "(" ~
+ ctfeJoin(paramNames) ~ ");\n";
+ code ~= "promise.succeed(result);\n";
+ code ~= "}\n";
+ code ~= "} catch (Exception e) {\n";
+ code ~= "promise.fail(e);\n";
+ code ~= "}\n";
+ code ~= "};\n";
+
+ // If the request is cancelled, set the result promise to cancelled
+ // as well. This could be moved into an additional TAsyncWorkItem
+ // delegate parameter.
+ code ~= q{
+ if (cancellation) {
+ cancellation.triggering.addCallback({
+ promise.cancel();
+ });
+ }
+ };
+
+ // Enqueue the work item and immediately return the promise (resp. its
+ // future interface).
+ code ~= "transport_.asyncManager.execute(transport_, work, cancellation);\n";
+ code ~= "return promise;\n";
+ code ~= "}\n";
+
+ }
+
+ code ~= "}\n";
+ return code;
+ }());
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/codegen/async_client_pool.d b/src/jaegertracing/thrift/lib/d/src/thrift/codegen/async_client_pool.d
new file mode 100644
index 000000000..26cb975a3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/codegen/async_client_pool.d
@@ -0,0 +1,906 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Utilities for asynchronously querying multiple servers, building on
+ * TAsyncClient.
+ *
+ * Terminology note: The names of the artifacts defined in this module are
+ * derived from »client pool«, because they operate on a pool of
+ * TAsyncClients. However, from a architectural point of view, they often
+ * represent a pool of hosts a Thrift client application communicates with
+ * using RPC calls.
+ */
+module thrift.codegen.async_client_pool;
+
+import core.sync.mutex;
+import core.time : Duration, dur;
+import std.algorithm : map;
+import std.array : array, empty;
+import std.exception : enforce;
+import std.traits : ParameterTypeTuple, ReturnType;
+import thrift.base;
+import thrift.codegen.base;
+import thrift.codegen.async_client;
+import thrift.internal.algorithm;
+import thrift.internal.codegen;
+import thrift.util.awaitable;
+import thrift.util.cancellation;
+import thrift.util.future;
+import thrift.internal.resource_pool;
+
+/**
+ * Represents a generic client pool which implements TFutureInterface!Interface
+ * using multiple TAsyncClients.
+ */
+interface TAsyncClientPoolBase(Interface) if (isService!Interface) :
+ TFutureInterface!Interface
+{
+ /// Shorthand for the client type this pool operates on.
+ alias TAsyncClientBase!Interface Client;
+
+ /**
+ * Adds a client to the pool.
+ */
+ void addClient(Client client);
+
+ /**
+ * Removes a client from the pool.
+ *
+ * Returns: Whether the client was found in the pool.
+ */
+ bool removeClient(Client client);
+
+ /**
+ * Called to determine whether an exception comes from a client from the
+ * pool not working properly, or if it an exception thrown at the
+ * application level.
+ *
+ * If the delegate returns true, the server/connection is considered to be
+ * at fault, if it returns false, the exception is just passed on to the
+ * caller.
+ *
+ * By default, returns true for instances of TTransportException and
+ * TApplicationException, false otherwise.
+ */
+ bool delegate(Exception) rpcFaultFilter() const @property;
+ void rpcFaultFilter(bool delegate(Exception)) @property; /// Ditto
+
+ /**
+ * Whether to open the underlying transports of a client before trying to
+ * execute a method if they are not open. This is usually desirable
+ * because it allows e.g. to automatically reconnect to a remote server
+ * if the network connection is dropped.
+ *
+ * Defaults to true.
+ */
+ bool reopenTransports() const @property;
+ void reopenTransports(bool) @property; /// Ditto
+}
+
+immutable bool delegate(Exception) defaultRpcFaultFilter;
+static this() {
+ defaultRpcFaultFilter = (Exception e) {
+ import thrift.protocol.base;
+ import thrift.transport.base;
+ return (
+ (cast(TTransportException)e !is null) ||
+ (cast(TApplicationException)e !is null)
+ );
+ };
+}
+
+/**
+ * A TAsyncClientPoolBase implementation which queries multiple servers in a
+ * row until a request succeeds, the result of which is then returned.
+ *
+ * The definition of »success« can be customized using the rpcFaultFilter()
+ * delegate property. If it is non-null and calling it for an exception set by
+ * a failed method invocation returns true, the error is considered to be
+ * caused by the RPC layer rather than the application layer, and the next
+ * server in the pool is tried. If there are no more clients to try, the
+ * operation is marked as failed with a TCompoundOperationException.
+ *
+ * If a TAsyncClient in the pool fails with an RPC exception for a number of
+ * consecutive tries, it is temporarily disabled (not tried any longer) for
+ * a certain duration. Both the limit and the timeout can be configured. If all
+ * clients fail (and keepTrying is false), the operation fails with a
+ * TCompoundOperationException which contains the collected RPC exceptions.
+ */
+final class TAsyncClientPool(Interface) if (isService!Interface) :
+ TAsyncClientPoolBase!Interface
+{
+ ///
+ this(Client[] clients) {
+ pool_ = new TResourcePool!Client(clients);
+ rpcFaultFilter_ = defaultRpcFaultFilter;
+ reopenTransports_ = true;
+ }
+
+ /+override+/ void addClient(Client client) {
+ pool_.add(client);
+ }
+
+ /+override+/ bool removeClient(Client client) {
+ return pool_.remove(client);
+ }
+
+ /**
+ * Whether to keep trying to find a working client if all have failed in a
+ * row.
+ *
+ * Defaults to false.
+ */
+ bool keepTrying() const @property {
+ return pool_.cycle;
+ }
+
+ /// Ditto
+ void keepTrying(bool value) @property {
+ pool_.cycle = value;
+ }
+
+ /**
+ * Whether to use a random permutation of the client pool on every call to
+ * execute(). This can be used e.g. as a simple form of load balancing.
+ *
+ * Defaults to true.
+ */
+ bool permuteClients() const @property {
+ return pool_.permute;
+ }
+
+ /// Ditto
+ void permuteClients(bool value) @property {
+ pool_.permute = value;
+ }
+
+ /**
+ * The number of consecutive faults after which a client is disabled until
+ * faultDisableDuration has passed. 0 to never disable clients.
+ *
+ * Defaults to 0.
+ */
+ ushort faultDisableCount() const @property {
+ return pool_.faultDisableCount;
+ }
+
+ /// Ditto
+ void faultDisableCount(ushort value) @property {
+ pool_.faultDisableCount = value;
+ }
+
+ /**
+ * The duration for which a client is no longer considered after it has
+ * failed too often.
+ *
+ * Defaults to one second.
+ */
+ Duration faultDisableDuration() const @property {
+ return pool_.faultDisableDuration;
+ }
+
+ /// Ditto
+ void faultDisableDuration(Duration value) @property {
+ pool_.faultDisableDuration = value;
+ }
+
+ /+override+/ bool delegate(Exception) rpcFaultFilter() const @property {
+ return rpcFaultFilter_;
+ }
+
+ /+override+/ void rpcFaultFilter(bool delegate(Exception) value) @property {
+ rpcFaultFilter_ = value;
+ }
+
+ /+override+/ bool reopenTransports() const @property {
+ return reopenTransports_;
+ }
+
+ /+override+/ void reopenTransports(bool value) @property {
+ reopenTransports_ = value;
+ }
+
+ mixin(fallbackPoolForwardCode!Interface());
+
+protected:
+ // The actual worker implementation to which RPC method calls are forwarded.
+ auto executeOnPool(string method, Args...)(Args args,
+ TCancellation cancellation
+ ) {
+ auto clients = pool_[];
+ if (clients.empty) {
+ throw new TException("No clients available to try.");
+ }
+
+ auto promise = new TPromise!(ReturnType!(MemberType!(Interface, method)));
+ Exception[] rpcExceptions;
+
+ void tryNext() {
+ while (clients.empty) {
+ Client next;
+ Duration waitTime;
+ if (clients.willBecomeNonempty(next, waitTime)) {
+ if (waitTime > dur!"hnsecs"(0)) {
+ if (waitTime < dur!"usecs"(10)) {
+ import core.thread;
+ Thread.sleep(waitTime);
+ } else {
+ next.transport.asyncManager.delay(waitTime, { tryNext(); });
+ return;
+ }
+ }
+ } else {
+ promise.fail(new TCompoundOperationException("All clients failed.",
+ rpcExceptions));
+ return;
+ }
+ }
+
+ auto client = clients.front;
+ clients.popFront;
+
+ if (reopenTransports) {
+ if (!client.transport.isOpen) {
+ try {
+ client.transport.open();
+ } catch (Exception e) {
+ pool_.recordFault(client);
+ tryNext();
+ return;
+ }
+ }
+ }
+
+ auto future = mixin("client." ~ method)(args, cancellation);
+ future.completion.addCallback({
+ if (future.status == TFutureStatus.CANCELLED) {
+ promise.cancel();
+ return;
+ }
+
+ auto e = future.getException();
+ if (e) {
+ if (rpcFaultFilter_ && rpcFaultFilter_(e)) {
+ pool_.recordFault(client);
+ rpcExceptions ~= e;
+ tryNext();
+ return;
+ }
+ }
+ pool_.recordSuccess(client);
+ promise.complete(future);
+ });
+ }
+
+ tryNext();
+ return promise;
+ }
+
+private:
+ TResourcePool!Client pool_;
+ bool delegate(Exception) rpcFaultFilter_;
+ bool reopenTransports_;
+}
+
+/**
+ * TAsyncClientPool construction helper to avoid having to explicitly
+ * specify the interface type, i.e. to allow the constructor being called
+ * using IFTI (see $(DMDBUG 6082, D Bugzilla enhancement request 6082)).
+ */
+TAsyncClientPool!Interface tAsyncClientPool(Interface)(
+ TAsyncClientBase!Interface[] clients
+) if (isService!Interface) {
+ return new typeof(return)(clients);
+}
+
+private {
+ // Cannot use an anonymous delegate literal for this because they aren't
+ // allowed in class scope.
+ string fallbackPoolForwardCode(Interface)() {
+ string code = "";
+
+ foreach (methodName; AllMemberMethodNames!Interface) {
+ enum qn = "Interface." ~ methodName;
+ code ~= "TFuture!(ReturnType!(" ~ qn ~ ")) " ~ methodName ~
+ "(ParameterTypeTuple!(" ~ qn ~ ") args, TCancellation cancellation = null) {\n";
+ code ~= "return executeOnPool!(`" ~ methodName ~ "`)(args, cancellation);\n";
+ code ~= "}\n";
+ }
+
+ return code;
+ }
+}
+
+/**
+ * A TAsyncClientPoolBase implementation which queries multiple servers at
+ * the same time and returns the first success response.
+ *
+ * The definition of »success« can be customized using the rpcFaultFilter()
+ * delegate property. If it is non-null and calling it for an exception set by
+ * a failed method invocation returns true, the error is considered to be
+ * caused by the RPC layer rather than the application layer, and the next
+ * server in the pool is tried. If all clients fail, the operation is marked
+ * as failed with a TCompoundOperationException.
+ */
+final class TAsyncFastestClientPool(Interface) if (isService!Interface) :
+ TAsyncClientPoolBase!Interface
+{
+ ///
+ this(Client[] clients) {
+ clients_ = clients;
+ rpcFaultFilter_ = defaultRpcFaultFilter;
+ reopenTransports_ = true;
+ }
+
+ /+override+/ void addClient(Client client) {
+ clients_ ~= client;
+ }
+
+ /+override+/ bool removeClient(Client client) {
+ auto oldLength = clients_.length;
+ clients_ = removeEqual(clients_, client);
+ return clients_.length < oldLength;
+ }
+
+
+ /+override+/ bool delegate(Exception) rpcFaultFilter() const @property {
+ return rpcFaultFilter_;
+ }
+
+ /+override+/ void rpcFaultFilter(bool delegate(Exception) value) @property {
+ rpcFaultFilter_ = value;
+ }
+
+ /+override+/bool reopenTransports() const @property {
+ return reopenTransports_;
+ }
+
+ /+override+/ void reopenTransports(bool value) @property {
+ reopenTransports_ = value;
+ }
+
+ mixin(fastestPoolForwardCode!Interface());
+
+private:
+ Client[] clients_;
+ bool delegate(Exception) rpcFaultFilter_;
+ bool reopenTransports_;
+}
+
+/**
+ * TAsyncFastestClientPool construction helper to avoid having to explicitly
+ * specify the interface type, i.e. to allow the constructor being called
+ * using IFTI (see $(DMDBUG 6082, D Bugzilla enhancement request 6082)).
+ */
+TAsyncFastestClientPool!Interface tAsyncFastestClientPool(Interface)(
+ TAsyncClientBase!Interface[] clients
+) if (isService!Interface) {
+ return new typeof(return)(clients);
+}
+
+private {
+ // Cannot use an anonymous delegate literal for this because they aren't
+ // allowed in class scope.
+ string fastestPoolForwardCode(Interface)() {
+ string code = "";
+
+ foreach (methodName; AllMemberMethodNames!Interface) {
+ enum qn = "Interface." ~ methodName;
+ code ~= "TFuture!(ReturnType!(" ~ qn ~ ")) " ~ methodName ~
+ "(ParameterTypeTuple!(" ~ qn ~ ") args, " ~
+ "TCancellation cancellation = null) {\n";
+ code ~= "enum methodName = `" ~ methodName ~ "`;\n";
+ code ~= q{
+ alias ReturnType!(MemberType!(Interface, methodName)) ResultType;
+
+ auto childCancellation = new TCancellationOrigin;
+
+ TFuture!ResultType[] futures;
+ futures.reserve(clients_.length);
+
+ foreach (c; clients_) {
+ if (reopenTransports) {
+ if (!c.transport.isOpen) {
+ try {
+ c.transport.open();
+ } catch (Exception e) {
+ continue;
+ }
+ }
+ }
+ futures ~= mixin("c." ~ methodName)(args, childCancellation);
+ }
+
+ return new FastestPoolJob!(ResultType)(
+ futures, rpcFaultFilter, cancellation, childCancellation);
+ };
+ code ~= "}\n";
+ }
+
+ return code;
+ }
+
+ final class FastestPoolJob(Result) : TFuture!Result {
+ this(TFuture!Result[] poolFutures, bool delegate(Exception) rpcFaultFilter,
+ TCancellation cancellation, TCancellationOrigin childCancellation
+ ) {
+ resultPromise_ = new TPromise!Result;
+ poolFutures_ = poolFutures;
+ rpcFaultFilter_ = rpcFaultFilter;
+ childCancellation_ = childCancellation;
+
+ foreach (future; poolFutures) {
+ future.completion.addCallback({
+ auto f = future;
+ return { completionCallback(f); };
+ }());
+ if (future.status != TFutureStatus.RUNNING) {
+ // If the current future is already completed, we are done, don't
+ // bother adding callbacks for the others (they would just return
+ // immediately after acquiring the lock).
+ return;
+ }
+ }
+
+ if (cancellation) {
+ cancellation.triggering.addCallback({
+ resultPromise_.cancel();
+ childCancellation.trigger();
+ });
+ }
+ }
+
+ TFutureStatus status() const @property {
+ return resultPromise_.status;
+ }
+
+ TAwaitable completion() @property {
+ return resultPromise_.completion;
+ }
+
+ Result get() {
+ return resultPromise_.get();
+ }
+
+ Exception getException() {
+ return resultPromise_.getException();
+ }
+
+ private:
+ void completionCallback(TFuture!Result future) {
+ synchronized {
+ if (future.status == TFutureStatus.CANCELLED) {
+ assert(resultPromise_.status != TFutureStatus.RUNNING);
+ return;
+ }
+
+ if (resultPromise_.status != TFutureStatus.RUNNING) {
+ // The operation has already been completed. This can happen if
+ // another client completed first, but this callback was already
+ // waiting for the lock when it called cancel().
+ return;
+ }
+
+ if (future.status == TFutureStatus.FAILED) {
+ auto e = future.getException();
+ if (rpcFaultFilter_ && rpcFaultFilter_(e)) {
+ rpcExceptions_ ~= e;
+
+ if (rpcExceptions_.length == poolFutures_.length) {
+ resultPromise_.fail(new TCompoundOperationException(
+ "All child operations failed, unable to retrieve a result.",
+ rpcExceptions_
+ ));
+ }
+
+ return;
+ }
+ }
+
+ // Store the result to the target promise.
+ resultPromise_.complete(future);
+
+ // Cancel the other futures, we would just discard their results.
+ // Note: We do this after we have stored the results to our promise,
+ // see the assert at the top of the function.
+ childCancellation_.trigger();
+ }
+ }
+
+ TPromise!Result resultPromise_;
+ TFuture!Result[] poolFutures_;
+ Exception[] rpcExceptions_;
+ bool delegate(Exception) rpcFaultFilter_;
+ TCancellationOrigin childCancellation_;
+ }
+}
+
+/**
+ * Allows easily aggregating results from a number of TAsyncClients.
+ *
+ * Contrary to TAsync{Fallback, Fastest}ClientPool, this class does not
+ * simply implement TFutureInterface!Interface. It manages a pool of clients,
+ * but allows the user to specify a custom accumulator function to use or to
+ * iterate over the results using a TFutureAggregatorRange.
+ *
+ * For each service method, TAsyncAggregator offers a method
+ * accepting the same arguments, and an optional TCancellation instance, just
+ * like with TFutureInterface. The return type, however, is a proxy object
+ * that offers the following methods:
+ * ---
+ * /++
+ * + Returns a thrift.util.future.TFutureAggregatorRange for the results of
+ * + the client pool method invocations.
+ * +
+ * + The [] (slicing) operator can also be used to obtain the range.
+ * +
+ * + Params:
+ * + timeout = A timeout to pass to the TFutureAggregatorRange constructor,
+ * + defaults to zero (no timeout).
+ * +/
+ * TFutureAggregatorRange!ReturnType range(Duration timeout = dur!"hnsecs"(0));
+ * auto opSlice() { return range(); } /// Ditto
+ *
+ * /++
+ * + Returns a future that gathers the results from the clients in the pool
+ * + and invokes a user-supplied accumulator function on them, returning its
+ * + return value to the client.
+ * +
+ * + In addition to the TFuture!AccumulatedType interface (where
+ * + AccumulatedType is the return type of the accumulator function), the
+ * + returned object also offers two additional methods, finish() and
+ * + finishGet(): By default, the accumulator functions is called after all
+ * + the results from the pool clients have become available. Calling finish()
+ * + causes the accumulator future to stop waiting for other results and
+ * + immediately invoking the accumulator function on the results currently
+ * + available. If all results are already available, finish() is a no-op.
+ * + finishGet() is a convenience shortcut for combining it with
+ * + a call to get() immediately afterwards, like waitGet() is for wait().
+ * +
+ * + The acc alias can point to any callable accepting either an array of
+ * + return values or an array of return values and an array of exceptions;
+ * + see isAccumulator!() for details. The default accumulator concatenates
+ * + return values that can be concatenated with each others (e.g. arrays),
+ * + and simply returns an array of values otherwise, failing with a
+ * + TCompoundOperationException no values were returned.
+ * +
+ * + The accumulator function is not executed in any of the async manager
+ * + worker threads associated with the async clients, but instead it is
+ * + invoked when the actual result is requested for the first time after the
+ * + operation has been completed. This also includes checking the status
+ * + of the operation once it is no longer running, since the accumulator
+ * + has to be run to determine whether the operation succeeded or failed.
+ * +/
+ * auto accumulate(alias acc = defaultAccumulator)() if (isAccumulator!acc);
+ * ---
+ *
+ * Example:
+ * ---
+ * // Some Thrift service.
+ * interface Foo {
+ * int foo(string name);
+ * byte[] bar();
+ * }
+ *
+ * // Create the aggregator pool – client0, client1, client2 are some
+ * // TAsyncClient!Foo instances, but in theory could also be other
+ * // TFutureInterface!Foo implementations (e.g. some async client pool).
+ * auto pool = new TAsyncAggregator!Foo([client0, client1, client2]);
+ *
+ * foreach (val; pool.foo("baz").range(dur!"seconds"(1))) {
+ * // Process all the results that are available before a second has passed,
+ * // in the order they arrive.
+ * writeln(val);
+ * }
+ *
+ * auto sumRoots = pool.foo("baz").accumulate!((int[] vals, Exceptions[] exs){
+ * if (vals.empty) {
+ * throw new TCompoundOperationException("All clients failed", exs);
+ * }
+ *
+ * // Just to illustrate that the type of the values can change, convert the
+ * // numbers to double and sum up their roots.
+ * double result = 0;
+ * foreach (v; vals) result += sqrt(cast(double)v);
+ * return result;
+ * })();
+ *
+ * // Wait up to three seconds for the result, and then accumulate what has
+ * // arrived so far.
+ * sumRoots.completion.wait(dur!"seconds"(3));
+ * writeln(sumRoots.finishGet());
+ *
+ * // For scalars, the default accumulator returns an array of the values.
+ * pragma(msg, typeof(pool.foo("").accumulate().get()); // int[].
+ *
+ * // For lists, etc., it concatenates the results together.
+ * pragma(msg, typeof(pool.bar().accumulate().get())); // byte[].
+ * ---
+ *
+ * Note: For the accumulate!() interface, you might currently hit a »cannot use
+ * local '…' as parameter to non-global template accumulate«-error, see
+ * $(DMDBUG 5710, DMD issue 5710). If your accumulator function does not need
+ * to access the surrounding scope, you might want to use a function literal
+ * instead of a delegate to avoid the issue.
+ */
+class TAsyncAggregator(Interface) if (isBaseService!Interface) {
+ /// Shorthand for the client type this instance operates on.
+ alias TAsyncClientBase!Interface Client;
+
+ ///
+ this(Client[] clients) {
+ clients_ = clients;
+ }
+
+ /// Whether to open the underlying transports of a client before trying to
+ /// execute a method if they are not open. This is usually desirable
+ /// because it allows e.g. to automatically reconnect to a remote server
+ /// if the network connection is dropped.
+ ///
+ /// Defaults to true.
+ bool reopenTransports = true;
+
+ mixin AggregatorOpDispatch!();
+
+private:
+ Client[] clients_;
+}
+
+/// Ditto
+class TAsyncAggregator(Interface) if (isDerivedService!Interface) :
+ TAsyncAggregator!(BaseService!Interface)
+{
+ /// Shorthand for the client type this instance operates on.
+ alias TAsyncClientBase!Interface Client;
+
+ ///
+ this(Client[] clients) {
+ super(cast(TAsyncClientBase!(BaseService!Interface)[])clients);
+ }
+
+ mixin AggregatorOpDispatch!();
+}
+
+/**
+ * Whether fun is a valid accumulator function for values of type ValueType.
+ *
+ * For this to be true, fun must be a callable matching one of the following
+ * argument lists:
+ * ---
+ * fun(ValueType[] values);
+ * fun(ValueType[] values, Exception[] exceptions);
+ * ---
+ *
+ * The second version is passed the collected array exceptions from all the
+ * clients in the pool.
+ *
+ * The return value of the accumulator function is passed to the client (via
+ * the result future). If it throws an exception, the operation is marked as
+ * failed with the given exception instead.
+ */
+template isAccumulator(ValueType, alias fun) {
+ enum isAccumulator = is(typeof(fun(cast(ValueType[])[]))) ||
+ is(typeof(fun(cast(ValueType[])[], cast(Exception[])[])));
+}
+
+/**
+ * TAsyncAggregator construction helper to avoid having to explicitly
+ * specify the interface type, i.e. to allow the constructor being called
+ * using IFTI (see $(DMDBUG 6082, D Bugzilla enhancement request 6082)).
+ */
+TAsyncAggregator!Interface tAsyncAggregator(Interface)(
+ TAsyncClientBase!Interface[] clients
+) if (isService!Interface) {
+ return new typeof(return)(clients);
+}
+
+private {
+ mixin template AggregatorOpDispatch() {
+ auto opDispatch(string name, Args...)(Args args) if (
+ is(typeof(mixin("Interface.init." ~ name)(args)))
+ ) {
+ alias ReturnType!(MemberType!(Interface, name)) ResultType;
+
+ auto childCancellation = new TCancellationOrigin;
+
+ TFuture!ResultType[] futures;
+ futures.reserve(clients_.length);
+
+ foreach (c; cast(Client[])clients_) {
+ if (reopenTransports) {
+ if (!c.transport.isOpen) {
+ try {
+ c.transport.open();
+ } catch (Exception e) {
+ continue;
+ }
+ }
+ }
+ futures ~= mixin("c." ~ name)(args, childCancellation);
+ }
+
+ return AggregationResult!ResultType(futures, childCancellation);
+ }
+ }
+
+ struct AggregationResult(T) {
+ auto opSlice() {
+ return range();
+ }
+
+ auto range(Duration timeout = dur!"hnsecs"(0)) {
+ return tFutureAggregatorRange(futures_, childCancellation_, timeout);
+ }
+
+ auto accumulate(alias acc = defaultAccumulator)() if (isAccumulator!(T, acc)) {
+ return new AccumulatorJob!(T, acc)(futures_, childCancellation_);
+ }
+
+ private:
+ TFuture!T[] futures_;
+ TCancellationOrigin childCancellation_;
+ }
+
+ auto defaultAccumulator(T)(T[] values, Exception[] exceptions) {
+ if (values.empty) {
+ throw new TCompoundOperationException("All clients failed",
+ exceptions);
+ }
+
+ static if (is(typeof(T.init ~ T.init))) {
+ import std.algorithm;
+ return reduce!"a ~ b"(values);
+ } else {
+ return values;
+ }
+ }
+
+ final class AccumulatorJob(T, alias accumulator) if (
+ isAccumulator!(T, accumulator)
+ ) : TFuture!(AccumulatorResult!(T, accumulator)) {
+ this(TFuture!T[] futures, TCancellationOrigin childCancellation) {
+ futures_ = futures;
+ childCancellation_ = childCancellation;
+ resultMutex_ = new Mutex;
+ completionEvent_ = new TOneshotEvent;
+
+ foreach (future; futures) {
+ future.completion.addCallback({
+ auto f = future;
+ return {
+ synchronized (resultMutex_) {
+ if (f.status == TFutureStatus.CANCELLED) {
+ if (!finished_) {
+ status_ = TFutureStatus.CANCELLED;
+ finished_ = true;
+ }
+ return;
+ }
+
+ if (f.status == TFutureStatus.FAILED) {
+ exceptions_ ~= f.getException();
+ } else {
+ results_ ~= f.get();
+ }
+
+ if (results_.length + exceptions_.length == futures_.length) {
+ finished_ = true;
+ completionEvent_.trigger();
+ }
+ }
+ };
+ }());
+ }
+ }
+
+ TFutureStatus status() @property {
+ synchronized (resultMutex_) {
+ if (!finished_) return TFutureStatus.RUNNING;
+ if (status_ != TFutureStatus.RUNNING) return status_;
+
+ try {
+ result_ = invokeAccumulator!accumulator(results_, exceptions_);
+ status_ = TFutureStatus.SUCCEEDED;
+ } catch (Exception e) {
+ exception_ = e;
+ status_ = TFutureStatus.FAILED;
+ }
+
+ return status_;
+ }
+ }
+
+ TAwaitable completion() @property {
+ return completionEvent_;
+ }
+
+ AccumulatorResult!(T, accumulator) get() {
+ auto s = status;
+
+ enforce(s != TFutureStatus.RUNNING,
+ new TFutureException("Operation not yet completed."));
+
+ if (s == TFutureStatus.CANCELLED) throw new TCancelledException;
+ if (s == TFutureStatus.FAILED) throw exception_;
+ return result_;
+ }
+
+ Exception getException() {
+ auto s = status;
+ enforce(s != TFutureStatus.RUNNING,
+ new TFutureException("Operation not yet completed."));
+
+ if (s == TFutureStatus.CANCELLED) throw new TCancelledException;
+
+ if (s == TFutureStatus.SUCCEEDED) {
+ return null;
+ }
+ return exception_;
+ }
+
+ void finish() {
+ synchronized (resultMutex_) {
+ if (!finished_) {
+ finished_ = true;
+ childCancellation_.trigger();
+ completionEvent_.trigger();
+ }
+ }
+ }
+
+ auto finishGet() {
+ finish();
+ return get();
+ }
+
+ private:
+ TFuture!T[] futures_;
+ TCancellationOrigin childCancellation_;
+
+ bool finished_;
+ T[] results_;
+ Exception[] exceptions_;
+
+ TFutureStatus status_;
+ Mutex resultMutex_;
+ union {
+ AccumulatorResult!(T, accumulator) result_;
+ Exception exception_;
+ }
+ TOneshotEvent completionEvent_;
+ }
+
+ auto invokeAccumulator(alias accumulator, T)(
+ T[] values, Exception[] exceptions
+ ) if (
+ isAccumulator!(T, accumulator)
+ ) {
+ static if (is(typeof(accumulator(values, exceptions)))) {
+ return accumulator(values, exceptions);
+ } else {
+ return accumulator(values);
+ }
+ }
+
+ template AccumulatorResult(T, alias acc) {
+ alias typeof(invokeAccumulator!acc(cast(T[])[], cast(Exception[])[]))
+ AccumulatorResult;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/codegen/base.d b/src/jaegertracing/thrift/lib/d/src/thrift/codegen/base.d
new file mode 100644
index 000000000..db549928c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/codegen/base.d
@@ -0,0 +1,1021 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Code generation metadata and templates used for implementing struct
+ * serialization.
+ *
+ * Many templates can be customized using field meta data, which is read from
+ * a manifest constant member of the given type called fieldMeta (if present),
+ * and is concatenated with the elements from the optional fieldMetaData
+ * template alias parameter.
+ *
+ * Some code generation templates take account of the optional TVerboseCodegen
+ * version declaration, which causes warning messages to be emitted if no
+ * metadata for a field/method has been found and the default behavior is
+ * used instead. If this version is not defined, the templates just silently
+ * behave like the Thrift compiler does in this situation, i.e. automatically
+ * assign negative ids (starting at -1) for fields and assume TReq.AUTO as
+ * requirement level.
+ */
+// Implementation note: All the templates in here taking a field metadata
+// parameter should ideally have a constraint that restricts the alias to
+// TFieldMeta[]-typed values, but the is() expressions seems to always fail.
+module thrift.codegen.base;
+
+import std.algorithm : find;
+import std.array : empty, front;
+import std.conv : to;
+import std.exception : enforce;
+import std.traits : BaseTypeTuple, isPointer, isSomeFunction, PointerTarget,
+ ReturnType;
+import thrift.base;
+import thrift.internal.codegen;
+import thrift.protocol.base;
+import thrift.util.hashset;
+
+/*
+ * Thrift struct/service meta data, which is used to store information from
+ * the interface definition files not representable in plain D, i.e. field
+ * requirement levels, Thrift field IDs, etc.
+ */
+
+/**
+ * Struct field requirement levels.
+ */
+enum TReq {
+ /// Detect the requiredness from the field type: if it is nullable, treat
+ /// the field as optional, if it is non-nullable, treat the field as
+ /// required. This is the default used for handling structs not generated
+ /// from an IDL file, and never emitted by the Thrift compiler. TReq.AUTO
+ /// shouldn't be specified explicitly.
+ // Implementation note: thrift.codegen templates use
+ // thrift.internal.codegen.memberReq to resolve AUTO to REQUIRED/OPTIONAL
+ // instead of handling it directly.
+ AUTO,
+
+ /// The field is treated as optional when deserializing/receiving the struct
+ /// and as required when serializing/sending. This is the Thrift default if
+ /// neither "required" nor "optional" are specified in the IDL file.
+ OPT_IN_REQ_OUT,
+
+ /// The field is optional.
+ OPTIONAL,
+
+ /// The field is required.
+ REQUIRED,
+
+ /// Ignore the struct field when serializing/deserializing.
+ IGNORE
+}
+
+/**
+ * The way how methods are called.
+ */
+enum TMethodType {
+ /// Called in the normal two-way scheme consisting of a request and a
+ /// response.
+ REGULAR,
+
+ /// A fire-and-forget one-way method, where no response is sent and the
+ /// client immediately returns.
+ ONEWAY
+}
+
+/**
+ * Compile-time metadata for a struct field.
+ */
+struct TFieldMeta {
+ /// The name of the field. Used for matching a TFieldMeta with the actual
+ /// D struct member during code generation.
+ string name;
+
+ /// The (Thrift) id of the field.
+ short id;
+
+ /// Whether the field is requried.
+ TReq req;
+
+ /// A code string containing a D expression for the default value, if there
+ /// is one.
+ string defaultValue;
+}
+
+/**
+ * Compile-time metadata for a service method.
+ */
+struct TMethodMeta {
+ /// The name of the method. Used for matching a TMethodMeta with the actual
+ /// method during code generation.
+ string name;
+
+ /// Meta information for the parameteres.
+ TParamMeta[] params;
+
+ /// Specifies which exceptions can be thrown by the method. All other
+ /// exceptions are converted to a TApplicationException instead.
+ TExceptionMeta[] exceptions;
+
+ /// The fundamental type of the method.
+ TMethodType type;
+}
+
+/**
+ * Compile-time metadata for a service method parameter.
+ */
+struct TParamMeta {
+ /// The name of the parameter. Contrary to TFieldMeta, it only serves
+ /// decorative purposes here.
+ string name;
+
+ /// The Thrift id of the parameter in the param struct.
+ short id;
+
+ /// A code string containing a D expression for the default value for the
+ /// parameter, if any.
+ string defaultValue;
+}
+
+/**
+ * Compile-time metadata for a service method exception annotation.
+ */
+struct TExceptionMeta {
+ /// The name of the exception »return value«. Contrary to TFieldMeta, it
+ /// only serves decorative purposes here, as it is only used in code not
+ /// visible to processor implementations/service clients.
+ string name;
+
+ /// The Thrift id of the exception field in the return value struct.
+ short id;
+
+ /// The name of the exception type.
+ string type;
+}
+
+/**
+ * A pair of two TPorotocols. To be used in places where a list of protocols
+ * is expected, for specifying different protocols for input and output.
+ */
+struct TProtocolPair(InputProtocol, OutputProtocol) if (
+ isTProtocol!InputProtocol && isTProtocol!OutputProtocol
+) {}
+
+/**
+ * true if T is a TProtocolPair.
+ */
+template isTProtocolPair(T) {
+ static if (is(T _ == TProtocolPair!(I, O), I, O)) {
+ enum isTProtocolPair = true;
+ } else {
+ enum isTProtocolPair = false;
+ }
+}
+
+unittest {
+ static assert(isTProtocolPair!(TProtocolPair!(TProtocol, TProtocol)));
+ static assert(!isTProtocolPair!TProtocol);
+}
+
+/**
+ * true if T is a TProtocol or a TProtocolPair.
+ */
+template isTProtocolOrPair(T) {
+ enum isTProtocolOrPair = isTProtocol!T || isTProtocolPair!T;
+}
+
+unittest {
+ static assert(isTProtocolOrPair!TProtocol);
+ static assert(isTProtocolOrPair!(TProtocolPair!(TProtocol, TProtocol)));
+ static assert(!isTProtocolOrPair!void);
+}
+
+/**
+ * true if T represents a Thrift service.
+ */
+template isService(T) {
+ enum isService = isBaseService!T || isDerivedService!T;
+}
+
+/**
+ * true if T represents a Thrift service not derived from another service.
+ */
+template isBaseService(T) {
+ static if(is(T _ == interface) &&
+ (!is(T TBases == super) || TBases.length == 0)
+ ) {
+ enum isBaseService = true;
+ } else {
+ enum isBaseService = false;
+ }
+}
+
+/**
+ * true if T represents a Thrift service derived from another service.
+ */
+template isDerivedService(T) {
+ static if(is(T _ == interface) &&
+ is(T TBases == super) && TBases.length == 1
+ ) {
+ enum isDerivedService = isService!(TBases[0]);
+ } else {
+ enum isDerivedService = false;
+ }
+}
+
+/**
+ * For derived services, gets the base service interface.
+ */
+template BaseService(T) if (isDerivedService!T) {
+ alias BaseTypeTuple!T[0] BaseService;
+}
+
+
+/*
+ * Code generation templates.
+ */
+
+/**
+ * Mixin template defining additional helper methods for using a struct with
+ * Thrift, and a member called isSetFlags if the struct contains any fields
+ * for which an »is set« flag is needed.
+ *
+ * It can only be used inside structs or Exception classes.
+ *
+ * For example, consider the following struct definition:
+ * ---
+ * struct Foo {
+ * string a;
+ * int b;
+ * int c;
+ *
+ * mixin TStructHelpers!([
+ * TFieldMeta("a", 1), // Implicitly optional (nullable).
+ * TFieldMeta("b", 2), // Implicitly required (non-nullable).
+ * TFieldMeta("c", 3, TReq.REQUIRED, "4")
+ * ]);
+ * }
+ * ---
+ *
+ * TStructHelper adds the following methods to the struct:
+ * ---
+ * /++
+ * + Sets member fieldName to the given value and marks it as set.
+ * +
+ * + Examples:
+ * + ---
+ * + auto f = Foo();
+ * + f.set!"b"(12345);
+ * + assert(f.isSet!"b");
+ * + ---
+ * +/
+ * void set(string fieldName)(MemberType!(This, fieldName) value);
+ *
+ * /++
+ * + Resets member fieldName to the init property of its type and marks it as
+ * + not set.
+ * +
+ * + Examples:
+ * + ---
+ * + // Set f.b to some value.
+ * + auto f = Foo();
+ * + f.set!"b"(12345);
+ * +
+ * + f.unset!b();
+ * +
+ * + // f.b is now unset again.
+ * + assert(!f.isSet!"b");
+ * + ---
+ * +/
+ * void unset(string fieldName)();
+ *
+ * /++
+ * + Returns whether member fieldName is set.
+ * +
+ * + Examples:
+ * + ---
+ * + auto f = Foo();
+ * + assert(!f.isSet!"b");
+ * + f.set!"b"(12345);
+ * + assert(f.isSet!"b");
+ * + ---
+ * +/
+ * bool isSet(string fieldName)() const @property;
+ *
+ * /++
+ * + Returns a string representation of the struct.
+ * +
+ * + Examples:
+ * + ---
+ * + auto f = Foo();
+ * + f.a = "a string";
+ * + assert(f.toString() == `Foo("a string", 0 (unset), 4)`);
+ * + ---
+ * +/
+ * string toString() const;
+ *
+ * /++
+ * + Deserializes the struct, setting its members to the values read from the
+ * + protocol. Forwards to readStruct(this, proto);
+ * +/
+ * void read(Protocol)(Protocol proto) if (isTProtocol!Protocol);
+ *
+ * /++
+ * + Serializes the struct to the target protocol. Forwards to
+ * + writeStruct(this, proto);
+ * +/
+ * void write(Protocol)(Protocol proto) const if (isTProtocol!Protocol);
+ * ---
+ *
+ * Additionally, an opEquals() implementation is provided which simply
+ * compares all fields, but disregards the is set struct, if any (the exact
+ * signature obviously differs between structs and exception classes). The
+ * metadata is stored in a manifest constant called fieldMeta.
+ *
+ * Note: To set the default values for fields where one has been specified in
+ * the field metadata, a parameterless static opCall is generated, because D
+ * does not allow parameterless (default) constructors for structs. Thus, be
+ * always to use to initialize structs:
+ * ---
+ * Foo foo; // Wrong!
+ * auto foo = Foo(); // Correct.
+ * ---
+ */
+mixin template TStructHelpers(alias fieldMetaData = cast(TFieldMeta[])null) if (
+ is(typeof(fieldMetaData) : TFieldMeta[])
+) {
+ import std.algorithm : any;
+ import thrift.codegen.base;
+ import thrift.internal.codegen : isNullable, MemberType, mergeFieldMeta,
+ FieldNames;
+ import thrift.protocol.base : TProtocol, isTProtocol;
+
+ alias typeof(this) This;
+ static assert(is(This == struct) || is(This : Exception),
+ "TStructHelpers can only be used inside a struct or an Exception class.");
+
+ static if (TIsSetFlags!(This, fieldMetaData).tupleof.length > 0) {
+ // If we need to keep isSet flags around, create an instance of the
+ // container struct.
+ TIsSetFlags!(This, fieldMetaData) isSetFlags;
+ enum fieldMeta = fieldMetaData ~ [TFieldMeta("isSetFlags", 0, TReq.IGNORE)];
+ } else {
+ enum fieldMeta = fieldMetaData;
+ }
+
+ void set(string fieldName)(MemberType!(This, fieldName) value) if (
+ is(MemberType!(This, fieldName))
+ ) {
+ __traits(getMember, this, fieldName) = value;
+ static if (is(typeof(mixin("this.isSetFlags." ~ fieldName)) : bool)) {
+ __traits(getMember, this.isSetFlags, fieldName) = true;
+ }
+ }
+
+ void unset(string fieldName)() if (is(MemberType!(This, fieldName))) {
+ static if (is(typeof(mixin("this.isSetFlags." ~ fieldName)) : bool)) {
+ __traits(getMember, this.isSetFlags, fieldName) = false;
+ }
+ __traits(getMember, this, fieldName) = MemberType!(This, fieldName).init;
+ }
+
+ bool isSet(string fieldName)() const @property if (
+ is(MemberType!(This, fieldName))
+ ) {
+ static if (isNullable!(MemberType!(This, fieldName))) {
+ return __traits(getMember, this, fieldName) !is null;
+ } else static if (is(typeof(mixin("this.isSetFlags." ~ fieldName)) : bool)) {
+ return __traits(getMember, this.isSetFlags, fieldName);
+ } else {
+ // This is a required field, which is always set.
+ return true;
+ }
+ }
+
+ static if (is(This _ == class)) {
+ override string toString() const {
+ return thriftToStringImpl();
+ }
+
+ override bool opEquals(Object other) const {
+ auto rhs = cast(This)other;
+ if (rhs) {
+ return thriftOpEqualsImpl(rhs);
+ }
+
+ return (cast()super).opEquals(other);
+ }
+
+ override size_t toHash() const {
+ return thriftToHashImpl();
+ }
+ } else {
+ string toString() const {
+ return thriftToStringImpl();
+ }
+
+ bool opEquals(ref const This other) const {
+ return thriftOpEqualsImpl(other);
+ }
+
+ size_t toHash() const @safe nothrow {
+ return thriftToHashImpl();
+ }
+ }
+
+ private string thriftToStringImpl() const {
+ import std.conv : to;
+ string result = This.stringof ~ "(";
+ mixin({
+ string code = "";
+ bool first = true;
+ foreach (name; FieldNames!(This, fieldMeta)) {
+ if (first) {
+ first = false;
+ } else {
+ code ~= "result ~= `, `;\n";
+ }
+ code ~= "result ~= `" ~ name ~ ": ` ~ to!string(cast()this." ~ name ~ ");\n";
+ code ~= "if (!isSet!q{" ~ name ~ "}) {\n";
+ code ~= "result ~= ` (unset)`;\n";
+ code ~= "}\n";
+ }
+ return code;
+ }());
+ result ~= ")";
+ return result;
+ }
+
+ private bool thriftOpEqualsImpl(const ref This rhs) const {
+ foreach (name; FieldNames!This) {
+ if (mixin("this." ~ name) != mixin("rhs." ~ name)) return false;
+ }
+ return true;
+ }
+
+ private size_t thriftToHashImpl() const @trusted nothrow {
+ size_t hash = 0;
+ foreach (i, _; this.tupleof) {
+ auto val = this.tupleof[i];
+ hash += typeid(val).getHash(&val);
+ }
+ return hash;
+ }
+
+ static if (any!`!a.defaultValue.empty`(mergeFieldMeta!(This, fieldMetaData))) {
+ static if (is(This _ == class)) {
+ this() {
+ mixin(thriftFieldInitCode!(mergeFieldMeta!(This, fieldMetaData))("this"));
+ }
+ } else {
+ // DMD @@BUG@@: Have to use auto here to avoid »no size yet for forward
+ // reference« errors.
+ static auto opCall() {
+ auto result = This.init;
+ mixin(thriftFieldInitCode!(mergeFieldMeta!(This, fieldMetaData))("result"));
+ return result;
+ }
+ }
+ }
+
+ void read(Protocol)(Protocol proto) if (isTProtocol!Protocol) {
+ // Need to explicitly specify fieldMetaData here, since it isn't already
+ // picked up in some situations (e.g. the TArgs struct for methods with
+ // multiple parameters in async_test_servers) otherwise. Due to a DMD
+ // @@BUG@@, we need to explicitly specify the other template parameters
+ // as well.
+ readStruct!(This, Protocol, fieldMetaData, false)(this, proto);
+ }
+
+ void write(Protocol)(Protocol proto) const if (isTProtocol!Protocol) {
+ writeStruct!(This, Protocol, fieldMetaData, false)(this, proto);
+ }
+}
+
+// DMD @@BUG@@: Having this inside TStructHelpers leads to weird lookup errors
+// (e.g. for std.arry.empty).
+string thriftFieldInitCode(alias fieldMeta)(string thisName) {
+ string code = "";
+ foreach (field; fieldMeta) {
+ if (field.defaultValue.empty) continue;
+ code ~= thisName ~ "." ~ field.name ~ " = " ~ field.defaultValue ~ ";\n";
+ }
+ return code;
+}
+
+unittest {
+ // Cannot make this nested in the unittest block due to a »no size yet for
+ // forward reference« error.
+ static struct Foo {
+ string a;
+ int b;
+ int c;
+
+ mixin TStructHelpers!([
+ TFieldMeta("a", 1),
+ TFieldMeta("b", 2, TReq.OPT_IN_REQ_OUT),
+ TFieldMeta("c", 3, TReq.REQUIRED, "4")
+ ]);
+ }
+
+ auto f = Foo();
+
+ f.set!"b"(12345);
+ assert(f.isSet!"b");
+ f.unset!"b"();
+ assert(!f.isSet!"b");
+ f.set!"b"(12345);
+ assert(f.isSet!"b");
+ f.unset!"b"();
+
+ f.a = "a string";
+ assert(f.toString() == `Foo(a: a string, b: 0 (unset), c: 4)`);
+}
+
+
+/**
+ * Generates an eponymous struct with boolean flags for the non-required
+ * non-nullable fields of T.
+ *
+ * Nullable fields are just set to null to signal »not set«, so no flag is
+ * emitted for them, even if they are optional.
+ *
+ * In most cases, you do not want to use this directly, but via TStructHelpers
+ * instead.
+ */
+template TIsSetFlags(T, alias fieldMetaData) {
+ mixin({
+ string code = "struct TIsSetFlags {\n";
+ foreach (meta; fieldMetaData) {
+ code ~= "static if (!is(MemberType!(T, `" ~ meta.name ~ "`))) {\n";
+ code ~= q{
+ static assert(false, "Field '" ~ meta.name ~
+ "' referenced in metadata not present in struct '" ~ T.stringof ~ "'.");
+ };
+ code ~= "}";
+ if (meta.req == TReq.OPTIONAL || meta.req == TReq.OPT_IN_REQ_OUT) {
+ code ~= "else static if (!isNullable!(MemberType!(T, `" ~ meta.name ~ "`))) {\n";
+ code ~= " bool " ~ meta.name ~ ";\n";
+ code ~= "}\n";
+ }
+ }
+ code ~= "}";
+ return code;
+ }());
+}
+
+/**
+ * Deserializes a Thrift struct from a protocol.
+ *
+ * Using the Protocol template parameter, the concrete TProtocol to use can be
+ * be specified. If the pointerStruct parameter is set to true, the struct
+ * fields are expected to be pointers to the actual data. This is used
+ * internally (combined with TPResultStruct) and usually should not be used in
+ * user code.
+ *
+ * This is a free function to make it possible to read exisiting structs from
+ * the wire without altering their definitions.
+ */
+void readStruct(T, Protocol, alias fieldMetaData = cast(TFieldMeta[])null,
+ bool pointerStruct = false)(auto ref T s, Protocol p) if (isTProtocol!Protocol)
+{
+ mixin({
+ string code;
+
+ // Check that all fields for which there is meta info are actually in the
+ // passed struct type.
+ foreach (field; mergeFieldMeta!(T, fieldMetaData)) {
+ code ~= "static assert(is(MemberType!(T, `" ~ field.name ~ "`)));\n";
+ }
+
+ // Returns the code string for reading a value of type F off the wire and
+ // assigning it to v. The level parameter is used to make sure that there
+ // are no conflicting variable names on recursive calls.
+ string readValueCode(ValueType)(string v, size_t level = 0) {
+ // Some non-ambigous names to use (shadowing is not allowed in D).
+ immutable i = "i" ~ to!string(level);
+ immutable elem = "elem" ~ to!string(level);
+ immutable key = "key" ~ to!string(level);
+ immutable list = "list" ~ to!string(level);
+ immutable map = "map" ~ to!string(level);
+ immutable set = "set" ~ to!string(level);
+ immutable value = "value" ~ to!string(level);
+
+ alias FullyUnqual!ValueType F;
+
+ static if (is(F == bool)) {
+ return v ~ " = p.readBool();";
+ } else static if (is(F == byte)) {
+ return v ~ " = p.readByte();";
+ } else static if (is(F == double)) {
+ return v ~ " = p.readDouble();";
+ } else static if (is(F == short)) {
+ return v ~ " = p.readI16();";
+ } else static if (is(F == int)) {
+ return v ~ " = p.readI32();";
+ } else static if (is(F == long)) {
+ return v ~ " = p.readI64();";
+ } else static if (is(F : string)) {
+ return v ~ " = p.readString();";
+ } else static if (is(F == enum)) {
+ return v ~ " = cast(typeof(" ~ v ~ "))p.readI32();";
+ } else static if (is(F _ : E[], E)) {
+ return "{\n" ~
+ "auto " ~ list ~ " = p.readListBegin();\n" ~
+ // TODO: Check element type here?
+ v ~ " = new typeof(" ~ v ~ "[0])[" ~ list ~ ".size];\n" ~
+ "foreach (" ~ i ~ "; 0 .. " ~ list ~ ".size) {\n" ~
+ readValueCode!E(v ~ "[" ~ i ~ "]", level + 1) ~ "\n" ~
+ "}\n" ~
+ "p.readListEnd();\n" ~
+ "}";
+ } else static if (is(F _ : V[K], K, V)) {
+ return "{\n" ~
+ "auto " ~ map ~ " = p.readMapBegin();" ~
+ v ~ " = null;\n" ~
+ // TODO: Check key/value types here?
+ "foreach (" ~ i ~ "; 0 .. " ~ map ~ ".size) {\n" ~
+ "FullyUnqual!(typeof(" ~ v ~ ".keys[0])) " ~ key ~ ";\n" ~
+ readValueCode!K(key, level + 1) ~ "\n" ~
+ "typeof(" ~ v ~ ".values[0]) " ~ value ~ ";\n" ~
+ readValueCode!V(value, level + 1) ~ "\n" ~
+ v ~ "[cast(typeof(" ~ v ~ ".keys[0]))" ~ key ~ "] = " ~ value ~ ";\n" ~
+ "}\n" ~
+ "p.readMapEnd();" ~
+ "}";
+ } else static if (is(F _ : HashSet!(E), E)) {
+ return "{\n" ~
+ "auto " ~ set ~ " = p.readSetBegin();" ~
+ // TODO: Check element type here?
+ v ~ " = new typeof(" ~ v ~ ")();\n" ~
+ "foreach (" ~ i ~ "; 0 .. " ~ set ~ ".size) {\n" ~
+ "typeof(" ~ v ~ "[][0]) " ~ elem ~ ";\n" ~
+ readValueCode!E(elem, level + 1) ~ "\n" ~
+ v ~ " ~= " ~ elem ~ ";\n" ~
+ "}\n" ~
+ "p.readSetEnd();" ~
+ "}";
+ } else static if (is(F == struct) || is(F : TException)) {
+ static if (is(F == struct)) {
+ auto result = v ~ " = typeof(" ~ v ~ ")();\n";
+ } else {
+ auto result = v ~ " = new typeof(" ~ v ~ ")();\n";
+ }
+
+ static if (__traits(compiles, F.init.read(TProtocol.init))) {
+ result ~= v ~ ".read(p);";
+ } else {
+ result ~= "readStruct(" ~ v ~ ", p);";
+ }
+ return result;
+ } else {
+ static assert(false, "Cannot represent type in Thrift: " ~ F.stringof);
+ }
+ }
+
+ string readFieldCode(FieldType)(string name, short id, TReq req) {
+ static if (pointerStruct && isPointer!FieldType) {
+ immutable v = "(*s." ~ name ~ ")";
+ alias PointerTarget!FieldType F;
+ } else {
+ immutable v = "s." ~ name;
+ alias FieldType F;
+ }
+
+ string code = "case " ~ to!string(id) ~ ":\n";
+ code ~= "if (f.type == " ~ dToTTypeString!F ~ ") {\n";
+ code ~= readValueCode!F(v) ~ "\n";
+ if (req == TReq.REQUIRED) {
+ // For required fields, set the corresponding local isSet variable.
+ code ~= "isSet_" ~ name ~ " = true;\n";
+ } else if (!isNullable!F){
+ code ~= "s.isSetFlags." ~ name ~ " = true;\n";
+ }
+ code ~= "} else skip(p, f.type);\n";
+ code ~= "break;\n";
+ return code;
+ }
+
+ // Code for the local boolean flags used to make sure required fields have
+ // been found.
+ string isSetFlagCode = "";
+
+ // Code for checking whether the flags for the required fields are true.
+ string isSetCheckCode = "";
+
+ /// Code for the case statements storing the fields to the result struct.
+ string readMembersCode = "";
+
+ // The last automatically assigned id – fields with no meta information
+ // are assigned (in lexical order) descending negative ids, starting with
+ // -1, just like the Thrift compiler does.
+ short lastId;
+
+ foreach (name; FieldNames!T) {
+ enum req = memberReq!(T, name, fieldMetaData);
+ if (req == TReq.REQUIRED) {
+ // For required fields, generate local bool flags to keep track
+ // whether the field has been encountered.
+ immutable n = "isSet_" ~ name;
+ isSetFlagCode ~= "bool " ~ n ~ ";\n";
+ isSetCheckCode ~= "enforce(" ~ n ~ ", new TProtocolException(" ~
+ "`Required field '" ~ name ~ "' not found in serialized data`, " ~
+ "TProtocolException.Type.INVALID_DATA));\n";
+ }
+
+ enum meta = find!`a.name == b`(mergeFieldMeta!(T, fieldMetaData), name);
+ static if (meta.empty) {
+ --lastId;
+ version (TVerboseCodegen) {
+ code ~= "pragma(msg, `[thrift.codegen.base.readStruct] Warning: No " ~
+ "meta information for field '" ~ name ~ "' in struct '" ~
+ T.stringof ~ "'. Assigned id: " ~ to!string(lastId) ~ ".`);\n";
+ }
+ readMembersCode ~= readFieldCode!(MemberType!(T, name))(
+ name, lastId, req);
+ } else static if (req != TReq.IGNORE) {
+ readMembersCode ~= readFieldCode!(MemberType!(T, name))(
+ name, meta.front.id, req);
+ }
+ }
+
+ code ~= isSetFlagCode;
+ code ~= "p.readStructBegin();\n";
+ code ~= "while (true) {\n";
+ code ~= "auto f = p.readFieldBegin();\n";
+ code ~= "if (f.type == TType.STOP) break;\n";
+ code ~= "switch(f.id) {\n";
+ code ~= readMembersCode;
+ code ~= "default: skip(p, f.type);\n";
+ code ~= "}\n";
+ code ~= "p.readFieldEnd();\n";
+ code ~= "}\n";
+ code ~= "p.readStructEnd();\n";
+ code ~= isSetCheckCode;
+
+ return code;
+ }());
+}
+
+/**
+ * Serializes a struct to the target protocol.
+ *
+ * Using the Protocol template parameter, the concrete TProtocol to use can be
+ * be specified. If the pointerStruct parameter is set to true, the struct
+ * fields are expected to be pointers to the actual data. This is used
+ * internally (combined with TPargsStruct) and usually should not be used in
+ * user code.
+ *
+ * This is a free function to make it possible to read exisiting structs from
+ * the wire without altering their definitions.
+ */
+void writeStruct(T, Protocol, alias fieldMetaData = cast(TFieldMeta[])null,
+ bool pointerStruct = false) (const T s, Protocol p) if (isTProtocol!Protocol)
+{
+ mixin({
+ // Check that all fields for which there is meta info are actually in the
+ // passed struct type.
+ string code = "";
+ foreach (field; mergeFieldMeta!(T, fieldMetaData)) {
+ code ~= "static assert(is(MemberType!(T, `" ~ field.name ~ "`)));\n";
+ }
+
+ // Check that required nullable members are non-null.
+ // WORKAROUND: To stop LDC from emitting the manifest constant »meta« below
+ // into the writeStruct function body this is inside the string mixin
+ // block – the code wouldn't depend on it (this is an LDC bug, and because
+ // of it a new array would be allocated on each method invocation at runtime).
+ foreach (name; StaticFilter!(
+ Compose!(isNullable, PApply!(MemberType, T)),
+ FieldNames!T
+ )) {
+ static if (memberReq!(T, name, fieldMetaData) == TReq.REQUIRED) {
+ code ~= "enforce(__traits(getMember, s, `" ~ name ~ "`) !is null,
+ new TException(`Required field '" ~ name ~ "' is null.`));\n";
+ }
+ }
+
+ return code;
+ }());
+
+ p.writeStructBegin(TStruct(T.stringof));
+ mixin({
+ string writeValueCode(ValueType)(string v, size_t level = 0) {
+ // Some non-ambigous names to use (shadowing is not allowed in D).
+ immutable elem = "elem" ~ to!string(level);
+ immutable key = "key" ~ to!string(level);
+ immutable value = "value" ~ to!string(level);
+
+ alias FullyUnqual!ValueType F;
+ static if (is(F == bool)) {
+ return "p.writeBool(" ~ v ~ ");";
+ } else static if (is(F == byte)) {
+ return "p.writeByte(" ~ v ~ ");";
+ } else static if (is(F == double)) {
+ return "p.writeDouble(" ~ v ~ ");";
+ } else static if (is(F == short)) {
+ return "p.writeI16(" ~ v ~ ");";
+ } else static if (is(F == int)) {
+ return "p.writeI32(" ~ v ~ ");";
+ } else static if (is(F == long)) {
+ return "p.writeI64(" ~ v ~ ");";
+ } else static if (is(F : string)) {
+ return "p.writeString(" ~ v ~ ");";
+ } else static if (is(F == enum)) {
+ return "p.writeI32(cast(int)" ~ v ~ ");";
+ } else static if (is(F _ : E[], E)) {
+ return "p.writeListBegin(TList(" ~ dToTTypeString!E ~ ", " ~ v ~
+ ".length));\n" ~
+ "foreach (" ~ elem ~ "; " ~ v ~ ") {\n" ~
+ writeValueCode!E(elem, level + 1) ~ "\n" ~
+ "}\n" ~
+ "p.writeListEnd();";
+ } else static if (is(F _ : V[K], K, V)) {
+ return "p.writeMapBegin(TMap(" ~ dToTTypeString!K ~ ", " ~
+ dToTTypeString!V ~ ", " ~ v ~ ".length));\n" ~
+ "foreach (" ~ key ~ ", " ~ value ~ "; " ~ v ~ ") {\n" ~
+ writeValueCode!K(key, level + 1) ~ "\n" ~
+ writeValueCode!V(value, level + 1) ~ "\n" ~
+ "}\n" ~
+ "p.writeMapEnd();";
+ } else static if (is(F _ : HashSet!E, E)) {
+ return "p.writeSetBegin(TSet(" ~ dToTTypeString!E ~ ", " ~ v ~
+ ".length));\n" ~
+ "foreach (" ~ elem ~ "; " ~ v ~ "[]) {\n" ~
+ writeValueCode!E(elem, level + 1) ~ "\n" ~
+ "}\n" ~
+ "p.writeSetEnd();";
+ } else static if (is(F == struct) || is(F : TException)) {
+ static if (__traits(compiles, F.init.write(TProtocol.init))) {
+ return v ~ ".write(p);";
+ } else {
+ return "writeStruct(" ~ v ~ ", p);";
+ }
+ } else {
+ static assert(false, "Cannot represent type in Thrift: " ~ F.stringof);
+ }
+ }
+
+ string writeFieldCode(FieldType)(string name, short id, TReq req) {
+ string code;
+ if (!pointerStruct && req == TReq.OPTIONAL) {
+ code ~= "if (s.isSet!`" ~ name ~ "`) {\n";
+ }
+
+ static if (pointerStruct && isPointer!FieldType) {
+ immutable v = "(*s." ~ name ~ ")";
+ alias PointerTarget!FieldType F;
+ } else {
+ immutable v = "s." ~ name;
+ alias FieldType F;
+ }
+
+ code ~= "p.writeFieldBegin(TField(`" ~ name ~ "`, " ~ dToTTypeString!F ~
+ ", " ~ to!string(id) ~ "));\n";
+ code ~= writeValueCode!F(v) ~ "\n";
+ code ~= "p.writeFieldEnd();\n";
+
+ if (!pointerStruct && req == TReq.OPTIONAL) {
+ code ~= "}\n";
+ }
+ return code;
+ }
+
+ // The last automatically assigned id – fields with no meta information
+ // are assigned (in lexical order) descending negative ids, starting with
+ // -1, just like the Thrift compiler does.
+ short lastId;
+
+ string code = "";
+ foreach (name; FieldNames!T) {
+ alias MemberType!(T, name) F;
+ enum req = memberReq!(T, name, fieldMetaData);
+ enum meta = find!`a.name == b`(mergeFieldMeta!(T, fieldMetaData), name);
+ if (meta.empty) {
+ --lastId;
+ version (TVerboseCodegen) {
+ code ~= "pragma(msg, `[thrift.codegen.base.writeStruct] Warning: No " ~
+ "meta information for field '" ~ name ~ "' in struct '" ~
+ T.stringof ~ "'. Assigned id: " ~ to!string(lastId) ~ ".`);\n";
+ }
+ code ~= writeFieldCode!F(name, lastId, req);
+ } else if (req != TReq.IGNORE) {
+ code ~= writeFieldCode!F(name, meta.front.id, req);
+ }
+ }
+
+ return code;
+ }());
+ p.writeFieldStop();
+ p.writeStructEnd();
+}
+
+unittest {
+ // Ensure that the generated code at least compiles for the basic field type
+ // combinations. Functionality checks are covered by the rest of the test
+ // suite.
+
+ static struct Test {
+ // Non-nullable.
+ int a1;
+ int a2;
+ int a3;
+ int a4;
+
+ // Nullable.
+ string b1;
+ string b2;
+ string b3;
+ string b4;
+
+ mixin TStructHelpers!([
+ TFieldMeta("a1", 1, TReq.OPT_IN_REQ_OUT),
+ TFieldMeta("a2", 2, TReq.OPTIONAL),
+ TFieldMeta("a3", 3, TReq.REQUIRED),
+ TFieldMeta("a4", 4, TReq.IGNORE),
+ TFieldMeta("b1", 5, TReq.OPT_IN_REQ_OUT),
+ TFieldMeta("b2", 6, TReq.OPTIONAL),
+ TFieldMeta("b3", 7, TReq.REQUIRED),
+ TFieldMeta("b4", 8, TReq.IGNORE),
+ ]);
+ }
+
+ static assert(__traits(compiles, { Test t; t.read(cast(TProtocol)null); }));
+ static assert(__traits(compiles, { Test t; t.write(cast(TProtocol)null); }));
+}
+
+// Ensure opEquals and toHash consistency.
+unittest {
+ struct TestEquals {
+ int a1;
+
+ mixin TStructHelpers!([
+ TFieldMeta("a1", 1, TReq.OPT_IN_REQ_OUT),
+ ]);
+ }
+
+ TestEquals a, b;
+ assert(a == b);
+ assert(a.toHash() == b.toHash());
+
+ a.a1 = 42;
+ assert(a != b);
+ assert(a.toHash() != b.toHash());
+
+ b.a1 = 42;
+ assert(a == b);
+ assert(a.toHash() == b.toHash());
+}
+
+private {
+ /*
+ * Returns a D code string containing the matching TType value for a passed
+ * D type, e.g. dToTTypeString!byte == "TType.BYTE".
+ */
+ template dToTTypeString(T) {
+ static if (is(FullyUnqual!T == bool)) {
+ enum dToTTypeString = "TType.BOOL";
+ } else static if (is(FullyUnqual!T == byte)) {
+ enum dToTTypeString = "TType.BYTE";
+ } else static if (is(FullyUnqual!T == double)) {
+ enum dToTTypeString = "TType.DOUBLE";
+ } else static if (is(FullyUnqual!T == short)) {
+ enum dToTTypeString = "TType.I16";
+ } else static if (is(FullyUnqual!T == int)) {
+ enum dToTTypeString = "TType.I32";
+ } else static if (is(FullyUnqual!T == long)) {
+ enum dToTTypeString = "TType.I64";
+ } else static if (is(FullyUnqual!T : string)) {
+ enum dToTTypeString = "TType.STRING";
+ } else static if (is(FullyUnqual!T == enum)) {
+ enum dToTTypeString = "TType.I32";
+ } else static if (is(FullyUnqual!T _ : U[], U)) {
+ enum dToTTypeString = "TType.LIST";
+ } else static if (is(FullyUnqual!T _ : V[K], K, V)) {
+ enum dToTTypeString = "TType.MAP";
+ } else static if (is(FullyUnqual!T _ : HashSet!E, E)) {
+ enum dToTTypeString = "TType.SET";
+ } else static if (is(FullyUnqual!T == struct)) {
+ enum dToTTypeString = "TType.STRUCT";
+ } else static if (is(FullyUnqual!T : TException)) {
+ enum dToTTypeString = "TType.STRUCT";
+ } else {
+ static assert(false, "Cannot represent type in Thrift: " ~ T.stringof);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/codegen/client.d b/src/jaegertracing/thrift/lib/d/src/thrift/codegen/client.d
new file mode 100644
index 000000000..117b07660
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/codegen/client.d
@@ -0,0 +1,486 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.codegen.client;
+
+import std.algorithm : find;
+import std.array : empty, front;
+import std.conv : to;
+import std.traits : isSomeFunction, ParameterStorageClass,
+ ParameterStorageClassTuple, ParameterTypeTuple, ReturnType;
+import thrift.codegen.base;
+import thrift.internal.codegen;
+import thrift.internal.ctfe;
+import thrift.protocol.base;
+
+/**
+ * Thrift service client, which implements an interface by synchronously
+ * calling a server over a TProtocol.
+ *
+ * TClientBase simply extends Interface with generic input/output protocol
+ * properties to serve as a supertype for all TClients for the same service,
+ * which might be instantiated with different concrete protocol types (there
+ * is no covariance for template type parameters). If Interface is derived
+ * from another interface BaseInterface, it also extends
+ * TClientBase!BaseInterface.
+ *
+ * TClient is the class that actually implements TClientBase. Just as
+ * TClientBase, it is also derived from TClient!BaseInterface for inheriting
+ * services.
+ *
+ * TClient takes two optional template arguments which can be used for
+ * specifying the actual TProtocol implementation used for optimization
+ * purposes, as virtual calls can completely be eliminated then. If
+ * OutputProtocol is not specified, it is assumed to be the same as
+ * InputProtocol. The protocol properties defined by TClientBase are exposed
+ * with their concrete type (return type covariance).
+ *
+ * In addition to implementing TClientBase!Interface, TClient offers the
+ * following constructors:
+ * ---
+ * this(InputProtocol iprot, OutputProtocol oprot);
+ * // Only if is(InputProtocol == OutputProtocol), to use the same protocol
+ * // for both input and output:
+ * this(InputProtocol prot);
+ * ---
+ *
+ * The sequence id of the method calls starts at zero and is automatically
+ * incremented.
+ */
+interface TClientBase(Interface) if (isBaseService!Interface) : Interface {
+ /**
+ * The input protocol used by the client.
+ */
+ TProtocol inputProtocol() @property;
+
+ /**
+ * The output protocol used by the client.
+ */
+ TProtocol outputProtocol() @property;
+}
+
+/// Ditto
+interface TClientBase(Interface) if (isDerivedService!Interface) :
+ TClientBase!(BaseService!Interface), Interface {}
+
+/// Ditto
+template TClient(Interface, InputProtocol = TProtocol, OutputProtocol = void) if (
+ isService!Interface && isTProtocol!InputProtocol &&
+ (isTProtocol!OutputProtocol || is(OutputProtocol == void))
+) {
+ mixin({
+ static if (isDerivedService!Interface) {
+ string code = "class TClient : TClient!(BaseService!Interface, " ~
+ "InputProtocol, OutputProtocol), TClientBase!Interface {\n";
+ code ~= q{
+ this(IProt iprot, OProt oprot) {
+ super(iprot, oprot);
+ }
+
+ static if (is(IProt == OProt)) {
+ this(IProt prot) {
+ super(prot);
+ }
+ }
+
+ // DMD @@BUG@@: If these are not present in this class (would be)
+ // inherited anyway, »not implemented« errors are raised.
+ override IProt inputProtocol() @property {
+ return super.inputProtocol;
+ }
+ override OProt outputProtocol() @property {
+ return super.outputProtocol;
+ }
+ };
+ } else {
+ string code = "class TClient : TClientBase!Interface {";
+ code ~= q{
+ alias InputProtocol IProt;
+ static if (isTProtocol!OutputProtocol) {
+ alias OutputProtocol OProt;
+ } else {
+ static assert(is(OutputProtocol == void));
+ alias InputProtocol OProt;
+ }
+
+ this(IProt iprot, OProt oprot) {
+ iprot_ = iprot;
+ oprot_ = oprot;
+ }
+
+ static if (is(IProt == OProt)) {
+ this(IProt prot) {
+ this(prot, prot);
+ }
+ }
+
+ IProt inputProtocol() @property {
+ return iprot_;
+ }
+
+ OProt outputProtocol() @property {
+ return oprot_;
+ }
+
+ protected IProt iprot_;
+ protected OProt oprot_;
+ protected int seqid_;
+ };
+ }
+
+ foreach (methodName; __traits(derivedMembers, Interface)) {
+ static if (isSomeFunction!(mixin("Interface." ~ methodName))) {
+ bool methodMetaFound;
+ TMethodMeta methodMeta;
+ static if (is(typeof(Interface.methodMeta) : TMethodMeta[])) {
+ enum meta = find!`a.name == b`(Interface.methodMeta, methodName);
+ if (!meta.empty) {
+ methodMetaFound = true;
+ methodMeta = meta.front;
+ }
+ }
+
+ // Generate the code for sending.
+ string[] paramList;
+ string paramAssignCode;
+ foreach (i, _; ParameterTypeTuple!(mixin("Interface." ~ methodName))) {
+ // Use the param name speficied in the meta information if any –
+ // just cosmetics in this case.
+ string paramName;
+ if (methodMetaFound && i < methodMeta.params.length) {
+ paramName = methodMeta.params[i].name;
+ } else {
+ paramName = "param" ~ to!string(i + 1);
+ }
+
+ immutable storage = ParameterStorageClassTuple!(
+ mixin("Interface." ~ methodName))[i];
+ paramList ~= ((storage & ParameterStorageClass.ref_) ? "ref " : "") ~
+ "ParameterTypeTuple!(Interface." ~ methodName ~ ")[" ~
+ to!string(i) ~ "] " ~ paramName;
+ paramAssignCode ~= "args." ~ paramName ~ " = &" ~ paramName ~ ";\n";
+ }
+ code ~= "ReturnType!(Interface." ~ methodName ~ ") " ~ methodName ~
+ "(" ~ ctfeJoin(paramList) ~ ") {\n";
+
+ code ~= "immutable methodName = `" ~ methodName ~ "`;\n";
+
+ immutable paramStructType =
+ "TPargsStruct!(Interface, `" ~ methodName ~ "`)";
+ code ~= paramStructType ~ " args = " ~ paramStructType ~ "();\n";
+ code ~= paramAssignCode;
+ code ~= "oprot_.writeMessageBegin(TMessage(`" ~ methodName ~ "`, ";
+ code ~= ((methodMetaFound && methodMeta.type == TMethodType.ONEWAY)
+ ? "TMessageType.ONEWAY" : "TMessageType.CALL");
+ code ~= ", ++seqid_));\n";
+ code ~= "args.write(oprot_);\n";
+ code ~= "oprot_.writeMessageEnd();\n";
+ code ~= "oprot_.transport.flush();\n";
+
+ // If this is not a oneway method, generate the receiving code.
+ if (!methodMetaFound || methodMeta.type != TMethodType.ONEWAY) {
+ code ~= "TPresultStruct!(Interface, `" ~ methodName ~ "`) result;\n";
+
+ if (!is(ReturnType!(mixin("Interface." ~ methodName)) == void)) {
+ code ~= "ReturnType!(Interface." ~ methodName ~ ") _return;\n";
+ code ~= "result.success = &_return;\n";
+ }
+
+ // TODO: The C++ implementation checks for matching method name here,
+ // should we do as well?
+ code ~= q{
+ auto msg = iprot_.readMessageBegin();
+ scope (exit) {
+ iprot_.readMessageEnd();
+ iprot_.transport.readEnd();
+ }
+
+ if (msg.type == TMessageType.EXCEPTION) {
+ auto x = new TApplicationException(null);
+ x.read(iprot_);
+ iprot_.transport.readEnd();
+ throw x;
+ }
+ if (msg.type != TMessageType.REPLY) {
+ skip(iprot_, TType.STRUCT);
+ iprot_.transport.readEnd();
+ }
+ if (msg.seqid != seqid_) {
+ throw new TApplicationException(
+ methodName ~ " failed: Out of sequence response.",
+ TApplicationException.Type.BAD_SEQUENCE_ID
+ );
+ }
+ result.read(iprot_);
+ };
+
+ if (methodMetaFound) {
+ foreach (e; methodMeta.exceptions) {
+ code ~= "if (result.isSet!`" ~ e.name ~ "`) throw result." ~
+ e.name ~ ";\n";
+ }
+ }
+
+ if (!is(ReturnType!(mixin("Interface." ~ methodName)) == void)) {
+ code ~= q{
+ if (result.isSet!`success`) return _return;
+ throw new TApplicationException(
+ methodName ~ " failed: Unknown result.",
+ TApplicationException.Type.MISSING_RESULT
+ );
+ };
+ }
+ }
+ code ~= "}\n";
+ }
+ }
+
+ code ~= "}\n";
+ return code;
+ }());
+}
+
+/**
+ * TClient construction helper to avoid having to explicitly specify
+ * the protocol types, i.e. to allow the constructor being called using IFTI
+ * (see $(DMDBUG 6082, D Bugzilla enhancement requet 6082)).
+ */
+TClient!(Interface, Prot) tClient(Interface, Prot)(Prot prot) if (
+ isService!Interface && isTProtocol!Prot
+) {
+ return new TClient!(Interface, Prot)(prot);
+}
+
+/// Ditto
+TClient!(Interface, IProt, Oprot) tClient(Interface, IProt, OProt)
+ (IProt iprot, OProt oprot) if (
+ isService!Interface && isTProtocol!IProt && isTProtocol!OProt
+) {
+ return new TClient!(Interface, IProt, OProt)(iprot, oprot);
+}
+
+/**
+ * Represents the arguments of a Thrift method call, as pointers to the (const)
+ * parameter type to avoid copying.
+ *
+ * There should usually be no reason to use this struct directly without the
+ * help of TClient, but it is documented publicly to help debugging in case
+ * of CTFE errors.
+ *
+ * Consider this example:
+ * ---
+ * interface Foo {
+ * int bar(string a, bool b);
+ *
+ * enum methodMeta = [
+ * TMethodMeta("bar", [TParamMeta("a", 1), TParamMeta("b", 2)])
+ * ];
+ * }
+ *
+ * alias TPargsStruct!(Foo, "bar") FooBarPargs;
+ * ---
+ *
+ * The definition of FooBarPargs is equivalent to (ignoring the necessary
+ * metadata to assign the field IDs):
+ * ---
+ * struct FooBarPargs {
+ * const(string)* a;
+ * const(bool)* b;
+ *
+ * void write(Protocol)(Protocol proto) const if (isTProtocol!Protocol);
+ * }
+ * ---
+ */
+template TPargsStruct(Interface, string methodName) {
+ static assert(is(typeof(mixin("Interface." ~ methodName))),
+ "Could not find method '" ~ methodName ~ "' in '" ~ Interface.stringof ~ "'.");
+ mixin({
+ bool methodMetaFound;
+ TMethodMeta methodMeta;
+ static if (is(typeof(Interface.methodMeta) : TMethodMeta[])) {
+ auto meta = find!`a.name == b`(Interface.methodMeta, methodName);
+ if (!meta.empty) {
+ methodMetaFound = true;
+ methodMeta = meta.front;
+ }
+ }
+
+ string memberCode;
+ string[] fieldMetaCodes;
+ foreach (i, _; ParameterTypeTuple!(mixin("Interface." ~ methodName))) {
+ // If we have no meta information, just use param1, param2, etc. as
+ // field names, it shouldn't really matter anyway. 1-based »indexing«
+ // is used to match the common scheme in the Thrift world.
+ string memberId;
+ string memberName;
+ if (methodMetaFound && i < methodMeta.params.length) {
+ memberId = to!string(methodMeta.params[i].id);
+ memberName = methodMeta.params[i].name;
+ } else {
+ memberId = to!string(i + 1);
+ memberName = "param" ~ to!string(i + 1);
+ }
+
+ // Workaround for DMD @@BUG@@ 6056: make an intermediary alias for the
+ // parameter type, and declare the member using const(memberNameType)*.
+ memberCode ~= "alias ParameterTypeTuple!(Interface." ~ methodName ~
+ ")[" ~ to!string(i) ~ "] " ~ memberName ~ "Type;\n";
+ memberCode ~= "const(" ~ memberName ~ "Type)* " ~ memberName ~ ";\n";
+
+ fieldMetaCodes ~= "TFieldMeta(`" ~ memberName ~ "`, " ~ memberId ~
+ ", TReq.OPT_IN_REQ_OUT)";
+ }
+
+ string code = "struct TPargsStruct {\n";
+ code ~= memberCode;
+ version (TVerboseCodegen) {
+ if (!methodMetaFound &&
+ ParameterTypeTuple!(mixin("Interface." ~ methodName)).length > 0)
+ {
+ code ~= "pragma(msg, `[thrift.codegen.base.TPargsStruct] Warning: No " ~
+ "meta information for method '" ~ methodName ~ "' in service '" ~
+ Interface.stringof ~ "' found.`);\n";
+ }
+ }
+ code ~= "void write(P)(P proto) const if (isTProtocol!P) {\n";
+ code ~= "writeStruct!(typeof(this), P, [" ~ ctfeJoin(fieldMetaCodes) ~
+ "], true)(this, proto);\n";
+ code ~= "}\n";
+ code ~= "}\n";
+ return code;
+ }());
+}
+
+/**
+ * Represents the result of a Thrift method call, using a pointer to the return
+ * value to avoid copying.
+ *
+ * There should usually be no reason to use this struct directly without the
+ * help of TClient, but it is documented publicly to help debugging in case
+ * of CTFE errors.
+ *
+ * Consider this example:
+ * ---
+ * interface Foo {
+ * int bar(string a);
+ *
+ * alias .FooException FooException;
+ *
+ * enum methodMeta = [
+ * TMethodMeta("bar",
+ * [TParamMeta("a", 1)],
+ * [TExceptionMeta("fooe", 1, "FooException")]
+ * )
+ * ];
+ * }
+ * alias TPresultStruct!(Foo, "bar") FooBarPresult;
+ * ---
+ *
+ * The definition of FooBarPresult is equivalent to (ignoring the necessary
+ * metadata to assign the field IDs):
+ * ---
+ * struct FooBarPresult {
+ * int* success;
+ * Foo.FooException fooe;
+ *
+ * struct IsSetFlags {
+ * bool success;
+ * }
+ * IsSetFlags isSetFlags;
+ *
+ * bool isSet(string fieldName)() const @property;
+ * void read(Protocol)(Protocol proto) if (isTProtocol!Protocol);
+ * }
+ * ---
+ */
+template TPresultStruct(Interface, string methodName) {
+ static assert(is(typeof(mixin("Interface." ~ methodName))),
+ "Could not find method '" ~ methodName ~ "' in '" ~ Interface.stringof ~ "'.");
+
+ mixin({
+ string code = "struct TPresultStruct {\n";
+
+ string[] fieldMetaCodes;
+
+ alias ReturnType!(mixin("Interface." ~ methodName)) ResultType;
+ static if (!is(ResultType == void)) {
+ code ~= q{
+ ReturnType!(mixin("Interface." ~ methodName))* success;
+ };
+ fieldMetaCodes ~= "TFieldMeta(`success`, 0, TReq.OPTIONAL)";
+
+ static if (!isNullable!ResultType) {
+ code ~= q{
+ struct IsSetFlags {
+ bool success;
+ }
+ IsSetFlags isSetFlags;
+ };
+ fieldMetaCodes ~= "TFieldMeta(`isSetFlags`, 0, TReq.IGNORE)";
+ }
+ }
+
+ bool methodMetaFound;
+ static if (is(typeof(Interface.methodMeta) : TMethodMeta[])) {
+ auto meta = find!`a.name == b`(Interface.methodMeta, methodName);
+ if (!meta.empty) {
+ foreach (e; meta.front.exceptions) {
+ code ~= "Interface." ~ e.type ~ " " ~ e.name ~ ";\n";
+ fieldMetaCodes ~= "TFieldMeta(`" ~ e.name ~ "`, " ~ to!string(e.id) ~
+ ", TReq.OPTIONAL)";
+ }
+ methodMetaFound = true;
+ }
+ }
+
+ version (TVerboseCodegen) {
+ if (!methodMetaFound &&
+ ParameterTypeTuple!(mixin("Interface." ~ methodName)).length > 0)
+ {
+ code ~= "pragma(msg, `[thrift.codegen.base.TPresultStruct] Warning: No " ~
+ "meta information for method '" ~ methodName ~ "' in service '" ~
+ Interface.stringof ~ "' found.`);\n";
+ }
+ }
+
+ code ~= q{
+ bool isSet(string fieldName)() const @property if (
+ is(MemberType!(typeof(this), fieldName))
+ ) {
+ static if (fieldName == "success") {
+ static if (isNullable!(typeof(*success))) {
+ return *success !is null;
+ } else {
+ return isSetFlags.success;
+ }
+ } else {
+ // We are dealing with an exception member, which, being a nullable
+ // type (exceptions are always classes), has no isSet flag.
+ return __traits(getMember, this, fieldName) !is null;
+ }
+ }
+ };
+
+ code ~= "void read(P)(P proto) if (isTProtocol!P) {\n";
+ code ~= "readStruct!(typeof(this), P, [" ~ ctfeJoin(fieldMetaCodes) ~
+ "], true)(this, proto);\n";
+ code ~= "}\n";
+ code ~= "}\n";
+ return code;
+ }());
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/codegen/client_pool.d b/src/jaegertracing/thrift/lib/d/src/thrift/codegen/client_pool.d
new file mode 100644
index 000000000..c46b74344
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/codegen/client_pool.d
@@ -0,0 +1,262 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.codegen.client_pool;
+
+import core.time : dur, Duration, TickDuration;
+import std.traits : ParameterTypeTuple, ReturnType;
+import thrift.base;
+import thrift.codegen.base;
+import thrift.codegen.client;
+import thrift.internal.codegen;
+import thrift.internal.resource_pool;
+
+/**
+ * Manages a pool of TClients for the given interface, forwarding RPC calls to
+ * members of the pool.
+ *
+ * If a request fails, another client from the pool is tried, and optionally,
+ * a client is disabled for a configurable amount of time if it fails too
+ * often. If all clients fail (and keepTrying is false), a
+ * TCompoundOperationException is thrown, containing all the collected RPC
+ * exceptions.
+ */
+class TClientPool(Interface) if (isService!Interface) : Interface {
+ /// Shorthand for TClientBase!Interface, the client type this instance
+ /// operates on.
+ alias TClientBase!Interface Client;
+
+ /**
+ * Creates a new instance and adds the given clients to the pool.
+ */
+ this(Client[] clients) {
+ pool_ = new TResourcePool!Client(clients);
+
+ rpcFaultFilter = (Exception e) {
+ import thrift.protocol.base;
+ import thrift.transport.base;
+ return (
+ (cast(TTransportException)e !is null) ||
+ (cast(TApplicationException)e !is null)
+ );
+ };
+ }
+
+ /**
+ * Executes an operation on the first currently active client.
+ *
+ * If the operation fails (throws an exception for which rpcFaultFilter is
+ * true), the failure is recorded and the next client in the pool is tried.
+ *
+ * Throws: Any non-rpc exception that occurs, a TCompoundOperationException
+ * if all clients failed with an rpc exception (if keepTrying is false).
+ *
+ * Example:
+ * ---
+ * interface Foo { string bar(); }
+ * auto poolClient = tClientPool([tClient!Foo(someProtocol)]);
+ * auto result = poolClient.execute((c){ return c.bar(); });
+ * ---
+ */
+ ResultType execute(ResultType)(scope ResultType delegate(Client) work) {
+ return executeOnPool!Client(work);
+ }
+
+ /**
+ * Adds a client to the pool.
+ */
+ void addClient(Client client) {
+ pool_.add(client);
+ }
+
+ /**
+ * Removes a client from the pool.
+ *
+ * Returns: Whether the client was found in the pool.
+ */
+ bool removeClient(Client client) {
+ return pool_.remove(client);
+ }
+
+ mixin(poolForwardCode!Interface());
+
+ /// Whether to open the underlying transports of a client before trying to
+ /// execute a method if they are not open. This is usually desirable
+ /// because it allows e.g. to automatically reconnect to a remote server
+ /// if the network connection is dropped.
+ ///
+ /// Defaults to true.
+ bool reopenTransports = true;
+
+ /// Called to determine whether an exception comes from a client from the
+ /// pool not working properly, or if it an exception thrown at the
+ /// application level.
+ ///
+ /// If the delegate returns true, the server/connection is considered to be
+ /// at fault, if it returns false, the exception is just passed on to the
+ /// caller.
+ ///
+ /// By default, returns true for instances of TTransportException and
+ /// TApplicationException, false otherwise.
+ bool delegate(Exception) rpcFaultFilter;
+
+ /**
+ * Whether to keep trying to find a working client if all have failed in a
+ * row.
+ *
+ * Defaults to false.
+ */
+ bool keepTrying() const @property {
+ return pool_.cycle;
+ }
+
+ /// Ditto
+ void keepTrying(bool value) @property {
+ pool_.cycle = value;
+ }
+
+ /**
+ * Whether to use a random permutation of the client pool on every call to
+ * execute(). This can be used e.g. as a simple form of load balancing.
+ *
+ * Defaults to true.
+ */
+ bool permuteClients() const @property {
+ return pool_.permute;
+ }
+
+ /// Ditto
+ void permuteClients(bool value) @property {
+ pool_.permute = value;
+ }
+
+ /**
+ * The number of consecutive faults after which a client is disabled until
+ * faultDisableDuration has passed. 0 to never disable clients.
+ *
+ * Defaults to 0.
+ */
+ ushort faultDisableCount() @property {
+ return pool_.faultDisableCount;
+ }
+
+ /// Ditto
+ void faultDisableCount(ushort value) @property {
+ pool_.faultDisableCount = value;
+ }
+
+ /**
+ * The duration for which a client is no longer considered after it has
+ * failed too often.
+ *
+ * Defaults to one second.
+ */
+ Duration faultDisableDuration() @property {
+ return pool_.faultDisableDuration;
+ }
+
+ /// Ditto
+ void faultDisableDuration(Duration value) @property {
+ pool_.faultDisableDuration = value;
+ }
+
+protected:
+ ResultType executeOnPool(ResultType)(scope ResultType delegate(Client) work) {
+ auto clients = pool_[];
+ if (clients.empty) {
+ throw new TException("No clients available to try.");
+ }
+
+ while (true) {
+ Exception[] rpcExceptions;
+ while (!clients.empty) {
+ auto c = clients.front;
+ clients.popFront;
+ try {
+ scope (success) {
+ pool_.recordSuccess(c);
+ }
+
+ if (reopenTransports) {
+ c.inputProtocol.transport.open();
+ c.outputProtocol.transport.open();
+ }
+
+ return work(c);
+ } catch (Exception e) {
+ if (rpcFaultFilter && rpcFaultFilter(e)) {
+ pool_.recordFault(c);
+ rpcExceptions ~= e;
+ } else {
+ // We are dealing with a normal exception thrown by the
+ // server-side method, just pass it on. As far as we are
+ // concerned, the method call succeeded.
+ pool_.recordSuccess(c);
+ throw e;
+ }
+ }
+ }
+
+ // If we get here, no client succeeded during the current iteration.
+ Duration waitTime;
+ Client dummy;
+ if (clients.willBecomeNonempty(dummy, waitTime)) {
+ if (waitTime > dur!"hnsecs"(0)) {
+ import core.thread;
+ Thread.sleep(waitTime);
+ }
+ } else {
+ throw new TCompoundOperationException("All clients failed.",
+ rpcExceptions);
+ }
+ }
+ }
+
+private:
+ TResourcePool!Client pool_;
+}
+
+private {
+ // Cannot use an anonymous delegate literal for this because they aren't
+ // allowed in class scope.
+ string poolForwardCode(Interface)() {
+ string code = "";
+
+ foreach (methodName; AllMemberMethodNames!Interface) {
+ enum qn = "Interface." ~ methodName;
+ code ~= "ReturnType!(" ~ qn ~ ") " ~ methodName ~
+ "(ParameterTypeTuple!(" ~ qn ~ ") args) {\n";
+ code ~= "return executeOnPool((Client c){ return c." ~
+ methodName ~ "(args); });\n";
+ code ~= "}\n";
+ }
+
+ return code;
+ }
+}
+
+/**
+ * TClientPool construction helper to avoid having to explicitly specify
+ * the interface type, i.e. to allow the constructor being called using IFTI
+ * (see $(DMDBUG 6082, D Bugzilla enhancement requet 6082)).
+ */
+TClientPool!Interface tClientPool(Interface)(
+ TClientBase!Interface[] clients
+) if (isService!Interface) {
+ return new typeof(return)(clients);
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/codegen/idlgen.d b/src/jaegertracing/thrift/lib/d/src/thrift/codegen/idlgen.d
new file mode 100644
index 000000000..9f889368c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/codegen/idlgen.d
@@ -0,0 +1,770 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Contains <b>experimental</b> functionality for generating Thrift IDL files
+ * (.thrift) from existing D data structures, i.e. the reverse of what the
+ * Thrift compiler does.
+ */
+module thrift.codegen.idlgen;
+
+import std.algorithm : find;
+import std.array : empty, front;
+import std.conv : to;
+import std.traits : EnumMembers, isSomeFunction, OriginalType,
+ ParameterTypeTuple, ReturnType;
+import std.typetuple : allSatisfy, staticIndexOf, staticMap, NoDuplicates,
+ TypeTuple;
+import thrift.base;
+import thrift.codegen.base;
+import thrift.internal.codegen;
+import thrift.internal.ctfe;
+import thrift.util.hashset;
+
+/**
+ * True if the passed type is a Thrift entity (struct, exception, enum,
+ * service).
+ */
+alias Any!(isStruct, isException, isEnum, isService) isThriftEntity;
+
+/**
+ * Returns an IDL string describing the passed »root« entities and all types
+ * they depend on.
+ */
+template idlString(Roots...) if (allSatisfy!(isThriftEntity, Roots)) {
+ enum idlString = idlStringImpl!Roots.result;
+}
+
+private {
+ template idlStringImpl(Roots...) if (allSatisfy!(isThriftEntity, Roots)) {
+ alias ForAllWithList!(
+ ConfinedTuple!(StaticFilter!(isService, Roots)),
+ AddBaseServices
+ ) Services;
+
+ alias TypeTuple!(
+ StaticFilter!(isEnum, Roots),
+ ForAllWithList!(
+ ConfinedTuple!(
+ StaticFilter!(Any!(isException, isStruct), Roots),
+ staticMap!(CompositeTypeDeps, staticMap!(ServiceTypeDeps, Services))
+ ),
+ AddStructWithDeps
+ )
+ ) Types;
+
+ enum result = ctfeJoin(
+ [
+ staticMap!(
+ enumIdlString,
+ StaticFilter!(isEnum, Types)
+ ),
+ staticMap!(
+ structIdlString,
+ StaticFilter!(Any!(isStruct, isException), Types)
+ ),
+ staticMap!(
+ serviceIdlString,
+ Services
+ )
+ ],
+ "\n"
+ );
+ }
+
+ template ServiceTypeDeps(T) if (isService!T) {
+ alias staticMap!(
+ PApply!(MethodTypeDeps, T),
+ FilterMethodNames!(T, __traits(derivedMembers, T))
+ ) ServiceTypeDeps;
+ }
+
+ template MethodTypeDeps(T, string name) if (
+ isService!T && isSomeFunction!(MemberType!(T, name))
+ ) {
+ alias TypeTuple!(
+ ReturnType!(MemberType!(T, name)),
+ ParameterTypeTuple!(MemberType!(T, name)),
+ ExceptionTypes!(T, name)
+ ) MethodTypeDeps;
+ }
+
+ template ExceptionTypes(T, string name) if (
+ isService!T && isSomeFunction!(MemberType!(T, name))
+ ) {
+ mixin({
+ enum meta = find!`a.name == b`(getMethodMeta!T, name);
+ if (meta.empty) return "alias TypeTuple!() ExceptionTypes;";
+
+ string result = "alias TypeTuple!(";
+ foreach (i, e; meta.front.exceptions) {
+ if (i > 0) result ~= ", ";
+ result ~= "mixin(`T." ~ e.type ~ "`)";
+ }
+ result ~= ") ExceptionTypes;";
+ return result;
+ }());
+ }
+
+ template AddBaseServices(T, List...) {
+ static if (staticIndexOf!(T, List) == -1) {
+ alias NoDuplicates!(BaseServices!T, List) AddBaseServices;
+ } else {
+ alias List AddStructWithDeps;
+ }
+ }
+
+ unittest {
+ interface A {}
+ interface B : A {}
+ interface C : B {}
+ interface D : A {}
+
+ static assert(is(AddBaseServices!(C) == TypeTuple!(A, B, C)));
+ static assert(is(ForAllWithList!(ConfinedTuple!(C, D), AddBaseServices) ==
+ TypeTuple!(A, D, B, C)));
+ }
+
+ template BaseServices(T, Rest...) if (isService!T) {
+ static if (isDerivedService!T) {
+ alias BaseServices!(BaseService!T, T, Rest) BaseServices;
+ } else {
+ alias TypeTuple!(T, Rest) BaseServices;
+ }
+ }
+
+ template AddStructWithDeps(T, List...) {
+ static if (staticIndexOf!(T, List) == -1) {
+ // T is not already in the List, so add T and the types it depends on in
+ // the front. Because with the Thrift compiler types can only depend on
+ // other types that have already been defined, we collect all the
+ // dependencies, prepend them to the list, and then prune the duplicates
+ // (keeping the first occurrences). If this requirement should ever be
+ // dropped from Thrift, this could be easily adapted to handle circular
+ // dependencies by passing TypeTuple!(T, List) to ForAllWithList instead
+ // of appending List afterwards, and removing the now unnecessary
+ // NoDuplicates.
+ alias NoDuplicates!(
+ ForAllWithList!(
+ ConfinedTuple!(
+ staticMap!(
+ CompositeTypeDeps,
+ staticMap!(
+ PApply!(MemberType, T),
+ FieldNames!T
+ )
+ )
+ ),
+ .AddStructWithDeps,
+ T
+ ),
+ List
+ ) AddStructWithDeps;
+ } else {
+ alias List AddStructWithDeps;
+ }
+ }
+
+ version (unittest) {
+ struct A {}
+ struct B {
+ A a;
+ int b;
+ A c;
+ string d;
+ }
+ struct C {
+ B b;
+ A a;
+ }
+
+ static assert(is(AddStructWithDeps!C == TypeTuple!(A, B, C)));
+
+ struct D {
+ C c;
+ mixin TStructHelpers!([TFieldMeta("c", 0, TReq.IGNORE)]);
+ }
+ static assert(is(AddStructWithDeps!D == TypeTuple!(D)));
+ }
+
+ version (unittest) {
+ // Circles in the type dependency graph are not allowed in Thrift, but make
+ // sure we fail in a sane way instead of crashing the compiler.
+
+ struct Rec1 {
+ Rec2[] other;
+ }
+
+ struct Rec2 {
+ Rec1[] other;
+ }
+
+ static assert(!__traits(compiles, AddStructWithDeps!Rec1));
+ }
+
+ /*
+ * Returns the non-primitive types T directly depends on.
+ *
+ * For example, CompositeTypeDeps!int would yield an empty type tuple,
+ * CompositeTypeDeps!SomeStruct would give SomeStruct, and
+ * CompositeTypeDeps!(A[B]) both CompositeTypeDeps!A and CompositeTypeDeps!B.
+ */
+ template CompositeTypeDeps(T) {
+ static if (is(FullyUnqual!T == bool) || is(FullyUnqual!T == byte) ||
+ is(FullyUnqual!T == short) || is(FullyUnqual!T == int) ||
+ is(FullyUnqual!T == long) || is(FullyUnqual!T : string) ||
+ is(FullyUnqual!T == double) || is(FullyUnqual!T == void)
+ ) {
+ alias TypeTuple!() CompositeTypeDeps;
+ } else static if (is(FullyUnqual!T _ : U[], U)) {
+ alias CompositeTypeDeps!U CompositeTypeDeps;
+ } else static if (is(FullyUnqual!T _ : HashSet!E, E)) {
+ alias CompositeTypeDeps!E CompositeTypeDeps;
+ } else static if (is(FullyUnqual!T _ : V[K], K, V)) {
+ alias TypeTuple!(CompositeTypeDeps!K, CompositeTypeDeps!V) CompositeTypeDeps;
+ } else static if (is(FullyUnqual!T == enum) || is(FullyUnqual!T == struct) ||
+ is(FullyUnqual!T : TException)
+ ) {
+ alias TypeTuple!(FullyUnqual!T) CompositeTypeDeps;
+ } else {
+ static assert(false, "Cannot represent type in Thrift: " ~ T.stringof);
+ }
+ }
+}
+
+/**
+ * Returns an IDL string describing the passed service. IDL code for any type
+ * dependcies is not included.
+ */
+template serviceIdlString(T) if (isService!T) {
+ enum serviceIdlString = {
+ string result = "service " ~ T.stringof;
+ static if (isDerivedService!T) {
+ result ~= " extends " ~ BaseService!T.stringof;
+ }
+ result ~= " {\n";
+
+ foreach (methodName; FilterMethodNames!(T, __traits(derivedMembers, T))) {
+ result ~= " ";
+
+ enum meta = find!`a.name == b`(T.methodMeta, methodName);
+
+ static if (!meta.empty && meta.front.type == TMethodType.ONEWAY) {
+ result ~= "oneway ";
+ }
+
+ alias ReturnType!(MemberType!(T, methodName)) RT;
+ static if (is(RT == void)) {
+ // We special-case this here instead of adding void to dToIdlType to
+ // avoid accepting things like void[].
+ result ~= "void ";
+ } else {
+ result ~= dToIdlType!RT ~ " ";
+ }
+ result ~= methodName ~ "(";
+
+ short lastId;
+ foreach (i, ParamType; ParameterTypeTuple!(MemberType!(T, methodName))) {
+ static if (!meta.empty && i < meta.front.params.length) {
+ enum havePM = true;
+ } else {
+ enum havePM = false;
+ }
+
+ short id;
+ static if (havePM) {
+ id = meta.front.params[i].id;
+ } else {
+ id = --lastId;
+ }
+
+ string paramName;
+ static if (havePM) {
+ paramName = meta.front.params[i].name;
+ } else {
+ paramName = "param" ~ to!string(i + 1);
+ }
+
+ result ~= to!string(id) ~ ": " ~ dToIdlType!ParamType ~ " " ~ paramName;
+
+ static if (havePM && !meta.front.params[i].defaultValue.empty) {
+ result ~= " = " ~ dToIdlConst(mixin(meta.front.params[i].defaultValue));
+ } else {
+ // Unfortunately, getting the default value for parameters from a
+ // function alias isn't possible – we can't transfer the default
+ // value to the IDL e.g. for interface Foo { void foo(int a = 5); }
+ // without the user explicitly declaring it in metadata.
+ }
+ result ~= ", ";
+ }
+ result ~= ")";
+
+ static if (!meta.empty && !meta.front.exceptions.empty) {
+ result ~= " throws (";
+ foreach (e; meta.front.exceptions) {
+ result ~= to!string(e.id) ~ ": " ~ e.type ~ " " ~ e.name ~ ", ";
+ }
+ result ~= ")";
+ }
+
+ result ~= ",\n";
+ }
+
+ result ~= "}\n";
+ return result;
+ }();
+}
+
+/**
+ * Returns an IDL string describing the passed enum. IDL code for any type
+ * dependcies is not included.
+ */
+template enumIdlString(T) if (isEnum!T) {
+ enum enumIdlString = {
+ static assert(is(OriginalType!T : long),
+ "Can only have integer enums in Thrift (not " ~ OriginalType!T.stringof ~
+ ", for " ~ T.stringof ~ ").");
+
+ string result = "enum " ~ T.stringof ~ " {\n";
+
+ foreach (name; __traits(derivedMembers, T)) {
+ result ~= " " ~ name ~ " = " ~ dToIdlConst(GetMember!(T, name)) ~ ",\n";
+ }
+
+ result ~= "}\n";
+ return result;
+ }();
+}
+
+/**
+ * Returns an IDL string describing the passed struct. IDL code for any type
+ * dependcies is not included.
+ */
+template structIdlString(T) if (isStruct!T || isException!T) {
+ enum structIdlString = {
+ mixin({
+ string code = "";
+ foreach (field; getFieldMeta!T) {
+ code ~= "static assert(is(MemberType!(T, `" ~ field.name ~ "`)));\n";
+ }
+ return code;
+ }());
+
+ string result;
+ static if (isException!T) {
+ result = "exception ";
+ } else {
+ result = "struct ";
+ }
+ result ~= T.stringof ~ " {\n";
+
+ // The last automatically assigned id – fields with no meta information
+ // are assigned (in lexical order) descending negative ids, starting with
+ // -1, just like the Thrift compiler does.
+ short lastId;
+
+ foreach (name; FieldNames!T) {
+ enum meta = find!`a.name == b`(getFieldMeta!T, name);
+
+ static if (meta.empty || meta.front.req != TReq.IGNORE) {
+ short id;
+ static if (meta.empty) {
+ id = --lastId;
+ } else {
+ id = meta.front.id;
+ }
+
+ result ~= " " ~ to!string(id) ~ ":";
+ static if (!meta.empty) {
+ result ~= dToIdlReq(meta.front.req);
+ }
+ result ~= " " ~ dToIdlType!(MemberType!(T, name)) ~ " " ~ name;
+
+ static if (!meta.empty && !meta.front.defaultValue.empty) {
+ result ~= " = " ~ dToIdlConst(mixin(meta.front.defaultValue));
+ } else static if (__traits(compiles, fieldInitA!(T, name))) {
+ static if (is(typeof(fieldInitA!(T, name))) &&
+ !is(typeof(fieldInitA!(T, name)) == void)
+ ) {
+ result ~= " = " ~ dToIdlConst(fieldInitA!(T, name));
+ }
+ } else static if (is(typeof(fieldInitB!(T, name))) &&
+ !is(typeof(fieldInitB!(T, name)) == void)
+ ) {
+ result ~= " = " ~ dToIdlConst(fieldInitB!(T, name));
+ }
+ result ~= ",\n";
+ }
+ }
+
+ result ~= "}\n";
+ return result;
+ }();
+}
+
+private {
+ // This very convoluted way of doing things was chosen because putting the
+ // static if directly into structIdlString caused »not evaluatable at compile
+ // time« errors to slip through even though typeof() was used, resp. the
+ // condition to be true even though the value couldn't actually be read at
+ // compile time due to a @@BUG@@ in DMD 2.055.
+ // The extra »compiled« field in fieldInitA is needed because we must not try
+ // to use != if !is compiled as well (but was false), e.g. for floating point
+ // types.
+ template fieldInitA(T, string name) {
+ static if (mixin("T.init." ~ name) !is MemberType!(T, name).init) {
+ enum fieldInitA = mixin("T.init." ~ name);
+ }
+ }
+
+ template fieldInitB(T, string name) {
+ static if (mixin("T.init." ~ name) != MemberType!(T, name).init) {
+ enum fieldInitB = mixin("T.init." ~ name);
+ }
+ }
+
+ template dToIdlType(T) {
+ static if (is(FullyUnqual!T == bool)) {
+ enum dToIdlType = "bool";
+ } else static if (is(FullyUnqual!T == byte)) {
+ enum dToIdlType = "byte";
+ } else static if (is(FullyUnqual!T == double)) {
+ enum dToIdlType = "double";
+ } else static if (is(FullyUnqual!T == short)) {
+ enum dToIdlType = "i16";
+ } else static if (is(FullyUnqual!T == int)) {
+ enum dToIdlType = "i32";
+ } else static if (is(FullyUnqual!T == long)) {
+ enum dToIdlType = "i64";
+ } else static if (is(FullyUnqual!T : string)) {
+ enum dToIdlType = "string";
+ } else static if (is(FullyUnqual!T _ : U[], U)) {
+ enum dToIdlType = "list<" ~ dToIdlType!U ~ ">";
+ } else static if (is(FullyUnqual!T _ : V[K], K, V)) {
+ enum dToIdlType = "map<" ~ dToIdlType!K ~ ", " ~ dToIdlType!V ~ ">";
+ } else static if (is(FullyUnqual!T _ : HashSet!E, E)) {
+ enum dToIdlType = "set<" ~ dToIdlType!E ~ ">";
+ } else static if (is(FullyUnqual!T == struct) || is(FullyUnqual!T == enum) ||
+ is(FullyUnqual!T : TException)
+ ) {
+ enum dToIdlType = FullyUnqual!(T).stringof;
+ } else {
+ static assert(false, "Cannot represent type in Thrift: " ~ T.stringof);
+ }
+ }
+
+ string dToIdlReq(TReq req) {
+ switch (req) {
+ case TReq.REQUIRED: return " required";
+ case TReq.OPTIONAL: return " optional";
+ default: return "";
+ }
+ }
+
+ string dToIdlConst(T)(T value) {
+ static if (is(FullyUnqual!T == bool)) {
+ return value ? "1" : "0";
+ } else static if (is(FullyUnqual!T == byte) ||
+ is(FullyUnqual!T == short) || is(FullyUnqual!T == int) ||
+ is(FullyUnqual!T == long)
+ ) {
+ return to!string(value);
+ } else static if (is(FullyUnqual!T : string)) {
+ return `"` ~ to!string(value) ~ `"`;
+ } else static if (is(FullyUnqual!T == double)) {
+ return ctfeToString(value);
+ } else static if (is(FullyUnqual!T _ : U[], U) ||
+ is(FullyUnqual!T _ : HashSet!E, E)
+ ) {
+ string result = "[";
+ foreach (e; value) {
+ result ~= dToIdlConst(e) ~ ", ";
+ }
+ result ~= "]";
+ return result;
+ } else static if (is(FullyUnqual!T _ : V[K], K, V)) {
+ string result = "{";
+ foreach (key, val; value) {
+ result ~= dToIdlConst(key) ~ ": " ~ dToIdlConst(val) ~ ", ";
+ }
+ result ~= "}";
+ return result;
+ } else static if (is(FullyUnqual!T == enum)) {
+ import std.conv;
+ import std.traits;
+ return to!string(cast(OriginalType!T)value);
+ } else static if (is(FullyUnqual!T == struct) ||
+ is(FullyUnqual!T : TException)
+ ) {
+ string result = "{";
+ foreach (name; __traits(derivedMembers, T)) {
+ static if (memberReq!(T, name) != TReq.IGNORE) {
+ result ~= name ~ ": " ~ dToIdlConst(mixin("value." ~ name)) ~ ", ";
+ }
+ }
+ result ~= "}";
+ return result;
+ } else {
+ static assert(false, "Cannot represent type in Thrift: " ~ T.stringof);
+ }
+ }
+}
+
+version (unittest) {
+ enum Foo {
+ a = 1,
+ b = 10,
+ c = 5
+ }
+
+ static assert(enumIdlString!Foo ==
+`enum Foo {
+ a = 1,
+ b = 10,
+ c = 5,
+}
+`);
+}
+
+
+version (unittest) {
+ struct WithoutMeta {
+ string a;
+ int b;
+ }
+
+ struct WithDefaults {
+ string a = "asdf";
+ double b = 3.1415;
+ WithoutMeta c;
+
+ mixin TStructHelpers!([
+ TFieldMeta("c", 1, TReq.init, `WithoutMeta("foo", 3)`)
+ ]);
+ }
+
+ // These are from DebugProtoTest.thrift.
+ struct OneOfEach {
+ bool im_true;
+ bool im_false;
+ byte a_bite;
+ short integer16;
+ int integer32;
+ long integer64;
+ double double_precision;
+ string some_characters;
+ string zomg_unicode;
+ bool what_who;
+ string base64;
+ byte[] byte_list;
+ short[] i16_list;
+ long[] i64_list;
+
+ mixin TStructHelpers!([
+ TFieldMeta(`im_true`, 1),
+ TFieldMeta(`im_false`, 2),
+ TFieldMeta(`a_bite`, 3, TReq.OPT_IN_REQ_OUT, q{cast(byte)127}),
+ TFieldMeta(`integer16`, 4, TReq.OPT_IN_REQ_OUT, q{cast(short)32767}),
+ TFieldMeta(`integer32`, 5),
+ TFieldMeta(`integer64`, 6, TReq.OPT_IN_REQ_OUT, q{10000000000L}),
+ TFieldMeta(`double_precision`, 7),
+ TFieldMeta(`some_characters`, 8),
+ TFieldMeta(`zomg_unicode`, 9),
+ TFieldMeta(`what_who`, 10),
+ TFieldMeta(`base64`, 11),
+ TFieldMeta(`byte_list`, 12, TReq.OPT_IN_REQ_OUT, q{{
+ byte[] v;
+ v ~= cast(byte)1;
+ v ~= cast(byte)2;
+ v ~= cast(byte)3;
+ return v;
+ }()}),
+ TFieldMeta(`i16_list`, 13, TReq.OPT_IN_REQ_OUT, q{{
+ short[] v;
+ v ~= cast(short)1;
+ v ~= cast(short)2;
+ v ~= cast(short)3;
+ return v;
+ }()}),
+ TFieldMeta(`i64_list`, 14, TReq.OPT_IN_REQ_OUT, q{{
+ long[] v;
+ v ~= 1L;
+ v ~= 2L;
+ v ~= 3L;
+ return v;
+ }()})
+ ]);
+ }
+
+ struct Bonk {
+ int type;
+ string message;
+
+ mixin TStructHelpers!([
+ TFieldMeta(`type`, 1),
+ TFieldMeta(`message`, 2)
+ ]);
+ }
+
+ struct HolyMoley {
+ OneOfEach[] big;
+ HashSet!(string[]) contain;
+ Bonk[][string] bonks;
+
+ mixin TStructHelpers!([
+ TFieldMeta(`big`, 1),
+ TFieldMeta(`contain`, 2),
+ TFieldMeta(`bonks`, 3)
+ ]);
+ }
+
+ static assert(structIdlString!WithoutMeta ==
+`struct WithoutMeta {
+ -1: string a,
+ -2: i32 b,
+}
+`);
+
+import std.algorithm;
+ static assert(structIdlString!WithDefaults.startsWith(
+`struct WithDefaults {
+ -1: string a = "asdf",
+ -2: double b = 3.141`));
+
+ static assert(structIdlString!WithDefaults.endsWith(
+`1: WithoutMeta c = {a: "foo", b: 3, },
+}
+`));
+
+ static assert(structIdlString!OneOfEach ==
+`struct OneOfEach {
+ 1: bool im_true,
+ 2: bool im_false,
+ 3: byte a_bite = 127,
+ 4: i16 integer16 = 32767,
+ 5: i32 integer32,
+ 6: i64 integer64 = 10000000000,
+ 7: double double_precision,
+ 8: string some_characters,
+ 9: string zomg_unicode,
+ 10: bool what_who,
+ 11: string base64,
+ 12: list<byte> byte_list = [1, 2, 3, ],
+ 13: list<i16> i16_list = [1, 2, 3, ],
+ 14: list<i64> i64_list = [1, 2, 3, ],
+}
+`);
+
+ static assert(structIdlString!Bonk ==
+`struct Bonk {
+ 1: i32 type,
+ 2: string message,
+}
+`);
+
+ static assert(structIdlString!HolyMoley ==
+`struct HolyMoley {
+ 1: list<OneOfEach> big,
+ 2: set<list<string>> contain,
+ 3: map<string, list<Bonk>> bonks,
+}
+`);
+}
+
+version (unittest) {
+ class ExceptionWithAMap : TException {
+ string blah;
+ string[string] map_field;
+
+ mixin TStructHelpers!([
+ TFieldMeta(`blah`, 1),
+ TFieldMeta(`map_field`, 2)
+ ]);
+ }
+
+ interface Srv {
+ void voidMethod();
+ int primitiveMethod();
+ OneOfEach structMethod();
+ void methodWithDefaultArgs(int something);
+ void onewayMethod();
+ void exceptionMethod();
+
+ alias .ExceptionWithAMap ExceptionWithAMap;
+
+ enum methodMeta = [
+ TMethodMeta(`methodWithDefaultArgs`,
+ [TParamMeta(`something`, 1, q{2})]
+ ),
+ TMethodMeta(`onewayMethod`,
+ [],
+ [],
+ TMethodType.ONEWAY
+ ),
+ TMethodMeta(`exceptionMethod`,
+ [],
+ [
+ TExceptionMeta("a", 1, "ExceptionWithAMap"),
+ TExceptionMeta("b", 2, "ExceptionWithAMap")
+ ]
+ )
+ ];
+ }
+
+ interface ChildSrv : Srv {
+ int childMethod(int arg);
+ }
+
+ static assert(idlString!ChildSrv ==
+`exception ExceptionWithAMap {
+ 1: string blah,
+ 2: map<string, string> map_field,
+}
+
+struct OneOfEach {
+ 1: bool im_true,
+ 2: bool im_false,
+ 3: byte a_bite = 127,
+ 4: i16 integer16 = 32767,
+ 5: i32 integer32,
+ 6: i64 integer64 = 10000000000,
+ 7: double double_precision,
+ 8: string some_characters,
+ 9: string zomg_unicode,
+ 10: bool what_who,
+ 11: string base64,
+ 12: list<byte> byte_list = [1, 2, 3, ],
+ 13: list<i16> i16_list = [1, 2, 3, ],
+ 14: list<i64> i64_list = [1, 2, 3, ],
+}
+
+service Srv {
+ void voidMethod(),
+ i32 primitiveMethod(),
+ OneOfEach structMethod(),
+ void methodWithDefaultArgs(1: i32 something = 2, ),
+ oneway void onewayMethod(),
+ void exceptionMethod() throws (1: ExceptionWithAMap a, 2: ExceptionWithAMap b, ),
+}
+
+service ChildSrv extends Srv {
+ i32 childMethod(-1: i32 param1, ),
+}
+`);
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/codegen/processor.d b/src/jaegertracing/thrift/lib/d/src/thrift/codegen/processor.d
new file mode 100644
index 000000000..5ce7ac605
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/codegen/processor.d
@@ -0,0 +1,497 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.codegen.processor;
+
+import std.algorithm : find;
+import std.array : empty, front;
+import std.conv : to;
+import std.traits : ParameterTypeTuple, ReturnType, Unqual;
+import std.typetuple : allSatisfy, TypeTuple;
+import std.variant : Variant;
+import thrift.base;
+import thrift.codegen.base;
+import thrift.internal.codegen;
+import thrift.internal.ctfe;
+import thrift.protocol.base;
+import thrift.protocol.processor;
+
+/**
+ * Service processor for Interface, which implements TProcessor by
+ * synchronously forwarding requests for the service methods to a handler
+ * implementing Interface.
+ *
+ * The generated class implements TProcessor and additionally allows a
+ * TProcessorEventHandler to be specified via the public eventHandler property.
+ * The constructor takes a single argument of type Interface, which is the
+ * handler to forward the requests to:
+ * ---
+ * this(Interface iface);
+ * TProcessorEventHandler eventHandler;
+ * ---
+ *
+ * If Interface is derived from another service BaseInterface, this class is
+ * also derived from TServiceProcessor!BaseInterface.
+ *
+ * The optional Protocols template tuple parameter can be used to specify
+ * one or more TProtocol implementations to specifically generate code for. If
+ * the actual types of the protocols passed to process() at runtime match one
+ * of the items from the list, the optimized code paths are taken, otherwise,
+ * a generic TProtocol version is used as fallback. For cases where the input
+ * and output protocols differ, TProtocolPair!(InputProtocol, OutputProtocol)
+ * can be used in the Protocols list:
+ * ---
+ * interface FooService { void foo(); }
+ * class FooImpl { override void foo {} }
+ *
+ * // Provides fast path if TBinaryProtocol!TBufferedTransport is used for
+ * // both input and output:
+ * alias TServiceProcessor!(FooService, TBinaryProtocol!TBufferedTransport)
+ * BinaryProcessor;
+ *
+ * auto proc = new BinaryProcessor(new FooImpl());
+ *
+ * // Low overhead.
+ * proc.process(tBinaryProtocol(tBufferTransport(someSocket)));
+ *
+ * // Not in the specialization list – higher overhead.
+ * proc.process(tBinaryProtocol(tFramedTransport(someSocket)));
+ *
+ * // Same as above, but optimized for the Compact protocol backed by a
+ * // TPipedTransport for input and a TBufferedTransport for output.
+ * alias TServiceProcessor!(FooService, TProtocolPair!(
+ * TCompactProtocol!TPipedTransport, TCompactProtocol!TBufferedTransport)
+ * ) MixedProcessor;
+ * ---
+ */
+template TServiceProcessor(Interface, Protocols...) if (
+ isService!Interface && allSatisfy!(isTProtocolOrPair, Protocols)
+) {
+ mixin({
+ static if (is(Interface BaseInterfaces == super) && BaseInterfaces.length > 0) {
+ static assert(BaseInterfaces.length == 1,
+ "Services cannot be derived from more than one parent.");
+
+ string code = "class TServiceProcessor : " ~
+ "TServiceProcessor!(BaseService!Interface) {\n";
+ code ~= "private Interface iface_;\n";
+
+ string constructorCode = "this(Interface iface) {\n";
+ constructorCode ~= "super(iface);\n";
+ constructorCode ~= "iface_ = iface;\n";
+ } else {
+ string code = "class TServiceProcessor : TProcessor {";
+ code ~= q{
+ override bool process(TProtocol iprot, TProtocol oprot,
+ Variant context = Variant()
+ ) {
+ auto msg = iprot.readMessageBegin();
+
+ void writeException(TApplicationException e) {
+ oprot.writeMessageBegin(TMessage(msg.name, TMessageType.EXCEPTION,
+ msg.seqid));
+ e.write(oprot);
+ oprot.writeMessageEnd();
+ oprot.transport.writeEnd();
+ oprot.transport.flush();
+ }
+
+ if (msg.type != TMessageType.CALL && msg.type != TMessageType.ONEWAY) {
+ skip(iprot, TType.STRUCT);
+ iprot.readMessageEnd();
+ iprot.transport.readEnd();
+
+ writeException(new TApplicationException(
+ TApplicationException.Type.INVALID_MESSAGE_TYPE));
+ return false;
+ }
+
+ auto dg = msg.name in processMap_;
+ if (!dg) {
+ skip(iprot, TType.STRUCT);
+ iprot.readMessageEnd();
+ iprot.transport.readEnd();
+
+ writeException(new TApplicationException("Invalid method name: '" ~
+ msg.name ~ "'.", TApplicationException.Type.INVALID_MESSAGE_TYPE));
+
+ return false;
+ }
+
+ (*dg)(msg.seqid, iprot, oprot, context);
+ return true;
+ }
+
+ TProcessorEventHandler eventHandler;
+
+ alias void delegate(int, TProtocol, TProtocol, Variant) ProcessFunc;
+ protected ProcessFunc[string] processMap_;
+ private Interface iface_;
+ };
+
+ string constructorCode = "this(Interface iface) {\n";
+ constructorCode ~= "iface_ = iface;\n";
+ }
+
+ // Generate the handling code for each method, consisting of the dispatch
+ // function, registering it in the constructor, and the actual templated
+ // handler function.
+ foreach (methodName;
+ FilterMethodNames!(Interface, __traits(derivedMembers, Interface))
+ ) {
+ // Register the processing function in the constructor.
+ immutable procFuncName = "process_" ~ methodName;
+ immutable dispatchFuncName = procFuncName ~ "_protocolDispatch";
+ constructorCode ~= "processMap_[`" ~ methodName ~ "`] = &" ~
+ dispatchFuncName ~ ";\n";
+
+ bool methodMetaFound;
+ TMethodMeta methodMeta;
+ static if (is(typeof(Interface.methodMeta) : TMethodMeta[])) {
+ enum meta = find!`a.name == b`(Interface.methodMeta, methodName);
+ if (!meta.empty) {
+ methodMetaFound = true;
+ methodMeta = meta.front;
+ }
+ }
+
+ // The dispatch function to call the specialized handler functions. We
+ // test the protocols if they can be converted to one of the passed
+ // protocol types, and if not, fall back to the generic TProtocol
+ // version of the processing function.
+ code ~= "void " ~ dispatchFuncName ~
+ "(int seqid, TProtocol iprot, TProtocol oprot, Variant context) {\n";
+ code ~= "foreach (Protocol; TypeTuple!(Protocols, TProtocol)) {\n";
+ code ~= q{
+ static if (is(Protocol _ : TProtocolPair!(I, O), I, O)) {
+ alias I IProt;
+ alias O OProt;
+ } else {
+ alias Protocol IProt;
+ alias Protocol OProt;
+ }
+ auto castedIProt = cast(IProt)iprot;
+ auto castedOProt = cast(OProt)oprot;
+ };
+ code ~= "if (castedIProt && castedOProt) {\n";
+ code ~= procFuncName ~
+ "!(IProt, OProt)(seqid, castedIProt, castedOProt, context);\n";
+ code ~= "return;\n";
+ code ~= "}\n";
+ code ~= "}\n";
+ code ~= "throw new TException(`Internal error: Null iprot/oprot " ~
+ "passed to processor protocol dispatch function.`);\n";
+ code ~= "}\n";
+
+ // The actual handler function, templated on the input and output
+ // protocol types.
+ code ~= "void " ~ procFuncName ~ "(IProt, OProt)(int seqid, IProt " ~
+ "iprot, OProt oprot, Variant connectionContext) " ~
+ "if (isTProtocol!IProt && isTProtocol!OProt) {\n";
+ code ~= "TArgsStruct!(Interface, `" ~ methodName ~ "`) args;\n";
+
+ // Store the (qualified) method name in a manifest constant to avoid
+ // having to litter the code below with lots of string manipulation.
+ code ~= "enum methodName = `" ~ methodName ~ "`;\n";
+
+ code ~= q{
+ enum qName = Interface.stringof ~ "." ~ methodName;
+
+ Variant callContext;
+ if (eventHandler) {
+ callContext = eventHandler.createContext(qName, connectionContext);
+ }
+
+ scope (exit) {
+ if (eventHandler) {
+ eventHandler.deleteContext(callContext, qName);
+ }
+ }
+
+ if (eventHandler) eventHandler.preRead(callContext, qName);
+
+ args.read(iprot);
+ iprot.readMessageEnd();
+ iprot.transport.readEnd();
+
+ if (eventHandler) eventHandler.postRead(callContext, qName);
+ };
+
+ code ~= "TResultStruct!(Interface, `" ~ methodName ~ "`) result;\n";
+ code ~= "try {\n";
+
+ // Generate the parameter list to pass to the called iface function.
+ string[] paramList;
+ foreach (i, _; ParameterTypeTuple!(mixin("Interface." ~ methodName))) {
+ string paramName;
+ if (methodMetaFound && i < methodMeta.params.length) {
+ paramName = methodMeta.params[i].name;
+ } else {
+ paramName = "param" ~ to!string(i + 1);
+ }
+ paramList ~= "args." ~ paramName;
+ }
+
+ immutable call = "iface_." ~ methodName ~ "(" ~ ctfeJoin(paramList) ~ ")";
+ if (is(ReturnType!(mixin("Interface." ~ methodName)) == void)) {
+ code ~= call ~ ";\n";
+ } else {
+ code ~= "result.set!`success`(" ~ call ~ ");\n";
+ }
+
+ // If this is not a oneway method, generate the receiving code.
+ if (!methodMetaFound || methodMeta.type != TMethodType.ONEWAY) {
+ if (methodMetaFound) {
+ foreach (e; methodMeta.exceptions) {
+ code ~= "} catch (Interface." ~ e.type ~ " " ~ e.name ~ ") {\n";
+ code ~= "result.set!`" ~ e.name ~ "`(" ~ e.name ~ ");\n";
+ }
+ }
+ code ~= "}\n";
+
+ code ~= q{
+ catch (Exception e) {
+ if (eventHandler) {
+ eventHandler.handlerError(callContext, qName, e);
+ }
+
+ auto x = new TApplicationException(to!string(e));
+ oprot.writeMessageBegin(
+ TMessage(methodName, TMessageType.EXCEPTION, seqid));
+ x.write(oprot);
+ oprot.writeMessageEnd();
+ oprot.transport.writeEnd();
+ oprot.transport.flush();
+ return;
+ }
+
+ if (eventHandler) eventHandler.preWrite(callContext, qName);
+
+ oprot.writeMessageBegin(TMessage(methodName,
+ TMessageType.REPLY, seqid));
+ result.write(oprot);
+ oprot.writeMessageEnd();
+ oprot.transport.writeEnd();
+ oprot.transport.flush();
+
+ if (eventHandler) eventHandler.postWrite(callContext, qName);
+ };
+ } else {
+ // For oneway methods, we obviously cannot notify the client of any
+ // exceptions, just call the event handler if one is set.
+ code ~= "}\n";
+ code ~= q{
+ catch (Exception e) {
+ if (eventHandler) {
+ eventHandler.handlerError(callContext, qName, e);
+ }
+ return;
+ }
+
+ if (eventHandler) eventHandler.onewayComplete(callContext, qName);
+ };
+ }
+ code ~= "}\n";
+
+ }
+
+ code ~= constructorCode ~ "}\n";
+ code ~= "}\n";
+
+ return code;
+ }());
+}
+
+/**
+ * A struct representing the arguments of a Thrift method call.
+ *
+ * There should usually be no reason to use this directly without the help of
+ * TServiceProcessor, but it is documented publicly to help debugging in case
+ * of CTFE errors.
+ *
+ * Consider this example:
+ * ---
+ * interface Foo {
+ * int bar(string a, bool b);
+ *
+ * enum methodMeta = [
+ * TMethodMeta("bar", [TParamMeta("a", 1), TParamMeta("b", 2)])
+ * ];
+ * }
+ *
+ * alias TArgsStruct!(Foo, "bar") FooBarArgs;
+ * ---
+ *
+ * The definition of FooBarArgs is equivalent to:
+ * ---
+ * struct FooBarArgs {
+ * string a;
+ * bool b;
+ *
+ * mixin TStructHelpers!([TFieldMeta("a", 1, TReq.OPT_IN_REQ_OUT),
+ * TFieldMeta("b", 2, TReq.OPT_IN_REQ_OUT)]);
+ * }
+ * ---
+ *
+ * If the TVerboseCodegen version is defined, a warning message is issued at
+ * compilation if no TMethodMeta for Interface.methodName is found.
+ */
+template TArgsStruct(Interface, string methodName) {
+ static assert(is(typeof(mixin("Interface." ~ methodName))),
+ "Could not find method '" ~ methodName ~ "' in '" ~ Interface.stringof ~ "'.");
+ mixin({
+ bool methodMetaFound;
+ TMethodMeta methodMeta;
+ static if (is(typeof(Interface.methodMeta) : TMethodMeta[])) {
+ auto meta = find!`a.name == b`(Interface.methodMeta, methodName);
+ if (!meta.empty) {
+ methodMetaFound = true;
+ methodMeta = meta.front;
+ }
+ }
+
+ string memberCode;
+ string[] fieldMetaCodes;
+ foreach (i, _; ParameterTypeTuple!(mixin("Interface." ~ methodName))) {
+ // If we have no meta information, just use param1, param2, etc. as
+ // field names, it shouldn't really matter anyway. 1-based »indexing«
+ // is used to match the common scheme in the Thrift world.
+ string memberId;
+ string memberName;
+ if (methodMetaFound && i < methodMeta.params.length) {
+ memberId = to!string(methodMeta.params[i].id);
+ memberName = methodMeta.params[i].name;
+ } else {
+ memberId = to!string(i + 1);
+ memberName = "param" ~ to!string(i + 1);
+ }
+
+ // Unqual!() is needed to generate mutable fields for ref const()
+ // struct parameters.
+ memberCode ~= "Unqual!(ParameterTypeTuple!(Interface." ~ methodName ~
+ ")[" ~ to!string(i) ~ "])" ~ memberName ~ ";\n";
+
+ fieldMetaCodes ~= "TFieldMeta(`" ~ memberName ~ "`, " ~ memberId ~
+ ", TReq.OPT_IN_REQ_OUT)";
+ }
+
+ string code = "struct TArgsStruct {\n";
+ code ~= memberCode;
+ version (TVerboseCodegen) {
+ if (!methodMetaFound &&
+ ParameterTypeTuple!(mixin("Interface." ~ methodName)).length > 0)
+ {
+ code ~= "pragma(msg, `[thrift.codegen.processor.TArgsStruct] Warning: No " ~
+ "meta information for method '" ~ methodName ~ "' in service '" ~
+ Interface.stringof ~ "' found.`);\n";
+ }
+ }
+ immutable fieldMetaCode =
+ fieldMetaCodes.empty ? "" : "[" ~ ctfeJoin(fieldMetaCodes) ~ "]";
+ code ~= "mixin TStructHelpers!(" ~ fieldMetaCode ~ ");\n";
+ code ~= "}\n";
+ return code;
+ }());
+}
+
+/**
+ * A struct representing the result of a Thrift method call.
+ *
+ * It contains a field called "success" for the return value of the function
+ * (with id 0), and additional fields for the exceptions declared for the
+ * method, if any.
+ *
+ * There should usually be no reason to use this directly without the help of
+ * TServiceProcessor, but it is documented publicly to help debugging in case
+ * of CTFE errors.
+ *
+ * Consider the following example:
+ * ---
+ * interface Foo {
+ * int bar(string a);
+ *
+ * alias .FooException FooException;
+ *
+ * enum methodMeta = [
+ * TMethodMeta("bar",
+ * [TParamMeta("a", 1)],
+ * [TExceptionMeta("fooe", 1, "FooException")]
+ * )
+ * ];
+ * }
+ * alias TResultStruct!(Foo, "bar") FooBarResult;
+ * ---
+ *
+ * The definition of FooBarResult is equivalent to:
+ * ---
+ * struct FooBarResult {
+ * int success;
+ * FooException fooe;
+ *
+ * mixin(TStructHelpers!([TFieldMeta("success", 0, TReq.OPTIONAL),
+ * TFieldMeta("fooe", 1, TReq.OPTIONAL)]));
+ * }
+ * ---
+ *
+ * If the TVerboseCodegen version is defined, a warning message is issued at
+ * compilation if no TMethodMeta for Interface.methodName is found.
+ */
+template TResultStruct(Interface, string methodName) {
+ static assert(is(typeof(mixin("Interface." ~ methodName))),
+ "Could not find method '" ~ methodName ~ "' in '" ~ Interface.stringof ~ "'.");
+
+ mixin({
+ string code = "struct TResultStruct {\n";
+
+ string[] fieldMetaCodes;
+
+ static if (!is(ReturnType!(mixin("Interface." ~ methodName)) == void)) {
+ code ~= "ReturnType!(Interface." ~ methodName ~ ") success;\n";
+ fieldMetaCodes ~= "TFieldMeta(`success`, 0, TReq.OPTIONAL)";
+ }
+
+ bool methodMetaFound;
+ static if (is(typeof(Interface.methodMeta) : TMethodMeta[])) {
+ auto meta = find!`a.name == b`(Interface.methodMeta, methodName);
+ if (!meta.empty) {
+ foreach (e; meta.front.exceptions) {
+ code ~= "Interface." ~ e.type ~ " " ~ e.name ~ ";\n";
+ fieldMetaCodes ~= "TFieldMeta(`" ~ e.name ~ "`, " ~ to!string(e.id) ~
+ ", TReq.OPTIONAL)";
+ }
+ methodMetaFound = true;
+ }
+ }
+
+ version (TVerboseCodegen) {
+ if (!methodMetaFound &&
+ ParameterTypeTuple!(mixin("Interface." ~ methodName)).length > 0)
+ {
+ code ~= "pragma(msg, `[thrift.codegen.processor.TResultStruct] Warning: No " ~
+ "meta information for method '" ~ methodName ~ "' in service '" ~
+ Interface.stringof ~ "' found.`);\n";
+ }
+ }
+
+ immutable fieldMetaCode =
+ fieldMetaCodes.empty ? "" : "[" ~ ctfeJoin(fieldMetaCodes) ~ "]";
+ code ~= "mixin TStructHelpers!(" ~ fieldMetaCode ~ ");\n";
+ code ~= "}\n";
+ return code;
+ }());
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/index.d b/src/jaegertracing/thrift/lib/d/src/thrift/index.d
new file mode 100644
index 000000000..12914b625
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/index.d
@@ -0,0 +1,33 @@
+Ddoc
+
+<h2>Package overview</h2>
+
+<dl>
+ <dt>$(D_CODE thrift.async)</dt>
+ <dd>Support infrastructure for handling client-side asynchronous operations using non-blocking I/O and coroutines.</dd>
+
+ <dt>$(D_CODE thrift.codegen)</dt>
+ <dd>
+ <p>Templates used for generating Thrift clients/processors from regular D struct and interface definitions.</p>
+ <p><strong>Note:</strong> Several artifacts in these modules have options for specifying the exact protocol types used. In this case, the amount of virtual calls can be greatly reduced and as a result, the code also can be optimized better. If performance is not a concern or the actual protocol type is not known at compile time, these parameters can just be left at their defaults.
+ </p>
+ </dd>
+
+ <dt>$(D_CODE thrift.internal)</dt>
+ <dd>Internal helper modules used by the Thrift library. This package is not part of the public API, and no stability guarantees are given whatsoever.</dd>
+
+ <dt>$(D_CODE thrift.protocol)</dt>
+ <dd>The Thrift protocol implemtations which specify how to pass messages over a TTransport.</dd>
+
+ <dt>$(D_CODE thrift.server)</dt>
+ <dd>Generic Thrift server implementations handling clients over a TTransport interface and forwarding requests to a TProcessor (which is in turn usually provided by thrift.codegen).</dd>
+
+ <dt>$(D_CODE thrift.transport)</dt>
+ <dd>The TTransport data source/sink interface used in the Thrift library and its imiplementations.</dd>
+
+ <dt>$(D_CODE thrift.util)</dt>
+ <dd>General-purpose utility modules not specific to Thrift, part of the public API.</dd>
+</dl>
+
+Macros:
+ TITLE = Thrift D Software Library
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/internal/algorithm.d b/src/jaegertracing/thrift/lib/d/src/thrift/internal/algorithm.d
new file mode 100644
index 000000000..0938ac269
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/internal/algorithm.d
@@ -0,0 +1,55 @@
+/**
+ * Contains a modified version of std.algorithm.remove that doesn't take an
+ * alias parameter to avoid DMD @@BUG6395@@.
+ */
+module thrift.internal.algorithm;
+
+import std.algorithm : move;
+import std.exception;
+import std.functional;
+import std.range;
+import std.traits;
+
+enum SwapStrategy
+{
+ unstable,
+ semistable,
+ stable,
+}
+
+Range removeEqual(SwapStrategy s = SwapStrategy.stable, Range, E)(Range range, E e)
+if (isBidirectionalRange!Range)
+{
+ auto result = range;
+ static if (s != SwapStrategy.stable)
+ {
+ for (;!range.empty;)
+ {
+ if (range.front !is e)
+ {
+ range.popFront;
+ continue;
+ }
+ move(range.back, range.front);
+ range.popBack;
+ result.popBack;
+ }
+ }
+ else
+ {
+ auto tgt = range;
+ for (; !range.empty; range.popFront)
+ {
+ if (range.front is e)
+ {
+ // yank this guy
+ result.popBack;
+ continue;
+ }
+ // keep this guy
+ move(range.front, tgt.front);
+ tgt.popFront;
+ }
+ }
+ return result;
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/internal/codegen.d b/src/jaegertracing/thrift/lib/d/src/thrift/internal/codegen.d
new file mode 100644
index 000000000..85f9d1891
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/internal/codegen.d
@@ -0,0 +1,451 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+module thrift.internal.codegen;
+
+import std.algorithm : canFind;
+import std.traits : InterfacesTuple, isSomeFunction, isSomeString;
+import std.typetuple : staticIndexOf, staticMap, NoDuplicates, TypeTuple;
+import thrift.codegen.base;
+
+/**
+ * Removes all type qualifiers from T.
+ *
+ * In contrast to std.traits.Unqual, FullyUnqual also removes qualifiers from
+ * array elements (e.g. immutable(byte[]) -> byte[], not immutable(byte)[]),
+ * excluding strings (string isn't reduced to char[]).
+ */
+template FullyUnqual(T) {
+ static if (is(T _ == const(U), U)) {
+ alias FullyUnqual!U FullyUnqual;
+ } else static if (is(T _ == immutable(U), U)) {
+ alias FullyUnqual!U FullyUnqual;
+ } else static if (is(T _ == shared(U), U)) {
+ alias FullyUnqual!U FullyUnqual;
+ } else static if (is(T _ == U[], U) && !isSomeString!T) {
+ alias FullyUnqual!(U)[] FullyUnqual;
+ } else static if (is(T _ == V[K], K, V)) {
+ alias FullyUnqual!(V)[FullyUnqual!K] FullyUnqual;
+ } else {
+ alias T FullyUnqual;
+ }
+}
+
+/**
+ * true if null can be assigned to the passed type, false if not.
+ */
+template isNullable(T) {
+ enum isNullable = __traits(compiles, { T t = null; });
+}
+
+template isStruct(T) {
+ enum isStruct = is(T == struct);
+}
+
+template isException(T) {
+ enum isException = is(T : Exception);
+}
+
+template isEnum(T) {
+ enum isEnum = is(T == enum);
+}
+
+/**
+ * Aliases itself to T.name.
+ */
+template GetMember(T, string name) {
+ mixin("alias T." ~ name ~ " GetMember;");
+}
+
+/**
+ * Aliases itself to typeof(symbol).
+ */
+template TypeOf(alias symbol) {
+ alias typeof(symbol) TypeOf;
+}
+
+/**
+ * Aliases itself to the type of the T member called name.
+ */
+alias Compose!(TypeOf, GetMember) MemberType;
+
+/**
+ * Returns the field metadata array for T if any, or an empty array otherwise.
+ */
+template getFieldMeta(T) if (isStruct!T || isException!T) {
+ static if (is(typeof(T.fieldMeta) == TFieldMeta[])) {
+ enum getFieldMeta = T.fieldMeta;
+ } else {
+ enum TFieldMeta[] getFieldMeta = [];
+ }
+}
+
+/**
+ * Merges the field metadata array for D with the passed array.
+ */
+template mergeFieldMeta(T, alias fieldMetaData = cast(TFieldMeta[])null) {
+ // Note: We don't use getFieldMeta here to avoid bug if it is instantiated
+ // from TIsSetFlags, see comment there.
+ static if (is(typeof(T.fieldMeta) == TFieldMeta[])) {
+ enum mergeFieldMeta = T.fieldMeta ~ fieldMetaData;
+ } else {
+ enum TFieldMeta[] mergeFieldMeta = fieldMetaData;
+ }
+}
+
+/**
+ * Returns the field requirement level for T.name.
+ */
+template memberReq(T, string name, alias fieldMetaData = cast(TFieldMeta[])null) {
+ enum memberReq = memberReqImpl!(T, name, fieldMetaData).result;
+}
+
+private {
+ import std.algorithm : find;
+ // DMD @@BUG@@: Missing import leads to failing build without error
+ // message in unittest/debug/thrift/codegen/async_client.
+ import std.array : empty, front;
+
+ template memberReqImpl(T, string name, alias fieldMetaData) {
+ enum meta = find!`a.name == b`(mergeFieldMeta!(T, fieldMetaData), name);
+ static if (meta.empty || meta.front.req == TReq.AUTO) {
+ static if (isNullable!(MemberType!(T, name))) {
+ enum result = TReq.OPTIONAL;
+ } else {
+ enum result = TReq.REQUIRED;
+ }
+ } else {
+ enum result = meta.front.req;
+ }
+ }
+}
+
+
+template notIgnored(T, string name, alias fieldMetaData = cast(TFieldMeta[])null) {
+ enum notIgnored = memberReq!(T, name, fieldMetaData) != TReq.IGNORE;
+}
+
+/**
+ * Returns the method metadata array for T if any, or an empty array otherwise.
+ */
+template getMethodMeta(T) if (isService!T) {
+ static if (is(typeof(T.methodMeta) == TMethodMeta[])) {
+ enum getMethodMeta = T.methodMeta;
+ } else {
+ enum TMethodMeta[] getMethodMeta = [];
+ }
+}
+
+
+/**
+ * true if T.name is a member variable. Exceptions include methods, static
+ * members, artifacts like package aliases, …
+ */
+template isValueMember(T, string name) {
+ static if (!is(MemberType!(T, name))) {
+ enum isValueMember = false;
+ } else static if (
+ is(MemberType!(T, name) == void) ||
+ isSomeFunction!(MemberType!(T, name)) ||
+ __traits(compiles, { return mixin("T." ~ name); }())
+ ) {
+ enum isValueMember = false;
+ } else {
+ enum isValueMember = true;
+ }
+}
+
+/**
+ * Returns a tuple containing the names of the fields of T, not including
+ * inherited fields. If a member is marked as TReq.IGNORE, it is not included
+ * as well.
+ */
+template FieldNames(T, alias fieldMetaData = cast(TFieldMeta[])null) {
+ alias StaticFilter!(
+ All!(
+ doesNotReadMembers,
+ PApply!(isValueMember, T),
+ PApply!(notIgnored, T, PApplySkip, fieldMetaData)
+ ),
+ __traits(derivedMembers, T)
+ ) FieldNames;
+}
+
+/*
+ * true if the passed member name is not a method generated by the
+ * TStructHelpers template that in its implementations queries the struct
+ * members.
+ *
+ * Kludge used internally to break a cycle caused a DMD forward reference
+ * regression, see THRIFT-2130.
+ */
+enum doesNotReadMembers(string name) = !["opEquals", "thriftOpEqualsImpl",
+ "toString", "thriftToStringImpl"].canFind(name);
+
+template derivedMembers(T) {
+ alias TypeTuple!(__traits(derivedMembers, T)) derivedMembers;
+}
+
+template AllMemberMethodNames(T) if (isService!T) {
+ alias NoDuplicates!(
+ FilterMethodNames!(
+ T,
+ staticMap!(
+ derivedMembers,
+ TypeTuple!(T, InterfacesTuple!T)
+ )
+ )
+ ) AllMemberMethodNames;
+}
+
+template FilterMethodNames(T, MemberNames...) {
+ alias StaticFilter!(
+ CompilesAndTrue!(
+ Compose!(isSomeFunction, TypeOf, PApply!(GetMember, T))
+ ),
+ MemberNames
+ ) FilterMethodNames;
+}
+
+/**
+ * Returns a type tuple containing only the elements of T for which the
+ * eponymous template predicate pred is true.
+ *
+ * Example:
+ * ---
+ * alias StaticFilter!(isIntegral, int, string, long, float[]) Filtered;
+ * static assert(is(Filtered == TypeTuple!(int, long)));
+ * ---
+ */
+template StaticFilter(alias pred, T...) {
+ static if (T.length == 0) {
+ alias TypeTuple!() StaticFilter;
+ } else static if (pred!(T[0])) {
+ alias TypeTuple!(T[0], StaticFilter!(pred, T[1 .. $])) StaticFilter;
+ } else {
+ alias StaticFilter!(pred, T[1 .. $]) StaticFilter;
+ }
+}
+
+/**
+ * Binds the first n arguments of a template to a particular value (where n is
+ * the number of arguments passed to PApply).
+ *
+ * The passed arguments are always applied starting from the left. However,
+ * the special PApplySkip marker template can be used to indicate that an
+ * argument should be skipped, so that e.g. the first and third argument
+ * to a template can be fixed, but the second and remaining arguments would
+ * still be left undefined.
+ *
+ * Skipping a number of parameters, but not providing enough arguments to
+ * assign all of them during instantiation of the resulting template is an
+ * error.
+ *
+ * Example:
+ * ---
+ * struct Foo(T, U, V) {}
+ * alias PApply!(Foo, int, long) PartialFoo;
+ * static assert(is(PartialFoo!float == Foo!(int, long, float)));
+ *
+ * alias PApply!(Test, int, PApplySkip, float) SkippedTest;
+ * static assert(is(SkippedTest!long == Test!(int, long, float)));
+ * ---
+ */
+template PApply(alias Target, T...) {
+ template PApply(U...) {
+ alias Target!(PApplyMergeArgs!(ConfinedTuple!T, U).Result) PApply;
+ }
+}
+
+/// Ditto.
+template PApplySkip() {}
+
+private template PApplyMergeArgs(alias Preset, Args...) {
+ static if (Preset.length == 0) {
+ alias Args Result;
+ } else {
+ enum nextSkip = staticIndexOf!(PApplySkip, Preset.Tuple);
+ static if (nextSkip == -1) {
+ alias TypeTuple!(Preset.Tuple, Args) Result;
+ } else static if (Args.length == 0) {
+ // Have to use a static if clause instead of putting the condition
+ // directly into the assert to avoid DMD trying to access Args[0]
+ // nevertheless below.
+ static assert(false,
+ "PArgsSkip encountered, but no argument left to bind.");
+ } else {
+ alias TypeTuple!(
+ Preset.Tuple[0 .. nextSkip],
+ Args[0],
+ PApplyMergeArgs!(
+ ConfinedTuple!(Preset.Tuple[nextSkip + 1 .. $]),
+ Args[1 .. $]
+ ).Result
+ ) Result;
+ }
+ }
+}
+
+unittest {
+ struct Test(T, U, V) {}
+ alias PApply!(Test, int, long) PartialTest;
+ static assert(is(PartialTest!float == Test!(int, long, float)));
+
+ alias PApply!(Test, int, PApplySkip, float) SkippedTest;
+ static assert(is(SkippedTest!long == Test!(int, long, float)));
+
+ alias PApply!(Test, int, PApplySkip, PApplySkip) TwoSkipped;
+ static assert(!__traits(compiles, TwoSkipped!long));
+}
+
+
+/**
+ * Composes a number of templates. The result is a template equivalent to
+ * all the passed templates evaluated from right to left, akin to the
+ * mathematical function composition notation: Instantiating Compose!(A, B, C)
+ * is the same as instantiating A!(B!(C!(…))).
+ *
+ * This is especially useful for creating a template to use with staticMap/
+ * StaticFilter, as demonstrated below.
+ *
+ * Example:
+ * ---
+ * template AllMethodNames(T) {
+ * alias StaticFilter!(
+ * CompilesAndTrue!(
+ * Compose!(isSomeFunction, TypeOf, PApply!(GetMember, T))
+ * ),
+ * __traits(allMembers, T)
+ * ) AllMethodNames;
+ * }
+ *
+ * pragma(msg, AllMethodNames!Object);
+ * ---
+ */
+template Compose(T...) {
+ static if (T.length == 0) {
+ template Compose(U...) {
+ alias U Compose;
+ }
+ } else {
+ template Compose(U...) {
+ alias Instantiate!(T[0], Instantiate!(.Compose!(T[1 .. $]), U)) Compose;
+ }
+ }
+}
+
+/**
+ * Instantiates the given template with the given list of parameters.
+ *
+ * Used to work around syntactic limiations of D with regard to instantiating
+ * a template from a type tuple (e.g. T[0]!(...) is not valid) or a template
+ * returning another template (e.g. Foo!(Bar)!(Baz) is not allowed).
+ */
+template Instantiate(alias Template, Params...) {
+ alias Template!Params Instantiate;
+}
+
+/**
+ * Combines several template predicates using logical AND, i.e. instantiating
+ * All!(a, b, c) with parameters P for some templates a, b, c is equivalent to
+ * a!P && b!P && c!P.
+ *
+ * The templates are evaluated from left to right, aborting evaluation in a
+ * shurt-cut manner if a false result is encountered, in which case the latter
+ * instantiations do not need to compile.
+ */
+template All(T...) {
+ static if (T.length == 0) {
+ template All(U...) {
+ enum All = true;
+ }
+ } else {
+ template All(U...) {
+ static if (Instantiate!(T[0], U)) {
+ alias Instantiate!(.All!(T[1 .. $]), U) All;
+ } else {
+ enum All = false;
+ }
+ }
+ }
+}
+
+/**
+ * Combines several template predicates using logical OR, i.e. instantiating
+ * Any!(a, b, c) with parameters P for some templates a, b, c is equivalent to
+ * a!P || b!P || c!P.
+ *
+ * The templates are evaluated from left to right, aborting evaluation in a
+ * shurt-cut manner if a true result is encountered, in which case the latter
+ * instantiations do not need to compile.
+ */
+template Any(T...) {
+ static if (T.length == 0) {
+ template Any(U...) {
+ enum Any = false;
+ }
+ } else {
+ template Any(U...) {
+ static if (Instantiate!(T[0], U)) {
+ enum Any = true;
+ } else {
+ alias Instantiate!(.Any!(T[1 .. $]), U) Any;
+ }
+ }
+ }
+}
+
+template ConfinedTuple(T...) {
+ alias T Tuple;
+ enum length = T.length;
+}
+
+/*
+ * foreach (Item; Items) {
+ * List = Operator!(Item, List);
+ * }
+ * where Items is a ConfinedTuple and List is a type tuple.
+ */
+template ForAllWithList(alias Items, alias Operator, List...) if (
+ is(typeof(Items.length) : size_t)
+){
+ static if (Items.length == 0) {
+ alias List ForAllWithList;
+ } else {
+ alias .ForAllWithList!(
+ ConfinedTuple!(Items.Tuple[1 .. $]),
+ Operator,
+ Operator!(Items.Tuple[0], List)
+ ) ForAllWithList;
+ }
+}
+
+/**
+ * Wraps the passed template predicate so it returns true if it compiles and
+ * evaluates to true, false it it doesn't compile or evaluates to false.
+ */
+template CompilesAndTrue(alias T) {
+ template CompilesAndTrue(U...) {
+ static if (is(typeof(T!U) : bool)) {
+ enum bool CompilesAndTrue = T!U;
+ } else {
+ enum bool CompilesAndTrue = false;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/internal/ctfe.d b/src/jaegertracing/thrift/lib/d/src/thrift/internal/ctfe.d
new file mode 100644
index 000000000..974db01e3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/internal/ctfe.d
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+module thrift.internal.ctfe;
+
+import std.conv : to;
+import std.traits;
+
+/*
+ * Simple eager join() for strings, std.algorithm.join isn't CTFEable yet.
+ */
+string ctfeJoin(string[] strings, string separator = ", ") {
+ string result;
+ if (strings.length > 0) {
+ result ~= strings[0];
+ foreach (s; strings[1..$]) {
+ result ~= separator ~ s;
+ }
+ }
+ return result;
+}
+
+/*
+ * A very primitive to!string() implementation for floating point numbers that
+ * is evaluatable at compile time.
+ *
+ * There is a wealth of problems associated with the algorithm used (e.g. 5.0
+ * prints as 4.999…, incorrect rounding, etc.), but a better alternative should
+ * be included with the D standard library instead of implementing it here.
+ */
+string ctfeToString(T)(T val) if (isFloatingPoint!T) {
+ if (val is T.nan) return "nan";
+ if (val is T.infinity) return "inf";
+ if (val is -T.infinity) return "-inf";
+ if (val is 0.0) return "0";
+ if (val is -0.0) return "-0";
+
+ auto b = val;
+
+ string result;
+ if (b < 0) {
+ result ~= '-';
+ b *= -1;
+ }
+
+ short magnitude;
+ while (b >= 10) {
+ ++magnitude;
+ b /= 10;
+ }
+ while (b < 1) {
+ --magnitude;
+ b *= 10;
+ }
+
+ foreach (i; 0 .. T.dig) {
+ if (i == 1) result ~= '.';
+
+ auto first = cast(ubyte)b;
+ result ~= to!string(first);
+
+ b -= first;
+ import std.math;
+ if (b < pow(10.0, i - T.dig)) break;
+ b *= 10;
+ }
+
+ if (magnitude != 0) result ~= "e" ~ to!string(magnitude);
+ return result;
+}
+
+unittest {
+ import std.algorithm;
+ static assert(ctfeToString(double.infinity) == "inf");
+ static assert(ctfeToString(-double.infinity) == "-inf");
+ static assert(ctfeToString(double.nan) == "nan");
+ static assert(ctfeToString(0.0) == "0");
+ static assert(ctfeToString(-0.0) == "-0");
+ static assert(ctfeToString(2.5) == "2.5");
+ static assert(ctfeToString(3.1415).startsWith("3.141"));
+ static assert(ctfeToString(2e-200) == "2e-200");
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/internal/endian.d b/src/jaegertracing/thrift/lib/d/src/thrift/internal/endian.d
new file mode 100644
index 000000000..31b9814ef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/internal/endian.d
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * Simple helpers for handling typical byte order-related issues.
+ */
+module thrift.internal.endian;
+
+import core.bitop : bswap;
+import std.traits : isIntegral;
+
+union IntBuf(T) {
+ ubyte[T.sizeof] bytes;
+ T value;
+}
+
+T byteSwap(T)(T t) pure nothrow @trusted if (isIntegral!T) {
+ static if (T.sizeof == 2) {
+ return cast(T)((t & 0xff) << 8) | cast(T)((t & 0xff00) >> 8);
+ } else static if (T.sizeof == 4) {
+ return cast(T)bswap(cast(uint)t);
+ } else static if (T.sizeof == 8) {
+ return cast(T)byteSwap(cast(uint)(t & 0xffffffff)) << 32 |
+ cast(T)bswap(cast(uint)(t >> 32));
+ } else static assert(false, "Type of size " ~ to!string(T.sizeof) ~ " not supported.");
+}
+
+T doNothing(T)(T val) { return val; }
+
+version (BigEndian) {
+ alias doNothing hostToNet;
+ alias doNothing netToHost;
+ alias byteSwap hostToLe;
+ alias byteSwap leToHost;
+} else {
+ alias byteSwap hostToNet;
+ alias byteSwap netToHost;
+ alias doNothing hostToLe;
+ alias doNothing leToHost;
+}
+
+unittest {
+ import std.exception;
+
+ IntBuf!short s;
+ s.bytes = [1, 2];
+ s.value = byteSwap(s.value);
+ enforce(s.bytes == [2, 1]);
+
+ IntBuf!int i;
+ i.bytes = [1, 2, 3, 4];
+ i.value = byteSwap(i.value);
+ enforce(i.bytes == [4, 3, 2, 1]);
+
+ IntBuf!long l;
+ l.bytes = [1, 2, 3, 4, 5, 6, 7, 8];
+ l.value = byteSwap(l.value);
+ enforce(l.bytes == [8, 7, 6, 5, 4, 3, 2, 1]);
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/internal/resource_pool.d b/src/jaegertracing/thrift/lib/d/src/thrift/internal/resource_pool.d
new file mode 100644
index 000000000..c0820a342
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/internal/resource_pool.d
@@ -0,0 +1,431 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.internal.resource_pool;
+
+import core.time : Duration, dur, TickDuration;
+import std.algorithm : minPos, reduce, remove;
+import std.array : array, empty;
+import std.exception : enforce;
+import std.conv : to;
+import std.random : randomCover, rndGen;
+import std.range : zip;
+import thrift.internal.algorithm : removeEqual;
+
+/**
+ * A pool of resources, which can be iterated over, and where resources that
+ * have failed too often can be temporarily disabled.
+ *
+ * This class is oblivious to the actual resource type managed.
+ */
+final class TResourcePool(Resource) {
+ /**
+ * Constructs a new instance.
+ *
+ * Params:
+ * resources = The initial members of the pool.
+ */
+ this(Resource[] resources) {
+ resources_ = resources;
+ }
+
+ /**
+ * Adds a resource to the pool.
+ */
+ void add(Resource resource) {
+ resources_ ~= resource;
+ }
+
+ /**
+ * Removes a resource from the pool.
+ *
+ * Returns: Whether the resource could be found in the pool.
+ */
+ bool remove(Resource resource) {
+ auto oldLength = resources_.length;
+ resources_ = removeEqual(resources_, resource);
+ return resources_.length < oldLength;
+ }
+
+ /**
+ * Returns an »enriched« input range to iterate over the pool members.
+ */
+ static struct Range {
+ /**
+ * Whether the range is empty.
+ *
+ * This is the case if all members of the pool have been popped (or skipped
+ * because they were disabled) and TResourcePool.cycle is false, or there
+ * is no element to return in cycle mode because all have been temporarily
+ * disabled.
+ */
+ bool empty() @property {
+ // If no resources are in the pool, the range will never become non-empty.
+ if (resources_.empty) return true;
+
+ // If we already got the next resource in the cache, it doesn't matter
+ // whether there are more.
+ if (cached_) return false;
+
+ size_t examineCount;
+ if (parent_.cycle) {
+ // We want to check all the resources, but not iterate more than once
+ // to avoid spinning in a loop if nothing is available.
+ examineCount = resources_.length;
+ } else {
+ // When not in cycle mode, we just iterate the list exactly once. If all
+ // items have been consumed, the interval below is empty.
+ examineCount = resources_.length - nextIndex_;
+ }
+
+ foreach (i; 0 .. examineCount) {
+ auto r = resources_[(nextIndex_ + i) % resources_.length];
+ auto fi = r in parent_.faultInfos_;
+
+ if (fi && fi.resetTime != fi.resetTime.init) {
+ if (fi.resetTime < parent_.getCurrentTick_()) {
+ // The timeout expired, remove the resource from the list and go
+ // ahead trying it.
+ parent_.faultInfos_.remove(r);
+ } else {
+ // The timeout didn't expire yet, try the next resource.
+ continue;
+ }
+ }
+
+ cache_ = r;
+ cached_ = true;
+ nextIndex_ = nextIndex_ + i + 1;
+ return false;
+ }
+
+ // If we get here, all resources are currently inactive or the non-cycle
+ // pool has been exhausted, so there is nothing we can do.
+ nextIndex_ = nextIndex_ + examineCount;
+ return true;
+ }
+
+ /**
+ * Returns the first resource in the range.
+ */
+ Resource front() @property {
+ enforce(!empty);
+ return cache_;
+ }
+
+ /**
+ * Removes the first resource from the range.
+ *
+ * Usually, this is combined with a call to TResourcePool.recordSuccess()
+ * or recordFault().
+ */
+ void popFront() {
+ enforce(!empty);
+ cached_ = false;
+ }
+
+ /**
+ * Returns whether the range will become non-empty at some point in the
+ * future, and provides additional information when this will happen and
+ * what will be the next resource.
+ *
+ * Makes only sense to call on empty ranges.
+ *
+ * Params:
+ * next = The next resource that will become available.
+ * waitTime = The duration until that resource will become available.
+ */
+ bool willBecomeNonempty(out Resource next, out Duration waitTime) {
+ // If no resources are in the pool, the range will never become non-empty.
+ if (resources_.empty) return false;
+
+ // If cycle mode is not enabled, a range never becomes non-empty after
+ // being empty once, because all the elements have already been
+ // used/skipped in order to become empty.
+ if (!parent_.cycle) return false;
+
+ auto fi = parent_.faultInfos_;
+ auto nextPair = minPos!"a[1].resetTime < b[1].resetTime"(
+ zip(fi.keys, fi.values)
+ ).front;
+
+ next = nextPair[0];
+ waitTime = to!Duration(nextPair[1].resetTime - parent_.getCurrentTick_());
+
+ return true;
+ }
+
+ private:
+ this(TResourcePool parent, Resource[] resources) {
+ parent_ = parent;
+ resources_ = resources;
+ }
+
+ TResourcePool parent_;
+
+ /// All available resources. We keep a copy of it as to not get confused
+ /// when resources are added to/removed from the parent pool.
+ Resource[] resources_;
+
+ /// After we have determined the next element in empty(), we store it here.
+ Resource cache_;
+
+ /// Whether there is currently something in the cache.
+ bool cached_;
+
+ /// The index to start searching from at the next call to empty().
+ size_t nextIndex_;
+ }
+
+ /// Ditto
+ Range opSlice() {
+ auto res = resources_;
+ if (permute) {
+ res = array(randomCover(res, rndGen));
+ }
+ return Range(this, res);
+ }
+
+ /**
+ * Records a success for an operation on the given resource, cancelling a
+ * fault streak, if any.
+ */
+ void recordSuccess(Resource resource) {
+ if (resource in faultInfos_) {
+ faultInfos_.remove(resource);
+ }
+ }
+
+ /**
+ * Records a fault for the given resource.
+ *
+ * If a resource fails consecutively for more than faultDisableCount times,
+ * it is temporarily disabled (no longer considered) until
+ * faultDisableDuration has passed.
+ */
+ void recordFault(Resource resource) {
+ auto fi = resource in faultInfos_;
+
+ if (!fi) {
+ faultInfos_[resource] = FaultInfo();
+ fi = resource in faultInfos_;
+ }
+
+ ++fi.count;
+ if (fi.count >= faultDisableCount) {
+ // If the resource has hit the fault count limit, disable it for
+ // specified duration.
+ fi.resetTime = getCurrentTick_() + cast(TickDuration)faultDisableDuration;
+ }
+ }
+
+ /**
+ * Whether to randomly permute the order of the resources in the pool when
+ * taking a range using opSlice().
+ *
+ * This can be used e.g. as a simple form of load balancing.
+ */
+ bool permute = true;
+
+ /**
+ * Whether to keep iterating over the pool members after all have been
+ * returned/have failed once.
+ */
+ bool cycle = false;
+
+ /**
+ * The number of consecutive faults after which a resource is disabled until
+ * faultDisableDuration has passed. Zero to never disable resources.
+ *
+ * Defaults to zero.
+ */
+ ushort faultDisableCount = 0;
+
+ /**
+ * The duration for which a resource is no longer considered after it has
+ * failed too often.
+ *
+ * Defaults to one second.
+ */
+ Duration faultDisableDuration = dur!"seconds"(1);
+
+private:
+ Resource[] resources_;
+ FaultInfo[Resource] faultInfos_;
+
+ /// Function to get the current timestamp from some monotonic system clock.
+ ///
+ /// This is overridable to be able to write timing-insensitive unit tests.
+ /// The extra indirection should not matter much performance-wise compared to
+ /// the actual system call, and by its very nature thisshould not be on a hot
+ /// path anyway.
+ typeof(&TickDuration.currSystemTick) getCurrentTick_ =
+ &TickDuration.currSystemTick;
+}
+
+private {
+ struct FaultInfo {
+ ushort count;
+ TickDuration resetTime;
+ }
+}
+
+unittest {
+ auto pool = new TResourcePool!Object([]);
+ enforce(pool[].empty);
+ Object dummyRes;
+ Duration dummyDur;
+ enforce(!pool[].willBecomeNonempty(dummyRes, dummyDur));
+}
+
+unittest {
+ import std.datetime;
+ import thrift.base;
+
+ auto a = new Object;
+ auto b = new Object;
+ auto c = new Object;
+ auto objs = [a, b, c];
+ auto pool = new TResourcePool!Object(objs);
+ pool.permute = false;
+
+ static Duration fakeClock;
+ pool.getCurrentTick_ = () => cast(TickDuration)fakeClock;
+
+ Object dummyRes = void;
+ Duration dummyDur = void;
+
+ {
+ auto r = pool[];
+
+ foreach (i, o; objs) {
+ enforce(!r.empty);
+ enforce(r.front == o);
+ r.popFront();
+ }
+
+ enforce(r.empty);
+ enforce(!r.willBecomeNonempty(dummyRes, dummyDur));
+ }
+
+ {
+ pool.faultDisableCount = 2;
+
+ enforce(pool[].front == a);
+ pool.recordFault(a);
+ enforce(pool[].front == a);
+ pool.recordSuccess(a);
+ enforce(pool[].front == a);
+ pool.recordFault(a);
+ enforce(pool[].front == a);
+ pool.recordFault(a);
+
+ auto r = pool[];
+ enforce(r.front == b);
+ r.popFront();
+ enforce(r.front == c);
+ r.popFront();
+ enforce(r.empty);
+ enforce(!r.willBecomeNonempty(dummyRes, dummyDur));
+
+ fakeClock += 2.seconds;
+ // Not in cycle mode, has to be still empty after the timeouts expired.
+ enforce(r.empty);
+ enforce(!r.willBecomeNonempty(dummyRes, dummyDur));
+
+ foreach (o; objs) pool.recordSuccess(o);
+ }
+
+ {
+ pool.faultDisableCount = 1;
+
+ pool.recordFault(a);
+ pool.recordFault(b);
+ pool.recordFault(c);
+
+ auto r = pool[];
+ enforce(r.empty);
+ enforce(!r.willBecomeNonempty(dummyRes, dummyDur));
+
+ foreach (o; objs) pool.recordSuccess(o);
+ }
+
+ pool.cycle = true;
+
+ {
+ auto r = pool[];
+
+ foreach (o; objs ~ objs) {
+ enforce(!r.empty);
+ enforce(r.front == o);
+ r.popFront();
+ }
+ }
+
+ {
+ pool.faultDisableCount = 2;
+
+ enforce(pool[].front == a);
+ pool.recordFault(a);
+ enforce(pool[].front == a);
+ pool.recordSuccess(a);
+ enforce(pool[].front == a);
+ pool.recordFault(a);
+ enforce(pool[].front == a);
+ pool.recordFault(a);
+
+ auto r = pool[];
+ enforce(r.front == b);
+ r.popFront();
+ enforce(r.front == c);
+ r.popFront();
+ enforce(r.front == b);
+
+ fakeClock += 2.seconds;
+
+ r.popFront();
+ enforce(r.front == c);
+
+ r.popFront();
+ enforce(r.front == a);
+
+ enforce(pool[].front == a);
+
+ foreach (o; objs) pool.recordSuccess(o);
+ }
+
+ {
+ pool.faultDisableCount = 1;
+
+ pool.recordFault(a);
+ fakeClock += 1.msecs;
+ pool.recordFault(b);
+ fakeClock += 1.msecs;
+ pool.recordFault(c);
+
+ auto r = pool[];
+ enforce(r.empty);
+
+ // Make sure willBecomeNonempty gets the order right.
+ enforce(r.willBecomeNonempty(dummyRes, dummyDur));
+ enforce(dummyRes == a);
+ enforce(dummyDur > Duration.zero);
+
+ foreach (o; objs) pool.recordSuccess(o);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/internal/socket.d b/src/jaegertracing/thrift/lib/d/src/thrift/internal/socket.d
new file mode 100644
index 000000000..6ca0a970e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/internal/socket.d
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Abstractions over OS-dependent socket functionality.
+ */
+module thrift.internal.socket;
+
+import std.conv : to;
+
+// FreeBSD and OS X return -1 and set ECONNRESET if socket was closed by
+// the other side, we need to check for that before throwing an exception.
+version (FreeBSD) {
+ enum connresetOnPeerShutdown = true;
+} else version (OSX) {
+ enum connresetOnPeerShutdown = true;
+} else {
+ enum connresetOnPeerShutdown = false;
+}
+
+version (Win32) {
+ import std.c.windows.winsock : WSAGetLastError, WSAEINTR, WSAEWOULDBLOCK;
+ import std.windows.syserror : sysErrorString;
+
+ // These are unfortunately not defined in std.c.windows.winsock, see
+ // http://msdn.microsoft.com/en-us/library/ms740668.aspx.
+ enum WSAECONNRESET = 10054;
+ enum WSAENOTCONN = 10057;
+ enum WSAETIMEDOUT = 10060;
+} else {
+ import core.stdc.errno : errno, EAGAIN, ECONNRESET, EINPROGRESS, EINTR,
+ ENOTCONN, EPIPE;
+ import core.stdc.string : strerror;
+}
+
+/*
+ * CONNECT_INPROGRESS_ERRNO: set by connect() for non-blocking sockets if the
+ * connection could not be immediately established.
+ * INTERRUPTED_ERRNO: set when blocking system calls are interrupted by
+ * signals or similar.
+ * TIMEOUT_ERRNO: set when a socket timeout has been exceeded.
+ * WOULD_BLOCK_ERRNO: set when send/recv would block on non-blocking sockets.
+ *
+ * isSocetCloseErrno(errno): returns true if errno indicates that the socket
+ * is logically in closed state now.
+ */
+version (Win32) {
+ alias WSAGetLastError getSocketErrno;
+ enum CONNECT_INPROGRESS_ERRNO = WSAEWOULDBLOCK;
+ enum INTERRUPTED_ERRNO = WSAEINTR;
+ enum TIMEOUT_ERRNO = WSAETIMEDOUT;
+ enum WOULD_BLOCK_ERRNO = WSAEWOULDBLOCK;
+
+ bool isSocketCloseErrno(typeof(getSocketErrno()) errno) {
+ return (errno == WSAECONNRESET || errno == WSAENOTCONN);
+ }
+} else {
+ alias errno getSocketErrno;
+ enum CONNECT_INPROGRESS_ERRNO = EINPROGRESS;
+ enum INTERRUPTED_ERRNO = EINTR;
+ enum WOULD_BLOCK_ERRNO = EAGAIN;
+
+ // TODO: The C++ TSocket implementation mentions that EAGAIN can also be
+ // set (undocumentedly) in out of resource conditions; it would be a good
+ // idea to contact the original authors of the C++ code for details and adapt
+ // the code accordingly.
+ enum TIMEOUT_ERRNO = EAGAIN;
+
+ bool isSocketCloseErrno(typeof(getSocketErrno()) errno) {
+ return (errno == EPIPE || errno == ECONNRESET || errno == ENOTCONN);
+ }
+}
+
+string socketErrnoString(uint errno) {
+ version (Win32) {
+ return sysErrorString(errno);
+ } else {
+ return to!string(strerror(errno));
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/internal/ssl.d b/src/jaegertracing/thrift/lib/d/src/thrift/internal/ssl.d
new file mode 100644
index 000000000..3af54b582
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/internal/ssl.d
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.internal.ssl;
+
+import core.memory : GC;
+import core.stdc.config;
+import core.stdc.errno : errno;
+import core.stdc.string : strerror;
+import deimos.openssl.err;
+import deimos.openssl.ssl;
+import deimos.openssl.x509v3;
+import std.array : empty, appender;
+import std.conv : to;
+import std.socket : Address;
+import thrift.transport.ssl;
+
+/**
+ * Checks if the peer is authorized after the SSL handshake has been
+ * completed on the given conncetion and throws an TSSLException if not.
+ *
+ * Params:
+ * ssl = The SSL connection to check.
+ * accessManager = The access manager to check the peer againts.
+ * peerAddress = The (IP) address of the peer.
+ * hostName = The host name of the peer.
+ */
+void authorize(SSL* ssl, TAccessManager accessManager,
+ Address peerAddress, lazy string hostName
+) {
+ alias TAccessManager.Decision Decision;
+
+ auto rc = SSL_get_verify_result(ssl);
+ if (rc != X509_V_OK) {
+ throw new TSSLException("SSL_get_verify_result(): " ~
+ to!string(X509_verify_cert_error_string(rc)));
+ }
+
+ auto cert = SSL_get_peer_certificate(ssl);
+ if (cert is null) {
+ // Certificate is not present.
+ if (SSL_get_verify_mode(ssl) & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+ throw new TSSLException(
+ "Authorize: Required certificate not present.");
+ }
+
+ // If we don't have an access manager set, we don't intend to authorize
+ // the client, so everything's fine.
+ if (accessManager) {
+ throw new TSSLException(
+ "Authorize: Certificate required for authorization.");
+ }
+ return;
+ }
+
+ if (accessManager is null) {
+ // No access manager set, can return immediately as the cert is valid
+ // and all peers are authorized.
+ X509_free(cert);
+ return;
+ }
+
+ // both certificate and access manager are present
+ auto decision = accessManager.verify(peerAddress);
+
+ if (decision != Decision.SKIP) {
+ X509_free(cert);
+ if (decision != Decision.ALLOW) {
+ throw new TSSLException("Authorize: Access denied based on remote IP.");
+ }
+ return;
+ }
+
+ // Check subjectAltName(s), if present.
+ auto alternatives = cast(STACK_OF!(GENERAL_NAME)*)
+ X509_get_ext_d2i(cert, NID_subject_alt_name, null, null);
+ if (alternatives != null) {
+ auto count = sk_GENERAL_NAME_num(alternatives);
+ for (int i = 0; decision == Decision.SKIP && i < count; i++) {
+ auto name = sk_GENERAL_NAME_value(alternatives, i);
+ if (name is null) {
+ continue;
+ }
+ auto data = ASN1_STRING_data(name.d.ia5);
+ auto length = ASN1_STRING_length(name.d.ia5);
+ switch (name.type) {
+ case GENERAL_NAME.GEN_DNS:
+ decision = accessManager.verify(hostName, cast(char[])data[0 .. length]);
+ break;
+ case GENERAL_NAME.GEN_IPADD:
+ decision = accessManager.verify(peerAddress, data[0 .. length]);
+ break;
+ default:
+ // Do nothing.
+ }
+ }
+
+ // DMD @@BUG@@: Empty template arguments parens should not be needed.
+ sk_GENERAL_NAME_pop_free!()(alternatives, &GENERAL_NAME_free);
+ }
+
+ // If we are alredy done, return.
+ if (decision != Decision.SKIP) {
+ X509_free(cert);
+ if (decision != Decision.ALLOW) {
+ throw new TSSLException("Authorize: Access denied.");
+ }
+ return;
+ }
+
+ // Check commonName.
+ auto name = X509_get_subject_name(cert);
+ if (name !is null) {
+ X509_NAME_ENTRY* entry;
+ char* utf8;
+ int last = -1;
+ while (decision == Decision.SKIP) {
+ last = X509_NAME_get_index_by_NID(name, NID_commonName, last);
+ if (last == -1)
+ break;
+ entry = X509_NAME_get_entry(name, last);
+ if (entry is null)
+ continue;
+ auto common = X509_NAME_ENTRY_get_data(entry);
+ auto size = ASN1_STRING_to_UTF8(&utf8, common);
+ decision = accessManager.verify(hostName, utf8[0 .. size]);
+ CRYPTO_free(utf8);
+ }
+ }
+ X509_free(cert);
+ if (decision != Decision.ALLOW) {
+ throw new TSSLException("Authorize: Could not authorize peer.");
+ }
+}
+
+/*
+ * OpenSSL error information used for storing D exceptions on the OpenSSL
+ * error stack.
+ */
+enum ERR_LIB_D_EXCEPTION = ERR_LIB_USER;
+enum ERR_F_D_EXCEPTION = 0; // function id - what to use here?
+enum ERR_R_D_EXCEPTION = 1234; // 99 and above are reserved for applications
+enum ERR_FILE_D_EXCEPTION = "d_exception";
+enum ERR_LINE_D_EXCEPTION = 0;
+enum ERR_FLAGS_D_EXCEPTION = 0;
+
+/**
+ * Returns an exception for the last.
+ *
+ * Params:
+ * location = An optional "location" to add to the error message (typically
+ * the last SSL API call).
+ */
+Exception getSSLException(string location = null, string clientFile = __FILE__,
+ size_t clientLine = __LINE__
+) {
+ // We can return either an exception saved from D BIO code, or a "true"
+ // OpenSSL error. Because there can possibly be more than one error on the
+ // error stack, we have to fetch all of them, and pick the last, i.e. newest
+ // one. We concatenate multiple successive OpenSSL error messages into a
+ // single one, but always just return the last D expcetion.
+ string message; // Probably better use an Appender here.
+ bool hadMessage;
+ Exception exception;
+
+ void initMessage() {
+ message.destroy();
+ hadMessage = false;
+ if (!location.empty) {
+ message ~= location;
+ message ~= ": ";
+ }
+ }
+ initMessage();
+
+ auto errn = errno;
+
+ const(char)* file = void;
+ int line = void;
+ const(char)* data = void;
+ int flags = void;
+ c_ulong code = void;
+ while ((code = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0) {
+ if (ERR_GET_REASON(code) == ERR_R_D_EXCEPTION) {
+ initMessage();
+ GC.removeRoot(cast(void*)data);
+ exception = cast(Exception)data;
+ } else {
+ exception = null;
+
+ if (hadMessage) {
+ message ~= ", ";
+ }
+
+ auto reason = ERR_reason_error_string(code);
+ if (reason) {
+ message ~= "SSL error: " ~ to!string(reason);
+ } else {
+ message ~= "SSL error #" ~ to!string(code);
+ }
+
+ hadMessage = true;
+ }
+ }
+
+ // If the last item from the stack was a D exception, throw it.
+ if (exception) return exception;
+
+ // We are dealing with an OpenSSL error that doesn't root in a D exception.
+ if (!hadMessage) {
+ // If we didn't get an actual error from the stack yet, try errno.
+ string errnString;
+ if (errn != 0) {
+ errnString = to!string(strerror(errn));
+ }
+ if (errnString.empty) {
+ message ~= "Unknown error";
+ } else {
+ message ~= errnString;
+ }
+ }
+
+ message ~= ".";
+ return new TSSLException(message, clientFile, clientLine);
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/internal/ssl_bio.d b/src/jaegertracing/thrift/lib/d/src/thrift/internal/ssl_bio.d
new file mode 100644
index 000000000..ae850275a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/internal/ssl_bio.d
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Provides a SSL BIO implementation wrapping a Thrift transport.
+ *
+ * This way, SSL I/O can be relayed over Thrift transport without introducing
+ * an additional layer of buffering, especially for the non-blocking
+ * transports.
+ *
+ * For the Thrift transport incarnations of the SSL entities, "tt" is used as
+ * prefix for clarity.
+ */
+module thrift.internal.ssl_bio;
+
+import core.stdc.config;
+import core.stdc.string : strlen;
+import core.memory : GC;
+import deimos.openssl.bio;
+import deimos.openssl.err;
+import thrift.base;
+import thrift.internal.ssl;
+import thrift.transport.base;
+
+/**
+ * Creates an SSL BIO object wrapping the given transport.
+ *
+ * Exceptions thrown by the transport are pushed onto the OpenSSL error stack,
+ * using the location/reason values from thrift.internal.ssl.ERR_*_D_EXCEPTION.
+ *
+ * The transport is assumed to be ready for reading and writing when the BIO
+ * functions are called, it is not opened by the implementation.
+ *
+ * Params:
+ * transport = The transport to wrap.
+ * closeTransport = Whether the close the transport when the SSL BIO is
+ * closed.
+ */
+BIO* createTTransportBIO(TTransport transport, bool closeTransport) {
+ auto result = BIO_new(cast(BIO_METHOD*)&ttBioMethod);
+ if (!result) return null;
+
+ GC.addRoot(cast(void*)transport);
+ BIO_set_fd(result, closeTransport, cast(c_long)cast(void*)transport);
+
+ return result;
+}
+
+private {
+ // Helper to get the Thrift transport assigned with the given BIO.
+ TTransport trans(BIO* b) nothrow {
+ auto result = cast(TTransport)b.ptr;
+ assert(result);
+ return result;
+ }
+
+ void setError(Exception e) nothrow {
+ ERR_put_error(ERR_LIB_D_EXCEPTION, ERR_F_D_EXCEPTION, ERR_R_D_EXCEPTION,
+ ERR_FILE_D_EXCEPTION, ERR_LINE_D_EXCEPTION);
+ try { GC.addRoot(cast(void*)e); } catch (Throwable) {}
+ ERR_set_error_data(cast(char*)e, ERR_FLAGS_D_EXCEPTION);
+ }
+
+ extern(C) int ttWrite(BIO* b, const(char)* data, int length) nothrow {
+ assert(b);
+ if (!data || length <= 0) return 0;
+ try {
+ trans(b).write((cast(ubyte*)data)[0 .. length]);
+ return length;
+ } catch (Exception e) {
+ setError(e);
+ return -1;
+ }
+ }
+
+ extern(C) int ttRead(BIO* b, char* data, int length) nothrow {
+ assert(b);
+ if (!data || length <= 0) return 0;
+ try {
+ return cast(int)trans(b).read((cast(ubyte*)data)[0 .. length]);
+ } catch (Exception e) {
+ setError(e);
+ return -1;
+ }
+ }
+
+ extern(C) int ttPuts(BIO* b, const(char)* str) nothrow {
+ return ttWrite(b, str, cast(int)strlen(str));
+ }
+
+ extern(C) c_long ttCtrl(BIO* b, int cmd, c_long num, void* ptr) nothrow {
+ assert(b);
+
+ switch (cmd) {
+ case BIO_C_SET_FD:
+ // Note that close flag and "fd" are actually reversed here because we
+ // need 64 bit width for the pointer – should probably drop BIO_set_fd
+ // altogether.
+ ttDestroy(b);
+ b.ptr = cast(void*)num;
+ b.shutdown = cast(int)ptr;
+ b.init_ = 1;
+ return 1;
+ case BIO_C_GET_FD:
+ if (!b.init_) return -1;
+ *(cast(void**)ptr) = b.ptr;
+ return cast(c_long)b.ptr;
+ case BIO_CTRL_GET_CLOSE:
+ return b.shutdown;
+ case BIO_CTRL_SET_CLOSE:
+ b.shutdown = cast(int)num;
+ return 1;
+ case BIO_CTRL_FLUSH:
+ try {
+ trans(b).flush();
+ return 1;
+ } catch (Exception e) {
+ setError(e);
+ return -1;
+ }
+ case BIO_CTRL_DUP:
+ // Seems like we have nothing to do on duplication, but couldn't find
+ // any documentation if this actually ever happens during normal SSL
+ // usage.
+ return 1;
+ default:
+ return 0;
+ }
+ }
+
+ extern(C) int ttCreate(BIO* b) nothrow {
+ assert(b);
+ b.init_ = 0;
+ b.num = 0; // User-defined number field, unused here.
+ b.ptr = null;
+ b.flags = 0;
+ return 1;
+ }
+
+ extern(C) int ttDestroy(BIO* b) nothrow {
+ if (!b) return 0;
+
+ int rc = 1;
+ if (b.shutdown) {
+ if (b.init_) {
+ try {
+ trans(b).close();
+ GC.removeRoot(cast(void*)trans(b));
+ b.ptr = null;
+ } catch (Exception e) {
+ setError(e);
+ rc = -1;
+ }
+ }
+ b.init_ = 0;
+ b.flags = 0;
+ }
+
+ return rc;
+ }
+
+ immutable BIO_METHOD ttBioMethod = {
+ BIO_TYPE_SOURCE_SINK,
+ "TTransport",
+ &ttWrite,
+ &ttRead,
+ &ttPuts,
+ null, // gets
+ &ttCtrl,
+ &ttCreate,
+ &ttDestroy,
+ null // callback_ctrl
+ };
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/internal/test/protocol.d b/src/jaegertracing/thrift/lib/d/src/thrift/internal/test/protocol.d
new file mode 100644
index 000000000..2d25154de
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/internal/test/protocol.d
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.internal.test.protocol;
+
+import std.exception;
+import thrift.transport.memory;
+import thrift.protocol.base;
+
+version (unittest):
+
+void testContainerSizeLimit(Protocol)() if (isTProtocol!Protocol) {
+ auto buffer = new TMemoryBuffer;
+ auto prot = new Protocol(buffer);
+
+ // Make sure reading fails if a container larger than the size limit is read.
+ prot.containerSizeLimit = 3;
+
+ {
+ prot.writeListBegin(TList(TType.I32, 4));
+ prot.writeI32(0); // Make sure size can be read e.g. for JSON protocol.
+ prot.reset();
+
+ auto e = cast(TProtocolException)collectException(prot.readListBegin());
+ enforce(e && e.type == TProtocolException.Type.SIZE_LIMIT);
+ prot.reset();
+ buffer.reset();
+ }
+
+ {
+ prot.writeMapBegin(TMap(TType.I32, TType.I32, 4));
+ prot.writeI32(0); // Make sure size can be read e.g. for JSON protocol.
+ prot.reset();
+
+ auto e = cast(TProtocolException)collectException(prot.readMapBegin());
+ enforce(e && e.type == TProtocolException.Type.SIZE_LIMIT);
+ prot.reset();
+ buffer.reset();
+ }
+
+ {
+ prot.writeSetBegin(TSet(TType.I32, 4));
+ prot.writeI32(0); // Make sure size can be read e.g. for JSON protocol.
+ prot.reset();
+
+ auto e = cast(TProtocolException)collectException(prot.readSetBegin());
+ enforce(e && e.type == TProtocolException.Type.SIZE_LIMIT);
+ prot.reset();
+ buffer.reset();
+ }
+
+ // Make sure reading works if the containers are smaller than the limit or
+ // no limit is set.
+ foreach (limit; [3, 0, -1]) {
+ prot.containerSizeLimit = limit;
+
+ {
+ prot.writeListBegin(TList(TType.I32, 2));
+ prot.writeI32(0);
+ prot.writeI32(1);
+ prot.writeListEnd();
+ prot.reset();
+
+ auto list = prot.readListBegin();
+ enforce(list.elemType == TType.I32);
+ enforce(list.size == 2);
+ enforce(prot.readI32() == 0);
+ enforce(prot.readI32() == 1);
+ prot.readListEnd();
+
+ prot.reset();
+ buffer.reset();
+ }
+
+ {
+ prot.writeMapBegin(TMap(TType.I32, TType.I32, 2));
+ prot.writeI32(0);
+ prot.writeI32(1);
+ prot.writeI32(2);
+ prot.writeI32(3);
+ prot.writeMapEnd();
+ prot.reset();
+
+ auto map = prot.readMapBegin();
+ enforce(map.keyType == TType.I32);
+ enforce(map.valueType == TType.I32);
+ enforce(map.size == 2);
+ enforce(prot.readI32() == 0);
+ enforce(prot.readI32() == 1);
+ enforce(prot.readI32() == 2);
+ enforce(prot.readI32() == 3);
+ prot.readMapEnd();
+
+ prot.reset();
+ buffer.reset();
+ }
+
+ {
+ prot.writeSetBegin(TSet(TType.I32, 2));
+ prot.writeI32(0);
+ prot.writeI32(1);
+ prot.writeSetEnd();
+ prot.reset();
+
+ auto set = prot.readSetBegin();
+ enforce(set.elemType == TType.I32);
+ enforce(set.size == 2);
+ enforce(prot.readI32() == 0);
+ enforce(prot.readI32() == 1);
+ prot.readSetEnd();
+
+ prot.reset();
+ buffer.reset();
+ }
+ }
+}
+
+void testStringSizeLimit(Protocol)() if (isTProtocol!Protocol) {
+ auto buffer = new TMemoryBuffer;
+ auto prot = new Protocol(buffer);
+
+ // Make sure reading fails if a string larger than the size limit is read.
+ prot.stringSizeLimit = 3;
+
+ {
+ prot.writeString("asdf");
+ prot.reset();
+
+ auto e = cast(TProtocolException)collectException(prot.readString());
+ enforce(e && e.type == TProtocolException.Type.SIZE_LIMIT);
+ prot.reset();
+ buffer.reset();
+ }
+
+ {
+ prot.writeBinary([1, 2, 3, 4]);
+ prot.reset();
+
+ auto e = cast(TProtocolException)collectException(prot.readBinary());
+ enforce(e && e.type == TProtocolException.Type.SIZE_LIMIT);
+ prot.reset();
+ buffer.reset();
+ }
+
+ // Make sure reading works if the containers are smaller than the limit or
+ // no limit is set.
+ foreach (limit; [3, 0, -1]) {
+ prot.containerSizeLimit = limit;
+
+ {
+ prot.writeString("as");
+ prot.reset();
+
+ enforce(prot.readString() == "as");
+ prot.reset();
+ buffer.reset();
+ }
+
+ {
+ prot.writeBinary([1, 2]);
+ prot.reset();
+
+ enforce(prot.readBinary() == [1, 2]);
+ prot.reset();
+ buffer.reset();
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/internal/test/server.d b/src/jaegertracing/thrift/lib/d/src/thrift/internal/test/server.d
new file mode 100644
index 000000000..fc5e86bbc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/internal/test/server.d
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.internal.test.server;
+
+import core.sync.condition;
+import core.sync.mutex;
+import core.thread : Thread;
+import std.datetime;
+import std.exception : enforce;
+import std.typecons : WhiteHole;
+import std.variant : Variant;
+import thrift.protocol.base;
+import thrift.protocol.binary;
+import thrift.protocol.processor;
+import thrift.server.base;
+import thrift.server.transport.socket;
+import thrift.transport.base;
+import thrift.util.cancellation;
+
+version(unittest):
+
+/**
+ * Tests if serving is stopped correctly if the cancellation passed to serve()
+ * is triggered.
+ *
+ * Because the tests are run many times in a loop, this is indirectly also a
+ * test whether socket, etc. handles are cleaned up correctly, because the
+ * application will likely run out of handles otherwise.
+ */
+void testServeCancel(Server)(void delegate(Server) serverSetup = null) if (
+ is(Server : TServer)
+) {
+ auto proc = new WhiteHole!TProcessor;
+ auto tf = new TTransportFactory;
+ auto pf = new TBinaryProtocolFactory!();
+
+ // Need a special case for TNonblockingServer which doesn't use
+ // TServerTransport.
+ static if (__traits(compiles, new Server(proc, 0, tf, pf))) {
+ auto server = new Server(proc, 0, tf, pf);
+ } else {
+ auto server = new Server(proc, new TServerSocket(0), tf, pf);
+ }
+
+ // On Windows, we use TCP sockets to replace socketpair(). Since they stay
+ // in TIME_WAIT for some time even if they are properly closed, we have to use
+ // a lower number of iterations to avoid running out of ports/buffer space.
+ version (Windows) {
+ enum ITERATIONS = 100;
+ } else {
+ enum ITERATIONS = 10000;
+ }
+
+ if (serverSetup) serverSetup(server);
+
+ auto servingMutex = new Mutex;
+ auto servingCondition = new Condition(servingMutex);
+ auto doneMutex = new Mutex;
+ auto doneCondition = new Condition(doneMutex);
+
+ class CancellingHandler : TServerEventHandler {
+ void preServe() {
+ synchronized (servingMutex) {
+ servingCondition.notifyAll();
+ }
+ }
+ Variant createContext(TProtocol input, TProtocol output) { return Variant.init; }
+ void deleteContext(Variant serverContext, TProtocol input, TProtocol output) {}
+ void preProcess(Variant serverContext, TTransport transport) {}
+ }
+ server.eventHandler = new CancellingHandler;
+
+ foreach (i; 0 .. ITERATIONS) {
+ synchronized (servingMutex) {
+ auto cancel = new TCancellationOrigin;
+ synchronized (doneMutex) {
+ auto serverThread = new Thread({
+ server.serve(cancel);
+ synchronized (doneMutex) {
+ doneCondition.notifyAll();
+ }
+ });
+ serverThread.isDaemon = true;
+ serverThread.start();
+
+ servingCondition.wait();
+
+ cancel.trigger();
+ enforce(doneCondition.wait(dur!"msecs"(3*1000)));
+ serverThread.join();
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/internal/traits.d b/src/jaegertracing/thrift/lib/d/src/thrift/internal/traits.d
new file mode 100644
index 000000000..8ce1089e8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/internal/traits.d
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.internal.traits;
+
+import std.traits;
+
+/**
+ * Adds »nothrow« to the type of the passed function pointer/delegate, if it
+ * is not already present.
+ *
+ * Technically, assumeNothrow just performs a cast, but using it has the
+ * advantage of being explicitly about the operation that is performed.
+ */
+auto assumeNothrow(T)(T t) if (isFunctionPointer!T || isDelegate!T) {
+ enum attrs = functionAttributes!T | FunctionAttribute.nothrow_;
+ return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/protocol/base.d b/src/jaegertracing/thrift/lib/d/src/thrift/protocol/base.d
new file mode 100644
index 000000000..5b6d84514
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/protocol/base.d
@@ -0,0 +1,449 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Defines the basic interface for a Thrift protocol and associated exception
+ * types.
+ *
+ * Most parts of the protocol API are typically not used in client code, as
+ * the actual serialization code is generated by thrift.codegen.* – the only
+ * interesting thing usually is that there are protocols which can be created
+ * from transports and passed around.
+ */
+module thrift.protocol.base;
+
+import thrift.base;
+import thrift.transport.base;
+
+/**
+ * The field types Thrift protocols support.
+ */
+enum TType : byte {
+ STOP = 0, /// Used to mark the end of a sequence of fields.
+ VOID = 1, ///
+ BOOL = 2, ///
+ BYTE = 3, ///
+ DOUBLE = 4, ///
+ I16 = 6, ///
+ I32 = 8, ///
+ I64 = 10, ///
+ STRING = 11, ///
+ STRUCT = 12, ///
+ MAP = 13, ///
+ SET = 14, ///
+ LIST = 15 ///
+}
+
+/**
+ * Types of Thrift RPC messages.
+ */
+enum TMessageType : byte {
+ CALL = 1, /// Call of a normal, two-way RPC method.
+ REPLY = 2, /// Reply to a normal method call.
+ EXCEPTION = 3, /// Reply to a method call if target raised a TApplicationException.
+ ONEWAY = 4 /// Call of a one-way RPC method which is not followed by a reply.
+}
+
+/**
+ * Descriptions of Thrift entities.
+ */
+struct TField {
+ string name;
+ TType type;
+ short id;
+}
+
+/// ditto
+struct TList {
+ TType elemType;
+ size_t size;
+}
+
+/// ditto
+struct TMap {
+ TType keyType;
+ TType valueType;
+ size_t size;
+}
+
+/// ditto
+struct TMessage {
+ string name;
+ TMessageType type;
+ int seqid;
+}
+
+/// ditto
+struct TSet {
+ TType elemType;
+ size_t size;
+}
+
+/// ditto
+struct TStruct {
+ string name;
+}
+
+/**
+ * Interface for a Thrift protocol implementation. Essentially, it defines
+ * a way of reading and writing all the base types, plus a mechanism for
+ * writing out structs with indexed fields.
+ *
+ * TProtocol objects should not be shared across multiple encoding contexts,
+ * as they may need to maintain internal state in some protocols (e.g. JSON).
+ * Note that is is acceptable for the TProtocol module to do its own internal
+ * buffered reads/writes to the underlying TTransport where appropriate (i.e.
+ * when parsing an input XML stream, reading could be batched rather than
+ * looking ahead character by character for a close tag).
+ */
+interface TProtocol {
+ /// The underlying transport used by the protocol.
+ TTransport transport() @property;
+
+ /*
+ * Writing methods.
+ */
+
+ void writeBool(bool b); ///
+ void writeByte(byte b); ///
+ void writeI16(short i16); ///
+ void writeI32(int i32); ///
+ void writeI64(long i64); ///
+ void writeDouble(double dub); ///
+ void writeString(string str); ///
+ void writeBinary(ubyte[] buf); ///
+
+ void writeMessageBegin(TMessage message); ///
+ void writeMessageEnd(); ///
+ void writeStructBegin(TStruct tstruct); ///
+ void writeStructEnd(); ///
+ void writeFieldBegin(TField field); ///
+ void writeFieldEnd(); ///
+ void writeFieldStop(); ///
+ void writeListBegin(TList list); ///
+ void writeListEnd(); ///
+ void writeMapBegin(TMap map); ///
+ void writeMapEnd(); ///
+ void writeSetBegin(TSet set); ///
+ void writeSetEnd(); ///
+
+ /*
+ * Reading methods.
+ */
+
+ bool readBool(); ///
+ byte readByte(); ///
+ short readI16(); ///
+ int readI32(); ///
+ long readI64(); ///
+ double readDouble(); ///
+ string readString(); ///
+ ubyte[] readBinary(); ///
+
+ TMessage readMessageBegin(); ///
+ void readMessageEnd(); ///
+ TStruct readStructBegin(); ///
+ void readStructEnd(); ///
+ TField readFieldBegin(); ///
+ void readFieldEnd(); ///
+ TList readListBegin(); ///
+ void readListEnd(); ///
+ TMap readMapBegin(); ///
+ void readMapEnd(); ///
+ TSet readSetBegin(); ///
+ void readSetEnd(); ///
+
+ /**
+ * Reset any internal state back to a blank slate, if the protocol is
+ * stateful.
+ */
+ void reset();
+}
+
+/**
+ * true if T is a TProtocol.
+ */
+template isTProtocol(T) {
+ enum isTProtocol = is(T : TProtocol);
+}
+
+unittest {
+ static assert(isTProtocol!TProtocol);
+ static assert(!isTProtocol!void);
+}
+
+/**
+ * Creates a protocol operating on a given transport.
+ */
+interface TProtocolFactory {
+ ///
+ TProtocol getProtocol(TTransport trans);
+}
+
+/**
+ * A protocol-level exception.
+ */
+class TProtocolException : TException {
+ /// The possible exception types.
+ enum Type {
+ UNKNOWN, ///
+ INVALID_DATA, ///
+ NEGATIVE_SIZE, ///
+ SIZE_LIMIT, ///
+ BAD_VERSION, ///
+ NOT_IMPLEMENTED, ///
+ DEPTH_LIMIT ///
+ }
+
+ ///
+ this(Type type, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
+ static string msgForType(Type type) {
+ switch (type) {
+ case Type.UNKNOWN: return "Unknown protocol exception";
+ case Type.INVALID_DATA: return "Invalid data";
+ case Type.NEGATIVE_SIZE: return "Negative size";
+ case Type.SIZE_LIMIT: return "Exceeded size limit";
+ case Type.BAD_VERSION: return "Invalid version";
+ case Type.NOT_IMPLEMENTED: return "Not implemented";
+ case Type.DEPTH_LIMIT: return "Exceeded size limit";
+ default: return "(Invalid exception type)";
+ }
+ }
+ this(msgForType(type), type, file, line, next);
+ }
+
+ ///
+ this(string msg, string file = __FILE__, size_t line = __LINE__,
+ Throwable next = null)
+ {
+ this(msg, Type.UNKNOWN, file, line, next);
+ }
+
+ ///
+ this(string msg, Type type, string file = __FILE__, size_t line = __LINE__,
+ Throwable next = null)
+ {
+ super(msg, file, line, next);
+ type_ = type;
+ }
+
+ ///
+ Type type() const @property {
+ return type_;
+ }
+
+protected:
+ Type type_;
+}
+
+/**
+ * Skips a field of the given type on the protocol.
+ *
+ * The main purpose of skip() is to allow treating struct and container types,
+ * (where multiple primitive types have to be skipped) the same as scalar types
+ * in generated code.
+ */
+void skip(Protocol)(Protocol prot, TType type) if (is(Protocol : TProtocol)) {
+ switch (type) {
+ case TType.BOOL:
+ prot.readBool();
+ break;
+
+ case TType.BYTE:
+ prot.readByte();
+ break;
+
+ case TType.I16:
+ prot.readI16();
+ break;
+
+ case TType.I32:
+ prot.readI32();
+ break;
+
+ case TType.I64:
+ prot.readI64();
+ break;
+
+ case TType.DOUBLE:
+ prot.readDouble();
+ break;
+
+ case TType.STRING:
+ prot.readBinary();
+ break;
+
+ case TType.STRUCT:
+ prot.readStructBegin();
+ while (true) {
+ auto f = prot.readFieldBegin();
+ if (f.type == TType.STOP) break;
+ skip(prot, f.type);
+ prot.readFieldEnd();
+ }
+ prot.readStructEnd();
+ break;
+
+ case TType.LIST:
+ auto l = prot.readListBegin();
+ foreach (i; 0 .. l.size) {
+ skip(prot, l.elemType);
+ }
+ prot.readListEnd();
+ break;
+
+ case TType.MAP:
+ auto m = prot.readMapBegin();
+ foreach (i; 0 .. m.size) {
+ skip(prot, m.keyType);
+ skip(prot, m.valueType);
+ }
+ prot.readMapEnd();
+ break;
+
+ case TType.SET:
+ auto s = prot.readSetBegin();
+ foreach (i; 0 .. s.size) {
+ skip(prot, s.elemType);
+ }
+ prot.readSetEnd();
+ break;
+
+ default:
+ throw new TProtocolException(TProtocolException.Type.INVALID_DATA);
+ }
+}
+
+/**
+ * Application-level exception.
+ *
+ * It is thrown if an RPC call went wrong on the application layer, e.g. if
+ * the receiver does not know the method name requested or a method invoked by
+ * the service processor throws an exception not part of the Thrift API.
+ */
+class TApplicationException : TException {
+ /// The possible exception types.
+ enum Type {
+ UNKNOWN = 0, ///
+ UNKNOWN_METHOD = 1, ///
+ INVALID_MESSAGE_TYPE = 2, ///
+ WRONG_METHOD_NAME = 3, ///
+ BAD_SEQUENCE_ID = 4, ///
+ MISSING_RESULT = 5, ///
+ INTERNAL_ERROR = 6, ///
+ PROTOCOL_ERROR = 7, ///
+ INVALID_TRANSFORM = 8, ///
+ INVALID_PROTOCOL = 9, ///
+ UNSUPPORTED_CLIENT_TYPE = 10 ///
+ }
+
+ ///
+ this(Type type, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
+ static string msgForType(Type type) {
+ switch (type) {
+ case Type.UNKNOWN: return "Unknown application exception";
+ case Type.UNKNOWN_METHOD: return "Unknown method";
+ case Type.INVALID_MESSAGE_TYPE: return "Invalid message type";
+ case Type.WRONG_METHOD_NAME: return "Wrong method name";
+ case Type.BAD_SEQUENCE_ID: return "Bad sequence identifier";
+ case Type.MISSING_RESULT: return "Missing result";
+ case Type.INTERNAL_ERROR: return "Internal error";
+ case Type.PROTOCOL_ERROR: return "Protocol error";
+ case Type.INVALID_TRANSFORM: return "Invalid transform";
+ case Type.INVALID_PROTOCOL: return "Invalid protocol";
+ case Type.UNSUPPORTED_CLIENT_TYPE: return "Unsupported client type";
+ default: return "(Invalid exception type)";
+ }
+ }
+ this(msgForType(type), type, file, line, next);
+ }
+
+ ///
+ this(string msg, string file = __FILE__, size_t line = __LINE__,
+ Throwable next = null)
+ {
+ this(msg, Type.UNKNOWN, file, line, next);
+ }
+
+ ///
+ this(string msg, Type type, string file = __FILE__, size_t line = __LINE__,
+ Throwable next = null)
+ {
+ super(msg, file, line, next);
+ type_ = type;
+ }
+
+ ///
+ Type type() @property const {
+ return type_;
+ }
+
+ // TODO: Replace hand-written read()/write() with thrift.codegen templates.
+
+ ///
+ void read(TProtocol iprot) {
+ iprot.readStructBegin();
+ while (true) {
+ auto f = iprot.readFieldBegin();
+ if (f.type == TType.STOP) break;
+
+ switch (f.id) {
+ case 1:
+ if (f.type == TType.STRING) {
+ msg = iprot.readString();
+ } else {
+ skip(iprot, f.type);
+ }
+ break;
+ case 2:
+ if (f.type == TType.I32) {
+ type_ = cast(Type)iprot.readI32();
+ } else {
+ skip(iprot, f.type);
+ }
+ break;
+ default:
+ skip(iprot, f.type);
+ break;
+ }
+ }
+ iprot.readStructEnd();
+ }
+
+ ///
+ void write(TProtocol oprot) const {
+ oprot.writeStructBegin(TStruct("TApplicationException"));
+
+ if (msg != null) {
+ oprot.writeFieldBegin(TField("message", TType.STRING, 1));
+ oprot.writeString(msg);
+ oprot.writeFieldEnd();
+ }
+
+ oprot.writeFieldBegin(TField("type", TType.I32, 2));
+ oprot.writeI32(type_);
+ oprot.writeFieldEnd();
+
+ oprot.writeFieldStop();
+ oprot.writeStructEnd();
+ }
+
+private:
+ Type type_;
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/protocol/binary.d b/src/jaegertracing/thrift/lib/d/src/thrift/protocol/binary.d
new file mode 100644
index 000000000..13d8fe88e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/protocol/binary.d
@@ -0,0 +1,414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.protocol.binary;
+
+import std.array : uninitializedArray;
+import std.typetuple : allSatisfy, TypeTuple;
+import thrift.protocol.base;
+import thrift.transport.base;
+import thrift.internal.endian;
+
+/**
+ * TProtocol implementation of the Binary Thrift protocol.
+ */
+final class TBinaryProtocol(Transport = TTransport) if (
+ isTTransport!Transport
+) : TProtocol {
+
+ /**
+ * Constructs a new instance.
+ *
+ * Params:
+ * trans = The transport to use.
+ * containerSizeLimit = If positive, the container size is limited to the
+ * given number of items.
+ * stringSizeLimit = If positive, the string length is limited to the
+ * given number of bytes.
+ * strictRead = If false, old peers which do not include the protocol
+ * version are tolerated.
+ * strictWrite = Whether to include the protocol version in the header.
+ */
+ this(Transport trans, int containerSizeLimit = 0, int stringSizeLimit = 0,
+ bool strictRead = false, bool strictWrite = true
+ ) {
+ trans_ = trans;
+ this.containerSizeLimit = containerSizeLimit;
+ this.stringSizeLimit = stringSizeLimit;
+ this.strictRead = strictRead;
+ this.strictWrite = strictWrite;
+ }
+
+ Transport transport() @property {
+ return trans_;
+ }
+
+ void reset() {}
+
+ /**
+ * If false, old peers which do not include the protocol version in the
+ * message header are tolerated.
+ *
+ * Defaults to false.
+ */
+ bool strictRead;
+
+ /**
+ * Whether to include the protocol version in the message header (older
+ * versions didn't).
+ *
+ * Defaults to true.
+ */
+ bool strictWrite;
+
+ /**
+ * If positive, limits the number of items of deserialized containers to the
+ * given amount.
+ *
+ * This is useful to avoid allocating excessive amounts of memory when broken
+ * data is received. If the limit is exceeded, a SIZE_LIMIT-type
+ * TProtocolException is thrown.
+ *
+ * Defaults to zero (no limit).
+ */
+ int containerSizeLimit;
+
+ /**
+ * If positive, limits the length of deserialized strings/binary data to the
+ * given number of bytes.
+ *
+ * This is useful to avoid allocating excessive amounts of memory when broken
+ * data is received. If the limit is exceeded, a SIZE_LIMIT-type
+ * TProtocolException is thrown.
+ *
+ * Defaults to zero (no limit).
+ */
+ int stringSizeLimit;
+
+ /*
+ * Writing methods.
+ */
+
+ void writeBool(bool b) {
+ writeByte(b ? 1 : 0);
+ }
+
+ void writeByte(byte b) {
+ trans_.write((cast(ubyte*)&b)[0 .. 1]);
+ }
+
+ void writeI16(short i16) {
+ short net = hostToNet(i16);
+ trans_.write((cast(ubyte*)&net)[0 .. 2]);
+ }
+
+ void writeI32(int i32) {
+ int net = hostToNet(i32);
+ trans_.write((cast(ubyte*)&net)[0 .. 4]);
+ }
+
+ void writeI64(long i64) {
+ long net = hostToNet(i64);
+ trans_.write((cast(ubyte*)&net)[0 .. 8]);
+ }
+
+ void writeDouble(double dub) {
+ static assert(double.sizeof == ulong.sizeof);
+ auto bits = hostToNet(*cast(ulong*)(&dub));
+ trans_.write((cast(ubyte*)&bits)[0 .. 8]);
+ }
+
+ void writeString(string str) {
+ writeBinary(cast(ubyte[])str);
+ }
+
+ void writeBinary(ubyte[] buf) {
+ assert(buf.length <= int.max);
+ writeI32(cast(int)buf.length);
+ trans_.write(buf);
+ }
+
+ void writeMessageBegin(TMessage message) {
+ if (strictWrite) {
+ int versn = VERSION_1 | message.type;
+ writeI32(versn);
+ writeString(message.name);
+ writeI32(message.seqid);
+ } else {
+ writeString(message.name);
+ writeByte(message.type);
+ writeI32(message.seqid);
+ }
+ }
+ void writeMessageEnd() {}
+
+ void writeStructBegin(TStruct tstruct) {}
+ void writeStructEnd() {}
+
+ void writeFieldBegin(TField field) {
+ writeByte(field.type);
+ writeI16(field.id);
+ }
+ void writeFieldEnd() {}
+
+ void writeFieldStop() {
+ writeByte(TType.STOP);
+ }
+
+ void writeListBegin(TList list) {
+ assert(list.size <= int.max);
+ writeByte(list.elemType);
+ writeI32(cast(int)list.size);
+ }
+ void writeListEnd() {}
+
+ void writeMapBegin(TMap map) {
+ assert(map.size <= int.max);
+ writeByte(map.keyType);
+ writeByte(map.valueType);
+ writeI32(cast(int)map.size);
+ }
+ void writeMapEnd() {}
+
+ void writeSetBegin(TSet set) {
+ assert(set.size <= int.max);
+ writeByte(set.elemType);
+ writeI32(cast(int)set.size);
+ }
+ void writeSetEnd() {}
+
+
+ /*
+ * Reading methods.
+ */
+
+ bool readBool() {
+ return readByte() != 0;
+ }
+
+ byte readByte() {
+ ubyte[1] b = void;
+ trans_.readAll(b);
+ return cast(byte)b[0];
+ }
+
+ short readI16() {
+ IntBuf!short b = void;
+ trans_.readAll(b.bytes);
+ return netToHost(b.value);
+ }
+
+ int readI32() {
+ IntBuf!int b = void;
+ trans_.readAll(b.bytes);
+ return netToHost(b.value);
+ }
+
+ long readI64() {
+ IntBuf!long b = void;
+ trans_.readAll(b.bytes);
+ return netToHost(b.value);
+ }
+
+ double readDouble() {
+ IntBuf!long b = void;
+ trans_.readAll(b.bytes);
+ b.value = netToHost(b.value);
+ return *cast(double*)(&b.value);
+ }
+
+ string readString() {
+ return cast(string)readBinary();
+ }
+
+ ubyte[] readBinary() {
+ return readBinaryBody(readSize(stringSizeLimit));
+ }
+
+ TMessage readMessageBegin() {
+ TMessage msg = void;
+
+ int size = readI32();
+ if (size < 0) {
+ int versn = size & VERSION_MASK;
+ if (versn != VERSION_1) {
+ throw new TProtocolException("Bad protocol version.",
+ TProtocolException.Type.BAD_VERSION);
+ }
+
+ msg.type = cast(TMessageType)(size & MESSAGE_TYPE_MASK);
+ msg.name = readString();
+ msg.seqid = readI32();
+ } else {
+ if (strictRead) {
+ throw new TProtocolException(
+ "Protocol version missing, old client?",
+ TProtocolException.Type.BAD_VERSION);
+ } else {
+ if (size < 0) {
+ throw new TProtocolException(TProtocolException.Type.NEGATIVE_SIZE);
+ }
+ msg.name = cast(string)readBinaryBody(size);
+ msg.type = cast(TMessageType)(readByte());
+ msg.seqid = readI32();
+ }
+ }
+
+ return msg;
+ }
+ void readMessageEnd() {}
+
+ TStruct readStructBegin() {
+ return TStruct();
+ }
+ void readStructEnd() {}
+
+ TField readFieldBegin() {
+ TField f = void;
+ f.name = null;
+ f.type = cast(TType)readByte();
+ if (f.type == TType.STOP) return f;
+ f.id = readI16();
+ return f;
+ }
+ void readFieldEnd() {}
+
+ TList readListBegin() {
+ return TList(cast(TType)readByte(), readSize(containerSizeLimit));
+ }
+ void readListEnd() {}
+
+ TMap readMapBegin() {
+ return TMap(cast(TType)readByte(), cast(TType)readByte(),
+ readSize(containerSizeLimit));
+ }
+ void readMapEnd() {}
+
+ TSet readSetBegin() {
+ return TSet(cast(TType)readByte(), readSize(containerSizeLimit));
+ }
+ void readSetEnd() {}
+
+private:
+ ubyte[] readBinaryBody(int size) {
+ if (size == 0) {
+ return null;
+ }
+
+ auto buf = uninitializedArray!(ubyte[])(size);
+ trans_.readAll(buf);
+ return buf;
+ }
+
+ int readSize(int limit) {
+ auto size = readI32();
+ if (size < 0) {
+ throw new TProtocolException(TProtocolException.Type.NEGATIVE_SIZE);
+ } else if (limit > 0 && size > limit) {
+ throw new TProtocolException(TProtocolException.Type.SIZE_LIMIT);
+ }
+ return size;
+ }
+
+ enum MESSAGE_TYPE_MASK = 0x000000ff;
+ enum VERSION_MASK = 0xffff0000;
+ enum VERSION_1 = 0x80010000;
+
+ Transport trans_;
+}
+
+/**
+ * TBinaryProtocol construction helper to avoid having to explicitly specify
+ * the transport type, i.e. to allow the constructor being called using IFTI
+ * (see $(LINK2 http://d.puremagic.com/issues/show_bug.cgi?id=6082, D Bugzilla
+ * enhancement requet 6082)).
+ */
+TBinaryProtocol!Transport tBinaryProtocol(Transport)(Transport trans,
+ int containerSizeLimit = 0, int stringSizeLimit = 0,
+ bool strictRead = false, bool strictWrite = true
+) if (isTTransport!Transport) {
+ return new TBinaryProtocol!Transport(trans, containerSizeLimit,
+ stringSizeLimit, strictRead, strictWrite);
+}
+
+unittest {
+ import std.exception;
+ import thrift.transport.memory;
+
+ // Check the message header format.
+ auto buf = new TMemoryBuffer;
+ auto binary = tBinaryProtocol(buf);
+ binary.writeMessageBegin(TMessage("foo", TMessageType.CALL, 0));
+
+ auto header = new ubyte[15];
+ buf.readAll(header);
+ enforce(header == [
+ 128, 1, 0, 1, // Version 1, TMessageType.CALL
+ 0, 0, 0, 3, // Method name length
+ 102, 111, 111, // Method name ("foo")
+ 0, 0, 0, 0, // Sequence id
+ ]);
+}
+
+unittest {
+ import thrift.internal.test.protocol;
+ testContainerSizeLimit!(TBinaryProtocol!())();
+ testStringSizeLimit!(TBinaryProtocol!())();
+}
+
+/**
+ * TProtocolFactory creating a TBinaryProtocol instance for passed in
+ * transports.
+ *
+ * The optional Transports template tuple parameter can be used to specify
+ * one or more TTransport implementations to specifically instantiate
+ * TBinaryProtocol for. If the actual transport types encountered at
+ * runtime match one of the transports in the list, a specialized protocol
+ * instance is created. Otherwise, a generic TTransport version is used.
+ */
+class TBinaryProtocolFactory(Transports...) if (
+ allSatisfy!(isTTransport, Transports)
+) : TProtocolFactory {
+ ///
+ this (int containerSizeLimit = 0, int stringSizeLimit = 0,
+ bool strictRead = false, bool strictWrite = true
+ ) {
+ strictRead_ = strictRead;
+ strictWrite_ = strictWrite;
+ containerSizeLimit_ = containerSizeLimit;
+ stringSizeLimit_ = stringSizeLimit;
+ }
+
+ TProtocol getProtocol(TTransport trans) const {
+ foreach (Transport; TypeTuple!(Transports, TTransport)) {
+ auto concreteTrans = cast(Transport)trans;
+ if (concreteTrans) {
+ return new TBinaryProtocol!Transport(concreteTrans,
+ containerSizeLimit_, stringSizeLimit_, strictRead_, strictWrite_);
+ }
+ }
+ throw new TProtocolException(
+ "Passed null transport to TBinaryProtocolFactoy.");
+ }
+
+protected:
+ bool strictRead_;
+ bool strictWrite_;
+ int containerSizeLimit_;
+ int stringSizeLimit_;
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/protocol/compact.d b/src/jaegertracing/thrift/lib/d/src/thrift/protocol/compact.d
new file mode 100644
index 000000000..9155c8199
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/protocol/compact.d
@@ -0,0 +1,698 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.protocol.compact;
+
+import std.array : uninitializedArray;
+import std.typetuple : allSatisfy, TypeTuple;
+import thrift.protocol.base;
+import thrift.transport.base;
+import thrift.internal.endian;
+
+/**
+ * D implementation of the Compact protocol.
+ *
+ * See THRIFT-110 for a protocol description. This implementation is based on
+ * the C++ one.
+ */
+final class TCompactProtocol(Transport = TTransport) if (
+ isTTransport!Transport
+) : TProtocol {
+ /**
+ * Constructs a new instance.
+ *
+ * Params:
+ * trans = The transport to use.
+ * containerSizeLimit = If positive, the container size is limited to the
+ * given number of items.
+ * stringSizeLimit = If positive, the string length is limited to the
+ * given number of bytes.
+ */
+ this(Transport trans, int containerSizeLimit = 0, int stringSizeLimit = 0) {
+ trans_ = trans;
+ this.containerSizeLimit = containerSizeLimit;
+ this.stringSizeLimit = stringSizeLimit;
+ }
+
+ Transport transport() @property {
+ return trans_;
+ }
+
+ void reset() {
+ lastFieldId_ = 0;
+ fieldIdStack_ = null;
+ booleanField_ = TField.init;
+ hasBoolValue_ = false;
+ }
+
+ /**
+ * If positive, limits the number of items of deserialized containers to the
+ * given amount.
+ *
+ * This is useful to avoid allocating excessive amounts of memory when broken
+ * data is received. If the limit is exceeded, a SIZE_LIMIT-type
+ * TProtocolException is thrown.
+ *
+ * Defaults to zero (no limit).
+ */
+ int containerSizeLimit;
+
+ /**
+ * If positive, limits the length of deserialized strings/binary data to the
+ * given number of bytes.
+ *
+ * This is useful to avoid allocating excessive amounts of memory when broken
+ * data is received. If the limit is exceeded, a SIZE_LIMIT-type
+ * TProtocolException is thrown.
+ *
+ * Defaults to zero (no limit).
+ */
+ int stringSizeLimit;
+
+ /*
+ * Writing methods.
+ */
+
+ void writeBool(bool b) {
+ if (booleanField_.name !is null) {
+ // we haven't written the field header yet
+ writeFieldBeginInternal(booleanField_,
+ b ? CType.BOOLEAN_TRUE : CType.BOOLEAN_FALSE);
+ booleanField_.name = null;
+ } else {
+ // we're not part of a field, so just write the value
+ writeByte(b ? CType.BOOLEAN_TRUE : CType.BOOLEAN_FALSE);
+ }
+ }
+
+ void writeByte(byte b) {
+ trans_.write((cast(ubyte*)&b)[0..1]);
+ }
+
+ void writeI16(short i16) {
+ writeVarint32(i32ToZigzag(i16));
+ }
+
+ void writeI32(int i32) {
+ writeVarint32(i32ToZigzag(i32));
+ }
+
+ void writeI64(long i64) {
+ writeVarint64(i64ToZigzag(i64));
+ }
+
+ void writeDouble(double dub) {
+ ulong bits = hostToLe(*cast(ulong*)(&dub));
+ trans_.write((cast(ubyte*)&bits)[0 .. 8]);
+ }
+
+ void writeString(string str) {
+ writeBinary(cast(ubyte[])str);
+ }
+
+ void writeBinary(ubyte[] buf) {
+ assert(buf.length <= int.max);
+ writeVarint32(cast(int)buf.length);
+ trans_.write(buf);
+ }
+
+ void writeMessageBegin(TMessage msg) {
+ writeByte(cast(byte)PROTOCOL_ID);
+ writeByte(cast(byte)((VERSION_N & VERSION_MASK) |
+ ((cast(int)msg.type << TYPE_SHIFT_AMOUNT) & TYPE_MASK)));
+ writeVarint32(msg.seqid);
+ writeString(msg.name);
+ }
+ void writeMessageEnd() {}
+
+ void writeStructBegin(TStruct tstruct) {
+ fieldIdStack_ ~= lastFieldId_;
+ lastFieldId_ = 0;
+ }
+
+ void writeStructEnd() {
+ lastFieldId_ = fieldIdStack_[$ - 1];
+ fieldIdStack_ = fieldIdStack_[0 .. $ - 1];
+ fieldIdStack_.assumeSafeAppend();
+ }
+
+ void writeFieldBegin(TField field) {
+ if (field.type == TType.BOOL) {
+ booleanField_.name = field.name;
+ booleanField_.type = field.type;
+ booleanField_.id = field.id;
+ } else {
+ return writeFieldBeginInternal(field);
+ }
+ }
+ void writeFieldEnd() {}
+
+ void writeFieldStop() {
+ writeByte(TType.STOP);
+ }
+
+ void writeListBegin(TList list) {
+ writeCollectionBegin(list.elemType, list.size);
+ }
+ void writeListEnd() {}
+
+ void writeMapBegin(TMap map) {
+ if (map.size == 0) {
+ writeByte(0);
+ } else {
+ assert(map.size <= int.max);
+ writeVarint32(cast(int)map.size);
+ writeByte(cast(byte)(toCType(map.keyType) << 4 | toCType(map.valueType)));
+ }
+ }
+ void writeMapEnd() {}
+
+ void writeSetBegin(TSet set) {
+ writeCollectionBegin(set.elemType, set.size);
+ }
+ void writeSetEnd() {}
+
+
+ /*
+ * Reading methods.
+ */
+
+ bool readBool() {
+ if (hasBoolValue_ == true) {
+ hasBoolValue_ = false;
+ return boolValue_;
+ }
+
+ return readByte() == CType.BOOLEAN_TRUE;
+ }
+
+ byte readByte() {
+ ubyte[1] b = void;
+ trans_.readAll(b);
+ return cast(byte)b[0];
+ }
+
+ short readI16() {
+ return cast(short)zigzagToI32(readVarint32());
+ }
+
+ int readI32() {
+ return zigzagToI32(readVarint32());
+ }
+
+ long readI64() {
+ return zigzagToI64(readVarint64());
+ }
+
+ double readDouble() {
+ IntBuf!long b = void;
+ trans_.readAll(b.bytes);
+ b.value = leToHost(b.value);
+ return *cast(double*)(&b.value);
+ }
+
+ string readString() {
+ return cast(string)readBinary();
+ }
+
+ ubyte[] readBinary() {
+ auto size = readVarint32();
+ checkSize(size, stringSizeLimit);
+
+ if (size == 0) {
+ return null;
+ }
+
+ auto buf = uninitializedArray!(ubyte[])(size);
+ trans_.readAll(buf);
+ return buf;
+ }
+
+ TMessage readMessageBegin() {
+ TMessage msg = void;
+
+ auto protocolId = readByte();
+ if (protocolId != cast(byte)PROTOCOL_ID) {
+ throw new TProtocolException("Bad protocol identifier",
+ TProtocolException.Type.BAD_VERSION);
+ }
+
+ auto versionAndType = readByte();
+ auto ver = versionAndType & VERSION_MASK;
+ if (ver != VERSION_N) {
+ throw new TProtocolException("Bad protocol version",
+ TProtocolException.Type.BAD_VERSION);
+ }
+
+ msg.type = cast(TMessageType)((versionAndType >> TYPE_SHIFT_AMOUNT) & TYPE_BITS);
+ msg.seqid = readVarint32();
+ msg.name = readString();
+
+ return msg;
+ }
+ void readMessageEnd() {}
+
+ TStruct readStructBegin() {
+ fieldIdStack_ ~= lastFieldId_;
+ lastFieldId_ = 0;
+ return TStruct();
+ }
+
+ void readStructEnd() {
+ lastFieldId_ = fieldIdStack_[$ - 1];
+ fieldIdStack_ = fieldIdStack_[0 .. $ - 1];
+ }
+
+ TField readFieldBegin() {
+ TField f = void;
+ f.name = null;
+
+ auto bite = readByte();
+ auto type = cast(CType)(bite & 0x0f);
+
+ if (type == CType.STOP) {
+ // Struct stop byte, nothing more to do.
+ f.id = 0;
+ f.type = TType.STOP;
+ return f;
+ }
+
+ // Mask off the 4 MSB of the type header, which could contain a field id
+ // delta.
+ auto modifier = cast(short)((bite & 0xf0) >> 4);
+ if (modifier > 0) {
+ f.id = cast(short)(lastFieldId_ + modifier);
+ } else {
+ // Delta encoding not used, just read the id as usual.
+ f.id = readI16();
+ }
+ f.type = getTType(type);
+
+ if (type == CType.BOOLEAN_TRUE || type == CType.BOOLEAN_FALSE) {
+ // For boolean fields, the value is encoded in the type – keep it around
+ // for the readBool() call.
+ hasBoolValue_ = true;
+ boolValue_ = (type == CType.BOOLEAN_TRUE ? true : false);
+ }
+
+ lastFieldId_ = f.id;
+ return f;
+ }
+ void readFieldEnd() {}
+
+ TList readListBegin() {
+ auto sizeAndType = readByte();
+
+ auto lsize = (sizeAndType >> 4) & 0xf;
+ if (lsize == 0xf) {
+ lsize = readVarint32();
+ }
+ checkSize(lsize, containerSizeLimit);
+
+ TList l = void;
+ l.elemType = getTType(cast(CType)(sizeAndType & 0x0f));
+ l.size = cast(size_t)lsize;
+
+ return l;
+ }
+ void readListEnd() {}
+
+ TMap readMapBegin() {
+ TMap m = void;
+
+ auto size = readVarint32();
+ ubyte kvType;
+ if (size != 0) {
+ kvType = readByte();
+ }
+ checkSize(size, containerSizeLimit);
+
+ m.size = size;
+ m.keyType = getTType(cast(CType)(kvType >> 4));
+ m.valueType = getTType(cast(CType)(kvType & 0xf));
+
+ return m;
+ }
+ void readMapEnd() {}
+
+ TSet readSetBegin() {
+ auto sizeAndType = readByte();
+
+ auto lsize = (sizeAndType >> 4) & 0xf;
+ if (lsize == 0xf) {
+ lsize = readVarint32();
+ }
+ checkSize(lsize, containerSizeLimit);
+
+ TSet s = void;
+ s.elemType = getTType(cast(CType)(sizeAndType & 0xf));
+ s.size = cast(size_t)lsize;
+
+ return s;
+ }
+ void readSetEnd() {}
+
+private:
+ void writeFieldBeginInternal(TField field, byte typeOverride = -1) {
+ // If there's a type override, use that.
+ auto typeToWrite = (typeOverride == -1 ? toCType(field.type) : typeOverride);
+
+ // check if we can use delta encoding for the field id
+ if (field.id > lastFieldId_ && (field.id - lastFieldId_) <= 15) {
+ // write them together
+ writeByte(cast(byte)((field.id - lastFieldId_) << 4 | typeToWrite));
+ } else {
+ // write them separate
+ writeByte(cast(byte)typeToWrite);
+ writeI16(field.id);
+ }
+
+ lastFieldId_ = field.id;
+ }
+
+
+ void writeCollectionBegin(TType elemType, size_t size) {
+ if (size <= 14) {
+ writeByte(cast(byte)(size << 4 | toCType(elemType)));
+ } else {
+ assert(size <= int.max);
+ writeByte(cast(byte)(0xf0 | toCType(elemType)));
+ writeVarint32(cast(int)size);
+ }
+ }
+
+ void writeVarint32(uint n) {
+ ubyte[5] buf = void;
+ ubyte wsize;
+
+ while (true) {
+ if ((n & ~0x7F) == 0) {
+ buf[wsize++] = cast(ubyte)n;
+ break;
+ } else {
+ buf[wsize++] = cast(ubyte)((n & 0x7F) | 0x80);
+ n >>= 7;
+ }
+ }
+
+ trans_.write(buf[0 .. wsize]);
+ }
+
+ /*
+ * Write an i64 as a varint. Results in 1-10 bytes on the wire.
+ */
+ void writeVarint64(ulong n) {
+ ubyte[10] buf = void;
+ ubyte wsize;
+
+ while (true) {
+ if ((n & ~0x7FL) == 0) {
+ buf[wsize++] = cast(ubyte)n;
+ break;
+ } else {
+ buf[wsize++] = cast(ubyte)((n & 0x7F) | 0x80);
+ n >>= 7;
+ }
+ }
+
+ trans_.write(buf[0 .. wsize]);
+ }
+
+ /*
+ * Convert l into a zigzag long. This allows negative numbers to be
+ * represented compactly as a varint.
+ */
+ ulong i64ToZigzag(long l) {
+ return (l << 1) ^ (l >> 63);
+ }
+
+ /*
+ * Convert n into a zigzag int. This allows negative numbers to be
+ * represented compactly as a varint.
+ */
+ uint i32ToZigzag(int n) {
+ return (n << 1) ^ (n >> 31);
+ }
+
+ CType toCType(TType type) {
+ final switch (type) {
+ case TType.STOP:
+ return CType.STOP;
+ case TType.BOOL:
+ return CType.BOOLEAN_TRUE;
+ case TType.BYTE:
+ return CType.BYTE;
+ case TType.DOUBLE:
+ return CType.DOUBLE;
+ case TType.I16:
+ return CType.I16;
+ case TType.I32:
+ return CType.I32;
+ case TType.I64:
+ return CType.I64;
+ case TType.STRING:
+ return CType.BINARY;
+ case TType.STRUCT:
+ return CType.STRUCT;
+ case TType.MAP:
+ return CType.MAP;
+ case TType.SET:
+ return CType.SET;
+ case TType.LIST:
+ return CType.LIST;
+ case TType.VOID:
+ assert(false, "Invalid type passed.");
+ }
+ }
+
+ int readVarint32() {
+ return cast(int)readVarint64();
+ }
+
+ long readVarint64() {
+ ulong val;
+ ubyte shift;
+ ubyte[10] buf = void; // 64 bits / (7 bits/byte) = 10 bytes.
+ auto bufSize = buf.sizeof;
+ auto borrowed = trans_.borrow(buf.ptr, bufSize);
+
+ ubyte rsize;
+
+ if (borrowed) {
+ // Fast path.
+ while (true) {
+ auto bite = borrowed[rsize];
+ rsize++;
+ val |= cast(ulong)(bite & 0x7f) << shift;
+ shift += 7;
+ if (!(bite & 0x80)) {
+ trans_.consume(rsize);
+ return val;
+ }
+ // Have to check for invalid data so we don't crash.
+ if (rsize == buf.sizeof) {
+ throw new TProtocolException(TProtocolException.Type.INVALID_DATA,
+ "Variable-length int over 10 bytes.");
+ }
+ }
+ } else {
+ // Slow path.
+ while (true) {
+ ubyte[1] bite;
+ trans_.readAll(bite);
+ ++rsize;
+
+ val |= cast(ulong)(bite[0] & 0x7f) << shift;
+ shift += 7;
+ if (!(bite[0] & 0x80)) {
+ return val;
+ }
+
+ // Might as well check for invalid data on the slow path too.
+ if (rsize >= buf.sizeof) {
+ throw new TProtocolException(TProtocolException.Type.INVALID_DATA,
+ "Variable-length int over 10 bytes.");
+ }
+ }
+ }
+ }
+
+ /*
+ * Convert from zigzag int to int.
+ */
+ int zigzagToI32(uint n) {
+ return (n >> 1) ^ -(n & 1);
+ }
+
+ /*
+ * Convert from zigzag long to long.
+ */
+ long zigzagToI64(ulong n) {
+ return (n >> 1) ^ -(n & 1);
+ }
+
+ TType getTType(CType type) {
+ final switch (type) {
+ case CType.STOP:
+ return TType.STOP;
+ case CType.BOOLEAN_FALSE:
+ return TType.BOOL;
+ case CType.BOOLEAN_TRUE:
+ return TType.BOOL;
+ case CType.BYTE:
+ return TType.BYTE;
+ case CType.I16:
+ return TType.I16;
+ case CType.I32:
+ return TType.I32;
+ case CType.I64:
+ return TType.I64;
+ case CType.DOUBLE:
+ return TType.DOUBLE;
+ case CType.BINARY:
+ return TType.STRING;
+ case CType.LIST:
+ return TType.LIST;
+ case CType.SET:
+ return TType.SET;
+ case CType.MAP:
+ return TType.MAP;
+ case CType.STRUCT:
+ return TType.STRUCT;
+ }
+ }
+
+ void checkSize(int size, int limit) {
+ if (size < 0) {
+ throw new TProtocolException(TProtocolException.Type.NEGATIVE_SIZE);
+ } else if (limit > 0 && size > limit) {
+ throw new TProtocolException(TProtocolException.Type.SIZE_LIMIT);
+ }
+ }
+
+ enum PROTOCOL_ID = 0x82;
+ enum VERSION_N = 1;
+ enum VERSION_MASK = 0b0001_1111;
+ enum TYPE_MASK = 0b1110_0000;
+ enum TYPE_BITS = 0b0000_0111;
+ enum TYPE_SHIFT_AMOUNT = 5;
+
+ // Probably need to implement a better stack at some point.
+ short[] fieldIdStack_;
+ short lastFieldId_;
+
+ TField booleanField_;
+
+ bool hasBoolValue_;
+ bool boolValue_;
+
+ Transport trans_;
+}
+
+/**
+ * TCompactProtocol construction helper to avoid having to explicitly specify
+ * the transport type, i.e. to allow the constructor being called using IFTI
+ * (see $(LINK2 http://d.puremagic.com/issues/show_bug.cgi?id=6082, D Bugzilla
+ * enhancement requet 6082)).
+ */
+TCompactProtocol!Transport tCompactProtocol(Transport)(Transport trans,
+ int containerSizeLimit = 0, int stringSizeLimit = 0
+) if (isTTransport!Transport)
+{
+ return new TCompactProtocol!Transport(trans,
+ containerSizeLimit, stringSizeLimit);
+}
+
+private {
+ enum CType : ubyte {
+ STOP = 0x0,
+ BOOLEAN_TRUE = 0x1,
+ BOOLEAN_FALSE = 0x2,
+ BYTE = 0x3,
+ I16 = 0x4,
+ I32 = 0x5,
+ I64 = 0x6,
+ DOUBLE = 0x7,
+ BINARY = 0x8,
+ LIST = 0x9,
+ SET = 0xa,
+ MAP = 0xb,
+ STRUCT = 0xc
+ }
+ static assert(CType.max <= 0xf,
+ "Compact protocol wire type representation must fit into 4 bits.");
+}
+
+unittest {
+ import std.exception;
+ import thrift.transport.memory;
+
+ // Check the message header format.
+ auto buf = new TMemoryBuffer;
+ auto compact = tCompactProtocol(buf);
+ compact.writeMessageBegin(TMessage("foo", TMessageType.CALL, 0));
+
+ auto header = new ubyte[7];
+ buf.readAll(header);
+ enforce(header == [
+ 130, // Protocol id.
+ 33, // Version/type byte.
+ 0, // Sequence id.
+ 3, 102, 111, 111 // Method name.
+ ]);
+}
+
+unittest {
+ import thrift.internal.test.protocol;
+ testContainerSizeLimit!(TCompactProtocol!())();
+ testStringSizeLimit!(TCompactProtocol!())();
+}
+
+/**
+ * TProtocolFactory creating a TCompactProtocol instance for passed in
+ * transports.
+ *
+ * The optional Transports template tuple parameter can be used to specify
+ * one or more TTransport implementations to specifically instantiate
+ * TCompactProtocol for. If the actual transport types encountered at
+ * runtime match one of the transports in the list, a specialized protocol
+ * instance is created. Otherwise, a generic TTransport version is used.
+ */
+class TCompactProtocolFactory(Transports...) if (
+ allSatisfy!(isTTransport, Transports)
+) : TProtocolFactory {
+ ///
+ this(int containerSizeLimit = 0, int stringSizeLimit = 0) {
+ containerSizeLimit_ = 0;
+ stringSizeLimit_ = 0;
+ }
+
+ TProtocol getProtocol(TTransport trans) const {
+ foreach (Transport; TypeTuple!(Transports, TTransport)) {
+ auto concreteTrans = cast(Transport)trans;
+ if (concreteTrans) {
+ return new TCompactProtocol!Transport(concreteTrans);
+ }
+ }
+ throw new TProtocolException(
+ "Passed null transport to TCompactProtocolFactory.");
+ }
+
+ int containerSizeLimit_;
+ int stringSizeLimit_;
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/protocol/json.d b/src/jaegertracing/thrift/lib/d/src/thrift/protocol/json.d
new file mode 100644
index 000000000..56a71dacc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/protocol/json.d
@@ -0,0 +1,1037 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.protocol.json;
+
+import std.algorithm;
+import std.array;
+import std.base64;
+import std.conv;
+import std.range;
+import std.string : format;
+import std.traits : isIntegral;
+import std.typetuple : allSatisfy, TypeTuple;
+import std.utf : toUTF8;
+import thrift.protocol.base;
+import thrift.transport.base;
+
+alias Base64Impl!('+', '/', Base64.NoPadding) Base64NoPad;
+
+/**
+ * Implementation of the Thrift JSON protocol.
+ */
+final class TJsonProtocol(Transport = TTransport) if (
+ isTTransport!Transport
+) : TProtocol {
+ /**
+ * Constructs a new instance.
+ *
+ * Params:
+ * trans = The transport to use.
+ * containerSizeLimit = If positive, the container size is limited to the
+ * given number of items.
+ * stringSizeLimit = If positive, the string length is limited to the
+ * given number of bytes.
+ */
+ this(Transport trans, int containerSizeLimit = 0, int stringSizeLimit = 0) {
+ trans_ = trans;
+ this.containerSizeLimit = containerSizeLimit;
+ this.stringSizeLimit = stringSizeLimit;
+
+ context_ = new Context();
+ reader_ = new LookaheadReader(trans);
+ }
+
+ Transport transport() @property {
+ return trans_;
+ }
+
+ void reset() {
+ destroy(contextStack_);
+ context_ = new Context();
+ reader_ = new LookaheadReader(trans_);
+ }
+
+ /**
+ * If positive, limits the number of items of deserialized containers to the
+ * given amount.
+ *
+ * This is useful to avoid allocating excessive amounts of memory when broken
+ * data is received. If the limit is exceeded, a SIZE_LIMIT-type
+ * TProtocolException is thrown.
+ *
+ * Defaults to zero (no limit).
+ */
+ int containerSizeLimit;
+
+ /**
+ * If positive, limits the length of deserialized strings/binary data to the
+ * given number of bytes.
+ *
+ * This is useful to avoid allocating excessive amounts of memory when broken
+ * data is received. If the limit is exceeded, a SIZE_LIMIT-type
+ * TProtocolException is thrown.
+ *
+ * Note: For binary data, the limit applies to the length of the
+ * Base64-encoded string data, not the resulting byte array.
+ *
+ * Defaults to zero (no limit).
+ */
+ int stringSizeLimit;
+
+ /*
+ * Writing methods.
+ */
+
+ void writeBool(bool b) {
+ writeJsonInteger(b ? 1 : 0);
+ }
+
+ void writeByte(byte b) {
+ writeJsonInteger(b);
+ }
+
+ void writeI16(short i16) {
+ writeJsonInteger(i16);
+ }
+
+ void writeI32(int i32) {
+ writeJsonInteger(i32);
+ }
+
+ void writeI64(long i64) {
+ writeJsonInteger(i64);
+ }
+
+ void writeDouble(double dub) {
+ context_.write(trans_);
+
+ string value;
+ if (dub is double.nan) {
+ value = NAN_STRING;
+ } else if (dub is double.infinity) {
+ value = INFINITY_STRING;
+ } else if (dub is -double.infinity) {
+ value = NEG_INFINITY_STRING;
+ }
+
+ bool escapeNum = value !is null || context_.escapeNum;
+
+ if (value is null) {
+ /* precision is 17 */
+ value = format("%.17g", dub);
+ }
+
+ if (escapeNum) trans_.write(STRING_DELIMITER);
+ trans_.write(cast(ubyte[])value);
+ if (escapeNum) trans_.write(STRING_DELIMITER);
+ }
+
+ void writeString(string str) {
+ context_.write(trans_);
+ trans_.write(STRING_DELIMITER);
+ foreach (c; str) {
+ writeJsonChar(c);
+ }
+ trans_.write(STRING_DELIMITER);
+ }
+
+ void writeBinary(ubyte[] buf) {
+ context_.write(trans_);
+
+ trans_.write(STRING_DELIMITER);
+ ubyte[4] b;
+ while (!buf.empty) {
+ auto toWrite = take(buf, 3);
+ Base64NoPad.encode(toWrite, b[]);
+ trans_.write(b[0 .. toWrite.length + 1]);
+ buf.popFrontN(toWrite.length);
+ }
+ trans_.write(STRING_DELIMITER);
+ }
+
+ void writeMessageBegin(TMessage msg) {
+ writeJsonArrayBegin();
+ writeJsonInteger(THRIFT_JSON_VERSION);
+ writeString(msg.name);
+ writeJsonInteger(cast(byte)msg.type);
+ writeJsonInteger(msg.seqid);
+ }
+
+ void writeMessageEnd() {
+ writeJsonArrayEnd();
+ }
+
+ void writeStructBegin(TStruct tstruct) {
+ writeJsonObjectBegin();
+ }
+
+ void writeStructEnd() {
+ writeJsonObjectEnd();
+ }
+
+ void writeFieldBegin(TField field) {
+ writeJsonInteger(field.id);
+ writeJsonObjectBegin();
+ writeString(getNameFromTType(field.type));
+ }
+
+ void writeFieldEnd() {
+ writeJsonObjectEnd();
+ }
+
+ void writeFieldStop() {}
+
+ void writeListBegin(TList list) {
+ writeJsonArrayBegin();
+ writeString(getNameFromTType(list.elemType));
+ writeJsonInteger(list.size);
+ }
+
+ void writeListEnd() {
+ writeJsonArrayEnd();
+ }
+
+ void writeMapBegin(TMap map) {
+ writeJsonArrayBegin();
+ writeString(getNameFromTType(map.keyType));
+ writeString(getNameFromTType(map.valueType));
+ writeJsonInteger(map.size);
+ writeJsonObjectBegin();
+ }
+
+ void writeMapEnd() {
+ writeJsonObjectEnd();
+ writeJsonArrayEnd();
+ }
+
+ void writeSetBegin(TSet set) {
+ writeJsonArrayBegin();
+ writeString(getNameFromTType(set.elemType));
+ writeJsonInteger(set.size);
+ }
+
+ void writeSetEnd() {
+ writeJsonArrayEnd();
+ }
+
+
+ /*
+ * Reading methods.
+ */
+
+ bool readBool() {
+ return readJsonInteger!byte() ? true : false;
+ }
+
+ byte readByte() {
+ return readJsonInteger!byte();
+ }
+
+ short readI16() {
+ return readJsonInteger!short();
+ }
+
+ int readI32() {
+ return readJsonInteger!int();
+ }
+
+ long readI64() {
+ return readJsonInteger!long();
+ }
+
+ double readDouble() {
+ context_.read(reader_);
+
+ if (reader_.peek() == STRING_DELIMITER) {
+ auto str = readJsonString(true);
+ if (str == NAN_STRING) {
+ return double.nan;
+ }
+ if (str == INFINITY_STRING) {
+ return double.infinity;
+ }
+ if (str == NEG_INFINITY_STRING) {
+ return -double.infinity;
+ }
+
+ if (!context_.escapeNum) {
+ // Throw exception -- we should not be in a string in this case
+ throw new TProtocolException("Numeric data unexpectedly quoted",
+ TProtocolException.Type.INVALID_DATA);
+ }
+ try {
+ return to!double(str);
+ } catch (ConvException e) {
+ throw new TProtocolException(`Expected numeric value; got "` ~ str ~
+ `".`, TProtocolException.Type.INVALID_DATA);
+ }
+ }
+ else {
+ if (context_.escapeNum) {
+ // This will throw - we should have had a quote if escapeNum == true
+ readJsonSyntaxChar(STRING_DELIMITER);
+ }
+
+ auto str = readJsonNumericChars();
+ try {
+ return to!double(str);
+ } catch (ConvException e) {
+ throw new TProtocolException(`Expected numeric value; got "` ~ str ~
+ `".`, TProtocolException.Type.INVALID_DATA);
+ }
+ }
+ }
+
+ string readString() {
+ return readJsonString(false);
+ }
+
+ ubyte[] readBinary() {
+ return Base64NoPad.decode(readString());
+ }
+
+ TMessage readMessageBegin() {
+ TMessage msg = void;
+
+ readJsonArrayBegin();
+
+ auto ver = readJsonInteger!short();
+ if (ver != THRIFT_JSON_VERSION) {
+ throw new TProtocolException("Message contained bad version.",
+ TProtocolException.Type.BAD_VERSION);
+ }
+
+ msg.name = readString();
+ msg.type = cast(TMessageType)readJsonInteger!byte();
+ msg.seqid = readJsonInteger!short();
+
+ return msg;
+ }
+
+ void readMessageEnd() {
+ readJsonArrayEnd();
+ }
+
+ TStruct readStructBegin() {
+ readJsonObjectBegin();
+ return TStruct();
+ }
+
+ void readStructEnd() {
+ readJsonObjectEnd();
+ }
+
+ TField readFieldBegin() {
+ TField f = void;
+ f.name = null;
+
+ auto ch = reader_.peek();
+ if (ch == OBJECT_END) {
+ f.type = TType.STOP;
+ } else {
+ f.id = readJsonInteger!short();
+ readJsonObjectBegin();
+ f.type = getTTypeFromName(readString());
+ }
+
+ return f;
+ }
+
+ void readFieldEnd() {
+ readJsonObjectEnd();
+ }
+
+ TList readListBegin() {
+ readJsonArrayBegin();
+ auto type = getTTypeFromName(readString());
+ auto size = readContainerSize();
+ return TList(type, size);
+ }
+
+ void readListEnd() {
+ readJsonArrayEnd();
+ }
+
+ TMap readMapBegin() {
+ readJsonArrayBegin();
+ auto keyType = getTTypeFromName(readString());
+ auto valueType = getTTypeFromName(readString());
+ auto size = readContainerSize();
+ readJsonObjectBegin();
+ return TMap(keyType, valueType, size);
+ }
+
+ void readMapEnd() {
+ readJsonObjectEnd();
+ readJsonArrayEnd();
+ }
+
+ TSet readSetBegin() {
+ readJsonArrayBegin();
+ auto type = getTTypeFromName(readString());
+ auto size = readContainerSize();
+ return TSet(type, size);
+ }
+
+ void readSetEnd() {
+ readJsonArrayEnd();
+ }
+
+private:
+ void pushContext(Context c) {
+ contextStack_ ~= context_;
+ context_ = c;
+ }
+
+ void popContext() {
+ context_ = contextStack_.back;
+ contextStack_.popBack();
+ contextStack_.assumeSafeAppend();
+ }
+
+ /*
+ * Writing functions
+ */
+
+ // Write the character ch as a Json escape sequence ("\u00xx")
+ void writeJsonEscapeChar(ubyte ch) {
+ trans_.write(ESCAPE_PREFIX);
+ trans_.write(ESCAPE_PREFIX);
+ auto outCh = hexChar(cast(ubyte)(ch >> 4));
+ trans_.write((&outCh)[0 .. 1]);
+ outCh = hexChar(ch);
+ trans_.write((&outCh)[0 .. 1]);
+ }
+
+ // Write the character ch as part of a Json string, escaping as appropriate.
+ void writeJsonChar(ubyte ch) {
+ if (ch >= 0x30) {
+ if (ch == '\\') { // Only special character >= 0x30 is '\'
+ trans_.write(BACKSLASH);
+ trans_.write(BACKSLASH);
+ } else {
+ trans_.write((&ch)[0 .. 1]);
+ }
+ }
+ else {
+ auto outCh = kJsonCharTable[ch];
+ // Check if regular character, backslash escaped, or Json escaped
+ if (outCh == 1) {
+ trans_.write((&ch)[0 .. 1]);
+ } else if (outCh > 1) {
+ trans_.write(BACKSLASH);
+ trans_.write((&outCh)[0 .. 1]);
+ } else {
+ writeJsonEscapeChar(ch);
+ }
+ }
+ }
+
+ // Convert the given integer type to a Json number, or a string
+ // if the context requires it (eg: key in a map pair).
+ void writeJsonInteger(T)(T num) if (isIntegral!T) {
+ context_.write(trans_);
+
+ auto escapeNum = context_.escapeNum();
+ if (escapeNum) trans_.write(STRING_DELIMITER);
+ trans_.write(cast(ubyte[])to!string(num));
+ if (escapeNum) trans_.write(STRING_DELIMITER);
+ }
+
+ void writeJsonObjectBegin() {
+ context_.write(trans_);
+ trans_.write(OBJECT_BEGIN);
+ pushContext(new PairContext());
+ }
+
+ void writeJsonObjectEnd() {
+ popContext();
+ trans_.write(OBJECT_END);
+ }
+
+ void writeJsonArrayBegin() {
+ context_.write(trans_);
+ trans_.write(ARRAY_BEGIN);
+ pushContext(new ListContext());
+ }
+
+ void writeJsonArrayEnd() {
+ popContext();
+ trans_.write(ARRAY_END);
+ }
+
+ /*
+ * Reading functions
+ */
+
+ int readContainerSize() {
+ auto size = readJsonInteger!int();
+ if (size < 0) {
+ throw new TProtocolException(TProtocolException.Type.NEGATIVE_SIZE);
+ } else if (containerSizeLimit > 0 && size > containerSizeLimit) {
+ throw new TProtocolException(TProtocolException.Type.SIZE_LIMIT);
+ }
+ return size;
+ }
+
+ void readJsonSyntaxChar(ubyte[1] ch) {
+ return readSyntaxChar(reader_, ch);
+ }
+
+ wchar readJsonEscapeChar() {
+ auto a = reader_.read();
+ auto b = reader_.read();
+ auto c = reader_.read();
+ auto d = reader_.read();
+ return cast(ushort)(
+ (hexVal(a[0]) << 12) + (hexVal(b[0]) << 8) +
+ (hexVal(c[0]) << 4) + hexVal(d[0])
+ );
+ }
+
+ string readJsonString(bool skipContext = false) {
+ if (!skipContext) context_.read(reader_);
+
+ readJsonSyntaxChar(STRING_DELIMITER);
+ auto buffer = appender!string();
+
+ wchar[] wchs;
+ int bytesRead;
+ while (true) {
+ auto ch = reader_.read();
+ if (ch == STRING_DELIMITER) {
+ break;
+ }
+
+ ++bytesRead;
+ if (stringSizeLimit > 0 && bytesRead > stringSizeLimit) {
+ throw new TProtocolException(TProtocolException.Type.SIZE_LIMIT);
+ }
+
+ if (ch == BACKSLASH) {
+ ch = reader_.read();
+ if (ch == ESCAPE_CHAR) {
+ auto wch = readJsonEscapeChar();
+ if (wch >= 0xD800 && wch <= 0xDBFF) {
+ wchs ~= wch;
+ } else if (wch >= 0xDC00 && wch <= 0xDFFF && wchs.length == 0) {
+ throw new TProtocolException("Missing UTF-16 high surrogate.",
+ TProtocolException.Type.INVALID_DATA);
+ } else {
+ wchs ~= wch;
+ buffer.put(wchs.toUTF8);
+ wchs = [];
+ }
+ continue;
+ } else {
+ auto pos = countUntil(kEscapeChars[], ch[0]);
+ if (pos == -1) {
+ throw new TProtocolException("Expected control char, got '" ~
+ cast(char)ch[0] ~ "'.", TProtocolException.Type.INVALID_DATA);
+ }
+ ch = kEscapeCharVals[pos];
+ }
+ }
+ if (wchs.length != 0) {
+ throw new TProtocolException("Missing UTF-16 low surrogate.",
+ TProtocolException.Type.INVALID_DATA);
+ }
+ buffer.put(ch[0]);
+ }
+
+ if (wchs.length != 0) {
+ throw new TProtocolException("Missing UTF-16 low surrogate.",
+ TProtocolException.Type.INVALID_DATA);
+ }
+ return buffer.data;
+ }
+
+ // Reads a sequence of characters, stopping at the first one that is not
+ // a valid Json numeric character.
+ string readJsonNumericChars() {
+ string str;
+ while (true) {
+ auto ch = reader_.peek();
+ if (!isJsonNumeric(ch[0])) {
+ break;
+ }
+ reader_.read();
+ str ~= ch;
+ }
+ return str;
+ }
+
+ // Reads a sequence of characters and assembles them into a number,
+ // returning them via num
+ T readJsonInteger(T)() if (isIntegral!T) {
+ context_.read(reader_);
+ if (context_.escapeNum()) {
+ readJsonSyntaxChar(STRING_DELIMITER);
+ }
+ auto str = readJsonNumericChars();
+ T num;
+ try {
+ num = to!T(str);
+ } catch (ConvException e) {
+ throw new TProtocolException(`Expected numeric value, got "` ~ str ~ `".`,
+ TProtocolException.Type.INVALID_DATA);
+ }
+ if (context_.escapeNum()) {
+ readJsonSyntaxChar(STRING_DELIMITER);
+ }
+ return num;
+ }
+
+ void readJsonObjectBegin() {
+ context_.read(reader_);
+ readJsonSyntaxChar(OBJECT_BEGIN);
+ pushContext(new PairContext());
+ }
+
+ void readJsonObjectEnd() {
+ readJsonSyntaxChar(OBJECT_END);
+ popContext();
+ }
+
+ void readJsonArrayBegin() {
+ context_.read(reader_);
+ readJsonSyntaxChar(ARRAY_BEGIN);
+ pushContext(new ListContext());
+ }
+
+ void readJsonArrayEnd() {
+ readJsonSyntaxChar(ARRAY_END);
+ popContext();
+ }
+
+ static {
+ final class LookaheadReader {
+ this(Transport trans) {
+ trans_ = trans;
+ }
+
+ ubyte[1] read() {
+ if (hasData_) {
+ hasData_ = false;
+ } else {
+ trans_.readAll(data_);
+ }
+ return data_;
+ }
+
+ ubyte[1] peek() {
+ if (!hasData_) {
+ trans_.readAll(data_);
+ hasData_ = true;
+ }
+ return data_;
+ }
+
+ private:
+ Transport trans_;
+ bool hasData_;
+ ubyte[1] data_;
+ }
+
+ /*
+ * Class to serve as base Json context and as base class for other context
+ * implementations
+ */
+ class Context {
+ /**
+ * Write context data to the transport. Default is to do nothing.
+ */
+ void write(Transport trans) {}
+
+ /**
+ * Read context data from the transport. Default is to do nothing.
+ */
+ void read(LookaheadReader reader) {}
+
+ /**
+ * Return true if numbers need to be escaped as strings in this context.
+ * Default behavior is to return false.
+ */
+ bool escapeNum() @property {
+ return false;
+ }
+ }
+
+ // Context class for object member key-value pairs
+ class PairContext : Context {
+ this() {
+ first_ = true;
+ colon_ = true;
+ }
+
+ override void write(Transport trans) {
+ if (first_) {
+ first_ = false;
+ colon_ = true;
+ } else {
+ trans.write(colon_ ? PAIR_SEP : ELEM_SEP);
+ colon_ = !colon_;
+ }
+ }
+
+ override void read(LookaheadReader reader) {
+ if (first_) {
+ first_ = false;
+ colon_ = true;
+ } else {
+ auto ch = (colon_ ? PAIR_SEP : ELEM_SEP);
+ colon_ = !colon_;
+ return readSyntaxChar(reader, ch);
+ }
+ }
+
+ // Numbers must be turned into strings if they are the key part of a pair
+ override bool escapeNum() @property {
+ return colon_;
+ }
+
+ private:
+ bool first_;
+ bool colon_;
+ }
+
+ class ListContext : Context {
+ this() {
+ first_ = true;
+ }
+
+ override void write(Transport trans) {
+ if (first_) {
+ first_ = false;
+ } else {
+ trans.write(ELEM_SEP);
+ }
+ }
+
+ override void read(LookaheadReader reader) {
+ if (first_) {
+ first_ = false;
+ } else {
+ readSyntaxChar(reader, ELEM_SEP);
+ }
+ }
+
+ private:
+ bool first_;
+ }
+
+ // Read 1 character from the transport trans and verify that it is the
+ // expected character ch.
+ // Throw a protocol exception if it is not.
+ void readSyntaxChar(LookaheadReader reader, ubyte[1] ch) {
+ auto ch2 = reader.read();
+ if (ch2 != ch) {
+ throw new TProtocolException("Expected '" ~ cast(char)ch[0] ~ "', got '" ~
+ cast(char)ch2[0] ~ "'.", TProtocolException.Type.INVALID_DATA);
+ }
+ }
+ }
+
+ // Probably need to implement a better stack at some point.
+ Context[] contextStack_;
+ Context context_;
+
+ Transport trans_;
+ LookaheadReader reader_;
+}
+
+/**
+ * TJsonProtocol construction helper to avoid having to explicitly specify
+ * the transport type, i.e. to allow the constructor being called using IFTI
+ * (see $(LINK2 http://d.puremagic.com/issues/show_bug.cgi?id=6082, D Bugzilla
+ * enhancement requet 6082)).
+ */
+TJsonProtocol!Transport tJsonProtocol(Transport)(Transport trans,
+ int containerSizeLimit = 0, int stringSizeLimit = 0
+) if (isTTransport!Transport) {
+ return new TJsonProtocol!Transport(trans, containerSizeLimit, stringSizeLimit);
+}
+
+unittest {
+ import std.exception;
+ import thrift.transport.memory;
+
+ // Check the message header format.
+ auto buf = new TMemoryBuffer;
+ auto json = tJsonProtocol(buf);
+ json.writeMessageBegin(TMessage("foo", TMessageType.CALL, 0));
+ json.writeMessageEnd();
+
+ auto header = new ubyte[13];
+ buf.readAll(header);
+ enforce(cast(char[])header == `[1,"foo",1,0]`);
+}
+
+unittest {
+ import std.exception;
+ import thrift.transport.memory;
+
+ // Check that short binary data is read correctly (the Thrift JSON format
+ // does not include padding chars in the Base64 encoded data).
+ auto buf = new TMemoryBuffer;
+ auto json = tJsonProtocol(buf);
+ json.writeBinary([1, 2]);
+ json.reset();
+ enforce(json.readBinary() == [1, 2]);
+}
+
+unittest {
+ import std.exception;
+ import thrift.transport.memory;
+
+ auto buf = new TMemoryBuffer(cast(ubyte[])"\"\\u0e01 \\ud835\\udd3e\"");
+ auto json = tJsonProtocol(buf);
+ auto str = json.readString();
+ enforce(str == "ภð”¾");
+}
+
+unittest {
+ // Thrown if low surrogate is missing.
+ import std.exception;
+ import thrift.transport.memory;
+
+ auto buf = new TMemoryBuffer(cast(ubyte[])"\"\\u0e01 \\ud835\"");
+ auto json = tJsonProtocol(buf);
+ assertThrown!TProtocolException(json.readString());
+}
+
+unittest {
+ // Thrown if high surrogate is missing.
+ import std.exception;
+ import thrift.transport.memory;
+
+ auto buf = new TMemoryBuffer(cast(ubyte[])"\"\\u0e01 \\udd3e\"");
+ auto json = tJsonProtocol(buf);
+ assertThrown!TProtocolException(json.readString());
+}
+
+unittest {
+ import thrift.internal.test.protocol;
+ testContainerSizeLimit!(TJsonProtocol!())();
+ testStringSizeLimit!(TJsonProtocol!())();
+}
+
+/**
+ * TProtocolFactory creating a TJsonProtocol instance for passed in
+ * transports.
+ *
+ * The optional Transports template tuple parameter can be used to specify
+ * one or more TTransport implementations to specifically instantiate
+ * TJsonProtocol for. If the actual transport types encountered at
+ * runtime match one of the transports in the list, a specialized protocol
+ * instance is created. Otherwise, a generic TTransport version is used.
+ */
+class TJsonProtocolFactory(Transports...) if (
+ allSatisfy!(isTTransport, Transports)
+) : TProtocolFactory {
+ TProtocol getProtocol(TTransport trans) const {
+ foreach (Transport; TypeTuple!(Transports, TTransport)) {
+ auto concreteTrans = cast(Transport)trans;
+ if (concreteTrans) {
+ auto p = new TJsonProtocol!Transport(concreteTrans);
+ return p;
+ }
+ }
+ throw new TProtocolException(
+ "Passed null transport to TJsonProtocolFactoy.");
+ }
+}
+
+private {
+ immutable ubyte[1] OBJECT_BEGIN = '{';
+ immutable ubyte[1] OBJECT_END = '}';
+ immutable ubyte[1] ARRAY_BEGIN = '[';
+ immutable ubyte[1] ARRAY_END = ']';
+ immutable ubyte[1] NEWLINE = '\n';
+ immutable ubyte[1] PAIR_SEP = ':';
+ immutable ubyte[1] ELEM_SEP = ',';
+ immutable ubyte[1] BACKSLASH = '\\';
+ immutable ubyte[1] STRING_DELIMITER = '"';
+ immutable ubyte[1] ZERO_CHAR = '0';
+ immutable ubyte[1] ESCAPE_CHAR = 'u';
+ immutable ubyte[4] ESCAPE_PREFIX = cast(ubyte[4])r"\u00";
+
+ enum THRIFT_JSON_VERSION = 1;
+
+ immutable NAN_STRING = "NaN";
+ immutable INFINITY_STRING = "Infinity";
+ immutable NEG_INFINITY_STRING = "-Infinity";
+
+ string getNameFromTType(TType typeID) {
+ final switch (typeID) {
+ case TType.BOOL:
+ return "tf";
+ case TType.BYTE:
+ return "i8";
+ case TType.I16:
+ return "i16";
+ case TType.I32:
+ return "i32";
+ case TType.I64:
+ return "i64";
+ case TType.DOUBLE:
+ return "dbl";
+ case TType.STRING:
+ return "str";
+ case TType.STRUCT:
+ return "rec";
+ case TType.MAP:
+ return "map";
+ case TType.LIST:
+ return "lst";
+ case TType.SET:
+ return "set";
+ case TType.STOP: goto case;
+ case TType.VOID:
+ assert(false, "Invalid type passed.");
+ }
+ }
+
+ TType getTTypeFromName(string name) {
+ TType result;
+ if (name.length > 1) {
+ switch (name[0]) {
+ case 'd':
+ result = TType.DOUBLE;
+ break;
+ case 'i':
+ switch (name[1]) {
+ case '8':
+ result = TType.BYTE;
+ break;
+ case '1':
+ result = TType.I16;
+ break;
+ case '3':
+ result = TType.I32;
+ break;
+ case '6':
+ result = TType.I64;
+ break;
+ default:
+ // Do nothing.
+ }
+ break;
+ case 'l':
+ result = TType.LIST;
+ break;
+ case 'm':
+ result = TType.MAP;
+ break;
+ case 'r':
+ result = TType.STRUCT;
+ break;
+ case 's':
+ if (name[1] == 't') {
+ result = TType.STRING;
+ }
+ else if (name[1] == 'e') {
+ result = TType.SET;
+ }
+ break;
+ case 't':
+ result = TType.BOOL;
+ break;
+ default:
+ // Do nothing.
+ }
+ }
+ if (result == TType.STOP) {
+ throw new TProtocolException("Unrecognized type",
+ TProtocolException.Type.NOT_IMPLEMENTED);
+ }
+ return result;
+ }
+
+ // This table describes the handling for the first 0x30 characters
+ // 0 : escape using "\u00xx" notation
+ // 1 : just output index
+ // <other> : escape using "\<other>" notation
+ immutable ubyte[0x30] kJsonCharTable = [
+ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
+ 1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
+ ];
+
+ // This string's characters must match up with the elements in kEscapeCharVals.
+ // I don't have '/' on this list even though it appears on www.json.org --
+ // it is not in the RFC
+ immutable kEscapeChars = cast(ubyte[7]) `"\\bfnrt`;
+
+ // The elements of this array must match up with the sequence of characters in
+ // kEscapeChars
+ immutable ubyte[7] kEscapeCharVals = [
+ '"', '\\', '\b', '\f', '\n', '\r', '\t',
+ ];
+
+ // Return the integer value of a hex character ch.
+ // Throw a protocol exception if the character is not [0-9a-f].
+ ubyte hexVal(ubyte ch) {
+ if ((ch >= '0') && (ch <= '9')) {
+ return cast(ubyte)(ch - '0');
+ } else if ((ch >= 'a') && (ch <= 'f')) {
+ return cast(ubyte)(ch - 'a' + 10);
+ }
+ else {
+ throw new TProtocolException("Expected hex val ([0-9a-f]), got '" ~
+ ch ~ "'.", TProtocolException.Type.INVALID_DATA);
+ }
+ }
+
+ // Return the hex character representing the integer val. The value is masked
+ // to make sure it is in the correct range.
+ ubyte hexChar(ubyte val) {
+ val &= 0x0F;
+ if (val < 10) {
+ return cast(ubyte)(val + '0');
+ } else {
+ return cast(ubyte)(val - 10 + 'a');
+ }
+ }
+
+ // Return true if the character ch is in [-+0-9.Ee]; false otherwise
+ bool isJsonNumeric(ubyte ch) {
+ switch (ch) {
+ case '+':
+ case '-':
+ case '.':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'E':
+ case 'e':
+ return true;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/protocol/processor.d b/src/jaegertracing/thrift/lib/d/src/thrift/protocol/processor.d
new file mode 100644
index 000000000..887421cc8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/protocol/processor.d
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.protocol.processor;
+
+// Use selective import once DMD @@BUG314@@ is fixed.
+import std.variant /+ : Variant +/;
+import thrift.protocol.base;
+import thrift.transport.base;
+
+/**
+ * A processor is a generic object which operates upon an input stream and
+ * writes to some output stream.
+ *
+ * The definition of this object is loose, though the typical case is for some
+ * sort of server that either generates responses to an input stream or
+ * forwards data from one pipe onto another.
+ *
+ * An implementation can optionally allow one or more TProcessorEventHandlers
+ * to be attached, providing an interface to hook custom code into the
+ * handling process, which can be used e.g. for gathering statistics.
+ */
+interface TProcessor {
+ ///
+ bool process(TProtocol iprot, TProtocol oprot,
+ Variant connectionContext = Variant()
+ ) in {
+ assert(iprot);
+ assert(oprot);
+ }
+
+ ///
+ final bool process(TProtocol prot, Variant connectionContext = Variant()) {
+ return process(prot, prot, connectionContext);
+ }
+}
+
+/**
+ * Handles events from a processor.
+ */
+interface TProcessorEventHandler {
+ /**
+ * Called before calling other callback methods.
+ *
+ * Expected to return some sort of »call context«, which is passed to all
+ * other callbacks for that function invocation.
+ */
+ Variant createContext(string methodName, Variant connectionContext);
+
+ /**
+ * Called when handling the method associated with a context has been
+ * finished – can be used to perform clean up work.
+ */
+ void deleteContext(Variant callContext, string methodName);
+
+ /**
+ * Called before reading arguments.
+ */
+ void preRead(Variant callContext, string methodName);
+
+ /**
+ * Called between reading arguments and calling the handler.
+ */
+ void postRead(Variant callContext, string methodName);
+
+ /**
+ * Called between calling the handler and writing the response.
+ */
+ void preWrite(Variant callContext, string methodName);
+
+ /**
+ * Called after writing the response.
+ */
+ void postWrite(Variant callContext, string methodName);
+
+ /**
+ * Called when handling a one-way function call is completed successfully.
+ */
+ void onewayComplete(Variant callContext, string methodName);
+
+ /**
+ * Called if the handler throws an undeclared exception.
+ */
+ void handlerError(Variant callContext, string methodName, Exception e);
+}
+
+struct TConnectionInfo {
+ /// The input and output protocols.
+ TProtocol input;
+ TProtocol output; /// Ditto.
+
+ /// The underlying transport used for the connection
+ /// This is the transport that was returned by TServerTransport.accept(),
+ /// and it may be different than the transport pointed to by the input and
+ /// output protocols.
+ TTransport transport;
+}
+
+interface TProcessorFactory {
+ /**
+ * Get the TProcessor to use for a particular connection.
+ *
+ * This method is always invoked in the same thread that the connection was
+ * accepted on, which is always the same thread for all current server
+ * implementations.
+ */
+ TProcessor getProcessor(ref const(TConnectionInfo) connInfo);
+}
+
+/**
+ * The default processor factory which always returns the same instance.
+ */
+class TSingletonProcessorFactory : TProcessorFactory {
+ /**
+ * Creates a new instance.
+ *
+ * Params:
+ * processor = The processor object to return from getProcessor().
+ */
+ this(TProcessor processor) {
+ processor_ = processor;
+ }
+
+ override TProcessor getProcessor(ref const(TConnectionInfo) connInfo) {
+ return processor_;
+ }
+
+private:
+ TProcessor processor_;
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/server/base.d b/src/jaegertracing/thrift/lib/d/src/thrift/server/base.d
new file mode 100644
index 000000000..a23b1c7f2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/server/base.d
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.server.base;
+
+import std.variant : Variant;
+import thrift.protocol.base;
+import thrift.protocol.binary;
+import thrift.protocol.processor;
+import thrift.server.transport.base;
+import thrift.transport.base;
+import thrift.util.cancellation;
+
+/**
+ * Base class for all Thrift servers.
+ *
+ * By setting the eventHandler property to a TServerEventHandler
+ * implementation, custom code can be integrated into the processing pipeline,
+ * which can be used e.g. for gathering statistics.
+ */
+class TServer {
+ /**
+ * Starts serving.
+ *
+ * Blocks until the server finishes, i.e. a serious problem occurred or the
+ * cancellation request has been triggered.
+ *
+ * Server implementations are expected to implement cancellation in a best-
+ * effort way – usually, it should be possible to immediately stop accepting
+ * connections and return after all currently active clients have been
+ * processed, but this might not be the case for every conceivable
+ * implementation.
+ */
+ abstract void serve(TCancellation cancellation = null);
+
+ /// The server event handler to notify. Null by default.
+ TServerEventHandler eventHandler;
+
+protected:
+ this(
+ TProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory
+ ) {
+ this(processor, serverTransport, transportFactory, transportFactory,
+ protocolFactory, protocolFactory);
+ }
+
+ this(
+ TProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory
+ ) {
+ this(processorFactory, serverTransport, transportFactory, transportFactory,
+ protocolFactory, protocolFactory);
+ }
+
+ this(
+ TProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory
+ ) {
+ this(new TSingletonProcessorFactory(processor), serverTransport,
+ inputTransportFactory, outputTransportFactory,
+ inputProtocolFactory, outputProtocolFactory);
+ }
+
+ this(
+ TProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory
+ ) {
+ import std.exception;
+ import thrift.base;
+ enforce(inputTransportFactory,
+ new TException("Input transport factory must not be null."));
+ enforce(outputTransportFactory,
+ new TException("Output transport factory must not be null."));
+ enforce(inputProtocolFactory,
+ new TException("Input protocol factory must not be null."));
+ enforce(outputProtocolFactory,
+ new TException("Output protocol factory must not be null."));
+
+ processorFactory_ = processorFactory;
+ serverTransport_ = serverTransport;
+ inputTransportFactory_ = inputTransportFactory;
+ outputTransportFactory_ = outputTransportFactory;
+ inputProtocolFactory_ = inputProtocolFactory;
+ outputProtocolFactory_ = outputProtocolFactory;
+ }
+
+ TProcessorFactory processorFactory_;
+ TServerTransport serverTransport_;
+ TTransportFactory inputTransportFactory_;
+ TTransportFactory outputTransportFactory_;
+ TProtocolFactory inputProtocolFactory_;
+ TProtocolFactory outputProtocolFactory_;
+
+public:
+
+ @property TProcessorFactory processorFactory()
+ {
+ return processorFactory_;
+ }
+
+ @property TServerTransport serverTransport()
+ {
+ return serverTransport_;
+ }
+
+ @property TTransportFactory inputTransportFactory()
+ {
+ return inputTransportFactory_;
+ }
+
+ @property TTransportFactory outputTransportFactory()
+ {
+ return outputTransportFactory_;
+ }
+
+ @property TProtocolFactory inputProtocolFactory()
+ {
+ return inputProtocolFactory_;
+ }
+
+ @property TProtocolFactory outputProtocolFactory()
+ {
+ return outputProtocolFactory_;
+ }
+}
+
+/**
+ * Handles events from a TServer core.
+ */
+interface TServerEventHandler {
+ /**
+ * Called before the server starts accepting connections.
+ */
+ void preServe();
+
+ /**
+ * Called when a new client has connected and processing is about to begin.
+ */
+ Variant createContext(TProtocol input, TProtocol output);
+
+ /**
+ * Called when request handling for a client has been finished – can be used
+ * to perform clean up work.
+ */
+ void deleteContext(Variant serverContext, TProtocol input, TProtocol output);
+
+ /**
+ * Called when the processor for a client call is about to be invoked.
+ */
+ void preProcess(Variant serverContext, TTransport transport);
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/server/nonblocking.d b/src/jaegertracing/thrift/lib/d/src/thrift/server/nonblocking.d
new file mode 100644
index 000000000..5860c0c42
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/server/nonblocking.d
@@ -0,0 +1,1397 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * A non-blocking server implementation that operates a set of I/O threads (by
+ * default only one) and either does processing »in-line« or off-loads it to a
+ * task pool.
+ *
+ * It *requires* TFramedTransport to be used on the client side, as it expects
+ * a 4 byte length indicator and writes out responses using the same framing.
+ *
+ * Because I/O is done asynchronous/event based, unfortunately
+ * TServerTransport can't be used.
+ *
+ * This implementation is based on the C++ one, with the exception of request
+ * timeouts and the drain task queue overload handling strategy not being
+ * implemented yet.
+ */
+// This really should use a D non-blocking I/O library, once one becomes
+// available.
+module thrift.server.nonblocking;
+
+import core.atomic : atomicLoad, atomicStore, atomicOp;
+import core.exception : onOutOfMemoryError;
+import core.memory : GC;
+import core.sync.mutex;
+import core.stdc.stdlib : free, realloc;
+import core.time : Duration, dur;
+import core.thread : Thread, ThreadGroup;
+import deimos.event2.event;
+import std.array : empty;
+import std.conv : emplace, to;
+import std.exception : enforce;
+import std.parallelism : TaskPool, task;
+import std.socket : Socket, socketPair, SocketAcceptException,
+ SocketException, TcpSocket;
+import std.variant : Variant;
+import thrift.base;
+import thrift.internal.endian;
+import thrift.internal.socket;
+import thrift.internal.traits;
+import thrift.protocol.base;
+import thrift.protocol.binary;
+import thrift.protocol.processor;
+import thrift.server.base;
+import thrift.server.transport.socket;
+import thrift.transport.base;
+import thrift.transport.memory;
+import thrift.transport.range;
+import thrift.transport.socket;
+import thrift.util.cancellation;
+
+/**
+ * Possible actions taken on new incoming connections when the server is
+ * overloaded.
+ */
+enum TOverloadAction {
+ /// Do not take any special actions while the server is overloaded, just
+ /// continue accepting connections.
+ NONE,
+
+ /// Immediately drop new connections after they have been accepted if the
+ /// server is overloaded.
+ CLOSE_ON_ACCEPT
+}
+
+///
+class TNonblockingServer : TServer {
+ ///
+ this(TProcessor processor, ushort port, TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory, TaskPool taskPool = null
+ ) {
+ this(new TSingletonProcessorFactory(processor), port, transportFactory,
+ transportFactory, protocolFactory, protocolFactory, taskPool);
+ }
+
+ ///
+ this(TProcessorFactory processorFactory, ushort port,
+ TTransportFactory transportFactory, TProtocolFactory protocolFactory,
+ TaskPool taskPool = null
+ ) {
+ this(processorFactory, port, transportFactory, transportFactory,
+ protocolFactory, protocolFactory, taskPool);
+ }
+
+ ///
+ this(
+ TProcessor processor,
+ ushort port,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ TaskPool taskPool = null
+ ) {
+ this(new TSingletonProcessorFactory(processor), port,
+ inputTransportFactory, outputTransportFactory,
+ inputProtocolFactory, outputProtocolFactory, taskPool);
+ }
+
+ ///
+ this(
+ TProcessorFactory processorFactory,
+ ushort port,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ TaskPool taskPool = null
+ ) {
+ super(processorFactory, null, inputTransportFactory,
+ outputTransportFactory, inputProtocolFactory, outputProtocolFactory);
+ port_ = port;
+
+ this.taskPool = taskPool;
+
+ connectionMutex_ = new Mutex;
+
+ connectionStackLimit = DEFAULT_CONNECTION_STACK_LIMIT;
+ maxActiveProcessors = DEFAULT_MAX_ACTIVE_PROCESSORS;
+ maxConnections = DEFAULT_MAX_CONNECTIONS;
+ overloadHysteresis = DEFAULT_OVERLOAD_HYSTERESIS;
+ overloadAction = DEFAULT_OVERLOAD_ACTION;
+ writeBufferDefaultSize = DEFAULT_WRITE_BUFFER_DEFAULT_SIZE;
+ idleReadBufferLimit = DEFAULT_IDLE_READ_BUFFER_LIMIT;
+ idleWriteBufferLimit = DEFAULT_IDLE_WRITE_BUFFER_LIMIT;
+ resizeBufferEveryN = DEFAULT_RESIZE_BUFFER_EVERY_N;
+ maxFrameSize = DEFAULT_MAX_FRAME_SIZE;
+ numIOThreads_ = DEFAULT_NUM_IO_THREADS;
+ }
+
+ override void serve(TCancellation cancellation = null) {
+ if (cancellation && cancellation.triggered) return;
+
+ // Initialize the listening socket.
+ // TODO: SO_KEEPALIVE, TCP_LOW_MIN_RTO, etc.
+ listenSocket_ = makeSocketAndListen(port_, TServerSocket.ACCEPT_BACKLOG,
+ BIND_RETRY_LIMIT, BIND_RETRY_DELAY, 0, 0, ipv6Only_);
+ listenSocket_.blocking = false;
+
+ logInfo("Using %s I/O thread(s).", numIOThreads_);
+ if (taskPool_) {
+ logInfo("Using task pool with size: %s.", numIOThreads_, taskPool_.size);
+ }
+
+ assert(numIOThreads_ > 0);
+ assert(ioLoops_.empty);
+ foreach (id; 0 .. numIOThreads_) {
+ // The IO loop on the first IO thread (this thread, i.e. the one serve()
+ // is called from) also accepts new connections.
+ auto listenSocket = (id == 0 ? listenSocket_ : null);
+ ioLoops_ ~= new IOLoop(this, listenSocket);
+ }
+
+ if (cancellation) {
+ cancellation.triggering.addCallback({
+ foreach (i, loop; ioLoops_) loop.stop();
+
+ // Stop accepting new connections right away.
+ listenSocket_.close();
+ listenSocket_ = null;
+ });
+ }
+
+ // Start the IO helper threads for all but the first loop, which we will run
+ // ourselves. Note that the threads run forever, only terminating if stop()
+ // is called.
+ auto threads = new ThreadGroup();
+ foreach (loop; ioLoops_[1 .. $]) {
+ auto t = new Thread(&loop.run);
+ threads.add(t);
+ t.start();
+ }
+
+ if (eventHandler) eventHandler.preServe();
+
+ // Run the primary (listener) IO thread loop in our main thread; this will
+ // block until the server is shutting down.
+ ioLoops_[0].run();
+
+ // Ensure all threads are finished before leaving serve().
+ threads.joinAll();
+
+ ioLoops_ = null;
+ }
+
+ /**
+ * Returns the number of currently active connections, i.e. open sockets.
+ */
+ size_t numConnections() const @property {
+ return numConnections_;
+ }
+
+ /**
+ * Returns the number of connection objects allocated, but not in use.
+ */
+ size_t numIdleConnections() const @property {
+ return connectionStack_.length;
+ }
+
+ /**
+ * Return count of number of connections which are currently processing.
+ *
+ * This is defined as a connection where all data has been received, and the
+ * processor was invoked but has not yet completed.
+ */
+ size_t numActiveProcessors() const @property {
+ return numActiveProcessors_;
+ }
+
+ /// Number of bind() retries.
+ enum BIND_RETRY_LIMIT = 0;
+
+ /// Duration between bind() retries.
+ enum BIND_RETRY_DELAY = dur!"hnsecs"(0);
+
+ /// Whether to listen on IPv6 only, if IPv6 support is detected
+ // (default: false).
+ void ipv6Only(bool value) @property {
+ ipv6Only_ = value;
+ }
+
+ /**
+ * The task pool to use for processing requests. If null, no additional
+ * threads are used and request are processed »inline«.
+ *
+ * Can safely be set even when the server is already running.
+ */
+ TaskPool taskPool() @property {
+ return taskPool_;
+ }
+
+ /// ditto
+ void taskPool(TaskPool pool) @property {
+ taskPool_ = pool;
+ }
+
+ /**
+ * Hysteresis for overload state.
+ *
+ * This is the fraction of the overload value that needs to be reached
+ * before the overload state is cleared. It must be between 0 and 1,
+ * practical choices probably lie between 0.5 and 0.9.
+ */
+ double overloadHysteresis() const @property {
+ return overloadHysteresis_;
+ }
+
+ /// Ditto
+ void overloadHysteresis(double value) @property {
+ enforce(0 < value && value <= 1,
+ "Invalid value for overload hysteresis: " ~ to!string(value));
+ overloadHysteresis_ = value;
+ }
+
+ /// Ditto
+ enum DEFAULT_OVERLOAD_HYSTERESIS = 0.8;
+
+ /**
+ * The action which will be taken on overload.
+ */
+ TOverloadAction overloadAction;
+
+ /// Ditto
+ enum DEFAULT_OVERLOAD_ACTION = TOverloadAction.NONE;
+
+ /**
+ * The write buffer is initialized (and when idleWriteBufferLimit_ is checked
+ * and found to be exceeded, reinitialized) to this size.
+ */
+ size_t writeBufferDefaultSize;
+
+ /// Ditto
+ enum size_t DEFAULT_WRITE_BUFFER_DEFAULT_SIZE = 1024;
+
+ /**
+ * Max read buffer size for an idle Connection. When we place an idle
+ * Connection into connectionStack_ or on every resizeBufferEveryN_ calls,
+ * we will free the buffer (such that it will be reinitialized by the next
+ * received frame) if it has exceeded this limit. 0 disables this check.
+ */
+ size_t idleReadBufferLimit;
+
+ /// Ditto
+ enum size_t DEFAULT_IDLE_READ_BUFFER_LIMIT = 1024;
+
+ /**
+ * Max write buffer size for an idle connection. When we place an idle
+ * Connection into connectionStack_ or on every resizeBufferEveryN_ calls,
+ * we ensure that its write buffer is <= to this size; otherwise we
+ * replace it with a new one of writeBufferDefaultSize_ bytes to ensure that
+ * idle connections don't hog memory. 0 disables this check.
+ */
+ size_t idleWriteBufferLimit;
+
+ /// Ditto
+ enum size_t DEFAULT_IDLE_WRITE_BUFFER_LIMIT = 1024;
+
+ /**
+ * Every N calls we check the buffer size limits on a connected Connection.
+ * 0 disables (i.e. the checks are only done when a connection closes).
+ */
+ uint resizeBufferEveryN;
+
+ /// Ditto
+ enum uint DEFAULT_RESIZE_BUFFER_EVERY_N = 512;
+
+ /// Limit for how many Connection objects to cache.
+ size_t connectionStackLimit;
+
+ /// Ditto
+ enum size_t DEFAULT_CONNECTION_STACK_LIMIT = 1024;
+
+ /// Limit for number of open connections before server goes into overload
+ /// state.
+ size_t maxConnections;
+
+ /// Ditto
+ enum size_t DEFAULT_MAX_CONNECTIONS = int.max;
+
+ /// Limit for number of connections processing or waiting to process
+ size_t maxActiveProcessors;
+
+ /// Ditto
+ enum size_t DEFAULT_MAX_ACTIVE_PROCESSORS = int.max;
+
+ /// Maximum frame size, in bytes.
+ ///
+ /// If a client tries to send a message larger than this limit, its
+ /// connection will be closed. This helps to avoid allocating huge buffers
+ /// on bogous input.
+ uint maxFrameSize;
+
+ /// Ditto
+ enum uint DEFAULT_MAX_FRAME_SIZE = 256 * 1024 * 1024;
+
+
+ size_t numIOThreads() @property {
+ return numIOThreads_;
+ }
+
+ void numIOThreads(size_t value) @property {
+ enforce(value >= 1, new TException("Must use at least one I/O thread."));
+ numIOThreads_ = value;
+ }
+
+ enum DEFAULT_NUM_IO_THREADS = 1;
+
+private:
+ /**
+ * C callback wrapper around acceptConnections(). Expects the custom argument
+ * to be the this pointer of the associated server instance.
+ */
+ extern(C) static void acceptConnectionsCallback(int fd, short which,
+ void* serverThis
+ ) {
+ (cast(TNonblockingServer)serverThis).acceptConnections(fd, which);
+ }
+
+ /**
+ * Called by libevent (IO loop 0/serve() thread only) when something
+ * happened on the listening socket.
+ */
+ void acceptConnections(int fd, short eventFlags) {
+ if (atomicLoad(ioLoops_[0].shuttingDown_)) return;
+
+ assert(!!listenSocket_,
+ "Server should be shutting down if listen socket is null.");
+ assert(fd == listenSocket_.handle);
+ assert(eventFlags & EV_READ);
+
+ // Accept as many new clients as possible, even though libevent signaled
+ // only one. This helps the number of calls into libevent space.
+ while (true) {
+ // It is lame to use exceptions for regular control flow (failing is
+ // excepted due to non-blocking mode of operation), but that's the
+ // interface std.socket offers…
+ Socket clientSocket;
+ try {
+ clientSocket = listenSocket_.accept();
+ } catch (SocketAcceptException e) {
+ if (e.errorCode != WOULD_BLOCK_ERRNO) {
+ logError("Error accepting connection: %s", e);
+ }
+ break;
+ }
+
+ // If the server is overloaded, this is the point to take the specified
+ // action.
+ if (overloadAction != TOverloadAction.NONE && checkOverloaded()) {
+ nConnectionsDropped_++;
+ nTotalConnectionsDropped_++;
+ if (overloadAction == TOverloadAction.CLOSE_ON_ACCEPT) {
+ clientSocket.close();
+ return;
+ }
+ }
+
+ try {
+ clientSocket.blocking = false;
+ } catch (SocketException e) {
+ logError("Couldn't set client socket to non-blocking mode: %s", e);
+ clientSocket.close();
+ return;
+ }
+
+ // Create a new Connection for this client socket.
+ Connection conn = void;
+ IOLoop loop = void;
+ bool thisThread = void;
+ synchronized (connectionMutex_) {
+ // Assign an I/O loop to the connection (round-robin).
+ assert(nextIOLoop_ >= 0);
+ assert(nextIOLoop_ < ioLoops_.length);
+ auto selectedThreadIdx = nextIOLoop_;
+ nextIOLoop_ = (nextIOLoop_ + 1) % ioLoops_.length;
+
+ loop = ioLoops_[selectedThreadIdx];
+ thisThread = (selectedThreadIdx == 0);
+
+ // Check the connection stack to see if we can re-use an existing one.
+ if (connectionStack_.empty) {
+ ++numConnections_;
+ conn = new Connection(clientSocket, loop);
+
+ // Make sure the connection does not get collected while it is active,
+ // i.e. hooked up with libevent.
+ GC.addRoot(cast(void*)conn);
+ } else {
+ conn = connectionStack_[$ - 1];
+ connectionStack_ = connectionStack_[0 .. $ - 1];
+ connectionStack_.assumeSafeAppend();
+ conn.init(clientSocket, loop);
+ }
+ }
+
+ loop.addConnection();
+
+ // Either notify the ioThread that is assigned this connection to
+ // start processing, or if it is us, we'll just ask this
+ // connection to do its initial state change here.
+ //
+ // (We need to avoid writing to our own notification pipe, to
+ // avoid possible deadlocks if the pipe is full.)
+ if (thisThread) {
+ conn.transition();
+ } else {
+ loop.notifyCompleted(conn);
+ }
+ }
+ }
+
+ /// Increment the count of connections currently processing.
+ void incrementActiveProcessors() {
+ atomicOp!"+="(numActiveProcessors_, 1);
+ }
+
+ /// Decrement the count of connections currently processing.
+ void decrementActiveProcessors() {
+ assert(numActiveProcessors_ > 0);
+ atomicOp!"-="(numActiveProcessors_, 1);
+ }
+
+ /**
+ * Determines if the server is currently overloaded.
+ *
+ * If the number of open connections or »processing« connections is over the
+ * respective limit, the server will enter overload handling mode and a
+ * warning will be logged. If below values are below the hysteresis curve,
+ * this will cause the server to exit it again.
+ *
+ * Returns: Whether the server is currently overloaded.
+ */
+ bool checkOverloaded() {
+ auto activeConnections = numConnections_ - connectionStack_.length;
+ if (numActiveProcessors_ > maxActiveProcessors ||
+ activeConnections > maxConnections) {
+ if (!overloaded_) {
+ logInfo("Entering overloaded state.");
+ overloaded_ = true;
+ }
+ } else {
+ if (overloaded_ &&
+ (numActiveProcessors_ <= overloadHysteresis_ * maxActiveProcessors) &&
+ (activeConnections <= overloadHysteresis_ * maxConnections))
+ {
+ logInfo("Exiting overloaded state, %s connection(s) dropped (% total).",
+ nConnectionsDropped_, nTotalConnectionsDropped_);
+ nConnectionsDropped_ = 0;
+ overloaded_ = false;
+ }
+ }
+
+ return overloaded_;
+ }
+
+ /**
+ * Marks a connection as inactive and either puts it back into the
+ * connection pool or leaves it for garbage collection.
+ */
+ void disposeConnection(Connection connection) {
+ synchronized (connectionMutex_) {
+ if (!connectionStackLimit ||
+ (connectionStack_.length < connectionStackLimit))
+ {
+ connection.checkIdleBufferLimit(idleReadBufferLimit,
+ idleWriteBufferLimit);
+ connectionStack_ ~= connection;
+ } else {
+ assert(numConnections_ > 0);
+ --numConnections_;
+
+ // Leave the connection object for collection now.
+ GC.removeRoot(cast(void*)connection);
+ }
+ }
+ }
+
+ /// Socket used to listen for connections and accepting them.
+ Socket listenSocket_;
+
+ /// Port to listen on.
+ ushort port_;
+
+ /// Whether to listen on IPv6 only.
+ bool ipv6Only_;
+
+ /// The total number of connections existing, both active and idle.
+ size_t numConnections_;
+
+ /// The number of connections which are currently waiting for the processor
+ /// to return.
+ shared size_t numActiveProcessors_;
+
+ /// Hysteresis for leaving overload state.
+ double overloadHysteresis_;
+
+ /// Whether the server is currently overloaded.
+ bool overloaded_;
+
+ /// Number of connections dropped since the server entered the current
+ /// overloaded state.
+ uint nConnectionsDropped_;
+
+ /// Number of connections dropped due to overload since the server started.
+ ulong nTotalConnectionsDropped_;
+
+ /// The task pool used for processing requests.
+ TaskPool taskPool_;
+
+ /// Number of IO threads this server will use (>= 1).
+ size_t numIOThreads_;
+
+ /// The IOLoops among which socket handling work is distributed.
+ IOLoop[] ioLoops_;
+
+ /// The index of the loop in ioLoops_ which will handle the next accepted
+ /// connection.
+ size_t nextIOLoop_;
+
+ /// All the connection objects which have been created but are not currently
+ /// in use. When a connection is closed, it it placed here to enable object
+ /// (resp. buffer) reuse.
+ Connection[] connectionStack_;
+
+ /// This mutex protects the connection stack.
+ Mutex connectionMutex_;
+}
+
+private {
+ /*
+ * Encapsulates a libevent event loop.
+ *
+ * The design is a bit of a mess, since the first loop is actually run on the
+ * server thread itself and is special because it is the only instance for
+ * which listenSocket_ is not null.
+ */
+ final class IOLoop {
+ /**
+ * Creates a new instance and set up the event base.
+ *
+ * If listenSocket is not null, the thread will also accept new
+ * connections itself.
+ */
+ this(TNonblockingServer server, Socket listenSocket) {
+ server_ = server;
+ listenSocket_ = listenSocket;
+ initMutex_ = new Mutex;
+ }
+
+ /**
+ * Runs the event loop; only returns after a call to stop().
+ */
+ void run() {
+ assert(!atomicLoad(initialized_), "IOLoop already running?!");
+
+ synchronized (initMutex_) {
+ if (atomicLoad(shuttingDown_)) return;
+ atomicStore(initialized_, true);
+
+ assert(!eventBase_);
+ eventBase_ = event_base_new();
+
+ if (listenSocket_) {
+ // Log the libevent version and backend.
+ logInfo("libevent version %s, using method %s.",
+ to!string(event_get_version()), to!string(event_base_get_method(eventBase_)));
+
+ // Register the event for the listening socket.
+ listenEvent_ = event_new(eventBase_, listenSocket_.handle,
+ EV_READ | EV_PERSIST | EV_ET,
+ assumeNothrow(&TNonblockingServer.acceptConnectionsCallback),
+ cast(void*)server_);
+ if (event_add(listenEvent_, null) == -1) {
+ throw new TException("event_add for the listening socket event failed.");
+ }
+ }
+
+ auto pair = socketPair();
+ foreach (s; pair) s.blocking = false;
+ completionSendSocket_ = pair[0];
+ completionReceiveSocket_ = pair[1];
+
+ // Register an event for the task completion notification socket.
+ completionEvent_ = event_new(eventBase_, completionReceiveSocket_.handle,
+ EV_READ | EV_PERSIST | EV_ET, assumeNothrow(&completedCallback),
+ cast(void*)this);
+
+ if (event_add(completionEvent_, null) == -1) {
+ throw new TException("event_add for the notification socket failed.");
+ }
+ }
+
+ // Run libevent engine, returns only after stop().
+ event_base_dispatch(eventBase_);
+
+ if (listenEvent_) {
+ event_free(listenEvent_);
+ listenEvent_ = null;
+ }
+
+ event_free(completionEvent_);
+ completionEvent_ = null;
+
+ completionSendSocket_.close();
+ completionSendSocket_ = null;
+
+ completionReceiveSocket_.close();
+ completionReceiveSocket_ = null;
+
+ event_base_free(eventBase_);
+ eventBase_ = null;
+
+ atomicStore(shuttingDown_, false);
+
+ initialized_ = false;
+ }
+
+ /**
+ * Adds a new connection handled by this loop.
+ */
+ void addConnection() {
+ ++numActiveConnections_;
+ }
+
+ /**
+ * Disposes a connection object (typically after it has been closed).
+ */
+ void disposeConnection(Connection conn) {
+ server_.disposeConnection(conn);
+ assert(numActiveConnections_ > 0);
+ --numActiveConnections_;
+ if (numActiveConnections_ == 0) {
+ if (atomicLoad(shuttingDown_)) {
+ event_base_loopbreak(eventBase_);
+ }
+ }
+ }
+
+ /**
+ * Notifies the event loop that the current step (initialization,
+ * processing of a request) on a certain connection has been completed.
+ *
+ * This function is thread-safe, but should never be called from the
+ * thread running the loop itself.
+ */
+ void notifyCompleted(Connection conn) {
+ assert(!!completionSendSocket_);
+ auto bytesSent = completionSendSocket_.send(cast(ubyte[])((&conn)[0 .. 1]));
+
+ if (bytesSent != Connection.sizeof) {
+ logError("Sending completion notification failed, connection will " ~
+ "not be properly terminated.");
+ }
+ }
+
+ /**
+ * Exits the event loop after all currently active connections have been
+ * closed.
+ *
+ * This function is thread-safe.
+ */
+ void stop() {
+ // There is a bug in either libevent or its documentation, having no
+ // events registered doesn't actually terminate the loop, because
+ // event_base_new() registers some internal one by calling
+ // evthread_make_base_notifiable().
+ // Due to this, we can't simply remove all events and expect the event
+ // loop to terminate. Instead, we ping the event loop using a null
+ // completion message. This way, we make sure to wake up the libevent
+ // thread if it not currently processing any connections. It will break
+ // out of the loop in disposeConnection() after the last active
+ // connection has been closed.
+ synchronized (initMutex_) {
+ atomicStore(shuttingDown_, true);
+ if (atomicLoad(initialized_)) notifyCompleted(null);
+ }
+ }
+
+ private:
+ /**
+ * C callback to call completed() from libevent.
+ *
+ * Expects the custom argument to be the this pointer of the associated
+ * IOLoop instance.
+ */
+ extern(C) static void completedCallback(int fd, short what, void* loopThis) {
+ assert(what & EV_READ);
+ auto loop = cast(IOLoop)loopThis;
+ assert(fd == loop.completionReceiveSocket_.handle);
+ loop.completed();
+ }
+
+ /**
+ * Reads from the completion receive socket and appropriately transitions
+ * the connections and shuts down the loop if requested.
+ */
+ void completed() {
+ Connection connection;
+ ptrdiff_t bytesRead;
+ while (true) {
+ bytesRead = completionReceiveSocket_.receive(
+ cast(ubyte[])((&connection)[0 .. 1]));
+ if (bytesRead < 0) {
+ auto errno = getSocketErrno();
+
+ if (errno != WOULD_BLOCK_ERRNO) {
+ logError("Reading from completion socket failed, some connection " ~
+ "will never be properly terminated: %s", socketErrnoString(errno));
+ }
+ }
+
+ if (bytesRead != Connection.sizeof) break;
+
+ if (!connection) {
+ assert(atomicLoad(shuttingDown_));
+ if (numActiveConnections_ == 0) {
+ event_base_loopbreak(eventBase_);
+ }
+ continue;
+ }
+
+ connection.transition();
+ }
+
+ if (bytesRead > 0) {
+ logError("Unexpected partial read from completion socket " ~
+ "(%s bytes instead of %s).", bytesRead, Connection.sizeof);
+ }
+ }
+
+ /// associated server
+ TNonblockingServer server_;
+
+ /// The managed listening socket, if any.
+ Socket listenSocket_;
+
+ /// The libevent event base for the loop.
+ event_base* eventBase_;
+
+ /// Triggered on listen socket events.
+ event* listenEvent_;
+
+ /// Triggered on completion receive socket events.
+ event* completionEvent_;
+
+ /// Socket used to send completion notification messages. Paired with
+ /// completionReceiveSocket_.
+ Socket completionSendSocket_;
+
+ /// Socket used to send completion notification messages. Paired with
+ /// completionSendSocket_.
+ Socket completionReceiveSocket_;
+
+ /// Whether the server is currently shutting down (i.e. the cancellation has
+ /// been triggered, but not all client connections have been closed yet).
+ shared bool shuttingDown_;
+
+ /// The number of currently active client connections.
+ size_t numActiveConnections_;
+
+ /// Guards loop startup so that the loop can be reliably shut down even if
+ /// another thread has just started to execute run(). Locked during
+ /// initialization in run(). When unlocked, the completion mechanism is
+ /// expected to be fully set up.
+ Mutex initMutex_;
+ shared bool initialized_; /// Ditto
+ }
+
+ /*
+ * I/O states a socket can be in.
+ */
+ enum SocketState {
+ RECV_FRAME_SIZE, /// The frame size is received.
+ RECV, /// The payload is received.
+ SEND /// The response is written back out.
+ }
+
+ /*
+ * States a connection can be in.
+ */
+ enum ConnectionState {
+ INIT, /// The connection will be initialized.
+ READ_FRAME_SIZE, /// The four frame size bytes are being read.
+ READ_REQUEST, /// The request payload itself is being read.
+ WAIT_PROCESSOR, /// The connection waits for the processor to finish.
+ SEND_RESULT /// The result is written back out.
+ }
+
+ /*
+ * A connection that is handled via libevent.
+ *
+ * Data received is buffered until the request is complete (returning back to
+ * libevent if not), at which point the processor is invoked.
+ */
+ final class Connection {
+ /**
+ * Constructs a new instance.
+ *
+ * To reuse a connection object later on, the init() function can be used
+ * to the same effect on the internal state.
+ */
+ this(Socket socket, IOLoop loop) {
+ // The input and output transport objects are reused between clients
+ // connections, so initialize them here rather than in init().
+ inputTransport_ = new TInputRangeTransport!(ubyte[])([]);
+ outputTransport_ = new TMemoryBuffer(loop.server_.writeBufferDefaultSize);
+
+ init(socket, loop);
+ }
+
+ /**
+ * Initializes the connection.
+ *
+ * Params:
+ * socket = The socket to work on.
+ * eventFlags = Any flags to pass to libevent.
+ * s = The server this connection is part of.
+ */
+ void init(Socket socket, IOLoop loop) {
+ // TODO: This allocation could be avoided.
+ socket_ = new TSocket(socket);
+
+ loop_ = loop;
+ server_ = loop_.server_;
+ connState_ = ConnectionState.INIT;
+ eventFlags_ = 0;
+
+ readBufferPos_ = 0;
+ readWant_ = 0;
+
+ writeBuffer_ = null;
+ writeBufferPos_ = 0;
+ largestWriteBufferSize_ = 0;
+
+ socketState_ = SocketState.RECV_FRAME_SIZE;
+ callsSinceResize_ = 0;
+
+ factoryInputTransport_ =
+ server_.inputTransportFactory.getTransport(inputTransport_);
+ factoryOutputTransport_ =
+ server_.outputTransportFactory.getTransport(outputTransport_);
+
+ inputProtocol_ =
+ server_.inputProtocolFactory.getProtocol(factoryInputTransport_);
+ outputProtocol_ =
+ server_.outputProtocolFactory.getProtocol(factoryOutputTransport_);
+
+ if (server_.eventHandler) {
+ connectionContext_ =
+ server_.eventHandler.createContext(inputProtocol_, outputProtocol_);
+ }
+
+ auto info = TConnectionInfo(inputProtocol_, outputProtocol_, socket_);
+ processor_ = server_.processorFactory.getProcessor(info);
+ }
+
+ ~this() {
+ free(readBuffer_);
+ if (event_) {
+ event_free(event_);
+ event_ = null;
+ }
+ }
+
+ /**
+ * Check buffers against the size limits and shrink them if exceeded.
+ *
+ * Params:
+ * readLimit = Read buffer size limit (in bytes, 0 to ignore).
+ * writeLimit = Write buffer size limit (in bytes, 0 to ignore).
+ */
+ void checkIdleBufferLimit(size_t readLimit, size_t writeLimit) {
+ if (readLimit > 0 && readBufferSize_ > readLimit) {
+ free(readBuffer_);
+ readBuffer_ = null;
+ readBufferSize_ = 0;
+ }
+
+ if (writeLimit > 0 && largestWriteBufferSize_ > writeLimit) {
+ // just start over
+ outputTransport_.reset(server_.writeBufferDefaultSize);
+ largestWriteBufferSize_ = 0;
+ }
+ }
+
+ /**
+ * Transitions the connection to the next state.
+ *
+ * This is called e.g. when the request has been read completely or all
+ * the data has been written back.
+ */
+ void transition() {
+ assert(!!loop_);
+ assert(!!server_);
+
+ // Switch upon the state that we are currently in and move to a new state
+ final switch (connState_) {
+ case ConnectionState.READ_REQUEST:
+ // We are done reading the request, package the read buffer into transport
+ // and get back some data from the dispatch function
+ inputTransport_.reset(readBuffer_[0 .. readBufferPos_]);
+ outputTransport_.reset();
+
+ // Prepend four bytes of blank space to the buffer so we can
+ // write the frame size there later.
+ // Strictly speaking, we wouldn't have to write anything, just
+ // increment the TMemoryBuffer writeOffset_. This would yield a tiny
+ // performance gain.
+ ubyte[4] space = void;
+ outputTransport_.write(space);
+
+ server_.incrementActiveProcessors();
+
+ taskPool_ = server_.taskPool;
+ if (taskPool_) {
+ // Create a new task and add it to the task pool queue.
+ auto processingTask = task!processRequest(this);
+ connState_ = ConnectionState.WAIT_PROCESSOR;
+ taskPool_.put(processingTask);
+
+ // We don't want to process any more data while the task is active.
+ unregisterEvent();
+ return;
+ }
+
+ // Just process it right now if there is no task pool set.
+ processRequest(this);
+ goto case;
+ case ConnectionState.WAIT_PROCESSOR:
+ // We have now finished processing the request, set the frame size
+ // for the outputTransport_ contents and set everything up to write
+ // it out via libevent.
+ server_.decrementActiveProcessors();
+
+ // Acquire the data written to the transport.
+ // KLUDGE: To avoid copying, we simply cast the const away and
+ // modify the internal buffer of the TMemoryBuffer – works with the
+ // current implementation, but isn't exactly beautiful.
+ writeBuffer_ = cast(ubyte[])outputTransport_.getContents();
+
+ assert(writeBuffer_.length >= 4, "The write buffer should have " ~
+ "least the initially added dummy length bytes.");
+ if (writeBuffer_.length == 4) {
+ // The request was one-way, no response to write.
+ goto case ConnectionState.INIT;
+ }
+
+ // Write the frame size into the four bytes reserved for it.
+ auto size = hostToNet(cast(uint)(writeBuffer_.length - 4));
+ writeBuffer_[0 .. 4] = cast(ubyte[])((&size)[0 .. 1]);
+
+ writeBufferPos_ = 0;
+ socketState_ = SocketState.SEND;
+ connState_ = ConnectionState.SEND_RESULT;
+ registerEvent(EV_WRITE | EV_PERSIST);
+
+ return;
+ case ConnectionState.SEND_RESULT:
+ // The result has been sent back to the client, we don't need the
+ // buffers anymore.
+ if (writeBuffer_.length > largestWriteBufferSize_) {
+ largestWriteBufferSize_ = writeBuffer_.length;
+ }
+
+ if (server_.resizeBufferEveryN > 0 &&
+ ++callsSinceResize_ >= server_.resizeBufferEveryN
+ ) {
+ checkIdleBufferLimit(server_.idleReadBufferLimit,
+ server_.idleWriteBufferLimit);
+ callsSinceResize_ = 0;
+ }
+
+ goto case;
+ case ConnectionState.INIT:
+ writeBuffer_ = null;
+ writeBufferPos_ = 0;
+ socketState_ = SocketState.RECV_FRAME_SIZE;
+ connState_ = ConnectionState.READ_FRAME_SIZE;
+ readBufferPos_ = 0;
+ registerEvent(EV_READ | EV_PERSIST);
+
+ return;
+ case ConnectionState.READ_FRAME_SIZE:
+ // We just read the request length, set up the buffers for reading
+ // the payload.
+ if (readWant_ > readBufferSize_) {
+ // The current buffer is too small, exponentially grow the buffer
+ // until it is big enough.
+
+ if (readBufferSize_ == 0) {
+ readBufferSize_ = 1;
+ }
+
+ auto newSize = readBufferSize_;
+ while (readWant_ > newSize) {
+ newSize *= 2;
+ }
+
+ auto newBuffer = cast(ubyte*)realloc(readBuffer_, newSize);
+ if (!newBuffer) onOutOfMemoryError();
+
+ readBuffer_ = newBuffer;
+ readBufferSize_ = newSize;
+ }
+
+ readBufferPos_= 0;
+
+ socketState_ = SocketState.RECV;
+ connState_ = ConnectionState.READ_REQUEST;
+
+ return;
+ }
+ }
+
+ private:
+ /**
+ * C callback to call workSocket() from libevent.
+ *
+ * Expects the custom argument to be the this pointer of the associated
+ * connection.
+ */
+ extern(C) static void workSocketCallback(int fd, short flags, void* connThis) {
+ auto conn = cast(Connection)connThis;
+ assert(fd == conn.socket_.socketHandle);
+ conn.workSocket();
+ }
+
+ /**
+ * Invoked by libevent when something happens on the socket.
+ */
+ void workSocket() {
+ final switch (socketState_) {
+ case SocketState.RECV_FRAME_SIZE:
+ // If some bytes have already been read, they have been kept in
+ // readWant_.
+ auto frameSize = readWant_;
+
+ try {
+ // Read from the socket
+ auto bytesRead = socket_.read(
+ (cast(ubyte[])((&frameSize)[0 .. 1]))[readBufferPos_ .. $]);
+ if (bytesRead == 0) {
+ // Couldn't read anything, but we have been notified – client
+ // has disconnected.
+ close();
+ return;
+ }
+
+ readBufferPos_ += bytesRead;
+ } catch (TTransportException te) {
+ logError("Failed to read frame size from client connection: %s", te);
+ close();
+ return;
+ }
+
+ if (readBufferPos_ < frameSize.sizeof) {
+ // Frame size not complete yet, save the current buffer in
+ // readWant_ so that the remaining bytes can be read later.
+ readWant_ = frameSize;
+ return;
+ }
+
+ auto size = netToHost(frameSize);
+ if (size > server_.maxFrameSize) {
+ logError("Frame size too large (%s > %s), client %s not using " ~
+ "TFramedTransport?", size, server_.maxFrameSize,
+ socket_.getPeerAddress().toHostNameString());
+ close();
+ return;
+ }
+ readWant_ = size;
+
+ // Now we know the frame size, set everything up for reading the
+ // payload.
+ transition();
+ return;
+
+ case SocketState.RECV:
+ // If we already got all the data, we should be in the SEND state.
+ assert(readBufferPos_ < readWant_);
+
+ size_t bytesRead;
+ try {
+ // Read as much as possible from the socket.
+ bytesRead = socket_.read(readBuffer_[readBufferPos_ .. readWant_]);
+ } catch (TTransportException te) {
+ logError("Failed to read from client socket: %s", te);
+ close();
+ return;
+ }
+
+ if (bytesRead == 0) {
+ // We were notified, but no bytes could be read -> the client
+ // disconnected.
+ close();
+ return;
+ }
+
+ readBufferPos_ += bytesRead;
+ assert(readBufferPos_ <= readWant_);
+
+ if (readBufferPos_ == readWant_) {
+ // The payload has been read completely, move on.
+ transition();
+ }
+
+ return;
+ case SocketState.SEND:
+ assert(writeBufferPos_ <= writeBuffer_.length);
+
+ if (writeBufferPos_ == writeBuffer_.length) {
+ // Nothing left to send – this shouldn't happen, just move on.
+ logInfo("WARNING: In send state, but no data to send.\n");
+ transition();
+ return;
+ }
+
+ size_t bytesSent;
+ try {
+ bytesSent = socket_.writeSome(writeBuffer_[writeBufferPos_ .. $]);
+ } catch (TTransportException te) {
+ logError("Failed to write to client socket: %s", te);
+ close();
+ return;
+ }
+
+ writeBufferPos_ += bytesSent;
+ assert(writeBufferPos_ <= writeBuffer_.length);
+
+ if (writeBufferPos_ == writeBuffer_.length) {
+ // The whole response has been written out, we are done.
+ transition();
+ }
+
+ return;
+ }
+ }
+
+ /**
+ * Registers a libevent event for workSocket() with the passed flags,
+ * unregistering the previous one (if any).
+ */
+ void registerEvent(short eventFlags) {
+ if (eventFlags_ == eventFlags) {
+ // Nothing to do if flags are the same.
+ return;
+ }
+
+ // Delete the previously existing event.
+ unregisterEvent();
+
+ eventFlags_ = eventFlags;
+
+ if (eventFlags == 0) return;
+
+ if (!event_) {
+ // If the event was not already allocated, do it now.
+ event_ = event_new(loop_.eventBase_, socket_.socketHandle,
+ eventFlags_, assumeNothrow(&workSocketCallback), cast(void*)this);
+ } else {
+ event_assign(event_, loop_.eventBase_, socket_.socketHandle,
+ eventFlags_, assumeNothrow(&workSocketCallback), cast(void*)this);
+ }
+
+ // Add the event
+ if (event_add(event_, null) == -1) {
+ logError("event_add() for client socket failed.");
+ }
+ }
+
+ /**
+ * Unregisters the current libevent event, if any.
+ */
+ void unregisterEvent() {
+ if (event_ && eventFlags_ != 0) {
+ eventFlags_ = 0;
+ if (event_del(event_) == -1) {
+ logError("event_del() for client socket failed.");
+ return;
+ }
+ }
+ }
+
+ /**
+ * Closes this connection and returns it back to the server.
+ */
+ void close() {
+ unregisterEvent();
+
+ if (server_.eventHandler) {
+ server_.eventHandler.deleteContext(
+ connectionContext_, inputProtocol_, outputProtocol_);
+ }
+
+ // Close the socket
+ socket_.close();
+
+ // close any factory produced transports.
+ factoryInputTransport_.close();
+ factoryOutputTransport_.close();
+
+ // This connection object can now be reused.
+ loop_.disposeConnection(this);
+ }
+
+ /// The server this connection belongs to.
+ TNonblockingServer server_;
+
+ /// The task pool used for this connection. This is cached instead of
+ /// directly using server_.taskPool to avoid confusion if it is changed in
+ /// another thread while the request is processed.
+ TaskPool taskPool_;
+
+ /// The I/O thread handling this connection.
+ IOLoop loop_;
+
+ /// The socket managed by this connection.
+ TSocket socket_;
+
+ /// The libevent object used for registering the workSocketCallback.
+ event* event_;
+
+ /// Libevent flags
+ short eventFlags_;
+
+ /// Socket mode
+ SocketState socketState_;
+
+ /// Application state
+ ConnectionState connState_;
+
+ /// The size of the frame to read. If still in READ_FRAME_SIZE state, some
+ /// of the bytes might not have been written, and the value might still be
+ /// in network byte order. An uint (not a size_t) because the frame size on
+ /// the wire is specified as one.
+ uint readWant_;
+
+ /// The position in the read buffer, i.e. the number of payload bytes
+ /// already received from the socket in READ_REQUEST state, resp. the
+ /// number of size bytes in READ_FRAME_SIZE state.
+ uint readBufferPos_;
+
+ /// Read buffer
+ ubyte* readBuffer_;
+
+ /// Read buffer size
+ size_t readBufferSize_;
+
+ /// Write buffer
+ ubyte[] writeBuffer_;
+
+ /// How far through writing are we?
+ size_t writeBufferPos_;
+
+ /// Largest size of write buffer seen since buffer was constructed
+ size_t largestWriteBufferSize_;
+
+ /// Number of calls since the last time checkIdleBufferLimit has been
+ /// invoked (see TServer.resizeBufferEveryN).
+ uint callsSinceResize_;
+
+ /// Base transports the processor reads from/writes to.
+ TInputRangeTransport!(ubyte[]) inputTransport_;
+ TMemoryBuffer outputTransport_;
+
+ /// The actual transports passed to the processor obtained via the
+ /// transport factory.
+ TTransport factoryInputTransport_;
+ TTransport factoryOutputTransport_; /// Ditto
+
+ /// Input/output protocols, connected to factory{Input, Output}Transport.
+ TProtocol inputProtocol_;
+ TProtocol outputProtocol_; /// Ditto.
+
+ /// Connection context optionally created by the server event handler.
+ Variant connectionContext_;
+
+ /// The processor used for this connection.
+ TProcessor processor_;
+ }
+}
+
+/*
+ * The request processing function, which invokes the processor for the server
+ * for all the RPC messages received over a connection.
+ *
+ * Must be public because it is passed as alias to std.parallelism.task().
+ */
+void processRequest(Connection connection) {
+ try {
+ while (true) {
+ with (connection) {
+ if (server_.eventHandler) {
+ server_.eventHandler.preProcess(connectionContext_, socket_);
+ }
+
+ if (!processor_.process(inputProtocol_, outputProtocol_,
+ connectionContext_) || !inputProtocol_.transport.peek()
+ ) {
+ // Something went fundamentally wrong or there is nothing more to
+ // process, close the connection.
+ break;
+ }
+ }
+ }
+ } catch (TTransportException ttx) {
+ logError("Client died: %s", ttx);
+ } catch (Exception e) {
+ logError("Uncaught exception: %s", e);
+ }
+
+ if (connection.taskPool_) connection.loop_.notifyCompleted(connection);
+}
+
+unittest {
+ import thrift.internal.test.server;
+
+ // Temporarily disable info log output in order not to spam the test results
+ // with startup info messages.
+ auto oldInfoLogSink = g_infoLogSink;
+ g_infoLogSink = null;
+ scope (exit) g_infoLogSink = oldInfoLogSink;
+
+ // Test in-line processing shutdown with one as well as several I/O threads.
+ testServeCancel!(TNonblockingServer)();
+ testServeCancel!(TNonblockingServer)((TNonblockingServer s) {
+ s.numIOThreads = 4;
+ });
+
+ // Test task pool processing shutdown with one as well as several I/O threads.
+ auto tp = new TaskPool(4);
+ tp.isDaemon = true;
+ testServeCancel!(TNonblockingServer)((TNonblockingServer s) {
+ s.taskPool = tp;
+ });
+ testServeCancel!(TNonblockingServer)((TNonblockingServer s) {
+ s.taskPool = tp;
+ s.numIOThreads = 4;
+ });
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/server/simple.d b/src/jaegertracing/thrift/lib/d/src/thrift/server/simple.d
new file mode 100644
index 000000000..5aba4c169
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/server/simple.d
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.server.simple;
+
+import std.variant : Variant;
+import thrift.base;
+import thrift.protocol.base;
+import thrift.protocol.processor;
+import thrift.server.base;
+import thrift.server.transport.base;
+import thrift.transport.base;
+import thrift.util.cancellation;
+
+/**
+ * The most basic server.
+ *
+ * It is single-threaded and after it accepts a connections, it processes
+ * requests on it until it closes, then waiting for the next connection.
+ *
+ * It is not so much of use in production than it is for writing unittests, or
+ * as an example on how to provide a custom TServer implementation.
+ */
+class TSimpleServer : TServer {
+ ///
+ this(
+ TProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory
+ ) {
+ super(processor, serverTransport, transportFactory, protocolFactory);
+ }
+
+ ///
+ this(
+ TProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory
+ ) {
+ super(processorFactory, serverTransport, transportFactory, protocolFactory);
+ }
+
+ ///
+ this(
+ TProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory
+ ) {
+ super(processor, serverTransport, inputTransportFactory,
+ outputTransportFactory, inputProtocolFactory, outputProtocolFactory);
+ }
+
+ this(
+ TProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory
+ ) {
+ super(processorFactory, serverTransport, inputTransportFactory,
+ outputTransportFactory, inputProtocolFactory, outputProtocolFactory);
+ }
+
+ override void serve(TCancellation cancellation = null) {
+ serverTransport_.listen();
+
+ if (eventHandler) eventHandler.preServe();
+
+ while (true) {
+ TTransport client;
+ TTransport inputTransport;
+ TTransport outputTransport;
+ TProtocol inputProtocol;
+ TProtocol outputProtocol;
+
+ try {
+ client = serverTransport_.accept(cancellation);
+ scope(failure) client.close();
+
+ inputTransport = inputTransportFactory_.getTransport(client);
+ scope(failure) inputTransport.close();
+
+ outputTransport = outputTransportFactory_.getTransport(client);
+ scope(failure) outputTransport.close();
+
+ inputProtocol = inputProtocolFactory_.getProtocol(inputTransport);
+ outputProtocol = outputProtocolFactory_.getProtocol(outputTransport);
+ } catch (TCancelledException tcx) {
+ break;
+ } catch (TTransportException ttx) {
+ logError("TServerTransport failed on accept: %s", ttx);
+ continue;
+ } catch (TException tx) {
+ logError("Caught TException on accept: %s", tx);
+ continue;
+ }
+
+ auto info = TConnectionInfo(inputProtocol, outputProtocol, client);
+ auto processor = processorFactory_.getProcessor(info);
+
+ Variant connectionContext;
+ if (eventHandler) {
+ connectionContext =
+ eventHandler.createContext(inputProtocol, outputProtocol);
+ }
+
+ try {
+ while (true) {
+ if (eventHandler) {
+ eventHandler.preProcess(connectionContext, client);
+ }
+
+ if (!processor.process(inputProtocol, outputProtocol,
+ connectionContext) || !inputProtocol.transport.peek()
+ ) {
+ // Something went fundamentlly wrong or there is nothing more to
+ // process, close the connection.
+ break;
+ }
+ }
+ } catch (TTransportException ttx) {
+ if (ttx.type() != TTransportException.Type.END_OF_FILE) {
+ logError("Client died unexpectedly: %s", ttx);
+ }
+ } catch (Exception e) {
+ logError("Uncaught exception: %s", e);
+ }
+
+ if (eventHandler) {
+ eventHandler.deleteContext(connectionContext, inputProtocol,
+ outputProtocol);
+ }
+
+ try {
+ inputTransport.close();
+ } catch (TTransportException ttx) {
+ logError("Input close failed: %s", ttx);
+ }
+ try {
+ outputTransport.close();
+ } catch (TTransportException ttx) {
+ logError("Output close failed: %s", ttx);
+ }
+ try {
+ client.close();
+ } catch (TTransportException ttx) {
+ logError("Client close failed: %s", ttx);
+ }
+ }
+
+ try {
+ serverTransport_.close();
+ } catch (TServerTransportException e) {
+ logError("Server transport failed to close(): %s", e);
+ }
+ }
+}
+
+unittest {
+ import thrift.internal.test.server;
+ testServeCancel!TSimpleServer();
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/server/taskpool.d b/src/jaegertracing/thrift/lib/d/src/thrift/server/taskpool.d
new file mode 100644
index 000000000..670e720fc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/server/taskpool.d
@@ -0,0 +1,304 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.server.taskpool;
+
+import core.sync.condition;
+import core.sync.mutex;
+import std.exception : enforce;
+import std.parallelism;
+import std.variant : Variant;
+import thrift.base;
+import thrift.protocol.base;
+import thrift.protocol.processor;
+import thrift.server.base;
+import thrift.server.transport.base;
+import thrift.transport.base;
+import thrift.util.cancellation;
+
+/**
+ * A server which dispatches client requests to a std.parallelism TaskPool.
+ */
+class TTaskPoolServer : TServer {
+ ///
+ this(
+ TProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory,
+ TaskPool taskPool = null
+ ) {
+ this(processor, serverTransport, transportFactory, transportFactory,
+ protocolFactory, protocolFactory, taskPool);
+ }
+
+ ///
+ this(
+ TProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory,
+ TaskPool taskPool = null
+ ) {
+ this(processorFactory, serverTransport, transportFactory, transportFactory,
+ protocolFactory, protocolFactory, taskPool);
+ }
+
+ ///
+ this(
+ TProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ TaskPool taskPool = null
+ ) {
+ this(new TSingletonProcessorFactory(processor), serverTransport,
+ inputTransportFactory, outputTransportFactory,
+ inputProtocolFactory, outputProtocolFactory);
+ }
+
+ ///
+ this(
+ TProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ TaskPool taskPool = null
+ ) {
+ super(processorFactory, serverTransport, inputTransportFactory,
+ outputTransportFactory, inputProtocolFactory, outputProtocolFactory);
+
+ if (taskPool) {
+ this.taskPool = taskPool;
+ } else {
+ auto ptp = std.parallelism.taskPool;
+ if (ptp.size > 0) {
+ taskPool_ = ptp;
+ } else {
+ // If the global task pool is empty (default on a single-core machine),
+ // create a new one with a single worker thread. The rationale for this
+ // is to avoid that an application which worked fine with no task pool
+ // explicitly set on the multi-core developer boxes suddenly fails on a
+ // single-core user machine.
+ taskPool_ = new TaskPool(1);
+ taskPool_.isDaemon = true;
+ }
+ }
+ }
+
+ override void serve(TCancellation cancellation = null) {
+ serverTransport_.listen();
+
+ if (eventHandler) eventHandler.preServe();
+
+ auto queueState = QueueState();
+
+ while (true) {
+ // Check if we can still handle more connections.
+ if (maxActiveConns) {
+ synchronized (queueState.mutex) {
+ while (queueState.activeConns >= maxActiveConns) {
+ queueState.connClosed.wait();
+ }
+ }
+ }
+
+ TTransport client;
+ TTransport inputTransport;
+ TTransport outputTransport;
+ TProtocol inputProtocol;
+ TProtocol outputProtocol;
+
+ try {
+ client = serverTransport_.accept(cancellation);
+ scope(failure) client.close();
+
+ inputTransport = inputTransportFactory_.getTransport(client);
+ scope(failure) inputTransport.close();
+
+ outputTransport = outputTransportFactory_.getTransport(client);
+ scope(failure) outputTransport.close();
+
+ inputProtocol = inputProtocolFactory_.getProtocol(inputTransport);
+ outputProtocol = outputProtocolFactory_.getProtocol(outputTransport);
+ } catch (TCancelledException tce) {
+ break;
+ } catch (TTransportException ttx) {
+ logError("TServerTransport failed on accept: %s", ttx);
+ continue;
+ } catch (TException tx) {
+ logError("Caught TException on accept: %s", tx);
+ continue;
+ }
+
+ auto info = TConnectionInfo(inputProtocol, outputProtocol, client);
+ auto processor = processorFactory_.getProcessor(info);
+
+ synchronized (queueState.mutex) {
+ ++queueState.activeConns;
+ }
+ taskPool_.put(task!worker(queueState, client, inputProtocol,
+ outputProtocol, processor, eventHandler));
+ }
+
+ // First, stop accepting new connections.
+ try {
+ serverTransport_.close();
+ } catch (TServerTransportException e) {
+ logError("Server transport failed to close: %s", e);
+ }
+
+ // Then, wait until all active connections are finished.
+ synchronized (queueState.mutex) {
+ while (queueState.activeConns > 0) {
+ queueState.connClosed.wait();
+ }
+ }
+ }
+
+ /**
+ * Sets the task pool to use.
+ *
+ * By default, the global std.parallelism taskPool instance is used, which
+ * might not be appropriate for many applications, e.g. where tuning the
+ * number of worker threads is desired. (On single-core systems, a private
+ * task pool with a single thread is used by default, since the global
+ * taskPool instance has no worker threads then.)
+ *
+ * Note: TTaskPoolServer expects that tasks are never dropped from the pool,
+ * e.g. by calling TaskPool.close() while there are still tasks in the
+ * queue. If this happens, serve() will never return.
+ */
+ void taskPool(TaskPool pool) @property {
+ enforce(pool !is null, "Cannot use a null task pool.");
+ enforce(pool.size > 0, "Cannot use a task pool with no worker threads.");
+ taskPool_ = pool;
+ }
+
+ /**
+ * The maximum number of client connections open at the same time. Zero for
+ * no limit, which is the default.
+ *
+ * If this limit is reached, no clients are accept()ed from the server
+ * transport any longer until another connection has been closed again.
+ */
+ size_t maxActiveConns;
+
+protected:
+ TaskPool taskPool_;
+}
+
+// Cannot be private as worker has to be passed as alias parameter to
+// another module.
+// private {
+ /*
+ * The state of the »connection queue«, i.e. used for keeping track of how
+ * many client connections are currently processed.
+ */
+ struct QueueState {
+ /// Protects the queue state.
+ Mutex mutex;
+
+ /// The number of active connections (from the time they are accept()ed
+ /// until they are closed when the worked task finishes).
+ size_t activeConns;
+
+ /// Signals that the number of active connections has been decreased, i.e.
+ /// that a connection has been closed.
+ Condition connClosed;
+
+ /// Returns an initialized instance.
+ static QueueState opCall() {
+ QueueState q;
+ q.mutex = new Mutex;
+ q.connClosed = new Condition(q.mutex);
+ return q;
+ }
+ }
+
+ void worker(ref QueueState queueState, TTransport client,
+ TProtocol inputProtocol, TProtocol outputProtocol,
+ TProcessor processor, TServerEventHandler eventHandler)
+ {
+ scope (exit) {
+ synchronized (queueState.mutex) {
+ assert(queueState.activeConns > 0);
+ --queueState.activeConns;
+ queueState.connClosed.notifyAll();
+ }
+ }
+
+ Variant connectionContext;
+ if (eventHandler) {
+ connectionContext =
+ eventHandler.createContext(inputProtocol, outputProtocol);
+ }
+
+ try {
+ while (true) {
+ if (eventHandler) {
+ eventHandler.preProcess(connectionContext, client);
+ }
+
+ if (!processor.process(inputProtocol, outputProtocol,
+ connectionContext) || !inputProtocol.transport.peek()
+ ) {
+ // Something went fundamentlly wrong or there is nothing more to
+ // process, close the connection.
+ break;
+ }
+ }
+ } catch (TTransportException ttx) {
+ if (ttx.type() != TTransportException.Type.END_OF_FILE) {
+ logError("Client died unexpectedly: %s", ttx);
+ }
+ } catch (Exception e) {
+ logError("Uncaught exception: %s", e);
+ }
+
+ if (eventHandler) {
+ eventHandler.deleteContext(connectionContext, inputProtocol,
+ outputProtocol);
+ }
+
+ try {
+ inputProtocol.transport.close();
+ } catch (TTransportException ttx) {
+ logError("Input close failed: %s", ttx);
+ }
+ try {
+ outputProtocol.transport.close();
+ } catch (TTransportException ttx) {
+ logError("Output close failed: %s", ttx);
+ }
+ try {
+ client.close();
+ } catch (TTransportException ttx) {
+ logError("Client close failed: %s", ttx);
+ }
+ }
+// }
+
+unittest {
+ import thrift.internal.test.server;
+ testServeCancel!TTaskPoolServer();
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/server/threaded.d b/src/jaegertracing/thrift/lib/d/src/thrift/server/threaded.d
new file mode 100644
index 000000000..300cc8457
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/server/threaded.d
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.server.threaded;
+
+import core.thread;
+import std.variant : Variant;
+import thrift.base;
+import thrift.protocol.base;
+import thrift.protocol.processor;
+import thrift.server.base;
+import thrift.server.transport.base;
+import thrift.transport.base;
+import thrift.util.cancellation;
+
+/**
+ * A simple threaded server which spawns a new thread per connection.
+ */
+class TThreadedServer : TServer {
+ ///
+ this(
+ TProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory
+ ) {
+ super(processor, serverTransport, transportFactory, protocolFactory);
+ }
+
+ ///
+ this(
+ TProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory
+ ) {
+ super(processorFactory, serverTransport, transportFactory, protocolFactory);
+ }
+
+ ///
+ this(
+ TProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory
+ ) {
+ super(processor, serverTransport, inputTransportFactory,
+ outputTransportFactory, inputProtocolFactory, outputProtocolFactory);
+ }
+
+ ///
+ this(
+ TProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory
+ ) {
+ super(processorFactory, serverTransport, inputTransportFactory,
+ outputTransportFactory, inputProtocolFactory, outputProtocolFactory);
+ }
+
+ override void serve(TCancellation cancellation = null) {
+ try {
+ // Start the server listening
+ serverTransport_.listen();
+ } catch (TTransportException ttx) {
+ logError("listen() failed: %s", ttx);
+ return;
+ }
+
+ if (eventHandler) eventHandler.preServe();
+
+ auto workerThreads = new ThreadGroup();
+
+ while (true) {
+ TTransport client;
+ TTransport inputTransport;
+ TTransport outputTransport;
+ TProtocol inputProtocol;
+ TProtocol outputProtocol;
+
+ try {
+ client = serverTransport_.accept(cancellation);
+ scope(failure) client.close();
+
+ inputTransport = inputTransportFactory_.getTransport(client);
+ scope(failure) inputTransport.close();
+
+ outputTransport = outputTransportFactory_.getTransport(client);
+ scope(failure) outputTransport.close();
+
+ inputProtocol = inputProtocolFactory_.getProtocol(inputTransport);
+ outputProtocol = outputProtocolFactory_.getProtocol(outputTransport);
+ } catch (TCancelledException tce) {
+ break;
+ } catch (TTransportException ttx) {
+ logError("TServerTransport failed on accept: %s", ttx);
+ continue;
+ } catch (TException tx) {
+ logError("Caught TException on accept: %s", tx);
+ continue;
+ }
+
+ auto info = TConnectionInfo(inputProtocol, outputProtocol, client);
+ auto processor = processorFactory_.getProcessor(info);
+ auto worker = new WorkerThread(client, inputProtocol, outputProtocol,
+ processor, eventHandler);
+ workerThreads.add(worker);
+ worker.start();
+ }
+
+ try {
+ serverTransport_.close();
+ } catch (TServerTransportException e) {
+ logError("Server transport failed to close: %s", e);
+ }
+ workerThreads.joinAll();
+ }
+}
+
+// The worker thread handling a client connection.
+private class WorkerThread : Thread {
+ this(TTransport client, TProtocol inputProtocol, TProtocol outputProtocol,
+ TProcessor processor, TServerEventHandler eventHandler)
+ {
+ client_ = client;
+ inputProtocol_ = inputProtocol;
+ outputProtocol_ = outputProtocol;
+ processor_ = processor;
+ eventHandler_ = eventHandler;
+
+ super(&run);
+ }
+
+ void run() {
+ Variant connectionContext;
+ if (eventHandler_) {
+ connectionContext =
+ eventHandler_.createContext(inputProtocol_, outputProtocol_);
+ }
+
+ try {
+ while (true) {
+ if (eventHandler_) {
+ eventHandler_.preProcess(connectionContext, client_);
+ }
+
+ if (!processor_.process(inputProtocol_, outputProtocol_,
+ connectionContext) || !inputProtocol_.transport.peek()
+ ) {
+ // Something went fundamentlly wrong or there is nothing more to
+ // process, close the connection.
+ break;
+ }
+ }
+ } catch (TTransportException ttx) {
+ if (ttx.type() != TTransportException.Type.END_OF_FILE) {
+ logError("Client died unexpectedly: %s", ttx);
+ }
+ } catch (Exception e) {
+ logError("Uncaught exception: %s", e);
+ }
+
+ if (eventHandler_) {
+ eventHandler_.deleteContext(connectionContext, inputProtocol_,
+ outputProtocol_);
+ }
+
+ try {
+ inputProtocol_.transport.close();
+ } catch (TTransportException ttx) {
+ logError("Input close failed: %s", ttx);
+ }
+ try {
+ outputProtocol_.transport.close();
+ } catch (TTransportException ttx) {
+ logError("Output close failed: %s", ttx);
+ }
+ try {
+ client_.close();
+ } catch (TTransportException ttx) {
+ logError("Client close failed: %s", ttx);
+ }
+ }
+
+private:
+ TTransport client_;
+ TProtocol inputProtocol_;
+ TProtocol outputProtocol_;
+ TProcessor processor_;
+ TServerEventHandler eventHandler_;
+}
+
+unittest {
+ import thrift.internal.test.server;
+ testServeCancel!TThreadedServer();
+}
+
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/server/transport/base.d b/src/jaegertracing/thrift/lib/d/src/thrift/server/transport/base.d
new file mode 100644
index 000000000..704e16d21
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/server/transport/base.d
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.server.transport.base;
+
+import thrift.base;
+import thrift.transport.base;
+import thrift.util.cancellation;
+
+/**
+ * Some kind of I/O device enabling servers to listen for incoming client
+ * connections and communicate with them via a TTransport interface.
+ */
+interface TServerTransport {
+ /**
+ * Starts listening for server connections.
+ *
+ * Just as simliar functions commonly found in socket libraries, this
+ * function does not block.
+ *
+ * If the socket is already listening, nothing happens.
+ *
+ * Throws: TServerTransportException if listening failed or the transport
+ * was already listening.
+ */
+ void listen();
+
+ /**
+ * Closes the server transport, causing it to stop listening.
+ *
+ * Throws: TServerTransportException if the transport was not listening.
+ */
+ void close();
+
+ /**
+ * Returns whether the server transport is currently listening.
+ */
+ bool isListening() @property;
+
+ /**
+ * Accepts a client connection and returns an opened TTransport for it,
+ * never returning null.
+ *
+ * Blocks until a client connection is available.
+ *
+ * Params:
+ * cancellation = If triggered, requests the call to stop blocking and
+ * return with a TCancelledException. Implementations are free to
+ * ignore this if they cannot provide a reasonable.
+ *
+ * Throws: TServerTransportException if accepting failed,
+ * TCancelledException if it was cancelled.
+ */
+ TTransport accept(TCancellation cancellation = null) out (result) {
+ assert(result !is null);
+ }
+}
+
+/**
+ * Server transport exception.
+ */
+class TServerTransportException : TException {
+ /**
+ * Error codes for the various types of exceptions.
+ */
+ enum Type {
+ ///
+ UNKNOWN,
+
+ /// The server socket is not listening, but excepted to be.
+ NOT_LISTENING,
+
+ /// The server socket is already listening, but expected not to be.
+ ALREADY_LISTENING,
+
+ /// An operation on the primary underlying resource, e.g. a socket used
+ /// for accepting connections, failed.
+ RESOURCE_FAILED
+ }
+
+ ///
+ this(Type type, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
+ this(errorMsg(type), type, file, line, next);
+ }
+
+ ///
+ this(string msg, string file = __FILE__, size_t line = __LINE__,
+ Throwable next = null)
+ {
+ this(msg, Type.UNKNOWN, file, line, next);
+ }
+
+ ///
+ this(string msg, Type type, string file = __FILE__, size_t line = __LINE__,
+ Throwable next = null)
+ {
+ super(msg, file, line, next);
+ type_ = type;
+ }
+
+ ///
+ Type type() const nothrow @property {
+ return type_;
+ }
+
+protected:
+ Type type_;
+
+private:
+ string errorMsg(Type type) {
+ string msg = "TTransportException: ";
+ switch (type) {
+ case Type.UNKNOWN: msg ~= "Unknown server transport exception"; break;
+ case Type.NOT_LISTENING: msg ~= "Server transport not listening"; break;
+ case Type.ALREADY_LISTENING: msg ~= "Server transport already listening"; break;
+ case Type.RESOURCE_FAILED: msg ~= "An underlying resource failed"; break;
+ default: msg ~= "(Invalid exception type)"; break;
+ }
+ return msg;
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/server/transport/socket.d b/src/jaegertracing/thrift/lib/d/src/thrift/server/transport/socket.d
new file mode 100644
index 000000000..e66d80e32
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/server/transport/socket.d
@@ -0,0 +1,380 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.server.transport.socket;
+
+import core.thread : dur, Duration, Thread;
+import core.stdc.string : strerror;
+import std.array : empty;
+import std.conv : text, to;
+import std.exception : enforce;
+import std.socket;
+import thrift.base;
+import thrift.internal.socket;
+import thrift.server.transport.base;
+import thrift.transport.base;
+import thrift.transport.socket;
+import thrift.util.awaitable;
+import thrift.util.cancellation;
+
+private alias TServerTransportException STE;
+
+/**
+ * Server socket implementation of TServerTransport.
+ *
+ * Maps to std.socket listen()/accept(); only provides TCP/IP sockets (i.e. no
+ * Unix sockets) for now, because they are not supported in std.socket.
+ */
+class TServerSocket : TServerTransport {
+ /**
+ * Constructs a new instance.
+ *
+ * Params:
+ * port = The TCP port to listen at (host is always 0.0.0.0).
+ * sendTimeout = The socket sending timeout.
+ * recvTimout = The socket receiving timeout.
+ */
+ this(ushort port, Duration sendTimeout = dur!"hnsecs"(0),
+ Duration recvTimeout = dur!"hnsecs"(0))
+ {
+ port_ = port;
+ sendTimeout_ = sendTimeout;
+ recvTimeout_ = recvTimeout;
+
+ cancellationNotifier_ = new TSocketNotifier;
+
+ socketSet_ = new SocketSet;
+ }
+
+ /// The port the server socket listens at.
+ ushort port() const @property {
+ return port_;
+ }
+
+ /// The socket sending timeout, zero to block infinitely.
+ void sendTimeout(Duration sendTimeout) @property {
+ sendTimeout_ = sendTimeout;
+ }
+
+ /// The socket receiving timeout, zero to block infinitely.
+ void recvTimeout(Duration recvTimeout) @property {
+ recvTimeout_ = recvTimeout;
+ }
+
+ /// The maximum number of listening retries if it fails.
+ void retryLimit(ushort retryLimit) @property {
+ retryLimit_ = retryLimit;
+ }
+
+ /// The delay between a listening attempt failing and retrying it.
+ void retryDelay(Duration retryDelay) @property {
+ retryDelay_ = retryDelay;
+ }
+
+ /// The size of the TCP send buffer, in bytes.
+ void tcpSendBuffer(int tcpSendBuffer) @property {
+ tcpSendBuffer_ = tcpSendBuffer;
+ }
+
+ /// The size of the TCP receiving buffer, in bytes.
+ void tcpRecvBuffer(int tcpRecvBuffer) @property {
+ tcpRecvBuffer_ = tcpRecvBuffer;
+ }
+
+ /// Whether to listen on IPv6 only, if IPv6 support is detected
+ /// (default: false).
+ void ipv6Only(bool value) @property {
+ ipv6Only_ = value;
+ }
+
+ override void listen() {
+ enforce(!isListening, new STE(STE.Type.ALREADY_LISTENING));
+
+ serverSocket_ = makeSocketAndListen(port_, ACCEPT_BACKLOG, retryLimit_,
+ retryDelay_, tcpSendBuffer_, tcpRecvBuffer_, ipv6Only_);
+ }
+
+ override void close() {
+ enforce(isListening, new STE(STE.Type.NOT_LISTENING));
+
+ serverSocket_.shutdown(SocketShutdown.BOTH);
+ serverSocket_.close();
+ serverSocket_ = null;
+ }
+
+ override bool isListening() @property {
+ return serverSocket_ !is null;
+ }
+
+ /// Number of connections listen() backlogs.
+ enum ACCEPT_BACKLOG = 1024;
+
+ override TTransport accept(TCancellation cancellation = null) {
+ enforce(isListening, new STE(STE.Type.NOT_LISTENING));
+
+ if (cancellation) cancellationNotifier_.attach(cancellation.triggering);
+ scope (exit) if (cancellation) cancellationNotifier_.detach();
+
+
+ // Too many EINTRs is a fault condition and would need to be handled
+ // manually by our caller, but we can tolerate a certain number.
+ enum MAX_EINTRS = 10;
+ uint numEintrs;
+
+ while (true) {
+ socketSet_.reset();
+ socketSet_.add(serverSocket_);
+ socketSet_.add(cancellationNotifier_.socket);
+
+ auto ret = Socket.select(socketSet_, null, null);
+ enforce(ret != 0, new STE("Socket.select() returned 0.",
+ STE.Type.RESOURCE_FAILED));
+
+ if (ret < 0) {
+ // Select itself failed, check if it was just due to an interrupted
+ // syscall.
+ if (getSocketErrno() == INTERRUPTED_ERRNO) {
+ if (numEintrs++ < MAX_EINTRS) {
+ continue;
+ } else {
+ throw new STE("Socket.select() was interrupted by a signal (EINTR) " ~
+ "more than " ~ to!string(MAX_EINTRS) ~ " times.",
+ STE.Type.RESOURCE_FAILED
+ );
+ }
+ }
+ throw new STE("Unknown error on Socket.select(): " ~
+ socketErrnoString(getSocketErrno()), STE.Type.RESOURCE_FAILED);
+ } else {
+ // Check for a ping on the interrupt socket.
+ if (socketSet_.isSet(cancellationNotifier_.socket)) {
+ cancellation.throwIfTriggered();
+ }
+
+ // Check for the actual server socket having a connection waiting.
+ if (socketSet_.isSet(serverSocket_)) {
+ break;
+ }
+ }
+ }
+
+ try {
+ auto client = createTSocket(serverSocket_.accept());
+ client.sendTimeout = sendTimeout_;
+ client.recvTimeout = recvTimeout_;
+ return client;
+ } catch (SocketException e) {
+ throw new STE("Unknown error on accepting: " ~ to!string(e),
+ STE.Type.RESOURCE_FAILED);
+ }
+ }
+
+protected:
+ /**
+ * Allows derived classes to create a different TSocket type.
+ */
+ TSocket createTSocket(Socket socket) {
+ return new TSocket(socket);
+ }
+
+private:
+ ushort port_;
+ Duration sendTimeout_;
+ Duration recvTimeout_;
+ ushort retryLimit_;
+ Duration retryDelay_;
+ uint tcpSendBuffer_;
+ uint tcpRecvBuffer_;
+ bool ipv6Only_;
+
+ Socket serverSocket_;
+ TSocketNotifier cancellationNotifier_;
+
+ // Keep socket set between accept() calls to avoid reallocating.
+ SocketSet socketSet_;
+}
+
+Socket makeSocketAndListen(ushort port, int backlog, ushort retryLimit,
+ Duration retryDelay, uint tcpSendBuffer = 0, uint tcpRecvBuffer = 0,
+ bool ipv6Only = false
+) {
+ Address localAddr;
+ try {
+ // null represents the wildcard address.
+ auto addrInfos = getAddressInfo(null, to!string(port),
+ AddressInfoFlags.PASSIVE, SocketType.STREAM, ProtocolType.TCP);
+ foreach (i, ai; addrInfos) {
+ // Prefer to bind to IPv6 addresses, because then IPv4 is listened to as
+ // well, but not the other way round.
+ if (ai.family == AddressFamily.INET6 || i == (addrInfos.length - 1)) {
+ localAddr = ai.address;
+ break;
+ }
+ }
+ } catch (Exception e) {
+ throw new STE("Could not determine local address to listen on.",
+ STE.Type.RESOURCE_FAILED, __FILE__, __LINE__, e);
+ }
+
+ Socket socket;
+ try {
+ socket = new Socket(localAddr.addressFamily, SocketType.STREAM,
+ ProtocolType.TCP);
+ } catch (SocketException e) {
+ throw new STE("Could not create accepting socket: " ~ to!string(e),
+ STE.Type.RESOURCE_FAILED);
+ }
+
+ try {
+ socket.setOption(SocketOptionLevel.IPV6, SocketOption.IPV6_V6ONLY, ipv6Only);
+ } catch (SocketException e) {
+ // This is somewhat expected on older systems (e.g. pre-Vista Windows),
+ // which do not support the IPV6_V6ONLY flag yet. Racy flag just to avoid
+ // log spew in unit tests.
+ shared static warned = false;
+ if (!warned) {
+ logError("Could not set IPV6_V6ONLY socket option: %s", e);
+ warned = true;
+ }
+ }
+
+ alias SocketOptionLevel.SOCKET lvlSock;
+
+ // Prevent 2 maximum segement lifetime delay on accept.
+ try {
+ socket.setOption(lvlSock, SocketOption.REUSEADDR, true);
+ } catch (SocketException e) {
+ throw new STE("Could not set REUSEADDR socket option: " ~ to!string(e),
+ STE.Type.RESOURCE_FAILED);
+ }
+
+ // Set TCP buffer sizes.
+ if (tcpSendBuffer > 0) {
+ try {
+ socket.setOption(lvlSock, SocketOption.SNDBUF, tcpSendBuffer);
+ } catch (SocketException e) {
+ throw new STE("Could not set socket send buffer size: " ~ to!string(e),
+ STE.Type.RESOURCE_FAILED);
+ }
+ }
+
+ if (tcpRecvBuffer > 0) {
+ try {
+ socket.setOption(lvlSock, SocketOption.RCVBUF, tcpRecvBuffer);
+ } catch (SocketException e) {
+ throw new STE("Could not set receive send buffer size: " ~ to!string(e),
+ STE.Type.RESOURCE_FAILED);
+ }
+ }
+
+ // Turn linger off to avoid blocking on socket close.
+ try {
+ Linger l;
+ l.on = 0;
+ l.time = 0;
+ socket.setOption(lvlSock, SocketOption.LINGER, l);
+ } catch (SocketException e) {
+ throw new STE("Could not disable socket linger: " ~ to!string(e),
+ STE.Type.RESOURCE_FAILED);
+ }
+
+ // Set TCP_NODELAY.
+ try {
+ socket.setOption(SocketOptionLevel.TCP, SocketOption.TCP_NODELAY, true);
+ } catch (SocketException e) {
+ throw new STE("Could not disable Nagle's algorithm: " ~ to!string(e),
+ STE.Type.RESOURCE_FAILED);
+ }
+
+ ushort retries;
+ while (true) {
+ try {
+ socket.bind(localAddr);
+ break;
+ } catch (SocketException) {}
+
+ // If bind() worked, we breaked outside the loop above.
+ retries++;
+ if (retries < retryLimit) {
+ Thread.sleep(retryDelay);
+ } else {
+ throw new STE(text("Could not bind to address: ", localAddr),
+ STE.Type.RESOURCE_FAILED);
+ }
+ }
+
+ socket.listen(backlog);
+ return socket;
+}
+
+unittest {
+ // Test interrupt().
+ {
+ auto sock = new TServerSocket(0);
+ sock.listen();
+ scope (exit) sock.close();
+
+ auto cancellation = new TCancellationOrigin;
+
+ auto intThread = new Thread({
+ // Sleep for a bit until the socket is accepting.
+ Thread.sleep(dur!"msecs"(50));
+ cancellation.trigger();
+ });
+ intThread.start();
+
+ import std.exception;
+ assertThrown!TCancelledException(sock.accept(cancellation));
+ }
+
+ // Test receive() timeout on accepted client sockets.
+ {
+ immutable port = 11122;
+ auto timeout = dur!"msecs"(500);
+ auto serverSock = new TServerSocket(port, timeout, timeout);
+ serverSock.listen();
+ scope (exit) serverSock.close();
+
+ auto clientSock = new TSocket("127.0.0.1", port);
+ clientSock.open();
+ scope (exit) clientSock.close();
+
+ shared bool hasTimedOut;
+ auto recvThread = new Thread({
+ auto sock = serverSock.accept();
+ ubyte[1] data;
+ try {
+ sock.read(data);
+ } catch (TTransportException e) {
+ if (e.type == TTransportException.Type.TIMED_OUT) {
+ hasTimedOut = true;
+ } else {
+ import std.stdio;
+ stderr.writeln(e);
+ }
+ }
+ });
+ recvThread.isDaemon = true;
+ recvThread.start();
+
+ // Wait for the timeout, with a little bit of spare time.
+ Thread.sleep(timeout + dur!"msecs"(50));
+ enforce(hasTimedOut,
+ "Client socket receive() blocked for longer than recvTimeout.");
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/server/transport/ssl.d b/src/jaegertracing/thrift/lib/d/src/thrift/server/transport/ssl.d
new file mode 100644
index 000000000..2dd9d2366
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/server/transport/ssl.d
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.server.transport.ssl;
+
+import std.datetime : Duration;
+import std.exception : enforce;
+import std.socket : Socket;
+import thrift.server.transport.socket;
+import thrift.transport.base;
+import thrift.transport.socket;
+import thrift.transport.ssl;
+
+/**
+ * A server transport implementation using SSL-encrypted sockets.
+ *
+ * Note:
+ * On Posix systems which do not have the BSD-specific SO_NOSIGPIPE flag, you
+ * might want to ignore the SIGPIPE signal, as OpenSSL might try to write to
+ * a closed socket if the peer disconnects abruptly:
+ * ---
+ * import core.stdc.signal;
+ * import core.sys.posix.signal;
+ * signal(SIGPIPE, SIG_IGN);
+ * ---
+ *
+ * See: thrift.transport.ssl.
+ */
+class TSSLServerSocket : TServerSocket {
+ /**
+ * Creates a new TSSLServerSocket.
+ *
+ * Params:
+ * port = The port on which to listen.
+ * sslContext = The TSSLContext to use for creating client
+ * sockets. Must be in server-side mode.
+ */
+ this(ushort port, TSSLContext sslContext) {
+ super(port);
+ setSSLContext(sslContext);
+ }
+
+ /**
+ * Creates a new TSSLServerSocket.
+ *
+ * Params:
+ * port = The port on which to listen.
+ * sendTimeout = The send timeout to set on the client sockets.
+ * recvTimeout = The receive timeout to set on the client sockets.
+ * sslContext = The TSSLContext to use for creating client
+ * sockets. Must be in server-side mode.
+ */
+ this(ushort port, Duration sendTimeout, Duration recvTimeout,
+ TSSLContext sslContext)
+ {
+ super(port, sendTimeout, recvTimeout);
+ setSSLContext(sslContext);
+ }
+
+protected:
+ override TSocket createTSocket(Socket socket) {
+ return new TSSLSocket(sslContext_, socket);
+ }
+
+private:
+ void setSSLContext(TSSLContext sslContext) {
+ enforce(sslContext.serverSide, new TTransportException(
+ "Need server-side SSL socket factory for TSSLServerSocket"));
+ sslContext_ = sslContext;
+ }
+
+ TSSLContext sslContext_;
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/transport/base.d b/src/jaegertracing/thrift/lib/d/src/thrift/transport/base.d
new file mode 100644
index 000000000..7e76a5948
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/transport/base.d
@@ -0,0 +1,370 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.transport.base;
+
+import core.stdc.string : strerror;
+import std.conv : text;
+import thrift.base;
+
+/**
+ * An entity data can be read from and/or written to.
+ *
+ * A TTransport implementation may capable of either reading or writing, but
+ * not necessarily both.
+ */
+interface TTransport {
+ /**
+ * Whether this transport is open.
+ *
+ * If a transport is closed, it can be opened by calling open(), and vice
+ * versa for close().
+ *
+ * While a transport should always be open when trying to read/write data,
+ * the related functions do not necessarily fail when called for a closed
+ * transport. Situations like this could occur e.g. with a wrapper
+ * transport which buffers data when the underlying transport has already
+ * been closed (possibly because the connection was abruptly closed), but
+ * there is still data left to be read in the buffers. This choice has been
+ * made to simplify transport implementations, in terms of both code
+ * complexity and runtime overhead.
+ */
+ bool isOpen() @property;
+
+ /**
+ * Tests whether there is more data to read or if the remote side is
+ * still open.
+ *
+ * A typical use case would be a server checking if it should process
+ * another request on the transport.
+ */
+ bool peek();
+
+ /**
+ * Opens the transport for communications.
+ *
+ * If the transport is already open, nothing happens.
+ *
+ * Throws: TTransportException if opening fails.
+ */
+ void open();
+
+ /**
+ * Closes the transport.
+ *
+ * If the transport is not open, nothing happens.
+ *
+ * Throws: TTransportException if closing fails.
+ */
+ void close();
+
+ /**
+ * Attempts to fill the given buffer by reading data.
+ *
+ * For potentially blocking data sources (e.g. sockets), read() will only
+ * block if no data is available at all. If there is some data available,
+ * but waiting for new data to arrive would be required to fill the whole
+ * buffer, the readily available data will be immediately returned – use
+ * readAll() if you want to wait until the whole buffer is filled.
+ *
+ * Params:
+ * buf = Slice to use as buffer.
+ *
+ * Returns: How many bytes were actually read
+ *
+ * Throws: TTransportException if an error occurs.
+ */
+ size_t read(ubyte[] buf);
+
+ /**
+ * Fills the given buffer by reading data into it, failing if not enough
+ * data is available.
+ *
+ * Params:
+ * buf = Slice to use as buffer.
+ *
+ * Throws: TTransportException if insufficient data is available or reading
+ * fails altogether.
+ */
+ void readAll(ubyte[] buf);
+
+ /**
+ * Must be called by clients when read is completed.
+ *
+ * Implementations can choose to perform a transport-specific action, e.g.
+ * logging the request to a file.
+ *
+ * Returns: The number of bytes read if available, 0 otherwise.
+ */
+ size_t readEnd();
+
+ /**
+ * Writes the passed slice of data.
+ *
+ * Note: You must call flush() to ensure the data is actually written,
+ * and available to be read back in the future. Destroying a TTransport
+ * object does not automatically flush pending data – if you destroy a
+ * TTransport object with written but unflushed data, that data may be
+ * discarded.
+ *
+ * Params:
+ * buf = Slice of data to write.
+ *
+ * Throws: TTransportException if an error occurs.
+ */
+ void write(in ubyte[] buf);
+
+ /**
+ * Must be called by clients when write is completed.
+ *
+ * Implementations can choose to perform a transport-specific action, e.g.
+ * logging the request to a file.
+ *
+ * Returns: The number of bytes written if available, 0 otherwise.
+ */
+ size_t writeEnd();
+
+ /**
+ * Flushes any pending data to be written.
+ *
+ * Must be called before destruction to ensure writes are actually complete,
+ * otherwise pending data may be discarded. Typically used with buffered
+ * transport mechanisms.
+ *
+ * Throws: TTransportException if an error occurs.
+ */
+ void flush();
+
+ /**
+ * Attempts to return a slice of <code>len</code> bytes of incoming data,
+ * possibly copied into buf, not consuming them (i.e.: a later read will
+ * return the same data).
+ *
+ * This method is meant to support protocols that need to read variable-
+ * length fields. They can attempt to borrow the maximum amount of data that
+ * they will need, then <code>consume()</code> what they actually use. Some
+ * transports will not support this method and others will fail occasionally,
+ * so protocols must be prepared to fall back to <code>read()</code> if
+ * borrow fails.
+ *
+ * The transport must be open when calling this.
+ *
+ * Params:
+ * buf = A buffer where the data can be stored if needed, or null to
+ * indicate that the caller is not supplying storage, but would like a
+ * slice of an internal buffer, if available.
+ * len = The number of bytes to borrow.
+ *
+ * Returns: If the borrow succeeds, a slice containing the borrowed data,
+ * null otherwise. The slice will be at least as long as requested, but
+ * may be longer if the returned slice points into an internal buffer
+ * rather than buf.
+ *
+ * Throws: TTransportException if an error occurs.
+ */
+ const(ubyte)[] borrow(ubyte* buf, size_t len) out (result) {
+ // FIXME: Commented out because len gets corrupted in
+ // thrift.transport.memory borrow() unittest.
+ version(none) assert(result is null || result.length >= len,
+ "Buffer returned by borrow() too short.");
+ }
+
+ /**
+ * Remove len bytes from the transport. This must always follow a borrow
+ * of at least len bytes, and should always succeed.
+ *
+ * The transport must be open when calling this.
+ *
+ * Params:
+ * len = Number of bytes to consume.
+ *
+ * Throws: TTransportException if an error occurs.
+ */
+ void consume(size_t len);
+}
+
+/**
+ * Provides basic fall-back implementations of the TTransport interface.
+ */
+class TBaseTransport : TTransport {
+ override bool isOpen() @property {
+ return false;
+ }
+
+ override bool peek() {
+ return isOpen;
+ }
+
+ override void open() {
+ throw new TTransportException("Cannot open TBaseTransport.",
+ TTransportException.Type.NOT_IMPLEMENTED);
+ }
+
+ override void close() {
+ throw new TTransportException("Cannot close TBaseTransport.",
+ TTransportException.Type.NOT_IMPLEMENTED);
+ }
+
+ override size_t read(ubyte[] buf) {
+ throw new TTransportException("Cannot read from a TBaseTransport.",
+ TTransportException.Type.NOT_IMPLEMENTED);
+ }
+
+ override void readAll(ubyte[] buf) {
+ size_t have;
+ while (have < buf.length) {
+ size_t get = read(buf[have..$]);
+ if (get <= 0) {
+ throw new TTransportException(text("Could not readAll() ", buf.length,
+ " bytes as no more data was available after ", have, " bytes."),
+ TTransportException.Type.END_OF_FILE);
+ }
+ have += get;
+ }
+ }
+
+ override size_t readEnd() {
+ // Do nothing by default, not needed by all implementations.
+ return 0;
+ }
+
+ override void write(in ubyte[] buf) {
+ throw new TTransportException("Cannot write to a TBaseTransport.",
+ TTransportException.Type.NOT_IMPLEMENTED);
+ }
+
+ override size_t writeEnd() {
+ // Do nothing by default, not needed by all implementations.
+ return 0;
+ }
+
+ override void flush() {
+ // Do nothing by default, not needed by all implementations.
+ }
+
+ override const(ubyte)[] borrow(ubyte* buf, size_t len) {
+ // borrow() is allowed to fail anyway, so just return null.
+ return null;
+ }
+
+ override void consume(size_t len) {
+ throw new TTransportException("Cannot consume from a TBaseTransport.",
+ TTransportException.Type.NOT_IMPLEMENTED);
+ }
+
+protected:
+ this() {}
+}
+
+/**
+ * Makes a TTransport which wraps a given source transport in some way.
+ *
+ * A common use case is inside server implementations, where the raw client
+ * connections accepted from e.g. TServerSocket need to be wrapped into
+ * buffered or compressed transports.
+ */
+class TTransportFactory {
+ /**
+ * Default implementation does nothing, just returns the transport given.
+ */
+ TTransport getTransport(TTransport trans) {
+ return trans;
+ }
+}
+
+/**
+ * Transport factory for transports which simply wrap an underlying TTransport
+ * without requiring additional configuration.
+ */
+class TWrapperTransportFactory(T) if (
+ is(T : TTransport) && __traits(compiles, new T(TTransport.init))
+) : TTransportFactory {
+ override T getTransport(TTransport trans) {
+ return new T(trans);
+ }
+}
+
+/**
+ * Transport-level exception.
+ */
+class TTransportException : TException {
+ /**
+ * Error codes for the various types of exceptions.
+ */
+ enum Type {
+ UNKNOWN, ///
+ NOT_OPEN, ///
+ TIMED_OUT, ///
+ END_OF_FILE, ///
+ INTERRUPTED, ///
+ BAD_ARGS, ///
+ CORRUPTED_DATA, ///
+ INTERNAL_ERROR, ///
+ NOT_IMPLEMENTED ///
+ }
+
+ ///
+ this(Type type, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
+ static string msgForType(Type type) {
+ switch (type) {
+ case Type.UNKNOWN: return "Unknown transport exception";
+ case Type.NOT_OPEN: return "Transport not open";
+ case Type.TIMED_OUT: return "Timed out";
+ case Type.END_OF_FILE: return "End of file";
+ case Type.INTERRUPTED: return "Interrupted";
+ case Type.BAD_ARGS: return "Invalid arguments";
+ case Type.CORRUPTED_DATA: return "Corrupted Data";
+ case Type.INTERNAL_ERROR: return "Internal error";
+ case Type.NOT_IMPLEMENTED: return "Not implemented";
+ default: return "(Invalid exception type)";
+ }
+ }
+ this(msgForType(type), type, file, line, next);
+ }
+
+ ///
+ this(string msg, string file = __FILE__, size_t line = __LINE__,
+ Throwable next = null)
+ {
+ this(msg, Type.UNKNOWN, file, line, next);
+ }
+
+ ///
+ this(string msg, Type type, string file = __FILE__, size_t line = __LINE__,
+ Throwable next = null)
+ {
+ super(msg, file, line, next);
+ type_ = type;
+ }
+
+ ///
+ Type type() const nothrow @property {
+ return type_;
+ }
+
+protected:
+ Type type_;
+}
+
+/**
+ * Meta-programming helper returning whether the passed type is a TTransport
+ * implementation.
+ */
+template isTTransport(T) {
+ enum isTTransport = is(T : TTransport);
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/transport/buffered.d b/src/jaegertracing/thrift/lib/d/src/thrift/transport/buffered.d
new file mode 100644
index 000000000..cabfbdc03
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/transport/buffered.d
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.transport.buffered;
+
+import std.algorithm : min;
+import std.array : empty;
+import std.exception : enforce;
+import thrift.transport.base;
+
+/**
+ * Wraps another transport and buffers reads and writes until the internal
+ * buffers are exhausted, at which point new data is fetched resp. the
+ * accumulated data is written out at once.
+ */
+final class TBufferedTransport : TBaseTransport {
+ /**
+ * Constructs a new instance, using the default buffer sizes.
+ *
+ * Params:
+ * transport = The underlying transport to wrap.
+ */
+ this(TTransport transport) {
+ this(transport, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Constructs a new instance, using the specified buffer size.
+ *
+ * Params:
+ * transport = The underlying transport to wrap.
+ * bufferSize = The size of the read and write buffers to use, in bytes.
+ */
+ this(TTransport transport, size_t bufferSize) {
+ this(transport, bufferSize, bufferSize);
+ }
+
+ /**
+ * Constructs a new instance, using the specified buffer size.
+ *
+ * Params:
+ * transport = The underlying transport to wrap.
+ * readBufferSize = The size of the read buffer to use, in bytes.
+ * writeBufferSize = The size of the write buffer to use, in bytes.
+ */
+ this(TTransport transport, size_t readBufferSize, size_t writeBufferSize) {
+ transport_ = transport;
+ readBuffer_ = new ubyte[readBufferSize];
+ writeBuffer_ = new ubyte[writeBufferSize];
+ writeAvail_ = writeBuffer_;
+ }
+
+ /// The default size of the read/write buffers, in bytes.
+ enum int DEFAULT_BUFFER_SIZE = 512;
+
+ override bool isOpen() @property {
+ return transport_.isOpen();
+ }
+
+ override bool peek() {
+ if (readAvail_.empty) {
+ // If there is nothing available to read, see if we can get something
+ // from the underlying transport.
+ auto bytesRead = transport_.read(readBuffer_);
+ readAvail_ = readBuffer_[0 .. bytesRead];
+ }
+
+ return !readAvail_.empty;
+ }
+
+ override void open() {
+ transport_.open();
+ }
+
+ override void close() {
+ if (!isOpen) return;
+ flush();
+ transport_.close();
+ }
+
+ override size_t read(ubyte[] buf) {
+ if (readAvail_.empty) {
+ // No data left in our buffer, fetch some from the underlying transport.
+
+ if (buf.length > readBuffer_.length) {
+ // If the amount of data requested is larger than our reading buffer,
+ // directly read to the passed buffer. This probably doesn't occur too
+ // often in practice (and even if it does, the underlying transport
+ // probably cannot fulfill the request at once anyway), but it can't
+ // harm to try…
+ return transport_.read(buf);
+ }
+
+ auto bytesRead = transport_.read(readBuffer_);
+ readAvail_ = readBuffer_[0 .. bytesRead];
+ }
+
+ // Hand over whatever we have.
+ auto give = min(readAvail_.length, buf.length);
+ buf[0 .. give] = readAvail_[0 .. give];
+ readAvail_ = readAvail_[give .. $];
+ return give;
+ }
+
+ /**
+ * Shortcut version of readAll.
+ */
+ override void readAll(ubyte[] buf) {
+ if (readAvail_.length >= buf.length) {
+ buf[] = readAvail_[0 .. buf.length];
+ readAvail_ = readAvail_[buf.length .. $];
+ return;
+ }
+
+ super.readAll(buf);
+ }
+
+ override void write(in ubyte[] buf) {
+ if (writeAvail_.length >= buf.length) {
+ // If the data fits in the buffer, just save it there.
+ writeAvail_[0 .. buf.length] = buf;
+ writeAvail_ = writeAvail_[buf.length .. $];
+ return;
+ }
+
+ // We have to decide if we copy data from buf to our internal buffer, or
+ // just directly write them out. The same considerations about avoiding
+ // syscalls as for C++ apply here.
+ auto bytesAvail = writeAvail_.ptr - writeBuffer_.ptr;
+ if ((bytesAvail + buf.length >= 2 * writeBuffer_.length) || (bytesAvail == 0)) {
+ // We would immediately need two syscalls anyway (or we don't have
+ // anything) in our buffer to write, so just write out both buffers.
+ if (bytesAvail > 0) {
+ transport_.write(writeBuffer_[0 .. bytesAvail]);
+ writeAvail_ = writeBuffer_;
+ }
+
+ transport_.write(buf);
+ return;
+ }
+
+ // Fill up our internal buffer for a write.
+ writeAvail_[] = buf[0 .. writeAvail_.length];
+ auto left = buf[writeAvail_.length .. $];
+ transport_.write(writeBuffer_);
+
+ // Copy the rest into our buffer.
+ writeBuffer_[0 .. left.length] = left[];
+ writeAvail_ = writeBuffer_[left.length .. $];
+ }
+
+ override void flush() {
+ // Write out any data waiting in the write buffer.
+ auto bytesAvail = writeAvail_.ptr - writeBuffer_.ptr;
+ if (bytesAvail > 0) {
+ // Note that we reset writeAvail_ prior to calling the underlying protocol
+ // to make sure the buffer is cleared even if the transport throws an
+ // exception.
+ writeAvail_ = writeBuffer_;
+ transport_.write(writeBuffer_[0 .. bytesAvail]);
+ }
+
+ // Flush the underlying transport.
+ transport_.flush();
+ }
+
+ override const(ubyte)[] borrow(ubyte* buf, size_t len) {
+ if (len <= readAvail_.length) {
+ return readAvail_;
+ }
+ return null;
+ }
+
+ override void consume(size_t len) {
+ enforce(len <= readBuffer_.length, new TTransportException(
+ "Invalid consume length.", TTransportException.Type.BAD_ARGS));
+ readAvail_ = readAvail_[len .. $];
+ }
+
+ /**
+ * The wrapped transport.
+ */
+ TTransport underlyingTransport() @property {
+ return transport_;
+ }
+
+private:
+ TTransport transport_;
+
+ ubyte[] readBuffer_;
+ ubyte[] writeBuffer_;
+
+ ubyte[] readAvail_;
+ ubyte[] writeAvail_;
+}
+
+/**
+ * Wraps given transports into TBufferedTransports.
+ */
+alias TWrapperTransportFactory!TBufferedTransport TBufferedTransportFactory;
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/transport/file.d b/src/jaegertracing/thrift/lib/d/src/thrift/transport/file.d
new file mode 100644
index 000000000..fe88e7306
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/transport/file.d
@@ -0,0 +1,1101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Transports for reading from/writing to Thrift »log files«.
+ *
+ * These transports are not »stupid« sources and sinks just reading and
+ * writing bytes from a file verbatim, but organize the contents in the form
+ * of so-called »events«, which refers to the data written between two flush()
+ * calls.
+ *
+ * Chunking is supported, events are guaranteed to never span chunk boundaries.
+ * As a consequence, an event can never be larger than the chunk size. The
+ * chunk size used is not saved with the file, so care has to be taken to make
+ * sure the same chunk size is used for reading and writing.
+ */
+module thrift.transport.file;
+
+import core.thread : Thread;
+import std.array : empty;
+import std.algorithm : min, max;
+import std.concurrency;
+import std.conv : to;
+import std.datetime : dur, Duration;
+import std.datetime.stopwatch : AutoStart, StopWatch;
+import std.exception;
+import std.stdio : File;
+import thrift.base;
+import thrift.transport.base;
+
+/// The default chunk size, in bytes.
+enum DEFAULT_CHUNK_SIZE = 16 * 1024 * 1024;
+
+/// The type used to represent event sizes in the file.
+alias uint EventSize;
+
+version (BigEndian) {
+ static assert(false,
+ "Little endian byte order is assumed in thrift.transport.file.");
+}
+
+/**
+ * A transport used to read log files. It can never be written to, calling
+ * write() throws.
+ *
+ * Contrary to the C++ design, explicitly opening the transport/file before
+ * using is necessary to allow manually closing the file without relying on the
+ * object lifetime. Otherwise, it's a straight port of the C++ implementation.
+ */
+final class TFileReaderTransport : TBaseTransport {
+ /**
+ * Creates a new file writer transport.
+ *
+ * Params:
+ * path = Path of the file to opperate on.
+ */
+ this(string path) {
+ path_ = path;
+ chunkSize_ = DEFAULT_CHUNK_SIZE;
+ readBufferSize_ = DEFAULT_READ_BUFFER_SIZE;
+ readTimeout_ = DEFAULT_READ_TIMEOUT;
+ corruptedEventSleepDuration_ = DEFAULT_CORRUPTED_EVENT_SLEEP_DURATION;
+ maxEventSize = DEFAULT_MAX_EVENT_SIZE;
+ }
+
+ override bool isOpen() @property {
+ return isOpen_;
+ }
+
+ override bool peek() {
+ if (!isOpen) return false;
+
+ // If there is no event currently processed, try fetching one from the
+ // file.
+ if (!currentEvent_) {
+ currentEvent_ = readEvent();
+
+ if (!currentEvent_) {
+ // Still nothing there, couldn't read a new event.
+ return false;
+ }
+ }
+ // check if there is anything to read
+ return (currentEvent_.length - currentEventPos_) > 0;
+ }
+
+ override void open() {
+ if (isOpen) return;
+ try {
+ file_ = File(path_, "rb");
+ } catch (Exception e) {
+ throw new TTransportException("Error on opening input file.",
+ TTransportException.Type.NOT_OPEN, __FILE__, __LINE__, e);
+ }
+ isOpen_ = true;
+ }
+
+ override void close() {
+ if (!isOpen) return;
+
+ file_.close();
+ isOpen_ = false;
+ readState_.resetAllValues();
+ }
+
+ override size_t read(ubyte[] buf) {
+ enforce(isOpen, new TTransportException(
+ "Cannot read if file is not open.", TTransportException.Type.NOT_OPEN));
+
+ // If there is no event currently processed, try fetching one from the
+ // file.
+ if (!currentEvent_) {
+ currentEvent_ = readEvent();
+
+ if (!currentEvent_) {
+ // Still nothing there, couldn't read a new event.
+ return 0;
+ }
+ }
+
+ auto len = buf.length;
+ auto remaining = currentEvent_.length - currentEventPos_;
+
+ if (remaining <= len) {
+ // If less than the requested length is available, read as much as
+ // possible.
+ buf[0 .. remaining] = currentEvent_[currentEventPos_ .. $];
+ currentEvent_ = null;
+ currentEventPos_ = 0;
+ return remaining;
+ }
+
+ // There will still be data left in the buffer after reading, pass out len
+ // bytes.
+ buf[] = currentEvent_[currentEventPos_ .. currentEventPos_ + len];
+ currentEventPos_ += len;
+ return len;
+ }
+
+ ulong getNumChunks() {
+ enforce(isOpen, new TTransportException(
+ "Cannot get number of chunks if file not open.",
+ TTransportException.Type.NOT_OPEN));
+
+ try {
+ auto fileSize = file_.size();
+ if (fileSize == 0) {
+ // Empty files have no chunks.
+ return 0;
+ }
+ return ((fileSize)/chunkSize_) + 1;
+ } catch (Exception e) {
+ throw new TTransportException("Error getting file size.", __FILE__,
+ __LINE__, e);
+ }
+ }
+
+ ulong getCurChunk() {
+ return offset_ / chunkSize_;
+ }
+
+ void seekToChunk(long chunk) {
+ enforce(isOpen, new TTransportException(
+ "Cannot get number of chunks if file not open.",
+ TTransportException.Type.NOT_OPEN));
+
+ auto numChunks = getNumChunks();
+
+ if (chunk < 0) {
+ // Count negative indices from the end.
+ chunk += numChunks;
+ }
+
+ if (chunk < 0) {
+ logError("Incorrect chunk number for reverse seek, seeking to " ~
+ "beginning instead: %s", chunk);
+ chunk = 0;
+ }
+
+ bool seekToEnd;
+ long minEndOffset;
+ if (chunk >= numChunks) {
+ logError("Trying to seek to non-existing chunk, seeking to " ~
+ "end of file instead: %s", chunk);
+ seekToEnd = true;
+ chunk = numChunks - 1;
+ // this is the min offset to process events till
+ minEndOffset = file_.size();
+ }
+
+ readState_.resetAllValues();
+ currentEvent_ = null;
+
+ try {
+ file_.seek(chunk * chunkSize_);
+ offset_ = chunk * chunkSize_;
+ } catch (Exception e) {
+ throw new TTransportException("Error seeking to chunk", __FILE__,
+ __LINE__, e);
+ }
+
+ if (seekToEnd) {
+ // Never wait on the end of the file for new content, we just want to
+ // find the last one.
+ auto oldReadTimeout = readTimeout_;
+ scope (exit) readTimeout_ = oldReadTimeout;
+ readTimeout_ = dur!"hnsecs"(0);
+
+ // Keep on reading unti the last event at point of seekToChunk call.
+ while ((offset_ + readState_.bufferPos_) < minEndOffset) {
+ if (readEvent() is null) {
+ break;
+ }
+ }
+ }
+ }
+
+ void seekToEnd() {
+ seekToChunk(getNumChunks());
+ }
+
+ /**
+ * The size of the chunks the file is divided into, in bytes.
+ */
+ ulong chunkSize() @property const {
+ return chunkSize_;
+ }
+
+ /// ditto
+ void chunkSize(ulong value) @property {
+ enforce(!isOpen, new TTransportException(
+ "Cannot set chunk size after TFileReaderTransport has been opened."));
+ enforce(value > EventSize.sizeof, new TTransportException("Chunks must " ~
+ "be large enough to accommodate at least a single byte of payload data."));
+ chunkSize_ = value;
+ }
+
+ /**
+ * If positive, wait the specified duration for new data when arriving at
+ * end of file. If negative, wait forever (tailing mode), waking up to check
+ * in the specified interval. If zero, do not wait at all.
+ *
+ * Defaults to 500 ms.
+ */
+ Duration readTimeout() @property const {
+ return readTimeout_;
+ }
+
+ /// ditto
+ void readTimeout(Duration value) @property {
+ readTimeout_ = value;
+ }
+
+ /// ditto
+ enum DEFAULT_READ_TIMEOUT = dur!"msecs"(500);
+
+ /**
+ * Read buffer size, in bytes.
+ *
+ * Defaults to 1 MiB.
+ */
+ size_t readBufferSize() @property const {
+ return readBufferSize_;
+ }
+
+ /// ditto
+ void readBufferSize(size_t value) @property {
+ if (readBuffer_) {
+ enforce(value <= readBufferSize_,
+ "Cannot shrink read buffer after first read.");
+ readBuffer_.length = value;
+ }
+ readBufferSize_ = value;
+ }
+
+ /// ditto
+ enum DEFAULT_READ_BUFFER_SIZE = 1 * 1024 * 1024;
+
+ /**
+ * Arbitrary event size limit, in bytes. Must be smaller than chunk size.
+ *
+ * Defaults to zero (no limit).
+ */
+ size_t maxEventSize() @property const {
+ return maxEventSize_;
+ }
+
+ /// ditto
+ void maxEventSize(size_t value) @property {
+ enforce(value <= chunkSize_ - EventSize.sizeof, "Events cannot span " ~
+ "mutiple chunks, maxEventSize must be smaller than chunk size.");
+ maxEventSize_ = value;
+ }
+
+ /// ditto
+ enum DEFAULT_MAX_EVENT_SIZE = 0;
+
+ /**
+ * The interval at which the thread wakes up to check for the next chunk
+ * in tailing mode.
+ *
+ * Defaults to one second.
+ */
+ Duration corruptedEventSleepDuration() const {
+ return corruptedEventSleepDuration_;
+ }
+
+ /// ditto
+ void corruptedEventSleepDuration(Duration value) {
+ corruptedEventSleepDuration_ = value;
+ }
+
+ /// ditto
+ enum DEFAULT_CORRUPTED_EVENT_SLEEP_DURATION = dur!"seconds"(1);
+
+ /**
+ * The maximum number of corrupted events tolerated before the whole chunk
+ * is skipped.
+ *
+ * Defaults to zero.
+ */
+ uint maxCorruptedEvents() @property const {
+ return maxCorruptedEvents_;
+ }
+
+ /// ditto
+ void maxCorruptedEvents(uint value) @property {
+ maxCorruptedEvents_ = value;
+ }
+
+ /// ditto
+ enum DEFAULT_MAX_CORRUPTED_EVENTS = 0;
+
+private:
+ ubyte[] readEvent() {
+ if (!readBuffer_) {
+ readBuffer_ = new ubyte[readBufferSize_];
+ }
+
+ bool timeoutExpired;
+ while (1) {
+ // read from the file if read buffer is exhausted
+ if (readState_.bufferPos_ == readState_.bufferLen_) {
+ // advance the offset pointer
+ offset_ += readState_.bufferLen_;
+
+ try {
+ // Need to clear eof flag before reading, otherwise tailing a file
+ // does not work.
+ file_.clearerr();
+
+ auto usedBuf = file_.rawRead(readBuffer_);
+ readState_.bufferLen_ = usedBuf.length;
+ } catch (Exception e) {
+ readState_.resetAllValues();
+ throw new TTransportException("Error while reading from file",
+ __FILE__, __LINE__, e);
+ }
+
+ readState_.bufferPos_ = 0;
+ readState_.lastDispatchPos_ = 0;
+
+ if (readState_.bufferLen_ == 0) {
+ // Reached end of file.
+ if (readTimeout_ < dur!"hnsecs"(0)) {
+ // Tailing mode, sleep for the specified duration and try again.
+ Thread.sleep(-readTimeout_);
+ continue;
+ } else if (readTimeout_ == dur!"hnsecs"(0) || timeoutExpired) {
+ // Either no timeout set, or it has already expired.
+ readState_.resetState(0);
+ return null;
+ } else {
+ // Timeout mode, sleep for the specified amount of time and retry.
+ Thread.sleep(readTimeout_);
+ timeoutExpired = true;
+ continue;
+ }
+ }
+ }
+
+ // Attempt to read an event from the buffer.
+ while (readState_.bufferPos_ < readState_.bufferLen_) {
+ if (readState_.readingSize_) {
+ if (readState_.eventSizeBuffPos_ == 0) {
+ if ((offset_ + readState_.bufferPos_)/chunkSize_ !=
+ ((offset_ + readState_.bufferPos_ + 3)/chunkSize_))
+ {
+ readState_.bufferPos_++;
+ continue;
+ }
+ }
+
+ readState_.eventSizeBuff_[readState_.eventSizeBuffPos_++] =
+ readBuffer_[readState_.bufferPos_++];
+
+ if (readState_.eventSizeBuffPos_ == 4) {
+ auto size = (cast(uint[])readState_.eventSizeBuff_)[0];
+
+ if (size == 0) {
+ // This is part of the zero padding between chunks.
+ readState_.resetState(readState_.lastDispatchPos_);
+ continue;
+ }
+
+ // got a valid event
+ readState_.readingSize_ = false;
+ readState_.eventLen_ = size;
+ readState_.eventPos_ = 0;
+
+ // check if the event is corrupted and perform recovery if required
+ if (isEventCorrupted()) {
+ performRecovery();
+ // start from the top
+ break;
+ }
+ }
+ } else {
+ if (!readState_.event_) {
+ readState_.event_ = new ubyte[readState_.eventLen_];
+ }
+
+ // take either the entire event or the remaining bytes in the buffer
+ auto reclaimBuffer = min(readState_.bufferLen_ - readState_.bufferPos_,
+ readState_.eventLen_ - readState_.eventPos_);
+
+ // copy data from read buffer into event buffer
+ readState_.event_[
+ readState_.eventPos_ .. readState_.eventPos_ + reclaimBuffer
+ ] = readBuffer_[
+ readState_.bufferPos_ .. readState_.bufferPos_ + reclaimBuffer
+ ];
+
+ // increment position ptrs
+ readState_.eventPos_ += reclaimBuffer;
+ readState_.bufferPos_ += reclaimBuffer;
+
+ // check if the event has been read in full
+ if (readState_.eventPos_ == readState_.eventLen_) {
+ // Reset the read state and return the completed event.
+ auto completeEvent = readState_.event_;
+ readState_.event_ = null;
+ readState_.resetState(readState_.bufferPos_);
+ return completeEvent;
+ }
+ }
+ }
+ }
+ }
+
+ bool isEventCorrupted() {
+ if ((maxEventSize_ > 0) && (readState_.eventLen_ > maxEventSize_)) {
+ // Event size is larger than user-speficied max-event size
+ logError("Corrupt event read: Event size (%s) greater than max " ~
+ "event size (%s)", readState_.eventLen_, maxEventSize_);
+ return true;
+ } else if (readState_.eventLen_ > chunkSize_) {
+ // Event size is larger than chunk size
+ logError("Corrupt event read: Event size (%s) greater than chunk " ~
+ "size (%s)", readState_.eventLen_, chunkSize_);
+ return true;
+ } else if (((offset_ + readState_.bufferPos_ - EventSize.sizeof) / chunkSize_) !=
+ ((offset_ + readState_.bufferPos_ + readState_.eventLen_ - EventSize.sizeof) / chunkSize_))
+ {
+ // Size indicates that event crosses chunk boundary
+ logError("Read corrupt event. Event crosses chunk boundary. " ~
+ "Event size: %s. Offset: %s", readState_.eventLen_,
+ (offset_ + readState_.bufferPos_ + EventSize.sizeof)
+ );
+
+ return true;
+ }
+
+ return false;
+ }
+
+ void performRecovery() {
+ // perform some kickass recovery
+ auto curChunk = getCurChunk();
+ if (lastBadChunk_ == curChunk) {
+ numCorruptedEventsInChunk_++;
+ } else {
+ lastBadChunk_ = curChunk;
+ numCorruptedEventsInChunk_ = 1;
+ }
+
+ if (numCorruptedEventsInChunk_ < maxCorruptedEvents_) {
+ // maybe there was an error in reading the file from disk
+ // seek to the beginning of chunk and try again
+ seekToChunk(curChunk);
+ } else {
+ // Just skip ahead to the next chunk if we not already at the last chunk.
+ if (curChunk != (getNumChunks() - 1)) {
+ seekToChunk(curChunk + 1);
+ } else if (readTimeout_ < dur!"hnsecs"(0)) {
+ // We are in tailing mode, wait until there is enough data to start
+ // the next chunk.
+ while(curChunk == (getNumChunks() - 1)) {
+ Thread.sleep(corruptedEventSleepDuration_);
+ }
+ seekToChunk(curChunk + 1);
+ } else {
+ // Pretty hosed at this stage, rewind the file back to the last
+ // successful point and punt on the error.
+ readState_.resetState(readState_.lastDispatchPos_);
+ currentEvent_ = null;
+ currentEventPos_ = 0;
+
+ throw new TTransportException("File corrupted at offset: " ~
+ to!string(offset_ + readState_.lastDispatchPos_),
+ TTransportException.Type.CORRUPTED_DATA);
+ }
+ }
+ }
+
+ string path_;
+ File file_;
+ bool isOpen_;
+ long offset_;
+ ubyte[] currentEvent_;
+ size_t currentEventPos_;
+ ulong chunkSize_;
+ Duration readTimeout_;
+ size_t maxEventSize_;
+
+ // Read buffer – lazily allocated on the first read().
+ ubyte[] readBuffer_;
+ size_t readBufferSize_;
+
+ static struct ReadState {
+ ubyte[] event_;
+ size_t eventLen_;
+ size_t eventPos_;
+
+ // keep track of event size
+ ubyte[4] eventSizeBuff_;
+ ubyte eventSizeBuffPos_;
+ bool readingSize_ = true;
+
+ // read buffer variables
+ size_t bufferPos_;
+ size_t bufferLen_;
+
+ // last successful dispatch point
+ size_t lastDispatchPos_;
+
+ void resetState(size_t lastDispatchPos) {
+ readingSize_ = true;
+ eventSizeBuffPos_ = 0;
+ lastDispatchPos_ = lastDispatchPos;
+ }
+
+ void resetAllValues() {
+ resetState(0);
+ bufferPos_ = 0;
+ bufferLen_ = 0;
+ event_ = null;
+ }
+ }
+ ReadState readState_;
+
+ ulong lastBadChunk_;
+ uint maxCorruptedEvents_;
+ uint numCorruptedEventsInChunk_;
+ Duration corruptedEventSleepDuration_;
+}
+
+/**
+ * A transport used to write log files. It can never be read from, calling
+ * read() throws.
+ *
+ * Contrary to the C++ design, explicitly opening the transport/file before
+ * using is necessary to allow manually closing the file without relying on the
+ * object lifetime.
+ */
+final class TFileWriterTransport : TBaseTransport {
+ /**
+ * Creates a new file writer transport.
+ *
+ * Params:
+ * path = Path of the file to opperate on.
+ */
+ this(string path) {
+ path_ = path;
+
+ chunkSize_ = DEFAULT_CHUNK_SIZE;
+ eventBufferSize_ = DEFAULT_EVENT_BUFFER_SIZE;
+ ioErrorSleepDuration = DEFAULT_IO_ERROR_SLEEP_DURATION;
+ maxFlushBytes_ = DEFAULT_MAX_FLUSH_BYTES;
+ maxFlushInterval_ = DEFAULT_MAX_FLUSH_INTERVAL;
+ }
+
+ override bool isOpen() @property {
+ return isOpen_;
+ }
+
+ /**
+ * A file writer transport can never be read from.
+ */
+ override bool peek() {
+ return false;
+ }
+
+ override void open() {
+ if (isOpen) return;
+
+ writerThread_ = spawn(
+ &writerThread,
+ path_,
+ chunkSize_,
+ maxFlushBytes_,
+ maxFlushInterval_,
+ ioErrorSleepDuration_
+ );
+ setMaxMailboxSize(writerThread_, eventBufferSize_, OnCrowding.block);
+ isOpen_ = true;
+ }
+
+ /**
+ * Closes the transport, i.e. the underlying file and the writer thread.
+ */
+ override void close() {
+ if (!isOpen) return;
+
+ send(writerThread_, ShutdownMessage(), thisTid);
+ receive((ShutdownMessage msg, Tid tid){});
+ isOpen_ = false;
+ }
+
+ /**
+ * Enqueues the passed slice of data for writing and immediately returns.
+ * write() only blocks if the event buffer has been exhausted.
+ *
+ * The transport must be open when calling this.
+ *
+ * Params:
+ * buf = Slice of data to write.
+ */
+ override void write(in ubyte[] buf) {
+ enforce(isOpen, new TTransportException(
+ "Cannot write to non-open file.", TTransportException.Type.NOT_OPEN));
+
+ if (buf.empty) {
+ logError("Cannot write empty event, skipping.");
+ return;
+ }
+
+ auto maxSize = chunkSize - EventSize.sizeof;
+ enforce(buf.length <= maxSize, new TTransportException(
+ "Cannot write more than " ~ to!string(maxSize) ~
+ "bytes at once due to chunk size."));
+
+ send(writerThread_, buf.idup);
+ }
+
+ /**
+ * Flushes any pending data to be written.
+ *
+ * The transport must be open when calling this.
+ *
+ * Throws: TTransportException if an error occurs.
+ */
+ override void flush() {
+ enforce(isOpen, new TTransportException(
+ "Cannot flush file if not open.", TTransportException.Type.NOT_OPEN));
+
+ send(writerThread_, FlushMessage(), thisTid);
+ receive((FlushMessage msg, Tid tid){});
+ }
+
+ /**
+ * The size of the chunks the file is divided into, in bytes.
+ *
+ * A single event (write call) never spans multiple chunks – this
+ * effectively limits the event size to chunkSize - EventSize.sizeof.
+ */
+ ulong chunkSize() @property {
+ return chunkSize_;
+ }
+
+ /// ditto
+ void chunkSize(ulong value) @property {
+ enforce(!isOpen, new TTransportException(
+ "Cannot set chunk size after TFileWriterTransport has been opened."));
+ chunkSize_ = value;
+ }
+
+ /**
+ * The maximum number of write() calls buffered, or zero for no limit.
+ *
+ * If the buffer is exhausted, write() will block until space becomes
+ * available.
+ */
+ size_t eventBufferSize() @property {
+ return eventBufferSize_;
+ }
+
+ /// ditto
+ void eventBufferSize(size_t value) @property {
+ eventBufferSize_ = value;
+ if (isOpen) {
+ setMaxMailboxSize(writerThread_, value, OnCrowding.throwException);
+ }
+ }
+
+ /// ditto
+ enum DEFAULT_EVENT_BUFFER_SIZE = 10_000;
+
+ /**
+ * Maximum number of bytes buffered before writing and flushing the file
+ * to disk.
+ *
+ * Currently cannot be set after the first call to write().
+ */
+ size_t maxFlushBytes() @property {
+ return maxFlushBytes_;
+ }
+
+ /// ditto
+ void maxFlushBytes(size_t value) @property {
+ maxFlushBytes_ = value;
+ if (isOpen) {
+ send(writerThread_, FlushBytesMessage(value));
+ }
+ }
+
+ /// ditto
+ enum DEFAULT_MAX_FLUSH_BYTES = 1000 * 1024;
+
+ /**
+ * Maximum interval between flushing the file to disk.
+ *
+ * Currenlty cannot be set after the first call to write().
+ */
+ Duration maxFlushInterval() @property {
+ return maxFlushInterval_;
+ }
+
+ /// ditto
+ void maxFlushInterval(Duration value) @property {
+ maxFlushInterval_ = value;
+ if (isOpen) {
+ send(writerThread_, FlushIntervalMessage(value));
+ }
+ }
+
+ /// ditto
+ enum DEFAULT_MAX_FLUSH_INTERVAL = dur!"seconds"(3);
+
+ /**
+ * When the writer thread encounteres an I/O error, it goes pauses for a
+ * short time before trying to reopen the output file. This controls the
+ * sleep duration.
+ */
+ Duration ioErrorSleepDuration() @property {
+ return ioErrorSleepDuration_;
+ }
+
+ /// ditto
+ void ioErrorSleepDuration(Duration value) @property {
+ ioErrorSleepDuration_ = value;
+ if (isOpen) {
+ send(writerThread_, FlushIntervalMessage(value));
+ }
+ }
+
+ /// ditto
+ enum DEFAULT_IO_ERROR_SLEEP_DURATION = dur!"msecs"(500);
+
+private:
+ string path_;
+ ulong chunkSize_;
+ size_t eventBufferSize_;
+ Duration ioErrorSleepDuration_;
+ size_t maxFlushBytes_;
+ Duration maxFlushInterval_;
+ bool isOpen_;
+ Tid writerThread_;
+}
+
+private {
+ // Signals that the file should be flushed on disk. Sent to the writer
+ // thread and sent back along with the tid for confirmation.
+ struct FlushMessage {}
+
+ // Signals that the writer thread should close the file and shut down. Sent
+ // to the writer thread and sent back along with the tid for confirmation.
+ struct ShutdownMessage {}
+
+ struct FlushBytesMessage {
+ size_t value;
+ }
+
+ struct FlushIntervalMessage {
+ Duration value;
+ }
+
+ struct IoErrorSleepDurationMessage {
+ Duration value;
+ }
+
+ void writerThread(
+ string path,
+ ulong chunkSize,
+ size_t maxFlushBytes,
+ Duration maxFlushInterval,
+ Duration ioErrorSleepDuration
+ ) {
+ bool errorOpening;
+ File file;
+ ulong offset;
+ try {
+ // Open file in appending and binary mode.
+ file = File(path, "ab");
+ offset = file.tell();
+ } catch (Exception e) {
+ logError("Error on opening output file in writer thread: %s", e);
+ errorOpening = true;
+ }
+
+ auto flushTimer = StopWatch(AutoStart.yes);
+ size_t unflushedByteCount;
+
+ Tid shutdownRequestTid;
+ bool shutdownRequested;
+ while (true) {
+ if (shutdownRequested) break;
+
+ bool forceFlush;
+ Tid flushRequestTid;
+ receiveTimeout(max(dur!"hnsecs"(0), maxFlushInterval - flushTimer.peek()),
+ (immutable(ubyte)[] data) {
+ while (errorOpening) {
+ logError("Writer thread going to sleep for %s µs due to IO errors",
+ ioErrorSleepDuration.total!"usecs");
+
+ // Sleep for ioErrorSleepDuration, being ready to be interrupted
+ // by shutdown requests.
+ auto timedOut = receiveTimeout(ioErrorSleepDuration,
+ (ShutdownMessage msg, Tid tid){ shutdownRequestTid = tid; });
+ if (!timedOut) {
+ // We got a shutdown request, just drop all events and exit the
+ // main loop as to not block application shutdown with our tries
+ // which we must assume to fail.
+ break;
+ }
+
+ try {
+ file = File(path, "ab");
+ unflushedByteCount = 0;
+ errorOpening = false;
+ logError("Output file %s reopened during writer thread error " ~
+ "recovery", path);
+ } catch (Exception e) {
+ logError("Unable to reopen output file %s during writer " ~
+ "thread error recovery", path);
+ }
+ }
+
+ // Make sure the event does not cross the chunk boundary by writing
+ // a padding consisting of zeroes if it would.
+ auto chunk1 = offset / chunkSize;
+ auto chunk2 = (offset + EventSize.sizeof + data.length - 1) / chunkSize;
+
+ if (chunk1 != chunk2) {
+ // TODO: The C++ implementation refetches the offset here to »keep
+ // in sync« – why would this be needed?
+ auto padding = cast(size_t)
+ ((((offset / chunkSize) + 1) * chunkSize) - offset);
+ auto zeroes = new ubyte[padding];
+ file.rawWrite(zeroes);
+ unflushedByteCount += padding;
+ offset += padding;
+ }
+
+ // TODO: 2 syscalls here, is this a problem performance-wise?
+ // Probably abysmal performance on Windows due to rawWrite
+ // implementation.
+ uint len = cast(uint)data.length;
+ file.rawWrite(cast(ubyte[])(&len)[0..1]);
+ file.rawWrite(data);
+
+ auto bytesWritten = EventSize.sizeof + data.length;
+ unflushedByteCount += bytesWritten;
+ offset += bytesWritten;
+ }, (FlushBytesMessage msg) {
+ maxFlushBytes = msg.value;
+ }, (FlushIntervalMessage msg) {
+ maxFlushInterval = msg.value;
+ }, (IoErrorSleepDurationMessage msg) {
+ ioErrorSleepDuration = msg.value;
+ }, (FlushMessage msg, Tid tid) {
+ forceFlush = true;
+ flushRequestTid = tid;
+ }, (OwnerTerminated msg) {
+ shutdownRequested = true;
+ }, (ShutdownMessage msg, Tid tid) {
+ shutdownRequested = true;
+ shutdownRequestTid = tid;
+ }
+ );
+
+ if (errorOpening) continue;
+
+ bool flush;
+ if (forceFlush || shutdownRequested || unflushedByteCount > maxFlushBytes) {
+ flush = true;
+ } else if (cast(Duration)flushTimer.peek() > maxFlushInterval) {
+ if (unflushedByteCount == 0) {
+ // If the flush timer is due, but no data has been written, don't
+ // needlessly fsync, but do reset the timer.
+ flushTimer.reset();
+ } else {
+ flush = true;
+ }
+ }
+
+ if (flush) {
+ file.flush();
+ flushTimer.reset();
+ unflushedByteCount = 0;
+ if (forceFlush) send(flushRequestTid, FlushMessage(), thisTid);
+ }
+ }
+
+ file.close();
+
+ if (shutdownRequestTid != Tid.init) {
+ send(shutdownRequestTid, ShutdownMessage(), thisTid);
+ }
+ }
+}
+
+version (unittest) {
+ import core.memory : GC;
+ import std.file;
+}
+
+unittest {
+ void tryRemove(string fileName) {
+ try {
+ remove(fileName);
+ } catch (Exception) {}
+ }
+
+ immutable fileName = "unittest.dat.tmp";
+ enforce(!exists(fileName), "Unit test output file " ~ fileName ~
+ " already exists.");
+
+ /*
+ * Check the most basic reading/writing operations.
+ */
+ {
+ scope (exit) tryRemove(fileName);
+
+ auto writer = new TFileWriterTransport(fileName);
+ writer.open();
+ scope (exit) writer.close();
+
+ writer.write([1, 2]);
+ writer.write([3, 4]);
+ writer.write([5, 6, 7]);
+ writer.flush();
+
+ auto reader = new TFileReaderTransport(fileName);
+ reader.open();
+ scope (exit) reader.close();
+
+ auto buf = new ubyte[7];
+ reader.readAll(buf);
+ enforce(buf == [1, 2, 3, 4, 5, 6, 7]);
+ }
+
+ /*
+ * Check that chunking works as expected.
+ */
+ {
+ scope (exit) tryRemove(fileName);
+
+ static assert(EventSize.sizeof == 4);
+ enum CHUNK_SIZE = 10;
+
+ // Write some contents to the file.
+ {
+ auto writer = new TFileWriterTransport(fileName);
+ writer.chunkSize = CHUNK_SIZE;
+ writer.open();
+ scope (exit) writer.close();
+
+ writer.write([0xde]);
+ writer.write([0xad]);
+ // Chunk boundary here.
+ writer.write([0xbe]);
+ // The next write doesn't fit in the five bytes remaining, so we expect
+ // padding zero bytes to be written.
+ writer.write([0xef, 0x12]);
+
+ try {
+ writer.write(new ubyte[CHUNK_SIZE]);
+ enforce(false, "Could write event not fitting in a single chunk.");
+ } catch (TTransportException e) {}
+
+ writer.flush();
+ }
+
+ // Check the raw contents of the file to see if chunk padding was written
+ // as expected.
+ auto file = File(fileName, "r");
+ enforce(file.size == 26);
+ auto written = new ubyte[26];
+ file.rawRead(written);
+ enforce(written == [
+ 1, 0, 0, 0, 0xde,
+ 1, 0, 0, 0, 0xad,
+ 1, 0, 0, 0, 0xbe,
+ 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 0xef, 0x12
+ ]);
+
+ // Read the data back in, getting all the events at once.
+ {
+ auto reader = new TFileReaderTransport(fileName);
+ reader.chunkSize = CHUNK_SIZE;
+ reader.open();
+ scope (exit) reader.close();
+
+ auto buf = new ubyte[5];
+ reader.readAll(buf);
+ enforce(buf == [0xde, 0xad, 0xbe, 0xef, 0x12]);
+ }
+ }
+
+ /*
+ * Make sure that close() exits "quickly", i.e. that there is no problem
+ * with the worker thread waking up.
+ */
+ {
+ import std.conv : text;
+ enum NUM_ITERATIONS = 1000;
+
+ uint numOver = 0;
+ foreach (n; 0 .. NUM_ITERATIONS) {
+ scope (exit) tryRemove(fileName);
+
+ auto transport = new TFileWriterTransport(fileName);
+ transport.open();
+
+ // Write something so that the writer thread gets started.
+ transport.write(cast(ubyte[])"foo");
+
+ // Every other iteration, also call flush(), just in case that potentially
+ // has any effect on how the writer thread wakes up.
+ if (n & 0x1) {
+ transport.flush();
+ }
+
+ // Time the call to close().
+ auto sw = StopWatch(AutoStart.yes);
+ transport.close();
+ sw.stop();
+
+ // If any attempt takes more than 500ms, treat that as a fatal failure to
+ // avoid looping over a potentially very slow operation.
+ enforce(sw.peek().total!"msecs" < 1500,
+ text("close() took ", sw.peek().total!"msecs", "ms."));
+
+ // Normally, it takes less than 5ms on my dev box.
+ // However, if the box is heavily loaded, some of the test runs can take
+ // longer. Additionally, on a Windows Server 2008 instance running in
+ // a VirtualBox VM, it has been observed that about a quarter of the runs
+ // takes (217 ± 1) ms, for reasons not yet known.
+ if (sw.peek().total!"msecs" > 50) {
+ ++numOver;
+ }
+
+ // Force garbage collection runs every now and then to make sure we
+ // don't run out of OS thread handles.
+ if (!(n % 100)) GC.collect();
+ }
+
+ // Make sure fewer than a third of the runs took longer than 5ms.
+ enforce(numOver < NUM_ITERATIONS / 3,
+ text(numOver, " iterations took more than 10 ms."));
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/transport/framed.d b/src/jaegertracing/thrift/lib/d/src/thrift/transport/framed.d
new file mode 100644
index 000000000..94effbbaf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/transport/framed.d
@@ -0,0 +1,334 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+module thrift.transport.framed;
+
+import core.bitop : bswap;
+import std.algorithm : min;
+import std.array : empty;
+import std.exception : enforce;
+import thrift.transport.base;
+
+/**
+ * Framed transport.
+ *
+ * All writes go into an in-memory buffer until flush is called, at which point
+ * the transport writes the length of the entire binary chunk followed by the
+ * data payload. The receiver on the other end then performs a single
+ * »fixed-length« read to get the whole message off the wire.
+ */
+final class TFramedTransport : TBaseTransport {
+ /**
+ * Constructs a new framed transport.
+ *
+ * Params:
+ * transport = The underlying transport to wrap.
+ */
+ this(TTransport transport) {
+ transport_ = transport;
+ }
+
+ /**
+ * Returns the wrapped transport.
+ */
+ TTransport underlyingTransport() @property {
+ return transport_;
+ }
+
+ override bool isOpen() @property {
+ return transport_.isOpen;
+ }
+
+ override bool peek() {
+ return rBuf_.length > 0 || transport_.peek();
+ }
+
+ override void open() {
+ transport_.open();
+ }
+
+ override void close() {
+ flush();
+ transport_.close();
+ }
+
+ /**
+ * Attempts to read data into the given buffer, stopping when the buffer is
+ * exhausted or the frame end is reached.
+ *
+ * TODO: Contrary to the C++ implementation, this never does cross-frame
+ * reads – is there actually a valid use case for that?
+ *
+ * Params:
+ * buf = Slice to use as buffer.
+ *
+ * Returns: How many bytes were actually read.
+ *
+ * Throws: TTransportException if an error occurs.
+ */
+ override size_t read(ubyte[] buf) {
+ // If the buffer is empty, read a new frame off the wire.
+ if (rBuf_.empty) {
+ bool gotFrame = readFrame();
+ if (!gotFrame) return 0;
+ }
+
+ auto size = min(rBuf_.length, buf.length);
+ buf[0..size] = rBuf_[0..size];
+ rBuf_ = rBuf_[size..$];
+ return size;
+ }
+
+ override void write(in ubyte[] buf) {
+ wBuf_ ~= buf;
+ }
+
+ override void flush() {
+ if (wBuf_.empty) return;
+
+ // Properly reset the write buffer even some of the protocol operations go
+ // wrong.
+ scope (exit) {
+ wBuf_.length = 0;
+ wBuf_.assumeSafeAppend();
+ }
+
+ int len = bswap(cast(int)wBuf_.length);
+ transport_.write(cast(ubyte[])(&len)[0..1]);
+ transport_.write(wBuf_);
+ transport_.flush();
+ }
+
+ override const(ubyte)[] borrow(ubyte* buf, size_t len) {
+ if (len <= rBuf_.length) {
+ return rBuf_;
+ } else {
+ // Don't try attempting cross-frame borrows, trying that does not make
+ // much sense anyway.
+ return null;
+ }
+ }
+
+ override void consume(size_t len) {
+ enforce(len <= rBuf_.length, new TTransportException(
+ "Invalid consume length", TTransportException.Type.BAD_ARGS));
+ rBuf_ = rBuf_[len .. $];
+ }
+
+private:
+ bool readFrame() {
+ // Read the size of the next frame. We can't use readAll() since that
+ // always throws an exception on EOF, but want to throw an exception only
+ // if EOF occurs after partial size data.
+ int size;
+ size_t size_read;
+ while (size_read < size.sizeof) {
+ auto data = (cast(ubyte*)&size)[size_read..size.sizeof];
+ auto read = transport_.read(data);
+ if (read == 0) {
+ if (size_read == 0) {
+ // EOF before any data was read.
+ return false;
+ } else {
+ // EOF after a partial frame header – illegal.
+ throw new TTransportException(
+ "No more data to read after partial frame header",
+ TTransportException.Type.END_OF_FILE
+ );
+ }
+ }
+ size_read += read;
+ }
+
+ size = bswap(size);
+ enforce(size >= 0, new TTransportException("Frame size has negative value",
+ TTransportException.Type.CORRUPTED_DATA));
+
+ // TODO: Benchmark this.
+ rBuf_.length = size;
+ rBuf_.assumeSafeAppend();
+
+ transport_.readAll(rBuf_);
+ return true;
+ }
+
+ TTransport transport_;
+ ubyte[] rBuf_;
+ ubyte[] wBuf_;
+}
+
+/**
+ * Wraps given transports into TFramedTransports.
+ */
+alias TWrapperTransportFactory!TFramedTransport TFramedTransportFactory;
+
+version (unittest) {
+ import std.random : Mt19937, uniform;
+ import thrift.transport.memory;
+}
+
+// Some basic random testing, always starting with the same seed for
+// deterministic unit test results – more tests in transport_test.
+unittest {
+ auto randGen = Mt19937(42);
+
+ // 32 kiB of data to work with.
+ auto data = new ubyte[1 << 15];
+ foreach (ref b; data) {
+ b = uniform!"[]"(cast(ubyte)0, cast(ubyte)255, randGen);
+ }
+
+ // Generate a list of chunk sizes to split the data into. A uniform
+ // distribution is not quite realistic, but std.random doesn't have anything
+ // else yet.
+ enum MAX_FRAME_LENGTH = 512;
+ auto chunkSizesList = new size_t[][2];
+ foreach (ref chunkSizes; chunkSizesList) {
+ size_t sum;
+ while (true) {
+ auto curLen = uniform(0, MAX_FRAME_LENGTH, randGen);
+ sum += curLen;
+ if (sum > data.length) break;
+ chunkSizes ~= curLen;
+ }
+ }
+ chunkSizesList ~= [data.length]; // Also test whole chunk at once.
+
+ // Test writing data.
+ {
+ foreach (chunkSizes; chunkSizesList) {
+ auto buf = new TMemoryBuffer;
+ auto framed = new TFramedTransport(buf);
+
+ auto remainingData = data;
+ foreach (chunkSize; chunkSizes) {
+ framed.write(remainingData[0..chunkSize]);
+ remainingData = remainingData[chunkSize..$];
+ }
+ framed.flush();
+
+ auto writtenData = data[0..($ - remainingData.length)];
+ auto actualData = buf.getContents();
+
+ // Check frame size.
+ int frameSize = bswap((cast(int[])(actualData[0..int.sizeof]))[0]);
+ enforce(frameSize == writtenData.length);
+
+ // Check actual data.
+ enforce(actualData[int.sizeof..$] == writtenData);
+ }
+ }
+
+ // Test reading data.
+ {
+ foreach (chunkSizes; chunkSizesList) {
+ auto buf = new TMemoryBuffer;
+
+ auto size = bswap(cast(int)data.length);
+ buf.write(cast(ubyte[])(&size)[0..1]);
+ buf.write(data);
+
+ auto framed = new TFramedTransport(buf);
+ ubyte[] readData;
+ readData.reserve(data.length);
+ foreach (chunkSize; chunkSizes) {
+ // This should work with read because we have one huge frame.
+ auto oldReadLen = readData.length;
+ readData.length += chunkSize;
+ framed.read(readData[oldReadLen..$]);
+ }
+
+ enforce(readData == data[0..readData.length]);
+ }
+ }
+
+ // Test combined reading/writing of multiple frames.
+ foreach (flushProbability; [1, 2, 4, 8, 16, 32]) {
+ foreach (chunkSizes; chunkSizesList) {
+ auto buf = new TMemoryBuffer;
+ auto framed = new TFramedTransport(buf);
+
+ size_t[] frameSizes;
+
+ // Write the data.
+ size_t frameSize;
+ auto remainingData = data;
+ foreach (chunkSize; chunkSizes) {
+ framed.write(remainingData[0..chunkSize]);
+ remainingData = remainingData[chunkSize..$];
+
+ frameSize += chunkSize;
+ if (frameSize > 0 && uniform(0, flushProbability, randGen) == 0) {
+ frameSizes ~= frameSize;
+ frameSize = 0;
+ framed.flush();
+ }
+ }
+ if (frameSize > 0) {
+ frameSizes ~= frameSize;
+ frameSize = 0;
+ framed.flush();
+ }
+
+ // Read it back.
+ auto readData = new ubyte[data.length - remainingData.length];
+ auto remainToRead = readData;
+ foreach (fSize; frameSizes) {
+ // We are exploiting an implementation detail of TFramedTransport:
+ // The read buffer starts empty and it will never return more than one
+ // frame per read, so by just requesting all of the data, we should
+ // always get exactly one frame.
+ auto got = framed.read(remainToRead);
+ enforce(got == fSize);
+ remainToRead = remainToRead[fSize..$];
+ }
+
+ enforce(remainToRead.empty);
+ enforce(readData == data[0..readData.length]);
+ }
+ }
+}
+
+// Test flush()ing an empty buffer.
+unittest {
+ auto buf = new TMemoryBuffer();
+ auto framed = new TFramedTransport(buf);
+ immutable out1 = [0, 0, 0, 1, 'a'];
+ immutable out2 = [0, 0, 0, 1, 'a', 0, 0, 0, 2, 'b', 'c'];
+
+ framed.flush();
+ enforce(buf.getContents() == []);
+ framed.flush();
+ framed.flush();
+ enforce(buf.getContents() == []);
+ framed.write(cast(ubyte[])"a");
+ enforce(buf.getContents() == []);
+ framed.flush();
+ enforce(buf.getContents() == out1);
+ framed.flush();
+ framed.flush();
+ enforce(buf.getContents() == out1);
+ framed.write(cast(ubyte[])"bc");
+ enforce(buf.getContents() == out1);
+ framed.flush();
+ enforce(buf.getContents() == out2);
+ framed.flush();
+ framed.flush();
+ enforce(buf.getContents() == out2);
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/transport/http.d b/src/jaegertracing/thrift/lib/d/src/thrift/transport/http.d
new file mode 100644
index 000000000..0e58deeb6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/transport/http.d
@@ -0,0 +1,459 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * HTTP tranpsort implementation, modelled after the C++ one.
+ *
+ * Unfortunately, libcurl is quite heavyweight and supports only client-side
+ * applications. This is an implementation of the basic HTTP/1.1 parts
+ * supporting HTTP 100 Continue, chunked transfer encoding, keepalive, etc.
+ */
+module thrift.transport.http;
+
+import std.algorithm : canFind, countUntil, endsWith, findSplit, min, startsWith;
+import std.ascii : toLower;
+import std.array : empty;
+import std.conv : parse, to;
+import std.datetime : Clock, UTC;
+import std.string : stripLeft;
+import thrift.base : VERSION;
+import thrift.transport.base;
+import thrift.transport.memory;
+import thrift.transport.socket;
+
+/**
+ * Base class for both client- and server-side HTTP transports.
+ */
+abstract class THttpTransport : TBaseTransport {
+ this(TTransport transport) {
+ transport_ = transport;
+ readHeaders_ = true;
+ httpBuf_ = new ubyte[HTTP_BUFFER_SIZE];
+ httpBufRemaining_ = httpBuf_[0 .. 0];
+ readBuffer_ = new TMemoryBuffer;
+ writeBuffer_ = new TMemoryBuffer;
+ }
+
+ override bool isOpen() {
+ return transport_.isOpen();
+ }
+
+ override bool peek() {
+ return transport_.peek();
+ }
+
+ override void open() {
+ transport_.open();
+ }
+
+ override void close() {
+ transport_.close();
+ }
+
+ override size_t read(ubyte[] buf) {
+ if (!readBuffer_.peek()) {
+ readBuffer_.reset();
+
+ if (!refill()) return 0;
+
+ if (readHeaders_) {
+ readHeaders();
+ }
+
+ size_t got;
+ if (chunked_) {
+ got = readChunked();
+ } else {
+ got = readContent(contentLength_);
+ }
+ readHeaders_ = true;
+
+ if (got == 0) return 0;
+ }
+ return readBuffer_.read(buf);
+ }
+
+ override size_t readEnd() {
+ // Read any pending chunked data (footers etc.)
+ if (chunked_) {
+ while (!chunkedDone_) {
+ readChunked();
+ }
+ }
+ return 0;
+ }
+
+ override void write(in ubyte[] buf) {
+ writeBuffer_.write(buf);
+ }
+
+ override void flush() {
+ auto data = writeBuffer_.getContents();
+ string header = getHeader(data.length);
+
+ transport_.write(cast(const(ubyte)[]) header);
+ transport_.write(data);
+ transport_.flush();
+
+ // Reset the buffer and header variables.
+ writeBuffer_.reset();
+ readHeaders_ = true;
+ }
+
+ /**
+ * The size of the buffer to read HTTP requests into, in bytes. Will expand
+ * as required.
+ */
+ enum HTTP_BUFFER_SIZE = 1024;
+
+protected:
+ abstract string getHeader(size_t dataLength);
+ abstract bool parseStatusLine(const(ubyte)[] status);
+
+ void parseHeader(const(ubyte)[] header) {
+ auto split = findSplit(header, [':']);
+ if (split[1].empty) {
+ // No colon found.
+ return;
+ }
+
+ static bool compToLower(ubyte a, ubyte b) {
+ return toLower(cast(char)a) == toLower(cast(char)b);
+ }
+
+ if (startsWith!compToLower(split[0], cast(ubyte[])"transfer-encoding")) {
+ if (endsWith!compToLower(split[2], cast(ubyte[])"chunked")) {
+ chunked_ = true;
+ }
+ } else if (startsWith!compToLower(split[0], cast(ubyte[])"content-length")) {
+ chunked_ = false;
+ auto lengthString = stripLeft(cast(const(char)[])split[2]);
+ contentLength_ = parse!size_t(lengthString);
+ }
+ }
+
+private:
+ ubyte[] readLine() {
+ while (true) {
+ auto split = findSplit(httpBufRemaining_, cast(ubyte[])"\r\n");
+
+ if (split[1].empty) {
+ // No CRLF yet, move whatever we have now to front and refill.
+ if (httpBufRemaining_.empty) {
+ httpBufRemaining_ = httpBuf_[0 .. 0];
+ } else {
+ httpBuf_[0 .. httpBufRemaining_.length] = httpBufRemaining_;
+ httpBufRemaining_ = httpBuf_[0 .. httpBufRemaining_.length];
+ }
+
+ if (!refill()) {
+ auto buf = httpBufRemaining_;
+ httpBufRemaining_ = httpBufRemaining_[$ - 1 .. $ - 1];
+ return buf;
+ }
+ } else {
+ // Set the remaining buffer to the part after \r\n and return the part
+ // (line) before it.
+ httpBufRemaining_ = split[2];
+ return split[0];
+ }
+ }
+ }
+
+ void readHeaders() {
+ // Initialize headers state variables
+ contentLength_ = 0;
+ chunked_ = false;
+ chunkedDone_ = false;
+ chunkSize_ = 0;
+
+ // Control state flow
+ bool statusLine = true;
+ bool finished;
+
+ // Loop until headers are finished
+ while (true) {
+ auto line = readLine();
+
+ if (line.length == 0) {
+ if (finished) {
+ readHeaders_ = false;
+ return;
+ } else {
+ // Must have been an HTTP 100, keep going for another status line
+ statusLine = true;
+ }
+ } else {
+ if (statusLine) {
+ statusLine = false;
+ finished = parseStatusLine(line);
+ } else {
+ parseHeader(line);
+ }
+ }
+ }
+ }
+
+ size_t readChunked() {
+ size_t length;
+
+ auto line = readLine();
+ size_t chunkSize;
+ try {
+ auto charLine = cast(char[])line;
+ chunkSize = parse!size_t(charLine, 16);
+ } catch (Exception e) {
+ throw new TTransportException("Invalid chunk size: " ~ to!string(line),
+ TTransportException.Type.CORRUPTED_DATA);
+ }
+
+ if (chunkSize == 0) {
+ readChunkedFooters();
+ } else {
+ // Read data content
+ length += readContent(chunkSize);
+ // Read trailing CRLF after content
+ readLine();
+ }
+ return length;
+ }
+
+ void readChunkedFooters() {
+ while (true) {
+ auto line = readLine();
+ if (line.length == 0) {
+ chunkedDone_ = true;
+ break;
+ }
+ }
+ }
+
+ size_t readContent(size_t size) {
+ auto need = size;
+ while (need > 0) {
+ if (httpBufRemaining_.length == 0) {
+ // We have given all the data, reset position to head of the buffer.
+ httpBufRemaining_ = httpBuf_[0 .. 0];
+ if (!refill()) return size - need;
+ }
+
+ auto give = min(httpBufRemaining_.length, need);
+ readBuffer_.write(cast(ubyte[])httpBufRemaining_[0 .. give]);
+ httpBufRemaining_ = httpBufRemaining_[give .. $];
+ need -= give;
+ }
+ return size;
+ }
+
+ bool refill() {
+ // Is there a nicer way to do this?
+ auto indexBegin = httpBufRemaining_.ptr - httpBuf_.ptr;
+ auto indexEnd = indexBegin + httpBufRemaining_.length;
+
+ if (httpBuf_.length - indexEnd <= (httpBuf_.length / 4)) {
+ httpBuf_.length *= 2;
+ }
+
+ // Read more data.
+ auto got = transport_.read(cast(ubyte[])httpBuf_[indexEnd .. $]);
+ if (got == 0) return false;
+ httpBufRemaining_ = httpBuf_[indexBegin .. indexEnd + got];
+ return true;
+ }
+
+ TTransport transport_;
+
+ TMemoryBuffer writeBuffer_;
+ TMemoryBuffer readBuffer_;
+
+ bool readHeaders_;
+ bool chunked_;
+ bool chunkedDone_;
+ size_t chunkSize_;
+ size_t contentLength_;
+
+ ubyte[] httpBuf_;
+ ubyte[] httpBufRemaining_;
+}
+
+/**
+ * HTTP client transport.
+ */
+final class TClientHttpTransport : THttpTransport {
+ /**
+ * Constructs a client http transport operating on the passed underlying
+ * transport.
+ *
+ * Params:
+ * transport = The underlying transport used for the actual I/O.
+ * host = The HTTP host string.
+ * path = The HTTP path string.
+ */
+ this(TTransport transport, string host, string path) {
+ super(transport);
+ host_ = host;
+ path_ = path;
+ }
+
+ /**
+ * Convenience overload for constructing a client HTTP transport using a
+ * TSocket connecting to the specified host and port.
+ *
+ * Params:
+ * host = The server to connect to, also used as HTTP host string.
+ * port = The port to connect to.
+ * path = The HTTP path string.
+ */
+ this(string host, ushort port, string path) {
+ this(new TSocket(host, port), host, path);
+ }
+
+protected:
+ override string getHeader(size_t dataLength) {
+ return "POST " ~ path_ ~ " HTTP/1.1\r\n" ~
+ "Host: " ~ host_ ~ "\r\n" ~
+ "Content-Type: application/x-thrift\r\n" ~
+ "Content-Length: " ~ to!string(dataLength) ~ "\r\n" ~
+ "Accept: application/x-thrift\r\n" ~
+ "User-Agent: Thrift/" ~ VERSION ~ " (D/TClientHttpTransport)\r\n" ~
+ "\r\n";
+ }
+
+ override bool parseStatusLine(const(ubyte)[] status) {
+ // HTTP-Version SP Status-Code SP Reason-Phrase CRLF
+ auto firstSplit = findSplit(status, [' ']);
+ if (firstSplit[1].empty) {
+ throw new TTransportException("Bad status: " ~ to!string(status),
+ TTransportException.Type.CORRUPTED_DATA);
+ }
+
+ auto codeReason = firstSplit[2][countUntil!"a != b"(firstSplit[2], ' ') .. $];
+ auto secondSplit = findSplit(codeReason, [' ']);
+ if (secondSplit[1].empty) {
+ throw new TTransportException("Bad status: " ~ to!string(status),
+ TTransportException.Type.CORRUPTED_DATA);
+ }
+
+ if (secondSplit[0] == "200") {
+ // HTTP 200 = OK, we got the response
+ return true;
+ } else if (secondSplit[0] == "100") {
+ // HTTP 100 = continue, just keep reading
+ return false;
+ }
+
+ throw new TTransportException("Bad status (unhandled status code): " ~
+ to!string(cast(const(char[]))status), TTransportException.Type.CORRUPTED_DATA);
+ }
+
+private:
+ string host_;
+ string path_;
+}
+
+/**
+ * HTTP server transport.
+ */
+final class TServerHttpTransport : THttpTransport {
+ /**
+ * Constructs a new instance.
+ *
+ * Param:
+ * transport = The underlying transport used for the actual I/O.
+ */
+ this(TTransport transport) {
+ super(transport);
+ }
+
+protected:
+ override string getHeader(size_t dataLength) {
+ return "HTTP/1.1 200 OK\r\n" ~
+ "Date: " ~ getRFC1123Time() ~ "\r\n" ~
+ "Server: Thrift/" ~ VERSION ~ "\r\n" ~
+ "Content-Type: application/x-thrift\r\n" ~
+ "Content-Length: " ~ to!string(dataLength) ~ "\r\n" ~
+ "Connection: Keep-Alive\r\n" ~
+ "\r\n";
+ }
+
+ override bool parseStatusLine(const(ubyte)[] status) {
+ // Method SP Request-URI SP HTTP-Version CRLF.
+ auto split = findSplit(status, [' ']);
+ if (split[1].empty) {
+ throw new TTransportException("Bad status: " ~ to!string(status),
+ TTransportException.Type.CORRUPTED_DATA);
+ }
+
+ auto uriVersion = split[2][countUntil!"a != b"(split[2], ' ') .. $];
+ if (!canFind(uriVersion, ' ')) {
+ throw new TTransportException("Bad status: " ~ to!string(status),
+ TTransportException.Type.CORRUPTED_DATA);
+ }
+
+ if (split[0] == "POST") {
+ // POST method ok, looking for content.
+ return true;
+ }
+
+ throw new TTransportException("Bad status (unsupported method): " ~
+ to!string(status), TTransportException.Type.CORRUPTED_DATA);
+ }
+}
+
+/**
+ * Wraps a transport into a HTTP server protocol.
+ */
+alias TWrapperTransportFactory!TServerHttpTransport TServerHttpTransportFactory;
+
+private {
+ import std.string : format;
+ string getRFC1123Time() {
+ auto sysTime = Clock.currTime(UTC());
+
+ auto dayName = capMemberName(sysTime.dayOfWeek);
+ auto monthName = capMemberName(sysTime.month);
+
+ return format("%s, %s %s %s %s:%s:%s GMT", dayName, sysTime.day,
+ monthName, sysTime.year, sysTime.hour, sysTime.minute, sysTime.second);
+ }
+
+ import std.ascii : toUpper;
+ import std.traits : EnumMembers;
+ string capMemberName(T)(T val) if (is(T == enum)) {
+ foreach (i, e; EnumMembers!T) {
+ enum name = __traits(derivedMembers, T)[i];
+ enum capName = cast(char) toUpper(name[0]) ~ name [1 .. $];
+ if (val == e) {
+ return capName;
+ }
+ }
+ throw new Exception("Not a member of " ~ T.stringof ~ ": " ~ to!string(val));
+ }
+
+ unittest {
+ enum Foo {
+ bar,
+ bAZ
+ }
+
+ import std.exception;
+ enforce(capMemberName(Foo.bar) == "Bar");
+ enforce(capMemberName(Foo.bAZ) == "BAZ");
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/transport/memory.d b/src/jaegertracing/thrift/lib/d/src/thrift/transport/memory.d
new file mode 100644
index 000000000..cdf0807ab
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/transport/memory.d
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.transport.memory;
+
+import core.exception : onOutOfMemoryError;
+import core.stdc.stdlib : free, realloc;
+import std.algorithm : min;
+import std.conv : text;
+import thrift.transport.base;
+
+/**
+ * A transport that simply reads from and writes to an in-memory buffer. Every
+ * time you call write on it, the data is simply placed into a buffer, and
+ * every time you call read, data is consumed from that buffer.
+ *
+ * Currently, the storage for written data is never reclaimed, even if the
+ * buffer contents have already been read out again.
+ */
+final class TMemoryBuffer : TBaseTransport {
+ /**
+ * Constructs a new memory transport with an empty internal buffer.
+ */
+ this() {}
+
+ /**
+ * Constructs a new memory transport with an empty internal buffer,
+ * reserving space for capacity bytes in advance.
+ *
+ * If the amount of data which will be written to the buffer is already
+ * known on construction, this can better performance over the default
+ * constructor because reallocations can be avoided.
+ *
+ * If the preallocated buffer is exhausted, data can still be written to the
+ * transport, but reallocations will happen.
+ *
+ * Params:
+ * capacity = Size of the initially reserved buffer (in bytes).
+ */
+ this(size_t capacity) {
+ reset(capacity);
+ }
+
+ /**
+ * Constructs a new memory transport initially containing the passed data.
+ *
+ * For now, the passed buffer is not intelligently used, the data is just
+ * copied to the internal buffer.
+ *
+ * Params:
+ * buffer = Initial contents available to be read.
+ */
+ this(in ubyte[] contents) {
+ auto size = contents.length;
+ reset(size);
+ buffer_[0 .. size] = contents[];
+ writeOffset_ = size;
+ }
+
+ /**
+ * Destructor, frees the internally allocated buffer.
+ */
+ ~this() {
+ free(buffer_);
+ }
+
+ /**
+ * Returns a read-only view of the current buffer contents.
+ *
+ * Note: For performance reasons, the returned slice is only valid for the
+ * life of this object, and may be invalidated on the next write() call at
+ * will – you might want to immediately .dup it if you intend to keep it
+ * around.
+ */
+ const(ubyte)[] getContents() {
+ return buffer_[readOffset_ .. writeOffset_];
+ }
+
+ /**
+ * A memory transport is always open.
+ */
+ override bool isOpen() @property {
+ return true;
+ }
+
+ override bool peek() {
+ return writeOffset_ - readOffset_ > 0;
+ }
+
+ /**
+ * Opening is a no-op() for a memory buffer.
+ */
+ override void open() {}
+
+ /**
+ * Closing is a no-op() for a memory buffer, it is always open.
+ */
+ override void close() {}
+
+ override size_t read(ubyte[] buf) {
+ auto size = min(buf.length, writeOffset_ - readOffset_);
+ buf[0 .. size] = buffer_[readOffset_ .. readOffset_ + size];
+ readOffset_ += size;
+ return size;
+ }
+
+ /**
+ * Shortcut version of readAll() – using this over TBaseTransport.readAll()
+ * can give us a nice speed increase because gives us a nice speed increase
+ * because it is typically a very hot path during deserialization.
+ */
+ override void readAll(ubyte[] buf) {
+ auto available = writeOffset_ - readOffset_;
+ if (buf.length > available) {
+ throw new TTransportException(text("Cannot readAll() ", buf.length,
+ " bytes of data because only ", available, " bytes are available."),
+ TTransportException.Type.END_OF_FILE);
+ }
+
+ buf[] = buffer_[readOffset_ .. readOffset_ + buf.length];
+ readOffset_ += buf.length;
+ }
+
+ override void write(in ubyte[] buf) {
+ auto need = buf.length;
+ if (bufferLen_ - writeOffset_ < need) {
+ // Exponential growth.
+ auto newLen = bufferLen_ + 1;
+ while (newLen - writeOffset_ < need) newLen *= 2;
+ cRealloc(buffer_, newLen);
+ bufferLen_ = newLen;
+ }
+
+ buffer_[writeOffset_ .. writeOffset_ + need] = buf[];
+ writeOffset_ += need;
+ }
+
+ override const(ubyte)[] borrow(ubyte* buf, size_t len) {
+ if (len <= writeOffset_ - readOffset_) {
+ return buffer_[readOffset_ .. writeOffset_];
+ } else {
+ return null;
+ }
+ }
+
+ override void consume(size_t len) {
+ readOffset_ += len;
+ }
+
+ void reset() {
+ readOffset_ = 0;
+ writeOffset_ = 0;
+ }
+
+ void reset(size_t capacity) {
+ readOffset_ = 0;
+ writeOffset_ = 0;
+ if (bufferLen_ < capacity) {
+ cRealloc(buffer_, capacity);
+ bufferLen_ = capacity;
+ }
+ }
+
+private:
+ ubyte* buffer_;
+ size_t bufferLen_;
+ size_t readOffset_;
+ size_t writeOffset_;
+}
+
+private {
+ void cRealloc(ref ubyte* data, size_t newSize) {
+ auto result = realloc(data, newSize);
+ if (result is null) onOutOfMemoryError();
+ data = cast(ubyte*)result;
+ }
+}
+
+version (unittest) {
+ import std.exception;
+}
+
+unittest {
+ auto a = new TMemoryBuffer(5);
+ immutable(ubyte[]) testData = [1, 2, 3, 4];
+ auto buf = new ubyte[testData.length];
+ enforce(a.isOpen);
+
+ // a should be empty.
+ enforce(!a.peek());
+ enforce(a.read(buf) == 0);
+ assertThrown!TTransportException(a.readAll(buf));
+
+ // Write some data and read it back again.
+ a.write(testData);
+ enforce(a.peek());
+ enforce(a.getContents() == testData);
+ enforce(a.read(buf) == testData.length);
+ enforce(buf == testData);
+
+ // a should be empty again.
+ enforce(!a.peek());
+ enforce(a.read(buf) == 0);
+ assertThrown!TTransportException(a.readAll(buf));
+
+ // Test the constructor which directly accepts initial data.
+ auto b = new TMemoryBuffer(testData);
+ enforce(b.isOpen);
+ enforce(b.peek());
+ enforce(b.getContents() == testData);
+
+ // Test borrow().
+ auto borrowed = b.borrow(null, testData.length);
+ enforce(borrowed == testData);
+ enforce(b.peek());
+ b.consume(testData.length);
+ enforce(!b.peek());
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/transport/piped.d b/src/jaegertracing/thrift/lib/d/src/thrift/transport/piped.d
new file mode 100644
index 000000000..9fe143278
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/transport/piped.d
@@ -0,0 +1,219 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.transport.piped;
+
+import thrift.transport.base;
+import thrift.transport.memory;
+
+/**
+ * Pipes data request from one transport to another when readEnd()
+ * or writeEnd() is called.
+ *
+ * A typical use case would be to log requests on e.g. a socket to
+ * disk (i. e. pipe them to a TFileWriterTransport).
+ *
+ * The implementation keeps an internal buffer which expands to
+ * hold the whole amount of data read/written until the corresponding *End()
+ * method is called.
+ *
+ * Contrary to the C++ implementation, this doesn't introduce yet another layer
+ * of input/output buffering, all calls are passed to the underlying source
+ * transport verbatim.
+ */
+final class TPipedTransport(Source = TTransport) if (
+ isTTransport!Source
+) : TBaseTransport {
+ /// The default initial buffer size if not explicitly specified, in bytes.
+ enum DEFAULT_INITIAL_BUFFER_SIZE = 512;
+
+ /**
+ * Constructs a new instance.
+ *
+ * By default, only reads are piped (pipeReads = true, pipeWrites = false).
+ *
+ * Params:
+ * srcTrans = The transport to which all requests are forwarded.
+ * dstTrans = The transport the read/written data is copied to.
+ * initialBufferSize = The default size of the read/write buffers, for
+ * performance tuning.
+ */
+ this(Source srcTrans, TTransport dstTrans,
+ size_t initialBufferSize = DEFAULT_INITIAL_BUFFER_SIZE
+ ) {
+ srcTrans_ = srcTrans;
+ dstTrans_ = dstTrans;
+
+ readBuffer_ = new TMemoryBuffer(initialBufferSize);
+ writeBuffer_ = new TMemoryBuffer(initialBufferSize);
+
+ pipeReads_ = true;
+ pipeWrites_ = false;
+ }
+
+ bool pipeReads() @property const {
+ return pipeReads_;
+ }
+
+ void pipeReads(bool value) @property {
+ if (!value) {
+ readBuffer_.reset();
+ }
+ pipeReads_ = value;
+ }
+
+ bool pipeWrites() @property const {
+ return pipeWrites_;
+ }
+
+ void pipeWrites(bool value) @property {
+ if (!value) {
+ writeBuffer_.reset();
+ }
+ pipeWrites_ = value;
+ }
+
+ override bool isOpen() {
+ return srcTrans_.isOpen();
+ }
+
+ override bool peek() {
+ return srcTrans_.peek();
+ }
+
+ override void open() {
+ srcTrans_.open();
+ }
+
+ override void close() {
+ srcTrans_.close();
+ }
+
+ override size_t read(ubyte[] buf) {
+ auto bytesRead = srcTrans_.read(buf);
+
+ if (pipeReads_) {
+ readBuffer_.write(buf[0 .. bytesRead]);
+ }
+
+ return bytesRead;
+ }
+
+ override size_t readEnd() {
+ if (pipeReads_) {
+ auto data = readBuffer_.getContents();
+ dstTrans_.write(data);
+ dstTrans_.flush();
+ readBuffer_.reset();
+
+ srcTrans_.readEnd();
+
+ // Return data.length instead of the readEnd() result of the source
+ // transports because it might not be available from it.
+ return data.length;
+ }
+
+ return srcTrans_.readEnd();
+ }
+
+ override void write(in ubyte[] buf) {
+ if (pipeWrites_) {
+ writeBuffer_.write(buf);
+ }
+
+ srcTrans_.write(buf);
+ }
+
+ override size_t writeEnd() {
+ if (pipeWrites_) {
+ auto data = writeBuffer_.getContents();
+ dstTrans_.write(data);
+ dstTrans_.flush();
+ writeBuffer_.reset();
+
+ srcTrans_.writeEnd();
+
+ // Return data.length instead of the readEnd() result of the source
+ // transports because it might not be available from it.
+ return data.length;
+ }
+
+ return srcTrans_.writeEnd();
+ }
+
+ override void flush() {
+ srcTrans_.flush();
+ }
+
+private:
+ Source srcTrans_;
+ TTransport dstTrans_;
+
+ TMemoryBuffer readBuffer_;
+ TMemoryBuffer writeBuffer_;
+
+ bool pipeReads_;
+ bool pipeWrites_;
+}
+
+/**
+ * TPipedTransport construction helper to avoid having to explicitly
+ * specify the transport types, i.e. to allow the constructor being called
+ * using IFTI (see $(DMDBUG 6082, D Bugzilla enhancement request 6082)).
+ */
+TPipedTransport!Source tPipedTransport(Source)(
+ Source srcTrans, TTransport dstTrans
+) if (isTTransport!Source) {
+ return new typeof(return)(srcTrans, dstTrans);
+}
+
+version (unittest) {
+ // DMD @@BUG@@: UFCS for std.array.empty doesn't work when import is moved
+ // into unittest block.
+ import std.array;
+ import std.exception : enforce;
+}
+
+unittest {
+ auto underlying = new TMemoryBuffer;
+ auto pipeTarget = new TMemoryBuffer;
+ auto trans = tPipedTransport(underlying, pipeTarget);
+
+ underlying.write(cast(ubyte[])"abcd");
+
+ ubyte[4] buffer;
+ trans.readAll(buffer[0 .. 2]);
+ enforce(buffer[0 .. 2] == "ab");
+ enforce(pipeTarget.getContents().empty);
+
+ trans.readEnd();
+ enforce(pipeTarget.getContents() == "ab");
+ pipeTarget.reset();
+
+ underlying.write(cast(ubyte[])"ef");
+ trans.readAll(buffer[0 .. 2]);
+ enforce(buffer[0 .. 2] == "cd");
+ enforce(pipeTarget.getContents().empty);
+
+ trans.readAll(buffer[0 .. 2]);
+ enforce(buffer[0 .. 2] == "ef");
+ enforce(pipeTarget.getContents().empty);
+
+ trans.readEnd();
+ enforce(pipeTarget.getContents() == "cdef");
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/transport/range.d b/src/jaegertracing/thrift/lib/d/src/thrift/transport/range.d
new file mode 100644
index 000000000..761cea132
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/transport/range.d
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Transports which operate on generic D ranges.
+ */
+module thrift.transport.range;
+
+import std.array : empty;
+import std.range;
+import std.traits : Unqual;
+import thrift.transport.base;
+
+/**
+ * Adapts an ubyte input range for reading via the TTransport interface.
+ *
+ * The case where R is a plain ubyte[] is reasonably optimized, so a possible
+ * use case for TInputRangeTransport would be to deserialize some data held in
+ * a memory buffer.
+ */
+final class TInputRangeTransport(R) if (
+ isInputRange!(Unqual!R) && is(ElementType!R : const(ubyte))
+) : TBaseTransport {
+ /**
+ * Constructs a new instance.
+ *
+ * Params:
+ * data = The input range to use as data.
+ */
+ this(R data) {
+ data_ = data;
+ }
+
+ /**
+ * An input range transport is always open.
+ */
+ override bool isOpen() @property {
+ return true;
+ }
+
+ override bool peek() {
+ return !data_.empty;
+ }
+
+ /**
+ * Opening is a no-op() for an input range transport.
+ */
+ override void open() {}
+
+ /**
+ * Closing is a no-op() for a memory buffer.
+ */
+ override void close() {}
+
+ override size_t read(ubyte[] buf) {
+ auto data = data_.take(buf.length);
+ auto bytes = data.length;
+
+ static if (is(typeof(R.init[1 .. 2]) : const(ubyte)[])) {
+ // put() is currently unnecessarily slow if both ranges are sliceable.
+ buf[0 .. bytes] = data[];
+ data_ = data_[bytes .. $];
+ } else {
+ buf.put(data);
+ }
+
+ return bytes;
+ }
+
+ /**
+ * Shortcut version of readAll() for slicable ranges.
+ *
+ * Because readAll() is typically a very hot path during deserialization,
+ * using this over TBaseTransport.readAll() gives us a nice increase in
+ * speed due to the reduced amount of indirections.
+ */
+ override void readAll(ubyte[] buf) {
+ static if (is(typeof(R.init[1 .. 2]) : const(ubyte)[])) {
+ if (buf.length <= data_.length) {
+ buf[] = data_[0 .. buf.length];
+ data_ = data_[buf.length .. $];
+ return;
+ }
+ }
+ super.readAll(buf);
+ }
+
+ override const(ubyte)[] borrow(ubyte* buf, size_t len) {
+ static if (is(R : const(ubyte)[])) {
+ // Can only borrow if our data type is actually an ubyte array.
+ if (len <= data_.length) {
+ return data_;
+ }
+ }
+ return null;
+ }
+
+ override void consume(size_t len) {
+ static if (is(R : const(ubyte)[])) {
+ if (len > data_.length) {
+ throw new TTransportException("Invalid consume length",
+ TTransportException.Type.BAD_ARGS);
+ }
+ data_ = data_[len .. $];
+ } else {
+ super.consume(len);
+ }
+ }
+
+ /**
+ * Sets a new data range to use.
+ */
+ void reset(R data) {
+ data_ = data;
+ }
+
+private:
+ R data_;
+}
+
+/**
+ * TInputRangeTransport construction helper to avoid having to explicitly
+ * specify the argument type, i.e. to allow the constructor being called using
+ * IFTI (see $(LINK2 http://d.puremagic.com/issues/show_bug.cgi?id=6082, D
+ * Bugzilla enhancement requet 6082)).
+ */
+TInputRangeTransport!R tInputRangeTransport(R)(R data) if (
+ is (TInputRangeTransport!R)
+) {
+ return new TInputRangeTransport!R(data);
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/transport/socket.d b/src/jaegertracing/thrift/lib/d/src/thrift/transport/socket.d
new file mode 100644
index 000000000..fcb38da36
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/transport/socket.d
@@ -0,0 +1,454 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.transport.socket;
+
+import core.stdc.errno: ECONNRESET;
+import core.thread : Thread;
+import core.time : dur, Duration;
+import std.array : empty;
+import std.conv : text, to;
+import std.exception : enforce;
+import std.socket;
+import thrift.base;
+import thrift.transport.base;
+import thrift.internal.socket;
+
+/**
+ * Common parts of a socket TTransport implementation, regardless of how the
+ * actual I/O is performed (sync/async).
+ */
+abstract class TSocketBase : TBaseTransport {
+ /**
+ * Constructor that takes an already created, connected (!) socket.
+ *
+ * Params:
+ * socket = Already created, connected socket object.
+ */
+ this(Socket socket) {
+ socket_ = socket;
+ setSocketOpts();
+ }
+
+ /**
+ * Creates a new unconnected socket that will connect to the given host
+ * on the given port.
+ *
+ * Params:
+ * host = Remote host.
+ * port = Remote port.
+ */
+ this(string host, ushort port) {
+ host_ = host;
+ port_ = port;
+ }
+
+ /**
+ * Checks whether the socket is connected.
+ */
+ override bool isOpen() @property {
+ return socket_ !is null;
+ }
+
+ /**
+ * Writes as much data to the socket as there can be in a single OS call.
+ *
+ * Params:
+ * buf = Data to write.
+ *
+ * Returns: The actual number of bytes written. Never more than buf.length.
+ */
+ abstract size_t writeSome(in ubyte[] buf) out (written) {
+ // DMD @@BUG@@: Enabling this e.g. fails the contract in the
+ // async_test_server, because buf.length evaluates to 0 here, even though
+ // in the method body it correctly is 27 (equal to the return value).
+ version (none) assert(written <= buf.length, text("Implementation wrote " ~
+ "more data than requested to?! (", written, " vs. ", buf.length, ")"));
+ } body {
+ assert(0, "DMD bug? – Why would contracts work for interfaces, but not " ~
+ "for abstract methods? " ~
+ "(Error: function […] in and out contracts require function body");
+ }
+
+ /**
+ * Returns the actual address of the peer the socket is connected to.
+ *
+ * In contrast, the host and port properties contain the address used to
+ * establish the connection, and are not updated after the connection.
+ *
+ * The socket must be open when calling this.
+ */
+ Address getPeerAddress() {
+ enforce(isOpen, new TTransportException("Cannot get peer host for " ~
+ "closed socket.", TTransportException.Type.NOT_OPEN));
+
+ if (!peerAddress_) {
+ peerAddress_ = socket_.remoteAddress();
+ assert(peerAddress_);
+ }
+
+ return peerAddress_;
+ }
+
+ /**
+ * The host the socket is connected to or will connect to. Null if an
+ * already connected socket was used to construct the object.
+ */
+ string host() const @property {
+ return host_;
+ }
+
+ /**
+ * The port the socket is connected to or will connect to. Zero if an
+ * already connected socket was used to construct the object.
+ */
+ ushort port() const @property {
+ return port_;
+ }
+
+ /// The socket send timeout.
+ Duration sendTimeout() const @property {
+ return sendTimeout_;
+ }
+
+ /// Ditto
+ void sendTimeout(Duration value) @property {
+ sendTimeout_ = value;
+ }
+
+ /// The socket receiving timeout. Values smaller than 500 ms are not
+ /// supported on Windows.
+ Duration recvTimeout() const @property {
+ return recvTimeout_;
+ }
+
+ /// Ditto
+ void recvTimeout(Duration value) @property {
+ recvTimeout_ = value;
+ }
+
+ /**
+ * Returns the OS handle of the underlying socket.
+ *
+ * Should not usually be used directly, but access to it can be necessary
+ * to interface with C libraries.
+ */
+ typeof(socket_.handle()) socketHandle() @property {
+ return socket_.handle();
+ }
+
+protected:
+ /**
+ * Sets the needed socket options.
+ */
+ void setSocketOpts() {
+ try {
+ alias SocketOptionLevel.SOCKET lvlSock;
+ Linger l;
+ l.on = 0;
+ l.time = 0;
+ socket_.setOption(lvlSock, SocketOption.LINGER, l);
+ } catch (SocketException e) {
+ logError("Could not set socket option: %s", e);
+ }
+
+ // Just try to disable Nagle's algorithm – this will fail if we are passed
+ // in a non-TCP socket via the Socket-accepting constructor.
+ try {
+ socket_.setOption(SocketOptionLevel.TCP, SocketOption.TCP_NODELAY, true);
+ } catch (SocketException e) {}
+ }
+
+ /// Remote host.
+ string host_;
+
+ /// Remote port.
+ ushort port_;
+
+ /// Timeout for sending.
+ Duration sendTimeout_;
+
+ /// Timeout for receiving.
+ Duration recvTimeout_;
+
+ /// Cached peer address.
+ Address peerAddress_;
+
+ /// Cached peer host name.
+ string peerHost_;
+
+ /// Cached peer port.
+ ushort peerPort_;
+
+ /// Wrapped socket object.
+ Socket socket_;
+}
+
+/**
+ * Socket implementation of the TTransport interface.
+ *
+ * Due to the limitations of std.socket, currently only TCP/IP sockets are
+ * supported (i.e. Unix domain sockets are not).
+ */
+class TSocket : TSocketBase {
+ ///
+ this(Socket socket) {
+ super(socket);
+ }
+
+ ///
+ this(string host, ushort port) {
+ super(host, port);
+ }
+
+ /**
+ * Connects the socket.
+ */
+ override void open() {
+ if (isOpen) return;
+
+ enforce(!host_.empty, new TTransportException(
+ "Cannot open socket to null host.", TTransportException.Type.NOT_OPEN));
+ enforce(port_ != 0, new TTransportException(
+ "Cannot open socket to port zero.", TTransportException.Type.NOT_OPEN));
+
+ Address[] addrs;
+ try {
+ addrs = getAddress(host_, port_);
+ } catch (SocketException e) {
+ throw new TTransportException("Could not resolve given host string.",
+ TTransportException.Type.NOT_OPEN, __FILE__, __LINE__, e);
+ }
+
+ Exception[] errors;
+ foreach (addr; addrs) {
+ try {
+ socket_ = new TcpSocket(addr.addressFamily);
+ setSocketOpts();
+ socket_.connect(addr);
+ break;
+ } catch (SocketException e) {
+ errors ~= e;
+ }
+ }
+ if (errors.length == addrs.length) {
+ socket_ = null;
+ // Need to throw a TTransportException to abide the TTransport API.
+ import std.algorithm, std.range;
+ throw new TTransportException(
+ text("Failed to connect to ", host_, ":", port_, "."),
+ TTransportException.Type.NOT_OPEN,
+ __FILE__, __LINE__,
+ new TCompoundOperationException(
+ text(
+ "All addresses tried failed (",
+ joiner(map!q{text(a[0], `: "`, a[1].msg, `"`)}(zip(addrs, errors)), ", "),
+ ")."
+ ),
+ errors
+ )
+ );
+ }
+ }
+
+ /**
+ * Closes the socket.
+ */
+ override void close() {
+ if (!isOpen) return;
+
+ socket_.close();
+ socket_ = null;
+ }
+
+ override bool peek() {
+ if (!isOpen) return false;
+
+ ubyte buf;
+ auto r = socket_.receive((&buf)[0 .. 1], SocketFlags.PEEK);
+ if (r == -1) {
+ auto lastErrno = getSocketErrno();
+ static if (connresetOnPeerShutdown) {
+ if (lastErrno == ECONNRESET) {
+ close();
+ return false;
+ }
+ }
+ throw new TTransportException("Peeking into socket failed: " ~
+ socketErrnoString(lastErrno), TTransportException.Type.UNKNOWN);
+ }
+ return (r > 0);
+ }
+
+ override size_t read(ubyte[] buf) {
+ enforce(isOpen, new TTransportException(
+ "Cannot read if socket is not open.", TTransportException.Type.NOT_OPEN));
+
+ typeof(getSocketErrno()) lastErrno;
+ ushort tries;
+ while (tries++ <= maxRecvRetries_) {
+ auto r = socket_.receive(cast(void[])buf);
+
+ // If recv went fine, immediately return.
+ if (r >= 0) return r;
+
+ // Something went wrong, find out how to handle it.
+ lastErrno = getSocketErrno();
+
+ if (lastErrno == INTERRUPTED_ERRNO) {
+ // If the syscall was interrupted, just try again.
+ continue;
+ }
+
+ static if (connresetOnPeerShutdown) {
+ // See top comment.
+ if (lastErrno == ECONNRESET) {
+ return 0;
+ }
+ }
+
+ // Not an error which is handled in a special way, just leave the loop.
+ break;
+ }
+
+ if (isSocketCloseErrno(lastErrno)) {
+ close();
+ throw new TTransportException("Receiving failed, closing socket: " ~
+ socketErrnoString(lastErrno), TTransportException.Type.NOT_OPEN);
+ } else if (lastErrno == TIMEOUT_ERRNO) {
+ throw new TTransportException(TTransportException.Type.TIMED_OUT);
+ } else {
+ throw new TTransportException("Receiving from socket failed: " ~
+ socketErrnoString(lastErrno), TTransportException.Type.UNKNOWN);
+ }
+ }
+
+ override void write(in ubyte[] buf) {
+ size_t sent;
+ while (sent < buf.length) {
+ auto b = writeSome(buf[sent .. $]);
+ if (b == 0) {
+ // This should only happen if the timeout set with SO_SNDTIMEO expired.
+ throw new TTransportException("send() timeout expired.",
+ TTransportException.Type.TIMED_OUT);
+ }
+ sent += b;
+ }
+ assert(sent == buf.length);
+ }
+
+ override size_t writeSome(in ubyte[] buf) {
+ enforce(isOpen, new TTransportException(
+ "Cannot write if file is not open.", TTransportException.Type.NOT_OPEN));
+
+ auto r = socket_.send(buf);
+
+ // Everything went well, just return the number of bytes written.
+ if (r > 0) return r;
+
+ // Handle error conditions.
+ if (r < 0) {
+ auto lastErrno = getSocketErrno();
+
+ if (lastErrno == WOULD_BLOCK_ERRNO) {
+ // Not an exceptional error per se – even with blocking sockets,
+ // EAGAIN apparently is returned sometimes on out-of-resource
+ // conditions (see the C++ implementation for details). Also, this
+ // allows using TSocket with non-blocking sockets e.g. in
+ // TNonblockingServer.
+ return 0;
+ }
+
+ auto type = TTransportException.Type.UNKNOWN;
+ if (isSocketCloseErrno(lastErrno)) {
+ type = TTransportException.Type.NOT_OPEN;
+ close();
+ }
+
+ throw new TTransportException("Sending to socket failed: " ~
+ socketErrnoString(lastErrno), type);
+ }
+
+ // send() should never return 0.
+ throw new TTransportException("Sending to socket failed (0 bytes written).",
+ TTransportException.Type.UNKNOWN);
+ }
+
+ override void sendTimeout(Duration value) @property {
+ super.sendTimeout(value);
+ setTimeout(SocketOption.SNDTIMEO, value);
+ }
+
+ override void recvTimeout(Duration value) @property {
+ super.recvTimeout(value);
+ setTimeout(SocketOption.RCVTIMEO, value);
+ }
+
+ /**
+ * Maximum number of retries for receiving from socket on read() in case of
+ * EAGAIN/EINTR.
+ */
+ ushort maxRecvRetries() @property const {
+ return maxRecvRetries_;
+ }
+
+ /// Ditto
+ void maxRecvRetries(ushort value) @property {
+ maxRecvRetries_ = value;
+ }
+
+ /// Ditto
+ enum DEFAULT_MAX_RECV_RETRIES = 5;
+
+protected:
+ override void setSocketOpts() {
+ super.setSocketOpts();
+ setTimeout(SocketOption.SNDTIMEO, sendTimeout_);
+ setTimeout(SocketOption.RCVTIMEO, recvTimeout_);
+ }
+
+ void setTimeout(SocketOption type, Duration value) {
+ assert(type == SocketOption.SNDTIMEO || type == SocketOption.RCVTIMEO);
+ version (Win32) {
+ if (value > dur!"hnsecs"(0) && value < dur!"msecs"(500)) {
+ logError(
+ "Socket %s timeout of %s ms might be raised to 500 ms on Windows.",
+ (type == SocketOption.SNDTIMEO) ? "send" : "receive",
+ value.total!"msecs"
+ );
+ }
+ }
+
+ if (socket_) {
+ try {
+ socket_.setOption(SocketOptionLevel.SOCKET, type, value);
+ } catch (SocketException e) {
+ throw new TTransportException(
+ "Could not set timeout.",
+ TTransportException.Type.UNKNOWN,
+ __FILE__,
+ __LINE__,
+ e
+ );
+ }
+ }
+ }
+
+ /// Maximum number of recv() retries.
+ ushort maxRecvRetries_ = DEFAULT_MAX_RECV_RETRIES;
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/transport/ssl.d b/src/jaegertracing/thrift/lib/d/src/thrift/transport/ssl.d
new file mode 100644
index 000000000..f8ce40eb7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/transport/ssl.d
@@ -0,0 +1,690 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * OpenSSL socket implementation, in large parts ported from C++.
+ */
+module thrift.transport.ssl;
+
+import core.exception : onOutOfMemoryError;
+import core.stdc.errno : errno, EINTR;
+import core.sync.mutex : Mutex;
+import core.memory : GC;
+import core.stdc.config;
+import core.stdc.stdlib : free, malloc;
+import std.ascii : toUpper;
+import std.array : empty, front, popFront;
+import std.conv : emplace, to;
+import std.exception : enforce;
+import std.socket : Address, InternetAddress, Internet6Address, Socket;
+import std.string : toStringz;
+import deimos.openssl.err;
+import deimos.openssl.rand;
+import deimos.openssl.ssl;
+import deimos.openssl.x509v3;
+import thrift.base;
+import thrift.internal.ssl;
+import thrift.transport.base;
+import thrift.transport.socket;
+
+/**
+ * SSL encrypted socket implementation using OpenSSL.
+ *
+ * Note:
+ * On Posix systems which do not have the BSD-specific SO_NOSIGPIPE flag, you
+ * might want to ignore the SIGPIPE signal, as OpenSSL might try to write to
+ * a closed socket if the peer disconnects abruptly:
+ * ---
+ * import core.stdc.signal;
+ * import core.sys.posix.signal;
+ * signal(SIGPIPE, SIG_IGN);
+ * ---
+ */
+final class TSSLSocket : TSocket {
+ /**
+ * Creates an instance that wraps an already created, connected (!) socket.
+ *
+ * Params:
+ * context = The SSL socket context to use. A reference to it is stored so
+ * that it doesn't get cleaned up while the socket is used.
+ * socket = Already created, connected socket object.
+ */
+ this(TSSLContext context, Socket socket) {
+ super(socket);
+ context_ = context;
+ serverSide_ = context.serverSide;
+ accessManager_ = context.accessManager;
+ }
+
+ /**
+ * Creates a new unconnected socket that will connect to the given host
+ * on the given port.
+ *
+ * Params:
+ * context = The SSL socket context to use. A reference to it is stored so
+ * that it doesn't get cleaned up while the socket is used.
+ * host = Remote host.
+ * port = Remote port.
+ */
+ this(TSSLContext context, string host, ushort port) {
+ super(host, port);
+ context_ = context;
+ serverSide_ = context.serverSide;
+ accessManager_ = context.accessManager;
+ }
+
+ override bool isOpen() @property {
+ if (ssl_ is null || !super.isOpen()) return false;
+
+ auto shutdown = SSL_get_shutdown(ssl_);
+ bool shutdownReceived = (shutdown & SSL_RECEIVED_SHUTDOWN) != 0;
+ bool shutdownSent = (shutdown & SSL_SENT_SHUTDOWN) != 0;
+ return !(shutdownReceived && shutdownSent);
+ }
+
+ override bool peek() {
+ if (!isOpen) return false;
+ checkHandshake();
+
+ byte bt;
+ auto rc = SSL_peek(ssl_, &bt, bt.sizeof);
+ enforce(rc >= 0, getSSLException("SSL_peek"));
+
+ if (rc == 0) {
+ ERR_clear_error();
+ }
+ return (rc > 0);
+ }
+
+ override void open() {
+ enforce(!serverSide_, "Cannot open a server-side SSL socket.");
+ if (isOpen) return;
+ super.open();
+ }
+
+ override void close() {
+ if (!isOpen) return;
+
+ if (ssl_ !is null) {
+ // Two-step SSL shutdown.
+ auto rc = SSL_shutdown(ssl_);
+ if (rc == 0) {
+ rc = SSL_shutdown(ssl_);
+ }
+ if (rc < 0) {
+ // Do not throw an exception here as leaving the transport "open" will
+ // probably produce only more errors, and the chance we can do
+ // something about the error e.g. by retrying is very low.
+ logError("Error shutting down SSL: %s", getSSLException());
+ }
+
+ SSL_free(ssl_);
+ ssl_ = null;
+ ERR_remove_state(0);
+ }
+ super.close();
+ }
+
+ override size_t read(ubyte[] buf) {
+ checkHandshake();
+
+ int bytes;
+ foreach (_; 0 .. maxRecvRetries) {
+ bytes = SSL_read(ssl_, buf.ptr, cast(int)buf.length);
+ if (bytes >= 0) break;
+
+ auto errnoCopy = errno;
+ if (SSL_get_error(ssl_, bytes) == SSL_ERROR_SYSCALL) {
+ if (ERR_get_error() == 0 && errnoCopy == EINTR) {
+ // FIXME: Windows.
+ continue;
+ }
+ }
+ throw getSSLException("SSL_read");
+ }
+ return bytes;
+ }
+
+ override void write(in ubyte[] buf) {
+ checkHandshake();
+
+ // Loop in case SSL_MODE_ENABLE_PARTIAL_WRITE is set in SSL_CTX.
+ size_t written = 0;
+ while (written < buf.length) {
+ auto bytes = SSL_write(ssl_, buf.ptr + written,
+ cast(int)(buf.length - written));
+ if (bytes <= 0) {
+ throw getSSLException("SSL_write");
+ }
+ written += bytes;
+ }
+ }
+
+ override void flush() {
+ checkHandshake();
+
+ auto bio = SSL_get_wbio(ssl_);
+ enforce(bio !is null, new TSSLException("SSL_get_wbio returned null"));
+
+ auto rc = BIO_flush(bio);
+ enforce(rc == 1, getSSLException("BIO_flush"));
+ }
+
+ /**
+ * Whether to use client or server side SSL handshake protocol.
+ */
+ bool serverSide() @property const {
+ return serverSide_;
+ }
+
+ /// Ditto
+ void serverSide(bool value) @property {
+ serverSide_ = value;
+ }
+
+ /**
+ * The access manager to use.
+ */
+ void accessManager(TAccessManager value) @property {
+ accessManager_ = value;
+ }
+
+private:
+ void checkHandshake() {
+ enforce(super.isOpen(), new TTransportException(
+ TTransportException.Type.NOT_OPEN));
+
+ if (ssl_ !is null) return;
+ ssl_ = context_.createSSL();
+
+ SSL_set_fd(ssl_, socketHandle);
+ int rc;
+ if (serverSide_) {
+ rc = SSL_accept(ssl_);
+ } else {
+ rc = SSL_connect(ssl_);
+ }
+ enforce(rc > 0, getSSLException());
+ authorize(ssl_, accessManager_, getPeerAddress(),
+ (serverSide_ ? getPeerAddress().toHostNameString() : host));
+ }
+
+ bool serverSide_;
+ SSL* ssl_;
+ TSSLContext context_;
+ TAccessManager accessManager_;
+}
+
+/**
+ * Represents an OpenSSL context with certification settings, etc. and handles
+ * initialization/teardown.
+ *
+ * OpenSSL is initialized when the first instance of this class is created
+ * and shut down when the last one is destroyed (thread-safe).
+ */
+class TSSLContext {
+ this() {
+ initMutex_.lock();
+ scope(exit) initMutex_.unlock();
+
+ if (count_ == 0) {
+ initializeOpenSSL();
+ randomize();
+ }
+ count_++;
+
+ static if (OPENSSL_VERSION_NUMBER >= 0x1010000f) { // OPENSSL_VERSION_AT_LEAST(1, 1)) {
+ ctx_ = SSL_CTX_new(TLS_method());
+ } else {
+ ctx_ = SSL_CTX_new(SSLv23_method());
+ SSL_CTX_set_options(ctx_, SSL_OP_NO_SSLv2);
+ }
+ SSL_CTX_set_options(ctx_, SSL_OP_NO_SSLv3); // THRIFT-3164
+ enforce(ctx_, getSSLException("SSL_CTX_new"));
+ SSL_CTX_set_mode(ctx_, SSL_MODE_AUTO_RETRY);
+ }
+
+ ~this() {
+ initMutex_.lock();
+ scope(exit) initMutex_.unlock();
+
+ if (ctx_ !is null) {
+ SSL_CTX_free(ctx_);
+ ctx_ = null;
+ }
+
+ count_--;
+ if (count_ == 0) {
+ cleanupOpenSSL();
+ }
+ }
+
+ /**
+ * Ciphers to be used in SSL handshake process.
+ *
+ * The string must be in the colon-delimited OpenSSL notation described in
+ * ciphers(1), for example: "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH".
+ */
+ void ciphers(string enable) @property {
+ auto rc = SSL_CTX_set_cipher_list(ctx_, toStringz(enable));
+
+ enforce(ERR_peek_error() == 0, getSSLException("SSL_CTX_set_cipher_list"));
+ enforce(rc > 0, new TSSLException("None of specified ciphers are supported"));
+ }
+
+ /**
+ * Whether peer is required to present a valid certificate.
+ */
+ void authenticate(bool required) @property {
+ int mode;
+ if (required) {
+ mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
+ SSL_VERIFY_CLIENT_ONCE;
+ } else {
+ mode = SSL_VERIFY_NONE;
+ }
+ SSL_CTX_set_verify(ctx_, mode, null);
+ }
+
+ /**
+ * Load server certificate.
+ *
+ * Params:
+ * path = Path to the certificate file.
+ * format = Certificate file format. Defaults to PEM, which is currently
+ * the only one supported.
+ */
+ void loadCertificate(string path, string format = "PEM") {
+ enforce(path !is null && format !is null, new TTransportException(
+ "loadCertificateChain: either <path> or <format> is null",
+ TTransportException.Type.BAD_ARGS));
+
+ if (format == "PEM") {
+ enforce(SSL_CTX_use_certificate_chain_file(ctx_, toStringz(path)),
+ getSSLException(
+ `Could not load SSL server certificate from file "` ~ path ~ `"`
+ )
+ );
+ } else {
+ throw new TSSLException("Unsupported certificate format: " ~ format);
+ }
+ }
+
+ /*
+ * Load private key.
+ *
+ * Params:
+ * path = Path to the certificate file.
+ * format = Private key file format. Defaults to PEM, which is currently
+ * the only one supported.
+ */
+ void loadPrivateKey(string path, string format = "PEM") {
+ enforce(path !is null && format !is null, new TTransportException(
+ "loadPrivateKey: either <path> or <format> is NULL",
+ TTransportException.Type.BAD_ARGS));
+
+ if (format == "PEM") {
+ enforce(SSL_CTX_use_PrivateKey_file(ctx_, toStringz(path), SSL_FILETYPE_PEM),
+ getSSLException(
+ `Could not load SSL private key from file "` ~ path ~ `"`
+ )
+ );
+ } else {
+ throw new TSSLException("Unsupported certificate format: " ~ format);
+ }
+ }
+
+ /**
+ * Load trusted certificates from specified file (in PEM format).
+ *
+ * Params.
+ * path = Path to the file containing the trusted certificates.
+ */
+ void loadTrustedCertificates(string path) {
+ enforce(path !is null, new TTransportException(
+ "loadTrustedCertificates: <path> is NULL",
+ TTransportException.Type.BAD_ARGS));
+
+ enforce(SSL_CTX_load_verify_locations(ctx_, toStringz(path), null),
+ getSSLException(
+ `Could not load SSL trusted certificate list from file "` ~ path ~ `"`
+ )
+ );
+ }
+
+ /**
+ * Called during OpenSSL initialization to seed the OpenSSL entropy pool.
+ *
+ * Defaults to simply calling RAND_poll(), but it can be overwritten if a
+ * different, perhaps more secure implementation is desired.
+ */
+ void randomize() {
+ RAND_poll();
+ }
+
+ /**
+ * Whether to use client or server side SSL handshake protocol.
+ */
+ bool serverSide() @property const {
+ return serverSide_;
+ }
+
+ /// Ditto
+ void serverSide(bool value) @property {
+ serverSide_ = value;
+ }
+
+ /**
+ * The access manager to use.
+ */
+ TAccessManager accessManager() @property {
+ if (!serverSide_ && !accessManager_) {
+ accessManager_ = new TDefaultClientAccessManager;
+ }
+ return accessManager_;
+ }
+
+ /// Ditto
+ void accessManager(TAccessManager value) @property {
+ accessManager_ = value;
+ }
+
+ SSL* createSSL() out (result) {
+ assert(result);
+ } body {
+ auto result = SSL_new(ctx_);
+ enforce(result, getSSLException("SSL_new"));
+ return result;
+ }
+
+protected:
+ /**
+ * Override this method for custom password callback. It may be called
+ * multiple times at any time during a session as necessary.
+ *
+ * Params:
+ * size = Maximum length of password, including null byte.
+ */
+ string getPassword(int size) nothrow out(result) {
+ assert(result.length < size);
+ } body {
+ return "";
+ }
+
+ /**
+ * Notifies OpenSSL to use getPassword() instead of the default password
+ * callback with getPassword().
+ */
+ void overrideDefaultPasswordCallback() {
+ SSL_CTX_set_default_passwd_cb(ctx_, &passwordCallback);
+ SSL_CTX_set_default_passwd_cb_userdata(ctx_, cast(void*)this);
+ }
+
+ SSL_CTX* ctx_;
+
+private:
+ bool serverSide_;
+ TAccessManager accessManager_;
+
+ shared static this() {
+ initMutex_ = new Mutex();
+ }
+
+ static void initializeOpenSSL() {
+ if (initialized_) {
+ return;
+ }
+ initialized_ = true;
+
+ static if (OPENSSL_VERSION_NUMBER < 0x1010000f) { // OPENSSL_VERSION_BEFORE(1, 1)) {
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ mutexes_ = new Mutex[CRYPTO_num_locks()];
+ foreach (ref m; mutexes_) {
+ m = new Mutex;
+ }
+
+ import thrift.internal.traits;
+ // As per the OpenSSL threads manpage, this isn't needed on Windows.
+ version (Posix) {
+ CRYPTO_set_id_callback(assumeNothrow(&threadIdCallback));
+ }
+ CRYPTO_set_locking_callback(assumeNothrow(&lockingCallback));
+ CRYPTO_set_dynlock_create_callback(assumeNothrow(&dynlockCreateCallback));
+ CRYPTO_set_dynlock_lock_callback(assumeNothrow(&dynlockLockCallback));
+ CRYPTO_set_dynlock_destroy_callback(assumeNothrow(&dynlockDestroyCallback));
+ }
+ }
+
+ static void cleanupOpenSSL() {
+ if (!initialized_) return;
+
+ initialized_ = false;
+ static if (OPENSSL_VERSION_NUMBER < 0x1010000f) { // OPENSSL_VERSION_BEFORE(1, 1)) {
+ CRYPTO_set_locking_callback(null);
+ CRYPTO_set_dynlock_create_callback(null);
+ CRYPTO_set_dynlock_lock_callback(null);
+ CRYPTO_set_dynlock_destroy_callback(null);
+ CRYPTO_cleanup_all_ex_data();
+ ERR_free_strings();
+ ERR_remove_state(0);
+ }
+ }
+
+ static extern(C) {
+ version (Posix) {
+ import core.sys.posix.pthread : pthread_self;
+ c_ulong threadIdCallback() {
+ return cast(c_ulong)pthread_self();
+ }
+ }
+
+ void lockingCallback(int mode, int n, const(char)* file, int line) {
+ if (mode & CRYPTO_LOCK) {
+ mutexes_[n].lock();
+ } else {
+ mutexes_[n].unlock();
+ }
+ }
+
+ CRYPTO_dynlock_value* dynlockCreateCallback(const(char)* file, int line) {
+ enum size = __traits(classInstanceSize, Mutex);
+ auto mem = malloc(size)[0 .. size];
+ if (!mem) onOutOfMemoryError();
+ GC.addRange(mem.ptr, size);
+ auto mutex = emplace!Mutex(mem);
+ return cast(CRYPTO_dynlock_value*)mutex;
+ }
+
+ void dynlockLockCallback(int mode, CRYPTO_dynlock_value* l,
+ const(char)* file, int line)
+ {
+ if (l is null) return;
+ if (mode & CRYPTO_LOCK) {
+ (cast(Mutex)l).lock();
+ } else {
+ (cast(Mutex)l).unlock();
+ }
+ }
+
+ void dynlockDestroyCallback(CRYPTO_dynlock_value* l,
+ const(char)* file, int line)
+ {
+ GC.removeRange(l);
+ destroy(cast(Mutex)l);
+ free(l);
+ }
+
+ int passwordCallback(char* password, int size, int, void* data) nothrow {
+ auto context = cast(TSSLContext) data;
+ auto userPassword = context.getPassword(size);
+ auto len = userPassword.length;
+ if (len > size) {
+ len = size;
+ }
+ password[0 .. len] = userPassword[0 .. len]; // TODO: \0 handling correct?
+ return cast(int)len;
+ }
+ }
+
+ static __gshared bool initialized_;
+ static __gshared Mutex initMutex_;
+ static __gshared Mutex[] mutexes_;
+ static __gshared uint count_;
+}
+
+/**
+ * Decides whether a remote host is legitimate or not.
+ *
+ * It is usually set at a TSSLContext, which then passes it to all the created
+ * TSSLSockets.
+ */
+class TAccessManager {
+ ///
+ enum Decision {
+ DENY = -1, /// Deny access.
+ SKIP = 0, /// Cannot decide, move on to next check (deny if last).
+ ALLOW = 1 /// Allow access.
+ }
+
+ /**
+ * Determines whether a peer should be granted access or not based on its
+ * IP address.
+ *
+ * Called once after SSL handshake is completes successfully and before peer
+ * certificate is examined.
+ *
+ * If a valid decision (ALLOW or DENY) is returned, the peer certificate
+ * will not be verified.
+ */
+ Decision verify(Address address) {
+ return Decision.DENY;
+ }
+
+ /**
+ * Determines whether a peer should be granted access or not based on a
+ * name from its certificate.
+ *
+ * Called every time a DNS subjectAltName/common name is extracted from the
+ * peer's certificate.
+ *
+ * Params:
+ * host = The actual host name string from the socket connection.
+ * certHost = A host name string from the certificate.
+ */
+ Decision verify(string host, const(char)[] certHost) {
+ return Decision.DENY;
+ }
+
+ /**
+ * Determines whether a peer should be granted access or not based on an IP
+ * address from its certificate.
+ *
+ * Called every time an IP subjectAltName is extracted from the peer's
+ * certificate.
+ *
+ * Params:
+ * address = The actual address from the socket connection.
+ * certHost = A host name string from the certificate.
+ */
+ Decision verify(Address address, ubyte[] certAddress) {
+ return Decision.DENY;
+ }
+}
+
+/**
+ * Default access manager implementation, which just checks the host name
+ * resp. IP address of the connection against the certificate.
+ */
+class TDefaultClientAccessManager : TAccessManager {
+ override Decision verify(Address address) {
+ return Decision.SKIP;
+ }
+
+ override Decision verify(string host, const(char)[] certHost) {
+ if (host.empty || certHost.empty) {
+ return Decision.SKIP;
+ }
+ return (matchName(host, certHost) ? Decision.ALLOW : Decision.SKIP);
+ }
+
+ override Decision verify(Address address, ubyte[] certAddress) {
+ bool match;
+ if (certAddress.length == 4) {
+ if (auto ia = cast(InternetAddress)address) {
+ match = ((cast(ubyte*)ia.addr())[0 .. 4] == certAddress[]);
+ }
+ } else if (certAddress.length == 16) {
+ if (auto ia = cast(Internet6Address)address) {
+ match = (ia.addr() == certAddress[]);
+ }
+ }
+ return (match ? Decision.ALLOW : Decision.SKIP);
+ }
+}
+
+private {
+ /**
+ * Matches a name with a pattern. The pattern may include wildcard. A single
+ * wildcard "*" can match up to one component in the domain name.
+ *
+ * Params:
+ * host = Host name to match, typically the SSL remote peer.
+ * pattern = Host name pattern, typically from the SSL certificate.
+ *
+ * Returns: true if host matches pattern, false otherwise.
+ */
+ bool matchName(const(char)[] host, const(char)[] pattern) {
+ while (!host.empty && !pattern.empty) {
+ if (toUpper(pattern.front) == toUpper(host.front)) {
+ host.popFront;
+ pattern.popFront;
+ } else if (pattern.front == '*') {
+ while (!host.empty && host.front != '.') {
+ host.popFront;
+ }
+ pattern.popFront;
+ } else {
+ break;
+ }
+ }
+ return (host.empty && pattern.empty);
+ }
+
+ unittest {
+ enforce(matchName("thrift.apache.org", "*.apache.org"));
+ enforce(!matchName("thrift.apache.org", "apache.org"));
+ enforce(matchName("thrift.apache.org", "thrift.*.*"));
+ enforce(matchName("", ""));
+ enforce(!matchName("", "*"));
+ }
+}
+
+/**
+ * SSL-level exception.
+ */
+class TSSLException : TTransportException {
+ ///
+ this(string msg, string file = __FILE__, size_t line = __LINE__,
+ Throwable next = null)
+ {
+ super(msg, TTransportException.Type.INTERNAL_ERROR, file, line, next);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/transport/zlib.d b/src/jaegertracing/thrift/lib/d/src/thrift/transport/zlib.d
new file mode 100644
index 000000000..9496f9bf6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/transport/zlib.d
@@ -0,0 +1,497 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+module thrift.transport.zlib;
+
+import core.bitop : bswap;
+import etc.c.zlib;
+import std.algorithm : min;
+import std.array : empty;
+import std.conv : to;
+import std.exception : enforce;
+import thrift.base;
+import thrift.transport.base;
+
+/**
+ * zlib transport. Compresses (deflates) data before writing it to the
+ * underlying transport, and decompresses (inflates) it after reading.
+ */
+final class TZlibTransport : TBaseTransport {
+ // These defaults have yet to be optimized.
+ enum DEFAULT_URBUF_SIZE = 128;
+ enum DEFAULT_CRBUF_SIZE = 1024;
+ enum DEFAULT_UWBUF_SIZE = 128;
+ enum DEFAULT_CWBUF_SIZE = 1024;
+
+ /**
+ * Constructs a new zlib transport.
+ *
+ * Params:
+ * transport = The underlying transport to wrap.
+ * urbufSize = The size of the uncompressed reading buffer, in bytes.
+ * crbufSize = The size of the compressed reading buffer, in bytes.
+ * uwbufSize = The size of the uncompressed writing buffer, in bytes.
+ * cwbufSize = The size of the compressed writing buffer, in bytes.
+ */
+ this(
+ TTransport transport,
+ size_t urbufSize = DEFAULT_URBUF_SIZE,
+ size_t crbufSize = DEFAULT_CRBUF_SIZE,
+ size_t uwbufSize = DEFAULT_UWBUF_SIZE,
+ size_t cwbufSize = DEFAULT_CWBUF_SIZE
+ ) {
+ transport_ = transport;
+
+ enforce(uwbufSize >= MIN_DIRECT_DEFLATE_SIZE, new TTransportException(
+ "TZLibTransport: uncompressed write buffer must be at least " ~
+ to!string(MIN_DIRECT_DEFLATE_SIZE) ~ "bytes in size.",
+ TTransportException.Type.BAD_ARGS));
+
+ urbuf_ = new ubyte[urbufSize];
+ crbuf_ = new ubyte[crbufSize];
+ uwbuf_ = new ubyte[uwbufSize];
+ cwbuf_ = new ubyte[cwbufSize];
+
+ rstream_ = new z_stream;
+ rstream_.next_in = crbuf_.ptr;
+ rstream_.avail_in = 0;
+ rstream_.next_out = urbuf_.ptr;
+ rstream_.avail_out = to!uint(urbuf_.length);
+
+ wstream_ = new z_stream;
+ wstream_.next_in = uwbuf_.ptr;
+ wstream_.avail_in = 0;
+ wstream_.next_out = cwbuf_.ptr;
+ wstream_.avail_out = to!uint(crbuf_.length);
+
+ zlibEnforce(inflateInit(rstream_), rstream_);
+ scope (failure) {
+ zlibLogError(inflateEnd(rstream_), rstream_);
+ }
+
+ zlibEnforce(deflateInit(wstream_, Z_DEFAULT_COMPRESSION), wstream_);
+ }
+
+ ~this() {
+ zlibLogError(inflateEnd(rstream_), rstream_);
+
+ auto result = deflateEnd(wstream_);
+ // Z_DATA_ERROR may indicate unflushed data, so just ignore it.
+ if (result != Z_DATA_ERROR) {
+ zlibLogError(result, wstream_);
+ }
+ }
+
+ /**
+ * Returns the wrapped transport.
+ */
+ TTransport underlyingTransport() @property {
+ return transport_;
+ }
+
+ override bool isOpen() @property {
+ return readAvail > 0 || transport_.isOpen;
+ }
+
+ override bool peek() {
+ return readAvail > 0 || transport_.peek();
+ }
+
+ override void open() {
+ transport_.open();
+ }
+
+ override void close() {
+ transport_.close();
+ }
+
+ override size_t read(ubyte[] buf) {
+ // The C++ implementation suggests to skip urbuf on big reads in future
+ // versions, we would benefit from it as well.
+ auto origLen = buf.length;
+ while (true) {
+ auto give = min(readAvail, buf.length);
+
+ // If std.range.put was optimized for slicable ranges, it could be used
+ // here as well.
+ buf[0 .. give] = urbuf_[urpos_ .. urpos_ + give];
+ buf = buf[give .. $];
+ urpos_ += give;
+
+ auto need = buf.length;
+ if (need == 0) {
+ // We could manage to get the all the data requested.
+ return origLen;
+ }
+
+ if (inputEnded_ || (need < origLen && rstream_.avail_in == 0)) {
+ // We didn't fill buf completely, but there is no more data available.
+ return origLen - need;
+ }
+
+ // Refill our buffer by reading more data through zlib.
+ rstream_.next_out = urbuf_.ptr;
+ rstream_.avail_out = to!uint(urbuf_.length);
+ urpos_ = 0;
+
+ if (!readFromZlib()) {
+ // Couldn't get more data from the underlying transport.
+ return origLen - need;
+ }
+ }
+ }
+
+ override void write(in ubyte[] buf) {
+ enforce(!outputFinished_, new TTransportException(
+ "write() called after finish()", TTransportException.Type.BAD_ARGS));
+
+ auto len = buf.length;
+ if (len > MIN_DIRECT_DEFLATE_SIZE) {
+ flushToZlib(uwbuf_[0 .. uwpos_], Z_NO_FLUSH);
+ uwpos_ = 0;
+ flushToZlib(buf, Z_NO_FLUSH);
+ } else if (len > 0) {
+ if (uwbuf_.length - uwpos_ < len) {
+ flushToZlib(uwbuf_[0 .. uwpos_], Z_NO_FLUSH);
+ uwpos_ = 0;
+ }
+ uwbuf_[uwpos_ .. uwpos_ + len] = buf[];
+ uwpos_ += len;
+ }
+ }
+
+ override void flush() {
+ enforce(!outputFinished_, new TTransportException(
+ "flush() called after finish()", TTransportException.Type.BAD_ARGS));
+
+ flushToTransport(Z_SYNC_FLUSH);
+ }
+
+ override const(ubyte)[] borrow(ubyte* buf, size_t len) {
+ if (len <= readAvail) {
+ return urbuf_[urpos_ .. $];
+ }
+ return null;
+ }
+
+ override void consume(size_t len) {
+ enforce(readAvail >= len, new TTransportException(
+ "consume() did not follow a borrow().", TTransportException.Type.BAD_ARGS));
+ urpos_ += len;
+ }
+
+ /**
+ * Finalize the zlib stream.
+ *
+ * This causes zlib to flush any pending write data and write end-of-stream
+ * information, including the checksum. Once finish() has been called, no
+ * new data can be written to the stream.
+ */
+ void finish() {
+ enforce(!outputFinished_, new TTransportException(
+ "flush() called on already finished TZlibTransport",
+ TTransportException.Type.BAD_ARGS));
+ flushToTransport(Z_FINISH);
+ }
+
+ /**
+ * Verify the checksum at the end of the zlib stream (by finish()).
+ *
+ * May only be called after all data has been read.
+ *
+ * Throws: TTransportException when the checksum is corrupted or there is
+ * still unread data left.
+ */
+ void verifyChecksum() {
+ // If zlib has already reported the end of the stream, the checksum has
+ // been verified, no.
+ if (inputEnded_) return;
+
+ enforce(!readAvail, new TTransportException(
+ "verifyChecksum() called before end of zlib stream",
+ TTransportException.Type.CORRUPTED_DATA));
+
+ rstream_.next_out = urbuf_.ptr;
+ rstream_.avail_out = to!uint(urbuf_.length);
+ urpos_ = 0;
+
+ // readFromZlib() will throw an exception if the checksum is bad.
+ enforce(readFromZlib(), new TTransportException(
+ "checksum not available yet in verifyChecksum()",
+ TTransportException.Type.CORRUPTED_DATA));
+
+ enforce(inputEnded_, new TTransportException(
+ "verifyChecksum() called before end of zlib stream",
+ TTransportException.Type.CORRUPTED_DATA));
+
+ // If we get here, we are at the end of the stream and thus zlib has
+ // successfully verified the checksum.
+ }
+
+private:
+ size_t readAvail() const @property {
+ return urbuf_.length - rstream_.avail_out - urpos_;
+ }
+
+ bool readFromZlib() {
+ assert(!inputEnded_);
+
+ if (rstream_.avail_in == 0) {
+ // zlib has used up all the compressed data we provided in crbuf, read
+ // some more from the underlying transport.
+ auto got = transport_.read(crbuf_);
+ if (got == 0) return false;
+ rstream_.next_in = crbuf_.ptr;
+ rstream_.avail_in = to!uint(got);
+ }
+
+ // We have some compressed data now, uncompress it.
+ auto zlib_result = inflate(rstream_, Z_SYNC_FLUSH);
+ if (zlib_result == Z_STREAM_END) {
+ inputEnded_ = true;
+ } else {
+ zlibEnforce(zlib_result, rstream_);
+ }
+
+ return true;
+ }
+
+ void flushToTransport(int type) {
+ // Compress remaining data in uwbuf_ to cwbuf_.
+ flushToZlib(uwbuf_[0 .. uwpos_], type);
+ uwpos_ = 0;
+
+ // Write all compressed data to the transport.
+ transport_.write(cwbuf_[0 .. $ - wstream_.avail_out]);
+ wstream_.next_out = cwbuf_.ptr;
+ wstream_.avail_out = to!uint(cwbuf_.length);
+
+ // Flush the transport.
+ transport_.flush();
+ }
+
+ void flushToZlib(in ubyte[] buf, int type) {
+ wstream_.next_in = cast(ubyte*)buf.ptr; // zlib only reads, cast is safe.
+ wstream_.avail_in = to!uint(buf.length);
+
+ while (true) {
+ if (type == Z_NO_FLUSH && wstream_.avail_in == 0) {
+ break;
+ }
+
+ if (wstream_.avail_out == 0) {
+ // cwbuf has been exhausted by zlib, flush to the underlying transport.
+ transport_.write(cwbuf_);
+ wstream_.next_out = cwbuf_.ptr;
+ wstream_.avail_out = to!uint(cwbuf_.length);
+ }
+
+ auto zlib_result = deflate(wstream_, type);
+
+ if (type == Z_FINISH && zlib_result == Z_STREAM_END) {
+ assert(wstream_.avail_in == 0);
+ outputFinished_ = true;
+ break;
+ }
+
+ zlibEnforce(zlib_result, wstream_);
+
+ if ((type == Z_SYNC_FLUSH || type == Z_FULL_FLUSH) &&
+ wstream_.avail_in == 0 && wstream_.avail_out != 0) {
+ break;
+ }
+ }
+ }
+
+ static void zlibEnforce(int status, z_stream* stream) {
+ if (status != Z_OK) {
+ throw new TZlibException(status, stream.msg);
+ }
+ }
+
+ static void zlibLogError(int status, z_stream* stream) {
+ if (status != Z_OK) {
+ logError("TZlibTransport: zlib failure in destructor: %s",
+ TZlibException.errorMessage(status, stream.msg));
+ }
+ }
+
+ // Writes smaller than this are buffered up (due to zlib handling overhead).
+ // Larger (or equal) writes are dumped straight to zlib.
+ enum MIN_DIRECT_DEFLATE_SIZE = 32;
+
+ TTransport transport_;
+ z_stream* rstream_;
+ z_stream* wstream_;
+
+ /// Whether zlib has reached the end of the input stream.
+ bool inputEnded_;
+
+ /// Whether the output stream was already finish()ed.
+ bool outputFinished_;
+
+ /// Compressed input data buffer.
+ ubyte[] crbuf_;
+
+ /// Uncompressed input data buffer.
+ ubyte[] urbuf_;
+ size_t urpos_;
+
+ /// Uncompressed output data buffer (where small writes are accumulated
+ /// before handing over to zlib).
+ ubyte[] uwbuf_;
+ size_t uwpos_;
+
+ /// Compressed output data buffer (filled by zlib, we flush it to the
+ /// underlying transport).
+ ubyte[] cwbuf_;
+}
+
+/**
+ * Wraps given transports into TZlibTransports.
+ */
+alias TWrapperTransportFactory!TZlibTransport TZlibTransportFactory;
+
+/**
+ * An INTERNAL_ERROR-type TTransportException originating from an error
+ * signaled by zlib.
+ */
+class TZlibException : TTransportException {
+ this(int statusCode, const(char)* msg) {
+ super(errorMessage(statusCode, msg), TTransportException.Type.INTERNAL_ERROR);
+ zlibStatusCode = statusCode;
+ zlibMsg = msg ? to!string(msg) : "(null)";
+ }
+
+ int zlibStatusCode;
+ string zlibMsg;
+
+ static string errorMessage(int statusCode, const(char)* msg) {
+ string result = "zlib error: ";
+
+ if (msg) {
+ result ~= to!string(msg);
+ } else {
+ result ~= "(no message)";
+ }
+
+ result ~= " (status code = " ~ to!string(statusCode) ~ ")";
+ return result;
+ }
+}
+
+version (unittest) {
+ import std.exception : collectException;
+ import thrift.transport.memory;
+}
+
+// Make sure basic reading/writing works.
+unittest {
+ auto buf = new TMemoryBuffer;
+ auto zlib = new TZlibTransport(buf);
+
+ immutable ubyte[] data = [1, 2, 3, 4, 5];
+ zlib.write(data);
+ zlib.finish();
+
+ auto result = new ubyte[data.length];
+ zlib.readAll(result);
+ enforce(data == result);
+ zlib.verifyChecksum();
+}
+
+// Make sure there is no data is written if write() is never called.
+unittest {
+ auto buf = new TMemoryBuffer;
+ {
+ scope zlib = new TZlibTransport(buf);
+ }
+ enforce(buf.getContents().length == 0);
+}
+
+// Make sure calling write()/flush()/finish() again after finish() throws.
+unittest {
+ auto buf = new TMemoryBuffer;
+ auto zlib = new TZlibTransport(buf);
+
+ zlib.write([1, 2, 3, 4, 5]);
+ zlib.finish();
+
+ auto ex = collectException!TTransportException(zlib.write([6]));
+ enforce(ex && ex.type == TTransportException.Type.BAD_ARGS);
+
+ ex = collectException!TTransportException(zlib.flush());
+ enforce(ex && ex.type == TTransportException.Type.BAD_ARGS);
+
+ ex = collectException!TTransportException(zlib.finish());
+ enforce(ex && ex.type == TTransportException.Type.BAD_ARGS);
+}
+
+// Make sure verifying the checksum works even if it requires starting a new
+// reading buffer after reading the payload has already been completed.
+unittest {
+ auto buf = new TMemoryBuffer;
+ auto zlib = new TZlibTransport(buf);
+
+ immutable ubyte[] data = [1, 2, 3, 4, 5];
+ zlib.write(data);
+ zlib.finish();
+
+ zlib = new TZlibTransport(buf, TZlibTransport.DEFAULT_URBUF_SIZE,
+ buf.getContents().length - 1); // The last byte belongs to the checksum.
+
+ auto result = new ubyte[data.length];
+ zlib.readAll(result);
+ enforce(data == result);
+
+ zlib.verifyChecksum();
+}
+
+// Make sure verifyChecksum() throws if we messed with the checksum.
+unittest {
+ import std.stdio;
+ import thrift.transport.range;
+
+ auto buf = new TMemoryBuffer;
+ auto zlib = new TZlibTransport(buf);
+
+ immutable ubyte[] data = [1, 2, 3, 4, 5];
+ zlib.write(data);
+ zlib.finish();
+
+ void testCorrupted(const(ubyte)[] corruptedData) {
+ auto reader = new TZlibTransport(tInputRangeTransport(corruptedData));
+ auto result = new ubyte[data.length];
+ try {
+ reader.readAll(result);
+
+ // If it does read without complaining, the result should be correct.
+ enforce(result == data);
+ } catch (TZlibException e) {}
+
+ auto ex = collectException!TTransportException(reader.verifyChecksum());
+ enforce(ex && ex.type == TTransportException.Type.CORRUPTED_DATA);
+ }
+
+ testCorrupted(buf.getContents()[0 .. $ - 1]);
+
+ auto modified = buf.getContents().dup;
+ ++modified[$ - 1];
+ testCorrupted(modified);
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/util/awaitable.d b/src/jaegertracing/thrift/lib/d/src/thrift/util/awaitable.d
new file mode 100644
index 000000000..38436ee38
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/util/awaitable.d
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.util.awaitable;
+
+import core.sync.condition;
+import core.sync.mutex;
+import core.time : Duration;
+import std.exception : enforce;
+import std.socket/+ : Socket, socketPair+/; // DMD @@BUG314@@
+import thrift.base;
+
+// To avoid DMD @@BUG6395@@.
+import thrift.internal.algorithm;
+
+/**
+ * An event that can occur at some point in the future and which can be
+ * awaited, either by blocking until it occurs, or by registering a callback
+ * delegate.
+ */
+interface TAwaitable {
+ /**
+ * Waits until the event occurs.
+ *
+ * Calling wait() for an event that has already occurred is a no-op.
+ */
+ void wait();
+
+ /**
+ * Waits until the event occurs or the specified timeout expires.
+ *
+ * Calling wait() for an event that has already occurred is a no-op.
+ *
+ * Returns: Whether the event was triggered before the timeout expired.
+ */
+ bool wait(Duration timeout);
+
+ /**
+ * Registers a callback that is called if the event occurs.
+ *
+ * The delegate will likely be invoked from a different thread, and is
+ * expected not to perform expensive work as it will usually be invoked
+ * synchronously by the notifying thread. The order in which registered
+ * callbacks are invoked is not specified.
+ *
+ * The callback must never throw, but nothrow semantics are difficult to
+ * enforce, so currently exceptions are just swallowed by
+ * TAwaitable implementations.
+ *
+ * If the event has already occurred, the delegate is immediately executed
+ * in the current thread.
+ */
+ void addCallback(void delegate() dg);
+
+ /**
+ * Removes a previously added callback.
+ *
+ * Returns: Whether the callback could be found in the list, i.e. whether it
+ * was previously added.
+ */
+ bool removeCallback(void delegate() dg);
+}
+
+/**
+ * A simple TAwaitable event triggered by just calling a trigger() method.
+ */
+class TOneshotEvent : TAwaitable {
+ this() {
+ mutex_ = new Mutex;
+ condition_ = new Condition(mutex_);
+ }
+
+ override void wait() {
+ synchronized (mutex_) {
+ while (!triggered_) condition_.wait();
+ }
+ }
+
+ override bool wait(Duration timeout) {
+ synchronized (mutex_) {
+ if (triggered_) return true;
+ condition_.wait(timeout);
+ return triggered_;
+ }
+ }
+
+ override void addCallback(void delegate() dg) {
+ mutex_.lock();
+ scope (failure) mutex_.unlock();
+
+ callbacks_ ~= dg;
+
+ if (triggered_) {
+ mutex_.unlock();
+ dg();
+ return;
+ }
+
+ mutex_.unlock();
+ }
+
+ override bool removeCallback(void delegate() dg) {
+ synchronized (mutex_) {
+ auto oldLength = callbacks_.length;
+ callbacks_ = removeEqual(callbacks_, dg);
+ return callbacks_.length < oldLength;
+ }
+ }
+
+ /**
+ * Triggers the event.
+ *
+ * Any registered event callbacks are executed synchronously before the
+ * function returns.
+ */
+ void trigger() {
+ synchronized (mutex_) {
+ if (!triggered_) {
+ triggered_ = true;
+ condition_.notifyAll();
+ foreach (c; callbacks_) c();
+ }
+ }
+ }
+
+private:
+ bool triggered_;
+ Mutex mutex_;
+ Condition condition_;
+ void delegate()[] callbacks_;
+}
+
+/**
+ * Translates TAwaitable events into dummy messages on a socket that can be
+ * used e.g. to wake up from a select() call.
+ */
+final class TSocketNotifier {
+ this() {
+ auto socks = socketPair();
+ foreach (s; socks) s.blocking = false;
+ sendSocket_ = socks[0];
+ recvSocket_ = socks[1];
+ }
+
+ /**
+ * The socket the messages will be sent to.
+ */
+ Socket socket() @property {
+ return recvSocket_;
+ }
+
+ /**
+ * Atatches the socket notifier to the specified awaitable, causing it to
+ * write a byte to the notification socket when the awaitable callbacks are
+ * invoked.
+ *
+ * If the event has already been triggered, the dummy byte is written
+ * immediately to the socket.
+ *
+ * A socket notifier can only be attached to a single awaitable at a time.
+ *
+ * Throws: TException if the socket notifier is already attached.
+ */
+ void attach(TAwaitable awaitable) {
+ enforce(!awaitable_, new TException("Already attached."));
+ awaitable.addCallback(&notify);
+ awaitable_ = awaitable;
+ }
+
+ /**
+ * Detaches the socket notifier from the awaitable it is currently attached
+ * to.
+ *
+ * Throws: TException if the socket notifier is not currently attached.
+ */
+ void detach() {
+ enforce(awaitable_, new TException("Not attached."));
+
+ // Soak up any not currently read notification bytes.
+ ubyte[1] dummy = void;
+ while (recvSocket_.receive(dummy) != Socket.ERROR) {}
+
+ auto couldRemove = awaitable_.removeCallback(&notify);
+ assert(couldRemove);
+ awaitable_ = null;
+ }
+
+private:
+ void notify() {
+ ubyte[1] zero;
+ sendSocket_.send(zero);
+ }
+
+ TAwaitable awaitable_;
+ Socket sendSocket_;
+ Socket recvSocket_;
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/util/cancellation.d b/src/jaegertracing/thrift/lib/d/src/thrift/util/cancellation.d
new file mode 100644
index 000000000..62552364d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/util/cancellation.d
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.util.cancellation;
+
+import core.atomic;
+import thrift.base;
+import thrift.util.awaitable;
+
+/**
+ * A cancellation request for asynchronous or blocking synchronous operations.
+ *
+ * It is passed to the entity creating an operation, which will usually monitor
+ * it either by polling or by adding event handlers, and cancel the operation
+ * if it is triggered.
+ *
+ * For synchronous operations, this usually means either throwing a
+ * TCancelledException or immediately returning, depending on whether
+ * cancellation is an expected part of the task outcome or not. For
+ * asynchronous operations, cancellation typically entails stopping background
+ * work and cancelling a result future, if not already completed.
+ *
+ * An operation accepting a TCancellation does not need to guarantee that it
+ * will actually be able to react to the cancellation request.
+ */
+interface TCancellation {
+ /**
+ * Whether the cancellation request has been triggered.
+ */
+ bool triggered() const @property;
+
+ /**
+ * Throws a TCancelledException if the cancellation request has already been
+ * triggered.
+ */
+ void throwIfTriggered() const;
+
+ /**
+ * A TAwaitable that can be used to wait for cancellation triggering.
+ */
+ TAwaitable triggering() @property;
+}
+
+/**
+ * The origin of a cancellation request, which provides a way to actually
+ * trigger it.
+ *
+ * This design allows operations to pass the TCancellation on to sub-tasks,
+ * while making sure that the cancellation can only be triggered by the
+ * »outermost« instance waiting for the result.
+ */
+final class TCancellationOrigin : TCancellation {
+ this() {
+ event_ = new TOneshotEvent;
+ }
+
+ /**
+ * Triggers the cancellation request.
+ */
+ void trigger() {
+ atomicStore(triggered_, true);
+ event_.trigger();
+ }
+
+ /+override+/ bool triggered() const @property {
+ return atomicLoad(triggered_);
+ }
+
+ /+override+/ void throwIfTriggered() const {
+ if (triggered) throw new TCancelledException;
+ }
+
+ /+override+/ TAwaitable triggering() @property {
+ return event_;
+ }
+
+private:
+ shared bool triggered_;
+ TOneshotEvent event_;
+}
+
+///
+class TCancelledException : TException {
+ ///
+ this(string msg = null, string file = __FILE__, size_t line = __LINE__,
+ Throwable next = null
+ ) {
+ super(msg ? msg : "The operation has been cancelled.", file, line, next);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/util/future.d b/src/jaegertracing/thrift/lib/d/src/thrift/util/future.d
new file mode 100644
index 000000000..2b32a01f3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/util/future.d
@@ -0,0 +1,549 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.util.future;
+
+import core.atomic;
+import core.sync.condition;
+import core.sync.mutex;
+import core.time : Duration;
+import std.array : empty, front, popFront;
+import std.conv : to;
+import std.exception : enforce;
+import std.traits : BaseTypeTuple, isSomeFunction, ParameterTypeTuple, ReturnType;
+import thrift.base;
+import thrift.util.awaitable;
+import thrift.util.cancellation;
+
+/**
+ * Represents an operation which is executed asynchronously and the result of
+ * which will become available at some point in the future.
+ *
+ * Once a operation is completed, the result of the operation can be fetched
+ * via the get() family of methods. There are three possible cases: Either the
+ * operation succeeded, then its return value is returned, or it failed by
+ * throwing, in which case the exception is rethrown, or it was cancelled
+ * before, then a TCancelledException is thrown. There might be TFuture
+ * implementations which never possibly enter the cancelled state.
+ *
+ * All methods are thread-safe, but keep in mind that any exception object or
+ * result (if it is a reference type, of course) is shared between all
+ * get()-family invocations.
+ */
+interface TFuture(ResultType) {
+ /**
+ * The status the operation is currently in.
+ *
+ * An operation starts out in RUNNING status, and changes state to one of the
+ * others at most once afterwards.
+ */
+ TFutureStatus status() @property;
+
+ /**
+ * A TAwaitable triggered when the operation leaves the RUNNING status.
+ */
+ TAwaitable completion() @property;
+
+ /**
+ * Convenience shorthand for waiting until the result is available and then
+ * get()ing it.
+ *
+ * If the operation has already completed, the result is immediately
+ * returned.
+ *
+ * The result of this method is »alias this«'d to the interface, so that
+ * TFuture can be used as a drop-in replacement for a simple value in
+ * synchronous code.
+ */
+ final ResultType waitGet() {
+ completion.wait();
+ return get();
+ }
+ final @property auto waitGetProperty() { return waitGet(); }
+ alias waitGetProperty this;
+
+ /**
+ * Convenience shorthand for waiting until the result is available and then
+ * get()ing it.
+ *
+ * If the operation completes in time, returns its result (resp. throws an
+ * exception for the failed/cancelled cases). If not, throws a
+ * TFutureException.
+ */
+ final ResultType waitGet(Duration timeout) {
+ enforce(completion.wait(timeout), new TFutureException(
+ "Operation did not complete in time."));
+ return get();
+ }
+
+ /**
+ * Returns the result of the operation.
+ *
+ * Throws: TFutureException if the operation has been cancelled,
+ * TCancelledException if it is not yet done; the set exception if it
+ * failed.
+ */
+ ResultType get();
+
+ /**
+ * Returns the captured exception if the operation failed, or null otherwise.
+ *
+ * Throws: TFutureException if not yet done, TCancelledException if the
+ * operation has been cancelled.
+ */
+ Exception getException();
+}
+
+/**
+ * The states the operation offering a future interface can be in.
+ */
+enum TFutureStatus : byte {
+ RUNNING, /// The operation is still running.
+ SUCCEEDED, /// The operation completed without throwing an exception.
+ FAILED, /// The operation completed by throwing an exception.
+ CANCELLED /// The operation was cancelled.
+}
+
+/**
+ * A TFuture covering the simple but common case where the result is simply
+ * set by a call to succeed()/fail().
+ *
+ * All methods are thread-safe, but usually, succeed()/fail() are only called
+ * from a single thread (different from the thread(s) waiting for the result
+ * using the TFuture interface, though).
+ */
+class TPromise(ResultType) : TFuture!ResultType {
+ this() {
+ statusMutex_ = new Mutex;
+ completionEvent_ = new TOneshotEvent;
+ }
+
+ override S status() const @property {
+ return atomicLoad(status_);
+ }
+
+ override TAwaitable completion() @property {
+ return completionEvent_;
+ }
+
+ override ResultType get() {
+ auto s = atomicLoad(status_);
+ enforce(s != S.RUNNING,
+ new TFutureException("Operation not yet completed."));
+
+ if (s == S.CANCELLED) throw new TCancelledException;
+ if (s == S.FAILED) throw exception_;
+
+ static if (!is(ResultType == void)) {
+ return result_;
+ }
+ }
+
+ override Exception getException() {
+ auto s = atomicLoad(status_);
+ enforce(s != S.RUNNING,
+ new TFutureException("Operation not yet completed."));
+
+ if (s == S.CANCELLED) throw new TCancelledException;
+ if (s == S.SUCCEEDED) return null;
+
+ return exception_;
+ }
+
+ static if (!is(ResultType == void)) {
+ /**
+ * Sets the result of the operation, marks it as done, and notifies any
+ * waiters.
+ *
+ * If the operation has been cancelled before, nothing happens.
+ *
+ * Throws: TFutureException if the operation is already completed.
+ */
+ void succeed(ResultType result) {
+ synchronized (statusMutex_) {
+ auto s = atomicLoad(status_);
+ if (s == S.CANCELLED) return;
+
+ enforce(s == S.RUNNING,
+ new TFutureException("Operation already completed."));
+ result_ = result;
+
+ atomicStore(status_, S.SUCCEEDED);
+ }
+
+ completionEvent_.trigger();
+ }
+ } else {
+ void succeed() {
+ synchronized (statusMutex_) {
+ auto s = atomicLoad(status_);
+ if (s == S.CANCELLED) return;
+
+ enforce(s == S.RUNNING,
+ new TFutureException("Operation already completed."));
+
+ atomicStore(status_, S.SUCCEEDED);
+ }
+
+ completionEvent_.trigger();
+ }
+ }
+
+ /**
+ * Marks the operation as failed with the specified exception and notifies
+ * any waiters.
+ *
+ * If the operation was already cancelled, nothing happens.
+ *
+ * Throws: TFutureException if the operation is already completed.
+ */
+ void fail(Exception exception) {
+ synchronized (statusMutex_) {
+ auto status = atomicLoad(status_);
+ if (status == S.CANCELLED) return;
+
+ enforce(status == S.RUNNING,
+ new TFutureException("Operation already completed."));
+ exception_ = exception;
+
+ atomicStore(status_, S.FAILED);
+ }
+
+ completionEvent_.trigger();
+ }
+
+
+ /**
+ * Marks this operation as completed and takes over the outcome of another
+ * TFuture of the same type.
+ *
+ * If this operation was already cancelled, nothing happens. If the other
+ * operation was cancelled, this operation is marked as failed with a
+ * TCancelledException.
+ *
+ * Throws: TFutureException if the passed in future was not completed or
+ * this operation is already completed.
+ */
+ void complete(TFuture!ResultType future) {
+ synchronized (statusMutex_) {
+ auto status = atomicLoad(status_);
+ if (status == S.CANCELLED) return;
+ enforce(status == S.RUNNING,
+ new TFutureException("Operation already completed."));
+
+ enforce(future.status != S.RUNNING, new TFutureException(
+ "The passed TFuture is not yet completed."));
+
+ status = future.status;
+ if (status == S.CANCELLED) {
+ status = S.FAILED;
+ exception_ = new TCancelledException;
+ } else if (status == S.FAILED) {
+ exception_ = future.getException();
+ } else static if (!is(ResultType == void)) {
+ result_ = future.get();
+ }
+
+ atomicStore(status_, status);
+ }
+
+ completionEvent_.trigger();
+ }
+
+ /**
+ * Marks this operation as cancelled and notifies any waiters.
+ *
+ * If the operation is already completed, nothing happens.
+ */
+ void cancel() {
+ synchronized (statusMutex_) {
+ auto status = atomicLoad(status_);
+ if (status == S.RUNNING) atomicStore(status_, S.CANCELLED);
+ }
+
+ completionEvent_.trigger();
+ }
+
+private:
+ // Convenience alias because TFutureStatus is ubiquitous in this class.
+ alias TFutureStatus S;
+
+ // The status the promise is currently in.
+ shared S status_;
+
+ union {
+ static if (!is(ResultType == void)) {
+ // Set if status_ is SUCCEEDED.
+ ResultType result_;
+ }
+ // Set if status_ is FAILED.
+ Exception exception_;
+ }
+
+ // Protects status_.
+ // As for result_ and exception_: They are only set once, while status_ is
+ // still RUNNING, so given that the operation has already completed, reading
+ // them is safe without holding some kind of lock.
+ Mutex statusMutex_;
+
+ // Triggered when the event completes.
+ TOneshotEvent completionEvent_;
+}
+
+///
+class TFutureException : TException {
+ ///
+ this(string msg = "", string file = __FILE__, size_t line = __LINE__,
+ Throwable next = null)
+ {
+ super(msg, file, line, next);
+ }
+}
+
+/**
+ * Creates an interface that is similar to a given one, but accepts an
+ * additional, optional TCancellation parameter each method, and returns
+ * TFutures instead of plain return values.
+ *
+ * For example, given the following declarations:
+ * ---
+ * interface Foo {
+ * void bar();
+ * string baz(int a);
+ * }
+ * alias TFutureInterface!Foo FutureFoo;
+ * ---
+ *
+ * FutureFoo would be equivalent to:
+ * ---
+ * interface FutureFoo {
+ * TFuture!void bar(TCancellation cancellation = null);
+ * TFuture!string baz(int a, TCancellation cancellation = null);
+ * }
+ * ---
+ */
+template TFutureInterface(Interface) if (is(Interface _ == interface)) {
+ mixin({
+ string code = "interface TFutureInterface \n";
+
+ static if (is(Interface Bases == super) && Bases.length > 0) {
+ code ~= ": ";
+ foreach (i; 0 .. Bases.length) {
+ if (i > 0) code ~= ", ";
+ code ~= "TFutureInterface!(BaseTypeTuple!Interface[" ~ to!string(i) ~ "]) ";
+ }
+ }
+
+ code ~= "{\n";
+
+ foreach (methodName; __traits(derivedMembers, Interface)) {
+ enum qn = "Interface." ~ methodName;
+ static if (isSomeFunction!(mixin(qn))) {
+ code ~= "TFuture!(ReturnType!(" ~ qn ~ ")) " ~ methodName ~
+ "(ParameterTypeTuple!(" ~ qn ~ "), TCancellation cancellation = null);\n";
+ }
+ }
+
+ code ~= "}\n";
+ return code;
+ }());
+}
+
+/**
+ * An input range that aggregates results from multiple asynchronous operations,
+ * returning them in the order they arrive.
+ *
+ * Additionally, a timeout can be set after which results from not yet finished
+ * futures will no longer be waited for, e.g. to ensure the time it takes to
+ * iterate over a set of results is limited.
+ */
+final class TFutureAggregatorRange(T) {
+ /**
+ * Constructs a new instance.
+ *
+ * Params:
+ * futures = The set of futures to collect results from.
+ * timeout = If positive, not yet finished futures will be cancelled and
+ * their results will not be taken into account.
+ */
+ this(TFuture!T[] futures, TCancellationOrigin childCancellation,
+ Duration timeout = dur!"hnsecs"(0)
+ ) {
+ if (timeout > dur!"hnsecs"(0)) {
+ timeoutSysTick_ = TickDuration.currSystemTick +
+ TickDuration.from!"hnsecs"(timeout.total!"hnsecs");
+ } else {
+ timeoutSysTick_ = TickDuration(0);
+ }
+
+ queueMutex_ = new Mutex;
+ queueNonEmptyCondition_ = new Condition(queueMutex_);
+ futures_ = futures;
+ childCancellation_ = childCancellation;
+
+ foreach (future; futures_) {
+ future.completion.addCallback({
+ auto f = future;
+ return {
+ if (f.status == TFutureStatus.CANCELLED) return;
+ assert(f.status != TFutureStatus.RUNNING);
+
+ synchronized (queueMutex_) {
+ completedQueue_ ~= f;
+
+ if (completedQueue_.length == 1) {
+ queueNonEmptyCondition_.notifyAll();
+ }
+ }
+ };
+ }());
+ }
+ }
+
+ /**
+ * Whether the range is empty.
+ *
+ * This is the case if the results from the completed futures not having
+ * failed have already been popped and either all future have been finished
+ * or the timeout has expired.
+ *
+ * Potentially blocks until a new result is available or the timeout has
+ * expired.
+ */
+ bool empty() @property {
+ if (finished_) return true;
+ if (bufferFilled_) return false;
+
+ while (true) {
+ TFuture!T future;
+ synchronized (queueMutex_) {
+ // The while loop is just being cautious about spurious wakeups, in
+ // case they should be possible.
+ while (completedQueue_.empty) {
+ auto remaining = to!Duration(timeoutSysTick_ -
+ TickDuration.currSystemTick);
+
+ if (remaining <= dur!"hnsecs"(0)) {
+ // No time left, but still no element received – we are empty now.
+ finished_ = true;
+ childCancellation_.trigger();
+ return true;
+ }
+
+ queueNonEmptyCondition_.wait(remaining);
+ }
+
+ future = completedQueue_.front;
+ completedQueue_.popFront();
+ }
+
+ ++completedCount_;
+ if (completedCount_ == futures_.length) {
+ // This was the last future in the list, there is no possibility
+ // another result could ever become available.
+ finished_ = true;
+ }
+
+ if (future.status == TFutureStatus.FAILED) {
+ // This one failed, loop again and try getting another item from
+ // the queue.
+ exceptions_ ~= future.getException();
+ } else {
+ resultBuffer_ = future.get();
+ bufferFilled_ = true;
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Returns the first element from the range.
+ *
+ * Potentially blocks until a new result is available or the timeout has
+ * expired.
+ *
+ * Throws: TException if the range is empty.
+ */
+ T front() {
+ enforce(!empty, new TException(
+ "Cannot get front of an empty future aggregator range."));
+ return resultBuffer_;
+ }
+
+ /**
+ * Removes the first element from the range.
+ *
+ * Potentially blocks until a new result is available or the timeout has
+ * expired.
+ *
+ * Throws: TException if the range is empty.
+ */
+ void popFront() {
+ enforce(!empty, new TException(
+ "Cannot pop front of an empty future aggregator range."));
+ bufferFilled_ = false;
+ }
+
+ /**
+ * The number of futures the result of which has been returned or which have
+ * failed so far.
+ */
+ size_t completedCount() @property const {
+ return completedCount_;
+ }
+
+ /**
+ * The exceptions collected from failed TFutures so far.
+ */
+ Exception[] exceptions() @property {
+ return exceptions_;
+ }
+
+private:
+ TFuture!T[] futures_;
+ TCancellationOrigin childCancellation_;
+
+ // The system tick this operation will time out, or zero if no timeout has
+ // been set.
+ TickDuration timeoutSysTick_;
+
+ bool finished_;
+
+ bool bufferFilled_;
+ T resultBuffer_;
+
+ Exception[] exceptions_;
+ size_t completedCount_;
+
+ // The queue of completed futures. This (and the associated condition) are
+ // the only parts of this class that are accessed by multiple threads.
+ TFuture!T[] completedQueue_;
+ Mutex queueMutex_;
+ Condition queueNonEmptyCondition_;
+}
+
+/**
+ * TFutureAggregatorRange construction helper to avoid having to explicitly
+ * specify the value type, i.e. to allow the constructor being called using IFTI
+ * (see $(DMDBUG 6082, D Bugzilla enhancement requet 6082)).
+ */
+TFutureAggregatorRange!T tFutureAggregatorRange(T)(TFuture!T[] futures,
+ TCancellationOrigin childCancellation, Duration timeout = dur!"hnsecs"(0)
+) {
+ return new TFutureAggregatorRange!T(futures, childCancellation, timeout);
+}
diff --git a/src/jaegertracing/thrift/lib/d/src/thrift/util/hashset.d b/src/jaegertracing/thrift/lib/d/src/thrift/util/hashset.d
new file mode 100644
index 000000000..ede122ef1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/src/thrift/util/hashset.d
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift.util.hashset;
+
+import std.algorithm : joiner, map;
+import std.conv : to;
+import std.traits : isImplicitlyConvertible, ParameterTypeTuple;
+import std.range : ElementType, isInputRange;
+
+struct Void {}
+
+/**
+ * A quickly hacked together hash set implementation backed by built-in
+ * associative arrays to have something to compile Thrift's set<> to until
+ * std.container gains something suitable.
+ */
+// Note: The funky pointer casts (i.e. *(cast(immutable(E)*)&e) instead of
+// just cast(immutable(E))e) are a workaround for LDC 2 compatibility.
+final class HashSet(E) {
+ ///
+ this() {}
+
+ ///
+ this(E[] elems...) {
+ insert(elems);
+ }
+
+ ///
+ void insert(Stuff)(Stuff stuff) if (isImplicitlyConvertible!(Stuff, E)) {
+ aa_[*(cast(immutable(E)*)&stuff)] = Void.init;
+ }
+
+ ///
+ void insert(Stuff)(Stuff stuff) if (
+ isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, E)
+ ) {
+ foreach (e; stuff) {
+ aa_[*(cast(immutable(E)*)&e)] = Void.init;
+ }
+ }
+
+ ///
+ void opOpAssign(string op : "~", Stuff)(Stuff stuff) {
+ insert(stuff);
+ }
+
+ ///
+ void remove(E e) {
+ aa_.remove(*(cast(immutable(E)*)&e));
+ }
+ alias remove removeKey;
+
+ ///
+ void removeAll() {
+ aa_ = null;
+ }
+
+ ///
+ size_t length() @property const {
+ return aa_.length;
+ }
+
+ ///
+ size_t empty() @property const {
+ return !aa_.length;
+ }
+
+ ///
+ bool opBinaryRight(string op : "in")(E e) const {
+ return (e in aa_) !is null;
+ }
+
+ ///
+ auto opSlice() const {
+ // TODO: Implement using AA key range once available in release DMD/druntime
+ // to avoid allocation.
+ return cast(E[])(aa_.keys);
+ }
+
+ ///
+ override string toString() const {
+ // Only provide toString() if to!string() is available for E (exceptions are
+ // e.g. delegates).
+ static if (is(typeof(to!string(E.init)) : string)) {
+ return "{" ~ to!string(joiner(map!`to!string(a)`(aa_.keys), ", ")) ~ "}";
+ } else {
+ // Cast to work around Object not being const-correct.
+ return (cast()super).toString();
+ }
+ }
+
+ ///
+ override bool opEquals(Object other) const {
+ auto rhs = cast(const(HashSet))other;
+ if (rhs) {
+ return aa_ == rhs.aa_;
+ }
+
+ // Cast to work around Object not being const-correct.
+ return (cast()super).opEquals(other);
+ }
+
+private:
+ Void[immutable(E)] aa_;
+}
+
+/// Ditto
+auto hashSet(E)(E[] elems...) {
+ return new HashSet!E(elems);
+}
+
+unittest {
+ import std.exception;
+
+ auto a = hashSet(1, 2, 2, 3);
+ enforce(a.length == 3);
+ enforce(2 in a);
+ enforce(5 !in a);
+ enforce(a.toString().length == 9);
+ a.remove(2);
+ enforce(a.length == 2);
+ enforce(2 !in a);
+ a.removeAll();
+ enforce(a.empty);
+ enforce(a.toString() == "{}");
+
+ void delegate() dg;
+ auto b = hashSet(dg);
+ static assert(__traits(compiles, b.toString()));
+}
diff --git a/src/jaegertracing/thrift/lib/d/test/Makefile.am b/src/jaegertracing/thrift/lib/d/test/Makefile.am
new file mode 100755
index 000000000..5ec8255bb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/test/Makefile.am
@@ -0,0 +1,112 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+AUTOMAKE_OPTIONS = serial-tests
+
+# Thrift compiler rules
+
+debug_proto_gen = $(addprefix gen-d/, DebugProtoTest_types.d)
+
+$(debug_proto_gen): $(top_srcdir)/test/DebugProtoTest.thrift
+ $(THRIFT) --gen d -nowarn $<
+
+stress_test_gen = $(addprefix gen-d/thrift/test/stress/, Service.d \
+ StressTest_types.d)
+
+$(stress_test_gen): $(top_srcdir)/test/StressTest.thrift
+ $(THRIFT) --gen d $<
+
+thrift_test_gen = $(addprefix gen-d/thrift/test/, SecondService.d \
+ ThriftTest.d ThriftTest_constants.d ThriftTest_types.d)
+
+$(thrift_test_gen): $(top_srcdir)/test/ThriftTest.thrift
+ $(THRIFT) --gen d $<
+
+
+# The actual test targets.
+# There just must be some way to reassign a variable without warnings in
+# Automake...
+targets__ = async_test client_pool_test serialization_benchmark \
+ stress_test_server thrift_test_client thrift_test_server transport_test
+ran_tests__ = client_pool_test \
+ transport_test \
+ async_test_runner.sh \
+ thrift_test_runner.sh
+
+libevent_dependent_targets = async_test_client client_pool_test \
+ stress_test_server thrift_test_server
+libevent_dependent_ran_tests = client_pool_test async_test_runner.sh thrift_test_runner.sh
+
+openssl_dependent_targets = async_test thrift_test_client thrift_test_server
+openssl_dependent_ran_tests = async_test_runner.sh thrift_test_runner.sh
+
+d_test_flags =
+
+if WITH_D_EVENT_TESTS
+d_test_flags += $(DMD_LIBEVENT_FLAGS) ../$(D_EVENT_LIB_NAME)
+targets_ = $(targets__)
+ran_tests_ = $(ran_tests__)
+else
+targets_ = $(filter-out $(libevent_dependent_targets), $(targets__))
+ran_tests_ = $(filter-out $(libevent_dependent_ran_tests), $(ran_tests__))
+endif
+
+if WITH_D_SSL_TESTS
+d_test_flags += $(DMD_OPENSSL_FLAGS) ../$(D_SSL_LIB_NAME)
+targets = $(targets_)
+ran_tests = $(ran_tests_)
+else
+targets = $(filter-out $(openssl_dependent_targets), $(targets_))
+ran_tests = $(filter-out $(openssl_dependent_ran_tests), $(ran_tests_))
+endif
+
+d_test_flags += -w -wi -O -release -inline -I$(top_srcdir)/lib/d/src -Igen-d \
+ $(top_builddir)/lib/d/$(D_LIB_NAME)
+
+
+async_test client_pool_test transport_test: %: %.d
+ $(DMD) $(d_test_flags) -of$@ $^
+
+serialization_benchmark: %: %.d $(debug_proto_gen)
+ $(DMD) $(d_test_flags) -of$@ $^
+
+stress_test_server: %: %.d test_utils.d $(stress_test_gen)
+ $(DMD) $(d_test_flags) -of$@ $^
+
+thrift_test_client: %: %.d thrift_test_common.d $(thrift_test_gen)
+ $(DMD) $(d_test_flags) -of$@ $^
+
+thrift_test_server: %: %.d thrift_test_common.d test_utils.d $(thrift_test_gen)
+ $(DMD) $(d_test_flags) -of$@ $^
+
+
+check-local: $(targets)
+
+clean-local:
+ $(RM) -rf gen-d $(targets) $(addsuffix .o, $(targets))
+
+
+# Tests ran as part of make check.
+
+async_test_runner.sh: async_test
+thrift_test_runner.sh: thrift_test_client thrift_test_server
+
+TESTS = $(ran_tests)
+
+precross: $(targets)
diff --git a/src/jaegertracing/thrift/lib/d/test/async_test.d b/src/jaegertracing/thrift/lib/d/test/async_test.d
new file mode 100644
index 000000000..51529ba86
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/test/async_test.d
@@ -0,0 +1,396 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless enforced by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module async_test;
+
+import core.atomic;
+import core.sync.condition : Condition;
+import core.sync.mutex : Mutex;
+import core.thread : dur, Thread, ThreadGroup;
+import std.conv : text;
+import std.datetime;
+import std.getopt;
+import std.exception : collectException, enforce;
+import std.parallelism : TaskPool;
+import std.stdio;
+import std.string;
+import std.variant : Variant;
+import thrift.base;
+import thrift.async.base;
+import thrift.async.libevent;
+import thrift.async.socket;
+import thrift.async.ssl;
+import thrift.codegen.async_client;
+import thrift.codegen.async_client_pool;
+import thrift.codegen.base;
+import thrift.codegen.processor;
+import thrift.protocol.base;
+import thrift.protocol.binary;
+import thrift.server.base;
+import thrift.server.simple;
+import thrift.server.transport.socket;
+import thrift.server.transport.ssl;
+import thrift.transport.base;
+import thrift.transport.buffered;
+import thrift.transport.ssl;
+import thrift.util.cancellation;
+
+version (Posix) {
+ import core.stdc.signal;
+ import core.sys.posix.signal;
+
+ // Disable SIGPIPE because SSL server will write to broken socket after
+ // client disconnected (see TSSLSocket docs).
+ shared static this() {
+ signal(SIGPIPE, SIG_IGN);
+ }
+}
+
+interface AsyncTest {
+ string echo(string value);
+ string delayedEcho(string value, long milliseconds);
+
+ void fail(string reason);
+ void delayedFail(string reason, long milliseconds);
+
+ enum methodMeta = [
+ TMethodMeta("fail", [], [TExceptionMeta("ate", 1, "AsyncTestException")]),
+ TMethodMeta("delayedFail", [], [TExceptionMeta("ate", 1, "AsyncTestException")])
+ ];
+ alias .AsyncTestException AsyncTestException;
+}
+
+class AsyncTestException : TException {
+ string reason;
+ mixin TStructHelpers!();
+}
+
+void main(string[] args) {
+ ushort port = 9090;
+ ushort managerCount = 2;
+ ushort serversPerManager = 5;
+ ushort threadsPerServer = 10;
+ uint iterations = 10;
+ bool ssl;
+ bool trace;
+
+ getopt(args,
+ "iterations", &iterations,
+ "managers", &managerCount,
+ "port", &port,
+ "servers-per-manager", &serversPerManager,
+ "ssl", &ssl,
+ "threads-per-server", &threadsPerServer,
+ "trace", &trace,
+ );
+
+ TTransportFactory clientTransportFactory;
+ TSSLContext serverSSLContext;
+ if (ssl) {
+ auto clientSSLContext = new TSSLContext();
+ with (clientSSLContext) {
+ authenticate = true;
+ ciphers = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH";
+ loadTrustedCertificates("../../../test/keys/CA.pem");
+ }
+ clientTransportFactory = new TAsyncSSLSocketFactory(clientSSLContext);
+
+ serverSSLContext = new TSSLContext();
+ with (serverSSLContext) {
+ serverSide = true;
+ ciphers = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH";
+ loadCertificate("../../../test/keys/server.crt");
+ loadPrivateKey("../../../test/keys/server.key");
+ }
+ } else {
+ clientTransportFactory = new TBufferedTransportFactory;
+ }
+
+
+ auto serverCancel = new TCancellationOrigin;
+ scope(exit) {
+ writeln("Triggering server shutdown...");
+ serverCancel.trigger();
+ writeln("done.");
+ }
+
+ auto managers = new TLibeventAsyncManager[managerCount];
+ scope (exit) foreach (ref m; managers) destroy(m);
+
+ auto clientsThreads = new ThreadGroup;
+ foreach (managerIndex, ref manager; managers) {
+ manager = new TLibeventAsyncManager;
+ foreach (serverIndex; 0 .. serversPerManager) {
+ auto currentPort = cast(ushort)
+ (port + managerIndex * serversPerManager + serverIndex);
+
+ // Start the server and wait until it is up and running.
+ auto servingMutex = new Mutex;
+ auto servingCondition = new Condition(servingMutex);
+ auto handler = new PreServeNotifyHandler(servingMutex, servingCondition);
+ synchronized (servingMutex) {
+ (new ServerThread!TSimpleServer(currentPort, serverSSLContext, trace,
+ serverCancel, handler)).start();
+ servingCondition.wait();
+ }
+
+ // We only run the timing tests for the first server on each async
+ // manager, so that we don't get spurious timing errors becaue of
+ // ordering issues.
+ auto runTimingTests = (serverIndex == 0);
+
+ auto c = new ClientsThread(manager, currentPort, clientTransportFactory,
+ threadsPerServer, iterations, runTimingTests, trace);
+ clientsThreads.add(c);
+ c.start();
+ }
+ }
+ clientsThreads.joinAll();
+}
+
+class AsyncTestHandler : AsyncTest {
+ this(bool trace) {
+ trace_ = trace;
+ }
+
+ override string echo(string value) {
+ if (trace_) writefln(`echo("%s")`, value);
+ return value;
+ }
+
+ override string delayedEcho(string value, long milliseconds) {
+ if (trace_) writef(`delayedEcho("%s", %s ms)... `, value, milliseconds);
+ Thread.sleep(dur!"msecs"(milliseconds));
+ if (trace_) writeln("returning.");
+
+ return value;
+ }
+
+ override void fail(string reason) {
+ if (trace_) writefln(`fail("%s")`, reason);
+ auto ate = new AsyncTestException;
+ ate.reason = reason;
+ throw ate;
+ }
+
+ override void delayedFail(string reason, long milliseconds) {
+ if (trace_) writef(`delayedFail("%s", %s ms)... `, reason, milliseconds);
+ Thread.sleep(dur!"msecs"(milliseconds));
+ if (trace_) writeln("returning.");
+
+ auto ate = new AsyncTestException;
+ ate.reason = reason;
+ throw ate;
+ }
+
+private:
+ bool trace_;
+ AsyncTestException ate_;
+}
+
+class PreServeNotifyHandler : TServerEventHandler {
+ this(Mutex servingMutex, Condition servingCondition) {
+ servingMutex_ = servingMutex;
+ servingCondition_ = servingCondition;
+ }
+
+ void preServe() {
+ synchronized (servingMutex_) {
+ servingCondition_.notifyAll();
+ }
+ }
+ Variant createContext(TProtocol input, TProtocol output) { return Variant.init; }
+ void deleteContext(Variant serverContext, TProtocol input, TProtocol output) {}
+ void preProcess(Variant serverContext, TTransport transport) {}
+
+private:
+ Mutex servingMutex_;
+ Condition servingCondition_;
+}
+
+class ServerThread(ServerType) : Thread {
+ this(ushort port, TSSLContext sslContext, bool trace,
+ TCancellation cancellation, TServerEventHandler eventHandler
+ ) {
+ port_ = port;
+ sslContext_ = sslContext;
+ trace_ = trace;
+ cancellation_ = cancellation;
+ eventHandler_ = eventHandler;
+
+ super(&run);
+ }
+
+ void run() {
+ TServerSocket serverSocket;
+ if (sslContext_) {
+ serverSocket = new TSSLServerSocket(port_, sslContext_);
+ } else {
+ serverSocket = new TServerSocket(port_);
+ }
+ auto transportFactory = new TBufferedTransportFactory;
+ auto protocolFactory = new TBinaryProtocolFactory!();
+ auto processor = new TServiceProcessor!AsyncTest(new AsyncTestHandler(trace_));
+
+ auto server = new ServerType(processor, serverSocket, transportFactory,
+ protocolFactory);
+ server.eventHandler = eventHandler_;
+
+ writefln("Starting server on port %s...", port_);
+ server.serve(cancellation_);
+ writefln("Server thread on port %s done.", port_);
+ }
+
+private:
+ ushort port_;
+ bool trace_;
+ TCancellation cancellation_;
+ TSSLContext sslContext_;
+ TServerEventHandler eventHandler_;
+}
+
+class ClientsThread : Thread {
+ this(TAsyncSocketManager manager, ushort port, TTransportFactory tf,
+ ushort threads, uint iterations, bool runTimingTests, bool trace
+ ) {
+ manager_ = manager;
+ port_ = port;
+ transportFactory_ = tf;
+ threads_ = threads;
+ iterations_ = iterations;
+ runTimingTests_ = runTimingTests;
+ trace_ = trace;
+ super(&run);
+ }
+
+ void run() {
+ auto transport = new TAsyncSocket(manager_, "localhost", port_);
+
+ {
+ auto client = new TAsyncClient!AsyncTest(
+ transport,
+ transportFactory_,
+ new TBinaryProtocolFactory!()
+ );
+ transport.open();
+ auto clientThreads = new ThreadGroup;
+ foreach (clientId; 0 .. threads_) {
+ clientThreads.create({
+ auto c = clientId;
+ return {
+ foreach (i; 0 .. iterations_) {
+ immutable id = text(port_, ":", c, ":", i);
+
+ {
+ if (trace_) writefln(`Calling echo("%s")... `, id);
+ auto a = client.echo(id);
+ enforce(a == id);
+ if (trace_) writefln(`echo("%s") done.`, id);
+ }
+
+ {
+ if (trace_) writefln(`Calling fail("%s")... `, id);
+ auto a = cast(AsyncTestException)collectException(client.fail(id).waitGet());
+ enforce(a && a.reason == id);
+ if (trace_) writefln(`fail("%s") done.`, id);
+ }
+ }
+ };
+ }());
+ }
+ clientThreads.joinAll();
+ transport.close();
+ }
+
+ if (runTimingTests_) {
+ auto client = new TAsyncClient!AsyncTest(
+ transport,
+ transportFactory_,
+ new TBinaryProtocolFactory!TBufferedTransport
+ );
+
+ // Temporarily redirect error logs to stdout, as SSL errors on the server
+ // side are expected when the client terminates aburptly (as is the case
+ // in the timeout test).
+ auto oldErrorLogSink = g_errorLogSink;
+ g_errorLogSink = g_infoLogSink;
+ scope (exit) g_errorLogSink = oldErrorLogSink;
+
+ foreach (i; 0 .. iterations_) {
+ transport.open();
+
+ immutable id = text(port_, ":", i);
+
+ {
+ if (trace_) writefln(`Calling delayedEcho("%s", 100 ms)...`, id);
+ auto a = client.delayedEcho(id, 100);
+ enforce(!a.completion.wait(dur!"usecs"(1)),
+ text("wait() succeeded early (", a.get(), ", ", id, ")."));
+ enforce(!a.completion.wait(dur!"usecs"(1)),
+ text("wait() succeeded early (", a.get(), ", ", id, ")."));
+ enforce(a.completion.wait(dur!"msecs"(200)),
+ text("wait() didn't succeed as expected (", id, ")."));
+ enforce(a.get() == id);
+ if (trace_) writefln(`... delayedEcho("%s") done.`, id);
+ }
+
+ {
+ if (trace_) writefln(`Calling delayedFail("%s", 100 ms)... `, id);
+ auto a = client.delayedFail(id, 100);
+ enforce(!a.completion.wait(dur!"usecs"(1)),
+ text("wait() succeeded early (", id, ", ", collectException(a.get()), ")."));
+ enforce(!a.completion.wait(dur!"usecs"(1)),
+ text("wait() succeeded early (", id, ", ", collectException(a.get()), ")."));
+ enforce(a.completion.wait(dur!"msecs"(200)),
+ text("wait() didn't succeed as expected (", id, ")."));
+ auto e = cast(AsyncTestException)collectException(a.get());
+ enforce(e && e.reason == id);
+ if (trace_) writefln(`... delayedFail("%s") done.`, id);
+ }
+
+ {
+ transport.recvTimeout = dur!"msecs"(50);
+
+ if (trace_) write(`Calling delayedEcho("socketTimeout", 100 ms)... `);
+ auto a = client.delayedEcho("socketTimeout", 100);
+ auto e = cast(TTransportException)collectException(a.waitGet());
+ enforce(e, text("Operation didn't fail as expected (", id, ")."));
+ enforce(e.type == TTransportException.Type.TIMED_OUT,
+ text("Wrong timeout exception type (", id, "): ", e));
+ if (trace_) writeln(`timed out as expected.`);
+
+ // Wait until the server thread reset before the next iteration.
+ Thread.sleep(dur!"msecs"(50));
+ transport.recvTimeout = dur!"hnsecs"(0);
+ }
+
+ transport.close();
+ }
+ }
+
+ writefln("Clients thread for port %s done.", port_);
+ }
+
+ TAsyncSocketManager manager_;
+ ushort port_;
+ TTransportFactory transportFactory_;
+ ushort threads_;
+ uint iterations_;
+ bool runTimingTests_;
+ bool trace_;
+}
diff --git a/src/jaegertracing/thrift/lib/d/test/async_test_runner.sh b/src/jaegertracing/thrift/lib/d/test/async_test_runner.sh
new file mode 100755
index 000000000..d56654f50
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/test/async_test_runner.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+CUR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+# Runs the async test in both SSL and non-SSL mode.
+${CUR}/async_test > /dev/null || exit 1
+echo "Non-SSL tests done."
+
+# THRIFT-4905: disabled the following test as it deadlocks / hangs
+# ${CUR}/async_test --ssl > /dev/null || exit 1
+# echo "SSL tests done."
+echo "THRIFT-4905: SSL tests are disabled. Fix them."
diff --git a/src/jaegertracing/thrift/lib/d/test/client_pool_test.d b/src/jaegertracing/thrift/lib/d/test/client_pool_test.d
new file mode 100644
index 000000000..b24c97afd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/test/client_pool_test.d
@@ -0,0 +1,442 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module client_pool_test;
+
+import core.sync.semaphore : Semaphore;
+import core.time : Duration, dur;
+import core.thread : Thread;
+import std.algorithm;
+import std.array;
+import std.conv;
+import std.exception;
+import std.getopt;
+import std.range;
+import std.stdio;
+import std.typecons;
+import std.variant : Variant;
+import thrift.base;
+import thrift.async.libevent;
+import thrift.async.socket;
+import thrift.codegen.base;
+import thrift.codegen.async_client;
+import thrift.codegen.async_client_pool;
+import thrift.codegen.client;
+import thrift.codegen.client_pool;
+import thrift.codegen.processor;
+import thrift.protocol.base;
+import thrift.protocol.binary;
+import thrift.server.base;
+import thrift.server.simple;
+import thrift.server.transport.socket;
+import thrift.transport.base;
+import thrift.transport.buffered;
+import thrift.transport.socket;
+import thrift.util.cancellation;
+import thrift.util.future;
+
+// We use this as our RPC-layer exception here to make sure socket/… problems
+// (that would usually considered to be RPC layer faults) cause the tests to
+// fail, even though we are testing the RPC exception handling.
+class TestServiceException : TException {
+ int port;
+}
+
+interface TestService {
+ int getPort();
+ alias .TestServiceException TestServiceException;
+ enum methodMeta = [TMethodMeta("getPort", [],
+ [TExceptionMeta("a", 1, "TestServiceException")])];
+}
+
+// Use some derived service, just to check that the pools handle inheritance
+// correctly.
+interface ExTestService : TestService {
+ int[] getPortInArray();
+ enum methodMeta = [TMethodMeta("getPortInArray", [],
+ [TExceptionMeta("a", 1, "TestServiceException")])];
+}
+
+class ExTestHandler : ExTestService {
+ this(ushort port, Duration delay, bool failing, bool trace) {
+ this.port = port;
+ this.delay = delay;
+ this.failing = failing;
+ this.trace = trace;
+ }
+
+ override int getPort() {
+ if (trace) {
+ stderr.writefln("getPort() called on %s (delay: %s, failing: %s)", port,
+ delay, failing);
+ }
+ sleep();
+ failIfEnabled();
+ return port;
+ }
+
+ override int[] getPortInArray() {
+ return [getPort()];
+ }
+
+ ushort port;
+ Duration delay;
+ bool failing;
+ bool trace;
+
+private:
+ void sleep() {
+ if (delay > dur!"hnsecs"(0)) Thread.sleep(delay);
+ }
+
+ void failIfEnabled() {
+ if (!failing) return;
+
+ auto e = new TestServiceException;
+ e.port = port;
+ throw e;
+ }
+}
+
+class ServerPreServeHandler : TServerEventHandler {
+ this(Semaphore sem) {
+ sem_ = sem;
+ }
+
+ override void preServe() {
+ sem_.notify();
+ }
+
+ Variant createContext(TProtocol input, TProtocol output) { return Variant.init; }
+ void deleteContext(Variant serverContext, TProtocol input, TProtocol output) {}
+ void preProcess(Variant serverContext, TTransport transport) {}
+
+private:
+ Semaphore sem_;
+}
+
+class ServerThread : Thread {
+ this(ExTestHandler handler, ServerPreServeHandler serverHandler, TCancellation cancellation) {
+ super(&run);
+ handler_ = handler;
+ cancellation_ = cancellation;
+ serverHandler_ = serverHandler;
+ }
+private:
+ void run() {
+ try {
+ auto protocolFactory = new TBinaryProtocolFactory!();
+ auto processor = new TServiceProcessor!ExTestService(handler_);
+ auto serverTransport = new TServerSocket(handler_.port);
+ serverTransport.recvTimeout = dur!"seconds"(3);
+ auto transportFactory = new TBufferedTransportFactory;
+
+ auto server = new TSimpleServer(processor, serverTransport, transportFactory, protocolFactory);
+ server.eventHandler = serverHandler_;
+ server.serve(cancellation_);
+ } catch (Exception e) {
+ writefln("Server thread on port %s failed: %s", handler_.port, e);
+ }
+ }
+
+ ExTestHandler handler_;
+ ServerPreServeHandler serverHandler_;
+ TCancellation cancellation_;
+}
+
+void main(string[] args) {
+ bool trace;
+ ushort port = 9090;
+ getopt(args, "port", &port, "trace", &trace);
+
+ auto serverCancellation = new TCancellationOrigin;
+ scope (exit) serverCancellation.trigger();
+
+ immutable ports = cast(immutable)array(map!"cast(ushort)a"(iota(port, port + 6)));
+
+ // semaphore that will be incremented whenever each server thread has bound and started listening
+ Semaphore sem = new Semaphore(0);
+
+version (none) {
+ // Cannot use this due to multiple DMD @@BUG@@s:
+ // 1. »function D main is a nested function and cannot be accessed from array«
+ // when calling array() on the result of the outer map() – would have to
+ // manually do the eager evaluation/array conversion.
+ // 2. »Zip.opSlice cannot get frame pointer to map« for the delay argument,
+ // can be worked around by calling array() on the map result first.
+ // 3. Even when using the workarounds for the last two points, the DMD-built
+ // executable crashes when building without (sic!) inlining enabled,
+ // the backtrace points into the first delegate literal.
+ auto handlers = array(map!((args){
+ return new ExTestHandler(args._0, args._1, args._2, trace);
+ })(zip(
+ ports,
+ map!((a){ return dur!`msecs`(a); })([1, 10, 100, 1, 10, 100]),
+ [false, false, false, true, true, true]
+ )));
+} else {
+ auto handlers = [
+ new ExTestHandler(cast(ushort)(port + 0), dur!"msecs"(1), false, trace),
+ new ExTestHandler(cast(ushort)(port + 1), dur!"msecs"(10), false, trace),
+ new ExTestHandler(cast(ushort)(port + 2), dur!"msecs"(100), false, trace),
+ new ExTestHandler(cast(ushort)(port + 3), dur!"msecs"(1), true, trace),
+ new ExTestHandler(cast(ushort)(port + 4), dur!"msecs"(10), true, trace),
+ new ExTestHandler(cast(ushort)(port + 5), dur!"msecs"(100), true, trace)
+ ];
+}
+
+ // Fire up the server threads.
+ foreach (h; handlers) (new ServerThread(h, new ServerPreServeHandler(sem), serverCancellation)).start();
+
+ // wait until all the handlers signal that they're ready to serve
+ foreach (h; handlers) (sem.wait(dur!`seconds`(1)));
+
+ syncClientPoolTest(ports, handlers);
+ asyncClientPoolTest(ports, handlers);
+ asyncFastestClientPoolTest(ports, handlers);
+ asyncAggregatorTest(ports, handlers);
+}
+
+
+void syncClientPoolTest(const(ushort)[] ports, ExTestHandler[] handlers) {
+ auto clients = array(map!((a){
+ return cast(TClientBase!ExTestService)tClient!ExTestService(
+ tBinaryProtocol(new TSocket("127.0.0.1", a))
+ );
+ })(ports));
+
+ scope(exit) foreach (c; clients) c.outputProtocol.transport.close();
+
+ // Try the case where the first client succeeds.
+ {
+ enforce(makePool(clients).getPort() == ports[0]);
+ }
+
+ // Try the case where all clients fail.
+ {
+ auto pool = makePool(clients[3 .. $]);
+ auto e = cast(TCompoundOperationException)collectException(pool.getPort());
+ enforce(e);
+ enforce(equal(map!"a.port"(cast(TestServiceException[])e.exceptions),
+ ports[3 .. $]));
+ }
+
+ // Try the case where the first clients fail, but a later one succeeds.
+ {
+ auto pool = makePool(clients[3 .. $] ~ clients[0 .. 3]);
+ enforce(pool.getPortInArray() == [ports[0]]);
+ }
+
+ // Make sure a client is properly deactivated when it has failed too often.
+ {
+ auto pool = makePool(clients);
+ pool.faultDisableCount = 1;
+ pool.faultDisableDuration = dur!"msecs"(50);
+
+ handlers[0].failing = true;
+ enforce(pool.getPort() == ports[1]);
+
+ handlers[0].failing = false;
+ enforce(pool.getPort() == ports[1]);
+
+ Thread.sleep(dur!"msecs"(50));
+ enforce(pool.getPort() == ports[0]);
+ }
+}
+
+auto makePool(TClientBase!ExTestService[] clients) {
+ auto p = tClientPool(clients);
+ p.permuteClients = false;
+ p.rpcFaultFilter = (Exception e) {
+ return (cast(TestServiceException)e !is null);
+ };
+ return p;
+}
+
+
+void asyncClientPoolTest(const(ushort)[] ports, ExTestHandler[] handlers) {
+ auto manager = new TLibeventAsyncManager;
+ scope (exit) manager.stop(dur!"hnsecs"(0));
+
+ auto clients = makeAsyncClients(manager, ports);
+ scope(exit) foreach (c; clients) c.transport.close();
+
+ // Try the case where the first client succeeds.
+ {
+ enforce(makeAsyncPool(clients).getPort() == ports[0]);
+ }
+
+ // Try the case where all clients fail.
+ {
+ auto pool = makeAsyncPool(clients[3 .. $]);
+ auto e = cast(TCompoundOperationException)collectException(pool.getPort().waitGet());
+ enforce(e);
+ enforce(equal(map!"a.port"(cast(TestServiceException[])e.exceptions),
+ ports[3 .. $]));
+ }
+
+ // Try the case where the first clients fail, but a later one succeeds.
+ {
+ auto pool = makeAsyncPool(clients[3 .. $] ~ clients[0 .. 3]);
+ enforce(pool.getPortInArray() == [ports[0]]);
+ }
+
+ // Make sure a client is properly deactivated when it has failed too often.
+ {
+ auto pool = makeAsyncPool(clients);
+ pool.faultDisableCount = 1;
+ pool.faultDisableDuration = dur!"msecs"(50);
+
+ handlers[0].failing = true;
+ enforce(pool.getPort() == ports[1]);
+
+ handlers[0].failing = false;
+ enforce(pool.getPort() == ports[1]);
+
+ Thread.sleep(dur!"msecs"(50));
+ enforce(pool.getPort() == ports[0]);
+ }
+}
+
+auto makeAsyncPool(TAsyncClientBase!ExTestService[] clients) {
+ auto p = tAsyncClientPool(clients);
+ p.permuteClients = false;
+ p.rpcFaultFilter = (Exception e) {
+ return (cast(TestServiceException)e !is null);
+ };
+ return p;
+}
+
+auto makeAsyncClients(TLibeventAsyncManager manager, in ushort[] ports) {
+ // DMD @@BUG@@ workaround: Using array on the lazyHandlers map result leads
+ // to »function D main is a nested function and cannot be accessed from array«.
+ // Thus, we manually do the array conversion.
+ auto lazyClients = map!((a){
+ return new TAsyncClient!ExTestService(
+ new TAsyncSocket(manager, "127.0.0.1", a),
+ new TBufferedTransportFactory,
+ new TBinaryProtocolFactory!(TBufferedTransport)
+ );
+ })(ports);
+ TAsyncClientBase!ExTestService[] clients;
+ foreach (c; lazyClients) clients ~= c;
+ return clients;
+}
+
+
+void asyncFastestClientPoolTest(const(ushort)[] ports, ExTestHandler[] handlers) {
+ auto manager = new TLibeventAsyncManager;
+ scope (exit) manager.stop(dur!"hnsecs"(0));
+
+ auto clients = makeAsyncClients(manager, ports);
+ scope(exit) foreach (c; clients) c.transport.close();
+
+ // Make sure the fastest client wins, even if they are called in some other
+ // order.
+ {
+ auto result = makeAsyncFastestPool(array(retro(clients))).getPort().waitGet();
+ enforce(result == ports[0]);
+ }
+
+ // Try the case where all clients fail.
+ {
+ auto pool = makeAsyncFastestPool(clients[3 .. $]);
+ auto e = cast(TCompoundOperationException)collectException(pool.getPort().waitGet());
+ enforce(e);
+ enforce(equal(map!"a.port"(cast(TestServiceException[])e.exceptions),
+ ports[3 .. $]));
+ }
+
+ // Try the case where the first clients fail, but a later one succeeds.
+ {
+ auto pool = makeAsyncFastestPool(clients[1 .. $]);
+ enforce(pool.getPortInArray() == [ports[1]]);
+ }
+}
+
+auto makeAsyncFastestPool(TAsyncClientBase!ExTestService[] clients) {
+ auto p = tAsyncFastestClientPool(clients);
+ p.rpcFaultFilter = (Exception e) {
+ return (cast(TestServiceException)e !is null);
+ };
+ return p;
+}
+
+
+void asyncAggregatorTest(const(ushort)[] ports, ExTestHandler[] handlers) {
+ auto manager = new TLibeventAsyncManager;
+ scope (exit) manager.stop(dur!"hnsecs"(0));
+
+ auto clients = makeAsyncClients(manager, ports);
+ scope(exit) foreach (c; clients) c.transport.close();
+
+ auto aggregator = tAsyncAggregator(
+ cast(TAsyncClientBase!ExTestService[])clients);
+
+ // Test aggregator range interface.
+ {
+ auto range = aggregator.getPort().range(dur!"msecs"(50));
+ enforce(equal(range, ports[0 .. 2][]));
+ enforce(equal(map!"a.port"(cast(TestServiceException[])range.exceptions),
+ ports[3 .. $ - 1]));
+ enforce(range.completedCount == 4);
+ }
+
+ // Test default accumulator for scalars.
+ {
+ auto fullResult = aggregator.getPort().accumulate();
+ enforce(fullResult.waitGet() == ports[0 .. 3]);
+
+ auto partialResult = aggregator.getPort().accumulate();
+ Thread.sleep(dur!"msecs"(20));
+ enforce(partialResult.finishGet() == ports[0 .. 2]);
+
+ }
+
+ // Test default accumulator for arrays.
+ {
+ auto fullResult = aggregator.getPortInArray().accumulate();
+ enforce(fullResult.waitGet() == ports[0 .. 3]);
+
+ auto partialResult = aggregator.getPortInArray().accumulate();
+ Thread.sleep(dur!"msecs"(20));
+ enforce(partialResult.finishGet() == ports[0 .. 2]);
+ }
+
+ // Test custom accumulator.
+ {
+ auto fullResult = aggregator.getPort().accumulate!(function(int[] results){
+ return reduce!"a + b"(results);
+ })();
+ enforce(fullResult.waitGet() == ports[0] + ports[1] + ports[2]);
+
+ auto partialResult = aggregator.getPort().accumulate!(
+ function(int[] results, Exception[] exceptions) {
+ // Return a tuple of the parameters so we can check them outside of
+ // this function (to verify the values, we need access to »ports«, but
+ // due to DMD @@BUG5710@@, we can't use a delegate literal).f
+ return tuple(results, exceptions);
+ }
+ )();
+ Thread.sleep(dur!"msecs"(20));
+ auto resultTuple = partialResult.finishGet();
+ enforce(resultTuple[0] == ports[0 .. 2]);
+ enforce(equal(map!"a.port"(cast(TestServiceException[])resultTuple[1]),
+ ports[3 .. $ - 1]));
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/test/serialization_benchmark.d b/src/jaegertracing/thrift/lib/d/test/serialization_benchmark.d
new file mode 100644
index 000000000..40d048094
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/test/serialization_benchmark.d
@@ -0,0 +1,70 @@
+/**
+ * An implementation of the mini serialization benchmark also available for
+ * C++ and Java.
+ *
+ * For meaningful results, you might want to make sure that
+ * the Thrift library is compiled with release build flags,
+ * e.g. by including the source files with the build instead
+ * of linking libthriftd:
+ *
+ dmd -w -O -release -inline -I../src -Igen-d -ofserialization_benchmark \
+ $(find ../src/thrift -name '*.d' -not -name index.d) \
+ gen-d/DebugProtoTest_types.d serialization_benchmark.d
+ */
+module serialization_benchmark;
+
+import std.datetime.stopwatch : AutoStart, StopWatch;
+import std.math : PI;
+import std.stdio;
+import thrift.protocol.binary;
+import thrift.transport.memory;
+import thrift.transport.range;
+import DebugProtoTest_types;
+
+void main() {
+ auto buf = new TMemoryBuffer;
+ enum ITERATIONS = 10_000_000;
+
+ {
+ auto ooe = OneOfEach();
+ ooe.im_true = true;
+ ooe.im_false = false;
+ ooe.a_bite = 0x7f;
+ ooe.integer16 = 27_000;
+ ooe.integer32 = 1 << 24;
+ ooe.integer64 = 6_000_000_000;
+ ooe.double_precision = PI;
+ ooe.some_characters = "JSON THIS! \"\1";
+ ooe.zomg_unicode = "\xd7\n\a\t";
+ ooe.base64 = "\1\2\3\255";
+
+ auto prot = tBinaryProtocol(buf);
+ auto sw = StopWatch(AutoStart.yes);
+ foreach (i; 0 .. ITERATIONS) {
+ buf.reset(120);
+ ooe.write(prot);
+ }
+ sw.stop();
+
+ auto msecs = sw.peek().total!"msecs";
+ writefln("Write: %s ms (%s kHz)", msecs, ITERATIONS / msecs);
+ }
+
+ auto data = buf.getContents().dup;
+
+ {
+ auto readBuf = tInputRangeTransport(data);
+ auto prot = tBinaryProtocol(readBuf);
+ auto ooe = OneOfEach();
+
+ auto sw = StopWatch(AutoStart.yes);
+ foreach (i; 0 .. ITERATIONS) {
+ readBuf.reset(data);
+ ooe.read(prot);
+ }
+ sw.stop();
+
+ auto msecs = sw.peek().total!"msecs";
+ writefln(" Read: %s ms (%s kHz)", msecs, ITERATIONS / msecs);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/test/stress_test_server.d b/src/jaegertracing/thrift/lib/d/test/stress_test_server.d
new file mode 100644
index 000000000..ddda098b3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/test/stress_test_server.d
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module stress_test_server;
+
+import std.getopt;
+import std.parallelism : totalCPUs;
+import std.stdio;
+import std.typetuple;
+import thrift.codegen.processor;
+import thrift.protocol.binary;
+import thrift.server.base;
+import thrift.server.transport.socket;
+import thrift.transport.buffered;
+import thrift.transport.memory;
+import thrift.transport.socket;
+import thrift.util.hashset;
+import test_utils;
+
+import thrift.test.stress.Service;
+
+class ServiceHandler : Service {
+ void echoVoid() { return; }
+ byte echoByte(byte arg) { return arg; }
+ int echoI32(int arg) { return arg; }
+ long echoI64(long arg) { return arg; }
+ byte[] echoList(byte[] arg) { return arg; }
+ HashSet!byte echoSet(HashSet!byte arg) { return arg; }
+ byte[byte] echoMap(byte[byte] arg) { return arg; }
+
+ string echoString(string arg) {
+ if (arg != "hello") {
+ stderr.writefln(`Wrong string received: %s instead of "hello"`, arg);
+ throw new Exception("Wrong string received.");
+ }
+ return arg;
+ }
+}
+
+void main(string[] args) {
+ ushort port = 9091;
+ auto serverType = ServerType.threaded;
+ TransportType transportType;
+ size_t numIOThreads = 1;
+ size_t taskPoolSize = totalCPUs;
+
+ getopt(args, "port", &port, "server-type", &serverType,
+ "transport-type", &transportType, "task-pool-size", &taskPoolSize,
+ "num-io-threads", &numIOThreads);
+
+ alias TypeTuple!(TBufferedTransport, TMemoryBuffer) AvailableTransports;
+
+ auto processor = new TServiceProcessor!(Service,
+ staticMap!(TBinaryProtocol, AvailableTransports))(new ServiceHandler());
+ auto serverSocket = new TServerSocket(port);
+ auto transportFactory = createTransportFactory(transportType);
+ auto protocolFactory = new TBinaryProtocolFactory!AvailableTransports;
+
+ auto server = createServer(serverType, taskPoolSize, numIOThreads,
+ processor, serverSocket, transportFactory, protocolFactory);
+
+ writefln("Starting %s %s StressTest server on port %s...", transportType,
+ serverType, port);
+ server.serve();
+ writeln("done.");
+}
diff --git a/src/jaegertracing/thrift/lib/d/test/test_utils.d b/src/jaegertracing/thrift/lib/d/test/test_utils.d
new file mode 100644
index 000000000..174100b79
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/test/test_utils.d
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Various helpers used by more than a single test.
+ */
+module test_utils;
+
+import std.parallelism : TaskPool;
+import thrift.protocol.base;
+import thrift.protocol.processor;
+import thrift.server.base;
+import thrift.server.nonblocking;
+import thrift.server.simple;
+import thrift.server.taskpool;
+import thrift.server.threaded;
+import thrift.server.transport.socket;
+import thrift.transport.base;
+import thrift.transport.buffered;
+import thrift.transport.framed;
+import thrift.transport.http;
+
+// This is a likely victim of @@BUG4744@@ when used with command argument
+// parsing.
+enum ServerType {
+ simple,
+ nonblocking,
+ pooledNonblocking,
+ taskpool,
+ threaded
+}
+
+TServer createServer(ServerType type, size_t taskPoolSize, size_t numIOThreads,
+ TProcessor processor, TServerSocket serverTransport,
+ TTransportFactory transportFactory, TProtocolFactory protocolFactory)
+{
+ final switch (type) {
+ case ServerType.simple:
+ return new TSimpleServer(processor, serverTransport,
+ transportFactory, protocolFactory);
+ case ServerType.nonblocking:
+ auto nb = new TNonblockingServer(processor, serverTransport.port,
+ transportFactory, protocolFactory);
+ nb.numIOThreads = numIOThreads;
+ return nb;
+ case ServerType.pooledNonblocking:
+ auto nb = new TNonblockingServer(processor, serverTransport.port,
+ transportFactory, protocolFactory, new TaskPool(taskPoolSize));
+ nb.numIOThreads = numIOThreads;
+ return nb;
+ case ServerType.taskpool:
+ auto tps = new TTaskPoolServer(processor, serverTransport,
+ transportFactory, protocolFactory);
+ tps.taskPool = new TaskPool(taskPoolSize);
+ return tps;
+ case ServerType.threaded:
+ return new TThreadedServer(processor, serverTransport,
+ transportFactory, protocolFactory);
+ }
+}
+
+enum TransportType {
+ buffered,
+ framed,
+ http,
+ raw
+}
+
+TTransportFactory createTransportFactory(TransportType type) {
+ final switch (type) {
+ case TransportType.buffered:
+ return new TBufferedTransportFactory;
+ case TransportType.framed:
+ return new TFramedTransportFactory;
+ case TransportType.http:
+ return new TServerHttpTransportFactory;
+ case TransportType.raw:
+ return new TTransportFactory;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/test/thrift_test_client.d b/src/jaegertracing/thrift/lib/d/test/thrift_test_client.d
new file mode 100644
index 000000000..49419f71a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/test/thrift_test_client.d
@@ -0,0 +1,386 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module thrift_test_client;
+
+import std.conv;
+import std.datetime;
+import std.exception : enforce;
+import std.getopt;
+import std.stdio;
+import std.string;
+import std.traits;
+import thrift.base;
+import thrift.codegen.client;
+import thrift.protocol.base;
+import thrift.protocol.binary;
+import thrift.protocol.compact;
+import thrift.protocol.json;
+import thrift.transport.base;
+import thrift.transport.buffered;
+import thrift.transport.framed;
+import thrift.transport.http;
+import thrift.transport.socket;
+import thrift.transport.ssl;
+import thrift.util.hashset;
+
+import thrift_test_common;
+import thrift.test.ThriftTest;
+import thrift.test.ThriftTest_types;
+
+enum TransportType {
+ buffered,
+ framed,
+ http,
+ raw
+}
+
+TProtocol createProtocol(T)(T trans, ProtocolType type) {
+ final switch (type) {
+ case ProtocolType.binary:
+ return tBinaryProtocol(trans);
+ case ProtocolType.compact:
+ return tCompactProtocol(trans);
+ case ProtocolType.json:
+ return tJsonProtocol(trans);
+ }
+}
+
+void main(string[] args) {
+ string host = "localhost";
+ ushort port = 9090;
+ uint numTests = 1;
+ bool ssl;
+ ProtocolType protocolType;
+ TransportType transportType;
+ bool trace;
+
+ getopt(args,
+ "numTests|n", &numTests,
+ "protocol", &protocolType,
+ "ssl", &ssl,
+ "transport", &transportType,
+ "trace", &trace,
+ "port", &port,
+ "host", (string _, string value) {
+ auto parts = split(value, ":");
+ if (parts.length > 1) {
+ // IPv6 addresses can contain colons, so take the last part for the
+ // port.
+ host = join(parts[0 .. $ - 1], ":");
+ port = to!ushort(parts[$ - 1]);
+ } else {
+ host = value;
+ }
+ }
+ );
+ port = to!ushort(port);
+
+ TSocket socket;
+ if (ssl) {
+ auto sslContext = new TSSLContext();
+ sslContext.ciphers = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH";
+ sslContext.authenticate = true;
+ sslContext.loadTrustedCertificates("../../../test/keys/CA.pem");
+ socket = new TSSLSocket(sslContext, host, port);
+ } else {
+ socket = new TSocket(host, port);
+ }
+
+ TProtocol protocol;
+ final switch (transportType) {
+ case TransportType.buffered:
+ protocol = createProtocol(new TBufferedTransport(socket), protocolType);
+ break;
+ case TransportType.framed:
+ protocol = createProtocol(new TFramedTransport(socket), protocolType);
+ break;
+ case TransportType.http:
+ protocol = createProtocol(
+ new TClientHttpTransport(socket, host, "/service"), protocolType);
+ break;
+ case TransportType.raw:
+ protocol = createProtocol(socket, protocolType);
+ break;
+ }
+
+ auto client = tClient!ThriftTest(protocol);
+
+ ulong time_min;
+ ulong time_max;
+ ulong time_tot;
+
+ StopWatch sw;
+ foreach(test; 0 .. numTests) {
+ sw.start();
+
+ protocol.transport.open();
+
+ if (trace) writefln("Test #%s, connect %s:%s", test + 1, host, port);
+
+ if (trace) write("testVoid()");
+ client.testVoid();
+ if (trace) writeln(" = void");
+
+ if (trace) write("testString(\"Test\")");
+ string s = client.testString("Test");
+ if (trace) writefln(" = \"%s\"", s);
+ enforce(s == "Test");
+
+ if (trace) write("testByte(1)");
+ byte u8 = client.testByte(1);
+ if (trace) writefln(" = %s", u8);
+ enforce(u8 == 1);
+
+ if (trace) write("testI32(-1)");
+ int i32 = client.testI32(-1);
+ if (trace) writefln(" = %s", i32);
+ enforce(i32 == -1);
+
+ if (trace) write("testI64(-34359738368)");
+ long i64 = client.testI64(-34359738368L);
+ if (trace) writefln(" = %s", i64);
+ enforce(i64 == -34359738368L);
+
+ if (trace) write("testDouble(-5.2098523)");
+ double dub = client.testDouble(-5.2098523);
+ if (trace) writefln(" = %s", dub);
+ enforce(dub == -5.2098523);
+
+ // TODO: add testBinary() call
+
+ Xtruct out1;
+ out1.string_thing = "Zero";
+ out1.byte_thing = 1;
+ out1.i32_thing = -3;
+ out1.i64_thing = -5;
+ if (trace) writef("testStruct(%s)", out1);
+ auto in1 = client.testStruct(out1);
+ if (trace) writefln(" = %s", in1);
+ enforce(in1 == out1);
+
+ if (trace) write("testNest({1, {\"Zero\", 1, -3, -5}), 5}");
+ Xtruct2 out2;
+ out2.byte_thing = 1;
+ out2.struct_thing = out1;
+ out2.i32_thing = 5;
+ auto in2 = client.testNest(out2);
+ in1 = in2.struct_thing;
+ if (trace) writefln(" = {%s, {\"%s\", %s, %s, %s}, %s}", in2.byte_thing,
+ in1.string_thing, in1.byte_thing, in1.i32_thing, in1.i64_thing,
+ in2.i32_thing);
+ enforce(in2 == out2);
+
+ int[int] mapout;
+ for (int i = 0; i < 5; ++i) {
+ mapout[i] = i - 10;
+ }
+ if (trace) writef("testMap({%s})", mapout);
+ auto mapin = client.testMap(mapout);
+ if (trace) writefln(" = {%s}", mapin);
+ enforce(mapin == mapout);
+
+ auto setout = new HashSet!int;
+ for (int i = -2; i < 3; ++i) {
+ setout ~= i;
+ }
+ if (trace) writef("testSet(%s)", setout);
+ auto setin = client.testSet(setout);
+ if (trace) writefln(" = %s", setin);
+ enforce(setin == setout);
+
+ int[] listout;
+ for (int i = -2; i < 3; ++i) {
+ listout ~= i;
+ }
+ if (trace) writef("testList(%s)", listout);
+ auto listin = client.testList(listout);
+ if (trace) writefln(" = %s", listin);
+ enforce(listin == listout);
+
+ {
+ if (trace) write("testEnum(ONE)");
+ auto ret = client.testEnum(Numberz.ONE);
+ if (trace) writefln(" = %s", ret);
+ enforce(ret == Numberz.ONE);
+
+ if (trace) write("testEnum(TWO)");
+ ret = client.testEnum(Numberz.TWO);
+ if (trace) writefln(" = %s", ret);
+ enforce(ret == Numberz.TWO);
+
+ if (trace) write("testEnum(THREE)");
+ ret = client.testEnum(Numberz.THREE);
+ if (trace) writefln(" = %s", ret);
+ enforce(ret == Numberz.THREE);
+
+ if (trace) write("testEnum(FIVE)");
+ ret = client.testEnum(Numberz.FIVE);
+ if (trace) writefln(" = %s", ret);
+ enforce(ret == Numberz.FIVE);
+
+ if (trace) write("testEnum(EIGHT)");
+ ret = client.testEnum(Numberz.EIGHT);
+ if (trace) writefln(" = %s", ret);
+ enforce(ret == Numberz.EIGHT);
+ }
+
+ if (trace) write("testTypedef(309858235082523)");
+ UserId uid = client.testTypedef(309858235082523L);
+ if (trace) writefln(" = %s", uid);
+ enforce(uid == 309858235082523L);
+
+ if (trace) write("testMapMap(1)");
+ auto mm = client.testMapMap(1);
+ if (trace) writefln(" = {%s}", mm);
+ // Simply doing == doesn't seem to work for nested AAs.
+ foreach (key, value; mm) {
+ enforce(testMapMapReturn[key] == value);
+ }
+ foreach (key, value; testMapMapReturn) {
+ enforce(mm[key] == value);
+ }
+
+ Insanity insane;
+ insane.userMap[Numberz.FIVE] = 5000;
+ Xtruct truck;
+ truck.string_thing = "Truck";
+ truck.byte_thing = 8;
+ truck.i32_thing = 8;
+ truck.i64_thing = 8;
+ insane.xtructs ~= truck;
+ if (trace) write("testInsanity()");
+ auto whoa = client.testInsanity(insane);
+ if (trace) writefln(" = %s", whoa);
+
+ // Commented for now, this is cumbersome to write without opEqual getting
+ // called on AA comparison.
+ // enforce(whoa == testInsanityReturn);
+
+ {
+ try {
+ if (trace) write("client.testException(\"Xception\") =>");
+ client.testException("Xception");
+ if (trace) writeln(" void\nFAILURE");
+ throw new Exception("testException failed.");
+ } catch (Xception e) {
+ if (trace) writefln(" {%s, \"%s\"}", e.errorCode, e.message);
+ }
+
+ try {
+ if (trace) write("client.testException(\"TException\") =>");
+ client.testException("Xception");
+ if (trace) writeln(" void\nFAILURE");
+ throw new Exception("testException failed.");
+ } catch (TException e) {
+ if (trace) writefln(" {%s}", e.msg);
+ }
+
+ try {
+ if (trace) write("client.testException(\"success\") =>");
+ client.testException("success");
+ if (trace) writeln(" void");
+ } catch (Exception e) {
+ if (trace) writeln(" exception\nFAILURE");
+ throw new Exception("testException failed.");
+ }
+ }
+
+ {
+ try {
+ if (trace) write("client.testMultiException(\"Xception\", \"test 1\") =>");
+ auto result = client.testMultiException("Xception", "test 1");
+ if (trace) writeln(" result\nFAILURE");
+ throw new Exception("testMultiException failed.");
+ } catch (Xception e) {
+ if (trace) writefln(" {%s, \"%s\"}", e.errorCode, e.message);
+ }
+
+ try {
+ if (trace) write("client.testMultiException(\"Xception2\", \"test 2\") =>");
+ auto result = client.testMultiException("Xception2", "test 2");
+ if (trace) writeln(" result\nFAILURE");
+ throw new Exception("testMultiException failed.");
+ } catch (Xception2 e) {
+ if (trace) writefln(" {%s, {\"%s\"}}",
+ e.errorCode, e.struct_thing.string_thing);
+ }
+
+ try {
+ if (trace) writef("client.testMultiException(\"success\", \"test 3\") =>");
+ auto result = client.testMultiException("success", "test 3");
+ if (trace) writefln(" {{\"%s\"}}", result.string_thing);
+ } catch (Exception e) {
+ if (trace) writeln(" exception\nFAILURE");
+ throw new Exception("testMultiException failed.");
+ }
+ }
+
+ // Do not run oneway test when doing multiple iterations, as it blocks the
+ // server for three seconds.
+ if (numTests == 1) {
+ if (trace) writef("client.testOneway(3) =>");
+ auto onewayWatch = StopWatch(AutoStart.yes);
+ client.testOneway(3);
+ onewayWatch.stop();
+ if (onewayWatch.peek().msecs > 200) {
+ if (trace) {
+ writefln(" FAILURE - took %s ms", onewayWatch.peek().usecs / 1000.0);
+ }
+ throw new Exception("testOneway failed.");
+ } else {
+ if (trace) {
+ writefln(" success - took %s ms", onewayWatch.peek().usecs / 1000.0);
+ }
+ }
+
+ // Redo a simple test after the oneway to make sure we aren't "off by
+ // one", which would be the case if the server treated oneway methods
+ // like normal ones.
+ if (trace) write("re-test testI32(-1)");
+ i32 = client.testI32(-1);
+ if (trace) writefln(" = %s", i32);
+ }
+
+ // Time metering.
+ sw.stop();
+
+ immutable tot = sw.peek().usecs;
+ if (trace) writefln("Total time: %s us\n", tot);
+
+ time_tot += tot;
+ if (time_min == 0 || tot < time_min) {
+ time_min = tot;
+ }
+ if (tot > time_max) {
+ time_max = tot;
+ }
+ protocol.transport.close();
+
+ sw.reset();
+ }
+
+ writeln("All tests done.");
+
+ if (numTests > 1) {
+ auto time_avg = time_tot / numTests;
+ writefln("Min time: %s us", time_min);
+ writefln("Max time: %s us", time_max);
+ writefln("Avg time: %s us", time_avg);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/d/test/thrift_test_common.d b/src/jaegertracing/thrift/lib/d/test/thrift_test_common.d
new file mode 100644
index 000000000..13a568613
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/test/thrift_test_common.d
@@ -0,0 +1,92 @@
+module thrift_test_common;
+
+import std.stdio;
+import thrift.test.ThriftTest_types;
+
+enum ProtocolType {
+ binary,
+ compact,
+ json
+}
+
+void writeInsanityReturn(in Insanity[Numberz][UserId] insane) {
+ write("{");
+ foreach(key1, value1; insane) {
+ writef("%s => {", key1);
+ foreach(key2, value2; value1) {
+ writef("%s => {", key2);
+ write("{");
+ foreach(key3, value3; value2.userMap) {
+ writef("%s => %s, ", key3, value3);
+ }
+ write("}, ");
+
+ write("{");
+ foreach (x; value2.xtructs) {
+ writef("{\"%s\", %s, %s, %s}, ",
+ x.string_thing, x.byte_thing, x.i32_thing, x.i64_thing);
+ }
+ write("}");
+
+ write("}, ");
+ }
+ write("}, ");
+ }
+ write("}");
+}
+
+Insanity[Numberz][UserId] testInsanityReturn;
+int[int][int] testMapMapReturn;
+
+static this() {
+ testInsanityReturn = {
+ Insanity[Numberz][UserId] insane;
+
+ Xtruct hello;
+ hello.string_thing = "Hello2";
+ hello.byte_thing = 2;
+ hello.i32_thing = 2;
+ hello.i64_thing = 2;
+
+ Xtruct goodbye;
+ goodbye.string_thing = "Goodbye4";
+ goodbye.byte_thing = 4;
+ goodbye.i32_thing = 4;
+ goodbye.i64_thing = 4;
+
+ Insanity crazy;
+ crazy.userMap[Numberz.EIGHT] = 8;
+ crazy.xtructs ~= goodbye;
+
+ Insanity looney;
+ // The C++ TestServer also assigns these to crazy, but that is probably
+ // an oversight.
+ looney.userMap[Numberz.FIVE] = 5;
+ looney.xtructs ~= hello;
+
+ Insanity[Numberz] first_map;
+ first_map[Numberz.TWO] = crazy;
+ first_map[Numberz.THREE] = crazy;
+ insane[1] = first_map;
+
+ Insanity[Numberz] second_map;
+ second_map[Numberz.SIX] = looney;
+ insane[2] = second_map;
+ return insane;
+ }();
+
+ testMapMapReturn = {
+ int[int] pos;
+ int[int] neg;
+
+ for (int i = 1; i < 5; i++) {
+ pos[i] = i;
+ neg[-i] = -i;
+ }
+
+ int[int][int] result;
+ result[4] = pos;
+ result[-4] = neg;
+ return result;
+ }();
+}
diff --git a/src/jaegertracing/thrift/lib/d/test/thrift_test_runner.sh b/src/jaegertracing/thrift/lib/d/test/thrift_test_runner.sh
new file mode 100755
index 000000000..51bfe9999
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/test/thrift_test_runner.sh
@@ -0,0 +1,93 @@
+#!/bin/bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Runs the D ThriftTest client and servers for all combinations of transport,
+# protocol, SSL-mode and server type.
+# Pass -k to keep going after failed tests.
+
+CUR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+protocols="binary compact json"
+# TODO: fix and enable http
+# transports="buffered framed raw http"
+transports="buffered framed raw"
+servers="simple taskpool threaded"
+framed_only_servers="nonblocking pooledNonblocking"
+
+# Don't leave any server instances behind when interrupted (e.g. by Ctrl+C)
+# or terminated.
+trap "kill $(jobs -p) 2>/dev/null" INT TERM
+
+for protocol in $protocols; do
+ for ssl in "" " --ssl"; do
+ for transport in $transports; do
+ for server in $servers $framed_only_servers; do
+ case $framed_only_servers in
+ *$server*) if [ $transport != "framed" ] || [ $ssl != "" ]; then continue; fi;;
+ esac
+
+ args="--transport=$transport --protocol=$protocol$ssl"
+ ${CUR}/thrift_test_server $args --server-type=$server > /dev/null &
+ server_pid=$!
+
+ # Give the server some time to get up and check if it runs (yes, this
+ # is a huge kludge, should add a connect timeout to test client).
+ client_rc=-1
+ if [ "$server" = "taskpool" ]; then
+ sleep 0.5
+ else
+ sleep 0.02
+ fi
+ kill -0 $server_pid 2>/dev/null
+ if [ $? -eq 0 ]; then
+ ${CUR}/thrift_test_client $args --numTests=10 > /dev/null
+ client_rc=$?
+
+ # Temporarily redirect stderr to null to avoid job control messages,
+ # restore it afterwards.
+ exec 3>&2
+ exec 2>/dev/null
+ kill $server_pid
+ exec 3>&2
+ fi
+
+ # Get the server exit code (wait should immediately return).
+ wait $server_pid
+ server_rc=$?
+
+ if [ $client_rc -ne 0 -o $server_rc -eq 1 ]; then
+ echo -e "\nTests failed for: $args --server-type=$server"
+ failed="true"
+ if [ "$1" != "-k" ]; then
+ exit 1
+ fi
+ else
+ echo -n "."
+ fi
+ done
+ done
+ done
+done
+
+echo
+if [ -z "$failed" ]; then
+ echo "All tests passed."
+fi
diff --git a/src/jaegertracing/thrift/lib/d/test/thrift_test_server.d b/src/jaegertracing/thrift/lib/d/test/thrift_test_server.d
new file mode 100644
index 000000000..ce820d699
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/test/thrift_test_server.d
@@ -0,0 +1,337 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+module thrift_test_server;
+
+import core.stdc.errno : errno;
+import core.stdc.signal : signal, SIGINT, SIG_DFL, SIG_ERR;
+import core.thread : dur, Thread;
+import std.algorithm;
+import std.exception : enforce;
+import std.getopt;
+import std.parallelism : totalCPUs;
+import std.string;
+import std.stdio;
+import std.typetuple : TypeTuple, staticMap;
+import thrift.base;
+import thrift.codegen.processor;
+import thrift.protocol.base;
+import thrift.protocol.binary;
+import thrift.protocol.compact;
+import thrift.protocol.json;
+import thrift.server.base;
+import thrift.server.transport.socket;
+import thrift.server.transport.ssl;
+import thrift.transport.base;
+import thrift.transport.buffered;
+import thrift.transport.framed;
+import thrift.transport.http;
+import thrift.transport.ssl;
+import thrift.util.cancellation;
+import thrift.util.hashset;
+import test_utils;
+
+import thrift_test_common;
+import thrift.test.ThriftTest_types;
+import thrift.test.ThriftTest;
+
+class TestHandler : ThriftTest {
+ this(bool trace) {
+ trace_ = trace;
+ }
+
+ override void testVoid() {
+ if (trace_) writeln("testVoid()");
+ }
+
+ override string testString(string thing) {
+ if (trace_) writefln("testString(\"%s\")", thing);
+ return thing;
+ }
+
+ override byte testByte(byte thing) {
+ if (trace_) writefln("testByte(%s)", thing);
+ return thing;
+ }
+
+ override int testI32(int thing) {
+ if (trace_) writefln("testI32(%s)", thing);
+ return thing;
+ }
+
+ override long testI64(long thing) {
+ if (trace_) writefln("testI64(%s)", thing);
+ return thing;
+ }
+
+ override double testDouble(double thing) {
+ if (trace_) writefln("testDouble(%s)", thing);
+ return thing;
+ }
+
+ override string testBinary(string thing) {
+ if (trace_) writefln("testBinary(\"%s\")", thing);
+ return thing;
+ }
+
+ override bool testBool(bool thing) {
+ if (trace_) writefln("testBool(\"%s\")", thing);
+ return thing;
+ }
+
+ override Xtruct testStruct(ref const(Xtruct) thing) {
+ if (trace_) writefln("testStruct({\"%s\", %s, %s, %s})",
+ thing.string_thing, thing.byte_thing, thing.i32_thing, thing.i64_thing);
+ return thing;
+ }
+
+ override Xtruct2 testNest(ref const(Xtruct2) nest) {
+ auto thing = nest.struct_thing;
+ if (trace_) writefln("testNest({%s, {\"%s\", %s, %s, %s}, %s})",
+ nest.byte_thing, thing.string_thing, thing.byte_thing, thing.i32_thing,
+ thing.i64_thing, nest.i32_thing);
+ return nest;
+ }
+
+ override int[int] testMap(int[int] thing) {
+ if (trace_) writefln("testMap({%s})", thing);
+ return thing;
+ }
+
+ override HashSet!int testSet(HashSet!int thing) {
+ if (trace_) writefln("testSet({%s})",
+ join(map!`to!string(a)`(thing[]), ", "));
+ return thing;
+ }
+
+ override int[] testList(int[] thing) {
+ if (trace_) writefln("testList(%s)", thing);
+ return thing;
+ }
+
+ override Numberz testEnum(Numberz thing) {
+ if (trace_) writefln("testEnum(%s)", thing);
+ return thing;
+ }
+
+ override UserId testTypedef(UserId thing) {
+ if (trace_) writefln("testTypedef(%s)", thing);
+ return thing;
+ }
+
+ override string[string] testStringMap(string[string] thing) {
+ if (trace_) writefln("testStringMap(%s)", thing);
+ return thing;
+ }
+
+ override int[int][int] testMapMap(int hello) {
+ if (trace_) writefln("testMapMap(%s)", hello);
+ return testMapMapReturn;
+ }
+
+ override Insanity[Numberz][UserId] testInsanity(ref const(Insanity) argument) {
+ if (trace_) writeln("testInsanity()");
+ Insanity[Numberz][UserId] ret;
+ Insanity[Numberz] m1;
+ Insanity[Numberz] m2;
+ Insanity tmp;
+ tmp = cast(Insanity)argument;
+ m1[Numberz.TWO] = tmp;
+ m1[Numberz.THREE] = tmp;
+ m2[Numberz.SIX] = Insanity();
+ ret[1] = m1;
+ ret[2] = m2;
+ return ret;
+ }
+
+ override Xtruct testMulti(byte arg0, int arg1, long arg2, string[short] arg3,
+ Numberz arg4, UserId arg5)
+ {
+ if (trace_) writeln("testMulti()");
+ return Xtruct("Hello2", arg0, arg1, arg2);
+ }
+
+ override void testException(string arg) {
+ if (trace_) writefln("testException(%s)", arg);
+ if (arg == "Xception") {
+ auto e = new Xception();
+ e.errorCode = 1001;
+ e.message = arg;
+ throw e;
+ } else if (arg == "TException") {
+ throw new TException();
+ } else if (arg == "ApplicationException") {
+ throw new TException();
+ }
+ }
+
+ override Xtruct testMultiException(string arg0, string arg1) {
+ if (trace_) writefln("testMultiException(%s, %s)", arg0, arg1);
+
+ if (arg0 == "Xception") {
+ auto e = new Xception();
+ e.errorCode = 1001;
+ e.message = "This is an Xception";
+ throw e;
+ } else if (arg0 == "Xception2") {
+ auto e = new Xception2();
+ e.errorCode = 2002;
+ e.struct_thing.string_thing = "This is an Xception2";
+ throw e;
+ } else {
+ return Xtruct(arg1);
+ }
+ }
+
+ override void testOneway(int sleepFor) {
+ if (trace_) writefln("testOneway(%s): Sleeping...", sleepFor);
+ Thread.sleep(dur!"seconds"(sleepFor));
+ if (trace_) writefln("testOneway(%s): done sleeping!", sleepFor);
+ }
+
+private:
+ bool trace_;
+}
+
+shared(bool) gShutdown = false;
+
+nothrow @nogc extern(C) void handleSignal(int sig) {
+ gShutdown = true;
+}
+
+// Runs a thread that waits for shutdown to be
+// signaled and then triggers cancellation,
+// causing the server to stop. While we could
+// use a signalfd for this purpose, we are instead
+// opting for a busy waiting scheme for maximum
+// portability since signalfd is a linux thing.
+
+class ShutdownThread : Thread {
+ this(TCancellationOrigin cancellation) {
+ cancellation_ = cancellation;
+ super(&run);
+ }
+
+private:
+ void run() {
+ while (!gShutdown) {
+ Thread.sleep(dur!("msecs")(25));
+ }
+ cancellation_.trigger();
+ }
+
+ TCancellationOrigin cancellation_;
+}
+
+void main(string[] args) {
+ ushort port = 9090;
+ ServerType serverType;
+ ProtocolType protocolType;
+ size_t numIOThreads = 1;
+ TransportType transportType;
+ bool ssl = false;
+ bool trace = true;
+ size_t taskPoolSize = totalCPUs;
+
+ getopt(args, "port", &port, "protocol", &protocolType, "server-type",
+ &serverType, "ssl", &ssl, "num-io-threads", &numIOThreads,
+ "task-pool-size", &taskPoolSize, "trace", &trace,
+ "transport", &transportType);
+
+ if (serverType == ServerType.nonblocking ||
+ serverType == ServerType.pooledNonblocking
+ ) {
+ enforce(transportType == TransportType.framed,
+ "Need to use framed transport with non-blocking server.");
+ enforce(!ssl, "The non-blocking server does not support SSL yet.");
+
+ // Don't wrap the contents into another layer of framing.
+ transportType = TransportType.raw;
+ }
+
+ version (ThriftTestTemplates) {
+ // Only exercise the specialized template code paths if explicitly enabled
+ // to reduce memory consumption on regular test suite runs – there should
+ // not be much that can go wrong with that specifically anyway.
+ alias TypeTuple!(TBufferedTransport, TFramedTransport, TServerHttpTransport)
+ AvailableTransports;
+ alias TypeTuple!(
+ staticMap!(TBinaryProtocol, AvailableTransports),
+ staticMap!(TCompactProtocol, AvailableTransports)
+ ) AvailableProtocols;
+ } else {
+ alias TypeTuple!() AvailableTransports;
+ alias TypeTuple!() AvailableProtocols;
+ }
+
+ TProtocolFactory protocolFactory;
+ final switch (protocolType) {
+ case ProtocolType.binary:
+ protocolFactory = new TBinaryProtocolFactory!AvailableTransports;
+ break;
+ case ProtocolType.compact:
+ protocolFactory = new TCompactProtocolFactory!AvailableTransports;
+ break;
+ case ProtocolType.json:
+ protocolFactory = new TJsonProtocolFactory!AvailableTransports;
+ break;
+ }
+
+ auto processor = new TServiceProcessor!(ThriftTest, AvailableProtocols)(
+ new TestHandler(trace));
+
+ TServerSocket serverSocket;
+ if (ssl) {
+ auto sslContext = new TSSLContext();
+ sslContext.serverSide = true;
+ sslContext.loadCertificate("../../../test/keys/server.crt");
+ sslContext.loadPrivateKey("../../../test/keys/server.key");
+ sslContext.ciphers = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH";
+ serverSocket = new TSSLServerSocket(port, sslContext);
+ } else {
+ serverSocket = new TServerSocket(port);
+ }
+
+ auto transportFactory = createTransportFactory(transportType);
+
+ auto server = createServer(serverType, numIOThreads, taskPoolSize,
+ processor, serverSocket, transportFactory, protocolFactory);
+
+ // Set up SIGINT signal handling
+ enforce(signal(SIGINT, &handleSignal) != SIG_ERR,
+ "Could not replace the SIGINT signal handler: errno {0}".format(errno()));
+
+ // Set up a server cancellation trigger
+ auto cancel = new TCancellationOrigin();
+
+ // Set up a listener for the shutdown condition - this will
+ // wake up when the signal occurs and trigger cancellation.
+ auto shutdown = new ShutdownThread(cancel);
+ shutdown.start();
+
+ // Serve from this thread; the signal will stop the server
+ // and control will return here
+ writefln("Starting %s/%s %s ThriftTest server %son port %s...", protocolType,
+ transportType, serverType, ssl ? "(using SSL) ": "", port);
+ server.serve(cancel);
+ shutdown.join();
+ signal(SIGINT, SIG_DFL);
+
+ writeln("done.");
+}
diff --git a/src/jaegertracing/thrift/lib/d/test/transport_test.d b/src/jaegertracing/thrift/lib/d/test/transport_test.d
new file mode 100644
index 000000000..623e03f0e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/d/test/transport_test.d
@@ -0,0 +1,803 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Exercises various transports, combined with the buffered/framed wrappers.
+ *
+ * Originally ported from the C++ version, with Windows support code added.
+ */
+module transport_test;
+
+import core.atomic;
+import core.time : Duration;
+import core.thread : Thread;
+import std.conv : to;
+import std.datetime;
+import std.exception : enforce;
+static import std.file;
+import std.getopt;
+import std.random : rndGen, uniform, unpredictableSeed;
+import std.socket;
+import std.stdio;
+import std.string;
+import std.typetuple;
+import thrift.transport.base;
+import thrift.transport.buffered;
+import thrift.transport.framed;
+import thrift.transport.file;
+import thrift.transport.http;
+import thrift.transport.memory;
+import thrift.transport.socket;
+import thrift.transport.zlib;
+
+/*
+ * Size generation helpers – used to be able to run the same testing code
+ * with both constant and random total/chunk sizes.
+ */
+
+interface SizeGenerator {
+ size_t nextSize();
+ string toString();
+}
+
+class ConstantSizeGenerator : SizeGenerator {
+ this(size_t value) {
+ value_ = value;
+ }
+
+ override size_t nextSize() {
+ return value_;
+ }
+
+ override string toString() const {
+ return to!string(value_);
+ }
+
+private:
+ size_t value_;
+}
+
+class RandomSizeGenerator : SizeGenerator {
+ this(size_t min, size_t max) {
+ min_ = min;
+ max_ = max;
+ }
+
+ override size_t nextSize() {
+ return uniform!"[]"(min_, max_);
+ }
+
+ override string toString() const {
+ return format("rand(%s, %s)", min_, max_);
+ }
+
+ size_t min() const @property {
+ return min_;
+ }
+
+ size_t max() const @property {
+ return max_;
+ }
+
+private:
+ size_t min_;
+ size_t max_;
+}
+
+
+/*
+ * Classes to set up coupled transports
+ */
+
+/**
+ * Helper class to represent a coupled pair of transports.
+ *
+ * Data written to the output transport can be read from the input transport.
+ *
+ * This is used as the base class for the various coupled transport
+ * implementations. It shouldn't be used directly.
+ */
+class CoupledTransports(Transport) if (isTTransport!Transport) {
+ Transport input;
+ Transport output;
+}
+
+template isCoupledTransports(T) {
+ static if (is(T _ : CoupledTransports!U, U)) {
+ enum isCoupledTransports = true;
+ } else {
+ enum isCoupledTransports = false;
+ }
+}
+
+/**
+ * Helper template class for creating coupled transports that wrap
+ * another transport.
+ */
+class CoupledWrapperTransports(WrapperTransport, InnerCoupledTransports) if (
+ isTTransport!WrapperTransport && isCoupledTransports!InnerCoupledTransports
+) : CoupledTransports!WrapperTransport {
+ this() {
+ inner_ = new InnerCoupledTransports();
+ if (inner_.input) {
+ input = new WrapperTransport(inner_.input);
+ }
+ if (inner_.output) {
+ output = new WrapperTransport(inner_.output);
+ }
+ }
+
+ ~this() {
+ destroy(inner_);
+ }
+
+private:
+ InnerCoupledTransports inner_;
+}
+
+import thrift.internal.codegen : PApply;
+alias PApply!(CoupledWrapperTransports, TBufferedTransport) CoupledBufferedTransports;
+alias PApply!(CoupledWrapperTransports, TFramedTransport) CoupledFramedTransports;
+alias PApply!(CoupledWrapperTransports, TZlibTransport) CoupledZlibTransports;
+
+/**
+ * Coupled TMemoryBuffers.
+ */
+class CoupledMemoryBuffers : CoupledTransports!TMemoryBuffer {
+ this() {
+ buf = new TMemoryBuffer;
+ input = buf;
+ output = buf;
+ }
+
+ TMemoryBuffer buf;
+}
+
+/**
+ * Coupled TSockets.
+ */
+class CoupledSocketTransports : CoupledTransports!TSocket {
+ this() {
+ auto sockets = socketPair();
+ input = new TSocket(sockets[0]);
+ output = new TSocket(sockets[1]);
+ }
+
+ ~this() {
+ input.close();
+ output.close();
+ }
+}
+
+/**
+ * Coupled TFileTransports
+ */
+class CoupledFileTransports : CoupledTransports!TTransport {
+ this() {
+ // We actually need the file name of the temp file here, so we can't just
+ // use the usual tempfile facilities.
+ do {
+ fileName_ = tmpDir ~ "/thrift.transport_test." ~ to!string(rndGen().front);
+ rndGen().popFront();
+ } while (std.file.exists(fileName_));
+
+ writefln("Using temp file: %s", fileName_);
+
+ auto writer = new TFileWriterTransport(fileName_);
+ writer.open();
+ output = writer;
+
+ // Wait until the file has been created.
+ writer.flush();
+
+ auto reader = new TFileReaderTransport(fileName_);
+ reader.open();
+ reader.readTimeout(dur!"msecs"(-1));
+ input = reader;
+ }
+
+ ~this() {
+ input.close();
+ output.close();
+ std.file.remove(fileName_);
+ }
+
+ static string tmpDir;
+
+private:
+ string fileName_;
+}
+
+
+/*
+ * Test functions
+ */
+
+/**
+ * Test interleaved write and read calls.
+ *
+ * Generates a buffer totalSize bytes long, then writes it to the transport,
+ * and verifies the written data can be read back correctly.
+ *
+ * Mode of operation:
+ * - call wChunkGenerator to figure out how large of a chunk to write
+ * - call wSizeGenerator to get the size for individual write() calls,
+ * and do this repeatedly until the entire chunk is written.
+ * - call rChunkGenerator to figure out how large of a chunk to read
+ * - call rSizeGenerator to get the size for individual read() calls,
+ * and do this repeatedly until the entire chunk is read.
+ * - repeat until the full buffer is written and read back,
+ * then compare the data read back against the original buffer
+ *
+ *
+ * - If any of the size generators return 0, this means to use the maximum
+ * possible size.
+ *
+ * - If maxOutstanding is non-zero, write chunk sizes will be chosen such that
+ * there are never more than maxOutstanding bytes waiting to be read back.
+ */
+void testReadWrite(CoupledTransports)(
+ size_t totalSize,
+ SizeGenerator wSizeGenerator,
+ SizeGenerator rSizeGenerator,
+ SizeGenerator wChunkGenerator,
+ SizeGenerator rChunkGenerator,
+ size_t maxOutstanding
+) if (
+ isCoupledTransports!CoupledTransports
+) {
+ scope transports = new CoupledTransports;
+ assert(transports.input);
+ assert(transports.output);
+
+ auto wbuf = new ubyte[totalSize];
+ auto rbuf = new ubyte[totalSize];
+
+ // Store some data in wbuf.
+ foreach (i, ref b; wbuf) {
+ b = i & 0xff;
+ }
+
+ size_t totalWritten;
+ size_t totalRead;
+ while (totalRead < totalSize) {
+ // Determine how large a chunk of data to write.
+ auto wChunkSize = wChunkGenerator.nextSize();
+ if (wChunkSize == 0 || wChunkSize > totalSize - totalWritten) {
+ wChunkSize = totalSize - totalWritten;
+ }
+
+ // Make sure (totalWritten - totalRead) + wChunkSize is less than
+ // maxOutstanding.
+ if (maxOutstanding > 0 &&
+ wChunkSize > maxOutstanding - (totalWritten - totalRead)) {
+ wChunkSize = maxOutstanding - (totalWritten - totalRead);
+ }
+
+ // Write the chunk.
+ size_t chunkWritten = 0;
+ while (chunkWritten < wChunkSize) {
+ auto writeSize = wSizeGenerator.nextSize();
+ if (writeSize == 0 || writeSize > wChunkSize - chunkWritten) {
+ writeSize = wChunkSize - chunkWritten;
+ }
+
+ transports.output.write(wbuf[totalWritten .. totalWritten + writeSize]);
+ chunkWritten += writeSize;
+ totalWritten += writeSize;
+ }
+
+ // Flush the data, so it will be available in the read transport
+ // Don't flush if wChunkSize is 0. (This should only happen if
+ // totalWritten == totalSize already, and we're only reading now.)
+ if (wChunkSize > 0) {
+ transports.output.flush();
+ }
+
+ // Determine how large a chunk of data to read back.
+ auto rChunkSize = rChunkGenerator.nextSize();
+ if (rChunkSize == 0 || rChunkSize > totalWritten - totalRead) {
+ rChunkSize = totalWritten - totalRead;
+ }
+
+ // Read the chunk.
+ size_t chunkRead;
+ while (chunkRead < rChunkSize) {
+ auto readSize = rSizeGenerator.nextSize();
+ if (readSize == 0 || readSize > rChunkSize - chunkRead) {
+ readSize = rChunkSize - chunkRead;
+ }
+
+ size_t bytesRead;
+ try {
+ bytesRead = transports.input.read(
+ rbuf[totalRead .. totalRead + readSize]);
+ } catch (TTransportException e) {
+ throw new Exception(format(`read(pos = %s, size = %s) threw ` ~
+ `exception "%s"; written so far: %s/%s bytes`, totalRead, readSize,
+ e.msg, totalWritten, totalSize));
+ }
+
+ enforce(bytesRead > 0, format(`read(pos = %s, size = %s) returned %s; ` ~
+ `written so far: %s/%s bytes`, totalRead, readSize, bytesRead,
+ totalWritten, totalSize));
+
+ chunkRead += bytesRead;
+ totalRead += bytesRead;
+ }
+ }
+
+ // make sure the data read back is identical to the data written
+ if (rbuf != wbuf) {
+ stderr.writefln("%s vs. %s", wbuf[$ - 4 .. $], rbuf[$ - 4 .. $]);
+ stderr.writefln("rbuf: %s vs. wbuf: %s", rbuf.length, wbuf.length);
+ }
+ enforce(rbuf == wbuf);
+}
+
+void testReadPartAvailable(CoupledTransports)() if (
+ isCoupledTransports!CoupledTransports
+) {
+ scope transports = new CoupledTransports;
+ assert(transports.input);
+ assert(transports.output);
+
+ ubyte[10] writeBuf = 'a';
+ ubyte[10] readBuf;
+
+ // Attemping to read 10 bytes when only 9 are available should return 9
+ // immediately.
+ transports.output.write(writeBuf[0 .. 9]);
+ transports.output.flush();
+
+ auto t = Trigger(dur!"seconds"(3), transports.output, 1);
+ auto bytesRead = transports.input.read(readBuf);
+ enforce(t.fired == 0);
+ enforce(bytesRead == 9);
+}
+
+void testReadPartialMidframe(CoupledTransports)() if (
+ isCoupledTransports!CoupledTransports
+) {
+ scope transports = new CoupledTransports;
+ assert(transports.input);
+ assert(transports.output);
+
+ ubyte[13] writeBuf = 'a';
+ ubyte[14] readBuf;
+
+ // Attempt to read 10 bytes, when only 9 are available, but after we have
+ // already read part of the data that is available. This exercises a
+ // different code path for several of the transports.
+ //
+ // For transports that add their own framing (e.g., TFramedTransport and
+ // TFileTransport), the two flush calls break up the data in to a 10 byte
+ // frame and a 3 byte frame. The first read then puts us partway through the
+ // first frame, and then we attempt to read past the end of that frame, and
+ // through the next frame, too.
+ //
+ // For buffered transports that perform read-ahead (e.g.,
+ // TBufferedTransport), the read-ahead will most likely see all 13 bytes
+ // written on the first read. The next read will then attempt to read past
+ // the end of the read-ahead buffer.
+ //
+ // Flush 10 bytes, then 3 bytes. This creates 2 separate frames for
+ // transports that track framing internally.
+ transports.output.write(writeBuf[0 .. 10]);
+ transports.output.flush();
+ transports.output.write(writeBuf[10 .. 13]);
+ transports.output.flush();
+
+ // Now read 4 bytes, so that we are partway through the written data.
+ auto bytesRead = transports.input.read(readBuf[0 .. 4]);
+ enforce(bytesRead == 4);
+
+ // Now attempt to read 10 bytes. Only 9 more are available.
+ //
+ // We should be able to get all 9 bytes, but it might take multiple read
+ // calls, since it is valid for read() to return fewer bytes than requested.
+ // (Most transports do immediately return 9 bytes, but the framing transports
+ // tend to only return to the end of the current frame, which is 6 bytes in
+ // this case.)
+ size_t totalRead = 0;
+ while (totalRead < 9) {
+ auto t = Trigger(dur!"seconds"(3), transports.output, 1);
+ bytesRead = transports.input.read(readBuf[4 + totalRead .. 14]);
+ enforce(t.fired == 0);
+ enforce(bytesRead > 0);
+ totalRead += bytesRead;
+ enforce(totalRead <= 9);
+ }
+
+ enforce(totalRead == 9);
+}
+
+void testBorrowPartAvailable(CoupledTransports)() if (
+ isCoupledTransports!CoupledTransports
+) {
+ scope transports = new CoupledTransports;
+ assert(transports.input);
+ assert(transports.output);
+
+ ubyte[9] writeBuf = 'a';
+ ubyte[10] readBuf;
+
+ // Attemping to borrow 10 bytes when only 9 are available should return NULL
+ // immediately.
+ transports.output.write(writeBuf);
+ transports.output.flush();
+
+ auto t = Trigger(dur!"seconds"(3), transports.output, 1);
+ auto borrowLen = readBuf.length;
+ auto borrowedBuf = transports.input.borrow(readBuf.ptr, borrowLen);
+ enforce(t.fired == 0);
+ enforce(borrowedBuf is null);
+}
+
+void testReadNoneAvailable(CoupledTransports)() if (
+ isCoupledTransports!CoupledTransports
+) {
+ scope transports = new CoupledTransports;
+ assert(transports.input);
+ assert(transports.output);
+
+ // Attempting to read when no data is available should either block until
+ // some data is available, or fail immediately. (e.g., TSocket blocks,
+ // TMemoryBuffer just fails.)
+ //
+ // If the transport blocks, it should succeed once some data is available,
+ // even if less than the amount requested becomes available.
+ ubyte[10] readBuf;
+
+ auto t = Trigger(dur!"seconds"(1), transports.output, 2);
+ t.add(dur!"seconds"(1), transports.output, 8);
+
+ auto bytesRead = transports.input.read(readBuf);
+ if (bytesRead == 0) {
+ enforce(t.fired == 0);
+ } else {
+ enforce(t.fired == 1);
+ enforce(bytesRead == 2);
+ }
+}
+
+void testBorrowNoneAvailable(CoupledTransports)() if (
+ isCoupledTransports!CoupledTransports
+) {
+ scope transports = new CoupledTransports;
+ assert(transports.input);
+ assert(transports.output);
+
+ ubyte[16] writeBuf = 'a';
+
+ // Attempting to borrow when no data is available should fail immediately
+ auto t = Trigger(dur!"seconds"(1), transports.output, 10);
+
+ auto borrowLen = 10;
+ auto borrowedBuf = transports.input.borrow(null, borrowLen);
+ enforce(borrowedBuf is null);
+ enforce(t.fired == 0);
+}
+
+
+void doRwTest(CoupledTransports)(
+ size_t totalSize,
+ SizeGenerator wSizeGen,
+ SizeGenerator rSizeGen,
+ SizeGenerator wChunkSizeGen = new ConstantSizeGenerator(0),
+ SizeGenerator rChunkSizeGen = new ConstantSizeGenerator(0),
+ size_t maxOutstanding = 0
+) if (
+ isCoupledTransports!CoupledTransports
+) {
+ totalSize = cast(size_t)(totalSize * g_sizeMultiplier);
+
+ scope(failure) {
+ writefln("Test failed for %s: testReadWrite(%s, %s, %s, %s, %s, %s)",
+ CoupledTransports.stringof, totalSize, wSizeGen, rSizeGen,
+ wChunkSizeGen, rChunkSizeGen, maxOutstanding);
+ }
+
+ testReadWrite!CoupledTransports(totalSize, wSizeGen, rSizeGen,
+ wChunkSizeGen, rChunkSizeGen, maxOutstanding);
+}
+
+void doBlockingTest(CoupledTransports)() if (
+ isCoupledTransports!CoupledTransports
+) {
+ void writeFailure(string name) {
+ writefln("Test failed for %s: %s()", CoupledTransports.stringof, name);
+ }
+
+ {
+ scope(failure) writeFailure("testReadPartAvailable");
+ testReadPartAvailable!CoupledTransports();
+ }
+
+ {
+ scope(failure) writeFailure("testReadPartialMidframe");
+ testReadPartialMidframe!CoupledTransports();
+ }
+
+ {
+ scope(failure) writeFailure("testReadNoneAvaliable");
+ testReadNoneAvailable!CoupledTransports();
+ }
+
+ {
+ scope(failure) writeFailure("testBorrowPartAvailable");
+ testBorrowPartAvailable!CoupledTransports();
+ }
+
+ {
+ scope(failure) writeFailure("testBorrowNoneAvailable");
+ testBorrowNoneAvailable!CoupledTransports();
+ }
+}
+
+SizeGenerator getGenerator(T)(T t) {
+ static if (is(T : SizeGenerator)) {
+ return t;
+ } else {
+ return new ConstantSizeGenerator(t);
+ }
+}
+
+template WrappedTransports(T) if (isCoupledTransports!T) {
+ alias TypeTuple!(
+ T,
+ CoupledBufferedTransports!T,
+ CoupledFramedTransports!T,
+ CoupledZlibTransports!T
+ ) WrappedTransports;
+}
+
+void testRw(C, R, S)(
+ size_t totalSize,
+ R wSize,
+ S rSize
+) if (
+ isCoupledTransports!C && is(typeof(getGenerator(wSize))) &&
+ is(typeof(getGenerator(rSize)))
+) {
+ testRw!C(totalSize, wSize, rSize, 0, 0, 0);
+}
+
+void testRw(C, R, S, T, U)(
+ size_t totalSize,
+ R wSize,
+ S rSize,
+ T wChunkSize,
+ U rChunkSize,
+ size_t maxOutstanding = 0
+) if (
+ isCoupledTransports!C && is(typeof(getGenerator(wSize))) &&
+ is(typeof(getGenerator(rSize))) && is(typeof(getGenerator(wChunkSize))) &&
+ is(typeof(getGenerator(rChunkSize)))
+) {
+ foreach (T; WrappedTransports!C) {
+ doRwTest!T(
+ totalSize,
+ getGenerator(wSize),
+ getGenerator(rSize),
+ getGenerator(wChunkSize),
+ getGenerator(rChunkSize),
+ maxOutstanding
+ );
+ }
+}
+
+void testBlocking(C)() if (isCoupledTransports!C) {
+ foreach (T; WrappedTransports!C) {
+ doBlockingTest!T();
+ }
+}
+
+// A quick hack, for the sake of brevity…
+float g_sizeMultiplier = 1;
+
+version (Posix) {
+ immutable defaultTempDir = "/tmp";
+} else version (Windows) {
+ import core.sys.windows.windows;
+ extern(Windows) DWORD GetTempPathA(DWORD nBufferLength, LPTSTR lpBuffer);
+
+ string defaultTempDir() @property {
+ char[MAX_PATH + 1] dir;
+ enforce(GetTempPathA(dir.length, dir.ptr));
+ return to!string(dir.ptr)[0 .. $ - 1];
+ }
+} else static assert(false);
+
+void main(string[] args) {
+ int seed = unpredictableSeed();
+ string tmpDir = defaultTempDir;
+
+ getopt(args, "seed", &seed, "size-multiplier", &g_sizeMultiplier,
+ "tmp-dir", &tmpDir);
+ enforce(g_sizeMultiplier >= 0, "Size multiplier must not be negative.");
+
+ writefln("Using seed: %s", seed);
+ rndGen().seed(seed);
+ CoupledFileTransports.tmpDir = tmpDir;
+
+ auto rand4k = new RandomSizeGenerator(1, 4096);
+
+ /*
+ * We do the basically the same set of tests for each transport type,
+ * although we tweak the parameters in some places.
+ */
+
+ // TMemoryBuffer tests
+ testRw!CoupledMemoryBuffers(1024 * 1024, 0, 0);
+ testRw!CoupledMemoryBuffers(1024 * 256, rand4k, rand4k);
+ testRw!CoupledMemoryBuffers(1024 * 256, 167, 163);
+ testRw!CoupledMemoryBuffers(1024 * 16, 1, 1);
+
+ testRw!CoupledMemoryBuffers(1024 * 256, 0, 0, rand4k, rand4k);
+ testRw!CoupledMemoryBuffers(1024 * 256, rand4k, rand4k, rand4k, rand4k);
+ testRw!CoupledMemoryBuffers(1024 * 256, 167, 163, rand4k, rand4k);
+ testRw!CoupledMemoryBuffers(1024 * 16, 1, 1, rand4k, rand4k);
+
+ testBlocking!CoupledMemoryBuffers();
+
+ // TSocket tests
+ enum socketMaxOutstanding = 4096;
+ testRw!CoupledSocketTransports(1024 * 1024, 0, 0,
+ 0, 0, socketMaxOutstanding);
+ testRw!CoupledSocketTransports(1024 * 256, rand4k, rand4k,
+ 0, 0, socketMaxOutstanding);
+ testRw!CoupledSocketTransports(1024 * 256, 167, 163,
+ 0, 0, socketMaxOutstanding);
+ // Doh. Apparently writing to a socket has some additional overhead for
+ // each send() call. If we have more than ~400 outstanding 1-byte write
+ // requests, additional send() calls start blocking.
+ testRw!CoupledSocketTransports(1024 * 16, 1, 1,
+ 0, 0, 250);
+ testRw!CoupledSocketTransports(1024 * 256, 0, 0,
+ rand4k, rand4k, socketMaxOutstanding);
+ testRw!CoupledSocketTransports(1024 * 256, rand4k, rand4k,
+ rand4k, rand4k, socketMaxOutstanding);
+ testRw!CoupledSocketTransports(1024 * 256, 167, 163,
+ rand4k, rand4k, socketMaxOutstanding);
+ testRw!CoupledSocketTransports(1024 * 16, 1, 1,
+ rand4k, rand4k, 250);
+
+ testBlocking!CoupledSocketTransports();
+
+ // File transport tests.
+
+ // Cannot write more than the frame size at once.
+ enum maxWriteAtOnce = 1024 * 1024 * 16 - 4;
+
+ testRw!CoupledFileTransports(1024 * 1024, maxWriteAtOnce, 0);
+ testRw!CoupledFileTransports(1024 * 256, rand4k, rand4k);
+ testRw!CoupledFileTransports(1024 * 256, 167, 163);
+ testRw!CoupledFileTransports(1024 * 16, 1, 1);
+
+ testRw!CoupledFileTransports(1024 * 256, 0, 0, rand4k, rand4k);
+ testRw!CoupledFileTransports(1024 * 256, rand4k, rand4k, rand4k, rand4k);
+ testRw!CoupledFileTransports(1024 * 256, 167, 163, rand4k, rand4k);
+ testRw!CoupledFileTransports(1024 * 16, 1, 1, rand4k, rand4k);
+
+ testBlocking!CoupledFileTransports();
+}
+
+
+/*
+ * Timer handling code for use in tests that check the transport blocking
+ * semantics.
+ *
+ * The implementation has been hacked together in a hurry and wastes a lot of
+ * threads, but speed should not be the concern here.
+ */
+
+struct Trigger {
+ this(Duration timeout, TTransport transport, size_t writeLength) {
+ mutex_ = new Mutex;
+ cancelCondition_ = new Condition(mutex_);
+ info_ = new Info(timeout, transport, writeLength);
+ startThread();
+ }
+
+ ~this() {
+ synchronized (mutex_) {
+ info_ = null;
+ cancelCondition_.notifyAll();
+ }
+ if (thread_) thread_.join();
+ }
+
+ @disable this(this) { assert(0); }
+
+ void add(Duration timeout, TTransport transport, size_t writeLength) {
+ synchronized (mutex_) {
+ auto info = new Info(timeout, transport, writeLength);
+ if (info_) {
+ auto prev = info_;
+ while (prev.next) prev = prev.next;
+ prev.next = info;
+ } else {
+ info_ = info;
+ startThread();
+ }
+ }
+ }
+
+ @property short fired() {
+ return atomicLoad(fired_);
+ }
+
+private:
+ void timerThread() {
+ // KLUDGE: Make sure the std.concurrency mbox is initialized on the timer
+ // thread to be able to unblock the file transport.
+ import std.concurrency;
+ thisTid;
+
+ synchronized (mutex_) {
+ while (info_) {
+ auto cancelled = cancelCondition_.wait(info_.timeout);
+ if (cancelled) {
+ info_ = null;
+ break;
+ }
+
+ atomicOp!"+="(fired_, 1);
+
+ // Write some data to the transport to unblock it.
+ auto buf = new ubyte[info_.writeLength];
+ buf[] = 'b';
+ info_.transport.write(buf);
+ info_.transport.flush();
+
+ info_ = info_.next;
+ }
+ }
+
+ thread_ = null;
+ }
+
+ void startThread() {
+ thread_ = new Thread(&timerThread);
+ thread_.start();
+ }
+
+ struct Info {
+ this(Duration timeout, TTransport transport, size_t writeLength) {
+ this.timeout = timeout;
+ this.transport = transport;
+ this.writeLength = writeLength;
+ }
+
+ Duration timeout;
+ TTransport transport;
+ size_t writeLength;
+ Info* next;
+ }
+
+ Info* info_;
+ Thread thread_;
+ shared short fired_;
+
+ import core.sync.mutex;
+ Mutex mutex_;
+ import core.sync.condition;
+ Condition cancelCondition_;
+}
diff --git a/src/jaegertracing/thrift/lib/dart/.analysis_options b/src/jaegertracing/thrift/lib/dart/.analysis_options
new file mode 100644
index 000000000..a10d4c5a0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/.analysis_options
@@ -0,0 +1,2 @@
+analyzer:
+ strong-mode: true
diff --git a/src/jaegertracing/thrift/lib/dart/LICENSE b/src/jaegertracing/thrift/lib/dart/LICENSE
new file mode 100644
index 000000000..4eacb6431
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/LICENSE
@@ -0,0 +1,16 @@
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
diff --git a/src/jaegertracing/thrift/lib/dart/Makefile.am b/src/jaegertracing/thrift/lib/dart/Makefile.am
new file mode 100644
index 000000000..373a883d6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/Makefile.am
@@ -0,0 +1,39 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+all-local:
+ $(DARTPUB) get
+
+clean-local:
+ $(RM) -r .pub
+ find . -type d -name ".dart_tool" | xargs $(RM) -r
+ find . -type f -name ".packages" | xargs $(RM)
+ find . -type d -name "packages" | xargs $(RM) -r
+
+check-local: all
+
+dist-hook:
+ $(RM) -r $(distdir)/.pub
+ find $(distdir) -type d -name ".dart_tool" | xargs $(RM) -r
+ find $(distdir) -type f -name ".packages" | xargs $(RM)
+ find $(distdir) -type d -name "packages" | xargs $(RM) -r
+
+EXTRA_DIST = \
+ .analysis_options
+
diff --git a/src/jaegertracing/thrift/lib/dart/README.md b/src/jaegertracing/thrift/lib/dart/README.md
new file mode 100644
index 000000000..4c3029124
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/README.md
@@ -0,0 +1,26 @@
+Thrift Dart Library
+
+License
+=======
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+Using Thrift with Dart
+====================
+
+Dart 1.24.3 or newer is required
diff --git a/src/jaegertracing/thrift/lib/dart/coding_standards.md b/src/jaegertracing/thrift/lib/dart/coding_standards.md
new file mode 100644
index 000000000..62f600365
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/coding_standards.md
@@ -0,0 +1,6 @@
+# Dart Coding Standards
+
+### Please follow:
+ * [Thrift General Coding Standards](/doc/coding_standards.md)
+ * [Use dartfmt](https://www.dartlang.org/tools/dartfmt/) and follow the
+ [Dart Style Guide](https://www.dartlang.org/articles/style-guide/)
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/browser/t_web_socket.dart b/src/jaegertracing/thrift/lib/dart/lib/src/browser/t_web_socket.dart
new file mode 100644
index 000000000..dac9ffdde
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/browser/t_web_socket.dart
@@ -0,0 +1,129 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+library thrift.src.browser;
+
+import 'dart:async';
+import 'package:dart2_constant/convert.dart' show base64;
+import 'dart:html' show CloseEvent;
+import 'dart:html' show Event;
+import 'dart:html' show MessageEvent;
+import 'dart:html' show WebSocket;
+import 'dart:typed_data' show Uint8List;
+
+import 'package:thrift/thrift.dart';
+
+/// A [TSocket] backed by a [WebSocket] from dart:html
+class TWebSocket implements TSocket {
+ final Uri url;
+
+ final StreamController<TSocketState> _onStateController;
+ Stream<TSocketState> get onState => _onStateController.stream;
+
+ final StreamController<Object> _onErrorController;
+ Stream<Object> get onError => _onErrorController.stream;
+
+ final StreamController<Uint8List> _onMessageController;
+ Stream<Uint8List> get onMessage => _onMessageController.stream;
+
+ final List<Uint8List> _requests = [];
+
+ TWebSocket(this.url)
+ : _onStateController = new StreamController.broadcast(),
+ _onErrorController = new StreamController.broadcast(),
+ _onMessageController = new StreamController.broadcast() {
+ if (url == null || !url.hasAuthority || !url.hasPort) {
+ throw new ArgumentError('Invalid url');
+ }
+ }
+
+ WebSocket _socket;
+
+ bool get isOpen => _socket != null && _socket.readyState == WebSocket.OPEN;
+
+ bool get isClosed =>
+ _socket == null || _socket.readyState == WebSocket.CLOSED;
+
+ Future open() {
+ if (!isClosed) {
+ throw new TTransportError(
+ TTransportErrorType.ALREADY_OPEN, 'Socket already connected');
+ }
+
+ _socket = new WebSocket(url.toString());
+ _socket.onError.listen(_onError);
+ _socket.onOpen.listen(_onOpen);
+ _socket.onClose.listen(_onClose);
+ _socket.onMessage.listen(_onMessage);
+
+ return _socket.onOpen.first;
+ }
+
+ Future close() {
+ if (_socket != null) {
+ _socket.close();
+ return _socket.onClose.first;
+ } else {
+ return new Future.value();
+ }
+ }
+
+ void send(Uint8List data) {
+ _requests.add(data);
+ _sendRequests();
+ }
+
+ void _sendRequests() {
+ while (isOpen && _requests.isNotEmpty) {
+ Uint8List data = _requests.removeAt(0);
+ _socket.sendString(base64.encode(data));
+ }
+ }
+
+ void _onOpen(Event event) {
+ _onStateController.add(TSocketState.OPEN);
+ _sendRequests();
+ }
+
+ void _onClose(CloseEvent event) {
+ _socket = null;
+
+ if (_requests.isNotEmpty) {
+ _onErrorController
+ .add(new StateError('Socket was closed with pending requests'));
+ }
+ _requests.clear();
+
+ _onStateController.add(TSocketState.CLOSED);
+ }
+
+ void _onMessage(MessageEvent message) {
+ try {
+ Uint8List data = new Uint8List.fromList(base64.decode(message.data));
+ _onMessageController.add(data);
+ } on FormatException catch (_) {
+ var error = new TProtocolError(TProtocolErrorType.INVALID_DATA,
+ "Expected a Base 64 encoded string.");
+ _onErrorController.add(error);
+ }
+ }
+
+ void _onError(Event event) {
+ close();
+ _onErrorController.add(event.toString());
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/console/t_tcp_socket.dart b/src/jaegertracing/thrift/lib/dart/lib/src/console/t_tcp_socket.dart
new file mode 100644
index 000000000..b71480334
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/console/t_tcp_socket.dart
@@ -0,0 +1,81 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+library thrift.src.console.t_tcp_socket;
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:typed_data' show Uint8List;
+
+import 'package:thrift/thrift.dart';
+
+/// A [TSocket] backed by a [Socket] from dart:io
+class TTcpSocket implements TSocket {
+ final StreamController<TSocketState> _onStateController;
+ Stream<TSocketState> get onState => _onStateController.stream;
+
+ final StreamController<Object> _onErrorController;
+ Stream<Object> get onError => _onErrorController.stream;
+
+ final StreamController<Uint8List> _onMessageController;
+ Stream<Uint8List> get onMessage => _onMessageController.stream;
+
+ TTcpSocket(Socket socket)
+ : _onStateController = new StreamController.broadcast(),
+ _onErrorController = new StreamController.broadcast(),
+ _onMessageController = new StreamController.broadcast() {
+ if (socket == null) {
+ throw new ArgumentError.notNull('socket');
+ }
+
+ _socket = socket;
+ _socket.listen(_onMessage, onError: _onError, onDone: close);
+ }
+
+ Socket _socket;
+
+ bool get isOpen => _socket != null;
+
+ bool get isClosed => _socket == null;
+
+ Future open() async {
+ _onStateController.add(TSocketState.OPEN);
+ }
+
+ Future close() async {
+ if (_socket != null) {
+ await _socket.close();
+ _socket = null;
+ }
+
+ _onStateController.add(TSocketState.CLOSED);
+ }
+
+ void send(Uint8List data) {
+ _socket.add(data);
+ }
+
+ void _onMessage(List<int> message) {
+ Uint8List data = new Uint8List.fromList(message);
+ _onMessageController.add(data);
+ }
+
+ void _onError(Object error) {
+ close();
+ _onErrorController.add('$error');
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/console/t_web_socket.dart b/src/jaegertracing/thrift/lib/dart/lib/src/console/t_web_socket.dart
new file mode 100644
index 000000000..c938a966f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/console/t_web_socket.dart
@@ -0,0 +1,88 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+library thrift.src.console.t_web_socket;
+
+import 'dart:async';
+import 'package:dart2_constant/convert.dart' show base64;
+import 'dart:io';
+import 'dart:typed_data' show Uint8List;
+
+import 'package:thrift/thrift.dart';
+
+/// A [TSocket] backed by a [WebSocket] from dart:io
+class TWebSocket implements TSocket {
+ final StreamController<TSocketState> _onStateController;
+ Stream<TSocketState> get onState => _onStateController.stream;
+
+ final StreamController<Object> _onErrorController;
+ Stream<Object> get onError => _onErrorController.stream;
+
+ final StreamController<Uint8List> _onMessageController;
+ Stream<Uint8List> get onMessage => _onMessageController.stream;
+
+ TWebSocket(WebSocket socket)
+ : _onStateController = new StreamController.broadcast(),
+ _onErrorController = new StreamController.broadcast(),
+ _onMessageController = new StreamController.broadcast() {
+ if (socket == null) {
+ throw new ArgumentError.notNull('socket');
+ }
+
+ _socket = socket;
+ _socket.listen(_onMessage, onError: _onError, onDone: close);
+ }
+
+ WebSocket _socket;
+
+ bool get isOpen => _socket != null;
+
+ bool get isClosed => _socket == null;
+
+ Future open() async {
+ _onStateController.add(TSocketState.OPEN);
+ }
+
+ Future close() async {
+ if (_socket != null) {
+ await _socket.close();
+ _socket = null;
+ }
+
+ _onStateController.add(TSocketState.CLOSED);
+ }
+
+ void send(Uint8List data) {
+ _socket.add(base64.encode(data));
+ }
+
+ void _onMessage(String message) {
+ try {
+ Uint8List data = new Uint8List.fromList(base64.decode(message));
+ _onMessageController.add(data);
+ } on FormatException catch (_) {
+ var error = new TProtocolError(TProtocolErrorType.INVALID_DATA,
+ "Expected a Base 64 encoded string.");
+ _onErrorController.add(error);
+ }
+ }
+
+ void _onError(Object error) {
+ close();
+ _onErrorController.add('$error');
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_binary_protocol.dart b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_binary_protocol.dart
new file mode 100644
index 000000000..a785d811c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_binary_protocol.dart
@@ -0,0 +1,281 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TBinaryProtocolFactory implements TProtocolFactory<TBinaryProtocol> {
+ TBinaryProtocolFactory({this.strictRead: false, this.strictWrite: true});
+
+ final bool strictRead;
+ final bool strictWrite;
+
+ TBinaryProtocol getProtocol(TTransport transport) {
+ return new TBinaryProtocol(transport,
+ strictRead: strictRead, strictWrite: strictWrite);
+ }
+}
+
+/// Binary protocol implementation for Thrift.
+///
+/// Adapted from the C# version.
+class TBinaryProtocol extends TProtocol {
+ static const int VERSION_MASK = 0xffff0000;
+ static const int VERSION_1 = 0x80010000;
+
+ static const Utf8Codec _utf8Codec = const Utf8Codec();
+
+ final bool strictRead;
+ final bool strictWrite;
+
+ TBinaryProtocol(TTransport transport,
+ {this.strictRead: false, this.strictWrite: true})
+ : super(transport);
+
+ /// write
+ void writeMessageBegin(TMessage message) {
+ if (strictWrite) {
+ int version = VERSION_1 | message.type;
+ writeI32(version);
+ writeString(message.name);
+ writeI32(message.seqid);
+ } else {
+ writeString(message.name);
+ writeByte(message.type);
+ writeI32(message.seqid);
+ }
+ }
+
+ void writeMessageEnd() {}
+
+ void writeStructBegin(TStruct struct) {}
+
+ void writeStructEnd() {}
+
+ void writeFieldBegin(TField field) {
+ writeByte(field.type);
+ writeI16(field.id);
+ }
+
+ void writeFieldEnd() {}
+
+ void writeFieldStop() {
+ writeByte(TType.STOP);
+ }
+
+ void writeMapBegin(TMap map) {
+ writeByte(map.keyType);
+ writeByte(map.valueType);
+ writeI32(map.length);
+ }
+
+ void writeMapEnd() {}
+
+ void writeListBegin(TList list) {
+ writeByte(list.elementType);
+ writeI32(list.length);
+ }
+
+ void writeListEnd() {}
+
+ void writeSetBegin(TSet set) {
+ writeByte(set.elementType);
+ writeI32(set.length);
+ }
+
+ void writeSetEnd() {}
+
+ void writeBool(bool b) {
+ if (b == null) b = false;
+ writeByte(b ? 1 : 0);
+ }
+
+ final ByteData _byteOut = new ByteData(1);
+ void writeByte(int byte) {
+ if (byte == null) byte = 0;
+ _byteOut.setUint8(0, byte);
+ transport.write(_byteOut.buffer.asUint8List(), 0, 1);
+ }
+
+ final ByteData _i16Out = new ByteData(2);
+ void writeI16(int i16) {
+ if (i16 == null) i16 = 0;
+ _i16Out.setInt16(0, i16);
+ transport.write(_i16Out.buffer.asUint8List(), 0, 2);
+ }
+
+ final ByteData _i32Out = new ByteData(4);
+ void writeI32(int i32) {
+ if (i32 == null) i32 = 0;
+ _i32Out.setInt32(0, i32);
+ transport.write(_i32Out.buffer.asUint8List(), 0, 4);
+ }
+
+ final Uint8List _i64Out = new Uint8List(8);
+ void writeI64(int i64) {
+ if (i64 == null) i64 = 0;
+ var i = new Int64(i64);
+ var bts = i.toBytes();
+ for (var j = 0; j < 8; j++) {
+ _i64Out[j] = bts[8 - j - 1];
+ }
+ transport.write(_i64Out, 0, 8);
+ }
+
+ void writeString(String s) {
+ var bytes = s != null ? _utf8Codec.encode(s) : new Uint8List.fromList([]);
+ writeI32(bytes.length);
+ transport.write(bytes, 0, bytes.length);
+ }
+
+ final ByteData _doubleOut = new ByteData(8);
+ void writeDouble(double d) {
+ if (d == null) d = 0.0;
+ _doubleOut.setFloat64(0, d);
+ transport.write(_doubleOut.buffer.asUint8List(), 0, 8);
+ }
+
+ void writeBinary(Uint8List bytes) {
+ var length = bytes.length;
+ writeI32(length);
+ transport.write(bytes, 0, length);
+ }
+
+ /// read
+ TMessage readMessageBegin() {
+ String name;
+ int type;
+ int seqid;
+
+ int size = readI32();
+ if (size < 0) {
+ int version = size & VERSION_MASK;
+ if (version != VERSION_1) {
+ throw new TProtocolError(TProtocolErrorType.BAD_VERSION,
+ "Bad version in readMessageBegin: $version");
+ }
+ type = size & 0x000000ff;
+ name = readString();
+ seqid = readI32();
+ } else {
+ if (strictRead) {
+ throw new TProtocolError(TProtocolErrorType.BAD_VERSION,
+ "Missing version in readMessageBegin");
+ }
+ name = _readString(size);
+ type = readByte();
+ seqid = readI32();
+ }
+ return new TMessage(name, type, seqid);
+ }
+
+ void readMessageEnd() {}
+
+ TStruct readStructBegin() {
+ return new TStruct();
+ }
+
+ void readStructEnd() {}
+
+ TField readFieldBegin() {
+ String name = "";
+ int type = readByte();
+ int id = type != TType.STOP ? readI16() : 0;
+
+ return new TField(name, type, id);
+ }
+
+ void readFieldEnd() {}
+
+ TMap readMapBegin() {
+ int keyType = readByte();
+ int valueType = readByte();
+ int length = readI32();
+
+ return new TMap(keyType, valueType, length);
+ }
+
+ void readMapEnd() {}
+
+ TList readListBegin() {
+ int elementType = readByte();
+ int length = readI32();
+
+ return new TList(elementType, length);
+ }
+
+ void readListEnd() {}
+
+ TSet readSetBegin() {
+ int elementType = readByte();
+ int length = readI32();
+
+ return new TSet(elementType, length);
+ }
+
+ void readSetEnd() {}
+
+ bool readBool() => readByte() == 1;
+
+ final Uint8List _byteIn = new Uint8List(1);
+ int readByte() {
+ transport.readAll(_byteIn, 0, 1);
+ return _byteIn.buffer.asByteData().getUint8(0);
+ }
+
+ final Uint8List _i16In = new Uint8List(2);
+ int readI16() {
+ transport.readAll(_i16In, 0, 2);
+ return _i16In.buffer.asByteData().getInt16(0);
+ }
+
+ final Uint8List _i32In = new Uint8List(4);
+ int readI32() {
+ transport.readAll(_i32In, 0, 4);
+ return _i32In.buffer.asByteData().getInt32(0);
+ }
+
+ final Uint8List _i64In = new Uint8List(8);
+ int readI64() {
+ transport.readAll(_i64In, 0, 8);
+ var i = new Int64.fromBytesBigEndian(_i64In);
+ return i.toInt();
+ }
+
+ final Uint8List _doubleIn = new Uint8List(8);
+ double readDouble() {
+ transport.readAll(_doubleIn, 0, 8);
+ return _doubleIn.buffer.asByteData().getFloat64(0);
+ }
+
+ String readString() {
+ int size = readI32();
+ return _readString(size);
+ }
+
+ String _readString(int size) {
+ Uint8List stringIn = new Uint8List(size);
+ transport.readAll(stringIn, 0, size);
+ return _utf8Codec.decode(stringIn);
+ }
+
+ Uint8List readBinary() {
+ int length = readI32();
+ Uint8List binaryIn = new Uint8List(length);
+ transport.readAll(binaryIn, 0, length);
+ return binaryIn;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_compact_protocol.dart b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_compact_protocol.dart
new file mode 100644
index 000000000..ee8094f8e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_compact_protocol.dart
@@ -0,0 +1,470 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// 'License'); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TCompactProtocolFactory implements TProtocolFactory<TCompactProtocol> {
+ TCompactProtocolFactory();
+
+ TCompactProtocol getProtocol(TTransport transport) {
+ return new TCompactProtocol(transport);
+ }
+}
+
+/// Compact protocol implementation for Thrift.
+///
+/// Use of fixnum library is required due to bugs like
+/// https://github.com/dart-lang/sdk/issues/15361
+///
+/// Adapted from the Java version.
+class TCompactProtocol extends TProtocol {
+ static const int PROTOCOL_ID = 0x82;
+ static const int VERSION = 1;
+ static const int VERSION_MASK = 0x1f;
+ static const int TYPE_MASK = 0xE0;
+ static const int TYPE_BITS = 0x07;
+ static const int TYPE_SHIFT_AMOUNT = 5;
+ static final TField TSTOP = new TField("", TType.STOP, 0);
+
+ static const int TYPE_BOOLEAN_TRUE = 0x01;
+ static const int TYPE_BOOLEAN_FALSE = 0x02;
+ static const int TYPE_BYTE = 0x03;
+ static const int TYPE_I16 = 0x04;
+ static const int TYPE_I32 = 0x05;
+ static const int TYPE_I64 = 0x06;
+ static const int TYPE_DOUBLE = 0x07;
+ static const int TYPE_BINARY = 0x08;
+ static const int TYPE_LIST = 0x09;
+ static const int TYPE_SET = 0x0A;
+ static const int TYPE_MAP = 0x0B;
+ static const int TYPE_STRUCT = 0x0C;
+
+ static final List<int> _typeMap = new List.unmodifiable(new List(16)
+ ..[TType.STOP] = TType.STOP
+ ..[TType.BOOL] = TYPE_BOOLEAN_TRUE
+ ..[TType.BYTE] = TYPE_BYTE
+ ..[TType.I16] = TYPE_I16
+ ..[TType.I32] = TYPE_I32
+ ..[TType.I64] = TYPE_I64
+ ..[TType.DOUBLE] = TYPE_DOUBLE
+ ..[TType.STRING] = TYPE_BINARY
+ ..[TType.LIST] = TYPE_LIST
+ ..[TType.SET] = TYPE_SET
+ ..[TType.MAP] = TYPE_MAP
+ ..[TType.STRUCT] = TYPE_STRUCT);
+
+ static const Utf8Codec _utf8Codec = const Utf8Codec();
+
+ // Pretend this is a stack
+ DoubleLinkedQueue<int> _lastField = new DoubleLinkedQueue<int>();
+ int _lastFieldId = 0;
+
+ TField _booleanField = null;
+ bool _boolValue = null;
+
+ final Uint8List tempList = new Uint8List(10);
+ final ByteData tempBD = new ByteData(10);
+
+ TCompactProtocol(TTransport transport) : super(transport);
+
+ /// Write
+ void writeMessageBegin(TMessage message) {
+ writeByte(PROTOCOL_ID);
+ writeByte((VERSION & VERSION_MASK) |
+ ((message.type << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
+ _writeVarInt32(new Int32(message.seqid));
+ writeString(message.name);
+ }
+
+ void writeMessageEnd() {}
+
+ void writeStructBegin(TStruct struct) {
+ _lastField.addLast(_lastFieldId);
+ _lastFieldId = 0;
+ }
+
+ void writeStructEnd() {
+ _lastFieldId = _lastField.removeLast();
+ }
+
+ void writeFieldBegin(TField field) {
+ if (field.type == TType.BOOL) {
+ _booleanField = field;
+ } else {
+ _writeFieldBegin(field, -1);
+ }
+ }
+
+ void _writeFieldBegin(TField field, int typeOverride) {
+ int typeToWrite =
+ typeOverride == -1 ? _getCompactType(field.type) : typeOverride;
+
+ if (field.id > _lastFieldId && field.id - _lastFieldId <= 15) {
+ writeByte((field.id - _lastFieldId) << 4 | typeToWrite);
+ } else {
+ writeByte(typeToWrite);
+ writeI16(field.id);
+ }
+
+ _lastFieldId = field.id;
+ }
+
+ void writeFieldEnd() {}
+
+ void writeFieldStop() {
+ writeByte(TType.STOP);
+ }
+
+ void writeMapBegin(TMap map) {
+ if (map.length == 0) {
+ writeByte(0);
+ } else {
+ _writeVarInt32(new Int32(map.length));
+ writeByte(
+ _getCompactType(map.keyType) << 4 | _getCompactType(map.valueType));
+ }
+ }
+
+ void writeMapEnd() {}
+
+ void writeListBegin(TList list) {
+ _writeCollectionBegin(list.elementType, list.length);
+ }
+
+ void writeListEnd() {}
+
+ void writeSetBegin(TSet set) {
+ _writeCollectionBegin(set.elementType, set.length);
+ }
+
+ void writeSetEnd() {}
+
+ void writeBool(bool b) {
+ if (b == null) b = false;
+ if (_booleanField != null) {
+ _writeFieldBegin(
+ _booleanField, b ? TYPE_BOOLEAN_TRUE : TYPE_BOOLEAN_FALSE);
+ _booleanField = null;
+ } else {
+ writeByte(b ? TYPE_BOOLEAN_TRUE : TYPE_BOOLEAN_FALSE);
+ }
+ }
+
+ void writeByte(int b) {
+ if (b == null) b = 0;
+ tempList[0] = b;
+ transport.write(tempList, 0, 1);
+ }
+
+ void writeI16(int i16) {
+ if (i16 == null) i16 = 0;
+ _writeVarInt32(_int32ToZigZag(new Int32(i16)));
+ }
+
+ void writeI32(int i32) {
+ if (i32 == null) i32 = 0;
+ _writeVarInt32(_int32ToZigZag(new Int32(i32)));
+ }
+
+ void writeI64(int i64) {
+ if (i64 == null) i64 = 0;
+ _writeVarInt64(_int64ToZigZag(new Int64(i64)));
+ }
+
+ void writeDouble(double d) {
+ if (d == null) d = 0.0;
+ tempBD.setFloat64(0, d, Endianness.little);
+ transport.write(tempBD.buffer.asUint8List(), 0, 8);
+ }
+
+ void writeString(String str) {
+ Uint8List bytes =
+ str != null ? _utf8Codec.encode(str) : new Uint8List.fromList([]);
+ writeBinary(bytes);
+ }
+
+ void writeBinary(Uint8List bytes) {
+ _writeVarInt32(new Int32(bytes.length));
+ transport.write(bytes, 0, bytes.length);
+ }
+
+ void _writeVarInt32(Int32 n) {
+ int idx = 0;
+ while (true) {
+ if ((n & ~0x7F) == 0) {
+ tempList[idx++] = (n & 0xFF).toInt();
+ break;
+ } else {
+ tempList[idx++] = (((n & 0x7F) | 0x80) & 0xFF).toInt();
+ n = n.shiftRightUnsigned(7);
+ }
+ }
+ transport.write(tempList, 0, idx);
+ }
+
+ void _writeVarInt64(Int64 n) {
+ int idx = 0;
+ while (true) {
+ if ((n & ~0x7F) == 0) {
+ tempList[idx++] = (n & 0xFF).toInt();
+ break;
+ } else {
+ tempList[idx++] = (((n & 0x7F) | 0x80) & 0xFF).toInt();
+ n = n.shiftRightUnsigned(7);
+ }
+ }
+ transport.write(tempList, 0, idx);
+ }
+
+ void _writeCollectionBegin(int elemType, int length) {
+ if (length <= 14) {
+ writeByte(length << 4 | _getCompactType(elemType));
+ } else {
+ writeByte(0xF0 | _getCompactType(elemType));
+ _writeVarInt32(new Int32(length));
+ }
+ }
+
+ Int32 _int32ToZigZag(Int32 n) {
+ return (n << 1) ^ (n >> 31);
+ }
+
+ Int64 _int64ToZigZag(Int64 n) {
+ return (n << 1) ^ (n >> 63);
+ }
+
+ /// Read
+ TMessage readMessageBegin() {
+ int protocolId = readByte();
+ if (protocolId != PROTOCOL_ID) {
+ throw new TProtocolError(TProtocolErrorType.BAD_VERSION,
+ 'Expected protocol id $PROTOCOL_ID but got $protocolId');
+ }
+ int versionAndType = readByte();
+ int version = versionAndType & VERSION_MASK;
+ if (version != VERSION) {
+ throw new TProtocolError(TProtocolErrorType.BAD_VERSION,
+ 'Expected version $VERSION but got $version');
+ }
+ int type = (versionAndType >> TYPE_SHIFT_AMOUNT) & TYPE_BITS;
+ int seqId = _readVarInt32().toInt();
+ String messageName = readString();
+ return new TMessage(messageName, type, seqId);
+ }
+
+ void readMessageEnd() {}
+
+ TStruct readStructBegin() {
+ _lastField.addLast(_lastFieldId);
+ _lastFieldId = 0;
+ // TODO make this a constant?
+ return new TStruct();
+ }
+
+ void readStructEnd() {
+ _lastFieldId = _lastField.removeLast();
+ }
+
+ TField readFieldBegin() {
+ int type = readByte();
+ if (type == TType.STOP) {
+ return TSTOP;
+ }
+
+ int fieldId;
+ int modifier = (type & 0xF0) >> 4;
+ if (modifier == 0) {
+ fieldId = readI16();
+ } else {
+ fieldId = _lastFieldId + modifier;
+ }
+
+ TField field = new TField('', _getTType(type & 0x0F), fieldId);
+ if (_isBoolType(type)) {
+ _boolValue = (type & 0x0F) == TYPE_BOOLEAN_TRUE;
+ }
+
+ _lastFieldId = field.id;
+ return field;
+ }
+
+ void readFieldEnd() {}
+
+ TMap readMapBegin() {
+ int length = _readVarInt32().toInt();
+ _checkNegReadLength(length);
+
+ int keyAndValueType = length == 0 ? 0 : readByte();
+ int keyType = _getTType(keyAndValueType >> 4);
+ int valueType = _getTType(keyAndValueType & 0x0F);
+ return new TMap(keyType, valueType, length);
+ }
+
+ void readMapEnd() {}
+
+ TList readListBegin() {
+ int lengthAndType = readByte();
+ int length = (lengthAndType >> 4) & 0x0F;
+ if (length == 15) {
+ length = _readVarInt32().toInt();
+ }
+ _checkNegReadLength(length);
+ int type = _getTType(lengthAndType);
+ return new TList(type, length);
+ }
+
+ void readListEnd() {}
+
+ TSet readSetBegin() {
+ TList tlist = readListBegin();
+ return new TSet(tlist.elementType, tlist.length);
+ }
+
+ void readSetEnd() {}
+
+ bool readBool() {
+ if (_boolValue != null) {
+ bool result = _boolValue;
+ _boolValue = null;
+ return result;
+ }
+ return readByte() == TYPE_BOOLEAN_TRUE;
+ }
+
+ int readByte() {
+ transport.readAll(tempList, 0, 1);
+ return tempList.buffer.asByteData().getUint8(0);
+ }
+
+ int readI16() {
+ return _zigzagToInt32(_readVarInt32()).toInt();
+ }
+
+ int readI32() {
+ return _zigzagToInt32(_readVarInt32()).toInt();
+ }
+
+ int readI64() {
+ return _zigzagToInt64(_readVarInt64()).toInt();
+ }
+
+ double readDouble() {
+ transport.readAll(tempList, 0, 8);
+ return tempList.buffer.asByteData().getFloat64(0, Endianness.little);
+ }
+
+ String readString() {
+ int length = _readVarInt32().toInt();
+ _checkNegReadLength(length);
+
+ // TODO look at using temp for small strings?
+ Uint8List buff = new Uint8List(length);
+ transport.readAll(buff, 0, length);
+ return _utf8Codec.decode(buff);
+ }
+
+ Uint8List readBinary() {
+ int length = _readVarInt32().toInt();
+ _checkNegReadLength(length);
+
+ Uint8List buff = new Uint8List(length);
+ transport.readAll(buff, 0, length);
+ return buff;
+ }
+
+ Int32 _readVarInt32() {
+ Int32 result = Int32.ZERO;
+ int shift = 0;
+ while (true) {
+ Int32 b = new Int32(readByte());
+ result |= (b & 0x7f) << shift;
+ if ((b & 0x80) != 0x80) break;
+ shift += 7;
+ }
+ return result;
+ }
+
+ Int64 _readVarInt64() {
+ Int64 result = Int64.ZERO;
+ int shift = 0;
+ while (true) {
+ Int64 b = new Int64(readByte());
+ result |= (b & 0x7f) << shift;
+ if ((b & 0x80) != 0x80) break;
+ shift += 7;
+ }
+ return result;
+ }
+
+ Int32 _zigzagToInt32(Int32 n) {
+ return (n.shiftRightUnsigned(1)) ^ -(n & 1);
+ }
+
+ Int64 _zigzagToInt64(Int64 n) {
+ return (n.shiftRightUnsigned(1)) ^ -(n & 1);
+ }
+
+ void _checkNegReadLength(int length) {
+ if (length < 0) {
+ throw new TProtocolError(
+ TProtocolErrorType.NEGATIVE_SIZE, 'Negative length: $length');
+ }
+ }
+
+ int _getCompactType(int ttype) {
+ return _typeMap[ttype];
+ }
+
+ int _getTType(int type) {
+ switch (type & 0x0F) {
+ case TType.STOP:
+ return TType.STOP;
+ case TYPE_BOOLEAN_FALSE:
+ case TYPE_BOOLEAN_TRUE:
+ return TType.BOOL;
+ case TYPE_BYTE:
+ return TType.BYTE;
+ case TYPE_I16:
+ return TType.I16;
+ case TYPE_I32:
+ return TType.I32;
+ case TYPE_I64:
+ return TType.I64;
+ case TYPE_DOUBLE:
+ return TType.DOUBLE;
+ case TYPE_BINARY:
+ return TType.STRING;
+ case TYPE_LIST:
+ return TType.LIST;
+ case TYPE_SET:
+ return TType.SET;
+ case TYPE_MAP:
+ return TType.MAP;
+ case TYPE_STRUCT:
+ return TType.STRUCT;
+ default:
+ throw new TProtocolError(
+ TProtocolErrorType.INVALID_DATA, "Unknown type: ${type & 0x0F}");
+ }
+ }
+
+ bool _isBoolType(int b) {
+ int lowerNibble = b & 0x0F;
+ return lowerNibble == TYPE_BOOLEAN_TRUE ||
+ lowerNibble == TYPE_BOOLEAN_FALSE;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_field.dart b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_field.dart
new file mode 100644
index 000000000..444b4e57a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_field.dart
@@ -0,0 +1,26 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TField {
+ final String name;
+ final int type;
+ final int id;
+
+ TField(this.name, this.type, this.id);
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_json_protocol.dart b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_json_protocol.dart
new file mode 100644
index 000000000..180568ddf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_json_protocol.dart
@@ -0,0 +1,784 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TJsonProtocolFactory implements TProtocolFactory<TJsonProtocol> {
+ TJsonProtocol getProtocol(TTransport transport) {
+ return new TJsonProtocol(transport);
+ }
+}
+
+/// JSON protocol implementation for Thrift.
+///
+/// Adapted from the C# version.
+class TJsonProtocol extends TProtocol {
+ static const int VERSION_1 = 1;
+
+ static const Utf8Codec utf8Codec = const Utf8Codec();
+
+ _BaseContext _context;
+ _BaseContext _rootContext;
+ _LookaheadReader _reader;
+
+ final List<_BaseContext> _contextStack = [];
+ final Uint8List _tempBuffer = new Uint8List(4);
+
+ TJsonProtocol(TTransport transport) : super(transport) {
+ _rootContext = new _BaseContext(this);
+ _reader = new _LookaheadReader(this);
+ _resetContext();
+ }
+
+ void _pushContext(_BaseContext c) {
+ _contextStack.add(c);
+ _context = c;
+ }
+
+ void _popContext() {
+ _contextStack.removeLast();
+ _context = _contextStack.isEmpty ? _rootContext : _contextStack.last;
+ }
+
+ void _resetContext() {
+ _contextStack.clear();
+ _context = _rootContext;
+ }
+
+ /// Read a byte that must match [char]; otherwise throw a [TProtocolError].
+ void _readJsonSyntaxChar(int charByte) {
+ int byte = _reader.read();
+ if (byte != charByte) {
+ throw new TProtocolError(TProtocolErrorType.INVALID_DATA,
+ "Expected character ${new String.fromCharCode(charByte)} but found: ${new String.fromCharCode(byte)}");
+ }
+ }
+
+ int _hexVal(int byte) {
+ if (byte >= _Constants.HEX_0_BYTES[0] &&
+ byte <= _Constants.HEX_9_BYTES[0]) {
+ return byte - _Constants.HEX_0_BYTES[0];
+ } else if (byte >= _Constants.HEX_A_BYTES[0] &&
+ byte <= _Constants.HEX_F_BYTES[0]) {
+ byte += 10;
+ return byte - _Constants.HEX_A_BYTES[0];
+ } else {
+ throw new TProtocolError(
+ TProtocolErrorType.INVALID_DATA, "Expected hex character");
+ }
+ }
+
+ int _hexChar(int byte) => byte.toRadixString(16).codeUnitAt(0);
+
+ /// write
+
+ /// Write the [bytes] as JSON characters, escaping as needed.
+ void _writeJsonString(Uint8List bytes) {
+ _context.write();
+ transport.writeAll(_Constants.QUOTE_BYTES);
+
+ int length = bytes.length;
+ for (int i = 0; i < length; i++) {
+ int byte = bytes[i];
+ if ((byte & 0x00FF) >= 0x30) {
+ if (byte == _Constants.BACKSLASH_BYTES[0]) {
+ transport.writeAll(_Constants.BACKSLASH_BYTES);
+ transport.writeAll(_Constants.BACKSLASH_BYTES);
+ } else {
+ transport.write(bytes, i, 1);
+ }
+ } else {
+ _tempBuffer[0] = _Constants.JSON_CHAR_TABLE[byte];
+ if (_tempBuffer[0] == 1) {
+ transport.write(bytes, i, 1);
+ } else if (_tempBuffer[0] > 1) {
+ transport.writeAll(_Constants.BACKSLASH_BYTES);
+ transport.write(_tempBuffer, 0, 1);
+ } else {
+ transport.writeAll(_Constants.ESCSEQ_BYTES);
+ _tempBuffer[0] = _hexChar(byte >> 4);
+ _tempBuffer[1] = _hexChar(byte);
+ transport.write(_tempBuffer, 0, 2);
+ }
+ }
+ }
+
+ transport.writeAll(_Constants.QUOTE_BYTES);
+ }
+
+ void _writeJsonInteger(int i) {
+ if (i == null) i = 0;
+
+ _context.write();
+ String str = i.toString();
+
+ if (_context.escapeNumbers) {
+ transport.writeAll(_Constants.QUOTE_BYTES);
+ }
+ transport.writeAll(utf8Codec.encode(str));
+ if (_context.escapeNumbers) {
+ transport.writeAll(_Constants.QUOTE_BYTES);
+ }
+ }
+
+ void _writeJsonDouble(double d) {
+ if (d == null) d = 0.0;
+
+ _context.write();
+ String str = d.toString();
+ bool escapeNumbers = d.isNaN || d.isInfinite || _context.escapeNumbers;
+
+ if (escapeNumbers) {
+ transport.writeAll(_Constants.QUOTE_BYTES);
+ }
+ transport.writeAll(utf8Codec.encode(str));
+ if (escapeNumbers) {
+ transport.writeAll(_Constants.QUOTE_BYTES);
+ }
+ }
+
+ void _writeJsonBase64(Uint8List bytes) {
+ _context.write();
+ transport.writeAll(_Constants.QUOTE_BYTES);
+
+ String base64text = base64.encode(bytes);
+ transport.writeAll(utf8Codec.encode(base64text));
+
+ transport.writeAll(_Constants.QUOTE_BYTES);
+ }
+
+ void _writeJsonObjectStart() {
+ _context.write();
+ transport.writeAll(_Constants.LBRACE_BYTES);
+ _pushContext(new _PairContext(this));
+ }
+
+ void _writeJsonObjectEnd() {
+ _popContext();
+ transport.writeAll(_Constants.RBRACE_BYTES);
+ }
+
+ void _writeJsonArrayStart() {
+ _context.write();
+ transport.writeAll(_Constants.LBRACKET_BYTES);
+ _pushContext(new _ListContext(this));
+ }
+
+ void _writeJsonArrayEnd() {
+ _popContext();
+ transport.writeAll(_Constants.RBRACKET_BYTES);
+ }
+
+ void writeMessageBegin(TMessage message) {
+ _resetContext();
+
+ _writeJsonArrayStart();
+ _writeJsonInteger(VERSION_1);
+
+ _writeJsonString(utf8Codec.encode(message.name));
+ _writeJsonInteger(message.type);
+ _writeJsonInteger(message.seqid);
+ }
+
+ void writeMessageEnd() {
+ _writeJsonArrayEnd();
+ }
+
+ void writeStructBegin(TStruct struct) {
+ _writeJsonObjectStart();
+ }
+
+ void writeStructEnd() {
+ _writeJsonObjectEnd();
+ }
+
+ void writeFieldBegin(TField field) {
+ _writeJsonInteger(field.id);
+ _writeJsonObjectStart();
+ _writeJsonString(_Constants.getTypeNameBytesForTypeId(field.type));
+ }
+
+ void writeFieldEnd() {
+ _writeJsonObjectEnd();
+ }
+
+ void writeFieldStop() {}
+
+ void writeMapBegin(TMap map) {
+ _writeJsonArrayStart();
+ _writeJsonString(_Constants.getTypeNameBytesForTypeId(map.keyType));
+ _writeJsonString(_Constants.getTypeNameBytesForTypeId(map.valueType));
+ _writeJsonInteger(map.length);
+ _writeJsonObjectStart();
+ }
+
+ void writeMapEnd() {
+ _writeJsonObjectEnd();
+ _writeJsonArrayEnd();
+ }
+
+ void writeListBegin(TList list) {
+ _writeJsonArrayStart();
+ _writeJsonString(_Constants.getTypeNameBytesForTypeId(list.elementType));
+ _writeJsonInteger(list.length);
+ }
+
+ void writeListEnd() {
+ _writeJsonArrayEnd();
+ }
+
+ void writeSetBegin(TSet set) {
+ _writeJsonArrayStart();
+ _writeJsonString(_Constants.getTypeNameBytesForTypeId(set.elementType));
+ _writeJsonInteger(set.length);
+ }
+
+ void writeSetEnd() {
+ _writeJsonArrayEnd();
+ }
+
+ void writeBool(bool b) {
+ if (b == null) b = false;
+ _writeJsonInteger(b ? 1 : 0);
+ }
+
+ void writeByte(int b) {
+ _writeJsonInteger(b);
+ }
+
+ void writeI16(int i16) {
+ _writeJsonInteger(i16);
+ }
+
+ void writeI32(int i32) {
+ _writeJsonInteger(i32);
+ }
+
+ void writeI64(int i64) {
+ _writeJsonInteger(i64);
+ }
+
+ void writeDouble(double d) {
+ _writeJsonDouble(d);
+ }
+
+ void writeString(String s) {
+ var bytes = s != null ? utf8Codec.encode(s) : new Uint8List.fromList([]);
+ _writeJsonString(bytes);
+ }
+
+ void writeBinary(Uint8List bytes) {
+ _writeJsonBase64(bytes);
+ }
+
+ bool _isHighSurrogate(int b) => b >= 0xD800 && b <= 0xDBFF;
+
+ bool _isLowSurrogate(int b) => b >= 0xDC00 && b <= 0xDFFF;
+
+ /// read
+
+ Uint8List _readJsonString({bool skipContext: false}) {
+ List<int> bytes = [];
+ List<int> codeunits = [];
+
+ if (!skipContext) {
+ _context.read();
+ }
+
+ _readJsonSyntaxChar(_Constants.QUOTE_BYTES[0]);
+ while (true) {
+ int byte = _reader.read();
+ if (byte == _Constants.QUOTE_BYTES[0]) {
+ break;
+ }
+
+ // escaped?
+ if (byte != _Constants.ESCSEQ_BYTES[0]) {
+ bytes.add(byte);
+ continue;
+ }
+
+ byte = _reader.read();
+
+ // distinguish between \uXXXX and control chars like \n
+ if (byte != _Constants.ESCSEQ_BYTES[1]) {
+ String char = new String.fromCharCode(byte);
+ int offset = _Constants.ESCAPE_CHARS.indexOf(char);
+ if (offset == -1) {
+ throw new TProtocolError(
+ TProtocolErrorType.INVALID_DATA, "Expected control char");
+ }
+ byte = _Constants.ESCAPE_CHAR_VALS.codeUnitAt(offset);
+ bytes.add(byte);
+ continue;
+ }
+
+ // it's \uXXXX
+ transport.readAll(_tempBuffer, 0, 4);
+ byte = (_hexVal(_tempBuffer[0]) << 12)
+ + (_hexVal(_tempBuffer[1]) << 8)
+ + (_hexVal(_tempBuffer[2]) << 4)
+ + _hexVal(_tempBuffer[3]);
+ if (_isHighSurrogate(byte)) {
+ if (codeunits.isNotEmpty) {
+ throw new TProtocolError(
+ TProtocolErrorType.INVALID_DATA, "Expected low surrogate");
+ }
+ codeunits.add(byte);
+ }
+ else if (_isLowSurrogate(byte)) {
+ if (codeunits.isEmpty) {
+ throw new TProtocolError(
+ TProtocolErrorType.INVALID_DATA, "Expected high surrogate");
+ }
+ codeunits.add(byte);
+ bytes.addAll(utf8Codec.encode(new String.fromCharCodes(codeunits)));
+ codeunits.clear();
+ }
+ else {
+ bytes.addAll(utf8Codec.encode(new String.fromCharCode(byte)));
+ }
+ }
+
+ if (codeunits.isNotEmpty) {
+ throw new TProtocolError(
+ TProtocolErrorType.INVALID_DATA, "Expected low surrogate");
+ }
+
+ return new Uint8List.fromList(bytes);
+ }
+
+ String _readJsonNumericChars() {
+ StringBuffer buffer = new StringBuffer();
+ while (true) {
+ if (!_Constants.isJsonNumeric(_reader.peek())) {
+ break;
+ }
+ buffer.write(new String.fromCharCode(_reader.read()));
+ }
+ return buffer.toString();
+ }
+
+ int _readJsonInteger() {
+ _context.read();
+
+ if (_context.escapeNumbers) {
+ _readJsonSyntaxChar(_Constants.QUOTE_BYTES[0]);
+ }
+ String str = _readJsonNumericChars();
+ if (_context.escapeNumbers) {
+ _readJsonSyntaxChar(_Constants.QUOTE_BYTES[0]);
+ }
+
+ try {
+ return int.parse(str);
+ } on FormatException catch (_) {
+ throw new TProtocolError(TProtocolErrorType.INVALID_DATA,
+ "Bad data encounted in numeric data");
+ }
+ }
+
+ double _readJsonDouble() {
+ _context.read();
+
+ if (_reader.peek() == _Constants.QUOTE_BYTES[0]) {
+ Uint8List bytes = _readJsonString(skipContext: true);
+ double d = double.parse(utf8Codec.decode(bytes), (_) {
+ throw new TProtocolError(TProtocolErrorType.INVALID_DATA,
+ "Bad data encounted in numeric data");
+ });
+ if (!_context.escapeNumbers && !d.isNaN && !d.isInfinite) {
+ throw new TProtocolError(TProtocolErrorType.INVALID_DATA,
+ "Numeric data unexpectedly quoted");
+ }
+ return d;
+ } else {
+ if (_context.escapeNumbers) {
+ // This will throw - we should have had a quote if escapeNumbers == true
+ _readJsonSyntaxChar(_Constants.QUOTE_BYTES[0]);
+ }
+ return double.parse(_readJsonNumericChars(), (_) {
+ throw new TProtocolError(TProtocolErrorType.INVALID_DATA,
+ "Bad data encounted in numeric data");
+ });
+ }
+ }
+
+ Uint8List _readJsonBase64() {
+ // convert UTF-8 bytes of a Base 64 encoded string to binary bytes
+ Uint8List base64Bytes = _readJsonString();
+ String base64text = utf8Codec.decode(base64Bytes);
+
+ return new Uint8List.fromList(base64.decode(base64text));
+ }
+
+ void _readJsonObjectStart() {
+ _context.read();
+ _readJsonSyntaxChar(_Constants.LBRACE_BYTES[0]);
+ _pushContext(new _PairContext(this));
+ }
+
+ void _readJsonObjectEnd() {
+ _readJsonSyntaxChar(_Constants.RBRACE_BYTES[0]);
+ _popContext();
+ }
+
+ void _readJsonArrayStart() {
+ _context.read();
+ _readJsonSyntaxChar(_Constants.LBRACKET_BYTES[0]);
+ _pushContext(new _ListContext(this));
+ }
+
+ void _readJsonArrayEnd() {
+ _readJsonSyntaxChar(_Constants.RBRACKET_BYTES[0]);
+ _popContext();
+ }
+
+ TMessage readMessageBegin() {
+ _resetContext();
+
+ _readJsonArrayStart();
+ if (_readJsonInteger() != VERSION_1) {
+ throw new TProtocolError(
+ TProtocolErrorType.BAD_VERSION, "Message contained bad version.");
+ }
+
+ Uint8List buffer = _readJsonString();
+ String name = utf8Codec.decode(buffer);
+ int type = _readJsonInteger();
+ int seqid = _readJsonInteger();
+
+ return new TMessage(name, type, seqid);
+ }
+
+ void readMessageEnd() {
+ _readJsonArrayEnd();
+ }
+
+ TStruct readStructBegin() {
+ _readJsonObjectStart();
+ return new TStruct();
+ }
+
+ void readStructEnd() {
+ _readJsonObjectEnd();
+ }
+
+ TField readFieldBegin() {
+ String name = "";
+ int type = TType.STOP;
+ int id = 0;
+
+ if (_reader.peek() != _Constants.RBRACE_BYTES[0]) {
+ id = _readJsonInteger();
+ _readJsonObjectStart();
+ type = _Constants.getTypeIdForTypeName(_readJsonString());
+ }
+
+ return new TField(name, type, id);
+ }
+
+ void readFieldEnd() {
+ _readJsonObjectEnd();
+ }
+
+ TMap readMapBegin() {
+ _readJsonArrayStart();
+ int keyType = _Constants.getTypeIdForTypeName(_readJsonString());
+ int valueType = _Constants.getTypeIdForTypeName(_readJsonString());
+ int length = _readJsonInteger();
+ _readJsonObjectStart();
+
+ return new TMap(keyType, valueType, length);
+ }
+
+ void readMapEnd() {
+ _readJsonObjectEnd();
+ _readJsonArrayEnd();
+ }
+
+ TList readListBegin() {
+ _readJsonArrayStart();
+ int elementType = _Constants.getTypeIdForTypeName(_readJsonString());
+ int length = _readJsonInteger();
+
+ return new TList(elementType, length);
+ }
+
+ void readListEnd() {
+ _readJsonArrayEnd();
+ }
+
+ TSet readSetBegin() {
+ _readJsonArrayStart();
+ int elementType = _Constants.getTypeIdForTypeName(_readJsonString());
+ int length = _readJsonInteger();
+
+ return new TSet(elementType, length);
+ }
+
+ void readSetEnd() {
+ _readJsonArrayEnd();
+ }
+
+ bool readBool() {
+ return _readJsonInteger() == 0 ? false : true;
+ }
+
+ int readByte() {
+ return _readJsonInteger();
+ }
+
+ int readI16() {
+ return _readJsonInteger();
+ }
+
+ int readI32() {
+ return _readJsonInteger();
+ }
+
+ int readI64() {
+ return _readJsonInteger();
+ }
+
+ double readDouble() {
+ return _readJsonDouble();
+ }
+
+ String readString() {
+ return utf8Codec.decode(_readJsonString());
+ }
+
+ Uint8List readBinary() {
+ return new Uint8List.fromList(_readJsonBase64());
+ }
+}
+
+class _Constants {
+ static const utf8codec = const Utf8Codec();
+
+ static final Uint8List HEX_0_BYTES = new Uint8List.fromList('0'.codeUnits);
+ static final Uint8List HEX_9_BYTES = new Uint8List.fromList('9'.codeUnits);
+ static final Uint8List HEX_A_BYTES = new Uint8List.fromList('a'.codeUnits);
+ static final Uint8List HEX_F_BYTES = new Uint8List.fromList('f'.codeUnits);
+ static final Uint8List COMMA_BYTES = new Uint8List.fromList(','.codeUnits);
+ static final Uint8List COLON_BYTES = new Uint8List.fromList(':'.codeUnits);
+ static final Uint8List LBRACE_BYTES = new Uint8List.fromList('{'.codeUnits);
+ static final Uint8List RBRACE_BYTES = new Uint8List.fromList('}'.codeUnits);
+ static final Uint8List LBRACKET_BYTES = new Uint8List.fromList('['.codeUnits);
+ static final Uint8List RBRACKET_BYTES = new Uint8List.fromList(']'.codeUnits);
+ static final Uint8List QUOTE_BYTES = new Uint8List.fromList('"'.codeUnits);
+ static final Uint8List BACKSLASH_BYTES =
+ new Uint8List.fromList(r'\'.codeUnits);
+
+ static final ESCSEQ_BYTES = new Uint8List.fromList(r'\u00'.codeUnits);
+
+ static final Uint8List JSON_CHAR_TABLE = new Uint8List.fromList([
+ 0, 0, 0, 0, 0, 0, 0, 0, // 8 bytes
+ 'b'.codeUnitAt(0), 't'.codeUnitAt(0), 'n'.codeUnitAt(0), 0, // 4 bytes
+ 'f'.codeUnitAt(0), 'r'.codeUnitAt(0), 0, 0, // 4 bytes
+ 0, 0, 0, 0, 0, 0, 0, 0, // 8 bytes
+ 0, 0, 0, 0, 0, 0, 0, 0, // 8 bytes
+ 1, 1, '"'.codeUnitAt(0), 1, 1, 1, 1, 1, // 8 bytes
+ 1, 1, 1, 1, 1, 1, 1, 1 // 8 bytes
+ ]);
+
+ static const String ESCAPE_CHARS = r'"\/bfnrt';
+ static const String ESCAPE_CHAR_VALS = '"\\/\b\f\n\r\t';
+
+ static const String NAME_BOOL = 'tf';
+ static const String NAME_BYTE = 'i8';
+ static const String NAME_I16 = 'i16';
+ static const String NAME_I32 = 'i32';
+ static const String NAME_I64 = 'i64';
+ static const String NAME_DOUBLE = 'dbl';
+ static const String NAME_STRUCT = 'rec';
+ static const String NAME_STRING = 'str';
+ static const String NAME_MAP = 'map';
+ static const String NAME_LIST = 'lst';
+ static const String NAME_SET = 'set';
+
+ static final Map<int, Uint8List> _TYPE_ID_TO_NAME_BYTES =
+ new Map.unmodifiable({
+ TType.BOOL: new Uint8List.fromList(NAME_BOOL.codeUnits),
+ TType.BYTE: new Uint8List.fromList(NAME_BYTE.codeUnits),
+ TType.I16: new Uint8List.fromList(NAME_I16.codeUnits),
+ TType.I32: new Uint8List.fromList(NAME_I32.codeUnits),
+ TType.I64: new Uint8List.fromList(NAME_I64.codeUnits),
+ TType.DOUBLE: new Uint8List.fromList(NAME_DOUBLE.codeUnits),
+ TType.STRING: new Uint8List.fromList(NAME_STRING.codeUnits),
+ TType.STRUCT: new Uint8List.fromList(NAME_STRUCT.codeUnits),
+ TType.MAP: new Uint8List.fromList(NAME_MAP.codeUnits),
+ TType.SET: new Uint8List.fromList(NAME_SET.codeUnits),
+ TType.LIST: new Uint8List.fromList(NAME_LIST.codeUnits)
+ });
+
+ static Uint8List getTypeNameBytesForTypeId(int typeId) {
+ if (!_TYPE_ID_TO_NAME_BYTES.containsKey(typeId)) {
+ throw new TProtocolError(
+ TProtocolErrorType.NOT_IMPLEMENTED, "Unrecognized type");
+ }
+
+ return _TYPE_ID_TO_NAME_BYTES[typeId];
+ }
+
+ static final Map<String, int> _NAME_TO_TYPE_ID = new Map.unmodifiable({
+ NAME_BOOL: TType.BOOL,
+ NAME_BYTE: TType.BYTE,
+ NAME_I16: TType.I16,
+ NAME_I32: TType.I32,
+ NAME_I64: TType.I64,
+ NAME_DOUBLE: TType.DOUBLE,
+ NAME_STRING: TType.STRING,
+ NAME_STRUCT: TType.STRUCT,
+ NAME_MAP: TType.MAP,
+ NAME_SET: TType.SET,
+ NAME_LIST: TType.LIST
+ });
+
+ static int getTypeIdForTypeName(Uint8List bytes) {
+ String name = utf8codec.decode(bytes);
+ if (!_NAME_TO_TYPE_ID.containsKey(name)) {
+ throw new TProtocolError(
+ TProtocolErrorType.NOT_IMPLEMENTED, "Unrecognized type");
+ }
+
+ return _NAME_TO_TYPE_ID[name];
+ }
+
+ static final Set<int> _JSON_NUMERICS = new Set.from([
+ '+'.codeUnitAt(0),
+ '-'.codeUnitAt(0),
+ '.'.codeUnitAt(0),
+ '0'.codeUnitAt(0),
+ '1'.codeUnitAt(0),
+ '2'.codeUnitAt(0),
+ '3'.codeUnitAt(0),
+ '4'.codeUnitAt(0),
+ '5'.codeUnitAt(0),
+ '6'.codeUnitAt(0),
+ '7'.codeUnitAt(0),
+ '8'.codeUnitAt(0),
+ '9'.codeUnitAt(0),
+ 'E'.codeUnitAt(0),
+ 'e'.codeUnitAt(0)
+ ]);
+
+ static bool isJsonNumeric(int byte) {
+ return _JSON_NUMERICS.contains(byte);
+ }
+}
+
+class _LookaheadReader {
+ final TJsonProtocol protocol;
+
+ _LookaheadReader(this.protocol);
+
+ bool _hasData = false;
+ final Uint8List _data = new Uint8List(1);
+
+ int read() {
+ if (_hasData) {
+ _hasData = false;
+ } else {
+ protocol.transport.readAll(_data, 0, 1);
+ }
+
+ return _data[0];
+ }
+
+ int peek() {
+ if (!_hasData) {
+ protocol.transport.readAll(_data, 0, 1);
+ }
+ _hasData = true;
+
+ return _data[0];
+ }
+}
+
+class _BaseContext {
+ final TJsonProtocol protocol;
+
+ _BaseContext(this.protocol);
+
+ void write() {}
+
+ void read() {}
+
+ bool get escapeNumbers => false;
+
+ String toString() => 'BaseContext';
+}
+
+class _ListContext extends _BaseContext {
+ _ListContext(TJsonProtocol protocol) : super(protocol);
+
+ bool _first = true;
+
+ void write() {
+ if (_first) {
+ _first = false;
+ } else {
+ protocol.transport.writeAll(_Constants.COMMA_BYTES);
+ }
+ }
+
+ void read() {
+ if (_first) {
+ _first = false;
+ } else {
+ protocol._readJsonSyntaxChar(_Constants.COMMA_BYTES[0]);
+ }
+ }
+
+ String toString() => 'ListContext';
+}
+
+class _PairContext extends _BaseContext {
+ _PairContext(TJsonProtocol protocol) : super(protocol);
+
+ bool _first = true;
+ bool _colon = true;
+
+ Uint8List get symbolBytes =>
+ _colon ? _Constants.COLON_BYTES : _Constants.COMMA_BYTES;
+
+ void write() {
+ if (_first) {
+ _first = false;
+ _colon = true;
+ } else {
+ protocol.transport.writeAll(symbolBytes);
+ _colon = !_colon;
+ }
+ }
+
+ void read() {
+ if (_first) {
+ _first = false;
+ _colon = true;
+ } else {
+ protocol._readJsonSyntaxChar(symbolBytes[0]);
+ _colon = !_colon;
+ }
+ }
+
+ bool get escapeNumbers => _colon;
+
+ String toString() => 'PairContext';
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_list.dart b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_list.dart
new file mode 100644
index 000000000..49f467329
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_list.dart
@@ -0,0 +1,25 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TList {
+ final int elementType;
+ final int length;
+
+ TList(this.elementType, this.length);
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_map.dart b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_map.dart
new file mode 100644
index 000000000..efdf6813a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_map.dart
@@ -0,0 +1,26 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TMap {
+ final int keyType;
+ final int valueType;
+ final int length;
+
+ TMap(this.keyType, this.valueType, this.length);
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_message.dart b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_message.dart
new file mode 100644
index 000000000..cc7b88680
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_message.dart
@@ -0,0 +1,35 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TMessageType {
+ static const int CALL = 1;
+ static const int REPLY = 2;
+ static const int EXCEPTION = 3;
+ static const int ONEWAY = 4;
+}
+
+class TMessage {
+ final String name;
+ final int type;
+ final int seqid;
+
+ TMessage(this.name, this.type, this.seqid);
+
+ String toString() => "<TMessage name: '$name' type: $type seqid: $seqid>";
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_multiplexed_protocol.dart b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_multiplexed_protocol.dart
new file mode 100644
index 000000000..078a6d72e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_multiplexed_protocol.dart
@@ -0,0 +1,43 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// Adapted from the C# version.
+class TMultiplexedProtocol extends TProtocolDecorator {
+ static const SEPARATOR = ':';
+
+ final String _serviceName;
+
+ TMultiplexedProtocol(TProtocol protocol, String serviceName)
+ : _serviceName = serviceName,
+ super(protocol) {
+ if (serviceName == null) {
+ throw new ArgumentError.notNull("serviceName");
+ }
+ }
+
+ void writeMessageBegin(TMessage message) {
+ if (message.type == TMessageType.CALL ||
+ message.type == TMessageType.ONEWAY) {
+ String name = _serviceName + SEPARATOR + message.name;
+ message = new TMessage(name, message.type, message.seqid);
+ }
+
+ super.writeMessageBegin(message);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol.dart b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol.dart
new file mode 100644
index 000000000..f49c0321d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol.dart
@@ -0,0 +1,95 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+abstract class TProtocol {
+ final TTransport transport;
+
+ TProtocol(this.transport);
+
+ /// Write
+ void writeMessageBegin(TMessage message);
+ void writeMessageEnd();
+
+ void writeStructBegin(TStruct struct);
+ void writeStructEnd();
+
+ void writeFieldBegin(TField field);
+ void writeFieldEnd();
+ void writeFieldStop();
+
+ void writeMapBegin(TMap map);
+ void writeMapEnd();
+
+ void writeListBegin(TList list);
+ void writeListEnd();
+
+ void writeSetBegin(TSet set);
+ void writeSetEnd();
+
+ void writeBool(bool b);
+
+ void writeByte(int b);
+
+ void writeI16(int i16);
+
+ void writeI32(int i32);
+
+ void writeI64(int i64);
+
+ void writeDouble(double d);
+
+ void writeString(String str);
+
+ void writeBinary(Uint8List bytes);
+
+ /// Read
+ TMessage readMessageBegin();
+ void readMessageEnd();
+
+ TStruct readStructBegin();
+ void readStructEnd();
+
+ TField readFieldBegin();
+ void readFieldEnd();
+
+ TMap readMapBegin();
+ void readMapEnd();
+
+ TList readListBegin();
+ void readListEnd();
+
+ TSet readSetBegin();
+ void readSetEnd();
+
+ bool readBool();
+
+ int readByte();
+
+ int readI16();
+
+ int readI32();
+
+ int readI64();
+
+ double readDouble();
+
+ String readString();
+
+ Uint8List readBinary();
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol_decorator.dart b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol_decorator.dart
new file mode 100644
index 000000000..9cd02f645
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol_decorator.dart
@@ -0,0 +1,150 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// Forward all operations to the wrapped protocol. Used as a base class.
+///
+/// Adapted from the C# version.
+class TProtocolDecorator extends TProtocol {
+ final TProtocol _protocol;
+
+ TProtocolDecorator(TProtocol protocol)
+ : _protocol = protocol,
+ super(protocol.transport);
+
+ /// Write
+
+ void writeMessageBegin(TMessage message) {
+ _protocol.writeMessageBegin(message);
+ }
+
+ void writeMessageEnd() {
+ _protocol.writeMessageEnd();
+ }
+
+ void writeStructBegin(TStruct struct) {
+ _protocol.writeStructBegin(struct);
+ }
+
+ void writeStructEnd() {
+ _protocol.writeStructEnd();
+ }
+
+ void writeFieldBegin(TField field) {
+ _protocol.writeFieldBegin(field);
+ }
+
+ void writeFieldEnd() {
+ _protocol.writeFieldEnd();
+ }
+
+ void writeFieldStop() {
+ _protocol.writeFieldStop();
+ }
+
+ void writeMapBegin(TMap map) {
+ _protocol.writeMapBegin(map);
+ }
+
+ void writeMapEnd() {
+ _protocol.writeMapEnd();
+ }
+
+ void writeListBegin(TList list) {
+ _protocol.writeListBegin(list);
+ }
+
+ void writeListEnd() {
+ _protocol.writeListEnd();
+ }
+
+ void writeSetBegin(TSet set) {
+ _protocol.writeSetBegin(set);
+ }
+
+ void writeSetEnd() {
+ _protocol.writeSetEnd();
+ }
+
+ void writeBool(bool b) {
+ _protocol.writeBool(b);
+ }
+
+ void writeByte(int b) {
+ _protocol.writeByte(b);
+ }
+
+ void writeI16(int i16) {
+ _protocol.writeI16(i16);
+ }
+
+ void writeI32(int i32) {
+ _protocol.writeI32(i32);
+ }
+
+ void writeI64(int i64) {
+ _protocol.writeI64(i64);
+ }
+
+ void writeDouble(double d) {
+ _protocol.writeDouble(d);
+ }
+
+ void writeString(String str) {
+ _protocol.writeString(str);
+ }
+
+ void writeBinary(Uint8List bytes) {
+ _protocol.writeBinary(bytes);
+ }
+
+ /// Read
+ TMessage readMessageBegin() => _protocol.readMessageBegin();
+ void readMessageEnd() => _protocol.readMessageEnd();
+
+ TStruct readStructBegin() => _protocol.readStructBegin();
+ void readStructEnd() => _protocol.readStructEnd();
+
+ TField readFieldBegin() => _protocol.readFieldBegin();
+ void readFieldEnd() => _protocol.readFieldEnd();
+
+ TMap readMapBegin() => _protocol.readMapBegin();
+ void readMapEnd() => _protocol.readMapEnd();
+
+ TList readListBegin() => _protocol.readListBegin();
+ void readListEnd() => _protocol.readListEnd();
+
+ TSet readSetBegin() => _protocol.readSetBegin();
+ void readSetEnd() => _protocol.readSetEnd();
+
+ bool readBool() => _protocol.readBool();
+
+ int readByte() => _protocol.readByte();
+
+ int readI16() => _protocol.readI16();
+
+ int readI32() => _protocol.readI32();
+
+ int readI64() => _protocol.readI64();
+
+ double readDouble() => _protocol.readDouble();
+
+ String readString() => _protocol.readString();
+
+ Uint8List readBinary() => _protocol.readBinary();
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol_error.dart b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol_error.dart
new file mode 100644
index 000000000..456baeb79
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol_error.dart
@@ -0,0 +1,33 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TProtocolErrorType {
+ static const int UNKNOWN = 0;
+ static const int INVALID_DATA = 1;
+ static const int NEGATIVE_SIZE = 2;
+ static const int SIZE_LIMIT = 3;
+ static const int BAD_VERSION = 4;
+ static const int NOT_IMPLEMENTED = 5;
+ static const int DEPTH_LIMIT = 6;
+}
+
+class TProtocolError extends TError {
+ TProtocolError([int type = TProtocolErrorType.UNKNOWN, String message = ""])
+ : super(type, message);
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol_factory.dart b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol_factory.dart
new file mode 100644
index 000000000..922c6cb69
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol_factory.dart
@@ -0,0 +1,22 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+abstract class TProtocolFactory<T extends TProtocol> {
+ T getProtocol(TTransport transport);
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol_util.dart b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol_util.dart
new file mode 100644
index 000000000..841ea8217
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_protocol_util.dart
@@ -0,0 +1,107 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TProtocolUtil {
+ // equal to JavaScript Number.MAX_SAFE_INTEGER, 2^53 - 1
+ static const int defaultRecursionLimit = 9007199254740991;
+
+ static int maxRecursionLimit = defaultRecursionLimit;
+
+ static skip(TProtocol prot, int type) {
+ _skip(prot, type, maxRecursionLimit);
+ }
+
+ static _skip(TProtocol prot, int type, int recursionLimit) {
+ if (recursionLimit <= 0) {
+ throw new TProtocolError(
+ TProtocolErrorType.DEPTH_LIMIT, "Depth limit exceeded");
+ }
+
+ switch (type) {
+ case TType.BOOL:
+ prot.readBool();
+ break;
+
+ case TType.BYTE:
+ prot.readByte();
+ break;
+
+ case TType.I16:
+ prot.readI16();
+ break;
+
+ case TType.I32:
+ prot.readI32();
+ break;
+
+ case TType.I64:
+ prot.readI64();
+ break;
+
+ case TType.DOUBLE:
+ prot.readDouble();
+ break;
+
+ case TType.STRING:
+ prot.readBinary();
+ break;
+
+ case TType.STRUCT:
+ prot.readStructBegin();
+ while (true) {
+ TField field = prot.readFieldBegin();
+ if (field.type == TType.STOP) {
+ break;
+ }
+ _skip(prot, field.type, recursionLimit - 1);
+ prot.readFieldEnd();
+ }
+ prot.readStructEnd();
+ break;
+
+ case TType.MAP:
+ TMap map = prot.readMapBegin();
+ for (int i = 0; i < map.length; i++) {
+ _skip(prot, map.keyType, recursionLimit - 1);
+ _skip(prot, map.valueType, recursionLimit - 1);
+ }
+ prot.readMapEnd();
+ break;
+
+ case TType.SET:
+ TSet set = prot.readSetBegin();
+ for (int i = 0; i < set.length; i++) {
+ _skip(prot, set.elementType, recursionLimit - 1);
+ }
+ prot.readSetEnd();
+ break;
+
+ case TType.LIST:
+ TList list = prot.readListBegin();
+ for (int i = 0; i < list.length; i++) {
+ _skip(prot, list.elementType, recursionLimit - 1);
+ }
+ prot.readListEnd();
+ break;
+
+ default:
+ throw new TProtocolError(TProtocolErrorType.INVALID_DATA, "Invalid data");
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_set.dart b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_set.dart
new file mode 100644
index 000000000..b342537e3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_set.dart
@@ -0,0 +1,25 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TSet {
+ final int elementType;
+ final int length;
+
+ TSet(this.elementType, this.length);
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_struct.dart b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_struct.dart
new file mode 100644
index 000000000..0303f6395
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_struct.dart
@@ -0,0 +1,24 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TStruct {
+ final String name;
+
+ TStruct([this.name = ""]);
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_type.dart b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_type.dart
new file mode 100644
index 000000000..3919d969d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/protocol/t_type.dart
@@ -0,0 +1,34 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TType {
+ static const int STOP = 0;
+ static const int VOID = 1;
+ static const int BOOL = 2;
+ static const int BYTE = 3;
+ static const int DOUBLE = 4;
+ static const int I16 = 6;
+ static const int I32 = 8;
+ static const int I64 = 10;
+ static const int STRING = 11;
+ static const int STRUCT = 12;
+ static const int MAP = 13;
+ static const int SET = 14;
+ static const int LIST = 15;
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/serializer/t_deserializer.dart b/src/jaegertracing/thrift/lib/dart/lib/src/serializer/t_deserializer.dart
new file mode 100644
index 000000000..aefbee25b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/serializer/t_deserializer.dart
@@ -0,0 +1,50 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TDeserializer {
+ final message = new TMessage('Deserializer', TMessageType.ONEWAY, 1);
+ TBufferedTransport transport;
+ TProtocol protocol;
+
+ TDeserializer({TProtocolFactory protocolFactory}) {
+ this.transport = new TBufferedTransport();
+
+ if (protocolFactory == null) {
+ protocolFactory = new TBinaryProtocolFactory();
+ }
+
+ this.protocol = protocolFactory.getProtocol(this.transport);
+ }
+
+ void read(TBase base, Uint8List data) {
+ transport.writeAll(data);
+
+ transport.flush();
+
+ base.read(protocol);
+ }
+
+ void readString(TBase base, String data) {
+
+ transport.writeAll(base64.decode(data));
+ transport.flush();
+
+ base.read(protocol);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/serializer/t_serializer.dart b/src/jaegertracing/thrift/lib/dart/lib/src/serializer/t_serializer.dart
new file mode 100644
index 000000000..feec8226d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/serializer/t_serializer.dart
@@ -0,0 +1,48 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TSerializer {
+ final message = new TMessage('Serializer', TMessageType.ONEWAY, 1);
+ TBufferedTransport transport;
+ TProtocol protocol;
+
+ TSerializer({TProtocolFactory protocolFactory}) {
+ this.transport = new TBufferedTransport();
+
+ if (protocolFactory == null) {
+ protocolFactory = new TBinaryProtocolFactory();
+ }
+
+ this.protocol = protocolFactory.getProtocol(this.transport);
+ }
+
+ Uint8List write(TBase base) {
+ base.write(protocol);
+
+ return transport.consumeWriteBuffer();
+ }
+
+ String writeString(TBase base) {
+ base.write(protocol);
+
+ Uint8List bytes = transport.consumeWriteBuffer();
+
+ return base64.encode(bytes);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/t_application_error.dart b/src/jaegertracing/thrift/lib/dart/lib/src/t_application_error.dart
new file mode 100644
index 000000000..6f8abd4bd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/t_application_error.dart
@@ -0,0 +1,104 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TApplicationErrorType {
+ static const int UNKNOWN = 0;
+ static const int UNKNOWN_METHOD = 1;
+ static const int INVALID_MESSAGE_TYPE = 2;
+ static const int WRONG_METHOD_NAME = 3;
+ static const int BAD_SEQUENCE_ID = 4;
+ static const int MISSING_RESULT = 5;
+ static const int INTERNAL_ERROR = 6;
+ static const int PROTOCOL_ERROR = 7;
+ static const int INVALID_TRANSFORM = 8;
+ static const int INVALID_PROTOCOL = 9;
+ static const int UNSUPPORTED_CLIENT_TYPE = 10;
+}
+
+class TApplicationError extends TError {
+ static final TStruct _struct = new TStruct("TApplicationError");
+ static const int MESSAGE = 1;
+ static final TField _messageField =
+ new TField("message", TType.STRING, MESSAGE);
+ static const int TYPE = 2;
+ static final TField _typeField = new TField("type", TType.I32, TYPE);
+
+ TApplicationError(
+ [int type = TApplicationErrorType.UNKNOWN, String message = ""])
+ : super(type, message);
+
+ static TApplicationError read(TProtocol iprot) {
+ TField field;
+
+ String message = null;
+ int type = TApplicationErrorType.UNKNOWN;
+
+ iprot.readStructBegin();
+ while (true) {
+ field = iprot.readFieldBegin();
+
+ if (field.type == TType.STOP) {
+ break;
+ }
+
+ switch (field.id) {
+ case MESSAGE:
+ if (field.type == TType.STRING) {
+ message = iprot.readString();
+ } else {
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ break;
+
+ case TYPE:
+ if (field.type == TType.I32) {
+ type = iprot.readI32();
+ } else {
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ break;
+
+ default:
+ TProtocolUtil.skip(iprot, field.type);
+ break;
+ }
+ iprot.readFieldEnd();
+ }
+ iprot.readStructEnd();
+
+ return new TApplicationError(type, message);
+ }
+
+ write(TProtocol oprot) {
+ oprot.writeStructBegin(_struct);
+
+ if (message != null && !message.isEmpty) {
+ oprot.writeFieldBegin(_messageField);
+ oprot.writeString(message);
+ oprot.writeFieldEnd();
+ }
+
+ oprot.writeFieldBegin(_typeField);
+ oprot.writeI32(type);
+ oprot.writeFieldEnd();
+
+ oprot.writeFieldStop();
+ oprot.writeStructEnd();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/t_base.dart b/src/jaegertracing/thrift/lib/dart/lib/src/t_base.dart
new file mode 100644
index 000000000..d5571b6dc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/t_base.dart
@@ -0,0 +1,37 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+abstract class TBase {
+ /// Reads the TObject from the given input protocol.
+ void read(TProtocol iprot);
+
+ /// Writes the objects out to the [oprot] protocol.
+ void write(TProtocol oprot);
+
+ /// Check if a field is currently set or unset, using the [fieldId].
+ bool isSet(int fieldId);
+
+ /// Get a field's value by [fieldId]. Primitive types will be wrapped in the
+ /// appropriate "boxed" types.
+ getFieldValue(int fieldId);
+
+ /// Set a field's value by [fieldId]. Primitive types must be "boxed" in the
+ /// appropriate object wrapper type.
+ setFieldValue(int fieldId, Object value);
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/t_error.dart b/src/jaegertracing/thrift/lib/dart/lib/src/t_error.dart
new file mode 100644
index 000000000..93ab73239
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/t_error.dart
@@ -0,0 +1,27 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TError extends Error {
+ final String message;
+ final int type;
+
+ TError(this.type, this.message);
+
+ String toString() => "<TError type: $type message: '$message'>";
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/t_processor.dart b/src/jaegertracing/thrift/lib/dart/lib/src/t_processor.dart
new file mode 100644
index 000000000..dcf20fbc7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/t_processor.dart
@@ -0,0 +1,24 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// A processor is a generic object which operates upon an input stream and
+/// writes to some output stream.
+abstract class TProcessor {
+ bool process(TProtocol input, TProtocol output);
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_buffered_transport.dart b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_buffered_transport.dart
new file mode 100644
index 000000000..b73a30c0e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_buffered_transport.dart
@@ -0,0 +1,98 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// Buffered implementation of [TTransport].
+class TBufferedTransport extends TTransport {
+ final List<int> _writeBuffer = [];
+ Iterator<int> _readIterator;
+
+ Uint8List consumeWriteBuffer() {
+ Uint8List buffer = new Uint8List.fromList(_writeBuffer);
+ _writeBuffer.clear();
+ return buffer;
+ }
+
+ void _setReadBuffer(Uint8List readBuffer) {
+ _readIterator = readBuffer != null ? readBuffer.iterator : null;
+ }
+
+ void _reset({bool isOpen: false}) {
+ _isOpen = isOpen;
+ _writeBuffer.clear();
+ _readIterator = null;
+ }
+
+ bool get hasReadData => _readIterator != null;
+
+ bool _isOpen;
+ bool get isOpen => _isOpen;
+
+ Future open() async {
+ _reset(isOpen: true);
+ }
+
+ Future close() async {
+ _reset(isOpen: false);
+ }
+
+ int read(Uint8List buffer, int offset, int length) {
+ if (buffer == null) {
+ throw new ArgumentError.notNull("buffer");
+ }
+
+ if (offset + length > buffer.length) {
+ throw new ArgumentError("The range exceeds the buffer length");
+ }
+
+ if (_readIterator == null || length <= 0) {
+ return 0;
+ }
+
+ int i = 0;
+ while (i < length && _readIterator.moveNext()) {
+ buffer[offset + i] = _readIterator.current;
+ i++;
+ }
+
+ // cleanup iterator when we've reached the end
+ if (_readIterator.current == null) {
+ _readIterator = null;
+ }
+
+ return i;
+ }
+
+ void write(Uint8List buffer, int offset, int length) {
+ if (buffer == null) {
+ throw new ArgumentError.notNull("buffer");
+ }
+
+ if (offset + length > buffer.length) {
+ throw new ArgumentError("The range exceeds the buffer length");
+ }
+
+ _writeBuffer.addAll(buffer.sublist(offset, offset + length));
+ }
+
+ Future flush() {
+ _readIterator = consumeWriteBuffer().iterator;
+
+ return new Future.value();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_framed_transport.dart b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_framed_transport.dart
new file mode 100644
index 000000000..2ef03f7f8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_framed_transport.dart
@@ -0,0 +1,169 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// Framed [TTransport].
+///
+/// Adapted from the Java Framed transport.
+class TFramedTransport extends TBufferedTransport {
+ static const int headerByteCount = 4;
+
+ final TTransport _transport;
+
+ final Uint8List _headerBytes = new Uint8List(headerByteCount);
+ int _receivedHeaderBytes = 0;
+
+ int _bodySize = 0;
+ Uint8List _body = null;
+ int _receivedBodyBytes = 0;
+
+ Completer<Uint8List> _frameCompleter = null;
+
+ TFramedTransport(TTransport transport) : _transport = transport {
+ if (transport == null) {
+ throw new ArgumentError.notNull("transport");
+ }
+ }
+
+ bool get isOpen => _transport.isOpen;
+
+ Future open() {
+ _reset(isOpen: true);
+ return _transport.open();
+ }
+
+ Future close() {
+ _reset(isOpen: false);
+ return _transport.close();
+ }
+
+ int read(Uint8List buffer, int offset, int length) {
+ if (hasReadData) {
+ int got = super.read(buffer, offset, length);
+ if (got > 0) return got;
+ }
+
+ // IMPORTANT: by the time you've got here,
+ // an entire frame is available for reading
+
+ return super.read(buffer, offset, length);
+ }
+
+ void _readFrame() {
+ if (_body == null) {
+ bool gotFullHeader = _readFrameHeader();
+ if (!gotFullHeader) {
+ return;
+ }
+ }
+
+ _readFrameBody();
+ }
+
+ bool _readFrameHeader() {
+ var remainingHeaderBytes = headerByteCount - _receivedHeaderBytes;
+
+ int got = _transport.read(_headerBytes, _receivedHeaderBytes, remainingHeaderBytes);
+ if (got < 0) {
+ throw new TTransportError(
+ TTransportErrorType.UNKNOWN, "Socket closed during frame header read");
+ }
+
+ _receivedHeaderBytes += got;
+
+ if (_receivedHeaderBytes == headerByteCount) {
+ int size = _headerBytes.buffer.asByteData().getUint32(0);
+
+ _receivedHeaderBytes = 0;
+
+ if (size < 0) {
+ throw new TTransportError(
+ TTransportErrorType.UNKNOWN, "Read a negative frame size: $size");
+ }
+
+ _bodySize = size;
+ _body = new Uint8List(_bodySize);
+ _receivedBodyBytes = 0;
+
+ return true;
+ } else {
+ _registerForReadableBytes();
+ return false;
+ }
+ }
+
+ void _readFrameBody() {
+ var remainingBodyBytes = _bodySize - _receivedBodyBytes;
+
+ int got = _transport.read(_body, _receivedBodyBytes, remainingBodyBytes);
+ if (got < 0) {
+ throw new TTransportError(
+ TTransportErrorType.UNKNOWN, "Socket closed during frame body read");
+ }
+
+ _receivedBodyBytes += got;
+
+ if (_receivedBodyBytes == _bodySize) {
+ var body = _body;
+
+ _bodySize = 0;
+ _body = null;
+ _receivedBodyBytes = 0;
+
+ _setReadBuffer(body);
+
+ var completer = _frameCompleter;
+ _frameCompleter = null;
+ completer.complete(new Uint8List(0));
+ } else {
+ _registerForReadableBytes();
+ }
+ }
+
+ Future flush() {
+ if (_frameCompleter == null) {
+ Uint8List buffer = consumeWriteBuffer();
+ int length = buffer.length;
+
+ _headerBytes.buffer.asByteData().setUint32(0, length);
+ _transport.write(_headerBytes, 0, headerByteCount);
+ _transport.write(buffer, 0, length);
+
+ _frameCompleter = new Completer<Uint8List>();
+ _registerForReadableBytes();
+ }
+
+ return _frameCompleter.future;
+ }
+
+ void _registerForReadableBytes() {
+ _transport.flush().then((_) {
+ _readFrame();
+ }).catchError((e) {
+ var completer = _frameCompleter;
+
+ _receivedHeaderBytes = 0;
+ _bodySize = 0;
+ _body = null;
+ _receivedBodyBytes = 0;
+ _frameCompleter = null;
+
+ completer.completeError(e);
+ });
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_http_transport.dart b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_http_transport.dart
new file mode 100644
index 000000000..630213fbf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_http_transport.dart
@@ -0,0 +1,99 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// HTTP implementation of [TTransport].
+///
+/// For example:
+///
+/// var transport = new THttpClientTransport(new BrowserClient(),
+/// new THttpConfig(url, {'X-My-Custom-Header': 'my value'}));
+/// var protocol = new TJsonProtocol(transport);
+/// var client = new MyThriftServiceClient(protocol);
+/// var result = client.myMethod();
+///
+/// Adapted from the JS XHR HTTP transport.
+class THttpClientTransport extends TBufferedTransport {
+ final Client httpClient;
+ final THttpConfig config;
+
+ THttpClientTransport(this.httpClient, this.config) {
+ if (httpClient == null) {
+ throw new ArgumentError.notNull("httpClient");
+ }
+ }
+
+ Future close() async {
+ _reset(isOpen: false);
+ httpClient.close();
+ }
+
+ Future flush() {
+ var requestBody = base64.encode(consumeWriteBuffer());
+
+ // Use a sync completer to ensure that the buffer can be read immediately
+ // after the read buffer is set, and avoid a race condition where another
+ // response could overwrite the read buffer.
+ var completer = new Completer.sync();
+
+ httpClient
+ .post(config.url, headers: config.headers, body: requestBody)
+ .then((response) {
+ Uint8List data;
+ try {
+ data = new Uint8List.fromList(base64.decode(response.body));
+ } on FormatException catch (_) {
+ throw new TProtocolError(TProtocolErrorType.INVALID_DATA,
+ "Expected a Base 64 encoded string.");
+ }
+
+ _setReadBuffer(data);
+ completer.complete();
+ });
+
+ return completer.future;
+ }
+}
+
+class THttpConfig {
+ final Uri url;
+
+ Map<String, String> _headers;
+ Map<String, String> get headers => _headers;
+
+ THttpConfig(this.url, Map<String, String> headers) {
+ if (url == null || !url.hasAuthority) {
+ throw new ArgumentError("Invalid url");
+ }
+
+ _initHeaders(headers);
+ }
+
+ void _initHeaders(Map<String, String> initial) {
+ var h = {};
+
+ if (initial != null) {
+ h.addAll(initial);
+ }
+
+ h['Content-Type'] = 'application/x-thrift';
+ h['Accept'] = 'application/x-thrift';
+
+ _headers = new Map.unmodifiable(h);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_message_reader.dart b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_message_reader.dart
new file mode 100644
index 000000000..8ca070834
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_message_reader.dart
@@ -0,0 +1,99 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// [TMessageReader] extracts a [TMessage] from bytes. This is used to allow a
+/// transport to inspect the message seqid and map responses to requests.
+class TMessageReader {
+ final TProtocolFactory protocolFactory;
+
+ final int byteOffset;
+ final _TMessageReaderTransport _transport;
+
+ /// Construct a [MessageReader]. The optional [byteOffset] specifies the
+ /// number of bytes to skip before reading the [TMessage].
+ TMessageReader(this.protocolFactory, {int byteOffset: 0})
+ : _transport = new _TMessageReaderTransport(),
+ this.byteOffset = byteOffset;
+
+ TMessage readMessage(Uint8List bytes) {
+ _transport.reset(bytes, byteOffset);
+ TProtocol protocol = protocolFactory.getProtocol(_transport);
+ TMessage message = protocol.readMessageBegin();
+ _transport.reset(null);
+
+ return message;
+ }
+}
+
+/// An internal class used to support [TMessageReader].
+class _TMessageReaderTransport extends TTransport {
+ _TMessageReaderTransport();
+
+ Iterator<int> _readIterator;
+
+ void reset(Uint8List bytes, [int offset = 0]) {
+ if (bytes == null) {
+ _readIterator = null;
+ return;
+ }
+
+ if (offset > bytes.length) {
+ throw new ArgumentError("The offset exceeds the bytes length");
+ }
+
+ _readIterator = bytes.iterator;
+
+ for (var i = 0; i < offset; i++) {
+ _readIterator.moveNext();
+ }
+ }
+
+ get isOpen => true;
+
+ Future open() => throw new UnsupportedError("Unsupported in MessageReader");
+
+ Future close() => throw new UnsupportedError("Unsupported in MessageReader");
+
+ int read(Uint8List buffer, int offset, int length) {
+ if (buffer == null) {
+ throw new ArgumentError.notNull("buffer");
+ }
+
+ if (offset + length > buffer.length) {
+ throw new ArgumentError("The range exceeds the buffer length");
+ }
+
+ if (_readIterator == null || length <= 0) {
+ return 0;
+ }
+
+ int i = 0;
+ while (i < length && _readIterator.moveNext()) {
+ buffer[offset + i] = _readIterator.current;
+ i++;
+ }
+
+ return i;
+ }
+
+ void write(Uint8List buffer, int offset, int length) =>
+ throw new UnsupportedError("Unsupported in MessageReader");
+
+ Future flush() => throw new UnsupportedError("Unsupported in MessageReader");
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_socket.dart b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_socket.dart
new file mode 100644
index 000000000..b2eb6b646
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_socket.dart
@@ -0,0 +1,38 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+enum TSocketState { CLOSED, OPEN }
+
+abstract class TSocket {
+ Stream<TSocketState> get onState;
+
+ Stream<Object> get onError;
+
+ Stream<Uint8List> get onMessage;
+
+ bool get isOpen;
+
+ bool get isClosed;
+
+ Future open();
+
+ Future close();
+
+ void send(Uint8List data);
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_socket_transport.dart b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_socket_transport.dart
new file mode 100644
index 000000000..c41374aff
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_socket_transport.dart
@@ -0,0 +1,177 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// Socket implementation of [TTransport].
+///
+/// For example:
+///
+/// var transport = new TClientSocketTransport(new TWebSocket(url));
+/// var protocol = new TBinaryProtocol(transport);
+/// var client = new MyThriftServiceClient(protocol);
+/// var result = client.myMethod();
+///
+/// Adapted from the JS WebSocket transport.
+abstract class TSocketTransport extends TBufferedTransport {
+ final Logger logger = new Logger('thrift.TSocketTransport');
+
+ final TSocket socket;
+
+ /// A transport using the provided [socket].
+ TSocketTransport(this.socket) {
+ if (socket == null) {
+ throw new ArgumentError.notNull('socket');
+ }
+
+ socket.onError.listen((e) => logger.warning(e));
+ socket.onMessage.listen(handleIncomingMessage);
+ }
+
+ bool get isOpen => socket.isOpen;
+
+ Future open() {
+ _reset(isOpen: true);
+ return socket.open();
+ }
+
+ Future close() {
+ _reset(isOpen: false);
+ return socket.close();
+ }
+
+ /// Make an incoming message available to read from the transport.
+ void handleIncomingMessage(Uint8List messageBytes) {
+ _setReadBuffer(messageBytes);
+ }
+}
+
+/// [TClientSocketTransport] is a basic client socket transport. It sends
+/// outgoing messages and expects a response.
+///
+/// NOTE: This transport expects a single threaded server, as it will process
+/// responses in FIFO order.
+class TClientSocketTransport extends TSocketTransport {
+ final List<Completer<Uint8List>> _completers = [];
+
+ TClientSocketTransport(TSocket socket) : super(socket);
+
+ Future flush() {
+ Uint8List bytes = consumeWriteBuffer();
+
+ // Use a sync completer to ensure that the buffer can be read immediately
+ // after the read buffer is set, and avoid a race condition where another
+ // response could overwrite the read buffer.
+ var completer = new Completer<Uint8List>.sync();
+ _completers.add(completer);
+
+ if (bytes.lengthInBytes > 0) {
+ socket.send(bytes);
+ }
+
+ return completer.future;
+ }
+
+ void handleIncomingMessage(Uint8List messageBytes) {
+ super.handleIncomingMessage(messageBytes);
+
+ if (_completers.isNotEmpty) {
+ var completer = _completers.removeAt(0);
+ completer.complete();
+ }
+ }
+}
+
+/// [TAsyncClientSocketTransport] sends outgoing messages and expects an
+/// asynchronous response.
+///
+/// NOTE: This transport uses a [MessageReader] to read a [TMessage] when an
+/// incoming message arrives to correlate a response to a request, using the
+/// seqid.
+class TAsyncClientSocketTransport extends TSocketTransport {
+ static const defaultTimeout = const Duration(seconds: 30);
+
+ final Map<int, Completer<Uint8List>> _completers = {};
+
+ final TMessageReader messageReader;
+
+ final Duration responseTimeout;
+
+ TAsyncClientSocketTransport(TSocket socket, TMessageReader messageReader,
+ {Duration responseTimeout: defaultTimeout})
+ : this.messageReader = messageReader,
+ this.responseTimeout = responseTimeout,
+ super(socket);
+
+ Future flush() {
+ Uint8List bytes = consumeWriteBuffer();
+ TMessage message = messageReader.readMessage(bytes);
+ int seqid = message.seqid;
+
+ // Use a sync completer to ensure that the buffer can be read immediately
+ // after the read buffer is set, and avoid a race condition where another
+ // response could overwrite the read buffer.
+ var completer = new Completer<Uint8List>.sync();
+ _completers[seqid] = completer;
+
+ if (responseTimeout != null) {
+ new Future.delayed(responseTimeout, () {
+ var completer = _completers.remove(seqid);
+ if (completer != null) {
+ completer.completeError(
+ new TimeoutException("Response timed out.", responseTimeout));
+ }
+ });
+ }
+
+ socket.send(bytes);
+
+ return completer.future;
+ }
+
+ void handleIncomingMessage(Uint8List messageBytes) {
+ super.handleIncomingMessage(messageBytes);
+
+ TMessage message = messageReader.readMessage(messageBytes);
+ var completer = _completers.remove(message.seqid);
+ if (completer != null) {
+ completer.complete();
+ }
+ }
+}
+
+/// [TServerSocketTransport] listens for incoming messages. When it sends a
+/// response, it does not expect an acknowledgement.
+class TServerSocketTransport extends TSocketTransport {
+ final StreamController _onIncomingMessageController;
+ Stream get onIncomingMessage => _onIncomingMessageController.stream;
+
+ TServerSocketTransport(TSocket socket)
+ : _onIncomingMessageController = new StreamController.broadcast(),
+ super(socket);
+
+ Future flush() async {
+ Uint8List message = consumeWriteBuffer();
+ socket.send(message);
+ }
+
+ void handleIncomingMessage(Uint8List messageBytes) {
+ super.handleIncomingMessage(messageBytes);
+
+ _onIncomingMessageController.add(null);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_transport.dart b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_transport.dart
new file mode 100644
index 000000000..563d5eb5a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_transport.dart
@@ -0,0 +1,70 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+abstract class TTransport {
+ /// Queries whether the transport is open.
+ /// Returns [true] if the transport is open.
+ bool get isOpen;
+
+ /// Opens the transport for reading/writing.
+ /// Throws [TTransportError] if the transport could not be opened.
+ Future open();
+
+ /// Closes the transport.
+ Future close();
+
+ /// Reads up to [length] bytes into [buffer], starting at [offset].
+ /// Returns the number of bytes actually read.
+ /// Throws [TTransportError] if there was an error reading data
+ int read(Uint8List buffer, int offset, int length);
+
+ /// Guarantees that all of [length] bytes are actually read off the transport.
+ /// Returns the number of bytes actually read, which must be equal to
+ /// [length].
+ /// Throws [TTransportError] if there was an error reading data
+ int readAll(Uint8List buffer, int offset, int length) {
+ int got = 0;
+ int ret = 0;
+ while (got < length) {
+ ret = read(buffer, offset + got, length - got);
+ if (ret <= 0) {
+ throw new TTransportError(
+ TTransportErrorType.UNKNOWN,
+ "Cannot read. Remote side has closed. Tried to read $length "
+ "bytes, but only got $got bytes.");
+ }
+ got += ret;
+ }
+ return got;
+ }
+
+ /// Writes up to [len] bytes from the buffer.
+ /// Throws [TTransportError] if there was an error writing data
+ void write(Uint8List buffer, int offset, int length);
+
+ /// Writes the [bytes] to the output.
+ /// Throws [TTransportError] if there was an error writing data
+ void writeAll(Uint8List buffer) {
+ write(buffer, 0, buffer.length);
+ }
+
+ /// Flush any pending data out of a transport buffer.
+ /// Throws [TTransportError] if there was an error writing out data.
+ Future flush();
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_transport_error.dart b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_transport_error.dart
new file mode 100644
index 000000000..d3508e052
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_transport_error.dart
@@ -0,0 +1,31 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TTransportErrorType {
+ static const int UNKNOWN = 0;
+ static const int NOT_OPEN = 1;
+ static const int ALREADY_OPEN = 2;
+ static const int TIMED_OUT = 3;
+ static const int END_OF_FILE = 4;
+}
+
+class TTransportError extends TError {
+ TTransportError([int type = TTransportErrorType.UNKNOWN, String message = ""])
+ : super(type, message);
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_transport_factory.dart b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_transport_factory.dart
new file mode 100644
index 000000000..7a10461d2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/src/transport/t_transport_factory.dart
@@ -0,0 +1,27 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// Factory class used to create wrapped instance of a [TTransport]. This is
+/// used primarily in servers.
+///
+/// Adapted from the Java version.
+class TTransportFactory {
+ Future<TTransport> getTransport(TTransport transport) =>
+ new Future.value(transport);
+}
diff --git a/src/jaegertracing/thrift/lib/dart/lib/thrift.dart b/src/jaegertracing/thrift/lib/dart/lib/thrift.dart
new file mode 100644
index 000000000..c429d773c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/thrift.dart
@@ -0,0 +1,65 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+library thrift;
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:convert' show Utf8Codec;
+import 'dart:typed_data' show ByteData;
+import 'dart:typed_data' show Uint8List;
+
+import 'package:dart2_constant/convert.dart' show base64;
+import 'package:dart2_constant/typed_data.dart' show Endianness;
+import 'package:fixnum/fixnum.dart';
+import 'package:http/http.dart' show Client;
+import 'package:logging/logging.dart';
+
+part 'src/t_application_error.dart';
+part 'src/t_base.dart';
+part 'src/t_error.dart';
+part 'src/t_processor.dart';
+
+part 'src/protocol/t_binary_protocol.dart';
+part 'src/protocol/t_compact_protocol.dart';
+part 'src/protocol/t_field.dart';
+part 'src/protocol/t_json_protocol.dart';
+part 'src/protocol/t_list.dart';
+part 'src/protocol/t_map.dart';
+part 'src/protocol/t_message.dart';
+part 'src/protocol/t_multiplexed_protocol.dart';
+part 'src/protocol/t_protocol.dart';
+part 'src/protocol/t_protocol_decorator.dart';
+part 'src/protocol/t_protocol_error.dart';
+part 'src/protocol/t_protocol_factory.dart';
+part 'src/protocol/t_protocol_util.dart';
+part 'src/protocol/t_set.dart';
+part 'src/protocol/t_struct.dart';
+part 'src/protocol/t_type.dart';
+
+part 'src/serializer/t_deserializer.dart';
+part 'src/serializer/t_serializer.dart';
+
+part 'src/transport/t_buffered_transport.dart';
+part 'src/transport/t_framed_transport.dart';
+part 'src/transport/t_http_transport.dart';
+part 'src/transport/t_message_reader.dart';
+part 'src/transport/t_socket.dart';
+part 'src/transport/t_transport.dart';
+part 'src/transport/t_transport_error.dart';
+part 'src/transport/t_transport_factory.dart';
+part 'src/transport/t_socket_transport.dart';
diff --git a/src/jaegertracing/thrift/lib/dart/lib/thrift_browser.dart b/src/jaegertracing/thrift/lib/dart/lib/thrift_browser.dart
new file mode 100644
index 000000000..2ebc25758
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/thrift_browser.dart
@@ -0,0 +1,22 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+library thrift_browser;
+
+/// Classes that are only supported in browser applications go here
+
+export 'src/browser/t_web_socket.dart' show TWebSocket;
diff --git a/src/jaegertracing/thrift/lib/dart/lib/thrift_console.dart b/src/jaegertracing/thrift/lib/dart/lib/thrift_console.dart
new file mode 100644
index 000000000..48a83d1dc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/lib/thrift_console.dart
@@ -0,0 +1,23 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+library thrift_console;
+
+/// Classes that are only supported in console applications go here
+
+export 'src/console/t_tcp_socket.dart' show TTcpSocket;
+export 'src/console/t_web_socket.dart' show TWebSocket;
diff --git a/src/jaegertracing/thrift/lib/dart/pubspec.yaml b/src/jaegertracing/thrift/lib/dart/pubspec.yaml
new file mode 100644
index 000000000..f406b9932
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/pubspec.yaml
@@ -0,0 +1,38 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# 'License'); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: thrift
+version: 0.13.0
+description: >
+ A Dart library for Apache Thrift
+author: Apache Thrift Developers <dev@thrift.apache.org>
+homepage: http://thrift.apache.org
+documentation: http://thrift.apache.org
+
+environment:
+ sdk: ">=1.24.3 <3.0.0"
+
+dependencies:
+ dart2_constant: ^1.0.0
+ fixnum: ^0.10.2
+ http: ^0.11.3
+ logging: ^0.11.0
+
+dev_dependencies:
+ dart_dev: ^2.0.0
+ mockito: ">=2.2.2 <4.0.0"
+ test: ">=0.12.30 <2.0.0"
diff --git a/src/jaegertracing/thrift/lib/dart/test/protocol/t_protocol_test.dart b/src/jaegertracing/thrift/lib/dart/test/protocol/t_protocol_test.dart
new file mode 100644
index 000000000..dc63dbb71
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/test/protocol/t_protocol_test.dart
@@ -0,0 +1,406 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+library thrift.test.transport.t_json_protocol_test;
+
+import 'dart:async';
+import 'dart:typed_data' show Uint8List;
+
+import 'package:dart2_constant/convert.dart' show utf8;
+import 'package:test/test.dart';
+import 'package:thrift/thrift.dart';
+
+void main() {
+ final message = new TMessage('my message', TMessageType.ONEWAY, 123);
+
+ TProtocol protocol;
+
+ Primitive getPrimitive(int tType) {
+ switch (tType) {
+ case TType.BOOL:
+ return new Primitive(protocol.readBool, protocol.writeBool, false);
+
+ case TType.BYTE:
+ return new Primitive(protocol.readByte, protocol.writeByte, 0);
+
+ case TType.I16:
+ return new Primitive(protocol.readI16, protocol.writeI16, 0);
+
+ case TType.I32:
+ return new Primitive(protocol.readI32, protocol.writeI32, 0);
+
+ case TType.I64:
+ return new Primitive(protocol.readI64, protocol.writeI64, 0);
+
+ case TType.DOUBLE:
+ return new Primitive(protocol.readDouble, protocol.writeDouble, 0);
+
+ case TType.STRING:
+ return new Primitive(protocol.readString, protocol.writeString, '');
+
+ default:
+ throw new UnsupportedError("Unsupported TType $tType");
+ }
+ }
+
+ Future primitiveTest(Primitive primitive, input) async {
+ primitive.write(input);
+ protocol.writeMessageEnd();
+
+ await protocol.transport.flush();
+
+ protocol.readMessageBegin();
+ var output = primitive.read();
+
+ expect(output, input);
+ }
+
+ Future primitiveNullTest(Primitive primitive) async {
+ primitive.write(null);
+ protocol.writeMessageEnd();
+
+ await protocol.transport.flush();
+
+ protocol.readMessageBegin();
+ var output = primitive.read();
+
+ expect(output, primitive.defaultValue);
+ }
+
+ var sharedTests = () {
+ test('Test message', () async {
+ protocol.writeMessageEnd();
+
+ await protocol.transport.flush();
+
+ var subject = protocol.readMessageBegin();
+
+ expect(subject.name, message.name);
+ expect(subject.type, message.type);
+ expect(subject.seqid, message.seqid);
+ });
+
+ test('Test struct', () async {
+ var input = new TStruct();
+
+ protocol.writeStructBegin(input);
+ protocol.writeStructEnd();
+ protocol.writeMessageEnd();
+
+ await protocol.transport.flush();
+
+ protocol.readMessageBegin();
+ var output = protocol.readStructBegin();
+
+ // name is not serialized, see C# version for reference
+ expect(output, isNotNull);
+ });
+
+ test('Test field', () async {
+ var input = new TField('my field', TType.MAP, 123);
+
+ protocol.writeFieldBegin(input);
+ protocol.writeFieldEnd();
+ protocol.writeMessageEnd();
+
+ await protocol.transport.flush();
+
+ protocol.readMessageBegin();
+ var output = protocol.readFieldBegin();
+
+ // name is not serialized, see C# version for reference
+ expect(output.type, input.type);
+ expect(output.id, input.id);
+ });
+
+ test('Test map', () async {
+ var input = new TMap(TType.STRING, TType.STRUCT, 123);
+
+ protocol.writeMapBegin(input);
+ protocol.writeMapEnd();
+ protocol.writeMessageEnd();
+
+ await protocol.transport.flush();
+
+ protocol.readMessageBegin();
+ var output = protocol.readMapBegin();
+
+ expect(output.keyType, input.keyType);
+ expect(output.valueType, input.valueType);
+ expect(output.length, input.length);
+ });
+
+ test('Test list', () async {
+ var input = new TList(TType.STRING, 123);
+
+ protocol.writeListBegin(input);
+ protocol.writeListEnd();
+ protocol.writeMessageEnd();
+
+ await protocol.transport.flush();
+
+ protocol.readMessageBegin();
+ var output = protocol.readListBegin();
+
+ expect(output.elementType, input.elementType);
+ expect(output.length, input.length);
+ });
+
+ test('Test set', () async {
+ var input = new TSet(TType.STRING, 123);
+
+ protocol.writeSetBegin(input);
+ protocol.writeSetEnd();
+ protocol.writeMessageEnd();
+
+ await protocol.transport.flush();
+
+ protocol.readMessageBegin();
+ var output = protocol.readListBegin();
+
+ expect(output.elementType, input.elementType);
+ expect(output.length, input.length);
+ });
+
+ test('Test bool', () async {
+ await primitiveTest(getPrimitive(TType.BOOL), true);
+ });
+
+ test('Test bool null', () async {
+ await primitiveNullTest(getPrimitive(TType.BOOL));
+ });
+
+ test('Test byte', () async {
+ await primitiveTest(getPrimitive(TType.BYTE), 64);
+ });
+
+ test('Test byte null', () async {
+ await primitiveNullTest(getPrimitive(TType.BYTE));
+ });
+
+ test('Test I16', () async {
+ await primitiveTest(getPrimitive(TType.I16), 32767);
+ });
+
+ test('Test I16 null', () async {
+ await primitiveNullTest(getPrimitive(TType.I16));
+ });
+
+ test('Test I32', () async {
+ await primitiveTest(getPrimitive(TType.I32), 2147483647);
+ });
+
+ test('Test I32 null', () async {
+ await primitiveNullTest(getPrimitive(TType.I32));
+ });
+
+ test('Test I64', () async {
+ await primitiveTest(getPrimitive(TType.I64), 9223372036854775807);
+ });
+
+ test('Test I64 null', () async {
+ await primitiveNullTest(getPrimitive(TType.I64));
+ });
+
+ test('Test double', () async {
+ await primitiveTest(getPrimitive(TType.DOUBLE), 3.1415926);
+ });
+
+ test('Test double null', () async {
+ await primitiveNullTest(getPrimitive(TType.DOUBLE));
+ });
+
+ test('Test string', () async {
+ var input = 'There are only two hard things in computer science: '
+ 'cache invalidation, naming things, and off-by-one errors.';
+ await primitiveTest(getPrimitive(TType.STRING), input);
+ });
+
+ test('Test string null', () async {
+ await primitiveNullTest(getPrimitive(TType.STRING));
+ });
+
+ test('Test binary', () async {
+ var input = new Uint8List.fromList(new List.filled(100, 123));
+
+ protocol.writeBinary(input);
+ protocol.writeMessageEnd();
+
+ await protocol.transport.flush();
+
+ protocol.readMessageBegin();
+ var output = protocol.readBinary();
+
+ expect(output.length, input.length);
+ expect(output.every((i) => i == 123), isTrue);
+ });
+
+ test('Test complex struct', () async {
+ // {1: {10: 20}, 2: {30: 40}}
+ protocol.writeStructBegin(new TStruct());
+ protocol.writeFieldBegin(new TField('success', TType.MAP, 0));
+ protocol.writeMapBegin(new TMap(TType.I32, TType.MAP, 2));
+
+ protocol.writeI32(1); // key
+ protocol.writeMapBegin(new TMap(TType.I32, TType.I32, 1));
+ protocol.writeI32(10); // key
+ protocol.writeI32(20); // value
+ protocol.writeMapEnd();
+
+ protocol.writeI32(2); // key
+ protocol.writeMapBegin(new TMap(TType.I32, TType.I32, 1));
+ protocol.writeI32(30); // key
+ protocol.writeI32(40); // value
+ protocol.writeMapEnd();
+
+ protocol.writeMapEnd();
+ protocol.writeFieldEnd();
+ protocol.writeFieldStop();
+ protocol.writeStructEnd();
+ protocol.writeMessageEnd();
+
+ await protocol.transport.flush();
+
+ protocol.readMessageBegin();
+ protocol.readStructBegin();
+ expect(protocol.readFieldBegin().type, TType.MAP);
+ expect(protocol.readMapBegin().length, 2);
+
+ expect(protocol.readI32(), 1); // key
+ expect(protocol.readMapBegin().length, 1);
+ expect(protocol.readI32(), 10); // key
+ expect(protocol.readI32(), 20); // value
+ protocol.readMapEnd();
+
+ expect(protocol.readI32(), 2); // key
+ expect(protocol.readMapBegin().length, 1);
+ expect(protocol.readI32(), 30); // key
+ expect(protocol.readI32(), 40); // value
+ protocol.readMapEnd();
+
+ protocol.readMapEnd();
+ protocol.readFieldEnd();
+ protocol.readStructEnd();
+ protocol.readMessageEnd();
+ });
+
+ test('Test nested maps and lists', () async {
+ // {1: [{10: 20}], 2: [{30: 40}]}
+ protocol.writeMapBegin(new TMap(TType.I32, TType.LIST, 2));
+
+ protocol.writeI32(1); // key
+ protocol.writeListBegin(new TList(TType.MAP, 1));
+ protocol.writeMapBegin(new TMap(TType.I32, TType.I32, 1));
+ protocol.writeI32(10); // key
+ protocol.writeI32(20); // value
+ protocol.writeMapEnd();
+ protocol.writeListEnd();
+
+ protocol.writeI32(2); // key
+ protocol.writeListBegin(new TList(TType.MAP, 1));
+ protocol.writeMapBegin(new TMap(TType.I32, TType.I32, 1));
+ protocol.writeI32(30); // key
+ protocol.writeI32(40); // value
+ protocol.writeMapEnd();
+ protocol.writeListEnd();
+
+ protocol.writeMapEnd();
+ protocol.writeMessageEnd();
+
+ await protocol.transport.flush();
+
+ protocol.readMessageBegin();
+ expect(protocol.readMapBegin().length, 2);
+
+ expect(protocol.readI32(), 1); // key
+ expect(protocol.readListBegin().length, 1);
+ expect(protocol.readMapBegin().length, 1);
+ expect(protocol.readI32(), 10); // key
+ expect(protocol.readI32(), 20); // value
+ protocol.readMapEnd();
+ protocol.readListEnd();
+
+ expect(protocol.readI32(), 2); // key
+ expect(protocol.readListBegin().length, 1);
+ expect(protocol.readMapBegin().length, 1);
+ expect(protocol.readI32(), 30); // key
+ expect(protocol.readI32(), 40); // value
+ protocol.readMapEnd();
+ protocol.readListEnd();
+
+ protocol.readMapEnd();
+ protocol.readMessageEnd();
+ });
+ };
+
+ group('JSON', () {
+ setUp(() {
+ protocol = new TJsonProtocol(new TBufferedTransport());
+ protocol.writeMessageBegin(message);
+ });
+
+ test('Test escaped unicode', () async {
+ /*
+ KOR_KAI
+ UTF-8: 0xE0 0xB8 0x81
+ UTF-16: 0x0E01
+ G clef:
+ UTF-8: 0xF0 0x9D 0x84 0x9E
+ UTF-16: 0xD834 0xDD1E
+ */
+ var buffer = utf8.encode(r'"\u0001\u0e01 \ud834\udd1e"');
+ var transport = new TBufferedTransport();
+ transport.writeAll(buffer);
+
+ var protocol = new TJsonProtocol(transport);
+
+ await protocol.transport.flush();
+
+ var subject = protocol.readString();
+ expect(subject,
+ utf8.decode([0x01, 0xE0, 0xB8, 0x81, 0x20, 0xF0, 0x9D, 0x84, 0x9E]));
+ });
+
+ group('shared tests', sharedTests);
+ });
+
+ group('binary', () {
+ setUp(() {
+ protocol = new TBinaryProtocol(new TBufferedTransport());
+ protocol.writeMessageBegin(message);
+ });
+
+ group('shared tests', sharedTests);
+ });
+
+ group('compact', () {
+ setUp(() {
+ protocol = new TCompactProtocol(new TBufferedTransport());
+ protocol.writeMessageBegin(message);
+ });
+
+ group('shared tests', sharedTests);
+ });
+}
+
+class Primitive {
+ final Function read;
+ final Function write;
+ final defaultValue;
+
+ Primitive(this.read, this.write, this.defaultValue);
+}
diff --git a/src/jaegertracing/thrift/lib/dart/test/serializer/serializer_test.dart b/src/jaegertracing/thrift/lib/dart/test/serializer/serializer_test.dart
new file mode 100644
index 000000000..2f76503c4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/test/serializer/serializer_test.dart
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+library thrift.test.serializer.serializer_test;
+
+import 'package:test/test.dart';
+import 'package:thrift/thrift.dart';
+import 'serializer_test_data.dart';
+
+void main() {
+ var serializer = () {
+ TDeserializer deserializer;
+ TSerializer serializer;
+ TestTObject testTObject;
+
+ setUp(() {
+ serializer = new TSerializer();
+ deserializer = new TDeserializer();
+
+ testTObject = new TestTObject();
+ testTObject.b = true;
+ testTObject.s = "TEST";
+ testTObject.d = 15.25;
+ testTObject.i = 10;
+
+ var testList = new List<String>();
+ testList.add("TEST 1");
+ testList.add("TEST 2");
+
+ testTObject.l = testList;
+ });
+
+ assertNewObjectEqualsTObject(TestTObject newObject) {
+ expect(newObject.l, equals(testTObject.l));
+ expect(newObject.b, equals(testTObject.b));
+ expect(newObject.i, equals(testTObject.i));
+ expect(newObject.d, equals(testTObject.d));
+ expect(newObject.s, equals(testTObject.s));
+ }
+
+ runWriteStringTest() {
+ var s = serializer.writeString(testTObject);
+
+ var newObject = new TestTObject();
+ deserializer.readString(newObject, s);
+
+ assertNewObjectEqualsTObject(newObject);
+ };
+
+ runWriteTest() {
+ var s = serializer.write(testTObject);
+
+ var newObject = new TestTObject();
+ deserializer.read(newObject, s);
+
+ assertNewObjectEqualsTObject(newObject);
+ };
+
+ test('JSON Protocol String', () {
+ serializer.protocol = new TJsonProtocol(serializer.transport);
+ deserializer.protocol = new TJsonProtocol(deserializer.transport);
+
+ runWriteStringTest();
+ });
+
+ test('JSON Protocol', () {
+ serializer.protocol = new TJsonProtocol(serializer.transport);
+ deserializer.protocol = new TJsonProtocol(deserializer.transport);
+
+ runWriteTest();
+ });
+
+ test('Binary Protocol String', () {
+ serializer.protocol = new TBinaryProtocol(serializer.transport);
+ deserializer.protocol = new TBinaryProtocol(deserializer.transport);
+
+ runWriteStringTest();
+ });
+
+ test('Binary Protocol', () {
+ serializer.protocol = new TBinaryProtocol(serializer.transport);
+ deserializer.protocol = new TBinaryProtocol(deserializer.transport);
+
+ runWriteTest();
+ });
+
+ test('Compact Protocol String', () {
+ serializer.protocol = new TCompactProtocol(serializer.transport);
+ deserializer.protocol = new TCompactProtocol(deserializer.transport);
+
+ runWriteStringTest();
+ });
+
+ test('Compact Protocol', () {
+ serializer.protocol = new TCompactProtocol(serializer.transport);
+ deserializer.protocol = new TCompactProtocol(deserializer.transport);
+
+ runWriteTest();
+ });
+ };
+
+ group('Serializer', serializer);
+}
diff --git a/src/jaegertracing/thrift/lib/dart/test/serializer/serializer_test_data.dart b/src/jaegertracing/thrift/lib/dart/test/serializer/serializer_test_data.dart
new file mode 100644
index 000000000..3586f08fc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/test/serializer/serializer_test_data.dart
@@ -0,0 +1,342 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+library thrift.test.serializer.serializer_test;
+
+import 'package:thrift/thrift.dart';
+
+/// TestTObject is a simple test struct
+class TestTObject implements TBase {
+ static final TStruct _STRUCT_DESC = new TStruct("TestTObject");
+ static final TField _I_FIELD_DESC = new TField("i", TType.I32, 1);
+ static final TField _D_FIELD_DESC = new TField("d", TType.DOUBLE, 2);
+ static final TField _S_FIELD_DESC = new TField("s", TType.STRING, 3);
+ static final TField _L_FIELD_DESC = new TField("l", TType.LIST, 4);
+ static final TField _B_FIELD_DESC = new TField("b", TType.BOOL, 5);
+
+ int _i;
+ static const int I = 1;
+ double _d;
+ static const int D = 2;
+ String _s;
+ static const int S = 3;
+ List<String> _l;
+ static const int L = 4;
+ bool _b;
+ static const int B = 5;
+
+ bool __isset_i = false;
+ bool __isset_d = false;
+ bool __isset_b = false;
+
+ TestTObject() {
+ }
+
+ // i
+ int get i => this._i;
+
+ set i(int i) {
+ this._i = i;
+ this.__isset_i = true;
+ }
+
+ bool isSetI() => this.__isset_i;
+
+ unsetI() {
+ this.__isset_i = false;
+ }
+
+ // d
+ double get d => this._d;
+
+ set d(double d) {
+ this._d = d;
+ this.__isset_d = true;
+ }
+
+ bool isSetD() => this.__isset_d;
+
+ unsetD() {
+ this.__isset_d = false;
+ }
+
+ // s
+ String get s => this._s;
+
+ set s(String s) {
+ this._s = s;
+ }
+
+ bool isSetS() => this.s != null;
+
+ unsetS() {
+ this.s = null;
+ }
+
+ // l
+ List<String> get l => this._l;
+
+ set l(List<String> l) {
+ this._l = l;
+ }
+
+ bool isSetL() => this.l != null;
+
+ unsetL() {
+ this.l = null;
+ }
+
+ // b
+ bool get b => this._b;
+
+ set b(bool b) {
+ this._b = b;
+ this.__isset_b = true;
+ }
+
+ bool isSetB() => this.__isset_b;
+
+ unsetB() {
+ this.__isset_b = false;
+ }
+
+ getFieldValue(int fieldID) {
+ switch (fieldID) {
+ case I:
+ return this.i;
+ case D:
+ return this.d;
+ case S:
+ return this.s;
+ case L:
+ return this.l;
+ case B:
+ return this.b;
+ default:
+ throw new ArgumentError("Field $fieldID doesn't exist!");
+ }
+ }
+
+ setFieldValue(int fieldID, Object value) {
+ switch (fieldID) {
+ case I:
+ if (value == null) {
+ unsetI();
+ } else {
+ this.i = value;
+ }
+ break;
+
+ case D:
+ if (value == null) {
+ unsetD();
+ } else {
+ this.d = value;
+ }
+ break;
+
+ case S:
+ if (value == null) {
+ unsetS();
+ } else {
+ this.s = value;
+ }
+ break;
+
+ case L:
+ if (value == null) {
+ unsetL();
+ } else {
+ this.l = value as List<String>;
+ }
+ break;
+
+ case B:
+ if (value == null) {
+ unsetB();
+ } else {
+ this.b = value;
+ }
+ break;
+
+ default:
+ throw new ArgumentError("Field $fieldID doesn't exist!");
+ }
+ }
+
+ // Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise
+ bool isSet(int fieldID) {
+ switch (fieldID) {
+ case I:
+ return isSetI();
+ case D:
+ return isSetD();
+ case S:
+ return isSetS();
+ case L:
+ return isSetL();
+ case B:
+ return isSetB();
+ default:
+ throw new ArgumentError("Field $fieldID doesn't exist!");
+ }
+ }
+
+ read(TProtocol iprot) {
+ TField field;
+ iprot.readStructBegin();
+ while (true) {
+ field = iprot.readFieldBegin();
+ if (field.type == TType.STOP) {
+ break;
+ }
+ switch (field.id) {
+ case I:
+ if (field.type == TType.I32) {
+ this.i = iprot.readI32();
+ this.__isset_i = true;
+ } else {
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ break;
+ case D:
+ if (field.type == TType.DOUBLE) {
+ this.d = iprot.readDouble();
+ this.__isset_d = true;
+ } else {
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ break;
+ case S:
+ if (field.type == TType.STRING) {
+ this.s = iprot.readString();
+ } else {
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ break;
+ case L:
+ if (field.type == TType.LIST) {
+ {
+ TList _list74 = iprot.readListBegin();
+ this.l = new List<String>();
+ for (int _i75 = 0; _i75 < _list74.length; ++_i75) {
+ String _elem76;
+ _elem76 = iprot.readString();
+ this.l.add(_elem76);
+ }
+ iprot.readListEnd();
+ }
+ } else {
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ break;
+ case B:
+ if (field.type == TType.BOOL) {
+ this.b = iprot.readBool();
+ this.__isset_b = true;
+ } else {
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ break;
+ default:
+ TProtocolUtil.skip(iprot, field.type);
+ break;
+ }
+ iprot.readFieldEnd();
+ }
+ iprot.readStructEnd();
+
+ // check for required fields of primitive type, which can't be checked in the validate method
+ validate();
+ }
+
+ write(TProtocol oprot) {
+ validate();
+
+ oprot.writeStructBegin(_STRUCT_DESC);
+ oprot.writeFieldBegin(_I_FIELD_DESC);
+ oprot.writeI32(this.i);
+ oprot.writeFieldEnd();
+ oprot.writeFieldBegin(_D_FIELD_DESC);
+ oprot.writeDouble(this.d);
+ oprot.writeFieldEnd();
+ if (this.s != null) {
+ oprot.writeFieldBegin(_S_FIELD_DESC);
+ oprot.writeString(this.s);
+ oprot.writeFieldEnd();
+ }
+ if (this.l != null) {
+ oprot.writeFieldBegin(_L_FIELD_DESC);
+ {
+ oprot.writeListBegin(new TList(TType.STRING, this.l.length));
+ for (var elem77 in this.l) {
+ oprot.writeString(elem77);
+ }
+ oprot.writeListEnd();
+ }
+ oprot.writeFieldEnd();
+ }
+ oprot.writeFieldBegin(_B_FIELD_DESC);
+ oprot.writeBool(this.b);
+ oprot.writeFieldEnd();
+ oprot.writeFieldStop();
+ oprot.writeStructEnd();
+ }
+
+ String toString() {
+ StringBuffer ret = new StringBuffer("TestTObject(");
+
+ ret.write("i:");
+ ret.write(this.i);
+
+ ret.write(", ");
+ ret.write("d:");
+ ret.write(this.d);
+
+ ret.write(", ");
+ ret.write("s:");
+ if (this.s == null) {
+ ret.write("null");
+ } else {
+ ret.write(this.s);
+ }
+
+ ret.write(", ");
+ ret.write("l:");
+ if (this.l == null) {
+ ret.write("null");
+ } else {
+ ret.write(this.l);
+ }
+
+ ret.write(", ");
+ ret.write("b:");
+ ret.write(this.b);
+
+ ret.write(")");
+
+ return ret.toString();
+ }
+
+ validate() {
+ // check for required fields
+ // check that fields of type enum have valid values
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/dart/test/t_application_error_test.dart b/src/jaegertracing/thrift/lib/dart/test/t_application_error_test.dart
new file mode 100644
index 000000000..511d8d691
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/test/t_application_error_test.dart
@@ -0,0 +1,46 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+library thrift.test.t_application_error_test;
+
+import 'package:test/test.dart';
+import 'package:thrift/thrift.dart';
+
+void main() {
+ TProtocol protocol;
+
+ setUp(() {
+ protocol = new TBinaryProtocol(new TBufferedTransport());
+ });
+
+ test('Write and read an application error', () {
+ var expectedType = TApplicationErrorType.INTERNAL_ERROR;
+ var expectedMessage = 'test error message';
+
+ TApplicationError error =
+ new TApplicationError(expectedType, expectedMessage);
+ error.write(protocol);
+
+ protocol.transport.flush();
+
+ TApplicationError subject = TApplicationError.read(protocol);
+
+ expect(subject, isNotNull);
+ expect(subject.type, expectedType);
+ expect(subject.message, expectedMessage);
+ });
+}
diff --git a/src/jaegertracing/thrift/lib/dart/test/transport/t_framed_transport_test.dart b/src/jaegertracing/thrift/lib/dart/test/transport/t_framed_transport_test.dart
new file mode 100644
index 000000000..7ab490539
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/test/transport/t_framed_transport_test.dart
@@ -0,0 +1,175 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+library thrift.test.transport.t_framed_transport_test;
+
+import 'dart:async';
+import 'dart:typed_data' show Uint8List;
+
+import 'package:dart2_constant/convert.dart' show utf8;
+import 'package:test/test.dart';
+import 'package:thrift/thrift.dart';
+
+void main() {
+ group('TFramedTransport partial reads', () {
+ final flushAwaitDuration = new Duration(seconds: 10);
+
+ FakeReadOnlySocket socket;
+ TSocketTransport socketTransport;
+ TFramedTransport transport;
+ var messageAvailable;
+
+ setUp(() {
+ socket = new FakeReadOnlySocket();
+ socketTransport = new TClientSocketTransport(socket);
+ transport = new TFramedTransport(socketTransport);
+ messageAvailable = false;
+ });
+
+ expectNoReadableBytes() {
+ var readBuffer = new Uint8List(128);
+ var readBytes = transport.read(readBuffer, 0, readBuffer.lengthInBytes);
+ expect(readBytes, 0);
+ expect(messageAvailable, false);
+ }
+
+ test('Test transport reads messages where header and body are sent separately', () async {
+ // buffer into which we'll read
+ var readBuffer = new Uint8List(10);
+ var readBytes;
+
+ // registers for readable bytes
+ var flushFuture = transport.flush().timeout(flushAwaitDuration);
+ flushFuture.then((_) {
+ messageAvailable = true;
+ });
+
+ // write header bytes
+ socket.messageController.add(new Uint8List.fromList([0x00, 0x00, 0x00, 0x06]));
+
+ // you shouldn't be able to get any bytes from the read,
+ // because the header has been consumed internally
+ expectNoReadableBytes();
+
+ // write first batch of body
+ socket.messageController.add(new Uint8List.fromList(utf8.encode("He")));
+
+ // you shouldn't be able to get any bytes from the read,
+ // because the frame has been consumed internally
+ expectNoReadableBytes();
+
+ // write second batch of body
+ socket.messageController.add(new Uint8List.fromList(utf8.encode("llo!")));
+
+ // have to wait for the flush to complete,
+ // because it's only then that the frame is available for reading
+ await flushFuture;
+ expect(messageAvailable, true);
+
+ // at this point the frame is complete, so we expect the read to complete
+ readBytes = transport.read(readBuffer, 0, readBuffer.lengthInBytes);
+ expect(readBytes, 6);
+ expect(readBuffer.sublist(0, 6), utf8.encode("Hello!"));
+ });
+
+ test('Test transport reads messages where header is sent in pieces '
+ 'and body is also sent in pieces', () async {
+ // buffer into which we'll read
+ var readBuffer = new Uint8List(10);
+ var readBytes;
+
+ // registers for readable bytes
+ var flushFuture = transport.flush().timeout(flushAwaitDuration);
+ flushFuture.then((_) {
+ messageAvailable = true;
+ });
+
+ // write first part of header bytes
+ socket.messageController.add(new Uint8List.fromList([0x00, 0x00]));
+
+ // you shouldn't be able to get any bytes from the read
+ expectNoReadableBytes();
+
+ // write second part of header bytes
+ socket.messageController.add(new Uint8List.fromList([0x00, 0x03]));
+
+ // you shouldn't be able to get any bytes from the read again
+ // because only the header was read, and there's no frame body
+ readBytes = expectNoReadableBytes();
+
+ // write first batch of body
+ socket.messageController.add(new Uint8List.fromList(utf8.encode("H")));
+
+ // you shouldn't be able to get any bytes from the read,
+ // because the frame has been consumed internally
+ expectNoReadableBytes();
+
+ // write second batch of body
+ socket.messageController.add(new Uint8List.fromList(utf8.encode("i!")));
+
+ // have to wait for the flush to complete,
+ // because it's only then that the frame is available for reading
+ await flushFuture;
+ expect(messageAvailable, true);
+
+ // at this point the frame is complete, so we expect the read to complete
+ readBytes = transport.read(readBuffer, 0, readBuffer.lengthInBytes);
+ expect(readBytes, 3);
+ expect(readBuffer.sublist(0, 3), utf8.encode("Hi!"));
+ });
+ });
+}
+
+
+
+class FakeReadOnlySocket extends TSocket {
+
+ StreamController<Uint8List> messageController = new StreamController<Uint8List>(sync: true);
+ StreamController<Object> errorController = new StreamController<Object>();
+ StreamController<TSocketState> stateController = new StreamController<TSocketState>();
+
+ @override
+ Future close() {
+ // noop
+ }
+
+ @override
+ bool get isClosed => false;
+
+ @override
+ bool get isOpen => true;
+
+ @override
+ Stream<Object> get onError => errorController.stream;
+
+ @override
+ Stream<Uint8List> get onMessage => messageController.stream;
+
+ @override
+ Stream<TSocketState> get onState => stateController.stream;
+
+ @override
+ Future open() {
+ // noop
+ }
+
+ @override
+ void send(Uint8List data) {
+ // noop
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/dart/test/transport/t_http_transport_test.dart b/src/jaegertracing/thrift/lib/dart/test/transport/t_http_transport_test.dart
new file mode 100644
index 000000000..03ccede9a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/test/transport/t_http_transport_test.dart
@@ -0,0 +1,164 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+library thrift.test.transport.t_socket_transport_test;
+
+import 'dart:async';
+import 'dart:convert' show Encoding;
+import 'dart:convert' show Utf8Codec;
+import 'dart:typed_data' show Uint8List;
+
+import 'package:dart2_constant/convert.dart' show base64;
+import 'package:http/http.dart' show BaseRequest;
+import 'package:http/http.dart' show Client;
+import 'package:http/http.dart' show Response;
+import 'package:http/http.dart' show StreamedResponse;
+import 'package:test/test.dart';
+import 'package:thrift/thrift.dart';
+
+void main() {
+ const utf8Codec = const Utf8Codec();
+
+ group('THttpClientTransport', () {
+ FakeHttpClient client;
+ THttpClientTransport transport;
+
+ setUp(() {
+ client = new FakeHttpClient(sync: false);
+ var config = new THttpConfig(Uri.parse('http://localhost'), {});
+ transport = new THttpClientTransport(client, config);
+ });
+
+ test('Test transport sends body', () async {
+ var expectedText = 'my request';
+ transport.writeAll(utf8Codec.encode(expectedText));
+
+ expect(client.postRequest, isEmpty);
+
+ await transport.flush();
+
+ expect(client.postRequest, isNotEmpty);
+
+ var requestText = utf8Codec.decode(base64.decode(client.postRequest));
+ expect(requestText, expectedText);
+ });
+
+ test('Test transport receives response', () async {
+ var expectedText = 'my response';
+ var expectedBytes = utf8Codec.encode(expectedText);
+ client.postResponse = base64.encode(expectedBytes);
+
+ transport.writeAll(utf8Codec.encode('my request'));
+ expect(transport.hasReadData, isFalse);
+
+ await transport.flush();
+
+ expect(transport.hasReadData, isTrue);
+
+ var buffer = new Uint8List(expectedBytes.length);
+ transport.readAll(buffer, 0, expectedBytes.length);
+
+ var bufferText = utf8Codec.decode(buffer);
+ expect(bufferText, expectedText);
+ });
+ });
+
+ group('THttpClientTransport with multiple messages', () {
+ FakeHttpClient client;
+ THttpClientTransport transport;
+
+ setUp(() {
+ client = new FakeHttpClient(sync: true);
+ var config = new THttpConfig(Uri.parse('http://localhost'), {});
+ transport = new THttpClientTransport(client, config);
+ });
+
+ test('Test read correct buffer after flush', () async {
+ String bufferText;
+ var expectedText = 'response 1';
+ var expectedBytes = utf8Codec.encode(expectedText);
+
+ // prepare a response
+ transport.writeAll(utf8Codec.encode('request 1'));
+ client.postResponse = base64.encode(expectedBytes);
+
+ Future responseReady = transport.flush().then((_) {
+ var buffer = new Uint8List(expectedBytes.length);
+ transport.readAll(buffer, 0, expectedBytes.length);
+ bufferText = utf8Codec.decode(buffer);
+ });
+
+ // prepare a second response
+ transport.writeAll(utf8Codec.encode('request 2'));
+ var response2Bytes = utf8Codec.encode('response 2');
+ client.postResponse = base64.encode(response2Bytes);
+ await transport.flush();
+
+ await responseReady;
+ expect(bufferText, expectedText);
+ });
+ });
+}
+
+class FakeHttpClient implements Client {
+ String postResponse = '';
+ String postRequest = '';
+
+ final bool sync;
+
+ FakeHttpClient({this.sync: false});
+
+ Future<Response> post(url,
+ {Map<String, String> headers, body, Encoding encoding}) {
+ postRequest = body;
+ var response = new Response(postResponse, 200);
+
+ if (sync) {
+ return new Future.sync(() => response);
+ } else {
+ return new Future.value(response);
+ }
+ }
+
+ Future<Response> head(url, {Map<String, String> headers}) =>
+ throw new UnimplementedError();
+
+ Future<Response> get(url, {Map<String, String> headers}) =>
+ throw new UnimplementedError();
+
+ Future<Response> put(url,
+ {Map<String, String> headers, body, Encoding encoding}) =>
+ throw new UnimplementedError();
+
+ Future<Response> patch(url,
+ {Map<String, String> headers, body, Encoding encoding}) =>
+ throw new UnimplementedError();
+
+ Future<Response> delete(url, {Map<String, String> headers}) =>
+ throw new UnimplementedError();
+
+ Future<String> read(url, {Map<String, String> headers}) =>
+ throw new UnimplementedError();
+
+ Future<Uint8List> readBytes(url, {Map<String, String> headers}) =>
+ throw new UnimplementedError();
+
+ Future<StreamedResponse> send(BaseRequest request) =>
+ throw new UnimplementedError();
+
+ void close() => throw new UnimplementedError();
+}
diff --git a/src/jaegertracing/thrift/lib/dart/test/transport/t_socket_transport_test.dart b/src/jaegertracing/thrift/lib/dart/test/transport/t_socket_transport_test.dart
new file mode 100644
index 000000000..90bffbe54
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/test/transport/t_socket_transport_test.dart
@@ -0,0 +1,311 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+library thrift.test.transport.t_socket_transport_test;
+
+import 'dart:async';
+import 'dart:convert' show Utf8Codec;
+import 'dart:typed_data' show Uint8List;
+
+import 'package:dart2_constant/convert.dart' show base64;
+import 'package:dart2_constant/core.dart' as core;
+import 'package:mockito/mockito.dart';
+import 'package:test/test.dart';
+import 'package:thrift/thrift.dart';
+
+void main() {
+ const utf8Codec = const Utf8Codec();
+
+ final requestText = 'my test request';
+ final requestBytes = new Uint8List.fromList(utf8Codec.encode(requestText));
+ final requestBase64 = base64.encode(requestBytes);
+
+ final responseText = 'response 1';
+ final responseBytes = new Uint8List.fromList(utf8Codec.encode(responseText));
+ final responseBase64 = base64.encode(responseBytes);
+
+ final framedResponseBase64 = base64.encode(_getFramedResponse(responseBytes));
+
+ group('TClientSocketTransport', () {
+ FakeSocket socket;
+ TTransport transport;
+
+ setUp(() async {
+ socket = new FakeSocket(sync: false);
+ await socket.open();
+ transport = new TClientSocketTransport(socket);
+ await transport.open();
+ transport.writeAll(requestBytes);
+ });
+
+ test('Test client sending data over transport', () async {
+ expect(socket.sendPayload, isNull);
+
+ Future responseReady = transport.flush();
+
+ // allow microtask events to finish
+ await new Future.value();
+
+ expect(socket.sendPayload, isNotNull);
+ expect(socket.sendPayload, requestBytes);
+
+ // simulate a response
+ socket.receiveFakeMessage(responseBase64);
+
+ await responseReady;
+ var buffer = new Uint8List(responseBytes.length);
+ transport.readAll(buffer, 0, responseBytes.length);
+ var bufferText = utf8Codec.decode(buffer);
+
+ expect(bufferText, responseText);
+ });
+ }, timeout: new Timeout(new Duration(seconds: 1)));
+
+ group('TClientSocketTransport with FramedTransport', () {
+ FakeSocket socket;
+ TTransport transport;
+
+ setUp(() async {
+ socket = new FakeSocket(sync: true);
+ await socket.open();
+
+ transport = new TFramedTransport(new TClientSocketTransport(socket));
+ await transport.open();
+ transport.writeAll(requestBytes);
+ });
+
+ test('Test client sending data over framed transport', () async {
+ String bufferText;
+
+ Future responseReady = transport.flush().then((_) {
+ var buffer = new Uint8List(responseBytes.length);
+ transport.readAll(buffer, 0, responseBytes.length);
+ bufferText = utf8Codec.decode(buffer);
+ });
+
+ // simulate a response
+ socket.receiveFakeMessage(framedResponseBase64);
+
+ await responseReady;
+ expect(bufferText, responseText);
+ });
+ }, timeout: new Timeout(new Duration(seconds: 1)));
+
+ group('TAsyncClientSocketTransport', () {
+ FakeSocket socket;
+ FakeProtocolFactory protocolFactory;
+ TTransport transport;
+
+ setUp(() async {
+ socket = new FakeSocket(sync: true);
+ await socket.open();
+
+ protocolFactory = new FakeProtocolFactory();
+ protocolFactory.message = new TMessage('foo', TMessageType.CALL, 123);
+ transport = new TAsyncClientSocketTransport(
+ socket, new TMessageReader(protocolFactory),
+ responseTimeout: core.Duration.zero);
+ await transport.open();
+ transport.writeAll(requestBytes);
+ });
+
+ test('Test response correlates to correct request', () async {
+ String bufferText;
+
+ Future responseReady = transport.flush().then((_) {
+ var buffer = new Uint8List(responseBytes.length);
+ transport.readAll(buffer, 0, responseBytes.length);
+ bufferText = utf8Codec.decode(buffer);
+ });
+
+ // simulate a response
+ protocolFactory.message = new TMessage('foo', TMessageType.REPLY, 123);
+ socket.receiveFakeMessage(responseBase64);
+
+ // simulate a second response
+ var response2Text = 'response 2';
+ var response2Bytes =
+ new Uint8List.fromList(utf8Codec.encode(response2Text));
+ var response2Base64 = base64.encode(response2Bytes);
+ protocolFactory.message = new TMessage('foo2', TMessageType.REPLY, 124);
+ socket.receiveFakeMessage(response2Base64);
+
+ await responseReady;
+ expect(bufferText, responseText);
+ });
+
+ test('Test response timeout', () async {
+ Future responseReady = transport.flush();
+ expect(responseReady, throwsA(new isInstanceOf<TimeoutException>()));
+ });
+ }, timeout: new Timeout(new Duration(seconds: 1)));
+
+ group('TAsyncClientSocketTransport with TFramedTransport', () {
+ FakeSocket socket;
+ FakeProtocolFactory protocolFactory;
+ TTransport transport;
+
+ setUp(() async {
+ socket = new FakeSocket(sync: true);
+ await socket.open();
+
+ protocolFactory = new FakeProtocolFactory();
+ protocolFactory.message = new TMessage('foo', TMessageType.CALL, 123);
+ var messageReader = new TMessageReader(protocolFactory,
+ byteOffset: TFramedTransport.headerByteCount);
+
+ transport = new TFramedTransport(new TAsyncClientSocketTransport(
+ socket, messageReader,
+ responseTimeout: core.Duration.zero));
+ await transport.open();
+ transport.writeAll(requestBytes);
+ });
+
+ test('Test async client sending data over framed transport', () async {
+ String bufferText;
+
+ Future responseReady = transport.flush().then((_) {
+ var buffer = new Uint8List(responseBytes.length);
+ transport.readAll(buffer, 0, responseBytes.length);
+ bufferText = utf8Codec.decode(buffer);
+ });
+
+ // simulate a response
+ protocolFactory.message = new TMessage('foo', TMessageType.REPLY, 123);
+ socket.receiveFakeMessage(framedResponseBase64);
+
+ await responseReady;
+ expect(bufferText, responseText);
+ });
+ }, timeout: new Timeout(new Duration(seconds: 1)));
+
+ group('TServerTransport', () {
+ test('Test server transport listens to socket', () async {
+ var socket = new FakeSocket();
+ await socket.open();
+ expect(socket.isOpen, isTrue);
+
+ var transport = new TServerSocketTransport(socket);
+ expect(transport.hasReadData, isFalse);
+
+ socket.receiveFakeMessage(requestBase64);
+
+ // allow microtask events to finish
+ await new Future.value();
+
+ expect(transport.hasReadData, isTrue);
+
+ var buffer = new Uint8List(requestBytes.length);
+ transport.readAll(buffer, 0, requestBytes.length);
+
+ var bufferText = utf8Codec.decode(buffer);
+ expect(bufferText, requestText);
+ });
+
+ test('Test server sending data over transport', () async {
+ var socket = new FakeSocket();
+ await socket.open();
+
+ var transport = new TServerSocketTransport(socket);
+
+ transport.writeAll(responseBytes);
+ expect(socket.sendPayload, isNull);
+
+ transport.flush();
+
+ // allow microtask events to finish
+ await new Future.value();
+
+ expect(socket.sendPayload, isNotNull);
+ expect(socket.sendPayload, responseBytes);
+ });
+ }, timeout: new Timeout(new Duration(seconds: 1)));
+}
+
+class FakeSocket extends TSocket {
+ final StreamController<TSocketState> _onStateController;
+ Stream<TSocketState> get onState => _onStateController.stream;
+
+ final StreamController<Object> _onErrorController;
+ Stream<Object> get onError => _onErrorController.stream;
+
+ final StreamController<Uint8List> _onMessageController;
+ Stream<Uint8List> get onMessage => _onMessageController.stream;
+
+ FakeSocket({bool sync: false})
+ : _onStateController = new StreamController.broadcast(sync: sync),
+ _onErrorController = new StreamController.broadcast(sync: sync),
+ _onMessageController = new StreamController.broadcast(sync: sync);
+
+ bool _isOpen;
+
+ bool get isOpen => _isOpen;
+
+ bool get isClosed => !isOpen;
+
+ Future open() async {
+ _isOpen = true;
+ _onStateController.add(TSocketState.OPEN);
+ }
+
+ Future close() async {
+ _isOpen = false;
+ _onStateController.add(TSocketState.CLOSED);
+ }
+
+ Uint8List _sendPayload;
+ Uint8List get sendPayload => _sendPayload;
+
+ void send(Uint8List data) {
+ if (!isOpen) throw new StateError('The socket is not open');
+
+ _sendPayload = data;
+ }
+
+ void receiveFakeMessage(String base64text) {
+ if (!isOpen) throw new StateError('The socket is not open');
+
+ var message = new Uint8List.fromList(base64.decode(base64text));
+ _onMessageController.add(message);
+ }
+}
+
+class FakeProtocolFactory implements TProtocolFactory {
+ FakeProtocolFactory();
+
+ TMessage message;
+
+ getProtocol(TTransport transport) => new FakeProtocol(message);
+}
+
+class FakeProtocol extends Mock implements TProtocol {
+ FakeProtocol(this._message);
+
+ TMessage _message;
+
+ readMessageBegin() => _message;
+}
+
+Uint8List _getFramedResponse(Uint8List responseBytes) {
+ var byteOffset = TFramedTransport.headerByteCount;
+ var response = new Uint8List(byteOffset + responseBytes.length);
+
+ response.buffer.asByteData().setInt32(0, responseBytes.length);
+ response.setAll(byteOffset, responseBytes);
+
+ return response;
+}
diff --git a/src/jaegertracing/thrift/lib/dart/test/transport/t_transport_test.dart b/src/jaegertracing/thrift/lib/dart/test/transport/t_transport_test.dart
new file mode 100644
index 000000000..0bb381ac8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/test/transport/t_transport_test.dart
@@ -0,0 +1,41 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+library thrift.test.transport.t_socket_transport_test;
+
+import 'package:test/test.dart';
+import 'package:thrift/thrift.dart';
+
+/// Common transport tests
+void main() {
+ group('TTransportFactory', () {
+ test('transport is returned from base factory', () async {
+ TTransport result;
+ TTransport transport = null;
+
+ var factory = new TTransportFactory();
+
+ result = await factory.getTransport(transport);
+ expect(result, isNull);
+
+ transport = new TBufferedTransport();
+ result = await factory.getTransport(transport);
+
+ expect(result, transport);
+ });
+ });
+}
diff --git a/src/jaegertracing/thrift/lib/dart/tool/dev.dart b/src/jaegertracing/thrift/lib/dart/tool/dev.dart
new file mode 100644
index 000000000..27f8b8fcf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/dart/tool/dev.dart
@@ -0,0 +1,33 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+library tool.dev;
+
+import 'package:dart_dev/dart_dev.dart' show dev, config;
+
+main(List<String> args) async {
+ // https://github.com/Workiva/dart_dev
+
+ var directories = ['lib/', 'test/', 'tool/'];
+ config.analyze.entryPoints = directories;
+ config.format.directories = directories;
+ config.copyLicense
+ ..licensePath = 'LICENSE_HEADER'
+ ..directories = directories;
+
+ await dev(args);
+}
diff --git a/src/jaegertracing/thrift/lib/delphi/DelphiThrift.groupproj b/src/jaegertracing/thrift/lib/delphi/DelphiThrift.groupproj
new file mode 100644
index 000000000..a172e496c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/DelphiThrift.groupproj
@@ -0,0 +1,156 @@
+ <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <ProjectGuid>{6BD327A5-7688-4263-B6A8-B15207CF4EC5}</ProjectGuid>
+ </PropertyGroup>
+ <ItemGroup>
+ <Projects Include="test\client.dproj">
+ <Dependencies/>
+ </Projects>
+ <Projects Include="test\server.dproj">
+ <Dependencies/>
+ </Projects>
+ <Projects Include="test\multiplexed\Multiplex.Test.Client.dproj">
+ <Dependencies/>
+ </Projects>
+ <Projects Include="test\multiplexed\Multiplex.Test.Server.dproj">
+ <Dependencies/>
+ </Projects>
+ <Projects Include="test\serializer\TestSerializer.dproj">
+ <Dependencies/>
+ </Projects>
+ <Projects Include="test\skip\skiptest_version1.dproj">
+ <Dependencies/>
+ </Projects>
+ <Projects Include="test\skip\skiptest_version2.dproj">
+ <Dependencies/>
+ </Projects>
+ <Projects Include="test\typeregistry\TestTypeRegistry.dproj">
+ <Dependencies/>
+ </Projects>
+ <Projects Include="..\..\tutorial\delphi\DelphiServer\DelphiServer.dproj">
+ <Dependencies/>
+ </Projects>
+ <Projects Include="..\..\tutorial\delphi\DelphiClient\DelphiClient.dproj">
+ <Dependencies/>
+ </Projects>
+ <Projects Include="test\keywords\ReservedKeywords.dproj">
+ <Dependencies/>
+ </Projects>
+ </ItemGroup>
+ <ProjectExtensions>
+ <Borland.Personality>Default.Personality.12</Borland.Personality>
+ <Borland.ProjectType/>
+ <BorlandProject>
+ <Default.Personality/>
+ </BorlandProject>
+ </ProjectExtensions>
+ <Target Name="client">
+ <MSBuild Projects="test\client.dproj"/>
+ </Target>
+ <Target Name="client:Clean">
+ <MSBuild Projects="test\client.dproj" Targets="Clean"/>
+ </Target>
+ <Target Name="client:Make">
+ <MSBuild Projects="test\client.dproj" Targets="Make"/>
+ </Target>
+ <Target Name="server">
+ <MSBuild Projects="test\server.dproj"/>
+ </Target>
+ <Target Name="server:Clean">
+ <MSBuild Projects="test\server.dproj" Targets="Clean"/>
+ </Target>
+ <Target Name="server:Make">
+ <MSBuild Projects="test\server.dproj" Targets="Make"/>
+ </Target>
+ <Target Name="Multiplex_Test_Client">
+ <MSBuild Projects="test\multiplexed\Multiplex.Test.Client.dproj"/>
+ </Target>
+ <Target Name="Multiplex_Test_Client:Clean">
+ <MSBuild Projects="test\multiplexed\Multiplex.Test.Client.dproj" Targets="Clean"/>
+ </Target>
+ <Target Name="Multiplex_Test_Client:Make">
+ <MSBuild Projects="test\multiplexed\Multiplex.Test.Client.dproj" Targets="Make"/>
+ </Target>
+ <Target Name="Multiplex_Test_Server">
+ <MSBuild Projects="test\multiplexed\Multiplex.Test.Server.dproj"/>
+ </Target>
+ <Target Name="Multiplex_Test_Server:Clean">
+ <MSBuild Projects="test\multiplexed\Multiplex.Test.Server.dproj" Targets="Clean"/>
+ </Target>
+ <Target Name="Multiplex_Test_Server:Make">
+ <MSBuild Projects="test\multiplexed\Multiplex.Test.Server.dproj" Targets="Make"/>
+ </Target>
+ <Target Name="TestSerializer">
+ <MSBuild Projects="test\serializer\TestSerializer.dproj"/>
+ </Target>
+ <Target Name="TestSerializer:Clean">
+ <MSBuild Projects="test\serializer\TestSerializer.dproj" Targets="Clean"/>
+ </Target>
+ <Target Name="TestSerializer:Make">
+ <MSBuild Projects="test\serializer\TestSerializer.dproj" Targets="Make"/>
+ </Target>
+ <Target Name="skiptest_version1">
+ <MSBuild Projects="test\skip\skiptest_version1.dproj"/>
+ </Target>
+ <Target Name="skiptest_version1:Clean">
+ <MSBuild Projects="test\skip\skiptest_version1.dproj" Targets="Clean"/>
+ </Target>
+ <Target Name="skiptest_version1:Make">
+ <MSBuild Projects="test\skip\skiptest_version1.dproj" Targets="Make"/>
+ </Target>
+ <Target Name="skiptest_version2">
+ <MSBuild Projects="test\skip\skiptest_version2.dproj"/>
+ </Target>
+ <Target Name="skiptest_version2:Clean">
+ <MSBuild Projects="test\skip\skiptest_version2.dproj" Targets="Clean"/>
+ </Target>
+ <Target Name="skiptest_version2:Make">
+ <MSBuild Projects="test\skip\skiptest_version2.dproj" Targets="Make"/>
+ </Target>
+ <Target Name="TestTypeRegistry">
+ <MSBuild Projects="test\typeregistry\TestTypeRegistry.dproj"/>
+ </Target>
+ <Target Name="TestTypeRegistry:Clean">
+ <MSBuild Projects="test\typeregistry\TestTypeRegistry.dproj" Targets="Clean"/>
+ </Target>
+ <Target Name="TestTypeRegistry:Make">
+ <MSBuild Projects="test\typeregistry\TestTypeRegistry.dproj" Targets="Make"/>
+ </Target>
+ <Target Name="DelphiServer">
+ <MSBuild Projects="..\..\tutorial\delphi\DelphiServer\DelphiServer.dproj"/>
+ </Target>
+ <Target Name="DelphiServer:Clean">
+ <MSBuild Projects="..\..\tutorial\delphi\DelphiServer\DelphiServer.dproj" Targets="Clean"/>
+ </Target>
+ <Target Name="DelphiServer:Make">
+ <MSBuild Projects="..\..\tutorial\delphi\DelphiServer\DelphiServer.dproj" Targets="Make"/>
+ </Target>
+ <Target Name="DelphiClient">
+ <MSBuild Projects="..\..\tutorial\delphi\DelphiClient\DelphiClient.dproj"/>
+ </Target>
+ <Target Name="DelphiClient:Clean">
+ <MSBuild Projects="..\..\tutorial\delphi\DelphiClient\DelphiClient.dproj" Targets="Clean"/>
+ </Target>
+ <Target Name="DelphiClient:Make">
+ <MSBuild Projects="..\..\tutorial\delphi\DelphiClient\DelphiClient.dproj" Targets="Make"/>
+ </Target>
+ <Target Name="ReservedKeywords">
+ <MSBuild Projects="test\keywords\ReservedKeywords.dproj"/>
+ </Target>
+ <Target Name="ReservedKeywords:Clean">
+ <MSBuild Projects="test\keywords\ReservedKeywords.dproj" Targets="Clean"/>
+ </Target>
+ <Target Name="ReservedKeywords:Make">
+ <MSBuild Projects="test\keywords\ReservedKeywords.dproj" Targets="Make"/>
+ </Target>
+ <Target Name="Build">
+ <CallTarget Targets="client;server;Multiplex_Test_Client;Multiplex_Test_Server;TestSerializer;skiptest_version1;skiptest_version2;TestTypeRegistry;DelphiServer;DelphiClient;ReservedKeywords"/>
+ </Target>
+ <Target Name="Clean">
+ <CallTarget Targets="client:Clean;server:Clean;Multiplex_Test_Client:Clean;Multiplex_Test_Server:Clean;TestSerializer:Clean;skiptest_version1:Clean;skiptest_version2:Clean;TestTypeRegistry:Clean;DelphiServer:Clean;DelphiClient:Clean;ReservedKeywords:Clean"/>
+ </Target>
+ <Target Name="Make">
+ <CallTarget Targets="client:Make;server:Make;Multiplex_Test_Client:Make;Multiplex_Test_Server:Make;TestSerializer:Make;skiptest_version1:Make;skiptest_version2:Make;TestTypeRegistry:Make;DelphiServer:Make;DelphiClient:Make;ReservedKeywords:Make"/>
+ </Target>
+ <Import Condition="Exists('$(BDS)\Bin\CodeGear.Group.Targets')" Project="$(BDS)\Bin\CodeGear.Group.Targets"/>
+ </Project>
diff --git a/src/jaegertracing/thrift/lib/delphi/README.md b/src/jaegertracing/thrift/lib/delphi/README.md
new file mode 100644
index 000000000..91799d04d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/README.md
@@ -0,0 +1,30 @@
+Thrift Delphi Software Library
+
+License
+=======
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+Using Thrift with Delphi
+====================
+
+The Thrift Delphi Library requires at least Delphi 2010.
+
+Because the Library heavily relies on generics, using it
+with earlier versions (such as Delphi 7) will *not* work.
+
diff --git a/src/jaegertracing/thrift/lib/delphi/coding_standards.md b/src/jaegertracing/thrift/lib/delphi/coding_standards.md
new file mode 100644
index 000000000..fa0390bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/coding_standards.md
@@ -0,0 +1 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Collections.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Collections.pas
new file mode 100644
index 000000000..3b56fe205
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Collections.pas
@@ -0,0 +1,692 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+unit Thrift.Collections;
+
+interface
+
+uses
+ SysUtils, Generics.Collections, Generics.Defaults, Thrift.Utils;
+
+type
+
+{$IF CompilerVersion < 21.0}
+ TArray<T> = array of T;
+{$IFEND}
+
+ IThriftContainer = interface( ISupportsToString)
+ ['{E05C0F9D-A4F5-491D-AADA-C926B4BDB6E4}']
+ end;
+
+
+ IThriftDictionary<TKey,TValue> = interface(IThriftContainer)
+ ['{25EDD506-F9D1-4008-A40F-5940364B7E46}']
+ function GetEnumerator: TEnumerator<TPair<TKey,TValue>>;
+
+ function GetKeys: TDictionary<TKey,TValue>.TKeyCollection;
+ function GetValues: TDictionary<TKey,TValue>.TValueCollection;
+ function GetItem(const Key: TKey): TValue;
+ procedure SetItem(const Key: TKey; const Value: TValue);
+ function GetCount: Integer;
+
+ procedure Add(const Key: TKey; const Value: TValue);
+ procedure Remove(const Key: TKey);
+{$IF CompilerVersion >= 21.0}
+ function ExtractPair(const Key: TKey): TPair<TKey,TValue>;
+{$IFEND}
+ procedure Clear;
+ procedure TrimExcess;
+ function TryGetValue(const Key: TKey; out Value: TValue): Boolean;
+ procedure AddOrSetValue(const Key: TKey; const Value: TValue);
+ function ContainsKey(const Key: TKey): Boolean;
+ function ContainsValue(const Value: TValue): Boolean;
+ function ToArray: TArray<TPair<TKey,TValue>>;
+
+ property Items[const Key: TKey]: TValue read GetItem write SetItem; default;
+ property Count: Integer read GetCount;
+ property Keys: TDictionary<TKey,TValue>.TKeyCollection read GetKeys;
+ property Values: TDictionary<TKey,TValue>.TValueCollection read GetValues;
+ end;
+
+ TThriftDictionaryImpl<TKey,TValue> = class( TInterfacedObject, IThriftDictionary<TKey,TValue>, IThriftContainer, ISupportsToString)
+ private
+ FDictionaly : TDictionary<TKey,TValue>;
+ protected
+ function GetEnumerator: TEnumerator<TPair<TKey,TValue>>;
+
+ function GetKeys: TDictionary<TKey,TValue>.TKeyCollection;
+ function GetValues: TDictionary<TKey,TValue>.TValueCollection;
+ function GetItem(const Key: TKey): TValue;
+ procedure SetItem(const Key: TKey; const Value: TValue);
+ function GetCount: Integer;
+
+ procedure Add(const Key: TKey; const Value: TValue);
+ procedure Remove(const Key: TKey);
+{$IF CompilerVersion >= 21.0}
+ function ExtractPair(const Key: TKey): TPair<TKey,TValue>;
+{$IFEND}
+ procedure Clear;
+ procedure TrimExcess;
+ function TryGetValue(const Key: TKey; out Value: TValue): Boolean;
+ procedure AddOrSetValue(const Key: TKey; const Value: TValue);
+ function ContainsKey(const Key: TKey): Boolean;
+ function ContainsValue(const Value: TValue): Boolean;
+ function ToArray: TArray<TPair<TKey,TValue>>;
+ property Items[const Key: TKey]: TValue read GetItem write SetItem; default;
+ property Count: Integer read GetCount;
+ property Keys: TDictionary<TKey,TValue>.TKeyCollection read GetKeys;
+ property Values: TDictionary<TKey,TValue>.TValueCollection read GetValues;
+ public
+ constructor Create(ACapacity: Integer = 0);
+ destructor Destroy; override;
+ function ToString : string; override;
+ end;
+
+ IThriftList<T> = interface(IThriftContainer)
+ ['{29BEEE31-9CB4-401B-AA04-5148A75F473B}']
+ function GetEnumerator: TEnumerator<T>;
+ function GetCapacity: Integer;
+ procedure SetCapacity(Value: Integer);
+ function GetCount: Integer;
+ procedure SetCount(Value: Integer);
+ function GetItem(Index: Integer): T;
+ procedure SetItem(Index: Integer; const Value: T);
+ function Add(const Value: T): Integer;
+ procedure AddRange(const Values: array of T); overload;
+ procedure AddRange(const Collection: IEnumerable<T>); overload;
+ procedure AddRange(Collection: TEnumerable<T>); overload;
+ procedure Insert(Index: Integer; const Value: T);
+ procedure InsertRange(Index: Integer; const Values: array of T); overload;
+ procedure InsertRange(Index: Integer; const Collection: IEnumerable<T>); overload;
+ procedure InsertRange(Index: Integer; const Collection: TEnumerable<T>); overload;
+ function Remove(const Value: T): Integer;
+ procedure Delete(Index: Integer);
+ procedure DeleteRange(AIndex, ACount: Integer);
+ function Extract(const Value: T): T;
+{$IF CompilerVersion >= 21.0}
+ procedure Exchange(Index1, Index2: Integer);
+ procedure Move(CurIndex, NewIndex: Integer);
+ function First: T;
+ function Last: T;
+{$IFEND}
+ procedure Clear;
+ function Contains(const Value: T): Boolean;
+ function IndexOf(const Value: T): Integer;
+ function LastIndexOf(const Value: T): Integer;
+ procedure Reverse;
+ procedure Sort; overload;
+ procedure Sort(const AComparer: IComparer<T>); overload;
+ function BinarySearch(const Item: T; out Index: Integer): Boolean; overload;
+ function BinarySearch(const Item: T; out Index: Integer; const AComparer: IComparer<T>): Boolean; overload;
+ procedure TrimExcess;
+ function ToArray: TArray<T>;
+ property Capacity: Integer read GetCapacity write SetCapacity;
+ property Count: Integer read GetCount write SetCount;
+ property Items[Index: Integer]: T read GetItem write SetItem; default;
+ end;
+
+ TThriftListImpl<T> = class( TInterfacedObject, IThriftList<T>, IThriftContainer, ISupportsToString)
+ private
+ FList : TList<T>;
+ protected
+ function GetEnumerator: TEnumerator<T>;
+ function GetCapacity: Integer;
+ procedure SetCapacity(Value: Integer);
+ function GetCount: Integer;
+ procedure SetCount(Value: Integer);
+ function GetItem(Index: Integer): T;
+ procedure SetItem(Index: Integer; const Value: T);
+ function Add(const Value: T): Integer;
+ procedure AddRange(const Values: array of T); overload;
+ procedure AddRange(const Collection: IEnumerable<T>); overload;
+ procedure AddRange(Collection: TEnumerable<T>); overload;
+ procedure Insert(Index: Integer; const Value: T);
+ procedure InsertRange(Index: Integer; const Values: array of T); overload;
+ procedure InsertRange(Index: Integer; const Collection: IEnumerable<T>); overload;
+ procedure InsertRange(Index: Integer; const Collection: TEnumerable<T>); overload;
+ function Remove(const Value: T): Integer;
+ procedure Delete(Index: Integer);
+ procedure DeleteRange(AIndex, ACount: Integer);
+ function Extract(const Value: T): T;
+{$IF CompilerVersion >= 21.0}
+ procedure Exchange(Index1, Index2: Integer);
+ procedure Move(CurIndex, NewIndex: Integer);
+ function First: T;
+ function Last: T;
+{$IFEND}
+ procedure Clear;
+ function Contains(const Value: T): Boolean;
+ function IndexOf(const Value: T): Integer;
+ function LastIndexOf(const Value: T): Integer;
+ procedure Reverse;
+ procedure Sort; overload;
+ procedure Sort(const AComparer: IComparer<T>); overload;
+ function BinarySearch(const Item: T; out Index: Integer): Boolean; overload;
+ function BinarySearch(const Item: T; out Index: Integer; const AComparer: IComparer<T>): Boolean; overload;
+ procedure TrimExcess;
+ function ToArray: TArray<T>;
+ property Capacity: Integer read GetCapacity write SetCapacity;
+ property Count: Integer read GetCount write SetCount;
+ property Items[Index: Integer]: T read GetItem write SetItem; default;
+ public
+ constructor Create;
+ destructor Destroy; override;
+ function ToString : string; override;
+ end;
+
+ IHashSet<TValue> = interface(IThriftContainer)
+ ['{0923A3B5-D4D4-48A8-91AD-40238E2EAD66}']
+ function GetEnumerator: TEnumerator<TValue>;
+ function GetIsReadOnly: Boolean;
+ function GetCount: Integer;
+ property Count: Integer read GetCount;
+ property IsReadOnly: Boolean read GetIsReadOnly;
+ procedure Add( const item: TValue);
+ procedure Clear;
+ function Contains( const item: TValue): Boolean;
+ procedure CopyTo(var A: TArray<TValue>; arrayIndex: Integer);
+ function Remove( const item: TValue ): Boolean;
+ end;
+
+ THashSetImpl<TValue> = class( TInterfacedObject, IHashSet<TValue>, IThriftContainer, ISupportsToString)
+ private
+ FDictionary : IThriftDictionary<TValue,Integer>;
+ FIsReadOnly: Boolean;
+ protected
+ function GetEnumerator: TEnumerator<TValue>;
+ function GetIsReadOnly: Boolean;
+ function GetCount: Integer;
+ property Count: Integer read GetCount;
+ property IsReadOnly: Boolean read FIsReadOnly;
+ procedure Add( const item: TValue);
+ procedure Clear;
+ function Contains( const item: TValue): Boolean;
+ procedure CopyTo(var A: TArray<TValue>; arrayIndex: Integer);
+ function Remove( const item: TValue ): Boolean;
+ public
+ constructor Create;
+ function ToString : string; override;
+ end;
+
+implementation
+
+{ THashSetImpl<TValue> }
+
+procedure THashSetImpl<TValue>.Add( const item: TValue);
+begin
+ if not FDictionary.ContainsKey(item) then
+ begin
+ FDictionary.Add( item, 0);
+ end;
+end;
+
+procedure THashSetImpl<TValue>.Clear;
+begin
+ FDictionary.Clear;
+end;
+
+function THashSetImpl<TValue>.Contains( const item: TValue): Boolean;
+begin
+ Result := FDictionary.ContainsKey(item);
+end;
+
+procedure THashSetImpl<TValue>.CopyTo(var A: TArray<TValue>; arrayIndex: Integer);
+var
+ i : Integer;
+ Enumlator : TEnumerator<TValue>;
+begin
+ Enumlator := GetEnumerator;
+ while Enumlator.MoveNext do
+ begin
+ A[arrayIndex] := Enumlator.Current;
+ Inc(arrayIndex);
+ end;
+end;
+
+constructor THashSetImpl<TValue>.Create;
+begin
+ inherited;
+ FDictionary := TThriftDictionaryImpl<TValue,Integer>.Create;
+end;
+
+function THashSetImpl<TValue>.GetCount: Integer;
+begin
+ Result := FDictionary.Count;
+end;
+
+function THashSetImpl<TValue>.GetEnumerator: TEnumerator<TValue>;
+begin
+ Result := FDictionary.Keys.GetEnumerator;
+end;
+
+function THashSetImpl<TValue>.GetIsReadOnly: Boolean;
+begin
+ Result := FIsReadOnly;
+end;
+
+function THashSetImpl<TValue>.Remove( const item: TValue): Boolean;
+begin
+ Result := False;
+ if FDictionary.ContainsKey( item ) then
+ begin
+ FDictionary.Remove( item );
+ Result := not FDictionary.ContainsKey( item );
+ end;
+end;
+
+function THashSetImpl<TValue>.ToString : string;
+var elm : TValue;
+ sb : TThriftStringBuilder;
+ first : Boolean;
+begin
+ sb := TThriftStringBuilder.Create('{');
+ try
+ first := TRUE;
+ for elm in FDictionary.Keys do begin
+ if first
+ then first := FALSE
+ else sb.Append(', ');
+
+ sb.Append( StringUtils<TValue>.ToString(elm));
+ end;
+ sb.Append('}');
+ Result := sb.ToString;
+ finally
+ sb.Free;
+ end;
+end;
+
+{ TThriftDictionaryImpl<TKey, TValue> }
+
+procedure TThriftDictionaryImpl<TKey, TValue>.Add(const Key: TKey;
+ const Value: TValue);
+begin
+ FDictionaly.Add( Key, Value);
+end;
+
+procedure TThriftDictionaryImpl<TKey, TValue>.AddOrSetValue(const Key: TKey;
+ const Value: TValue);
+begin
+ FDictionaly.AddOrSetValue( Key, Value);
+end;
+
+procedure TThriftDictionaryImpl<TKey, TValue>.Clear;
+begin
+ FDictionaly.Clear;
+end;
+
+function TThriftDictionaryImpl<TKey, TValue>.ContainsKey(
+ const Key: TKey): Boolean;
+begin
+ Result := FDictionaly.ContainsKey( Key );
+end;
+
+function TThriftDictionaryImpl<TKey, TValue>.ContainsValue(
+ const Value: TValue): Boolean;
+begin
+ Result := FDictionaly.ContainsValue( Value );
+end;
+
+constructor TThriftDictionaryImpl<TKey, TValue>.Create(ACapacity: Integer);
+begin
+ inherited Create;
+ FDictionaly := TDictionary<TKey,TValue>.Create( ACapacity );
+end;
+
+destructor TThriftDictionaryImpl<TKey, TValue>.Destroy;
+begin
+ FDictionaly.Free;
+ inherited;
+end;
+
+{$IF CompilerVersion >= 21.0}
+function TThriftDictionaryImpl<TKey, TValue>.ExtractPair( const Key: TKey): TPair<TKey, TValue>;
+begin
+ Result := FDictionaly.ExtractPair( Key);
+end;
+{$IFEND}
+
+function TThriftDictionaryImpl<TKey, TValue>.GetCount: Integer;
+begin
+ Result := FDictionaly.Count;
+end;
+
+function TThriftDictionaryImpl<TKey, TValue>.GetEnumerator: TEnumerator<TPair<TKey, TValue>>;
+begin
+ Result := FDictionaly.GetEnumerator;
+end;
+
+function TThriftDictionaryImpl<TKey, TValue>.GetItem(const Key: TKey): TValue;
+begin
+ Result := FDictionaly.Items[Key];
+end;
+
+function TThriftDictionaryImpl<TKey, TValue>.GetKeys: TDictionary<TKey, TValue>.TKeyCollection;
+begin
+ Result := FDictionaly.Keys;
+end;
+
+function TThriftDictionaryImpl<TKey, TValue>.GetValues: TDictionary<TKey, TValue>.TValueCollection;
+begin
+ Result := FDictionaly.Values;
+end;
+
+procedure TThriftDictionaryImpl<TKey, TValue>.Remove(const Key: TKey);
+begin
+ FDictionaly.Remove( Key );
+end;
+
+procedure TThriftDictionaryImpl<TKey, TValue>.SetItem(const Key: TKey;
+ const Value: TValue);
+begin
+ FDictionaly.AddOrSetValue( Key, Value);
+end;
+
+function TThriftDictionaryImpl<TKey, TValue>.ToArray: TArray<TPair<TKey, TValue>>;
+{$IF CompilerVersion < 22.0}
+var
+ x : TPair<TKey, TValue>;
+ i : Integer;
+{$IFEND}
+begin
+{$IF CompilerVersion < 22.0}
+ SetLength(Result, Count);
+ i := 0;
+ for x in FDictionaly do
+ begin
+ Result[i] := x;
+ Inc( i );
+ end;
+{$ELSE}
+ Result := FDictionaly.ToArray;
+{$IFEND}
+end;
+
+function TThriftDictionaryImpl<TKey, TValue>.ToString : string;
+var pair : TPair<TKey, TValue>;
+ sb : TThriftStringBuilder;
+ first : Boolean;
+begin
+ sb := TThriftStringBuilder.Create('{');
+ try
+ first := TRUE;
+ for pair in FDictionaly do begin
+ if first
+ then first := FALSE
+ else sb.Append(', ');
+
+ sb.Append( '(');
+ sb.Append( StringUtils<TKey>.ToString(pair.Key));
+ sb.Append(' => ');
+ sb.Append( StringUtils<TValue>.ToString(pair.Value));
+ sb.Append(')');
+ end;
+ sb.Append('}');
+ Result := sb.ToString;
+ finally
+ sb.Free;
+ end;
+end;
+
+procedure TThriftDictionaryImpl<TKey, TValue>.TrimExcess;
+begin
+ FDictionaly.TrimExcess;
+end;
+
+function TThriftDictionaryImpl<TKey, TValue>.TryGetValue(const Key: TKey;
+ out Value: TValue): Boolean;
+begin
+ Result := FDictionaly.TryGetValue( Key, Value);
+end;
+
+{ TThriftListImpl<T> }
+
+function TThriftListImpl<T>.Add(const Value: T): Integer;
+begin
+ Result := FList.Add( Value );
+end;
+
+procedure TThriftListImpl<T>.AddRange(Collection: TEnumerable<T>);
+begin
+ FList.AddRange( Collection );
+end;
+
+procedure TThriftListImpl<T>.AddRange(const Collection: IEnumerable<T>);
+begin
+ FList.AddRange( Collection );
+end;
+
+procedure TThriftListImpl<T>.AddRange(const Values: array of T);
+begin
+ FList.AddRange( Values );
+end;
+
+function TThriftListImpl<T>.BinarySearch(const Item: T;
+ out Index: Integer): Boolean;
+begin
+ Result := FList.BinarySearch( Item, Index);
+end;
+
+function TThriftListImpl<T>.BinarySearch(const Item: T; out Index: Integer;
+ const AComparer: IComparer<T>): Boolean;
+begin
+ Result := FList.BinarySearch( Item, Index, AComparer);
+end;
+
+procedure TThriftListImpl<T>.Clear;
+begin
+ FList.Clear;
+end;
+
+function TThriftListImpl<T>.Contains(const Value: T): Boolean;
+begin
+ Result := FList.Contains( Value );
+end;
+
+constructor TThriftListImpl<T>.Create;
+begin
+ inherited;
+ FList := TList<T>.Create;
+end;
+
+procedure TThriftListImpl<T>.Delete(Index: Integer);
+begin
+ FList.Delete( Index )
+end;
+
+procedure TThriftListImpl<T>.DeleteRange(AIndex, ACount: Integer);
+begin
+ FList.DeleteRange( AIndex, ACount)
+end;
+
+destructor TThriftListImpl<T>.Destroy;
+begin
+ FList.Free;
+ inherited;
+end;
+
+{$IF CompilerVersion >= 21.0}
+procedure TThriftListImpl<T>.Exchange(Index1, Index2: Integer);
+begin
+ FList.Exchange( Index1, Index2 )
+end;
+{$IFEND}
+
+function TThriftListImpl<T>.Extract(const Value: T): T;
+begin
+ Result := FList.Extract( Value )
+end;
+
+{$IF CompilerVersion >= 21.0}
+function TThriftListImpl<T>.First: T;
+begin
+ Result := FList.First;
+end;
+{$IFEND}
+
+function TThriftListImpl<T>.GetCapacity: Integer;
+begin
+ Result := FList.Capacity;
+end;
+
+function TThriftListImpl<T>.GetCount: Integer;
+begin
+ Result := FList.Count;
+end;
+
+function TThriftListImpl<T>.GetEnumerator: TEnumerator<T>;
+begin
+ Result := FList.GetEnumerator;
+end;
+
+function TThriftListImpl<T>.GetItem(Index: Integer): T;
+begin
+ Result := FList[Index];
+end;
+
+function TThriftListImpl<T>.IndexOf(const Value: T): Integer;
+begin
+ Result := FList.IndexOf( Value );
+end;
+
+procedure TThriftListImpl<T>.Insert(Index: Integer; const Value: T);
+begin
+ FList.Insert( Index, Value);
+end;
+
+procedure TThriftListImpl<T>.InsertRange(Index: Integer;
+ const Collection: TEnumerable<T>);
+begin
+ FList.InsertRange( Index, Collection );
+end;
+
+procedure TThriftListImpl<T>.InsertRange(Index: Integer;
+ const Values: array of T);
+begin
+ FList.InsertRange( Index, Values);
+end;
+
+procedure TThriftListImpl<T>.InsertRange(Index: Integer;
+ const Collection: IEnumerable<T>);
+begin
+ FList.InsertRange( Index, Collection );
+end;
+
+{$IF CompilerVersion >= 21.0}
+function TThriftListImpl<T>.Last: T;
+begin
+ Result := FList.Last;
+end;
+{$IFEND}
+
+function TThriftListImpl<T>.LastIndexOf(const Value: T): Integer;
+begin
+ Result := FList.LastIndexOf( Value );
+end;
+
+{$IF CompilerVersion >= 21.0}
+procedure TThriftListImpl<T>.Move(CurIndex, NewIndex: Integer);
+begin
+ FList.Move( CurIndex, NewIndex);
+end;
+{$IFEND}
+
+function TThriftListImpl<T>.Remove(const Value: T): Integer;
+begin
+ Result := FList.Remove( Value );
+end;
+
+procedure TThriftListImpl<T>.Reverse;
+begin
+ FList.Reverse;
+end;
+
+procedure TThriftListImpl<T>.SetCapacity(Value: Integer);
+begin
+ FList.Capacity := Value;
+end;
+
+procedure TThriftListImpl<T>.SetCount(Value: Integer);
+begin
+ FList.Count := Value;
+end;
+
+procedure TThriftListImpl<T>.SetItem(Index: Integer; const Value: T);
+begin
+ FList[Index] := Value;
+end;
+
+procedure TThriftListImpl<T>.Sort;
+begin
+ FList.Sort;
+end;
+
+procedure TThriftListImpl<T>.Sort(const AComparer: IComparer<T>);
+begin
+ FList.Sort(AComparer);
+end;
+
+function TThriftListImpl<T>.ToArray: TArray<T>;
+{$IF CompilerVersion < 22.0}
+var
+ x : T;
+ i : Integer;
+{$IFEND}
+begin
+{$IF CompilerVersion < 22.0}
+ SetLength(Result, Count);
+ i := 0;
+ for x in FList do
+ begin
+ Result[i] := x;
+ Inc( i );
+ end;
+{$ELSE}
+ Result := FList.ToArray;
+{$IFEND}
+end;
+
+function TThriftListImpl<T>.ToString : string;
+var elm : T;
+ sb : TThriftStringBuilder;
+ first : Boolean;
+begin
+ sb := TThriftStringBuilder.Create('{');
+ try
+ first := TRUE;
+ for elm in FList do begin
+ if first
+ then first := FALSE
+ else sb.Append(', ');
+
+ sb.Append( StringUtils<T>.ToString(elm));
+ end;
+ sb.Append('}');
+ Result := sb.ToString;
+ finally
+ sb.Free;
+ end;
+end;
+
+procedure TThriftListImpl<T>.TrimExcess;
+begin
+ FList.TrimExcess;
+end;
+
+end.
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Defines.inc b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Defines.inc
new file mode 100644
index 000000000..499ccae12
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Defines.inc
@@ -0,0 +1,50 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+
+// Good lists of Delphi version numbers
+// https://github.com/project-jedi/jedi/blob/master/jedi.inc
+// http://docwiki.embarcadero.com/RADStudio/Seattle/en/Compiler_Versions
+
+
+// start with most backwards compatible defaults
+
+{$DEFINE OLD_UNIT_NAMES}
+{$DEFINE OLD_SOCKETS} // TODO: add socket support for CompilerVersion >= 28.0
+{$UNDEF HAVE_CLASS_CTOR}
+
+
+// enable features as they are available
+
+{$IF CompilerVersion >= 21.0} // Delphi 2010
+ {$DEFINE HAVE_CLASS_CTOR}
+{$IFEND}
+
+{$IF CompilerVersion >= 23.0} // Delphi XE2
+ {$UNDEF OLD_UNIT_NAMES}
+{$IFEND}
+
+{$IF CompilerVersion >= 28.0} // Delphi XE7
+ {$UNDEF OLD_SOCKETS}
+{$IFEND}
+
+
+// EOF
+
+
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Exception.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Exception.pas
new file mode 100644
index 000000000..5d15c3656
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Exception.pas
@@ -0,0 +1,62 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+{$SCOPEDENUMS ON}
+
+unit Thrift.Exception;
+
+interface
+
+uses
+ Classes, SysUtils;
+
+type
+ // base class for all Thrift exceptions
+ TException = class( SysUtils.Exception)
+ public
+ function Message : string; // hide inherited property: allow read, but prevent accidental writes
+ procedure UpdateMessageProperty; // update inherited message property with toString()
+ end;
+
+
+
+
+implementation
+
+{ TException }
+
+function TException.Message;
+// allow read (exception summary), but prevent accidental writes
+// read will return the exception summary
+begin
+ result := Self.ToString;
+end;
+
+procedure TException.UpdateMessageProperty;
+// Update the inherited Message property to better conform to standard behaviour.
+// Nice benefit: The IDE is now able to show the exception message again.
+begin
+ inherited Message := Self.ToString; // produces a summary text
+end;
+
+
+
+
+end.
+
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Processor.Multiplex.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Processor.Multiplex.pas
new file mode 100644
index 000000000..8cf23db07
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Processor.Multiplex.pas
@@ -0,0 +1,231 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+unit Thrift.Processor.Multiplex;
+
+
+interface
+
+uses
+ SysUtils,
+ Generics.Collections,
+ Thrift,
+ Thrift.Protocol,
+ Thrift.Protocol.Multiplex;
+
+{ TMultiplexedProcessor is a TProcessor allowing a single TServer to provide multiple services.
+ To do so, you instantiate the processor and then register additional processors with it,
+ as shown in the following example:
+
+
+ TMultiplexedProcessor processor = new TMultiplexedProcessor();
+
+ processor.registerProcessor(
+ "Calculator",
+ new Calculator.Processor(new CalculatorHandler()));
+
+ processor.registerProcessor(
+ "WeatherReport",
+ new WeatherReport.Processor(new WeatherReportHandler()));
+
+ TServerTransport t = new TServerSocket(9090);
+ TSimpleServer server = new TSimpleServer(processor, t);
+
+ server.serve();
+}
+
+
+type
+ IMultiplexedProcessor = interface( IProcessor)
+ ['{807F9D19-6CF4-4789-840E-93E87A12EB63}']
+ // Register a service with this TMultiplexedProcessor. This allows us
+ // to broker requests to individual services by using the service name
+ // to select them at request time.
+ procedure RegisterProcessor( const serviceName : String; const processor : IProcessor; const asDefault : Boolean = FALSE);
+ end;
+
+
+ TMultiplexedProcessorImpl = class( TInterfacedObject, IMultiplexedProcessor, IProcessor)
+ private type
+ // Our goal was to work with any protocol. In order to do that, we needed
+ // to allow them to call readMessageBegin() and get a TMessage in exactly
+ // the standard format, without the service name prepended to TMessage.name.
+ TStoredMessageProtocol = class( TProtocolDecorator)
+ private
+ FMessageBegin : TThriftMessage;
+ public
+ constructor Create( const protocol : IProtocol; const aMsgBegin : TThriftMessage);
+ function ReadMessageBegin: TThriftMessage; override;
+ end;
+
+ private
+ FServiceProcessorMap : TDictionary<String, IProcessor>;
+ FDefaultProcessor : IProcessor;
+
+ procedure Error( const oprot : IProtocol; const msg : TThriftMessage;
+ extype : TApplicationExceptionSpecializedClass; const etxt : string);
+
+ public
+ constructor Create;
+ destructor Destroy; override;
+
+ // Register a service with this TMultiplexedProcessorImpl. This allows us
+ // to broker requests to individual services by using the service name
+ // to select them at request time.
+ procedure RegisterProcessor( const serviceName : String; const processor : IProcessor; const asDefault : Boolean = FALSE);
+
+ { This implementation of process performs the following steps:
+ - Read the beginning of the message.
+ - Extract the service name from the message.
+ - Using the service name to locate the appropriate processor.
+ - Dispatch to the processor, with a decorated instance of TProtocol
+ that allows readMessageBegin() to return the original TMessage.
+
+ An exception is thrown if the message type is not CALL or ONEWAY
+ or if the service is unknown (or not properly registered).
+ }
+ function Process( const iprot, oprot: IProtocol; const events : IProcessorEvents = nil): Boolean;
+ end;
+
+
+implementation
+
+constructor TMultiplexedProcessorImpl.TStoredMessageProtocol.Create( const protocol : IProtocol; const aMsgBegin : TThriftMessage);
+begin
+ inherited Create( protocol);
+ FMessageBegin := aMsgBegin;
+end;
+
+
+function TMultiplexedProcessorImpl.TStoredMessageProtocol.ReadMessageBegin: TThriftMessage;
+begin
+ result := FMessageBegin;
+end;
+
+
+constructor TMultiplexedProcessorImpl.Create;
+begin
+ inherited Create;
+ FServiceProcessorMap := TDictionary<string,IProcessor>.Create;
+end;
+
+
+destructor TMultiplexedProcessorImpl.Destroy;
+begin
+ try
+ FreeAndNil( FServiceProcessorMap);
+ finally
+ inherited Destroy;
+ end;
+end;
+
+
+procedure TMultiplexedProcessorImpl.RegisterProcessor( const serviceName : String; const processor : IProcessor; const asDefault : Boolean);
+begin
+ FServiceProcessorMap.Add( serviceName, processor);
+
+ if asDefault then begin
+ if FDefaultProcessor = nil
+ then FDefaultProcessor := processor
+ else raise TApplicationExceptionInternalError.Create('Only one default service allowed');
+ end;
+end;
+
+
+procedure TMultiplexedProcessorImpl.Error( const oprot : IProtocol; const msg : TThriftMessage;
+ extype : TApplicationExceptionSpecializedClass;
+ const etxt : string);
+var appex : TApplicationException;
+ newMsg : TThriftMessage;
+begin
+ appex := extype.Create(etxt);
+ try
+ Init( newMsg, msg.Name, TMessageType.Exception, msg.SeqID);
+
+ oprot.WriteMessageBegin(newMsg);
+ appex.Write(oprot);
+ oprot.WriteMessageEnd();
+ oprot.Transport.Flush();
+
+ finally
+ appex.Free;
+ end;
+end;
+
+
+function TMultiplexedProcessorImpl.Process(const iprot, oprot : IProtocol; const events : IProcessorEvents = nil): Boolean;
+var msg, newMsg : TThriftMessage;
+ idx : Integer;
+ sService : string;
+ processor : IProcessor;
+ protocol : IProtocol;
+const
+ ERROR_INVALID_MSGTYPE = 'Message must be "call" or "oneway"';
+ ERROR_INCOMPATIBLE_PROT = 'No service name found in "%s". Client is expected to use TMultiplexProtocol.';
+ ERROR_UNKNOWN_SERVICE = 'Service "%s" is not registered with MultiplexedProcessor';
+begin
+ // Use the actual underlying protocol (e.g. TBinaryProtocol) to read the message header.
+ // This pulls the message "off the wire", which we'll deal with at the end of this method.
+ msg := iprot.readMessageBegin();
+ if not (msg.Type_ in [TMessageType.Call, TMessageType.Oneway]) then begin
+ Error( oprot, msg,
+ TApplicationExceptionInvalidMessageType,
+ ERROR_INVALID_MSGTYPE);
+ Exit( FALSE);
+ end;
+
+ // Extract the service name
+ // use FDefaultProcessor as fallback if there is no separator
+ idx := Pos( TMultiplexedProtocol.SEPARATOR, msg.Name);
+ if idx > 0 then begin
+
+ // Create a new TMessage, something that can be consumed by any TProtocol
+ sService := Copy( msg.Name, 1, idx-1);
+ if not FServiceProcessorMap.TryGetValue( sService, processor)
+ then begin
+ Error( oprot, msg,
+ TApplicationExceptionInternalError,
+ Format(ERROR_UNKNOWN_SERVICE,[sService]));
+ Exit( FALSE);
+ end;
+
+ // Create a new TMessage, removing the service name
+ Inc( idx, Length(TMultiplexedProtocol.SEPARATOR));
+ Init( newMsg, Copy( msg.Name, idx, MAXINT), msg.Type_, msg.SeqID);
+
+ end
+ else if FDefaultProcessor <> nil then begin
+ processor := FDefaultProcessor;
+ newMsg := msg; // no need to change
+
+ end
+ else begin
+ Error( oprot, msg,
+ TApplicationExceptionInvalidProtocol,
+ Format(ERROR_INCOMPATIBLE_PROT,[msg.Name]));
+ Exit( FALSE);
+ end;
+
+ // Dispatch processing to the stored processor
+ protocol := TStoredMessageProtocol.Create( iprot, newMsg);
+ result := processor.process( protocol, oprot, events);
+end;
+
+
+end.
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.Compact.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.Compact.pas
new file mode 100644
index 000000000..07cab9a05
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.Compact.pas
@@ -0,0 +1,1118 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+{$SCOPEDENUMS ON}
+
+unit Thrift.Protocol.Compact;
+
+interface
+
+uses
+ Classes,
+ SysUtils,
+ Math,
+ Generics.Collections,
+ Thrift.Transport,
+ Thrift.Protocol,
+ Thrift.Utils;
+
+type
+ ICompactProtocol = interface( IProtocol)
+ ['{C01927EC-021A-45F7-93B1-23D6A5420EDD}']
+ end;
+
+ // Compact protocol implementation for thrift.
+ // Adapted from the C# version.
+ TCompactProtocolImpl = class( TProtocolImpl, ICompactProtocol)
+ public
+ type
+ TFactory = class( TInterfacedObject, IProtocolFactory)
+ public
+ function GetProtocol( const trans: ITransport): IProtocol;
+ end;
+
+ private const
+
+ { TODO
+ static TStruct ANONYMOUS_STRUCT = new TStruct("");
+ static TField TSTOP = new TField("", TType.Stop, (short)0);
+ }
+
+ PROTOCOL_ID = Byte( $82);
+ VERSION = Byte( 1);
+ VERSION_MASK = Byte( $1F); // 0001 1111
+ TYPE_MASK = Byte( $E0); // 1110 0000
+ TYPE_BITS = Byte( $07); // 0000 0111
+ TYPE_SHIFT_AMOUNT = Byte( 5);
+
+ private type
+ // All of the on-wire type codes.
+ Types = (
+ STOP = $00,
+ BOOLEAN_TRUE = $01,
+ BOOLEAN_FALSE = $02,
+ BYTE_ = $03,
+ I16 = $04,
+ I32 = $05,
+ I64 = $06,
+ DOUBLE_ = $07,
+ BINARY = $08,
+ LIST = $09,
+ SET_ = $0A,
+ MAP = $0B,
+ STRUCT = $0C
+ );
+
+ private const
+ ttypeToCompactType : array[TType] of Types = (
+ Types.STOP, // Stop = 0,
+ Types(-1), // Void = 1,
+ Types.BOOLEAN_TRUE, // Bool_ = 2,
+ Types.BYTE_, // Byte_ = 3,
+ Types.DOUBLE_, // Double_ = 4,
+ Types(-5), // unused
+ Types.I16, // I16 = 6,
+ Types(-7), // unused
+ Types.I32, // I32 = 8,
+ Types(-9), // unused
+ Types.I64, // I64 = 10,
+ Types.BINARY, // String_ = 11,
+ Types.STRUCT, // Struct = 12,
+ Types.MAP, // Map = 13,
+ Types.SET_, // Set_ = 14,
+ Types.LIST // List = 15,
+ );
+
+ tcompactTypeToType : array[Types] of TType = (
+ TType.Stop, // STOP
+ TType.Bool_, // BOOLEAN_TRUE
+ TType.Bool_, // BOOLEAN_FALSE
+ TType.Byte_, // BYTE_
+ TType.I16, // I16
+ TType.I32, // I32
+ TType.I64, // I64
+ TType.Double_, // DOUBLE_
+ TType.String_, // BINARY
+ TType.List, // LIST
+ TType.Set_, // SET_
+ TType.Map, // MAP
+ TType.Struct // STRUCT
+ );
+
+ private
+ // Used to keep track of the last field for the current and previous structs,
+ // so we can do the delta stuff.
+ lastField_ : TStack<Integer>;
+ lastFieldId_ : Integer;
+
+ // If we encounter a boolean field begin, save the TField here so it can
+ // have the value incorporated.
+ private booleanField_ : TThriftField;
+
+ // If we Read a field header, and it's a boolean field, save the boolean
+ // value here so that ReadBool can use it.
+ private boolValue_ : ( unused, bool_true, bool_false);
+
+ public
+ constructor Create(const trans : ITransport);
+ destructor Destroy; override;
+
+ procedure Reset;
+
+ private
+ procedure WriteByteDirect( const b : Byte); overload;
+
+ // Writes a byte without any possibility of all that field header nonsense.
+ procedure WriteByteDirect( const n : Integer); overload;
+
+ // Write an i32 as a varint. Results in 1-5 bytes on the wire.
+ // TODO: make a permanent buffer like WriteVarint64?
+ procedure WriteVarint32( n : Cardinal);
+
+ private
+ // The workhorse of WriteFieldBegin. It has the option of doing a 'type override'
+ // of the type header. This is used specifically in the boolean field case.
+ procedure WriteFieldBeginInternal( const field : TThriftField; typeOverride : Byte);
+
+ public
+ procedure WriteMessageBegin( const msg: TThriftMessage); override;
+ procedure WriteMessageEnd; override;
+ procedure WriteStructBegin( const struc: TThriftStruct); override;
+ procedure WriteStructEnd; override;
+ procedure WriteFieldBegin( const field: TThriftField); override;
+ procedure WriteFieldEnd; override;
+ procedure WriteFieldStop; override;
+ procedure WriteMapBegin( const map: TThriftMap); override;
+ procedure WriteMapEnd; override;
+ procedure WriteListBegin( const list: TThriftList); override;
+ procedure WriteListEnd(); override;
+ procedure WriteSetBegin( const set_: TThriftSet ); override;
+ procedure WriteSetEnd(); override;
+ procedure WriteBool( b: Boolean); override;
+ procedure WriteByte( b: ShortInt); override;
+ procedure WriteI16( i16: SmallInt); override;
+ procedure WriteI32( i32: Integer); override;
+ procedure WriteI64( const i64: Int64); override;
+ procedure WriteDouble( const dub: Double); override;
+ procedure WriteBinary( const b: TBytes); overload; override;
+
+ private
+ class function DoubleToInt64Bits( const db : Double) : Int64;
+ class function Int64BitsToDouble( const i64 : Int64) : Double;
+
+ // Abstract method for writing the start of lists and sets. List and sets on
+ // the wire differ only by the type indicator.
+ procedure WriteCollectionBegin( const elemType : TType; size : Integer);
+
+ procedure WriteVarint64( n : UInt64);
+
+ // Convert l into a zigzag long. This allows negative numbers to be
+ // represented compactly as a varint.
+ class function longToZigzag( const n : Int64) : UInt64;
+
+ // Convert n into a zigzag int. This allows negative numbers to be
+ // represented compactly as a varint.
+ class function intToZigZag( const n : Integer) : Cardinal;
+
+ //Convert a Int64 into little-endian bytes in buf starting at off and going until off+7.
+ class procedure fixedLongToBytes( const n : Int64; var buf : TBytes);
+
+ public
+ function ReadMessageBegin: TThriftMessage; override;
+ procedure ReadMessageEnd(); override;
+ function ReadStructBegin: TThriftStruct; override;
+ procedure ReadStructEnd; override;
+ function ReadFieldBegin: TThriftField; override;
+ procedure ReadFieldEnd(); override;
+ function ReadMapBegin: TThriftMap; override;
+ procedure ReadMapEnd(); override;
+ function ReadListBegin: TThriftList; override;
+ procedure ReadListEnd(); override;
+ function ReadSetBegin: TThriftSet; override;
+ procedure ReadSetEnd(); override;
+ function ReadBool: Boolean; override;
+ function ReadByte: ShortInt; override;
+ function ReadI16: SmallInt; override;
+ function ReadI32: Integer; override;
+ function ReadI64: Int64; override;
+ function ReadDouble:Double; override;
+ function ReadBinary: TBytes; overload; override;
+
+ private
+ // Internal Reading methods
+
+ // Read an i32 from the wire as a varint. The MSB of each byte is set
+ // if there is another byte to follow. This can Read up to 5 bytes.
+ function ReadVarint32 : Cardinal;
+
+ // Read an i64 from the wire as a proper varint. The MSB of each byte is set
+ // if there is another byte to follow. This can Read up to 10 bytes.
+ function ReadVarint64 : UInt64;
+
+
+ // encoding helpers
+
+ // Convert from zigzag Integer to Integer.
+ class function zigzagToInt( const n : Cardinal ) : Integer;
+
+ // Convert from zigzag Int64 to Int64.
+ class function zigzagToLong( const n : UInt64) : Int64;
+
+ // Note that it's important that the mask bytes are Int64 literals,
+ // otherwise they'll default to ints, and when you shift an Integer left 56 bits,
+ // you just get a messed up Integer.
+ class function bytesToLong( const bytes : TBytes) : Int64;
+
+ // type testing and converting
+ class function isBoolType( const b : byte) : Boolean;
+
+ // Given a TCompactProtocol.Types constant, convert it to its corresponding TType value.
+ class function getTType( const type_ : byte) : TType;
+
+ // Given a TType value, find the appropriate TCompactProtocol.Types constant.
+ class function getCompactType( const ttype : TType) : Byte;
+ end;
+
+
+implementation
+
+
+
+//--- TCompactProtocolImpl.TFactory ----------------------------------------
+
+
+function TCompactProtocolImpl.TFactory.GetProtocol( const trans: ITransport): IProtocol;
+begin
+ result := TCompactProtocolImpl.Create( trans);
+end;
+
+
+//--- TCompactProtocolImpl -------------------------------------------------
+
+
+constructor TCompactProtocolImpl.Create(const trans: ITransport);
+begin
+ inherited Create( trans);
+
+ lastFieldId_ := 0;
+ lastField_ := TStack<Integer>.Create;
+
+ Init( booleanField_, '', TType.Stop, 0);
+ boolValue_ := unused;
+end;
+
+
+destructor TCompactProtocolImpl.Destroy;
+begin
+ try
+ FreeAndNil( lastField_);
+ finally
+ inherited Destroy;
+ end;
+end;
+
+
+
+procedure TCompactProtocolImpl.Reset;
+begin
+ lastField_.Clear();
+ lastFieldId_ := 0;
+ Init( booleanField_, '', TType.Stop, 0);
+ boolValue_ := unused;
+end;
+
+
+// Writes a byte without any possibility of all that field header nonsense.
+// Used internally by other writing methods that know they need to Write a byte.
+procedure TCompactProtocolImpl.WriteByteDirect( const b : Byte);
+begin
+ Transport.Write( @b, SizeOf(b));
+end;
+
+
+// Writes a byte without any possibility of all that field header nonsense.
+procedure TCompactProtocolImpl.WriteByteDirect( const n : Integer);
+begin
+ WriteByteDirect( Byte(n));
+end;
+
+
+// Write an i32 as a varint. Results in 1-5 bytes on the wire.
+procedure TCompactProtocolImpl.WriteVarint32( n : Cardinal);
+var i32buf : TBytes;
+ idx : Integer;
+begin
+ SetLength( i32buf, 5);
+ idx := 0;
+ while TRUE do begin
+ ASSERT( idx < Length(i32buf));
+
+ // last part?
+ if ((n and not $7F) = 0) then begin
+ i32buf[idx] := Byte(n);
+ Inc(idx);
+ Break;
+ end;
+
+ i32buf[idx] := Byte((n and $7F) or $80);
+ Inc(idx);
+ n := n shr 7;
+ end;
+
+ Transport.Write( i32buf, 0, idx);
+end;
+
+
+// Write a message header to the wire. Compact Protocol messages contain the
+// protocol version so we can migrate forwards in the future if need be.
+procedure TCompactProtocolImpl.WriteMessageBegin( const msg: TThriftMessage);
+var versionAndType : Byte;
+begin
+ Reset;
+
+ versionAndType := Byte( VERSION and VERSION_MASK)
+ or Byte( (Cardinal(msg.Type_) shl TYPE_SHIFT_AMOUNT) and TYPE_MASK);
+
+ WriteByteDirect( PROTOCOL_ID);
+ WriteByteDirect( versionAndType);
+ WriteVarint32( Cardinal(msg.SeqID));
+ WriteString( msg.Name);
+end;
+
+
+// Write a struct begin. This doesn't actually put anything on the wire. We use it as an
+// opportunity to put special placeholder markers on the field stack so we can get the
+// field id deltas correct.
+procedure TCompactProtocolImpl.WriteStructBegin( const struc: TThriftStruct);
+begin
+ lastField_.Push(lastFieldId_);
+ lastFieldId_ := 0;
+end;
+
+
+// Write a struct end. This doesn't actually put anything on the wire. We use this as an
+// opportunity to pop the last field from the current struct off of the field stack.
+procedure TCompactProtocolImpl.WriteStructEnd;
+begin
+ lastFieldId_ := lastField_.Pop();
+end;
+
+
+// Write a field header containing the field id and field type. If the difference between the
+// current field id and the last one is small (< 15), then the field id will be encoded in
+// the 4 MSB as a delta. Otherwise, the field id will follow the type header as a zigzag varint.
+procedure TCompactProtocolImpl.WriteFieldBegin( const field: TThriftField);
+begin
+ case field.Type_ of
+ TType.Bool_ : booleanField_ := field; // we want to possibly include the value, so we'll wait.
+ else
+ WriteFieldBeginInternal(field, $FF);
+ end;
+end;
+
+
+// The workhorse of WriteFieldBegin. It has the option of doing a 'type override'
+// of the type header. This is used specifically in the boolean field case.
+procedure TCompactProtocolImpl.WriteFieldBeginInternal( const field : TThriftField; typeOverride : Byte);
+var typeToWrite : Byte;
+begin
+ // if there's a type override, use that.
+ if typeOverride = $FF
+ then typeToWrite := getCompactType( field.Type_)
+ else typeToWrite := typeOverride;
+
+ // check if we can use delta encoding for the field id
+ if (field.ID > lastFieldId_) and ((field.ID - lastFieldId_) <= 15)
+ then begin
+ // Write them together
+ WriteByteDirect( ((field.ID - lastFieldId_) shl 4) or typeToWrite);
+ end
+ else begin
+ // Write them separate
+ WriteByteDirect( typeToWrite);
+ WriteI16( field.ID);
+ end;
+
+ lastFieldId_ := field.ID;
+end;
+
+
+// Write the STOP symbol so we know there are no more fields in this struct.
+procedure TCompactProtocolImpl.WriteFieldStop;
+begin
+ WriteByteDirect( Byte( Types.STOP));
+end;
+
+
+// Write a map header. If the map is empty, omit the key and value type
+// headers, as we don't need any additional information to skip it.
+procedure TCompactProtocolImpl.WriteMapBegin( const map: TThriftMap);
+var key, val : Byte;
+begin
+ if (map.Count = 0)
+ then WriteByteDirect( 0)
+ else begin
+ WriteVarint32( Cardinal( map.Count));
+ key := getCompactType(map.KeyType);
+ val := getCompactType(map.ValueType);
+ WriteByteDirect( (key shl 4) or val);
+ end;
+end;
+
+
+// Write a list header.
+procedure TCompactProtocolImpl.WriteListBegin( const list: TThriftList);
+begin
+ WriteCollectionBegin( list.ElementType, list.Count);
+end;
+
+
+// Write a set header.
+procedure TCompactProtocolImpl.WriteSetBegin( const set_: TThriftSet );
+begin
+ WriteCollectionBegin( set_.ElementType, set_.Count);
+end;
+
+
+// Write a boolean value. Potentially, this could be a boolean field, in
+// which case the field header info isn't written yet. If so, decide what the
+// right type header is for the value and then Write the field header.
+// Otherwise, Write a single byte.
+procedure TCompactProtocolImpl.WriteBool( b: Boolean);
+var bt : Types;
+begin
+ if b
+ then bt := Types.BOOLEAN_TRUE
+ else bt := Types.BOOLEAN_FALSE;
+
+ if booleanField_.Type_ = TType.Bool_ then begin
+ // we haven't written the field header yet
+ WriteFieldBeginInternal( booleanField_, Byte(bt));
+ booleanField_.Type_ := TType.Stop;
+ end
+ else begin
+ // we're not part of a field, so just Write the value.
+ WriteByteDirect( Byte(bt));
+ end;
+end;
+
+
+// Write a byte. Nothing to see here!
+procedure TCompactProtocolImpl.WriteByte( b: ShortInt);
+begin
+ WriteByteDirect( Byte(b));
+end;
+
+
+// Write an I16 as a zigzag varint.
+procedure TCompactProtocolImpl.WriteI16( i16: SmallInt);
+begin
+ WriteVarint32( intToZigZag( i16));
+end;
+
+
+// Write an i32 as a zigzag varint.
+procedure TCompactProtocolImpl.WriteI32( i32: Integer);
+begin
+ WriteVarint32( intToZigZag( i32));
+end;
+
+
+// Write an i64 as a zigzag varint.
+procedure TCompactProtocolImpl.WriteI64( const i64: Int64);
+begin
+ WriteVarint64( longToZigzag( i64));
+end;
+
+
+class function TCompactProtocolImpl.DoubleToInt64Bits( const db : Double) : Int64;
+begin
+ ASSERT( SizeOf(db) = SizeOf(result));
+ Move( db, result, SizeOf(result));
+end;
+
+
+class function TCompactProtocolImpl.Int64BitsToDouble( const i64 : Int64) : Double;
+begin
+ ASSERT( SizeOf(i64) = SizeOf(result));
+ Move( i64, result, SizeOf(result));
+end;
+
+
+// Write a double to the wire as 8 bytes.
+procedure TCompactProtocolImpl.WriteDouble( const dub: Double);
+var data : TBytes;
+begin
+ fixedLongToBytes( DoubleToInt64Bits(dub), data);
+ Transport.Write( data);
+end;
+
+
+// Write a byte array, using a varint for the size.
+procedure TCompactProtocolImpl.WriteBinary( const b: TBytes);
+begin
+ WriteVarint32( Cardinal(Length(b)));
+ Transport.Write( b);
+end;
+
+procedure TCompactProtocolImpl.WriteMessageEnd;
+begin
+ // nothing to do
+end;
+
+
+procedure TCompactProtocolImpl.WriteMapEnd;
+begin
+ // nothing to do
+end;
+
+
+procedure TCompactProtocolImpl.WriteListEnd;
+begin
+ // nothing to do
+end;
+
+
+procedure TCompactProtocolImpl.WriteSetEnd;
+begin
+ // nothing to do
+end;
+
+
+procedure TCompactProtocolImpl.WriteFieldEnd;
+begin
+ // nothing to do
+end;
+
+
+// Abstract method for writing the start of lists and sets. List and sets on
+// the wire differ only by the type indicator.
+procedure TCompactProtocolImpl.WriteCollectionBegin( const elemType : TType; size : Integer);
+begin
+ if size <= 14
+ then WriteByteDirect( (size shl 4) or getCompactType(elemType))
+ else begin
+ WriteByteDirect( $F0 or getCompactType(elemType));
+ WriteVarint32( Cardinal(size));
+ end;
+end;
+
+
+// Write an i64 as a varint. Results in 1-10 bytes on the wire.
+procedure TCompactProtocolImpl.WriteVarint64( n : UInt64);
+var varint64out : TBytes;
+ idx : Integer;
+begin
+ SetLength( varint64out, 10);
+ idx := 0;
+ while TRUE do begin
+ ASSERT( idx < Length(varint64out));
+
+ // last one?
+ if (n and not UInt64($7F)) = 0 then begin
+ varint64out[idx] := Byte(n);
+ Inc(idx);
+ Break;
+ end;
+
+ varint64out[idx] := Byte((n and $7F) or $80);
+ Inc(idx);
+ n := n shr 7;
+ end;
+
+ Transport.Write( varint64out, 0, idx);
+end;
+
+
+// Convert l into a zigzag Int64. This allows negative numbers to be
+// represented compactly as a varint.
+class function TCompactProtocolImpl.longToZigzag( const n : Int64) : UInt64;
+begin
+ // there is no arithmetic right shift in Delphi
+ if n >= 0
+ then result := UInt64(n shl 1)
+ else result := UInt64(n shl 1) xor $FFFFFFFFFFFFFFFF;
+end;
+
+
+// Convert n into a zigzag Integer. This allows negative numbers to be
+// represented compactly as a varint.
+class function TCompactProtocolImpl.intToZigZag( const n : Integer) : Cardinal;
+begin
+ // there is no arithmetic right shift in Delphi
+ if n >= 0
+ then result := Cardinal(n shl 1)
+ else result := Cardinal(n shl 1) xor $FFFFFFFF;
+end;
+
+
+// Convert a Int64 into 8 little-endian bytes in buf
+class procedure TCompactProtocolImpl.fixedLongToBytes( const n : Int64; var buf : TBytes);
+begin
+ SetLength( buf, 8);
+ buf[0] := Byte( n and $FF);
+ buf[1] := Byte((n shr 8) and $FF);
+ buf[2] := Byte((n shr 16) and $FF);
+ buf[3] := Byte((n shr 24) and $FF);
+ buf[4] := Byte((n shr 32) and $FF);
+ buf[5] := Byte((n shr 40) and $FF);
+ buf[6] := Byte((n shr 48) and $FF);
+ buf[7] := Byte((n shr 56) and $FF);
+end;
+
+
+
+// Read a message header.
+function TCompactProtocolImpl.ReadMessageBegin : TThriftMessage;
+var protocolId, versionAndType, version, type_ : Byte;
+ seqid : Integer;
+ msgNm : String;
+begin
+ Reset;
+
+ protocolId := Byte( ReadByte);
+ if (protocolId <> PROTOCOL_ID)
+ then raise TProtocolExceptionBadVersion.Create( 'Expected protocol id ' + IntToHex(PROTOCOL_ID,2)
+ + ' but got ' + IntToHex(protocolId,2));
+
+ versionAndType := Byte( ReadByte);
+ version := Byte( versionAndType and VERSION_MASK);
+ if (version <> VERSION)
+ then raise TProtocolExceptionBadVersion.Create( 'Expected version ' +IntToStr(VERSION)
+ + ' but got ' + IntToStr(version));
+
+ type_ := Byte( (versionAndType shr TYPE_SHIFT_AMOUNT) and TYPE_BITS);
+ seqid := Integer( ReadVarint32);
+ msgNm := ReadString;
+ Init( result, msgNm, TMessageType(type_), seqid);
+end;
+
+
+// Read a struct begin. There's nothing on the wire for this, but it is our
+// opportunity to push a new struct begin marker onto the field stack.
+function TCompactProtocolImpl.ReadStructBegin: TThriftStruct;
+begin
+ lastField_.Push( lastFieldId_);
+ lastFieldId_ := 0;
+ Init( result);
+end;
+
+
+// Doesn't actually consume any wire data, just removes the last field for
+// this struct from the field stack.
+procedure TCompactProtocolImpl.ReadStructEnd;
+begin
+ // consume the last field we Read off the wire.
+ lastFieldId_ := lastField_.Pop();
+end;
+
+
+// Read a field header off the wire.
+function TCompactProtocolImpl.ReadFieldBegin: TThriftField;
+var type_ : Byte;
+ modifier : ShortInt;
+ fieldId : SmallInt;
+begin
+ type_ := Byte( ReadByte);
+
+ // if it's a stop, then we can return immediately, as the struct is over.
+ if type_ = Byte(Types.STOP) then begin
+ Init( result, '', TType.Stop, 0);
+ Exit;
+ end;
+
+ // mask off the 4 MSB of the type header. it could contain a field id delta.
+ modifier := ShortInt( (type_ and $F0) shr 4);
+ if (modifier = 0)
+ then fieldId := ReadI16 // not a delta. look ahead for the zigzag varint field id.
+ else fieldId := SmallInt( lastFieldId_ + modifier); // add the delta to the last Read field id.
+
+ Init( result, '', getTType(Byte(type_ and $0F)), fieldId);
+
+ // if this happens to be a boolean field, the value is encoded in the type
+ // save the boolean value in a special instance variable.
+ if isBoolType(type_) then begin
+ if Byte(type_ and $0F) = Byte(Types.BOOLEAN_TRUE)
+ then boolValue_ := bool_true
+ else boolValue_ := bool_false;
+ end;
+
+ // push the new field onto the field stack so we can keep the deltas going.
+ lastFieldId_ := result.ID;
+end;
+
+
+// Read a map header off the wire. If the size is zero, skip Reading the key
+// and value type. This means that 0-length maps will yield TMaps without the
+// "correct" types.
+function TCompactProtocolImpl.ReadMapBegin: TThriftMap;
+var size : Integer;
+ keyAndValueType : Byte;
+ key, val : TType;
+begin
+ size := Integer( ReadVarint32);
+ if size = 0
+ then keyAndValueType := 0
+ else keyAndValueType := Byte( ReadByte);
+
+ key := getTType( Byte( keyAndValueType shr 4));
+ val := getTType( Byte( keyAndValueType and $F));
+ Init( result, key, val, size);
+ ASSERT( (result.KeyType = key) and (result.ValueType = val));
+end;
+
+
+// Read a list header off the wire. If the list size is 0-14, the size will
+// be packed into the element type header. If it's a longer list, the 4 MSB
+// of the element type header will be $F, and a varint will follow with the
+// true size.
+function TCompactProtocolImpl.ReadListBegin: TThriftList;
+var size_and_type : Byte;
+ size : Integer;
+ type_ : TType;
+begin
+ size_and_type := Byte( ReadByte);
+
+ size := (size_and_type shr 4) and $0F;
+ if (size = 15)
+ then size := Integer( ReadVarint32);
+
+ type_ := getTType( size_and_type);
+ Init( result, type_, size);
+end;
+
+
+// Read a set header off the wire. If the set size is 0-14, the size will
+// be packed into the element type header. If it's a longer set, the 4 MSB
+// of the element type header will be $F, and a varint will follow with the
+// true size.
+function TCompactProtocolImpl.ReadSetBegin: TThriftSet;
+var size_and_type : Byte;
+ size : Integer;
+ type_ : TType;
+begin
+ size_and_type := Byte( ReadByte);
+
+ size := (size_and_type shr 4) and $0F;
+ if (size = 15)
+ then size := Integer( ReadVarint32);
+
+ type_ := getTType( size_and_type);
+ Init( result, type_, size);
+end;
+
+
+// Read a boolean off the wire. If this is a boolean field, the value should
+// already have been Read during ReadFieldBegin, so we'll just consume the
+// pre-stored value. Otherwise, Read a byte.
+function TCompactProtocolImpl.ReadBool: Boolean;
+begin
+ if boolValue_ <> unused then begin
+ result := (boolValue_ = bool_true);
+ boolValue_ := unused;
+ Exit;
+ end;
+
+ result := (Byte(ReadByte) = Byte(Types.BOOLEAN_TRUE));
+end;
+
+
+// Read a single byte off the wire. Nothing interesting here.
+function TCompactProtocolImpl.ReadByte: ShortInt;
+begin
+ Transport.ReadAll( @result, SizeOf(result), 0, 1);
+end;
+
+
+// Read an i16 from the wire as a zigzag varint.
+function TCompactProtocolImpl.ReadI16: SmallInt;
+begin
+ result := SmallInt( zigzagToInt( ReadVarint32));
+end;
+
+
+// Read an i32 from the wire as a zigzag varint.
+function TCompactProtocolImpl.ReadI32: Integer;
+begin
+ result := zigzagToInt( ReadVarint32);
+end;
+
+
+// Read an i64 from the wire as a zigzag varint.
+function TCompactProtocolImpl.ReadI64: Int64;
+begin
+ result := zigzagToLong( ReadVarint64);
+end;
+
+
+// No magic here - just Read a double off the wire.
+function TCompactProtocolImpl.ReadDouble:Double;
+var longBits : TBytes;
+begin
+ SetLength( longBits, 8);
+ Transport.ReadAll( longBits, 0, 8);
+ result := Int64BitsToDouble( bytesToLong( longBits));
+end;
+
+
+// Read a byte[] from the wire.
+function TCompactProtocolImpl.ReadBinary: TBytes;
+var length : Integer;
+begin
+ length := Integer( ReadVarint32);
+ SetLength( result, length);
+ if (length > 0)
+ then Transport.ReadAll( result, 0, length);
+end;
+
+
+procedure TCompactProtocolImpl.ReadMessageEnd;
+begin
+ // nothing to do
+end;
+
+
+procedure TCompactProtocolImpl.ReadFieldEnd;
+begin
+ // nothing to do
+end;
+
+
+procedure TCompactProtocolImpl.ReadMapEnd;
+begin
+ // nothing to do
+end;
+
+
+procedure TCompactProtocolImpl.ReadListEnd;
+begin
+ // nothing to do
+end;
+
+
+procedure TCompactProtocolImpl.ReadSetEnd;
+begin
+ // nothing to do
+end;
+
+
+
+// Read an i32 from the wire as a varint. The MSB of each byte is set
+// if there is another byte to follow. This can Read up to 5 bytes.
+function TCompactProtocolImpl.ReadVarint32 : Cardinal;
+var shift : Integer;
+ b : Byte;
+begin
+ result := 0;
+ shift := 0;
+ while TRUE do begin
+ b := Byte( ReadByte);
+ result := result or (Cardinal(b and $7F) shl shift);
+ if ((b and $80) <> $80)
+ then Break;
+ Inc( shift, 7);
+ end;
+end;
+
+
+// Read an i64 from the wire as a proper varint. The MSB of each byte is set
+// if there is another byte to follow. This can Read up to 10 bytes.
+function TCompactProtocolImpl.ReadVarint64 : UInt64;
+var shift : Integer;
+ b : Byte;
+begin
+ result := 0;
+ shift := 0;
+ while TRUE do begin
+ b := Byte( ReadByte);
+ result := result or (UInt64(b and $7F) shl shift);
+ if ((b and $80) <> $80)
+ then Break;
+ Inc( shift, 7);
+ end;
+end;
+
+
+// Convert from zigzag Integer to Integer.
+class function TCompactProtocolImpl.zigzagToInt( const n : Cardinal ) : Integer;
+begin
+ result := Integer(n shr 1) xor (-Integer(n and 1));
+end;
+
+
+// Convert from zigzag Int64 to Int64.
+class function TCompactProtocolImpl.zigzagToLong( const n : UInt64) : Int64;
+begin
+ result := Int64(n shr 1) xor (-Int64(n and 1));
+end;
+
+
+// Note that it's important that the mask bytes are Int64 literals,
+// otherwise they'll default to ints, and when you shift an Integer left 56 bits,
+// you just get a messed up Integer.
+class function TCompactProtocolImpl.bytesToLong( const bytes : TBytes) : Int64;
+begin
+ ASSERT( Length(bytes) >= 8);
+ result := (Int64(bytes[7] and $FF) shl 56) or
+ (Int64(bytes[6] and $FF) shl 48) or
+ (Int64(bytes[5] and $FF) shl 40) or
+ (Int64(bytes[4] and $FF) shl 32) or
+ (Int64(bytes[3] and $FF) shl 24) or
+ (Int64(bytes[2] and $FF) shl 16) or
+ (Int64(bytes[1] and $FF) shl 8) or
+ (Int64(bytes[0] and $FF));
+end;
+
+
+class function TCompactProtocolImpl.isBoolType( const b : byte) : Boolean;
+var lowerNibble : Byte;
+begin
+ lowerNibble := b and $0f;
+ result := (Types(lowerNibble) in [Types.BOOLEAN_TRUE, Types.BOOLEAN_FALSE]);
+end;
+
+
+// Given a TCompactProtocol.Types constant, convert it to its corresponding TType value.
+class function TCompactProtocolImpl.getTType( const type_ : byte) : TType;
+var tct : Types;
+begin
+ tct := Types( type_ and $0F);
+ if tct in [Low(Types)..High(Types)]
+ then result := tcompactTypeToType[tct]
+ else raise TProtocolExceptionInvalidData.Create('don''t know what type: '+IntToStr(Ord(tct)));
+end;
+
+
+// Given a TType value, find the appropriate TCompactProtocol.Types constant.
+class function TCompactProtocolImpl.getCompactType( const ttype : TType) : Byte;
+begin
+ if ttype in VALID_TTYPES
+ then result := Byte( ttypeToCompactType[ttype])
+ else raise TProtocolExceptionInvalidData.Create('don''t know what type: '+IntToStr(Ord(ttype)));
+end;
+
+
+//--- unit tests -------------------------------------------
+
+{$IFDEF Debug}
+procedure TestDoubleToInt64Bits;
+
+ procedure TestPair( const a : Double; const b : Int64);
+ begin
+ ASSERT( TCompactProtocolImpl.DoubleToInt64Bits(a) = b);
+ ASSERT( TCompactProtocolImpl.Int64BitsToDouble(b) = a);
+ end;
+
+begin
+ TestPair( 1.0000000000000000E+000, Int64($3FF0000000000000));
+ TestPair( 1.5000000000000000E+001, Int64($402E000000000000));
+ TestPair( 2.5500000000000000E+002, Int64($406FE00000000000));
+ TestPair( 4.2949672950000000E+009, Int64($41EFFFFFFFE00000));
+ TestPair( 3.9062500000000000E-003, Int64($3F70000000000000));
+ TestPair( 2.3283064365386963E-010, Int64($3DF0000000000000));
+ TestPair( 1.2345678901230000E-300, Int64($01AA74FE1C1E7E45));
+ TestPair( 1.2345678901234500E-150, Int64($20D02A36586DB4BB));
+ TestPair( 1.2345678901234565E+000, Int64($3FF3C0CA428C59FA));
+ TestPair( 1.2345678901234567E+000, Int64($3FF3C0CA428C59FB));
+ TestPair( 1.2345678901234569E+000, Int64($3FF3C0CA428C59FC));
+ TestPair( 1.2345678901234569E+150, Int64($5F182344CD3CDF9F));
+ TestPair( 1.2345678901234569E+300, Int64($7E3D7EE8BCBBD352));
+ TestPair( -1.7976931348623157E+308, Int64($FFEFFFFFFFFFFFFF));
+ TestPair( 1.7976931348623157E+308, Int64($7FEFFFFFFFFFFFFF));
+ TestPair( 4.9406564584124654E-324, Int64($0000000000000001));
+ TestPair( 0.0000000000000000E+000, Int64($0000000000000000));
+ TestPair( 4.94065645841247E-324, Int64($0000000000000001));
+ TestPair( 3.2378592100206092E-319, Int64($000000000000FFFF));
+ TestPair( 1.3906711615669959E-309, Int64($0000FFFFFFFFFFFF));
+ TestPair( NegInfinity, Int64($FFF0000000000000));
+ TestPair( Infinity, Int64($7FF0000000000000));
+
+ // NaN is special
+ ASSERT( TCompactProtocolImpl.DoubleToInt64Bits( NaN) = Int64($FFF8000000000000));
+ ASSERT( IsNan( TCompactProtocolImpl.Int64BitsToDouble( Int64($FFF8000000000000))));
+end;
+{$ENDIF}
+
+
+{$IFDEF Debug}
+procedure TestZigZag;
+
+ procedure Test32( const test : Integer);
+ var zz : Cardinal;
+ begin
+ zz := TCompactProtocolImpl.intToZigZag(test);
+ ASSERT( TCompactProtocolImpl.zigzagToInt(zz) = test, IntToStr(test));
+ end;
+
+ procedure Test64( const test : Int64);
+ var zz : UInt64;
+ begin
+ zz := TCompactProtocolImpl.longToZigzag(test);
+ ASSERT( TCompactProtocolImpl.zigzagToLong(zz) = test, IntToStr(test));
+ end;
+
+var i : Integer;
+begin
+ // protobuf testcases
+ ASSERT( TCompactProtocolImpl.intToZigZag(0) = 0, 'pb #1 to ZigZag');
+ ASSERT( TCompactProtocolImpl.intToZigZag(-1) = 1, 'pb #2 to ZigZag');
+ ASSERT( TCompactProtocolImpl.intToZigZag(1) = 2, 'pb #3 to ZigZag');
+ ASSERT( TCompactProtocolImpl.intToZigZag(-2) = 3, 'pb #4 to ZigZag');
+ ASSERT( TCompactProtocolImpl.intToZigZag(+2147483647) = 4294967294, 'pb #5 to ZigZag');
+ ASSERT( TCompactProtocolImpl.intToZigZag(-2147483648) = 4294967295, 'pb #6 to ZigZag');
+
+ // protobuf testcases
+ ASSERT( TCompactProtocolImpl.zigzagToInt(0) = 0, 'pb #1 from ZigZag');
+ ASSERT( TCompactProtocolImpl.zigzagToInt(1) = -1, 'pb #2 from ZigZag');
+ ASSERT( TCompactProtocolImpl.zigzagToInt(2) = 1, 'pb #3 from ZigZag');
+ ASSERT( TCompactProtocolImpl.zigzagToInt(3) = -2, 'pb #4 from ZigZag');
+ ASSERT( TCompactProtocolImpl.zigzagToInt(4294967294) = +2147483647, 'pb #5 from ZigZag');
+ ASSERT( TCompactProtocolImpl.zigzagToInt(4294967295) = -2147483648, 'pb #6 from ZigZag');
+
+ // back and forth 32
+ Test32( 0);
+ for i := 0 to 30 do begin
+ Test32( +(Integer(1) shl i));
+ Test32( -(Integer(1) shl i));
+ end;
+ Test32( Integer($7FFFFFFF));
+ Test32( Integer($80000000));
+
+ // back and forth 64
+ Test64( 0);
+ for i := 0 to 62 do begin
+ Test64( +(Int64(1) shl i));
+ Test64( -(Int64(1) shl i));
+ end;
+ Test64( Int64($7FFFFFFFFFFFFFFF));
+ Test64( Int64($8000000000000000));
+end;
+{$ENDIF}
+
+
+{$IFDEF Debug}
+procedure TestLongBytes;
+
+ procedure Test( const test : Int64);
+ var buf : TBytes;
+ begin
+ TCompactProtocolImpl.fixedLongToBytes( test, buf);
+ ASSERT( TCompactProtocolImpl.bytesToLong( buf) = test, IntToStr(test));
+ end;
+
+var i : Integer;
+begin
+ Test( 0);
+ for i := 0 to 62 do begin
+ Test( +(Int64(1) shl i));
+ Test( -(Int64(1) shl i));
+ end;
+ Test( Int64($7FFFFFFFFFFFFFFF));
+ Test( Int64($8000000000000000));
+end;
+{$ENDIF}
+
+
+{$IFDEF Debug}
+procedure UnitTest;
+var w : WORD;
+const FPU_CW_DENORMALIZED = $0002;
+begin
+ w := Get8087CW;
+ try
+ Set8087CW( w or FPU_CW_DENORMALIZED);
+
+ TestDoubleToInt64Bits;
+ TestZigZag;
+ TestLongBytes;
+
+ finally
+ Set8087CW( w);
+ end;
+end;
+{$ENDIF}
+
+
+initialization
+ {$IFDEF Debug}
+ UnitTest;
+ {$ENDIF}
+
+end.
+
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.JSON.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.JSON.pas
new file mode 100644
index 000000000..30600aa80
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.JSON.pas
@@ -0,0 +1,1237 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+{$SCOPEDENUMS ON}
+
+unit Thrift.Protocol.JSON;
+
+interface
+
+uses
+ Character,
+ Classes,
+ SysUtils,
+ Math,
+ Generics.Collections,
+ Thrift.Transport,
+ Thrift.Protocol,
+ Thrift.Utils;
+
+type
+ IJSONProtocol = interface( IProtocol)
+ ['{F0DAFDBD-692A-4B71-9736-F5D485A2178F}']
+ // Read a byte that must match b; otherwise an exception is thrown.
+ procedure ReadJSONSyntaxChar( b : Byte);
+ end;
+
+ // JSON protocol implementation for thrift.
+ // This is a full-featured protocol supporting Write and Read.
+ // Please see the C++ class header for a detailed description of the protocol's wire format.
+ // Adapted from the C# version.
+ TJSONProtocolImpl = class( TProtocolImpl, IJSONProtocol)
+ public
+ type
+ TFactory = class( TInterfacedObject, IProtocolFactory)
+ public
+ function GetProtocol( const trans: ITransport): IProtocol;
+ end;
+
+ private
+ class function GetTypeNameForTypeID(typeID : TType) : string;
+ class function GetTypeIDForTypeName( const name : string) : TType;
+
+ protected
+ type
+ // Base class for tracking JSON contexts that may require
+ // inserting/Reading additional JSON syntax characters.
+ // This base context does nothing.
+ TJSONBaseContext = class
+ protected
+ FProto : Pointer; // weak IJSONProtocol;
+ public
+ constructor Create( const aProto : IJSONProtocol);
+ procedure Write; virtual;
+ procedure Read; virtual;
+ function EscapeNumbers : Boolean; virtual;
+ end;
+
+ // Context for JSON lists.
+ // Will insert/Read commas before each item except for the first one.
+ TJSONListContext = class( TJSONBaseContext)
+ private
+ FFirst : Boolean;
+ public
+ constructor Create( const aProto : IJSONProtocol);
+ procedure Write; override;
+ procedure Read; override;
+ end;
+
+ // Context for JSON records. Will insert/Read colons before the value portion of each record
+ // pair, and commas before each key except the first. In addition, will indicate that numbers
+ // in the key position need to be escaped in quotes (since JSON keys must be strings).
+ TJSONPairContext = class( TJSONBaseContext)
+ private
+ FFirst, FColon : Boolean;
+ public
+ constructor Create( const aProto : IJSONProtocol);
+ procedure Write; override;
+ procedure Read; override;
+ function EscapeNumbers : Boolean; override;
+ end;
+
+ // Holds up to one byte from the transport
+ TLookaheadReader = class
+ protected
+ FProto : Pointer; // weak IJSONProtocol;
+ constructor Create( const aProto : IJSONProtocol);
+
+ private
+ FHasData : Boolean;
+ FData : Byte;
+
+ public
+ // Return and consume the next byte to be Read, either taking it from the
+ // data buffer if present or getting it from the transport otherwise.
+ function Read : Byte;
+
+ // Return the next byte to be Read without consuming, filling the data
+ // buffer if it has not been filled alReady.
+ function Peek : Byte;
+ end;
+
+ protected
+ // Stack of nested contexts that we may be in
+ FContextStack : TStack<TJSONBaseContext>;
+
+ // Current context that we are in
+ FContext : TJSONBaseContext;
+
+ // Reader that manages a 1-byte buffer
+ FReader : TLookaheadReader;
+
+ // Push/pop a new JSON context onto/from the stack.
+ procedure ResetContextStack;
+ procedure PushContext( const aCtx : TJSONBaseContext);
+ procedure PopContext;
+
+ public
+ // TJSONProtocolImpl Constructor
+ constructor Create( const aTrans : ITransport);
+ destructor Destroy; override;
+
+ protected
+ // IJSONProtocol
+ // Read a byte that must match b; otherwise an exception is thrown.
+ procedure ReadJSONSyntaxChar( b : Byte);
+
+ private
+ // Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its corresponding hex value
+ class function HexVal( ch : Byte) : Byte;
+
+ // Convert a byte containing a hex value to its corresponding hex character
+ class function HexChar( val : Byte) : Byte;
+
+ // Write the bytes in array buf as a JSON characters, escaping as needed
+ procedure WriteJSONString( const b : TBytes); overload;
+ procedure WriteJSONString( const str : string); overload;
+
+ // Write out number as a JSON value. If the context dictates so, it will be
+ // wrapped in quotes to output as a JSON string.
+ procedure WriteJSONInteger( const num : Int64);
+
+ // Write out a double as a JSON value. If it is NaN or infinity or if the
+ // context dictates escaping, Write out as JSON string.
+ procedure WriteJSONDouble( const num : Double);
+
+ // Write out contents of byte array b as a JSON string with base-64 encoded data
+ procedure WriteJSONBase64( const b : TBytes);
+
+ procedure WriteJSONObjectStart;
+ procedure WriteJSONObjectEnd;
+ procedure WriteJSONArrayStart;
+ procedure WriteJSONArrayEnd;
+
+ public
+ // IProtocol
+ procedure WriteMessageBegin( const aMsg : TThriftMessage); override;
+ procedure WriteMessageEnd; override;
+ procedure WriteStructBegin( const struc: TThriftStruct); override;
+ procedure WriteStructEnd; override;
+ procedure WriteFieldBegin( const field: TThriftField); override;
+ procedure WriteFieldEnd; override;
+ procedure WriteFieldStop; override;
+ procedure WriteMapBegin( const map: TThriftMap); override;
+ procedure WriteMapEnd; override;
+ procedure WriteListBegin( const list: TThriftList); override;
+ procedure WriteListEnd(); override;
+ procedure WriteSetBegin( const set_: TThriftSet ); override;
+ procedure WriteSetEnd(); override;
+ procedure WriteBool( b: Boolean); override;
+ procedure WriteByte( b: ShortInt); override;
+ procedure WriteI16( i16: SmallInt); override;
+ procedure WriteI32( i32: Integer); override;
+ procedure WriteI64( const i64: Int64); override;
+ procedure WriteDouble( const d: Double); override;
+ procedure WriteString( const s: string ); override;
+ procedure WriteBinary( const b: TBytes); override;
+ //
+ function ReadMessageBegin: TThriftMessage; override;
+ procedure ReadMessageEnd(); override;
+ function ReadStructBegin: TThriftStruct; override;
+ procedure ReadStructEnd; override;
+ function ReadFieldBegin: TThriftField; override;
+ procedure ReadFieldEnd(); override;
+ function ReadMapBegin: TThriftMap; override;
+ procedure ReadMapEnd(); override;
+ function ReadListBegin: TThriftList; override;
+ procedure ReadListEnd(); override;
+ function ReadSetBegin: TThriftSet; override;
+ procedure ReadSetEnd(); override;
+ function ReadBool: Boolean; override;
+ function ReadByte: ShortInt; override;
+ function ReadI16: SmallInt; override;
+ function ReadI32: Integer; override;
+ function ReadI64: Int64; override;
+ function ReadDouble:Double; override;
+ function ReadString : string; override;
+ function ReadBinary: TBytes; override;
+
+
+ private
+ // Reading methods.
+
+ // Read in a JSON string, unescaping as appropriate.
+ // Skip Reading from the context if skipContext is true.
+ function ReadJSONString( skipContext : Boolean) : TBytes;
+
+ // Return true if the given byte could be a valid part of a JSON number.
+ function IsJSONNumeric( b : Byte) : Boolean;
+
+ // Read in a sequence of characters that are all valid in JSON numbers. Does
+ // not do a complete regex check to validate that this is actually a number.
+ function ReadJSONNumericChars : String;
+
+ // Read in a JSON number. If the context dictates, Read in enclosing quotes.
+ function ReadJSONInteger : Int64;
+
+ // Read in a JSON double value. Throw if the value is not wrapped in quotes
+ // when expected or if wrapped in quotes when not expected.
+ function ReadJSONDouble : Double;
+
+ // Read in a JSON string containing base-64 encoded data and decode it.
+ function ReadJSONBase64 : TBytes;
+
+ procedure ReadJSONObjectStart;
+ procedure ReadJSONObjectEnd;
+ procedure ReadJSONArrayStart;
+ procedure ReadJSONArrayEnd;
+ end;
+
+
+implementation
+
+var
+ COMMA : TBytes;
+ COLON : TBytes;
+ LBRACE : TBytes;
+ RBRACE : TBytes;
+ LBRACKET : TBytes;
+ RBRACKET : TBytes;
+ QUOTE : TBytes;
+ BACKSLASH : TBytes;
+ ESCSEQ : TBytes;
+
+const
+ VERSION = 1;
+ JSON_CHAR_TABLE : array[0..$2F] of Byte
+ = (0,0,0,0, 0,0,0,0, Byte('b'),Byte('t'),Byte('n'),0, Byte('f'),Byte('r'),0,0,
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ 1,1,Byte('"'),1, 1,1,1,1, 1,1,1,1, 1,1,1,1);
+
+ ESCAPE_CHARS = '"\/btnfr';
+ ESCAPE_CHAR_VALS = '"\/'#8#9#10#12#13;
+
+ DEF_STRING_SIZE = 16;
+
+ NAME_BOOL = 'tf';
+ NAME_BYTE = 'i8';
+ NAME_I16 = 'i16';
+ NAME_I32 = 'i32';
+ NAME_I64 = 'i64';
+ NAME_DOUBLE = 'dbl';
+ NAME_STRUCT = 'rec';
+ NAME_STRING = 'str';
+ NAME_MAP = 'map';
+ NAME_LIST = 'lst';
+ NAME_SET = 'set';
+
+ INVARIANT_CULTURE : TFormatSettings
+ = ( ThousandSeparator: ',';
+ DecimalSeparator: '.');
+
+
+
+//--- TJSONProtocolImpl ----------------------
+
+
+function TJSONProtocolImpl.TFactory.GetProtocol( const trans: ITransport): IProtocol;
+begin
+ result := TJSONProtocolImpl.Create(trans);
+end;
+
+class function TJSONProtocolImpl.GetTypeNameForTypeID(typeID : TType) : string;
+begin
+ case typeID of
+ TType.Bool_: result := NAME_BOOL;
+ TType.Byte_: result := NAME_BYTE;
+ TType.I16: result := NAME_I16;
+ TType.I32: result := NAME_I32;
+ TType.I64: result := NAME_I64;
+ TType.Double_: result := NAME_DOUBLE;
+ TType.String_: result := NAME_STRING;
+ TType.Struct: result := NAME_STRUCT;
+ TType.Map: result := NAME_MAP;
+ TType.Set_: result := NAME_SET;
+ TType.List: result := NAME_LIST;
+ else
+ raise TProtocolExceptionNotImplemented.Create('Unrecognized type ('+IntToStr(Ord(typeID))+')');
+ end;
+end;
+
+
+class function TJSONProtocolImpl.GetTypeIDForTypeName( const name : string) : TType;
+begin
+ if name = NAME_BOOL then result := TType.Bool_
+ else if name = NAME_BYTE then result := TType.Byte_
+ else if name = NAME_I16 then result := TType.I16
+ else if name = NAME_I32 then result := TType.I32
+ else if name = NAME_I64 then result := TType.I64
+ else if name = NAME_DOUBLE then result := TType.Double_
+ else if name = NAME_STRUCT then result := TType.Struct
+ else if name = NAME_STRING then result := TType.String_
+ else if name = NAME_MAP then result := TType.Map
+ else if name = NAME_LIST then result := TType.List
+ else if name = NAME_SET then result := TType.Set_
+ else raise TProtocolExceptionNotImplemented.Create('Unrecognized type ('+name+')');
+end;
+
+
+constructor TJSONProtocolImpl.TJSONBaseContext.Create( const aProto : IJSONProtocol);
+begin
+ inherited Create;
+ FProto := Pointer(aProto);
+end;
+
+
+procedure TJSONProtocolImpl.TJSONBaseContext.Write;
+begin
+ // nothing
+end;
+
+
+procedure TJSONProtocolImpl.TJSONBaseContext.Read;
+begin
+ // nothing
+end;
+
+
+function TJSONProtocolImpl.TJSONBaseContext.EscapeNumbers : Boolean;
+begin
+ result := FALSE;
+end;
+
+
+constructor TJSONProtocolImpl.TJSONListContext.Create( const aProto : IJSONProtocol);
+begin
+ inherited Create( aProto);
+ FFirst := TRUE;
+end;
+
+
+procedure TJSONProtocolImpl.TJSONListContext.Write;
+begin
+ if FFirst
+ then FFirst := FALSE
+ else IJSONProtocol(FProto).Transport.Write( COMMA);
+end;
+
+
+procedure TJSONProtocolImpl.TJSONListContext.Read;
+begin
+ if FFirst
+ then FFirst := FALSE
+ else IJSONProtocol(FProto).ReadJSONSyntaxChar( COMMA[0]);
+end;
+
+
+constructor TJSONProtocolImpl.TJSONPairContext.Create( const aProto : IJSONProtocol);
+begin
+ inherited Create( aProto);
+ FFirst := TRUE;
+ FColon := TRUE;
+end;
+
+
+procedure TJSONProtocolImpl.TJSONPairContext.Write;
+begin
+ if FFirst then begin
+ FFirst := FALSE;
+ FColon := TRUE;
+ end
+ else begin
+ if FColon
+ then IJSONProtocol(FProto).Transport.Write( COLON)
+ else IJSONProtocol(FProto).Transport.Write( COMMA);
+ FColon := not FColon;
+ end;
+end;
+
+
+procedure TJSONProtocolImpl.TJSONPairContext.Read;
+begin
+ if FFirst then begin
+ FFirst := FALSE;
+ FColon := TRUE;
+ end
+ else begin
+ if FColon
+ then IJSONProtocol(FProto).ReadJSONSyntaxChar( COLON[0])
+ else IJSONProtocol(FProto).ReadJSONSyntaxChar( COMMA[0]);
+ FColon := not FColon;
+ end;
+end;
+
+
+function TJSONProtocolImpl.TJSONPairContext.EscapeNumbers : Boolean;
+begin
+ result := FColon;
+end;
+
+
+constructor TJSONProtocolImpl.TLookaheadReader.Create( const aProto : IJSONProtocol);
+begin
+ inherited Create;
+ FProto := Pointer(aProto);
+ FHasData := FALSE;
+end;
+
+
+function TJSONProtocolImpl.TLookaheadReader.Read : Byte;
+begin
+ if FHasData
+ then FHasData := FALSE
+ else begin
+ IJSONProtocol(FProto).Transport.ReadAll( @FData, SizeOf(FData), 0, 1);
+ end;
+ result := FData;
+end;
+
+
+function TJSONProtocolImpl.TLookaheadReader.Peek : Byte;
+begin
+ if not FHasData then begin
+ IJSONProtocol(FProto).Transport.ReadAll( @FData, SizeOf(FData), 0, 1);
+ FHasData := TRUE;
+ end;
+ result := FData;
+end;
+
+
+constructor TJSONProtocolImpl.Create( const aTrans : ITransport);
+begin
+ inherited Create( aTrans);
+
+ // Stack of nested contexts that we may be in
+ FContextStack := TStack<TJSONBaseContext>.Create;
+
+ FContext := TJSONBaseContext.Create( Self);
+ FReader := TLookaheadReader.Create( Self);
+end;
+
+
+destructor TJSONProtocolImpl.Destroy;
+begin
+ try
+ ResetContextStack; // free any contents
+ FreeAndNil( FReader);
+ FreeAndNil( FContext);
+ FreeAndNil( FContextStack);
+ finally
+ inherited Destroy;
+ end;
+end;
+
+
+procedure TJSONProtocolImpl.ResetContextStack;
+begin
+ while FContextStack.Count > 0
+ do PopContext;
+end;
+
+
+procedure TJSONProtocolImpl.PushContext( const aCtx : TJSONBaseContext);
+begin
+ FContextStack.Push( FContext);
+ FContext := aCtx;
+end;
+
+
+procedure TJSONProtocolImpl.PopContext;
+begin
+ FreeAndNil(FContext);
+ FContext := FContextStack.Pop;
+end;
+
+
+procedure TJSONProtocolImpl.ReadJSONSyntaxChar( b : Byte);
+var ch : Byte;
+begin
+ ch := FReader.Read;
+ if (ch <> b)
+ then raise TProtocolExceptionInvalidData.Create('Unexpected character ('+Char(ch)+')');
+end;
+
+
+class function TJSONProtocolImpl.HexVal( ch : Byte) : Byte;
+var i : Integer;
+begin
+ i := StrToIntDef( '$0'+Char(ch), -1);
+ if (0 <= i) and (i < $10)
+ then result := i
+ else raise TProtocolExceptionInvalidData.Create('Expected hex character ('+Char(ch)+')');
+end;
+
+
+class function TJSONProtocolImpl.HexChar( val : Byte) : Byte;
+const HEXCHARS = '0123456789ABCDEF';
+begin
+ result := Byte( PChar(HEXCHARS)[val and $0F]);
+ ASSERT( Pos( Char(result), HEXCHARS) > 0);
+end;
+
+
+procedure TJSONProtocolImpl.WriteJSONString( const str : string);
+begin
+ WriteJSONString( SysUtils.TEncoding.UTF8.GetBytes( str));
+end;
+
+
+procedure TJSONProtocolImpl.WriteJSONString( const b : TBytes);
+var i : Integer;
+ tmp : TBytes;
+begin
+ FContext.Write;
+ Transport.Write( QUOTE);
+ for i := 0 to Length(b)-1 do begin
+
+ if (b[i] and $00FF) >= $30 then begin
+
+ if (b[i] = BACKSLASH[0]) then begin
+ Transport.Write( BACKSLASH);
+ Transport.Write( BACKSLASH);
+ end
+ else begin
+ Transport.Write( b, i, 1);
+ end;
+
+ end
+ else begin
+ SetLength( tmp, 2);
+ tmp[0] := JSON_CHAR_TABLE[b[i]];
+ if (tmp[0] = 1) then begin
+ Transport.Write( b, i, 1)
+ end
+ else if (tmp[0] > 1) then begin
+ Transport.Write( BACKSLASH);
+ Transport.Write( tmp, 0, 1);
+ end
+ else begin
+ Transport.Write( ESCSEQ);
+ tmp[0] := HexChar( b[i] div $10);
+ tmp[1] := HexChar( b[i]);
+ Transport.Write( tmp, 0, 2);
+ end;
+ end;
+ end;
+ Transport.Write( QUOTE);
+end;
+
+
+procedure TJSONProtocolImpl.WriteJSONInteger( const num : Int64);
+var str : String;
+ escapeNum : Boolean;
+begin
+ FContext.Write;
+ str := IntToStr(num);
+
+ escapeNum := FContext.EscapeNumbers;
+ if escapeNum
+ then Transport.Write( QUOTE);
+
+ Transport.Write( SysUtils.TEncoding.UTF8.GetBytes( str));
+
+ if escapeNum
+ then Transport.Write( QUOTE);
+end;
+
+
+procedure TJSONProtocolImpl.WriteJSONDouble( const num : Double);
+var str : string;
+ special : Boolean;
+ escapeNum : Boolean;
+begin
+ FContext.Write;
+
+ str := FloatToStr( num, INVARIANT_CULTURE);
+ special := FALSE;
+
+ case UpCase(str[1]) of
+ 'N' : special := TRUE; // NaN
+ 'I' : special := TRUE; // Infinity
+ '-' : special := (UpCase(str[2]) = 'I'); // -Infinity
+ end;
+
+ escapeNum := special or FContext.EscapeNumbers;
+
+
+ if escapeNum
+ then Transport.Write( QUOTE);
+
+ Transport.Write( SysUtils.TEncoding.UTF8.GetBytes( str));
+
+ if escapeNum
+ then Transport.Write( QUOTE);
+end;
+
+
+procedure TJSONProtocolImpl.WriteJSONBase64( const b : TBytes);
+var len, off, cnt : Integer;
+ tmpBuf : TBytes;
+begin
+ FContext.Write;
+ Transport.Write( QUOTE);
+
+ len := Length(b);
+ off := 0;
+ SetLength( tmpBuf, 4);
+
+ while len >= 3 do begin
+ // Encode 3 bytes at a time
+ Base64Utils.Encode( b, off, 3, tmpBuf, 0);
+ Transport.Write( tmpBuf, 0, 4);
+ Inc( off, 3);
+ Dec( len, 3);
+ end;
+
+ // Encode remainder, if any
+ if len > 0 then begin
+ cnt := Base64Utils.Encode( b, off, len, tmpBuf, 0);
+ Transport.Write( tmpBuf, 0, cnt);
+ end;
+
+ Transport.Write( QUOTE);
+end;
+
+
+procedure TJSONProtocolImpl.WriteJSONObjectStart;
+begin
+ FContext.Write;
+ Transport.Write( LBRACE);
+ PushContext( TJSONPairContext.Create( Self));
+end;
+
+
+procedure TJSONProtocolImpl.WriteJSONObjectEnd;
+begin
+ PopContext;
+ Transport.Write( RBRACE);
+end;
+
+
+procedure TJSONProtocolImpl.WriteJSONArrayStart;
+begin
+ FContext.Write;
+ Transport.Write( LBRACKET);
+ PushContext( TJSONListContext.Create( Self));
+end;
+
+
+procedure TJSONProtocolImpl.WriteJSONArrayEnd;
+begin
+ PopContext;
+ Transport.Write( RBRACKET);
+end;
+
+
+procedure TJSONProtocolImpl.WriteMessageBegin( const aMsg : TThriftMessage);
+begin
+ ResetContextStack; // THRIFT-1473
+
+ WriteJSONArrayStart;
+ WriteJSONInteger(VERSION);
+
+ WriteJSONString( SysUtils.TEncoding.UTF8.GetBytes( aMsg.Name));
+
+ WriteJSONInteger( LongInt( aMsg.Type_));
+ WriteJSONInteger( aMsg.SeqID);
+end;
+
+procedure TJSONProtocolImpl.WriteMessageEnd;
+begin
+ WriteJSONArrayEnd;
+end;
+
+
+procedure TJSONProtocolImpl.WriteStructBegin( const struc: TThriftStruct);
+begin
+ WriteJSONObjectStart;
+end;
+
+
+procedure TJSONProtocolImpl.WriteStructEnd;
+begin
+ WriteJSONObjectEnd;
+end;
+
+
+procedure TJSONProtocolImpl.WriteFieldBegin( const field : TThriftField);
+begin
+ WriteJSONInteger(field.ID);
+ WriteJSONObjectStart;
+ WriteJSONString( GetTypeNameForTypeID(field.Type_));
+end;
+
+
+procedure TJSONProtocolImpl.WriteFieldEnd;
+begin
+ WriteJSONObjectEnd;
+end;
+
+
+procedure TJSONProtocolImpl.WriteFieldStop;
+begin
+ // nothing to do
+end;
+
+procedure TJSONProtocolImpl.WriteMapBegin( const map: TThriftMap);
+begin
+ WriteJSONArrayStart;
+ WriteJSONString( GetTypeNameForTypeID( map.KeyType));
+ WriteJSONString( GetTypeNameForTypeID( map.ValueType));
+ WriteJSONInteger( map.Count);
+ WriteJSONObjectStart;
+end;
+
+
+procedure TJSONProtocolImpl.WriteMapEnd;
+begin
+ WriteJSONObjectEnd;
+ WriteJSONArrayEnd;
+end;
+
+
+procedure TJSONProtocolImpl.WriteListBegin( const list: TThriftList);
+begin
+ WriteJSONArrayStart;
+ WriteJSONString( GetTypeNameForTypeID( list.ElementType));
+ WriteJSONInteger(list.Count);
+end;
+
+
+procedure TJSONProtocolImpl.WriteListEnd;
+begin
+ WriteJSONArrayEnd;
+end;
+
+
+procedure TJSONProtocolImpl.WriteSetBegin( const set_: TThriftSet);
+begin
+ WriteJSONArrayStart;
+ WriteJSONString( GetTypeNameForTypeID( set_.ElementType));
+ WriteJSONInteger( set_.Count);
+end;
+
+
+procedure TJSONProtocolImpl.WriteSetEnd;
+begin
+ WriteJSONArrayEnd;
+end;
+
+procedure TJSONProtocolImpl.WriteBool( b: Boolean);
+begin
+ if b
+ then WriteJSONInteger( 1)
+ else WriteJSONInteger( 0);
+end;
+
+procedure TJSONProtocolImpl.WriteByte( b: ShortInt);
+begin
+ WriteJSONInteger( b);
+end;
+
+procedure TJSONProtocolImpl.WriteI16( i16: SmallInt);
+begin
+ WriteJSONInteger( i16);
+end;
+
+procedure TJSONProtocolImpl.WriteI32( i32: Integer);
+begin
+ WriteJSONInteger( i32);
+end;
+
+procedure TJSONProtocolImpl.WriteI64( const i64: Int64);
+begin
+ WriteJSONInteger(i64);
+end;
+
+procedure TJSONProtocolImpl.WriteDouble( const d: Double);
+begin
+ WriteJSONDouble( d);
+end;
+
+procedure TJSONProtocolImpl.WriteString( const s: string );
+begin
+ WriteJSONString( SysUtils.TEncoding.UTF8.GetBytes( s));
+end;
+
+procedure TJSONProtocolImpl.WriteBinary( const b: TBytes);
+begin
+ WriteJSONBase64( b);
+end;
+
+
+function TJSONProtocolImpl.ReadJSONString( skipContext : Boolean) : TBytes;
+var buffer : TMemoryStream;
+ ch : Byte;
+ wch : Word;
+ highSurogate: Char;
+ surrogatePairs: Array[0..1] of Char;
+ off : Integer;
+ tmp : TBytes;
+begin
+ highSurogate := #0;
+ buffer := TMemoryStream.Create;
+ try
+ if not skipContext
+ then FContext.Read;
+
+ ReadJSONSyntaxChar( QUOTE[0]);
+
+ while TRUE do begin
+ ch := FReader.Read;
+
+ if (ch = QUOTE[0])
+ then Break;
+
+ // check for escapes
+ if (ch <> ESCSEQ[0]) then begin
+ buffer.Write( ch, 1);
+ Continue;
+ end;
+
+ // distuinguish between \uNNNN and \?
+ ch := FReader.Read;
+ if (ch <> ESCSEQ[1])
+ then begin
+ off := Pos( Char(ch), ESCAPE_CHARS);
+ if off < 1
+ then raise TProtocolExceptionInvalidData.Create('Expected control char');
+ ch := Byte( ESCAPE_CHAR_VALS[off]);
+ buffer.Write( ch, 1);
+ Continue;
+ end;
+
+ // it is \uXXXX
+ SetLength( tmp, 4);
+ Transport.ReadAll( tmp, 0, 4);
+ wch := (HexVal(tmp[0]) shl 12)
+ + (HexVal(tmp[1]) shl 8)
+ + (HexVal(tmp[2]) shl 4)
+ + HexVal(tmp[3]);
+
+ // we need to make UTF8 bytes from it, to be decoded later
+ if CharUtils.IsHighSurrogate(char(wch)) then begin
+ if highSurogate <> #0
+ then raise TProtocolExceptionInvalidData.Create('Expected low surrogate char');
+ highSurogate := char(wch);
+ end
+ else if CharUtils.IsLowSurrogate(char(wch)) then begin
+ if highSurogate = #0
+ then TProtocolExceptionInvalidData.Create('Expected high surrogate char');
+ surrogatePairs[0] := highSurogate;
+ surrogatePairs[1] := char(wch);
+ tmp := TEncoding.UTF8.GetBytes(surrogatePairs);
+ buffer.Write( tmp[0], Length(tmp));
+ highSurogate := #0;
+ end
+ else begin
+ tmp := SysUtils.TEncoding.UTF8.GetBytes(Char(wch));
+ buffer.Write( tmp[0], Length(tmp));
+ end;
+ end;
+
+ if highSurogate <> #0
+ then raise TProtocolExceptionInvalidData.Create('Expected low surrogate char');
+
+ SetLength( result, buffer.Size);
+ if buffer.Size > 0 then Move( buffer.Memory^, result[0], Length(result));
+
+ finally
+ buffer.Free;
+ end;
+end;
+
+
+function TJSONProtocolImpl.IsJSONNumeric( b : Byte) : Boolean;
+const NUMCHARS = ['+','-','.','0','1','2','3','4','5','6','7','8','9','E','e'];
+begin
+ result := CharInSet( Char(b), NUMCHARS);
+end;
+
+
+function TJSONProtocolImpl.ReadJSONNumericChars : string;
+var strbld : TThriftStringBuilder;
+ ch : Byte;
+begin
+ strbld := TThriftStringBuilder.Create;
+ try
+ while TRUE do begin
+ ch := FReader.Peek;
+ if IsJSONNumeric(ch)
+ then strbld.Append( Char(FReader.Read))
+ else Break;
+ end;
+ result := strbld.ToString;
+
+ finally
+ strbld.Free;
+ end;
+end;
+
+
+function TJSONProtocolImpl.ReadJSONInteger : Int64;
+var str : string;
+begin
+ FContext.Read;
+ if FContext.EscapeNumbers
+ then ReadJSONSyntaxChar( QUOTE[0]);
+
+ str := ReadJSONNumericChars;
+
+ if FContext.EscapeNumbers
+ then ReadJSONSyntaxChar( QUOTE[0]);
+
+ try
+ result := StrToInt64(str);
+ except
+ on e:Exception do begin
+ raise TProtocolExceptionInvalidData.Create('Bad data encounted in numeric data ('+str+') ('+e.Message+')');
+ end;
+ end;
+end;
+
+
+function TJSONProtocolImpl.ReadJSONDouble : Double;
+var dub : Double;
+ str : string;
+begin
+ FContext.Read;
+
+ if FReader.Peek = QUOTE[0]
+ then begin
+ str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString( TRUE));
+ dub := StrToFloat( str, INVARIANT_CULTURE);
+
+ if not FContext.EscapeNumbers()
+ and not Math.IsNaN(dub)
+ and not Math.IsInfinite(dub)
+ then begin
+ // Throw exception -- we should not be in a string in Self case
+ raise TProtocolExceptionInvalidData.Create('Numeric data unexpectedly quoted');
+ end;
+ result := dub;
+ Exit;
+ end;
+
+ // will throw - we should have had a quote if escapeNum == true
+ if FContext.EscapeNumbers
+ then ReadJSONSyntaxChar( QUOTE[0]);
+
+ try
+ str := ReadJSONNumericChars;
+ result := StrToFloat( str, INVARIANT_CULTURE);
+ except
+ on e:Exception
+ do raise TProtocolExceptionInvalidData.Create('Bad data encounted in numeric data ('+str+') ('+e.Message+')');
+ end;
+end;
+
+
+function TJSONProtocolImpl.ReadJSONBase64 : TBytes;
+var b : TBytes;
+ len, off, size : Integer;
+begin
+ b := ReadJSONString(false);
+
+ len := Length(b);
+ off := 0;
+ size := 0;
+
+ // reduce len to ignore fill bytes
+ Dec(len);
+ while (len >= 0) and (b[len] = Byte('=')) do Dec(len);
+ Inc(len);
+
+ // read & decode full byte triplets = 4 source bytes
+ while (len >= 4) do begin
+ // Decode 4 bytes at a time
+ Inc( size, Base64Utils.Decode( b, off, 4, b, size)); // decoded in place
+ Inc( off, 4);
+ Dec( len, 4);
+ end;
+
+ // Don't decode if we hit the end or got a single leftover byte (invalid
+ // base64 but legal for skip of regular string type)
+ if len > 1 then begin
+ // Decode remainder
+ Inc( size, Base64Utils.Decode( b, off, len, b, size)); // decoded in place
+ end;
+
+ // resize to final size and return the data
+ SetLength( b, size);
+ result := b;
+end;
+
+
+procedure TJSONProtocolImpl.ReadJSONObjectStart;
+begin
+ FContext.Read;
+ ReadJSONSyntaxChar( LBRACE[0]);
+ PushContext( TJSONPairContext.Create( Self));
+end;
+
+
+procedure TJSONProtocolImpl.ReadJSONObjectEnd;
+begin
+ ReadJSONSyntaxChar( RBRACE[0]);
+ PopContext;
+end;
+
+
+procedure TJSONProtocolImpl.ReadJSONArrayStart;
+begin
+ FContext.Read;
+ ReadJSONSyntaxChar( LBRACKET[0]);
+ PushContext( TJSONListContext.Create( Self));
+end;
+
+
+procedure TJSONProtocolImpl.ReadJSONArrayEnd;
+begin
+ ReadJSONSyntaxChar( RBRACKET[0]);
+ PopContext;
+end;
+
+
+function TJSONProtocolImpl.ReadMessageBegin: TThriftMessage;
+begin
+ ResetContextStack; // THRIFT-1473
+
+ Init( result);
+ ReadJSONArrayStart;
+
+ if ReadJSONInteger <> VERSION
+ then raise TProtocolExceptionBadVersion.Create('Message contained bad version.');
+
+ result.Name := SysUtils.TEncoding.UTF8.GetString( ReadJSONString( FALSE));
+ result.Type_ := TMessageType( ReadJSONInteger);
+ result.SeqID := ReadJSONInteger;
+end;
+
+
+procedure TJSONProtocolImpl.ReadMessageEnd;
+begin
+ ReadJSONArrayEnd;
+end;
+
+
+function TJSONProtocolImpl.ReadStructBegin : TThriftStruct ;
+begin
+ ReadJSONObjectStart;
+ Init( result);
+end;
+
+
+procedure TJSONProtocolImpl.ReadStructEnd;
+begin
+ ReadJSONObjectEnd;
+end;
+
+
+function TJSONProtocolImpl.ReadFieldBegin : TThriftField;
+var ch : Byte;
+ str : string;
+begin
+ Init( result);
+ ch := FReader.Peek;
+ if ch = RBRACE[0]
+ then result.Type_ := TType.Stop
+ else begin
+ result.ID := ReadJSONInteger;
+ ReadJSONObjectStart;
+
+ str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString( FALSE));
+ result.Type_ := GetTypeIDForTypeName( str);
+ end;
+end;
+
+
+procedure TJSONProtocolImpl.ReadFieldEnd;
+begin
+ ReadJSONObjectEnd;
+end;
+
+
+function TJSONProtocolImpl.ReadMapBegin : TThriftMap;
+var str : string;
+begin
+ Init( result);
+ ReadJSONArrayStart;
+
+ str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE));
+ result.KeyType := GetTypeIDForTypeName( str);
+
+ str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE));
+ result.ValueType := GetTypeIDForTypeName( str);
+
+ result.Count := ReadJSONInteger;
+ ReadJSONObjectStart;
+end;
+
+
+procedure TJSONProtocolImpl.ReadMapEnd;
+begin
+ ReadJSONObjectEnd;
+ ReadJSONArrayEnd;
+end;
+
+
+function TJSONProtocolImpl.ReadListBegin : TThriftList;
+var str : string;
+begin
+ Init( result);
+ ReadJSONArrayStart;
+
+ str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE));
+ result.ElementType := GetTypeIDForTypeName( str);
+ result.Count := ReadJSONInteger;
+end;
+
+
+procedure TJSONProtocolImpl.ReadListEnd;
+begin
+ ReadJSONArrayEnd;
+end;
+
+
+function TJSONProtocolImpl.ReadSetBegin : TThriftSet;
+var str : string;
+begin
+ Init( result);
+ ReadJSONArrayStart;
+
+ str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE));
+ result.ElementType := GetTypeIDForTypeName( str);
+ result.Count := ReadJSONInteger;
+end;
+
+
+procedure TJSONProtocolImpl.ReadSetEnd;
+begin
+ ReadJSONArrayEnd;
+end;
+
+
+function TJSONProtocolImpl.ReadBool : Boolean;
+begin
+ result := (ReadJSONInteger <> 0);
+end;
+
+
+function TJSONProtocolImpl.ReadByte : ShortInt;
+begin
+ result := ReadJSONInteger;
+end;
+
+
+function TJSONProtocolImpl.ReadI16 : SmallInt;
+begin
+ result := ReadJSONInteger;
+end;
+
+
+function TJSONProtocolImpl.ReadI32 : LongInt;
+begin
+ result := ReadJSONInteger;
+end;
+
+
+function TJSONProtocolImpl.ReadI64 : Int64;
+begin
+ result := ReadJSONInteger;
+end;
+
+
+function TJSONProtocolImpl.ReadDouble : Double;
+begin
+ result := ReadJSONDouble;
+end;
+
+
+function TJSONProtocolImpl.ReadString : string;
+begin
+ result := SysUtils.TEncoding.UTF8.GetString( ReadJSONString( FALSE));
+end;
+
+
+function TJSONProtocolImpl.ReadBinary : TBytes;
+begin
+ result := ReadJSONBase64;
+end;
+
+
+//--- init code ---
+
+procedure InitBytes( var b : TBytes; aData : array of Byte);
+begin
+ SetLength( b, Length(aData));
+ Move( aData, b[0], Length(b));
+end;
+
+initialization
+ InitBytes( COMMA, [Byte(',')]);
+ InitBytes( COLON, [Byte(':')]);
+ InitBytes( LBRACE, [Byte('{')]);
+ InitBytes( RBRACE, [Byte('}')]);
+ InitBytes( LBRACKET, [Byte('[')]);
+ InitBytes( RBRACKET, [Byte(']')]);
+ InitBytes( QUOTE, [Byte('"')]);
+ InitBytes( BACKSLASH, [Byte('\')]);
+ InitBytes( ESCSEQ, [Byte('\'),Byte('u'),Byte('0'),Byte('0')]);
+end.
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.Multiplex.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.Multiplex.pas
new file mode 100644
index 000000000..93a38380d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.Multiplex.pas
@@ -0,0 +1,107 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+unit Thrift.Protocol.Multiplex;
+
+interface
+
+uses Thrift.Protocol;
+
+{ TMultiplexedProtocol is a protocol-independent concrete decorator
+ that allows a Thrift client to communicate with a multiplexing Thrift server,
+ by prepending the service name to the function name during function calls.
+
+ NOTE: THIS IS NOT USED BY SERVERS.
+ On the server, use TMultiplexedProcessor to handle requests from a multiplexing client.
+
+ This example uses a single socket transport to invoke two services:
+
+ TSocket transport = new TSocket("localhost", 9090);
+ transport.open();
+
+ TBinaryProtocol protocol = new TBinaryProtocol(transport);
+
+ TMultiplexedProtocol mp = new TMultiplexedProtocol(protocol, "Calculator");
+ Calculator.Client service = new Calculator.Client(mp);
+
+ TMultiplexedProtocol mp2 = new TMultiplexedProtocol(protocol, "WeatherReport");
+ WeatherReport.Client service2 = new WeatherReport.Client(mp2);
+
+ System.out.println(service.add(2,2));
+ System.out.println(service2.getTemperature());
+
+}
+
+type
+ TMultiplexedProtocol = class( TProtocolDecorator)
+ public const
+ { Used to delimit the service name from the function name }
+ SEPARATOR = ':';
+
+ private
+ FServiceName : String;
+
+ public
+ { Wrap the specified protocol, allowing it to be used to communicate with a multiplexing server.
+ The serviceName is required as it is prepended to the message header so that the multiplexing
+ server can broker the function call to the proper service.
+
+ Args:
+ protocol ....... Your communication protocol of choice, e.g. TBinaryProtocol.
+ serviceName .... The service name of the service communicating via this protocol.
+ }
+ constructor Create( const aProtocol : IProtocol; const aServiceName : string);
+
+ { Prepends the service name to the function name, separated by SEPARATOR.
+ Args: The original message.
+ }
+ procedure WriteMessageBegin( const msg: TThriftMessage); override;
+ end;
+
+
+implementation
+
+
+constructor TMultiplexedProtocol.Create(const aProtocol: IProtocol; const aServiceName: string);
+begin
+ ASSERT( aServiceName <> '');
+ inherited Create(aProtocol);
+ FServiceName := aServiceName;
+end;
+
+
+procedure TMultiplexedProtocol.WriteMessageBegin( const msg: TThriftMessage);
+// Prepends the service name to the function name, separated by TMultiplexedProtocol.SEPARATOR.
+var newMsg : TThriftMessage;
+begin
+ case msg.Type_ of
+ TMessageType.Call,
+ TMessageType.Oneway : begin
+ Init( newMsg, FServiceName + SEPARATOR + msg.Name, msg.Type_, msg.SeqID);
+ inherited WriteMessageBegin( newMsg);
+ end;
+
+ else
+ inherited WriteMessageBegin( msg);
+ end;
+end;
+
+
+end.
+
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.pas
new file mode 100644
index 000000000..609dfc605
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.pas
@@ -0,0 +1,1370 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+{$SCOPEDENUMS ON}
+
+unit Thrift.Protocol;
+
+interface
+
+uses
+ Classes,
+ SysUtils,
+ Contnrs,
+ Thrift.Exception,
+ Thrift.Stream,
+ Thrift.Utils,
+ Thrift.Collections,
+ Thrift.Transport;
+
+type
+
+ TType = (
+ Stop = 0,
+ Void = 1,
+ Bool_ = 2,
+ Byte_ = 3,
+ Double_ = 4,
+ I16 = 6,
+ I32 = 8,
+ I64 = 10,
+ String_ = 11,
+ Struct = 12,
+ Map = 13,
+ Set_ = 14,
+ List = 15
+ );
+
+ TMessageType = (
+ Call = 1,
+ Reply = 2,
+ Exception = 3,
+ Oneway = 4
+ );
+
+const
+ VALID_TTYPES = [
+ TType.Stop, TType.Void,
+ TType.Bool_, TType.Byte_, TType.Double_, TType.I16, TType.I32, TType.I64, TType.String_,
+ TType.Struct, TType.Map, TType.Set_, TType.List
+ ];
+
+ VALID_MESSAGETYPES = [Low(TMessageType)..High(TMessageType)];
+
+const
+ DEFAULT_RECURSION_LIMIT = 64;
+
+type
+ IProtocol = interface;
+
+ TThriftMessage = record
+ Name: string;
+ Type_: TMessageType;
+ SeqID: Integer;
+ end;
+
+ TThriftStruct = record
+ Name: string;
+ end;
+
+ TThriftField = record
+ Name: string;
+ Type_: TType;
+ Id: SmallInt;
+ end;
+
+ TThriftList = record
+ ElementType: TType;
+ Count: Integer;
+ end;
+
+ TThriftMap = record
+ KeyType: TType;
+ ValueType: TType;
+ Count: Integer;
+ end;
+
+ TThriftSet = record
+ ElementType: TType;
+ Count: Integer;
+ end;
+
+
+
+ IProtocolFactory = interface
+ ['{7CD64A10-4E9F-4E99-93BF-708A31F4A67B}']
+ function GetProtocol( const trans: ITransport): IProtocol;
+ end;
+
+ TProtocolException = class( TException)
+ public
+ const // TODO(jensg): change into enum
+ UNKNOWN = 0;
+ INVALID_DATA = 1;
+ NEGATIVE_SIZE = 2;
+ SIZE_LIMIT = 3;
+ BAD_VERSION = 4;
+ NOT_IMPLEMENTED = 5;
+ DEPTH_LIMIT = 6;
+ protected
+ constructor HiddenCreate(const Msg: string);
+ public
+ // purposefully hide inherited constructor
+ class function Create(const Msg: string): TProtocolException; overload; deprecated 'Use specialized TProtocolException types (or regenerate from IDL)';
+ class function Create: TProtocolException; overload; deprecated 'Use specialized TProtocolException types (or regenerate from IDL)';
+ class function Create( type_: Integer): TProtocolException; overload; deprecated 'Use specialized TProtocolException types (or regenerate from IDL)';
+ class function Create( type_: Integer; const msg: string): TProtocolException; overload; deprecated 'Use specialized TProtocolException types (or regenerate from IDL)';
+ end;
+
+ // Needed to remove deprecation warning
+ TProtocolExceptionSpecialized = class abstract (TProtocolException)
+ public
+ constructor Create(const Msg: string);
+ end;
+
+ TProtocolExceptionUnknown = class (TProtocolExceptionSpecialized);
+ TProtocolExceptionInvalidData = class (TProtocolExceptionSpecialized);
+ TProtocolExceptionNegativeSize = class (TProtocolExceptionSpecialized);
+ TProtocolExceptionSizeLimit = class (TProtocolExceptionSpecialized);
+ TProtocolExceptionBadVersion = class (TProtocolExceptionSpecialized);
+ TProtocolExceptionNotImplemented = class (TProtocolExceptionSpecialized);
+ TProtocolExceptionDepthLimit = class (TProtocolExceptionSpecialized);
+
+
+ TProtocolUtil = class
+ public
+ class procedure Skip( prot: IProtocol; type_: TType);
+ end;
+
+ IProtocolRecursionTracker = interface
+ ['{29CA033F-BB56-49B1-9EE3-31B1E82FC7A5}']
+ // no members yet
+ end;
+
+ TProtocolRecursionTrackerImpl = class abstract( TInterfacedObject, IProtocolRecursionTracker)
+ protected
+ FProtocol : IProtocol;
+ public
+ constructor Create( prot : IProtocol);
+ destructor Destroy; override;
+ end;
+
+ IProtocol = interface
+ ['{602A7FFB-0D9E-4CD8-8D7F-E5076660588A}']
+ function GetTransport: ITransport;
+ procedure WriteMessageBegin( const msg: TThriftMessage);
+ procedure WriteMessageEnd;
+ procedure WriteStructBegin( const struc: TThriftStruct);
+ procedure WriteStructEnd;
+ procedure WriteFieldBegin( const field: TThriftField);
+ procedure WriteFieldEnd;
+ procedure WriteFieldStop;
+ procedure WriteMapBegin( const map: TThriftMap);
+ procedure WriteMapEnd;
+ procedure WriteListBegin( const list: TThriftList);
+ procedure WriteListEnd();
+ procedure WriteSetBegin( const set_: TThriftSet );
+ procedure WriteSetEnd();
+ procedure WriteBool( b: Boolean);
+ procedure WriteByte( b: ShortInt);
+ procedure WriteI16( i16: SmallInt);
+ procedure WriteI32( i32: Integer);
+ procedure WriteI64( const i64: Int64);
+ procedure WriteDouble( const d: Double);
+ procedure WriteString( const s: string );
+ procedure WriteAnsiString( const s: AnsiString);
+ procedure WriteBinary( const b: TBytes);
+
+ function ReadMessageBegin: TThriftMessage;
+ procedure ReadMessageEnd();
+ function ReadStructBegin: TThriftStruct;
+ procedure ReadStructEnd;
+ function ReadFieldBegin: TThriftField;
+ procedure ReadFieldEnd();
+ function ReadMapBegin: TThriftMap;
+ procedure ReadMapEnd();
+ function ReadListBegin: TThriftList;
+ procedure ReadListEnd();
+ function ReadSetBegin: TThriftSet;
+ procedure ReadSetEnd();
+ function ReadBool: Boolean;
+ function ReadByte: ShortInt;
+ function ReadI16: SmallInt;
+ function ReadI32: Integer;
+ function ReadI64: Int64;
+ function ReadDouble:Double;
+ function ReadBinary: TBytes;
+ function ReadString: string;
+ function ReadAnsiString: AnsiString;
+
+ procedure SetRecursionLimit( value : Integer);
+ function GetRecursionLimit : Integer;
+ function NextRecursionLevel : IProtocolRecursionTracker;
+ procedure IncrementRecursionDepth;
+ procedure DecrementRecursionDepth;
+
+ property Transport: ITransport read GetTransport;
+ property RecursionLimit : Integer read GetRecursionLimit write SetRecursionLimit;
+ end;
+
+ TProtocolImpl = class abstract( TInterfacedObject, IProtocol)
+ protected
+ FTrans : ITransport;
+ FRecursionLimit : Integer;
+ FRecursionDepth : Integer;
+
+ procedure SetRecursionLimit( value : Integer);
+ function GetRecursionLimit : Integer;
+ function NextRecursionLevel : IProtocolRecursionTracker;
+ procedure IncrementRecursionDepth;
+ procedure DecrementRecursionDepth;
+
+ function GetTransport: ITransport;
+ public
+ procedure WriteMessageBegin( const msg: TThriftMessage); virtual; abstract;
+ procedure WriteMessageEnd; virtual; abstract;
+ procedure WriteStructBegin( const struc: TThriftStruct); virtual; abstract;
+ procedure WriteStructEnd; virtual; abstract;
+ procedure WriteFieldBegin( const field: TThriftField); virtual; abstract;
+ procedure WriteFieldEnd; virtual; abstract;
+ procedure WriteFieldStop; virtual; abstract;
+ procedure WriteMapBegin( const map: TThriftMap); virtual; abstract;
+ procedure WriteMapEnd; virtual; abstract;
+ procedure WriteListBegin( const list: TThriftList); virtual; abstract;
+ procedure WriteListEnd(); virtual; abstract;
+ procedure WriteSetBegin( const set_: TThriftSet ); virtual; abstract;
+ procedure WriteSetEnd(); virtual; abstract;
+ procedure WriteBool( b: Boolean); virtual; abstract;
+ procedure WriteByte( b: ShortInt); virtual; abstract;
+ procedure WriteI16( i16: SmallInt); virtual; abstract;
+ procedure WriteI32( i32: Integer); virtual; abstract;
+ procedure WriteI64( const i64: Int64); virtual; abstract;
+ procedure WriteDouble( const d: Double); virtual; abstract;
+ procedure WriteString( const s: string ); virtual;
+ procedure WriteAnsiString( const s: AnsiString); virtual;
+ procedure WriteBinary( const b: TBytes); virtual; abstract;
+
+ function ReadMessageBegin: TThriftMessage; virtual; abstract;
+ procedure ReadMessageEnd(); virtual; abstract;
+ function ReadStructBegin: TThriftStruct; virtual; abstract;
+ procedure ReadStructEnd; virtual; abstract;
+ function ReadFieldBegin: TThriftField; virtual; abstract;
+ procedure ReadFieldEnd(); virtual; abstract;
+ function ReadMapBegin: TThriftMap; virtual; abstract;
+ procedure ReadMapEnd(); virtual; abstract;
+ function ReadListBegin: TThriftList; virtual; abstract;
+ procedure ReadListEnd(); virtual; abstract;
+ function ReadSetBegin: TThriftSet; virtual; abstract;
+ procedure ReadSetEnd(); virtual; abstract;
+ function ReadBool: Boolean; virtual; abstract;
+ function ReadByte: ShortInt; virtual; abstract;
+ function ReadI16: SmallInt; virtual; abstract;
+ function ReadI32: Integer; virtual; abstract;
+ function ReadI64: Int64; virtual; abstract;
+ function ReadDouble:Double; virtual; abstract;
+ function ReadBinary: TBytes; virtual; abstract;
+ function ReadString: string; virtual;
+ function ReadAnsiString: AnsiString; virtual;
+
+ property Transport: ITransport read GetTransport;
+
+ constructor Create( trans: ITransport );
+ end;
+
+ IBase = interface( ISupportsToString)
+ ['{AFF6CECA-5200-4540-950E-9B89E0C1C00C}']
+ procedure Read( const iprot: IProtocol);
+ procedure Write( const iprot: IProtocol);
+ end;
+
+
+ TBinaryProtocolImpl = class( TProtocolImpl )
+ protected
+ const
+ VERSION_MASK : Cardinal = $ffff0000;
+ VERSION_1 : Cardinal = $80010000;
+ protected
+ FStrictRead : Boolean;
+ FStrictWrite : Boolean;
+
+ private
+ function ReadAll( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer ): Integer; inline;
+ function ReadStringBody( size: Integer): string;
+
+ public
+
+ type
+ TFactory = class( TInterfacedObject, IProtocolFactory)
+ protected
+ FStrictRead : Boolean;
+ FStrictWrite : Boolean;
+ public
+ function GetProtocol( const trans: ITransport): IProtocol;
+ constructor Create( AStrictRead, AStrictWrite: Boolean ); overload;
+ constructor Create; overload;
+ end;
+
+ constructor Create( const trans: ITransport); overload;
+ constructor Create( const trans: ITransport; strictRead: Boolean; strictWrite: Boolean); overload;
+
+ procedure WriteMessageBegin( const msg: TThriftMessage); override;
+ procedure WriteMessageEnd; override;
+ procedure WriteStructBegin( const struc: TThriftStruct); override;
+ procedure WriteStructEnd; override;
+ procedure WriteFieldBegin( const field: TThriftField); override;
+ procedure WriteFieldEnd; override;
+ procedure WriteFieldStop; override;
+ procedure WriteMapBegin( const map: TThriftMap); override;
+ procedure WriteMapEnd; override;
+ procedure WriteListBegin( const list: TThriftList); override;
+ procedure WriteListEnd(); override;
+ procedure WriteSetBegin( const set_: TThriftSet ); override;
+ procedure WriteSetEnd(); override;
+ procedure WriteBool( b: Boolean); override;
+ procedure WriteByte( b: ShortInt); override;
+ procedure WriteI16( i16: SmallInt); override;
+ procedure WriteI32( i32: Integer); override;
+ procedure WriteI64( const i64: Int64); override;
+ procedure WriteDouble( const d: Double); override;
+ procedure WriteBinary( const b: TBytes); override;
+
+ function ReadMessageBegin: TThriftMessage; override;
+ procedure ReadMessageEnd(); override;
+ function ReadStructBegin: TThriftStruct; override;
+ procedure ReadStructEnd; override;
+ function ReadFieldBegin: TThriftField; override;
+ procedure ReadFieldEnd(); override;
+ function ReadMapBegin: TThriftMap; override;
+ procedure ReadMapEnd(); override;
+ function ReadListBegin: TThriftList; override;
+ procedure ReadListEnd(); override;
+ function ReadSetBegin: TThriftSet; override;
+ procedure ReadSetEnd(); override;
+ function ReadBool: Boolean; override;
+ function ReadByte: ShortInt; override;
+ function ReadI16: SmallInt; override;
+ function ReadI32: Integer; override;
+ function ReadI64: Int64; override;
+ function ReadDouble:Double; override;
+ function ReadBinary: TBytes; override;
+
+ end;
+
+
+ { TProtocolDecorator forwards all requests to an enclosed TProtocol instance,
+ providing a way to author concise concrete decorator subclasses. The decorator
+ does not (and should not) modify the behaviour of the enclosed TProtocol
+
+ See p.175 of Design Patterns (by Gamma et al.)
+ }
+ TProtocolDecorator = class( TProtocolImpl)
+ private
+ FWrappedProtocol : IProtocol;
+
+ public
+ // Encloses the specified protocol.
+ // All operations will be forward to the given protocol. Must be non-null.
+ constructor Create( const aProtocol : IProtocol);
+
+ procedure WriteMessageBegin( const msg: TThriftMessage); override;
+ procedure WriteMessageEnd; override;
+ procedure WriteStructBegin( const struc: TThriftStruct); override;
+ procedure WriteStructEnd; override;
+ procedure WriteFieldBegin( const field: TThriftField); override;
+ procedure WriteFieldEnd; override;
+ procedure WriteFieldStop; override;
+ procedure WriteMapBegin( const map: TThriftMap); override;
+ procedure WriteMapEnd; override;
+ procedure WriteListBegin( const list: TThriftList); override;
+ procedure WriteListEnd(); override;
+ procedure WriteSetBegin( const set_: TThriftSet ); override;
+ procedure WriteSetEnd(); override;
+ procedure WriteBool( b: Boolean); override;
+ procedure WriteByte( b: ShortInt); override;
+ procedure WriteI16( i16: SmallInt); override;
+ procedure WriteI32( i32: Integer); override;
+ procedure WriteI64( const i64: Int64); override;
+ procedure WriteDouble( const d: Double); override;
+ procedure WriteString( const s: string ); override;
+ procedure WriteAnsiString( const s: AnsiString); override;
+ procedure WriteBinary( const b: TBytes); override;
+
+ function ReadMessageBegin: TThriftMessage; override;
+ procedure ReadMessageEnd(); override;
+ function ReadStructBegin: TThriftStruct; override;
+ procedure ReadStructEnd; override;
+ function ReadFieldBegin: TThriftField; override;
+ procedure ReadFieldEnd(); override;
+ function ReadMapBegin: TThriftMap; override;
+ procedure ReadMapEnd(); override;
+ function ReadListBegin: TThriftList; override;
+ procedure ReadListEnd(); override;
+ function ReadSetBegin: TThriftSet; override;
+ procedure ReadSetEnd(); override;
+ function ReadBool: Boolean; override;
+ function ReadByte: ShortInt; override;
+ function ReadI16: SmallInt; override;
+ function ReadI32: Integer; override;
+ function ReadI64: Int64; override;
+ function ReadDouble:Double; override;
+ function ReadBinary: TBytes; override;
+ function ReadString: string; override;
+ function ReadAnsiString: AnsiString; override;
+ end;
+
+
+type
+ IRequestEvents = interface
+ ['{F926A26A-5B00-4560-86FA-2CAE3BA73DAF}']
+ // Called before reading arguments.
+ procedure PreRead;
+ // Called between reading arguments and calling the handler.
+ procedure PostRead;
+ // Called between calling the handler and writing the response.
+ procedure PreWrite;
+ // Called after writing the response.
+ procedure PostWrite;
+ // Called when an oneway (async) function call completes successfully.
+ procedure OnewayComplete;
+ // Called if the handler throws an undeclared exception.
+ procedure UnhandledError( const e : Exception);
+ // Called when a client has finished request-handling to clean up
+ procedure CleanupContext;
+ end;
+
+
+ IProcessorEvents = interface
+ ['{A8661119-657C-447D-93C5-512E36162A45}']
+ // Called when a client is about to call the processor.
+ procedure Processing( const transport : ITransport);
+ // Called on any service function invocation
+ function CreateRequestContext( const aFunctionName : string) : IRequestEvents;
+ // Called when a client has finished request-handling to clean up
+ procedure CleanupContext;
+ end;
+
+
+ IProcessor = interface
+ ['{7BAE92A5-46DA-4F13-B6EA-0EABE233EE5F}']
+ function Process( const iprot :IProtocol; const oprot: IProtocol; const events : IProcessorEvents = nil): Boolean;
+ end;
+
+
+procedure Init( var rec : TThriftMessage; const AName: string = ''; const AMessageType: TMessageType = Low(TMessageType); const ASeqID: Integer = 0); overload; inline;
+procedure Init( var rec : TThriftStruct; const AName: string = ''); overload; inline;
+procedure Init( var rec : TThriftField; const AName: string = ''; const AType: TType = Low(TType); const AID: SmallInt = 0); overload; inline;
+procedure Init( var rec : TThriftMap; const AKeyType: TType = Low(TType); const AValueType: TType = Low(TType); const ACount: Integer = 0); overload; inline;
+procedure Init( var rec : TThriftSet; const AElementType: TType = Low(TType); const ACount: Integer = 0); overload; inline;
+procedure Init( var rec : TThriftList; const AElementType: TType = Low(TType); const ACount: Integer = 0); overload; inline;
+
+
+implementation
+
+function ConvertInt64ToDouble( const n: Int64): Double;
+begin
+ ASSERT( SizeOf(n) = SizeOf(Result));
+ System.Move( n, Result, SizeOf(Result));
+end;
+
+function ConvertDoubleToInt64( const d: Double): Int64;
+begin
+ ASSERT( SizeOf(d) = SizeOf(Result));
+ System.Move( d, Result, SizeOf(Result));
+end;
+
+
+
+{ TProtocolRecursionTrackerImpl }
+
+constructor TProtocolRecursionTrackerImpl.Create( prot : IProtocol);
+begin
+ inherited Create;
+
+ // storing the pointer *after* the (successful) increment is important here
+ prot.IncrementRecursionDepth;
+ FProtocol := prot;
+end;
+
+destructor TProtocolRecursionTrackerImpl.Destroy;
+begin
+ try
+ // we have to release the reference iff the pointer has been stored
+ if FProtocol <> nil then begin
+ FProtocol.DecrementRecursionDepth;
+ FProtocol := nil;
+ end;
+ finally
+ inherited Destroy;
+ end;
+end;
+
+{ TProtocolImpl }
+
+constructor TProtocolImpl.Create(trans: ITransport);
+begin
+ inherited Create;
+ FTrans := trans;
+ FRecursionLimit := DEFAULT_RECURSION_LIMIT;
+ FRecursionDepth := 0;
+end;
+
+procedure TProtocolImpl.SetRecursionLimit( value : Integer);
+begin
+ FRecursionLimit := value;
+end;
+
+function TProtocolImpl.GetRecursionLimit : Integer;
+begin
+ result := FRecursionLimit;
+end;
+
+function TProtocolImpl.NextRecursionLevel : IProtocolRecursionTracker;
+begin
+ result := TProtocolRecursionTrackerImpl.Create(Self);
+end;
+
+procedure TProtocolImpl.IncrementRecursionDepth;
+begin
+ if FRecursionDepth < FRecursionLimit
+ then Inc(FRecursionDepth)
+ else raise TProtocolExceptionDepthLimit.Create('Depth limit exceeded');
+end;
+
+procedure TProtocolImpl.DecrementRecursionDepth;
+begin
+ Dec(FRecursionDepth)
+end;
+
+function TProtocolImpl.GetTransport: ITransport;
+begin
+ Result := FTrans;
+end;
+
+function TProtocolImpl.ReadAnsiString: AnsiString;
+var
+ b : TBytes;
+ len : Integer;
+begin
+ Result := '';
+ b := ReadBinary;
+ len := Length( b );
+ if len > 0 then
+ begin
+ SetLength( Result, len);
+ System.Move( b[0], Pointer(Result)^, len );
+ end;
+end;
+
+function TProtocolImpl.ReadString: string;
+begin
+ Result := TEncoding.UTF8.GetString( ReadBinary );
+end;
+
+procedure TProtocolImpl.WriteAnsiString(const s: AnsiString);
+var
+ b : TBytes;
+ len : Integer;
+begin
+ len := Length(s);
+ SetLength( b, len);
+ if len > 0 then
+ begin
+ System.Move( Pointer(s)^, b[0], len );
+ end;
+ WriteBinary( b );
+end;
+
+procedure TProtocolImpl.WriteString(const s: string);
+var
+ b : TBytes;
+begin
+ b := TEncoding.UTF8.GetBytes(s);
+ WriteBinary( b );
+end;
+
+{ TProtocolUtil }
+
+class procedure TProtocolUtil.Skip( prot: IProtocol; type_: TType);
+var field : TThriftField;
+ map : TThriftMap;
+ set_ : TThriftSet;
+ list : TThriftList;
+ i : Integer;
+ tracker : IProtocolRecursionTracker;
+begin
+ tracker := prot.NextRecursionLevel;
+ case type_ of
+ // simple types
+ TType.Bool_ : prot.ReadBool();
+ TType.Byte_ : prot.ReadByte();
+ TType.I16 : prot.ReadI16();
+ TType.I32 : prot.ReadI32();
+ TType.I64 : prot.ReadI64();
+ TType.Double_ : prot.ReadDouble();
+ TType.String_ : prot.ReadBinary();// Don't try to decode the string, just skip it.
+
+ // structured types
+ TType.Struct : begin
+ prot.ReadStructBegin();
+ while TRUE do begin
+ field := prot.ReadFieldBegin();
+ if (field.Type_ = TType.Stop) then Break;
+ Skip(prot, field.Type_);
+ prot.ReadFieldEnd();
+ end;
+ prot.ReadStructEnd();
+ end;
+
+ TType.Map : begin
+ map := prot.ReadMapBegin();
+ for i := 0 to map.Count-1 do begin
+ Skip(prot, map.KeyType);
+ Skip(prot, map.ValueType);
+ end;
+ prot.ReadMapEnd();
+ end;
+
+ TType.Set_ : begin
+ set_ := prot.ReadSetBegin();
+ for i := 0 to set_.Count-1
+ do Skip( prot, set_.ElementType);
+ prot.ReadSetEnd();
+ end;
+
+ TType.List : begin
+ list := prot.ReadListBegin();
+ for i := 0 to list.Count-1
+ do Skip( prot, list.ElementType);
+ prot.ReadListEnd();
+ end;
+
+ else
+ raise TProtocolExceptionInvalidData.Create('Unexpected type '+IntToStr(Ord(type_)));
+ end;
+end;
+
+
+{ TBinaryProtocolImpl }
+
+constructor TBinaryProtocolImpl.Create( const trans: ITransport);
+begin
+ //no inherited
+ Create( trans, False, True);
+end;
+
+constructor TBinaryProtocolImpl.Create( const trans: ITransport; strictRead,
+ strictWrite: Boolean);
+begin
+ inherited Create( trans );
+ FStrictRead := strictRead;
+ FStrictWrite := strictWrite;
+end;
+
+function TBinaryProtocolImpl.ReadAll( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer ): Integer;
+begin
+ Result := FTrans.ReadAll( pBuf, buflen, off, len );
+end;
+
+function TBinaryProtocolImpl.ReadBinary: TBytes;
+var
+ size : Integer;
+ buf : TBytes;
+begin
+ size := ReadI32;
+ SetLength( buf, size );
+ FTrans.ReadAll( buf, 0, size);
+ Result := buf;
+end;
+
+function TBinaryProtocolImpl.ReadBool: Boolean;
+begin
+ Result := (ReadByte = 1);
+end;
+
+function TBinaryProtocolImpl.ReadByte: ShortInt;
+begin
+ ReadAll( @result, SizeOf(result), 0, 1);
+end;
+
+function TBinaryProtocolImpl.ReadDouble: Double;
+begin
+ Result := ConvertInt64ToDouble( ReadI64 )
+end;
+
+function TBinaryProtocolImpl.ReadFieldBegin: TThriftField;
+begin
+ Init( result, '', TType( ReadByte), 0);
+ if ( result.Type_ <> TType.Stop ) then begin
+ result.Id := ReadI16;
+ end;
+end;
+
+procedure TBinaryProtocolImpl.ReadFieldEnd;
+begin
+
+end;
+
+function TBinaryProtocolImpl.ReadI16: SmallInt;
+var i16in : packed array[0..1] of Byte;
+begin
+ ReadAll( @i16in, Sizeof(i16in), 0, 2);
+ Result := SmallInt(((i16in[0] and $FF) shl 8) or (i16in[1] and $FF));
+end;
+
+function TBinaryProtocolImpl.ReadI32: Integer;
+var i32in : packed array[0..3] of Byte;
+begin
+ ReadAll( @i32in, SizeOf(i32in), 0, 4);
+
+ Result := Integer(
+ ((i32in[0] and $FF) shl 24) or
+ ((i32in[1] and $FF) shl 16) or
+ ((i32in[2] and $FF) shl 8) or
+ (i32in[3] and $FF));
+
+end;
+
+function TBinaryProtocolImpl.ReadI64: Int64;
+var i64in : packed array[0..7] of Byte;
+begin
+ ReadAll( @i64in, SizeOf(i64in), 0, 8);
+ Result :=
+ (Int64( i64in[0] and $FF) shl 56) or
+ (Int64( i64in[1] and $FF) shl 48) or
+ (Int64( i64in[2] and $FF) shl 40) or
+ (Int64( i64in[3] and $FF) shl 32) or
+ (Int64( i64in[4] and $FF) shl 24) or
+ (Int64( i64in[5] and $FF) shl 16) or
+ (Int64( i64in[6] and $FF) shl 8) or
+ (Int64( i64in[7] and $FF));
+end;
+
+function TBinaryProtocolImpl.ReadListBegin: TThriftList;
+begin
+ result.ElementType := TType(ReadByte);
+ result.Count := ReadI32;
+end;
+
+procedure TBinaryProtocolImpl.ReadListEnd;
+begin
+
+end;
+
+function TBinaryProtocolImpl.ReadMapBegin: TThriftMap;
+begin
+ result.KeyType := TType(ReadByte);
+ result.ValueType := TType(ReadByte);
+ result.Count := ReadI32;
+end;
+
+procedure TBinaryProtocolImpl.ReadMapEnd;
+begin
+
+end;
+
+function TBinaryProtocolImpl.ReadMessageBegin: TThriftMessage;
+var
+ size : Integer;
+ version : Integer;
+begin
+ Init( result);
+ size := ReadI32;
+ if (size < 0) then begin
+ version := size and Integer( VERSION_MASK);
+ if ( version <> Integer( VERSION_1)) then begin
+ raise TProtocolExceptionBadVersion.Create('Bad version in ReadMessageBegin: ' + IntToStr(version) );
+ end;
+ result.Type_ := TMessageType( size and $000000ff);
+ result.Name := ReadString;
+ result.SeqID := ReadI32;
+ end
+ else begin
+ if FStrictRead then begin
+ raise TProtocolExceptionBadVersion.Create('Missing version in readMessageBegin, old client?' );
+ end;
+ result.Name := ReadStringBody( size );
+ result.Type_ := TMessageType( ReadByte );
+ result.SeqID := ReadI32;
+ end;
+end;
+
+procedure TBinaryProtocolImpl.ReadMessageEnd;
+begin
+ inherited;
+
+end;
+
+function TBinaryProtocolImpl.ReadSetBegin: TThriftSet;
+begin
+ result.ElementType := TType(ReadByte);
+ result.Count := ReadI32;
+end;
+
+procedure TBinaryProtocolImpl.ReadSetEnd;
+begin
+
+end;
+
+function TBinaryProtocolImpl.ReadStringBody( size: Integer): string;
+var
+ buf : TBytes;
+begin
+ SetLength( buf, size );
+ FTrans.ReadAll( buf, 0, size );
+ Result := TEncoding.UTF8.GetString( buf);
+end;
+
+function TBinaryProtocolImpl.ReadStructBegin: TThriftStruct;
+begin
+ Init( Result);
+end;
+
+procedure TBinaryProtocolImpl.ReadStructEnd;
+begin
+ inherited;
+
+end;
+
+procedure TBinaryProtocolImpl.WriteBinary( const b: TBytes);
+var iLen : Integer;
+begin
+ iLen := Length(b);
+ WriteI32( iLen);
+ if iLen > 0 then FTrans.Write(b, 0, iLen);
+end;
+
+procedure TBinaryProtocolImpl.WriteBool(b: Boolean);
+begin
+ if b then begin
+ WriteByte( 1 );
+ end else begin
+ WriteByte( 0 );
+ end;
+end;
+
+procedure TBinaryProtocolImpl.WriteByte(b: ShortInt);
+begin
+ FTrans.Write( @b, 0, 1);
+end;
+
+procedure TBinaryProtocolImpl.WriteDouble( const d: Double);
+begin
+ WriteI64(ConvertDoubleToInt64(d));
+end;
+
+procedure TBinaryProtocolImpl.WriteFieldBegin( const field: TThriftField);
+begin
+ WriteByte(ShortInt(field.Type_));
+ WriteI16(field.ID);
+end;
+
+procedure TBinaryProtocolImpl.WriteFieldEnd;
+begin
+
+end;
+
+procedure TBinaryProtocolImpl.WriteFieldStop;
+begin
+ WriteByte(ShortInt(TType.Stop));
+end;
+
+procedure TBinaryProtocolImpl.WriteI16(i16: SmallInt);
+var i16out : packed array[0..1] of Byte;
+begin
+ i16out[0] := Byte($FF and (i16 shr 8));
+ i16out[1] := Byte($FF and i16);
+ FTrans.Write( @i16out, 0, 2);
+end;
+
+procedure TBinaryProtocolImpl.WriteI32(i32: Integer);
+var i32out : packed array[0..3] of Byte;
+begin
+ i32out[0] := Byte($FF and (i32 shr 24));
+ i32out[1] := Byte($FF and (i32 shr 16));
+ i32out[2] := Byte($FF and (i32 shr 8));
+ i32out[3] := Byte($FF and i32);
+ FTrans.Write( @i32out, 0, 4);
+end;
+
+procedure TBinaryProtocolImpl.WriteI64( const i64: Int64);
+var i64out : packed array[0..7] of Byte;
+begin
+ i64out[0] := Byte($FF and (i64 shr 56));
+ i64out[1] := Byte($FF and (i64 shr 48));
+ i64out[2] := Byte($FF and (i64 shr 40));
+ i64out[3] := Byte($FF and (i64 shr 32));
+ i64out[4] := Byte($FF and (i64 shr 24));
+ i64out[5] := Byte($FF and (i64 shr 16));
+ i64out[6] := Byte($FF and (i64 shr 8));
+ i64out[7] := Byte($FF and i64);
+ FTrans.Write( @i64out, 0, 8);
+end;
+
+procedure TBinaryProtocolImpl.WriteListBegin( const list: TThriftList);
+begin
+ WriteByte(ShortInt(list.ElementType));
+ WriteI32(list.Count);
+end;
+
+procedure TBinaryProtocolImpl.WriteListEnd;
+begin
+
+end;
+
+procedure TBinaryProtocolImpl.WriteMapBegin( const map: TThriftMap);
+begin
+ WriteByte(ShortInt(map.KeyType));
+ WriteByte(ShortInt(map.ValueType));
+ WriteI32(map.Count);
+end;
+
+procedure TBinaryProtocolImpl.WriteMapEnd;
+begin
+
+end;
+
+procedure TBinaryProtocolImpl.WriteMessageBegin( const msg: TThriftMessage);
+var
+ version : Cardinal;
+begin
+ if FStrictWrite then
+ begin
+ version := VERSION_1 or Cardinal( msg.Type_);
+ WriteI32( Integer( version) );
+ WriteString( msg.Name);
+ WriteI32( msg.SeqID);
+ end else
+ begin
+ WriteString( msg.Name);
+ WriteByte(ShortInt( msg.Type_));
+ WriteI32( msg.SeqID);
+ end;
+end;
+
+procedure TBinaryProtocolImpl.WriteMessageEnd;
+begin
+
+end;
+
+procedure TBinaryProtocolImpl.WriteSetBegin( const set_: TThriftSet);
+begin
+ WriteByte(ShortInt(set_.ElementType));
+ WriteI32(set_.Count);
+end;
+
+procedure TBinaryProtocolImpl.WriteSetEnd;
+begin
+
+end;
+
+procedure TBinaryProtocolImpl.WriteStructBegin( const struc: TThriftStruct);
+begin
+
+end;
+
+procedure TBinaryProtocolImpl.WriteStructEnd;
+begin
+
+end;
+
+{ TProtocolException }
+
+constructor TProtocolException.HiddenCreate(const Msg: string);
+begin
+ inherited Create(Msg);
+end;
+
+class function TProtocolException.Create(const Msg: string): TProtocolException;
+begin
+ Result := TProtocolExceptionUnknown.Create(Msg);
+end;
+
+class function TProtocolException.Create: TProtocolException;
+begin
+ Result := TProtocolExceptionUnknown.Create('');
+end;
+
+class function TProtocolException.Create(type_: Integer): TProtocolException;
+begin
+{$WARN SYMBOL_DEPRECATED OFF}
+ Result := Create(type_, '');
+{$WARN SYMBOL_DEPRECATED DEFAULT}
+end;
+
+class function TProtocolException.Create(type_: Integer; const msg: string): TProtocolException;
+begin
+ case type_ of
+ INVALID_DATA: Result := TProtocolExceptionInvalidData.Create(msg);
+ NEGATIVE_SIZE: Result := TProtocolExceptionNegativeSize.Create(msg);
+ SIZE_LIMIT: Result := TProtocolExceptionSizeLimit.Create(msg);
+ BAD_VERSION: Result := TProtocolExceptionBadVersion.Create(msg);
+ NOT_IMPLEMENTED: Result := TProtocolExceptionNotImplemented.Create(msg);
+ DEPTH_LIMIT: Result := TProtocolExceptionDepthLimit.Create(msg);
+ else
+ Result := TProtocolExceptionUnknown.Create(msg);
+ end;
+end;
+
+{ TProtocolExceptionSpecialized }
+
+constructor TProtocolExceptionSpecialized.Create(const Msg: string);
+begin
+ inherited HiddenCreate(Msg);
+end;
+
+{ TBinaryProtocolImpl.TFactory }
+
+constructor TBinaryProtocolImpl.TFactory.Create(AStrictRead, AStrictWrite: Boolean);
+begin
+ inherited Create;
+ FStrictRead := AStrictRead;
+ FStrictWrite := AStrictWrite;
+end;
+
+constructor TBinaryProtocolImpl.TFactory.Create;
+begin
+ //no inherited;
+ Create( False, True )
+end;
+
+function TBinaryProtocolImpl.TFactory.GetProtocol( const trans: ITransport): IProtocol;
+begin
+ Result := TBinaryProtocolImpl.Create( trans, FStrictRead, FStrictWrite);
+end;
+
+
+{ TProtocolDecorator }
+
+constructor TProtocolDecorator.Create( const aProtocol : IProtocol);
+begin
+ ASSERT( aProtocol <> nil);
+ inherited Create( aProtocol.Transport);
+ FWrappedProtocol := aProtocol;
+end;
+
+
+procedure TProtocolDecorator.WriteMessageBegin( const msg: TThriftMessage);
+begin
+ FWrappedProtocol.WriteMessageBegin( msg);
+end;
+
+
+procedure TProtocolDecorator.WriteMessageEnd;
+begin
+ FWrappedProtocol.WriteMessageEnd;
+end;
+
+
+procedure TProtocolDecorator.WriteStructBegin( const struc: TThriftStruct);
+begin
+ FWrappedProtocol.WriteStructBegin( struc);
+end;
+
+
+procedure TProtocolDecorator.WriteStructEnd;
+begin
+ FWrappedProtocol.WriteStructEnd;
+end;
+
+
+procedure TProtocolDecorator.WriteFieldBegin( const field: TThriftField);
+begin
+ FWrappedProtocol.WriteFieldBegin( field);
+end;
+
+
+procedure TProtocolDecorator.WriteFieldEnd;
+begin
+ FWrappedProtocol.WriteFieldEnd;
+end;
+
+
+procedure TProtocolDecorator.WriteFieldStop;
+begin
+ FWrappedProtocol.WriteFieldStop;
+end;
+
+
+procedure TProtocolDecorator.WriteMapBegin( const map: TThriftMap);
+begin
+ FWrappedProtocol.WriteMapBegin( map);
+end;
+
+
+procedure TProtocolDecorator.WriteMapEnd;
+begin
+ FWrappedProtocol.WriteMapEnd;
+end;
+
+
+procedure TProtocolDecorator.WriteListBegin( const list: TThriftList);
+begin
+ FWrappedProtocol.WriteListBegin( list);
+end;
+
+
+procedure TProtocolDecorator.WriteListEnd();
+begin
+ FWrappedProtocol.WriteListEnd();
+end;
+
+
+procedure TProtocolDecorator.WriteSetBegin( const set_: TThriftSet );
+begin
+ FWrappedProtocol.WriteSetBegin( set_);
+end;
+
+
+procedure TProtocolDecorator.WriteSetEnd();
+begin
+ FWrappedProtocol.WriteSetEnd();
+end;
+
+
+procedure TProtocolDecorator.WriteBool( b: Boolean);
+begin
+ FWrappedProtocol.WriteBool( b);
+end;
+
+
+procedure TProtocolDecorator.WriteByte( b: ShortInt);
+begin
+ FWrappedProtocol.WriteByte( b);
+end;
+
+
+procedure TProtocolDecorator.WriteI16( i16: SmallInt);
+begin
+ FWrappedProtocol.WriteI16( i16);
+end;
+
+
+procedure TProtocolDecorator.WriteI32( i32: Integer);
+begin
+ FWrappedProtocol.WriteI32( i32);
+end;
+
+
+procedure TProtocolDecorator.WriteI64( const i64: Int64);
+begin
+ FWrappedProtocol.WriteI64( i64);
+end;
+
+
+procedure TProtocolDecorator.WriteDouble( const d: Double);
+begin
+ FWrappedProtocol.WriteDouble( d);
+end;
+
+
+procedure TProtocolDecorator.WriteString( const s: string );
+begin
+ FWrappedProtocol.WriteString( s);
+end;
+
+
+procedure TProtocolDecorator.WriteAnsiString( const s: AnsiString);
+begin
+ FWrappedProtocol.WriteAnsiString( s);
+end;
+
+
+procedure TProtocolDecorator.WriteBinary( const b: TBytes);
+begin
+ FWrappedProtocol.WriteBinary( b);
+end;
+
+
+function TProtocolDecorator.ReadMessageBegin: TThriftMessage;
+begin
+ result := FWrappedProtocol.ReadMessageBegin;
+end;
+
+
+procedure TProtocolDecorator.ReadMessageEnd();
+begin
+ FWrappedProtocol.ReadMessageEnd();
+end;
+
+
+function TProtocolDecorator.ReadStructBegin: TThriftStruct;
+begin
+ result := FWrappedProtocol.ReadStructBegin;
+end;
+
+
+procedure TProtocolDecorator.ReadStructEnd;
+begin
+ FWrappedProtocol.ReadStructEnd;
+end;
+
+
+function TProtocolDecorator.ReadFieldBegin: TThriftField;
+begin
+ result := FWrappedProtocol.ReadFieldBegin;
+end;
+
+
+procedure TProtocolDecorator.ReadFieldEnd();
+begin
+ FWrappedProtocol.ReadFieldEnd();
+end;
+
+
+function TProtocolDecorator.ReadMapBegin: TThriftMap;
+begin
+ result := FWrappedProtocol.ReadMapBegin;
+end;
+
+
+procedure TProtocolDecorator.ReadMapEnd();
+begin
+ FWrappedProtocol.ReadMapEnd();
+end;
+
+
+function TProtocolDecorator.ReadListBegin: TThriftList;
+begin
+ result := FWrappedProtocol.ReadListBegin;
+end;
+
+
+procedure TProtocolDecorator.ReadListEnd();
+begin
+ FWrappedProtocol.ReadListEnd();
+end;
+
+
+function TProtocolDecorator.ReadSetBegin: TThriftSet;
+begin
+ result := FWrappedProtocol.ReadSetBegin;
+end;
+
+
+procedure TProtocolDecorator.ReadSetEnd();
+begin
+ FWrappedProtocol.ReadSetEnd();
+end;
+
+
+function TProtocolDecorator.ReadBool: Boolean;
+begin
+ result := FWrappedProtocol.ReadBool;
+end;
+
+
+function TProtocolDecorator.ReadByte: ShortInt;
+begin
+ result := FWrappedProtocol.ReadByte;
+end;
+
+
+function TProtocolDecorator.ReadI16: SmallInt;
+begin
+ result := FWrappedProtocol.ReadI16;
+end;
+
+
+function TProtocolDecorator.ReadI32: Integer;
+begin
+ result := FWrappedProtocol.ReadI32;
+end;
+
+
+function TProtocolDecorator.ReadI64: Int64;
+begin
+ result := FWrappedProtocol.ReadI64;
+end;
+
+
+function TProtocolDecorator.ReadDouble:Double;
+begin
+ result := FWrappedProtocol.ReadDouble;
+end;
+
+
+function TProtocolDecorator.ReadBinary: TBytes;
+begin
+ result := FWrappedProtocol.ReadBinary;
+end;
+
+
+function TProtocolDecorator.ReadString: string;
+begin
+ result := FWrappedProtocol.ReadString;
+end;
+
+
+function TProtocolDecorator.ReadAnsiString: AnsiString;
+begin
+ result := FWrappedProtocol.ReadAnsiString;
+end;
+
+
+{ Init helper functions }
+
+procedure Init( var rec : TThriftMessage; const AName: string; const AMessageType: TMessageType; const ASeqID: Integer);
+begin
+ rec.Name := AName;
+ rec.Type_ := AMessageType;
+ rec.SeqID := ASeqID;
+end;
+
+
+procedure Init( var rec : TThriftStruct; const AName: string = '');
+begin
+ rec.Name := AName;
+end;
+
+
+procedure Init( var rec : TThriftField; const AName: string; const AType: TType; const AID: SmallInt);
+begin
+ rec.Name := AName;
+ rec.Type_ := AType;
+ rec.Id := AId;
+end;
+
+
+procedure Init( var rec : TThriftMap; const AKeyType, AValueType: TType; const ACount: Integer);
+begin
+ rec.ValueType := AValueType;
+ rec.KeyType := AKeyType;
+ rec.Count := ACount;
+end;
+
+
+procedure Init( var rec : TThriftSet; const AElementType: TType; const ACount: Integer);
+begin
+ rec.Count := ACount;
+ rec.ElementType := AElementType;
+end;
+
+
+procedure Init( var rec : TThriftList; const AElementType: TType; const ACount: Integer);
+begin
+ rec.Count := ACount;
+ rec.ElementType := AElementType;
+end;
+
+
+
+
+
+end.
+
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Serializer.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Serializer.pas
new file mode 100644
index 000000000..5f2905a97
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Serializer.pas
@@ -0,0 +1,230 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+unit Thrift.Serializer;
+
+{$I Thrift.Defines.inc}
+
+interface
+
+uses
+ {$IFDEF OLD_UNIT_NAMES}
+ Classes, Windows, SysUtils,
+ {$ELSE}
+ System.Classes, Winapi.Windows, System.SysUtils,
+ {$ENDIF}
+ Thrift.Protocol,
+ Thrift.Transport,
+ Thrift.Stream;
+
+
+type
+ // Generic utility for easily serializing objects into a byte array or Stream.
+ TSerializer = class
+ private
+ FStream : TMemoryStream;
+ FTransport : ITransport;
+ FProtocol : IProtocol;
+
+ public
+ // Create a new TSerializer that uses the TBinaryProtocol by default.
+ constructor Create; overload;
+
+ // Create a new TSerializer.
+ // It will use the TProtocol specified by the factory that is passed in.
+ constructor Create( const factory : IProtocolFactory); overload;
+
+ // DTOR
+ destructor Destroy; override;
+
+ // Serialize the Thrift object.
+ function Serialize( const input : IBase) : TBytes; overload;
+ procedure Serialize( const input : IBase; const aStm : TStream); overload;
+ end;
+
+
+ // Generic utility for easily deserializing objects from byte array or Stream.
+ TDeserializer = class
+ private
+ FStream : TMemoryStream;
+ FTransport : ITransport;
+ FProtocol : IProtocol;
+
+ public
+ // Create a new TDeserializer that uses the TBinaryProtocol by default.
+ constructor Create; overload;
+
+ // Create a new TDeserializer.
+ // It will use the TProtocol specified by the factory that is passed in.
+ constructor Create( const factory : IProtocolFactory); overload;
+
+ // DTOR
+ destructor Destroy; override;
+
+ // Deserialize the Thrift object data.
+ procedure Deserialize( const input : TBytes; const target : IBase); overload;
+ procedure Deserialize( const input : TStream; const target : IBase); overload;
+ end;
+
+
+
+implementation
+
+
+{ TSerializer }
+
+
+constructor TSerializer.Create();
+// Create a new TSerializer that uses the TBinaryProtocol by default.
+begin
+ //no inherited;
+ Create( TBinaryProtocolImpl.TFactory.Create);
+end;
+
+
+constructor TSerializer.Create( const factory : IProtocolFactory);
+// Create a new TSerializer.
+// It will use the TProtocol specified by the factory that is passed in.
+var adapter : IThriftStream;
+begin
+ inherited Create;
+ FStream := TMemoryStream.Create;
+ adapter := TThriftStreamAdapterDelphi.Create( FStream, FALSE);
+ FTransport := TStreamTransportImpl.Create( nil, adapter);
+ FProtocol := factory.GetProtocol( FTransport);
+end;
+
+
+destructor TSerializer.Destroy;
+begin
+ try
+ FProtocol := nil;
+ FTransport := nil;
+ FreeAndNil( FStream);
+ finally
+ inherited Destroy;
+ end;
+end;
+
+
+function TSerializer.Serialize( const input : IBase) : TBytes;
+// Serialize the Thrift object into a byte array. The process is simple,
+// just clear the byte array output, write the object into it, and grab the
+// raw bytes.
+var iBytes : Int64;
+begin
+ try
+ FStream.Size := 0;
+ input.Write( FProtocol);
+ SetLength( result, FStream.Size);
+ iBytes := Length(result);
+ if iBytes > 0
+ then Move( FStream.Memory^, result[0], iBytes);
+ finally
+ FStream.Size := 0; // free any allocated memory
+ end;
+end;
+
+
+procedure TSerializer.Serialize( const input : IBase; const aStm : TStream);
+// Serialize the Thrift object into a byte array. The process is simple,
+// just clear the byte array output, write the object into it, and grab the
+// raw bytes.
+const COPY_ENTIRE_STREAM = 0;
+begin
+ try
+ FStream.Size := 0;
+ input.Write( FProtocol);
+ aStm.CopyFrom( FStream, COPY_ENTIRE_STREAM);
+ finally
+ FStream.Size := 0; // free any allocated memory
+ end;
+end;
+
+
+{ TDeserializer }
+
+
+constructor TDeserializer.Create();
+// Create a new TDeserializer that uses the TBinaryProtocol by default.
+begin
+ //no inherited;
+ Create( TBinaryProtocolImpl.TFactory.Create);
+end;
+
+
+constructor TDeserializer.Create( const factory : IProtocolFactory);
+// Create a new TDeserializer.
+// It will use the TProtocol specified by the factory that is passed in.
+var adapter : IThriftStream;
+begin
+ inherited Create;
+ FStream := TMemoryStream.Create;
+ adapter := TThriftStreamAdapterDelphi.Create( FStream, FALSE);
+ FTransport := TStreamTransportImpl.Create( adapter, nil);
+ FProtocol := factory.GetProtocol( FTransport);
+end;
+
+
+destructor TDeserializer.Destroy;
+begin
+ try
+ FProtocol := nil;
+ FTransport := nil;
+ FreeAndNil( FStream);
+ finally
+ inherited Destroy;
+ end;
+end;
+
+
+procedure TDeserializer.Deserialize( const input : TBytes; const target : IBase);
+// Deserialize the Thrift object data from the byte array.
+var iBytes : Int64;
+begin
+ try
+ iBytes := Length(input);
+ FStream.Size := iBytes;
+ if iBytes > 0
+ then Move( input[0], FStream.Memory^, iBytes);
+
+ target.Read( FProtocol);
+ finally
+ FStream.Size := 0; // free any allocated memory
+ end;
+end;
+
+
+procedure TDeserializer.Deserialize( const input : TStream; const target : IBase);
+// Deserialize the Thrift object data from the byte array.
+const COPY_ENTIRE_STREAM = 0;
+var before : Int64;
+begin
+ try
+ before := FStream.Position;
+ FStream.CopyFrom( input, COPY_ENTIRE_STREAM);
+ FStream.Position := before;
+ target.Read( FProtocol);
+ finally
+ FStream.Size := 0; // free any allocated memory
+ end;
+end;
+
+
+end.
+
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Server.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Server.pas
new file mode 100644
index 000000000..13c5762cf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Server.pas
@@ -0,0 +1,423 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+ unit Thrift.Server;
+
+{$I Thrift.Defines.inc}
+{$I-} // prevent annoying errors with default log delegate and no console
+
+interface
+
+uses
+ {$IFDEF OLD_UNIT_NAMES}
+ Windows, SysUtils,
+ {$ELSE}
+ Winapi.Windows, System.SysUtils,
+ {$ENDIF}
+ Thrift,
+ Thrift.Protocol,
+ Thrift.Transport;
+
+type
+ IServerEvents = interface
+ ['{9E2A99C5-EE85-40B2-9A52-2D1722B18176}']
+ // Called before the server begins.
+ procedure PreServe;
+ // Called when the server transport is ready to accept requests
+ procedure PreAccept;
+ // Called when a new client has connected and the server is about to being processing.
+ function CreateProcessingContext( const input, output : IProtocol) : IProcessorEvents;
+ end;
+
+
+ IServer = interface
+ ['{ADC46F2D-8199-4D1C-96D2-87FD54351723}']
+ procedure Serve;
+ procedure Stop;
+
+ function GetServerEvents : IServerEvents;
+ procedure SetServerEvents( const value : IServerEvents);
+
+ property ServerEvents : IServerEvents read GetServerEvents write SetServerEvents;
+ end;
+
+ TServerImpl = class abstract( TInterfacedObject, IServer )
+ public
+ type
+ TLogDelegate = reference to procedure( const str: string);
+ protected
+ FProcessor : IProcessor;
+ FServerTransport : IServerTransport;
+ FInputTransportFactory : ITransportFactory;
+ FOutputTransportFactory : ITransportFactory;
+ FInputProtocolFactory : IProtocolFactory;
+ FOutputProtocolFactory : IProtocolFactory;
+ FLogDelegate : TLogDelegate;
+ FServerEvents : IServerEvents;
+
+ class procedure DefaultLogDelegate( const str: string);
+
+ function GetServerEvents : IServerEvents;
+ procedure SetServerEvents( const value : IServerEvents);
+
+ procedure Serve; virtual; abstract;
+ procedure Stop; virtual; abstract;
+ public
+ constructor Create(
+ const AProcessor :IProcessor;
+ const AServerTransport: IServerTransport;
+ const AInputTransportFactory : ITransportFactory;
+ const AOutputTransportFactory : ITransportFactory;
+ const AInputProtocolFactory : IProtocolFactory;
+ const AOutputProtocolFactory : IProtocolFactory;
+ const ALogDelegate : TLogDelegate
+ ); overload;
+
+ constructor Create(
+ const AProcessor :IProcessor;
+ const AServerTransport: IServerTransport
+ ); overload;
+
+ constructor Create(
+ const AProcessor :IProcessor;
+ const AServerTransport: IServerTransport;
+ const ALogDelegate: TLogDelegate
+ ); overload;
+
+ constructor Create(
+ const AProcessor :IProcessor;
+ const AServerTransport: IServerTransport;
+ const ATransportFactory : ITransportFactory
+ ); overload;
+
+ constructor Create(
+ const AProcessor :IProcessor;
+ const AServerTransport: IServerTransport;
+ const ATransportFactory : ITransportFactory;
+ const AProtocolFactory : IProtocolFactory
+ ); overload;
+ end;
+
+ TSimpleServer = class( TServerImpl)
+ private
+ FStop : Boolean;
+ public
+ constructor Create( const AProcessor: IProcessor; const AServerTransport: IServerTransport); overload;
+ constructor Create( const AProcessor: IProcessor; const AServerTransport: IServerTransport;
+ ALogDel: TServerImpl.TLogDelegate); overload;
+ constructor Create( const AProcessor: IProcessor; const AServerTransport: IServerTransport;
+ const ATransportFactory: ITransportFactory); overload;
+ constructor Create( const AProcessor: IProcessor; const AServerTransport: IServerTransport;
+ const ATransportFactory: ITransportFactory; const AProtocolFactory: IProtocolFactory); overload;
+
+ procedure Serve; override;
+ procedure Stop; override;
+ end;
+
+
+implementation
+
+{ TServerImpl }
+
+constructor TServerImpl.Create( const AProcessor: IProcessor;
+ const AServerTransport: IServerTransport; const ALogDelegate: TLogDelegate);
+var
+ InputFactory, OutputFactory : IProtocolFactory;
+ InputTransFactory, OutputTransFactory : ITransportFactory;
+
+begin
+ InputFactory := TBinaryProtocolImpl.TFactory.Create;
+ OutputFactory := TBinaryProtocolImpl.TFactory.Create;
+ InputTransFactory := TTransportFactoryImpl.Create;
+ OutputTransFactory := TTransportFactoryImpl.Create;
+
+ //no inherited;
+ Create(
+ AProcessor,
+ AServerTransport,
+ InputTransFactory,
+ OutputTransFactory,
+ InputFactory,
+ OutputFactory,
+ ALogDelegate
+ );
+end;
+
+constructor TServerImpl.Create(const AProcessor: IProcessor;
+ const AServerTransport: IServerTransport);
+var
+ InputFactory, OutputFactory : IProtocolFactory;
+ InputTransFactory, OutputTransFactory : ITransportFactory;
+
+begin
+ InputFactory := TBinaryProtocolImpl.TFactory.Create;
+ OutputFactory := TBinaryProtocolImpl.TFactory.Create;
+ InputTransFactory := TTransportFactoryImpl.Create;
+ OutputTransFactory := TTransportFactoryImpl.Create;
+
+ //no inherited;
+ Create(
+ AProcessor,
+ AServerTransport,
+ InputTransFactory,
+ OutputTransFactory,
+ InputFactory,
+ OutputFactory,
+ DefaultLogDelegate
+ );
+end;
+
+constructor TServerImpl.Create(const AProcessor: IProcessor;
+ const AServerTransport: IServerTransport; const ATransportFactory: ITransportFactory);
+var
+ InputProtocolFactory : IProtocolFactory;
+ OutputProtocolFactory : IProtocolFactory;
+begin
+ InputProtocolFactory := TBinaryProtocolImpl.TFactory.Create;
+ OutputProtocolFactory := TBinaryProtocolImpl.TFactory.Create;
+
+ //no inherited;
+ Create( AProcessor, AServerTransport, ATransportFactory, ATransportFactory,
+ InputProtocolFactory, OutputProtocolFactory, DefaultLogDelegate);
+end;
+
+constructor TServerImpl.Create(const AProcessor: IProcessor;
+ const AServerTransport: IServerTransport;
+ const AInputTransportFactory, AOutputTransportFactory: ITransportFactory;
+ const AInputProtocolFactory, AOutputProtocolFactory: IProtocolFactory;
+ const ALogDelegate : TLogDelegate);
+begin
+ inherited Create;
+ FProcessor := AProcessor;
+ FServerTransport := AServerTransport;
+ FInputTransportFactory := AInputTransportFactory;
+ FOutputTransportFactory := AOutputTransportFactory;
+ FInputProtocolFactory := AInputProtocolFactory;
+ FOutputProtocolFactory := AOutputProtocolFactory;
+ FLogDelegate := ALogDelegate;
+end;
+
+class procedure TServerImpl.DefaultLogDelegate( const str: string);
+begin
+ try
+ Writeln( str);
+ if IoResult <> 0 then OutputDebugString(PChar(str));
+ except
+ OutputDebugString(PChar(str));
+ end;
+end;
+
+constructor TServerImpl.Create( const AProcessor: IProcessor;
+ const AServerTransport: IServerTransport; const ATransportFactory: ITransportFactory;
+ const AProtocolFactory: IProtocolFactory);
+begin
+ //no inherited;
+ Create( AProcessor, AServerTransport,
+ ATransportFactory, ATransportFactory,
+ AProtocolFactory, AProtocolFactory,
+ DefaultLogDelegate);
+end;
+
+
+function TServerImpl.GetServerEvents : IServerEvents;
+begin
+ result := FServerEvents;
+end;
+
+
+procedure TServerImpl.SetServerEvents( const value : IServerEvents);
+begin
+ // if you need more than one, provide a specialized IServerEvents implementation
+ FServerEvents := value;
+end;
+
+
+{ TSimpleServer }
+
+constructor TSimpleServer.Create( const AProcessor: IProcessor;
+ const AServerTransport: IServerTransport);
+var
+ InputProtocolFactory : IProtocolFactory;
+ OutputProtocolFactory : IProtocolFactory;
+ InputTransportFactory : ITransportFactory;
+ OutputTransportFactory : ITransportFactory;
+begin
+ InputProtocolFactory := TBinaryProtocolImpl.TFactory.Create;
+ OutputProtocolFactory := TBinaryProtocolImpl.TFactory.Create;
+ InputTransportFactory := TTransportFactoryImpl.Create;
+ OutputTransportFactory := TTransportFactoryImpl.Create;
+
+ inherited Create( AProcessor, AServerTransport, InputTransportFactory,
+ OutputTransportFactory, InputProtocolFactory, OutputProtocolFactory, DefaultLogDelegate);
+end;
+
+constructor TSimpleServer.Create( const AProcessor: IProcessor;
+ const AServerTransport: IServerTransport; ALogDel: TServerImpl.TLogDelegate);
+var
+ InputProtocolFactory : IProtocolFactory;
+ OutputProtocolFactory : IProtocolFactory;
+ InputTransportFactory : ITransportFactory;
+ OutputTransportFactory : ITransportFactory;
+begin
+ InputProtocolFactory := TBinaryProtocolImpl.TFactory.Create;
+ OutputProtocolFactory := TBinaryProtocolImpl.TFactory.Create;
+ InputTransportFactory := TTransportFactoryImpl.Create;
+ OutputTransportFactory := TTransportFactoryImpl.Create;
+
+ inherited Create( AProcessor, AServerTransport, InputTransportFactory,
+ OutputTransportFactory, InputProtocolFactory, OutputProtocolFactory, ALogDel);
+end;
+
+constructor TSimpleServer.Create( const AProcessor: IProcessor;
+ const AServerTransport: IServerTransport; const ATransportFactory: ITransportFactory);
+begin
+ inherited Create( AProcessor, AServerTransport, ATransportFactory,
+ ATransportFactory, TBinaryProtocolImpl.TFactory.Create, TBinaryProtocolImpl.TFactory.Create, DefaultLogDelegate);
+end;
+
+constructor TSimpleServer.Create( const AProcessor: IProcessor;
+ const AServerTransport: IServerTransport; const ATransportFactory: ITransportFactory;
+ const AProtocolFactory: IProtocolFactory);
+begin
+ inherited Create( AProcessor, AServerTransport, ATransportFactory,
+ ATransportFactory, AProtocolFactory, AProtocolFactory, DefaultLogDelegate);
+end;
+
+procedure TSimpleServer.Serve;
+var
+ client : ITransport;
+ InputTransport : ITransport;
+ OutputTransport : ITransport;
+ InputProtocol : IProtocol;
+ OutputProtocol : IProtocol;
+ context : IProcessorEvents;
+begin
+ try
+ FServerTransport.Listen;
+ except
+ on E: Exception do
+ begin
+ FLogDelegate( E.ToString);
+ end;
+ end;
+
+ if FServerEvents <> nil
+ then FServerEvents.PreServe;
+
+ client := nil;
+ while (not FStop) do
+ begin
+ try
+ // clean up any old instances before waiting for clients
+ InputTransport := nil;
+ OutputTransport := nil;
+ InputProtocol := nil;
+ OutputProtocol := nil;
+
+ // close any old connections before before waiting for new clients
+ if client <> nil then try
+ try
+ client.Close;
+ finally
+ client := nil;
+ end;
+ except
+ // catch all, we can't do much about it at this point
+ end;
+
+ client := FServerTransport.Accept( procedure
+ begin
+ if FServerEvents <> nil
+ then FServerEvents.PreAccept;
+ end);
+
+ if client = nil then begin
+ if FStop
+ then Abort // silent exception
+ else raise TTransportExceptionUnknown.Create('ServerTransport.Accept() may not return NULL');
+ end;
+
+ FLogDelegate( 'Client Connected!');
+
+ InputTransport := FInputTransportFactory.GetTransport( client );
+ OutputTransport := FOutputTransportFactory.GetTransport( client );
+ InputProtocol := FInputProtocolFactory.GetProtocol( InputTransport );
+ OutputProtocol := FOutputProtocolFactory.GetProtocol( OutputTransport );
+
+ if FServerEvents <> nil
+ then context := FServerEvents.CreateProcessingContext( InputProtocol, OutputProtocol)
+ else context := nil;
+
+ while not FStop do begin
+ if context <> nil
+ then context.Processing( client);
+ if not FProcessor.Process( InputProtocol, OutputProtocol, context)
+ then Break;
+ end;
+
+ except
+ on E: TTransportException do
+ begin
+ if FStop
+ then FLogDelegate('TSimpleServer was shutting down, caught ' + E.ToString)
+ else FLogDelegate( E.ToString);
+ end;
+ on E: Exception do
+ begin
+ FLogDelegate( E.ToString);
+ end;
+ end;
+
+ if context <> nil
+ then begin
+ context.CleanupContext;
+ context := nil;
+ end;
+
+ if InputTransport <> nil then
+ begin
+ InputTransport.Close;
+ end;
+ if OutputTransport <> nil then
+ begin
+ OutputTransport.Close;
+ end;
+ end;
+
+ if FStop then
+ begin
+ try
+ FServerTransport.Close;
+ except
+ on E: TTransportException do
+ begin
+ FLogDelegate('TServerTranport failed on close: ' + E.Message);
+ end;
+ end;
+ FStop := False;
+ end;
+end;
+
+procedure TSimpleServer.Stop;
+begin
+ FStop := True;
+ FServerTransport.Close;
+end;
+
+end.
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Socket.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Socket.pas
new file mode 100644
index 000000000..f0cab79db
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Socket.pas
@@ -0,0 +1,1617 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+unit Thrift.Socket;
+
+{$I Thrift.Defines.inc}
+{$I-} // prevent annoying errors with default log delegate and no console
+
+interface
+{$IFNDEF OLD_SOCKETS} // not for OLD_SOCKETS
+
+uses
+ Winapi.Windows, Winapi.Winsock2;
+
+const
+ AI_PASSIVE = $00000001; // Socket address will be used in bind() call
+ AI_CANONNAME = $00000002; // Return canonical name in first ai_canonname
+ AI_NUMERICHOST = $00000004; // Nodename must be a numeric address string
+ AI_NUMERICSERV = $00000008; // Servicename must be a numeric port number
+
+ AI_ALL = $00000100; // Query both IP6 and IP4 with AI_V4MAPPED
+ AI_ADDRCONFIG = $00000400; // Resolution only if global address configured
+ AI_V4MAPPED = $00000800; // On v6 failure, query v4 and convert to V4MAPPED format
+
+ AI_NON_AUTHORITATIVE = $00004000; // LUP_NON_AUTHORITATIVE
+ AI_SECURE = $00008000; // LUP_SECURE
+ AI_RETURN_PREFERRED_NAMES = $00010000; // LUP_RETURN_PREFERRED_NAMES
+
+ AI_FQDN = $00020000; // Return the FQDN in ai_canonname
+ AI_FILESERVER = $00040000; // Resolving fileserver name resolution
+
+type
+ PAddrInfoA = ^TAddrInfoA;
+ TAddrInfoA = record
+ ai_flags: Integer;
+ ai_family: Integer;
+ ai_socktype: Integer;
+ ai_protocol: Integer;
+ ai_addrlen: NativeUInt;
+ ai_canonname: PAnsiChar;
+ ai_addr: PSockAddr;
+ ai_next: PAddrInfoA;
+ end;
+
+ PAddrInfoW = ^TAddrInfoW;
+ TAddrInfoW = record
+ ai_flags: Integer;
+ ai_family: Integer;
+ ai_socktype: Integer;
+ ai_protocol: Integer;
+ ai_addrlen: NativeUInt;
+ ai_canonname: PChar;
+ ai_addr: PSockAddr;
+ ai_next: PAddrInfoW;
+ end;
+
+ TAddressFamily = USHORT;
+
+ TIn6Addr = record
+ case Integer of
+ 0: (_Byte: array[0..15] of UCHAR);
+ 1: (_Word: array[0..7] of USHORT);
+ end;
+
+ TScopeId = record
+ public
+ Value: ULONG;
+ private
+ function GetBitField(Loc: Integer): Integer; inline;
+ procedure SetBitField(Loc: Integer; const aValue: Integer); inline;
+ public
+ property Zone: Integer index $0028 read GetBitField write SetBitField;
+ property Level: Integer index $2804 read GetBitField write SetBitField;
+ end;
+
+ TSockAddrIn6 = record
+ sin6_family: TAddressFamily;
+ sin6_port: USHORT;
+ sin6_flowinfo: ULONG;
+ sin6_addr: TIn6Addr;
+ case Integer of
+ 0: (sin6_scope_id: ULONG);
+ 1: (sin6_scope_struct: TScopeId);
+ end;
+ PSockAddrIn6 = ^TSockAddrIn6;
+
+const
+ NI_NOFQDN = $01; // Only return nodename portion for local hosts
+ NI_NUMERICHOST = $02; // Return numeric form of the host's address
+ NI_NAMEREQD = $04; // Error if the host's name not in DNS
+ NI_NUMERICSERV = $08; // Return numeric form of the service (port #)
+ NI_DGRAM = $10; // Service is a datagram service
+
+ NI_MAXHOST = 1025; // Max size of a fully-qualified domain name
+ NI_MAXSERV = 32; // Max size of a service name
+
+function getaddrinfo(pNodeName, pServiceName: PAnsiChar; const pHints: TAddrInfoA; var ppResult: PAddrInfoA): Integer; stdcall;
+function GetAddrInfoW(pNodeName, pServiceName: PWideChar; const pHints: TAddrInfoW; var ppResult: PAddrInfoW): Integer; stdcall;
+procedure freeaddrinfo(pAddrInfo: PAddrInfoA); stdcall;
+procedure FreeAddrInfoW(pAddrInfo: PAddrInfoW); stdcall;
+function getnameinfo(const pSockaddr: TSockAddr; SockaddrLength: Integer; pNodeBuffer: PAnsiChar; NodeBufferSize: DWORD; pServiceBuffer: PAnsiChar;
+ ServiceBufferSize: DWORD; Flags: Integer): Integer; stdcall;
+function GetNameInfoW(const pSockaddr: TSockAddr; SockaddrLength: Integer; pNodeBuffer: PWideChar; NodeBufferSize: DWORD; pServiceBuffer: PWideChar;
+ ServiceBufferSize: DWORD; Flags: Integer): Integer; stdcall;
+
+type
+ TSmartPointerDestroyer<T> = reference to procedure(Value: T);
+
+ ISmartPointer<T> = reference to function: T;
+
+ TSmartPointer<T> = class(TInterfacedObject, ISmartPointer<T>)
+ private
+ FValue: T;
+ FDestroyer: TSmartPointerDestroyer<T>;
+ public
+ constructor Create(AValue: T; ADestroyer: TSmartPointerDestroyer<T>);
+ destructor Destroy; override;
+ function Invoke: T;
+ end;
+
+ TBaseSocket = class abstract
+ public type
+ TLogDelegate = reference to procedure( const str: string);
+ strict private
+ FPort: Integer;
+ FSocket: Winapi.Winsock2.TSocket;
+ FSendTimeout,
+ FRecvTimeout: Longword;
+ FKeepAlive: Boolean;
+ FLogDelegate: TLogDelegate;
+ class constructor Create;
+ class destructor Destroy;
+ class procedure DefaultLogDelegate(const Str: string);
+ protected type
+ IGetAddrInfoWrapper = interface
+ function Init: Integer;
+ function GetRes: PAddrInfoW;
+ property Res: PAddrInfoW read GetRes;
+ end;
+ TGetAddrInfoWrapper = class(TInterfacedObject, IGetAddrInfoWrapper)
+ strict private
+ FNode: string;
+ FService: string;
+ FHints,
+ FRes: PAddrInfoW;
+ public
+ constructor Create(ANode, AService: string; AHints: PAddrInfoW);
+ destructor Destroy; override;
+ function Init: Integer;
+ function GetRes: PAddrInfoW;
+ property Res: PAddrInfoW read GetRes;
+ end;
+ strict protected
+ procedure CommonInit; virtual;
+ function CreateSocket(AAddress: string; APort: Integer): IGetAddrInfoWrapper;
+ procedure SetRecvTimeout(ARecvTimeout: Longword); virtual;
+ procedure SetSendTimeout(ASendTimeout: Longword); virtual;
+ procedure SetKeepAlive(AKeepAlive: Boolean); virtual;
+ procedure SetSocket(ASocket: Winapi.Winsock2.TSocket);
+ property LogDelegate: TLogDelegate read FLogDelegate;
+ public
+ //
+ // Constructs a new socket. Note that this does NOT actually connect the
+ // socket.
+ //
+ constructor Create(ALogDelegate: TLogDelegate = nil); overload;
+ constructor Create(APort: Integer; ALogDelegate: TLogDelegate = nil); overload;
+
+ //
+ // Destroys the socket object, closing it if necessary.
+ //
+ destructor Destroy; override;
+
+ //
+ // Shuts down communications on the socket
+ //
+ procedure Close; virtual;
+
+ // The port that the socket is connected to
+ property Port: Integer read FPort write FPort;
+
+ // The receive timeout
+ property RecvTimeout: Longword read FRecvTimeout write SetRecvTimeout;
+
+ // The send timeout
+ property SendTimeout: Longword read FSendTimeout write SetSendTimeout;
+
+ // Set SO_KEEPALIVE
+ property KeepAlive: Boolean read FKeepAlive write SetKeepAlive;
+
+ // The underlying socket descriptor
+ property Socket: Winapi.Winsock2.TSocket read FSocket write SetSocket;
+ end;
+
+ TSocket = class(TBaseSocket)
+ strict private type
+ TCachedPeerAddr = record
+ case Integer of
+ 0: (ipv4: TSockAddrIn);
+ 1: (ipv6: TSockAddrIn6);
+ end;
+ strict private
+ FHost: string;
+ FPeerHost: string;
+ FPeerAddress: string;
+ FPeerPort: Integer;
+ FInterruptListener: ISmartPointer<Winapi.Winsock2.TSocket>;
+ FConnTimeout: Longword;
+ FLingerOn: Boolean;
+ FLingerVal: Integer;
+ FNoDelay: Boolean;
+ FMaxRecvRetries: Longword;
+ FCachedPeerAddr: TCachedPeerAddr;
+ procedure InitPeerInfo;
+ procedure OpenConnection(Res: TBaseSocket.IGetAddrInfoWrapper);
+ procedure LocalOpen;
+ procedure SetGenericTimeout(S: Winapi.Winsock2.TSocket; Timeout: Longword; OptName: Integer);
+ function GetIsOpen: Boolean;
+ procedure SetNoDelay(ANoDelay: Boolean);
+ function GetSocketInfo: string;
+ function GetPeerHost: string;
+ function GetPeerAddress: string;
+ function GetPeerPort: Integer;
+ function GetOrigin: string;
+ strict protected
+ procedure CommonInit; override;
+ procedure SetRecvTimeout(ARecvTimeout: Longword); override;
+ procedure SetSendTimeout(ASendTimeout: Longword); override;
+ procedure SetKeepAlive(AKeepAlive: Boolean); override;
+ public
+ //
+ // Constructs a new socket. Note that this does NOT actually connect the
+ // socket.
+ //
+ constructor Create(ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
+
+ //
+ // Constructs a new socket. Note that this does NOT actually connect the
+ // socket.
+ //
+ // @param host An IP address or hostname to connect to
+ // @param port The port to connect on
+ //
+ constructor Create(AHost: string; APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
+
+ //
+ // Constructor to create socket from socket descriptor.
+ //
+ constructor Create(ASocket: Winapi.Winsock2.TSocket; ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
+
+ //
+ // Constructor to create socket from socket descriptor that
+ // can be interrupted safely.
+ //
+ constructor Create(ASocket: Winapi.Winsock2.TSocket; AInterruptListener: ISmartPointer<Winapi.Winsock2.TSocket>;
+ ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
+
+ //
+ // Creates and opens the socket
+ //
+ // @throws ETransportationException If the socket could not connect
+ //
+ procedure Open;
+
+ //
+ // Shuts down communications on the socket
+ //
+ procedure Close; override;
+
+ //
+ // Reads from the underlying socket.
+ // \returns the number of bytes read or 0 indicates EOF
+ // \throws TTransportException of types:
+ // Interrupted means the socket was interrupted
+ // out of a blocking call
+ // NotOpen means the socket has been closed
+ // TimedOut means the receive timeout expired
+ // Unknown means something unexpected happened
+ //
+ function Read(var Buf; Len: Integer): Integer;
+
+ //
+ // Writes to the underlying socket. Loops until done or fail.
+ //
+ procedure Write(const Buf; Len: Integer);
+
+ //
+ // Writes to the underlying socket. Does single send() and returns result.
+ //
+ function WritePartial(const Buf; Len: Integer): Integer;
+
+ //
+ // Returns a cached copy of the peer address.
+ //
+ function GetCachedAddress(out Len: Integer): PSockAddr;
+
+ //
+ // Set a cache of the peer address (used when trivially available: e.g.
+ // accept() or connect()). Only caches IPV4 and IPV6; unset for others.
+ //
+ procedure SetCachedAddress(const Addr: TSockAddr; Len: Integer);
+
+ //
+ // Controls whether the linger option is set on the socket.
+ //
+ // @param on Whether SO_LINGER is on
+ // @param linger If linger is active, the number of seconds to linger for
+ //
+ procedure SetLinger(LingerOn: Boolean; LingerVal: Integer);
+
+ //
+ // Calls select() on the socket to see if there is more data available.
+ //
+ function Peek: Boolean;
+
+ // Whether the socket is alive
+ property IsOpen: Boolean read GetIsOpen;
+
+ // The host that the socket is connected to
+ property Host: string read FHost write FHost;
+
+ // Whether to enable or disable Nagle's algorithm
+ property NoDelay: Boolean read FNoDelay write SetNoDelay;
+
+ // Connect timeout
+ property ConnTimeout: Longword read FConnTimeout write FConnTimeout;
+
+ // The max number of recv retries in the case of a WSAEWOULDBLOCK
+ property MaxRecvRetries: Longword read FMaxRecvRetries write FMaxRecvRetries;
+
+ // Socket information formatted as a string <Host: x Port: x>
+ property SocketInfo: string read GetSocketInfo;
+
+ // The DNS name of the host to which the socket is connected
+ property PeerHost: string read GetPeerHost;
+
+ // The address of the host to which the socket is connected
+ property PeerAddress: string read GetPeerAddress;
+
+ // The port of the host to which the socket is connected
+ property PeerPort: Integer read GetPeerPort;
+
+ // The origin the socket is connected to
+ property Origin: string read GetOrigin;
+ end;
+
+ TServerSocketFunc = reference to procedure(sock: Winapi.Winsock2.TSocket);
+
+ TServerSocket = class(TBaseSocket)
+ strict private
+ FAddress: string;
+ FAcceptBacklog,
+ FRetryLimit,
+ FRetryDelay,
+ FTcpSendBuffer,
+ FTcpRecvBuffer: Integer;
+ FAcceptTimeout: Longword;
+ FListening,
+ FInterruptableChildren: Boolean;
+ FInterruptSockWriter, // is notified on Interrupt()
+ FInterruptSockReader, // is used in select with FSocket for interruptability
+ FChildInterruptSockWriter: Winapi.Winsock2.TSocket; // is notified on InterruptChildren()
+ FChildInterruptSockReader: ISmartPointer<Winapi.Winsock2.TSocket>; // if FnterruptableChildren this is shared with child TSockets
+ FListenCallback,
+ FAcceptCallback: TServerSocketFunc;
+ function CreateSocketObj(Client: Winapi.Winsock2.TSocket): TSocket;
+ procedure Notify(NotifySocket: Winapi.Winsock2.TSocket);
+ procedure SetInterruptableChildren(AValue: Boolean);
+ strict protected
+ procedure CommonInit; override;
+ public const
+ DEFAULT_BACKLOG = 1024;
+ public
+ //
+ // Constructor.
+ //
+ // @param port Port number to bind to
+ //
+ constructor Create(APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
+
+ //
+ // Constructor.
+ //
+ // @param port Port number to bind to
+ // @param sendTimeout Socket send timeout
+ // @param recvTimeout Socket receive timeout
+ //
+ constructor Create(APort: Integer; ASendTimeout, ARecvTimeout: Longword; ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
+
+ //
+ // Constructor.
+ //
+ // @param address Address to bind to
+ // @param port Port number to bind to
+ //
+ constructor Create(AAddress: string; APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
+
+ procedure Listen;
+ function Accept: TSocket;
+ procedure Interrupt;
+ procedure InterruptChildren;
+ procedure Close; override;
+
+ property AcceptBacklog: Integer read FAcceptBacklog write FAcceptBacklog;
+ property AcceptTimeout: Longword read FAcceptTimeout write FAcceptTimeout;
+ property RetryLimit: Integer read FRetryLimit write FRetryLimit;
+ property RetryDelay: Integer read FRetryDelay write FRetryDelay;
+ property TcpSendBuffer: Integer read FTcpSendBuffer write FTcpSendBuffer;
+ property TcpRecvBuffer: Integer read FTcpRecvBuffer write FTcpRecvBuffer;
+
+ // When enabled (the default), new children TSockets will be constructed so
+ // they can be interrupted by TServerTransport.InterruptChildren().
+ // This is more expensive in terms of system calls (poll + recv) however
+ // ensures a connected client cannot interfere with TServer.Stop().
+ //
+ // When disabled, TSocket children do not incur an additional poll() call.
+ // Server-side reads are more efficient, however a client can interfere with
+ // the server's ability to shutdown properly by staying connected.
+ //
+ // Must be called before listen(); mode cannot be switched after that.
+ // \throws EPropertyError if listen() has been called
+ property InterruptableChildren: Boolean read FInterruptableChildren write SetInterruptableChildren;
+
+ // listenCallback gets called just before listen, and after all Thrift
+ // setsockopt calls have been made. If you have custom setsockopt
+ // things that need to happen on the listening socket, this is the place to do it.
+ property ListenCallback: TServerSocketFunc read FListenCallback write FListenCallback;
+
+ // acceptCallback gets called after each accept call, on the newly created socket.
+ // It is called after all Thrift setsockopt calls have been made. If you have
+ // custom setsockopt things that need to happen on the accepted
+ // socket, this is the place to do it.
+ property AcceptCallback: TServerSocketFunc read FAcceptCallback write FAcceptCallback;
+ end;
+
+{$ENDIF} // not for OLD_SOCKETS
+implementation
+{$IFNDEF OLD_SOCKETS} // not for OLD_SOCKETS
+
+uses
+ System.SysUtils, System.Math, System.DateUtils, Thrift.Transport;
+
+constructor TBaseSocket.TGetAddrInfoWrapper.Create(ANode, AService: string; AHints: PAddrInfoW);
+begin
+ inherited Create;
+ FNode := ANode;
+ FService := AService;
+ FHints := AHints;
+ FRes := nil;
+end;
+
+destructor TBaseSocket.TGetAddrInfoWrapper.Destroy;
+begin
+ if Assigned(FRes) then
+ FreeAddrInfoW(FRes);
+ inherited Destroy;
+end;
+
+function TBaseSocket.TGetAddrInfoWrapper.Init: Integer;
+begin
+ if FRes = nil then
+ Exit(GetAddrInfoW(@FNode[1], @FService[1], FHints^, FRes));
+ Result := 0;
+end;
+
+function TBaseSocket.TGetAddrInfoWrapper.GetRes: PAddrInfoW;
+begin
+ Result := FRes;
+end;
+
+procedure DestroyerOfFineSockets(ssock: Winapi.Winsock2.TSocket);
+begin
+ closesocket(ssock);
+end;
+
+function TScopeId.GetBitField(Loc: Integer): Integer;
+begin
+ Result := (Value shr (Loc shr 8)) and ((1 shl (Loc and $FF)) - 1);
+end;
+
+procedure TScopeId.SetBitField(Loc: Integer; const aValue: Integer);
+begin
+ Value := (Value and ULONG((not ((1 shl (Loc and $FF)) - 1)))) or ULONG(aValue shl (Loc shr 8));
+end;
+
+function getaddrinfo; external 'ws2_32.dll' name 'getaddrinfo';
+function GetAddrInfoW; external 'ws2_32.dll' name 'GetAddrInfoW';
+procedure freeaddrinfo; external 'ws2_32.dll' name 'freeaddrinfo';
+procedure FreeAddrInfoW; external 'ws2_32.dll' name 'FreeAddrInfoW';
+function getnameinfo; external 'ws2_32.dll' name 'getnameinfo';
+function GetNameInfoW; external 'ws2_32.dll' name 'GetNameInfoW';
+
+constructor TSmartPointer<T>.Create(AValue: T; ADestroyer: TSmartPointerDestroyer<T>);
+begin
+ inherited Create;
+ FValue := AValue;
+ FDestroyer := ADestroyer;
+end;
+
+destructor TSmartPointer<T>.Destroy;
+begin
+ if Assigned(FDestroyer) then FDestroyer(FValue);
+ inherited Destroy;
+end;
+
+function TSmartPointer<T>.Invoke: T;
+begin
+ Result := FValue;
+end;
+
+class constructor TBaseSocket.Create;
+var
+ Version: WORD;
+ Data: WSAData;
+ Error: Integer;
+begin
+ Version := $0202;
+ FillChar(Data, SizeOf(Data), 0);
+ Error := WSAStartup(Version, Data);
+ if Error <> 0 then
+ raise Exception.Create('Failed to initialize Winsock.');
+end;
+
+class destructor TBaseSocket.Destroy;
+begin
+ WSACleanup;
+end;
+
+class procedure TBaseSocket.DefaultLogDelegate(const Str: string);
+var
+ OutStr: string;
+begin
+ OutStr := Format('Thrift: %s %s', [DateTimeToStr(Now, TFormatSettings.Create), Str]);
+ try
+ Writeln(OutStr);
+ if IoResult <> 0 then OutputDebugString(PChar(OutStr));
+ except
+ OutputDebugString(PChar(OutStr));
+ end;
+end;
+
+procedure TBaseSocket.CommonInit;
+begin
+ FSocket := INVALID_SOCKET;
+ FPort := 0;
+ FSendTimeout := 0;
+ FRecvTimeout := 0;
+ FKeepAlive := False;
+ FLogDelegate := DefaultLogDelegate;
+end;
+
+function TBaseSocket.CreateSocket(AAddress: string; APort: Integer): IGetAddrInfoWrapper;
+var
+ Hints: TAddrInfoW;
+ Res: PAddrInfoW;
+ ThePort: array[0..5] of Char;
+ Error: Integer;
+begin
+ FillChar(Hints, SizeOf(Hints), 0);
+ Hints.ai_family := PF_UNSPEC;
+ Hints.ai_socktype := SOCK_STREAM;
+ Hints.ai_flags := AI_PASSIVE or AI_ADDRCONFIG;
+ StrFmt(ThePort, '%d', [FPort]);
+
+ Result := TGetAddrInfoWrapper.Create(AAddress, ThePort, @Hints);
+ Error := Result.Init;
+ if Error <> 0 then begin
+ LogDelegate(Format('GetAddrInfoW %d: %s', [Error, SysErrorMessage(Error)]));
+ Close;
+ raise TTransportExceptionNotOpen.Create('Could not resolve host for server socket.');
+ end;
+
+ // Pick the ipv6 address first since ipv4 addresses can be mapped
+ // into ipv6 space.
+ Res := Result.Res;
+ while Assigned(Res) do begin
+ if (Res^.ai_family = AF_INET6) or (not Assigned(Res^.ai_next)) then
+ Break;
+ Res := Res^.ai_next;
+ end;
+
+ FSocket := Winapi.Winsock2.socket(Res^.ai_family, Res^.ai_socktype, Res^.ai_protocol);
+ if FSocket = INVALID_SOCKET then begin
+ Error := WSAGetLastError;
+ LogDelegate(Format('TBaseSocket.CreateSocket() socket() %s', [SysErrorMessage(Error)]));
+ Close;
+ raise TTransportExceptionNotOpen.Create(Format('socket(): %s', [SysErrorMessage(Error)]));
+ end;
+end;
+
+procedure TBaseSocket.SetRecvTimeout(ARecvTimeout: Longword);
+begin
+ FRecvTimeout := ARecvTimeout;
+end;
+
+procedure TBaseSocket.SetSendTimeout(ASendTimeout: Longword);
+begin
+ FSendTimeout := ASendTimeout;
+end;
+
+procedure TBaseSocket.SetKeepAlive(AKeepAlive: Boolean);
+begin
+ FKeepAlive := AKeepAlive;
+end;
+
+procedure TBaseSocket.SetSocket(ASocket: Winapi.Winsock2.TSocket);
+begin
+ if FSocket <> INVALID_SOCKET then
+ Close;
+ FSocket := ASocket;
+end;
+
+constructor TBaseSocket.Create(ALogDelegate: TLogDelegate);
+begin
+ inherited Create;
+ CommonInit;
+ if Assigned(ALogDelegate) then FLogDelegate := ALogDelegate;
+end;
+
+constructor TBaseSocket.Create(APort: Integer; ALogDelegate: TLogDelegate);
+begin
+ inherited Create;
+ CommonInit;
+ FPort := APort;
+ if Assigned(ALogDelegate) then FLogDelegate := ALogDelegate;
+end;
+
+destructor TBaseSocket.Destroy;
+begin
+ Close;
+ inherited Destroy;
+end;
+
+procedure TBaseSocket.Close;
+begin
+ if FSocket <> INVALID_SOCKET then begin
+ shutdown(FSocket, SD_BOTH);
+ closesocket(FSocket);
+ end;
+ FSocket := INVALID_SOCKET;
+end;
+
+procedure TSocket.InitPeerInfo;
+begin
+ FCachedPeerAddr.ipv4.sin_family := AF_UNSPEC;
+ FPeerHost := '';
+ FPeerAddress := '';
+ FPeerPort := 0;
+end;
+
+procedure TSocket.CommonInit;
+begin
+ inherited CommonInit;
+ FHost := '';
+ FInterruptListener := nil;
+ FConnTimeout := 0;
+ FLingerOn := True;
+ FLingerVal := 0;
+ FNoDelay := True;
+ FMaxRecvRetries := 5;
+ InitPeerInfo;
+end;
+
+procedure TSocket.OpenConnection(Res: TBaseSocket.IGetAddrInfoWrapper);
+label
+ Done;
+var
+ ErrnoCopy: Integer;
+ Ret,
+ Ret2: Integer;
+ Fds: TFdSet;
+ TVal: TTimeVal;
+ PTVal: PTimeVal;
+ Val,
+ Lon: Integer;
+ One,
+ Zero: Cardinal;
+begin
+ if SendTimeout > 0 then SetSendTimeout(SendTimeout);
+ if RecvTimeout > 0 then SetRecvTimeout(RecvTimeout);
+ if KeepAlive then SetKeepAlive(KeepAlive);
+ SetLinger(FLingerOn, FLingerVal);
+ SetNoDelay(FNoDelay);
+
+ // Set the socket to be non blocking for connect if a timeout exists
+ Zero := 0;
+ if FConnTimeout > 0 then begin
+ One := 1;
+ if ioctlsocket(Socket, Integer(FIONBIO), One) = SOCKET_ERROR then begin
+ ErrnoCopy := WSAGetLastError;
+ LogDelegate(Format('TSocket.OpenConnection() ioctlsocket() %s %s', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionNotOpen.Create(Format('ioctlsocket() failed: %s', [SysErrorMessage(ErrnoCopy)]));
+ end;
+ end
+ else begin
+ if ioctlsocket(Socket, Integer(FIONBIO), Zero) = SOCKET_ERROR then begin
+ ErrnoCopy := WSAGetLastError;
+ LogDelegate(Format('TSocket.OpenConnection() ioctlsocket() %s %s', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionNotOpen.Create(Format('ioctlsocket() failed: %s', [SysErrorMessage(ErrnoCopy)]));
+ end;
+ end;
+
+ Ret := connect(Socket, Res.Res^.ai_addr^, Res.Res^.ai_addrlen);
+ if Ret = 0 then goto Done;
+
+ ErrnoCopy := WSAGetLastError;
+ if (ErrnoCopy <> WSAEINPROGRESS) and (ErrnoCopy <> WSAEWOULDBLOCK) then begin
+ LogDelegate(Format('TSocket.OpenConnection() connect() ', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionNotOpen.Create(Format('connect() failed: %s', [SysErrorMessage(ErrnoCopy)]));
+ end;
+
+ FD_ZERO(Fds);
+ _FD_SET(Socket, Fds);
+ if FConnTimeout > 0 then begin
+ TVal.tv_sec := FConnTimeout div 1000;
+ TVal.tv_usec := (FConnTimeout mod 1000) * 1000;
+ PTVal := @TVal;
+ end
+ else
+ PTVal := nil;
+ Ret := select(1, nil, @Fds, nil, PTVal);
+
+ if Ret > 0 then begin
+ // Ensure the socket is connected and that there are no errors set
+ Lon := SizeOf(Val);
+ Ret2 := getsockopt(Socket, SOL_SOCKET, SO_ERROR, @Val, Lon);
+ if Ret2 = SOCKET_ERROR then begin
+ ErrnoCopy := WSAGetLastError;
+ LogDelegate(Format('TSocket.OpenConnection() getsockopt() ', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionNotOpen.Create(Format('getsockopt(): %s', [SysErrorMessage(ErrnoCopy)]));
+ end;
+ // no errors on socket, go to town
+ if Val = 0 then goto Done;
+ LogDelegate(Format('TSocket.OpenConnection() error on socket (after select()) ', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionNotOpen.Create(Format('socket OpenConnection() error: %s', [SysErrorMessage(Val)]));
+ end
+ else if Ret = 0 then begin
+ // socket timed out
+ LogDelegate(Format('TSocket.OpenConnection() timed out ', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionNotOpen.Create('OpenConnection() timed out');
+ end
+ else begin
+ // error on select()
+ ErrnoCopy := WSAGetLastError;
+ LogDelegate(Format('TSocket.OpenConnection() select() ', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionNotOpen.Create(Format('select() failed: %s', [SysErrorMessage(ErrnoCopy)]));
+ end;
+
+Done:
+ // Set socket back to normal mode (blocking)
+ ioctlsocket(Socket, Integer(FIONBIO), Zero);
+ SetCachedAddress(Res.Res^.ai_addr^, Res.Res^.ai_addrlen);
+end;
+
+procedure TSocket.LocalOpen;
+var
+ Res: TBaseSocket.IGetAddrInfoWrapper;
+begin
+ if IsOpen then Exit;
+
+ // Validate port number
+ if (Port < 0) or (Port > $FFFF) then
+ raise TTransportExceptionBadArgs.Create('Specified port is invalid');
+
+ Res := CreateSocket(Host, Port);
+
+ OpenConnection(Res);
+end;
+
+procedure TSocket.SetGenericTimeout(S: Winapi.Winsock2.TSocket; Timeout: Longword; OptName: Integer);
+var
+ Time: DWORD;
+begin
+ if S = INVALID_SOCKET then
+ Exit;
+
+ Time := Timeout;
+
+ if setsockopt(S, SOL_SOCKET, OptName, @Time, SizeOf(Time)) = SOCKET_ERROR then
+ LogDelegate(Format('SetGenericTimeout() setsockopt() %s', [SysErrorMessage(WSAGetLastError)]));
+end;
+
+function TSocket.GetIsOpen: Boolean;
+begin
+ Result := Socket <> INVALID_SOCKET;
+end;
+
+procedure TSocket.SetNoDelay(ANoDelay: Boolean);
+var
+ V: Integer;
+begin
+ FNoDelay := ANoDelay;
+ if Socket = INVALID_SOCKET then
+ Exit;
+
+ V := IfThen(FNoDelay, 1, 0);
+ if setsockopt(Socket, IPPROTO_TCP, TCP_NODELAY, @V, SizeOf(V)) = SOCKET_ERROR then
+ LogDelegate(Format('TSocket.SetNoDelay() setsockopt() %s %s', [SocketInfo, SysErrorMessage(WSAGetLastError)]));
+end;
+
+function TSocket.GetSocketInfo: string;
+begin
+ if (FHost = '') or (Port = 0) then
+ Result := '<Host: ' + GetPeerAddress + ' Port: ' + GetPeerPort.ToString + '>'
+ else
+ Result := '<Host: ' + FHost + ' Port: ' + Port.ToString + '>';
+end;
+
+function TSocket.GetPeerHost: string;
+var
+ Addr: TSockAddrStorage;
+ AddrPtr: PSockAddr;
+ AddrLen: Integer;
+ ClientHost: array[0..NI_MAXHOST-1] of Char;
+ ClientService: array[0..NI_MAXSERV-1] of Char;
+begin
+ if FPeerHost = '' then begin
+ if Socket = INVALID_SOCKET then
+ Exit(FPeerHost);
+
+ AddrPtr := GetCachedAddress(AddrLen);
+ if AddrPtr = nil then begin
+ AddrLen := SizeOf(Addr);
+ if getpeername(Socket, PSockAddr(@Addr)^, AddrLen) <> 0 then
+ Exit(FPeerHost);
+ AddrPtr := PSockAddr(@Addr);
+ SetCachedAddress(AddrPtr^, AddrLen);
+ end;
+
+ GetNameInfoW(AddrPtr^, AddrLen, ClientHost, NI_MAXHOST, ClientService, NI_MAXSERV, 0);
+ FPeerHost := ClientHost;
+ end;
+ Result := FPeerHost;
+end;
+
+function TSocket.GetPeerAddress: string;
+var
+ Addr: TSockAddrStorage;
+ AddrPtr: PSockAddr;
+ AddrLen: Integer;
+ ClientHost: array[0..NI_MAXHOST-1] of Char;
+ ClientService: array[0..NI_MAXSERV-1] of Char;
+begin
+ if FPeerAddress = '' then begin
+ if Socket = INVALID_SOCKET then
+ Exit(FPeerAddress);
+
+ AddrPtr := GetCachedAddress(AddrLen);
+ if AddrPtr = nil then begin
+ AddrLen := SizeOf(Addr);
+ if getpeername(Socket, PSockAddr(@Addr)^, AddrLen) <> 0 then
+ Exit(FPeerHost);
+ AddrPtr := PSockAddr(@Addr);
+ SetCachedAddress(AddrPtr^, AddrLen);
+ end;
+
+ GetNameInfoW(AddrPtr^, AddrLen, ClientHost, NI_MAXHOST, ClientService, NI_MAXSERV, NI_NUMERICHOST or NI_NUMERICSERV);
+ FPeerAddress := ClientHost;
+ TryStrToInt(ClientService, FPeerPort);
+ end;
+ Result := FPeerAddress
+end;
+
+function TSocket.GetPeerPort: Integer;
+begin
+ GetPeerAddress;
+ Result := FPeerPort;
+end;
+
+function TSocket.GetOrigin: string;
+begin
+ Result := GetPeerHost + ':' + GetPeerPort.ToString;
+end;
+
+procedure TSocket.SetRecvTimeout(ARecvTimeout: Longword);
+begin
+ inherited SetRecvTimeout(ARecvTimeout);
+ SetGenericTimeout(Socket, ARecvTimeout, SO_RCVTIMEO);
+end;
+
+procedure TSocket.SetSendTimeout(ASendTimeout: Longword);
+begin
+ inherited SetSendTimeout(ASendTimeout);
+ SetGenericTimeout(Socket, ASendTimeout, SO_SNDTIMEO);
+end;
+
+procedure TSocket.SetKeepAlive(AKeepAlive: Boolean);
+var
+ Value: Integer;
+begin
+ inherited SetKeepAlive(AKeepAlive);
+
+ Value := IfThen(KeepAlive, 1, 0);
+ if setsockopt(Socket, SOL_SOCKET, SO_KEEPALIVE, @Value, SizeOf(Value)) = SOCKET_ERROR then
+ LogDelegate(Format('TSocket.SetKeepAlive() setsockopt() %s %s', [SocketInfo, SysErrorMessage(WSAGetLastError)]));
+end;
+
+constructor TSocket.Create(ALogDelegate: TBaseSocket.TLogDelegate = nil);
+begin
+ // Not needed, but just a placeholder
+ inherited Create(ALogDelegate);
+end;
+
+constructor TSocket.Create(AHost: string; APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate);
+begin
+ inherited Create(APort, ALogDelegate);
+ FHost := AHost;
+end;
+
+constructor TSocket.Create(ASocket: Winapi.Winsock2.TSocket; ALogDelegate: TBaseSocket.TLogDelegate);
+begin
+ inherited Create(ALogDelegate);
+ Socket := ASocket;
+end;
+
+constructor TSocket.Create(ASocket: Winapi.Winsock2.TSocket; AInterruptListener: ISmartPointer<Winapi.Winsock2.TSocket>;
+ ALogDelegate: TBaseSocket.TLogDelegate);
+begin
+ inherited Create(ALogDelegate);
+ Socket := ASocket;
+ FInterruptListener := AInterruptListener;
+end;
+
+procedure TSocket.Open;
+begin
+ if IsOpen then Exit;
+ LocalOpen;
+end;
+
+procedure TSocket.Close;
+begin
+ inherited Close;
+ InitPeerInfo;
+end;
+
+function TSocket.Read(var Buf; Len: Integer): Integer;
+label
+ TryAgain;
+var
+ Retries: Longword;
+ EAgainThreshold,
+ ReadElapsed: UInt64;
+ Start: TDateTime;
+ Got: Integer;
+ Fds: TFdSet;
+ ErrnoCopy: Integer;
+ TVal: TTimeVal;
+ PTVal: PTimeVal;
+ Ret: Integer;
+begin
+ if Socket = INVALID_SOCKET then
+ raise TTransportExceptionNotOpen.Create('Called read on non-open socket');
+
+ Retries := 0;
+
+ // THRIFT_EAGAIN can be signalled both when a timeout has occurred and when
+ // the system is out of resources (an awesome undocumented feature).
+ // The following is an approximation of the time interval under which
+ // THRIFT_EAGAIN is taken to indicate an out of resources error.
+ EAgainThreshold := 0;
+ if RecvTimeout <> 0 then
+ // if a readTimeout is specified along with a max number of recv retries, then
+ // the threshold will ensure that the read timeout is not exceeded even in the
+ // case of resource errors
+ EAgainThreshold := RecvTimeout div IfThen(FMaxRecvRetries > 0, FMaxRecvRetries, 2);
+
+TryAgain:
+ // Read from the socket
+ if RecvTimeout > 0 then
+ Start := Now
+ else
+ // if there is no read timeout we don't need the TOD to determine whether
+ // an THRIFT_EAGAIN is due to a timeout or an out-of-resource condition.
+ Start := 0;
+
+ if Assigned(FInterruptListener) then begin
+ FD_ZERO(Fds);
+ _FD_SET(Socket, Fds);
+ _FD_SET(FInterruptListener, Fds);
+ if RecvTimeout > 0 then begin
+ TVal.tv_sec := RecvTimeout div 1000;
+ TVal.tv_usec := (RecvTimeout mod 1000) * 1000;
+ PTVal := @TVal;
+ end
+ else
+ PTVal := nil;
+
+ Ret := select(2, @Fds, nil, nil, PTVal);
+ ErrnoCopy := WSAGetLastError;
+ if Ret < 0 then begin
+ // error cases
+ if (ErrnoCopy = WSAEINTR) and (Retries < FMaxRecvRetries) then begin
+ Inc(Retries);
+ goto TryAgain;
+ end;
+ LogDelegate(Format('TSocket.Read() select() %s', [SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionUnknown.Create(Format('Unknown: %s', [SysErrorMessage(ErrnoCopy)]));
+ end
+ else if Ret > 0 then begin
+ // Check the interruptListener
+ if FD_ISSET(FInterruptListener, Fds) then
+ raise TTransportExceptionInterrupted.Create('Interrupted');
+ end
+ else // Ret = 0
+ raise TTransportExceptionTimedOut.Create('WSAEWOULDBLOCK (timed out)');
+
+ // falling through means there is something to recv and it cannot block
+ end;
+
+ Got := recv(Socket, Buf, Len, 0);
+ ErrnoCopy := WSAGetLastError;
+ // Check for error on read
+ if Got < 0 then begin
+ if ErrnoCopy = WSAEWOULDBLOCK then begin
+ // if no timeout we can assume that resource exhaustion has occurred.
+ if RecvTimeout = 0 then
+ raise TTransportExceptionTimedOut.Create('WSAEWOULDBLOCK (unavailable resources)');
+ // check if this is the lack of resources or timeout case
+ ReadElapsed := MilliSecondsBetween(Now, Start);
+ if (EAgainThreshold = 0) or (ReadElapsed < EAgainThreshold) then begin
+ if Retries < FMaxRecvRetries then begin
+ Inc(Retries);
+ Sleep(1);
+ goto TryAgain;
+ end
+ else
+ raise TTransportExceptionTimedOut.Create('WSAEWOULDBLOCK (unavailable resources)');
+ end
+ else
+ // infer that timeout has been hit
+ raise TTransportExceptionTimedOut.Create('WSAEWOULDBLOCK (timed out)');
+ end;
+
+ // If interrupted, try again
+ if (ErrnoCopy = WSAEINTR) and (Retries < FMaxRecvRetries) then begin
+ Inc(Retries);
+ goto TryAgain;
+ end;
+
+ if ErrnoCopy = WSAECONNRESET then
+ Exit(0);
+
+ // This ish isn't open
+ if ErrnoCopy = WSAENOTCONN then
+ raise TTransportExceptionNotOpen.Create('WSAENOTCONN');
+
+ // Timed out!
+ if ErrnoCopy = WSAETIMEDOUT then
+ raise TTransportExceptionNotOpen.Create('WSAETIMEDOUT');
+
+ // Now it's not a try again case, but a real probblez
+ LogDelegate(Format('TSocket.Read() recv() %s %s', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
+
+ // Some other error, whatevz
+ raise TTransportExceptionUnknown.Create(Format('Unknown: %s', [SysErrorMessage(ErrnoCopy)]));
+ end;
+
+ Result := Got;
+end;
+
+procedure TSocket.Write(const Buf; Len: Integer);
+var
+ Sent, B: Integer;
+begin
+ Sent := 0;
+ while Sent < Len do begin
+ B := WritePartial((PByte(@Buf) + Sent)^, Len - Sent);
+ if B = 0 then
+ // This should only happen if the timeout set with SO_SNDTIMEO expired.
+ // Raise an exception.
+ raise TTransportExceptionTimedOut.Create('send timeout expired');
+ Inc(Sent, B);
+ end;
+end;
+
+function TSocket.WritePartial(const Buf; Len: Integer): Integer;
+var
+ B: Integer;
+ ErrnoCopy: Integer;
+begin
+ if Socket = INVALID_SOCKET then
+ raise TTransportExceptionNotOpen.Create('Called write on non-open socket');
+
+ B := send(Socket, Buf, Len, 0);
+
+ if B < 0 then begin
+ // Fail on a send error
+ ErrnoCopy := WSAGetLastError;
+ if ErrnoCopy = WSAEWOULDBLOCK then
+ Exit(0);
+
+ LogDelegate(Format('TSocket.WritePartial() send() %s %s', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
+
+ if (ErrnoCopy = WSAECONNRESET) or (ErrnoCopy = WSAENOTCONN) then begin
+ Close;
+ raise TTransportExceptionNotOpen.Create(Format('write() send(): %s', [SysErrorMessage(ErrnoCopy)]));
+ end;
+
+ raise TTransportExceptionUnknown.Create(Format('write() send(): %s', [SysErrorMessage(ErrnoCopy)]));
+ end;
+
+ // Fail on blocked send
+ if B = 0 then
+ raise TTransportExceptionNotOpen.Create('Socket send returned 0.');
+
+ Result := B;
+end;
+
+function TSocket.GetCachedAddress(out Len: Integer): PSockAddr;
+begin
+ case FCachedPeerAddr.ipv4.sin_family of
+ AF_INET: begin
+ Len := SizeOf(TSockAddrIn);
+ Result := PSockAddr(@FCachedPeerAddr.ipv4);
+ end;
+ AF_INET6: begin
+ Len := SizeOf(TSockAddrIn6);
+ Result := PSockAddr(@FCachedPeerAddr.ipv6);
+ end;
+ else
+ Len := 0;
+ Result := nil;
+ end;
+end;
+
+procedure TSocket.SetCachedAddress(const Addr: TSockAddr; Len: Integer);
+begin
+ case Addr.sa_family of
+ AF_INET: if Len = SizeOf(TSockAddrIn) then FCachedPeerAddr.ipv4 := PSockAddrIn(@Addr)^;
+ AF_INET6: if Len = SizeOf(TSockAddrIn6) then FCachedPeerAddr.ipv6 := PSockAddrIn6(@Addr)^;
+ end;
+ FPeerAddress := '';
+ FPeerHost := '';
+ FPeerPort := 0;
+end;
+
+procedure TSocket.SetLinger(LingerOn: Boolean; LingerVal: Integer);
+var
+ L: TLinger;
+begin
+ FLingerOn := LingerOn;
+ FLingerVal := LingerVal;
+ if Socket = INVALID_SOCKET then
+ Exit;
+
+ L.l_onoff := IfThen(FLingerOn, 1, 0);
+ L.l_linger := LingerVal;
+
+ if setsockopt(Socket, SOL_SOCKET, SO_LINGER, @L, SizeOf(L)) = SOCKET_ERROR then
+ LogDelegate(Format('TSocket.SetLinger() setsockopt() %s %s', [SocketInfo, SysErrorMessage(WSAGetLastError)]));
+end;
+
+function TSocket.Peek: Boolean;
+var
+ Retries: Longword;
+ Fds: TFdSet;
+ TVal: TTimeVal;
+ PTVal: PTimeVal;
+ Ret: Integer;
+ ErrnoCopy: Integer;
+ Buf: Byte;
+begin
+ if not IsOpen then Exit(False);
+
+ if Assigned(FInterruptListener) then begin
+ Retries := 0;
+ while true do begin
+ FD_ZERO(Fds);
+ _FD_SET(Socket, Fds);
+ _FD_SET(FInterruptListener, Fds);
+ if RecvTimeout > 0 then begin
+ TVal.tv_sec := RecvTimeout div 1000;
+ TVal.tv_usec := (RecvTimeout mod 1000) * 1000;
+ PTVal := @TVal;
+ end
+ else
+ PTVal := nil;
+
+ Ret := select(2, @Fds, nil, nil, PTVal);
+ ErrnoCopy := WSAGetLastError;
+ if Ret < 0 then begin
+ // error cases
+ if (ErrnoCopy = WSAEINTR) and (Retries < FMaxRecvRetries) then begin
+ Inc(Retries);
+ Continue;
+ end;
+ LogDelegate(Format('TSocket.Peek() select() %s', [SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionUnknown.Create(Format('Unknown: %s', [SysErrorMessage(ErrnoCopy)]));
+ end
+ else if Ret > 0 then begin
+ // Check the interruptListener
+ if FD_ISSET(FInterruptListener, Fds) then
+ Exit(False);
+ // There must be data or a disconnection, fall through to the PEEK
+ Break;
+ end
+ else
+ // timeout
+ Exit(False);
+ end;
+ end;
+
+ // Check to see if data is available or if the remote side closed
+ Ret := recv(Socket, Buf, 1, MSG_PEEK);
+ if Ret = SOCKET_ERROR then begin
+ ErrnoCopy := WSAGetLastError;
+ if ErrnoCopy = WSAECONNRESET then begin
+ Close;
+ Exit(False);
+ end;
+ LogDelegate(Format('TSocket.Peek() recv() %s %s', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionUnknown.Create(Format('recv(): %s', [SysErrorMessage(ErrnoCopy)]));
+ end;
+ Result := Ret > 0;
+end;
+
+function TServerSocket.CreateSocketObj(Client: Winapi.Winsock2.TSocket): TSocket;
+begin
+ if FInterruptableChildren then
+ Result := TSocket.Create(Client, FChildInterruptSockReader)
+ else
+ Result := TSocket.Create(Client);
+end;
+
+procedure TServerSocket.Notify(NotifySocket: Winapi.Winsock2.TSocket);
+var
+ Byt: Byte;
+begin
+ if NotifySocket <> INVALID_SOCKET then begin
+ Byt := 0;
+ if send(NotifySocket, Byt, SizeOf(Byt), 0) = SOCKET_ERROR then
+ LogDelegate(Format('TServerSocket.Notify() send() %s', [SysErrorMessage(WSAGetLastError)]));
+ end;
+end;
+
+procedure TServerSocket.SetInterruptableChildren(AValue: Boolean);
+begin
+ if FListening then
+ raise Exception.Create('InterruptableChildren cannot be set after listen()');
+ FInterruptableChildren := AValue;
+end;
+
+procedure TServerSocket.CommonInit;
+begin
+ inherited CommonInit;
+ FInterruptableChildren := True;
+ FAcceptBacklog := DEFAULT_BACKLOG;
+ FAcceptTimeout := 0;
+ FRetryLimit := 0;
+ FRetryDelay := 0;
+ FTcpSendBuffer := 0;
+ FTcpRecvBuffer := 0;
+ FListening := False;
+ FInterruptSockWriter := INVALID_SOCKET;
+ FInterruptSockReader := INVALID_SOCKET;
+ FChildInterruptSockWriter := INVALID_SOCKET;
+end;
+
+constructor TServerSocket.Create(APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate = nil);
+begin
+ // Unnecessary, but here for documentation purposes
+ inherited Create(APort, ALogDelegate);
+end;
+
+constructor TServerSocket.Create(APort: Integer; ASendTimeout, ARecvTimeout: Longword; ALogDelegate: TBaseSocket.TLogDelegate);
+begin
+ inherited Create(APort, ALogDelegate);
+ SendTimeout := ASendTimeout;
+ RecvTimeout := ARecvTimeout;
+end;
+
+constructor TServerSocket.Create(AAddress: string; APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate);
+begin
+ inherited Create(APort, ALogDelegate);
+ FAddress := AAddress;
+end;
+
+procedure TServerSocket.Listen;
+
+ function CreateSocketPair(var Reader, Writer: Winapi.Winsock2.TSocket): Integer;
+ label
+ Error;
+ type
+ TSAUnion = record
+ case Integer of
+ 0: (inaddr: TSockAddrIn);
+ 1: (addr: TSockAddr);
+ end;
+ var
+ a: TSAUnion;
+ listener: Winapi.Winsock2.TSocket;
+ e: Integer;
+ addrlen: Integer;
+ flags: DWORD;
+ reuse: Integer;
+ begin
+ addrlen := SizeOf(a.inaddr);
+ flags := 0;
+ reuse := 1;
+
+ listener := Winapi.Winsock2.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if listener = INVALID_SOCKET then
+ Exit(SOCKET_ERROR);
+
+ FillChar(a, SizeOf(a), 0);
+ a.inaddr.sin_family := AF_INET;
+ a.inaddr.sin_addr.s_addr := htonl(INADDR_LOOPBACK);
+ a.inaddr.sin_port := 0;
+ Reader := INVALID_SOCKET;
+ Writer := INVALID_SOCKET;
+
+ // ignore errors coming out of this setsockopt. This is because
+ // SO_EXCLUSIVEADDRUSE requires admin privileges on WinXP, but we don't
+ // want to force socket pairs to be an admin.
+ setsockopt(listener, SOL_SOCKET, Integer(SO_EXCLUSIVEADDRUSE), @reuse, SizeOf(reuse));
+ if bind(listener, a.addr, SizeOf(a.inaddr)) = SOCKET_ERROR then
+ goto Error;
+
+ if getsockname(listener, a.addr, addrlen) = SOCKET_ERROR then
+ goto Error;
+
+ if Winapi.Winsock2.listen(listener, 1) = SOCKET_ERROR then
+ goto Error;
+
+ Reader := WSASocket(AF_INET, SOCK_STREAM, 0, nil, 0, flags);
+ if Reader = INVALID_SOCKET then
+ goto Error;
+
+ if connect(Reader, a.addr, SizeOf(a.inaddr)) = SOCKET_ERROR then
+ goto Error;
+
+ Writer := Winapi.Winsock2.accept(listener, nil, nil);
+ if Writer = INVALID_SOCKET then
+ goto Error;
+
+ closesocket(listener);
+ Exit(0);
+
+ Error:
+ e := WSAGetLastError;
+ closesocket(listener);
+ closesocket(Reader);
+ closesocket(Writer);
+ WSASetLastError(e);
+ Result := SOCKET_ERROR;
+ end;
+
+var
+ TempIntReader,
+ TempIntWriter: Winapi.Winsock2.TSocket;
+ One: Cardinal;
+ ErrnoCopy: Integer;
+ Ling: TLinger;
+ Retries: Integer;
+ AddrInfo: IGetAddrInfoWrapper;
+ SA: TSockAddrStorage;
+ Len: Integer;
+begin
+ // Create the socket pair used to interrupt
+ if CreateSocketPair(TempIntReader, TempIntWriter) = SOCKET_ERROR then begin
+ LogDelegate(Format('TServerSocket.Listen() CreateSocketPair() Interrupt %s', [SysErrorMessage(WSAGetLastError)]));
+ FInterruptSockReader := INVALID_SOCKET;
+ FInterruptSockWriter := INVALID_SOCKET;
+ end
+ else begin
+ FInterruptSockReader := TempIntReader;
+ FInterruptSockWriter := TempIntWriter;
+ end;
+
+ // Create the socket pair used to interrupt all clients
+ if CreateSocketPair(TempIntReader, TempIntWriter) = SOCKET_ERROR then begin
+ LogDelegate(Format('TServerSocket.Listen() CreateSocketPair() ChildInterrupt %s', [SysErrorMessage(WSAGetLastError)]));
+ FChildInterruptSockReader := TSmartPointer<Winapi.Winsock2.TSocket>.Create(INVALID_SOCKET, nil);
+ FChildInterruptSockWriter := INVALID_SOCKET;
+ end
+ else begin
+ FChildInterruptSockReader := TSmartPointer<Winapi.Winsock2.TSocket>.Create(TempIntReader, DestroyerOfFineSockets);
+ FChildInterruptSockWriter := TempIntWriter;
+ end;
+
+ if (Port < 0) or (Port > $FFFF) then
+ raise TTransportExceptionBadArgs.Create('Specified port is invalid');
+
+ AddrInfo := CreateSocket(FAddress, Port);
+
+ // Set SO_EXCLUSIVEADDRUSE to prevent 2MSL delay on accept
+ One := 1;
+ setsockopt(Socket, SOL_SOCKET, Integer(SO_EXCLUSIVEADDRUSE), @one, SizeOf(One));
+ // ignore errors coming out of this setsockopt on Windows. This is because
+ // SO_EXCLUSIVEADDRUSE requires admin privileges on WinXP, but we don't
+ // want to force servers to be an admin.
+
+ // Set TCP buffer sizes
+ if FTcpSendBuffer > 0 then begin
+ if setsockopt(Socket, SOL_SOCKET, SO_SNDBUF, @FTcpSendBuffer, SizeOf(FTcpSendBuffer)) = SOCKET_ERROR then begin
+ ErrnoCopy := WSAGetLastError;
+ LogDelegate(Format('TServerSocket.Listen() setsockopt() SO_SNDBUF %s', [SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionNotOpen.Create(Format('Could not set SO_SNDBUF: %s', [SysErrorMessage(ErrnoCopy)]));
+ end;
+ end;
+
+ if FTcpRecvBuffer > 0 then begin
+ if setsockopt(Socket, SOL_SOCKET, SO_RCVBUF, @FTcpRecvBuffer, SizeOf(FTcpRecvBuffer)) = SOCKET_ERROR then begin
+ ErrnoCopy := WSAGetLastError;
+ LogDelegate(Format('TServerSocket.Listen() setsockopt() SO_RCVBUF %s', [SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionNotOpen.Create(Format('Could not set SO_RCVBUF: %s', [SysErrorMessage(ErrnoCopy)]));
+ end;
+ end;
+
+ // Turn linger off, don't want to block on calls to close
+ Ling.l_onoff := 0;
+ Ling.l_linger := 0;
+ if setsockopt(Socket, SOL_SOCKET, SO_LINGER, @Ling, SizeOf(Ling)) = SOCKET_ERROR then begin
+ ErrnoCopy := WSAGetLastError;
+ LogDelegate(Format('TServerSocket.Listen() setsockopt() SO_LINGER %s', [SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionNotOpen.Create(Format('Could not set SO_LINGER: %s', [SysErrorMessage(ErrnoCopy)]));
+ end;
+
+ // TCP Nodelay, speed over bandwidth
+ if setsockopt(Socket, IPPROTO_TCP, TCP_NODELAY, @One, SizeOf(One)) = SOCKET_ERROR then begin
+ ErrnoCopy := WSAGetLastError;
+ LogDelegate(Format('TServerSocket.Listen() setsockopt() TCP_NODELAY %s', [SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionNotOpen.Create(Format('Could not set TCP_NODELAY: %s', [SysErrorMessage(ErrnoCopy)]));
+ end;
+
+ // Set NONBLOCK on the accept socket
+ if ioctlsocket(Socket, Integer(FIONBIO), One) = SOCKET_ERROR then begin
+ ErrnoCopy := WSAGetLastError;
+ LogDelegate(Format('TServerSocket.Listen() ioctlsocket() FIONBIO %s', [SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionNotOpen.Create(Format('ioctlsocket() FIONBIO: %s', [SysErrorMessage(ErrnoCopy)]));
+ end;
+
+ // prepare the port information
+ // we may want to try to bind more than once, since THRIFT_NO_SOCKET_CACHING doesn't
+ // always seem to work. The client can configure the retry variables.
+ Retries := 0;
+ while True do begin
+ if bind(Socket, AddrInfo.Res^.ai_addr^, AddrInfo.Res^.ai_addrlen) = 0 then
+ Break;
+ Inc(Retries);
+ if Retries > FRetryLimit then
+ Break;
+ Sleep(FRetryDelay * 1000);
+ end;
+
+ // retrieve bind info
+ if (Port = 0) and (Retries < FRetryLimit) then begin
+ Len := SizeOf(SA);
+ FillChar(SA, Len, 0);
+ if getsockname(Socket, PSockAddr(@SA)^, Len) = SOCKET_ERROR then
+ LogDelegate(Format('TServerSocket.Listen() getsockname() %s', [SysErrorMessage(WSAGetLastError)]))
+ else begin
+ if SA.ss_family = AF_INET6 then
+ Port := ntohs(PSockAddrIn6(@SA)^.sin6_port)
+ else
+ Port := ntohs(PSockAddrIn(@SA)^.sin_port);
+ end;
+ end;
+
+ // throw an error if we failed to bind properly
+ if (Retries > FRetryLimit) then begin
+ LogDelegate(Format('TServerSocket.Listen() BIND %d', [Port]));
+ Close;
+ raise TTransportExceptionNotOpen.Create(Format('Could not bind: %s', [SysErrorMessage(WSAGetLastError)]));
+ end;
+
+ if Assigned(FListenCallback) then
+ FListenCallback(Socket);
+
+ // Call listen
+ if Winapi.Winsock2.listen(Socket, FAcceptBacklog) = SOCKET_ERROR then begin
+ ErrnoCopy := WSAGetLastError;
+ LogDelegate(Format('TServerSocket.Listen() listen() %s', [SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionNotOpen.Create(Format('Could not listen: %s', [SysErrorMessage(ErrnoCopy)]));
+ end;
+
+ // The socket is now listening!
+end;
+
+function TServerSocket.Accept: TSocket;
+var
+ Fds: TFdSet;
+ MaxEInters,
+ NumEInters: Integer;
+ TVal: TTimeVal;
+ PTVal: PTimeVal;
+ ErrnoCopy: Integer;
+ Buf: Byte;
+ ClientAddress: TSockAddrStorage;
+ Size: Integer;
+ ClientSocket: Winapi.Winsock2.TSocket;
+ Zero: Cardinal;
+ Client: TSocket;
+ Ret: Integer;
+begin
+ MaxEInters := 5;
+ NumEInters := 0;
+
+ while True do begin
+ FD_ZERO(Fds);
+ _FD_SET(Socket, Fds);
+ _FD_SET(FInterruptSockReader, Fds);
+ if FAcceptTimeout > 0 then begin
+ TVal.tv_sec := FAcceptTimeout div 1000;
+ TVal.tv_usec := (FAcceptTimeout mod 1000) * 1000;
+ PTVal := @TVal;
+ end
+ else
+ PTVal := nil;
+
+ // TODO: if WSAEINTR is received, we'll restart the timeout.
+ // To be accurate, we need to fix this in the future.
+ Ret := select(2, @Fds, nil, nil, PTVal);
+
+ if Ret < 0 then begin
+ // error cases
+ if (WSAGetLastError = WSAEINTR) and (NumEInters < MaxEInters) then begin
+ // THRIFT_EINTR needs to be handled manually and we can tolerate
+ // a certain number
+ Inc(NumEInters);
+ Continue;
+ end;
+ ErrnoCopy := WSAGetLastError;
+ LogDelegate(Format('TServerSocket.Accept() select() %s', [SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionUnknown.Create(Format('Unknown: %s', [SysErrorMessage(ErrnoCopy)]));
+ end
+ else if Ret > 0 then begin
+ // Check for an interrupt signal
+ if (FInterruptSockReader <> INVALID_SOCKET) and FD_ISSET(FInterruptSockReader, Fds) then begin
+ if recv(FInterruptSockReader, Buf, SizeOf(Buf), 0) = SOCKET_ERROR then
+ LogDelegate(Format('TServerSocket.Accept() recv() interrupt %s', [SysErrorMessage(WSAGetLastError)]));
+ raise TTransportExceptionInterrupted.Create('interrupted');
+ end;
+
+ // Check for the actual server socket being ready
+ if FD_ISSET(Socket, Fds) then
+ Break;
+ end
+ else begin
+ LogDelegate('TServerSocket.Accept() select() 0');
+ raise TTransportExceptionUnknown.Create('unknown error');
+ end;
+ end;
+
+ Size := SizeOf(ClientAddress);
+ ClientSocket := Winapi.Winsock2.accept(Socket, @ClientAddress, @Size);
+ if ClientSocket = INVALID_SOCKET then begin
+ ErrnoCopy := WSAGetLastError;
+ LogDelegate(Format('TServerSocket.Accept() accept() %s', [SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionUnknown.Create(Format('accept(): %s', [SysErrorMessage(ErrnoCopy)]));
+ end;
+
+ // Make sure client socket is blocking
+ Zero := 0;
+ if ioctlsocket(ClientSocket, Integer(FIONBIO), Zero) = SOCKET_ERROR then begin
+ ErrnoCopy := WSAGetLastError;
+ closesocket(ClientSocket);
+ LogDelegate(Format('TServerSocket.Accept() ioctlsocket() FIONBIO %s', [SysErrorMessage(ErrnoCopy)]));
+ raise TTransportExceptionUnknown.Create(Format('ioctlsocket(): %s', [SysErrorMessage(ErrnoCopy)]));
+ end;
+
+ Client := CreateSocketObj(ClientSocket);
+ if SendTimeout > 0 then
+ Client.SendTimeout := SendTimeout;
+ if RecvTimeout > 0 then
+ Client.RecvTimeout := RecvTimeout;
+ if KeepAlive then
+ Client.KeepAlive := KeepAlive;
+ Client.SetCachedAddress(PSockAddr(@ClientAddress)^, Size);
+
+ if Assigned(FAcceptCallback) then
+ FAcceptCallback(ClientSocket);
+
+ Result := Client;
+end;
+
+procedure TServerSocket.Interrupt;
+begin
+ Notify(FInterruptSockWriter);
+end;
+
+procedure TServerSocket.InterruptChildren;
+begin
+ Notify(FChildInterruptSockWriter);
+end;
+
+procedure TServerSocket.Close;
+begin
+ inherited Close;
+ if FInterruptSockWriter <> INVALID_SOCKET then
+ closesocket(FInterruptSockWriter);
+ if FInterruptSockReader <> INVALID_SOCKET then
+ closesocket(FInterruptSockReader);
+ if FChildInterruptSockWriter <> INVALID_SOCKET then
+ closesocket(FChildInterruptSockWriter);
+ FChildInterruptSockReader := TSmartPointer<Winapi.Winsock2.TSocket>.Create(INVALID_SOCKET, nil);
+ FListening := False;
+end;
+
+{$ENDIF} // not for OLD_SOCKETS
+end.
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Stream.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Stream.pas
new file mode 100644
index 000000000..3308c53a5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Stream.pas
@@ -0,0 +1,319 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+unit Thrift.Stream;
+
+{$I Thrift.Defines.inc}
+
+interface
+
+uses
+ Classes,
+ SysUtils,
+ SysConst,
+ RTLConsts,
+ {$IFDEF OLD_UNIT_NAMES}
+ ActiveX,
+ {$ELSE}
+ Winapi.ActiveX,
+ {$ENDIF}
+ Thrift.Utils;
+
+type
+
+ IThriftStream = interface
+ ['{2A77D916-7446-46C1-8545-0AEC0008DBCA}']
+ procedure Write( const buffer: TBytes; offset: Integer; count: Integer); overload;
+ procedure Write( const pBuf : Pointer; offset: Integer; count: Integer); overload;
+ function Read( var buffer: TBytes; offset: Integer; count: Integer): Integer; overload;
+ function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; overload;
+ procedure Open;
+ procedure Close;
+ procedure Flush;
+ function IsOpen: Boolean;
+ function ToArray: TBytes;
+ end;
+
+ TThriftStreamImpl = class( TInterfacedObject, IThriftStream)
+ private
+ procedure CheckSizeAndOffset( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer); overload;
+ protected
+ procedure Write( const buffer: TBytes; offset: Integer; count: Integer); overload; inline;
+ procedure Write( const pBuf : Pointer; offset: Integer; count: Integer); overload; virtual;
+ function Read( var buffer: TBytes; offset: Integer; count: Integer): Integer; overload; inline;
+ function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; overload; virtual;
+ procedure Open; virtual; abstract;
+ procedure Close; virtual; abstract;
+ procedure Flush; virtual; abstract;
+ function IsOpen: Boolean; virtual; abstract;
+ function ToArray: TBytes; virtual; abstract;
+ end;
+
+ TThriftStreamAdapterDelphi = class( TThriftStreamImpl )
+ private
+ FStream : TStream;
+ FOwnsStream : Boolean;
+ protected
+ procedure Write( const pBuf : Pointer; offset: Integer; count: Integer); override;
+ function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override;
+ procedure Open; override;
+ procedure Close; override;
+ procedure Flush; override;
+ function IsOpen: Boolean; override;
+ function ToArray: TBytes; override;
+ public
+ constructor Create( const AStream: TStream; AOwnsStream : Boolean);
+ destructor Destroy; override;
+ end;
+
+ TThriftStreamAdapterCOM = class( TThriftStreamImpl)
+ private
+ FStream : IStream;
+ protected
+ procedure Write( const pBuf : Pointer; offset: Integer; count: Integer); override;
+ function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override;
+ procedure Open; override;
+ procedure Close; override;
+ procedure Flush; override;
+ function IsOpen: Boolean; override;
+ function ToArray: TBytes; override;
+ public
+ constructor Create( const AStream: IStream);
+ end;
+
+implementation
+
+{ TThriftStreamAdapterCOM }
+
+procedure TThriftStreamAdapterCOM.Close;
+begin
+ FStream := nil;
+end;
+
+constructor TThriftStreamAdapterCOM.Create( const AStream: IStream);
+begin
+ inherited Create;
+ FStream := AStream;
+end;
+
+procedure TThriftStreamAdapterCOM.Flush;
+begin
+ if IsOpen then begin
+ if FStream <> nil then begin
+ FStream.Commit( STGC_DEFAULT );
+ end;
+ end;
+end;
+
+function TThriftStreamAdapterCOM.IsOpen: Boolean;
+begin
+ Result := FStream <> nil;
+end;
+
+procedure TThriftStreamAdapterCOM.Open;
+begin
+ // nothing to do
+end;
+
+function TThriftStreamAdapterCOM.Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer;
+var pTmp : PByte;
+begin
+ inherited;
+
+ if count >= buflen-offset
+ then count := buflen-offset;
+
+ Result := 0;
+ if FStream <> nil then begin
+ if count > 0 then begin
+ pTmp := pBuf;
+ Inc( pTmp, offset);
+ FStream.Read( pTmp, count, @Result);
+ end;
+ end;
+end;
+
+function TThriftStreamAdapterCOM.ToArray: TBytes;
+var
+ statstg: TStatStg;
+ len : Integer;
+ NewPos : {$IF CompilerVersion >= 29.0} UInt64 {$ELSE} Int64 {$IFEND};
+ cbRead : Integer;
+begin
+ FillChar( statstg, SizeOf( statstg), 0);
+ len := 0;
+ if IsOpen then begin
+ if Succeeded( FStream.Stat( statstg, STATFLAG_NONAME )) then begin
+ len := statstg.cbSize;
+ end;
+ end;
+
+ SetLength( Result, len );
+
+ if len > 0 then begin
+ if Succeeded( FStream.Seek( 0, STREAM_SEEK_SET, NewPos) ) then begin
+ FStream.Read( @Result[0], len, @cbRead);
+ end;
+ end;
+end;
+
+procedure TThriftStreamAdapterCOM.Write( const pBuf: Pointer; offset: Integer; count: Integer);
+var nWritten : Integer;
+ pTmp : PByte;
+begin
+ inherited;
+ if IsOpen then begin
+ if count > 0 then begin
+ pTmp := pBuf;
+ Inc( pTmp, offset);
+ FStream.Write( pTmp, count, @nWritten);
+ end;
+ end;
+end;
+
+{ TThriftStreamImpl }
+
+procedure TThriftStreamImpl.CheckSizeAndOffset( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer);
+begin
+ if count > 0 then begin
+ if (offset < 0) or ( offset >= buflen) then begin
+ raise ERangeError.Create( SBitsIndexError );
+ end;
+ if count > buflen then begin
+ raise ERangeError.Create( SBitsIndexError );
+ end;
+ end;
+end;
+
+function TThriftStreamImpl.Read(var buffer: TBytes; offset, count: Integer): Integer;
+begin
+ if Length(buffer) > 0
+ then Result := Read( @buffer[0], Length(buffer), offset, count)
+ else Result := 0;
+end;
+
+function TThriftStreamImpl.Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer;
+begin
+ Result := 0;
+ CheckSizeAndOffset( pBuf, buflen, offset, count );
+end;
+
+procedure TThriftStreamImpl.Write(const buffer: TBytes; offset, count: Integer);
+begin
+ if Length(buffer) > 0
+ then Write( @buffer[0], offset, count);
+end;
+
+procedure TThriftStreamImpl.Write( const pBuf : Pointer; offset: Integer; count: Integer);
+begin
+ CheckSizeAndOffset( pBuf, offset+count, offset, count);
+end;
+
+{ TThriftStreamAdapterDelphi }
+
+procedure TThriftStreamAdapterDelphi.Close;
+begin
+ FStream.Free;
+ FStream := nil;
+ FOwnsStream := False;
+end;
+
+constructor TThriftStreamAdapterDelphi.Create( const AStream: TStream; AOwnsStream: Boolean);
+begin
+ inherited Create;
+ FStream := AStream;
+ FOwnsStream := AOwnsStream;
+end;
+
+destructor TThriftStreamAdapterDelphi.Destroy;
+begin
+ if FOwnsStream
+ then Close;
+
+ inherited;
+end;
+
+procedure TThriftStreamAdapterDelphi.Flush;
+begin
+ // nothing to do
+end;
+
+function TThriftStreamAdapterDelphi.IsOpen: Boolean;
+begin
+ Result := FStream <> nil;
+end;
+
+procedure TThriftStreamAdapterDelphi.Open;
+begin
+ // nothing to do
+end;
+
+function TThriftStreamAdapterDelphi.Read(const pBuf : Pointer; const buflen : Integer; offset, count: Integer): Integer;
+var pTmp : PByte;
+begin
+ inherited;
+
+ if count >= buflen-offset
+ then count := buflen-offset;
+
+ if count > 0 then begin
+ pTmp := pBuf;
+ Inc( pTmp, offset);
+ Result := FStream.Read( pTmp^, count)
+ end
+ else Result := 0;
+end;
+
+function TThriftStreamAdapterDelphi.ToArray: TBytes;
+var
+ OrgPos : Integer;
+ len : Integer;
+begin
+ len := 0;
+ if FStream <> nil then
+ begin
+ len := FStream.Size;
+ end;
+
+ SetLength( Result, len );
+
+ if len > 0 then
+ begin
+ OrgPos := FStream.Position;
+ try
+ FStream.Position := 0;
+ FStream.ReadBuffer( Pointer(@Result[0])^, len );
+ finally
+ FStream.Position := OrgPos;
+ end;
+ end
+end;
+
+procedure TThriftStreamAdapterDelphi.Write(const pBuf : Pointer; offset, count: Integer);
+var pTmp : PByte;
+begin
+ inherited;
+ if count > 0 then begin
+ pTmp := pBuf;
+ Inc( pTmp, offset);
+ FStream.Write( pTmp^, count)
+ end;
+end;
+
+end.
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Transport.MsxmlHTTP.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Transport.MsxmlHTTP.pas
new file mode 100644
index 000000000..c666e7fed
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Transport.MsxmlHTTP.pas
@@ -0,0 +1,268 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+unit Thrift.Transport.MsxmlHTTP;
+
+{$I Thrift.Defines.inc}
+{$SCOPEDENUMS ON}
+
+interface
+
+uses
+ Classes,
+ SysUtils,
+ Math,
+ Generics.Collections,
+ {$IFDEF OLD_UNIT_NAMES}
+ ActiveX, msxml,
+ {$ELSE}
+ Winapi.ActiveX, Winapi.msxml,
+ {$ENDIF}
+ Thrift.Collections,
+ Thrift.Transport,
+ Thrift.Exception,
+ Thrift.Utils,
+ Thrift.Stream;
+
+type
+ TMsxmlHTTPClientImpl = class( TTransportImpl, IHTTPClient)
+ private
+ FUri : string;
+ FInputStream : IThriftStream;
+ FOutputStream : IThriftStream;
+ FDnsResolveTimeout : Integer;
+ FConnectionTimeout : Integer;
+ FSendTimeout : Integer;
+ FReadTimeout : Integer;
+ FCustomHeaders : IThriftDictionary<string,string>;
+
+ function CreateRequest: IXMLHTTPRequest;
+ protected
+ function GetIsOpen: Boolean; override;
+ procedure Open(); override;
+ procedure Close(); override;
+ function Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; override;
+ procedure Write( const pBuf : Pointer; off, len : Integer); override;
+ procedure Flush; override;
+
+ procedure SetDnsResolveTimeout(const Value: Integer);
+ function GetDnsResolveTimeout: Integer;
+ procedure SetConnectionTimeout(const Value: Integer);
+ function GetConnectionTimeout: Integer;
+ procedure SetSendTimeout(const Value: Integer);
+ function GetSendTimeout: Integer;
+ procedure SetReadTimeout(const Value: Integer);
+ function GetReadTimeout: Integer;
+ function GetSecureProtocols : TSecureProtocols;
+ procedure SetSecureProtocols( const value : TSecureProtocols);
+
+ function GetCustomHeaders: IThriftDictionary<string,string>;
+ procedure SendRequest;
+
+ property DnsResolveTimeout: Integer read GetDnsResolveTimeout write SetDnsResolveTimeout;
+ property ConnectionTimeout: Integer read GetConnectionTimeout write SetConnectionTimeout;
+ property SendTimeout: Integer read GetSendTimeout write SetSendTimeout;
+ property ReadTimeout: Integer read GetReadTimeout write SetReadTimeout;
+ property CustomHeaders: IThriftDictionary<string,string> read GetCustomHeaders;
+ public
+ constructor Create( const AUri: string);
+ destructor Destroy; override;
+ end;
+
+
+implementation
+
+
+{ TMsxmlHTTPClientImpl }
+
+constructor TMsxmlHTTPClientImpl.Create(const AUri: string);
+begin
+ inherited Create;
+ FUri := AUri;
+
+ // defaults according to MSDN
+ FDnsResolveTimeout := 0; // no timeout
+ FConnectionTimeout := 60 * 1000;
+ FSendTimeout := 30 * 1000;
+ FReadTimeout := 30 * 1000;
+
+ FCustomHeaders := TThriftDictionaryImpl<string,string>.Create;
+ FOutputStream := TThriftStreamAdapterDelphi.Create( TMemoryStream.Create, True);
+end;
+
+function TMsxmlHTTPClientImpl.CreateRequest: IXMLHTTPRequest;
+var
+ pair : TPair<string,string>;
+ srvHttp : IServerXMLHTTPRequest;
+begin
+ {$IF CompilerVersion >= 21.0}
+ Result := CoServerXMLHTTP.Create;
+ {$ELSE}
+ Result := CoXMLHTTPRequest.Create;
+ {$IFEND}
+
+ // setting a timeout value to 0 (zero) means "no timeout" for that setting
+ if Supports( result, IServerXMLHTTPRequest, srvHttp)
+ then srvHttp.setTimeouts( DnsResolveTimeout, ConnectionTimeout, SendTimeout, ReadTimeout);
+
+ Result.open('POST', FUri, False, '', '');
+ Result.setRequestHeader( 'Content-Type', THRIFT_MIMETYPE);
+ Result.setRequestHeader( 'Accept', THRIFT_MIMETYPE);
+ Result.setRequestHeader( 'User-Agent', 'Delphi/IHTTPClient');
+
+ for pair in FCustomHeaders do begin
+ Result.setRequestHeader( pair.Key, pair.Value );
+ end;
+end;
+
+destructor TMsxmlHTTPClientImpl.Destroy;
+begin
+ Close;
+ inherited;
+end;
+
+function TMsxmlHTTPClientImpl.GetDnsResolveTimeout: Integer;
+begin
+ Result := FDnsResolveTimeout;
+end;
+
+procedure TMsxmlHTTPClientImpl.SetDnsResolveTimeout(const Value: Integer);
+begin
+ FDnsResolveTimeout := Value;
+end;
+
+function TMsxmlHTTPClientImpl.GetConnectionTimeout: Integer;
+begin
+ Result := FConnectionTimeout;
+end;
+
+procedure TMsxmlHTTPClientImpl.SetConnectionTimeout(const Value: Integer);
+begin
+ FConnectionTimeout := Value;
+end;
+
+function TMsxmlHTTPClientImpl.GetSendTimeout: Integer;
+begin
+ Result := FSendTimeout;
+end;
+
+procedure TMsxmlHTTPClientImpl.SetSendTimeout(const Value: Integer);
+begin
+ FSendTimeout := Value;
+end;
+
+function TMsxmlHTTPClientImpl.GetReadTimeout: Integer;
+begin
+ Result := FReadTimeout;
+end;
+
+procedure TMsxmlHTTPClientImpl.SetReadTimeout(const Value: Integer);
+begin
+ FReadTimeout := Value;
+end;
+
+function TMsxmlHTTPClientImpl.GetSecureProtocols : TSecureProtocols;
+begin
+ Result := [];
+end;
+
+procedure TMsxmlHTTPClientImpl.SetSecureProtocols( const value : TSecureProtocols);
+begin
+ raise TTransportExceptionBadArgs.Create('SetSecureProtocols: Not supported with '+ClassName);
+end;
+
+function TMsxmlHTTPClientImpl.GetCustomHeaders: IThriftDictionary<string,string>;
+begin
+ Result := FCustomHeaders;
+end;
+
+function TMsxmlHTTPClientImpl.GetIsOpen: Boolean;
+begin
+ Result := True;
+end;
+
+procedure TMsxmlHTTPClientImpl.Open;
+begin
+ FOutputStream := TThriftStreamAdapterDelphi.Create( TMemoryStream.Create, True);
+end;
+
+procedure TMsxmlHTTPClientImpl.Close;
+begin
+ FInputStream := nil;
+ FOutputStream := nil;
+end;
+
+procedure TMsxmlHTTPClientImpl.Flush;
+begin
+ try
+ SendRequest;
+ finally
+ FOutputStream := nil;
+ FOutputStream := TThriftStreamAdapterDelphi.Create( TMemoryStream.Create, True);
+ ASSERT( FOutputStream <> nil);
+ end;
+end;
+
+function TMsxmlHTTPClientImpl.Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer;
+begin
+ if FInputStream = nil then begin
+ raise TTransportExceptionNotOpen.Create('No request has been sent');
+ end;
+
+ try
+ Result := FInputStream.Read( pBuf, buflen, off, len)
+ except
+ on E: Exception
+ do raise TTransportExceptionUnknown.Create(E.Message);
+ end;
+end;
+
+procedure TMsxmlHTTPClientImpl.SendRequest;
+var
+ xmlhttp : IXMLHTTPRequest;
+ ms : TMemoryStream;
+ a : TBytes;
+ len : Integer;
+begin
+ xmlhttp := CreateRequest;
+
+ ms := TMemoryStream.Create;
+ try
+ a := FOutputStream.ToArray;
+ len := Length(a);
+ if len > 0 then begin
+ ms.WriteBuffer( Pointer(@a[0])^, len);
+ end;
+ ms.Position := 0;
+ xmlhttp.send( IUnknown( TStreamAdapter.Create( ms, soReference )));
+ FInputStream := nil;
+ FInputStream := TThriftStreamAdapterCOM.Create( IUnknown( xmlhttp.responseStream) as IStream);
+ finally
+ ms.Free;
+ end;
+end;
+
+procedure TMsxmlHTTPClientImpl.Write( const pBuf : Pointer; off, len : Integer);
+begin
+ FOutputStream.Write( pBuf, off, len);
+end;
+
+
+
+end.
+
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Transport.Pipes.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Transport.Pipes.pas
new file mode 100644
index 000000000..77a343b0c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Transport.Pipes.pas
@@ -0,0 +1,1044 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+unit Thrift.Transport.Pipes;
+
+{$WARN SYMBOL_PLATFORM OFF}
+{$I Thrift.Defines.inc}
+
+interface
+
+uses
+ {$IFDEF OLD_UNIT_NAMES}
+ Windows, SysUtils, Math, AccCtrl, AclAPI, SyncObjs,
+ {$ELSE}
+ Winapi.Windows, System.SysUtils, System.Math, Winapi.AccCtrl, Winapi.AclAPI, System.SyncObjs,
+ {$ENDIF}
+ Thrift.Transport,
+ Thrift.Utils,
+ Thrift.Stream;
+
+const
+ DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT = 10; // default: fail fast on open
+
+
+type
+ //--- Pipe Streams ---
+
+
+ TPipeStreamBase = class( TThriftStreamImpl)
+ strict protected
+ FPipe : THandle;
+ FTimeout : DWORD;
+ FOpenTimeOut : DWORD; // separate value to allow for fail-fast-on-open scenarios
+ FOverlapped : Boolean;
+
+ procedure Write( const pBuf : Pointer; offset, count : Integer); override;
+ function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override;
+ //procedure Open; override; - see derived classes
+ procedure Close; override;
+ procedure Flush; override;
+
+ function ReadDirect( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; overload;
+ function ReadOverlapped( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; overload;
+ procedure WriteDirect( const pBuf : Pointer; offset: Integer; count: Integer); overload;
+ procedure WriteOverlapped( const pBuf : Pointer; offset: Integer; count: Integer); overload;
+
+ function IsOpen: Boolean; override;
+ function ToArray: TBytes; override;
+ public
+ constructor Create( aEnableOverlapped : Boolean;
+ const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
+ const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT);
+ destructor Destroy; override;
+ end;
+
+
+ TNamedPipeStreamImpl = class sealed( TPipeStreamBase)
+ strict private
+ FPipeName : string;
+ FShareMode : DWORD;
+ FSecurityAttribs : PSecurityAttributes;
+
+ strict protected
+ procedure Open; override;
+
+ public
+ constructor Create( const aPipeName : string;
+ const aEnableOverlapped : Boolean;
+ const aShareMode: DWORD = 0;
+ const aSecurityAttributes: PSecurityAttributes = nil;
+ const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
+ const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT); overload;
+ end;
+
+
+ THandlePipeStreamImpl = class sealed( TPipeStreamBase)
+ strict private
+ FSrcHandle : THandle;
+
+ strict protected
+ procedure Open; override;
+
+ public
+ constructor Create( const aPipeHandle : THandle;
+ const aOwnsHandle, aEnableOverlapped : Boolean;
+ const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT); overload;
+ destructor Destroy; override;
+ end;
+
+
+ //--- Pipe Transports ---
+
+
+ IPipeTransport = interface( IStreamTransport)
+ ['{5E05CC85-434F-428F-BFB2-856A168B5558}']
+ end;
+
+
+ TPipeTransportBase = class( TStreamTransportImpl, IPipeTransport)
+ public
+ // ITransport
+ function GetIsOpen: Boolean; override;
+ procedure Open; override;
+ procedure Close; override;
+ end;
+
+
+ TNamedPipeTransportClientEndImpl = class( TPipeTransportBase)
+ public
+ // Named pipe constructors
+ constructor Create( aPipe : THandle; aOwnsHandle : Boolean;
+ const aTimeOut : DWORD); overload;
+ constructor Create( const aPipeName : string;
+ const aShareMode: DWORD = 0;
+ const aSecurityAttributes: PSecurityAttributes = nil;
+ const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
+ const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT); overload;
+ end;
+
+
+ TNamedPipeTransportServerEndImpl = class( TNamedPipeTransportClientEndImpl)
+ strict private
+ FHandle : THandle;
+ public
+ // ITransport
+ procedure Close; override;
+ constructor Create( aPipe : THandle; aOwnsHandle : Boolean;
+ const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT); reintroduce;
+ end;
+
+
+ TAnonymousPipeTransportImpl = class( TPipeTransportBase)
+ public
+ // Anonymous pipe constructor
+ constructor Create(const aPipeRead, aPipeWrite : THandle;
+ aOwnsHandles : Boolean;
+ const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT); overload;
+ end;
+
+
+ //--- Server Transports ---
+
+
+ IAnonymousPipeServerTransport = interface( IServerTransport)
+ ['{7AEE6793-47B9-4E49-981A-C39E9108E9AD}']
+ // Server side anonymous pipe ends
+ function ReadHandle : THandle;
+ function WriteHandle : THandle;
+ // Client side anonymous pipe ends
+ function ClientAnonRead : THandle;
+ function ClientAnonWrite : THandle;
+ end;
+
+
+ INamedPipeServerTransport = interface( IServerTransport)
+ ['{9DF9EE48-D065-40AF-8F67-D33037D3D960}']
+ function Handle : THandle;
+ end;
+
+
+ TPipeServerTransportBase = class( TServerTransportImpl)
+ strict protected
+ FStopServer : TEvent;
+ procedure InternalClose; virtual; abstract;
+ function QueryStopServer : Boolean;
+ public
+ constructor Create;
+ destructor Destroy; override;
+ procedure Listen; override;
+ procedure Close; override;
+ end;
+
+
+ TAnonymousPipeServerTransportImpl = class( TPipeServerTransportBase, IAnonymousPipeServerTransport)
+ strict private
+ FBufSize : DWORD;
+
+ // Server side anonymous pipe handles
+ FReadHandle,
+ FWriteHandle : THandle;
+
+ //Client side anonymous pipe handles
+ FClientAnonRead,
+ FClientAnonWrite : THandle;
+
+ FTimeOut: DWORD;
+ protected
+ function Accept(const fnAccepting: TProc): ITransport; override;
+
+ function CreateAnonPipe : Boolean;
+
+ // IAnonymousPipeServerTransport
+ function ReadHandle : THandle;
+ function WriteHandle : THandle;
+ function ClientAnonRead : THandle;
+ function ClientAnonWrite : THandle;
+
+ procedure InternalClose; override;
+
+ public
+ constructor Create(aBufsize : Cardinal = 4096; aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT);
+ end;
+
+
+ TNamedPipeServerTransportImpl = class( TPipeServerTransportBase, INamedPipeServerTransport)
+ strict private
+ FPipeName : string;
+ FMaxConns : DWORD;
+ FBufSize : DWORD;
+ FTimeout : DWORD;
+ FHandle : THandle;
+ FConnected : Boolean;
+
+
+ strict protected
+ function Accept(const fnAccepting: TProc): ITransport; override;
+ function CreateNamedPipe : THandle;
+ function CreateTransportInstance : ITransport;
+
+ // INamedPipeServerTransport
+ function Handle : THandle;
+ procedure InternalClose; override;
+
+ public
+ constructor Create( aPipename : string; aBufsize : Cardinal = 4096;
+ aMaxConns : Cardinal = PIPE_UNLIMITED_INSTANCES;
+ aTimeOut : Cardinal = INFINITE);
+ end;
+
+
+implementation
+
+
+procedure ClosePipeHandle( var hPipe : THandle);
+begin
+ if hPipe <> INVALID_HANDLE_VALUE
+ then try
+ CloseHandle( hPipe);
+ finally
+ hPipe := INVALID_HANDLE_VALUE;
+ end;
+end;
+
+
+function DuplicatePipeHandle( const hSource : THandle) : THandle;
+begin
+ if not DuplicateHandle( GetCurrentProcess, hSource,
+ GetCurrentProcess, @result,
+ 0, FALSE, DUPLICATE_SAME_ACCESS)
+ then raise TTransportExceptionNotOpen.Create('DuplicateHandle: '+SysErrorMessage(GetLastError));
+end;
+
+
+
+{ TPipeStreamBase }
+
+
+constructor TPipeStreamBase.Create( aEnableOverlapped : Boolean;
+ const aTimeOut, aOpenTimeOut : DWORD);
+begin
+ inherited Create;
+ ASSERT( aTimeout > 0); // aOpenTimeout may be 0
+ FPipe := INVALID_HANDLE_VALUE;
+ FTimeout := aTimeOut;
+ FOpenTimeOut := aOpenTimeOut;
+ FOverlapped := aEnableOverlapped;
+end;
+
+
+destructor TPipeStreamBase.Destroy;
+begin
+ try
+ Close;
+ finally
+ inherited Destroy;
+ end;
+end;
+
+
+procedure TPipeStreamBase.Close;
+begin
+ ClosePipeHandle( FPipe);
+end;
+
+
+procedure TPipeStreamBase.Flush;
+begin
+ FlushFileBuffers( FPipe);
+end;
+
+
+function TPipeStreamBase.IsOpen: Boolean;
+begin
+ result := (FPipe <> INVALID_HANDLE_VALUE);
+end;
+
+
+procedure TPipeStreamBase.Write( const pBuf : Pointer; offset, count : Integer);
+begin
+ if FOverlapped
+ then WriteOverlapped( pBuf, offset, count)
+ else WriteDirect( pBuf, offset, count);
+end;
+
+
+function TPipeStreamBase.Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer;
+begin
+ if FOverlapped
+ then result := ReadOverlapped( pBuf, buflen, offset, count)
+ else result := ReadDirect( pBuf, buflen, offset, count);
+end;
+
+
+procedure TPipeStreamBase.WriteDirect( const pBuf : Pointer; offset: Integer; count: Integer);
+var cbWritten, nBytes : DWORD;
+ pData : PByte;
+begin
+ if not IsOpen
+ then raise TTransportExceptionNotOpen.Create('Called write on non-open pipe');
+
+ // if necessary, send the data in chunks
+ // there's a system limit around 0x10000 bytes that we hit otherwise
+ // MSDN: "Pipe write operations across a network are limited to 65,535 bytes per write. For more information regarding pipes, see the Remarks section."
+ nBytes := Min( 15*4096, count); // 16 would exceed the limit
+ pData := pBuf;
+ Inc( pData, offset);
+ while nBytes > 0 do begin
+ if not WriteFile( FPipe, pData^, nBytes, cbWritten, nil)
+ then raise TTransportExceptionNotOpen.Create('Write to pipe failed');
+
+ Inc( pData, cbWritten);
+ Dec( count, cbWritten);
+ nBytes := Min( nBytes, count);
+ end;
+end;
+
+
+procedure TPipeStreamBase.WriteOverlapped( const pBuf : Pointer; offset: Integer; count: Integer);
+var cbWritten, dwWait, dwError, nBytes : DWORD;
+ overlapped : IOverlappedHelper;
+ pData : PByte;
+begin
+ if not IsOpen
+ then raise TTransportExceptionNotOpen.Create('Called write on non-open pipe');
+
+ // if necessary, send the data in chunks
+ // there's a system limit around 0x10000 bytes that we hit otherwise
+ // MSDN: "Pipe write operations across a network are limited to 65,535 bytes per write. For more information regarding pipes, see the Remarks section."
+ nBytes := Min( 15*4096, count); // 16 would exceed the limit
+ pData := pBuf;
+ Inc( pData, offset);
+ while nBytes > 0 do begin
+ overlapped := TOverlappedHelperImpl.Create;
+ if not WriteFile( FPipe, pData^, nBytes, cbWritten, overlapped.OverlappedPtr)
+ then begin
+ dwError := GetLastError;
+ case dwError of
+ ERROR_IO_PENDING : begin
+ dwWait := overlapped.WaitFor(FTimeout);
+
+ if (dwWait = WAIT_TIMEOUT) then begin
+ CancelIo( FPipe); // prevents possible AV on invalid overlapped ptr
+ raise TTransportExceptionTimedOut.Create('Pipe write timed out');
+ end;
+
+ if (dwWait <> WAIT_OBJECT_0)
+ or not GetOverlappedResult( FPipe, overlapped.Overlapped, cbWritten, TRUE)
+ then raise TTransportExceptionUnknown.Create('Pipe write error');
+ end;
+
+ else
+ raise TTransportExceptionUnknown.Create(SysErrorMessage(dwError));
+ end;
+ end;
+
+ ASSERT( DWORD(nBytes) = cbWritten);
+
+ Inc( pData, cbWritten);
+ Dec( count, cbWritten);
+ nBytes := Min( nBytes, count);
+ end;
+end;
+
+
+function TPipeStreamBase.ReadDirect( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer;
+var cbRead, dwErr, nRemaining : DWORD;
+ bytes, retries : LongInt;
+ bOk : Boolean;
+ pData : PByte;
+const INTERVAL = 10; // ms
+begin
+ if not IsOpen
+ then raise TTransportExceptionNotOpen.Create('Called read on non-open pipe');
+
+ // MSDN: Handle can be a handle to a named pipe instance,
+ // or it can be a handle to the read end of an anonymous pipe,
+ // The handle must have GENERIC_READ access to the pipe.
+ if FTimeOut <> INFINITE then begin
+ retries := Max( 1, Round( 1.0 * FTimeOut / INTERVAL));
+ while TRUE do begin
+ if not PeekNamedPipe( FPipe, nil, 0, nil, @bytes, nil) then begin
+ dwErr := GetLastError;
+ if (dwErr = ERROR_INVALID_HANDLE)
+ or (dwErr = ERROR_BROKEN_PIPE)
+ or (dwErr = ERROR_PIPE_NOT_CONNECTED)
+ then begin
+ result := 0; // other side closed the pipe
+ Exit;
+ end;
+ end
+ else if bytes > 0 then begin
+ Break; // there are data
+ end;
+
+ Dec( retries);
+ if retries > 0
+ then Sleep( INTERVAL)
+ else raise TTransportExceptionTimedOut.Create('Pipe read timed out');
+ end;
+ end;
+
+ result := 0;
+ nRemaining := count;
+ pData := pBuf;
+ Inc( pData, offset);
+ while nRemaining > 0 do begin
+ // read the data (or block INFINITE-ly)
+ bOk := ReadFile( FPipe, pData^, nRemaining, cbRead, nil);
+ if (not bOk) and (GetLastError() <> ERROR_MORE_DATA)
+ then Break; // No more data, possibly because client disconnected.
+
+ Dec( nRemaining, cbRead);
+ Inc( pData, cbRead);
+ Inc( result, cbRead);
+ end;
+end;
+
+
+function TPipeStreamBase.ReadOverlapped( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer;
+var cbRead, dwWait, dwError, nRemaining : DWORD;
+ bOk : Boolean;
+ overlapped : IOverlappedHelper;
+ pData : PByte;
+begin
+ if not IsOpen
+ then raise TTransportExceptionNotOpen.Create('Called read on non-open pipe');
+
+ result := 0;
+ nRemaining := count;
+ pData := pBuf;
+ Inc( pData, offset);
+ while nRemaining > 0 do begin
+ overlapped := TOverlappedHelperImpl.Create;
+
+ // read the data
+ bOk := ReadFile( FPipe, pData^, nRemaining, cbRead, overlapped.OverlappedPtr);
+ if not bOk then begin
+ dwError := GetLastError;
+ case dwError of
+ ERROR_IO_PENDING : begin
+ dwWait := overlapped.WaitFor(FTimeout);
+
+ if (dwWait = WAIT_TIMEOUT) then begin
+ CancelIo( FPipe); // prevents possible AV on invalid overlapped ptr
+ raise TTransportExceptionTimedOut.Create('Pipe read timed out');
+ end;
+
+ if (dwWait <> WAIT_OBJECT_0)
+ or not GetOverlappedResult( FPipe, overlapped.Overlapped, cbRead, TRUE)
+ then raise TTransportExceptionUnknown.Create('Pipe read error');
+ end;
+
+ else
+ raise TTransportExceptionUnknown.Create(SysErrorMessage(dwError));
+ end;
+ end;
+
+ ASSERT( cbRead > 0); // see TTransportImpl.ReadAll()
+ ASSERT( cbRead <= DWORD(nRemaining));
+ Dec( nRemaining, cbRead);
+ Inc( pData, cbRead);
+ Inc( result, cbRead);
+ end;
+end;
+
+
+function TPipeStreamBase.ToArray: TBytes;
+var bytes : LongInt;
+begin
+ SetLength( result, 0);
+ bytes := 0;
+
+ if IsOpen
+ and PeekNamedPipe( FPipe, nil, 0, nil, @bytes, nil)
+ and (bytes > 0)
+ then begin
+ SetLength( result, bytes);
+ Read( result, 0, bytes);
+ end;
+end;
+
+
+{ TNamedPipeStreamImpl }
+
+
+constructor TNamedPipeStreamImpl.Create( const aPipeName : string;
+ const aEnableOverlapped : Boolean;
+ const aShareMode: DWORD;
+ const aSecurityAttributes: PSecurityAttributes;
+ const aTimeOut, aOpenTimeOut : DWORD);
+begin
+ inherited Create( aEnableOverlapped, aTimeout, aOpenTimeOut);
+
+ FPipeName := aPipeName;
+ FShareMode := aShareMode;
+ FSecurityAttribs := aSecurityAttributes;
+
+ if Copy(FPipeName,1,2) <> '\\'
+ then FPipeName := '\\.\pipe\' + FPipeName; // assume localhost
+end;
+
+
+procedure TNamedPipeStreamImpl.Open;
+var hPipe : THandle;
+ retries, timeout, dwErr : DWORD;
+const INTERVAL = 10; // ms
+begin
+ if IsOpen then Exit;
+
+ retries := Max( 1, Round( 1.0 * FOpenTimeOut / INTERVAL));
+ timeout := FOpenTimeOut;
+
+ // if the server hasn't gotten to the point where the pipe has been created, at least wait the timeout
+ // According to MSDN, if no instances of the specified named pipe exist, the WaitNamedPipe function
+ // returns IMMEDIATELY, regardless of the time-out value.
+ // Always use INTERVAL, since WaitNamedPipe(0) defaults to some other value
+ while not WaitNamedPipe( PChar(FPipeName), INTERVAL) do begin
+ dwErr := GetLastError;
+ if dwErr <> ERROR_FILE_NOT_FOUND
+ then raise TTransportExceptionNotOpen.Create('Unable to open pipe, '+SysErrorMessage(dwErr));
+
+ if timeout <> INFINITE then begin
+ if (retries > 0)
+ then Dec(retries)
+ else raise TTransportExceptionNotOpen.Create('Unable to open pipe, timed out');
+ end;
+
+ Sleep(INTERVAL)
+ end;
+
+ // open that thingy
+ hPipe := CreateFile( PChar( FPipeName),
+ GENERIC_READ or GENERIC_WRITE,
+ FShareMode, // sharing
+ FSecurityAttribs, // security attributes
+ OPEN_EXISTING, // opens existing pipe
+ FILE_FLAG_OVERLAPPED or FILE_FLAG_WRITE_THROUGH, // async+fast, please
+ 0); // no template file
+
+ if hPipe = INVALID_HANDLE_VALUE
+ then raise TTransportExceptionNotOpen.Create('Unable to open pipe, '+SysErrorMessage(GetLastError));
+
+ // everything fine
+ FPipe := hPipe;
+end;
+
+
+{ THandlePipeStreamImpl }
+
+
+constructor THandlePipeStreamImpl.Create( const aPipeHandle : THandle;
+ const aOwnsHandle, aEnableOverlapped : Boolean;
+ const aTimeOut : DWORD);
+begin
+ inherited Create( aEnableOverlapped, aTimeOut);
+
+ if aOwnsHandle
+ then FSrcHandle := aPipeHandle
+ else FSrcHandle := DuplicatePipeHandle( aPipeHandle);
+
+ Open;
+end;
+
+
+destructor THandlePipeStreamImpl.Destroy;
+begin
+ try
+ ClosePipeHandle( FSrcHandle);
+ finally
+ inherited Destroy;
+ end;
+end;
+
+
+procedure THandlePipeStreamImpl.Open;
+begin
+ if not IsOpen
+ then FPipe := DuplicatePipeHandle( FSrcHandle);
+end;
+
+
+{ TPipeTransportBase }
+
+
+function TPipeTransportBase.GetIsOpen: Boolean;
+begin
+ result := (FInputStream <> nil) and (FInputStream.IsOpen)
+ and (FOutputStream <> nil) and (FOutputStream.IsOpen);
+end;
+
+
+procedure TPipeTransportBase.Open;
+begin
+ FInputStream.Open;
+ FOutputStream.Open;
+end;
+
+
+procedure TPipeTransportBase.Close;
+begin
+ FInputStream.Close;
+ FOutputStream.Close;
+end;
+
+
+{ TNamedPipeTransportClientEndImpl }
+
+
+constructor TNamedPipeTransportClientEndImpl.Create( const aPipeName : string; const aShareMode: DWORD;
+ const aSecurityAttributes: PSecurityAttributes;
+ const aTimeOut, aOpenTimeOut : DWORD);
+// Named pipe constructor
+begin
+ inherited Create( nil, nil);
+ FInputStream := TNamedPipeStreamImpl.Create( aPipeName, TRUE, aShareMode, aSecurityAttributes, aTimeOut, aOpenTimeOut);
+ FOutputStream := FInputStream; // true for named pipes
+end;
+
+
+constructor TNamedPipeTransportClientEndImpl.Create( aPipe : THandle; aOwnsHandle : Boolean;
+ const aTimeOut : DWORD);
+// Named pipe constructor
+begin
+ inherited Create( nil, nil);
+ FInputStream := THandlePipeStreamImpl.Create( aPipe, TRUE, aOwnsHandle, aTimeOut);
+ FOutputStream := FInputStream; // true for named pipes
+end;
+
+
+{ TNamedPipeTransportServerEndImpl }
+
+
+constructor TNamedPipeTransportServerEndImpl.Create( aPipe : THandle; aOwnsHandle : Boolean;
+ const aTimeOut : DWORD);
+// Named pipe constructor
+begin
+ FHandle := DuplicatePipeHandle( aPipe);
+ inherited Create( aPipe, aOwnsHandle, aTimeOut);
+end;
+
+
+procedure TNamedPipeTransportServerEndImpl.Close;
+begin
+ FlushFileBuffers( FHandle);
+ DisconnectNamedPipe( FHandle); // force client off the pipe
+ ClosePipeHandle( FHandle);
+
+ inherited Close;
+end;
+
+
+{ TAnonymousPipeTransportImpl }
+
+
+constructor TAnonymousPipeTransportImpl.Create( const aPipeRead, aPipeWrite : THandle;
+ aOwnsHandles : Boolean;
+ const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT);
+// Anonymous pipe constructor
+begin
+ inherited Create( nil, nil);
+ // overlapped is not supported with AnonPipes, see MSDN
+ FInputStream := THandlePipeStreamImpl.Create( aPipeRead, aOwnsHandles, FALSE, aTimeOut);
+ FOutputStream := THandlePipeStreamImpl.Create( aPipeWrite, aOwnsHandles, FALSE, aTimeOut);
+end;
+
+
+{ TPipeServerTransportBase }
+
+
+constructor TPipeServerTransportBase.Create;
+begin
+ inherited Create;
+ FStopServer := TEvent.Create(nil,TRUE,FALSE,''); // manual reset
+end;
+
+
+destructor TPipeServerTransportBase.Destroy;
+begin
+ try
+ FreeAndNil( FStopServer);
+ finally
+ inherited Destroy;
+ end;
+end;
+
+
+function TPipeServerTransportBase.QueryStopServer : Boolean;
+begin
+ result := (FStopServer = nil)
+ or (FStopServer.WaitFor(0) <> wrTimeout);
+end;
+
+
+procedure TPipeServerTransportBase.Listen;
+begin
+ FStopServer.ResetEvent;
+end;
+
+
+procedure TPipeServerTransportBase.Close;
+begin
+ FStopServer.SetEvent;
+ InternalClose;
+end;
+
+
+{ TAnonymousPipeServerTransportImpl }
+
+
+constructor TAnonymousPipeServerTransportImpl.Create(aBufsize : Cardinal; aTimeOut : DWORD);
+// Anonymous pipe CTOR
+begin
+ inherited Create;
+ FBufsize := aBufSize;
+ FReadHandle := INVALID_HANDLE_VALUE;
+ FWriteHandle := INVALID_HANDLE_VALUE;
+ FClientAnonRead := INVALID_HANDLE_VALUE;
+ FClientAnonWrite := INVALID_HANDLE_VALUE;
+ FTimeOut := aTimeOut;
+
+ // The anonymous pipe needs to be created first so that the server can
+ // pass the handles on to the client before the serve (acceptImpl)
+ // blocking call.
+ if not CreateAnonPipe
+ then raise TTransportExceptionNotOpen.Create(ClassName+'.Create() failed');
+end;
+
+
+function TAnonymousPipeServerTransportImpl.Accept(const fnAccepting: TProc): ITransport;
+var buf : Byte;
+ br : DWORD;
+begin
+ if Assigned(fnAccepting)
+ then fnAccepting();
+
+ // This 0-byte read serves merely as a blocking call.
+ if not ReadFile( FReadHandle, buf, 0, br, nil)
+ and (GetLastError() <> ERROR_MORE_DATA)
+ then raise TTransportExceptionNotOpen.Create('TServerPipe unable to initiate pipe communication');
+
+ // create the transport impl
+ result := TAnonymousPipeTransportImpl.Create( FReadHandle, FWriteHandle, FALSE, FTimeOut);
+end;
+
+
+procedure TAnonymousPipeServerTransportImpl.InternalClose;
+begin
+ ClosePipeHandle( FReadHandle);
+ ClosePipeHandle( FWriteHandle);
+ ClosePipeHandle( FClientAnonRead);
+ ClosePipeHandle( FClientAnonWrite);
+end;
+
+
+function TAnonymousPipeServerTransportImpl.ReadHandle : THandle;
+begin
+ result := FReadHandle;
+end;
+
+
+function TAnonymousPipeServerTransportImpl.WriteHandle : THandle;
+begin
+ result := FWriteHandle;
+end;
+
+
+function TAnonymousPipeServerTransportImpl.ClientAnonRead : THandle;
+begin
+ result := FClientAnonRead;
+end;
+
+
+function TAnonymousPipeServerTransportImpl.ClientAnonWrite : THandle;
+begin
+ result := FClientAnonWrite;
+end;
+
+
+function TAnonymousPipeServerTransportImpl.CreateAnonPipe : Boolean;
+var sd : PSECURITY_DESCRIPTOR;
+ sa : SECURITY_ATTRIBUTES; //TSecurityAttributes;
+ hCAR, hPipeW, hCAW, hPipe : THandle;
+begin
+ sd := PSECURITY_DESCRIPTOR( LocalAlloc( LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH));
+ try
+ Win32Check( InitializeSecurityDescriptor( sd, SECURITY_DESCRIPTOR_REVISION));
+ Win32Check( SetSecurityDescriptorDacl( sd, TRUE, nil, FALSE));
+
+ sa.nLength := sizeof( sa);
+ sa.lpSecurityDescriptor := sd;
+ sa.bInheritHandle := TRUE; //allow passing handle to child
+
+ Result := CreatePipe( hCAR, hPipeW, @sa, FBufSize); //create stdin pipe
+ if not Result then begin //create stdin pipe
+ raise TTransportExceptionNotOpen.Create('TServerPipe CreatePipe (anon) failed, '+SysErrorMessage(GetLastError));
+ Exit;
+ end;
+
+ Result := CreatePipe( hPipe, hCAW, @sa, FBufSize); //create stdout pipe
+ if not Result then begin //create stdout pipe
+ CloseHandle( hCAR);
+ CloseHandle( hPipeW);
+ raise TTransportExceptionNotOpen.Create('TServerPipe CreatePipe (anon) failed, '+SysErrorMessage(GetLastError));
+ Exit;
+ end;
+
+ FClientAnonRead := hCAR;
+ FClientAnonWrite := hCAW;
+ FReadHandle := hPipe;
+ FWriteHandle := hPipeW;
+ finally
+ if sd <> nil then LocalFree( Cardinal(sd));
+ end;
+end;
+
+
+{ TNamedPipeServerTransportImpl }
+
+
+constructor TNamedPipeServerTransportImpl.Create( aPipename : string; aBufsize, aMaxConns, aTimeOut : Cardinal);
+// Named Pipe CTOR
+begin
+ inherited Create;
+ ASSERT( aTimeout > 0);
+ FPipeName := aPipename;
+ FBufsize := aBufSize;
+ FMaxConns := Max( 1, Min( PIPE_UNLIMITED_INSTANCES, aMaxConns));
+ FHandle := INVALID_HANDLE_VALUE;
+ FTimeout := aTimeOut;
+ FConnected := FALSE;
+
+ if Copy(FPipeName,1,2) <> '\\'
+ then FPipeName := '\\.\pipe\' + FPipeName; // assume localhost
+end;
+
+
+function TNamedPipeServerTransportImpl.Accept(const fnAccepting: TProc): ITransport;
+var dwError, dwWait, dwDummy : DWORD;
+ overlapped : IOverlappedHelper;
+ handles : array[0..1] of THandle;
+begin
+ overlapped := TOverlappedHelperImpl.Create;
+
+ ASSERT( not FConnected);
+ CreateNamedPipe;
+ while not FConnected do begin
+
+ if QueryStopServer then begin
+ InternalClose;
+ Abort;
+ end;
+
+ if Assigned(fnAccepting)
+ then fnAccepting();
+
+ // Wait for the client to connect; if it succeeds, the
+ // function returns a nonzero value. If the function returns
+ // zero, GetLastError should return ERROR_PIPE_CONNECTED.
+ if ConnectNamedPipe( Handle, overlapped.OverlappedPtr) then begin
+ FConnected := TRUE;
+ Break;
+ end;
+
+ // ConnectNamedPipe() returns FALSE for OverlappedIO, even if connected.
+ // We have to check GetLastError() explicitly to find out
+ dwError := GetLastError;
+ case dwError of
+ ERROR_PIPE_CONNECTED : begin
+ FConnected := not QueryStopServer; // special case: pipe immediately connected
+ end;
+
+ ERROR_IO_PENDING : begin
+ handles[0] := overlapped.WaitHandle;
+ handles[1] := FStopServer.Handle;
+ dwWait := WaitForMultipleObjects( 2, @handles, FALSE, FTimeout);
+ FConnected := (dwWait = WAIT_OBJECT_0)
+ and GetOverlappedResult( Handle, overlapped.Overlapped, dwDummy, TRUE)
+ and not QueryStopServer;
+ end;
+
+ else
+ InternalClose;
+ raise TTransportExceptionNotOpen.Create('Client connection failed');
+ end;
+ end;
+
+ // create the transport impl
+ result := CreateTransportInstance;
+end;
+
+
+function TNamedPipeServerTransportImpl.CreateTransportInstance : ITransport;
+// create the transport impl
+var hPipe : THandle;
+begin
+ hPipe := THandle( InterlockedExchangePointer( Pointer(FHandle), Pointer(INVALID_HANDLE_VALUE)));
+ try
+ FConnected := FALSE;
+ result := TNamedPipeTransportServerEndImpl.Create( hPipe, TRUE, FTimeout);
+ except
+ ClosePipeHandle(hPipe);
+ raise;
+ end;
+end;
+
+
+procedure TNamedPipeServerTransportImpl.InternalClose;
+var hPipe : THandle;
+begin
+ hPipe := THandle( InterlockedExchangePointer( Pointer(FHandle), Pointer(INVALID_HANDLE_VALUE)));
+ if hPipe = INVALID_HANDLE_VALUE then Exit;
+
+ try
+ if FConnected
+ then FlushFileBuffers( hPipe)
+ else CancelIo( hPipe);
+ DisconnectNamedPipe( hPipe);
+ finally
+ ClosePipeHandle( hPipe);
+ FConnected := FALSE;
+ end;
+end;
+
+
+function TNamedPipeServerTransportImpl.Handle : THandle;
+begin
+ {$IFDEF WIN64}
+ result := THandle( InterlockedExchangeAdd64( Int64(FHandle), 0));
+ {$ELSE}
+ result := THandle( InterlockedExchangeAdd( Integer(FHandle), 0));
+ {$ENDIF}
+end;
+
+
+function TNamedPipeServerTransportImpl.CreateNamedPipe : THandle;
+var SIDAuthWorld : SID_IDENTIFIER_AUTHORITY ;
+ everyone_sid : PSID;
+ ea : EXPLICIT_ACCESS;
+ acl : PACL;
+ sd : PSECURITY_DESCRIPTOR;
+ sa : SECURITY_ATTRIBUTES;
+const
+ SECURITY_WORLD_SID_AUTHORITY : TSIDIdentifierAuthority = (Value : (0,0,0,0,0,1));
+ SECURITY_WORLD_RID = $00000000;
+begin
+ sd := nil;
+ everyone_sid := nil;
+ try
+ ASSERT( (FHandle = INVALID_HANDLE_VALUE) and not FConnected);
+
+ // Windows - set security to allow non-elevated apps
+ // to access pipes created by elevated apps.
+ SIDAuthWorld := SECURITY_WORLD_SID_AUTHORITY;
+ AllocateAndInitializeSid( SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, everyone_sid);
+
+ ZeroMemory( @ea, SizeOf(ea));
+ ea.grfAccessPermissions := GENERIC_ALL; //SPECIFIC_RIGHTS_ALL or STANDARD_RIGHTS_ALL;
+ ea.grfAccessMode := SET_ACCESS;
+ ea.grfInheritance := NO_INHERITANCE;
+ ea.Trustee.TrusteeForm := TRUSTEE_IS_SID;
+ ea.Trustee.TrusteeType := TRUSTEE_IS_WELL_KNOWN_GROUP;
+ ea.Trustee.ptstrName := PChar(everyone_sid);
+
+ acl := nil;
+ SetEntriesInAcl( 1, @ea, nil, acl);
+
+ sd := PSECURITY_DESCRIPTOR( LocalAlloc( LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH));
+ Win32Check( InitializeSecurityDescriptor( sd, SECURITY_DESCRIPTOR_REVISION));
+ Win32Check( SetSecurityDescriptorDacl( sd, TRUE, acl, FALSE));
+
+ sa.nLength := SizeOf(sa);
+ sa.lpSecurityDescriptor := sd;
+ sa.bInheritHandle := FALSE;
+
+ // Create an instance of the named pipe
+ {$IFDEF OLD_UNIT_NAMES}
+ result := Windows.CreateNamedPipe(
+ {$ELSE}
+ result := Winapi.Windows.CreateNamedPipe(
+ {$ENDIF}
+ PChar( FPipeName), // pipe name
+ PIPE_ACCESS_DUPLEX or // read/write access
+ FILE_FLAG_OVERLAPPED, // async mode
+ PIPE_TYPE_BYTE or // byte type pipe
+ PIPE_READMODE_BYTE, // byte read mode
+ FMaxConns, // max. instances
+ FBufSize, // output buffer size
+ FBufSize, // input buffer size
+ FTimeout, // time-out, see MSDN
+ @sa // default security attribute
+ );
+
+ if( result <> INVALID_HANDLE_VALUE)
+ then InterlockedExchangePointer( Pointer(FHandle), Pointer(result))
+ else raise TTransportExceptionNotOpen.Create('CreateNamedPipe() failed ' + IntToStr(GetLastError));
+
+ finally
+ if sd <> nil then LocalFree( Cardinal( sd));
+ if acl <> nil then LocalFree( Cardinal( acl));
+ if everyone_sid <> nil then FreeSid(everyone_sid);
+ end;
+end;
+
+
+
+end.
+
+
+
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Transport.WinHTTP.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Transport.WinHTTP.pas
new file mode 100644
index 000000000..262e38fb1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Transport.WinHTTP.pas
@@ -0,0 +1,408 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+unit Thrift.Transport.WinHTTP;
+
+{$I Thrift.Defines.inc}
+{$SCOPEDENUMS ON}
+
+interface
+
+uses
+ Classes,
+ SysUtils,
+ Math,
+ Generics.Collections,
+ Thrift.Collections,
+ Thrift.Transport,
+ Thrift.Exception,
+ Thrift.Utils,
+ Thrift.WinHTTP,
+ Thrift.Stream;
+
+type
+ TWinHTTPClientImpl = class( TTransportImpl, IHTTPClient)
+ private
+ FUri : string;
+ FInputStream : IThriftStream;
+ FOutputMemoryStream : TMemoryStream;
+ FDnsResolveTimeout : Integer;
+ FConnectionTimeout : Integer;
+ FSendTimeout : Integer;
+ FReadTimeout : Integer;
+ FCustomHeaders : IThriftDictionary<string,string>;
+ FSecureProtocols : TSecureProtocols;
+
+ function CreateRequest: IWinHTTPRequest;
+ function SecureProtocolsAsWinHTTPFlags : Cardinal;
+
+ private
+ type
+ TErrorInfo = ( SplitUrl, WinHTTPSession, WinHTTPConnection, WinHTTPRequest, RequestSetup, AutoProxy );
+
+ THTTPResponseStream = class( TThriftStreamImpl)
+ private
+ FRequest : IWinHTTPRequest;
+ protected
+ procedure Write( const pBuf : Pointer; offset: Integer; count: Integer); override;
+ function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override;
+ procedure Open; override;
+ procedure Close; override;
+ procedure Flush; override;
+ function IsOpen: Boolean; override;
+ function ToArray: TBytes; override;
+ public
+ constructor Create( const aRequest : IWinHTTPRequest);
+ destructor Destroy; override;
+ end;
+
+ protected
+ function GetIsOpen: Boolean; override;
+ procedure Open(); override;
+ procedure Close(); override;
+ function Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; override;
+ procedure Write( const pBuf : Pointer; off, len : Integer); override;
+ procedure Flush; override;
+
+ procedure SetDnsResolveTimeout(const Value: Integer);
+ function GetDnsResolveTimeout: Integer;
+ procedure SetConnectionTimeout(const Value: Integer);
+ function GetConnectionTimeout: Integer;
+ procedure SetSendTimeout(const Value: Integer);
+ function GetSendTimeout: Integer;
+ procedure SetReadTimeout(const Value: Integer);
+ function GetReadTimeout: Integer;
+ function GetSecureProtocols : TSecureProtocols;
+ procedure SetSecureProtocols( const value : TSecureProtocols);
+
+ function GetCustomHeaders: IThriftDictionary<string,string>;
+ procedure SendRequest;
+
+ property DnsResolveTimeout: Integer read GetDnsResolveTimeout write SetDnsResolveTimeout;
+ property ConnectionTimeout: Integer read GetConnectionTimeout write SetConnectionTimeout;
+ property SendTimeout: Integer read GetSendTimeout write SetSendTimeout;
+ property ReadTimeout: Integer read GetReadTimeout write SetReadTimeout;
+ property CustomHeaders: IThriftDictionary<string,string> read GetCustomHeaders;
+ public
+ constructor Create( const AUri: string);
+ destructor Destroy; override;
+ end;
+
+implementation
+
+
+{ TWinHTTPClientImpl }
+
+constructor TWinHTTPClientImpl.Create(const AUri: string);
+begin
+ inherited Create;
+ FUri := AUri;
+
+ // defaults according to MSDN
+ FDnsResolveTimeout := 0; // no timeout
+ FConnectionTimeout := 60 * 1000;
+ FSendTimeout := 30 * 1000;
+ FReadTimeout := 30 * 1000;
+
+ FSecureProtocols := DEFAULT_THRIFT_SECUREPROTOCOLS;
+
+ FCustomHeaders := TThriftDictionaryImpl<string,string>.Create;
+ FOutputMemoryStream := TMemoryStream.Create;
+end;
+
+destructor TWinHTTPClientImpl.Destroy;
+begin
+ Close;
+ FreeAndNil( FOutputMemoryStream);
+ inherited;
+end;
+
+function TWinHTTPClientImpl.CreateRequest: IWinHTTPRequest;
+var
+ pair : TPair<string,string>;
+ session : IWinHTTPSession;
+ connect : IWinHTTPConnection;
+ url : IWinHTTPUrl;
+ sPath : string;
+ info : TErrorInfo;
+begin
+ info := TErrorInfo.SplitUrl;
+ try
+ url := TWinHTTPUrlImpl.Create( FUri);
+
+ info := TErrorInfo.WinHTTPSession;
+ session := TWinHTTPSessionImpl.Create('Apache Thrift Delphi WinHTTP');
+ session.EnableSecureProtocols( SecureProtocolsAsWinHTTPFlags);
+
+ info := TErrorInfo.WinHTTPConnection;
+ connect := session.Connect( url.HostName, url.Port);
+
+ info := TErrorInfo.WinHTTPRequest;
+ sPath := url.UrlPath + url.ExtraInfo;
+ result := connect.OpenRequest( (url.Scheme = 'https'), 'POST', sPath, THRIFT_MIMETYPE);
+
+ // setting a timeout value to 0 (zero) means "no timeout" for that setting
+ info := TErrorInfo.RequestSetup;
+ result.SetTimeouts( DnsResolveTimeout, ConnectionTimeout, SendTimeout, ReadTimeout);
+
+ // headers
+ result.AddRequestHeader( 'Content-Type: '+THRIFT_MIMETYPE, WINHTTP_ADDREQ_FLAG_ADD);
+ for pair in FCustomHeaders do begin
+ Result.AddRequestHeader( pair.Key +': '+ pair.Value, WINHTTP_ADDREQ_FLAG_ADD);
+ end;
+
+ // enable automatic gzip,deflate decompression
+ result.EnableAutomaticContentDecompression(TRUE);
+
+ // AutoProxy support
+ info := TErrorInfo.AutoProxy;
+ result.TryAutoProxy( FUri);
+ except
+ on e:TException do raise;
+ on e:Exception do raise TTransportExceptionUnknown.Create( e.Message+' (at '+EnumUtils<TErrorInfo>.ToString(Ord(info))+')');
+ end;
+end;
+
+
+function TWinHTTPClientImpl.SecureProtocolsAsWinHTTPFlags : Cardinal;
+const
+ PROTOCOL_MAPPING : array[TSecureProtocol] of Cardinal = (
+ WINHTTP_FLAG_SECURE_PROTOCOL_SSL2,
+ WINHTTP_FLAG_SECURE_PROTOCOL_SSL3,
+ WINHTTP_FLAG_SECURE_PROTOCOL_TLS1,
+ WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1,
+ WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2
+ );
+var
+ prot : TSecureProtocol;
+ protos : TSecureProtocols;
+begin
+ result := 0;
+ protos := GetSecureProtocols;
+ for prot := Low(TSecureProtocol) to High(TSecureProtocol) do begin
+ if prot in protos
+ then result := result or PROTOCOL_MAPPING[prot];
+ end;
+end;
+
+
+function TWinHTTPClientImpl.GetDnsResolveTimeout: Integer;
+begin
+ Result := FDnsResolveTimeout;
+end;
+
+procedure TWinHTTPClientImpl.SetDnsResolveTimeout(const Value: Integer);
+begin
+ FDnsResolveTimeout := Value;
+end;
+
+function TWinHTTPClientImpl.GetConnectionTimeout: Integer;
+begin
+ Result := FConnectionTimeout;
+end;
+
+procedure TWinHTTPClientImpl.SetConnectionTimeout(const Value: Integer);
+begin
+ FConnectionTimeout := Value;
+end;
+
+function TWinHTTPClientImpl.GetSendTimeout: Integer;
+begin
+ Result := FSendTimeout;
+end;
+
+procedure TWinHTTPClientImpl.SetSendTimeout(const Value: Integer);
+begin
+ FSendTimeout := Value;
+end;
+
+function TWinHTTPClientImpl.GetReadTimeout: Integer;
+begin
+ Result := FReadTimeout;
+end;
+
+procedure TWinHTTPClientImpl.SetReadTimeout(const Value: Integer);
+begin
+ FReadTimeout := Value;
+end;
+
+function TWinHTTPClientImpl.GetSecureProtocols : TSecureProtocols;
+begin
+ Result := FSecureProtocols;
+end;
+
+procedure TWinHTTPClientImpl.SetSecureProtocols( const value : TSecureProtocols);
+begin
+ FSecureProtocols := Value;
+end;
+
+function TWinHTTPClientImpl.GetCustomHeaders: IThriftDictionary<string,string>;
+begin
+ Result := FCustomHeaders;
+end;
+
+function TWinHTTPClientImpl.GetIsOpen: Boolean;
+begin
+ Result := True;
+end;
+
+procedure TWinHTTPClientImpl.Open;
+begin
+ FreeAndNil( FOutputMemoryStream);
+ FOutputMemoryStream := TMemoryStream.Create;
+end;
+
+procedure TWinHTTPClientImpl.Close;
+begin
+ FInputStream := nil;
+ FreeAndNil( FOutputMemoryStream);
+end;
+
+procedure TWinHTTPClientImpl.Flush;
+begin
+ try
+ SendRequest;
+ finally
+ FreeAndNil( FOutputMemoryStream);
+ FOutputMemoryStream := TMemoryStream.Create;
+ ASSERT( FOutputMemoryStream <> nil);
+ end;
+end;
+
+function TWinHTTPClientImpl.Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer;
+begin
+ if FInputStream = nil then begin
+ raise TTransportExceptionNotOpen.Create('No request has been sent');
+ end;
+
+ try
+ Result := FInputStream.Read( pBuf, buflen, off, len)
+ except
+ on E: Exception
+ do raise TTransportExceptionUnknown.Create(E.Message);
+ end;
+end;
+
+procedure TWinHTTPClientImpl.SendRequest;
+var
+ http : IWinHTTPRequest;
+ pData : PByte;
+ len : Integer;
+ error : Cardinal;
+ sMsg : string;
+begin
+ http := CreateRequest;
+
+ pData := FOutputMemoryStream.Memory;
+ len := FOutputMemoryStream.Size;
+
+ // send all data immediately, since we have it in memory
+ if not http.SendRequest( pData, len, 0) then begin
+ error := Cardinal( GetLastError);
+ sMsg := 'WinHTTP send error '+IntToStr(Int64(error))+' '+WinHttpSysErrorMessage(error);
+ raise TTransportExceptionUnknown.Create(sMsg);
+ end;
+
+ // end request and start receiving
+ if not http.FlushAndReceiveResponse then begin
+ error := Cardinal( GetLastError);
+ sMsg := 'WinHTTP recv error '+IntToStr(Int64(error))+' '+WinHttpSysErrorMessage(error);
+ if error = ERROR_WINHTTP_TIMEOUT
+ then raise TTransportExceptionTimedOut.Create( sMsg)
+ else raise TTransportExceptionInterrupted.Create( sMsg);
+ end;
+
+ FInputStream := THTTPResponseStream.Create(http);
+end;
+
+procedure TWinHTTPClientImpl.Write( const pBuf : Pointer; off, len : Integer);
+var pTmp : PByte;
+begin
+ pTmp := pBuf;
+ Inc(pTmp,off);
+ FOutputMemoryStream.Write( pTmp^, len);
+end;
+
+
+{ TWinHTTPClientImpl.THTTPResponseStream }
+
+constructor TWinHTTPClientImpl.THTTPResponseStream.Create( const aRequest : IWinHTTPRequest);
+begin
+ inherited Create;
+ FRequest := aRequest;
+end;
+
+destructor TWinHTTPClientImpl.THTTPResponseStream.Destroy;
+begin
+ try
+ Close;
+ finally
+ inherited Destroy;
+ end;
+end;
+
+procedure TWinHTTPClientImpl.THTTPResponseStream.Close;
+begin
+ FRequest := nil;
+end;
+
+procedure TWinHTTPClientImpl.THTTPResponseStream.Flush;
+begin
+ raise ENotImplemented(ClassName+'.Flush');
+end;
+
+function TWinHTTPClientImpl.THTTPResponseStream.IsOpen: Boolean;
+begin
+ Result := FRequest <> nil;
+end;
+
+procedure TWinHTTPClientImpl.THTTPResponseStream.Open;
+begin
+ // nothing to do
+end;
+
+procedure TWinHTTPClientImpl.THTTPResponseStream.Write(const pBuf : Pointer; offset, count: Integer);
+begin
+ inherited; // check pointers
+ raise ENotImplemented(ClassName+'.Write');
+end;
+
+function TWinHTTPClientImpl.THTTPResponseStream.Read(const pBuf : Pointer; const buflen : Integer; offset, count: Integer): Integer;
+var pTmp : PByte;
+begin
+ inherited; // check pointers
+
+ if count >= buflen-offset
+ then count := buflen-offset;
+
+ if count > 0 then begin
+ pTmp := pBuf;
+ Inc( pTmp, offset);
+ Result := FRequest.ReadData( pTmp, count);
+ ASSERT( Result >= 0);
+ end
+ else Result := 0;
+end;
+
+function TWinHTTPClientImpl.THTTPResponseStream.ToArray: TBytes;
+begin
+ raise ENotImplemented(ClassName+'.ToArray');
+end;
+
+
+end.
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Transport.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Transport.pas
new file mode 100644
index 000000000..c2071df89
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Transport.pas
@@ -0,0 +1,1523 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+unit Thrift.Transport;
+
+{$I Thrift.Defines.inc}
+{$SCOPEDENUMS ON}
+
+interface
+
+uses
+ Classes,
+ SysUtils,
+ Math,
+ Generics.Collections,
+ {$IFDEF OLD_UNIT_NAMES}
+ WinSock, Sockets,
+ {$ELSE}
+ Winapi.WinSock,
+ {$IFDEF OLD_SOCKETS}
+ Web.Win.Sockets,
+ {$ELSE}
+ Thrift.Socket,
+ {$ENDIF}
+ {$ENDIF}
+ Thrift.Collections,
+ Thrift.Exception,
+ Thrift.Utils,
+ Thrift.WinHTTP,
+ Thrift.Stream;
+
+type
+ ITransport = interface
+ ['{DB84961E-8BB3-4532-99E1-A8C7AC2300F7}']
+ function GetIsOpen: Boolean;
+ property IsOpen: Boolean read GetIsOpen;
+ function Peek: Boolean;
+ procedure Open;
+ procedure Close;
+ function Read(var buf: TBytes; off: Integer; len: Integer): Integer; overload;
+ function Read(const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; overload;
+ function ReadAll(var buf: TBytes; off: Integer; len: Integer): Integer; overload;
+ function ReadAll(const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; overload;
+ procedure Write( const buf: TBytes); overload;
+ procedure Write( const buf: TBytes; off: Integer; len: Integer); overload;
+ procedure Write( const pBuf : Pointer; off, len : Integer); overload;
+ procedure Write( const pBuf : Pointer; len : Integer); overload;
+ procedure Flush;
+ end;
+
+ TTransportImpl = class( TInterfacedObject, ITransport)
+ protected
+ function GetIsOpen: Boolean; virtual; abstract;
+ property IsOpen: Boolean read GetIsOpen;
+ function Peek: Boolean; virtual;
+ procedure Open(); virtual; abstract;
+ procedure Close(); virtual; abstract;
+ function Read(var buf: TBytes; off: Integer; len: Integer): Integer; overload; inline;
+ function Read(const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; overload; virtual; abstract;
+ function ReadAll(var buf: TBytes; off: Integer; len: Integer): Integer; overload; inline;
+ function ReadAll(const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; overload; virtual;
+ procedure Write( const buf: TBytes); overload; inline;
+ procedure Write( const buf: TBytes; off: Integer; len: Integer); overload; inline;
+ procedure Write( const pBuf : Pointer; len : Integer); overload; inline;
+ procedure Write( const pBuf : Pointer; off, len : Integer); overload; virtual; abstract;
+ procedure Flush; virtual;
+ end;
+
+ TTransportException = class( TException)
+ public
+ type
+ TExceptionType = (
+ Unknown,
+ NotOpen,
+ AlreadyOpen,
+ TimedOut,
+ EndOfFile,
+ BadArgs,
+ Interrupted
+ );
+ private
+ function GetType: TExceptionType;
+ protected
+ constructor HiddenCreate(const Msg: string);
+ public
+ class function Create( AType: TExceptionType): TTransportException; overload; deprecated 'Use specialized TTransportException types (or regenerate from IDL)';
+ class function Create( const msg: string): TTransportException; reintroduce; overload; deprecated 'Use specialized TTransportException types (or regenerate from IDL)';
+ class function Create( AType: TExceptionType; const msg: string): TTransportException; overload; deprecated 'Use specialized TTransportException types (or regenerate from IDL)';
+ property Type_: TExceptionType read GetType;
+ end;
+
+ // Needed to remove deprecation warning
+ TTransportExceptionSpecialized = class abstract (TTransportException)
+ public
+ constructor Create(const Msg: string);
+ end;
+
+ TTransportExceptionUnknown = class (TTransportExceptionSpecialized);
+ TTransportExceptionNotOpen = class (TTransportExceptionSpecialized);
+ TTransportExceptionAlreadyOpen = class (TTransportExceptionSpecialized);
+ TTransportExceptionTimedOut = class (TTransportExceptionSpecialized);
+ TTransportExceptionEndOfFile = class (TTransportExceptionSpecialized);
+ TTransportExceptionBadArgs = class (TTransportExceptionSpecialized);
+ TTransportExceptionInterrupted = class (TTransportExceptionSpecialized);
+
+ TSecureProtocol = (
+ SSL_2, SSL_3, TLS_1, // outdated, for compatibilty only
+ TLS_1_1, TLS_1_2 // secure (as of today)
+ );
+
+ TSecureProtocols = set of TSecureProtocol;
+
+ IHTTPClient = interface( ITransport )
+ ['{7BF615DD-8680-4004-A5B2-88947BA3BA3D}']
+ procedure SetDnsResolveTimeout(const Value: Integer);
+ function GetDnsResolveTimeout: Integer;
+ procedure SetConnectionTimeout(const Value: Integer);
+ function GetConnectionTimeout: Integer;
+ procedure SetSendTimeout(const Value: Integer);
+ function GetSendTimeout: Integer;
+ procedure SetReadTimeout(const Value: Integer);
+ function GetReadTimeout: Integer;
+ function GetCustomHeaders: IThriftDictionary<string,string>;
+ procedure SendRequest;
+ function GetSecureProtocols : TSecureProtocols;
+ procedure SetSecureProtocols( const value : TSecureProtocols);
+
+ property DnsResolveTimeout: Integer read GetDnsResolveTimeout write SetDnsResolveTimeout;
+ property ConnectionTimeout: Integer read GetConnectionTimeout write SetConnectionTimeout;
+ property SendTimeout: Integer read GetSendTimeout write SetSendTimeout;
+ property ReadTimeout: Integer read GetReadTimeout write SetReadTimeout;
+ property CustomHeaders: IThriftDictionary<string,string> read GetCustomHeaders;
+ property SecureProtocols : TSecureProtocols read GetSecureProtocols write SetSecureProtocols;
+ end;
+
+ IServerTransport = interface
+ ['{C43B87ED-69EA-47C4-B77C-15E288252900}']
+ procedure Listen;
+ procedure Close;
+ function Accept( const fnAccepting: TProc): ITransport;
+ end;
+
+ TServerTransportImpl = class( TInterfacedObject, IServerTransport)
+ protected
+ procedure Listen; virtual; abstract;
+ procedure Close; virtual; abstract;
+ function Accept( const fnAccepting: TProc): ITransport; virtual; abstract;
+ end;
+
+ ITransportFactory = interface
+ ['{DD809446-000F-49E1-9BFF-E0D0DC76A9D7}']
+ function GetTransport( const ATrans: ITransport): ITransport;
+ end;
+
+ TTransportFactoryImpl = class( TInterfacedObject, ITransportFactory)
+ function GetTransport( const ATrans: ITransport): ITransport; virtual;
+ end;
+
+ TTcpSocketStreamImpl = class( TThriftStreamImpl )
+{$IFDEF OLD_SOCKETS}
+ private type
+ TWaitForData = ( wfd_HaveData, wfd_Timeout, wfd_Error);
+ private
+ FTcpClient : TCustomIpClient;
+ FTimeout : Integer;
+ function Select( ReadReady, WriteReady, ExceptFlag: PBoolean;
+ TimeOut: Integer; var wsaError : Integer): Integer;
+ function WaitForData( TimeOut : Integer; pBuf : Pointer; DesiredBytes: Integer;
+ var wsaError, bytesReady : Integer): TWaitForData;
+{$ELSE}
+ FTcpClient: TSocket;
+ protected const
+ SLEEP_TIME = 200;
+{$ENDIF}
+ protected
+ procedure Write( const pBuf : Pointer; offset, count: Integer); override;
+ function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override;
+ procedure Open; override;
+ procedure Close; override;
+ procedure Flush; override;
+
+ function IsOpen: Boolean; override;
+ function ToArray: TBytes; override;
+ public
+{$IFDEF OLD_SOCKETS}
+ constructor Create( const ATcpClient: TCustomIpClient; const aTimeout : Integer = 0);
+{$ELSE}
+ constructor Create( const ATcpClient: TSocket; const aTimeout : Longword = 0);
+{$ENDIF}
+ end;
+
+ IStreamTransport = interface( ITransport )
+ ['{A8479B47-2A3E-4421-A9A0-D5A9EDCC634A}']
+ function GetInputStream: IThriftStream;
+ function GetOutputStream: IThriftStream;
+ property InputStream : IThriftStream read GetInputStream;
+ property OutputStream : IThriftStream read GetOutputStream;
+ end;
+
+ TStreamTransportImpl = class( TTransportImpl, IStreamTransport)
+ protected
+ FInputStream : IThriftStream;
+ FOutputStream : IThriftStream;
+ protected
+ function GetIsOpen: Boolean; override;
+
+ function GetInputStream: IThriftStream;
+ function GetOutputStream: IThriftStream;
+ public
+ property InputStream : IThriftStream read GetInputStream;
+ property OutputStream : IThriftStream read GetOutputStream;
+
+ procedure Open; override;
+ procedure Close; override;
+ procedure Flush; override;
+ function Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; override;
+ procedure Write( const pBuf : Pointer; off, len : Integer); override;
+ constructor Create( const AInputStream : IThriftStream; const AOutputStream : IThriftStream);
+ destructor Destroy; override;
+ end;
+
+ TBufferedStreamImpl = class( TThriftStreamImpl)
+ private
+ FStream : IThriftStream;
+ FBufSize : Integer;
+ FReadBuffer : TMemoryStream;
+ FWriteBuffer : TMemoryStream;
+ protected
+ procedure Write( const pBuf : Pointer; offset: Integer; count: Integer); override;
+ function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override;
+ procedure Open; override;
+ procedure Close; override;
+ procedure Flush; override;
+ function IsOpen: Boolean; override;
+ function ToArray: TBytes; override;
+ public
+ constructor Create( const AStream: IThriftStream; ABufSize: Integer);
+ destructor Destroy; override;
+ end;
+
+ TServerSocketImpl = class( TServerTransportImpl)
+ private
+{$IFDEF OLD_SOCKETS}
+ FServer : TTcpServer;
+ FPort : Integer;
+ FClientTimeout : Integer;
+{$ELSE}
+ FServer: TServerSocket;
+{$ENDIF}
+ FUseBufferedSocket : Boolean;
+ FOwnsServer : Boolean;
+ protected
+ function Accept( const fnAccepting: TProc) : ITransport; override;
+ public
+{$IFDEF OLD_SOCKETS}
+ constructor Create( const AServer: TTcpServer; AClientTimeout: Integer = 0); overload;
+ constructor Create( APort: Integer; AClientTimeout: Integer = 0; AUseBufferedSockets: Boolean = FALSE); overload;
+{$ELSE}
+ constructor Create( const AServer: TServerSocket; AClientTimeout: Longword = 0); overload;
+ constructor Create( APort: Integer; AClientTimeout: Longword = 0; AUseBufferedSockets: Boolean = FALSE); overload;
+{$ENDIF}
+ destructor Destroy; override;
+ procedure Listen; override;
+ procedure Close; override;
+ end;
+
+ TBufferedTransportImpl = class( TTransportImpl )
+ private
+ FInputBuffer : IThriftStream;
+ FOutputBuffer : IThriftStream;
+ FTransport : IStreamTransport;
+ FBufSize : Integer;
+
+ procedure InitBuffers;
+ function GetUnderlyingTransport: ITransport;
+ protected
+ function GetIsOpen: Boolean; override;
+ procedure Flush; override;
+ public
+ procedure Open(); override;
+ procedure Close(); override;
+ function Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; override;
+ procedure Write( const pBuf : Pointer; off, len : Integer); override;
+ constructor Create( const ATransport : IStreamTransport ); overload;
+ constructor Create( const ATransport : IStreamTransport; ABufSize: Integer); overload;
+ property UnderlyingTransport: ITransport read GetUnderlyingTransport;
+ property IsOpen: Boolean read GetIsOpen;
+ end;
+
+ TSocketImpl = class(TStreamTransportImpl)
+ private
+{$IFDEF OLD_SOCKETS}
+ FClient : TCustomIpClient;
+{$ELSE}
+ FClient: TSocket;
+{$ENDIF}
+ FOwnsClient : Boolean;
+ FHost : string;
+ FPort : Integer;
+{$IFDEF OLD_SOCKETS}
+ FTimeout : Integer;
+{$ELSE}
+ FTimeout : Longword;
+{$ENDIF}
+
+ procedure InitSocket;
+ protected
+ function GetIsOpen: Boolean; override;
+ public
+ procedure Open; override;
+{$IFDEF OLD_SOCKETS}
+ constructor Create( const AClient : TCustomIpClient; aOwnsClient : Boolean; ATimeout: Integer = 0); overload;
+ constructor Create( const AHost: string; APort: Integer; ATimeout: Integer = 0); overload;
+{$ELSE}
+ constructor Create(const AClient: TSocket; aOwnsClient: Boolean); overload;
+ constructor Create( const AHost: string; APort: Integer; ATimeout: Longword = 0); overload;
+{$ENDIF}
+ destructor Destroy; override;
+ procedure Close; override;
+{$IFDEF OLD_SOCKETS}
+ property TcpClient: TCustomIpClient read FClient;
+{$ELSE}
+ property TcpClient: TSocket read FClient;
+{$ENDIF}
+ property Host : string read FHost;
+ property Port: Integer read FPort;
+ end;
+
+ TFramedTransportImpl = class( TTransportImpl)
+ private const
+ FHeaderSize : Integer = 4;
+ private class var
+ FHeader_Dummy : array of Byte;
+ protected
+ FTransport : ITransport;
+ FWriteBuffer : TMemoryStream;
+ FReadBuffer : TMemoryStream;
+
+ procedure InitWriteBuffer;
+ procedure ReadFrame;
+ public
+ type
+ TFactory = class( TTransportFactoryImpl )
+ public
+ function GetTransport( const ATrans: ITransport): ITransport; override;
+ end;
+
+ {$IFDEF HAVE_CLASS_CTOR}
+ class constructor Create;
+ {$ENDIF}
+
+ constructor Create; overload;
+ constructor Create( const ATrans: ITransport); overload;
+ destructor Destroy; override;
+
+ procedure Open(); override;
+ function GetIsOpen: Boolean; override;
+
+ procedure Close(); override;
+ function Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; override;
+ procedure Write( const pBuf : Pointer; off, len : Integer); override;
+ procedure Flush; override;
+ end;
+
+{$IFNDEF HAVE_CLASS_CTOR}
+procedure TFramedTransportImpl_Initialize;
+{$ENDIF}
+
+const
+ DEFAULT_THRIFT_TIMEOUT = 5 * 1000; // ms
+ DEFAULT_THRIFT_SECUREPROTOCOLS = [ TSecureProtocol.TLS_1_1, TSecureProtocol.TLS_1_2];
+
+
+
+implementation
+
+{ TTransportImpl }
+
+procedure TTransportImpl.Flush;
+begin
+ // nothing to do
+end;
+
+function TTransportImpl.Peek: Boolean;
+begin
+ Result := IsOpen;
+end;
+
+function TTransportImpl.Read(var buf: TBytes; off: Integer; len: Integer): Integer;
+begin
+ if Length(buf) > 0
+ then result := Read( @buf[0], Length(buf), off, len)
+ else result := 0;
+end;
+
+function TTransportImpl.ReadAll(var buf: TBytes; off: Integer; len: Integer): Integer;
+begin
+ if Length(buf) > 0
+ then result := ReadAll( @buf[0], Length(buf), off, len)
+ else result := 0;
+end;
+
+procedure TTransportImpl.Write( const buf: TBytes);
+begin
+ if Length(buf) > 0
+ then Write( @buf[0], 0, Length(buf));
+end;
+
+procedure TTransportImpl.Write( const buf: TBytes; off: Integer; len: Integer);
+begin
+ if Length(buf) > 0
+ then Write( @buf[0], off, len);
+end;
+
+function TTransportImpl.ReadAll(const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer;
+var ret : Integer;
+begin
+ result := 0;
+ while result < len do begin
+ ret := Read( pBuf, buflen, off + result, len - result);
+ if ret > 0
+ then Inc( result, ret)
+ else raise TTransportExceptionNotOpen.Create( 'Cannot read, Remote side has closed' );
+ end;
+end;
+
+procedure TTransportImpl.Write( const pBuf : Pointer; len : Integer);
+begin
+ Self.Write( pBuf, 0, len);
+end;
+
+{ TTransportException }
+
+function TTransportException.GetType: TExceptionType;
+begin
+ if Self is TTransportExceptionNotOpen then Result := TExceptionType.NotOpen
+ else if Self is TTransportExceptionAlreadyOpen then Result := TExceptionType.AlreadyOpen
+ else if Self is TTransportExceptionTimedOut then Result := TExceptionType.TimedOut
+ else if Self is TTransportExceptionEndOfFile then Result := TExceptionType.EndOfFile
+ else if Self is TTransportExceptionBadArgs then Result := TExceptionType.BadArgs
+ else if Self is TTransportExceptionInterrupted then Result := TExceptionType.Interrupted
+ else Result := TExceptionType.Unknown;
+end;
+
+constructor TTransportException.HiddenCreate(const Msg: string);
+begin
+ inherited Create(Msg);
+end;
+
+class function TTransportException.Create(AType: TExceptionType): TTransportException;
+begin
+ //no inherited;
+{$WARN SYMBOL_DEPRECATED OFF}
+ Result := Create(AType, '')
+{$WARN SYMBOL_DEPRECATED DEFAULT}
+end;
+
+class function TTransportException.Create(AType: TExceptionType;
+ const msg: string): TTransportException;
+begin
+ case AType of
+ TExceptionType.NotOpen: Result := TTransportExceptionNotOpen.Create(msg);
+ TExceptionType.AlreadyOpen: Result := TTransportExceptionAlreadyOpen.Create(msg);
+ TExceptionType.TimedOut: Result := TTransportExceptionTimedOut.Create(msg);
+ TExceptionType.EndOfFile: Result := TTransportExceptionEndOfFile.Create(msg);
+ TExceptionType.BadArgs: Result := TTransportExceptionBadArgs.Create(msg);
+ TExceptionType.Interrupted: Result := TTransportExceptionInterrupted.Create(msg);
+ else
+ Result := TTransportExceptionUnknown.Create(msg);
+ end;
+end;
+
+class function TTransportException.Create(const msg: string): TTransportException;
+begin
+ Result := TTransportExceptionUnknown.Create(Msg);
+end;
+
+{ TTransportExceptionSpecialized }
+
+constructor TTransportExceptionSpecialized.Create(const Msg: string);
+begin
+ inherited HiddenCreate(Msg);
+end;
+
+{ TTransportFactoryImpl }
+
+function TTransportFactoryImpl.GetTransport( const ATrans: ITransport): ITransport;
+begin
+ Result := ATrans;
+end;
+
+{ TServerSocket }
+
+{$IFDEF OLD_SOCKETS}
+constructor TServerSocketImpl.Create( const AServer: TTcpServer; AClientTimeout: Integer);
+begin
+ inherited Create;
+ FServer := AServer;
+ FClientTimeout := AClientTimeout;
+end;
+{$ELSE}
+constructor TServerSocketImpl.Create( const AServer: TServerSocket; AClientTimeout: Longword);
+begin
+ inherited Create;
+ FServer := AServer;
+ FServer.RecvTimeout := AClientTimeout;
+ FServer.SendTimeout := AClientTimeout;
+end;
+{$ENDIF}
+
+{$IFDEF OLD_SOCKETS}
+constructor TServerSocketImpl.Create(APort, AClientTimeout: Integer; AUseBufferedSockets: Boolean);
+{$ELSE}
+constructor TServerSocketImpl.Create(APort: Integer; AClientTimeout: Longword; AUseBufferedSockets: Boolean);
+{$ENDIF}
+begin
+ inherited Create;
+{$IFDEF OLD_SOCKETS}
+ FPort := APort;
+ FClientTimeout := AClientTimeout;
+ FServer := TTcpServer.Create( nil );
+ FServer.BlockMode := bmBlocking;
+ {$IF CompilerVersion >= 21.0}
+ FServer.LocalPort := AnsiString( IntToStr( FPort));
+ {$ELSE}
+ FServer.LocalPort := IntToStr( FPort);
+ {$IFEND}
+{$ELSE}
+ FServer := TServerSocket.Create(APort, AClientTimeout, AClientTimeout);
+{$ENDIF}
+ FUseBufferedSocket := AUseBufferedSockets;
+ FOwnsServer := True;
+end;
+
+destructor TServerSocketImpl.Destroy;
+begin
+ if FOwnsServer then begin
+ FServer.Free;
+ FServer := nil;
+ end;
+ inherited;
+end;
+
+function TServerSocketImpl.Accept( const fnAccepting: TProc): ITransport;
+var
+{$IFDEF OLD_SOCKETS}
+ client : TCustomIpClient;
+{$ELSE}
+ client: TSocket;
+{$ENDIF}
+ trans : IStreamTransport;
+begin
+ if FServer = nil then begin
+ raise TTransportExceptionNotOpen.Create('No underlying server socket.');
+ end;
+
+{$IFDEF OLD_SOCKETS}
+ client := nil;
+ try
+ client := TCustomIpClient.Create(nil);
+
+ if Assigned(fnAccepting)
+ then fnAccepting();
+
+ if not FServer.Accept( client) then begin
+ client.Free;
+ Result := nil;
+ Exit;
+ end;
+
+ if client = nil then begin
+ Result := nil;
+ Exit;
+ end;
+
+ trans := TSocketImpl.Create( client, TRUE, FClientTimeout);
+ client := nil; // trans owns it now
+
+ if FUseBufferedSocket
+ then result := TBufferedTransportImpl.Create( trans)
+ else result := trans;
+
+ except
+ on E: Exception do begin
+ client.Free;
+ raise TTransportExceptionUnknown.Create(E.ToString);
+ end;
+ end;
+{$ELSE}
+ if Assigned(fnAccepting) then
+ fnAccepting();
+
+ client := FServer.Accept;
+ try
+ trans := TSocketImpl.Create(client, True);
+ client := nil;
+
+ if FUseBufferedSocket then
+ Result := TBufferedTransportImpl.Create(trans)
+ else
+ Result := trans;
+ except
+ client.Free;
+ raise;
+ end;
+{$ENDIF}
+end;
+
+procedure TServerSocketImpl.Listen;
+begin
+ if FServer <> nil then
+ begin
+{$IFDEF OLD_SOCKETS}
+ try
+ FServer.Active := True;
+ except
+ on E: Exception
+ do raise TTransportExceptionUnknown.Create('Could not accept on listening socket: ' + E.Message);
+ end;
+{$ELSE}
+ FServer.Listen;
+{$ENDIF}
+ end;
+end;
+
+procedure TServerSocketImpl.Close;
+begin
+ if FServer <> nil then
+{$IFDEF OLD_SOCKETS}
+ try
+ FServer.Active := False;
+ except
+ on E: Exception
+ do raise TTransportExceptionUnknown.Create('Error on closing socket : ' + E.Message);
+ end;
+{$ELSE}
+ FServer.Close;
+{$ENDIF}
+end;
+
+{ TSocket }
+
+{$IFDEF OLD_SOCKETS}
+constructor TSocketImpl.Create( const AClient : TCustomIpClient; aOwnsClient : Boolean; ATimeout: Integer = 0);
+var stream : IThriftStream;
+begin
+ FClient := AClient;
+ FTimeout := ATimeout;
+ FOwnsClient := aOwnsClient;
+ stream := TTcpSocketStreamImpl.Create( FClient, FTimeout);
+ inherited Create( stream, stream);
+end;
+{$ELSE}
+constructor TSocketImpl.Create(const AClient: TSocket; aOwnsClient: Boolean);
+var stream : IThriftStream;
+begin
+ FClient := AClient;
+ FTimeout := AClient.RecvTimeout;
+ FOwnsClient := aOwnsClient;
+ stream := TTcpSocketStreamImpl.Create(FClient, FTimeout);
+ inherited Create(stream, stream);
+end;
+{$ENDIF}
+
+{$IFDEF OLD_SOCKETS}
+constructor TSocketImpl.Create(const AHost: string; APort, ATimeout: Integer);
+{$ELSE}
+constructor TSocketImpl.Create(const AHost: string; APort: Integer; ATimeout: Longword);
+{$ENDIF}
+begin
+ inherited Create(nil,nil);
+ FHost := AHost;
+ FPort := APort;
+ FTimeout := ATimeout;
+ InitSocket;
+end;
+
+destructor TSocketImpl.Destroy;
+begin
+ if FOwnsClient
+ then FreeAndNil( FClient);
+ inherited;
+end;
+
+procedure TSocketImpl.Close;
+begin
+ inherited Close;
+
+ FInputStream := nil;
+ FOutputStream := nil;
+
+ if FOwnsClient
+ then FreeAndNil( FClient)
+ else FClient := nil;
+end;
+
+function TSocketImpl.GetIsOpen: Boolean;
+begin
+{$IFDEF OLD_SOCKETS}
+ Result := (FClient <> nil) and FClient.Connected;
+{$ELSE}
+ Result := (FClient <> nil) and FClient.IsOpen
+{$ENDIF}
+end;
+
+procedure TSocketImpl.InitSocket;
+var
+ stream : IThriftStream;
+begin
+ if FOwnsClient
+ then FreeAndNil( FClient)
+ else FClient := nil;
+
+{$IFDEF OLD_SOCKETS}
+ FClient := TTcpClient.Create( nil);
+{$ELSE}
+ FClient := TSocket.Create(FHost, FPort);
+{$ENDIF}
+ FOwnsClient := True;
+
+ stream := TTcpSocketStreamImpl.Create( FClient, FTimeout);
+ FInputStream := stream;
+ FOutputStream := stream;
+end;
+
+procedure TSocketImpl.Open;
+begin
+ if IsOpen then begin
+ raise TTransportExceptionAlreadyOpen.Create('Socket already connected');
+ end;
+
+ if FHost = '' then begin
+ raise TTransportExceptionNotOpen.Create('Cannot open null host');
+ end;
+
+ if Port <= 0 then begin
+ raise TTransportExceptionNotOpen.Create('Cannot open without port');
+ end;
+
+ if FClient = nil
+ then InitSocket;
+
+{$IFDEF OLD_SOCKETS}
+ FClient.RemoteHost := TSocketHost( Host);
+ FClient.RemotePort := TSocketPort( IntToStr( Port));
+ FClient.Connect;
+{$ELSE}
+ FClient.Open;
+{$ENDIF}
+
+ FInputStream := TTcpSocketStreamImpl.Create( FClient, FTimeout);
+ FOutputStream := FInputStream;
+end;
+
+{ TBufferedStream }
+
+procedure TBufferedStreamImpl.Close;
+begin
+ Flush;
+ FStream := nil;
+
+ FReadBuffer.Free;
+ FReadBuffer := nil;
+
+ FWriteBuffer.Free;
+ FWriteBuffer := nil;
+end;
+
+constructor TBufferedStreamImpl.Create( const AStream: IThriftStream; ABufSize: Integer);
+begin
+ inherited Create;
+ FStream := AStream;
+ FBufSize := ABufSize;
+ FReadBuffer := TMemoryStream.Create;
+ FWriteBuffer := TMemoryStream.Create;
+end;
+
+destructor TBufferedStreamImpl.Destroy;
+begin
+ Close;
+ inherited;
+end;
+
+procedure TBufferedStreamImpl.Flush;
+var
+ buf : TBytes;
+ len : Integer;
+begin
+ if IsOpen then begin
+ len := FWriteBuffer.Size;
+ if len > 0 then begin
+ SetLength( buf, len );
+ FWriteBuffer.Position := 0;
+ FWriteBuffer.Read( Pointer(@buf[0])^, len );
+ FStream.Write( buf, 0, len );
+ end;
+ FWriteBuffer.Clear;
+ end;
+end;
+
+function TBufferedStreamImpl.IsOpen: Boolean;
+begin
+ Result := (FWriteBuffer <> nil)
+ and (FReadBuffer <> nil)
+ and (FStream <> nil)
+ and FStream.IsOpen;
+end;
+
+procedure TBufferedStreamImpl.Open;
+begin
+ FStream.Open;
+end;
+
+function TBufferedStreamImpl.Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer;
+var
+ nRead : Integer;
+ tempbuf : TBytes;
+ pTmp : PByte;
+begin
+ inherited;
+ Result := 0;
+
+ if IsOpen then begin
+ while count > 0 do begin
+
+ if FReadBuffer.Position >= FReadBuffer.Size then begin
+ FReadBuffer.Clear;
+ SetLength( tempbuf, FBufSize);
+ nRead := FStream.Read( tempbuf, 0, FBufSize );
+ if nRead = 0 then Break; // avoid infinite loop
+
+ FReadBuffer.WriteBuffer( Pointer(@tempbuf[0])^, nRead );
+ FReadBuffer.Position := 0;
+ end;
+
+ if FReadBuffer.Position < FReadBuffer.Size then begin
+ nRead := Min( FReadBuffer.Size - FReadBuffer.Position, count);
+ pTmp := pBuf;
+ Inc( pTmp, offset);
+ Inc( Result, FReadBuffer.Read( pTmp^, nRead));
+ Dec( count, nRead);
+ Inc( offset, nRead);
+ end;
+ end;
+ end;
+end;
+
+function TBufferedStreamImpl.ToArray: TBytes;
+var len : Integer;
+begin
+ len := 0;
+
+ if IsOpen then begin
+ len := FReadBuffer.Size;
+ end;
+
+ SetLength( Result, len);
+
+ if len > 0 then begin
+ FReadBuffer.Position := 0;
+ FReadBuffer.Read( Pointer(@Result[0])^, len );
+ end;
+end;
+
+procedure TBufferedStreamImpl.Write( const pBuf : Pointer; offset: Integer; count: Integer);
+var pTmp : PByte;
+begin
+ inherited;
+ if count > 0 then begin
+ if IsOpen then begin
+ pTmp := pBuf;
+ Inc( pTmp, offset);
+ FWriteBuffer.Write( pTmp^, count );
+ if FWriteBuffer.Size > FBufSize then begin
+ Flush;
+ end;
+ end;
+ end;
+end;
+
+{ TStreamTransportImpl }
+
+constructor TStreamTransportImpl.Create( const AInputStream : IThriftStream; const AOutputStream : IThriftStream);
+begin
+ inherited Create;
+ FInputStream := AInputStream;
+ FOutputStream := AOutputStream;
+end;
+
+destructor TStreamTransportImpl.Destroy;
+begin
+ FInputStream := nil;
+ FOutputStream := nil;
+ inherited;
+end;
+
+procedure TStreamTransportImpl.Close;
+begin
+ FInputStream := nil;
+ FOutputStream := nil;
+end;
+
+procedure TStreamTransportImpl.Flush;
+begin
+ if FOutputStream = nil then begin
+ raise TTransportExceptionNotOpen.Create('Cannot flush null outputstream' );
+ end;
+
+ FOutputStream.Flush;
+end;
+
+function TStreamTransportImpl.GetInputStream: IThriftStream;
+begin
+ Result := FInputStream;
+end;
+
+function TStreamTransportImpl.GetIsOpen: Boolean;
+begin
+ Result := True;
+end;
+
+function TStreamTransportImpl.GetOutputStream: IThriftStream;
+begin
+ Result := FOutputStream;
+end;
+
+procedure TStreamTransportImpl.Open;
+begin
+
+end;
+
+function TStreamTransportImpl.Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer;
+begin
+ if FInputStream = nil then begin
+ raise TTransportExceptionNotOpen.Create('Cannot read from null inputstream' );
+ end;
+
+ Result := FInputStream.Read( pBuf,buflen, off, len );
+end;
+
+procedure TStreamTransportImpl.Write( const pBuf : Pointer; off, len : Integer);
+begin
+ if FOutputStream = nil then begin
+ raise TTransportExceptionNotOpen.Create('Cannot write to null outputstream' );
+ end;
+
+ FOutputStream.Write( pBuf, off, len );
+end;
+
+{ TBufferedTransportImpl }
+
+constructor TBufferedTransportImpl.Create( const ATransport: IStreamTransport);
+begin
+ //no inherited;
+ Create( ATransport, 1024 );
+end;
+
+constructor TBufferedTransportImpl.Create( const ATransport: IStreamTransport; ABufSize: Integer);
+begin
+ inherited Create;
+ FTransport := ATransport;
+ FBufSize := ABufSize;
+ InitBuffers;
+end;
+
+procedure TBufferedTransportImpl.Close;
+begin
+ FTransport.Close;
+ FInputBuffer := nil;
+ FOutputBuffer := nil;
+end;
+
+procedure TBufferedTransportImpl.Flush;
+begin
+ if FOutputBuffer <> nil then begin
+ FOutputBuffer.Flush;
+ end;
+end;
+
+function TBufferedTransportImpl.GetIsOpen: Boolean;
+begin
+ Result := FTransport.IsOpen;
+end;
+
+function TBufferedTransportImpl.GetUnderlyingTransport: ITransport;
+begin
+ Result := FTransport;
+end;
+
+procedure TBufferedTransportImpl.InitBuffers;
+begin
+ if FTransport.InputStream <> nil then begin
+ FInputBuffer := TBufferedStreamImpl.Create( FTransport.InputStream, FBufSize );
+ end;
+ if FTransport.OutputStream <> nil then begin
+ FOutputBuffer := TBufferedStreamImpl.Create( FTransport.OutputStream, FBufSize );
+ end;
+end;
+
+procedure TBufferedTransportImpl.Open;
+begin
+ FTransport.Open;
+ InitBuffers; // we need to get the buffers to match FTransport substreams again
+end;
+
+function TBufferedTransportImpl.Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer;
+begin
+ Result := 0;
+ if FInputBuffer <> nil then begin
+ Result := FInputBuffer.Read( pBuf,buflen, off, len );
+ end;
+end;
+
+procedure TBufferedTransportImpl.Write( const pBuf : Pointer; off, len : Integer);
+begin
+ if FOutputBuffer <> nil then begin
+ FOutputBuffer.Write( pBuf, off, len );
+ end;
+end;
+
+{ TFramedTransportImpl }
+
+{$IFDEF HAVE_CLASS_CTOR}
+class constructor TFramedTransportImpl.Create;
+begin
+ SetLength( FHeader_Dummy, FHeaderSize);
+ FillChar( FHeader_Dummy[0], Length( FHeader_Dummy) * SizeOf( Byte ), 0);
+end;
+{$ELSE}
+procedure TFramedTransportImpl_Initialize;
+begin
+ SetLength( TFramedTransportImpl.FHeader_Dummy, TFramedTransportImpl.FHeaderSize);
+ FillChar( TFramedTransportImpl.FHeader_Dummy[0],
+ Length( TFramedTransportImpl.FHeader_Dummy) * SizeOf( Byte ), 0);
+end;
+{$ENDIF}
+
+constructor TFramedTransportImpl.Create;
+begin
+ inherited Create;
+ InitWriteBuffer;
+end;
+
+procedure TFramedTransportImpl.Close;
+begin
+ FTransport.Close;
+end;
+
+constructor TFramedTransportImpl.Create( const ATrans: ITransport);
+begin
+ inherited Create;
+ InitWriteBuffer;
+ FTransport := ATrans;
+end;
+
+destructor TFramedTransportImpl.Destroy;
+begin
+ FWriteBuffer.Free;
+ FReadBuffer.Free;
+ inherited;
+end;
+
+procedure TFramedTransportImpl.Flush;
+var
+ buf : TBytes;
+ len : Integer;
+ data_len : Integer;
+
+begin
+ len := FWriteBuffer.Size;
+ SetLength( buf, len);
+ if len > 0 then begin
+ System.Move( FWriteBuffer.Memory^, buf[0], len );
+ end;
+
+ data_len := len - FHeaderSize;
+ if (data_len < 0) then begin
+ raise TTransportExceptionUnknown.Create('TFramedTransport.Flush: data_len < 0' );
+ end;
+
+ InitWriteBuffer;
+
+ buf[0] := Byte($FF and (data_len shr 24));
+ buf[1] := Byte($FF and (data_len shr 16));
+ buf[2] := Byte($FF and (data_len shr 8));
+ buf[3] := Byte($FF and data_len);
+
+ FTransport.Write( buf, 0, len );
+ FTransport.Flush;
+end;
+
+function TFramedTransportImpl.GetIsOpen: Boolean;
+begin
+ Result := FTransport.IsOpen;
+end;
+
+type
+ TAccessMemoryStream = class(TMemoryStream)
+ end;
+
+procedure TFramedTransportImpl.InitWriteBuffer;
+begin
+ FWriteBuffer.Free;
+ FWriteBuffer := TMemoryStream.Create;
+ TAccessMemoryStream(FWriteBuffer).Capacity := 1024;
+ FWriteBuffer.Write( Pointer(@FHeader_Dummy[0])^, FHeaderSize);
+end;
+
+procedure TFramedTransportImpl.Open;
+begin
+ FTransport.Open;
+end;
+
+function TFramedTransportImpl.Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer;
+var pTmp : PByte;
+begin
+ if len > (buflen-off)
+ then len := buflen-off;
+
+ pTmp := pBuf;
+ Inc( pTmp, off);
+
+ if (FReadBuffer <> nil) and (len > 0) then begin
+ result := FReadBuffer.Read( pTmp^, len);
+ if result > 0 then begin
+ Exit;
+ end;
+ end;
+
+ ReadFrame;
+ if len > 0
+ then Result := FReadBuffer.Read( pTmp^, len)
+ else Result := 0;
+end;
+
+procedure TFramedTransportImpl.ReadFrame;
+var
+ i32rd : TBytes;
+ size : Integer;
+ buff : TBytes;
+begin
+ SetLength( i32rd, FHeaderSize );
+ FTransport.ReadAll( i32rd, 0, FHeaderSize);
+ size :=
+ ((i32rd[0] and $FF) shl 24) or
+ ((i32rd[1] and $FF) shl 16) or
+ ((i32rd[2] and $FF) shl 8) or
+ (i32rd[3] and $FF);
+ SetLength( buff, size );
+ FTransport.ReadAll( buff, 0, size );
+ FReadBuffer.Free;
+ FReadBuffer := TMemoryStream.Create;
+ if Length(buff) > 0
+ then FReadBuffer.Write( Pointer(@buff[0])^, size );
+ FReadBuffer.Position := 0;
+end;
+
+procedure TFramedTransportImpl.Write( const pBuf : Pointer; off, len : Integer);
+var pTmp : PByte;
+begin
+ if len > 0 then begin
+ pTmp := pBuf;
+ Inc( pTmp, off);
+
+ FWriteBuffer.Write( pTmp^, len );
+ end;
+end;
+
+{ TFramedTransport.TFactory }
+
+function TFramedTransportImpl.TFactory.GetTransport( const ATrans: ITransport): ITransport;
+begin
+ Result := TFramedTransportImpl.Create( ATrans );
+end;
+
+{ TTcpSocketStreamImpl }
+
+procedure TTcpSocketStreamImpl.Close;
+begin
+ FTcpClient.Close;
+end;
+
+{$IFDEF OLD_SOCKETS}
+constructor TTcpSocketStreamImpl.Create( const ATcpClient: TCustomIpClient; const aTimeout : Integer);
+begin
+ inherited Create;
+ FTcpClient := ATcpClient;
+ FTimeout := aTimeout;
+end;
+{$ELSE}
+constructor TTcpSocketStreamImpl.Create( const ATcpClient: TSocket; const aTimeout : Longword);
+begin
+ inherited Create;
+ FTcpClient := ATcpClient;
+ if aTimeout = 0 then
+ FTcpClient.RecvTimeout := SLEEP_TIME
+ else
+ FTcpClient.RecvTimeout := aTimeout;
+ FTcpClient.SendTimeout := aTimeout;
+end;
+{$ENDIF}
+
+procedure TTcpSocketStreamImpl.Flush;
+begin
+
+end;
+
+function TTcpSocketStreamImpl.IsOpen: Boolean;
+begin
+{$IFDEF OLD_SOCKETS}
+ Result := FTcpClient.Active;
+{$ELSE}
+ Result := FTcpClient.IsOpen;
+{$ENDIF}
+end;
+
+procedure TTcpSocketStreamImpl.Open;
+begin
+ FTcpClient.Open;
+end;
+
+
+{$IFDEF OLD_SOCKETS}
+function TTcpSocketStreamImpl.Select( ReadReady, WriteReady, ExceptFlag: PBoolean;
+ TimeOut: Integer; var wsaError : Integer): Integer;
+var
+ ReadFds: TFDset;
+ ReadFdsptr: PFDset;
+ WriteFds: TFDset;
+ WriteFdsptr: PFDset;
+ ExceptFds: TFDset;
+ ExceptFdsptr: PFDset;
+ tv: timeval;
+ Timeptr: PTimeval;
+ socket : TSocket;
+begin
+ if not FTcpClient.Active then begin
+ wsaError := WSAEINVAL;
+ Exit( SOCKET_ERROR);
+ end;
+
+ socket := FTcpClient.Handle;
+
+ if Assigned(ReadReady) then begin
+ ReadFdsptr := @ReadFds;
+ FD_ZERO(ReadFds);
+ FD_SET(socket, ReadFds);
+ end
+ else begin
+ ReadFdsptr := nil;
+ end;
+
+ if Assigned(WriteReady) then begin
+ WriteFdsptr := @WriteFds;
+ FD_ZERO(WriteFds);
+ FD_SET(socket, WriteFds);
+ end
+ else begin
+ WriteFdsptr := nil;
+ end;
+
+ if Assigned(ExceptFlag) then begin
+ ExceptFdsptr := @ExceptFds;
+ FD_ZERO(ExceptFds);
+ FD_SET(socket, ExceptFds);
+ end
+ else begin
+ ExceptFdsptr := nil;
+ end;
+
+ if TimeOut >= 0 then begin
+ tv.tv_sec := TimeOut div 1000;
+ tv.tv_usec := 1000 * (TimeOut mod 1000);
+ Timeptr := @tv;
+ end
+ else begin
+ Timeptr := nil; // wait forever
+ end;
+
+ wsaError := 0;
+ try
+ {$IFDEF MSWINDOWS}
+ {$IFDEF OLD_UNIT_NAMES}
+ result := WinSock.select( socket + 1, ReadFdsptr, WriteFdsptr, ExceptFdsptr, Timeptr);
+ {$ELSE}
+ result := Winapi.WinSock.select( socket + 1, ReadFdsptr, WriteFdsptr, ExceptFdsptr, Timeptr);
+ {$ENDIF}
+ {$ENDIF}
+ {$IFDEF LINUX}
+ result := Libc.select( socket + 1, ReadFdsptr, WriteFdsptr, ExceptFdsptr, Timeptr);
+ {$ENDIF}
+
+ if result = SOCKET_ERROR
+ then wsaError := WSAGetLastError;
+
+ except
+ result := SOCKET_ERROR;
+ end;
+
+ if Assigned(ReadReady) then
+ ReadReady^ := FD_ISSET(socket, ReadFds);
+
+ if Assigned(WriteReady) then
+ WriteReady^ := FD_ISSET(socket, WriteFds);
+
+ if Assigned(ExceptFlag) then
+ ExceptFlag^ := FD_ISSET(socket, ExceptFds);
+end;
+{$ENDIF}
+
+{$IFDEF OLD_SOCKETS}
+function TTcpSocketStreamImpl.WaitForData( TimeOut : Integer; pBuf : Pointer;
+ DesiredBytes : Integer;
+ var wsaError, bytesReady : Integer): TWaitForData;
+var bCanRead, bError : Boolean;
+ retval : Integer;
+const
+ MSG_PEEK = {$IFDEF OLD_UNIT_NAMES} WinSock.MSG_PEEK {$ELSE} Winapi.WinSock.MSG_PEEK {$ENDIF};
+begin
+ bytesReady := 0;
+
+ // The select function returns the total number of socket handles that are ready
+ // and contained in the fd_set structures, zero if the time limit expired,
+ // or SOCKET_ERROR if an error occurred. If the return value is SOCKET_ERROR,
+ // WSAGetLastError can be used to retrieve a specific error code.
+ retval := Self.Select( @bCanRead, nil, @bError, TimeOut, wsaError);
+ if retval = SOCKET_ERROR
+ then Exit( TWaitForData.wfd_Error);
+ if (retval = 0) or not bCanRead
+ then Exit( TWaitForData.wfd_Timeout);
+
+ // recv() returns the number of bytes received, or -1 if an error occurred.
+ // The return value will be 0 when the peer has performed an orderly shutdown.
+
+ retval := recv( FTcpClient.Handle, pBuf^, DesiredBytes, MSG_PEEK);
+ if retval <= 0
+ then Exit( TWaitForData.wfd_Error);
+
+ // at least we have some data
+ bytesReady := Min( retval, DesiredBytes);
+ result := TWaitForData.wfd_HaveData;
+end;
+{$ENDIF}
+
+{$IFDEF OLD_SOCKETS}
+function TTcpSocketStreamImpl.Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer;
+// old sockets version
+var wfd : TWaitForData;
+ wsaError,
+ msecs : Integer;
+ nBytes : Integer;
+ pTmp : PByte;
+begin
+ inherited;
+
+ if FTimeout > 0
+ then msecs := FTimeout
+ else msecs := DEFAULT_THRIFT_TIMEOUT;
+
+ result := 0;
+ pTmp := pBuf;
+ Inc( pTmp, offset);
+ while count > 0 do begin
+
+ while TRUE do begin
+ wfd := WaitForData( msecs, pTmp, count, wsaError, nBytes);
+ case wfd of
+ TWaitForData.wfd_Error : Exit;
+ TWaitForData.wfd_HaveData : Break;
+ TWaitForData.wfd_Timeout : begin
+ if (FTimeout = 0)
+ then Exit
+ else begin
+ raise TTransportExceptionTimedOut.Create(SysErrorMessage(Cardinal(wsaError)));
+
+ end;
+ end;
+ else
+ ASSERT( FALSE);
+ end;
+ end;
+
+ // reduce the timeout once we got data
+ if FTimeout > 0
+ then msecs := FTimeout div 10
+ else msecs := DEFAULT_THRIFT_TIMEOUT div 10;
+ msecs := Max( msecs, 200);
+
+ ASSERT( nBytes <= count);
+ nBytes := FTcpClient.ReceiveBuf( pTmp^, nBytes);
+ Inc( pTmp, nBytes);
+ Dec( count, nBytes);
+ Inc( result, nBytes);
+ end;
+end;
+
+function TTcpSocketStreamImpl.ToArray: TBytes;
+// old sockets version
+var len : Integer;
+begin
+ len := 0;
+ if IsOpen then begin
+ len := FTcpClient.BytesReceived;
+ end;
+
+ SetLength( Result, len );
+
+ if len > 0 then begin
+ FTcpClient.ReceiveBuf( Pointer(@Result[0])^, len);
+ end;
+end;
+
+procedure TTcpSocketStreamImpl.Write( const pBuf : Pointer; offset, count: Integer);
+// old sockets version
+var bCanWrite, bError : Boolean;
+ retval, wsaError : Integer;
+ pTmp : PByte;
+begin
+ inherited;
+
+ if not FTcpClient.Active
+ then raise TTransportExceptionNotOpen.Create('not open');
+
+ // The select function returns the total number of socket handles that are ready
+ // and contained in the fd_set structures, zero if the time limit expired,
+ // or SOCKET_ERROR if an error occurred. If the return value is SOCKET_ERROR,
+ // WSAGetLastError can be used to retrieve a specific error code.
+ retval := Self.Select( nil, @bCanWrite, @bError, FTimeOut, wsaError);
+ if retval = SOCKET_ERROR
+ then raise TTransportExceptionUnknown.Create(SysErrorMessage(Cardinal(wsaError)));
+
+ if (retval = 0)
+ then raise TTransportExceptionTimedOut.Create('timed out');
+
+ if bError or not bCanWrite
+ then raise TTransportExceptionUnknown.Create('unknown error');
+
+ pTmp := pBuf;
+ Inc( pTmp, offset);
+ FTcpClient.SendBuf( pTmp^, count);
+end;
+
+{$ELSE}
+
+function TTcpSocketStreamImpl.Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer;
+// new sockets version
+var nBytes : Integer;
+ pTmp : PByte;
+begin
+ inherited;
+
+ result := 0;
+ pTmp := pBuf;
+ Inc( pTmp, offset);
+ while count > 0 do begin
+ nBytes := FTcpClient.Read( pTmp^, count);
+ if nBytes = 0 then Exit;
+ Inc( pTmp, nBytes);
+ Dec( count, nBytes);
+ Inc( result, nBytes);
+ end;
+end;
+
+function TTcpSocketStreamImpl.ToArray: TBytes;
+// new sockets version
+var len : Integer;
+begin
+ len := 0;
+ try
+ if FTcpClient.Peek then
+ repeat
+ SetLength(Result, Length(Result) + 1024);
+ len := FTcpClient.Read(Result[Length(Result) - 1024], 1024);
+ until len < 1024;
+ except
+ on TTransportException do begin { don't allow default exceptions } end;
+ else raise;
+ end;
+ if len > 0 then
+ SetLength(Result, Length(Result) - 1024 + len);
+end;
+
+procedure TTcpSocketStreamImpl.Write( const pBuf : Pointer; offset, count: Integer);
+// new sockets version
+var pTmp : PByte;
+begin
+ inherited;
+
+ if not FTcpClient.IsOpen
+ then raise TTransportExceptionNotOpen.Create('not open');
+
+ pTmp := pBuf;
+ Inc( pTmp, offset);
+ FTcpClient.Write( pTmp^, count);
+end;
+
+{$ENDIF}
+
+
+{$IF CompilerVersion < 21.0}
+initialization
+begin
+ TFramedTransportImpl_Initialize;
+end;
+{$IFEND}
+
+
+end.
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.TypeRegistry.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.TypeRegistry.pas
new file mode 100644
index 000000000..c18e97fe6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.TypeRegistry.pas
@@ -0,0 +1,95 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+unit Thrift.TypeRegistry;
+
+interface
+
+uses
+ Generics.Collections, TypInfo,
+ Thrift.Protocol;
+
+type
+ TFactoryMethod<T> = function:T;
+
+ TypeRegistry = class
+ private
+ class var FTypeInfoToFactoryLookup : TDictionary<Pointer, Pointer>;
+ public
+ class constructor Create;
+ class destructor Destroy;
+ class procedure RegisterTypeFactory<F>(const aFactoryMethod: TFactoryMethod<F>);
+ class function Construct<F>: F;
+ class function ConstructFromTypeInfo(const aTypeInfo: PTypeInfo): IBase;
+ end;
+
+implementation
+
+
+{ TypeRegistration }
+
+class constructor TypeRegistry.Create;
+begin
+ FTypeInfoToFactoryLookup := TDictionary<Pointer, Pointer>.Create;
+end;
+
+class destructor TypeRegistry.Destroy;
+begin
+ FTypeInfoToFactoryLookup.Free;
+end;
+
+class procedure TypeRegistry.RegisterTypeFactory<F>(const aFactoryMethod: TFactoryMethod<F>);
+var
+ TypeInfo : Pointer;
+begin
+ TypeInfo := System.TypeInfo(F);
+
+ if (TypeInfo <> nil) and (PTypeInfo(TypeInfo).Kind = tkInterface)
+ then FTypeInfoToFactoryLookup.AddOrSetValue(TypeInfo, @aFactoryMethod);
+end;
+
+class function TypeRegistry.Construct<F>: F;
+var
+ TypeInfo : PTypeInfo;
+ Factory : Pointer;
+begin
+ Result := default(F);
+
+ TypeInfo := System.TypeInfo(F);
+
+ if Assigned(TypeInfo) and (TypeInfo.Kind = tkInterface)
+ then begin
+ if FTypeInfoToFactoryLookup.TryGetValue(TypeInfo, Factory)
+ then Result := TFactoryMethod<F>(Factory)();
+ end;
+end;
+
+class function TypeRegistry.ConstructFromTypeInfo(const aTypeInfo: PTypeInfo): IBase;
+var
+ Factory : Pointer;
+begin
+ Result := nil;
+ if FTypeInfoToFactoryLookup.TryGetValue(aTypeInfo, Factory)
+ then Result := IBase(TFactoryMethod<IBase>(Factory)());
+end;
+
+
+
+
+end.
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Utils.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Utils.pas
new file mode 100644
index 000000000..ede265646
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Utils.pas
@@ -0,0 +1,336 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+unit Thrift.Utils;
+
+interface
+
+{$I Thrift.Defines.inc}
+
+uses
+ {$IFDEF OLD_UNIT_NAMES}
+ Classes, Windows, SysUtils, Character, SyncObjs, TypInfo, Rtti;
+ {$ELSE}
+ System.Classes, Winapi.Windows, System.SysUtils, System.Character,
+ System.SyncObjs, System.TypInfo, System.Rtti;
+ {$ENDIF}
+
+type
+ ISupportsToString = interface
+ ['{AF71C350-E0CD-4E94-B77C-0310DC8227FF}']
+ function ToString : string;
+ end;
+
+
+ IOverlappedHelper = interface
+ ['{A1832EFA-2E02-4884-8F09-F0A0277157FA}']
+ function Overlapped : TOverlapped;
+ function OverlappedPtr : POverlapped;
+ function WaitHandle : THandle;
+ function WaitFor(dwTimeout: DWORD) : DWORD;
+ end;
+
+ TOverlappedHelperImpl = class( TInterfacedObject, IOverlappedHelper)
+ strict protected
+ FOverlapped : TOverlapped;
+ FEvent : TEvent;
+
+ // IOverlappedHelper
+ function Overlapped : TOverlapped;
+ function OverlappedPtr : POverlapped;
+ function WaitHandle : THandle;
+ function WaitFor(dwTimeout: DWORD) : DWORD;
+ public
+ constructor Create;
+ destructor Destroy; override;
+ end;
+
+
+ TThriftStringBuilder = class( TStringBuilder)
+ public
+ function Append(const Value: TBytes): TStringBuilder; overload;
+ function Append(const Value: ISupportsToString): TStringBuilder; overload;
+ end;
+
+
+ Base64Utils = class sealed
+ public
+ class function Encode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer; static;
+ class function Decode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer; static;
+ end;
+
+
+ CharUtils = class sealed
+ public
+ class function IsHighSurrogate( const c : Char) : Boolean; static; inline;
+ class function IsLowSurrogate( const c : Char) : Boolean; static; inline;
+ end;
+
+ EnumUtils<T> = class sealed
+ public
+ class function ToString(const value : Integer) : string; reintroduce; static; inline;
+ end;
+
+ StringUtils<T> = class sealed
+ public
+ class function ToString(const value : T) : string; reintroduce; static; inline;
+ end;
+
+
+const
+ THRIFT_MIMETYPE = 'application/x-thrift';
+
+{$IFDEF Win64}
+function InterlockedExchangeAdd64( var Addend : Int64; Value : Int64) : Int64;
+{$ENDIF}
+
+
+implementation
+
+{ TOverlappedHelperImpl }
+
+constructor TOverlappedHelperImpl.Create;
+begin
+ inherited Create;
+ FillChar( FOverlapped, SizeOf(FOverlapped), 0);
+ FEvent := TEvent.Create( nil, TRUE, FALSE, ''); // always ManualReset, see MSDN
+ FOverlapped.hEvent := FEvent.Handle;
+end;
+
+
+
+destructor TOverlappedHelperImpl.Destroy;
+begin
+ try
+ FOverlapped.hEvent := 0;
+ FreeAndNil( FEvent);
+
+ finally
+ inherited Destroy;
+ end;
+
+end;
+
+
+function TOverlappedHelperImpl.Overlapped : TOverlapped;
+begin
+ result := FOverlapped;
+end;
+
+
+function TOverlappedHelperImpl.OverlappedPtr : POverlapped;
+begin
+ result := @FOverlapped;
+end;
+
+
+function TOverlappedHelperImpl.WaitHandle : THandle;
+begin
+ result := FOverlapped.hEvent;
+end;
+
+
+function TOverlappedHelperImpl.WaitFor( dwTimeout : DWORD) : DWORD;
+begin
+ result := WaitForSingleObject( FOverlapped.hEvent, dwTimeout);
+end;
+
+
+{ Base64Utils }
+
+class function Base64Utils.Encode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer;
+const ENCODE_TABLE : PAnsiChar = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+begin
+ ASSERT( len in [1..3]);
+ dst[dstOff] := Byte( ENCODE_TABLE[ (src[srcOff] shr 2) and $3F]);
+ case len of
+ 3 : begin
+ Inc(dstOff);
+ dst[dstOff] := Byte( ENCODE_TABLE[ ((src[srcOff] shl 4) and $30) or ((src[srcOff + 1] shr 4) and $0F)]);
+ Inc(dstOff);
+ dst[dstOff] := Byte( ENCODE_TABLE[ ((src[srcOff + 1] shl 2) and $3C) or ((src[srcOff + 2] shr 6) and $03)]);
+ Inc(dstOff);
+ dst[dstOff] := Byte( ENCODE_TABLE[ src[srcOff + 2] and $3F]);
+ result := 4;
+ end;
+
+ 2 : begin
+ Inc(dstOff);
+ dst[dstOff] := Byte( ENCODE_TABLE[ ((src[srcOff] shl 4) and $30) or ((src[srcOff + 1] shr 4) and $0F)]);
+ Inc(dstOff);
+ dst[dstOff] := Byte( ENCODE_TABLE[ (src[srcOff + 1] shl 2) and $3C]);
+ result := 3;
+ end;
+
+ 1 : begin
+ Inc(dstOff);
+ dst[dstOff] := Byte( ENCODE_TABLE[ (src[srcOff] shl 4) and $30]);
+ result := 2;
+ end;
+
+ else
+ ASSERT( FALSE);
+ result := 0; // because invalid call
+ end;
+end;
+
+
+class function Base64Utils.Decode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer;
+const DECODE_TABLE : array[0..$FF] of Integer
+ = ( -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
+ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
+ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
+ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
+ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 );
+begin
+ ASSERT( len in [1..4]);
+ result := 1;
+ dst[dstOff] := ((DECODE_TABLE[src[srcOff] and $0FF] shl 2)
+ or (DECODE_TABLE[src[srcOff + 1] and $0FF] shr 4));
+
+ if (len > 2) then begin
+ Inc( result);
+ Inc( dstOff);
+ dst[dstOff] := (((DECODE_TABLE[src[srcOff + 1] and $0FF] shl 4) and $F0)
+ or (DECODE_TABLE[src[srcOff + 2] and $0FF] shr 2));
+
+ if (len > 3) then begin
+ Inc( result);
+ Inc( dstOff);
+ dst[dstOff] := (((DECODE_TABLE[src[srcOff + 2] and $0FF] shl 6) and $C0)
+ or DECODE_TABLE[src[srcOff + 3] and $0FF]);
+ end;
+ end;
+end;
+
+
+class function CharUtils.IsHighSurrogate( const c : Char) : Boolean;
+begin
+ {$IF CompilerVersion < 25.0}
+ {$IFDEF OLD_UNIT_NAMES}
+ result := Character.IsHighSurrogate(c);
+ {$ELSE}
+ result := System.Character.IsHighSurrogate(c);
+ {$ENDIF}
+ {$ELSE}
+ result := c.IsHighSurrogate();
+ {$IFEND}
+end;
+
+
+class function CharUtils.IsLowSurrogate( const c : Char) : Boolean;
+begin
+ {$IF CompilerVersion < 25.0}
+ {$IFDEF OLD_UNIT_NAMES}
+ result := Character.IsLowSurrogate(c);
+ {$ELSE}
+ result := System.Character.IsLowSurrogate(c);
+ {$ENDIF}
+ {$ELSE}
+ result := c.IsLowSurrogate();
+ {$IFEND}
+end;
+
+
+{$IFDEF Win64}
+
+function InterlockedCompareExchange64( var Target : Int64; Exchange, Comparand : Int64) : Int64; inline;
+begin
+ {$IFDEF OLD_UNIT_NAMES}
+ result := Windows.InterlockedCompareExchange64( Target, Exchange, Comparand);
+ {$ELSE}
+ result := WinApi.Windows.InterlockedCompareExchange64( Target, Exchange, Comparand);
+ {$ENDIF}
+end;
+
+
+function InterlockedExchangeAdd64( var Addend : Int64; Value : Int64) : Int64;
+var old : Int64;
+begin
+ repeat
+ Old := Addend;
+ until (InterlockedCompareExchange64( Addend, Old + Value, Old) = Old);
+ result := Old;
+end;
+
+{$ENDIF}
+
+
+{ EnumUtils<T> }
+
+class function EnumUtils<T>.ToString(const value : Integer) : string;
+var pType : PTypeInfo;
+begin
+ pType := PTypeInfo(TypeInfo(T));
+ if Assigned(pType) and (pType^.Kind = tkEnumeration)
+ then result := GetEnumName(pType,value)
+ else result := IntToStr(Ord(value));
+end;
+
+
+{ StringUtils<T> }
+
+class function StringUtils<T>.ToString(const value : T) : string;
+type PInterface = ^IInterface;
+var pType : PTypeInfo;
+ stos : ISupportsToString;
+ pIntf : PInterface; // Workaround: Rio does not allow the direct typecast
+begin
+ pType := PTypeInfo(TypeInfo(T));
+ if Assigned(pType) then begin
+ case pType^.Kind of
+ tkInterface : begin
+ pIntf := PInterface(@value);
+ if Supports( pIntf^, ISupportsToString, stos) then begin
+ result := stos.toString;
+ Exit;
+ end;
+ end;
+ end;
+ end;
+
+ result := TValue.From<T>(value).ToString;
+end;
+
+
+{ TThriftStringBuilder }
+
+function TThriftStringBuilder.Append(const Value: TBytes): TStringBuilder;
+begin
+ Result := Append( string( RawByteString(Value)) );
+end;
+
+function TThriftStringBuilder.Append( const Value: ISupportsToString): TStringBuilder;
+begin
+ Result := Append( Value.ToString );
+end;
+
+
+end.
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.WinHTTP.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.WinHTTP.pas
new file mode 100644
index 000000000..854d7c080
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.WinHTTP.pas
@@ -0,0 +1,1273 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+unit Thrift.WinHTTP;
+
+{$I Thrift.Defines.inc}
+{$SCOPEDENUMS ON}
+
+// packing according to winhttp.h
+{$IFDEF Win64} {$ALIGN 8} {$ELSE} {$ALIGN 4} {$ENDIF}
+
+interface
+
+uses
+ Windows,
+ Classes,
+ SysUtils,
+ Math,
+ Generics.Collections;
+
+
+type
+ HINTERNET = type Pointer;
+ INTERNET_PORT = type WORD;
+ INTERNET_SCHEME = type Integer;
+ LPLPCWSTR = ^LPCWSTR;
+
+ LPURL_COMPONENTS = ^URL_COMPONENTS;
+ URL_COMPONENTS = record
+ dwStructSize : DWORD; // set to SizeOf(URL_COMPONENTS)
+ lpszScheme : LPWSTR; // scheme name
+ dwSchemeLength : DWORD;
+ nScheme : INTERNET_SCHEME; // enumerated scheme type
+ lpszHostName : LPWSTR; // host name
+ dwHostNameLength : DWORD;
+ nPort : INTERNET_PORT; // port number
+ lpszUserName : LPWSTR; // user name
+ dwUserNameLength : DWORD;
+ lpszPassword : LPWSTR; // password
+ dwPasswordLength : DWORD;
+ lpszUrlPath : LPWSTR; // URL-path
+ dwUrlPathLength : DWORD;
+ lpszExtraInfo : LPWSTR; // extra information
+ dwExtraInfoLength : DWORD;
+ end;
+
+ URL_COMPONENTSW = URL_COMPONENTS;
+ LPURL_COMPONENTSW = LPURL_COMPONENTS;
+
+
+ // When retrieving proxy data, an application must free the lpszProxy and
+ // lpszProxyBypass strings contained in this structure (if they are non-NULL)
+ // using the GlobalFree function.
+ LPWINHTTP_PROXY_INFO = ^WINHTTP_PROXY_INFO;
+ WINHTTP_PROXY_INFO = record
+ dwAccessType : DWORD; // see WINHTTP_ACCESS_* types below
+ lpszProxy : LPWSTR; // proxy server list
+ lpszProxyBypass : LPWSTR; // proxy bypass list
+ end;
+
+ LPWINHTTP_PROXY_INFOW = ^WINHTTP_PROXY_INFOW;
+ WINHTTP_PROXY_INFOW = WINHTTP_PROXY_INFO;
+
+
+ WINHTTP_AUTOPROXY_OPTIONS = record
+ dwFlags : DWORD;
+ dwAutoDetectFlags : DWORD;
+ lpszAutoConfigUrl : LPCWSTR;
+ lpvReserved : LPVOID;
+ dwReserved : DWORD;
+ fAutoLogonIfChallenged : BOOL;
+ end;
+
+
+ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG = record
+ fAutoDetect : BOOL;
+ lpszAutoConfigUrl : LPWSTR;
+ lpszProxy : LPWSTR;
+ lpszProxyBypass : LPWSTR;
+ end;
+
+
+
+
+function WinHttpCloseHandle( aHandle : HINTERNET) : BOOL; stdcall;
+
+function WinHttpOpen( const pszAgentW : LPCWSTR;
+ const dwAccessType : DWORD;
+ const pszProxyW : LPCWSTR;
+ const pszProxyBypassW : LPCWSTR;
+ const dwFlags : DWORD
+ ) : HINTERNET; stdcall;
+
+function WinHttpConnect( const hSession : HINTERNET;
+ const pswzServerName : LPCWSTR;
+ const nServerPort : INTERNET_PORT;
+ const dwReserved : DWORD
+ ) : HINTERNET; stdcall;
+
+function WinHttpOpenRequest( const hConnect : HINTERNET;
+ const pwszVerb, pwszObjectName, pwszVersion, pwszReferrer : LPCWSTR;
+ const ppwszAcceptTypes : LPLPCWSTR;
+ const dwFlags : DWORD
+ ) : HINTERNET; stdcall;
+
+function WinHttpQueryOption( const hInternet : HINTERNET;
+ const dwOption : DWORD;
+ const pBuffer : Pointer;
+ var dwBufferLength : DWORD) : BOOL; stdcall;
+
+function WinHttpSetOption( const hInternet : HINTERNET;
+ const dwOption : DWORD;
+ const pBuffer : Pointer;
+ const dwBufferLength : DWORD) : BOOL; stdcall;
+
+function WinHttpSetTimeouts( const hRequestOrSession : HINTERNET;
+ const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32
+ ) : BOOL; stdcall;
+
+function WinHttpAddRequestHeaders( const hRequest : HINTERNET;
+ const pwszHeaders : LPCWSTR;
+ const dwHeadersLengthInChars : DWORD;
+ const dwModifiers : DWORD
+ ) : BOOL; stdcall;
+
+function WinHttpGetProxyForUrl( const hSession : HINTERNET;
+ const lpcwszUrl : LPCWSTR;
+ const options : WINHTTP_AUTOPROXY_OPTIONS;
+ const info : WINHTTP_PROXY_INFO
+ ) : BOOL; stdcall;
+
+function WinHttpGetIEProxyConfigForCurrentUser( var config : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
+ ) : BOOL; stdcall;
+
+
+function WinHttpSendRequest( const hRequest : HINTERNET;
+ const lpszHeaders : LPCWSTR;
+ const dwHeadersLength : DWORD;
+ const lpOptional : Pointer;
+ const dwOptionalLength : DWORD;
+ const dwTotalLength : DWORD;
+ const pContext : Pointer
+ ) : BOOL; stdcall;
+
+function WinHttpWriteData( const hRequest : HINTERNET;
+ const pBuf : Pointer;
+ const dwBytesToWrite : DWORD;
+ out dwBytesWritten : DWORD
+ ) : BOOL; stdcall;
+
+function WinHttpReceiveResponse( const hRequest : HINTERNET; const lpReserved : Pointer) : BOOL; stdcall;
+
+function WinHttpQueryHeaders( const hRequest : HINTERNET;
+ const dwInfoLevel : DWORD;
+ const pwszName : LPCWSTR;
+ const lpBuffer : Pointer;
+ var dwBufferLength : DWORD;
+ var dwIndex : DWORD
+ ) : BOOL; stdcall;
+
+function WinHttpQueryDataAvailable( const hRequest : HINTERNET;
+ var dwNumberOfBytesAvailable : DWORD
+ ) : BOOL; stdcall;
+
+function WinHttpReadData( const hRequest : HINTERNET;
+ const lpBuffer : Pointer;
+ const dwBytesToRead : DWORD;
+ out dwBytesRead : DWORD
+ ) : BOOL; stdcall;
+
+function WinHttpCrackUrl( const pwszUrl : LPCWSTR;
+ const dwUrlLength : DWORD;
+ const dwFlags : DWORD;
+ var urlComponents : URL_COMPONENTS
+ ) : BOOL; stdcall;
+
+function WinHttpCreateUrl( const UrlComponents : URL_COMPONENTS;
+ const dwFlags : DWORD;
+ const pwszUrl : LPCWSTR;
+ var pdwUrlLength : DWORD
+ ) : BOOL; stdcall;
+
+
+const
+ // WinHttpOpen dwAccessType values
+ WINHTTP_ACCESS_TYPE_DEFAULT_PROXY = 0;
+ WINHTTP_ACCESS_TYPE_NO_PROXY = 1;
+ WINHTTP_ACCESS_TYPE_NAMED_PROXY = 3;
+
+ // flags for WinHttpOpen():
+ WINHTTP_FLAG_ASYNC = $10000000; // want async session, requires WinHttpSetStatusCallback() usage
+
+ // ports
+ INTERNET_DEFAULT_PORT = 0; // use the protocol-specific default (80 or 443)
+
+ // flags for WinHttpOpenRequest():
+ WINHTTP_FLAG_SECURE = $00800000; // use SSL if applicable (HTTPS)
+ WINHTTP_FLAG_ESCAPE_PERCENT = $00000004; // if escaping enabled, escape percent as well
+ WINHTTP_FLAG_NULL_CODEPAGE = $00000008; // assume all symbols are ASCII, use fast convertion
+ WINHTTP_FLAG_BYPASS_PROXY_CACHE = $00000100; // add "pragma: no-cache" request header
+ WINHTTP_FLAG_REFRESH = WINHTTP_FLAG_BYPASS_PROXY_CACHE;
+ WINHTTP_FLAG_ESCAPE_DISABLE = $00000040; // disable escaping
+ WINHTTP_FLAG_ESCAPE_DISABLE_QUERY = $00000080; // if escaping enabled escape path part, but do not escape query
+
+ // flags for WinHttpSendRequest():
+ WINHTTP_NO_ADDITIONAL_HEADERS = nil;
+ WINHTTP_NO_REQUEST_DATA = nil;
+
+ // WinHttpAddRequestHeaders() dwModifiers
+ WINHTTP_ADDREQ_INDEX_MASK = $0000FFFF;
+ WINHTTP_ADDREQ_FLAGS_MASK = $FFFF0000;
+
+ WINHTTP_ADDREQ_FLAG_ADD_IF_NEW = $10000000;
+ WINHTTP_ADDREQ_FLAG_ADD = $20000000;
+ WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA = $40000000;
+ WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON = $01000000;
+ WINHTTP_ADDREQ_FLAG_COALESCE = WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA;
+ WINHTTP_ADDREQ_FLAG_REPLACE = $80000000;
+
+ // URL functions
+ ICU_NO_ENCODE = $20000000; // Don't convert unsafe characters to escape sequence
+ ICU_DECODE = $10000000; // Convert %XX escape sequences to characters
+ ICU_NO_META = $08000000; // Don't convert .. etc. meta path sequences
+ ICU_ENCODE_SPACES_ONLY = $04000000; // Encode spaces only
+ ICU_BROWSER_MODE = $02000000; // Special encode/decode rules for browser
+ ICU_ENCODE_PERCENT = $00001000; // Encode any percent (ASCII25)
+
+ ICU_ESCAPE = $80000000; // (un)escape URL characters
+ ICU_ESCAPE_AUTHORITY = $00002000; // causes InternetCreateUrlA to escape chars in authority components (user, pwd, host)
+ ICU_REJECT_USERPWD = $00004000; // rejects usrls whick have username/pwd sections
+
+ INTERNET_SCHEME_HTTP = INTERNET_SCHEME(1);
+ INTERNET_SCHEME_HTTPS = INTERNET_SCHEME(2);
+
+ WINHTTP_NO_CLIENT_CERT_CONTEXT = nil;
+
+ // options manifests for WinHttp{Query|Set}Option
+ WINHTTP_OPTION_CALLBACK = 1;
+ WINHTTP_OPTION_RESOLVE_TIMEOUT = 2;
+ WINHTTP_OPTION_CONNECT_TIMEOUT = 3;
+ WINHTTP_OPTION_CONNECT_RETRIES = 4;
+ WINHTTP_OPTION_SEND_TIMEOUT = 5;
+ WINHTTP_OPTION_RECEIVE_TIMEOUT = 6;
+ WINHTTP_OPTION_RECEIVE_RESPONSE_TIMEOUT = 7;
+ WINHTTP_OPTION_HANDLE_TYPE = 9;
+ WINHTTP_OPTION_READ_BUFFER_SIZE = 12;
+ WINHTTP_OPTION_WRITE_BUFFER_SIZE = 13;
+ WINHTTP_OPTION_PARENT_HANDLE = 21;
+ WINHTTP_OPTION_EXTENDED_ERROR = 24;
+ WINHTTP_OPTION_SECURITY_FLAGS = 31;
+ WINHTTP_OPTION_SECURITY_CERTIFICATE_STRUCT = 32;
+ WINHTTP_OPTION_URL = 34;
+ WINHTTP_OPTION_SECURITY_KEY_BITNESS = 36;
+ WINHTTP_OPTION_PROXY = 38;
+ WINHTTP_OPTION_USER_AGENT = 41;
+ WINHTTP_OPTION_CONTEXT_VALUE = 45;
+ WINHTTP_OPTION_CLIENT_CERT_CONTEXT = 47;
+ WINHTTP_OPTION_REQUEST_PRIORITY = 58;
+ WINHTTP_OPTION_HTTP_VERSION = 59;
+ WINHTTP_OPTION_DISABLE_FEATURE = 63;
+ WINHTTP_OPTION_CODEPAGE = 68;
+ WINHTTP_OPTION_MAX_CONNS_PER_SERVER = 73;
+ WINHTTP_OPTION_MAX_CONNS_PER_1_0_SERVER = 74;
+ WINHTTP_OPTION_AUTOLOGON_POLICY = 77;
+ WINHTTP_OPTION_SERVER_CERT_CONTEXT = 78;
+ WINHTTP_OPTION_ENABLE_FEATURE = 79;
+ WINHTTP_OPTION_WORKER_THREAD_COUNT = 80;
+ WINHTTP_OPTION_PASSPORT_COBRANDING_TEXT = 81;
+ WINHTTP_OPTION_PASSPORT_COBRANDING_URL = 82;
+ WINHTTP_OPTION_CONFIGURE_PASSPORT_AUTH = 83;
+ WINHTTP_OPTION_SECURE_PROTOCOLS = 84;
+ WINHTTP_OPTION_ENABLETRACING = 85;
+ WINHTTP_OPTION_PASSPORT_SIGN_OUT = 86;
+ WINHTTP_OPTION_PASSPORT_RETURN_URL = 87;
+ WINHTTP_OPTION_REDIRECT_POLICY = 88;
+ WINHTTP_OPTION_MAX_HTTP_AUTOMATIC_REDIRECTS = 89;
+ WINHTTP_OPTION_MAX_HTTP_STATUS_CONTINUE = 90;
+ WINHTTP_OPTION_MAX_RESPONSE_HEADER_SIZE = 91;
+ WINHTTP_OPTION_MAX_RESPONSE_DRAIN_SIZE = 92;
+ WINHTTP_OPTION_CONNECTION_INFO = 93;
+ WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST = 94;
+ WINHTTP_OPTION_SPN = 96;
+ WINHTTP_OPTION_GLOBAL_PROXY_CREDS = 97;
+ WINHTTP_OPTION_GLOBAL_SERVER_CREDS = 98;
+ WINHTTP_OPTION_UNLOAD_NOTIFY_EVENT = 99;
+ WINHTTP_OPTION_REJECT_USERPWD_IN_URL = 100;
+ WINHTTP_OPTION_USE_GLOBAL_SERVER_CREDENTIALS = 101;
+ WINHTTP_OPTION_RECEIVE_PROXY_CONNECT_RESPONSE = 103;
+ WINHTTP_OPTION_IS_PROXY_CONNECT_RESPONSE = 104;
+ WINHTTP_OPTION_SERVER_SPN_USED = 106;
+ WINHTTP_OPTION_PROXY_SPN_USED = 107;
+ WINHTTP_OPTION_SERVER_CBT = 108;
+ // options for newer WinHTTP versions
+ WINHTTP_OPTION_DECOMPRESSION = 118;
+ //
+ WINHTTP_FIRST_OPTION = WINHTTP_OPTION_CALLBACK;
+ //WINHTTP_LAST_OPTION = WINHTTP_OPTION_SERVER_CBT;
+
+ WINHTTP_OPTION_USERNAME = $1000;
+ WINHTTP_OPTION_PASSWORD = $1001;
+ WINHTTP_OPTION_PROXY_USERNAME = $1002;
+ WINHTTP_OPTION_PROXY_PASSWORD = $1003;
+
+ // manifest value for WINHTTP_OPTION_MAX_CONNS_PER_SERVER and WINHTTP_OPTION_MAX_CONNS_PER_1_0_SERVER
+ WINHTTP_CONNS_PER_SERVER_UNLIMITED = $FFFFFFFF;
+
+ // values for WINHTTP_OPTION_AUTOLOGON_POLICY
+ WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM = 0;
+ WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW = 1;
+ WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH = 2;
+
+ WINHTTP_AUTOLOGON_SECURITY_LEVEL_DEFAULT = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM;
+
+ // values for WINHTTP_OPTION_REDIRECT_POLICY
+ WINHTTP_OPTION_REDIRECT_POLICY_NEVER = 0;
+ WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP = 1;
+ WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS = 2;
+
+ WINHTTP_OPTION_REDIRECT_POLICY_LAST = WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS;
+ WINHTTP_OPTION_REDIRECT_POLICY_DEFAULT = WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP;
+
+ WINHTTP_DISABLE_PASSPORT_AUTH = $00000000;
+ WINHTTP_ENABLE_PASSPORT_AUTH = $10000000;
+ WINHTTP_DISABLE_PASSPORT_KEYRING = $20000000;
+ WINHTTP_ENABLE_PASSPORT_KEYRING = $40000000;
+
+ // values for WINHTTP_OPTION_DISABLE_FEATURE
+ WINHTTP_DISABLE_COOKIES = $00000001;
+ WINHTTP_DISABLE_REDIRECTS = $00000002;
+ WINHTTP_DISABLE_AUTHENTICATION = $00000004;
+ WINHTTP_DISABLE_KEEP_ALIVE = $00000008;
+
+ // values for WINHTTP_OPTION_ENABLE_FEATURE
+ WINHTTP_ENABLE_SSL_REVOCATION = $00000001;
+ WINHTTP_ENABLE_SSL_REVERT_IMPERSONATION = $00000002;
+
+ // values for WINHTTP_OPTION_SPN
+ WINHTTP_DISABLE_SPN_SERVER_PORT = $00000000;
+ WINHTTP_ENABLE_SPN_SERVER_PORT = $00000001;
+ WINHTTP_OPTION_SPN_MASK = WINHTTP_ENABLE_SPN_SERVER_PORT;
+
+ // winhttp handle types
+ WINHTTP_HANDLE_TYPE_SESSION = 1;
+ WINHTTP_HANDLE_TYPE_CONNECT = 2;
+ WINHTTP_HANDLE_TYPE_REQUEST = 3;
+
+ // values for auth schemes
+ WINHTTP_AUTH_SCHEME_BASIC = $00000001;
+ WINHTTP_AUTH_SCHEME_NTLM = $00000002;
+ WINHTTP_AUTH_SCHEME_PASSPORT = $00000004;
+ WINHTTP_AUTH_SCHEME_DIGEST = $00000008;
+ WINHTTP_AUTH_SCHEME_NEGOTIATE = $00000010;
+
+ // WinHttp supported Authentication Targets
+ WINHTTP_AUTH_TARGET_SERVER = $00000000;
+ WINHTTP_AUTH_TARGET_PROXY = $00000001;
+
+ // options for WINHTTP_OPTION_DECOMPRESSION
+ WINHTTP_DECOMPRESSION_FLAG_GZIP = $00000001;
+ WINHTTP_DECOMPRESSION_FLAG_DEFLATE = $00000002;
+ WINHTTP_DECOMPRESSION_FLAG_ALL = WINHTTP_DECOMPRESSION_FLAG_GZIP
+ or WINHTTP_DECOMPRESSION_FLAG_DEFLATE;
+
+ // values for WINHTTP_OPTION_SECURITY_FLAGS
+
+ // query only
+ SECURITY_FLAG_SECURE = $00000001; // can query only
+ SECURITY_FLAG_STRENGTH_WEAK = $10000000;
+ SECURITY_FLAG_STRENGTH_MEDIUM = $40000000;
+ SECURITY_FLAG_STRENGTH_STRONG = $20000000;
+
+ // Secure connection error status flags
+ WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED = $00000001;
+ WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT = $00000002;
+ WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED = $00000004;
+ WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA = $00000008;
+ WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID = $00000010;
+ WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID = $00000020;
+ WINHTTP_CALLBACK_STATUS_FLAG_CERT_WRONG_USAGE = $00000040;
+ WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR = $80000000;
+
+ WINHTTP_FLAG_SECURE_PROTOCOL_SSL2 = $00000008;
+ WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 = $00000020;
+ WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 = $00000080;
+ WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 = $00000200;
+ WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 = $00000800;
+
+ // Note: SECURE_PROTOCOL_ALL does not include TLS1.1 and higher!
+ WINHTTP_FLAG_SECURE_PROTOCOL_ALL = WINHTTP_FLAG_SECURE_PROTOCOL_SSL2
+ or WINHTTP_FLAG_SECURE_PROTOCOL_SSL3
+ or WINHTTP_FLAG_SECURE_PROTOCOL_TLS1;
+
+ // AutoProxy
+ WINHTTP_AUTOPROXY_AUTO_DETECT = $00000001;
+ WINHTTP_AUTOPROXY_CONFIG_URL = $00000002;
+ WINHTTP_AUTOPROXY_HOST_KEEPCASE = $00000004;
+ WINHTTP_AUTOPROXY_HOST_LOWERCASE = $00000008;
+ WINHTTP_AUTOPROXY_RUN_INPROCESS = $00010000;
+ WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY = $00020000;
+
+ // Flags for dwAutoDetectFlags
+ WINHTTP_AUTO_DETECT_TYPE_DHCP = $00000001;
+ WINHTTP_AUTO_DETECT_TYPE_DNS_A = $00000002;
+
+const
+ WINHTTP_ERROR_BASE = 12000;
+ ERROR_WINHTTP_OUT_OF_HANDLES = WINHTTP_ERROR_BASE + 1;
+ ERROR_WINHTTP_TIMEOUT = WINHTTP_ERROR_BASE + 2;
+ ERROR_WINHTTP_INTERNAL_ERROR = WINHTTP_ERROR_BASE + 4;
+ ERROR_WINHTTP_INVALID_URL = WINHTTP_ERROR_BASE + 5;
+ ERROR_WINHTTP_UNRECOGNIZED_SCHEME = WINHTTP_ERROR_BASE + 6;
+ ERROR_WINHTTP_NAME_NOT_RESOLVED = WINHTTP_ERROR_BASE + 7;
+ ERROR_WINHTTP_INVALID_OPTION = WINHTTP_ERROR_BASE + 9;
+ ERROR_WINHTTP_OPTION_NOT_SETTABLE = WINHTTP_ERROR_BASE + 11;
+ ERROR_WINHTTP_SHUTDOWN = WINHTTP_ERROR_BASE + 12;
+ ERROR_WINHTTP_LOGIN_FAILURE = WINHTTP_ERROR_BASE + 15;
+ ERROR_WINHTTP_OPERATION_CANCELLED = WINHTTP_ERROR_BASE + 17;
+ ERROR_WINHTTP_INCORRECT_HANDLE_TYPE = WINHTTP_ERROR_BASE + 18;
+ ERROR_WINHTTP_INCORRECT_HANDLE_STATE = WINHTTP_ERROR_BASE + 19;
+ ERROR_WINHTTP_CANNOT_CONNECT = WINHTTP_ERROR_BASE + 29;
+ ERROR_WINHTTP_CONNECTION_ERROR = WINHTTP_ERROR_BASE + 30;
+ ERROR_WINHTTP_RESEND_REQUEST = WINHTTP_ERROR_BASE + 32;
+ ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED = WINHTTP_ERROR_BASE + 44;
+ ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN = WINHTTP_ERROR_BASE + 100;
+ ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND = WINHTTP_ERROR_BASE + 101;
+ ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND = WINHTTP_ERROR_BASE + 102;
+ ERROR_WINHTTP_CANNOT_CALL_AFTER_OPEN = WINHTTP_ERROR_BASE + 103;
+ ERROR_WINHTTP_HEADER_NOT_FOUND = WINHTTP_ERROR_BASE + 150;
+ ERROR_WINHTTP_INVALID_SERVER_RESPONSE = WINHTTP_ERROR_BASE + 152;
+ ERROR_WINHTTP_INVALID_HEADER = WINHTTP_ERROR_BASE + 153;
+ ERROR_WINHTTP_INVALID_QUERY_REQUEST = WINHTTP_ERROR_BASE + 154;
+ ERROR_WINHTTP_HEADER_ALREADY_EXISTS = WINHTTP_ERROR_BASE + 155;
+ ERROR_WINHTTP_REDIRECT_FAILED = WINHTTP_ERROR_BASE + 156;
+ ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR = WINHTTP_ERROR_BASE + 178;
+ ERROR_WINHTTP_BAD_AUTO_PROXY_SCRIPT = WINHTTP_ERROR_BASE + 166;
+ ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT = WINHTTP_ERROR_BASE + 167;
+ ERROR_WINHTTP_NOT_INITIALIZED = WINHTTP_ERROR_BASE + 172;
+ ERROR_WINHTTP_SECURE_FAILURE = WINHTTP_ERROR_BASE + 175;
+
+ // Certificate security errors. Additional information is provided
+ // via the WINHTTP_CALLBACK_STATUS_SECURE_FAILURE callback notification.
+ ERROR_WINHTTP_SECURE_CERT_DATE_INVALID = WINHTTP_ERROR_BASE + 37;
+ ERROR_WINHTTP_SECURE_CERT_CN_INVALID = WINHTTP_ERROR_BASE + 38;
+ ERROR_WINHTTP_SECURE_INVALID_CA = WINHTTP_ERROR_BASE + 45;
+ ERROR_WINHTTP_SECURE_CERT_REV_FAILED = WINHTTP_ERROR_BASE + 57;
+ ERROR_WINHTTP_SECURE_CHANNEL_ERROR = WINHTTP_ERROR_BASE + 157;
+ ERROR_WINHTTP_SECURE_INVALID_CERT = WINHTTP_ERROR_BASE + 169;
+ ERROR_WINHTTP_SECURE_CERT_REVOKED = WINHTTP_ERROR_BASE + 170;
+ ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE = WINHTTP_ERROR_BASE + 179;
+
+ ERROR_WINHTTP_AUTODETECTION_FAILED = WINHTTP_ERROR_BASE + 180;
+ ERROR_WINHTTP_HEADER_COUNT_EXCEEDED = WINHTTP_ERROR_BASE + 181;
+ ERROR_WINHTTP_HEADER_SIZE_OVERFLOW = WINHTTP_ERROR_BASE + 182;
+ ERROR_WINHTTP_CHUNKED_ENCODING_HEADER_SIZE_OVERFLOW = WINHTTP_ERROR_BASE + 183;
+ ERROR_WINHTTP_RESPONSE_DRAIN_OVERFLOW = WINHTTP_ERROR_BASE + 184;
+ ERROR_WINHTTP_CLIENT_CERT_NO_PRIVATE_KEY = WINHTTP_ERROR_BASE + 185;
+ ERROR_WINHTTP_CLIENT_CERT_NO_ACCESS_PRIVATE_KEY = WINHTTP_ERROR_BASE + 186;
+
+ WINHTTP_ERROR_LAST = WINHTTP_ERROR_BASE + 186;
+
+
+const
+ WINHTTP_THRIFT_DEFAULTS = WINHTTP_FLAG_NULL_CODEPAGE
+ or WINHTTP_FLAG_BYPASS_PROXY_CACHE
+ or WINHTTP_FLAG_ESCAPE_DISABLE;
+
+
+
+type
+ IWinHTTPSession = interface;
+ IWinHTTPConnection = interface;
+
+ IWinHTTPRequest = interface
+ ['{F65952F2-2F3B-47DC-B524-F1694E6D2AD7}']
+ function Handle : HINTERNET;
+ function Connection : IWinHTTPConnection;
+ function AddRequestHeader( const aHeader : string; const addflag : DWORD = WINHTTP_ADDREQ_FLAG_ADD) : Boolean;
+ function SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean;
+ procedure TryAutoProxy( const aUrl : string);
+ procedure EnableAutomaticContentDecompression( const aEnable : Boolean);
+ function SendRequest( const pBuf : Pointer; const dwBytes : DWORD; const dwExtra : DWORD = 0) : Boolean;
+ function WriteExtraData( const pBuf : Pointer; const dwBytes : DWORD) : DWORD;
+ function FlushAndReceiveResponse : Boolean;
+ function ReadData( const dwRead : DWORD) : TBytes; overload;
+ function ReadData( const pBuf : Pointer; const dwRead : DWORD) : DWORD; overload;
+ end;
+
+ IWinHTTPConnection = interface
+ ['{ED5BCA49-84D6-4CFE-BF18-3238B1FF2AFB}']
+ function Handle : HINTERNET;
+ function Session : IWinHTTPSession;
+ function OpenRequest( const secure : Boolean; const aVerb, aObjName, aAcceptTypes : UnicodeString) : IWinHTTPRequest;
+ end;
+
+ IWinHTTPSession = interface
+ ['{261ADCB7-5465-4407-8840-468C17F009F0}']
+ function Handle : HINTERNET;
+ function Connect( const aHostName : UnicodeString; const aPort : INTERNET_PORT = INTERNET_DEFAULT_PORT) : IWinHTTPConnection;
+ function SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean;
+ function EnableSecureProtocols( const aFlagSet : DWORD) : Boolean;
+ end;
+
+ IWinHTTPUrl = interface
+ ['{78BE977C-4171-4AF5-A250-FD2890205E63}']
+ // url parts getter
+ function GetScheme : UnicodeString;
+ function GetNumScheme : INTERNET_SCHEME;
+ function GetHostName : UnicodeString;
+ function GetPort : INTERNET_PORT;
+ function GetUserName : UnicodeString;
+ function GetPassword : UnicodeString;
+ function GetUrlPath : UnicodeString;
+ function GetExtraInfo : UnicodeString;
+
+ // url parts setter
+ procedure SetScheme( const value : UnicodeString);
+ procedure SetHostName ( const value : UnicodeString);
+ procedure SetPort( const value : INTERNET_PORT);
+ procedure SetUserName( const value : UnicodeString);
+ procedure SetPassword( const value : UnicodeString);
+ procedure SetUrlPath( const value : UnicodeString);
+ procedure SetExtraInfo( const value : UnicodeString);
+
+ // url as a whole
+ function BuildUrl : UnicodeString;
+ procedure CrackUrl( const value : UnicodeString);
+
+ // url parts
+ property Scheme : UnicodeString read GetScheme write SetScheme;
+ property NumScheme : INTERNET_SCHEME read GetNumScheme; // readonly
+ property HostName : UnicodeString read GetHostName write SetHostName;
+ property Port : INTERNET_PORT read GetPort write SetPort;
+ property UserName : UnicodeString read GetUserName write SetUserName;
+ property Password : UnicodeString read GetPassword write SetPassword;
+ property UrlPath : UnicodeString read GetUrlPath write SetUrlPath;
+ property ExtraInfo : UnicodeString read GetExtraInfo write SetExtraInfo;
+
+ // url as a whole
+ property CompleteURL : UnicodeString read BuildUrl write CrackUrl;
+ end;
+
+
+
+
+type
+ TWinHTTPHandleObjectImpl = class( TInterfacedObject)
+ strict protected
+ FHandle : HINTERNET;
+ function Handle : HINTERNET;
+ public
+ constructor Create( const aHandle : HINTERNET);
+ destructor Destroy; override;
+ end;
+
+
+ TWinHTTPSessionImpl = class( TWinHTTPHandleObjectImpl, IWinHTTPSession)
+ strict protected
+
+ // IWinHTTPSession
+ function Connect( const aHostName : UnicodeString; const aPort : INTERNET_PORT = INTERNET_DEFAULT_PORT) : IWinHTTPConnection;
+ function SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean;
+ function EnableSecureProtocols( const aFlagSet : DWORD) : Boolean;
+ public
+ constructor Create( const aAgent : UnicodeString;
+ const aAccessType : DWORD = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
+ const aProxy : UnicodeString = '';
+ const aProxyBypass : UnicodeString = '';
+ const aFlags : DWORD = 0);
+ destructor Destroy; override;
+ end;
+
+
+ TWinHTTPConnectionImpl = class( TWinHTTPHandleObjectImpl, IWinHTTPConnection)
+ strict protected
+ FSession : IWinHTTPSession;
+
+ // IWinHTTPConnection
+ function OpenRequest( const secure : Boolean; const aVerb, aObjName, aAcceptTypes : UnicodeString) : IWinHTTPRequest;
+ function Session : IWinHTTPSession;
+
+ public
+ constructor Create( const aSession : IWinHTTPSession; const aHostName : UnicodeString; const aPort : INTERNET_PORT);
+ destructor Destroy; override;
+ end;
+
+
+ TAcceptTypesArray = array of string;
+
+ TWinHTTPRequestImpl = class( TWinHTTPHandleObjectImpl, IWinHTTPRequest)
+ strict protected
+ FConnection : IWinHTTPConnection;
+
+ // IWinHTTPRequest
+ function Connection : IWinHTTPConnection;
+ function AddRequestHeader( const aHeader : string; const addflag : DWORD = WINHTTP_ADDREQ_FLAG_ADD) : Boolean;
+ function SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean;
+ procedure TryAutoProxy( const aUrl : string);
+ procedure EnableAutomaticContentDecompression( const aEnable : Boolean);
+ function SendRequest( const pBuf : Pointer; const dwBytes : DWORD; const dwExtra : DWORD = 0) : Boolean;
+ function WriteExtraData( const pBuf : Pointer; const dwBytes : DWORD) : DWORD;
+ function FlushAndReceiveResponse : Boolean;
+ function ReadData( const dwRead : DWORD) : TBytes; overload;
+ function ReadData( const pBuf : Pointer; const dwRead : DWORD) : DWORD; overload;
+
+ public
+ constructor Create( const aConnection : IWinHTTPConnection;
+ const aVerb, aObjName : UnicodeString;
+ const aVersion : UnicodeString = '';
+ const aReferrer : UnicodeString = '';
+ const aAcceptTypes : UnicodeString = '*/*';
+ const aFlags : DWORD = WINHTTP_THRIFT_DEFAULTS
+ );
+
+ destructor Destroy; override;
+ end;
+
+
+ TWinHTTPUrlImpl = class( TInterfacedObject, IWinHTTPUrl)
+ strict private
+ FScheme : UnicodeString;
+ FNumScheme : INTERNET_SCHEME;
+ FHostName : UnicodeString;
+ FPort : INTERNET_PORT;
+ FUserName : UnicodeString;
+ FPassword : UnicodeString;
+ FUrlPath : UnicodeString;
+ FExtraInfo : UnicodeString;
+
+ strict protected
+ // url parts getter
+ function GetScheme : UnicodeString;
+ function GetNumScheme : INTERNET_SCHEME;
+ function GetHostName : UnicodeString;
+ function GetPort : INTERNET_PORT;
+ function GetUserName : UnicodeString;
+ function GetPassword : UnicodeString;
+ function GetUrlPath : UnicodeString;
+ function GetExtraInfo : UnicodeString;
+
+ // url parts setter
+ procedure SetScheme( const value : UnicodeString);
+ procedure SetHostName ( const value : UnicodeString);
+ procedure SetPort( const value : INTERNET_PORT);
+ procedure SetUserName( const value : UnicodeString);
+ procedure SetPassword( const value : UnicodeString);
+ procedure SetUrlPath( const value : UnicodeString);
+ procedure SetExtraInfo( const value : UnicodeString);
+
+ // url as a whole
+ function BuildUrl : UnicodeString;
+ procedure CrackUrl( const value : UnicodeString);
+
+ public
+ constructor Create( const aUri : UnicodeString);
+ destructor Destroy; override;
+ end;
+
+
+ WINHTTP_PROXY_INFO_Helper = record helper for WINHTTP_PROXY_INFO
+ procedure Initialize;
+ procedure FreeAllocatedResources;
+ end;
+
+
+ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG_Helper = record helper for WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
+ procedure Initialize;
+ procedure FreeAllocatedResources;
+ end;
+
+
+ EWinHTTPException = class(Exception);
+
+{ helper functions }
+
+function WinHttpSysErrorMessage( const error : Cardinal): string;
+procedure RaiseLastWinHttpError;
+
+
+implementation
+
+const WINHTTP_DLL = 'WinHTTP.dll';
+
+function WinHttpCloseHandle; stdcall; external WINHTTP_DLL;
+function WinHttpOpen; stdcall; external WINHTTP_DLL;
+function WinHttpConnect; stdcall; external WINHTTP_DLL;
+function WinHttpOpenRequest; stdcall; external WINHTTP_DLL;
+function WinHttpSendRequest; stdcall; external WINHTTP_DLL;
+function WinHttpSetTimeouts; stdcall; external WINHTTP_DLL;
+function WinHttpQueryOption; stdcall; external WINHTTP_DLL;
+function WinHttpSetOption; stdcall; external WINHTTP_DLL;
+function WinHttpAddRequestHeaders; stdcall; external WINHTTP_DLL;
+function WinHttpGetProxyForUrl; stdcall; external WINHTTP_DLL;
+function WinHttpGetIEProxyConfigForCurrentUser; stdcall; external WINHTTP_DLL;
+function WinHttpWriteData; stdcall; external WINHTTP_DLL;
+function WinHttpReceiveResponse; stdcall; external WINHTTP_DLL;
+function WinHttpQueryHeaders; stdcall; external WINHTTP_DLL;
+function WinHttpQueryDataAvailable; stdcall; external WINHTTP_DLL;
+function WinHttpReadData; stdcall; external WINHTTP_DLL;
+function WinHttpCrackUrl; stdcall; external WINHTTP_DLL;
+function WinHttpCreateUrl; stdcall; external WINHTTP_DLL;
+
+
+{ helper functions }
+
+function WinHttpSysErrorMessage( const error : Cardinal): string;
+const FLAGS = FORMAT_MESSAGE_ALLOCATE_BUFFER
+ or FORMAT_MESSAGE_IGNORE_INSERTS
+ or FORMAT_MESSAGE_FROM_SYSTEM
+ or FORMAT_MESSAGE_FROM_HMODULE;
+var pBuffer : PChar;
+ nChars : Cardinal;
+begin
+ if (error < WINHTTP_ERROR_BASE)
+ or (error > WINHTTP_ERROR_LAST)
+ then Exit( SysUtils.SysErrorMessage( error));
+
+ pBuffer := nil;
+ try
+ nChars := FormatMessage( FLAGS,
+ Pointer( GetModuleHandle( WINHTTP_DLL)),
+ error,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
+ @pBuffer, 0,
+ nil);
+ SetString( result, pBuffer, nChars);
+ finally
+ LocalFree( NativeUInt( pBuffer));
+ end;
+end;
+
+
+procedure RaiseLastWinHttpError;
+var error : Cardinal;
+ sMsg : string;
+begin
+ error := Cardinal( GetLastError);
+ if error <> NOERROR then begin
+ sMSg := IntToStr(Integer(error))+' '+WinHttpSysErrorMessage(error);
+ raise EWinHTTPException.Create( sMsg);
+ end;
+end;
+
+
+
+{ misc. record helper }
+
+
+procedure GlobalFreeAndNil( var p : LPWSTR);
+begin
+ if p <> nil then begin
+ GlobalFree( HGLOBAL( p));
+ p := nil;
+ end;
+end;
+
+
+procedure WINHTTP_PROXY_INFO_Helper.Initialize;
+begin
+ FillChar( Self, SizeOf(Self), 0);
+end;
+
+
+procedure WINHTTP_PROXY_INFO_Helper.FreeAllocatedResources;
+// The caller must free the lpszProxy and lpszProxyBypass strings
+// if they are non-NULL. Use GlobalFree to free the strings.
+begin
+ GlobalFreeAndNil( lpszProxy);
+ GlobalFreeAndNil( lpszProxyBypass);
+ Initialize;
+end;
+
+
+procedure WINHTTP_CURRENT_USER_IE_PROXY_CONFIG_Helper.Initialize;
+begin
+ FillChar( Self, SizeOf(Self), 0);
+end;
+
+
+procedure WINHTTP_CURRENT_USER_IE_PROXY_CONFIG_Helper.FreeAllocatedResources;
+// The caller must free the lpszProxy, lpszProxyBypass and lpszAutoConfigUrl strings
+// if they are non-NULL. Use GlobalFree to free the strings.
+begin
+ GlobalFreeAndNil( lpszProxy);
+ GlobalFreeAndNil( lpszProxyBypass);
+ GlobalFreeAndNil( lpszAutoConfigUrl);
+ Initialize;
+end;
+
+
+{ TWinHTTPHandleObjectImpl }
+
+constructor TWinHTTPHandleObjectImpl.Create( const aHandle : HINTERNET);
+begin
+ inherited Create;
+ FHandle := aHandle;
+
+ if FHandle = nil
+ then raise EWinHTTPException.Create('Invalid handle');
+end;
+
+
+destructor TWinHTTPHandleObjectImpl.Destroy;
+begin
+ try
+ if Assigned(FHandle) then begin
+ WinHttpCloseHandle(FHandle);
+ FHandle := nil;
+ end;
+
+ finally
+ inherited Destroy;
+ end;
+end;
+
+
+function TWinHTTPHandleObjectImpl.Handle : HINTERNET;
+begin
+ result := FHandle;
+end;
+
+
+{ TWinHTTPSessionImpl }
+
+
+constructor TWinHTTPSessionImpl.Create( const aAgent : UnicodeString; const aAccessType : DWORD;
+ const aProxy, aProxyBypass : UnicodeString; const aFlags : DWORD);
+var handle : HINTERNET;
+begin
+ handle := WinHttpOpen( PWideChar(aAgent), aAccessType,
+ PWideChar(Pointer(aProxy)), // may be nil
+ PWideChar(Pointer(aProxyBypass)), // may be nil
+ aFlags);
+ if handle = nil then RaiseLastWinHttpError;
+ inherited Create( handle);
+end;
+
+
+destructor TWinHTTPSessionImpl.Destroy;
+begin
+ inherited Destroy;
+ // add code here
+end;
+
+
+function TWinHTTPSessionImpl.Connect( const aHostName : UnicodeString; const aPort : INTERNET_PORT) : IWinHTTPConnection;
+begin
+ result := TWinHTTPConnectionImpl.Create( Self, aHostName, aPort);
+end;
+
+
+function TWinHTTPSessionImpl.SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean;
+begin
+ result := WinHttpSetTimeouts( FHandle, aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout);
+end;
+
+
+function TWinHTTPSessionImpl.EnableSecureProtocols( const aFlagSet : DWORD) : Boolean;
+var dwSize : DWORD;
+begin
+ dwSize := SizeOf(aFlagSet);
+ result := WinHttpSetOption( Handle, WINHTTP_OPTION_SECURE_PROTOCOLS, @aFlagset, dwSize);
+end;
+
+
+{ TWinHTTPConnectionImpl }
+
+constructor TWinHTTPConnectionImpl.Create( const aSession : IWinHTTPSession; const aHostName : UnicodeString; const aPort : INTERNET_PORT);
+var handle : HINTERNET;
+begin
+ FSession := aSession;
+ handle := WinHttpConnect( FSession.Handle, PWideChar(aHostName), aPort, 0);
+ if handle = nil then RaiseLastWinHttpError;
+ inherited Create( handle);
+end;
+
+
+destructor TWinHTTPConnectionImpl.Destroy;
+begin
+ inherited Destroy;
+ FSession := nil;
+end;
+
+
+function TWinHTTPConnectionImpl.Session : IWinHTTPSession;
+begin
+ result := FSession;
+end;
+
+
+function TWinHTTPConnectionImpl.OpenRequest( const secure : Boolean; const aVerb, aObjName, aAcceptTypes : UnicodeString) : IWinHTTPRequest;
+var dwFlags : DWORD;
+begin
+ dwFlags := WINHTTP_THRIFT_DEFAULTS;
+ if secure
+ then dwFlags := dwFlags or WINHTTP_FLAG_SECURE
+ else dwFlags := dwFlags and not WINHTTP_FLAG_SECURE;
+
+ result := TWinHTTPRequestImpl.Create( Self, aVerb, aObjName, '', '', aAcceptTypes, dwFlags);
+end;
+
+
+{ TWinHTTPRequestImpl }
+
+constructor TWinHTTPRequestImpl.Create( const aConnection : IWinHTTPConnection;
+ const aVerb, aObjName, aVersion, aReferrer : UnicodeString;
+ const aAcceptTypes : UnicodeString;
+ const aFlags : DWORD
+ );
+var handle : HINTERNET;
+ accept : array[0..1] of PWideChar;
+begin
+ FConnection := aConnection;
+
+ accept[0] := PWideChar(aAcceptTypes);
+ accept[1] := nil;
+
+ handle := WinHttpOpenRequest( FConnection.Handle,
+ PWideChar(UpperCase(aVerb)),
+ PWideChar(aObjName),
+ PWideChar(aVersion),
+ PWideChar(aReferrer),
+ @accept,
+ aFlags);
+ if handle = nil then RaiseLastWinHttpError;
+ inherited Create( handle);
+end;
+
+
+destructor TWinHTTPRequestImpl.Destroy;
+begin
+ inherited Destroy;
+ FConnection := nil;
+end;
+
+
+function TWinHTTPRequestImpl.Connection : IWinHTTPConnection;
+begin
+ result := FConnection;
+end;
+
+
+function TWinHTTPRequestImpl.SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean;
+begin
+ result := WinHttpSetTimeouts( FHandle, aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout);
+end;
+
+
+function TWinHTTPRequestImpl.AddRequestHeader( const aHeader : string; const addflag : DWORD) : Boolean;
+begin
+ result := WinHttpAddRequestHeaders( FHandle, PWideChar(aHeader), DWORD(-1), addflag);
+end;
+
+
+procedure TWinHTTPRequestImpl.TryAutoProxy( const aUrl : string);
+// From MSDN:
+// AutoProxy support is not fully integrated into the HTTP stack in WinHTTP.
+// Before sending a request, the application must call WinHttpGetProxyForUrl
+// to obtain the name of a proxy server and then call WinHttpSetOption using
+// WINHTTP_OPTION_PROXY to set the proxy configuration on the WinHTTP request
+// handle created by WinHttpOpenRequest.
+// See https://docs.microsoft.com/en-us/windows/desktop/winhttp/winhttp-autoproxy-api
+var
+ options : WINHTTP_AUTOPROXY_OPTIONS;
+ proxy : WINHTTP_PROXY_INFO;
+ ieProxy : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG;
+ dwSize : DWORD;
+begin
+ // try AutoProxy via PAC first
+ proxy.Initialize;
+ try
+ FillChar( options, SizeOf(options), 0);
+ options.dwFlags := WINHTTP_AUTOPROXY_AUTO_DETECT;
+ options.dwAutoDetectFlags := WINHTTP_AUTO_DETECT_TYPE_DHCP or WINHTTP_AUTO_DETECT_TYPE_DNS_A;
+ options.fAutoLogonIfChallenged := TRUE;
+ if WinHttpGetProxyForUrl( FConnection.Session.Handle, PChar(aUrl), options, proxy) then begin
+ dwSize := SizeOf(proxy);
+ WinHttpSetOption( Handle, WINHTTP_OPTION_PROXY, @proxy, dwSize);
+ Exit;
+ end;
+
+ finally
+ proxy.FreeAllocatedResources;
+ end;
+
+ // Use IE settings as a fallback, useful in client (i.e. non-server) environments
+ ieProxy.Initialize;
+ try
+ if WinHttpGetIEProxyConfigForCurrentUser( ieProxy)
+ then begin
+
+ // lpszAutoConfigUrl = "Use automatic proxy configuration"
+ if ieProxy.lpszAutoConfigUrl <> nil then begin
+ options.lpszAutoConfigUrl := ieProxy.lpszAutoConfigUrl;
+ options.dwFlags := options.dwFlags or WINHTTP_AUTOPROXY_CONFIG_URL;
+
+ proxy.Initialize;
+ try
+ if WinHttpGetProxyForUrl( FConnection.Session.Handle, PChar(aUrl), options, proxy) then begin
+ dwSize := SizeOf(proxy);
+ WinHttpSetOption( Handle, WINHTTP_OPTION_PROXY, @proxy, dwSize);
+ Exit;
+ end;
+ finally
+ proxy.FreeAllocatedResources;
+ end;
+ end;
+
+ // lpszProxy = "use a proxy server"
+ if ieProxy.lpszProxy <> nil then begin
+ proxy.Initialize;
+ try
+ proxy.dwAccessType := WINHTTP_ACCESS_TYPE_NAMED_PROXY;
+ proxy.lpszProxy := ieProxy.lpszProxy;
+ proxy.lpszProxyBypass := ieProxy.lpszProxyBypass;
+ dwSize := SizeOf(proxy);
+ WinHttpSetOption( Handle, WINHTTP_OPTION_PROXY, @proxy, dwSize);
+ Exit;
+ finally
+ proxy.Initialize; // not FreeAllocatedResources, we only hold pointer copies!
+ end;
+ end;
+
+ end;
+
+ finally
+ ieProxy.FreeAllocatedResources;
+ end;
+end;
+
+
+procedure TWinHTTPRequestImpl.EnableAutomaticContentDecompression( const aEnable : Boolean);
+// Enable automatic gzip,deflate decompression on systems that support this option
+// From the docs: WinHTTP will automatically set an appropriate Accept-Encoding header,
+// overriding any value supplied by the caller -> we don't have to do this
+// Available on Win 8.1 or higher
+var value : DWORD;
+begin
+ if aEnable
+ then value := WINHTTP_DECOMPRESSION_FLAG_ALL
+ else value := 0;
+
+ // ignore returned value, the option is not supported with older WinHTTP versions
+ WinHttpSetOption( Handle, WINHTTP_OPTION_DECOMPRESSION, @value, SizeOf(DWORD));
+end;
+
+
+function TWinHTTPRequestImpl.SendRequest( const pBuf : Pointer; const dwBytes, dwExtra : DWORD) : Boolean;
+begin
+ result := WinHttpSendRequest( FHandle,
+ WINHTTP_NO_ADDITIONAL_HEADERS, 0,
+ pBuf, dwBytes, // number of bytes in pBuf
+ dwBytes + dwExtra, // becomes the Content-Length
+ nil); // context for async operations
+end;
+
+
+function TWinHTTPRequestImpl.WriteExtraData( const pBuf : Pointer; const dwBytes : DWORD) : DWORD;
+begin
+ if not WinHttpWriteData( FHandle, pBuf, dwBytes, result)
+ then result := 0;
+end;
+
+
+function TWinHTTPRequestImpl.FlushAndReceiveResponse : Boolean;
+begin
+ result := WinHttpReceiveResponse( FHandle, nil);
+end;
+
+
+function TWinHTTPRequestImpl.ReadData( const dwRead : DWORD) : TBytes;
+var dwAvailable, dwReceived : DWORD;
+begin
+ if WinHttpQueryDataAvailable( FHandle, dwAvailable)
+ then dwAvailable := Min( dwRead, dwAvailable)
+ else dwAvailable := 0;
+
+ SetLength( result, dwAvailable);
+ if dwAvailable = 0 then Exit;
+
+ if WinHttpReadData( FHandle, @result[0], Length(result), dwReceived)
+ then SetLength( result, dwReceived)
+ else SetLength( result, 0);
+end;
+
+
+function TWinHTTPRequestImpl.ReadData( const pBuf : Pointer; const dwRead : DWORD) : DWORD;
+var dwAvailable : DWORD;
+begin
+ if WinHttpQueryDataAvailable( FHandle, dwAvailable)
+ then dwAvailable := Min( dwRead, dwAvailable)
+ else dwAvailable := 0;
+
+ if (dwAvailable = 0)
+ or not WinHttpReadData( FHandle, pBuf, dwAvailable, result)
+ then result := 0;
+end;
+
+
+{ TWinHTTPUrlImpl }
+
+constructor TWinHTTPUrlImpl.Create(const aUri: UnicodeString);
+begin
+ inherited Create;
+ CrackUrl( aUri)
+end;
+
+
+destructor TWinHTTPUrlImpl.Destroy;
+begin
+ inherited Destroy;
+end;
+
+
+procedure TWinHTTPUrlImpl.CrackURL( const value : UnicodeString);
+const FLAGS = 0; // no special operations, leave components as-is
+var components : URL_COMPONENTS;
+begin
+ FillChar(components, SizeOf(components), 0);
+ components.dwStructSize := SizeOf(components);
+
+ if value <> '' then begin
+ { For the WinHttpCrackUrl function, [...] if the pointer member is NULL but the
+ length member is not zero, both the pointer and length members are returned. }
+ components.dwSchemeLength := DWORD(-1);
+ components.dwHostNameLength := DWORD(-1);
+ components.dwUserNameLength := DWORD(-1);
+ components.dwPasswordLength := DWORD(-1);
+ components.dwUrlPathLength := DWORD(-1);
+ components.dwExtraInfoLength := DWORD(-1);
+
+ WinHttpCrackUrl( PWideChar(value), Length(value), FLAGS, components);
+ end;
+
+ FNumScheme := components.nScheme;
+ FPort := components.nPort;
+ SetString( FScheme, components.lpszScheme, components.dwSchemeLength);
+ SetString( FHostName, components.lpszHostName, components.dwHostNameLength);
+ SetString( FUserName, components.lpszUserName, components.dwUserNameLength);
+ SetString( FPassword, components.lpszPassword, components.dwPasswordLength);
+ SetString( FUrlPath, components.lpszUrlPath, components.dwUrlPathLength);
+ SetString( FExtraInfo, components.lpszExtraInfo, components.dwExtraInfoLength);
+end;
+
+
+function TWinHTTPUrlImpl.BuildUrl : UnicodeString;
+const FLAGS = 0; // no special operations, leave components as-is
+var components : URL_COMPONENTS;
+ dwChars : DWORD;
+begin
+ FillChar(components, SizeOf(components), 0);
+ components.dwStructSize := SizeOf(components);
+ components.lpszScheme := PWideChar(FScheme);
+ components.dwSchemeLength := Length(FScheme);
+ components.lpszHostName := PWideChar(FHostName);
+ components.dwHostNameLength := Length(FHostName);
+ components.nPort := FPort;
+ components.lpszUserName := PWideChar(FUserName);
+ components.dwUserNameLength := Length(FUserName);
+ components.lpszPassword := PWideChar(FPassword);
+ components.dwPasswordLength := Length(FPassword);
+ components.lpszUrlPath := PWideChar(FUrlPath);
+ components.dwUrlPathLength := Length(FUrlPath);
+ components.lpszExtraInfo := PWideChar(FExtraInfo);
+ components.dwExtraInfoLength := Length(FExtraInfo);
+
+ WinHttpCreateUrl( components, FLAGS, nil, dwChars);
+ if dwChars = 0
+ then result := ''
+ else begin
+ SetLength( result, dwChars + 1);
+ WinHttpCreateUrl( components, FLAGS, @result[1], dwChars);
+ SetLength( result, dwChars); // cut off terminating #0
+ end;
+end;
+
+
+function TWinHTTPUrlImpl.GetExtraInfo: UnicodeString;
+begin
+ result := FExtraInfo;
+end;
+
+function TWinHTTPUrlImpl.GetHostName: UnicodeString;
+begin
+ result := FHostName;
+end;
+
+function TWinHTTPUrlImpl.GetNumScheme: INTERNET_SCHEME;
+begin
+ result := FNumScheme;
+end;
+
+function TWinHTTPUrlImpl.GetPassword: UnicodeString;
+begin
+ result := FPassword;
+end;
+
+function TWinHTTPUrlImpl.GetPort: INTERNET_PORT;
+begin
+ result := FPort;
+end;
+
+function TWinHTTPUrlImpl.GetScheme: UnicodeString;
+begin
+ result := FScheme;
+end;
+
+function TWinHTTPUrlImpl.GetUrlPath: UnicodeString;
+begin
+ result := FUrlPath;
+end;
+
+function TWinHTTPUrlImpl.GetUserName: UnicodeString;
+begin
+ result := FUserName;
+end;
+
+procedure TWinHTTPUrlImpl.SetExtraInfo(const value: UnicodeString);
+begin
+ FExtraInfo := value;
+end;
+
+procedure TWinHTTPUrlImpl.SetHostName(const value: UnicodeString);
+begin
+ FHostName := value;
+end;
+
+procedure TWinHTTPUrlImpl.SetPassword(const value: UnicodeString);
+begin
+ FPassword := value;
+end;
+
+procedure TWinHTTPUrlImpl.SetPort(const value: INTERNET_PORT);
+begin
+ FPort := value;
+end;
+
+procedure TWinHTTPUrlImpl.SetScheme(const value: UnicodeString);
+begin
+ FScheme := value;
+end;
+
+procedure TWinHTTPUrlImpl.SetUrlPath(const value: UnicodeString);
+begin
+ FUrlPath := value;
+end;
+
+procedure TWinHTTPUrlImpl.SetUserName(const value: UnicodeString);
+begin
+ FUserName := value;
+end;
+
+
+initialization
+ OutputDebugString( PChar( SysErrorMessage( 12002)));
+
+end.
+
+
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.pas
new file mode 100644
index 000000000..2ee83441b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.pas
@@ -0,0 +1,239 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+unit Thrift;
+
+interface
+
+uses
+ SysUtils,
+ Thrift.Exception,
+ Thrift.Protocol;
+
+const
+ Version = '0.13.0';
+
+type
+ TException = Thrift.Exception.TException; // compatibility alias
+
+ TApplicationExceptionSpecializedClass = class of TApplicationExceptionSpecialized;
+
+ TApplicationException = class( TException)
+ public
+ type
+{$SCOPEDENUMS ON}
+ TExceptionType = (
+ Unknown,
+ UnknownMethod,
+ InvalidMessageType,
+ WrongMethodName,
+ BadSequenceID,
+ MissingResult,
+ InternalError,
+ ProtocolError,
+ InvalidTransform,
+ InvalidProtocol,
+ UnsupportedClientType
+ );
+{$SCOPEDENUMS OFF}
+ private
+ function GetType: TExceptionType;
+ protected
+ constructor HiddenCreate(const Msg: string);
+ public
+ // purposefully hide inherited constructor
+ class function Create(const Msg: string): TApplicationException; overload; deprecated 'Use specialized TApplicationException types (or regenerate from IDL)';
+ class function Create: TApplicationException; overload; deprecated 'Use specialized TApplicationException types (or regenerate from IDL)';
+ class function Create( AType: TExceptionType): TApplicationException; overload; deprecated 'Use specialized TApplicationException types (or regenerate from IDL)';
+ class function Create( AType: TExceptionType; const msg: string): TApplicationException; overload; deprecated 'Use specialized TApplicationException types (or regenerate from IDL)';
+
+ class function GetSpecializedExceptionType(AType: TExceptionType): TApplicationExceptionSpecializedClass;
+
+ class function Read( const iprot: IProtocol): TApplicationException;
+ procedure Write( const oprot: IProtocol );
+ end;
+
+ // Needed to remove deprecation warning
+ TApplicationExceptionSpecialized = class abstract (TApplicationException)
+ public
+ constructor Create(const Msg: string);
+ end;
+
+ TApplicationExceptionUnknown = class (TApplicationExceptionSpecialized);
+ TApplicationExceptionUnknownMethod = class (TApplicationExceptionSpecialized);
+ TApplicationExceptionInvalidMessageType = class (TApplicationExceptionSpecialized);
+ TApplicationExceptionWrongMethodName = class (TApplicationExceptionSpecialized);
+ TApplicationExceptionBadSequenceID = class (TApplicationExceptionSpecialized);
+ TApplicationExceptionMissingResult = class (TApplicationExceptionSpecialized);
+ TApplicationExceptionInternalError = class (TApplicationExceptionSpecialized);
+ TApplicationExceptionProtocolError = class (TApplicationExceptionSpecialized);
+ TApplicationExceptionInvalidTransform = class (TApplicationExceptionSpecialized);
+ TApplicationExceptionInvalidProtocol = class (TApplicationExceptionSpecialized);
+ TApplicationExceptionUnsupportedClientType = class (TApplicationExceptionSpecialized);
+
+
+implementation
+
+{ TApplicationException }
+
+function TApplicationException.GetType: TExceptionType;
+begin
+ if Self is TApplicationExceptionUnknownMethod then Result := TExceptionType.UnknownMethod
+ else if Self is TApplicationExceptionInvalidMessageType then Result := TExceptionType.InvalidMessageType
+ else if Self is TApplicationExceptionWrongMethodName then Result := TExceptionType.WrongMethodName
+ else if Self is TApplicationExceptionBadSequenceID then Result := TExceptionType.BadSequenceID
+ else if Self is TApplicationExceptionMissingResult then Result := TExceptionType.MissingResult
+ else if Self is TApplicationExceptionInternalError then Result := TExceptionType.InternalError
+ else if Self is TApplicationExceptionProtocolError then Result := TExceptionType.ProtocolError
+ else if Self is TApplicationExceptionInvalidTransform then Result := TExceptionType.InvalidTransform
+ else if Self is TApplicationExceptionInvalidProtocol then Result := TExceptionType.InvalidProtocol
+ else if Self is TApplicationExceptionUnsupportedClientType then Result := TExceptionType.UnsupportedClientType
+ else Result := TExceptionType.Unknown;
+end;
+
+constructor TApplicationException.HiddenCreate(const Msg: string);
+begin
+ inherited Create(Msg);
+end;
+
+class function TApplicationException.Create(const Msg: string): TApplicationException;
+begin
+ Result := TApplicationExceptionUnknown.Create(Msg);
+end;
+
+class function TApplicationException.Create: TApplicationException;
+begin
+ Result := TApplicationExceptionUnknown.Create('');
+end;
+
+class function TApplicationException.Create( AType: TExceptionType): TApplicationException;
+begin
+{$WARN SYMBOL_DEPRECATED OFF}
+ Result := Create(AType, '');
+{$WARN SYMBOL_DEPRECATED DEFAULT}
+end;
+
+class function TApplicationException.Create( AType: TExceptionType; const msg: string): TApplicationException;
+begin
+ Result := GetSpecializedExceptionType(AType).Create(msg);
+end;
+
+class function TApplicationException.GetSpecializedExceptionType(AType: TExceptionType): TApplicationExceptionSpecializedClass;
+begin
+ case AType of
+ TExceptionType.UnknownMethod: Result := TApplicationExceptionUnknownMethod;
+ TExceptionType.InvalidMessageType: Result := TApplicationExceptionInvalidMessageType;
+ TExceptionType.WrongMethodName: Result := TApplicationExceptionWrongMethodName;
+ TExceptionType.BadSequenceID: Result := TApplicationExceptionBadSequenceID;
+ TExceptionType.MissingResult: Result := TApplicationExceptionMissingResult;
+ TExceptionType.InternalError: Result := TApplicationExceptionInternalError;
+ TExceptionType.ProtocolError: Result := TApplicationExceptionProtocolError;
+ TExceptionType.InvalidTransform: Result := TApplicationExceptionInvalidTransform;
+ TExceptionType.InvalidProtocol: Result := TApplicationExceptionInvalidProtocol;
+ TExceptionType.UnsupportedClientType: Result := TApplicationExceptionUnsupportedClientType;
+ else
+ Result := TApplicationExceptionUnknown;
+ end;
+end;
+
+class function TApplicationException.Read( const iprot: IProtocol): TApplicationException;
+var
+ field : TThriftField;
+ msg : string;
+ typ : TExceptionType;
+ struc : TThriftStruct;
+begin
+ msg := '';
+ typ := TExceptionType.Unknown;
+ struc := iprot.ReadStructBegin;
+ while ( True ) do
+ begin
+ field := iprot.ReadFieldBegin;
+ if ( field.Type_ = TType.Stop) then
+ begin
+ Break;
+ end;
+
+ case field.Id of
+ 1 : begin
+ if ( field.Type_ = TType.String_) then
+ begin
+ msg := iprot.ReadString;
+ end else
+ begin
+ TProtocolUtil.Skip( iprot, field.Type_ );
+ end;
+ end;
+
+ 2 : begin
+ if ( field.Type_ = TType.I32) then
+ begin
+ typ := TExceptionType( iprot.ReadI32 );
+ end else
+ begin
+ TProtocolUtil.Skip( iprot, field.Type_ );
+ end;
+ end else
+ begin
+ TProtocolUtil.Skip( iprot, field.Type_);
+ end;
+ end;
+ iprot.ReadFieldEnd;
+ end;
+ iprot.ReadStructEnd;
+ Result := GetSpecializedExceptionType(typ).Create(msg);
+end;
+
+procedure TApplicationException.Write( const oprot: IProtocol);
+var
+ struc : TThriftStruct;
+ field : TThriftField;
+begin
+ Init(struc, 'TApplicationException');
+ Init(field);
+
+ oprot.WriteStructBegin( struc );
+ if Message <> '' then
+ begin
+ field.Name := 'message';
+ field.Type_ := TType.String_;
+ field.Id := 1;
+ oprot.WriteFieldBegin( field );
+ oprot.WriteString( Message );
+ oprot.WriteFieldEnd;
+ end;
+
+ field.Name := 'type';
+ field.Type_ := TType.I32;
+ field.Id := 2;
+ oprot.WriteFieldBegin(field);
+ oprot.WriteI32(Integer(GetType));
+ oprot.WriteFieldEnd();
+ oprot.WriteFieldStop();
+ oprot.WriteStructEnd();
+end;
+
+{ TApplicationExceptionSpecialized }
+
+constructor TApplicationExceptionSpecialized.Create(const Msg: string);
+begin
+ inherited HiddenCreate(Msg);
+end;
+
+end.
diff --git a/src/jaegertracing/thrift/lib/delphi/test/ConsoleHelper.pas b/src/jaegertracing/thrift/lib/delphi/test/ConsoleHelper.pas
new file mode 100644
index 000000000..0a8ddcf10
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/ConsoleHelper.pas
@@ -0,0 +1,132 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+unit ConsoleHelper;
+
+interface
+
+uses Classes;
+
+type
+ TThriftConsole = class
+ public
+ procedure Write( const S: string); virtual;
+ procedure WriteLine( const S: string); virtual;
+ end;
+
+ TGUIConsole = class( TThriftConsole )
+ private
+ FLineBreak : Boolean;
+ FMemo : TStrings;
+
+ procedure InternalWrite( const S: string; bWriteLine: Boolean);
+ public
+ procedure Write( const S: string); override;
+ procedure WriteLine( const S: string); override;
+ constructor Create( AMemo: TStrings);
+ end;
+
+function Console: TThriftConsole;
+procedure ChangeConsole( AConsole: TThriftConsole );
+procedure RestoreConsoleToDefault;
+
+implementation
+
+var
+ FDefaultConsole : TThriftConsole;
+ FConsole : TThriftConsole;
+
+function Console: TThriftConsole;
+begin
+ Result := FConsole;
+end;
+
+{ TThriftConsole }
+
+procedure TThriftConsole.Write(const S: string);
+begin
+ System.Write( S );
+end;
+
+procedure TThriftConsole.WriteLine(const S: string);
+begin
+ System.Writeln( S );
+end;
+
+procedure ChangeConsole( AConsole: TThriftConsole );
+begin
+ FConsole := AConsole;
+end;
+
+procedure RestoreConsoleToDefault;
+begin
+ FConsole := FDefaultConsole;
+end;
+
+{ TGUIConsole }
+
+constructor TGUIConsole.Create( AMemo: TStrings);
+begin
+ inherited Create;
+ FMemo := AMemo;
+ FLineBreak := True;
+end;
+
+procedure TGUIConsole.InternalWrite(const S: string; bWriteLine: Boolean);
+var
+ idx : Integer;
+begin
+ if FLineBreak then
+ begin
+ FMemo.Add( S );
+ end else
+ begin
+ idx := FMemo.Count - 1;
+ if idx < 0 then
+ FMemo.Add( S )
+ else
+ FMemo[idx] := FMemo[idx] + S;
+ end;
+ FLineBreak := bWriteLine;
+end;
+
+procedure TGUIConsole.Write(const S: string);
+begin
+ InternalWrite( S, False);
+end;
+
+procedure TGUIConsole.WriteLine(const S: string);
+begin
+ InternalWrite( S, True);
+end;
+
+initialization
+begin
+ FDefaultConsole := TThriftConsole.Create;
+ FConsole := FDefaultConsole;
+end;
+
+finalization
+begin
+ FDefaultConsole.Free;
+end;
+
+end.
+
+
diff --git a/src/jaegertracing/thrift/lib/delphi/test/Performance/DataFactory.pas b/src/jaegertracing/thrift/lib/delphi/test/Performance/DataFactory.pas
new file mode 100644
index 000000000..e131822a3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/Performance/DataFactory.pas
@@ -0,0 +1,176 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+unit DataFactory;
+
+interface
+
+uses
+ SysUtils,
+ Thrift.Collections,
+ Thrift.Test;
+
+type
+ TestDataFactory = class
+ strict protected
+ class function CreateSetField(const count : Integer) : IHashSet< IInsanity>; static;
+ class function CreateInsanity(const count : Integer) : IInsanity; static;
+ class function CreateBytesArray(const count : Integer) : TBytes; static;
+ class function CreateXtructs(const count : Integer) : IThriftList< IXtruct>; static;
+ class function CreateXtruct(const count : Integer) : IXtruct; static;
+ class function CreateListField(const count : Integer) : IThriftList< IThriftDictionary< IHashSet< Integer>, IThriftDictionary< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>>>; static;
+ class function CreateUserMap(const count : Integer) : IThriftDictionary< TNumberz, Int64>; static;
+ class function CreateListFieldData(const count : Integer) : IThriftDictionary< IHashSet< Integer>, IThriftDictionary< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>>; static;
+ class function CreateIntHashSet(const count : Integer) : IHashSet< Integer>; static;
+ class function CreateListFieldDataDict(const count : Integer) : IThriftDictionary< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>; static;
+ class function CreateListFieldDataDictValue(const count : Integer) : IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>; static;
+ class function CreateListFieldDataDictValueList(const count : Integer) : IThriftList< IThriftDictionary< IInsanity, string>>; static;
+ class function CreateListFieldDataDictValueListDict(const count : Integer) : IThriftDictionary< IInsanity, string>; static;
+ public
+ class function CreateCrazyNesting(const count : Integer = 10) : ICrazyNesting; static;
+ end;
+
+implementation
+
+
+class function TestDataFactory.CreateCrazyNesting(const count : Integer = 10) : ICrazyNesting;
+begin
+ if (count <= 0)
+ then Exit(nil);
+
+ result := TCrazyNestingImpl.Create;
+ result.Binary_field := CreateBytesArray(count);
+ result.List_field := CreateListField(count);
+ result.Set_field := CreateSetField(count);
+ result.String_field := Format('data level %d', [count]);
+end;
+
+class function TestDataFactory.CreateSetField(const count : Integer) : IHashSet< IInsanity>;
+var i : Integer;
+begin
+ result := THashSetImpl< IInsanity>.Create;
+ for i := 0 to count-1 do begin
+ result.Add(CreateInsanity(count));
+ end;
+end;
+
+class function TestDataFactory.CreateInsanity(const count : Integer) : IInsanity;
+begin
+ result := TInsanityImpl.Create;
+ result.UserMap := CreateUserMap(count);
+ result.Xtructs := CreateXtructs(count);
+end;
+
+class function TestDataFactory.CreateXtructs(const count : Integer) : IThriftList< IXtruct>;
+var i : Integer;
+begin
+ result := TThriftListImpl< IXtruct>.Create;
+ for i := 0 to count-1 do begin
+ result.Add(CreateXtruct(count));
+ end;
+end;
+
+class function TestDataFactory.CreateXtruct(const count : Integer) : IXtruct;
+begin
+ result := TXtructImpl.Create;
+ result.Byte_thing := SmallInt(count mod 128);
+ result.I32_thing := count;
+ result.I64_thing := count;
+ result.String_thing := Format('data level %d', [count]);
+end;
+
+class function TestDataFactory.CreateUserMap(const count : Integer) : IThriftDictionary< TNumberz, Int64>;
+begin
+ result := TThriftDictionaryImpl< TNumberz, Int64>.Create;
+ result.Add(TNumberz.ONE, count);
+ result.Add(TNumberz.TWO, count);
+ result.Add(TNumberz.THREE, count);
+ result.Add(TNumberz.FIVE, count);
+ result.Add(TNumberz.SIX, count);
+ result.Add(TNumberz.EIGHT, count);
+end;
+
+class function TestDataFactory.CreateListField(const count : Integer) : IThriftList< IThriftDictionary< IHashSet< Integer>, IThriftDictionary< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>>>;
+var i : Integer;
+begin
+ result := TThriftListImpl< IThriftDictionary< IHashSet< Integer>, IThriftDictionary< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>>>.Create;
+ for i := 0 to count-1 do begin
+ result.Add(CreateListFieldData(count));
+ end;
+end;
+
+class function TestDataFactory.CreateListFieldData(const count : Integer) : IThriftDictionary< IHashSet< Integer>, IThriftDictionary< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>>;
+var i : Integer;
+begin
+ result := TThriftDictionaryImpl< IHashSet< Integer>, IThriftDictionary< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>>.Create;
+ for i := 0 to count-1 do begin
+ result.Add( CreateIntHashSet(count), CreateListFieldDataDict(count));
+ end;
+end;
+
+class function TestDataFactory.CreateIntHashSet(const count : Integer) : IHashSet< Integer>;
+var i : Integer;
+begin
+ result := THashSetImpl< Integer>.Create;
+ for i := 0 to count-1 do begin
+ result.Add(i);
+ end;
+end;
+
+class function TestDataFactory.CreateListFieldDataDict(const count : Integer) : IThriftDictionary< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>;
+var i : Integer;
+begin
+ result := TThriftDictionaryImpl< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>.Create;
+ for i := 0 to count-1 do begin
+ result.Add(i, CreateListFieldDataDictValue(count));
+ end;
+end;
+
+class function TestDataFactory.CreateListFieldDataDictValue(const count : Integer) : IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>;
+var i : Integer;
+begin
+ result := THashSetImpl< IThriftList< IThriftDictionary< IInsanity, string>>>.Create;
+ for i := 0 to count-1 do begin
+ result.Add( CreateListFieldDataDictValueList(count));
+ end;
+end;
+
+class function TestDataFactory.CreateListFieldDataDictValueList(const count : Integer) : IThriftList< IThriftDictionary< IInsanity, string>>;
+var i : Integer;
+begin
+ result := TThriftListImpl< IThriftDictionary< IInsanity, string>>.Create;
+ for i := 0 to count-1 do begin
+ result.Add(CreateListFieldDataDictValueListDict(count));
+ end;
+end;
+
+class function TestDataFactory.CreateListFieldDataDictValueListDict(const count : Integer) : IThriftDictionary< IInsanity, string>;
+begin
+ result := TThriftDictionaryImpl< IInsanity, string>.Create;
+ result.Add(CreateInsanity(count), Format('data level %d', [count]));
+end;
+
+class function TestDataFactory.CreateBytesArray(const count : Integer) : TBytes;
+var i : Integer;
+begin
+ SetLength( result, count);
+ for i := 0 to count-1 do begin
+ result[i] := i mod $FF;
+ end;
+end;
+
+end.
+
diff --git a/src/jaegertracing/thrift/lib/delphi/test/Performance/PerfTests.pas b/src/jaegertracing/thrift/lib/delphi/test/Performance/PerfTests.pas
new file mode 100644
index 000000000..2c820b1f3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/Performance/PerfTests.pas
@@ -0,0 +1,173 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+unit PerfTests;
+
+interface
+
+uses
+ Windows, Classes, SysUtils,
+ Thrift.Collections,
+ Thrift.Test,
+ Thrift.Protocol,
+ Thrift.Protocol.JSON,
+ Thrift.Protocol.Compact,
+ Thrift.Transport,
+ Thrift.Stream,
+ ConsoleHelper,
+ TestConstants,
+ DataFactory;
+
+type
+ TPerformanceTests = class
+ strict private
+ Testdata : ICrazyNesting;
+ MemBuffer : TMemoryStream;
+ Transport : ITransport;
+
+ procedure ProtocolPeformanceTest;
+ procedure RunTest( const ptyp : TKnownProtocol; const layered : TLayeredTransport);
+ function GenericProtocolFactory(const ptyp : TKnownProtocol; const layered : TLayeredTransport; const forWrite : Boolean) : IProtocol;
+ function GetProtocolTransportName(const ptyp : TKnownProtocol; const layered : TLayeredTransport) : string;
+ public
+ class function Execute : Integer;
+ end;
+
+
+implementation
+
+
+// not available in all versions, so make sure we have this one imported
+function IsDebuggerPresent: BOOL; stdcall; external KERNEL32 name 'IsDebuggerPresent';
+
+
+class function TPerformanceTests.Execute : Integer;
+var instance : TPerformanceTests;
+begin
+ instance := TPerformanceTests.Create;
+ instance.ProtocolPeformanceTest;
+
+ // debug only
+ if IsDebuggerPresent then begin
+ Console.Write('Hit ENTER ...');
+ ReadLn;
+ end;
+
+ result := 0;
+end;
+
+
+procedure TPerformanceTests.ProtocolPeformanceTest;
+var layered : TLayeredTransport;
+begin
+ Console.WriteLine('Setting up for ProtocolPeformanceTest ...');
+ Testdata := TestDataFactory.CreateCrazyNesting();
+
+ for layered := Low(TLayeredTransport) to High(TLayeredTransport) do begin
+ RunTest( TKnownProtocol.prot_Binary, layered);
+ RunTest( TKnownProtocol.prot_Compact, layered);
+ RunTest( TKnownProtocol.prot_JSON, layered);
+ end;
+end;
+
+
+procedure TPerformanceTests.RunTest( const ptyp : TKnownProtocol; const layered : TLayeredTransport);
+var freq, start, stop : Int64;
+ proto : IProtocol;
+ restored : ICrazyNesting;
+begin
+ QueryPerformanceFrequency( freq);
+
+ proto := GenericProtocolFactory( ptyp, layered, TRUE);
+ QueryPerformanceCounter( start);
+ Testdata.Write(proto);
+ Transport.Flush;
+ QueryPerformanceCounter( stop);
+ Console.WriteLine( Format('RunTest(%s): write = %d msec', [
+ GetProtocolTransportName(ptyp,layered),
+ Round(1000.0*(stop-start)/freq)
+ ]));
+
+ restored := TCrazyNestingImpl.Create;
+ proto := GenericProtocolFactory( ptyp, layered, FALSE);
+ QueryPerformanceCounter( start);
+ restored.Read(proto);
+ QueryPerformanceCounter( stop);
+ Console.WriteLine( Format('RunTest(%s): read = %d msec', [
+ GetProtocolTransportName(ptyp,layered),
+ Round(1000.0*(stop-start)/freq)
+ ]));
+end;
+
+
+function TPerformanceTests.GenericProtocolFactory(const ptyp : TKnownProtocol; const layered : TLayeredTransport; const forWrite : Boolean) : IProtocol;
+var newBuf : TMemoryStream;
+ stream : IThriftStream;
+ trans : IStreamTransport;
+const COPY_ENTIRE_STREAM = 0;
+begin
+ // read happens after write here, so let's take over the written bytes
+ newBuf := TMemoryStream.Create;
+ if not forWrite then newBuf.CopyFrom( MemBuffer, COPY_ENTIRE_STREAM);
+ MemBuffer := newBuf;
+ MemBuffer.Position := 0;
+
+ // layered transports anyone?
+ stream := TThriftStreamAdapterDelphi.Create( newBuf, TRUE);
+ if forWrite
+ then trans := TStreamTransportImpl.Create( nil, stream)
+ else trans := TStreamTransportImpl.Create( stream, nil);
+ case layered of
+ trns_Framed : Transport := TFramedTransportImpl.Create( trans);
+ trns_Buffered : Transport := TBufferedTransportImpl.Create( trans);
+ else
+ Transport := trans;
+ end;
+
+ if not Transport.IsOpen
+ then Transport.Open;
+
+ case ptyp of
+ prot_Binary : result := TBinaryProtocolImpl.Create(trans);
+ prot_Compact : result := TCompactProtocolImpl.Create(trans);
+ prot_JSON : result := TJSONProtocolImpl.Create(trans);
+ else
+ ASSERT(FALSE);
+ end;
+end;
+
+
+function TPerformanceTests.GetProtocolTransportName(const ptyp : TKnownProtocol; const layered : TLayeredTransport) : string;
+begin
+ case layered of
+ trns_Framed : result := ' + framed';
+ trns_Buffered : result := ' + buffered';
+ else
+ result := '';
+ end;
+
+ case ptyp of
+ prot_Binary : result := 'binary' + result;
+ prot_Compact : result := 'compact' + result;
+ prot_JSON : result := 'JSON' + result;
+ else
+ ASSERT(FALSE);
+ end;
+end;
+
+
+end.
+
diff --git a/src/jaegertracing/thrift/lib/delphi/test/TestClient.pas b/src/jaegertracing/thrift/lib/delphi/test/TestClient.pas
new file mode 100644
index 000000000..e59c32720
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/TestClient.pas
@@ -0,0 +1,1506 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+unit TestClient;
+
+{$I ../src/Thrift.Defines.inc}
+
+{.$DEFINE StressTest} // activate to stress-test the server with frequent connects/disconnects
+{.$DEFINE PerfTest} // activate the performance test
+{$DEFINE Exceptions} // activate the exceptions test (or disable while debugging)
+
+{$if CompilerVersion >= 28}
+{$DEFINE SupportsAsync}
+{$ifend}
+
+{$WARN SYMBOL_PLATFORM OFF} // Win32Check
+
+interface
+
+uses
+ Windows, SysUtils, Classes, Math, ComObj, ActiveX,
+ {$IFDEF SupportsAsync} System.Threading, {$ENDIF}
+ DateUtils,
+ Generics.Collections,
+ TestConstants,
+ ConsoleHelper,
+ PerfTests,
+ Thrift,
+ Thrift.Protocol.Compact,
+ Thrift.Protocol.JSON,
+ Thrift.Protocol,
+ Thrift.Transport.Pipes,
+ Thrift.Transport.WinHTTP,
+ Thrift.Transport.MsxmlHTTP,
+ Thrift.Transport,
+ Thrift.Stream,
+ Thrift.Test,
+ Thrift.WinHTTP,
+ Thrift.Utils,
+ Thrift.Collections;
+
+type
+ TThreadConsole = class
+ private
+ FThread : TThread;
+ public
+ procedure Write( const S : string);
+ procedure WriteLine( const S : string);
+ constructor Create( AThread: TThread);
+ end;
+
+ TTestSetup = record
+ protType : TKnownProtocol;
+ endpoint : TEndpointTransport;
+ layered : TLayeredTransports;
+ useSSL : Boolean; // include where appropriate (TLayeredTransport?)
+ host : string;
+ port : Integer;
+ sPipeName : string;
+ hAnonRead, hAnonWrite : THandle;
+ end;
+
+ TClientThread = class( TThread )
+ private type
+ TTestGroup = (
+ test_Unknown,
+ test_BaseTypes,
+ test_Structs,
+ test_Containers,
+ test_Exceptions
+ // new values here
+ );
+ TTestGroups = set of TTestGroup;
+
+ TTestSize = (
+ Empty, // Edge case: the zero-length empty binary
+ Normal, // Fairly small array of usual size (256 bytes)
+ ByteArrayTest, // THRIFT-4454 Large writes/reads may cause range check errors in debug mode
+ PipeWriteLimit, // THRIFT-4372 Pipe write operations across a network are limited to 65,535 bytes per write.
+ TwentyMB // that's quite a bit of data
+ );
+
+ private
+ FSetup : TTestSetup;
+ FTransport : ITransport;
+ FProtocol : IProtocol;
+ FNumIteration : Integer;
+ FConsole : TThreadConsole;
+
+ // test reporting, will be refactored out into separate class later
+ FTestGroup : string;
+ FCurrentTest : TTestGroup;
+ FSuccesses : Integer;
+ FErrors : TStringList;
+ FFailed : TTestGroups;
+ FExecuted : TTestGroups;
+ procedure StartTestGroup( const aGroup : string; const aTest : TTestGroup);
+ procedure Expect( aTestResult : Boolean; const aTestInfo : string);
+ procedure ReportResults;
+ function CalculateExitCode : Byte;
+
+ procedure ClientTest;
+ {$IFDEF SupportsAsync}
+ procedure ClientAsyncTest;
+ {$ENDIF}
+
+ procedure InitializeProtocolTransportStack;
+ procedure ShutdownProtocolTransportStack;
+ function InitializeHttpTransport( const aTimeoutSetting : Integer) : IHTTPClient;
+
+ procedure JSONProtocolReadWriteTest;
+ function PrepareBinaryData( aRandomDist : Boolean; aSize : TTestSize) : TBytes;
+ {$IFDEF StressTest}
+ procedure StressTest(const client : TThriftTest.Iface);
+ {$ENDIF}
+ {$IFDEF Win64}
+ procedure UseInterlockedExchangeAdd64;
+ {$ENDIF}
+ protected
+ procedure Execute; override;
+ public
+ constructor Create( const aSetup : TTestSetup; const aNumIteration: Integer);
+ destructor Destroy; override;
+ end;
+
+ TTestClient = class
+ private
+ class var
+ FNumIteration : Integer;
+ FNumThread : Integer;
+
+ class procedure PrintCmdLineHelp;
+ class procedure InvalidArgs;
+ public
+ class function Execute( const args: array of string) : Byte;
+ end;
+
+
+implementation
+
+const
+ EXITCODE_SUCCESS = $00; // no errors bits set
+ //
+ EXITCODE_FAILBIT_BASETYPES = $01;
+ EXITCODE_FAILBIT_STRUCTS = $02;
+ EXITCODE_FAILBIT_CONTAINERS = $04;
+ EXITCODE_FAILBIT_EXCEPTIONS = $08;
+
+ MAP_FAILURES_TO_EXITCODE_BITS : array[TClientThread.TTestGroup] of Byte = (
+ EXITCODE_SUCCESS, // no bits here
+ EXITCODE_FAILBIT_BASETYPES,
+ EXITCODE_FAILBIT_STRUCTS,
+ EXITCODE_FAILBIT_CONTAINERS,
+ EXITCODE_FAILBIT_EXCEPTIONS
+ );
+
+
+
+function BoolToString( b : Boolean) : string;
+// overrides global BoolToString()
+begin
+ if b
+ then result := 'true'
+ else result := 'false';
+end;
+
+// not available in all versions, so make sure we have this one imported
+function IsDebuggerPresent: BOOL; stdcall; external KERNEL32 name 'IsDebuggerPresent';
+
+{ TTestClient }
+
+class procedure TTestClient.PrintCmdLineHelp;
+const HELPTEXT = ' [options]'#10
+ + #10
+ + 'Allowed options:'#10
+ + ' -h [ --help ] produce help message'#10
+ + ' --host arg (=localhost) Host to connect'#10
+ + ' --port arg (=9090) Port number to connect'#10
+ + ' --domain-socket arg Domain Socket (e.g. /tmp/ThriftTest.thrift),'#10
+ + ' instead of host and port'#10
+ + ' --named-pipe arg Windows Named Pipe (e.g. MyThriftPipe)'#10
+ + ' --anon-pipes hRead hWrite Windows Anonymous Pipes pair (handles)'#10
+ + ' --transport arg (=sockets) Transport: buffered, framed, http, winhttp'#10
+ + ' --protocol arg (=binary) Protocol: binary, compact, json'#10
+ + ' --ssl Encrypted Transport using SSL'#10
+ + ' -n [ --testloops ] arg (=1) Number of Tests'#10
+ + ' -t [ --threads ] arg (=1) Number of Test threads'#10
+ + ' --performance Run the built-in performance test (no other arguments)'#10
+ ;
+begin
+ Writeln( ChangeFileExt(ExtractFileName(ParamStr(0)),'') + HELPTEXT);
+end;
+
+class procedure TTestClient.InvalidArgs;
+begin
+ Console.WriteLine( 'Invalid args.');
+ Console.WriteLine( ChangeFileExt(ExtractFileName(ParamStr(0)),'') + ' -h for more information');
+ Abort;
+end;
+
+class function TTestClient.Execute(const args: array of string) : Byte;
+var
+ i : Integer;
+ threadExitCode : Byte;
+ s : string;
+ threads : array of TThread;
+ dtStart : TDateTime;
+ test : Integer;
+ thread : TThread;
+ setup : TTestSetup;
+begin
+ // init record
+ with setup do begin
+ protType := prot_Binary;
+ endpoint := trns_Sockets;
+ layered := [];
+ useSSL := FALSE;
+ host := 'localhost';
+ port := 9090;
+ sPipeName := '';
+ hAnonRead := INVALID_HANDLE_VALUE;
+ hAnonWrite := INVALID_HANDLE_VALUE;
+ end;
+
+ try
+ i := 0;
+ while ( i < Length(args) ) do begin
+ s := args[i];
+ Inc( i);
+
+ if (s = '-h') or (s = '--help') then begin
+ // -h [ --help ] produce help message
+ PrintCmdLineHelp;
+ result := $FF; // all tests failed
+ Exit;
+ end
+ else if s = '--host' then begin
+ // --host arg (=localhost) Host to connect
+ setup.host := args[i];
+ Inc( i);
+ end
+ else if s = '--port' then begin
+ // --port arg (=9090) Port number to connect
+ s := args[i];
+ Inc( i);
+ setup.port := StrToIntDef(s,0);
+ if setup.port <= 0 then InvalidArgs;
+ end
+ else if s = '--domain-socket' then begin
+ // --domain-socket arg Domain Socket (e.g. /tmp/ThriftTest.thrift), instead of host and port
+ raise Exception.Create('domain-socket not supported');
+ end
+ else if s = '--named-pipe' then begin
+ // --named-pipe arg Windows Named Pipe (e.g. MyThriftPipe)
+ setup.endpoint := trns_NamedPipes;
+ setup.sPipeName := args[i];
+ Inc( i);
+ Console.WriteLine('Using named pipe ('+setup.sPipeName+')');
+ end
+ else if s = '--anon-pipes' then begin
+ // --anon-pipes hRead hWrite Windows Anonymous Pipes pair (handles)
+ setup.endpoint := trns_AnonPipes;
+ setup.hAnonRead := THandle( StrToIntDef( args[i], Integer(INVALID_HANDLE_VALUE)));
+ Inc( i);
+ setup.hAnonWrite := THandle( StrToIntDef( args[i], Integer(INVALID_HANDLE_VALUE)));
+ Inc( i);
+ Console.WriteLine('Using anonymous pipes ('+IntToStr(Integer(setup.hAnonRead))+' and '+IntToStr(Integer(setup.hAnonWrite))+')');
+ end
+ else if s = '--transport' then begin
+ // --transport arg (=sockets) Transport: buffered, framed, http, winhttp, evhttp
+ s := args[i];
+ Inc( i);
+
+ if s = 'buffered' then Include( setup.layered, trns_Buffered)
+ else if s = 'framed' then Include( setup.layered, trns_Framed)
+ else if s = 'http' then setup.endpoint := trns_MsXmlHttp
+ else if s = 'winhttp' then setup.endpoint := trns_WinHttp
+ else if s = 'evhttp' then setup.endpoint := trns_EvHttp // recognized, but not supported
+ else InvalidArgs;
+ end
+ else if s = '--protocol' then begin
+ // --protocol arg (=binary) Protocol: binary, compact, json
+ s := args[i];
+ Inc( i);
+
+ if s = 'binary' then setup.protType := prot_Binary
+ else if s = 'compact' then setup.protType := prot_Compact
+ else if s = 'json' then setup.protType := prot_JSON
+ else InvalidArgs;
+ end
+ else if s = '--ssl' then begin
+ // --ssl Encrypted Transport using SSL
+ setup.useSSL := TRUE;
+
+ end
+ else if (s = '-n') or (s = '--testloops') then begin
+ // -n [ --testloops ] arg (=1) Number of Tests
+ FNumIteration := StrToIntDef( args[i], 0);
+ Inc( i);
+ if FNumIteration <= 0
+ then InvalidArgs;
+
+ end
+ else if (s = '-t') or (s = '--threads') then begin
+ // -t [ --threads ] arg (=1) Number of Test threads
+ FNumThread := StrToIntDef( args[i], 0);
+ Inc( i);
+ if FNumThread <= 0
+ then InvalidArgs;
+ end
+ else if (s = '--performance') then begin
+ result := TPerformanceTests.Execute;
+ Exit;
+ end
+ else begin
+ InvalidArgs;
+ end;
+ end;
+
+
+ // In the anonymous pipes mode the client is launched by the test server
+ // -> behave nicely and allow for attaching a debugger to this process
+ if (setup.endpoint = trns_AnonPipes) and not IsDebuggerPresent
+ then MessageBox( 0, 'Attach Debugger and/or click OK to continue.',
+ 'Thrift TestClient (Delphi)',
+ MB_OK or MB_ICONEXCLAMATION);
+
+ SetLength( threads, FNumThread);
+ dtStart := Now;
+
+ // layered transports are not really meant to be stacked upon each other
+ if (trns_Framed in setup.layered) then begin
+ Console.WriteLine('Using framed transport');
+ end
+ else if (trns_Buffered in setup.layered) then begin
+ Console.WriteLine('Using buffered transport');
+ end;
+
+ Console.WriteLine(THRIFT_PROTOCOLS[setup.protType]+' protocol');
+
+ for test := 0 to FNumThread - 1 do begin
+ thread := TClientThread.Create( setup, FNumIteration);
+ threads[test] := thread;
+ thread.Start;
+ end;
+
+ result := 0;
+ for test := 0 to FNumThread - 1 do begin
+ threadExitCode := threads[test].WaitFor;
+ result := result or threadExitCode;
+ threads[test].Free;
+ threads[test] := nil;
+ end;
+
+ Console.Write('Total time: ' + IntToStr( MilliSecondsBetween(Now, dtStart)));
+
+ except
+ on E: EAbort do raise;
+ on E: Exception do begin
+ Console.WriteLine( E.Message + #10 + E.StackTrace);
+ raise;
+ end;
+ end;
+
+ Console.WriteLine('');
+ Console.WriteLine('done!');
+end;
+
+{ TClientThread }
+
+procedure TClientThread.ClientTest;
+var
+ client : TThriftTest.Iface;
+ s : string;
+ i8 : ShortInt;
+ i32 : Integer;
+ i64 : Int64;
+ binOut,binIn : TBytes;
+ dub : Double;
+ o : IXtruct;
+ o2 : IXtruct2;
+ i : IXtruct;
+ i2 : IXtruct2;
+ mapout : IThriftDictionary<Integer,Integer>;
+ mapin : IThriftDictionary<Integer,Integer>;
+ strmapout : IThriftDictionary<string,string>;
+ strmapin : IThriftDictionary<string,string>;
+ j : Integer;
+ first : Boolean;
+ key : Integer;
+ strkey : string;
+ listout : IThriftList<Integer>;
+ listin : IThriftList<Integer>;
+ setout : IHashSet<Integer>;
+ setin : IHashSet<Integer>;
+ ret : TNumberz;
+ uid : Int64;
+ mm : IThriftDictionary<Integer, IThriftDictionary<Integer, Integer>>;
+ pos : IThriftDictionary<Integer, Integer>;
+ neg : IThriftDictionary<Integer, Integer>;
+ m2 : IThriftDictionary<Integer, Integer>;
+ k2 : Integer;
+ insane : IInsanity;
+ truck : IXtruct;
+ whoa : IThriftDictionary<Int64, IThriftDictionary<TNumberz, IInsanity>>;
+ key64 : Int64;
+ val : IThriftDictionary<TNumberz, IInsanity>;
+ k2_2 : TNumberz;
+ k3 : TNumberz;
+ v2 : IInsanity;
+ userMap : IThriftDictionary<TNumberz, Int64>;
+ xtructs : IThriftList<IXtruct>;
+ x : IXtruct;
+ arg0 : ShortInt;
+ arg1 : Integer;
+ arg2 : Int64;
+ arg3 : IThriftDictionary<SmallInt, string>;
+ arg4 : TNumberz;
+ arg5 : Int64;
+ {$IFDEF PerfTest}
+ StartTick : Cardinal;
+ k : Integer;
+ {$ENDIF}
+ hello, goodbye : IXtruct;
+ crazy : IInsanity;
+ looney : IInsanity;
+ first_map : IThriftDictionary<TNumberz, IInsanity>;
+ second_map : IThriftDictionary<TNumberz, IInsanity>;
+ pair : TPair<TNumberz, TUserId>;
+ testsize : TTestSize;
+begin
+ client := TThriftTest.TClient.Create( FProtocol);
+ FTransport.Open;
+
+ {$IFDEF StressTest}
+ StressTest( client);
+ {$ENDIF StressTest}
+
+ {$IFDEF Exceptions}
+ // in-depth exception test
+ // (1) do we get an exception at all?
+ // (2) do we get the right exception?
+ // (3) does the exception contain the expected data?
+ StartTestGroup( 'testException', test_Exceptions);
+ // case 1: exception type declared in IDL at the function call
+ try
+ client.testException('Xception');
+ Expect( FALSE, 'testException(''Xception''): must trow an exception');
+ except
+ on e:TXception do begin
+ Expect( e.ErrorCode = 1001, 'error code');
+ Expect( e.Message_ = 'Xception', 'error message');
+ Console.WriteLine( ' = ' + IntToStr(e.ErrorCode) + ', ' + e.Message_ );
+ end;
+ on e:TTransportException do Expect( FALSE, 'Unexpected : "'+e.ToString+'"');
+ on e:Exception do Expect( FALSE, 'Unexpected exception "'+e.ClassName+'": '+e.Message);
+ end;
+
+ // case 2: exception type NOT declared in IDL at the function call
+ // this will close the connection
+ try
+ client.testException('TException');
+ Expect( FALSE, 'testException(''TException''): must trow an exception');
+ except
+ on e:TTransportException do begin
+ Console.WriteLine( e.ClassName+' = '+e.Message); // this is what we get
+ end;
+ on e:TApplicationException do begin
+ Console.WriteLine( e.ClassName+' = '+e.Message); // this is what we get
+ end;
+ on e:TException do Expect( FALSE, 'Unexpected exception "'+e.ClassName+'": '+e.Message);
+ on e:Exception do Expect( FALSE, 'Unexpected exception "'+e.ClassName+'": '+e.Message);
+ end;
+
+
+ if FTransport.IsOpen then FTransport.Close;
+ FTransport.Open; // re-open connection, server has already closed
+
+
+ // case 3: no exception
+ try
+ client.testException('something');
+ Expect( TRUE, 'testException(''something''): must not trow an exception');
+ except
+ on e:TTransportException do Expect( FALSE, 'Unexpected : "'+e.ToString+'"');
+ on e:Exception do Expect( FALSE, 'Unexpected exception "'+e.ClassName+'": '+e.Message);
+ end;
+ {$ENDIF Exceptions}
+
+
+ // simple things
+ StartTestGroup( 'simple Thrift calls', test_BaseTypes);
+ client.testVoid();
+ Expect( TRUE, 'testVoid()'); // success := no exception
+
+ s := BoolToString( client.testBool(TRUE));
+ Expect( s = BoolToString(TRUE), 'testBool(TRUE) = '+s);
+ s := BoolToString( client.testBool(FALSE));
+ Expect( s = BoolToString(FALSE), 'testBool(FALSE) = '+s);
+
+ s := client.testString('Test');
+ Expect( s = 'Test', 'testString(''Test'') = "'+s+'"');
+
+ s := client.testString(''); // empty string
+ Expect( s = '', 'testString('''') = "'+s+'"');
+
+ s := client.testString(HUGE_TEST_STRING);
+ Expect( length(s) = length(HUGE_TEST_STRING),
+ 'testString( length(HUGE_TEST_STRING) = '+IntToStr(Length(HUGE_TEST_STRING))+') '
+ +'=> length(result) = '+IntToStr(Length(s)));
+
+ i8 := client.testByte(1);
+ Expect( i8 = 1, 'testByte(1) = ' + IntToStr( i8 ));
+
+ i32 := client.testI32(-1);
+ Expect( i32 = -1, 'testI32(-1) = ' + IntToStr(i32));
+
+ Console.WriteLine('testI64(-34359738368)');
+ i64 := client.testI64(-34359738368);
+ Expect( i64 = -34359738368, 'testI64(-34359738368) = ' + IntToStr( i64));
+
+ // random binary small
+ for testsize := Low(TTestSize) to High(TTestSize) do begin
+ binOut := PrepareBinaryData( TRUE, testsize);
+ Console.WriteLine('testBinary('+IntToStr(Length(binOut))+' bytes)');
+ try
+ binIn := client.testBinary(binOut);
+ Expect( Length(binOut) = Length(binIn), 'testBinary('+IntToStr(Length(binOut))+' bytes): '+IntToStr(Length(binIn))+' bytes received');
+ i32 := Min( Length(binOut), Length(binIn));
+ Expect( CompareMem( binOut, binIn, i32), 'testBinary('+IntToStr(Length(binOut))+' bytes): validating received data');
+ except
+ on e:TApplicationException do Console.WriteLine('testBinary(): '+e.Message);
+ on e:Exception do Expect( FALSE, 'testBinary(): Unexpected exception "'+e.ClassName+'": '+e.Message);
+ end;
+ end;
+
+ Console.WriteLine('testDouble(5.325098235)');
+ dub := client.testDouble(5.325098235);
+ Expect( abs(dub-5.325098235) < 1e-14, 'testDouble(5.325098235) = ' + FloatToStr( dub));
+
+ // structs
+ StartTestGroup( 'testStruct', test_Structs);
+ Console.WriteLine('testStruct({''Zero'', 1, -3, -5})');
+ o := TXtructImpl.Create;
+ o.String_thing := 'Zero';
+ o.Byte_thing := 1;
+ o.I32_thing := -3;
+ o.I64_thing := -5;
+ i := client.testStruct(o);
+ Expect( i.String_thing = 'Zero', 'i.String_thing = "'+i.String_thing+'"');
+ Expect( i.Byte_thing = 1, 'i.Byte_thing = '+IntToStr(i.Byte_thing));
+ Expect( i.I32_thing = -3, 'i.I32_thing = '+IntToStr(i.I32_thing));
+ Expect( i.I64_thing = -5, 'i.I64_thing = '+IntToStr(i.I64_thing));
+ Expect( i.__isset_String_thing, 'i.__isset_String_thing = '+BoolToString(i.__isset_String_thing));
+ Expect( i.__isset_Byte_thing, 'i.__isset_Byte_thing = '+BoolToString(i.__isset_Byte_thing));
+ Expect( i.__isset_I32_thing, 'i.__isset_I32_thing = '+BoolToString(i.__isset_I32_thing));
+ Expect( i.__isset_I64_thing, 'i.__isset_I64_thing = '+BoolToString(i.__isset_I64_thing));
+
+ // nested structs
+ StartTestGroup( 'testNest', test_Structs);
+ Console.WriteLine('testNest({1, {''Zero'', 1, -3, -5}, 5})');
+ o2 := TXtruct2Impl.Create;
+ o2.Byte_thing := 1;
+ o2.Struct_thing := o;
+ o2.I32_thing := 5;
+ i2 := client.testNest(o2);
+ i := i2.Struct_thing;
+ Expect( i.String_thing = 'Zero', 'i.String_thing = "'+i.String_thing+'"');
+ Expect( i.Byte_thing = 1, 'i.Byte_thing = '+IntToStr(i.Byte_thing));
+ Expect( i.I32_thing = -3, 'i.I32_thing = '+IntToStr(i.I32_thing));
+ Expect( i.I64_thing = -5, 'i.I64_thing = '+IntToStr(i.I64_thing));
+ Expect( i2.Byte_thing = 1, 'i2.Byte_thing = '+IntToStr(i2.Byte_thing));
+ Expect( i2.I32_thing = 5, 'i2.I32_thing = '+IntToStr(i2.I32_thing));
+ Expect( i.__isset_String_thing, 'i.__isset_String_thing = '+BoolToString(i.__isset_String_thing));
+ Expect( i.__isset_Byte_thing, 'i.__isset_Byte_thing = '+BoolToString(i.__isset_Byte_thing));
+ Expect( i.__isset_I32_thing, 'i.__isset_I32_thing = '+BoolToString(i.__isset_I32_thing));
+ Expect( i.__isset_I64_thing, 'i.__isset_I64_thing = '+BoolToString(i.__isset_I64_thing));
+ Expect( i2.__isset_Byte_thing, 'i2.__isset_Byte_thing');
+ Expect( i2.__isset_I32_thing, 'i2.__isset_I32_thing');
+
+ // map<type1,type2>: A map of strictly unique keys to values.
+ // Translates to an STL map, Java HashMap, PHP associative array, Python/Ruby dictionary, etc.
+ StartTestGroup( 'testMap', test_Containers);
+ mapout := TThriftDictionaryImpl<Integer,Integer>.Create;
+ for j := 0 to 4 do
+ begin
+ mapout.AddOrSetValue( j, j - 10);
+ end;
+ Console.Write('testMap({');
+ first := True;
+ for key in mapout.Keys do
+ begin
+ if first
+ then first := False
+ else Console.Write( ', ' );
+ Console.Write( IntToStr( key) + ' => ' + IntToStr( mapout[key]));
+ end;
+ Console.WriteLine('})');
+
+ mapin := client.testMap( mapout );
+ Expect( mapin.Count = mapout.Count, 'testMap: mapin.Count = mapout.Count');
+ for j := 0 to 4 do
+ begin
+ Expect( mapout.ContainsKey(j), 'testMap: mapout.ContainsKey('+IntToStr(j)+') = '+BoolToString(mapout.ContainsKey(j)));
+ end;
+ for key in mapin.Keys do
+ begin
+ Expect( mapin[key] = mapout[key], 'testMap: '+IntToStr(key) + ' => ' + IntToStr( mapin[key]));
+ Expect( mapin[key] = key - 10, 'testMap: mapin['+IntToStr(key)+'] = '+IntToStr( mapin[key]));
+ end;
+
+
+ // map<type1,type2>: A map of strictly unique keys to values.
+ // Translates to an STL map, Java HashMap, PHP associative array, Python/Ruby dictionary, etc.
+ StartTestGroup( 'testStringMap', test_Containers);
+ strmapout := TThriftDictionaryImpl<string,string>.Create;
+ for j := 0 to 4 do
+ begin
+ strmapout.AddOrSetValue( IntToStr(j), IntToStr(j - 10));
+ end;
+ Console.Write('testStringMap({');
+ first := True;
+ for strkey in strmapout.Keys do
+ begin
+ if first
+ then first := False
+ else Console.Write( ', ' );
+ Console.Write( strkey + ' => ' + strmapout[strkey]);
+ end;
+ Console.WriteLine('})');
+
+ strmapin := client.testStringMap( strmapout );
+ Expect( strmapin.Count = strmapout.Count, 'testStringMap: strmapin.Count = strmapout.Count');
+ for j := 0 to 4 do
+ begin
+ Expect( strmapout.ContainsKey(IntToStr(j)),
+ 'testStringMap: strmapout.ContainsKey('+IntToStr(j)+') = '
+ + BoolToString(strmapout.ContainsKey(IntToStr(j))));
+ end;
+ for strkey in strmapin.Keys do
+ begin
+ Expect( strmapin[strkey] = strmapout[strkey], 'testStringMap: '+strkey + ' => ' + strmapin[strkey]);
+ Expect( strmapin[strkey] = IntToStr( StrToInt(strkey) - 10), 'testStringMap: strmapin['+strkey+'] = '+strmapin[strkey]);
+ end;
+
+
+ // set<type>: An unordered set of unique elements.
+ // Translates to an STL set, Java HashSet, set in Python, etc.
+ // Note: PHP does not support sets, so it is treated similar to a List
+ StartTestGroup( 'testSet', test_Containers);
+ setout := THashSetImpl<Integer>.Create;
+ for j := -2 to 2 do
+ begin
+ setout.Add( j );
+ end;
+ Console.Write('testSet({');
+ first := True;
+ for j in setout do
+ begin
+ if first
+ then first := False
+ else Console.Write(', ');
+ Console.Write(IntToStr( j));
+ end;
+ Console.WriteLine('})');
+
+ setin := client.testSet(setout);
+ Expect( setin.Count = setout.Count, 'testSet: setin.Count = setout.Count');
+ Expect( setin.Count = 5, 'testSet: setin.Count = '+IntToStr(setin.Count));
+ for j := -2 to 2 do // unordered, we can't rely on the order => test for known elements only
+ begin
+ Expect( setin.Contains(j), 'testSet: setin.Contains('+IntToStr(j)+') => '+BoolToString(setin.Contains(j)));
+ end;
+
+ // list<type>: An ordered list of elements.
+ // Translates to an STL vector, Java ArrayList, native arrays in scripting languages, etc.
+ StartTestGroup( 'testList', test_Containers);
+ listout := TThriftListImpl<Integer>.Create;
+ listout.Add( +1);
+ listout.Add( -2);
+ listout.Add( +3);
+ listout.Add( -4);
+ listout.Add( 0);
+ Console.Write('testList({');
+ first := True;
+ for j in listout do
+ begin
+ if first
+ then first := False
+ else Console.Write(', ');
+ Console.Write(IntToStr( j));
+ end;
+ Console.WriteLine('})');
+
+ listin := client.testList(listout);
+ Expect( listin.Count = listout.Count, 'testList: listin.Count = listout.Count');
+ Expect( listin.Count = 5, 'testList: listin.Count = '+IntToStr(listin.Count));
+ Expect( listin[0] = +1, 'listin[0] = '+IntToStr( listin[0]));
+ Expect( listin[1] = -2, 'listin[1] = '+IntToStr( listin[1]));
+ Expect( listin[2] = +3, 'listin[2] = '+IntToStr( listin[2]));
+ Expect( listin[3] = -4, 'listin[3] = '+IntToStr( listin[3]));
+ Expect( listin[4] = 0, 'listin[4] = '+IntToStr( listin[4]));
+
+ // enums
+ ret := client.testEnum(TNumberz.ONE);
+ Expect( ret = TNumberz.ONE, 'testEnum(ONE) = '+IntToStr(Ord(ret)));
+
+ ret := client.testEnum(TNumberz.TWO);
+ Expect( ret = TNumberz.TWO, 'testEnum(TWO) = '+IntToStr(Ord(ret)));
+
+ ret := client.testEnum(TNumberz.THREE);
+ Expect( ret = TNumberz.THREE, 'testEnum(THREE) = '+IntToStr(Ord(ret)));
+
+ ret := client.testEnum(TNumberz.FIVE);
+ Expect( ret = TNumberz.FIVE, 'testEnum(FIVE) = '+IntToStr(Ord(ret)));
+
+ ret := client.testEnum(TNumberz.EIGHT);
+ Expect( ret = TNumberz.EIGHT, 'testEnum(EIGHT) = '+IntToStr(Ord(ret)));
+
+
+ // typedef
+ uid := client.testTypedef(309858235082523);
+ Expect( uid = 309858235082523, 'testTypedef(309858235082523) = '+IntToStr(uid));
+
+
+ // maps of maps
+ StartTestGroup( 'testMapMap(1)', test_Containers);
+ mm := client.testMapMap(1);
+ Console.Write(' = {');
+ for key in mm.Keys do
+ begin
+ Console.Write( IntToStr( key) + ' => {');
+ m2 := mm[key];
+ for k2 in m2.Keys do
+ begin
+ Console.Write( IntToStr( k2) + ' => ' + IntToStr( m2[k2]) + ', ');
+ end;
+ Console.Write('}, ');
+ end;
+ Console.WriteLine('}');
+
+ // verify result data
+ Expect( mm.Count = 2, 'mm.Count = '+IntToStr(mm.Count));
+ pos := mm[4];
+ neg := mm[-4];
+ for j := 1 to 4 do
+ begin
+ Expect( pos[j] = j, 'pos[j] = '+IntToStr(pos[j]));
+ Expect( neg[-j] = -j, 'neg[-j] = '+IntToStr(neg[-j]));
+ end;
+
+
+
+ // insanity
+ StartTestGroup( 'testInsanity', test_Structs);
+ insane := TInsanityImpl.Create;
+ insane.UserMap := TThriftDictionaryImpl<TNumberz, Int64>.Create;
+ insane.UserMap.AddOrSetValue( TNumberz.FIVE, 5000);
+ truck := TXtructImpl.Create;
+ truck.String_thing := 'Truck';
+ truck.Byte_thing := -8; // byte is signed
+ truck.I32_thing := 32;
+ truck.I64_thing := 64;
+ insane.Xtructs := TThriftListImpl<IXtruct>.Create;
+ insane.Xtructs.Add( truck );
+ whoa := client.testInsanity( insane );
+ Console.Write(' = {');
+ for key64 in whoa.Keys do
+ begin
+ val := whoa[key64];
+ Console.Write( IntToStr( key64) + ' => {');
+ for k2_2 in val.Keys do
+ begin
+ v2 := val[k2_2];
+ Console.Write( IntToStr( Integer( k2_2)) + ' => {');
+ userMap := v2.UserMap;
+ Console.Write('{');
+ if userMap <> nil then
+ begin
+ for k3 in userMap.Keys do
+ begin
+ Console.Write( IntToStr( Integer( k3)) + ' => ' + IntToStr( userMap[k3]) + ', ');
+ end;
+ end else
+ begin
+ Console.Write('null');
+ end;
+ Console.Write('}, ');
+ xtructs := v2.Xtructs;
+ Console.Write('{');
+
+ if xtructs <> nil then
+ begin
+ for x in xtructs do
+ begin
+ Console.Write('{"' + x.String_thing + '", ' +
+ IntToStr( x.Byte_thing) + ', ' +
+ IntToStr( x.I32_thing) + ', ' +
+ IntToStr( x.I32_thing) + '}, ');
+ end;
+ end else
+ begin
+ Console.Write('null');
+ end;
+ Console.Write('}');
+ Console.Write('}, ');
+ end;
+ Console.Write('}, ');
+ end;
+ Console.WriteLine('}');
+
+ (**
+ * So you think you've got this all worked, out eh?
+ *
+ * Creates a the returned map with these values and prints it out:
+ * { 1 => { 2 => argument,
+ * 3 => argument,
+ * },
+ * 2 => { 6 => <empty Insanity struct>, },
+ * }
+ * @return map<UserId, map<Numberz,Insanity>> - a map with the above values
+ *)
+
+ // verify result data
+ Expect( whoa.Count = 2, 'whoa.Count = '+IntToStr(whoa.Count));
+ //
+ first_map := whoa[1];
+ second_map := whoa[2];
+ Expect( first_map.Count = 2, 'first_map.Count = '+IntToStr(first_map.Count));
+ Expect( second_map.Count = 1, 'second_map.Count = '+IntToStr(second_map.Count));
+ //
+ looney := second_map[TNumberz.SIX];
+ Expect( Assigned(looney), 'Assigned(looney) = '+BoolToString(Assigned(looney)));
+ Expect( not looney.__isset_UserMap, 'looney.__isset_UserMap = '+BoolToString(looney.__isset_UserMap));
+ Expect( not looney.__isset_Xtructs, 'looney.__isset_Xtructs = '+BoolToString(looney.__isset_Xtructs));
+ //
+ for ret in [TNumberz.TWO, TNumberz.THREE] do begin
+ crazy := first_map[ret];
+ Console.WriteLine('first_map['+intToStr(Ord(ret))+']');
+
+ Expect( crazy.__isset_UserMap, 'crazy.__isset_UserMap = '+BoolToString(crazy.__isset_UserMap));
+ Expect( crazy.__isset_Xtructs, 'crazy.__isset_Xtructs = '+BoolToString(crazy.__isset_Xtructs));
+
+ Expect( crazy.UserMap.Count = insane.UserMap.Count, 'crazy.UserMap.Count = '+IntToStr(crazy.UserMap.Count));
+ for pair in insane.UserMap do begin
+ Expect( crazy.UserMap[pair.Key] = pair.Value, 'crazy.UserMap['+IntToStr(Ord(pair.key))+'] = '+IntToStr(crazy.UserMap[pair.Key]));
+ end;
+
+ Expect( crazy.Xtructs.Count = insane.Xtructs.Count, 'crazy.Xtructs.Count = '+IntToStr(crazy.Xtructs.Count));
+ for arg0 := 0 to insane.Xtructs.Count-1 do begin
+ hello := insane.Xtructs[arg0];
+ goodbye := crazy.Xtructs[arg0];
+ Expect( goodbye.String_thing = hello.String_thing, 'goodbye.String_thing = '+goodbye.String_thing);
+ Expect( goodbye.Byte_thing = hello.Byte_thing, 'goodbye.Byte_thing = '+IntToStr(goodbye.Byte_thing));
+ Expect( goodbye.I32_thing = hello.I32_thing, 'goodbye.I32_thing = '+IntToStr(goodbye.I32_thing));
+ Expect( goodbye.I64_thing = hello.I64_thing, 'goodbye.I64_thing = '+IntToStr(goodbye.I64_thing));
+ end;
+ end;
+
+
+ // multi args
+ StartTestGroup( 'testMulti', test_BaseTypes);
+ arg0 := 1;
+ arg1 := 2;
+ arg2 := High(Int64);
+ arg3 := TThriftDictionaryImpl<SmallInt, string>.Create;
+ arg3.AddOrSetValue( 1, 'one');
+ arg4 := TNumberz.FIVE;
+ arg5 := 5000000;
+ Console.WriteLine('Test Multi(' + IntToStr( arg0) + ',' +
+ IntToStr( arg1) + ',' + IntToStr( arg2) + ',' +
+ arg3.ToString + ',' + IntToStr( Integer( arg4)) + ',' +
+ IntToStr( arg5) + ')');
+
+ i := client.testMulti( arg0, arg1, arg2, arg3, arg4, arg5);
+ Expect( i.String_thing = 'Hello2', 'testMulti: i.String_thing = "'+i.String_thing+'"');
+ Expect( i.Byte_thing = arg0, 'testMulti: i.Byte_thing = '+IntToStr(i.Byte_thing));
+ Expect( i.I32_thing = arg1, 'testMulti: i.I32_thing = '+IntToStr(i.I32_thing));
+ Expect( i.I64_thing = arg2, 'testMulti: i.I64_thing = '+IntToStr(i.I64_thing));
+ Expect( i.__isset_String_thing, 'testMulti: i.__isset_String_thing = '+BoolToString(i.__isset_String_thing));
+ Expect( i.__isset_Byte_thing, 'testMulti: i.__isset_Byte_thing = '+BoolToString(i.__isset_Byte_thing));
+ Expect( i.__isset_I32_thing, 'testMulti: i.__isset_I32_thing = '+BoolToString(i.__isset_I32_thing));
+ Expect( i.__isset_I64_thing, 'testMulti: i.__isset_I64_thing = '+BoolToString(i.__isset_I64_thing));
+
+ // multi exception
+ StartTestGroup( 'testMultiException(1)', test_Exceptions);
+ try
+ i := client.testMultiException( 'need more pizza', 'run out of beer');
+ Expect( i.String_thing = 'run out of beer', 'i.String_thing = "' +i.String_thing+ '"');
+ Expect( i.__isset_String_thing, 'i.__isset_String_thing = '+BoolToString(i.__isset_String_thing));
+ { this is not necessarily true, these fields are default-serialized
+ Expect( not i.__isset_Byte_thing, 'i.__isset_Byte_thing = '+BoolToString(i.__isset_Byte_thing));
+ Expect( not i.__isset_I32_thing, 'i.__isset_I32_thing = '+BoolToString(i.__isset_I32_thing));
+ Expect( not i.__isset_I64_thing, 'i.__isset_I64_thing = '+BoolToString(i.__isset_I64_thing));
+ }
+ except
+ on e:Exception do Expect( FALSE, 'Unexpected exception "'+e.ClassName+'": '+e.Message);
+ end;
+
+ StartTestGroup( 'testMultiException(Xception)', test_Exceptions);
+ try
+ i := client.testMultiException( 'Xception', 'second test');
+ Expect( FALSE, 'testMultiException(''Xception''): must trow an exception');
+ except
+ on x:TXception do begin
+ Expect( x.__isset_ErrorCode, 'x.__isset_ErrorCode = '+BoolToString(x.__isset_ErrorCode));
+ Expect( x.__isset_Message_, 'x.__isset_Message_ = '+BoolToString(x.__isset_Message_));
+ Expect( x.ErrorCode = 1001, 'x.ErrorCode = '+IntToStr(x.ErrorCode));
+ Expect( x.Message_ = 'This is an Xception', 'x.Message = "'+x.Message_+'"');
+ end;
+ on e:Exception do Expect( FALSE, 'Unexpected exception "'+e.ClassName+'": '+e.Message);
+ end;
+
+ StartTestGroup( 'testMultiException(Xception2)', test_Exceptions);
+ try
+ i := client.testMultiException( 'Xception2', 'third test');
+ Expect( FALSE, 'testMultiException(''Xception2''): must trow an exception');
+ except
+ on x:TXception2 do begin
+ Expect( x.__isset_ErrorCode, 'x.__isset_ErrorCode = '+BoolToString(x.__isset_ErrorCode));
+ Expect( x.__isset_Struct_thing, 'x.__isset_Struct_thing = '+BoolToString(x.__isset_Struct_thing));
+ Expect( x.ErrorCode = 2002, 'x.ErrorCode = '+IntToStr(x.ErrorCode));
+ Expect( x.Struct_thing.String_thing = 'This is an Xception2', 'x.Struct_thing.String_thing = "'+x.Struct_thing.String_thing+'"');
+ Expect( x.Struct_thing.__isset_String_thing, 'x.Struct_thing.__isset_String_thing = '+BoolToString(x.Struct_thing.__isset_String_thing));
+ { this is not necessarily true, these fields are default-serialized
+ Expect( not x.Struct_thing.__isset_Byte_thing, 'x.Struct_thing.__isset_Byte_thing = '+BoolToString(x.Struct_thing.__isset_Byte_thing));
+ Expect( not x.Struct_thing.__isset_I32_thing, 'x.Struct_thing.__isset_I32_thing = '+BoolToString(x.Struct_thing.__isset_I32_thing));
+ Expect( not x.Struct_thing.__isset_I64_thing, 'x.Struct_thing.__isset_I64_thing = '+BoolToString(x.Struct_thing.__isset_I64_thing));
+ }
+ end;
+ on e:Exception do Expect( FALSE, 'Unexpected exception "'+e.ClassName+'": '+e.Message);
+ end;
+
+
+ // oneway functions
+ StartTestGroup( 'Test Oneway(1)', test_Unknown);
+ client.testOneway(1);
+ Expect( TRUE, 'Test Oneway(1)'); // success := no exception
+
+ // call time
+ {$IFDEF PerfTest}
+ StartTestGroup( 'Test Calltime()');
+ StartTick := GetTickCount;
+ for k := 0 to 1000 - 1 do
+ begin
+ client.testVoid();
+ end;
+ Console.WriteLine(' = ' + FloatToStr( (GetTickCount - StartTick) / 1000 ) + ' ms a testVoid() call' );
+ {$ENDIF PerfTest}
+
+ // no more tests here
+ StartTestGroup( '', test_Unknown);
+end;
+
+
+{$IFDEF SupportsAsync}
+procedure TClientThread.ClientAsyncTest;
+var
+ client : TThriftTest.IAsync;
+ s : string;
+ i8 : ShortInt;
+begin
+ StartTestGroup( 'Async Tests', test_Unknown);
+ client := TThriftTest.TClient.Create( FProtocol);
+ FTransport.Open;
+
+ // oneway void functions
+ client.testOnewayAsync(1).Wait;
+ Expect( TRUE, 'Test Oneway(1)'); // success := no exception
+
+ // normal functions
+ s := client.testStringAsync(HUGE_TEST_STRING).Value;
+ Expect( length(s) = length(HUGE_TEST_STRING),
+ 'testString( length(HUGE_TEST_STRING) = '+IntToStr(Length(HUGE_TEST_STRING))+') '
+ +'=> length(result) = '+IntToStr(Length(s)));
+
+ i8 := client.testByte(1).Value;
+ Expect( i8 = 1, 'testByte(1) = ' + IntToStr( i8 ));
+end;
+{$ENDIF}
+
+
+{$IFDEF StressTest}
+procedure TClientThread.StressTest(const client : TThriftTest.Iface);
+begin
+ while TRUE do begin
+ try
+ if not FTransport.IsOpen then FTransport.Open; // re-open connection, server has already closed
+ try
+ client.testString('Test');
+ Write('.');
+ finally
+ if FTransport.IsOpen then FTransport.Close;
+ end;
+ except
+ on e:Exception do Writeln(#10+e.message);
+ end;
+ end;
+end;
+{$ENDIF}
+
+
+function TClientThread.PrepareBinaryData( aRandomDist : Boolean; aSize : TTestSize) : TBytes;
+var i : Integer;
+begin
+ case aSize of
+ Empty : SetLength( result, 0);
+ Normal : SetLength( result, $100);
+ ByteArrayTest : SetLength( result, SizeOf(TByteArray) + 128);
+ PipeWriteLimit : SetLength( result, 65535 + 128);
+ TwentyMB : SetLength( result, 20 * 1024 * 1024);
+ else
+ raise EArgumentException.Create('aSize');
+ end;
+
+ ASSERT( Low(result) = 0);
+ if Length(result) = 0 then Exit;
+
+ // linear distribution, unless random is requested
+ if not aRandomDist then begin
+ for i := Low(result) to High(result) do begin
+ result[i] := i mod $100;
+ end;
+ Exit;
+ end;
+
+ // random distribution of all 256 values
+ FillChar( result[0], Length(result) * SizeOf(result[0]), $0);
+ for i := Low(result) to High(result) do begin
+ result[i] := Byte( Random($100));
+ end;
+end;
+
+
+{$IFDEF Win64}
+procedure TClientThread.UseInterlockedExchangeAdd64;
+var a,b : Int64;
+begin
+ a := 1;
+ b := 2;
+ Thrift.Utils.InterlockedExchangeAdd64( a,b);
+ Expect( a = 3, 'InterlockedExchangeAdd64');
+end;
+{$ENDIF}
+
+
+procedure TClientThread.JSONProtocolReadWriteTest;
+// Tests only then read/write procedures of the JSON protocol
+// All tests succeed, if we can read what we wrote before
+// Note that passing this test does not imply, that our JSON is really compatible to what
+// other clients or servers expect as the real JSON. This is beyond the scope of this test.
+var prot : IProtocol;
+ stm : TStringStream;
+ list : TThriftList;
+ binary, binRead, emptyBinary : TBytes;
+ i,iErr : Integer;
+const
+ TEST_SHORT = ShortInt( $FE);
+ TEST_SMALL = SmallInt( $FEDC);
+ TEST_LONG = LongInt( $FEDCBA98);
+ TEST_I64 = Int64( $FEDCBA9876543210);
+ TEST_DOUBLE = -1.234e-56;
+ DELTA_DOUBLE = TEST_DOUBLE * 1e-14;
+ TEST_STRING = 'abc-'#$00E4#$00f6#$00fc; // german umlauts (en-us: "funny chars")
+ // Test THRIFT-2336 and THRIFT-3404 with U+1D11E (G Clef symbol) and 'РуÑÑкое Ðазвание';
+ G_CLEF_AND_CYRILLIC_TEXT = #$1d11e' '#$0420#$0443#$0441#$0441#$043a#$043e#$0435' '#$041d#$0430#$0437#$0432#$0430#$043d#$0438#$0435;
+ G_CLEF_AND_CYRILLIC_JSON = '"\ud834\udd1e \u0420\u0443\u0441\u0441\u043a\u043e\u0435 \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435"';
+ // test both possible solidus encodings
+ SOLIDUS_JSON_DATA = '"one/two\/three"';
+ SOLIDUS_EXCPECTED = 'one/two/three';
+begin
+ stm := TStringStream.Create;
+ try
+ StartTestGroup( 'JsonProtocolTest', test_Unknown);
+
+ // prepare binary data
+ binary := PrepareBinaryData( FALSE, Normal);
+ SetLength( emptyBinary, 0); // empty binary data block
+
+ // output setup
+ prot := TJSONProtocolImpl.Create(
+ TStreamTransportImpl.Create(
+ nil, TThriftStreamAdapterDelphi.Create( stm, FALSE)));
+
+ // write
+ Init( list, TType.String_, 9);
+ prot.WriteListBegin( list);
+ prot.WriteBool( TRUE);
+ prot.WriteBool( FALSE);
+ prot.WriteByte( TEST_SHORT);
+ prot.WriteI16( TEST_SMALL);
+ prot.WriteI32( TEST_LONG);
+ prot.WriteI64( TEST_I64);
+ prot.WriteDouble( TEST_DOUBLE);
+ prot.WriteString( TEST_STRING);
+ prot.WriteBinary( binary);
+ prot.WriteString( ''); // empty string
+ prot.WriteBinary( emptyBinary); // empty binary data block
+ prot.WriteListEnd;
+
+ // input setup
+ Expect( stm.Position = stm.Size, 'Stream position/length after write');
+ stm.Position := 0;
+ prot := TJSONProtocolImpl.Create(
+ TStreamTransportImpl.Create(
+ TThriftStreamAdapterDelphi.Create( stm, FALSE), nil));
+
+ // read and compare
+ list := prot.ReadListBegin;
+ Expect( list.ElementType = TType.String_, 'list element type');
+ Expect( list.Count = 9, 'list element count');
+ Expect( prot.ReadBool, 'WriteBool/ReadBool: TRUE');
+ Expect( not prot.ReadBool, 'WriteBool/ReadBool: FALSE');
+ Expect( prot.ReadByte = TEST_SHORT, 'WriteByte/ReadByte');
+ Expect( prot.ReadI16 = TEST_SMALL, 'WriteI16/ReadI16');
+ Expect( prot.ReadI32 = TEST_LONG, 'WriteI32/ReadI32');
+ Expect( prot.ReadI64 = TEST_I64, 'WriteI64/ReadI64');
+ Expect( abs(prot.ReadDouble-TEST_DOUBLE) < abs(DELTA_DOUBLE), 'WriteDouble/ReadDouble');
+ Expect( prot.ReadString = TEST_STRING, 'WriteString/ReadString');
+ binRead := prot.ReadBinary;
+ Expect( Length(prot.ReadString) = 0, 'WriteString/ReadString (empty string)');
+ Expect( Length(prot.ReadBinary) = 0, 'empty WriteBinary/ReadBinary (empty data block)');
+ prot.ReadListEnd;
+
+ // test binary data
+ Expect( Length(binary) = Length(binRead), 'Binary data length check');
+ iErr := -1;
+ for i := Low(binary) to High(binary) do begin
+ if binary[i] <> binRead[i] then begin
+ iErr := i;
+ Break;
+ end;
+ end;
+ if iErr < 0
+ then Expect( TRUE, 'Binary data check ('+IntToStr(Length(binary))+' Bytes)')
+ else Expect( FALSE, 'Binary data check at offset '+IntToStr(iErr));
+
+ Expect( stm.Position = stm.Size, 'Stream position after read');
+
+
+ // Solidus can be encoded in two ways. Make sure we can read both
+ stm.Position := 0;
+ stm.Size := 0;
+ stm.WriteString(SOLIDUS_JSON_DATA);
+ stm.Position := 0;
+ prot := TJSONProtocolImpl.Create(
+ TStreamTransportImpl.Create(
+ TThriftStreamAdapterDelphi.Create( stm, FALSE), nil));
+ Expect( prot.ReadString = SOLIDUS_EXCPECTED, 'Solidus encoding');
+
+
+ // Widechars should work too. Do they?
+ // After writing, we ensure that we are able to read it back
+ // We can't assume hex-encoding, since (nearly) any Unicode char is valid JSON
+ stm.Position := 0;
+ stm.Size := 0;
+ prot := TJSONProtocolImpl.Create(
+ TStreamTransportImpl.Create(
+ nil, TThriftStreamAdapterDelphi.Create( stm, FALSE)));
+ prot.WriteString( G_CLEF_AND_CYRILLIC_TEXT);
+ stm.Position := 0;
+ prot := TJSONProtocolImpl.Create(
+ TStreamTransportImpl.Create(
+ TThriftStreamAdapterDelphi.Create( stm, FALSE), nil));
+ Expect( prot.ReadString = G_CLEF_AND_CYRILLIC_TEXT, 'Writing JSON with chars > 8 bit');
+
+ // Widechars should work with hex-encoding too. Do they?
+ stm.Position := 0;
+ stm.Size := 0;
+ stm.WriteString( G_CLEF_AND_CYRILLIC_JSON);
+ stm.Position := 0;
+ prot := TJSONProtocolImpl.Create(
+ TStreamTransportImpl.Create(
+ TThriftStreamAdapterDelphi.Create( stm, FALSE), nil));
+ Expect( prot.ReadString = G_CLEF_AND_CYRILLIC_TEXT, 'Reading JSON with chars > 8 bit');
+
+
+ finally
+ stm.Free;
+ prot := nil; //-> Release
+ StartTestGroup( '', test_Unknown); // no more tests here
+ end;
+end;
+
+
+procedure TClientThread.StartTestGroup( const aGroup : string; const aTest : TTestGroup);
+begin
+ FTestGroup := aGroup;
+ FCurrentTest := aTest;
+
+ Include( FExecuted, aTest);
+
+ if FTestGroup <> '' then begin
+ Console.WriteLine('');
+ Console.WriteLine( aGroup+' tests');
+ Console.WriteLine( StringOfChar('-',60));
+ end;
+end;
+
+
+procedure TClientThread.Expect( aTestResult : Boolean; const aTestInfo : string);
+begin
+ if aTestResult then begin
+ Inc(FSuccesses);
+ Console.WriteLine( aTestInfo+': passed');
+ end
+ else begin
+ FErrors.Add( FTestGroup+': '+aTestInfo);
+ Include( FFailed, FCurrentTest);
+ Console.WriteLine( aTestInfo+': *** FAILED ***');
+
+ // We have a failed test!
+ // -> issue DebugBreak ONLY if a debugger is attached,
+ // -> unhandled DebugBreaks would cause Windows to terminate the app otherwise
+ if IsDebuggerPresent
+ then {$IFDEF CPUX64} DebugBreak {$ELSE} asm int 3 end {$ENDIF};
+ end;
+end;
+
+
+procedure TClientThread.ReportResults;
+var nTotal : Integer;
+ sLine : string;
+begin
+ // prevent us from stupid DIV/0 errors
+ nTotal := FSuccesses + FErrors.Count;
+ if nTotal = 0 then begin
+ Console.WriteLine('No results logged');
+ Exit;
+ end;
+
+ Console.WriteLine('');
+ Console.WriteLine( StringOfChar('=',60));
+ Console.WriteLine( IntToStr(nTotal)+' tests performed');
+ Console.WriteLine( IntToStr(FSuccesses)+' tests succeeded ('+IntToStr(round(100*FSuccesses/nTotal))+'%)');
+ Console.WriteLine( IntToStr(FErrors.Count)+' tests failed ('+IntToStr(round(100*FErrors.Count/nTotal))+'%)');
+ Console.WriteLine( StringOfChar('=',60));
+ if FErrors.Count > 0 then begin
+ Console.WriteLine('FAILED TESTS:');
+ for sLine in FErrors do Console.WriteLine('- '+sLine);
+ Console.WriteLine( StringOfChar('=',60));
+ InterlockedIncrement( ExitCode); // return <> 0 on errors
+ end;
+ Console.WriteLine('');
+end;
+
+
+function TClientThread.CalculateExitCode : Byte;
+var test : TTestGroup;
+begin
+ result := EXITCODE_SUCCESS;
+ for test := Low(TTestGroup) to High(TTestGroup) do begin
+ if (test in FFailed) or not (test in FExecuted)
+ then result := result or MAP_FAILURES_TO_EXITCODE_BITS[test];
+ end;
+end;
+
+
+constructor TClientThread.Create( const aSetup : TTestSetup; const aNumIteration: Integer);
+begin
+ FSetup := aSetup;
+ FNumIteration := ANumIteration;
+
+ FConsole := TThreadConsole.Create( Self );
+ FCurrentTest := test_Unknown;
+
+ // error list: keep correct order, allow for duplicates
+ FErrors := TStringList.Create;
+ FErrors.Sorted := FALSE;
+ FErrors.Duplicates := dupAccept;
+
+ inherited Create( TRUE);
+end;
+
+destructor TClientThread.Destroy;
+begin
+ FreeAndNil( FConsole);
+ FreeAndNil( FErrors);
+ inherited;
+end;
+
+procedure TClientThread.Execute;
+var
+ i : Integer;
+begin
+ // perform all tests
+ try
+ {$IFDEF Win64}
+ UseInterlockedExchangeAdd64;
+ {$ENDIF}
+ JSONProtocolReadWriteTest;
+
+ // must be run in the context of the thread
+ InitializeProtocolTransportStack;
+ try
+ for i := 0 to FNumIteration - 1 do begin
+ ClientTest;
+ {$IFDEF SupportsAsync}
+ ClientAsyncTest;
+ {$ENDIF}
+ end;
+
+ // report the outcome
+ ReportResults;
+ SetReturnValue( CalculateExitCode);
+
+ finally
+ ShutdownProtocolTransportStack;
+ end;
+
+ except
+ on e:Exception do Expect( FALSE, 'unexpected exception: "'+e.message+'"');
+ end;
+end;
+
+
+function TClientThread.InitializeHttpTransport( const aTimeoutSetting : Integer) : IHTTPClient;
+var sUrl : string;
+ comps : URL_COMPONENTS;
+ dwChars : DWORD;
+begin
+ ASSERT( FSetup.endpoint in [trns_MsxmlHttp, trns_WinHttp]);
+
+ if FSetup.useSSL
+ then sUrl := 'https://'
+ else sUrl := 'http://';
+
+ sUrl := sUrl + FSetup.host;
+
+ // add the port number if necessary and at the right place
+ FillChar( comps, SizeOf(comps), 0);
+ comps.dwStructSize := SizeOf(comps);
+ comps.dwSchemeLength := MAXINT;
+ comps.dwHostNameLength := MAXINT;
+ comps.dwUserNameLength := MAXINT;
+ comps.dwPasswordLength := MAXINT;
+ comps.dwUrlPathLength := MAXINT;
+ comps.dwExtraInfoLength := MAXINT;
+ Win32Check( WinHttpCrackUrl( PChar(sUrl), Length(sUrl), 0, comps));
+ case FSetup.port of
+ 80 : if FSetup.useSSL then comps.nPort := FSetup.port;
+ 443 : if not FSetup.useSSL then comps.nPort := FSetup.port;
+ else
+ if FSetup.port > 0 then comps.nPort := FSetup.port;
+ end;
+ dwChars := Length(sUrl) + 64;
+ SetLength( sUrl, dwChars);
+ Win32Check( WinHttpCreateUrl( comps, 0, @sUrl[1], dwChars));
+ SetLength( sUrl, dwChars);
+
+
+ Console.WriteLine('Target URL: '+sUrl);
+ case FSetup.endpoint of
+ trns_MsxmlHttp : result := TMsxmlHTTPClientImpl.Create( sUrl);
+ trns_WinHttp : result := TWinHTTPClientImpl.Create( sUrl);
+ else
+ raise Exception.Create(ENDPOINT_TRANSPORTS[FSetup.endpoint]+' unhandled case');
+ end;
+
+ result.DnsResolveTimeout := aTimeoutSetting;
+ result.ConnectionTimeout := aTimeoutSetting;
+ result.SendTimeout := aTimeoutSetting;
+ result.ReadTimeout := aTimeoutSetting;
+end;
+
+
+procedure TClientThread.InitializeProtocolTransportStack;
+var streamtrans : IStreamTransport;
+ canSSL : Boolean;
+const
+ DEBUG_TIMEOUT = 30 * 1000;
+ RELEASE_TIMEOUT = DEFAULT_THRIFT_TIMEOUT;
+ PIPE_TIMEOUT = RELEASE_TIMEOUT;
+ HTTP_TIMEOUTS = 10 * 1000;
+begin
+ // needed for HTTP clients as they utilize the MSXML COM components
+ OleCheck( CoInitialize( nil));
+
+ canSSL := FALSE;
+ case FSetup.endpoint of
+ trns_Sockets: begin
+ Console.WriteLine('Using sockets ('+FSetup.host+' port '+IntToStr(FSetup.port)+')');
+ streamtrans := TSocketImpl.Create( FSetup.host, FSetup.port );
+ FTransport := streamtrans;
+ end;
+
+ trns_MsxmlHttp,
+ trns_WinHttp: begin
+ Console.WriteLine('Using HTTPClient');
+ FTransport := InitializeHttpTransport( HTTP_TIMEOUTS);
+ canSSL := TRUE;
+ end;
+
+ trns_EvHttp: begin
+ raise Exception.Create(ENDPOINT_TRANSPORTS[FSetup.endpoint]+' transport not implemented');
+ end;
+
+ trns_NamedPipes: begin
+ streamtrans := TNamedPipeTransportClientEndImpl.Create( FSetup.sPipeName, 0, nil, PIPE_TIMEOUT, PIPE_TIMEOUT);
+ FTransport := streamtrans;
+ end;
+
+ trns_AnonPipes: begin
+ streamtrans := TAnonymousPipeTransportImpl.Create( FSetup.hAnonRead, FSetup.hAnonWrite, FALSE);
+ FTransport := streamtrans;
+ end;
+
+ else
+ raise Exception.Create('Unhandled endpoint transport');
+ end;
+ ASSERT( FTransport <> nil);
+
+ // layered transports are not really meant to be stacked upon each other
+ if (trns_Framed in FSetup.layered) then begin
+ FTransport := TFramedTransportImpl.Create( FTransport);
+ end
+ else if (trns_Buffered in FSetup.layered) and (streamtrans <> nil) then begin
+ FTransport := TBufferedTransportImpl.Create( streamtrans, 32); // small buffer to test read()
+ end;
+
+ if FSetup.useSSL and not canSSL then begin
+ raise Exception.Create('SSL/TLS not implemented');
+ end;
+
+ // create protocol instance, default to BinaryProtocol
+ case FSetup.protType of
+ prot_Binary : FProtocol := TBinaryProtocolImpl.Create( FTransport, BINARY_STRICT_READ, BINARY_STRICT_WRITE);
+ prot_JSON : FProtocol := TJSONProtocolImpl.Create( FTransport);
+ prot_Compact : FProtocol := TCompactProtocolImpl.Create( FTransport);
+ else
+ raise Exception.Create('Unhandled protocol');
+ end;
+
+ ASSERT( (FTransport <> nil) and (FProtocol <> nil));
+end;
+
+
+procedure TClientThread.ShutdownProtocolTransportStack;
+begin
+ try
+ FProtocol := nil;
+
+ if FTransport <> nil then begin
+ FTransport.Close;
+ FTransport := nil;
+ end;
+
+ finally
+ CoUninitialize;
+ end;
+end;
+
+
+{ TThreadConsole }
+
+constructor TThreadConsole.Create(AThread: TThread);
+begin
+ inherited Create;
+ FThread := AThread;
+end;
+
+procedure TThreadConsole.Write(const S: string);
+var
+ proc : TThreadProcedure;
+begin
+ proc := procedure
+ begin
+ Console.Write( S );
+ end;
+ TThread.Synchronize( FThread, proc);
+end;
+
+procedure TThreadConsole.WriteLine(const S: string);
+var
+ proc : TThreadProcedure;
+begin
+ proc := procedure
+ begin
+ Console.WriteLine( S );
+ end;
+ TThread.Synchronize( FThread, proc);
+end;
+
+initialization
+begin
+ TTestClient.FNumIteration := 1;
+ TTestClient.FNumThread := 1;
+end;
+
+end.
diff --git a/src/jaegertracing/thrift/lib/delphi/test/TestConstants.pas b/src/jaegertracing/thrift/lib/delphi/test/TestConstants.pas
new file mode 100644
index 000000000..ae3b3e8a3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/TestConstants.pas
@@ -0,0 +1,164 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+unit TestConstants;
+
+interface
+
+uses SysUtils;
+
+type
+ TKnownProtocol = (
+ prot_Binary, // default binary protocol
+ prot_JSON, // JSON protocol
+ prot_Compact
+ );
+
+ TServerType = (
+ srv_Simple,
+ srv_Nonblocking,
+ srv_Threadpool,
+ srv_Threaded
+ );
+
+ TEndpointTransport = (
+ trns_Sockets,
+ trns_MsxmlHttp,
+ trns_WinHttp,
+ trns_NamedPipes,
+ trns_AnonPipes,
+ trns_EvHttp // as listed on http://thrift.apache.org/test
+ );
+
+ TLayeredTransport = (
+ trns_None,
+ trns_Buffered,
+ trns_Framed
+ );
+
+ TLayeredTransports = set of TLayeredTransport;
+
+const
+ SERVER_TYPES : array[TServerType] of string
+ = ('Simple', 'Nonblocking', 'Threadpool', 'Threaded');
+
+ THRIFT_PROTOCOLS : array[TKnownProtocol] of string
+ = ('Binary', 'JSON', 'Compact');
+
+ LAYERED_TRANSPORTS : array[TLayeredTransport] of string
+ = ('None', 'Buffered', 'Framed');
+
+ ENDPOINT_TRANSPORTS : array[TEndpointTransport] of string
+ = ('Sockets', 'Http', 'WinHttp', 'Named Pipes','Anon Pipes', 'EvHttp');
+
+ // defaults are: read=false, write=true
+ BINARY_STRICT_READ = FALSE;
+ BINARY_STRICT_WRITE = FALSE;
+
+ HUGE_TEST_STRING = 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+ + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+ + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+ + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+ + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+ + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. '
+ + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+ + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+ + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+ + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+ + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+ + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. '
+ + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+ + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+ + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+ + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+ + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+ + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. '
+ + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+ + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+ + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+ + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+ + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+ + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. '
+ + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+ + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+ + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+ + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+ + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+ + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. '
+ + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+ + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+ + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+ + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+ + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+ + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. '
+ + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+ + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+ + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+ + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+ + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+ + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. '
+ + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+ + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+ + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+ + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+ + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+ + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. '
+ + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+ + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+ + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+ + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+ + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+ + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+ + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. ';
+
+
+function BytesToHex( const bytes : TBytes) : string;
+
+
+implementation
+
+
+function BytesToHex( const bytes : TBytes) : string;
+var i : Integer;
+begin
+ result := '';
+ for i := Low(bytes) to High(bytes) do begin
+ result := result + IntToHex(bytes[i],2);
+ end;
+end;
+
+
+end.
diff --git a/src/jaegertracing/thrift/lib/delphi/test/TestServer.pas b/src/jaegertracing/thrift/lib/delphi/test/TestServer.pas
new file mode 100644
index 000000000..2a80d52a7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/TestServer.pas
@@ -0,0 +1,684 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+unit TestServer;
+
+{$I ../src/Thrift.Defines.inc}
+{$WARN SYMBOL_PLATFORM OFF}
+
+{.$DEFINE RunEndless} // activate to interactively stress-test the server stop routines via Ctrl+C
+
+interface
+
+uses
+ Windows, SysUtils,
+ Generics.Collections,
+ Thrift.Server,
+ Thrift.Transport,
+ Thrift.Transport.Pipes,
+ Thrift.Protocol,
+ Thrift.Protocol.JSON,
+ Thrift.Protocol.Compact,
+ Thrift.Collections,
+ Thrift.Utils,
+ Thrift.Test,
+ Thrift,
+ TestConstants,
+ TestServerEvents,
+ ConsoleHelper,
+ Contnrs;
+
+type
+ TTestServer = class
+ public
+ type
+
+ ITestHandler = interface( TThriftTest.Iface )
+ procedure SetServer( const AServer : IServer );
+ procedure TestStop;
+ end;
+
+ TTestHandlerImpl = class( TInterfacedObject, ITestHandler )
+ private
+ FServer : IServer;
+ protected
+ procedure testVoid();
+ function testBool(thing: Boolean): Boolean;
+ function testString(const thing: string): string;
+ function testByte(thing: ShortInt): ShortInt;
+ function testI32(thing: Integer): Integer;
+ function testI64(const thing: Int64): Int64;
+ function testDouble(const thing: Double): Double;
+ function testBinary(const thing: TBytes): TBytes;
+ function testStruct(const thing: IXtruct): IXtruct;
+ function testNest(const thing: IXtruct2): IXtruct2;
+ function testMap(const thing: IThriftDictionary<Integer, Integer>): IThriftDictionary<Integer, Integer>;
+ function testStringMap(const thing: IThriftDictionary<string, string>): IThriftDictionary<string, string>;
+ function testSet(const thing: IHashSet<Integer>): IHashSet<Integer>;
+ function testList(const thing: IThriftList<Integer>): IThriftList<Integer>;
+ function testEnum(thing: TNumberz): TNumberz;
+ function testTypedef(const thing: Int64): Int64;
+ function testMapMap(hello: Integer): IThriftDictionary<Integer, IThriftDictionary<Integer, Integer>>;
+ function testInsanity(const argument: IInsanity): IThriftDictionary<Int64, IThriftDictionary<TNumberz, IInsanity>>;
+ function testMulti(arg0: ShortInt; arg1: Integer; const arg2: Int64; const arg3: IThriftDictionary<SmallInt, string>; arg4: TNumberz; const arg5: Int64): IXtruct;
+ procedure testException(const arg: string);
+ function testMultiException(const arg0: string; const arg1: string): IXtruct;
+ procedure testOneway(secondsToSleep: Integer);
+
+ procedure TestStop;
+ procedure SetServer( const AServer : IServer );
+ end;
+
+ class procedure PrintCmdLineHelp;
+ class procedure InvalidArgs;
+
+ class procedure LaunchAnonPipeChild( const app : string; const transport : IAnonymousPipeServerTransport);
+ class procedure Execute( const args: array of string);
+ end;
+
+implementation
+
+
+var g_Handler : TTestServer.ITestHandler = nil;
+
+
+function MyConsoleEventHandler( dwCtrlType : DWORD) : BOOL; stdcall;
+// Note that this Handler procedure is called from another thread
+var handler : TTestServer.ITestHandler;
+begin
+ result := TRUE;
+ try
+ case dwCtrlType of
+ CTRL_C_EVENT : Console.WriteLine( 'Ctrl+C pressed');
+ CTRL_BREAK_EVENT : Console.WriteLine( 'Ctrl+Break pressed');
+ CTRL_CLOSE_EVENT : Console.WriteLine( 'Received CloseTask signal');
+ CTRL_LOGOFF_EVENT : Console.WriteLine( 'Received LogOff signal');
+ CTRL_SHUTDOWN_EVENT : Console.WriteLine( 'Received Shutdown signal');
+ else
+ Console.WriteLine( 'Received console event #'+IntToStr(Integer(dwCtrlType)));
+ end;
+
+ handler := g_Handler;
+ if handler <> nil then handler.TestStop;
+
+ except
+ // catch all
+ end;
+end;
+
+
+{ TTestServer.TTestHandlerImpl }
+
+procedure TTestServer.TTestHandlerImpl.SetServer( const AServer: IServer);
+begin
+ FServer := AServer;
+end;
+
+function TTestServer.TTestHandlerImpl.testByte(thing: ShortInt): ShortInt;
+begin
+ Console.WriteLine('testByte("' + IntToStr( thing) + '")');
+ Result := thing;
+end;
+
+function TTestServer.TTestHandlerImpl.testDouble( const thing: Double): Double;
+begin
+ Console.WriteLine('testDouble("' + FloatToStr( thing ) + '")');
+ Result := thing;
+end;
+
+function TTestServer.TTestHandlerImpl.testBinary(const thing: TBytes): TBytes;
+begin
+ Console.WriteLine('testBinary('+IntToStr(Length(thing)) + ' bytes)');
+ Result := thing;
+end;
+
+function TTestServer.TTestHandlerImpl.testEnum(thing: TNumberz): TNumberz;
+begin
+ Console.WriteLine('testEnum(' + EnumUtils<TNumberz>.ToString(Ord(thing)) + ')');
+ Result := thing;
+end;
+
+procedure TTestServer.TTestHandlerImpl.testException(const arg: string);
+begin
+ Console.WriteLine('testException(' + arg + ')');
+ if ( arg = 'Xception') then
+ begin
+ raise TXception.Create( 1001, arg);
+ end;
+
+ if (arg = 'TException') then
+ begin
+ raise TException.Create('TException');
+ end;
+
+ // else do not throw anything
+end;
+
+function TTestServer.TTestHandlerImpl.testI32(thing: Integer): Integer;
+begin
+ Console.WriteLine('testI32("' + IntToStr( thing) + '")');
+ Result := thing;
+end;
+
+function TTestServer.TTestHandlerImpl.testI64( const thing: Int64): Int64;
+begin
+ Console.WriteLine('testI64("' + IntToStr( thing) + '")');
+ Result := thing;
+end;
+
+function TTestServer.TTestHandlerImpl.testInsanity(
+ const argument: IInsanity): IThriftDictionary<Int64, IThriftDictionary<TNumberz, IInsanity>>;
+var
+ looney : IInsanity;
+ first_map : IThriftDictionary<TNumberz, IInsanity>;
+ second_map : IThriftDictionary<TNumberz, IInsanity>;
+ insane : IThriftDictionary<Int64, IThriftDictionary<TNumberz, IInsanity>>;
+
+begin
+ Console.Write('testInsanity(');
+ if argument <> nil then Console.Write(argument.ToString);
+ Console.WriteLine(')');
+
+
+ (**
+ * So you think you've got this all worked, out eh?
+ *
+ * Creates a the returned map with these values and prints it out:
+ * { 1 => { 2 => argument,
+ * 3 => argument,
+ * },
+ * 2 => { 6 => <empty Insanity struct>, },
+ * }
+ * @return map<UserId, map<Numberz,Insanity>> - a map with the above values
+ *)
+
+ first_map := TThriftDictionaryImpl<TNumberz, IInsanity>.Create;
+ second_map := TThriftDictionaryImpl<TNumberz, IInsanity>.Create;
+
+ first_map.AddOrSetValue( TNumberz.TWO, argument);
+ first_map.AddOrSetValue( TNumberz.THREE, argument);
+
+ looney := TInsanityImpl.Create;
+ second_map.AddOrSetValue( TNumberz.SIX, looney);
+
+ insane := TThriftDictionaryImpl<Int64, IThriftDictionary<TNumberz, IInsanity>>.Create;
+
+ insane.AddOrSetValue( 1, first_map);
+ insane.AddOrSetValue( 2, second_map);
+
+ Result := insane;
+end;
+
+function TTestServer.TTestHandlerImpl.testList( const thing: IThriftList<Integer>): IThriftList<Integer>;
+begin
+ Console.Write('testList(');
+ if thing <> nil then Console.Write(thing.ToString);
+ Console.WriteLine(')');
+ Result := thing;
+end;
+
+function TTestServer.TTestHandlerImpl.testMap(
+ const thing: IThriftDictionary<Integer, Integer>): IThriftDictionary<Integer, Integer>;
+begin
+ Console.Write('testMap(');
+ if thing <> nil then Console.Write(thing.ToString);
+ Console.WriteLine(')');
+ Result := thing;
+end;
+
+function TTestServer.TTestHandlerImpl.TestMapMap(
+ hello: Integer): IThriftDictionary<Integer, IThriftDictionary<Integer, Integer>>;
+var
+ mapmap : IThriftDictionary<Integer, IThriftDictionary<Integer, Integer>>;
+ pos : IThriftDictionary<Integer, Integer>;
+ neg : IThriftDictionary<Integer, Integer>;
+ i : Integer;
+begin
+ Console.WriteLine('testMapMap(' + IntToStr( hello) + ')');
+ mapmap := TThriftDictionaryImpl<Integer, IThriftDictionary<Integer, Integer>>.Create;
+ pos := TThriftDictionaryImpl<Integer, Integer>.Create;
+ neg := TThriftDictionaryImpl<Integer, Integer>.Create;
+
+ for i := 1 to 4 do
+ begin
+ pos.AddOrSetValue( i, i);
+ neg.AddOrSetValue( -i, -i);
+ end;
+
+ mapmap.AddOrSetValue(4, pos);
+ mapmap.AddOrSetValue( -4, neg);
+
+ Result := mapmap;
+end;
+
+function TTestServer.TTestHandlerImpl.testMulti(arg0: ShortInt; arg1: Integer;
+ const arg2: Int64; const arg3: IThriftDictionary<SmallInt, string>;
+ arg4: TNumberz; const arg5: Int64): IXtruct;
+var
+ hello : IXtruct;
+begin
+ Console.WriteLine('testMulti()');
+ hello := TXtructImpl.Create;
+ hello.String_thing := 'Hello2';
+ hello.Byte_thing := arg0;
+ hello.I32_thing := arg1;
+ hello.I64_thing := arg2;
+ Result := hello;
+end;
+
+function TTestServer.TTestHandlerImpl.testMultiException( const arg0, arg1: string): IXtruct;
+var
+ x2 : TXception2;
+begin
+ Console.WriteLine('testMultiException(' + arg0 + ', ' + arg1 + ')');
+ if ( arg0 = 'Xception') then begin
+ raise TXception.Create( 1001, 'This is an Xception'); // test the new rich CTOR
+ end;
+
+ if ( arg0 = 'Xception2') then begin
+ x2 := TXception2.Create; // the old way still works too?
+ x2.ErrorCode := 2002;
+ x2.Struct_thing := TXtructImpl.Create;
+ x2.Struct_thing.String_thing := 'This is an Xception2';
+ x2.UpdateMessageProperty;
+ raise x2;
+ end;
+
+ Result := TXtructImpl.Create;
+ Result.String_thing := arg1;
+end;
+
+function TTestServer.TTestHandlerImpl.testNest( const thing: IXtruct2): IXtruct2;
+begin
+ Console.Write('testNest(');
+ if thing <> nil then Console.Write(thing.ToString);
+ Console.WriteLine(')');
+
+ Result := thing;
+end;
+
+procedure TTestServer.TTestHandlerImpl.testOneway(secondsToSleep: Integer);
+begin
+ Console.WriteLine('testOneway(' + IntToStr( secondsToSleep )+ '), sleeping...');
+ Sleep(secondsToSleep * 1000);
+ Console.WriteLine('testOneway finished');
+end;
+
+function TTestServer.TTestHandlerImpl.testSet( const thing: IHashSet<Integer>):IHashSet<Integer>;
+begin
+ Console.Write('testSet(');
+ if thing <> nil then Console.Write(thing.ToString);
+ Console.WriteLine(')');;
+
+ Result := thing;
+end;
+
+procedure TTestServer.TTestHandlerImpl.testStop;
+begin
+ if FServer <> nil then begin
+ FServer.Stop;
+ end;
+end;
+
+function TTestServer.TTestHandlerImpl.testBool(thing: Boolean): Boolean;
+begin
+ Console.WriteLine('testBool(' + BoolToStr(thing,true) + ')');
+ Result := thing;
+end;
+
+function TTestServer.TTestHandlerImpl.testString( const thing: string): string;
+begin
+ Console.WriteLine('teststring("' + thing + '")');
+ Result := thing;
+end;
+
+function TTestServer.TTestHandlerImpl.testStringMap(
+ const thing: IThriftDictionary<string, string>): IThriftDictionary<string, string>;
+begin
+ Console.Write('testStringMap(');
+ if thing <> nil then Console.Write(thing.ToString);
+ Console.WriteLine(')');
+
+ Result := thing;
+end;
+
+function TTestServer.TTestHandlerImpl.testTypedef( const thing: Int64): Int64;
+begin
+ Console.WriteLine('testTypedef(' + IntToStr( thing) + ')');
+ Result := thing;
+end;
+
+procedure TTestServer.TTestHandlerImpl.TestVoid;
+begin
+ Console.WriteLine('testVoid()');
+end;
+
+function TTestServer.TTestHandlerImpl.testStruct( const thing: IXtruct): IXtruct;
+begin
+ Console.Write('testStruct(');
+ if thing <> nil then Console.Write(thing.ToString);
+ Console.WriteLine(')');
+
+ Result := thing;
+end;
+
+
+{ TTestServer }
+
+
+class procedure TTestServer.PrintCmdLineHelp;
+const HELPTEXT = ' [options]'#10
+ + #10
+ + 'Allowed options:'#10
+ + ' -h [ --help ] produce help message'#10
+ + ' --port arg (=9090) Port number to listen'#10
+ + ' --domain-socket arg Unix Domain Socket (e.g. /tmp/ThriftTest.thrift)'#10
+ + ' --named-pipe arg Windows Named Pipe (e.g. MyThriftPipe)'#10
+ + ' --server-type arg (=simple) type of server, "simple", "thread-pool",'#10
+ + ' "threaded", or "nonblocking"'#10
+ + ' --transport arg (=socket) transport: buffered, framed, http, anonpipe'#10
+ + ' --protocol arg (=binary) protocol: binary, compact, json'#10
+ + ' --ssl Encrypted Transport using SSL'#10
+ + ' --processor-events processor-events'#10
+ + ' -n [ --workers ] arg (=4) Number of thread pools workers. Only valid for'#10
+ + ' thread-pool server type'#10
+ ;
+begin
+ Console.WriteLine( ChangeFileExt(ExtractFileName(ParamStr(0)),'') + HELPTEXT);
+end;
+
+class procedure TTestServer.InvalidArgs;
+begin
+ Console.WriteLine( 'Invalid args.');
+ Console.WriteLine( ChangeFileExt(ExtractFileName(ParamStr(0)),'') + ' -h for more information');
+ Abort;
+end;
+
+class procedure TTestServer.LaunchAnonPipeChild( const app : string; const transport : IAnonymousPipeServerTransport);
+//Launch child process and pass R/W anonymous pipe handles on cmd line.
+//This is a simple example and does not include elevation or other
+//advanced features.
+var pi : PROCESS_INFORMATION;
+ si : STARTUPINFO;
+ sArg, sHandles, sCmdLine : string;
+ i : Integer;
+begin
+ GetStartupInfo( si); //set startupinfo for the spawned process
+
+ // preformat handles args
+ sHandles := Format( '%d %d',
+ [ Integer(transport.ClientAnonRead),
+ Integer(transport.ClientAnonWrite)]);
+
+ // pass all settings to client
+ sCmdLine := app;
+ for i := 1 to ParamCount do begin
+ sArg := ParamStr(i);
+
+ // add anonymous handles and quote strings where appropriate
+ if sArg = '-anon'
+ then sArg := sArg +' '+ sHandles
+ else begin
+ if Pos(' ',sArg) > 0
+ then sArg := '"'+sArg+'"';
+ end;;
+
+ sCmdLine := sCmdLine +' '+ sArg;
+ end;
+
+ // spawn the child process
+ Console.WriteLine('Starting client '+sCmdLine);
+ Win32Check( CreateProcess( nil, PChar(sCmdLine), nil,nil,TRUE,0,nil,nil,si,pi));
+
+ CloseHandle( pi.hThread);
+ CloseHandle( pi.hProcess);
+end;
+
+
+class procedure TTestServer.Execute( const args: array of string);
+var
+ Port : Integer;
+ ServerEvents : Boolean;
+ sPipeName : string;
+ testHandler : ITestHandler;
+ testProcessor : IProcessor;
+ ServerTrans : IServerTransport;
+ ServerEngine : IServer;
+ anonymouspipe : IAnonymousPipeServerTransport;
+ namedpipe : INamedPipeServerTransport;
+ TransportFactory : ITransportFactory;
+ ProtocolFactory : IProtocolFactory;
+ i, numWorker : Integer;
+ s : string;
+ protType : TKnownProtocol;
+ servertype : TServerType;
+ endpoint : TEndpointTransport;
+ layered : TLayeredTransports;
+ UseSSL : Boolean; // include where appropriate (TLayeredTransport?)
+begin
+ try
+ ServerEvents := FALSE;
+ protType := prot_Binary;
+ servertype := srv_Simple;
+ endpoint := trns_Sockets;
+ layered := [];
+ UseSSL := FALSE;
+ Port := 9090;
+ sPipeName := '';
+ numWorker := 4;
+
+ i := 0;
+ while ( i < Length(args) ) do begin
+ s := args[i];
+ Inc(i);
+
+ // Allowed options:
+ if (s = '-h') or (s = '--help') then begin
+ // -h [ --help ] produce help message
+ PrintCmdLineHelp;
+ Exit;
+ end
+ else if (s = '--port') then begin
+ // --port arg (=9090) Port number to listen
+ s := args[i];
+ Inc(i);
+ Port := StrToIntDef( s, Port);
+ end
+ else if (s = '--domain-socket') then begin
+ // --domain-socket arg Unix Domain Socket (e.g. /tmp/ThriftTest.thrift)
+ raise Exception.Create('domain-socket not supported');
+ end
+ else if (s = '--named-pipe') then begin
+ // --named-pipe arg Windows Named Pipe (e.g. MyThriftPipe)
+ endpoint := trns_NamedPipes;
+ sPipeName := args[i]; // -pipe <name>
+ Inc( i );
+ end
+ else if (s = '--server-type') then begin
+ // --server-type arg (=simple) type of server,
+ // arg = "simple", "thread-pool", "threaded", or "nonblocking"
+ s := args[i];
+ Inc(i);
+
+ if s = 'simple' then servertype := srv_Simple
+ else if s = 'thread-pool' then servertype := srv_Threadpool
+ else if s = 'threaded' then servertype := srv_Threaded
+ else if s = 'nonblocking' then servertype := srv_Nonblocking
+ else InvalidArgs;
+ end
+ else if (s = '--transport') then begin
+ // --transport arg (=buffered) transport: buffered, framed, http
+ s := args[i];
+ Inc(i);
+
+ if s = 'buffered' then Include( layered, trns_Buffered)
+ else if s = 'framed' then Include( layered, trns_Framed)
+ else if s = 'http' then endpoint := trns_MsxmlHttp
+ else if s = 'winhttp' then endpoint := trns_WinHttp
+ else if s = 'anonpipe' then endpoint := trns_AnonPipes
+ else InvalidArgs;
+ end
+ else if (s = '--protocol') then begin
+ // --protocol arg (=binary) protocol: binary, compact, json
+ s := args[i];
+ Inc(i);
+
+ if s = 'binary' then protType := prot_Binary
+ else if s = 'compact' then protType := prot_Compact
+ else if s = 'json' then protType := prot_JSON
+ else InvalidArgs;
+ end
+ else if (s = '--ssl') then begin
+ // --ssl Encrypted Transport using SSL
+ UseSSL := TRUE;
+ end
+ else if (s = '--processor-events') then begin
+ // --processor-events processor-events
+ ServerEvents := TRUE;
+ end
+ else if (s = '-n') or (s = '--workers') then begin
+ // -n [ --workers ] arg (=4) Number of thread pools workers.
+ // Only valid for thread-pool server type
+ s := args[i];
+ numWorker := StrToIntDef(s,0);
+ if numWorker > 0
+ then Inc(i)
+ else numWorker := 4;
+ end
+ else begin
+ InvalidArgs;
+ end;
+ end;
+
+
+ Console.WriteLine('Server configuration: ');
+
+ // create protocol factory, default to BinaryProtocol
+ case protType of
+ prot_Binary : ProtocolFactory := TBinaryProtocolImpl.TFactory.Create( BINARY_STRICT_READ, BINARY_STRICT_WRITE);
+ prot_JSON : ProtocolFactory := TJSONProtocolImpl.TFactory.Create;
+ prot_Compact : ProtocolFactory := TCompactProtocolImpl.TFactory.Create;
+ else
+ raise Exception.Create('Unhandled protocol');
+ end;
+ ASSERT( ProtocolFactory <> nil);
+ Console.WriteLine('- '+THRIFT_PROTOCOLS[protType]+' protocol');
+
+ case endpoint of
+
+ trns_Sockets : begin
+ Console.WriteLine('- sockets (port '+IntToStr(port)+')');
+ if (trns_Buffered in layered) then Console.WriteLine('- buffered');
+ servertrans := TServerSocketImpl.Create( Port, 0, (trns_Buffered in layered));
+ end;
+
+ trns_MsxmlHttp,
+ trns_WinHttp : begin
+ raise Exception.Create('HTTP server transport not implemented');
+ end;
+
+ trns_NamedPipes : begin
+ Console.WriteLine('- named pipe ('+sPipeName+')');
+ namedpipe := TNamedPipeServerTransportImpl.Create( sPipeName, 4096, PIPE_UNLIMITED_INSTANCES);
+ servertrans := namedpipe;
+ end;
+
+ trns_AnonPipes : begin
+ Console.WriteLine('- anonymous pipes');
+ anonymouspipe := TAnonymousPipeServerTransportImpl.Create;
+ servertrans := anonymouspipe;
+ end
+
+ else
+ raise Exception.Create('Unhandled endpoint transport');
+ end;
+ ASSERT( servertrans <> nil);
+
+ if UseSSL then begin
+ raise Exception.Create('SSL not implemented');
+ end;
+
+ if (trns_Framed in layered) then begin
+ Console.WriteLine('- framed transport');
+ TransportFactory := TFramedTransportImpl.TFactory.Create
+ end
+ else begin
+ TransportFactory := TTransportFactoryImpl.Create;
+ end;
+ ASSERT( TransportFactory <> nil);
+
+ testHandler := TTestHandlerImpl.Create;
+ testProcessor := TThriftTest.TProcessorImpl.Create( testHandler );
+
+ case servertype of
+ srv_Simple : begin
+ ServerEngine := TSimpleServer.Create( testProcessor, ServerTrans, TransportFactory, ProtocolFactory);
+ end;
+
+ srv_Nonblocking : begin
+ raise Exception.Create(SERVER_TYPES[servertype]+' server not implemented');
+ end;
+
+ srv_Threadpool,
+ srv_Threaded: begin
+ if numWorker > 1 then {use here};
+ raise Exception.Create(SERVER_TYPES[servertype]+' server not implemented');
+ end;
+
+ else
+ raise Exception.Create('Unhandled server type');
+ end;
+ ASSERT( ServerEngine <> nil);
+
+ testHandler.SetServer( ServerEngine);
+
+ // test events?
+ if ServerEvents then begin
+ Console.WriteLine('- server events test enabled');
+ ServerEngine.ServerEvents := TServerEventsImpl.Create;
+ end;
+
+ // start the client now when we have the anon handles, but before the server starts
+ if endpoint = trns_AnonPipes
+ then LaunchAnonPipeChild( ExtractFilePath(ParamStr(0))+'client.exe', anonymouspipe);
+
+ // install Ctrl+C handler before the server starts
+ g_Handler := testHandler;
+ SetConsoleCtrlHandler( @MyConsoleEventHandler, TRUE);
+
+ Console.WriteLine('');
+ repeat
+ Console.WriteLine('Starting the server ...');
+ serverEngine.Serve;
+ until {$IFDEF RunEndless} FALSE {$ELSE} TRUE {$ENDIF};
+
+ testHandler.SetServer( nil);
+ g_Handler := nil;
+
+ except
+ on E: EAbort do raise;
+ on E: Exception do begin
+ Console.WriteLine( E.Message + #10 + E.StackTrace );
+ end;
+ end;
+ Console.WriteLine( 'done.');
+end;
+
+
+end.
diff --git a/src/jaegertracing/thrift/lib/delphi/test/TestServerEvents.pas b/src/jaegertracing/thrift/lib/delphi/test/TestServerEvents.pas
new file mode 100644
index 000000000..2208cd4ba
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/TestServerEvents.pas
@@ -0,0 +1,174 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+unit TestServerEvents;
+
+interface
+
+uses
+ SysUtils,
+ Thrift,
+ Thrift.Protocol,
+ Thrift.Transport,
+ Thrift.Server,
+ ConsoleHelper;
+
+type
+ TRequestEventsImpl = class( TInterfacedObject, IRequestEvents)
+ protected
+ FStart : TDateTime;
+ // IRequestProcessingEvents
+ procedure PreRead;
+ procedure PostRead;
+ procedure PreWrite;
+ procedure PostWrite;
+ procedure OnewayComplete;
+ procedure UnhandledError( const e : Exception);
+ procedure CleanupContext;
+ public
+ constructor Create;
+ end;
+
+
+ TProcessorEventsImpl = class( TInterfacedObject, IProcessorEvents)
+ protected
+ FReqs : Integer;
+ // IProcessorEvents
+ procedure Processing( const transport : ITransport);
+ function CreateRequestContext( const aFunctionName : string) : IRequestEvents;
+ procedure CleanupContext;
+ public
+ constructor Create;
+ end;
+
+
+ TServerEventsImpl = class( TInterfacedObject, IServerEvents)
+ protected
+ // IServerEvents
+ procedure PreServe;
+ procedure PreAccept;
+ function CreateProcessingContext( const input, output : IProtocol) : IProcessorEvents;
+ end;
+
+
+implementation
+
+{ TServerEventsImpl }
+
+procedure TServerEventsImpl.PreServe;
+begin
+ Console.WriteLine('ServerEvents: Server starting to serve requests');
+end;
+
+
+procedure TServerEventsImpl.PreAccept;
+begin
+ Console.WriteLine('ServerEvents: Server transport is ready to accept incoming calls');
+end;
+
+
+function TServerEventsImpl.CreateProcessingContext(const input, output: IProtocol): IProcessorEvents;
+begin
+ result := TProcessorEventsImpl.Create;
+end;
+
+
+{ TProcessorEventsImpl }
+
+constructor TProcessorEventsImpl.Create;
+begin
+ inherited Create;
+ FReqs := 0;
+ Console.WriteLine('ProcessorEvents: Client connected, processing begins');
+end;
+
+procedure TProcessorEventsImpl.Processing(const transport: ITransport);
+begin
+ Console.WriteLine('ProcessorEvents: Processing of incoming request begins');
+end;
+
+
+function TProcessorEventsImpl.CreateRequestContext( const aFunctionName: string): IRequestEvents;
+begin
+ result := TRequestEventsImpl.Create;
+ Inc( FReqs);
+end;
+
+
+procedure TProcessorEventsImpl.CleanupContext;
+begin
+ Console.WriteLine( 'ProcessorEvents: completed after handling '+IntToStr(FReqs)+' requests.');
+end;
+
+
+{ TRequestEventsImpl }
+
+
+constructor TRequestEventsImpl.Create;
+begin
+ inherited Create;
+ FStart := Now;
+ Console.WriteLine('RequestEvents: New request');
+end;
+
+
+procedure TRequestEventsImpl.PreRead;
+begin
+ Console.WriteLine('RequestEvents: Reading request message ...');
+end;
+
+
+procedure TRequestEventsImpl.PostRead;
+begin
+ Console.WriteLine('RequestEvents: Reading request message completed');
+end;
+
+procedure TRequestEventsImpl.PreWrite;
+begin
+ Console.WriteLine('RequestEvents: Writing response message ...');
+end;
+
+
+procedure TRequestEventsImpl.PostWrite;
+begin
+ Console.WriteLine('RequestEvents: Writing response message completed');
+end;
+
+
+procedure TRequestEventsImpl.OnewayComplete;
+begin
+ Console.WriteLine('RequestEvents: Oneway message processed');
+end;
+
+
+procedure TRequestEventsImpl.UnhandledError(const e: Exception);
+begin
+ Console.WriteLine('RequestEvents: Unhandled exception of type '+e.classname);
+end;
+
+
+procedure TRequestEventsImpl.CleanupContext;
+var millis : Double;
+begin
+ millis := (Now - FStart) * (24*60*60*1000);
+ Console.WriteLine( 'Request processing completed in '+IntToStr(Round(millis))+' ms');
+end;
+
+
+end.
diff --git a/src/jaegertracing/thrift/lib/delphi/test/client.dpr b/src/jaegertracing/thrift/lib/delphi/test/client.dpr
new file mode 100644
index 000000000..83727f619
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/client.dpr
@@ -0,0 +1,77 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+
+program client;
+
+{$APPTYPE CONSOLE}
+
+uses
+ SysUtils,
+ DataFactory in 'Performance\DataFactory.pas',
+ PerfTests in 'Performance\PerfTests.pas',
+ TestClient in 'TestClient.pas',
+ Thrift.Test, // in 'gen-delphi\Thrift.Test.pas',
+ Thrift in '..\src\Thrift.pas',
+ Thrift.Transport in '..\src\Thrift.Transport.pas',
+ Thrift.Socket in '..\src\Thrift.Socket.pas',
+ Thrift.Exception in '..\src\Thrift.Exception.pas',
+ Thrift.Transport.Pipes in '..\src\Thrift.Transport.Pipes.pas',
+ Thrift.Transport.WinHTTP in '..\src\Thrift.Transport.WinHTTP.pas',
+ Thrift.Transport.MsxmlHTTP in '..\src\Thrift.Transport.MsxmlHTTP.pas',
+ Thrift.Protocol in '..\src\Thrift.Protocol.pas',
+ Thrift.Protocol.JSON in '..\src\Thrift.Protocol.JSON.pas',
+ Thrift.Protocol.Compact in '..\src\Thrift.Protocol.Compact.pas',
+ Thrift.Protocol.Multiplex in '..\src\Thrift.Protocol.Multiplex.pas',
+ Thrift.Collections in '..\src\Thrift.Collections.pas',
+ Thrift.Server in '..\src\Thrift.Server.pas',
+ Thrift.Stream in '..\src\Thrift.Stream.pas',
+ Thrift.TypeRegistry in '..\src\Thrift.TypeRegistry.pas',
+ Thrift.WinHTTP in '..\src\Thrift.WinHTTP.pas',
+ Thrift.Utils in '..\src\Thrift.Utils.pas';
+
+var
+ nParamCount : Integer;
+ args : array of string;
+ i : Integer;
+ arg : string;
+
+begin
+ try
+ Writeln( 'Delphi TestClient '+Thrift.Version);
+ nParamCount := ParamCount;
+ SetLength( args, nParamCount);
+ for i := 1 to nParamCount do begin
+ arg := ParamStr( i );
+ args[i-1] := arg;
+ end;
+
+ ExitCode := TTestClient.Execute( args);
+
+ except
+ on E: EAbort do begin
+ ExitCode := $FF;
+ end;
+ on E: Exception do begin
+ Writeln(E.ClassName, ': ', E.Message);
+ ExitCode := $FF;
+ end;
+ end;
+end.
+
diff --git a/src/jaegertracing/thrift/lib/delphi/test/codegen/README.md b/src/jaegertracing/thrift/lib/delphi/test/codegen/README.md
new file mode 100644
index 000000000..a0145890f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/codegen/README.md
@@ -0,0 +1,28 @@
+How to use the test case:
+----------------------------------------------
+- copy and the template batch file
+- open the batch file and adjust configuration as necessary
+- run the batch
+
+
+Configuration:
+----------------------------------------------
+SVNWORKDIR
+should point to the Thrift working copy root
+
+MY_THRIFT_FILES
+can be set to point to a folder with more thrift IDL files.
+If you don't have any such files, just leave the setting blank.
+
+BIN
+Local MSYS binary folder. Your THRIFT.EXE is installed here.
+
+MINGW_BIN
+Local MinGW bin folder. Contains DLL files required by THRIFT.EXE
+
+DCC
+Identifies the Delphi Command Line compiler (dcc32.exe)
+To be configuired only, if the default is not suitable.
+
+----------------------------------------------
+*EOF* \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/delphi/test/codegen/run-Pascal-Codegen-Tests.bat.tmpl b/src/jaegertracing/thrift/lib/delphi/test/codegen/run-Pascal-Codegen-Tests.bat.tmpl
new file mode 100644
index 000000000..dbab0ae7c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/codegen/run-Pascal-Codegen-Tests.bat.tmpl
@@ -0,0 +1,173 @@
+REM /*
+REM * Licensed to the Apache Software Foundation (ASF) under one
+REM * or more contributor license agreements. See the NOTICE file
+REM * distributed with this work for additional information
+REM * regarding copyright ownership. The ASF licenses this file
+REM * to you under the Apache License, Version 2.0 (the
+REM * "License"); you may not use this file except in compliance
+REM * with the License. You may obtain a copy of the License at
+REM *
+REM * http://www.apache.org/licenses/LICENSE-2.0
+REM *
+REM * Unless required by applicable law or agreed to in writing,
+REM * software distributed under the License is distributed on an
+REM * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+REM * KIND, either express or implied. See the License for the
+REM * specific language governing permissions and limitations
+REM * under the License.
+REM */
+@echo off
+if ""=="%1" goto CONFIG
+goto HANDLEDIR
+
+REM -----------------------------------------------------
+:CONFIG
+REM -----------------------------------------------------
+
+rem * CONFIGURATION BEGIN
+rem * configuration settings, adjust as necessary to meet your system setup
+set SVNWORKDIR=
+set MY_THRIFT_FILES=
+set BIN=C:\MSys10\local\bin
+set MINGW_BIN=C:\MinGW\bin
+set DCC=
+set SUBDIR=gen-delphi
+rem * CONFIGURATION END
+
+
+REM -----------------------------------------------------
+:START
+REM -----------------------------------------------------
+
+rem * configured?
+if "%SVNWORKDIR%"=="" goto CONFIG_ERROR
+
+rem * try to find dcc32.exe
+echo Looking for dcc32.exe ...
+if not exist "%DCC%" set DCC=%ProgramFiles%\Embarcadero\RAD Studio\8.0\bin\dcc32.exe
+if not exist "%DCC%" set DCC=%ProgramFiles(x86)%\Embarcadero\RAD Studio\8.0\bin\dcc32.exe
+if not exist "%DCC%" goto CONFIG_ERROR
+echo Found %DCC%
+echo.
+
+rem * some helpers
+set PATH=%BIN%;%MINGW_BIN%;%PATH%
+set TARGET=%SVNWORKDIR%\..\thrift-testing
+set SOURCE=%SVNWORKDIR%
+set TESTAPP=TestProject
+set UNITSEARCH=%SOURCE%\lib\pas\src;%SOURCE%\lib\delphi\src
+set OUTDCU="%TARGET%\dcu"
+set LOGFILE=%TARGET%\%SUBDIR%\codegen.log
+
+rem * create and/or empty target dirs
+if not exist "%TARGET%" md "%TARGET%"
+if not exist "%TARGET%\%SUBDIR%" md "%TARGET%\%SUBDIR%"
+if not exist "%OUTDCU%" md "%OUTDCU%"
+if exist "%TARGET%\*.thrift" del "%TARGET%\*.thrift" /Q
+if exist "%TARGET%\%SUBDIR%\*.*" del "%TARGET%\%SUBDIR%\*.*" /Q
+if exist "%OUTDCU%\*.*" del "%OUTDCU%\*.*" /Q
+
+rem * recurse through thrift WC and "my thrift files" folder
+rem * copies all .thrift files into thrift-testing
+call %0 %SOURCE%
+if not "%MY_THRIFT_FILES%"=="" call %0 %MY_THRIFT_FILES%
+
+rem * compile all thrift files, generate PAS and C++ code
+echo.
+echo Generating code, please wait ...
+cd "%TARGET%"
+for %%a in (*.thrift) do "%BIN%\thrift.exe" -v --gen delphi:register_types,constprefix,events,xmldoc "%%a" 2>> "%LOGFILE%"
+REM * for %%a in (*.thrift) do "%BIN%\thrift.exe" -v --gen cpp "%%a" >> NUL:
+cmd /c start notepad "%LOGFILE%"
+cd ..
+
+rem * check for special Delphi testcases being processed
+if not exist "%TARGET%\%SUBDIR%\ReservedKeywords.pas" goto TESTCASE_MISSING
+
+
+rem * generate a minimal DPR file that uses all generated pascal units
+cd "%TARGET%\%SUBDIR%\"
+if exist inherited.* ren inherited.* _inherited.*
+echo program %TESTAPP%; > %TESTAPP%.dpr
+echo {$APPTYPE CONSOLE} >> %TESTAPP%.dpr
+echo. >> %TESTAPP%.dpr
+echo uses >> %TESTAPP%.dpr
+for %%a in (*.pas) do echo %%~na, >> %TESTAPP%.dpr
+echo Windows, Classes, SysUtils; >> %TESTAPP%.dpr
+echo. >> %TESTAPP%.dpr
+echo begin >> %TESTAPP%.dpr
+echo Writeln('Successfully compiled!'); >> %TESTAPP%.dpr
+echo Writeln('List of units:'); >> %TESTAPP%.dpr
+for %%a in (*.pas) do echo Write('%%~na':30,'':10); >> %TESTAPP%.dpr
+echo Writeln; >> %TESTAPP%.dpr
+echo end. >> %TESTAPP%.dpr
+echo. >> %TESTAPP%.dpr
+cd ..\..
+
+rem * try to compile the DPR
+rem * this should not throw any errors, warnings or hints
+"%DCC%" -B "%TARGET%\%SUBDIR%\%TESTAPP%" -U"%UNITSEARCH%" -I"%UNITSEARCH%" -N"%OUTDCU%" -E"%TARGET%\%SUBDIR%"
+dir "%TARGET%\%SUBDIR%\%TESTAPP%.exe"
+if not exist "%TARGET%\%SUBDIR%\%TESTAPP%.exe" goto CODEGEN_FAILED
+echo.
+echo -----------------------------------------------------------------
+echo The compiled program is now executed. If it hangs or crashes, we
+echo have a serious problem with the generated code. Expected output
+echo is "Successfully compiled:" followed by a list of generated units.
+echo -----------------------------------------------------------------
+"%TARGET%\%SUBDIR%\%TESTAPP%.exe"
+echo -----------------------------------------------------------------
+echo.
+pause
+GOTO EOF
+
+REM -----------------------------------------------------
+:DXE_NOT_FOUND
+REM -----------------------------------------------------
+echo Delphi Compiler (dcc32.exe) not found.
+echo Please check the "DCC" setting in this batch.
+echo.
+cmd /c start notepad README.MD
+cmd /c start notepad %0
+pause
+GOTO EOF
+
+
+REM -----------------------------------------------------
+:CONFIG_ERROR
+REM -----------------------------------------------------
+echo Missing, incomplete or wrong configuration settings!
+cmd /c start notepad README.MD
+cmd /c start notepad %0
+pause
+GOTO EOF
+
+
+REM -----------------------------------------------------
+:TESTCASE_MISSING
+REM -----------------------------------------------------
+echo Missing an expected Delphi testcase!
+pause
+GOTO EOF
+
+
+REM -----------------------------------------------------
+:CODEGEN_FAILED
+REM -----------------------------------------------------
+echo Code generation FAILED!
+pause
+GOTO EOF
+
+
+REM -----------------------------------------------------
+:HANDLEDIR
+REM -----------------------------------------------------
+REM echo %1
+for /D %%a in (%1\*) do call %0 %%a
+if exist "%1\*.thrift" copy /b "%1\*.thrift" "%TARGET%\*.*"
+GOTO EOF
+
+
+REM -----------------------------------------------------
+:EOF
+REM -----------------------------------------------------
diff --git a/src/jaegertracing/thrift/lib/delphi/test/keywords/ReservedIncluded.thrift b/src/jaegertracing/thrift/lib/delphi/test/keywords/ReservedIncluded.thrift
new file mode 100644
index 000000000..8b47a50bc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/keywords/ReservedIncluded.thrift
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// make sure generated code does not produce name collisions with predefined keywords
+namespace delphi SysUtils
+
+const i32 integer = 42
+
+// EOF
diff --git a/src/jaegertracing/thrift/lib/delphi/test/keywords/ReservedKeywords.dpr b/src/jaegertracing/thrift/lib/delphi/test/keywords/ReservedKeywords.dpr
new file mode 100644
index 000000000..1fbc8c1d7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/keywords/ReservedKeywords.dpr
@@ -0,0 +1,15 @@
+program ReservedKeywords;
+
+{$APPTYPE CONSOLE}
+
+uses
+ SysUtils, System_;
+
+begin
+ try
+ { TODO -oUser -cConsole Main : Code hier einfügen }
+ except
+ on E: Exception do
+ Writeln(E.ClassName, ': ', E.Message);
+ end;
+end.
diff --git a/src/jaegertracing/thrift/lib/delphi/test/keywords/ReservedKeywords.dproj b/src/jaegertracing/thrift/lib/delphi/test/keywords/ReservedKeywords.dproj
new file mode 100644
index 000000000..6bd9544bc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/keywords/ReservedKeywords.dproj
@@ -0,0 +1,112 @@
+ <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <ProjectGuid>{F2E9B6FC-A931-4271-8E30-5A4E402481B4}</ProjectGuid>
+ <MainSource>ReservedKeywords.dpr</MainSource>
+ <ProjectVersion>12.3</ProjectVersion>
+ <Basis>True</Basis>
+ <Config Condition="'$(Config)'==''">Debug</Config>
+ <Platform>Win32</Platform>
+ <AppType>Console</AppType>
+ <FrameworkType>None</FrameworkType>
+ <DCC_DCCCompiler>DCC32</DCC_DCCCompiler>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Config)'=='Basis' or '$(Base)'!=''">
+ <Base>true</Base>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_1)'!=''">
+ <Cfg_1>true</Cfg_1>
+ <CfgParent>Base</CfgParent>
+ <Base>true</Base>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_2)'!=''">
+ <Cfg_2>true</Cfg_2>
+ <CfgParent>Base</CfgParent>
+ <Base>true</Base>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Base)'!=''">
+ <DCC_ImageBase>00400000</DCC_ImageBase>
+ <DCC_DcuOutput>.\$(Config)\$(Platform)</DCC_DcuOutput>
+ <DCC_UnitSearchPath>gen-delphi;..\..\src;$(DCC_UnitSearchPath)</DCC_UnitSearchPath>
+ <DCC_UnitAlias>WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;$(DCC_UnitAlias)</DCC_UnitAlias>
+ <DCC_ExeOutput>.\$(Config)\$(Platform)</DCC_ExeOutput>
+ <DCC_N>false</DCC_N>
+ <DCC_S>false</DCC_S>
+ <DCC_K>false</DCC_K>
+ <DCC_E>false</DCC_E>
+ <DCC_F>false</DCC_F>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Cfg_1)'!=''">
+ <DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
+ <DCC_Optimize>false</DCC_Optimize>
+ <DCC_GenerateStackFrames>true</DCC_GenerateStackFrames>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Cfg_2)'!=''">
+ <DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
+ <DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
+ <DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
+ <DCC_DebugInformation>false</DCC_DebugInformation>
+ </PropertyGroup>
+ <ItemGroup>
+ <DelphiCompile Include="ReservedKeywords.dpr">
+ <MainSource>MainSource</MainSource>
+ </DelphiCompile>
+ <BuildConfiguration Include="Release">
+ <Key>Cfg_2</Key>
+ <CfgParent>Base</CfgParent>
+ </BuildConfiguration>
+ <BuildConfiguration Include="Basis">
+ <Key>Base</Key>
+ </BuildConfiguration>
+ <BuildConfiguration Include="Debug">
+ <Key>Cfg_1</Key>
+ <CfgParent>Base</CfgParent>
+ </BuildConfiguration>
+ </ItemGroup>
+ <Import Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')" Project="$(BDS)\Bin\CodeGear.Delphi.Targets"/>
+ <Import Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')" Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj"/>
+ <PropertyGroup>
+ <PreBuildEvent><![CDATA[thrift -r -gen delphi ReservedKeywords.thrift]]></PreBuildEvent>
+ </PropertyGroup>
+ <ProjectExtensions>
+ <Borland.Personality>Delphi.Personality.12</Borland.Personality>
+ <Borland.ProjectType/>
+ <BorlandProject>
+ <Delphi.Personality>
+ <VersionInfo>
+ <VersionInfo Name="IncludeVerInfo">False</VersionInfo>
+ <VersionInfo Name="AutoIncBuild">False</VersionInfo>
+ <VersionInfo Name="MajorVer">1</VersionInfo>
+ <VersionInfo Name="MinorVer">0</VersionInfo>
+ <VersionInfo Name="Release">0</VersionInfo>
+ <VersionInfo Name="Build">0</VersionInfo>
+ <VersionInfo Name="Debug">False</VersionInfo>
+ <VersionInfo Name="PreRelease">False</VersionInfo>
+ <VersionInfo Name="Special">False</VersionInfo>
+ <VersionInfo Name="Private">False</VersionInfo>
+ <VersionInfo Name="DLL">False</VersionInfo>
+ <VersionInfo Name="Locale">1031</VersionInfo>
+ <VersionInfo Name="CodePage">1252</VersionInfo>
+ </VersionInfo>
+ <VersionInfoKeys>
+ <VersionInfoKeys Name="CompanyName"/>
+ <VersionInfoKeys Name="FileDescription"/>
+ <VersionInfoKeys Name="FileVersion">1.0.0.0</VersionInfoKeys>
+ <VersionInfoKeys Name="InternalName"/>
+ <VersionInfoKeys Name="LegalCopyright"/>
+ <VersionInfoKeys Name="LegalTrademarks"/>
+ <VersionInfoKeys Name="OriginalFilename"/>
+ <VersionInfoKeys Name="ProductName"/>
+ <VersionInfoKeys Name="ProductVersion">1.0.0.0</VersionInfoKeys>
+ <VersionInfoKeys Name="Comments"/>
+ </VersionInfoKeys>
+ <Source>
+ <Source Name="MainSource">ReservedKeywords.dpr</Source>
+ </Source>
+ </Delphi.Personality>
+ <Platforms>
+ <Platform value="Win32">True</Platform>
+ </Platforms>
+ </BorlandProject>
+ <ProjectFileVersion>12</ProjectFileVersion>
+ </ProjectExtensions>
+ </Project>
diff --git a/src/jaegertracing/thrift/lib/delphi/test/keywords/ReservedKeywords.thrift b/src/jaegertracing/thrift/lib/delphi/test/keywords/ReservedKeywords.thrift
new file mode 100644
index 000000000..2f49d742c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/keywords/ReservedKeywords.thrift
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// make sure generated code does not produce name collisions with predefined keywords
+namespace delphi System
+
+include "ReservedIncluded.thrift"
+
+
+typedef i32 Cardinal
+typedef string message
+typedef list< map< Cardinal, message>> program
+
+struct unit {
+ 1: Cardinal downto;
+ 2: program procedure;
+}
+
+typedef set< unit> units
+
+exception exception1 {
+ 1: program message;
+ 2: unit array;
+}
+
+service constructor {
+ unit Create(1: Cardinal asm; 2: message inherited) throws (1: exception1 label);
+ units Destroy();
+}
+
+const Cardinal downto = +1
+const Cardinal published = -1
+
+enum keywords {
+ record = 1,
+ repeat = 2,
+ deprecated = 3
+}
+
+
+struct Struct_lists {
+ 1: list<Struct_simple> init;
+ 2: list<Struct_simple> struc;
+ 3: list<Struct_simple> field;
+ 4: list<Struct_simple> field_;
+ 5: list<Struct_simple> tracker;
+ 6: list<Struct_simple> Self;
+}
+
+struct Struct_structs {
+ 1: Struct_simple init;
+ 2: Struct_simple struc;
+ 3: Struct_simple field;
+ 4: Struct_simple field_;
+ 5: Struct_simple tracker;
+ 6: Struct_simple Self;
+}
+
+struct Struct_simple {
+ 1: bool init;
+ 2: bool struc;
+ 3: bool field;
+ 4: bool field_;
+ 5: bool tracker;
+ 6: bool Self;
+}
+
+struct Struct_strings {
+ 1: string init;
+ 2: string struc;
+ 3: string field;
+ 4: string field_;
+ 5: string tracker;
+ 6: string Self;
+}
+
+struct Struct_binary {
+ 1: binary init;
+ 2: binary struc;
+ 3: binary field;
+ 4: binary field_;
+ 5: binary tracker;
+ 6: binary Self;
+}
+
+
+typedef i32 IProtocol
+typedef i32 ITransport
+typedef i32 IFace
+typedef i32 IAsync
+typedef i32 System
+typedef i32 SysUtils
+typedef i32 Generics
+typedef i32 Thrift
+
+struct Struct_Thrift_Names {
+ 1: IProtocol IProtocol
+ 2: ITransport ITransport
+ 3: IFace IFace
+ 4: IAsync IAsync
+ 5: System System
+ 6: SysUtils SysUtils
+ 7: Generics Generics
+ 8: Thrift Thrift
+}
+
+
+enum Thrift4554_Enum {
+ Foo = 0,
+ Bar = 1,
+ Baz = 2,
+}
+
+struct Thrift4554_Struct {
+ 1 : optional double MinValue
+ 2 : optional double MaxValue
+ 3 : optional bool Integer // causes issue
+ 4 : optional Thrift4554_Enum Foo
+}
+
+
+// EOF
diff --git a/src/jaegertracing/thrift/lib/delphi/test/maketest.sh b/src/jaegertracing/thrift/lib/delphi/test/maketest.sh
new file mode 100755
index 000000000..8f0639c05
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/maketest.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+../../../compiler/cpp/thrift --gen delphi -o . ../../../test/ThriftTest.thrift
+
diff --git a/src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Client.Main.pas b/src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Client.Main.pas
new file mode 100644
index 000000000..35fdf6f5b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Client.Main.pas
@@ -0,0 +1,131 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+unit Multiplex.Client.Main;
+
+{.$DEFINE StressTest} // activate to stress-test the server with frequent connects/disconnects
+{.$DEFINE PerfTest} // activate to activate the performance test
+
+interface
+
+uses
+ Windows, SysUtils, Classes,
+ DateUtils,
+ Generics.Collections,
+ Thrift,
+ Thrift.Protocol,
+ Thrift.Protocol.Multiplex,
+ Thrift.Transport.Pipes,
+ Thrift.Transport,
+ Thrift.Stream,
+ Thrift.Collections,
+ Benchmark, // in gen-delphi folder
+ Aggr, // in gen-delphi folder
+ Multiplex.Test.Common;
+
+type
+ TTestClient = class
+ protected
+ FProtocol : IProtocol;
+
+ procedure ParseArgs( const args: array of string);
+ procedure Setup;
+ procedure Run;
+ public
+ constructor Create( const args: array of string);
+ class procedure Execute( const args: array of string);
+ end;
+
+implementation
+
+
+type
+ IServiceClient = interface
+ ['{7745C1C2-AB20-43BA-B6F0-08BF92DE0BAC}']
+ procedure Test;
+ end;
+
+//--- TTestClient -------------------------------------
+
+
+class procedure TTestClient.Execute( const args: array of string);
+var client : TTestClient;
+begin
+ client := TTestClient.Create(args);
+ try
+ client.Run;
+ finally
+ client.Free;
+ end;
+end;
+
+
+constructor TTestClient.Create( const args: array of string);
+begin
+ inherited Create;
+ ParseArgs(args);
+ Setup;
+end;
+
+
+procedure TTestClient.ParseArgs( const args: array of string);
+begin
+ if Length(args) <> 0
+ then raise Exception.Create('No args accepted so far');
+end;
+
+
+procedure TTestClient.Setup;
+var trans : ITransport;
+begin
+ trans := TSocketImpl.Create( 'localhost', 9090);
+ trans := TFramedTransportImpl.Create( trans);
+ trans.Open;
+ FProtocol := TBinaryProtocolImpl.Create( trans, TRUE, TRUE);
+end;
+
+
+procedure TTestClient.Run;
+var bench : TBenchmarkService.Iface;
+ aggr : TAggr.Iface;
+ multiplex : IProtocol;
+ i : Integer;
+begin
+ try
+ multiplex := TMultiplexedProtocol.Create( FProtocol, NAME_BENCHMARKSERVICE);
+ bench := TBenchmarkService.TClient.Create( multiplex);
+
+ multiplex := TMultiplexedProtocol.Create( FProtocol, NAME_AGGR);
+ aggr := TAggr.TClient.Create( multiplex);
+
+ for i := 1 to 10
+ do aggr.addValue( bench.fibonacci(i));
+
+ for i in aggr.getValues
+ do Write(IntToStr(i)+' ');
+ WriteLn;
+ except
+ on e:Exception do Writeln(#10+e.Message);
+ end;
+end;
+
+
+end.
+
+
diff --git a/src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Server.Main.pas b/src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Server.Main.pas
new file mode 100644
index 000000000..3860f5ace
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Server.Main.pas
@@ -0,0 +1,201 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+unit Multiplex.Server.Main;
+
+{$WARN SYMBOL_PLATFORM OFF}
+
+{.$DEFINE RunEndless} // activate to interactively stress-test the server stop routines via Ctrl+C
+
+interface
+
+uses
+ Windows, SysUtils,
+ Generics.Collections,
+ Thrift.Server,
+ Thrift.Transport,
+ Thrift.Transport.Pipes,
+ Thrift.Protocol,
+ Thrift.Protocol.Multiplex,
+ Thrift.Processor.Multiplex,
+ Thrift.Collections,
+ Thrift.Utils,
+ Thrift,
+ Benchmark, // in gen-delphi folder
+ Aggr, // in gen-delphi folder
+ Multiplex.Test.Common,
+ ConsoleHelper,
+ Contnrs;
+
+type
+ TTestServer = class
+ public type
+ ITestHandler = interface
+ ['{CAE09AAB-80FB-48E9-B3A8-7F9B96F5419A}']
+ procedure SetServer( const AServer : IServer );
+ end;
+
+ protected type
+ TTestHandlerImpl = class( TInterfacedObject, ITestHandler)
+ private
+ FServer : IServer;
+ protected
+ // ITestHandler
+ procedure SetServer( const AServer : IServer );
+
+ property Server : IServer read FServer write SetServer;
+ end;
+
+ TBenchmarkServiceImpl = class( TTestHandlerImpl, TBenchmarkService.Iface)
+ protected
+ // TBenchmarkService.Iface
+ function fibonacci(n: ShortInt): Integer;
+ end;
+
+
+ TAggrImpl = class( TTestHandlerImpl, TAggr.Iface)
+ protected
+ FList : IThriftList<Integer>;
+
+ // TAggr.Iface
+ procedure addValue(value: Integer);
+ function getValues(): IThriftList<Integer>;
+ public
+ constructor Create;
+ destructor Destroy; override;
+ end;
+
+ public
+ class procedure Execute( const args: array of string);
+ end;
+
+
+implementation
+
+
+{ TTestServer.TTestHandlerImpl }
+
+procedure TTestServer.TTestHandlerImpl.SetServer( const AServer: IServer);
+begin
+ FServer := AServer;
+end;
+
+
+{ TTestServer.TBenchmarkServiceImpl }
+
+function TTestServer.TBenchmarkServiceImpl.fibonacci(n: ShortInt): Integer;
+var prev, next : Integer;
+begin
+ prev := 0;
+ result := 1;
+ while n > 0 do begin
+ next := result + prev;
+ prev := result;
+ result := next;
+ Dec(n);
+ end;
+end;
+
+{ TTestServer.TAggrImpl }
+
+constructor TTestServer.TAggrImpl.Create;
+begin
+ inherited Create;
+ FList := TThriftListImpl<Integer>.Create;
+end;
+
+
+destructor TTestServer.TAggrImpl.Destroy;
+begin
+ try
+ FreeAndNil( FList);
+ finally
+ inherited Destroy;
+ end;
+end;
+
+
+procedure TTestServer.TAggrImpl.addValue(value: Integer);
+begin
+ FList.Add( value);
+end;
+
+
+function TTestServer.TAggrImpl.getValues(): IThriftList<Integer>;
+begin
+ result := FList;
+end;
+
+
+{ TTestServer }
+
+class procedure TTestServer.Execute( const args: array of string);
+var
+ TransportFactory : ITransportFactory;
+ ProtocolFactory : IProtocolFactory;
+ ServerTrans : IServerTransport;
+ benchHandler : TBenchmarkService.Iface;
+ aggrHandler : TAggr.Iface;
+ benchProcessor : IProcessor;
+ aggrProcessor : IProcessor;
+ multiplex : IMultiplexedProcessor;
+ ServerEngine : IServer;
+begin
+ try
+ // create protocol factory, default to BinaryProtocol
+ ProtocolFactory := TBinaryProtocolImpl.TFactory.Create( TRUE, TRUE);
+ servertrans := TServerSocketImpl.Create( 9090, 0, FALSE);
+ TransportFactory := TFramedTransportImpl.TFactory.Create;
+
+ benchHandler := TBenchmarkServiceImpl.Create;
+ benchProcessor := TBenchmarkService.TProcessorImpl.Create( benchHandler);
+
+ aggrHandler := TAggrImpl.Create;
+ aggrProcessor := TAggr.TProcessorImpl.Create( aggrHandler);
+
+ multiplex := TMultiplexedProcessorImpl.Create;
+ multiplex.RegisterProcessor( NAME_BENCHMARKSERVICE, benchProcessor);
+ multiplex.RegisterProcessor( NAME_AGGR, aggrProcessor);
+
+ ServerEngine := TSimpleServer.Create( multiplex,
+ ServerTrans,
+ TransportFactory,
+ ProtocolFactory);
+
+ (benchHandler as ITestHandler).SetServer( ServerEngine);
+ (aggrHandler as ITestHandler).SetServer( ServerEngine);
+
+ Console.WriteLine('Starting the server ...');
+ ServerEngine.serve();
+
+ (benchHandler as ITestHandler).SetServer( nil);
+ (aggrHandler as ITestHandler).SetServer( nil);
+
+ except
+ on E: Exception do
+ begin
+ Console.Write( E.Message);
+ end;
+ end;
+ Console.WriteLine( 'done.');
+end;
+
+
+end.
+
diff --git a/src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Test.Client.dpr b/src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Test.Client.dpr
new file mode 100644
index 000000000..a57e93a2e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Test.Client.dpr
@@ -0,0 +1,68 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+
+program Multiplex.Test.Client;
+
+{$APPTYPE CONSOLE}
+
+uses
+ SysUtils,
+ Multiplex.Client.Main in 'Multiplex.Client.Main.pas',
+ Thrift in '..\..\src\Thrift.pas',
+ Thrift.Socket in '..\..\src\Thrift.Socket.pas',
+ Thrift.Exception in '..\..\src\Thrift.Exception.pas',
+ Thrift.Transport in '..\..\src\Thrift.Transport.pas',
+ Thrift.Transport.Pipes in '..\..\src\Thrift.Transport.Pipes.pas',
+ Thrift.Protocol in '..\..\src\Thrift.Protocol.pas',
+ Thrift.Protocol.Multiplex in '..\..\src\Thrift.Protocol.Multiplex.pas',
+ Thrift.Collections in '..\..\src\Thrift.Collections.pas',
+ Thrift.Server in '..\..\src\Thrift.Server.pas',
+ Thrift.Stream in '..\..\src\Thrift.Stream.pas',
+ Thrift.TypeRegistry in '..\..\src\Thrift.TypeRegistry.pas',
+ Thrift.WinHTTP in '..\..\src\Thrift.WinHTTP.pas',
+ Thrift.Utils in '..\..\src\Thrift.Utils.pas';
+
+var
+ nParamCount : Integer;
+ args : array of string;
+ i : Integer;
+ arg : string;
+ s : string;
+
+begin
+ try
+ Writeln( 'Multiplex TestClient '+Thrift.Version);
+ nParamCount := ParamCount;
+ SetLength( args, nParamCount);
+ for i := 1 to nParamCount do
+ begin
+ arg := ParamStr( i );
+ args[i-1] := arg;
+ end;
+ TTestClient.Execute( args );
+ Readln;
+ except
+ on E: Exception do begin
+ Writeln(E.ClassName, ': ', E.Message);
+ ExitCode := $FFFF;
+ end;
+ end;
+end.
+
diff --git a/src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Test.Common.pas b/src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Test.Common.pas
new file mode 100644
index 000000000..2caf08108
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Test.Common.pas
@@ -0,0 +1,35 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+unit Multiplex.Test.Common;
+
+interface
+
+const
+ NAME_BENCHMARKSERVICE = 'BenchmarkService';
+ NAME_AGGR = 'Aggr';
+
+
+implementation
+
+// nix
+
+end.
+
+
diff --git a/src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Test.Server.dpr b/src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Test.Server.dpr
new file mode 100644
index 000000000..81ed3ddc4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/multiplexed/Multiplex.Test.Server.dpr
@@ -0,0 +1,69 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+program Multiplex.Test.Server;
+
+{$APPTYPE CONSOLE}
+
+uses
+ SysUtils,
+ Multiplex.Server.Main in 'Multiplex.Server.Main.pas',
+ ConsoleHelper in '..\ConsoleHelper.pas',
+ Thrift in '..\..\src\Thrift.pas',
+ Thrift.Exception in '..\..\src\Thrift.Exception.pas',
+ Thrift.Socket in '..\..\src\Thrift.Socket.pas',
+ Thrift.Transport in '..\..\src\Thrift.Transport.pas',
+ Thrift.Transport.Pipes in '..\..\src\Thrift.Transport.Pipes.pas',
+ Thrift.Protocol in '..\..\src\Thrift.Protocol.pas',
+ Thrift.Protocol.Multiplex in '..\..\src\Thrift.Protocol.Multiplex.pas',
+ Thrift.Processor.Multiplex in '..\..\src\Thrift.Processor.Multiplex.pas',
+ Thrift.Collections in '..\..\src\Thrift.Collections.pas',
+ Thrift.Server in '..\..\src\Thrift.Server.pas',
+ Thrift.Utils in '..\..\src\Thrift.Utils.pas',
+ Thrift.WinHTTP in '..\..\src\Thrift.WinHTTP.pas',
+ Thrift.TypeRegistry in '..\..\src\Thrift.TypeRegistry.pas',
+ Thrift.Stream in '..\..\src\Thrift.Stream.pas';
+
+var
+ nParamCount : Integer;
+ args : array of string;
+ i : Integer;
+ arg : string;
+ s : string;
+
+begin
+ try
+ Writeln( 'Multiplex TestServer '+Thrift.Version);
+ nParamCount := ParamCount;
+ SetLength( args, nParamCount);
+ for i := 1 to nParamCount do
+ begin
+ arg := ParamStr( i );
+ args[i-1] := arg;
+ end;
+ TTestServer.Execute( args );
+ Writeln('Press ENTER to close ... '); Readln;
+ except
+ on E: Exception do
+ Writeln(E.ClassName, ': ', E.Message);
+ end;
+end.
+
+
+
diff --git a/src/jaegertracing/thrift/lib/delphi/test/serializer/TestSerializer.Data.pas b/src/jaegertracing/thrift/lib/delphi/test/serializer/TestSerializer.Data.pas
new file mode 100644
index 000000000..2420e9a2f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/serializer/TestSerializer.Data.pas
@@ -0,0 +1,354 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+unit TestSerializer.Data;
+
+interface
+
+uses
+ SysUtils,
+ Thrift.Collections,
+ DebugProtoTest;
+
+
+type
+ Fixtures = class
+ public
+ class function CreateOneOfEach : IOneOfEach;
+ class function CreateNesting : INesting;
+ class function CreateHolyMoley : IHolyMoley;
+ class function CreateCompactProtoTestStruct : ICompactProtoTestStruct;
+
+ // These byte arrays are serialized versions of the above structs.
+ // They were serialized in binary protocol using thrift 0.6.x and are used to
+ // test backwards compatibility with respect to the standard scheme.
+ (*
+ all data copied from JAVA version,
+ to be used later
+
+ public static final byte[] persistentBytesOneOfEach = new byte[] {
+ $02, $00, $01, $01, $02, $00, $02, $00, $03, $00,
+ $03, $D6, $06, $00, $04, $69, $78, $08, $00, $05,
+ $01, $00, $00, $00, $0A, $00, $06, $00, $00, $00,
+ $01, $65, $A0, $BC, $00, $04, $00, $07, $40, $09,
+ $21, $FB, $54, $44, $2D, $18, $0B, $00, $08, $00,
+ $00, $00, $0D, $4A, $53, $4F, $4E, $20, $54, $48,
+ $49, $53, $21, $20, $22, $01, $0B, $00, $09, $00,
+ $00, $00, $2E, $D3, $80, $E2, $85, $AE, $CE, $9D,
+ $20, $D0, $9D, $CE, $BF, $E2, $85, $BF, $D0, $BE,
+ $C9, $A1, $D0, $B3, $D0, $B0, $CF, $81, $E2, $84,
+ $8E, $20, $CE, $91, $74, $74, $CE, $B1, $E2, $85,
+ $BD, $CE, $BA, $EF, $BF, $BD, $E2, $80, $BC, $02,
+ $00, $0A, $00, $0B, $00, $0B, $00, $00, $00, $06,
+ $62, $61, $73, $65, $36, $34, $0F, $00, $0C, $03,
+ $00, $00, $00, $03, $01, $02, $03, $0F, $00, $0D,
+ $06, $00, $00, $00, $03, $00, $01, $00, $02, $00,
+ $03, $0F, $00, $0E, $0A, $00, $00, $00, $03, $00,
+ $00, $00, $00, $00, $00, $00, $01, $00, $00, $00,
+ $00, $00, $00, $00, $02, $00, $00, $00, $00, $00,
+ $00, $00, $03, $00 };
+
+
+ public static final byte[] persistentBytesNesting = new byte[] {
+ $0C, $00, $01, $08, $00, $01, $00, $00, $7A, $69,
+ $0B, $00, $02, $00, $00, $00, $13, $49, $20, $61,
+ $6D, $20, $61, $20, $62, $6F, $6E, $6B, $2E, $2E,
+ $2E, $20, $78, $6F, $72, $21, $00, $0C, $00, $02,
+ $02, $00, $01, $01, $02, $00, $02, $00, $03, $00,
+ $03, $D6, $06, $00, $04, $69, $78, $08, $00, $05,
+ $01, $00, $00, $00, $0A, $00, $06, $00, $00, $00,
+ $01, $65, $A0, $BC, $00, $04, $00, $07, $40, $09,
+ $21, $FB, $54, $44, $2D, $18, $0B, $00, $08, $00,
+ $00, $00, $0D, $4A, $53, $4F, $4E, $20, $54, $48,
+ $49, $53, $21, $20, $22, $01, $0B, $00, $09, $00,
+ $00, $00, $2E, $D3, $80, $E2, $85, $AE, $CE, $9D,
+ $20, $D0, $9D, $CE, $BF, $E2, $85, $BF, $D0, $BE,
+ $C9, $A1, $D0, $B3, $D0, $B0, $CF, $81, $E2, $84,
+ $8E, $20, $CE, $91, $74, $74, $CE, $B1, $E2, $85,
+ $BD, $CE, $BA, $EF, $BF, $BD, $E2, $80, $BC, $02,
+ $00, $0A, $00, $0B, $00, $0B, $00, $00, $00, $06,
+ $62, $61, $73, $65, $36, $34, $0F, $00, $0C, $03,
+ $00, $00, $00, $03, $01, $02, $03, $0F, $00, $0D,
+ $06, $00, $00, $00, $03, $00, $01, $00, $02, $00,
+ $03, $0F, $00, $0E, $0A, $00, $00, $00, $03, $00,
+ $00, $00, $00, $00, $00, $00, $01, $00, $00, $00,
+ $00, $00, $00, $00, $02, $00, $00, $00, $00, $00,
+ $00, $00, $03, $00, $00 };
+
+ public static final byte[] persistentBytesHolyMoley = new byte[] {
+ $0F, $00, $01, $0C, $00, $00, $00, $02, $02, $00,
+ $01, $01, $02, $00, $02, $00, $03, $00, $03, $23,
+ $06, $00, $04, $69, $78, $08, $00, $05, $01, $00,
+ $00, $00, $0A, $00, $06, $00, $00, $00, $01, $65,
+ $A0, $BC, $00, $04, $00, $07, $40, $09, $21, $FB,
+ $54, $44, $2D, $18, $0B, $00, $08, $00, $00, $00,
+ $0D, $4A, $53, $4F, $4E, $20, $54, $48, $49, $53,
+ $21, $20, $22, $01, $0B, $00, $09, $00, $00, $00,
+ $2E, $D3, $80, $E2, $85, $AE, $CE, $9D, $20, $D0,
+ $9D, $CE, $BF, $E2, $85, $BF, $D0, $BE, $C9, $A1,
+ $D0, $B3, $D0, $B0, $CF, $81, $E2, $84, $8E, $20,
+ $CE, $91, $74, $74, $CE, $B1, $E2, $85, $BD, $CE,
+ $BA, $EF, $BF, $BD, $E2, $80, $BC, $02, $00, $0A,
+ $00, $0B, $00, $0B, $00, $00, $00, $06, $62, $61,
+ $73, $65, $36, $34, $0F, $00, $0C, $03, $00, $00,
+ $00, $03, $01, $02, $03, $0F, $00, $0D, $06, $00,
+ $00, $00, $03, $00, $01, $00, $02, $00, $03, $0F,
+ $00, $0E, $0A, $00, $00, $00, $03, $00, $00, $00,
+ $00, $00, $00, $00, $01, $00, $00, $00, $00, $00,
+ $00, $00, $02, $00, $00, $00, $00, $00, $00, $00,
+ $03, $00, $02, $00, $01, $01, $02, $00, $02, $00,
+ $03, $00, $03, $D6, $06, $00, $04, $69, $78, $08,
+ $00, $05, $01, $00, $00, $00, $0A, $00, $06, $00,
+ $00, $00, $01, $65, $A0, $BC, $00, $04, $00, $07,
+ $40, $09, $21, $FB, $54, $44, $2D, $18, $0B, $00,
+ $08, $00, $00, $00, $0D, $4A, $53, $4F, $4E, $20,
+ $54, $48, $49, $53, $21, $20, $22, $01, $0B, $00,
+ $09, $00, $00, $00, $2E, $D3, $80, $E2, $85, $AE,
+ $CE, $9D, $20, $D0, $9D, $CE, $BF, $E2, $85, $BF,
+ $D0, $BE, $C9, $A1, $D0, $B3, $D0, $B0, $CF, $81,
+ $E2, $84, $8E, $20, $CE, $91, $74, $74, $CE, $B1,
+ $E2, $85, $BD, $CE, $BA, $EF, $BF, $BD, $E2, $80,
+ $BC, $02, $00, $0A, $00, $0B, $00, $0B, $00, $00,
+ $00, $06, $62, $61, $73, $65, $36, $34, $0F, $00,
+ $0C, $03, $00, $00, $00, $03, $01, $02, $03, $0F,
+ $00, $0D, $06, $00, $00, $00, $03, $00, $01, $00,
+ $02, $00, $03, $0F, $00, $0E, $0A, $00, $00, $00,
+ $03, $00, $00, $00, $00, $00, $00, $00, $01, $00,
+ $00, $00, $00, $00, $00, $00, $02, $00, $00, $00,
+ $00, $00, $00, $00, $03, $00, $0E, $00, $02, $0F,
+ $00, $00, $00, $03, $0B, $00, $00, $00, $00, $0B,
+ $00, $00, $00, $03, $00, $00, $00, $0F, $74, $68,
+ $65, $6E, $20, $61, $20, $6F, $6E, $65, $2C, $20,
+ $74, $77, $6F, $00, $00, $00, $06, $74, $68, $72,
+ $65, $65, $21, $00, $00, $00, $06, $46, $4F, $55,
+ $52, $21, $21, $0B, $00, $00, $00, $02, $00, $00,
+ $00, $09, $61, $6E, $64, $20, $61, $20, $6F, $6E,
+ $65, $00, $00, $00, $09, $61, $6E, $64, $20, $61,
+ $20, $74, $77, $6F, $0D, $00, $03, $0B, $0F, $00,
+ $00, $00, $03, $00, $00, $00, $03, $74, $77, $6F,
+ $0C, $00, $00, $00, $02, $08, $00, $01, $00, $00,
+ $00, $01, $0B, $00, $02, $00, $00, $00, $05, $57,
+ $61, $69, $74, $2E, $00, $08, $00, $01, $00, $00,
+ $00, $02, $0B, $00, $02, $00, $00, $00, $05, $57,
+ $68, $61, $74, $3F, $00, $00, $00, $00, $05, $74,
+ $68, $72, $65, $65, $0C, $00, $00, $00, $00, $00,
+ $00, $00, $04, $7A, $65, $72, $6F, $0C, $00, $00,
+ $00, $00, $00 };
+
+
+*)
+
+ private
+ const
+ kUnicodeBytes : packed array[0..43] of Byte
+ = ( $d3, $80, $e2, $85, $ae, $ce, $9d, $20, $d0, $9d,
+ $ce, $bf, $e2, $85, $bf, $d0, $be, $c9, $a1, $d0,
+ $b3, $d0, $b0, $cf, $81, $e2, $84, $8e, $20, $ce,
+ $91, $74, $74, $ce, $b1, $e2, $85, $bd, $ce, $ba,
+ $83, $e2, $80, $bc);
+
+ end;
+
+
+implementation
+
+
+class function Fixtures.CreateOneOfEach : IOneOfEach;
+var db : Double;
+ us : Utf8String;
+begin
+ result := TOneOfEachImpl.Create;
+ result.setIm_true( TRUE);
+ result.setIm_false( FALSE);
+ result.setA_bite( ShortInt($D6));
+ result.setInteger16( 27000);
+ result.setInteger32( 1 shl 24);
+ result.setInteger64( Int64(6000) * Int64(1000) * Int64(1000));
+ db := Pi;
+ result.setDouble_precision( db);
+ result.setSome_characters( 'JSON THIS! \"\1');
+
+ // ??
+ SetLength( us, Length(kUnicodeBytes));
+ Move( kUnicodeBytes[0], us[1], Length(kUnicodeBytes));
+ // ??
+ SetString( us, PChar(@kUnicodeBytes[0]), Length(kUnicodeBytes));
+ // !!
+ result.setZomg_unicode( UnicodeString( us));
+
+ {$IF cDebugProtoTest_Option_AnsiStr_Binary}
+ result.SetBase64('base64');
+ {$ELSE}
+ result.SetBase64( TEncoding.UTF8.GetBytes('base64'));
+ {$IFEND}
+
+ // byte, i16, and i64 lists are populated by default constructor
+end;
+
+
+class function Fixtures.CreateNesting : INesting;
+var bonk : IBonk;
+begin
+ bonk := TBonkImpl.Create;
+ bonk.Type_ := 31337;
+ bonk.Message := 'I am a bonk... xor!';
+
+ result := TNestingImpl.Create;
+ result.My_bonk := bonk;
+ result.My_ooe := CreateOneOfEach;
+end;
+
+
+class function Fixtures.CreateHolyMoley : IHolyMoley;
+var big : IThriftList<IOneOfEach>;
+ stage1 : IThriftList<String>;
+ stage2 : IThriftList<IBonk>;
+ b : IBonk;
+begin
+ result := THolyMoleyImpl.Create;
+
+ big := TThriftListImpl<IOneOfEach>.Create;
+ big.add( CreateOneOfEach);
+ big.add( CreateNesting.my_ooe);
+ result.Big := big;
+ result.Big[0].setA_bite( $22);
+ result.Big[0].setA_bite( $23);
+
+ result.Contain := THashSetImpl< IThriftList<string>>.Create;
+ stage1 := TThriftListImpl<String>.Create;
+ stage1.add( 'and a one');
+ stage1.add( 'and a two');
+ result.Contain.add( stage1);
+
+ stage1 := TThriftListImpl<String>.Create;
+ stage1.add( 'then a one, two');
+ stage1.add( 'three!');
+ stage1.add( 'FOUR!!');
+ result.Contain.add( stage1);
+
+ stage1 := TThriftListImpl<String>.Create;
+ result.Contain.add( stage1);
+
+ stage2 := TThriftListImpl<IBonk>.Create;
+ result.Bonks := TThriftDictionaryImpl< String, IThriftList< IBonk>>.Create;
+ // one empty
+ result.Bonks.Add( 'zero', stage2);
+
+ // one with two
+ stage2 := TThriftListImpl<IBonk>.Create;
+ b := TBonkImpl.Create;
+ b.type_ := 1;
+ b.message := 'Wait.';
+ stage2.Add( b);
+ b := TBonkImpl.Create;
+ b.type_ := 2;
+ b.message := 'What?';
+ stage2.Add( b);
+ result.Bonks.Add( 'two', stage2);
+
+ // one with three
+ stage2 := TThriftListImpl<IBonk>.Create;
+ b := TBonkImpl.Create;
+ b.type_ := 3;
+ b.message := 'quoth';
+ stage2.Add( b);
+ b := TBonkImpl.Create;
+ b.type_ := 4;
+ b.message := 'the raven';
+ stage2.Add( b);
+ b := TBonkImpl.Create;
+ b.type_ := 5;
+ b.message := 'nevermore';
+ stage2.Add( b);
+ result.bonks.Add( 'three', stage2);
+end;
+
+
+class function Fixtures.CreateCompactProtoTestStruct : ICompactProtoTestStruct;
+// superhuge compact proto test struct
+begin
+ result := TCompactProtoTestStructImpl.Create;
+ result.A_byte := TDebugProtoTestConstants.COMPACT_TEST.A_byte;
+ result.A_i16 := TDebugProtoTestConstants.COMPACT_TEST.A_i16;
+ result.A_i32 := TDebugProtoTestConstants.COMPACT_TEST.A_i32;
+ result.A_i64 := TDebugProtoTestConstants.COMPACT_TEST.A_i64;
+ result.A_double := TDebugProtoTestConstants.COMPACT_TEST.A_double;
+ result.A_string := TDebugProtoTestConstants.COMPACT_TEST.A_string;
+ result.A_binary := TDebugProtoTestConstants.COMPACT_TEST.A_binary;
+ result.True_field := TDebugProtoTestConstants.COMPACT_TEST.True_field;
+ result.False_field := TDebugProtoTestConstants.COMPACT_TEST.False_field;
+ result.Empty_struct_field := TDebugProtoTestConstants.COMPACT_TEST.Empty_struct_field;
+ result.Byte_list := TDebugProtoTestConstants.COMPACT_TEST.Byte_list;
+ result.I16_list := TDebugProtoTestConstants.COMPACT_TEST.I16_list;
+ result.I32_list := TDebugProtoTestConstants.COMPACT_TEST.I32_list;
+ result.I64_list := TDebugProtoTestConstants.COMPACT_TEST.I64_list;
+ result.Double_list := TDebugProtoTestConstants.COMPACT_TEST.Double_list;
+ result.String_list := TDebugProtoTestConstants.COMPACT_TEST.String_list;
+ result.Binary_list := TDebugProtoTestConstants.COMPACT_TEST.Binary_list;
+ result.Boolean_list := TDebugProtoTestConstants.COMPACT_TEST.Boolean_list;
+ result.Struct_list := TDebugProtoTestConstants.COMPACT_TEST.Struct_list;
+ result.Byte_set := TDebugProtoTestConstants.COMPACT_TEST.Byte_set;
+ result.I16_set := TDebugProtoTestConstants.COMPACT_TEST.I16_set;
+ result.I32_set := TDebugProtoTestConstants.COMPACT_TEST.I32_set;
+ result.I64_set := TDebugProtoTestConstants.COMPACT_TEST.I64_set;
+ result.Double_set := TDebugProtoTestConstants.COMPACT_TEST.Double_set;
+ result.String_set := TDebugProtoTestConstants.COMPACT_TEST.String_set;
+ result.String_set := TDebugProtoTestConstants.COMPACT_TEST.String_set;
+ result.String_set := TDebugProtoTestConstants.COMPACT_TEST.String_set;
+ result.Binary_set := TDebugProtoTestConstants.COMPACT_TEST.Binary_set;
+ result.Boolean_set := TDebugProtoTestConstants.COMPACT_TEST.Boolean_set;
+ result.Struct_set := TDebugProtoTestConstants.COMPACT_TEST.Struct_set;
+ result.Byte_byte_map := TDebugProtoTestConstants.COMPACT_TEST.Byte_byte_map;
+ result.I16_byte_map := TDebugProtoTestConstants.COMPACT_TEST.I16_byte_map;
+ result.I32_byte_map := TDebugProtoTestConstants.COMPACT_TEST.I32_byte_map;
+ result.I64_byte_map := TDebugProtoTestConstants.COMPACT_TEST.I64_byte_map;
+ result.Double_byte_map := TDebugProtoTestConstants.COMPACT_TEST.Double_byte_map;
+ result.String_byte_map := TDebugProtoTestConstants.COMPACT_TEST.String_byte_map;
+ result.Binary_byte_map := TDebugProtoTestConstants.COMPACT_TEST.Binary_byte_map;
+ result.Boolean_byte_map := TDebugProtoTestConstants.COMPACT_TEST.Boolean_byte_map;
+ result.Byte_i16_map := TDebugProtoTestConstants.COMPACT_TEST.Byte_i16_map;
+ result.Byte_i32_map := TDebugProtoTestConstants.COMPACT_TEST.Byte_i32_map;
+ result.Byte_i64_map := TDebugProtoTestConstants.COMPACT_TEST.Byte_i64_map;
+ result.Byte_double_map := TDebugProtoTestConstants.COMPACT_TEST.Byte_double_map;
+ result.Byte_string_map := TDebugProtoTestConstants.COMPACT_TEST.Byte_string_map;
+ result.Byte_binary_map := TDebugProtoTestConstants.COMPACT_TEST.Byte_binary_map;
+ result.Byte_boolean_map := TDebugProtoTestConstants.COMPACT_TEST.Byte_boolean_map;
+ result.List_byte_map := TDebugProtoTestConstants.COMPACT_TEST.List_byte_map;
+ result.Set_byte_map := TDebugProtoTestConstants.COMPACT_TEST.Set_byte_map;
+ result.Map_byte_map := TDebugProtoTestConstants.COMPACT_TEST.Map_byte_map;
+ result.Byte_map_map := TDebugProtoTestConstants.COMPACT_TEST.Byte_map_map;
+ result.Byte_set_map := TDebugProtoTestConstants.COMPACT_TEST.Byte_set_map;
+ result.Byte_list_map := TDebugProtoTestConstants.COMPACT_TEST.Byte_list_map;
+
+ result.Field500 := 500;
+ result.Field5000 := 5000;
+ result.Field20000 := 20000;
+
+ {$IF cDebugProtoTest_Option_AnsiStr_Binary}
+ result.A_binary := AnsiString( #0#1#2#3#4#5#6#7#8);
+ {$ELSE}
+ result.A_binary := TEncoding.UTF8.GetBytes( #0#1#2#3#4#5#6#7#8);
+ {$IFEND}
+end;
+
+
+
+
+end.
+
diff --git a/src/jaegertracing/thrift/lib/delphi/test/serializer/TestSerializer.dpr b/src/jaegertracing/thrift/lib/delphi/test/serializer/TestSerializer.dpr
new file mode 100644
index 000000000..56d0d15d4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/serializer/TestSerializer.dpr
@@ -0,0 +1,283 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+program TestSerializer;
+
+{$APPTYPE CONSOLE}
+
+uses
+ Classes, Windows, SysUtils, Generics.Collections,
+ Thrift in '..\..\src\Thrift.pas',
+ Thrift.Exception in '..\..\src\Thrift.Exception.pas',
+ Thrift.Socket in '..\..\src\Thrift.Socket.pas',
+ Thrift.Transport in '..\..\src\Thrift.Transport.pas',
+ Thrift.Protocol in '..\..\src\Thrift.Protocol.pas',
+ Thrift.Protocol.JSON in '..\..\src\Thrift.Protocol.JSON.pas',
+ Thrift.Protocol.Compact in '..\..\src\Thrift.Protocol.Compact.pas',
+ Thrift.Collections in '..\..\src\Thrift.Collections.pas',
+ Thrift.Server in '..\..\src\Thrift.Server.pas',
+ Thrift.Utils in '..\..\src\Thrift.Utils.pas',
+ Thrift.Serializer in '..\..\src\Thrift.Serializer.pas',
+ Thrift.Stream in '..\..\src\Thrift.Stream.pas',
+ Thrift.WinHTTP in '..\..\src\Thrift.WinHTTP.pas',
+ Thrift.TypeRegistry in '..\..\src\Thrift.TypeRegistry.pas',
+ System_,
+ DebugProtoTest,
+ TestSerializer.Data;
+
+
+
+type
+ TTestSerializer = class //extends TestCase {
+ private type
+ TMethod = (
+ mt_Bytes,
+ mt_Stream
+ );
+
+ private
+ FProtocols : TList< IProtocolFactory>;
+
+ class function Serialize(const input : IBase; const factory : IProtocolFactory) : TBytes; overload;
+ class procedure Serialize(const input : IBase; const factory : IProtocolFactory; const aStream : TStream); overload;
+ class procedure Deserialize( const input : TBytes; const target : IBase; const factory : IProtocolFactory); overload;
+ class procedure Deserialize( const input : TStream; const target : IBase; const factory : IProtocolFactory); overload;
+
+ procedure Test_Serializer_Deserializer;
+ procedure Test_OneOfEach( const method : TMethod; const factory : IProtocolFactory; const stream : TFileStream);
+ procedure Test_CompactStruct( const method : TMethod; const factory : IProtocolFactory; const stream : TFileStream);
+
+ public
+ constructor Create;
+ destructor Destroy; override;
+
+ procedure RunTests;
+ end;
+
+
+
+{ TTestSerializer }
+
+constructor TTestSerializer.Create;
+begin
+ inherited Create;
+ FProtocols := TList< IProtocolFactory>.Create;
+ FProtocols.Add( TBinaryProtocolImpl.TFactory.Create);
+ FProtocols.Add( TCompactProtocolImpl.TFactory.Create);
+ FProtocols.Add( TJSONProtocolImpl.TFactory.Create);
+end;
+
+
+destructor TTestSerializer.Destroy;
+begin
+ try
+ FreeAndNil( FProtocols);
+ finally
+ inherited Destroy;
+ end;
+end;
+
+
+procedure TTestSerializer.Test_OneOfEach( const method : TMethod; const factory : IProtocolFactory; const stream : TFileStream);
+var tested, correct : IOneOfEach;
+ bytes : TBytes;
+ i : Integer;
+begin
+ // write
+ tested := Fixtures.CreateOneOfEach;
+ case method of
+ mt_Bytes: bytes := Serialize( tested, factory);
+ mt_Stream: begin
+ stream.Size := 0;
+ Serialize( tested, factory, stream);
+ end
+ else
+ ASSERT( FALSE);
+ end;
+
+ // init + read
+ tested := TOneOfEachImpl.Create;
+ case method of
+ mt_Bytes: Deserialize( bytes, tested, factory);
+ mt_Stream: begin
+ stream.Position := 0;
+ Deserialize( stream, tested, factory);
+ end
+ else
+ ASSERT( FALSE);
+ end;
+
+ // check
+ correct := Fixtures.CreateOneOfEach;
+ ASSERT( tested.Im_true = correct.Im_true);
+ ASSERT( tested.Im_false = correct.Im_false);
+ ASSERT( tested.A_bite = correct.A_bite);
+ ASSERT( tested.Integer16 = correct.Integer16);
+ ASSERT( tested.Integer32 = correct.Integer32);
+ ASSERT( tested.Integer64 = correct.Integer64);
+ ASSERT( Abs( tested.Double_precision - correct.Double_precision) < 1E-12);
+ ASSERT( tested.Some_characters = correct.Some_characters);
+ ASSERT( tested.Zomg_unicode = correct.Zomg_unicode);
+ ASSERT( tested.What_who = correct.What_who);
+
+ ASSERT( Length(tested.Base64) = Length(correct.Base64));
+ ASSERT( CompareMem( @tested.Base64[0], @correct.Base64[0], Length(correct.Base64)));
+
+ ASSERT( tested.Byte_list.Count = correct.Byte_list.Count);
+ for i := 0 to tested.Byte_list.Count-1
+ do ASSERT( tested.Byte_list[i] = correct.Byte_list[i]);
+
+ ASSERT( tested.I16_list.Count = correct.I16_list.Count);
+ for i := 0 to tested.I16_list.Count-1
+ do ASSERT( tested.I16_list[i] = correct.I16_list[i]);
+
+ ASSERT( tested.I64_list.Count = correct.I64_list.Count);
+ for i := 0 to tested.I64_list.Count-1
+ do ASSERT( tested.I64_list[i] = correct.I64_list[i]);
+end;
+
+
+procedure TTestSerializer.Test_CompactStruct( const method : TMethod; const factory : IProtocolFactory; const stream : TFileStream);
+var tested, correct : ICompactProtoTestStruct;
+ bytes : TBytes;
+begin
+ // write
+ tested := Fixtures.CreateCompactProtoTestStruct;
+ case method of
+ mt_Bytes: bytes := Serialize( tested, factory);
+ mt_Stream: begin
+ stream.Size := 0;
+ Serialize( tested, factory, stream);
+ end
+ else
+ ASSERT( FALSE);
+ end;
+
+ // init + read
+ correct := TCompactProtoTestStructImpl.Create;
+ case method of
+ mt_Bytes: Deserialize( bytes, tested, factory);
+ mt_Stream: begin
+ stream.Position := 0;
+ Deserialize( stream, tested, factory);
+ end
+ else
+ ASSERT( FALSE);
+ end;
+
+ // check
+ correct := Fixtures.CreateCompactProtoTestStruct;
+ ASSERT( correct.Field500 = tested.Field500);
+ ASSERT( correct.Field5000 = tested.Field5000);
+ ASSERT( correct.Field20000 = tested.Field20000);
+end;
+
+
+procedure TTestSerializer.Test_Serializer_Deserializer;
+var factory : IProtocolFactory;
+ stream : TFileStream;
+ method : TMethod;
+begin
+ stream := TFileStream.Create( 'TestSerializer.dat', fmCreate);
+ try
+
+ for method in [Low(TMethod)..High(TMethod)] do begin
+ for factory in FProtocols do begin
+
+ Test_OneOfEach( method, factory, stream);
+ Test_CompactStruct( method, factory, stream);
+ end;
+ end;
+
+ finally
+ stream.Free;
+ end;
+end;
+
+
+procedure TTestSerializer.RunTests;
+begin
+ try
+ Test_Serializer_Deserializer;
+ except
+ on e:Exception do begin
+ Writeln( e.Message);
+ Write('Hit ENTER to close ... '); Readln;
+ end;
+ end;
+end;
+
+
+class function TTestSerializer.Serialize(const input : IBase; const factory : IProtocolFactory) : TBytes;
+var serial : TSerializer;
+begin
+ serial := TSerializer.Create( factory);
+ try
+ result := serial.Serialize( input);
+ finally
+ serial.Free;
+ end;
+end;
+
+
+class procedure TTestSerializer.Serialize(const input : IBase; const factory : IProtocolFactory; const aStream : TStream);
+var serial : TSerializer;
+begin
+ serial := TSerializer.Create( factory);
+ try
+ serial.Serialize( input, aStream);
+ finally
+ serial.Free;
+ end;
+end;
+
+
+class procedure TTestSerializer.Deserialize( const input : TBytes; const target : IBase; const factory : IProtocolFactory);
+var serial : TDeserializer;
+begin
+ serial := TDeserializer.Create( factory);
+ try
+ serial.Deserialize( input, target);
+ finally
+ serial.Free;
+ end;
+end;
+
+class procedure TTestSerializer.Deserialize( const input : TStream; const target : IBase; const factory : IProtocolFactory);
+var serial : TDeserializer;
+begin
+ serial := TDeserializer.Create( factory);
+ try
+ serial.Deserialize( input, target);
+ finally
+ serial.Free;
+ end;
+end;
+
+
+var test : TTestSerializer;
+begin
+ test := TTestSerializer.Create;
+ try
+ test.RunTests;
+ finally
+ test.Free;
+ end;
+end.
+
diff --git a/src/jaegertracing/thrift/lib/delphi/test/server.dpr b/src/jaegertracing/thrift/lib/delphi/test/server.dpr
new file mode 100644
index 000000000..9731dd4fa
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/server.dpr
@@ -0,0 +1,74 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+program server;
+
+{$APPTYPE CONSOLE}
+
+uses
+ SysUtils,
+ TestServer in 'TestServer.pas',
+ TestServerEvents in 'TestServerEvents.pas',
+ Thrift.Test, // in gen-delphi folder
+ Thrift in '..\src\Thrift.pas',
+ Thrift.Exception in '..\src\Thrift.Exception.pas',
+ Thrift.Transport in '..\src\Thrift.Transport.pas',
+ Thrift.Socket in '..\src\Thrift.Socket.pas',
+ Thrift.Transport.Pipes in '..\src\Thrift.Transport.Pipes.pas',
+ Thrift.Protocol in '..\src\Thrift.Protocol.pas',
+ Thrift.Protocol.JSON in '..\src\Thrift.Protocol.JSON.pas',
+ Thrift.Protocol.Compact in '..\src\Thrift.Protocol.Compact.pas',
+ Thrift.Protocol.Multiplex in '..\src\Thrift.Protocol.Multiplex.pas',
+ Thrift.Processor.Multiplex in '..\src\Thrift.Processor.Multiplex.pas',
+ Thrift.Collections in '..\src\Thrift.Collections.pas',
+ Thrift.Server in '..\src\Thrift.Server.pas',
+ Thrift.TypeRegistry in '..\src\Thrift.TypeRegistry.pas',
+ Thrift.Utils in '..\src\Thrift.Utils.pas',
+ Thrift.WinHTTP in '..\src\Thrift.WinHTTP.pas',
+ Thrift.Stream in '..\src\Thrift.Stream.pas';
+
+var
+ nParamCount : Integer;
+ args : array of string;
+ i : Integer;
+ arg : string;
+
+begin
+ try
+ Writeln( 'Delphi TestServer '+Thrift.Version);
+ nParamCount := ParamCount;
+ SetLength( args, nParamCount);
+ for i := 1 to nParamCount do begin
+ arg := ParamStr( i );
+ args[i-1] := arg;
+ end;
+
+ TTestServer.Execute( args );
+
+ except
+ on E: EAbort do begin
+ ExitCode := $FF;
+ end;
+ on E: Exception do begin
+ Writeln(E.ClassName, ': ', E.Message);
+ ExitCode := $FF;
+ end;
+ end;
+end.
+
diff --git a/src/jaegertracing/thrift/lib/delphi/test/skip/README.md b/src/jaegertracing/thrift/lib/delphi/test/skip/README.md
new file mode 100644
index 000000000..f34936834
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/skip/README.md
@@ -0,0 +1,11 @@
+These two projects belong together. Both programs
+simulate server and client for different versions
+of the same protocol.
+
+The intention of this test is to ensure fully
+working compatibility features of the Delphi Thrift
+implementation.
+
+The expected test result is, that no errors occur
+with both programs, regardless in which order they
+might be started.
diff --git a/src/jaegertracing/thrift/lib/delphi/test/skip/idl/skiptest_version_1.thrift b/src/jaegertracing/thrift/lib/delphi/test/skip/idl/skiptest_version_1.thrift
new file mode 100644
index 000000000..8353c5e12
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/skip/idl/skiptest_version_1.thrift
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+// version 1 of the interface
+
+namespace * Skiptest.One
+
+const i32 SKIPTESTSERVICE_VERSION = 1
+
+struct Pong {
+ 1 : optional i32 version1
+}
+
+struct Ping {
+ 1 : optional i32 version1
+}
+
+exception PongFailed {
+ 222 : optional i32 pongErrorCode
+}
+
+
+service SkipTestService {
+ void PingPong( 1: Ping pong) throws (444: PongFailed pof);
+}
+
+
+// EOF \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/delphi/test/skip/idl/skiptest_version_2.thrift b/src/jaegertracing/thrift/lib/delphi/test/skip/idl/skiptest_version_2.thrift
new file mode 100644
index 000000000..f3352d327
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/skip/idl/skiptest_version_2.thrift
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+// version 2 of the interface
+
+namespace * Skiptest.Two
+
+const i32 SKIPTESTSERVICE_VERSION = 2
+
+struct Pong {
+ 1 : optional i32 version1
+ 2 : optional i16 version2
+}
+
+struct Ping {
+ 1 : optional i32 version1
+ 10 : optional bool boolVal
+ 11 : optional byte byteVal
+ 12 : optional double dbVal
+ 13 : optional i16 i16Val
+ 14 : optional i32 i32Val
+ 15 : optional i64 i64Val
+ 16 : optional string strVal
+ 17 : optional Pong structVal
+ 18 : optional map< list< Pong>, set< string>> mapVal
+}
+
+exception PingFailed {
+ 1 : optional i32 pingErrorCode
+}
+
+exception PongFailed {
+ 222 : optional i32 pongErrorCode
+ 10 : optional bool boolVal
+ 11 : optional byte byteVal
+ 12 : optional double dbVal
+ 13 : optional i16 i16Val
+ 14 : optional i32 i32Val
+ 15 : optional i64 i64Val
+ 16 : optional string strVal
+ 17 : optional Pong structVal
+ 18 : optional map< list< Pong>, set< string>> mapVal
+}
+
+
+service SkipTestService {
+ Ping PingPong( 1: Ping ping, 3: Pong pong) throws (1: PingFailed pif, 444: PongFailed pof);
+}
+
+
+// EOF
+
diff --git a/src/jaegertracing/thrift/lib/delphi/test/skip/skiptest_version1.dpr b/src/jaegertracing/thrift/lib/delphi/test/skip/skiptest_version1.dpr
new file mode 100644
index 000000000..0bfe96fef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/skip/skiptest_version1.dpr
@@ -0,0 +1,202 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+program skiptest_version1;
+
+{$APPTYPE CONSOLE}
+
+uses
+ Classes, Windows, SysUtils,
+ Skiptest.One,
+ Thrift in '..\..\src\Thrift.pas',
+ Thrift.Exception in '..\..\src\Thrift.Exception.pas',
+ Thrift.Socket in '..\..\src\Thrift.Socket.pas',
+ Thrift.Transport in '..\..\src\Thrift.Transport.pas',
+ Thrift.Protocol in '..\..\src\Thrift.Protocol.pas',
+ Thrift.Protocol.JSON in '..\..\src\Thrift.Protocol.JSON.pas',
+ Thrift.Collections in '..\..\src\Thrift.Collections.pas',
+ Thrift.Server in '..\..\src\Thrift.Server.pas',
+ Thrift.Utils in '..\..\src\Thrift.Utils.pas',
+ Thrift.WinHTTP in '..\..\src\Thrift.WinHTTP.pas',
+ Thrift.TypeRegistry in '..\..\src\Thrift.TypeRegistry.pas',
+ Thrift.Stream in '..\..\src\Thrift.Stream.pas';
+
+const
+ REQUEST_EXT = '.request';
+ RESPONSE_EXT = '.response';
+
+
+function CreatePing : IPing;
+begin
+ result := TPingImpl.Create;
+ result.Version1 := Tskiptest_version_1Constants.SKIPTESTSERVICE_VERSION;
+end;
+
+
+type
+ TDummyServer = class( TInterfacedObject, TSkipTestService.Iface)
+ protected
+ // TSkipTestService.Iface
+ procedure PingPong(const ping: IPing);
+ end;
+
+
+procedure TDummyServer.PingPong(const ping: IPing);
+// TSkipTestService.Iface
+begin
+ Writeln('- performing request from version '+IntToStr(ping.Version1)+' client');
+end;
+
+
+function CreateProtocol( protfact : IProtocolFactory; stm : TStream; aForInput : Boolean) : IProtocol;
+var adapt : IThriftStream;
+ trans : ITransport;
+begin
+ adapt := TThriftStreamAdapterDelphi.Create( stm, FALSE);
+ if aForInput
+ then trans := TStreamTransportImpl.Create( adapt, nil)
+ else trans := TStreamTransportImpl.Create( nil, adapt);
+ result := protfact.GetProtocol( trans);
+end;
+
+
+procedure CreateRequest( protfact : IProtocolFactory; fname : string);
+var stm : TFileStream;
+ ping : IPing;
+ proto : IProtocol;
+ client : TSkipTestService.TClient; // we need access to send/recv_pingpong()
+ cliRef : IUnknown; // holds the refcount
+begin
+ Writeln('- creating new request');
+ stm := TFileStream.Create( fname+REQUEST_EXT+'.tmp', fmCreate);
+ try
+ ping := CreatePing;
+
+ // save request data
+ proto := CreateProtocol( protfact, stm, FALSE);
+ client := TSkipTestService.TClient.Create( nil, proto);
+ cliRef := client as IUnknown;
+ client.send_PingPong( ping);
+
+ finally
+ client := nil; // not Free!
+ cliRef := nil;
+ stm.Free;
+ if client = nil then {warning suppressed};
+ end;
+
+ DeleteFile( fname+REQUEST_EXT);
+ RenameFile( fname+REQUEST_EXT+'.tmp', fname+REQUEST_EXT);
+end;
+
+
+procedure ReadResponse( protfact : IProtocolFactory; fname : string);
+var stm : TFileStream;
+ proto : IProtocol;
+ client : TSkipTestService.TClient; // we need access to send/recv_pingpong()
+ cliRef : IUnknown; // holds the refcount
+begin
+ Writeln('- reading response');
+ stm := TFileStream.Create( fname+RESPONSE_EXT, fmOpenRead);
+ try
+ // save request data
+ proto := CreateProtocol( protfact, stm, TRUE);
+ client := TSkipTestService.TClient.Create( proto, nil);
+ cliRef := client as IUnknown;
+ client.recv_PingPong;
+
+ finally
+ client := nil; // not Free!
+ cliRef := nil;
+ stm.Free;
+ if client = nil then {warning suppressed};
+ end;
+end;
+
+
+procedure ProcessFile( protfact : IProtocolFactory; fname : string);
+var stmIn, stmOut : TFileStream;
+ protIn, protOut : IProtocol;
+ server : IProcessor;
+begin
+ Writeln('- processing request');
+ stmOut := nil;
+ stmIn := TFileStream.Create( fname+REQUEST_EXT, fmOpenRead);
+ try
+ stmOut := TFileStream.Create( fname+RESPONSE_EXT+'.tmp', fmCreate);
+
+ // process request and write response data
+ protIn := CreateProtocol( protfact, stmIn, TRUE);
+ protOut := CreateProtocol( protfact, stmOut, FALSE);
+
+ server := TSkipTestService.TProcessorImpl.Create( TDummyServer.Create);
+ server.Process( protIn, protOut);
+
+ finally
+ server := nil; // not Free!
+ stmIn.Free;
+ stmOut.Free;
+ if server = nil then {warning suppressed};
+ end;
+
+ DeleteFile( fname+RESPONSE_EXT);
+ RenameFile( fname+RESPONSE_EXT+'.tmp', fname+RESPONSE_EXT);
+end;
+
+
+procedure Test( protfact : IProtocolFactory; fname : string);
+begin
+ // try to read an existing request
+ if FileExists( fname + REQUEST_EXT) then begin
+ ProcessFile( protfact, fname);
+ ReadResponse( protfact, fname);
+ end;
+
+ // create a new request and try to process
+ CreateRequest( protfact, fname);
+ ProcessFile( protfact, fname);
+ ReadResponse( protfact, fname);
+end;
+
+
+const
+ FILE_BINARY = 'pingpong.bin';
+ FILE_JSON = 'pingpong.json';
+begin
+ try
+ Writeln( 'Delphi SkipTest '+IntToStr(Tskiptest_version_1Constants.SKIPTESTSERVICE_VERSION)+' using '+Thrift.Version);
+
+ Writeln;
+ Writeln('Binary protocol');
+ Test( TBinaryProtocolImpl.TFactory.Create, FILE_BINARY);
+
+ Writeln;
+ Writeln('JSON protocol');
+ Test( TJSONProtocolImpl.TFactory.Create, FILE_JSON);
+
+ Writeln;
+ Writeln('Test completed without errors.');
+ Writeln;
+ Write('Press ENTER to close ...'); Readln;
+ except
+ on E: Exception do
+ Writeln(E.ClassName, ': ', E.Message);
+ end;
+end.
+
diff --git a/src/jaegertracing/thrift/lib/delphi/test/skip/skiptest_version2.dpr b/src/jaegertracing/thrift/lib/delphi/test/skip/skiptest_version2.dpr
new file mode 100644
index 000000000..7893748a0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/skip/skiptest_version2.dpr
@@ -0,0 +1,229 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+program skiptest_version2;
+
+{$APPTYPE CONSOLE}
+
+uses
+ Classes, Windows, SysUtils,
+ Skiptest.Two,
+ Thrift in '..\..\src\Thrift.pas',
+ Thrift.Exception in '..\..\src\Thrift.Exception.pas',
+ Thrift.Socket in '..\..\src\Thrift.Socket.pas',
+ Thrift.Transport in '..\..\src\Thrift.Transport.pas',
+ Thrift.Protocol in '..\..\src\Thrift.Protocol.pas',
+ Thrift.Protocol.JSON in '..\..\src\Thrift.Protocol.JSON.pas',
+ Thrift.Collections in '..\..\src\Thrift.Collections.pas',
+ Thrift.Server in '..\..\src\Thrift.Server.pas',
+ Thrift.Utils in '..\..\src\Thrift.Utils.pas',
+ Thrift.WinHTTP in '..\..\src\Thrift.WinHTTP.pas',
+ Thrift.TypeRegistry in '..\..\src\Thrift.TypeRegistry.pas',
+ Thrift.Stream in '..\..\src\Thrift.Stream.pas';
+
+const
+ REQUEST_EXT = '.request';
+ RESPONSE_EXT = '.response';
+
+function CreatePing : IPing;
+var list : IThriftList<IPong>;
+ set_ : IHashSet<string>;
+begin
+ result := TPingImpl.Create;
+ result.Version1 := Tskiptest_version_2Constants.SKIPTESTSERVICE_VERSION;
+ result.BoolVal := TRUE;
+ result.ByteVal := 2;
+ result.DbVal := 3;
+ result.I16Val := 4;
+ result.I32Val := 5;
+ result.I64Val := 6;
+ result.StrVal := 'seven';
+
+ result.StructVal := TPongImpl.Create;
+ result.StructVal.Version1 := -1;
+ result.StructVal.Version2 := -2;
+
+ list := TThriftListImpl<IPong>.Create;
+ list.Add( result.StructVal);
+ list.Add( result.StructVal);
+
+ set_ := THashSetImpl<string>.Create;
+ set_.Add( 'one');
+ set_.Add( 'uno');
+ set_.Add( 'eins');
+ set_.Add( 'een');
+
+ result.MapVal := TThriftDictionaryImpl< IThriftList<IPong>, IHashSet<string>>.Create;
+ result.MapVal.Add( list, set_);
+end;
+
+
+type
+ TDummyServer = class( TInterfacedObject, TSkipTestService.Iface)
+ protected
+ // TSkipTestService.Iface
+ function PingPong(const ping: IPing; const pong: IPong): IPing;
+ end;
+
+
+function TDummyServer.PingPong(const ping: IPing; const pong: IPong): IPing;
+// TSkipTestService.Iface
+begin
+ Writeln('- performing request from version '+IntToStr(ping.Version1)+' client');
+ result := CreatePing;
+end;
+
+
+function CreateProtocol( protfact : IProtocolFactory; stm : TStream; aForInput : Boolean) : IProtocol;
+var adapt : IThriftStream;
+ trans : ITransport;
+begin
+ adapt := TThriftStreamAdapterDelphi.Create( stm, FALSE);
+ if aForInput
+ then trans := TStreamTransportImpl.Create( adapt, nil)
+ else trans := TStreamTransportImpl.Create( nil, adapt);
+ result := protfact.GetProtocol( trans);
+end;
+
+
+procedure CreateRequest( protfact : IProtocolFactory; fname : string);
+var stm : TFileStream;
+ ping : IPing;
+ proto : IProtocol;
+ client : TSkipTestService.TClient; // we need access to send/recv_pingpong()
+ cliRef : IUnknown; // holds the refcount
+begin
+ Writeln('- creating new request');
+ stm := TFileStream.Create( fname+REQUEST_EXT+'.tmp', fmCreate);
+ try
+ ping := CreatePing;
+
+ // save request data
+ proto := CreateProtocol( protfact, stm, FALSE);
+ client := TSkipTestService.TClient.Create( nil, proto);
+ cliRef := client as IUnknown;
+ client.send_PingPong( ping, ping.StructVal);
+
+ finally
+ client := nil; // not Free!
+ cliRef := nil;
+ stm.Free;
+ if client = nil then {warning suppressed};
+ end;
+
+ DeleteFile( fname+REQUEST_EXT);
+ RenameFile( fname+REQUEST_EXT+'.tmp', fname+REQUEST_EXT);
+end;
+
+
+procedure ReadResponse( protfact : IProtocolFactory; fname : string);
+var stm : TFileStream;
+ ping : IPing;
+ proto : IProtocol;
+ client : TSkipTestService.TClient; // we need access to send/recv_pingpong()
+ cliRef : IUnknown; // holds the refcount
+begin
+ Writeln('- reading response');
+ stm := TFileStream.Create( fname+RESPONSE_EXT, fmOpenRead);
+ try
+ // save request data
+ proto := CreateProtocol( protfact, stm, TRUE);
+ client := TSkipTestService.TClient.Create( proto, nil);
+ cliRef := client as IUnknown;
+ ping := client.recv_PingPong;
+
+ finally
+ client := nil; // not Free!
+ cliRef := nil;
+ stm.Free;
+ if client = nil then {warning suppressed};
+ end;
+end;
+
+
+procedure ProcessFile( protfact : IProtocolFactory; fname : string);
+var stmIn, stmOut : TFileStream;
+ protIn, protOut : IProtocol;
+ server : IProcessor;
+begin
+ Writeln('- processing request');
+ stmOut := nil;
+ stmIn := TFileStream.Create( fname+REQUEST_EXT, fmOpenRead);
+ try
+ stmOut := TFileStream.Create( fname+RESPONSE_EXT+'.tmp', fmCreate);
+
+ // process request and write response data
+ protIn := CreateProtocol( protfact, stmIn, TRUE);
+ protOut := CreateProtocol( protfact, stmOut, FALSE);
+
+ server := TSkipTestService.TProcessorImpl.Create( TDummyServer.Create);
+ server.Process( protIn, protOut);
+
+ finally
+ server := nil; // not Free!
+ stmIn.Free;
+ stmOut.Free;
+ if server = nil then {warning suppressed};
+ end;
+
+ DeleteFile( fname+RESPONSE_EXT);
+ RenameFile( fname+RESPONSE_EXT+'.tmp', fname+RESPONSE_EXT);
+end;
+
+
+procedure Test( protfact : IProtocolFactory; fname : string);
+begin
+ // try to read an existing request
+ if FileExists( fname + REQUEST_EXT) then begin
+ ProcessFile( protfact, fname);
+ ReadResponse( protfact, fname);
+ end;
+
+ // create a new request and try to process
+ CreateRequest( protfact, fname);
+ ProcessFile( protfact, fname);
+ ReadResponse( protfact, fname);
+end;
+
+
+const
+ FILE_BINARY = 'pingpong.bin';
+ FILE_JSON = 'pingpong.json';
+begin
+ try
+ Writeln( 'Delphi SkipTest '+IntToStr(Tskiptest_version_2Constants.SKIPTESTSERVICE_VERSION)+' using '+Thrift.Version);
+
+ Writeln;
+ Writeln('Binary protocol');
+ Test( TBinaryProtocolImpl.TFactory.Create, FILE_BINARY);
+
+ Writeln;
+ Writeln('JSON protocol');
+ Test( TJSONProtocolImpl.TFactory.Create, FILE_JSON);
+
+ Writeln;
+ Writeln('Test completed without errors.');
+ Writeln;
+ Write('Press ENTER to close ...'); Readln;
+ except
+ on E: Exception do
+ Writeln(E.ClassName, ': ', E.Message);
+ end;
+end.
+
diff --git a/src/jaegertracing/thrift/lib/delphi/test/typeregistry/TestTypeRegistry.dpr b/src/jaegertracing/thrift/lib/delphi/test/typeregistry/TestTypeRegistry.dpr
new file mode 100644
index 000000000..fd5e3dd4e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/test/typeregistry/TestTypeRegistry.dpr
@@ -0,0 +1,91 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+program TestTypeRegistry;
+
+{$APPTYPE CONSOLE}
+
+uses
+ Classes, Windows, SysUtils, Generics.Collections, TypInfo,
+ Thrift in '..\..\src\Thrift.pas',
+ Thrift.Transport in '..\..\src\Thrift.Transport.pas',
+ Thrift.Exception in '..\..\src\Thrift.Exception.pas',
+ Thrift.Socket in '..\..\src\Thrift.Socket.pas',
+ Thrift.Protocol in '..\..\src\Thrift.Protocol.pas',
+ Thrift.Protocol.JSON in '..\..\src\Thrift.Protocol.JSON.pas',
+ Thrift.Collections in '..\..\src\Thrift.Collections.pas',
+ Thrift.Server in '..\..\src\Thrift.Server.pas',
+ Thrift.Utils in '..\..\src\Thrift.Utils.pas',
+ Thrift.Serializer in '..\..\src\Thrift.Serializer.pas',
+ Thrift.Stream in '..\..\src\Thrift.Stream.pas',
+ Thrift.WinHTTP in '..\..\src\Thrift.WinHTTP.pas',
+ Thrift.TypeRegistry in '..\..\src\Thrift.TypeRegistry.pas',
+ DebugProtoTest;
+
+type
+ Tester<T : IInterface> = class
+ public
+ class procedure Test;
+ end;
+
+class procedure Tester<T>.Test;
+var instance : T;
+ name : string;
+begin
+ instance := TypeRegistry.Construct<T>;
+ name := GetTypeName(TypeInfo(T));
+ if instance <> nil
+ then Writeln( name, ' = ok')
+ else begin
+ Writeln( name, ' = failed');
+ raise Exception.Create( 'Test with '+name+' failed!');
+ end;
+end;
+
+begin
+ Writeln('Testing ...');
+ Tester<IDoubles>.Test;
+ Tester<IOneOfEach>.Test;
+ Tester<IBonk>.Test;
+ Tester<INesting>.Test;
+ Tester<IHolyMoley>.Test;
+ Tester<IBackwards>.Test;
+ Tester<IEmpty>.Test;
+ Tester<IWrapper>.Test;
+ Tester<IRandomStuff>.Test;
+ Tester<IBase64>.Test;
+ Tester<ICompactProtoTestStruct>.Test;
+ Tester<ISingleMapTestStruct>.Test;
+ Tester<IBlowUp>.Test;
+ Tester<IReverseOrderStruct>.Test;
+ Tester<IStructWithSomeEnum>.Test;
+ Tester<ITestUnion>.Test;
+ Tester<ITestUnionMinusStringField>.Test;
+ Tester<IComparableUnion>.Test;
+ Tester<IStructWithAUnion>.Test;
+ Tester<IPrimitiveThenStruct>.Test;
+ Tester<IStructWithASomemap>.Test;
+ Tester<IBigFieldIdStruct>.Test;
+ Tester<IBreaksRubyCompactProtocol>.Test;
+ Tester<ITupleProtocolTestStruct>.Test;
+ Writeln('Completed.');
+
+
+end.
+
diff --git a/src/jaegertracing/thrift/lib/erl/Makefile.am b/src/jaegertracing/thrift/lib/erl/Makefile.am
new file mode 100644
index 000000000..d4544a339
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/Makefile.am
@@ -0,0 +1,97 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+THRIFT = ../../compiler/cpp/thrift
+THRIFT_OMIT_FILE = test/Thrift_omit_without.thrift
+THRIFT_FILES = $(wildcard test/*.thrift) \
+ $(THRIFT_OMIT_FILE) \
+ ../../test/ConstantsDemo.thrift \
+ ../../test/NameConflictTest.thrift \
+ ../../test/DoubleConstantsTest.thrift \
+ ../../test/ThriftTest.thrift
+
+if ERLANG_OTP16
+ERL_FLAG = erl:otp16
+ERL_FLAG_LEGACY = erl:otp16,legacynames
+# otp16 + maps does not make sense. We need to generate it anyway to avoid include error.
+ERL_FLAG_MAPS = erl:otp16
+else
+ERL_FLAG = erl
+ERL_FLAG_LEGACY = erl:legacynames
+ERL_FLAG_MAPS = erl:maps
+endif
+
+$(THRIFT_OMIT_FILE): test/Thrift_omit_with.thrift
+ grep -v omit $< >$@
+
+.generated: $(THRIFT) $(THRIFT_FILES)
+ for f in $(THRIFT_FILES) ; do \
+ $(THRIFT) --gen $(ERL_FLAG) -o test $$f ; \
+ done
+ $(THRIFT) --gen $(ERL_FLAG_LEGACY) -o test test/flags/LegacyNames.thrift
+ $(THRIFT) --gen $(ERL_FLAG_MAPS) -o test test/flags/Thrift3214.thrift
+ touch .generated
+
+all: .generated
+ $(REBAR) compile
+
+check: .generated
+ $(REBAR) eunit
+
+install: all
+ mkdir -p $(DESTDIR)$(ERLANG_INSTALL_LIB_DIR_thrift) ; \
+ mkdir -p $(DESTDIR)$(ERLANG_INSTALL_LIB_DIR_thrift)/ebin ; \
+ mkdir -p $(DESTDIR)$(ERLANG_INSTALL_LIB_DIR_thrift)/include ; \
+ mkdir -p $(DESTDIR)$(ERLANG_INSTALL_LIB_DIR_thrift)/src ; \
+ for p in ebin/*.app* ebin/*.beam include/*.hrl src/*.erl ; \
+ do $(INSTALL) $$p $(DESTDIR)$(ERLANG_INSTALL_LIB_DIR_thrift)/$$p ; \
+ done
+
+uninstall:
+ $(RM) -rf $(DESTDIR)$(ERLANG_INSTALL_LIB_DIR_thrift)
+
+clean-local:
+ $(REBAR) clean
+ $(RM) .generated
+ $(RM) -r .rebar/
+ $(RM) -r _build/
+ $(RM) -r test/gen-erl/
+ $(RM) $(THRIFT_OMIT_FILE)
+
+maintainer-clean-local:
+ $(RM) -r ebin/
+
+dist-hook:
+ $(RM) $(distdir)/.generated
+ $(RM) -r $(distdir)/.rebar/
+ $(RM) -r $(distdir)/_build/
+ $(RM) -r $(distdir)/ebin/
+ $(RM) -r $(distdir)/test/gen-erl/
+ $(RM) $(distdir)/$(THRIFT_OMIT_FILE)
+
+EXTRA_DIST = \
+ include \
+ src \
+ coding_standards.md \
+ rebar.config \
+ rebar.config.script \
+ test \
+ README.md
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/src/jaegertracing/thrift/lib/erl/README.md b/src/jaegertracing/thrift/lib/erl/README.md
new file mode 100644
index 000000000..433d05c34
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/README.md
@@ -0,0 +1,51 @@
+# Thrift Erlang Software Library #
+
+## License ##
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+## Release Notes ##
+
+### 0.9.2 ###
+
+as of 0.9.2 struct and function naming conventions have changed. to retain the
+old naming conventions (for backwards compatibility) use the compiler option
+`legacynames`
+
+## Example ##
+
+Example session using thrift_client:
+
+```erl
+1> {ok, C0} = thrift_client_util:new("localhost", 9090, thrift_test_thrift, []), ok.
+ok
+2> {C1, R1} = thrift_client:call(C0, testVoid, []), R1.
+{ok,ok}
+3> {C2, R2} = thrift_client:call(C1, testVoid, [asdf]), R2.
+{error,{bad_args,testVoid,[asdf]}}
+4> {C3, R3} = thrift_client:call(C2, testI32, [123]), R3.
+{ok,123}
+5> {C4, R4} = thrift_client:call(C3, testOneway, [1]), R4.
+{ok,ok}
+6> {C5, R5} = thrift_client:call(C4, testXception, ["foo"]), R5.
+{error,{no_function,testXception}}
+7> {C6, R6} = thrift_client:call(C5, testException, ["foo"]), R6.
+{ok,ok}
+8> {C7, R7} = (catch thrift_client:call(C6, testException, ["Xception"])), R7.
+{exception,{xception,1001,<<"Xception">>}}
+```
diff --git a/src/jaegertracing/thrift/lib/erl/coding_standards.md b/src/jaegertracing/thrift/lib/erl/coding_standards.md
new file mode 100644
index 000000000..4d37dbcc6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/coding_standards.md
@@ -0,0 +1,3 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
+
+Particularly for Erlang please follow the Erlang [Programming Rules and Conventions](http://www.erlang.se/doc/programming_rules.shtml).
diff --git a/src/jaegertracing/thrift/lib/erl/include/thrift_constants.hrl b/src/jaegertracing/thrift/lib/erl/include/thrift_constants.hrl
new file mode 100644
index 000000000..7cb29ebdb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/include/thrift_constants.hrl
@@ -0,0 +1,62 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+%% TType
+-define(tType_STOP, 0).
+-define(tType_VOID, 1).
+-define(tType_BOOL, 2).
+-define(tType_BYTE, 3).
+-define(tType_I8, 3).
+-define(tType_DOUBLE, 4).
+-define(tType_I16, 6).
+-define(tType_I32, 8).
+-define(tType_I64, 10).
+-define(tType_STRING, 11).
+-define(tType_STRUCT, 12).
+-define(tType_MAP, 13).
+-define(tType_SET, 14).
+-define(tType_LIST, 15).
+
+% TMessageType
+-define(tMessageType_CALL, 1).
+-define(tMessageType_REPLY, 2).
+-define(tMessageType_EXCEPTION, 3).
+-define(tMessageType_ONEWAY, 4).
+
+% TApplicationException
+-define(TApplicationException_Structure,
+ {struct, [{1, string},
+ {2, i32}]}).
+
+-record('TApplicationException', {message, type}).
+
+-define(TApplicationException_UNKNOWN, 0).
+-define(TApplicationException_UNKNOWN_METHOD, 1).
+-define(TApplicationException_INVALID_MESSAGE_TYPE, 2).
+-define(TApplicationException_WRONG_METHOD_NAME, 3).
+-define(TApplicationException_BAD_SEQUENCE_ID, 4).
+-define(TApplicationException_MISSING_RESULT, 5).
+-define(TApplicationException_INTERNAL_ERROR, 6).
+-define(TApplicationException_PROTOCOL_ERROR, 7).
+-define(TApplicationException_INVALID_TRANSFORM, 8).
+-define(TApplicationException_INVALID_PROTOCOL, 9).
+-define(TApplicationException_UNSUPPORTED_CLIENT_TYPE, 10).
+
+-define (MULTIPLEXED_SERVICE_SEPARATOR, ":").
+-define (MULTIPLEXED_ERROR_HANDLER_KEY, "error_handler").
diff --git a/src/jaegertracing/thrift/lib/erl/include/thrift_protocol.hrl b/src/jaegertracing/thrift/lib/erl/include/thrift_protocol.hrl
new file mode 100644
index 000000000..bc0acc883
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/include/thrift_protocol.hrl
@@ -0,0 +1,66 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-ifndef(THRIFT_PROTOCOL_INCLUDED).
+-define(THRIFT_PROTOCOL_INCLUDED, true).
+
+-record(protocol_message_begin, {name :: string(), type :: integer(), seqid :: integer()}).
+-record(protocol_struct_begin, {name :: string()}).
+-record(protocol_field_begin, {name :: string(), type :: integer(), id :: integer()}).
+-record(protocol_map_begin, {ktype :: integer(), vtype :: integer(), size :: integer()}).
+-record(protocol_list_begin, {etype :: integer(), size :: integer()}).
+-record(protocol_set_begin, {etype :: integer(), size :: integer()}).
+
+-type tprot_header_val() :: #protocol_message_begin{}
+ | #protocol_struct_begin{}
+ | #protocol_field_begin{}
+ | #protocol_map_begin{}
+ | #protocol_list_begin{}
+ | #protocol_set_begin{}
+ .
+-type tprot_empty_tag() :: message_end
+ | struct_begin
+ | struct_end
+ | field_end
+ | map_end
+ | list_end
+ | set_end
+ .
+-type tprot_header_tag() :: message_begin
+ | field_begin
+ | map_begin
+ | list_begin
+ | set_begin
+ .
+-type tprot_data_tag() :: ui32
+ | bool
+ | byte
+ | i16
+ | i32
+ | i64
+ | double
+ | string
+ .
+-type tprot_cont_tag() :: {list, _Type}
+ | {map, _KType, _VType}
+ | {set, _Type}
+ .
+
+
+-endif.
diff --git a/src/jaegertracing/thrift/lib/erl/include/thrift_protocol_behaviour.hrl b/src/jaegertracing/thrift/lib/erl/include/thrift_protocol_behaviour.hrl
new file mode 100644
index 000000000..abe300b1b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/include/thrift_protocol_behaviour.hrl
@@ -0,0 +1,37 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+%% Signature specifications for protocol implementations.
+
+-ifndef(THRIFT_PROTOCOL_BEHAVIOUR_INCLUDED).
+-define(THRIFT_PROTOCOL_BEHAVIOUR_INCLUDED, true).
+
+-spec flush_transport(state()) -> {state(), ok | {error, _Reason}}.
+-spec close_transport(state()) -> {state(), ok | {error, _Reason}}.
+
+-spec write(state(), any()) -> {state(), ok | {error, _Reason}}.
+
+%% NOTE: Keep this in sync with thrift_protocol:read and read_specific.
+-spec read
+ (state(), tprot_empty_tag()) -> {state(), ok | {error, _Reason}};
+ (state(), tprot_header_tag()) -> {state(), tprot_header_val() | {error, _Reason}};
+ (state(), tprot_data_tag()) -> {state(), {ok, any()} | {error, _Reason}}.
+
+
+-endif.
diff --git a/src/jaegertracing/thrift/lib/erl/include/thrift_transport_behaviour.hrl b/src/jaegertracing/thrift/lib/erl/include/thrift_transport_behaviour.hrl
new file mode 100644
index 000000000..dbc05aacf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/include/thrift_transport_behaviour.hrl
@@ -0,0 +1,31 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+%% Signature specifications for transport implementations.
+
+-ifndef(THRIFT_TRANSPORT_BEHAVIOUR_INCLUDED).
+-define(THRIFT_TRANSPORT_BEHAVIOUR_INCLUDED, true).
+
+-spec write(state(), iolist() | binary()) -> {state(), ok | {error, _Reason}}.
+-spec read(state(), non_neg_integer()) -> {state(), {ok, binary()} | {error, _Reason}}.
+-spec flush(state()) -> {state(), ok | {error, _Reason}}.
+-spec close(state()) -> {state(), ok | {error, _Reason}}.
+
+
+-endif.
diff --git a/src/jaegertracing/thrift/lib/erl/rebar.config b/src/jaegertracing/thrift/lib/erl/rebar.config
new file mode 100644
index 000000000..1b32947a5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/rebar.config
@@ -0,0 +1,3 @@
+{erl_opts, [{platform_define, "^R.*", otp16_or_less}, debug_info]}.
+
+{profiles, [{test, [{deps, [meck]}]}]}.
diff --git a/src/jaegertracing/thrift/lib/erl/rebar.config.script b/src/jaegertracing/thrift/lib/erl/rebar.config.script
new file mode 100644
index 000000000..c73382326
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/rebar.config.script
@@ -0,0 +1,7 @@
+Def0 = case not erlang:is_builtin(erlang, monotonic_time, 0) of
+ true -> [];
+ false -> [{d, time_correction}]
+ end,
+Defs = Def0,
+lists:keystore(erl_opts, 1, CONFIG,
+ {erl_opts, proplists:get_value(erl_opts, CONFIG, []) ++ Defs}).
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift.app.src b/src/jaegertracing/thrift/lib/erl/src/thrift.app.src
new file mode 100644
index 000000000..e5e6ed5cf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift.app.src
@@ -0,0 +1,74 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+%%% -*- mode:erlang -*-
+{application, thrift, [
+ % A quick description of the application.
+ {description, "Thrift bindings"},
+
+ % The version of the applicaton
+ {vsn, "0.13.0"},
+
+ % All modules used by the application.
+ {modules, [
+ thrift_base64_transport,
+ thrift_binary_protocol,
+ thrift_buffered_transport,
+ thrift_client_util,
+ thrift_client,
+ thrift_disk_log_transport,
+ thrift_file_transport,
+ thrift_framed_transport,
+ thrift_http_transport,
+ thrift_json_parser,
+ thrift_json_protocol,
+ thrift_membuffer_transport,
+ thrift_memory_buffer,
+ thrift_processor,
+ thrift_protocol,
+ thrift_reconnecting_client,
+ thrift_server,
+ thrift_service,
+ thrift_socket_server,
+ thrift_socket_transport,
+ thrift_transport_state_test,
+ thrift_transport
+ ]},
+
+ % All of the registered names the application uses. This can be ignored.
+ {registered, []},
+
+ % Applications that are to be started prior to this one. This can be ignored
+ % leave it alone unless you understand it well and let the .rel files in
+ % your release handle this.
+ {applications, [kernel, stdlib]},
+
+ % OTP application loader will load, but not start, included apps. Again
+ % this can be ignored as well. To load but not start an application it
+ % is easier to include it in the .rel file followed by the atom 'none'
+ {included_applications, []},
+
+ % configuration parameters similar to those in the config file specified
+ % on the command line. can be fetched with gas:get_env
+ {env, [
+ % If an error/crash occurs during processing of a function,
+ % should the TApplicationException serialized back to the client
+ % include the erlang backtrace?
+ {exceptions_include_traces, true}
+ ]}
+]}.
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_base64_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_base64_transport.erl
new file mode 100644
index 000000000..d31f2bacf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_base64_transport.erl
@@ -0,0 +1,69 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_base64_transport).
+
+-behaviour(thrift_transport).
+
+%% API
+-export([new/1, new_transport_factory/1]).
+
+%% thrift_transport callbacks
+-export([write/2, read/2, flush/1, close/1]).
+
+%% State
+-record(b64_transport, {wrapped}).
+-type state() :: #b64_transport{}.
+-include("thrift_transport_behaviour.hrl").
+
+new(Wrapped) ->
+ State = #b64_transport{wrapped = Wrapped},
+ thrift_transport:new(?MODULE, State).
+
+
+write(This = #b64_transport{wrapped = Wrapped}, Data) ->
+ {NewWrapped, Result} = thrift_transport:write(Wrapped, base64:encode(iolist_to_binary(Data))),
+ {This#b64_transport{wrapped = NewWrapped}, Result}.
+
+
+%% base64 doesn't support reading quite yet since it would involve
+%% nasty buffering and such
+read(This = #b64_transport{}, _Data) ->
+ {This, {error, no_reads_allowed}}.
+
+
+flush(This = #b64_transport{wrapped = Wrapped0}) ->
+ {Wrapped1, ok} = thrift_transport:write(Wrapped0, <<"\n">>),
+ {Wrapped2, ok} = thrift_transport:flush(Wrapped1),
+ {This#b64_transport{wrapped = Wrapped2}, ok}.
+
+
+close(This0) ->
+ {This1 = #b64_transport{wrapped = Wrapped}, ok} = flush(This0),
+ {NewWrapped, ok} = thrift_transport:close(Wrapped),
+ {This1#b64_transport{wrapped = NewWrapped}, ok}.
+
+
+%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+new_transport_factory(WrapFactory) ->
+ F = fun() ->
+ {ok, Wrapped} = WrapFactory(),
+ new(Wrapped)
+ end,
+ {ok, F}.
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_binary_protocol.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_binary_protocol.erl
new file mode 100644
index 000000000..85abb62d2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_binary_protocol.erl
@@ -0,0 +1,347 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_binary_protocol).
+
+-behaviour(thrift_protocol).
+
+-include("thrift_constants.hrl").
+-include("thrift_protocol.hrl").
+
+-export([new/1, new/2,
+ read/2,
+ write/2,
+ flush_transport/1,
+ close_transport/1,
+
+ new_protocol_factory/2
+ ]).
+
+-record(binary_protocol, {transport,
+ strict_read=true,
+ strict_write=true
+ }).
+-type state() :: #binary_protocol{}.
+-include("thrift_protocol_behaviour.hrl").
+
+-define(VERSION_MASK, 16#FFFF0000).
+-define(VERSION_1, 16#80010000).
+-define(TYPE_MASK, 16#000000ff).
+
+new(Transport) ->
+ new(Transport, _Options = []).
+
+new(Transport, Options) ->
+ State = #binary_protocol{transport = Transport},
+ State1 = parse_options(Options, State),
+ thrift_protocol:new(?MODULE, State1).
+
+parse_options([], State) ->
+ State;
+parse_options([{strict_read, Bool} | Rest], State) when is_boolean(Bool) ->
+ parse_options(Rest, State#binary_protocol{strict_read=Bool});
+parse_options([{strict_write, Bool} | Rest], State) when is_boolean(Bool) ->
+ parse_options(Rest, State#binary_protocol{strict_write=Bool}).
+
+
+flush_transport(This = #binary_protocol{transport = Transport}) ->
+ {NewTransport, Result} = thrift_transport:flush(Transport),
+ {This#binary_protocol{transport = NewTransport}, Result}.
+
+close_transport(This = #binary_protocol{transport = Transport}) ->
+ {NewTransport, Result} = thrift_transport:close(Transport),
+ {This#binary_protocol{transport = NewTransport}, Result}.
+
+%%%
+%%% instance methods
+%%%
+
+write(This0, #protocol_message_begin{
+ name = Name,
+ type = Type,
+ seqid = Seqid}) ->
+ case This0#binary_protocol.strict_write of
+ true ->
+ {This1, ok} = write(This0, {i32, ?VERSION_1 bor Type}),
+ {This2, ok} = write(This1, {string, Name}),
+ {This3, ok} = write(This2, {i32, Seqid}),
+ {This3, ok};
+ false ->
+ {This1, ok} = write(This0, {string, Name}),
+ {This2, ok} = write(This1, {byte, Type}),
+ {This3, ok} = write(This2, {i32, Seqid}),
+ {This3, ok}
+ end;
+
+write(This, message_end) -> {This, ok};
+
+write(This0, #protocol_field_begin{
+ name = _Name,
+ type = Type,
+ id = Id}) ->
+ {This1, ok} = write(This0, {byte, Type}),
+ {This2, ok} = write(This1, {i16, Id}),
+ {This2, ok};
+
+write(This, field_stop) ->
+ write(This, {byte, ?tType_STOP});
+
+write(This, field_end) -> {This, ok};
+
+write(This0, #protocol_map_begin{
+ ktype = Ktype,
+ vtype = Vtype,
+ size = Size}) ->
+ {This1, ok} = write(This0, {byte, Ktype}),
+ {This2, ok} = write(This1, {byte, Vtype}),
+ {This3, ok} = write(This2, {i32, Size}),
+ {This3, ok};
+
+write(This, map_end) -> {This, ok};
+
+write(This0, #protocol_list_begin{
+ etype = Etype,
+ size = Size}) ->
+ {This1, ok} = write(This0, {byte, Etype}),
+ {This2, ok} = write(This1, {i32, Size}),
+ {This2, ok};
+
+write(This, list_end) -> {This, ok};
+
+write(This0, #protocol_set_begin{
+ etype = Etype,
+ size = Size}) ->
+ {This1, ok} = write(This0, {byte, Etype}),
+ {This2, ok} = write(This1, {i32, Size}),
+ {This2, ok};
+
+write(This, set_end) -> {This, ok};
+
+write(This, #protocol_struct_begin{}) -> {This, ok};
+write(This, struct_end) -> {This, ok};
+
+write(This, {bool, true}) -> write(This, {byte, 1});
+write(This, {bool, false}) -> write(This, {byte, 0});
+
+write(This, {byte, Byte}) ->
+ write(This, <<Byte:8/big-signed>>);
+
+write(This, {i16, I16}) ->
+ write(This, <<I16:16/big-signed>>);
+
+write(This, {i32, I32}) ->
+ write(This, <<I32:32/big-signed>>);
+
+write(This, {i64, I64}) ->
+ write(This, <<I64:64/big-signed>>);
+
+write(This, {double, Double}) ->
+ write(This, <<Double:64/big-signed-float>>);
+
+write(This0, {string, Str}) when is_list(Str) ->
+ {This1, ok} = write(This0, {i32, length(Str)}),
+ {This2, ok} = write(This1, list_to_binary(Str)),
+ {This2, ok};
+
+write(This0, {string, Bin}) when is_binary(Bin) ->
+ {This1, ok} = write(This0, {i32, size(Bin)}),
+ {This2, ok} = write(This1, Bin),
+ {This2, ok};
+
+%% Data :: iolist()
+write(This = #binary_protocol{transport = Trans}, Data) ->
+ {NewTransport, Result} = thrift_transport:write(Trans, Data),
+ {This#binary_protocol{transport = NewTransport}, Result}.
+
+%%
+
+read(This0, message_begin) ->
+ {This1, Initial} = read(This0, ui32),
+ case Initial of
+ {ok, Sz} when Sz band ?VERSION_MASK =:= ?VERSION_1 ->
+ %% we're at version 1
+ {This2, {ok, Name}} = read(This1, string),
+ {This3, {ok, SeqId}} = read(This2, i32),
+ Type = Sz band ?TYPE_MASK,
+ {This3, #protocol_message_begin{name = binary_to_list(Name),
+ type = Type,
+ seqid = SeqId}};
+
+ {ok, Sz} when Sz < 0 ->
+ %% there's a version number but it's unexpected
+ {This1, {error, {bad_binary_protocol_version, Sz}}};
+
+ {ok, _Sz} when This1#binary_protocol.strict_read =:= true ->
+ %% strict_read is true and there's no version header; that's an error
+ {This1, {error, no_binary_protocol_version}};
+
+ {ok, Sz} when This1#binary_protocol.strict_read =:= false ->
+ %% strict_read is false, so just read the old way
+ {This2, {ok, Name}} = read_data(This1, Sz),
+ {This3, {ok, Type}} = read(This2, byte),
+ {This4, {ok, SeqId}} = read(This3, i32),
+ {This4, #protocol_message_begin{name = binary_to_list(Name),
+ type = Type,
+ seqid = SeqId}};
+
+ Else ->
+ {This1, Else}
+ end;
+
+read(This, message_end) -> {This, ok};
+
+read(This, struct_begin) -> {This, ok};
+read(This, struct_end) -> {This, ok};
+
+read(This0, field_begin) ->
+ {This1, Result} = read(This0, byte),
+ case Result of
+ {ok, Type = ?tType_STOP} ->
+ {This1, #protocol_field_begin{type = Type}};
+ {ok, Type} ->
+ {This2, {ok, Id}} = read(This1, i16),
+ {This2, #protocol_field_begin{type = Type,
+ id = Id}}
+ end;
+
+read(This, field_end) -> {This, ok};
+
+read(This0, map_begin) ->
+ {This1, {ok, Ktype}} = read(This0, byte),
+ {This2, {ok, Vtype}} = read(This1, byte),
+ {This3, {ok, Size}} = read(This2, i32),
+ {This3, #protocol_map_begin{ktype = Ktype,
+ vtype = Vtype,
+ size = Size}};
+read(This, map_end) -> {This, ok};
+
+read(This0, list_begin) ->
+ {This1, {ok, Etype}} = read(This0, byte),
+ {This2, {ok, Size}} = read(This1, i32),
+ {This2, #protocol_list_begin{etype = Etype,
+ size = Size}};
+read(This, list_end) -> {This, ok};
+
+read(This0, set_begin) ->
+ {This1, {ok, Etype}} = read(This0, byte),
+ {This2, {ok, Size}} = read(This1, i32),
+ {This2, #protocol_set_begin{etype = Etype,
+ size = Size}};
+read(This, set_end) -> {This, ok};
+
+read(This0, field_stop) ->
+ {This1, {ok, ?tType_STOP}} = read(This0, byte),
+ {This1, ok};
+
+%%
+
+read(This0, bool) ->
+ {This1, Result} = read(This0, byte),
+ case Result of
+ {ok, Byte} -> {This1, {ok, Byte /= 0}};
+ Else -> {This1, Else}
+ end;
+
+read(This0, byte) ->
+ {This1, Bytes} = read_data(This0, 1),
+ case Bytes of
+ {ok, <<Val:8/integer-signed-big, _/binary>>} -> {This1, {ok, Val}};
+ Else -> {This1, Else}
+ end;
+
+read(This0, i16) ->
+ {This1, Bytes} = read_data(This0, 2),
+ case Bytes of
+ {ok, <<Val:16/integer-signed-big, _/binary>>} -> {This1, {ok, Val}};
+ Else -> {This1, Else}
+ end;
+
+read(This0, i32) ->
+ {This1, Bytes} = read_data(This0, 4),
+ case Bytes of
+ {ok, <<Val:32/integer-signed-big, _/binary>>} -> {This1, {ok, Val}};
+ Else -> {This1, Else}
+ end;
+
+%% unsigned ints aren't used by thrift itself, but it's used for the parsing
+%% of the packet version header. Without this special function BEAM works fine
+%% but hipe thinks it received a bad version header.
+read(This0, ui32) ->
+ {This1, Bytes} = read_data(This0, 4),
+ case Bytes of
+ {ok, <<Val:32/integer-unsigned-big, _/binary>>} -> {This1, {ok, Val}};
+ Else -> {This1, Else}
+ end;
+
+read(This0, i64) ->
+ {This1, Bytes} = read_data(This0, 8),
+ case Bytes of
+ {ok, <<Val:64/integer-signed-big, _/binary>>} -> {This1, {ok, Val}};
+ Else -> {This1, Else}
+ end;
+
+read(This0, double) ->
+ {This1, Bytes} = read_data(This0, 8),
+ case Bytes of
+ {ok, <<Val:64/float-signed-big, _/binary>>} -> {This1, {ok, Val}};
+ Else -> {This1, Else}
+ end;
+
+% returns a binary directly, call binary_to_list if necessary
+read(This0, string) ->
+ {This1, {ok, Sz}} = read(This0, i32),
+ read_data(This1, Sz).
+
+-spec read_data(#binary_protocol{}, non_neg_integer()) ->
+ {#binary_protocol{}, {ok, binary()} | {error, _Reason}}.
+read_data(This, 0) -> {This, {ok, <<>>}};
+read_data(This = #binary_protocol{transport = Trans}, Len) when is_integer(Len) andalso Len > 0 ->
+ {NewTransport, Result} = thrift_transport:read(Trans, Len),
+ {This#binary_protocol{transport = NewTransport}, Result}.
+
+
+%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-record(tbp_opts, {strict_read = true,
+ strict_write = true}).
+
+parse_factory_options([], Opts) ->
+ Opts;
+parse_factory_options([{strict_read, Bool} | Rest], Opts) when is_boolean(Bool) ->
+ parse_factory_options(Rest, Opts#tbp_opts{strict_read=Bool});
+parse_factory_options([{strict_write, Bool} | Rest], Opts) when is_boolean(Bool) ->
+ parse_factory_options(Rest, Opts#tbp_opts{strict_write=Bool}).
+
+
+%% returns a (fun() -> thrift_protocol())
+new_protocol_factory(TransportFactory, Options) ->
+ ParsedOpts = parse_factory_options(Options, #tbp_opts{}),
+ F = fun() ->
+ case TransportFactory() of
+ {ok, Transport} ->
+ thrift_binary_protocol:new(
+ Transport,
+ [{strict_read, ParsedOpts#tbp_opts.strict_read},
+ {strict_write, ParsedOpts#tbp_opts.strict_write}]);
+ {error, Error} ->
+ {error, Error}
+ end
+ end,
+ {ok, F}.
+
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_buffered_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_buffered_transport.erl
new file mode 100644
index 000000000..e9d3fffa7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_buffered_transport.erl
@@ -0,0 +1,98 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_buffered_transport).
+
+-behaviour(thrift_transport).
+
+%% constructor
+-export([new/1]).
+%% protocol callbacks
+-export([read/2, read_exact/2, write/2, flush/1, close/1]).
+%% legacy api
+-export([new_transport_factory/1]).
+
+
+-record(t_buffered, {
+ wrapped,
+ write_buffer
+}).
+
+-type state() :: #t_buffered{}.
+
+
+-spec new(Transport::thrift_transport:t_transport()) ->
+ thrift_transport:t_transport().
+
+new(Wrapped) ->
+ State = #t_buffered{
+ wrapped = Wrapped,
+ write_buffer = []
+ },
+ thrift_transport:new(?MODULE, State).
+
+
+-include("thrift_transport_behaviour.hrl").
+
+
+%% reads data through from the wrapped transport
+read(State = #t_buffered{wrapped = Wrapped}, Len)
+when is_integer(Len), Len >= 0 ->
+ {NewState, Response} = thrift_transport:read(Wrapped, Len),
+ {State#t_buffered{wrapped = NewState}, Response}.
+
+
+%% reads data through from the wrapped transport
+read_exact(State = #t_buffered{wrapped = Wrapped}, Len)
+when is_integer(Len), Len >= 0 ->
+ {NewState, Response} = thrift_transport:read_exact(Wrapped, Len),
+ {State#t_buffered{wrapped = NewState}, Response}.
+
+
+write(State = #t_buffered{write_buffer = Buffer}, Data) ->
+ {State#t_buffered{write_buffer = [Buffer, Data]}, ok}.
+
+
+flush(State = #t_buffered{wrapped = Wrapped, write_buffer = Buffer}) ->
+ case iolist_size(Buffer) of
+ %% if write buffer is empty, do nothing
+ 0 -> {State, ok};
+ _ ->
+ {Written, Response} = thrift_transport:write(Wrapped, Buffer),
+ {Flushed, ok} = thrift_transport:flush(Written),
+ {State#t_buffered{wrapped = Flushed, write_buffer = []}, Response}
+ end.
+
+
+close(State = #t_buffered{wrapped = Wrapped}) ->
+ {Closed, Result} = thrift_transport:close(Wrapped),
+ {State#t_buffered{wrapped = Closed}, Result}.
+
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+new_transport_factory(WrapFactory) ->
+ F = fun() ->
+ {ok, Wrapped} = WrapFactory(),
+ new(Wrapped)
+ end,
+ {ok, F}.
+
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_client.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_client.erl
new file mode 100644
index 000000000..1a9cb50a4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_client.erl
@@ -0,0 +1,162 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_client).
+
+%% API
+-export([new/2, call/3, send_call/3, close/1]).
+
+-include("thrift_constants.hrl").
+-include("thrift_protocol.hrl").
+
+-record(tclient, {service, protocol, seqid}).
+
+
+new(Protocol, Service)
+ when is_atom(Service) ->
+ {ok, #tclient{protocol = Protocol,
+ service = Service,
+ seqid = 0}}.
+
+-spec call(#tclient{}, atom(), list()) -> {#tclient{}, {ok, any()} | {error, any()}}.
+call(Client = #tclient{}, Function, Args)
+when is_atom(Function), is_list(Args) ->
+ case send_function_call(Client, Function, Args) of
+ {ok, Client1} -> receive_function_result(Client1, Function);
+ {{error, X}, Client1} -> {Client1, {error, X}};
+ Else -> Else
+ end.
+
+
+%% Sends a function call but does not read the result. This is useful
+%% if you're trying to log non-oneway function calls to write-only
+%% transports like thrift_disk_log_transport.
+-spec send_call(#tclient{}, atom(), list()) -> {#tclient{}, ok}.
+send_call(Client = #tclient{}, Function, Args)
+ when is_atom(Function), is_list(Args) ->
+ case send_function_call(Client, Function, Args) of
+ {ok, Client1} -> {Client1, ok};
+ Else -> Else
+ end.
+
+-spec close(#tclient{}) -> ok.
+close(#tclient{protocol=Protocol}) ->
+ thrift_protocol:close_transport(Protocol).
+
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+-spec send_function_call(#tclient{}, atom(), list()) -> {ok | {error, any()}, #tclient{}}.
+send_function_call(Client = #tclient{service = Service}, Function, Args) ->
+ {Params, Reply} = try
+ {Service:function_info(Function, params_type), Service:function_info(Function, reply_type)}
+ catch error:function_clause -> {no_function, 0}
+ end,
+ MsgType = case Reply of
+ oneway_void -> ?tMessageType_ONEWAY;
+ _ -> ?tMessageType_CALL
+ end,
+ case Params of
+ no_function ->
+ {{error, {no_function, Function}}, Client};
+ {struct, PList} when length(PList) =/= length(Args) ->
+ {{error, {bad_args, Function, Args}}, Client};
+ {struct, _PList} -> write_message(Client, Function, Args, Params, MsgType)
+ end.
+
+-spec write_message(#tclient{}, atom(), list(), {struct, list()}, integer()) ->
+ {ok | {error, any()}, #tclient{}}.
+write_message(Client = #tclient{protocol = P0, seqid = Seq}, Function, Args, Params, MsgType) ->
+ try
+ {P1, ok} = thrift_protocol:write(P0, #protocol_message_begin{
+ name = atom_to_list(Function),
+ type = MsgType,
+ seqid = Seq
+ }),
+ {P2, ok} = thrift_protocol:write(P1, {Params, list_to_tuple([Function|Args])}),
+ {P3, ok} = thrift_protocol:write(P2, message_end),
+ {P4, ok} = thrift_protocol:flush_transport(P3),
+ {ok, Client#tclient{protocol = P4}}
+ catch
+ error:{badmatch, {_, {error, _} = Error}} -> {Error, Client}
+ end.
+
+-spec receive_function_result(#tclient{}, atom()) -> {#tclient{}, {ok, any()} | {error, any()}}.
+receive_function_result(Client = #tclient{service = Service}, Function) ->
+ ResultType = Service:function_info(Function, reply_type),
+ read_result(Client, Function, ResultType).
+
+read_result(Client, _Function, oneway_void) ->
+ {Client, {ok, ok}};
+
+read_result(Client = #tclient{protocol = Proto0,
+ seqid = SeqId},
+ Function,
+ ReplyType) ->
+ case thrift_protocol:read(Proto0, message_begin) of
+ {Proto1, {error, Reason}} ->
+ NewClient = Client#tclient{protocol = Proto1},
+ {NewClient, {error, Reason}};
+ {Proto1, MessageBegin} ->
+ NewClient = Client#tclient{protocol = Proto1},
+ case MessageBegin of
+ #protocol_message_begin{seqid = RetSeqId} when RetSeqId =/= SeqId ->
+ {NewClient, {error, {bad_seq_id, SeqId}}};
+ #protocol_message_begin{type = ?tMessageType_EXCEPTION} ->
+ handle_application_exception(NewClient);
+ #protocol_message_begin{type = ?tMessageType_REPLY} ->
+ handle_reply(NewClient, Function, ReplyType)
+ end
+ end.
+
+
+handle_reply(Client = #tclient{protocol = Proto0,
+ service = Service},
+ Function,
+ ReplyType) ->
+ {struct, ExceptionFields} = Service:function_info(Function, exceptions),
+ ReplyStructDef = {struct, [{0, ReplyType}] ++ ExceptionFields},
+ {Proto1, {ok, Reply}} = thrift_protocol:read(Proto0, ReplyStructDef),
+ {Proto2, ok} = thrift_protocol:read(Proto1, message_end),
+ NewClient = Client#tclient{protocol = Proto2},
+ ReplyList = tuple_to_list(Reply),
+ true = length(ReplyList) == length(ExceptionFields) + 1,
+ ExceptionVals = tl(ReplyList),
+ Thrown = [X || X <- ExceptionVals,
+ X =/= undefined],
+ case Thrown of
+ [] when ReplyType == {struct, []} ->
+ {NewClient, {ok, ok}};
+ [] ->
+ {NewClient, {ok, hd(ReplyList)}};
+ [Exception] ->
+ throw({NewClient, {exception, Exception}})
+ end.
+
+handle_application_exception(Client = #tclient{protocol = Proto0}) ->
+ {Proto1, {ok, Exception}} =
+ thrift_protocol:read(Proto0, ?TApplicationException_Structure),
+ {Proto2, ok} = thrift_protocol:read(Proto1, message_end),
+ XRecord = list_to_tuple(
+ ['TApplicationException' | tuple_to_list(Exception)]),
+ error_logger:error_msg("X: ~p~n", [XRecord]),
+ true = is_record(XRecord, 'TApplicationException'),
+ NewClient = Client#tclient{protocol = Proto2},
+ throw({NewClient, {exception, XRecord}}).
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_client_util.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_client_util.erl
new file mode 100644
index 000000000..1dbe51e9d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_client_util.erl
@@ -0,0 +1,112 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_client_util).
+
+-export([new/4]).
+-export([new_multiplexed/3, new_multiplexed/4]).
+
+-type service_name() :: nonempty_string().
+-type service_module() :: atom().
+-type multiplexed_service_map() :: [{ServiceName::service_name(), ServiceModule::service_module()}].
+
+%%
+%% Splits client options into client, protocol, and transport options
+%%
+%% split_options([Options...]) -> {ProtocolOptions, TransportOptions}
+%%
+split_options(Options) ->
+ split_options(Options, [], []).
+
+split_options([], ProtoIn, TransIn) ->
+ {ProtoIn, TransIn};
+
+split_options([Opt = {OptKey, _} | Rest], ProtoIn, TransIn)
+ when OptKey =:= strict_read;
+ OptKey =:= strict_write;
+ OptKey =:= protocol ->
+ split_options(Rest, [Opt | ProtoIn], TransIn);
+
+split_options([Opt = {OptKey, _} | Rest], ProtoIn, TransIn)
+ when OptKey =:= framed;
+ OptKey =:= connect_timeout;
+ OptKey =:= recv_timeout;
+ OptKey =:= sockopts;
+ OptKey =:= ssltransport;
+ OptKey =:= ssloptions->
+ split_options(Rest, ProtoIn, [Opt | TransIn]).
+
+
+%% Client constructor for the common-case of socket transports
+new(Host, Port, Service, Options)
+ when is_integer(Port), is_atom(Service), is_list(Options) ->
+ {ProtoOpts, TransOpts0} = split_options(Options),
+
+ {TransportModule, TransOpts2} = case lists:keytake(ssltransport, 1, TransOpts0) of
+ {value, {_, true}, TransOpts1} -> {thrift_sslsocket_transport, TransOpts1};
+ false -> {thrift_socket_transport, TransOpts0}
+ end,
+
+ {ProtocolModule, ProtoOpts1} = case lists:keytake(protocol, 1, ProtoOpts) of
+ {value, {_, compact}, Opts} -> {thrift_compact_protocol, Opts};
+ {value, {_, json}, Opts} -> {thrift_json_protocol, Opts};
+ {value, {_, binary}, Opts} -> {thrift_binary_protocol, Opts};
+ false -> {thrift_binary_protocol, ProtoOpts}
+ end,
+ {ok, TransportFactory} =
+ TransportModule:new_transport_factory(Host, Port, TransOpts2),
+
+ {ok, ProtocolFactory} = ProtocolModule:new_protocol_factory(
+ TransportFactory, ProtoOpts1),
+
+ case ProtocolFactory() of
+ {ok, Protocol} ->
+ thrift_client:new(Protocol, Service);
+ {error, Error} ->
+ {error, Error}
+ end.
+
+-spec new_multiplexed(Host, Port, Services, Options) -> {ok, ServiceThriftClientList} when
+ Host :: nonempty_string(),
+ Port :: non_neg_integer(),
+ Services :: multiplexed_service_map(),
+ Options :: list(),
+ ServiceThriftClientList :: [{ServiceName::list(), ThriftClient::term()}].
+new_multiplexed(Host, Port, Services, Options) when is_integer(Port),
+ is_list(Services),
+ is_list(Options) ->
+ new_multiplexed(thrift_socket_transport:new_transport_factory(Host, Port, Options), Services, Options).
+
+-spec new_multiplexed(TransportFactoryTuple, Services, Options) -> {ok, ServiceThriftClientList} when
+ TransportFactoryTuple :: {ok, TransportFactory::term()},
+ Services :: multiplexed_service_map(),
+ Options :: list(),
+ ServiceThriftClientList :: [{ServiceName::service_name(), ThriftClient::term()}].
+new_multiplexed(TransportFactoryTuple, Services, Options) when is_list(Services),
+ is_list(Options),
+ is_tuple(TransportFactoryTuple) ->
+ {ProtoOpts, _} = split_options(Options),
+
+ {ok, TransportFactory} = TransportFactoryTuple,
+
+ {ok, ProtocolFactory} = thrift_binary_protocol:new_protocol_factory(TransportFactory, ProtoOpts),
+
+ {ok, Protocol} = ProtocolFactory(),
+
+ {ok, [{ServiceName, element(2, thrift_client:new(element(2, thrift_multiplexed_protocol:new(Protocol, ServiceName)), Service))} || {ServiceName, Service} <- Services]}.
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_compact_protocol.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_compact_protocol.erl
new file mode 100644
index 000000000..0f1422191
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_compact_protocol.erl
@@ -0,0 +1,390 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_compact_protocol).
+
+-behaviour(thrift_protocol).
+
+-include("thrift_constants.hrl").
+-include("thrift_protocol.hrl").
+
+-export([new/1, new/2,
+ read/2,
+ write/2,
+ flush_transport/1,
+ close_transport/1,
+ new_protocol_factory/2
+ ]).
+
+-define(ID_NONE, 16#10000).
+-define(CBOOL_NONE, 0).
+-define(CBOOL_TRUE, 1).
+-define(CBOOL_FALSE, 2).
+
+-record(t_compact, {transport,
+ % state for pending boolean fields
+ read_stack=[],
+ read_value=?CBOOL_NONE,
+ write_stack=[],
+ write_id=?ID_NONE
+ }).
+-type state() :: #t_compact{}.
+-include("thrift_protocol_behaviour.hrl").
+
+-define(PROTOCOL_ID, 16#82).
+-define(VERSION_MASK, 16#1f).
+-define(VERSION_1, 16#01).
+-define(TYPE_MASK, 16#E0).
+-define(TYPE_BITS, 16#07).
+-define(TYPE_SHIFT_AMOUNT, 5).
+
+typeid_to_compact(?tType_STOP) -> 16#0;
+typeid_to_compact(?tType_BOOL) -> 16#2;
+typeid_to_compact(?tType_I8) -> 16#3;
+typeid_to_compact(?tType_I16) -> 16#4;
+typeid_to_compact(?tType_I32) -> 16#5;
+typeid_to_compact(?tType_I64) -> 16#6;
+typeid_to_compact(?tType_DOUBLE) -> 16#7;
+typeid_to_compact(?tType_STRING) -> 16#8;
+typeid_to_compact(?tType_STRUCT) -> 16#C;
+typeid_to_compact(?tType_MAP) -> 16#B;
+typeid_to_compact(?tType_SET) -> 16#A;
+typeid_to_compact(?tType_LIST) -> 16#9.
+
+compact_to_typeid(16#0) -> ?tType_STOP;
+compact_to_typeid(?CBOOL_FALSE) -> ?tType_BOOL;
+compact_to_typeid(?CBOOL_TRUE) -> ?tType_BOOL;
+compact_to_typeid(16#7) -> ?tType_DOUBLE;
+compact_to_typeid(16#3) -> ?tType_I8;
+compact_to_typeid(16#4) -> ?tType_I16;
+compact_to_typeid(16#5) -> ?tType_I32;
+compact_to_typeid(16#6) -> ?tType_I64;
+compact_to_typeid(16#8) -> ?tType_STRING;
+compact_to_typeid(16#C) -> ?tType_STRUCT;
+compact_to_typeid(16#B) -> ?tType_MAP;
+compact_to_typeid(16#A) -> ?tType_SET;
+compact_to_typeid(16#9) -> ?tType_LIST.
+
+bool_to_cbool(Value) when Value -> ?CBOOL_TRUE;
+bool_to_cbool(_) -> ?CBOOL_FALSE.
+cbool_to_bool(Value) -> Value =:= ?CBOOL_TRUE.
+
+new(Transport) -> new(Transport, _Options = []).
+
+new(Transport, _Options) ->
+ State = #t_compact{transport = Transport},
+ thrift_protocol:new(?MODULE, State).
+
+flush_transport(This = #t_compact{transport = Transport}) ->
+ {NewTransport, Result} = thrift_transport:flush(Transport),
+ {This#t_compact{transport = NewTransport}, Result}.
+
+close_transport(This = #t_compact{transport = Transport}) ->
+ {NewTransport, Result} = thrift_transport:close(Transport),
+ {This#t_compact{transport = NewTransport}, Result}.
+
+%%%
+%%% instance methods
+%%%
+
+write_field_begin(This0 = #t_compact{write_stack=[LastId|T]}, CompactType, Id) ->
+ IdDiff = Id - LastId,
+ This1 = This0#t_compact{write_stack=[Id|T]},
+ case (IdDiff > 0) and (IdDiff < 16) of
+ true -> write(This1, {byte, (IdDiff bsl 4) bor CompactType});
+ false ->
+ {This2, ok} = write(This1, {byte, CompactType}),
+ write(This2, {i16, Id})
+ end.
+
+-spec to_zigzag(integer()) -> non_neg_integer().
+to_zigzag(Value) -> 16#FFFFFFFFFFFFFFFF band ((Value bsl 1) bxor (Value bsr 63)).
+
+-spec from_zigzag(non_neg_integer()) -> integer().
+from_zigzag(Value) -> (Value bsr 1) bxor -(Value band 1).
+
+-spec to_varint(non_neg_integer(), iolist()) -> iolist().
+to_varint(Value, Acc) when (Value < 16#80) -> [Acc, Value];
+to_varint(Value, Acc) ->
+ to_varint(Value bsr 7, [Acc, ((Value band 16#7F) bor 16#80)]).
+
+-spec read_varint(#t_compact{}, non_neg_integer(), non_neg_integer()) -> non_neg_integer().
+read_varint(This0, Acc, Count) ->
+ {This1, {ok, Byte}} = read(This0, byte),
+ case (Byte band 16#80) of
+ 0 -> {This1, {ok, (Byte bsl (7 * Count)) + Acc}};
+ _ -> read_varint(This1, ((Byte band 16#7f) bsl (7 * Count)) + Acc, Count + 1)
+ end.
+
+write(This0, #protocol_message_begin{
+ name = Name,
+ type = Type,
+ seqid = Seqid}) ->
+ {This1, ok} = write(This0, {byte, ?PROTOCOL_ID}),
+ {This2, ok} = write(This1, {byte, (?VERSION_1 band ?VERSION_MASK) bor (Type bsl ?TYPE_SHIFT_AMOUNT)}),
+ {This3, ok} = write(This2, {ui32, Seqid}),
+ {This4, ok} = write(This3, {string, Name}),
+ {This4, ok};
+
+write(This, message_end) -> {This, ok};
+
+write(This0, #protocol_field_begin{
+ name = _Name,
+ type = Type,
+ id = Id})
+when (Type =:= ?tType_BOOL) -> {This0#t_compact{write_id = Id}, ok};
+
+write(This0, #protocol_field_begin{
+ name = _Name,
+ type = Type,
+ id = Id}) ->
+ write_field_begin(This0, typeid_to_compact(Type), Id);
+
+write(This, field_stop) -> write(This, {byte, ?tType_STOP});
+
+write(This, field_end) -> {This, ok};
+
+write(This0, #protocol_map_begin{
+ ktype = _Ktype,
+ vtype = _Vtype,
+ size = Size})
+when Size =:= 0 ->
+ write(This0, {byte, 0});
+
+write(This0, #protocol_map_begin{
+ ktype = Ktype,
+ vtype = Vtype,
+ size = Size}) ->
+ {This1, ok} = write(This0, {ui32, Size}),
+ write(This1, {byte, (typeid_to_compact(Ktype) bsl 4) bor typeid_to_compact(Vtype)});
+
+write(This, map_end) -> {This, ok};
+
+write(This0, #protocol_list_begin{
+ etype = Etype,
+ size = Size})
+when Size < 16#f ->
+ write(This0, {byte, (Size bsl 4) bor typeid_to_compact(Etype)});
+
+write(This0, #protocol_list_begin{
+ etype = Etype,
+ size = Size}) ->
+ {This1, ok} = write(This0, {byte, 16#f0 bor typeid_to_compact(Etype)}),
+ write(This1, {ui32, Size});
+
+write(This, list_end) -> {This, ok};
+
+write(This0, #protocol_set_begin{
+ etype = Etype,
+ size = Size}) ->
+ write(This0, #protocol_list_begin{etype = Etype, size = Size});
+
+write(This, set_end) -> {This, ok};
+
+write(This = #t_compact{write_stack = Stack}, #protocol_struct_begin{}) ->
+ {This#t_compact{write_stack = [0|Stack]}, ok};
+write(This = #t_compact{write_stack = [_|T]}, struct_end) ->
+ {This#t_compact{write_stack = T}, ok};
+
+write(This = #t_compact{write_id = ?ID_NONE}, {bool, Value}) ->
+ write(This, {byte, bool_to_cbool(Value)});
+
+write(This0 = #t_compact{write_id = Id}, {bool, Value}) ->
+ {This1, ok} = write_field_begin(This0, bool_to_cbool(Value), Id),
+ {This1#t_compact{write_id = ?ID_NONE}, ok};
+
+write(This, {byte, Value}) when is_integer(Value) ->
+ write(This, <<Value:8/big-signed>>);
+
+write(This, {i16, Value}) when is_integer(Value) -> write(This, to_varint(to_zigzag(Value), []));
+write(This, {ui32, Value}) when is_integer(Value) -> write(This, to_varint(Value, []));
+write(This, {i32, Value}) when is_integer(Value) ->
+ write(This, to_varint(to_zigzag(Value), []));
+write(This, {i64, Value}) when is_integer(Value) -> write(This, to_varint(to_zigzag(Value), []));
+
+write(This, {double, Double}) ->
+ write(This, <<Double:64/float-signed-little>>);
+
+write(This0, {string, Str}) when is_list(Str) ->
+ % TODO: limit length
+ {This1, ok} = write(This0, {ui32, length(Str)}),
+ {This2, ok} = write(This1, list_to_binary(Str)),
+ {This2, ok};
+
+write(This0, {string, Bin}) when is_binary(Bin) ->
+ % TODO: limit length
+ {This1, ok} = write(This0, {ui32, size(Bin)}),
+ {This2, ok} = write(This1, Bin),
+ {This2, ok};
+
+%% Data :: iolist()
+write(This = #t_compact{transport = Trans}, Data) ->
+ {NewTransport, Result} = thrift_transport:write(Trans, Data),
+ {This#t_compact{transport = NewTransport}, Result}.
+
+%%
+%%
+
+read(This0, message_begin) ->
+ {This1, {ok, ?PROTOCOL_ID}} = read(This0, ubyte),
+ {This2, {ok, VerAndType}} = read(This1, ubyte),
+ ?VERSION_1 = VerAndType band ?VERSION_MASK,
+ {This3, {ok, SeqId}} = read(This2, ui32),
+ {This4, {ok, Name}} = read(This3, string),
+ {This4, #protocol_message_begin{
+ name = binary_to_list(Name),
+ type = (VerAndType bsr ?TYPE_SHIFT_AMOUNT) band ?TYPE_BITS,
+ seqid = SeqId}};
+
+read(This, message_end) -> {This, ok};
+
+read(This = #t_compact{read_stack = Stack}, struct_begin) ->
+ {This#t_compact{read_stack = [0|Stack]}, ok};
+read(This = #t_compact{read_stack = [_H|T]}, struct_end) ->
+ {This#t_compact{read_stack = T}, ok};
+
+read(This0 = #t_compact{read_stack = [LastId|T]}, field_begin) ->
+ {This1, {ok, Byte}} = read(This0, ubyte),
+ case Byte band 16#f of
+ CompactType = ?tType_STOP ->
+ {This1, #protocol_field_begin{type = CompactType}};
+ CompactType ->
+ {This2, {ok, Id}} = case Byte bsr 4 of
+ 0 -> read(This1, i16);
+ IdDiff ->
+ {This1, {ok, LastId + IdDiff}}
+ end,
+ case compact_to_typeid(CompactType) of
+ ?tType_BOOL ->
+ {This2#t_compact{read_stack = [Id|T], read_value = cbool_to_bool(CompactType)},
+ #protocol_field_begin{type = ?tType_BOOL, id = Id}};
+ Type ->
+ {This2#t_compact{read_stack = [Id|T]},
+ #protocol_field_begin{type = Type, id = Id}}
+ end
+ end;
+
+read(This, field_end) -> {This, ok};
+
+read(This0, map_begin) ->
+ {This1, {ok, Size}} = read(This0, ui32),
+ {This2, {ok, KV}} = case Size of
+ 0 -> {This1, {ok, 0}};
+ _ -> read(This1, ubyte)
+ end,
+ {This2, #protocol_map_begin{ktype = compact_to_typeid(KV bsr 4),
+ vtype = compact_to_typeid(KV band 16#f),
+ size = Size}};
+read(This, map_end) -> {This, ok};
+
+read(This0, list_begin) ->
+ {This1, {ok, SizeAndType}} = read(This0, ubyte),
+ {This2, {ok, Size}} = case (SizeAndType bsr 4) band 16#f of
+ 16#f -> read(This1, ui32);
+ Else -> {This1, {ok, Else}}
+ end,
+ {This2, #protocol_list_begin{etype = compact_to_typeid(SizeAndType band 16#f),
+ size = Size}};
+
+read(This, list_end) -> {This, ok};
+
+read(This0, set_begin) ->
+ {This1, {ok, SizeAndType}} = read(This0, ubyte),
+ {This2, {ok, Size}} = case (SizeAndType bsr 4) band 16#f of
+ 16#f -> read(This1, ui32);
+ Else -> {This1, {ok, Else}}
+ end,
+ {This2, #protocol_set_begin{etype = compact_to_typeid(SizeAndType band 16#f),
+ size = Size}};
+
+read(This, set_end) -> {This, ok};
+
+read(This0, field_stop) ->
+ {This1, {ok, ?tType_STOP}} = read(This0, ubyte),
+ {This1, ok};
+
+%%
+
+read(This0 = #t_compact{read_value = ?CBOOL_NONE}, bool) ->
+ {This1, {ok, Byte}} = read(This0, ubyte),
+ {This1, {ok, cbool_to_bool(Byte)}};
+
+read(This0 = #t_compact{read_value = Bool}, bool) ->
+ {This0#t_compact{read_value = ?CBOOL_NONE}, {ok, Bool}};
+
+read(This0, ubyte) ->
+ {This1, {ok, <<Val:8/integer-unsigned-big, _/binary>>}} = read_data(This0, 1),
+ {This1, {ok, Val}};
+
+read(This0, byte) ->
+ {This1, Bytes} = read_data(This0, 1),
+ case Bytes of
+ {ok, <<Val:8/integer-signed-big, _/binary>>} -> {This1, {ok, Val}};
+ Else -> {This1, Else}
+ end;
+
+read(This0, i16) ->
+ {This1, {ok, Zigzag}} = read_varint(This0, 0, 0),
+ {This1, {ok, from_zigzag(Zigzag)}};
+
+read(This0, ui32) -> read_varint(This0, 0, 0);
+
+read(This0, i32) ->
+ {This1, {ok, Zigzag}} = read_varint(This0, 0, 0),
+ {This1, {ok, from_zigzag(Zigzag)}};
+
+read(This0, i64) ->
+ {This1, {ok, Zigzag}} = read_varint(This0, 0, 0),
+ {This1, {ok, from_zigzag(Zigzag)}};
+
+read(This0, double) ->
+ {This1, Bytes} = read_data(This0, 8),
+ case Bytes of
+ {ok, <<Val:64/float-signed-little, _/binary>>} -> {This1, {ok, Val}};
+ Else -> {This1, Else}
+ end;
+
+% returns a binary directly, call binary_to_list if necessary
+read(This0, string) ->
+ {This1, {ok, Sz}} = read(This0, ui32),
+ read_data(This1, Sz).
+
+-spec read_data(#t_compact{}, non_neg_integer()) ->
+ {#t_compact{}, {ok, binary()} | {error, _Reason}}.
+read_data(This, 0) -> {This, {ok, <<>>}};
+read_data(This = #t_compact{transport = Trans}, Len) when is_integer(Len) andalso Len > 0 ->
+ {NewTransport, Result} = thrift_transport:read(Trans, Len),
+ {This#t_compact{transport = NewTransport}, Result}.
+
+
+%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% returns a (fun() -> thrift_protocol())
+new_protocol_factory(TransportFactory, _Options) ->
+ F = fun() ->
+ case TransportFactory() of
+ {ok, Transport} ->
+ thrift_compact_protocol:new(
+ Transport,
+ []);
+ {error, Error} ->
+ {error, Error}
+ end
+ end,
+ {ok, F}.
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_disk_log_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_disk_log_transport.erl
new file mode 100644
index 000000000..de8ee417b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_disk_log_transport.erl
@@ -0,0 +1,123 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+%%% Todo: this might be better off as a gen_server type of transport
+%%% that handles stuff like group commit, similar to TFileTransport
+%%% in cpp land
+-module(thrift_disk_log_transport).
+
+-behaviour(thrift_transport).
+
+%% API
+-export([new/2, new_transport_factory/2, new_transport_factory/3]).
+
+%% thrift_transport callbacks
+-export([read/2, write/2, force_flush/1, flush/1, close/1]).
+
+%% state
+-record(dl_transport, {log,
+ close_on_close = false,
+ sync_every = infinity,
+ sync_tref}).
+-type state() :: #dl_transport{}.
+-include("thrift_transport_behaviour.hrl").
+
+
+%% Create a transport attached to an already open log.
+%% If you'd like this transport to close the disk_log using disk_log:lclose()
+%% when the transport is closed, pass a {close_on_close, true} tuple in the
+%% Opts list.
+new(LogName, Opts) when is_atom(LogName), is_list(Opts) ->
+ State = parse_opts(Opts, #dl_transport{log = LogName}),
+
+ State2 =
+ case State#dl_transport.sync_every of
+ N when is_integer(N), N > 0 ->
+ {ok, TRef} = timer:apply_interval(N, ?MODULE, force_flush, [State]),
+ State#dl_transport{sync_tref = TRef};
+ _ -> State
+ end,
+
+ thrift_transport:new(?MODULE, State2).
+
+
+parse_opts([], State) ->
+ State;
+parse_opts([{close_on_close, Bool} | Rest], State) when is_boolean(Bool) ->
+ parse_opts(Rest, State#dl_transport{close_on_close = Bool});
+parse_opts([{sync_every, Int} | Rest], State) when is_integer(Int), Int > 0 ->
+ parse_opts(Rest, State#dl_transport{sync_every = Int}).
+
+
+%%%% TRANSPORT IMPLENTATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% disk_log_transport is write-only
+read(State, _Len) ->
+ {State, {error, no_read_from_disk_log}}.
+
+write(This = #dl_transport{log = Log}, Data) ->
+ {This, disk_log:balog(Log, erlang:iolist_to_binary(Data))}.
+
+force_flush(#dl_transport{log = Log}) ->
+ error_logger:info_msg("~p syncing~n", [?MODULE]),
+ disk_log:sync(Log).
+
+flush(This = #dl_transport{log = Log, sync_every = SE}) ->
+ case SE of
+ undefined -> % no time-based sync
+ disk_log:sync(Log);
+ _Else -> % sync will happen automagically
+ ok
+ end,
+ {This, ok}.
+
+
+
+
+%% On close, close the underlying log if we're configured to do so.
+close(This = #dl_transport{close_on_close = false}) ->
+ {This, ok};
+close(This = #dl_transport{log = Log}) ->
+ {This, disk_log:lclose(Log)}.
+
+
+%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+new_transport_factory(Name, ExtraLogOpts) ->
+ new_transport_factory(Name, ExtraLogOpts, [{close_on_close, true},
+ {sync_every, 500}]).
+
+new_transport_factory(Name, ExtraLogOpts, TransportOpts) ->
+ F = fun() -> factory_impl(Name, ExtraLogOpts, TransportOpts) end,
+ {ok, F}.
+
+factory_impl(Name, ExtraLogOpts, TransportOpts) ->
+ LogOpts = [{name, Name},
+ {format, external},
+ {type, wrap} |
+ ExtraLogOpts],
+ Log =
+ case disk_log:open(LogOpts) of
+ {ok, LogS} ->
+ LogS;
+ {repaired, LogS, Info1, Info2} ->
+ error_logger:info_msg("Disk log ~p repaired: ~p, ~p~n", [LogS, Info1, Info2]),
+ LogS
+ end,
+ new(Log, TransportOpts).
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_file_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_file_transport.erl
new file mode 100644
index 000000000..071152b63
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_file_transport.erl
@@ -0,0 +1,115 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_file_transport).
+
+-behaviour(thrift_transport).
+
+%% constructors
+-export([new/1, new/2]).
+%% protocol callbacks
+-export([read/2, read_exact/2, write/2, flush/1, close/1]).
+%% legacy api
+-export([new_reader/1]).
+
+
+-record(t_file, {
+ device,
+ should_close = true,
+ mode = write
+}).
+
+-type state() :: #t_file{}.
+
+
+-spec new(Device::file:io_device()) ->
+ thrift_transport:t_transport().
+
+new(Device) -> new(Device, []).
+
+-spec new(Device::file:io_device(), Opts::list()) ->
+ thrift_transport:t_transport().
+
+%% Device should be opened in raw and binary mode.
+new(Device, Opts) when is_list(Opts) ->
+ State = parse_opts(Opts, #t_file{device = Device}),
+ thrift_transport:new(?MODULE, State).
+
+
+parse_opts([{should_close, Bool}|Rest], State)
+when is_boolean(Bool) ->
+ parse_opts(Rest, State#t_file{should_close = Bool});
+parse_opts([{mode, Mode}|Rest], State)
+when Mode =:= write; Mode =:= read ->
+ parse_opts(Rest, State#t_file{mode = Mode});
+parse_opts([], State) ->
+ State.
+
+
+-include("thrift_transport_behaviour.hrl").
+
+
+read(State = #t_file{device = Device, mode = read}, Len)
+when is_integer(Len), Len >= 0 ->
+ case file:read(Device, Len) of
+ eof -> {State, {error, eof}};
+ {ok, Result} -> {State, {ok, iolist_to_binary(Result)}}
+ end;
+read(State, _) ->
+ {State, {error, write_mode}}.
+
+
+read_exact(State = #t_file{device = Device, mode = read}, Len)
+when is_integer(Len), Len >= 0 ->
+ case file:read(Device, Len) of
+ eof -> {State, {error, eof}};
+ {ok, Result} ->
+ case iolist_size(Result) of
+ X when X < Len -> {State, {error, eof}};
+ _ -> {State, {ok, iolist_to_binary(Result)}}
+ end
+ end;
+read_exact(State, _) ->
+ {State, {error, write_mode}}.
+
+
+write(State = #t_file{device = Device, mode = write}, Data) ->
+ {State, file:write(Device, Data)};
+write(State, _) ->
+ {State, {error, read_mode}}.
+
+
+flush(State = #t_file{device = Device, mode = write}) ->
+ {State, file:sync(Device)}.
+
+
+close(State = #t_file{device = Device, should_close = SC}) ->
+ case SC of
+ true -> {State, file:close(Device)};
+ false -> {State, ok}
+ end.
+
+
+%% legacy api. left for compatibility
+new_reader(Filename) ->
+ case file:open(Filename, [read, binary, {read_ahead, 1024*1024}]) of
+ {ok, IODevice} -> new(IODevice, [{should_close, true}, {mode, read}]);
+ Error -> Error
+ end.
+
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_framed_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_framed_transport.erl
new file mode 100644
index 000000000..9a5d6af55
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_framed_transport.erl
@@ -0,0 +1,126 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_framed_transport).
+
+-behaviour(thrift_transport).
+
+%% constructor
+-export([new/1]).
+%% protocol callbacks
+-export([read/2, read_exact/2, write/2, flush/1, close/1]).
+
+
+-record(t_framed, {
+ wrapped,
+ read_buffer,
+ write_buffer
+}).
+
+-type state() :: #t_framed{}.
+
+
+-spec new(Transport::thrift_transport:t_transport()) ->
+ thrift_transport:t_transport().
+
+new(Wrapped) ->
+ State = #t_framed{
+ wrapped = Wrapped,
+ read_buffer = [],
+ write_buffer = []
+ },
+ thrift_transport:new(?MODULE, State).
+
+
+-include("thrift_transport_behaviour.hrl").
+
+
+read(State = #t_framed{wrapped = Wrapped, read_buffer = Buffer}, Len)
+when is_integer(Len), Len >= 0 ->
+ Binary = iolist_to_binary(Buffer),
+ case Binary of
+ <<>> when Len > 0 ->
+ case next_frame(Wrapped) of
+ {NewState, {ok, Frame}} ->
+ NewBinary = iolist_to_binary([Binary, Frame]),
+ Give = min(iolist_size(NewBinary), Len),
+ {Result, Remaining} = split_binary(NewBinary, Give),
+ {State#t_framed{wrapped = NewState, read_buffer = Remaining}, {ok, Result}};
+ {NewState, Error} ->
+ {State#t_framed{wrapped = NewState}, Error}
+ end;
+ %% read of zero bytes
+ <<>> -> {State, {ok, <<>>}};
+ %% read buffer is nonempty
+ _ ->
+ Give = min(iolist_size(Binary), Len),
+ {Result, Remaining} = split_binary(Binary, Give),
+ {State#t_framed{read_buffer = Remaining}, {ok, Result}}
+ end.
+
+
+read_exact(State = #t_framed{wrapped = Wrapped, read_buffer = Buffer}, Len)
+when is_integer(Len), Len >= 0 ->
+ Binary = iolist_to_binary(Buffer),
+ case iolist_size(Binary) of
+ %% read buffer is larger than requested read size
+ X when X >= Len ->
+ {Result, Remaining} = split_binary(Binary, Len),
+ {State#t_framed{read_buffer = Remaining}, {ok, Result}};
+ %% read buffer is insufficient for requested read size
+ _ ->
+ case next_frame(Wrapped) of
+ {NewState, {ok, Frame}} ->
+ read_exact(
+ State#t_framed{wrapped = NewState, read_buffer = [Buffer, Frame]},
+ Len
+ );
+ {NewState, Error} ->
+ {State#t_framed{wrapped = NewState}, Error}
+ end
+ end.
+
+next_frame(Transport) ->
+ case thrift_transport:read_exact(Transport, 4) of
+ {NewState, {ok, <<FrameLength:32/integer-signed-big>>}} ->
+ thrift_transport:read_exact(NewState, FrameLength);
+ Error -> Error
+ end.
+
+
+write(State = #t_framed{write_buffer = Buffer}, Data) ->
+ {State#t_framed{write_buffer = [Buffer, Data]}, ok}.
+
+
+flush(State = #t_framed{write_buffer = Buffer, wrapped = Wrapped}) ->
+ case iolist_size(Buffer) of
+ %% if write buffer is empty, do nothing
+ 0 -> {State, ok};
+ FrameLen ->
+ Data = [<<FrameLen:32/integer-signed-big>>, Buffer],
+ {Written, Response} = thrift_transport:write(Wrapped, Data),
+ {Flushed, ok} = thrift_transport:flush(Written),
+ {State#t_framed{wrapped = Flushed, write_buffer = []}, Response}
+ end.
+
+
+close(State = #t_framed{wrapped = Wrapped}) ->
+ {Closed, Result} = thrift_transport:close(Wrapped),
+ {State#t_framed{wrapped = Closed}, Result}.
+
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_http_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_http_transport.erl
new file mode 100644
index 000000000..46bcede53
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_http_transport.erl
@@ -0,0 +1,116 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_http_transport).
+
+-behaviour(thrift_transport).
+
+%% API
+-export([new/2, new/3]).
+
+%% thrift_transport callbacks
+-export([write/2, read/2, flush/1, close/1]).
+
+-record(http_transport, {host, % string()
+ path, % string()
+ read_buffer, % iolist()
+ write_buffer, % iolist()
+ http_options, % see http(3)
+ extra_headers % [{str(), str()}, ...]
+ }).
+-type state() :: #http_transport{}.
+-include("thrift_transport_behaviour.hrl").
+
+new(Host, Path) ->
+ new(Host, Path, _Options = []).
+
+%%--------------------------------------------------------------------
+%% Options include:
+%% {http_options, HttpOptions} = See http(3)
+%% {extra_headers, ExtraHeaders} = List of extra HTTP headers
+%%--------------------------------------------------------------------
+new(Host, Path, Options) ->
+ State1 = #http_transport{host = Host,
+ path = Path,
+ read_buffer = [],
+ write_buffer = [],
+ http_options = [],
+ extra_headers = []},
+ ApplyOption =
+ fun
+ ({http_options, HttpOpts}, State = #http_transport{}) ->
+ State#http_transport{http_options = HttpOpts};
+ ({extra_headers, ExtraHeaders}, State = #http_transport{}) ->
+ State#http_transport{extra_headers = ExtraHeaders};
+ (Other, #http_transport{}) ->
+ {invalid_option, Other};
+ (_, Error) ->
+ Error
+ end,
+ case lists:foldl(ApplyOption, State1, Options) of
+ State2 = #http_transport{} ->
+ thrift_transport:new(?MODULE, State2);
+ Else ->
+ {error, Else}
+ end.
+
+%% Writes data into the buffer
+write(State = #http_transport{write_buffer = WBuf}, Data) ->
+ {State#http_transport{write_buffer = [WBuf, Data]}, ok}.
+
+%% Flushes the buffer, making a request
+flush(State = #http_transport{host = Host,
+ path = Path,
+ read_buffer = Rbuf,
+ write_buffer = Wbuf,
+ http_options = HttpOptions,
+ extra_headers = ExtraHeaders}) ->
+ case iolist_to_binary(Wbuf) of
+ <<>> ->
+ %% Don't bother flushing empty buffers.
+ {State, ok};
+ WBinary ->
+ {ok, {{_Version, 200, _ReasonPhrase}, _Headers, Body}} =
+ httpc:request(post,
+ {"http://" ++ Host ++ Path,
+ [{"User-Agent", "Erlang/thrift_http_transport"} | ExtraHeaders],
+ "application/x-thrift",
+ WBinary},
+ HttpOptions,
+ [{body_format, binary}]),
+
+ State1 = State#http_transport{read_buffer = [Rbuf, Body],
+ write_buffer = []},
+ {State1, ok}
+ end.
+
+close(State) ->
+ {State, ok}.
+
+read(State = #http_transport{read_buffer = RBuf}, Len) when is_integer(Len) ->
+ %% Pull off Give bytes, return them to the user, leave the rest in the buffer.
+ Give = min(iolist_size(RBuf), Len),
+ case iolist_to_binary(RBuf) of
+ <<Data:Give/binary, RBuf1/binary>> ->
+ Response = {ok, Data},
+ State1 = State#http_transport{read_buffer=RBuf1},
+ {State1, Response};
+ _ ->
+ {State, {error, 'EOF'}}
+ end.
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_json_parser.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_json_parser.erl
new file mode 100644
index 000000000..4e47f10ec
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_json_parser.erl
@@ -0,0 +1,419 @@
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+%% The json parser implementation was created by
+%% alisdair sullivan <alisdair@hartbrake.com> based on
+%% the jsx json library
+
+-module(thrift_json_parser).
+-export([parser/0, handle_event/2]).
+
+
+-record(config, {strict_utf8 = false :: boolean()}).
+
+
+parser() -> fun(JSON) -> start(JSON, {?MODULE, []}, [], #config{}) end.
+
+
+handle_event(Event, {Handler, State}, _Config) -> {Handler, Handler:handle_event(Event, State)}.
+
+handle_event(end_json, State) -> lists:reverse([end_json] ++ State);
+handle_event(Event, State) -> [Event] ++ State.
+
+
+%% whitespace
+-define(space, 16#20).
+-define(tab, 16#09).
+-define(cr, 16#0D).
+-define(newline, 16#0A).
+
+%% object delimiters
+-define(start_object, 16#7B).
+-define(end_object, 16#7D).
+
+%% array delimiters
+-define(start_array, 16#5B).
+-define(end_array, 16#5D).
+
+%% kv seperator
+-define(comma, 16#2C).
+-define(doublequote, 16#22).
+-define(singlequote, 16#27).
+-define(colon, 16#3A).
+
+%% string escape sequences
+-define(rsolidus, 16#5C).
+-define(solidus, 16#2F).
+
+%% math
+-define(zero, 16#30).
+-define(decimalpoint, 16#2E).
+-define(negative, 16#2D).
+-define(positive, 16#2B).
+
+%% comments
+-define(star, 16#2A).
+
+
+%% some useful guards
+-define(is_hex(Symbol),
+ (Symbol >= $a andalso Symbol =< $f) orelse
+ (Symbol >= $A andalso Symbol =< $F) orelse
+ (Symbol >= $0 andalso Symbol =< $9)
+).
+
+-define(is_nonzero(Symbol),
+ Symbol >= $1 andalso Symbol =< $9
+).
+
+-define(is_whitespace(Symbol),
+ Symbol =:= ?space; Symbol =:= ?tab; Symbol =:= ?cr; Symbol =:= ?newline
+).
+
+
+%% lists are benchmarked to be faster (tho higher in memory usage) than binaries
+new_seq() -> [].
+new_seq(C) -> [C].
+
+acc_seq(Seq, C) when is_list(C) -> lists:reverse(C) ++ Seq;
+acc_seq(Seq, C) -> [C] ++ Seq.
+
+end_seq(Seq) -> unicode:characters_to_binary(lists:reverse(Seq)).
+
+end_seq(Seq, _) -> end_seq(Seq).
+
+
+start(<<16#ef, 16#bb, 16#bf, Rest/binary>>, Handler, Stack, Config) ->
+ value(Rest, Handler, Stack, Config);
+start(Bin, Handler, Stack, Config) ->
+ value(Bin, Handler, Stack, Config).
+
+
+value(<<?doublequote, Rest/binary>>, Handler, Stack, Config) ->
+ string(Rest, Handler, new_seq(), Stack, Config);
+value(<<$t, Rest/binary>>, Handler, Stack, Config) ->
+ true(Rest, Handler, Stack, Config);
+value(<<$f, Rest/binary>>, Handler, Stack, Config) ->
+ false(Rest, Handler, Stack, Config);
+value(<<$n, Rest/binary>>, Handler, Stack, Config) ->
+ null(Rest, Handler, Stack, Config);
+value(<<?negative, Rest/binary>>, Handler, Stack, Config) ->
+ negative(Rest, Handler, new_seq($-), Stack, Config);
+value(<<?zero, Rest/binary>>, Handler, Stack, Config) ->
+ zero(Rest, Handler, new_seq($0), Stack, Config);
+value(<<S, Rest/binary>>, Handler, Stack, Config) when ?is_nonzero(S) ->
+ integer(Rest, Handler, new_seq(S), Stack, Config);
+value(<<?start_object, Rest/binary>>, Handler, Stack, Config) ->
+ object(Rest, handle_event(start_object, Handler, Config), [key|Stack], Config);
+value(<<?start_array, Rest/binary>>, Handler, Stack, Config) ->
+ array(Rest, handle_event(start_array, Handler, Config), [array|Stack], Config);
+value(<<S, Rest/binary>>, Handler, Stack, Config) when ?is_whitespace(S) ->
+ value(Rest, Handler, Stack, Config);
+value(_Bin, _Handler, _Stack, _Config) ->
+ erlang:error(badarg).
+
+
+object(<<?doublequote, Rest/binary>>, Handler, Stack, Config) ->
+ string(Rest, Handler, new_seq(), Stack, Config);
+object(<<?end_object, Rest/binary>>, Handler, [key|Stack], Config) ->
+ maybe_done(Rest, handle_event(end_object, Handler, Config), Stack, Config);
+object(<<S, Rest/binary>>, Handler, Stack, Config) when ?is_whitespace(S) ->
+ object(Rest, Handler, Stack, Config);
+object(_Bin, _Handler, _Stack, _Config) ->
+ erlang:error(badarg).
+
+
+array(<<?end_array, Rest/binary>>, Handler, [array|Stack], Config) ->
+ maybe_done(Rest, handle_event(end_array, Handler, Config), Stack, Config);
+array(<<S, Rest/binary>>, Handler, Stack, Config) when ?is_whitespace(S) ->
+ array(Rest, Handler, Stack, Config);
+array(Bin, Handler, Stack, Config) ->
+ value(Bin, Handler, Stack, Config).
+
+
+colon(<<?colon, Rest/binary>>, Handler, [key|Stack], Config) ->
+ value(Rest, Handler, [object|Stack], Config);
+colon(<<S, Rest/binary>>, Handler, Stack, Config) when ?is_whitespace(S) ->
+ colon(Rest, Handler, Stack, Config);
+colon(_Bin, _Handler, _Stack, _Config) ->
+ erlang:error(badarg).
+
+
+key(<<?doublequote, Rest/binary>>, Handler, Stack, Config) ->
+ string(Rest, Handler, new_seq(), Stack, Config);
+key(<<S, Rest/binary>>, Handler, Stack, Config) when ?is_whitespace(S) ->
+ key(Rest, Handler, Stack, Config);
+key(_Bin, _Handler, _Stack, _Config) ->
+ erlang:error(badarg).
+
+
+%% note that if you encounter an error from string and you can't find the clause that
+%% caused it here, it might be in unescape below
+string(<<?doublequote, Rest/binary>>, Handler, Acc, Stack, Config) ->
+ doublequote(Rest, Handler, Acc, Stack, Config);
+string(<<?solidus, Rest/binary>>, Handler, Acc, Stack, Config) ->
+ string(Rest, Handler, acc_seq(Acc, ?solidus), Stack, Config);
+string(<<?rsolidus/utf8, Rest/binary>>, Handler, Acc, Stack, Config) ->
+ unescape(Rest, Handler, Acc, Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X >= 16#20, X < 16#2028 ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X == 16#2028; X == 16#2029 ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X > 16#2029, X < 16#d800 ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X > 16#dfff, X < 16#fdd0 ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X > 16#fdef, X < 16#fffe ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X >= 16#10000, X < 16#1fffe ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X >= 16#20000, X < 16#2fffe ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X >= 16#30000, X < 16#3fffe ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X >= 16#40000, X < 16#4fffe ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X >= 16#50000, X < 16#5fffe ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X >= 16#60000, X < 16#6fffe ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X >= 16#70000, X < 16#7fffe ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X >= 16#80000, X < 16#8fffe ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X >= 16#90000, X < 16#9fffe ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X >= 16#a0000, X < 16#afffe ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X >= 16#b0000, X < 16#bfffe ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X >= 16#c0000, X < 16#cfffe ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X >= 16#d0000, X < 16#dfffe ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X >= 16#e0000, X < 16#efffe ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X >= 16#f0000, X < 16#ffffe ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+string(<<X/utf8, Rest/binary>>, Handler, Acc, Stack, Config) when X >= 16#100000, X < 16#10fffe ->
+ string(Rest, Handler, acc_seq(Acc, X), Stack, Config);
+%% surrogates
+string(<<237, X, _, Rest/binary>>, Handler, Acc, Stack, Config=#config{strict_utf8=false})
+ when X >= 160 ->
+ string(Rest, Handler, acc_seq(Acc, 16#fffd), Stack, Config);
+%% u+xfffe, u+xffff, control codes and other noncharacters
+string(<<_/utf8, Rest/binary>>, Handler, Acc, Stack, Config=#config{strict_utf8=false}) ->
+ string(Rest, Handler, acc_seq(Acc, 16#fffd), Stack, Config);
+%% u+fffe and u+ffff for R14BXX (subsequent runtimes will happily match the
+%% preceding clause
+string(<<239, 191, X, Rest/binary>>, Handler, Acc, Stack, Config=#config{strict_utf8=false})
+ when X == 190; X == 191 ->
+ string(Rest, Handler, acc_seq(Acc, 16#fffd), Stack, Config);
+%% overlong encodings and missing continuations of a 2 byte sequence
+string(<<X, Rest/binary>>, Handler, Acc, Stack, Config=#config{strict_utf8=false})
+ when X >= 192, X =< 223 ->
+ strip_continuations(Rest, Handler, Acc, Stack, Config, 1);
+%% overlong encodings and missing continuations of a 3 byte sequence
+string(<<X, Rest/binary>>, Handler, Acc, Stack, Config=#config{strict_utf8=false})
+ when X >= 224, X =< 239 ->
+ strip_continuations(Rest, Handler, Acc, Stack, Config, 2);
+%% overlong encodings and missing continuations of a 4 byte sequence
+string(<<X, Rest/binary>>, Handler, Acc, Stack, Config=#config{strict_utf8=false})
+ when X >= 240, X =< 247 ->
+ strip_continuations(Rest, Handler, Acc, Stack, Config, 3);
+%% incompletes and unexpected bytes, including orphan continuations
+string(<<_, Rest/binary>>, Handler, Acc, Stack, Config=#config{strict_utf8=false}) ->
+ string(Rest, Handler, acc_seq(Acc, 16#fffd), Stack, Config);
+string(_Bin, _Handler, _Acc, _Stack, _Config) ->
+ erlang:error(badarg).
+
+
+doublequote(Rest, Handler, Acc, [key|_] = Stack, Config) ->
+ colon(Rest, handle_event({key, end_seq(Acc, Config)}, Handler, Config), Stack, Config);
+doublequote(Rest, Handler, Acc, Stack, Config) ->
+ maybe_done(Rest, handle_event({string, end_seq(Acc, Config)}, Handler, Config), Stack, Config).
+
+
+%% strips continuation bytes after bad utf bytes, guards against both too short
+%% and overlong sequences. N is the maximum number of bytes to strip
+strip_continuations(<<Rest/binary>>, Handler, Acc, Stack, Config, 0) ->
+ string(Rest, Handler, acc_seq(Acc, 16#fffd), Stack, Config);
+strip_continuations(<<X, Rest/binary>>, Handler, Acc, Stack, Config, N) when X >= 128, X =< 191 ->
+ strip_continuations(Rest, Handler, Acc, Stack, Config, N - 1);
+%% not a continuation byte, insert a replacement character for sequence thus
+%% far and dispatch back to string
+strip_continuations(<<Rest/binary>>, Handler, Acc, Stack, Config, _) ->
+ string(Rest, Handler, acc_seq(Acc, 16#fffd), Stack, Config).
+
+
+%% this all gets really gross and should probably eventually be folded into
+%% but for now it fakes being part of string on incompletes and errors
+unescape(<<$b, Rest/binary>>, Handler, Acc, Stack, Config) ->
+ string(Rest, Handler, acc_seq(Acc, $\b), Stack, Config);
+unescape(<<$f, Rest/binary>>, Handler, Acc, Stack, Config) ->
+ string(Rest, Handler, acc_seq(Acc, $\f), Stack, Config);
+unescape(<<$n, Rest/binary>>, Handler, Acc, Stack, Config) ->
+ string(Rest, Handler, acc_seq(Acc, $\n), Stack, Config);
+unescape(<<$r, Rest/binary>>, Handler, Acc, Stack, Config) ->
+ string(Rest, Handler, acc_seq(Acc, $\r), Stack, Config);
+unescape(<<$t, Rest/binary>>, Handler, Acc, Stack, Config) ->
+ string(Rest, Handler, acc_seq(Acc, $\t), Stack, Config);
+unescape(<<?doublequote, Rest/binary>>, Handler, Acc, Stack, Config) ->
+ string(Rest, Handler, acc_seq(Acc, $\"), Stack, Config);
+unescape(<<?rsolidus, Rest/binary>>, Handler, Acc, Stack, Config) ->
+ string(Rest, Handler, acc_seq(Acc, $\\), Stack, Config);
+unescape(<<?solidus, Rest/binary>>, Handler, Acc, Stack, Config) ->
+ string(Rest, Handler, acc_seq(Acc, $/), Stack, Config);
+unescape(<<$u, $d, A, B, C, ?rsolidus, $u, $d, X, Y, Z, Rest/binary>>, Handler, Acc, Stack, Config)
+ when (A == $8 orelse A == $9 orelse A == $a orelse A == $b),
+ (X == $c orelse X == $d orelse X == $e orelse X == $f),
+ ?is_hex(B), ?is_hex(C), ?is_hex(Y), ?is_hex(Z)
+ ->
+ High = erlang:list_to_integer([$d, A, B, C], 16),
+ Low = erlang:list_to_integer([$d, X, Y, Z], 16),
+ Codepoint = (High - 16#d800) * 16#400 + (Low - 16#dc00) + 16#10000,
+ string(Rest, Handler, acc_seq(Acc, Codepoint), Stack, Config);
+unescape(<<$u, $d, A, B, C, ?rsolidus, $u, W, X, Y, Z, Rest/binary>>, Handler, Acc, Stack, Config)
+ when (A == $8 orelse A == $9 orelse A == $a orelse A == $b),
+ ?is_hex(B), ?is_hex(C), ?is_hex(W), ?is_hex(X), ?is_hex(Y), ?is_hex(Z)
+ ->
+ string(Rest, Handler, acc_seq(Acc, [16#fffd, 16#fffd]), Stack, Config);
+unescape(<<$u, A, B, C, D, Rest/binary>>, Handler, Acc, Stack, Config)
+ when ?is_hex(A), ?is_hex(B), ?is_hex(C), ?is_hex(D) ->
+ case erlang:list_to_integer([A, B, C, D], 16) of
+ Codepoint when Codepoint < 16#d800; Codepoint > 16#dfff ->
+ string(Rest, Handler, acc_seq(Acc, Codepoint), Stack, Config);
+ _ ->
+ string(Rest, Handler, acc_seq(Acc, 16#fffd), Stack, Config)
+ end;
+unescape(_Bin, _Handler, _Acc, _Stack, _Config) ->
+ erlang:error(badarg).
+
+
+%% like in strings, there's some pseudo states in here that will never
+%% show up in errors or incompletes. some show up in value, some show
+%% up in integer, decimal or exp
+negative(<<$0, Rest/binary>>, Handler, Acc, Stack, Config) ->
+ zero(Rest, Handler, acc_seq(Acc, $0), Stack, Config);
+negative(<<S, Rest/binary>>, Handler, Acc, Stack, Config) when ?is_nonzero(S) ->
+ integer(Rest, Handler, acc_seq(Acc, S), Stack, Config);
+negative(_Bin, _Handler, _Acc, _Stack, _Config) ->
+ erlang:error(badarg).
+
+
+zero(<<?decimalpoint, Rest/binary>>, Handler, Acc, Stack, Config) ->
+ decimal(Rest, Handler, acc_seq(Acc, ?decimalpoint), Stack, Config);
+zero(<<S, Rest/binary>>, Handler, Acc, Stack, Config) when S =:= $e; S =:= $E ->
+ e(Rest, Handler, acc_seq(Acc, ".0e"), Stack, Config);
+zero(Bin, Handler, Acc, Stack, Config) ->
+ finish_number(Bin, Handler, {zero, Acc}, Stack, Config).
+
+
+integer(<<S, Rest/binary>>, Handler, Acc, Stack, Config) when S =:= ?zero; ?is_nonzero(S) ->
+ integer(Rest, Handler, acc_seq(Acc, S), Stack, Config);
+integer(<<?decimalpoint, Rest/binary>>, Handler, Acc, Stack, Config) ->
+ initialdecimal(Rest, Handler, acc_seq(Acc, ?decimalpoint), Stack, Config);
+integer(<<S, Rest/binary>>, Handler, Acc, Stack, Config) when S =:= $e; S =:= $E ->
+ e(Rest, Handler, acc_seq(Acc, ".0e"), Stack, Config);
+integer(Bin, Handler, Acc, Stack, Config) ->
+ finish_number(Bin, Handler, {integer, Acc}, Stack, Config).
+
+
+initialdecimal(<<S, Rest/binary>>, Handler, Acc, Stack, Config) when S =:= ?zero; ?is_nonzero(S) ->
+ decimal(Rest, Handler, acc_seq(Acc, S), Stack, Config);
+initialdecimal(_Bin, _Handler, _Acc, _Stack, _Config) ->
+ erlang:error(badarg).
+
+
+decimal(<<S, Rest/binary>>, Handler, Acc, Stack, Config) when S =:= ?zero; ?is_nonzero(S) ->
+ decimal(Rest, Handler, acc_seq(Acc, S), Stack, Config);
+decimal(<<S, Rest/binary>>, Handler, Acc, Stack, Config) when S =:= $e; S =:= $E ->
+ e(Rest, Handler, acc_seq(Acc, $e), Stack, Config);
+decimal(Bin, Handler, Acc, Stack, Config) ->
+ finish_number(Bin, Handler, {decimal, Acc}, Stack, Config).
+
+
+e(<<S, Rest/binary>>, Handler, Acc, Stack, Config) when S =:= ?zero; ?is_nonzero(S) ->
+ exp(Rest, Handler, acc_seq(Acc, S), Stack, Config);
+e(<<Sign, Rest/binary>>, Handler, Acc, Stack, Config) when Sign =:= ?positive; Sign =:= ?negative ->
+ ex(Rest, Handler, acc_seq(Acc, Sign), Stack, Config);
+e(_Bin, _Handler, _Acc, _Stack, _Config) ->
+ erlang:error(badarg).
+
+
+ex(<<S, Rest/binary>>, Handler, Acc, Stack, Config) when S =:= ?zero; ?is_nonzero(S) ->
+ exp(Rest, Handler, acc_seq(Acc, S), Stack, Config);
+ex(_Bin, _Handler, _Acc, _Stack, _Config) ->
+ erlang:error(badarg).
+
+
+exp(<<S, Rest/binary>>, Handler, Acc, Stack, Config) when S =:= ?zero; ?is_nonzero(S) ->
+ exp(Rest, Handler, acc_seq(Acc, S), Stack, Config);
+exp(Bin, Handler, Acc, Stack, Config) ->
+ finish_number(Bin, Handler, {exp, Acc}, Stack, Config).
+
+
+finish_number(Rest, Handler, Acc, [], Config) ->
+ maybe_done(Rest, handle_event(format_number(Acc), Handler, Config), [], Config);
+finish_number(Rest, Handler, Acc, Stack, Config) ->
+ maybe_done(Rest, handle_event(format_number(Acc), Handler, Config), Stack, Config).
+
+
+format_number({zero, Acc}) -> {integer, list_to_integer(lists:reverse(Acc))};
+format_number({integer, Acc}) -> {integer, list_to_integer(lists:reverse(Acc))};
+format_number({decimal, Acc}) -> {float, list_to_float(lists:reverse(Acc))};
+format_number({exp, Acc}) -> {float, list_to_float(lists:reverse(Acc))}.
+
+
+true(<<$r, $u, $e, Rest/binary>>, Handler, Stack, Config) ->
+ maybe_done(Rest, handle_event({literal, true}, Handler, Config), Stack, Config);
+true(_Bin, _Handler, _Stack, _Config) ->
+ erlang:error(badarg).
+
+
+false(<<$a, $l, $s, $e, Rest/binary>>, Handler, Stack, Config) ->
+ maybe_done(Rest, handle_event({literal, false}, Handler, Config), Stack, Config);
+false(_Bin, _Handler, _Stack, _Config) ->
+ erlang:error(badarg).
+
+
+null(<<$u, $l, $l, Rest/binary>>, Handler, Stack, Config) ->
+ maybe_done(Rest, handle_event({literal, null}, Handler, Config), Stack, Config);
+null(_Bin, _Handler, _Stack, _Config) ->
+ erlang:error(badarg).
+
+
+maybe_done(<<Rest/binary>>, Handler, [], Config) ->
+ done(Rest, handle_event(end_json, Handler, Config), [], Config);
+maybe_done(<<?end_object, Rest/binary>>, Handler, [object|Stack], Config) ->
+ maybe_done(Rest, handle_event(end_object, Handler, Config), Stack, Config);
+maybe_done(<<?end_array, Rest/binary>>, Handler, [array|Stack], Config) ->
+ maybe_done(Rest, handle_event(end_array, Handler, Config), Stack, Config);
+maybe_done(<<?comma, Rest/binary>>, Handler, [object|Stack], Config) ->
+ key(Rest, Handler, [key|Stack], Config);
+maybe_done(<<?comma, Rest/binary>>, Handler, [array|_] = Stack, Config) ->
+ value(Rest, Handler, Stack, Config);
+maybe_done(<<S, Rest/binary>>, Handler, Stack, Config) when ?is_whitespace(S) ->
+ maybe_done(Rest, Handler, Stack, Config);
+maybe_done(_Bin, _Handler, _Stack, _Config) ->
+ erlang:error(badarg).
+
+
+done(<<S, Rest/binary>>, Handler, [], Config) when ?is_whitespace(S) ->
+ done(Rest, Handler, [], Config);
+done(<<>>, {_Handler, State}, [], _Config) -> State;
+done(_Bin, _Handler, _Stack, _Config) -> erlang:error(badarg).
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_json_protocol.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_json_protocol.erl
new file mode 100644
index 000000000..c5f3da838
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_json_protocol.erl
@@ -0,0 +1,567 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+%% The JSON protocol implementation was created by
+%% Peter Neumark <neumark.peter@gmail.com> based on
+%% the binary protocol implementation.
+
+-module(thrift_json_protocol).
+
+-behaviour(thrift_protocol).
+
+-include("thrift_constants.hrl").
+-include("thrift_protocol.hrl").
+
+-export([new/1, new/2,
+ read/2,
+ write/2,
+ flush_transport/1,
+ close_transport/1,
+ new_protocol_factory/2
+ ]).
+
+-record(json_context, {
+ % the type of json_context: array or object
+ type,
+ % fields read or written
+ fields_processed = 0
+}).
+
+-record(json_protocol, {
+ transport,
+ context_stack = [],
+ jsx
+}).
+-type state() :: #json_protocol{}.
+-include("thrift_protocol_behaviour.hrl").
+
+-define(VERSION_1, 1).
+-define(JSON_DOUBLE_PRECISION, 16).
+
+typeid_to_json(?tType_BOOL) -> "tf";
+typeid_to_json(?tType_BYTE) -> "i8";
+typeid_to_json(?tType_DOUBLE) -> "dbl";
+typeid_to_json(?tType_I8) -> "i8";
+typeid_to_json(?tType_I16) -> "i16";
+typeid_to_json(?tType_I32) -> "i32";
+typeid_to_json(?tType_I64) -> "i64";
+typeid_to_json(?tType_STRING) -> "str";
+typeid_to_json(?tType_STRUCT) -> "rec";
+typeid_to_json(?tType_MAP) -> "map";
+typeid_to_json(?tType_SET) -> "set";
+typeid_to_json(?tType_LIST) -> "lst".
+
+json_to_typeid("tf") -> ?tType_BOOL;
+json_to_typeid("dbl") -> ?tType_DOUBLE;
+json_to_typeid("i8") -> ?tType_I8;
+json_to_typeid("i16") -> ?tType_I16;
+json_to_typeid("i32") -> ?tType_I32;
+json_to_typeid("i64") -> ?tType_I64;
+json_to_typeid("str") -> ?tType_STRING;
+json_to_typeid("rec") -> ?tType_STRUCT;
+json_to_typeid("map") -> ?tType_MAP;
+json_to_typeid("set") -> ?tType_SET;
+json_to_typeid("lst") -> ?tType_LIST.
+
+start_context(object) -> "{";
+start_context(array) -> "[".
+
+end_context(object) -> "}";
+end_context(array) -> "]".
+
+
+new(Transport) ->
+ new(Transport, _Options = []).
+
+new(Transport, _Options) ->
+ State = #json_protocol{transport = Transport},
+ thrift_protocol:new(?MODULE, State).
+
+flush_transport(This = #json_protocol{transport = Transport}) ->
+ {NewTransport, Result} = thrift_transport:flush(Transport),
+ {This#json_protocol{
+ transport = NewTransport,
+ context_stack = []
+ }, Result}.
+
+close_transport(This = #json_protocol{transport = Transport}) ->
+ {NewTransport, Result} = thrift_transport:close(Transport),
+ {This#json_protocol{
+ transport = NewTransport,
+ context_stack = [],
+ jsx = undefined
+ }, Result}.
+
+%%%
+%%% instance methods
+%%%
+% places a new context on the stack:
+write(#json_protocol{context_stack = Stack} = State0, {enter_context, Type}) ->
+ {State1, ok} = write_values(State0, [{context_pre_item, false}]),
+ State2 = State1#json_protocol{context_stack = [
+ #json_context{type=Type}|Stack]},
+ write_values(State2, [list_to_binary(start_context(Type))]);
+
+% removes the topmost context from stack
+write(#json_protocol{context_stack = [CurrCtxt|Stack]} = State0, {exit_context}) ->
+ Type = CurrCtxt#json_context.type,
+ State1 = State0#json_protocol{context_stack = Stack},
+ write_values(State1, [
+ list_to_binary(end_context(Type)),
+ {context_post_item, false}
+ ]);
+
+% writes necessary prelude to field or container depending on current context
+write(#json_protocol{context_stack = []} = This0,
+ {context_pre_item, _}) -> {This0, ok};
+write(#json_protocol{context_stack = [Context|_CtxtTail]} = This0,
+ {context_pre_item, MayNeedQuotes}) ->
+ FieldNo = Context#json_context.fields_processed,
+ CtxtType = Context#json_context.type,
+ Rem = FieldNo rem 2,
+ case {CtxtType, FieldNo, Rem, MayNeedQuotes} of
+ {array, N, _, _} when N > 0 -> % array element (not first)
+ write(This0, <<",">>);
+ {object, 0, _, true} -> % non-string object key (first)
+ write(This0, <<"\"">>);
+ {object, N, 0, true} when N > 0 -> % non-string object key (not first)
+ write(This0, <<",\"">>);
+ {object, N, 0, false} when N > 0-> % string object key (not first)
+ write(This0, <<",">>);
+ _ -> % no pre-field necessary
+ {This0, ok}
+ end;
+
+% writes necessary postlude to field or container depending on current context
+write(#json_protocol{context_stack = []} = This0,
+ {context_post_item, _}) -> {This0, ok};
+write(#json_protocol{context_stack = [Context|CtxtTail]} = This0,
+ {context_post_item, MayNeedQuotes}) ->
+ FieldNo = Context#json_context.fields_processed,
+ CtxtType = Context#json_context.type,
+ Rem = FieldNo rem 2,
+ {This1, ok} = case {CtxtType, Rem, MayNeedQuotes} of
+ {object, 0, true} -> % non-string object key
+ write(This0, <<"\":">>);
+ {object, 0, false} -> % string object key
+ write(This0, <<":">>);
+ _ -> % no pre-field necessary
+ {This0, ok}
+ end,
+ NewContext = Context#json_context{fields_processed = FieldNo + 1},
+ {This1#json_protocol{context_stack=[NewContext|CtxtTail]}, ok};
+
+write(This0, #protocol_message_begin{
+ name = Name,
+ type = Type,
+ seqid = Seqid}) ->
+ write_values(This0, [
+ {enter_context, array},
+ {i32, ?VERSION_1},
+ {string, Name},
+ {i32, Type},
+ {i32, Seqid}
+ ]);
+
+write(This, message_end) ->
+ write_values(This, [{exit_context}]);
+
+% Example field expression: "1":{"dbl":3.14}
+write(This0, #protocol_field_begin{
+ name = _Name,
+ type = Type,
+ id = Id}) ->
+ write_values(This0, [
+ % entering 'outer' object
+ {i16, Id},
+ % entering 'outer' object
+ {enter_context, object},
+ {string, typeid_to_json(Type)}
+ ]);
+
+write(This, field_stop) ->
+ {This, ok};
+
+write(This, field_end) ->
+ write_values(This,[{exit_context}]);
+
+% Example message with map: [1,"testMap",1,0,{"1":{"map":["i32","i32",3,{"7":77,"8":88,"9":99}]}}]
+write(This0, #protocol_map_begin{
+ ktype = Ktype,
+ vtype = Vtype,
+ size = Size}) ->
+ write_values(This0, [
+ {enter_context, array},
+ {string, typeid_to_json(Ktype)},
+ {string, typeid_to_json(Vtype)},
+ {i32, Size},
+ {enter_context, object}
+ ]);
+
+write(This, map_end) ->
+ write_values(This,[
+ {exit_context},
+ {exit_context}
+ ]);
+
+write(This0, #protocol_list_begin{
+ etype = Etype,
+ size = Size}) ->
+ write_values(This0, [
+ {enter_context, array},
+ {string, typeid_to_json(Etype)},
+ {i32, Size}
+ ]);
+
+write(This, list_end) ->
+ write_values(This,[
+ {exit_context}
+ ]);
+
+% example message with set: [1,"testSet",1,0,{"1":{"set":["i32",3,1,2,3]}}]
+write(This0, #protocol_set_begin{
+ etype = Etype,
+ size = Size}) ->
+ write_values(This0, [
+ {enter_context, array},
+ {string, typeid_to_json(Etype)},
+ {i32, Size}
+ ]);
+
+write(This, set_end) ->
+ write_values(This,[
+ {exit_context}
+ ]);
+% example message with struct: [1,"testStruct",1,0,{"1":{"rec":{"1":{"str":"worked"},"4":{"i8":1},"9":{"i32":1073741824},"11":{"i64":1152921504606847000}}}}]
+write(This, #protocol_struct_begin{}) ->
+ write_values(This, [
+ {enter_context, object}
+ ]);
+
+write(This, struct_end) ->
+ write_values(This,[
+ {exit_context}
+ ]);
+
+write(This, {bool, true}) -> write_values(This, [
+ {context_pre_item, true},
+ <<"true">>,
+ {context_post_item, true}
+ ]);
+
+write(This, {bool, false}) -> write_values(This, [
+ {context_pre_item, true},
+ <<"false">>,
+ {context_post_item, true}
+ ]);
+
+write(This, {byte, Byte}) -> write_values(This, [
+ {context_pre_item, true},
+ list_to_binary(integer_to_list(Byte)),
+ {context_post_item, true}
+ ]);
+
+write(This, {i16, I16}) ->
+ write(This, {byte, I16});
+
+write(This, {i32, I32}) ->
+ write(This, {byte, I32});
+
+write(This, {i64, I64}) ->
+ write(This, {byte, I64});
+
+write(This, {double, Double}) -> write_values(This, [
+ {context_pre_item, true},
+ list_to_binary(io_lib:format("~.*f", [?JSON_DOUBLE_PRECISION,Double])),
+ {context_post_item, true}
+ ]);
+
+write(This0, {string, Str}) -> write_values(This0, [
+ {context_pre_item, false},
+ case is_binary(Str) of
+ true -> Str;
+ false -> <<"\"", (list_to_binary(Str))/binary, "\"">>
+ end,
+ {context_post_item, false}
+ ]);
+
+%% TODO: binary fields should be base64 encoded?
+
+%% Data :: iolist()
+write(This = #json_protocol{transport = Trans}, Data) ->
+ %io:format("Data ~p Ctxt ~p~n~n", [Data, This#json_protocol.context_stack]),
+ {NewTransport, Result} = thrift_transport:write(Trans, Data),
+ {This#json_protocol{transport = NewTransport}, Result}.
+
+write_values(This0, ValueList) ->
+ FinalState = lists:foldl(
+ fun(Val, ThisIn) ->
+ {ThisOut, ok} = write(ThisIn, Val),
+ ThisOut
+ end,
+ This0,
+ ValueList),
+ {FinalState, ok}.
+
+%% I wish the erlang version of the transport interface included a
+%% read_all function (like eg. the java implementation). Since it doesn't,
+%% here's my version (even though it probably shouldn't be in this file).
+%%
+%% The resulting binary is immediately send to the JSX stream parser.
+%% Subsequent calls to read actually operate on the events returned by JSX.
+read_all(#json_protocol{transport = Transport0} = State) ->
+ {Transport1, Bin} = read_all_1(Transport0, []),
+ P = thrift_json_parser:parser(),
+ [First|Rest] = P(Bin),
+ State#json_protocol{
+ transport = Transport1,
+ jsx = {event, First, Rest}
+ }.
+
+read_all_1(Transport0, IoList) ->
+ {Transport1, Result} = thrift_transport:read(Transport0, 1),
+ case Result of
+ {ok, <<>>} -> % nothing read: assume we're done
+ {Transport1, iolist_to_binary(lists:reverse(IoList))};
+ {ok, Data} -> % character successfully read; read more
+ read_all_1(Transport1, [Data|IoList]);
+ {error, 'EOF'} -> % we're done
+ {Transport1, iolist_to_binary(lists:reverse(IoList))}
+ end.
+
+% Expect reads an event from the JSX event stream. It receives an event or data
+% type as input. Comparing the read event from the one is was passed, it
+% returns an error if something other than the expected value is encountered.
+% Expect also maintains the context stack in #json_protocol.
+expect(#json_protocol{jsx={event, {Type, Data}=Ev, [Next|Rest]}}=State, ExpectedType) ->
+ NextState = State#json_protocol{jsx={event, Next, Rest}},
+ case Type == ExpectedType of
+ true ->
+ {NextState, {ok, convert_data(Type, Data)}};
+ false ->
+ {NextState, {error, {unexpected_json_event, Ev}}}
+ end;
+
+expect(#json_protocol{jsx={event, Event, Next}}=State, ExpectedEvent) ->
+ expect(State#json_protocol{jsx={event, {Event, none}, Next}}, ExpectedEvent).
+
+convert_data(integer, I) -> list_to_integer(I);
+convert_data(float, F) -> list_to_float(F);
+convert_data(_, D) -> D.
+
+expect_many(State, ExpectedList) ->
+ expect_many_1(State, ExpectedList, [], ok).
+
+expect_many_1(State, [], ResultList, Status) ->
+ {State, {Status, lists:reverse(ResultList)}};
+expect_many_1(State, [Expected|ExpTail], ResultList, _PrevStatus) ->
+ {State1, {Status, Data}} = expect(State, Expected),
+ NewResultList = [Data|ResultList],
+ case Status of
+ % in case of error, end prematurely
+ error -> expect_many_1(State1, [], NewResultList, Status);
+ ok -> expect_many_1(State1, ExpTail, NewResultList, Status)
+ end.
+
+% wrapper around expect to make life easier for container opening/closing functions
+expect_nodata(This, ExpectedList) ->
+ case expect_many(This, ExpectedList) of
+ {State, {ok, _}} ->
+ {State, ok};
+ Error ->
+ Error
+ end.
+
+read_field(#json_protocol{jsx={event, Field, [Next|Rest]}} = State) ->
+ NewState = State#json_protocol{jsx={event, Next, Rest}},
+ {NewState, Field}.
+
+read(This0, message_begin) ->
+ % call read_all to get the contents of the transport buffer into JSX.
+ This1 = read_all(This0),
+ case expect_many(This1,
+ [start_array, integer, string, integer, integer]) of
+ {This2, {ok, [_, Version, Name, Type, SeqId]}} ->
+ case Version =:= ?VERSION_1 of
+ true ->
+ {This2, #protocol_message_begin{name = Name,
+ type = Type,
+ seqid = SeqId}};
+ false ->
+ {This2, {error, no_json_protocol_version}}
+ end;
+ Other -> Other
+ end;
+
+read(This, message_end) ->
+ expect_nodata(This, [end_array]);
+
+read(This, struct_begin) ->
+ expect_nodata(This, [start_object]);
+
+read(This, struct_end) ->
+ expect_nodata(This, [end_object]);
+
+read(This0, field_begin) ->
+ {This1, Read} = expect_many(This0,
+ [%field id
+ key,
+ % {} surrounding field
+ start_object,
+ % type of field
+ key]),
+ case Read of
+ {ok, [FieldIdStr, _, FieldType]} ->
+ {This1, #protocol_field_begin{
+ type = json_to_typeid(FieldType),
+ id = list_to_integer(FieldIdStr)}}; % TODO: do we need to wrap this in a try/catch?
+ {error,[{unexpected_json_event, {end_object,none}}]} ->
+ {This1, #protocol_field_begin{type = ?tType_STOP}};
+ Other ->
+ io:format("**** OTHER branch selected ****"),
+ {This1, Other}
+ end;
+
+read(This, field_end) ->
+ expect_nodata(This, [end_object]);
+
+% Example message with map: [1,"testMap",1,0,{"1":{"map":["i32","i32",3,{"7":77,"8":88,"9":99}]}}]
+read(This0, map_begin) ->
+ case expect_many(This0,
+ [start_array,
+ % key type
+ string,
+ % value type
+ string,
+ % size
+ integer,
+ % the following object contains the map
+ start_object]) of
+ {This1, {ok, [_, Ktype, Vtype, Size, _]}} ->
+ {This1, #protocol_map_begin{ktype = Ktype,
+ vtype = Vtype,
+ size = Size}};
+ Other -> Other
+ end;
+
+read(This, map_end) ->
+ expect_nodata(This, [end_object, end_array]);
+
+read(This0, list_begin) ->
+ case expect_many(This0,
+ [start_array,
+ % element type
+ string,
+ % size
+ integer]) of
+ {This1, {ok, [_, Etype, Size]}} ->
+ {This1, #protocol_list_begin{
+ etype = Etype,
+ size = Size}};
+ Other -> Other
+ end;
+
+read(This, list_end) ->
+ expect_nodata(This, [end_array]);
+
+% example message with set: [1,"testSet",1,0,{"1":{"set":["i32",3,1,2,3]}}]
+read(This0, set_begin) ->
+ case expect_many(This0,
+ [start_array,
+ % element type
+ string,
+ % size
+ integer]) of
+ {This1, {ok, [_, Etype, Size]}} ->
+ {This1, #protocol_set_begin{
+ etype = Etype,
+ size = Size}};
+ Other -> Other
+ end;
+
+read(This, set_end) ->
+ expect_nodata(This, [end_array]);
+
+read(This0, field_stop) ->
+ {This0, ok};
+%%
+
+read(This0, bool) ->
+ {This1, Field} = read_field(This0),
+ Value = case Field of
+ {literal, I} ->
+ {ok, I};
+ _Other ->
+ {error, unexpected_event_for_boolean}
+ end,
+ {This1, Value};
+
+read(This0, byte) ->
+ {This1, Field} = read_field(This0),
+ Value = case Field of
+ {key, K} ->
+ {ok, list_to_integer(K)};
+ {integer, I} ->
+ {ok, list_to_integer(I)};
+ _Other ->
+ {error, unexpected_event_for_integer}
+ end,
+ {This1, Value};
+
+read(This0, i16) ->
+ read(This0, byte);
+
+read(This0, i32) ->
+ read(This0, byte);
+
+read(This0, i64) ->
+ read(This0, byte);
+
+read(This0, double) ->
+ {This1, Field} = read_field(This0),
+ Value = case Field of
+ {float, I} ->
+ {ok, list_to_float(I)};
+ _Other ->
+ {error, unexpected_event_for_double}
+ end,
+ {This1, Value};
+
+% returns a binary directly, call binary_to_list if necessary
+read(This0, string) ->
+ {This1, Field} = read_field(This0),
+ Value = case Field of
+ {string, I} ->
+ {ok, I};
+ {key, J} ->
+ {ok, J};
+ _Other ->
+ {error, unexpected_event_for_string}
+ end,
+ {This1, Value}.
+
+%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% returns a (fun() -> thrift_protocol())
+new_protocol_factory(TransportFactory, _Options) ->
+ % Only strice read/write are implemented
+ F = fun() ->
+ {ok, Transport} = TransportFactory(),
+ thrift_json_protocol:new(Transport, [])
+ end,
+ {ok, F}.
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_membuffer_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_membuffer_transport.erl
new file mode 100644
index 000000000..be9acb23a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_membuffer_transport.erl
@@ -0,0 +1,83 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_membuffer_transport).
+
+-behaviour(thrift_transport).
+
+%% constructors
+-export([new/0, new/1]).
+%% protocol callbacks
+-export([read/2, read_exact/2, write/2, flush/1, close/1]).
+
+
+-record(t_membuffer, {
+ buffer = []
+}).
+
+-type state() :: #t_membuffer{}.
+
+
+-spec new() -> thrift_transport:t_transport().
+
+new() -> new([]).
+
+-spec new(Buf::iodata()) -> thrift_transport:t_transport().
+
+new(Buf) when is_list(Buf) ->
+ State = #t_membuffer{buffer = Buf},
+ thrift_transport:new(?MODULE, State);
+new(Buf) when is_binary(Buf) ->
+ State = #t_membuffer{buffer = [Buf]},
+ thrift_transport:new(?MODULE, State).
+
+
+-include("thrift_transport_behaviour.hrl").
+
+
+read(State = #t_membuffer{buffer = Buf}, Len)
+when is_integer(Len), Len >= 0 ->
+ Binary = iolist_to_binary(Buf),
+ Give = min(iolist_size(Binary), Len),
+ {Result, Remaining} = split_binary(Binary, Give),
+ {State#t_membuffer{buffer = Remaining}, {ok, Result}}.
+
+
+read_exact(State = #t_membuffer{buffer = Buf}, Len)
+when is_integer(Len), Len >= 0 ->
+ Binary = iolist_to_binary(Buf),
+ case iolist_size(Binary) of
+ X when X >= Len ->
+ {Result, Remaining} = split_binary(Binary, Len),
+ {State#t_membuffer{buffer = Remaining}, {ok, Result}};
+ _ ->
+ {State, {error, eof}}
+ end.
+
+
+write(State = #t_membuffer{buffer = Buf}, Data)
+when is_list(Data); is_binary(Data) ->
+ {State#t_membuffer{buffer = [Buf, Data]}, ok}.
+
+
+flush(State) -> {State, ok}.
+
+
+close(State) -> {State, ok}.
+
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_memory_buffer.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_memory_buffer.erl
new file mode 100644
index 000000000..6a59ea56d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_memory_buffer.erl
@@ -0,0 +1,47 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_memory_buffer).
+
+-behaviour(thrift_transport).
+
+%% constructors
+-export([new/0, new/1]).
+%% protocol callbacks
+-export([read/2, write/2, flush/1, close/1]).
+%% legacy api
+-export([new_transport_factory/0]).
+
+
+%% wrapper around thrift_membuffer_transport for legacy reasons
+
+new() -> thrift_membuffer_transport:new().
+
+new(State) -> thrift_membuffer_transport:new(State).
+
+new_transport_factory() -> {ok, fun() -> new() end}.
+
+write(State, Data) -> thrift_membuffer_transport:write(State, Data).
+
+read(State, Data) -> thrift_membuffer_transport:read(State, Data).
+
+flush(State) -> thrift_membuffer_transport:flush(State).
+
+close(State) -> thrift_membuffer_transport:close(State).
+
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_multiplexed_map_wrapper.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_multiplexed_map_wrapper.erl
new file mode 100644
index 000000000..34c5e95f5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_multiplexed_map_wrapper.erl
@@ -0,0 +1,57 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_multiplexed_map_wrapper).
+
+-export([
+ new/0
+ ,store/3
+ ,find/2
+ ,fetch/2
+ ]).
+
+-type service_handler() :: nonempty_string().
+-type module_() :: atom().
+-type service_handler_map() :: [{ServiceHandler::service_handler(), Module::module_()}].
+
+-spec new() -> service_handler_map().
+new() ->
+ orddict:new().
+
+-spec store(ServiceHandler, Module, Map) -> NewMap when
+ ServiceHandler :: service_handler(),
+ Module :: module_(),
+ Map :: service_handler_map(),
+ NewMap :: service_handler_map().
+store(ServiceHandler, Module, Map) ->
+ orddict:store(ServiceHandler, Module, Map).
+
+-spec find(ServiceHandler, Map) -> {ok, Module} | error when
+ ServiceHandler :: service_handler(),
+ Module :: module_(),
+ Map :: service_handler_map().
+find(ServiceHandler, Map) ->
+ orddict:find(ServiceHandler, Map).
+
+-spec fetch(ServiceHandler, Map) -> Module when
+ ServiceHandler :: service_handler(),
+ Module :: module_(),
+ Map :: service_handler_map().
+fetch(ServiceHandler, Map) ->
+ orddict:fetch(ServiceHandler, Map).
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_multiplexed_protocol.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_multiplexed_protocol.erl
new file mode 100644
index 000000000..5f7b70c80
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_multiplexed_protocol.erl
@@ -0,0 +1,83 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_multiplexed_protocol).
+
+-behaviour(thrift_protocol).
+
+-include("thrift_constants.hrl").
+-include("thrift_protocol.hrl").
+
+-include("thrift_protocol_behaviour.hrl").
+
+-export([new/2,
+ read/2,
+ write/2,
+ flush_transport/1,
+ close_transport/1
+ ]).
+
+-record(protocol, {module, data}).
+-type protocol() :: #protocol{}.
+
+-record (multiplexed_protocol, {protocol_module_to_decorate::atom(),
+ protocol_data_to_decorate::term(),
+ service_name::nonempty_string()}).
+
+-type state() :: #multiplexed_protocol{}.
+
+-spec new(ProtocolToDecorate::protocol(), ServiceName::nonempty_string()) -> {ok, Protocol::protocol()}.
+new(ProtocolToDecorate, ServiceName) when is_record(ProtocolToDecorate, protocol),
+ is_list(ServiceName) ->
+ State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolToDecorate#protocol.module,
+ protocol_data_to_decorate = ProtocolToDecorate#protocol.data,
+ service_name = ServiceName},
+ thrift_protocol:new(?MODULE, State).
+
+flush_transport(State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolModuleToDecorate,
+ protocol_data_to_decorate = State0}) ->
+ {State1, ok} = ProtocolModuleToDecorate:flush_transport(State0),
+ {State#multiplexed_protocol{protocol_data_to_decorate = State1}, ok}.
+
+close_transport(State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolModuleToDecorate,
+ protocol_data_to_decorate = State0}) ->
+ {State1, ok} = ProtocolModuleToDecorate:close_transport(State0),
+ {State#multiplexed_protocol{protocol_data_to_decorate = State1}, ok}.
+
+write(State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolModuleToDecorate,
+ protocol_data_to_decorate = State0,
+ service_name = ServiceName},
+ Message = #protocol_message_begin{name = Name}) ->
+ {State1, ok} = ProtocolModuleToDecorate:write(State0,
+ Message#protocol_message_begin{name=ServiceName ++
+ ?MULTIPLEXED_SERVICE_SEPARATOR ++
+ Name}),
+ {State#multiplexed_protocol{protocol_data_to_decorate = State1}, ok};
+
+write(State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolModuleToDecorate,
+ protocol_data_to_decorate = State0},
+ Message) ->
+ {State1, ok} = ProtocolModuleToDecorate:write(State0, Message),
+ {State#multiplexed_protocol{protocol_data_to_decorate = State1}, ok}.
+
+read(State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolModuleToDecorate,
+ protocol_data_to_decorate = State0},
+ Message) ->
+ {State1, Result} = ProtocolModuleToDecorate:read(State0, Message),
+ {State#multiplexed_protocol{protocol_data_to_decorate = State1}, Result}.
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_processor.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_processor.erl
new file mode 100644
index 000000000..5c9f26f52
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_processor.erl
@@ -0,0 +1,219 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_processor).
+
+-export([init/1]).
+
+-include("thrift_constants.hrl").
+-include("thrift_protocol.hrl").
+
+-record(thrift_processor, {handler, protocol, service}).
+
+init({_Server, ProtoGen, Service, Handler}) when is_function(ProtoGen, 0) ->
+ {ok, Proto} = ProtoGen(),
+ loop(#thrift_processor{protocol = Proto,
+ service = Service,
+ handler = Handler}).
+
+loop(State0 = #thrift_processor{protocol = Proto0,
+ handler = Handler,
+ service = Service}) ->
+
+ {Proto1, MessageBegin} = thrift_protocol:read(Proto0, message_begin),
+ State1 = State0#thrift_processor{protocol = Proto1},
+
+ ErrorHandler = fun
+ (HandlerModules) when is_list(HandlerModules) -> thrift_multiplexed_map_wrapper:fetch(?MULTIPLEXED_ERROR_HANDLER_KEY, HandlerModules);
+ (HandlerModule) -> HandlerModule
+ end,
+
+ case MessageBegin of
+
+ #protocol_message_begin{name = Function,
+ type = Type,
+ seqid = Seqid} when Type =:= ?tMessageType_CALL; Type =:= ?tMessageType_ONEWAY ->
+ case string:tokens(Function, ?MULTIPLEXED_SERVICE_SEPARATOR) of
+ [ServiceName, FunctionName] ->
+ ServiceModule = thrift_multiplexed_map_wrapper:fetch(ServiceName, Service),
+ ServiceHandler = thrift_multiplexed_map_wrapper:fetch(ServiceName, Handler),
+ case handle_function(State1#thrift_processor{service=ServiceModule, handler=ServiceHandler}, list_to_atom(FunctionName), Seqid) of
+ {State2, ok} -> loop(State2#thrift_processor{service=Service, handler=Handler});
+ {_State2, {error, Reason}} ->
+ apply(ErrorHandler(Handler), handle_error, [list_to_atom(Function), Reason]),
+ thrift_protocol:close_transport(Proto1),
+ ok
+ end;
+ _ ->
+ case handle_function(State1, list_to_atom(Function), Seqid) of
+ {State2, ok} -> loop(State2);
+ {_State2, {error, Reason}} ->
+ apply(ErrorHandler(Handler), handle_error, [list_to_atom(Function), Reason]),
+ thrift_protocol:close_transport(Proto1),
+ ok
+ end
+ end;
+ {error, timeout = Reason} ->
+ apply(ErrorHandler(Handler), handle_error, [undefined, Reason]),
+ thrift_protocol:close_transport(Proto1),
+ ok;
+ {error, closed = Reason} ->
+ %% error_logger:info_msg("Client disconnected~n"),
+ apply(ErrorHandler(Handler), handle_error, [undefined, Reason]),
+ thrift_protocol:close_transport(Proto1),
+ exit(shutdown);
+ {error, Reason} ->
+ apply(ErrorHandler(Handler), handle_error, [undefined, Reason]),
+ thrift_protocol:close_transport(Proto1),
+ exit(shutdown)
+ end.
+
+handle_function(State0=#thrift_processor{protocol = Proto0,
+ handler = Handler,
+ service = Service},
+ Function,
+ Seqid) ->
+ InParams = Service:function_info(Function, params_type),
+
+ {Proto1, {ok, Params}} = thrift_protocol:read(Proto0, InParams),
+ State1 = State0#thrift_processor{protocol = Proto1},
+
+ try
+ Result = Handler:handle_function(Function, Params),
+ %% {Micro, Result} = better_timer(Handler, handle_function, [Function, Params]),
+ %% error_logger:info_msg("Processed ~p(~p) in ~.4fms~n",
+ %% [Function, Params, Micro/1000.0]),
+ handle_success(State1, Function, Result, Seqid)
+ catch
+ Type:Data when Type =:= throw orelse Type =:= error ->
+ handle_function_catch(State1, Function, Type, Data, Seqid)
+ end.
+
+handle_function_catch(State = #thrift_processor{service = Service},
+ Function, ErrType, ErrData, Seqid) ->
+ IsOneway = Service:function_info(Function, reply_type) =:= oneway_void,
+
+ case {ErrType, ErrData} of
+ _ when IsOneway ->
+ Stack = erlang:get_stacktrace(),
+ error_logger:warning_msg(
+ "oneway void ~p threw error which must be ignored: ~p",
+ [Function, {ErrType, ErrData, Stack}]),
+ {State, ok};
+
+ {throw, Exception} when is_tuple(Exception), size(Exception) > 0 ->
+ %error_logger:warning_msg("~p threw exception: ~p~n", [Function, Exception]),
+ handle_exception(State, Function, Exception, Seqid);
+ % we still want to accept more requests from this client
+
+ {error, Error} ->
+ handle_error(State, Function, Error, Seqid)
+ end.
+
+handle_success(State = #thrift_processor{service = Service},
+ Function,
+ Result,
+ Seqid) ->
+ ReplyType = Service:function_info(Function, reply_type),
+ StructName = atom_to_list(Function) ++ "_result",
+
+ case Result of
+ {reply, ReplyData} ->
+ Reply = {{struct, [{0, ReplyType}]}, {StructName, ReplyData}},
+ send_reply(State, Function, ?tMessageType_REPLY, Reply, Seqid);
+
+ ok when ReplyType == {struct, []} ->
+ send_reply(State, Function, ?tMessageType_REPLY, {ReplyType, {StructName}}, Seqid);
+
+ ok when ReplyType == oneway_void ->
+ %% no reply for oneway void
+ {State, ok}
+ end.
+
+handle_exception(State = #thrift_processor{service = Service},
+ Function,
+ Exception,
+ Seqid) ->
+ ExceptionType = element(1, Exception),
+ %% Fetch a structure like {struct, [{-2, {struct, {Module, Type}}},
+ %% {-3, {struct, {Module, Type}}}]}
+
+ ReplySpec = Service:function_info(Function, exceptions),
+ {struct, XInfo} = ReplySpec,
+
+ true = is_list(XInfo),
+
+ %% Assuming we had a type1 exception, we'd get: [undefined, Exception, undefined]
+ %% e.g.: [{-1, type0}, {-2, type1}, {-3, type2}]
+ ExceptionList = [case Type of
+ ExceptionType -> Exception;
+ _ -> undefined
+ end
+ || {_Fid, {struct, {_Module, Type}}} <- XInfo],
+
+ ExceptionTuple = list_to_tuple([Function | ExceptionList]),
+
+ % Make sure we got at least one defined
+ case lists:all(fun(X) -> X =:= undefined end, ExceptionList) of
+ true ->
+ handle_unknown_exception(State, Function, Exception, Seqid);
+ false ->
+ send_reply(State, Function, ?tMessageType_REPLY, {ReplySpec, ExceptionTuple}, Seqid)
+ end.
+
+%%
+%% Called when an exception has been explicitly thrown by the service, but it was
+%% not one of the exceptions that was defined for the function.
+%%
+handle_unknown_exception(State, Function, Exception, Seqid) ->
+ handle_error(State, Function, {exception_not_declared_as_thrown,
+ Exception}, Seqid).
+
+handle_error(State, Function, Error, Seqid) ->
+ Stack = erlang:get_stacktrace(),
+ error_logger:error_msg("~p had an error: ~p~n", [Function, {Error, Stack}]),
+
+ Message =
+ case application:get_env(thrift, exceptions_include_traces) of
+ {ok, true} ->
+ lists:flatten(io_lib:format("An error occurred: ~p~n",
+ [{Error, Stack}]));
+ _ ->
+ "An unknown handler error occurred."
+ end,
+ Reply = {?TApplicationException_Structure,
+ #'TApplicationException'{
+ message = Message,
+ type = ?TApplicationException_UNKNOWN}},
+ send_reply(State, Function, ?tMessageType_EXCEPTION, Reply, Seqid).
+
+send_reply(State = #thrift_processor{protocol = Proto0}, Function, ReplyMessageType, Reply, Seqid) ->
+ try
+ {Proto1, ok} = thrift_protocol:write(Proto0, #protocol_message_begin{
+ name = atom_to_list(Function),
+ type = ReplyMessageType,
+ seqid = Seqid}),
+ {Proto2, ok} = thrift_protocol:write(Proto1, Reply),
+ {Proto3, ok} = thrift_protocol:write(Proto2, message_end),
+ {Proto4, ok} = thrift_protocol:flush_transport(Proto3),
+ {State#thrift_processor{protocol = Proto4}, ok}
+ catch
+ error:{badmatch, {_, {error, _} = Error}} ->
+ {State, Error}
+ end.
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_protocol.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_protocol.erl
new file mode 100644
index 000000000..2fe10d6a2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_protocol.erl
@@ -0,0 +1,412 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_protocol).
+
+-export([new/2,
+ write/2,
+ read/2,
+ read/3,
+ skip/2,
+ flush_transport/1,
+ close_transport/1,
+ typeid_to_atom/1
+ ]).
+
+-export([behaviour_info/1]).
+
+-include("thrift_constants.hrl").
+-include("thrift_protocol.hrl").
+
+-record(protocol, {module, data}).
+
+behaviour_info(callbacks) ->
+ [
+ {read, 2},
+ {write, 2},
+ {flush_transport, 1},
+ {close_transport, 1}
+ ];
+behaviour_info(_Else) -> undefined.
+
+new(Module, Data) when is_atom(Module) ->
+ {ok, #protocol{module = Module,
+ data = Data}}.
+
+-spec flush_transport(#protocol{}) -> {#protocol{}, ok}.
+flush_transport(Proto = #protocol{module = Module,
+ data = Data}) ->
+ {NewData, Result} = Module:flush_transport(Data),
+ {Proto#protocol{data = NewData}, Result}.
+
+-spec close_transport(#protocol{}) -> ok.
+close_transport(#protocol{module = Module,
+ data = Data}) ->
+ Module:close_transport(Data).
+
+typeid_to_atom(?tType_STOP) -> field_stop;
+typeid_to_atom(?tType_VOID) -> void;
+typeid_to_atom(?tType_BOOL) -> bool;
+typeid_to_atom(?tType_DOUBLE) -> double;
+typeid_to_atom(?tType_I8) -> byte;
+typeid_to_atom(?tType_I16) -> i16;
+typeid_to_atom(?tType_I32) -> i32;
+typeid_to_atom(?tType_I64) -> i64;
+typeid_to_atom(?tType_STRING) -> string;
+typeid_to_atom(?tType_STRUCT) -> struct;
+typeid_to_atom(?tType_MAP) -> map;
+typeid_to_atom(?tType_SET) -> set;
+typeid_to_atom(?tType_LIST) -> list.
+
+term_to_typeid(void) -> ?tType_VOID;
+term_to_typeid(bool) -> ?tType_BOOL;
+term_to_typeid(byte) -> ?tType_I8;
+term_to_typeid(double) -> ?tType_DOUBLE;
+term_to_typeid(i8) -> ?tType_I8;
+term_to_typeid(i16) -> ?tType_I16;
+term_to_typeid(i32) -> ?tType_I32;
+term_to_typeid(i64) -> ?tType_I64;
+term_to_typeid(string) -> ?tType_STRING;
+term_to_typeid({struct, _}) -> ?tType_STRUCT;
+term_to_typeid({map, _, _}) -> ?tType_MAP;
+term_to_typeid({set, _}) -> ?tType_SET;
+term_to_typeid({list, _}) -> ?tType_LIST.
+
+%% Structure is like:
+%% [{Fid, Type}, ...]
+-spec read(#protocol{}, {struct, _StructDef}, atom()) -> {#protocol{}, {ok, tuple()}}.
+read(IProto0, {struct, Structure}, Tag)
+ when is_list(Structure), is_atom(Tag) ->
+
+ % If we want a tagged tuple, we need to offset all the tuple indices
+ % by 1 to avoid overwriting the tag.
+ Offset = if Tag =/= undefined -> 1; true -> 0 end,
+ IndexList = case length(Structure) of
+ N when N > 0 -> lists:seq(1 + Offset, N + Offset);
+ _ -> []
+ end,
+
+ SWithIndices = [{Fid, {Type, Index}} ||
+ {{Fid, Type}, Index} <-
+ lists:zip(Structure, IndexList)],
+ % Fid -> {Type, Index}
+ SDict = dict:from_list(SWithIndices),
+
+ {IProto1, ok} = read(IProto0, struct_begin),
+ RTuple0 = erlang:make_tuple(length(Structure) + Offset, undefined),
+ RTuple1 = if Tag =/= undefined -> setelement(1, RTuple0, Tag);
+ true -> RTuple0
+ end,
+
+ {IProto2, RTuple2} = read_struct_loop(IProto1, SDict, RTuple1),
+ {IProto2, {ok, RTuple2}}.
+
+
+%% NOTE: Keep this in sync with thrift_protocol_behaviour:read
+-spec read
+ (#protocol{}, {struct, _Info}) -> {#protocol{}, {ok, tuple()} | {error, _Reason}};
+ (#protocol{}, tprot_cont_tag()) -> {#protocol{}, {ok, any()} | {error, _Reason}};
+ (#protocol{}, tprot_empty_tag()) -> {#protocol{}, ok | {error, _Reason}};
+ (#protocol{}, tprot_header_tag()) -> {#protocol{}, tprot_header_val() | {error, _Reason}};
+ (#protocol{}, tprot_data_tag()) -> {#protocol{}, {ok, any()} | {error, _Reason}}.
+
+read(IProto, {struct, {Module, StructureName}}) when is_atom(Module),
+ is_atom(StructureName) ->
+ read(IProto, Module:struct_info(StructureName), StructureName);
+
+read(IProto, S={struct, Structure}) when is_list(Structure) ->
+ read(IProto, S, undefined);
+
+read(IProto0, {list, Type}) ->
+ {IProto1, #protocol_list_begin{etype = EType, size = Size}} =
+ read(IProto0, list_begin),
+ {EType, EType} = {term_to_typeid(Type), EType},
+ {List, IProto2} = lists:mapfoldl(fun(_, ProtoS0) ->
+ {ProtoS1, {ok, Item}} = read(ProtoS0, Type),
+ {Item, ProtoS1}
+ end,
+ IProto1,
+ lists:duplicate(Size, 0)),
+ {IProto3, ok} = read(IProto2, list_end),
+ {IProto3, {ok, List}};
+
+read(IProto0, {map, KeyType, ValType}) ->
+ {IProto1, #protocol_map_begin{size = Size, ktype = KType, vtype = VType}} =
+ read(IProto0, map_begin),
+ _ = case Size of
+ 0 -> 0;
+ _ ->
+ {KType, KType} = {term_to_typeid(KeyType), KType},
+ {VType, VType} = {term_to_typeid(ValType), VType}
+ end,
+ {List, IProto2} = lists:mapfoldl(fun(_, ProtoS0) ->
+ {ProtoS1, {ok, Key}} = read(ProtoS0, KeyType),
+ {ProtoS2, {ok, Val}} = read(ProtoS1, ValType),
+ {{Key, Val}, ProtoS2}
+ end,
+ IProto1,
+ lists:duplicate(Size, 0)),
+ {IProto3, ok} = read(IProto2, map_end),
+ {IProto3, {ok, dict:from_list(List)}};
+
+read(IProto0, {set, Type}) ->
+ {IProto1, #protocol_set_begin{etype = EType, size = Size}} =
+ read(IProto0, set_begin),
+ {EType, EType} = {term_to_typeid(Type), EType},
+ {List, IProto2} = lists:mapfoldl(fun(_, ProtoS0) ->
+ {ProtoS1, {ok, Item}} = read(ProtoS0, Type),
+ {Item, ProtoS1}
+ end,
+ IProto1,
+ lists:duplicate(Size, 0)),
+ {IProto3, ok} = read(IProto2, set_end),
+ {IProto3, {ok, sets:from_list(List)}};
+
+read(Protocol, ProtocolType) ->
+ read_specific(Protocol, ProtocolType).
+
+%% NOTE: Keep this in sync with thrift_protocol_behaviour:read
+-spec read_specific
+ (#protocol{}, tprot_empty_tag()) -> {#protocol{}, ok | {error, _Reason}};
+ (#protocol{}, tprot_header_tag()) -> {#protocol{}, tprot_header_val() | {error, _Reason}};
+ (#protocol{}, tprot_data_tag()) -> {#protocol{}, {ok, any()} | {error, _Reason}}.
+read_specific(Proto = #protocol{module = Module,
+ data = ModuleData}, ProtocolType) ->
+ {NewData, Result} = Module:read(ModuleData, ProtocolType),
+ {Proto#protocol{data = NewData}, Result}.
+
+read_struct_loop(IProto0, SDict, RTuple) ->
+ {IProto1, #protocol_field_begin{type = FType, id = Fid}} =
+ thrift_protocol:read(IProto0, field_begin),
+ case {FType, Fid} of
+ {?tType_STOP, _} ->
+ {IProto2, ok} = read(IProto1, struct_end),
+ {IProto2, RTuple};
+ _Else ->
+ case dict:find(Fid, SDict) of
+ {ok, {Type, Index}} ->
+ case term_to_typeid(Type) of
+ FType ->
+ {IProto2, {ok, Val}} = read(IProto1, Type),
+ {IProto3, ok} = thrift_protocol:read(IProto2, field_end),
+ NewRTuple = setelement(Index, RTuple, Val),
+ read_struct_loop(IProto3, SDict, NewRTuple);
+ Expected ->
+ error_logger:info_msg(
+ "Skipping field ~p with wrong type (~p != ~p)~n",
+ [Fid, FType, Expected]),
+ skip_field(FType, IProto1, SDict, RTuple)
+ end;
+ _Else2 ->
+ skip_field(FType, IProto1, SDict, RTuple)
+ end
+ end.
+
+skip_field(FType, IProto0, SDict, RTuple) ->
+ {IProto1, ok} = skip(IProto0, typeid_to_atom(FType)),
+ {IProto2, ok} = read(IProto1, field_end),
+ read_struct_loop(IProto2, SDict, RTuple).
+
+-spec skip(#protocol{}, atom()) -> {#protocol{}, ok}.
+
+skip(Proto0, struct) ->
+ {Proto1, ok} = read(Proto0, struct_begin),
+ {Proto2, ok} = skip_struct_loop(Proto1),
+ {Proto3, ok} = read(Proto2, struct_end),
+ {Proto3, ok};
+
+skip(Proto0, map) ->
+ {Proto1, Map} = read(Proto0, map_begin),
+ {Proto2, ok} = skip_map_loop(Proto1, Map),
+ {Proto3, ok} = read(Proto2, map_end),
+ {Proto3, ok};
+
+skip(Proto0, set) ->
+ {Proto1, Set} = read(Proto0, set_begin),
+ {Proto2, ok} = skip_set_loop(Proto1, Set),
+ {Proto3, ok} = read(Proto2, set_end),
+ {Proto3, ok};
+
+skip(Proto0, list) ->
+ {Proto1, List} = read(Proto0, list_begin),
+ {Proto2, ok} = skip_list_loop(Proto1, List),
+ {Proto3, ok} = read(Proto2, list_end),
+ {Proto3, ok};
+
+skip(Proto0, Type) when is_atom(Type) ->
+ {Proto1, _Ignore} = read(Proto0, Type),
+ {Proto1, ok}.
+
+
+skip_struct_loop(Proto0) ->
+ {Proto1, #protocol_field_begin{type = Type}} = read(Proto0, field_begin),
+ case Type of
+ ?tType_STOP ->
+ {Proto1, ok};
+ _Else ->
+ {Proto2, ok} = skip(Proto1, typeid_to_atom(Type)),
+ {Proto3, ok} = read(Proto2, field_end),
+ skip_struct_loop(Proto3)
+ end.
+
+skip_map_loop(Proto0, Map = #protocol_map_begin{ktype = Ktype,
+ vtype = Vtype,
+ size = Size}) ->
+ case Size of
+ N when N > 0 ->
+ {Proto1, ok} = skip(Proto0, typeid_to_atom(Ktype)),
+ {Proto2, ok} = skip(Proto1, typeid_to_atom(Vtype)),
+ skip_map_loop(Proto2,
+ Map#protocol_map_begin{size = Size - 1});
+ 0 -> {Proto0, ok}
+ end.
+
+skip_set_loop(Proto0, Map = #protocol_set_begin{etype = Etype,
+ size = Size}) ->
+ case Size of
+ N when N > 0 ->
+ {Proto1, ok} = skip(Proto0, typeid_to_atom(Etype)),
+ skip_set_loop(Proto1,
+ Map#protocol_set_begin{size = Size - 1});
+ 0 -> {Proto0, ok}
+ end.
+
+skip_list_loop(Proto0, Map = #protocol_list_begin{etype = Etype,
+ size = Size}) ->
+ case Size of
+ N when N > 0 ->
+ {Proto1, ok} = skip(Proto0, typeid_to_atom(Etype)),
+ skip_list_loop(Proto1,
+ Map#protocol_list_begin{size = Size - 1});
+ 0 -> {Proto0, ok}
+ end.
+
+
+%%--------------------------------------------------------------------
+%% Function: write(OProto, {Type, Data}) -> ok
+%%
+%% Type = {struct, StructDef} |
+%% {list, Type} |
+%% {map, KeyType, ValType} |
+%% {set, Type} |
+%% BaseType
+%%
+%% Data =
+%% tuple() -- for struct
+%% | list() -- for list
+%% | dictionary() -- for map
+%% | set() -- for set
+%% | any() -- for base types
+%%
+%% Description:
+%%--------------------------------------------------------------------
+-spec write(#protocol{}, any()) -> {#protocol{}, ok | {error, _Reason}}.
+
+write(Proto0, {{struct, StructDef}, Data})
+ when is_list(StructDef), is_tuple(Data), length(StructDef) == size(Data) - 1 ->
+
+ [StructName | Elems] = tuple_to_list(Data),
+ {Proto1, ok} = write(Proto0, #protocol_struct_begin{name = StructName}),
+ {Proto2, ok} = struct_write_loop(Proto1, StructDef, Elems),
+ {Proto3, ok} = write(Proto2, struct_end),
+ {Proto3, ok};
+
+write(Proto, {{struct, {Module, StructureName}}, Data})
+ when is_atom(Module),
+ is_atom(StructureName),
+ element(1, Data) =:= StructureName ->
+ write(Proto, {Module:struct_info(StructureName), Data});
+
+write(_, {{struct, {Module, StructureName}}, Data})
+ when is_atom(Module),
+ is_atom(StructureName) ->
+ erlang:error(struct_unmatched, {{provided, element(1, Data)},
+ {expected, StructureName}});
+
+write(Proto0, {{list, Type}, Data})
+ when is_list(Data) ->
+ {Proto1, ok} = write(Proto0,
+ #protocol_list_begin{
+ etype = term_to_typeid(Type),
+ size = length(Data)
+ }),
+ Proto2 = lists:foldl(fun(Elem, ProtoIn) ->
+ {ProtoOut, ok} = write(ProtoIn, {Type, Elem}),
+ ProtoOut
+ end,
+ Proto1,
+ Data),
+ {Proto3, ok} = write(Proto2, list_end),
+ {Proto3, ok};
+
+write(Proto0, {{map, KeyType, ValType}, Data}) ->
+ {Proto1, ok} = write(Proto0,
+ #protocol_map_begin{
+ ktype = term_to_typeid(KeyType),
+ vtype = term_to_typeid(ValType),
+ size = dict:size(Data)
+ }),
+ Proto2 = dict:fold(fun(KeyData, ValData, ProtoS0) ->
+ {ProtoS1, ok} = write(ProtoS0, {KeyType, KeyData}),
+ {ProtoS2, ok} = write(ProtoS1, {ValType, ValData}),
+ ProtoS2
+ end,
+ Proto1,
+ Data),
+ {Proto3, ok} = write(Proto2, map_end),
+ {Proto3, ok};
+
+write(Proto0, {{set, Type}, Data}) ->
+ true = sets:is_set(Data),
+ {Proto1, ok} = write(Proto0,
+ #protocol_set_begin{
+ etype = term_to_typeid(Type),
+ size = sets:size(Data)
+ }),
+ Proto2 = sets:fold(fun(Elem, ProtoIn) ->
+ {ProtoOut, ok} = write(ProtoIn, {Type, Elem}),
+ ProtoOut
+ end,
+ Proto1,
+ Data),
+ {Proto3, ok} = write(Proto2, set_end),
+ {Proto3, ok};
+
+write(Proto = #protocol{module = Module,
+ data = ModuleData}, Data) ->
+ {NewData, Result} = Module:write(ModuleData, Data),
+ {Proto#protocol{data = NewData}, Result}.
+
+struct_write_loop(Proto0, [{Fid, Type} | RestStructDef], [Data | RestData]) ->
+ NewProto = case Data of
+ undefined ->
+ Proto0; % null fields are skipped in response
+ _ ->
+ {Proto1, ok} = write(Proto0,
+ #protocol_field_begin{
+ type = term_to_typeid(Type),
+ id = Fid
+ }),
+ {Proto2, ok} = write(Proto1, {Type, Data}),
+ {Proto3, ok} = write(Proto2, field_end),
+ Proto3
+ end,
+ struct_write_loop(NewProto, RestStructDef, RestData);
+struct_write_loop(Proto, [], []) ->
+ write(Proto, field_stop).
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_reconnecting_client.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_reconnecting_client.erl
new file mode 100644
index 000000000..538fd3ad1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_reconnecting_client.erl
@@ -0,0 +1,258 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_reconnecting_client).
+
+-behaviour(gen_server).
+
+%% API
+-export([ call/3,
+ get_stats/1,
+ get_and_reset_stats/1 ]).
+
+-export([ start_link/6 ]).
+
+%% gen_server callbacks
+-export([ init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3 ]).
+
+-record( state, { client = nil,
+ host,
+ port,
+ thrift_svc,
+ thrift_opts,
+ reconn_min,
+ reconn_max,
+ reconn_time = 0,
+ op_cnt_dict,
+ op_time_dict } ).
+
+%%====================================================================
+%% API
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
+%% Description: Starts the server
+%%--------------------------------------------------------------------
+start_link( Host, Port,
+ ThriftSvc, ThriftOpts,
+ ReconnMin, ReconnMax ) ->
+ gen_server:start_link( ?MODULE,
+ [ Host, Port,
+ ThriftSvc, ThriftOpts,
+ ReconnMin, ReconnMax ],
+ [] ).
+
+call( Pid, Op, Args ) ->
+ gen_server:call( Pid, { call, Op, Args } ).
+
+get_stats( Pid ) ->
+ gen_server:call( Pid, get_stats ).
+
+get_and_reset_stats( Pid ) ->
+ gen_server:call( Pid, get_and_reset_stats ).
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% Description: Start the server.
+%%--------------------------------------------------------------------
+init( [ Host, Port, TSvc, TOpts, ReconnMin, ReconnMax ] ) ->
+ process_flag( trap_exit, true ),
+
+ State = #state{ host = Host,
+ port = Port,
+ thrift_svc = TSvc,
+ thrift_opts = TOpts,
+ reconn_min = ReconnMin,
+ reconn_max = ReconnMax,
+ op_cnt_dict = dict:new(),
+ op_time_dict = dict:new() },
+
+ { ok, try_connect( State ) }.
+
+%%--------------------------------------------------------------------
+%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} |
+%% {stop, Reason, State}
+%% Description: Handling call messages
+%%--------------------------------------------------------------------
+handle_call( { call, Op, _ },
+ _From,
+ State = #state{ client = nil } ) ->
+ { reply, { error, noconn }, incr_stats( Op, "failfast", 1, State ) };
+
+handle_call( { call, Op, Args },
+ _From,
+ State=#state{ client = Client } ) ->
+
+ Timer = timer_fun(),
+ Result = ( catch thrift_client:call( Client, Op, Args) ),
+ Time = Timer(),
+
+ case Result of
+ { C, { ok, Reply } } ->
+ S = incr_stats( Op, "success", Time, State#state{ client = C } ),
+ { reply, {ok, Reply }, S };
+ { _, { E, Msg } } when E == error; E == exception ->
+ S = incr_stats( Op, "error", Time, try_connect( State ) ),
+ { reply, { E, Msg }, S };
+ Other ->
+ S = incr_stats( Op, "error", Time, try_connect( State ) ),
+ { reply, Other, S }
+ end;
+
+handle_call( get_stats,
+ _From,
+ State = #state{} ) ->
+ { reply, stats( State ), State };
+
+handle_call( get_and_reset_stats,
+ _From,
+ State = #state{} ) ->
+ { reply, stats( State ), reset_stats( State ) }.
+
+%%--------------------------------------------------------------------
+%% Function: handle_cast(Msg, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling cast messages
+%%--------------------------------------------------------------------
+handle_cast( _Msg, State ) ->
+ { noreply, State }.
+
+%%--------------------------------------------------------------------
+%% Function: handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling all non call/cast messages
+%%--------------------------------------------------------------------
+handle_info( try_connect, State ) ->
+ { noreply, try_connect( State ) };
+
+handle_info( _Info, State ) ->
+ { noreply, State }.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, State) -> void()
+%% Description: This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any necessary
+%% cleaning up. When it returns, the gen_server terminates with Reason.
+%% The return value is ignored.
+%%--------------------------------------------------------------------
+terminate( _Reason, #state{ client = Client } ) ->
+ thrift_client:close( Client ),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change( _OldVsn, State, _Extra ) ->
+ { ok, State }.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+try_connect( State = #state{ client = OldClient,
+ host = Host,
+ port = Port,
+ thrift_svc = TSvc,
+ thrift_opts = TOpts } ) ->
+
+ case OldClient of
+ nil -> ok;
+ _ -> ( catch thrift_client:close( OldClient ) )
+ end,
+
+ case catch thrift_client_util:new( Host, Port, TSvc, TOpts ) of
+ { ok, Client } ->
+ State#state{ client = Client, reconn_time = 0 };
+ { E, Msg } when E == error; E == exception ->
+ ReconnTime = reconn_time( State ),
+ error_logger:error_msg( "[~w] ~w connect failed (~w), trying again in ~w ms~n",
+ [ self(), TSvc, Msg, ReconnTime ] ),
+ erlang:send_after( ReconnTime, self(), try_connect ),
+ State#state{ client = nil, reconn_time = ReconnTime }
+ end.
+
+
+reconn_time( #state{ reconn_min = ReconnMin, reconn_time = 0 } ) ->
+ ReconnMin;
+reconn_time( #state{ reconn_max = ReconnMax, reconn_time = ReconnMax } ) ->
+ ReconnMax;
+reconn_time( #state{ reconn_max = ReconnMax, reconn_time = R } ) ->
+ Backoff = 2 * R,
+ case Backoff > ReconnMax of
+ true -> ReconnMax;
+ false -> Backoff
+ end.
+
+-ifdef(time_correction).
+timer_fun() ->
+ T1 = erlang:monotonic_time(),
+ fun() ->
+ T2 = erlang:monotonic_time(),
+ erlang:convert_time_unit(T2 - T1, native, micro_seconds)
+ end.
+-else.
+timer_fun() ->
+ T1 = erlang:timestamp(),
+ fun() ->
+ T2 = erlang:timestamp(),
+ timer:now_diff(T2, T1)
+ end.
+-endif.
+
+incr_stats( Op, Result, Time,
+ State = #state{ op_cnt_dict = OpCntDict,
+ op_time_dict = OpTimeDict } ) ->
+ Key = lists:flatten( [ atom_to_list( Op ), [ "_" | Result ] ] ),
+ State#state{ op_cnt_dict = dict:update_counter( Key, 1, OpCntDict ),
+ op_time_dict = dict:update_counter( Key, Time, OpTimeDict ) }.
+
+
+stats( #state{ thrift_svc = TSvc,
+ op_cnt_dict = OpCntDict,
+ op_time_dict = OpTimeDict } ) ->
+ Svc = atom_to_list(TSvc),
+
+ F = fun( Key, Count, Stats ) ->
+ Name = lists:flatten( [ Svc, [ "_" | Key ] ] ),
+ Micros = dict:fetch( Key, OpTimeDict ),
+ [ { Name, Count, Micros } | Stats ]
+ end,
+
+ dict:fold( F, [], OpCntDict ).
+
+reset_stats( State = #state{} ) ->
+ State#state{ op_cnt_dict = dict:new(), op_time_dict = dict:new() }.
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_server.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_server.erl
new file mode 100644
index 000000000..5012e168f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_server.erl
@@ -0,0 +1,183 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_server).
+
+-behaviour(gen_server).
+
+%% API
+-export([start_link/3, stop/1, take_socket/2]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(SERVER, ?MODULE).
+
+-record(state, {listen_socket, acceptor_ref, service, handler}).
+
+%%====================================================================
+%% API
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
+%% Description: Starts the server
+%%--------------------------------------------------------------------
+start_link(Port, Service, HandlerModule) when is_integer(Port), is_atom(HandlerModule) ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, {Port, Service, HandlerModule}, []).
+
+%%--------------------------------------------------------------------
+%% Function: stop(Pid) -> ok, {error, Reason}
+%% Description: Stops the server.
+%%--------------------------------------------------------------------
+stop(Pid) when is_pid(Pid) ->
+ gen_server:call(Pid, stop).
+
+
+take_socket(Server, Socket) ->
+ gen_server:call(Server, {take_socket, Socket}).
+
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% Description: Initiates the server
+%%--------------------------------------------------------------------
+init({Port, Service, Handler}) ->
+ {ok, Socket} = gen_tcp:listen(Port,
+ [binary,
+ {packet, 0},
+ {active, false},
+ {nodelay, true},
+ {reuseaddr, true}]),
+ {ok, Ref} = prim_inet:async_accept(Socket, -1),
+ {ok, #state{listen_socket = Socket,
+ acceptor_ref = Ref,
+ service = Service,
+ handler = Handler}}.
+
+%%--------------------------------------------------------------------
+%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} |
+%% {stop, Reason, State}
+%% Description: Handling call messages
+%%--------------------------------------------------------------------
+handle_call(stop, _From, State) ->
+ {stop, stopped, ok, State};
+
+handle_call({take_socket, Socket}, {FromPid, _Tag}, State) ->
+ Result = gen_tcp:controlling_process(Socket, FromPid),
+ {reply, Result, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_cast(Msg, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling cast messages
+%%--------------------------------------------------------------------
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling all non call/cast messages
+%%--------------------------------------------------------------------
+handle_info({inet_async, ListenSocket, Ref, {ok, ClientSocket}},
+ State = #state{listen_socket = ListenSocket,
+ acceptor_ref = Ref,
+ service = Service,
+ handler = Handler}) ->
+ case set_sockopt(ListenSocket, ClientSocket) of
+ ok ->
+ %% New client connected - start processor
+ start_processor(ClientSocket, Service, Handler),
+ {ok, NewRef} = prim_inet:async_accept(ListenSocket, -1),
+ {noreply, State#state{acceptor_ref = NewRef}};
+ {error, Reason} ->
+ error_logger:error_msg("Couldn't set socket opts: ~p~n",
+ [Reason]),
+ {stop, Reason, State}
+ end;
+
+handle_info({inet_async, _ListenSocket, _Ref, Error}, State) ->
+ error_logger:error_msg("Error in acceptor: ~p~n", [Error]),
+ {stop, Error, State};
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, State) -> void()
+%% Description: This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any necessary
+%% cleaning up. When it returns, the gen_server terminates with Reason.
+%% The return value is ignored.
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+set_sockopt(ListenSocket, ClientSocket) ->
+ true = inet_db:register_socket(ClientSocket, inet_tcp),
+ case prim_inet:getopts(ListenSocket,
+ [active, nodelay, keepalive, delay_send, priority, tos]) of
+ {ok, Opts} ->
+ case prim_inet:setopts(ClientSocket, Opts) of
+ ok -> ok;
+ Error -> gen_tcp:close(ClientSocket),
+ Error
+ end;
+ Error ->
+ gen_tcp:close(ClientSocket),
+ Error
+ end.
+
+start_processor(Socket, Service, Handler) ->
+ Server = self(),
+
+ ProtoGen = fun() ->
+ % Become the controlling process
+ ok = take_socket(Server, Socket),
+ {ok, SocketTransport} = thrift_socket_transport:new(Socket),
+ {ok, BufferedTransport} = thrift_buffered_transport:new(SocketTransport),
+ {ok, Protocol} = thrift_binary_protocol:new(BufferedTransport),
+ {ok, Protocol}
+ end,
+
+ spawn(thrift_processor, init, [{Server, ProtoGen, Service, Handler}]).
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_service.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_service.erl
new file mode 100644
index 000000000..2ed7b57b0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_service.erl
@@ -0,0 +1,25 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_service).
+
+-export([behaviour_info/1]).
+
+behaviour_info(callbacks) ->
+ [{function_info, 2}].
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_socket_server.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_socket_server.erl
new file mode 100644
index 000000000..432e65b56
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_socket_server.erl
@@ -0,0 +1,324 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_socket_server).
+
+-behaviour(gen_server).
+
+-include ("thrift_constants.hrl").
+
+-ifdef(TEST).
+ -compile(export_all).
+ -export_records([thrift_socket_server]).
+-else.
+ -export([start/1, stop/1]).
+
+ -export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3,
+ handle_info/2]).
+
+ -export([acceptor_loop/1]).
+-endif.
+
+-record(thrift_socket_server,
+ {port,
+ service,
+ handler,
+ name,
+ max=2048,
+ ip=any,
+ listen=null,
+ acceptor=null,
+ socket_opts=[{recv_timeout, 500}],
+ protocol=binary,
+ framed=false,
+ ssltransport=false,
+ ssloptions=[]
+ }).
+
+start(State=#thrift_socket_server{}) ->
+ start_server(State);
+start(Options) ->
+ start(parse_options(Options)).
+
+stop(Name) when is_atom(Name) ->
+ gen_server:cast(Name, stop);
+stop(Pid) when is_pid(Pid) ->
+ gen_server:cast(Pid, stop);
+stop({local, Name}) ->
+ stop(Name);
+stop({global, Name}) ->
+ stop(Name);
+stop(Options) ->
+ State = parse_options(Options),
+ stop(State#thrift_socket_server.name).
+
+%% Internal API
+
+parse_options(Options) ->
+ parse_options(Options, #thrift_socket_server{}).
+
+parse_options([], State) ->
+ State;
+parse_options([{name, L} | Rest], State) when is_list(L) ->
+ Name = {local, list_to_atom(L)},
+ parse_options(Rest, State#thrift_socket_server{name=Name});
+parse_options([{name, A} | Rest], State) when is_atom(A) ->
+ Name = {local, A},
+ parse_options(Rest, State#thrift_socket_server{name=Name});
+parse_options([{name, Name} | Rest], State) ->
+ parse_options(Rest, State#thrift_socket_server{name=Name});
+parse_options([{port, L} | Rest], State) when is_list(L) ->
+ Port = list_to_integer(L),
+ parse_options(Rest, State#thrift_socket_server{port=Port});
+parse_options([{port, Port} | Rest], State) ->
+ parse_options(Rest, State#thrift_socket_server{port=Port});
+parse_options([{ip, Ip} | Rest], State) ->
+ ParsedIp = case Ip of
+ any ->
+ any;
+ Ip when is_tuple(Ip) ->
+ Ip;
+ Ip when is_list(Ip) ->
+ {ok, IpTuple} = inet_parse:address(Ip),
+ IpTuple
+ end,
+ parse_options(Rest, State#thrift_socket_server{ip=ParsedIp});
+parse_options([{socket_opts, L} | Rest], State) when is_list(L), length(L) > 0 ->
+ parse_options(Rest, State#thrift_socket_server{socket_opts=L});
+
+parse_options([{handler, []} | _Rest], _State) ->
+ throw("At least an error handler must be defined.");
+parse_options([{handler, ServiceHandlerPropertyList} | Rest], State) when is_list(ServiceHandlerPropertyList) ->
+ ServiceHandlerMap =
+ case State#thrift_socket_server.handler of
+ undefined ->
+ lists:foldl(
+ fun ({ServiceName, ServiceHandler}, Acc) when is_list(ServiceName), is_atom(ServiceHandler) ->
+ thrift_multiplexed_map_wrapper:store(ServiceName, ServiceHandler, Acc);
+ (_, _Acc) ->
+ throw("The handler option is not properly configured for multiplexed services. It should be a kind of [{\"error_handler\", Module::atom()}, {SericeName::list(), Module::atom()}, ...]")
+ end, thrift_multiplexed_map_wrapper:new(), ServiceHandlerPropertyList);
+ _ -> throw("Error while parsing the handler option.")
+ end,
+ case thrift_multiplexed_map_wrapper:find(?MULTIPLEXED_ERROR_HANDLER_KEY, ServiceHandlerMap) of
+ {ok, _ErrorHandler} -> parse_options(Rest, State#thrift_socket_server{handler=ServiceHandlerMap});
+ error -> throw("The handler option is not properly configured for multiplexed services. It should be a kind of [{\"error_handler\", Module::atom()}, {SericeName::list(), Module::atom()}, ...]")
+ end;
+parse_options([{handler, Handler} | Rest], State) when State#thrift_socket_server.handler == undefined, is_atom(Handler) ->
+ parse_options(Rest, State#thrift_socket_server{handler=Handler});
+
+parse_options([{service, []} | _Rest], _State) ->
+ throw("At least one service module must be defined.");
+parse_options([{service, ServiceModulePropertyList} | Rest], State) when is_list(ServiceModulePropertyList) ->
+ ServiceModuleMap =
+ case State#thrift_socket_server.service of
+ undefined ->
+ lists:foldl(
+ fun ({ServiceName, ServiceModule}, Acc) when is_list(ServiceName), is_atom(ServiceModule) ->
+ thrift_multiplexed_map_wrapper:store(ServiceName, ServiceModule, Acc);
+ (_, _Acc) ->
+ throw("The service option is not properly configured for multiplexed services. It should be a kind of [{SericeName::list(), ServiceModule::atom()}, ...]")
+ end, thrift_multiplexed_map_wrapper:new(), ServiceModulePropertyList);
+ _ -> throw("Error while parsing the service option.")
+ end,
+ parse_options(Rest, State#thrift_socket_server{service=ServiceModuleMap});
+parse_options([{service, Service} | Rest], State) when State#thrift_socket_server.service == undefined, is_atom(Service) ->
+ parse_options(Rest, State#thrift_socket_server{service=Service});
+
+parse_options([{max, Max} | Rest], State) ->
+ MaxInt = case Max of
+ Max when is_list(Max) ->
+ list_to_integer(Max);
+ Max when is_integer(Max) ->
+ Max
+ end,
+ parse_options(Rest, State#thrift_socket_server{max=MaxInt});
+
+parse_options([{protocol, Proto} | Rest], State) when is_atom(Proto) ->
+ parse_options(Rest, State#thrift_socket_server{protocol=Proto});
+
+parse_options([{framed, Framed} | Rest], State) when is_boolean(Framed) ->
+ parse_options(Rest, State#thrift_socket_server{framed=Framed});
+
+parse_options([{ssltransport, SSLTransport} | Rest], State) when is_boolean(SSLTransport) ->
+ parse_options(Rest, State#thrift_socket_server{ssltransport=SSLTransport});
+parse_options([{ssloptions, SSLOptions} | Rest], State) when is_list(SSLOptions) ->
+ parse_options(Rest, State#thrift_socket_server{ssloptions=SSLOptions}).
+
+start_server(State=#thrift_socket_server{name=Name}) ->
+ case Name of
+ undefined ->
+ gen_server:start_link(?MODULE, State, []);
+ _ ->
+ gen_server:start_link(Name, ?MODULE, State, [])
+ end.
+
+init(State=#thrift_socket_server{ip=Ip, port=Port}) ->
+ process_flag(trap_exit, true),
+ BaseOpts = [binary,
+ {reuseaddr, true},
+ {packet, 0},
+ {backlog, 4096},
+ {recbuf, 8192},
+ {active, false}],
+ Opts = case Ip of
+ any ->
+ BaseOpts;
+ Ip ->
+ [{ip, Ip} | BaseOpts]
+ end,
+ case gen_tcp_listen(Port, Opts, State) of
+ {stop, eacces} ->
+ %% fdsrv module allows another shot to bind
+ %% ports which require root access
+ case Port < 1024 of
+ true ->
+ case fdsrv:start() of
+ {ok, _} ->
+ case fdsrv:bind_socket(tcp, Port) of
+ {ok, Fd} ->
+ gen_tcp_listen(Port, [{fd, Fd} | Opts], State);
+ _ ->
+ {stop, fdsrv_bind_failed}
+ end;
+ _ ->
+ {stop, fdsrv_start_failed}
+ end;
+ false ->
+ {stop, eacces}
+ end;
+ Other ->
+ error_logger:info_msg("thrift service listening on port ~p", [Port]),
+ Other
+ end.
+
+gen_tcp_listen(Port, Opts, State) ->
+ case gen_tcp:listen(Port, Opts) of
+ {ok, Listen} ->
+ {ok, ListenPort} = inet:port(Listen),
+ {ok, new_acceptor(State#thrift_socket_server{listen=Listen,
+ port=ListenPort})};
+ {error, Reason} ->
+ {stop, Reason}
+ end.
+
+new_acceptor(State=#thrift_socket_server{max=0}) ->
+ error_logger:error_msg("Not accepting new connections"),
+ State#thrift_socket_server{acceptor=null};
+new_acceptor(State=#thrift_socket_server{listen=Listen,
+ service=Service, handler=Handler,
+ socket_opts=Opts, framed=Framed, protocol=Proto,
+ ssltransport=SslTransport, ssloptions=SslOptions
+ }) ->
+ Pid = proc_lib:spawn_link(?MODULE, acceptor_loop,
+ [{self(), Listen, Service, Handler, Opts, Framed, SslTransport, SslOptions, Proto}]),
+ State#thrift_socket_server{acceptor=Pid}.
+
+acceptor_loop({Server, Listen, Service, Handler, SocketOpts, Framed, SslTransport, SslOptions, Proto})
+ when is_pid(Server), is_list(SocketOpts) ->
+ case catch gen_tcp:accept(Listen) of % infinite timeout
+ {ok, Socket} ->
+ gen_server:cast(Server, {accepted, self()}),
+ ProtoGen = fun() ->
+ {ok, SocketTransport} = case SslTransport of
+ true -> thrift_sslsocket_transport:new(Socket, SocketOpts, SslOptions);
+ false -> thrift_socket_transport:new(Socket, SocketOpts)
+ end,
+ {ok, Transport} = case Framed of
+ true -> thrift_framed_transport:new(SocketTransport);
+ false -> thrift_buffered_transport:new(SocketTransport)
+ end,
+ {ok, Protocol} = case Proto of
+ compact -> thrift_compact_protocol:new(Transport);
+ json -> thrift_json_protocol:new(Transport);
+ _ -> thrift_binary_protocol:new(Transport)
+ end,
+ {ok, Protocol}
+ end,
+ thrift_processor:init({Server, ProtoGen, Service, Handler});
+ {error, closed} ->
+ exit({error, closed});
+ Other ->
+ error_logger:error_report(
+ [{application, thrift},
+ "Accept failed error",
+ lists:flatten(io_lib:format("~p", [Other]))]),
+ exit({error, accept_failed})
+ end.
+
+handle_call({get, port}, _From, State=#thrift_socket_server{port=Port}) ->
+ {reply, Port, State};
+handle_call(_Message, _From, State) ->
+ Res = error,
+ {reply, Res, State}.
+
+handle_cast({accepted, Pid},
+ State=#thrift_socket_server{acceptor=Pid, max=Max}) ->
+ % io:format("accepted ~p~n", [Pid]),
+ State1 = State#thrift_socket_server{max=Max - 1},
+ {noreply, new_acceptor(State1)};
+handle_cast(stop, State) ->
+ {stop, normal, State}.
+
+terminate(Reason, #thrift_socket_server{listen=Listen, port=Port}) ->
+ gen_tcp:close(Listen),
+ case Reason of
+ normal -> ok;
+ shutdown -> ok;
+ _ -> {backtrace, Bt} = erlang:process_info(self(), backtrace),
+ error_logger:error_report({?MODULE, ?LINE,
+ {child_error, Reason, Bt}})
+ end,
+ case Port < 1024 of
+ true ->
+ catch fdsrv:stop(),
+ ok;
+ false ->
+ ok
+ end.
+
+code_change(_OldVsn, State, _Extra) ->
+ State.
+
+handle_info({'EXIT', Pid, normal},
+ State=#thrift_socket_server{acceptor=Pid}) ->
+ {noreply, new_acceptor(State)};
+handle_info({'EXIT', Pid, Reason},
+ State=#thrift_socket_server{acceptor=Pid}) ->
+ error_logger:error_report({?MODULE, ?LINE,
+ {acceptor_error, Reason}}),
+ timer:sleep(100),
+ {noreply, new_acceptor(State)};
+handle_info({'EXIT', _LoopPid, Reason},
+ State=#thrift_socket_server{acceptor=Pid, max=Max}) ->
+ case Reason of
+ normal -> ok;
+ shutdown -> ok;
+ _ -> error_logger:error_report({?MODULE, ?LINE,
+ {child_error, Reason, erlang:get_stacktrace()}})
+ end,
+ State1 = State#thrift_socket_server{max=Max + 1},
+ State2 = case Pid of
+ null -> new_acceptor(State1);
+ _ -> State1
+ end,
+ {noreply, State2};
+handle_info(Info, State) ->
+ error_logger:info_report([{'INFO', Info}, {'State', State}]),
+ {noreply, State}.
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_socket_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_socket_transport.erl
new file mode 100644
index 000000000..fa10ed0c6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_socket_transport.erl
@@ -0,0 +1,176 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_socket_transport).
+
+-behaviour(thrift_transport).
+
+%% constructors
+-export([new/1, new/2]).
+%% transport callbacks
+-export([read/2, read_exact/2, write/2, flush/1, close/1]).
+%% legacy api
+-export([new_transport_factory/3]).
+
+
+-record(t_socket, {
+ socket,
+ recv_timeout=60000,
+ buffer = []
+}).
+
+-type state() :: #t_socket{}.
+
+
+-spec new(Socket::any()) ->
+ thrift_transport:t_transport().
+
+new(Socket) -> new(Socket, []).
+
+-spec new(Socket::any(), Opts::list()) ->
+ thrift_transport:t_transport().
+
+new(Socket, Opts) when is_list(Opts) ->
+ State = parse_opts(Opts, #t_socket{socket = Socket}),
+ thrift_transport:new(?MODULE, State).
+
+
+parse_opts([{recv_timeout, Timeout}|Rest], State)
+when is_integer(Timeout), Timeout > 0 ->
+ parse_opts(Rest, State#t_socket{recv_timeout = Timeout});
+parse_opts([{recv_timeout, infinity}|Rest], State) ->
+ parse_opts(Rest, State#t_socket{recv_timeout = infinity});
+parse_opts([], State) ->
+ State.
+
+
+-include("thrift_transport_behaviour.hrl").
+
+
+read(State = #t_socket{buffer = Buf}, Len)
+when is_integer(Len), Len >= 0 ->
+ Binary = iolist_to_binary(Buf),
+ case iolist_size(Binary) of
+ X when X >= Len ->
+ {Result, Remaining} = split_binary(Binary, Len),
+ {State#t_socket{buffer = Remaining}, {ok, Result}};
+ _ -> recv(State, Len)
+ end.
+
+recv(State = #t_socket{socket = Socket, buffer = Buf}, Len) ->
+ case gen_tcp:recv(Socket, 0, State#t_socket.recv_timeout) of
+ {error, Error} ->
+ gen_tcp:close(Socket),
+ {State, {error, Error}};
+ {ok, Data} ->
+ Binary = iolist_to_binary([Buf, Data]),
+ Give = min(iolist_size(Binary), Len),
+ {Result, Remaining} = split_binary(Binary, Give),
+ {State#t_socket{buffer = Remaining}, {ok, Result}}
+ end.
+
+
+read_exact(State = #t_socket{buffer = Buf}, Len)
+when is_integer(Len), Len >= 0 ->
+ Binary = iolist_to_binary(Buf),
+ case iolist_size(Binary) of
+ X when X >= Len -> read(State, Len);
+ X ->
+ case gen_tcp:recv(State#t_socket.socket, Len - X, State#t_socket.recv_timeout) of
+ {error, Error} ->
+ gen_tcp:close(State#t_socket.socket),
+ {State, {error, Error}};
+ {ok, Data} ->
+ {State#t_socket{buffer = []}, {ok, <<Binary/binary, Data/binary>>}}
+ end
+ end.
+
+
+write(State = #t_socket{socket = Socket}, Data) ->
+ case gen_tcp:send(Socket, Data) of
+ {error, Error} ->
+ gen_tcp:close(Socket),
+ {State, {error, Error}};
+ ok -> {State, ok}
+ end.
+
+
+flush(State) ->
+ {State#t_socket{buffer = []}, ok}.
+
+
+close(State = #t_socket{socket = Socket}) ->
+ {State, gen_tcp:close(Socket)}.
+
+
+%% legacy api. left for compatibility
+
+%% The following "local" record is filled in by parse_factory_options/2
+%% below. These options can be passed to new_protocol_factory/3 in a
+%% proplists-style option list. They're parsed like this so it is an O(n)
+%% operation instead of O(n^2)
+-record(factory_opts, {
+ connect_timeout = infinity,
+ sockopts = [],
+ framed = false
+}).
+
+parse_factory_options([], FactoryOpts, TransOpts) -> {FactoryOpts, TransOpts};
+parse_factory_options([{framed, Bool}|Rest], FactoryOpts, TransOpts)
+when is_boolean(Bool) ->
+ parse_factory_options(Rest, FactoryOpts#factory_opts{framed = Bool}, TransOpts);
+parse_factory_options([{sockopts, OptList}|Rest], FactoryOpts, TransOpts)
+when is_list(OptList) ->
+ parse_factory_options(Rest, FactoryOpts#factory_opts{sockopts = OptList}, TransOpts);
+parse_factory_options([{connect_timeout, TO}|Rest], FactoryOpts, TransOpts)
+when TO =:= infinity; is_integer(TO) ->
+ parse_factory_options(Rest, FactoryOpts#factory_opts{connect_timeout = TO}, TransOpts);
+parse_factory_options([{recv_timeout, TO}|Rest], FactoryOpts, TransOpts)
+when TO =:= infinity; is_integer(TO) ->
+ parse_factory_options(Rest, FactoryOpts, [{recv_timeout, TO}] ++ TransOpts).
+
+
+%% Generates a "transport factory" function - a fun which returns a thrift_transport()
+%% instance.
+%% State can be passed into a protocol factory to generate a connection to a
+%% thrift server over a socket.
+new_transport_factory(Host, Port, Options) ->
+ {FactoryOpts, TransOpts} = parse_factory_options(Options, #factory_opts{}, []),
+ {ok, fun() -> SockOpts = [binary,
+ {packet, 0},
+ {active, false},
+ {nodelay, true}|FactoryOpts#factory_opts.sockopts
+ ],
+ case catch gen_tcp:connect(
+ Host,
+ Port,
+ SockOpts,
+ FactoryOpts#factory_opts.connect_timeout
+ ) of
+ {ok, Sock} ->
+ {ok, Transport} = thrift_socket_transport:new(Sock, TransOpts),
+ {ok, BufTransport} = case FactoryOpts#factory_opts.framed of
+ true -> thrift_framed_transport:new(Transport);
+ false -> thrift_buffered_transport:new(Transport)
+ end,
+ {ok, BufTransport};
+ Error -> Error
+ end
+ end}.
+
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_sslsocket_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_sslsocket_transport.erl
new file mode 100644
index 000000000..211153f7b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_sslsocket_transport.erl
@@ -0,0 +1,147 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+-module(thrift_sslsocket_transport).
+
+-include("thrift_transport_behaviour.hrl").
+
+-behaviour(thrift_transport).
+
+-export([new/3,
+ write/2, read/2, flush/1, close/1,
+
+ new_transport_factory/3]).
+
+%% Export only for the transport factory
+-export([new/2]).
+
+-record(data, {socket,
+ recv_timeout=infinity}).
+-type state() :: #data{}.
+
+%% The following "local" record is filled in by parse_factory_options/2
+%% below. These options can be passed to new_protocol_factory/3 in a
+%% proplists-style option list. They're parsed like this so it is an O(n)
+%% operation instead of O(n^2)
+-record(factory_opts, {connect_timeout = infinity,
+ sockopts = [],
+ framed = false,
+ ssloptions = []}).
+
+parse_factory_options([], Opts) ->
+ Opts;
+parse_factory_options([{framed, Bool} | Rest], Opts) when is_boolean(Bool) ->
+ parse_factory_options(Rest, Opts#factory_opts{framed=Bool});
+parse_factory_options([{sockopts, OptList} | Rest], Opts) when is_list(OptList) ->
+ parse_factory_options(Rest, Opts#factory_opts{sockopts=OptList});
+parse_factory_options([{connect_timeout, TO} | Rest], Opts) when TO =:= infinity; is_integer(TO) ->
+ parse_factory_options(Rest, Opts#factory_opts{connect_timeout=TO});
+parse_factory_options([{ssloptions, SslOptions} | Rest], Opts) when is_list(SslOptions) ->
+ parse_factory_options(Rest, Opts#factory_opts{ssloptions=SslOptions}).
+
+new(Socket, SockOpts, SslOptions) when is_list(SockOpts), is_list(SslOptions) ->
+ inet:setopts(Socket, [{active, false}]), %% => prevent the ssl handshake messages get lost
+
+ %% upgrade to an ssl socket
+ case catch ssl:ssl_accept(Socket, SslOptions) of % infinite timeout
+ {ok, SslSocket} ->
+ new(SslSocket, SockOpts);
+ {error, Reason} ->
+ exit({error, Reason});
+ Other ->
+ error_logger:error_report(
+ [{application, thrift},
+ "SSL accept failed error",
+ lists:flatten(io_lib:format("~p", [Other]))]),
+ exit({error, ssl_accept_failed})
+ end.
+
+new(SslSocket, SockOpts) ->
+ State =
+ case lists:keysearch(recv_timeout, 1, SockOpts) of
+ {value, {recv_timeout, Timeout}}
+ when is_integer(Timeout), Timeout > 0 ->
+ #data{socket=SslSocket, recv_timeout=Timeout};
+ _ ->
+ #data{socket=SslSocket}
+ end,
+ thrift_transport:new(?MODULE, State).
+
+%% Data :: iolist()
+write(This = #data{socket = Socket}, Data) ->
+ {This, ssl:send(Socket, Data)}.
+
+read(This = #data{socket=Socket, recv_timeout=Timeout}, Len)
+ when is_integer(Len), Len >= 0 ->
+ case ssl:recv(Socket, Len, Timeout) of
+ Err = {error, timeout} ->
+ error_logger:info_msg("read timeout: peer conn ~p", [inet:peername(Socket)]),
+ ssl:close(Socket),
+ {This, Err};
+ Data ->
+ {This, Data}
+ end.
+
+%% We can't really flush - everything is flushed when we write
+flush(This) ->
+ {This, ok}.
+
+close(This = #data{socket = Socket}) ->
+ {This, ssl:close(Socket)}.
+
+%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%
+%% Generates a "transport factory" function - a fun which returns a thrift_transport()
+%% instance.
+%% This can be passed into a protocol factory to generate a connection to a
+%% thrift server over a socket.
+%%
+new_transport_factory(Host, Port, Options) ->
+ ParsedOpts = parse_factory_options(Options, #factory_opts{}),
+
+ F = fun() ->
+ SockOpts = [binary,
+ {packet, 0},
+ {active, false},
+ {nodelay, true} |
+ ParsedOpts#factory_opts.sockopts],
+ case catch gen_tcp:connect(Host, Port, SockOpts,
+ ParsedOpts#factory_opts.connect_timeout) of
+ {ok, Sock} ->
+ SslSock = case catch ssl:connect(Sock, ParsedOpts#factory_opts.ssloptions,
+ ParsedOpts#factory_opts.connect_timeout) of
+ {ok, SslSocket} ->
+ SslSocket;
+ Other ->
+ error_logger:info_msg("error while connecting over ssl - reason: ~p~n", [Other]),
+ catch gen_tcp:close(Sock),
+ exit(error)
+ end,
+ {ok, Transport} = thrift_sslsocket_transport:new(SslSock, SockOpts),
+ {ok, BufTransport} =
+ case ParsedOpts#factory_opts.framed of
+ true -> thrift_framed_transport:new(Transport);
+ false -> thrift_buffered_transport:new(Transport)
+ end,
+ {ok, BufTransport};
+ Error ->
+ Error
+ end
+ end,
+ {ok, F}. \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_transport.erl
new file mode 100644
index 000000000..2414bde36
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_transport.erl
@@ -0,0 +1,126 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_transport).
+
+-export([behaviour_info/1]).
+%% constructors
+-export([new/1, new/2]).
+%% transport callbacks
+-export([read/2, read_exact/2, write/2, flush/1, close/1]).
+
+-export_type([t_transport/0]).
+
+
+behaviour_info(callbacks) ->
+ [{read, 2}, {write, 2}, {flush, 1}, {close, 1}].
+
+
+-record(t_transport, {
+ module,
+ state
+}).
+
+-type state() :: #t_transport{}.
+-type t_transport() :: #t_transport{}.
+
+
+-ifdef(transport_wrapper_module).
+-define(debug_wrap(Transport),
+ case Transport#t_transport.module of
+ ?transport_wrapper_module -> Transport;
+ _Else ->
+ {ok, Result} = ?transport_wrapper_module:new(Transport),
+ Result
+ end
+).
+-else.
+-define(debug_wrap(Transport), Transport).
+-endif.
+
+
+-type wrappable() ::
+ binary() |
+ list() |
+ {membuffer, binary() | list()} |
+ {tcp, port()} |
+ {tcp, port(), list()} |
+ {file, file:io_device()} |
+ {file, file:io_device(), list()} |
+ {file, file:filename()} |
+ {file, file:filename(), list()}.
+
+-spec new(wrappable()) -> {ok, #t_transport{}}.
+
+new({membuffer, Membuffer}) when is_binary(Membuffer); is_list(Membuffer) ->
+ thrift_membuffer_transport:new(Membuffer);
+new({membuffer, Membuffer, []}) when is_binary(Membuffer); is_list(Membuffer) ->
+ thrift_membuffer_transport:new(Membuffer);
+new({tcp, Socket}) when is_port(Socket) ->
+ new({tcp, Socket, []});
+new({tcp, Socket, Opts}) when is_port(Socket) ->
+ thrift_socket_transport:new(Socket, Opts);
+new({file, Filename}) when is_list(Filename); is_binary(Filename) ->
+ new({file, Filename, []});
+new({file, Filename, Opts}) when is_list(Filename); is_binary(Filename) ->
+ {ok, File} = file:open(Filename, [raw, binary]),
+ new({file, File, Opts});
+new({file, File, Opts}) ->
+ thrift_file_transport:new(File, Opts).
+
+-spec new(Module::module(), State::any()) -> {ok, #t_transport{}}.
+
+new(Module, State) when is_atom(Module) ->
+ {ok, ?debug_wrap(#t_transport{module = Module, state = State})}.
+
+
+-include("thrift_transport_behaviour.hrl").
+
+
+read(Transport = #t_transport{module = Module}, Len)
+when is_integer(Len), Len >= 0 ->
+ {NewState, Result} = Module:read(Transport#t_transport.state, Len),
+ {Transport#t_transport{state = NewState}, Result}.
+
+
+read_exact(Transport = #t_transport{module = Module}, Len)
+when is_integer(Len), Len >= 0 ->
+ case lists:keyfind(read_exact, 1, Module:module_info(exports)) of
+ {read_exact, 2} ->
+ {NewState, Result} = Module:read_exact(Transport#t_transport.state, Len),
+ {Transport#t_transport{state = NewState}, Result};
+ _ ->
+ read(Transport, Len)
+ end.
+
+
+write(Transport = #t_transport{module = Module}, Data) ->
+ {NewState, Result} = Module:write(Transport#t_transport.state, Data),
+ {Transport#t_transport{state = NewState}, Result}.
+
+
+flush(Transport = #t_transport{module = Module}) ->
+ {NewState, Result} = Module:flush(Transport#t_transport.state),
+ {Transport#t_transport{state = NewState}, Result}.
+
+
+close(Transport = #t_transport{module = Module}) ->
+ {NewState, Result} = Module:close(Transport#t_transport.state),
+ {Transport#t_transport{state = NewState}, Result}.
+
diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_transport_state_test.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_transport_state_test.erl
new file mode 100644
index 000000000..e83a44d26
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/src/thrift_transport_state_test.erl
@@ -0,0 +1,117 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(thrift_transport_state_test).
+
+-behaviour(gen_server).
+-behaviour(thrift_transport).
+
+%% API
+-export([new/1]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+%% thrift_transport callbacks
+-export([write/2, read/2, flush/1, close/1]).
+
+-record(trans, {wrapped, % #thrift_transport{}
+ version :: integer(),
+ counter :: pid()
+ }).
+-type state() :: #trans{}.
+-include("thrift_transport_behaviour.hrl").
+
+-record(state, {cversion :: integer()}).
+
+
+new(WrappedTransport) ->
+ case gen_server:start_link(?MODULE, [], []) of
+ {ok, Pid} ->
+ Trans = #trans{wrapped = WrappedTransport,
+ version = 0,
+ counter = Pid},
+ thrift_transport:new(?MODULE, Trans);
+ Else ->
+ Else
+ end.
+
+%%====================================================================
+%% thrift_transport callbacks
+%%====================================================================
+
+write(Transport0 = #trans{wrapped = Wrapped0}, Data) ->
+ Transport1 = check_version(Transport0),
+ {Wrapped1, Result} = thrift_transport:write(Wrapped0, Data),
+ Transport2 = Transport1#trans{wrapped = Wrapped1},
+ {Transport2, Result}.
+
+flush(Transport0 = #trans{wrapped = Wrapped0}) ->
+ Transport1 = check_version(Transport0),
+ {Wrapped1, Result} = thrift_transport:flush(Wrapped0),
+ Transport2 = Transport1#trans{wrapped = Wrapped1},
+ {Transport2, Result}.
+
+close(Transport0 = #trans{wrapped = Wrapped0}) ->
+ Transport1 = check_version(Transport0),
+ shutdown_counter(Transport1),
+ {Wrapped1, Result} = thrift_transport:close(Wrapped0),
+ Transport2 = Transport1#trans{wrapped = Wrapped1},
+ {Transport2, Result}.
+
+read(Transport0 = #trans{wrapped = Wrapped0}, Len) ->
+ Transport1 = check_version(Transport0),
+ {Wrapped1, Result} = thrift_transport:read(Wrapped0, Len),
+ Transport2 = Transport1#trans{wrapped = Wrapped1},
+ {Transport2, Result}.
+
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+
+init([]) ->
+ {ok, #state{cversion = 0}}.
+
+handle_call(check_version, _From, State = #state{cversion = Version}) ->
+ {reply, Version, State#state{cversion = Version+1}}.
+
+handle_cast(shutdown, State) ->
+ {stop, normal, State}.
+
+handle_info(_Info, State) -> {noreply, State}.
+code_change(_OldVsn, State, _Extra) -> {ok, State}.
+terminate(_Reason, _State) -> ok.
+
+%%--------------------------------------------------------------------
+%% Internal functions
+%%--------------------------------------------------------------------
+
+check_version(Transport = #trans{version = Version, counter = Counter}) ->
+ case gen_server:call(Counter, check_version) of
+ Version ->
+ Transport#trans{version = Version+1};
+ _Else ->
+ % State wasn't propagated properly. Die.
+ erlang:error(state_not_propagated)
+ end.
+
+shutdown_counter(#trans{counter = Counter}) ->
+ gen_server:cast(Counter, shutdown).
diff --git a/src/jaegertracing/thrift/lib/erl/test/Thrift1151.thrift b/src/jaegertracing/thrift/lib/erl/test/Thrift1151.thrift
new file mode 100644
index 000000000..6f934a7b1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/Thrift1151.thrift
@@ -0,0 +1,3 @@
+struct StructA { 1: i16 x; }
+struct StructB { 1: i32 x; }
+struct StructC { 1: StructA x; }
diff --git a/src/jaegertracing/thrift/lib/erl/test/Thrift1475.thrift b/src/jaegertracing/thrift/lib/erl/test/Thrift1475.thrift
new file mode 100644
index 000000000..7adeb7e65
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/Thrift1475.thrift
@@ -0,0 +1,34 @@
+struct StructB
+{
+ 1: string x
+}
+
+struct StructA
+{
+ 1: string a,
+ 2: binary b,
+ 3: optional string c,
+ 4: optional binary d,
+ 5: required string e,
+ 6: required binary f,
+ 7: string g = "foo",
+ 8: i32 h,
+ 9: optional i32 i,
+ 10: required i32 j,
+ 11: required i32 k = 5,
+ 12: double l,
+ 13: optional double m,
+ 14: required double n,
+ 15: double o = 3.14159,
+ 16: list<string> string_list,
+ 17: list<byte> byte_list = [1, 2, 3],
+ 18: required list<string> rsl,
+ 19: optional list<string> osl,
+ 20: set<string> string_set,
+ 21: required set<string> rss,
+ 22: optional set<string> oss,
+ 23: map<string, string> string_map,
+ 24: required map<string, string> rsm,
+ 25: optional map<string, string> osm,
+ 26: StructB structb
+}
diff --git a/src/jaegertracing/thrift/lib/erl/test/Thrift_omit_with.thrift b/src/jaegertracing/thrift/lib/erl/test/Thrift_omit_with.thrift
new file mode 100644
index 000000000..8bffc7c73
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/Thrift_omit_with.thrift
@@ -0,0 +1,22 @@
+struct test1 {
+ 1: i32 one
+ 2: i32 two // omit
+ 3: i32 three
+}
+
+struct test2 {
+ 1: i32 one
+ 2: test2 two // omit
+ 3: i32 three
+}
+
+struct test3 {
+ 1: i32 one
+ 2: list<test1> two // omit
+}
+
+struct test4 {
+ 1: i32 one
+ 2: map<i32,test1> two // omit
+}
+
diff --git a/src/jaegertracing/thrift/lib/erl/test/flags/LegacyNames.thrift b/src/jaegertracing/thrift/lib/erl/test/flags/LegacyNames.thrift
new file mode 100644
index 000000000..38f272907
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/flags/LegacyNames.thrift
@@ -0,0 +1,33 @@
+enum Numberz
+{
+ ONE = 1,
+ TWO,
+ THREE,
+ FIVE = 5,
+ SIX,
+ EIGHT = 8
+}
+
+const Numberz myNumberz = Numberz.ONE;
+
+struct CapitalizedStruct
+{
+ 1: i32 Id,
+ 2: binary message
+}
+
+struct ListCapitalizedStructs
+{
+ 1: list<CapitalizedStruct> structs
+}
+
+exception Xception {
+ 1: i32 errorCode,
+ 2: binary message
+}
+
+service LegacyNames
+{
+ ListCapitalizedStructs Names(1: CapitalizedStruct foo, 2: CapitalizedStruct bar)
+ throws(1: Xception err)
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/erl/test/flags/Thrift3214.thrift b/src/jaegertracing/thrift/lib/erl/test/flags/Thrift3214.thrift
new file mode 100644
index 000000000..a9110cedd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/flags/Thrift3214.thrift
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+struct StringMap
+{
+ 1: map<i32, string> data = {1: "a", 2: "b"};
+}
diff --git a/src/jaegertracing/thrift/lib/erl/test/legacy_names_test.erl b/src/jaegertracing/thrift/lib/erl/test/legacy_names_test.erl
new file mode 100644
index 000000000..c16aa3ee2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/legacy_names_test.erl
@@ -0,0 +1,69 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(legacy_names_test).
+-compile(export_all).
+
+-include_lib("eunit/include/eunit.hrl").
+
+-include("gen-erl/legacyNames_constants.hrl").
+
+record_generation_test_() ->
+ [
+ {"capitalizedStruct record", ?_assertMatch(
+ {capitalizedStruct, _, _},
+ #capitalizedStruct{id=null,message=null}
+ )}
+ ].
+
+struct_info_test_() ->
+ [
+ {"capitalizedStruct extended definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, i32, 'id', undefined},
+ {2, undefined, string, 'message', undefined}
+ ]},
+ legacyNames_types:struct_info_ext(capitalizedStruct)
+ )},
+ {"listCapitalizedStructs extended definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, {list, {struct, {'legacyNames_types', 'capitalizedStruct'}}}, 'structs', []}
+ ]},
+ legacyNames_types:struct_info_ext(listCapitalizedStructs)
+ )}
+ ].
+
+service_info_test_() ->
+ [
+ {"names params", ?_assertEqual(
+ {struct, [
+ {1, {struct, {'legacyNames_types', 'capitalizedStruct'}}},
+ {2, {struct, {'legacyNames_types', 'capitalizedStruct'}}}
+ ]},
+ legacyNames_thrift:function_info(names, params_type)
+ )},
+ {"names reply", ?_assertEqual(
+ {struct, {'legacyNames_types', 'listCapitalizedStructs'}},
+ legacyNames_thrift:function_info(names, reply_type)
+ )},
+ {"names exceptions", ?_assertEqual(
+ {struct, [{1, {struct, {'legacyNames_types', 'xception'}}}]},
+ legacyNames_thrift:function_info(names, exceptions)
+ )}
+ ].
diff --git a/src/jaegertracing/thrift/lib/erl/test/multiplexing.thrift b/src/jaegertracing/thrift/lib/erl/test/multiplexing.thrift
new file mode 100644
index 000000000..7c7994b97
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/multiplexing.thrift
@@ -0,0 +1,7 @@
+service Multiplexing_Calculator {
+ i32 add(1: i32 x, 2: i32 y)
+}
+
+service Multiplexing_WeatherReport {
+ double getTemperature()
+}
diff --git a/src/jaegertracing/thrift/lib/erl/test/multiplexing_test.erl b/src/jaegertracing/thrift/lib/erl/test/multiplexing_test.erl
new file mode 100644
index 000000000..0f2d616b8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/multiplexing_test.erl
@@ -0,0 +1,57 @@
+-module(multiplexing_test).
+
+-include_lib("eunit/include/eunit.hrl").
+
+-export([
+ handle_function/2
+ ,handle_error/2
+]).
+
+start_multiplexed_server_test() ->
+
+ Port = 9090,
+ Services = [
+ {"Multiplexing_Calculator", multiplexing__calculator_thrift},
+ {"Multiplexing_WeatherReport", multiplexing__weather_report_thrift}
+ ],
+
+ {ok, Pid} = thrift_socket_server:start([
+ {ip, "127.0.0.1"},
+ {port, Port},
+ {name, ?MODULE},
+ {service, Services},
+ {handler, [
+ {"error_handler", ?MODULE},
+ {"Multiplexing_Calculator", ?MODULE},
+ {"Multiplexing_WeatherReport", ?MODULE}
+ ]}
+ ]),
+
+ {ok, [{"Multiplexing_Calculator", CalculatorClient0},
+ {"Multiplexing_WeatherReport", WeatherReportClient0}]} = thrift_client_util:new_multiplexed("127.0.0.1", Port, Services, []),
+
+ ?assertMatch({_, {error, {bad_args, _, _}}}, thrift_client:call(WeatherReportClient0, getTemperature, [1])),
+ ?assertMatch({_, {error, {bad_args, _, _}}}, thrift_client:call(CalculatorClient0, add, [1])),
+ ?assertMatch({_, {error, {bad_args, _, _}}}, thrift_client:call(CalculatorClient0, add, [1,1,1])),
+
+ ?assertMatch({_, {error, {no_function, _}}}, thrift_client:call(CalculatorClient0, getTemperature, [])),
+ ?assertMatch({_, {error, {no_function, _}}}, thrift_client:call(WeatherReportClient0, add, [41, 1])),
+
+ ?assertMatch({_, {ok, 42}}, thrift_client:call(CalculatorClient0, add, [41, 1])),
+ ?assertMatch({_, {ok, 42.0}}, thrift_client:call(WeatherReportClient0, getTemperature, [])),
+
+ thrift_socket_server:stop(Pid).
+
+%% HANDLE FUNCTIONS
+
+%% Calculator handles
+handle_function(add, {X, Y}) ->
+ {reply, X + Y};
+
+%% WeatherReport handles
+handle_function(getTemperature, {}) ->
+ {reply, 42.0}.
+
+handle_error(_F, _Reason) ->
+%% ?debugHere, ?debugVal({_F, _Reason}),
+ ok. \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/erl/test/name_conflict_test.erl b/src/jaegertracing/thrift/lib/erl/test/name_conflict_test.erl
new file mode 100644
index 000000000..b01df5732
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/name_conflict_test.erl
@@ -0,0 +1,299 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(name_conflict_test).
+-compile(export_all).
+
+-include_lib("eunit/include/eunit.hrl").
+
+-include("gen-erl/name_conflict_test_constants.hrl").
+
+record_generation_test_() ->
+ [
+ {"using record", ?_assertMatch(
+ {using, _, _},
+ #using{single=null,integer=null}
+ )},
+ {"delegate record", ?_assertMatch(
+ {delegate, _, _},
+ #delegate{partial=null,delegate=null}
+ )},
+ {"get record", ?_assertMatch(
+ {get, _},
+ #get{sbyte=null}
+ )},
+ {"partial record", ?_assertMatch(
+ {partial, _, _, _},
+ #partial{using=null}
+ )},
+ {"ClassAndProp record", ?_assertMatch(
+ {'ClassAndProp', _, _, _, _},
+ #'ClassAndProp'{
+ 'ClassAndProp'=null,
+ 'ClassAndProp_'=null,
+ 'ClassAndProp__'=null,
+ 'ClassAndProper'=null
+ }
+ )},
+ {"second_chance record", ?_assertMatch(
+ {second_chance, _, _, _, _},
+ #second_chance{
+ 'SECOND_CHANCE'=null,
+ 'SECOND_CHANCE_'=null,
+ 'SECOND_CHANCE__'=null,
+ 'SECOND_CHANCES'=null
+ }
+ )},
+ {"NOW_EAT_THIS record", ?_assertMatch(
+ {'NOW_EAT_THIS', _, _, _, _},
+ #'NOW_EAT_THIS'{
+ now_eat_this=null,
+ now_eat_this_=null,
+ now_eat_this__=null,
+ now_eat_this_and_this=null
+ }
+ )},
+ {"TheEdgeCase record", ?_assertMatch(
+ {'TheEdgeCase', _, _, _, _, _, _},
+ #'TheEdgeCase'{
+ theEdgeCase=null,
+ theEdgeCase_=null,
+ theEdgeCase__=null,
+ 'TheEdgeCase'=null,
+ 'TheEdgeCase_'=null,
+ 'TheEdgeCase__'=null
+ }
+ )},
+ {"Tricky_ record", ?_assertMatch(
+ {'Tricky_', _, _},
+ #'Tricky_'{tricky=null,'Tricky'=null}
+ )},
+ {"Nested record", ?_assertMatch(
+ {'Nested', _, _, _, _, _, _},
+ #'Nested'{
+ 'ClassAndProp'=null,
+ second_chance=null,
+ 'NOW_EAT_THIS'=null,
+ 'TheEdgeCase'=null,
+ 'Tricky_'=null,
+ 'Nested'=null
+ }
+ )},
+ {"Problem_ record", ?_assertMatch(
+ {'Problem_', _, _},
+ #'Problem_'{problem=null,'Problem'=null}
+ )}
+ ].
+
+struct_info_test_() ->
+ [
+ {"using definition", ?_assertEqual(
+ {struct, [{1, double},{2, double}]},
+ name_conflict_test_types:struct_info(using)
+ )},
+ {"delegate definition", ?_assertEqual(
+ {struct, [
+ {1, string},
+ {2, {struct, {name_conflict_test_types, delegate}}}
+ ]},
+ name_conflict_test_types:struct_info(delegate)
+ )},
+ {"get definition", ?_assertEqual(
+ {struct, [{1, bool}]},
+ name_conflict_test_types:struct_info(get)
+ )},
+ {"partial definition", ?_assertEqual(
+ {struct, [
+ {1, {struct, {name_conflict_test_types, using}}},
+ {2, bool},
+ {3, bool}
+ ]},
+ name_conflict_test_types:struct_info(partial)
+ )},
+ {"ClassAndProp definition", ?_assertEqual(
+ {struct, [{1, bool},{2, bool},{3, bool},{4, bool}]},
+ name_conflict_test_types:struct_info('ClassAndProp')
+ )},
+ {"second_chance definition", ?_assertEqual(
+ {struct, [{1, bool},{2, bool},{3, bool},{4, bool}]},
+ name_conflict_test_types:struct_info(second_chance)
+ )},
+ {"NOW_EAT_THIS definition", ?_assertEqual(
+ {struct, [{1, bool},{2, bool},{3, bool},{4, bool}]},
+ name_conflict_test_types:struct_info('NOW_EAT_THIS')
+ )},
+ {"TheEdgeCase definition", ?_assertEqual(
+ {struct, [{1, bool},{2, bool},{3, bool},{4, bool},{5, bool},{6, bool}]},
+ name_conflict_test_types:struct_info('TheEdgeCase')
+ )},
+ {"Tricky_ definition", ?_assertEqual(
+ {struct, [{1, bool},{2, bool}]},
+ name_conflict_test_types:struct_info('Tricky_')
+ )},
+ {"Nested definition", ?_assertEqual(
+ {struct, [
+ {1, {struct, {name_conflict_test_types, 'ClassAndProp'}}},
+ {2, {struct, {name_conflict_test_types, second_chance}}},
+ {3, {struct, {name_conflict_test_types, 'NOW_EAT_THIS'}}},
+ {4, {struct, {name_conflict_test_types, 'TheEdgeCase'}}},
+ {5, {struct, {name_conflict_test_types, 'Tricky_'}}},
+ {6, {struct, {name_conflict_test_types, 'Nested'}}}
+ ]},
+ name_conflict_test_types:struct_info('Nested')
+ )},
+ {"Problem_ definition", ?_assertEqual(
+ {struct, [{1, bool},{2, bool}]},
+ name_conflict_test_types:struct_info('Problem_')
+ )},
+ {"using extended definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, double, single, undefined},
+ {2, undefined, double, integer, undefined}
+ ]},
+ name_conflict_test_types:struct_info_ext(using)
+ )},
+ {"delegate extended definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, string, partial, undefined},
+ {2, undefined, {struct, {name_conflict_test_types, delegate}}, delegate, undefined}
+ ]},
+ name_conflict_test_types:struct_info_ext(delegate)
+ )},
+ {"get extended definition", ?_assertEqual(
+ {struct, [{1, undefined, bool, sbyte, undefined}]},
+ name_conflict_test_types:struct_info_ext(get)
+ )},
+ {"partial extended definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, {struct, {name_conflict_test_types, using}}, using, #using{}},
+ {2, undefined, bool, read, undefined},
+ {3, undefined, bool, write, undefined}
+ ]},
+ name_conflict_test_types:struct_info_ext(partial)
+ )},
+ {"ClassAndProp extended definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, bool, 'ClassAndProp', undefined},
+ {2, undefined, bool, 'ClassAndProp_', undefined},
+ {3, undefined, bool, 'ClassAndProp__', undefined},
+ {4, undefined, bool, 'ClassAndProper', undefined}
+ ]},
+ name_conflict_test_types:struct_info_ext('ClassAndProp')
+ )},
+ {"second_chance extended definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, bool, 'SECOND_CHANCE', undefined},
+ {2, undefined, bool, 'SECOND_CHANCE_', undefined},
+ {3, undefined, bool, 'SECOND_CHANCE__', undefined},
+ {4, undefined, bool, 'SECOND_CHANCES', undefined}
+ ]},
+ name_conflict_test_types:struct_info_ext(second_chance)
+ )},
+ {"NOW_EAT_THIS extended definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, bool, now_eat_this, undefined},
+ {2, undefined, bool, now_eat_this_, undefined},
+ {3, undefined, bool, now_eat_this__, undefined},
+ {4, undefined, bool, now_eat_this_and_this, undefined}
+ ]},
+ name_conflict_test_types:struct_info_ext('NOW_EAT_THIS')
+ )},
+ {"TheEdgeCase extended definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, bool, theEdgeCase, undefined},
+ {2, undefined, bool, theEdgeCase_, undefined},
+ {3, undefined, bool, theEdgeCase__, undefined},
+ {4, undefined, bool, 'TheEdgeCase', undefined},
+ {5, undefined, bool, 'TheEdgeCase_', undefined},
+ {6, undefined, bool, 'TheEdgeCase__', undefined}
+ ]},
+ name_conflict_test_types:struct_info_ext('TheEdgeCase')
+ )},
+ {"Tricky_ extended definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, bool, tricky, undefined},
+ {2, undefined, bool, 'Tricky', undefined}
+ ]},
+ name_conflict_test_types:struct_info_ext('Tricky_')
+ )},
+ {"Nested extended definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, {struct, {
+ name_conflict_test_types,
+ 'ClassAndProp'
+ }}, 'ClassAndProp', #'ClassAndProp'{}},
+ {2, undefined, {struct, {
+ name_conflict_test_types,
+ second_chance
+ }}, second_chance, #second_chance{}},
+ {3, undefined, {struct, {
+ name_conflict_test_types,
+ 'NOW_EAT_THIS'
+ }}, 'NOW_EAT_THIS', #'NOW_EAT_THIS'{}},
+ {4, undefined, {struct, {
+ name_conflict_test_types,
+ 'TheEdgeCase'
+ }}, 'TheEdgeCase', #'TheEdgeCase'{}},
+ {5, undefined, {struct, {
+ name_conflict_test_types,
+ 'Tricky_'
+ }}, 'Tricky_', #'Tricky_'{}},
+ {6, undefined, {struct, {
+ name_conflict_test_types,
+ 'Nested'
+ }}, 'Nested', undefined}
+ ]},
+ name_conflict_test_types:struct_info_ext('Nested')
+ )},
+ {"Problem_ extended definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, bool, problem, undefined},
+ {2, undefined, bool, 'Problem', undefined}
+ ]},
+ name_conflict_test_types:struct_info_ext('Problem_')
+ )}
+ ].
+
+service_info_test_() ->
+ [
+ {"event params", ?_assertEqual(
+ {struct, [{1, {struct, {name_conflict_test_types, partial}}}]},
+ extern_thrift:function_info(event, params_type)
+ )},
+ {"event reply", ?_assertEqual(
+ {struct, {name_conflict_test_types, delegate}},
+ extern_thrift:function_info(event, reply_type)
+ )},
+ {"event exceptions", ?_assertEqual(
+ {struct, []},
+ extern_thrift:function_info(event, exceptions)
+ )},
+ {"Foo params", ?_assertEqual(
+ {struct, [{1, {struct, {name_conflict_test_types, 'Nested'}}}]},
+ extern_thrift:function_info('Foo', params_type)
+ )},
+ {"Foo reply", ?_assertEqual(
+ {struct, []},
+ extern_thrift:function_info('Foo', reply_type)
+ )},
+ {"Foo exceptions", ?_assertEqual(
+ {struct, [{1, {struct, {name_conflict_test_types, 'Problem_'}}}]},
+ extern_thrift:function_info('Foo', exceptions)
+ )}
+ ].
diff --git a/src/jaegertracing/thrift/lib/erl/test/stress_server.erl b/src/jaegertracing/thrift/lib/erl/test/stress_server.erl
new file mode 100644
index 000000000..35fff0693
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/stress_server.erl
@@ -0,0 +1,64 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(stress_server).
+
+
+-export([start_link/1,
+
+ handle_function/2,
+
+ echoVoid/0,
+ echoByte/1,
+ echoI32/1,
+ echoI64/1,
+ echoString/1,
+ echoList/1,
+ echoSet/1,
+ echoMap/1
+ ]).
+
+start_link(Port) ->
+ thrift_server:start_link(Port, service_thrift, ?MODULE).
+
+
+handle_function(Function, Args) ->
+ case apply(?MODULE, Function, tuple_to_list(Args)) of
+ ok ->
+ ok;
+ Else -> {reply, Else}
+ end.
+
+
+echoVoid() ->
+ ok.
+echoByte(X) ->
+ X.
+echoI32(X) ->
+ X.
+echoI64(X) ->
+ X.
+echoString(X) ->
+ X.
+echoList(X) ->
+ X.
+echoSet(X) ->
+ X.
+echoMap(X) ->
+ X.
diff --git a/src/jaegertracing/thrift/lib/erl/test/test_const.erl b/src/jaegertracing/thrift/lib/erl/test/test_const.erl
new file mode 100644
index 000000000..627777baa
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/test_const.erl
@@ -0,0 +1,54 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(test_const).
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+-include("gen-erl/constants_demo_types.hrl").
+
+namespace_test() ->
+ %% Verify that records produced by ConstantsDemo.thrift have the right namespace.
+ io:format(user, "in namespace_test()\n", []),
+ {struct, _} = constants_demo_types:struct_info('consts_thing'),
+ {struct, _} = constants_demo_types:struct_info('consts_Blah'),
+ ok.
+
+const_map_test() ->
+ ?assertEqual(233, constants_demo_constants:gen_map(35532)),
+ ?assertError(function_clause, constants_demo_constants:gen_map(0)),
+
+ ?assertEqual(853, constants_demo_constants:gen_map(43523, default)),
+ ?assertEqual(default, constants_demo_constants:gen_map(10110, default)),
+
+ ?assertEqual(98325, constants_demo_constants:gen_map2("lkjsdf")),
+ ?assertError(function_clause, constants_demo_constants:gen_map2("nonexist")),
+
+ ?assertEqual(233, constants_demo_constants:gen_map2("hello", 321)),
+ ?assertEqual(321, constants_demo_constants:gen_map2("goodbye", 321)).
+
+const_list_test() ->
+ ?assertEqual(23598352, constants_demo_constants:gen_list(2)),
+ ?assertError(function_clause, constants_demo_constants:gen_list(0)),
+
+ ?assertEqual(3253523, constants_demo_constants:gen_list(3, default)),
+ ?assertEqual(default, constants_demo_constants:gen_list(10, default)).
+
+-endif. %% TEST
diff --git a/src/jaegertracing/thrift/lib/erl/test/test_disklog.erl b/src/jaegertracing/thrift/lib/erl/test/test_disklog.erl
new file mode 100644
index 000000000..dcb6fc1b9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/test_disklog.erl
@@ -0,0 +1,99 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(test_disklog).
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+disklog_test() ->
+ {ok, TransportFactory} =
+ thrift_disk_log_transport:new_transport_factory(
+ test_disklog,
+ [{file, "./test_log"},
+ {size, {1024*1024, 10}}]),
+ {ok, ProtocolFactory} =
+ thrift_binary_protocol:new_protocol_factory( TransportFactory, []),
+ {ok, Proto} = ProtocolFactory(),
+ {ok, Client0} = thrift_client:new(Proto, thrift_test_thrift),
+
+ io:format("Client started~n"),
+
+ % We have to make oneway calls into this client only since otherwise it
+ % will try to read from the disklog and go boom.
+ {Client1, {ok, ok}} = thrift_client:call(Client0, testOneway, [16#deadbeef]),
+ io:format("Call written~n"),
+
+ % Use the send_call method to write a non-oneway call into the log
+ {Client2, ok} =
+ thrift_client:send_call(Client1, testString, [<<"hello world">>]),
+ io:format("Non-oneway call sent~n"),
+
+ {_Client3, ok} = thrift_client:close(Client2),
+ io:format("Client closed~n"),
+
+ lists:foreach(fun(File) -> file:delete(File) end, [
+ "./test_log.1",
+ "./test_log.idx",
+ "./test_log.siz"
+ ]),
+ io:format("Cleaning up test files~n"),
+
+ ok.
+
+disklog_base64_test() ->
+ {ok, TransportFactory} =
+ thrift_disk_log_transport:new_transport_factory(
+ test_disklog,
+ [{file, "./test_b64_log"},
+ {size, {1024*1024, 10}}]),
+ {ok, B64Factory} =
+ thrift_base64_transport:new_transport_factory(TransportFactory),
+ {ok, BufFactory} =
+ thrift_buffered_transport:new_transport_factory(B64Factory),
+ {ok, ProtocolFactory} =
+ thrift_binary_protocol:new_protocol_factory(BufFactory, []),
+ {ok, Proto} = ProtocolFactory(),
+ {ok, Client0} = thrift_client:new(Proto, thrift_test_thrift),
+
+ io:format("Client started~n"),
+
+ % We have to make oneway calls into this client only since otherwise
+ % it will try to read from the disklog and go boom.
+ {Client1, {ok, ok}} = thrift_client:call(Client0, testOneway, [16#deadbeef]),
+ io:format("Call written~n"),
+
+ % Use the send_call method to write a non-oneway call into the log
+ {Client2, ok} =
+ thrift_client:send_call(Client1, testString, [<<"hello world">>]),
+ io:format("Non-oneway call sent~n"),
+
+ {_Client3, ok} = thrift_client:close(Client2),
+ io:format("Client closed~n"),
+
+ lists:foreach(fun(File) -> file:delete(File) end, [
+ "./test_b64_log.1",
+ "./test_b64_log.idx",
+ "./test_b64_log.siz"
+ ]),
+ io:format("Cleaning up test files~n"),
+
+ ok.
+
+-endif.
diff --git a/src/jaegertracing/thrift/lib/erl/test/test_omit.erl b/src/jaegertracing/thrift/lib/erl/test/test_omit.erl
new file mode 100644
index 000000000..80841e291
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/test_omit.erl
@@ -0,0 +1,79 @@
+-module(test_omit).
+
+-include("gen-erl/thrift_omit_with_types.hrl").
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+omit_struct1_test() ->
+ %% In this test, the field that is deleted is a basic type (an i32).
+ A = #test1{one = 1, three = 3},
+ B = #test1{one = 1, two = 2, three = 3},
+ {ok, Transport} = thrift_membuffer_transport:new(),
+ {ok, P0} = thrift_binary_protocol:new(Transport),
+
+ {P1, ok} = thrift_protocol:write(P0, {{struct, {thrift_omit_with_types, element(1, A)}}, A}),
+ {P2, {ok, O0}} = thrift_protocol:read(P1, {struct, {thrift_omit_without_types, element(1, A)}}),
+ ?assertEqual(element(1, A), element(1, O0)),
+ ?assertEqual(element(2, A), element(2, O0)),
+ ?assertEqual(element(4, A), element(3, O0)),
+
+ {P3, ok} = thrift_protocol:write(P2, {{struct, {thrift_omit_with_types, element(1, B)}}, B}),
+ {_P4, {ok, O1}} = thrift_protocol:read(P3, {struct, {thrift_omit_without_types, element(1, A)}}),
+ ?assertEqual(element(1, A), element(1, O1)),
+ ?assertEqual(element(2, A), element(2, O1)),
+ ?assertEqual(element(4, A), element(3, O1)),
+
+ ok.
+
+omit_struct2_test() ->
+ %% In this test, the field that is deleted is a struct.
+ A = #test2{one = 1, two = #test2{one = 10, three = 30}, three = 3},
+ B = #test2{one = 1, two = #test2{one = 10, two = #test2{one = 100}, three = 30}, three = 3},
+
+ {ok, Transport} = thrift_membuffer_transport:new(),
+ {ok, P0} = thrift_binary_protocol:new(Transport),
+
+ {P1, ok} = thrift_protocol:write(P0, {{struct, {thrift_omit_with_types, element(1, A)}}, A}),
+ {P2, {ok, O0}} = thrift_protocol:read(P1, {struct, {thrift_omit_without_types, element(1, A)}}),
+ ?assertEqual(element(1, A), element(1, O0)),
+ ?assertEqual(element(2, A), element(2, O0)),
+ ?assertEqual(element(4, A), element(3, O0)),
+
+ {P3, ok} = thrift_protocol:write(P2, {{struct, {thrift_omit_with_types, element(1, B)}}, B}),
+ {_P4, {ok, O1}} = thrift_protocol:read(P3, {struct, {thrift_omit_without_types, element(1, A)}}),
+ ?assertEqual(element(1, A), element(1, O1)),
+ ?assertEqual(element(2, A), element(2, O1)),
+ ?assertEqual(element(4, A), element(3, O1)),
+
+ ok.
+
+omit_list_test() ->
+ %% In this test, the field that is deleted is a list.
+ A = #test1{one = 1, two = 2, three = 3},
+ B = #test3{one = 1, two = [ A ]},
+
+ {ok, Transport} = thrift_membuffer_transport:new(),
+ {ok, P0} = thrift_binary_protocol:new(Transport),
+
+ {P1, ok} = thrift_protocol:write(P0, {{struct, {thrift_omit_with_types, element(1, B)}}, B}),
+ {_P2, {ok, O0}} = thrift_protocol:read(P1, {struct, {thrift_omit_without_types, element(1, B)}}),
+ ?assertEqual(element(2, B), element(2, O0)),
+
+ ok.
+
+omit_map_test() ->
+ %% In this test, the field that is deleted is a map.
+ A = #test1{one = 1, two = 2, three = 3},
+ B = #test4{one = 1, two = dict:from_list([ {2, A} ])},
+
+ {ok, Transport} = thrift_membuffer_transport:new(),
+ {ok, P0} = thrift_binary_protocol:new(Transport),
+
+ {P1, ok} = thrift_protocol:write(P0, {{struct, {thrift_omit_with_types, element(1, B)}}, B}),
+ {_P2, {ok, O0}} = thrift_protocol:read(P1, {struct, {thrift_omit_without_types, element(1, B)}}),
+ ?assertEqual(element(2, B), element(2, O0)),
+
+ ok.
+
+-endif. %% TEST
diff --git a/src/jaegertracing/thrift/lib/erl/test/test_rendered_double_constants.erl b/src/jaegertracing/thrift/lib/erl/test/test_rendered_double_constants.erl
new file mode 100644
index 000000000..87fce8130
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/test_rendered_double_constants.erl
@@ -0,0 +1,68 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(test_rendered_double_constants).
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+-include("gen-erl/double_constants_test_constants.hrl").
+
+-define(EPSILON, 0.0000001).
+
+rendered_double_constants_test() ->
+ ?assert(abs(1.0 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST) =< ?EPSILON),
+ ?assert(abs(-100.0 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST) =< ?EPSILON),
+ ?assert(abs(9223372036854775807.0 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST) =< ?EPSILON),
+ ?assert(abs(-9223372036854775807.0 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST) =< ?EPSILON),
+ ?assert(abs(3.14159265359 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST) =< ?EPSILON),
+ ?assert(abs(1000000.1 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST) =< ?EPSILON),
+ ?assert(abs(-1000000.1 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST) =< ?EPSILON),
+ ?assert(abs(1.7e+308 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST) =< ?EPSILON),
+ ?assert(abs(9223372036854775816.43 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST) =< ?EPSILON),
+ ?assert(abs(-1.7e+308 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST) =< ?EPSILON),
+ ?assert(abs(-9223372036854775816.43 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST) =< ?EPSILON),
+ ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST)),
+ ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST)),
+ ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST)),
+ ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST)),
+ ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST)),
+ ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST)),
+ ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST)),
+ ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST)),
+ ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST)),
+ ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST)),
+ ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST)).
+
+rendered_double_list_test() ->
+ ?assertEqual(12, length(?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)),
+ ?assert(abs(1.0 - lists:nth(1, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON),
+ ?assert(abs(-100.0 - lists:nth(2, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON),
+ ?assert(abs(100.0 - lists:nth(3, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON),
+ ?assert(abs(9223372036854775807.0 - lists:nth(4, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON),
+ ?assert(abs(-9223372036854775807.0 - lists:nth(5, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON),
+ ?assert(abs(3.14159265359 - lists:nth(6, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON),
+ ?assert(abs(1000000.1 - lists:nth(7, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON),
+ ?assert(abs(-1000000.1 - lists:nth(8, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON),
+ ?assert(abs(1.7e+308 - lists:nth(9, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON),
+ ?assert(abs(-1.7e+308 - lists:nth(10, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON),
+ ?assert(abs(9223372036854775816.43 - lists:nth(11, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON),
+ ?assert(abs(-9223372036854775816.43 - lists:nth(12, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON).
+
+-endif. %% TEST \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/erl/test/test_thrift_1151.erl b/src/jaegertracing/thrift/lib/erl/test/test_thrift_1151.erl
new file mode 100644
index 000000000..f4a910e7d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/test_thrift_1151.erl
@@ -0,0 +1,34 @@
+-module(test_thrift_1151).
+
+-include("gen-erl/thrift1151_types.hrl").
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+unmatched_struct_test() ->
+ S1 = #'StructC'{x=#'StructB'{x=1}},
+ {ok, Transport} = thrift_memory_buffer:new(),
+ {ok, Protocol} = thrift_binary_protocol:new(Transport),
+ ?assertException(
+ error,
+ struct_unmatched,
+ thrift_protocol:write(
+ Protocol,
+ {{struct, element(2, thrift1151_types:struct_info('StructC'))}, S1}
+ )
+ ).
+
+badarg_test() ->
+ S2 = #'StructC'{x=#'StructA'{x="1"}},
+ {ok, Transport} = thrift_memory_buffer:new(),
+ {ok, Protocol} = thrift_binary_protocol:new(Transport),
+ ?assertException(
+ error,
+ badarg,
+ thrift_protocol:write(
+ Protocol,
+ {{struct, element(2, thrift1151_types:struct_info('StructC'))}, S2}
+ )
+ ).
+
+-endif.
diff --git a/src/jaegertracing/thrift/lib/erl/test/test_thrift_3214.erl b/src/jaegertracing/thrift/lib/erl/test/test_thrift_3214.erl
new file mode 100644
index 000000000..0f9544bef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/test_thrift_3214.erl
@@ -0,0 +1,60 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(test_thrift_3214).
+-compile(export_all).
+
+-include("gen-erl/thrift3214_types.hrl").
+
+-ifdef(TEST).
+-ifndef(otp16_or_less).
+-include_lib("eunit/include/eunit.hrl").
+
+record_generation_test_() ->
+ [
+ {"StringMap record", ?_assertMatch(
+ {'StringMap', _},
+ #'StringMap'{data=#{50 => "foo"}}
+ )},
+ {"StringMap record defaults", ?_assertEqual(
+ {'StringMap', #{1 => "a", 2 => "b"}},
+ #'StringMap'{}
+ )},
+ {"StringMap record dict from list", ?_assertNotEqual(
+ {'StringMap', dict:from_list([{1, "a"}, {2, "b"}])},
+ #'StringMap'{}
+ )},
+ {"StringMap record map from list", ?_assertEqual(
+ {'StringMap', maps:from_list([{1, "a"}, {2, "b"}])},
+ #'StringMap'{}
+ )}
+ ].
+
+struct_info_test_() ->
+ [
+ {"StringMap extended definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, {map, i32, string}, 'data', #{1 => "a", 2 => "b"}}
+ ]},
+ thrift3214_types:struct_info_ext('StringMap')
+ )}
+ ].
+
+-endif.
+-endif.
diff --git a/src/jaegertracing/thrift/lib/erl/test/test_thrift_buffered_transport.erl b/src/jaegertracing/thrift/lib/erl/test/test_thrift_buffered_transport.erl
new file mode 100644
index 000000000..8519e82a1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/test_thrift_buffered_transport.erl
@@ -0,0 +1,359 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(test_thrift_buffered_transport).
+-include_lib("eunit/include/eunit.hrl").
+
+
+new(Transport) -> thrift_buffered_transport:new(Transport).
+
+new_test_() ->
+ [
+ {"new buffered membuffer", ?_assertMatch(
+ {ok, {t_transport, thrift_buffered_transport, {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, []}},
+ []
+ }}},
+ new({t_transport, thrift_membuffer_transport, {t_membuffer, []}})
+ )}
+ ].
+
+
+read(Frame, Bytes) -> thrift_buffered_transport:read(Frame, Bytes).
+
+read_test_() ->
+ [
+ {"read zero bytes from an empty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ {ok, <<>>}
+ },
+ read(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ 0
+ )
+ )},
+ {"read 1 byte from an empty buffered membuffer", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ 1
+ )
+ )},
+ {"read zero bytes from nonempty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<"hallo world">>
+ }},
+ []
+ },
+ {ok, <<>>}
+ },
+ read(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<"hallo world">>
+ }},
+ []
+ },
+ 0
+ )
+ )},
+ {"read 1 byte from nonempty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"allo world">>}},
+ []
+ },
+ {ok, <<"h">>}
+ },
+ read(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}},
+ []
+ },
+ 1
+ )
+ )},
+ {"read 1 byte from nonempty buffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"allo world">>}},
+ []
+ },
+ {ok, <<"h">>}
+ },
+ read(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}},
+ []
+ },
+ 1
+ )
+ )},
+ {"read a zillion bytes from nonempty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ {ok, <<"hallo world">>}
+ },
+ read(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}},
+ []
+ },
+ 65536
+ )
+ )}
+ ].
+
+
+read_exact(Frame, Bytes) -> thrift_buffered_transport:read_exact(Frame, Bytes).
+
+read_exact_test_() ->
+ [
+ {"read exactly zero bytes from an empty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ {ok, <<>>}
+ },
+ read_exact(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ 0
+ )
+ )},
+ {"read exactly 1 byte from an empty buffered membuffer", ?_assertMatch(
+ {_, {error, eof}},
+ read_exact(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ 1
+ )
+ )},
+ {"read exactly zero bytes from nonempty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}},
+ []
+ },
+ {ok, <<>>}
+ },
+ read_exact(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}},
+ []
+ },
+ 0
+ )
+ )},
+ {"read exactly 1 byte from nonempty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"allo world">>}},
+ []
+ },
+ {ok, <<"h">>}
+ },
+ read_exact(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<"hallo world">>
+ }},
+ []
+ },
+ 1
+ )
+ )},
+ {"read exactly 1 byte from nonempty buffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"allo world">>}},
+ []
+ },
+ {ok, <<"h">>}
+ },
+ read_exact(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}},
+ []
+ },
+ 1
+ )
+ )},
+ {"read exactly a zillion bytes from nonempty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}},
+ []
+ },
+ {error, eof}
+ },
+ read_exact(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<"hallo world">>
+ }},
+ []
+ },
+ 65536
+ )
+ )}
+ ].
+
+
+write(Framed, Data) -> thrift_buffered_transport:write(Framed, Data).
+
+write_test_() ->
+ [
+ {"write empty list to empty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [[], []]
+ },
+ ok
+ },
+ write(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ []
+ )
+ )},
+ {"write empty list to nonempty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [["hallo world"], []]
+ },
+ ok
+ },
+ write(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ ["hallo world"]
+ },
+ []
+ )
+ )},
+ {"write empty binary to empty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [[], <<>>]
+ },
+ ok
+ },
+ write(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ <<>>
+ )
+ )},
+ {"write empty binary to nonempty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [["hallo world"], <<>>]
+ },
+ ok
+ },
+ write(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ ["hallo world"]
+ },
+ <<>>
+ )
+ )}
+ ].
+
+
+flush(Transport) -> thrift_buffered_transport:flush(Transport).
+
+flush_test_() ->
+ [
+ {"flush empty buffered membuffer", ?_assertMatch(
+ {{t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ ok
+ },
+ flush({t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ })
+ )},
+ {"flush nonempty buffered membuffer", ?_assertMatch(
+ {{t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ [<<>>, <<"hallo world">>]
+ }},
+ []
+ },
+ ok
+ },
+ flush({t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<"hallo world">>
+ })
+ )}
+ ].
+
+
+close(Transport) -> thrift_buffered_transport:close(Transport).
+
+close_test_() ->
+ {"close buffered membuffer", ?_assertMatch(
+ {{t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ ok
+ },
+ close({t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ })
+ )}.
+
diff --git a/src/jaegertracing/thrift/lib/erl/test/test_thrift_compact_protocol.erl b/src/jaegertracing/thrift/lib/erl/test/test_thrift_compact_protocol.erl
new file mode 100644
index 000000000..5da78c612
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/test_thrift_compact_protocol.erl
@@ -0,0 +1,219 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(test_thrift_compact_protocol).
+-include_lib("eunit/include/eunit.hrl").
+-include("thrift_constants.hrl").
+-include("thrift_protocol.hrl").
+
+
+new(Transport) -> thrift_compact_protocol:new(Transport).
+new() ->
+ {ok, Transport} = thrift_membuffer_transport:new(),
+ thrift_compact_protocol:new(Transport).
+
+new_test() ->
+ new(thrift_membuffer_transport:new()).
+
+write(This, Value) -> thrift_protocol:write(This, Value).
+read(This, Type) -> thrift_protocol:read(This, Type).
+
+str(This0, Value0) ->
+ {This1, ok} = write(This0, {string, Value0}),
+ {This2, {ok, Value1}} = read(This1, string),
+ ?assertEqual(Value0, binary_to_list(Value1)),
+ {This2, ok}.
+string_test() ->
+ {ok, This0} = new(),
+ {This1, ok} = str(This0, "aaa"),
+ {This2, ok} = str(This1, ""),
+ {This2, ok}.
+
+round_trip(This0, Type, Value0) ->
+ {This1, ok} = write(This0, {Type, Value0}),
+ {This2, {ok, Value1}} = read(This1, Type),
+ ?assertEqual(Value0, Value1),
+ {This2, ok}.
+
+bool_test() ->
+ {ok, This0} = new(),
+ {This1, ok} = round_trip(This0, bool, true),
+ {This2, ok} = round_trip(This1, bool, false),
+ {This2, ok}.
+
+byte(This0, Value0) -> round_trip(This0, byte, Value0).
+byte_test() ->
+ {ok, This0} = new(),
+ {This1, ok} = byte(This0, 0),
+ {This2, ok} = byte(This1, 42),
+ {This3, ok} = byte(This2, -1),
+ {This4, ok} = byte(This3, -128),
+ {This4, ok}.
+
+i16(This0, Value0) -> round_trip(This0, i16, Value0).
+i16_test() ->
+ {ok, This0} = new(),
+ {This1, ok} = i16(This0, 0),
+ {This2, ok} = i16(This1, 42),
+ {This3, ok} = i16(This2, 30000),
+ {This4, ok} = i16(This3, -1),
+ {This5, ok} = i16(This4, -128),
+ {This6, ok} = i16(This5, -30000),
+ {This6, ok}.
+
+i32(This0, Value0) -> round_trip(This0, i32, Value0).
+i32_test() ->
+ {ok, This0} = new(),
+ {This1, ok} = i32(This0, 0),
+ {This2, ok} = i32(This1, 42),
+ {This3, ok} = i32(This2, 30000),
+ {This4, ok} = i32(This3, 2000000002),
+ {This5, ok} = i32(This4, -1),
+ {This6, ok} = i32(This5, -128),
+ {This7, ok} = i32(This6, -30000),
+ {This8, ok} = i32(This7, -2000000002),
+ {This8, ok}.
+
+i64(This0, Value0) -> round_trip(This0, i64, Value0).
+i64_test() ->
+ {ok, This0} = new(),
+ {This1, ok} = i64(This0, 0),
+ {This2, ok} = i64(This1, 42),
+ {This3, ok} = i64(This2, 30000),
+ {This4, ok} = i64(This3, 2000000002),
+ {This5, ok} = i64(This4, 100000000000000064),
+ {This6, ok} = i64(This5, -1),
+ {This7, ok} = i64(This6, -128),
+ {This8, ok} = i64(This7, -30000),
+ {This9, ok} = i64(This8, -2000000002),
+ {This10, ok} = i64(This9, -100000000000000064),
+ {This10, ok}.
+
+struct_test() ->
+ {ok, P0} = new(),
+ {P1, ok} = write(P0, #protocol_message_begin{ name = "Message1", type = ?tType_I8, seqid = 3}),
+ {P2, ok} = write(P1, #protocol_struct_begin{}),
+ {P3, ok} = write(P2, #protocol_field_begin{ name = "field1", type = ?tType_I8, id = 1}),
+ {P4, ok} = write(P3, {byte, 42}),
+ {P5, ok} = write(P4, field_end),
+ {P6, ok} = write(P5, #protocol_field_begin{ name = "field2", type = ?tType_I8, id = 14}),
+ {P7, ok} = write(P6, {byte, 3}),
+ {P8, ok} = write(P7, field_end),
+ {P9, ok} = write(P8, #protocol_field_begin{ name = "field3", type = ?tType_I8, id = 42}),
+ {P10, ok} = write(P9, {byte, 8}),
+ {P11, ok} = write(P10, field_end),
+ {P12, ok} = write(P11, field_stop),
+ {P13, ok} = write(P12, struct_end),
+ {P14, ok} = write(P13, message_end),
+
+ {P15, #protocol_message_begin{ name = "Message1", type = ?tType_I8, seqid = 3}} = read(P14, message_begin),
+ {P16, ok} = read(P15, struct_begin),
+ {P17, #protocol_field_begin{ type = ?tType_I8, id = 1 }} = read(P16, field_begin),
+ {P18, {ok, 42}} = read(P17, byte),
+ {P19, ok} = read(P18, field_end),
+ {P20, #protocol_field_begin{ type = ?tType_I8, id = 14 }} = read(P19, field_begin),
+ {P21, {ok, 3}} = read(P20, byte),
+ {P22, ok} = read(P21, field_end),
+ {P23, #protocol_field_begin{ type = ?tType_I8, id = 42 }} = read(P22, field_begin),
+ {P24, {ok, 8}} = read(P23, byte),
+ {P25, ok} = read(P24, field_end),
+ {P26, #protocol_field_begin{ type = ?tType_STOP}} = read(P25, field_begin),
+ {P27, ok} = read(P26, struct_end),
+ {P28, ok} = read(P27, message_end),
+ {P28, ok}.
+
+bool_field_test() ->
+ {ok, P0} = new(),
+ {P1, ok} = write(P0, #protocol_message_begin{ name = "Message1", type = ?tType_I8, seqid = 3}),
+ {P2, ok} = write(P1, #protocol_struct_begin{}),
+ {P3, ok} = write(P2, #protocol_field_begin{ name = "field1", type = ?tType_BOOL, id = 1}),
+ {P4, ok} = write(P3, {bool, true}),
+ {P5, ok} = write(P4, field_end),
+ {P6, ok} = write(P5, #protocol_field_begin{ name = "field2", type = ?tType_BOOL, id = 14}),
+ {P7, ok} = write(P6, {bool, false}),
+ {P8, ok} = write(P7, field_end),
+ {P9, ok} = write(P8, #protocol_field_begin{ name = "field3", type = ?tType_BOOL, id = 42}),
+ {P10, ok} = write(P9, {bool, true}),
+ {P11, ok} = write(P10, field_end),
+ {P12, ok} = write(P11, field_stop),
+ {P13, ok} = write(P12, struct_end),
+ {P14, ok} = write(P13, message_end),
+
+ {P15, #protocol_message_begin{ name = "Message1", type = ?tType_I8, seqid = 3}} = read(P14, message_begin),
+ {P16, ok} = read(P15, struct_begin),
+ {P17, #protocol_field_begin{ type = ?tType_BOOL, id = 1 }} = read(P16, field_begin),
+ {P18, {ok, true}} = read(P17, bool),
+ {P19, ok} = read(P18, field_end),
+ {P20, #protocol_field_begin{ type = ?tType_BOOL, id = 14 }} = read(P19, field_begin),
+ {P21, {ok, false}} = read(P20, bool),
+ {P22, ok} = read(P21, field_end),
+ {P23, #protocol_field_begin{ type = ?tType_BOOL, id = 42 }} = read(P22, field_begin),
+ {P24, {ok, true}} = read(P23, bool),
+ {P25, ok} = read(P24, field_end),
+ {P26, #protocol_field_begin{ type = ?tType_STOP}} = read(P25, field_begin),
+ {P27, ok} = read(P26, struct_end),
+ {P28, ok} = read(P27, message_end),
+ {P28, ok}.
+
+nesting_test() ->
+ {ok, P0} = new(),
+ {P1, ok} = write(P0, #protocol_message_begin{ name = "Message1", type = ?tType_I8, seqid = 3}),
+ {P2, ok} = write(P1, #protocol_struct_begin{}),
+ {P3, ok} = write(P2, #protocol_field_begin{ name = "field1", type = ?tType_BOOL, id = 14}),
+ {P4, ok} = write(P3, {bool, true}),
+ {P5, ok} = write(P4, field_end),
+
+ {P6, ok} = write(P5, #protocol_field_begin{ name = "field2", type = ?tType_STRUCT, id = 28}),
+ {P7, ok} = write(P6, #protocol_struct_begin{}),
+ {P8, ok} = write(P7, #protocol_field_begin{ name = "field2_1", type = ?tType_BOOL, id = 30000}),
+ {P9, ok} = write(P8, {bool, false}),
+ {P10, ok} = write(P9, field_end),
+ {P11, ok} = write(P10, field_stop),
+ {P12, ok} = write(P11, struct_end),
+ {P13, ok} = write(P12, field_end),
+
+ {P14, ok} = write(P13, #protocol_field_begin{ name = "field3", type = ?tType_BOOL, id = 42}),
+ {P15, ok} = write(P14, {bool, true}),
+ {P16, ok} = write(P15, field_end),
+ {P17, ok} = write(P16, field_stop),
+ {P18, ok} = write(P17, struct_end),
+ {P19, ok} = write(P18, message_end),
+
+ {P20, #protocol_message_begin{ name = "Message1", type = ?tType_I8, seqid = 3}} = read(P19, message_begin),
+ {P21, ok} = read(P20, struct_begin),
+ {P22, #protocol_field_begin{ type = ?tType_BOOL, id = 14 }} = read(P21, field_begin),
+ {P23, {ok, true}} = read(P22, bool),
+ {P24, ok} = read(P23, field_end),
+
+ {P25, #protocol_field_begin{ type = ?tType_STRUCT, id = 28 }} = read(P24, field_begin),
+ {P26, ok} = read(P25, struct_begin),
+ {P27, #protocol_field_begin{ type = ?tType_BOOL, id = 30000 }} = read(P26, field_begin),
+ {P28, {ok, false}} = read(P27, bool),
+ {P29, ok} = read(P28, field_end),
+ {P30, #protocol_field_begin{ type = ?tType_STOP }} = read(P29, field_begin),
+ {P31, ok} = read(P30, struct_end),
+ {P32, ok} = read(P31, field_end),
+
+ {P33, #protocol_field_begin{ type = ?tType_BOOL, id = 42 }} = read(P32, field_begin),
+ {P34, {ok, true}} = read(P33, bool),
+ {P35, ok} = read(P34, field_end),
+ {P36, #protocol_field_begin{ type = ?tType_STOP }} = read(P35, field_begin),
+ {P37, ok} = read(P36, struct_end),
+ {P38, ok} = read(P37, message_end),
+ {P38, ok}.
diff --git a/src/jaegertracing/thrift/lib/erl/test/test_thrift_file_transport.erl b/src/jaegertracing/thrift/lib/erl/test/test_thrift_file_transport.erl
new file mode 100644
index 000000000..3e5c1d1e4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/test_thrift_file_transport.erl
@@ -0,0 +1,213 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(test_thrift_file_transport).
+-include_lib("eunit/include/eunit.hrl").
+
+
+new(File) -> thrift_file_transport:new(File).
+new(File, Opts) -> thrift_file_transport:new(File, Opts).
+
+new_test_() ->
+ [
+ {"new file", ?_assertMatch(
+ {ok, {_, thrift_file_transport, {t_file, a_fake_file, true, write}}},
+ new(a_fake_file)
+ )},
+ {"new file in read mode", ?_assertMatch(
+ {ok, {_, thrift_file_transport, {t_file, a_fake_file, true, read}}},
+ new(a_fake_file, [{mode, read}])
+ )},
+ {"new file in write mode", ?_assertMatch(
+ {ok, {_, thrift_file_transport, {t_file, a_fake_file, true, write}}},
+ new(a_fake_file, [{mode, write}])
+ )},
+ {"new file in should_close true mode", ?_assertMatch(
+ {ok, {_, thrift_file_transport, {t_file, a_fake_file, true, write}}},
+ new(a_fake_file, [{should_close, true}])
+ )},
+ {"new file in should_close false mode", ?_assertMatch(
+ {ok, {_, thrift_file_transport, {t_file, a_fake_file, false, write}}},
+ new(a_fake_file, [{should_close, false}])
+ )}
+ ].
+
+
+read(File, Bytes) -> thrift_file_transport:read(File, Bytes).
+
+read_test_() ->
+ {setup,
+ fun() ->
+ meck:new(file, [unstick, passthrough]),
+ meck:expect(file, read, fun(Bin, N) ->
+ {Result, _} = split_binary(Bin, min(iolist_size(Bin), N)),
+ {ok, Result}
+ end)
+ end,
+ fun(_) -> meck:unload(file) end,
+ [
+ {"read zero bytes from empty file", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read({t_file, <<>>, true, read}, 0)
+ )},
+ {"read 1 byte from empty file", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read({t_file, <<>>, true, read}, 1)
+ )},
+ {"read zero bytes from nonempty file", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read({t_file, <<"hallo world">>, true, read}, 0)
+ )},
+ {"read 1 byte from nonempty file", ?_assertMatch(
+ {_, {ok, <<"h">>}},
+ read({t_file, <<"hallo world">>, true, read}, 1)
+ )},
+ {"read a zillion bytes from nonempty file", ?_assertMatch(
+ {_, {ok, <<"hallo world">>}},
+ read({t_file, <<"hallo world">>, true, read}, 65536)
+ )},
+ {"read 0 byte from file in write mode", ?_assertMatch(
+ {_, {error, write_mode}},
+ read({t_file, <<>>, true, write}, 0)
+ )},
+ {"read 1 byte from file in write mode", ?_assertMatch(
+ {_, {error, write_mode}},
+ read({t_file, <<>>, true, write}, 1)
+ )}
+ ]
+ }.
+
+
+read_exact(File, Bytes) -> thrift_file_transport:read_exact(File, Bytes).
+
+read_exact_test_() ->
+ {setup,
+ fun() ->
+ meck:new(file, [unstick, passthrough]),
+ meck:expect(file, read, fun(Bin, N) ->
+ {Result, _} = split_binary(Bin, min(iolist_size(Bin), N)),
+ {ok, Result}
+ end)
+ end,
+ fun(_) -> meck:unload(file) end,
+ [
+ {"read exactly zero bytes from empty file", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read_exact({t_file, <<>>, true, read}, 0)
+ )},
+ {"read exactly 1 byte from empty file", ?_assertMatch(
+ {_, {error, eof}},
+ read_exact({t_file, <<>>, true, read}, 1)
+ )},
+ {"read exactly zero bytes from nonempty file", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read_exact({t_file, <<"hallo world">>, true, read}, 0)
+ )},
+ {"read exactly 1 byte from nonempty file", ?_assertMatch(
+ {_, {ok, <<"h">>}},
+ read_exact({t_file, <<"hallo world">>, true, read}, 1)
+ )},
+ {"read exactly a zillion bytes from nonempty file", ?_assertMatch(
+ {_, {error, eof}},
+ read_exact({t_file, <<"hallo world">>, true, read}, 65536)
+ )},
+ {"read exactly 0 byte from file in write mode", ?_assertMatch(
+ {_, {error, write_mode}},
+ read_exact({t_file, <<>>, true, write}, 0)
+ )},
+ {"read exactly 1 byte from file in write mode", ?_assertMatch(
+ {_, {error, write_mode}},
+ read_exact({t_file, <<>>, true, write}, 1)
+ )}
+ ]
+ }.
+
+
+write(File, Data) -> thrift_file_transport:write(File, Data).
+
+write_test_() ->
+ {setup,
+ fun() ->
+ meck:new(file, [unstick, passthrough]),
+ meck:expect(file, write, fun(_, _) -> ok end)
+ end,
+ fun(_) -> meck:unload(file) end,
+ [
+ {"write empty list to file", ?_assertMatch(
+ {{t_file, a_fake_file, true, write}, ok},
+ write({t_file, a_fake_file, true, write}, [])
+ )},
+ {"write empty binary to file", ?_assertMatch(
+ {{t_file, a_fake_file, true, write}, ok},
+ write({t_file, a_fake_file, true, write}, <<>>)
+ )},
+ {"write a list to file", ?_assertMatch(
+ {{t_file, a_fake_file, true, write}, ok},
+ write({t_file, a_fake_file, true, write}, "hallo world")
+ )},
+ {"write a binary to file", ?_assertMatch(
+ {{t_file, a_fake_file, true, write}, ok},
+ write({t_file, a_fake_file, true, write}, <<"hallo world">>)
+ )},
+ {"write a binary to file in read mode", ?_assertMatch(
+ {_, {error, read_mode}},
+ write({t_file, a_fake_file, true, read}, <<"hallo world">>)
+ )},
+ {"write a list to file in read mode", ?_assertMatch(
+ {_, {error, read_mode}},
+ write({t_file, a_fake_file, true, read}, "hallo world")
+ )}
+ ]
+ }.
+
+
+flush(Transport) -> thrift_file_transport:flush(Transport).
+
+flush_test_() ->
+ {setup,
+ fun() ->
+ meck:new(file, [unstick, passthrough]),
+ meck:expect(file, sync, fun(_File) -> ok end)
+ end,
+ fun(_) -> meck:unload(file) end,
+ [
+ {"flush file", ?_assertMatch(
+ {{t_file, a_fake_file, true, write}, ok},
+ flush({t_file, a_fake_file, true, write})
+ )}
+ ]
+ }.
+
+
+close(Transport) -> thrift_file_transport:close(Transport).
+
+close_test_() ->
+ {setup,
+ fun() ->
+ meck:new(file, [unstick, passthrough]),
+ meck:expect(file, close, fun(_) -> ok end)
+ end,
+ fun(_) -> meck:unload(file) end,
+ [
+ {"close file", ?_assertMatch(
+ {{t_file, a_fake_file, true, write}, ok},
+ close({t_file, a_fake_file, true, write})
+ )}
+ ]
+ }. \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/erl/test/test_thrift_framed_transport.erl b/src/jaegertracing/thrift/lib/erl/test/test_thrift_framed_transport.erl
new file mode 100644
index 000000000..8a538a53a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/test_thrift_framed_transport.erl
@@ -0,0 +1,404 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(test_thrift_framed_transport).
+-include_lib("eunit/include/eunit.hrl").
+
+
+new(Transport) -> thrift_framed_transport:new(Transport).
+
+new_test_() ->
+ [
+ {"new framed membuffer", ?_assertMatch(
+ {ok, {t_transport, thrift_framed_transport, {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, []}},
+ [],
+ []
+ }}},
+ new({t_transport, thrift_membuffer_transport, {t_membuffer, []}})
+ )}
+ ].
+
+
+read(Frame, Bytes) -> thrift_framed_transport:read(Frame, Bytes).
+
+read_test_() ->
+ [
+ {"read zero bytes from an empty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ {ok, <<>>}
+ },
+ read(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ 0
+ )
+ )},
+ {"read 1 byte from an empty framed membuffer", ?_assertMatch(
+ {_, {error, eof}},
+ read(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ 1
+ )
+ )},
+ {"read zero bytes from nonempty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<0, 0, 0, 11, "hallo world">>
+ }},
+ [],
+ []
+ },
+ {ok, <<>>}
+ },
+ read(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<0, 0, 0, 11, "hallo world">>
+ }},
+ [],
+ []
+ },
+ 0
+ )
+ )},
+ {"read 1 byte from nonempty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<"allo world">>,
+ []
+ },
+ {ok, <<"h">>}
+ },
+ read(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<0, 0, 0, 11, "hallo world">>
+ }},
+ [],
+ []
+ },
+ 1
+ )
+ )},
+ {"read 1 byte from nonempty buffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<"allo world">>,
+ []
+ },
+ {ok, <<"h">>}
+ },
+ read(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<"hallo world">>,
+ []
+ },
+ 1
+ )
+ )},
+ {"read a zillion bytes from nonempty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<>>,
+ []
+ },
+ {ok, <<"hallo world">>}
+ },
+ read(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<0, 0, 0, 11, "hallo world">>
+ }},
+ [],
+ []
+ },
+ 65536
+ )
+ )}
+ ].
+
+
+read_exact(Frame, Bytes) -> thrift_framed_transport:read_exact(Frame, Bytes).
+
+read_exact_test_() ->
+ [
+ {"read exactly zero bytes from an empty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<>>,
+ []
+ },
+ {ok, <<>>}
+ },
+ read_exact(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ 0
+ )
+ )},
+ {"read exactly 1 byte from an empty framed membuffer", ?_assertMatch(
+ {_, {error, eof}},
+ read_exact(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ 1
+ )
+ )},
+ {"read exactly zero bytes from nonempty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<0, 0, 0, 11, "hallo world">>
+ }},
+ <<>>,
+ []
+ },
+ {ok, <<>>}
+ },
+ read_exact(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<0, 0, 0, 11, "hallo world">>
+ }},
+ [],
+ []
+ },
+ 0
+ )
+ )},
+ {"read exactly 1 byte from nonempty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<"allo world">>,
+ []
+ },
+ {ok, <<"h">>}
+ },
+ read_exact(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<0, 0, 0, 11, "hallo world">>
+ }},
+ [],
+ []
+ },
+ 1
+ )
+ )},
+ {"read exactly 1 byte from nonempty buffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<"allo world">>,
+ []
+ },
+ {ok, <<"h">>}
+ },
+ read_exact(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<"hallo world">>,
+ []
+ },
+ 1
+ )
+ )},
+ {"read exactly a zillion bytes from nonempty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [[],<<"hallo world">>],
+ []
+ },
+ {error, eof}
+ },
+ read_exact(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<0, 0, 0, 11, "hallo world">>
+ }},
+ [],
+ []
+ },
+ 65536
+ )
+ )}
+ ].
+
+
+write(Framed, Data) -> thrift_framed_transport:write(Framed, Data).
+
+write_test_() ->
+ [
+ {"write empty list to empty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ [[], []]
+ },
+ ok
+ },
+ write(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ []
+ )
+ )},
+ {"write empty list to nonempty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ [["hallo world"], []]
+ },
+ ok
+ },
+ write(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ ["hallo world"]
+ },
+ []
+ )
+ )},
+ {"write empty binary to empty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ [[], <<>>]
+ },
+ ok
+ },
+ write(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ <<>>
+ )
+ )},
+ {"write empty binary to nonempty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ [["hallo world"], <<>>]
+ },
+ ok
+ },
+ write(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ ["hallo world"]
+ },
+ <<>>
+ )
+ )}
+ ].
+
+
+flush(Transport) -> thrift_framed_transport:flush(Transport).
+
+flush_test_() ->
+ [
+ {"flush empty framed membuffer", ?_assertMatch(
+ {{t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ ok
+ },
+ flush({t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ })
+ )},
+ {"flush nonempty framed membuffer", ?_assertMatch(
+ {{t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ [<<>>, [<<0, 0, 0, 11>>, <<"hallo world">>]]
+ }},
+ [],
+ []
+ },
+ ok
+ },
+ flush({t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ <<"hallo world">>
+ })
+ )}
+ ].
+
+
+close(Transport) -> thrift_framed_transport:close(Transport).
+
+close_test_() ->
+ {"close framed membuffer", ?_assertMatch(
+ {{t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ ok
+ },
+ close({t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ })
+ )}.
+
diff --git a/src/jaegertracing/thrift/lib/erl/test/test_thrift_membuffer_transport.erl b/src/jaegertracing/thrift/lib/erl/test/test_thrift_membuffer_transport.erl
new file mode 100644
index 000000000..9689c7987
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/test_thrift_membuffer_transport.erl
@@ -0,0 +1,167 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(test_thrift_membuffer_transport).
+-include_lib("eunit/include/eunit.hrl").
+
+
+new() -> thrift_membuffer_transport:new().
+new(Data) -> thrift_membuffer_transport:new(Data).
+
+new_test_() ->
+ [
+ {"new empty membuffer", ?_assertMatch(
+ {ok, {_, _, {t_membuffer, []}}},
+ new()
+ )},
+ {"new membuffer with <<>>", ?_assertMatch(
+ {ok, {_, _, {t_membuffer, [<<>>]}}},
+ new(<<>>)
+ )},
+ {"new membuffer with []", ?_assertMatch(
+ {ok, {_, _, {t_membuffer, []}}},
+ new([])
+ )},
+ {"new membuffer with <<\"hallo world\">>", ?_assertMatch(
+ {ok, {_, _, {t_membuffer, [<<"hallo world">>]}}},
+ new(<<"hallo world">>)
+ )},
+ {"new membuffer with \"hallo world\"", ?_assertMatch(
+ {ok, {_, _, {t_membuffer, "hallo world"}}},
+ new("hallo world")
+ )}
+ ].
+
+
+read(Membuffer, Bytes) -> thrift_membuffer_transport:read(Membuffer, Bytes).
+
+read_test_() ->
+ [
+ {"read zero bytes from an empty membuffer", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read({t_membuffer, []}, 0)
+ )},
+ {"read 1 byte from an empty membuffer", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read({t_membuffer, []}, 1)
+ )},
+ {"read zero bytes from nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, <<"hallo world">>}, {ok, <<>>}},
+ read({t_membuffer, [["hallo", " "], "world"]}, 0)
+ )},
+ {"read 1 byte from nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, <<"allo world">>}, {ok, <<"h">>}},
+ read({t_membuffer, [["hallo", " "], "world"]}, 1)
+ )},
+ {"read a zillion bytes from nonempty buffer", ?_assertMatch(
+ {{t_membuffer, <<>>}, {ok, <<"hallo world">>}},
+ read({t_membuffer, [["hallo", " "], "world"]}, 65536)
+ )}
+ ].
+
+
+read_exact(Membuffer, Bytes) ->
+ thrift_membuffer_transport:read_exact(Membuffer, Bytes).
+
+read_exact_test_() ->
+ [
+ {"read exactly zero bytes from an empty membuffer", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read_exact({t_membuffer, []}, 0)
+ )},
+ {"read exactly 1 byte from an empty membuffer", ?_assertMatch(
+ {_, {error, eof}},
+ read_exact({t_membuffer, []}, 1)
+ )},
+ {"read exactly zero bytes from nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, <<"hallo world">>}, {ok, <<>>}},
+ read_exact({t_membuffer, [["hallo", " "], "world"]}, 0)
+ )},
+ {"read exactly 1 byte from nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, <<"allo world">>}, {ok, <<"h">>}},
+ read_exact({t_membuffer, [["hallo", " "], "world"]}, 1)
+ )},
+ {"read exactly a zillion bytes from nonempty buffer", ?_assertMatch(
+ {{t_membuffer, [["hallo", " "], "world"]}, {error, eof}},
+ read_exact({t_membuffer, [["hallo", " "], "world"]}, 65536)
+ )}
+ ].
+
+
+write(Membuffer, Data) -> thrift_membuffer_transport:write(Membuffer, Data).
+
+write_test_() ->
+ [
+ {"write empty list to empty membuffer", ?_assertMatch(
+ {{t_membuffer, [[], []]}, ok},
+ write({t_membuffer, []}, [])
+ )},
+ {"write empty list to nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, ["hallo world", []]}, ok},
+ write({t_membuffer, "hallo world"}, [])
+ )},
+ {"write empty binary to empty membuffer", ?_assertMatch(
+ {{t_membuffer, [[], <<>>]}, ok},
+ write({t_membuffer, []}, <<>>)
+ )},
+ {"write empty binary to nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, ["hallo world", <<>>]}, ok},
+ write({t_membuffer, "hallo world"}, <<>>)
+ )},
+ {"write a list to empty membuffer", ?_assertMatch(
+ {{t_membuffer, [[], "hallo world"]}, ok},
+ write({t_membuffer, []}, "hallo world")
+ )},
+ {"write a list to nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, [["hallo", " "], "world"]}, ok},
+ write({t_membuffer, ["hallo", " "]}, "world")
+ )},
+ {"write a binary to empty membuffer", ?_assertMatch(
+ {{t_membuffer, [[], <<"hallo world">>]}, ok},
+ write({t_membuffer, []}, <<"hallo world">>)
+ )},
+ {"write a binary to nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, [["hallo", " "], <<"world">>]}, ok},
+ write({t_membuffer, ["hallo", " "]}, <<"world">>)
+ )}
+ ].
+
+
+flush(Transport) -> thrift_membuffer_transport:flush(Transport).
+
+flush_test_() ->
+ [
+ {"flush empty membuffer", ?_assertMatch(
+ {{t_membuffer, []}, ok},
+ flush({t_membuffer, []})
+ )},
+ {"flush nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, [<<"hallo world">>]}, ok},
+ flush({t_membuffer, [<<"hallo world">>]})
+ )}
+ ].
+
+
+close(Transport) -> thrift_membuffer_transport:close(Transport).
+
+close_test_() ->
+ {"close membuffer", ?_assertMatch(
+ {{t_membuffer, _}, ok},
+ close({t_membuffer, []})
+ )}. \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/erl/test/test_thrift_socket_transport.erl b/src/jaegertracing/thrift/lib/erl/test/test_thrift_socket_transport.erl
new file mode 100644
index 000000000..5bc0f246d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/test_thrift_socket_transport.erl
@@ -0,0 +1,199 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(test_thrift_socket_transport).
+-include_lib("eunit/include/eunit.hrl").
+
+
+new(Socket) -> thrift_socket_transport:new(Socket).
+new(Socket, Opts) -> thrift_socket_transport:new(Socket, Opts).
+
+new_test_() ->
+ [
+ {"new socket", ?_assertMatch(
+ {ok, {_, thrift_socket_transport, {t_socket, a_fake_socket, 60000, []}}},
+ new(a_fake_socket)
+ )},
+ {"new socket with no options", ?_assertMatch(
+ {ok, {_, thrift_socket_transport, {t_socket, a_fake_socket, 60000, []}}},
+ new(a_fake_socket, [])
+ )},
+ {"new socket with integer timeout", ?_assertMatch(
+ {ok, {_, thrift_socket_transport, {t_socket, a_fake_socket, 5000, []}}},
+ new(a_fake_socket, [{recv_timeout, 5000}])
+ )},
+ {"new socket with infinity timeout", ?_assertMatch(
+ {ok, {_, thrift_socket_transport, {t_socket, a_fake_socket, infinity, []}}},
+ new(a_fake_socket, [{recv_timeout, infinity}])
+ )}
+ ].
+
+
+read(Socket, Bytes) -> thrift_socket_transport:read(Socket, Bytes).
+
+read_test_() ->
+ {setup,
+ fun() ->
+ meck:new(gen_tcp, [unstick, passthrough]),
+ meck:expect(gen_tcp, recv, fun(Bin, 0, _) -> {ok, Bin} end)
+ end,
+ fun(_) -> meck:unload(gen_tcp) end,
+ [
+ {"read zero bytes from empty socket", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read({t_socket, <<>>, 60000, []}, 0)
+ )},
+ {"read 1 byte from empty socket", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read({t_socket, <<>>, 60000, []}, 1)
+ )},
+ {"read zero bytes from nonempty socket", ?_assertMatch(
+ {{t_socket, _, _, _}, {ok, <<>>}},
+ read({t_socket, <<"hallo world">>, 60000, []}, 0)
+ )},
+ {"read 1 byte from nonempty socket", ?_assertMatch(
+ {{t_socket, _, _, <<"allo world">>}, {ok, <<"h">>}},
+ read({t_socket, <<"hallo world">>, 60000, []}, 1)
+ )},
+ {"read a zillion bytes from nonempty socket", ?_assertMatch(
+ {{t_socket, _, _, <<>>}, {ok, <<"hallo world">>}},
+ read({t_socket, <<"hallo world">>, 60000, []}, 65536)
+ )},
+ {"read 1 byte from previously buffered socket", ?_assertMatch(
+ {{t_socket, _, _, <<"allo">>}, {ok, <<"h">>}},
+ read({t_socket, <<" world">>, 60000, <<"hallo">>}, 1)
+ )},
+ {"read 6 byte from previously buffered socket", ?_assertMatch(
+ {{t_socket, _, _, <<"world">>}, {ok, <<"hallo ">>}},
+ read({t_socket, <<" world">>, 60000, <<"hallo">>}, 6)
+ )},
+ {"read a zillion bytes from previously buffered socket", ?_assertMatch(
+ {{t_socket, _, _, <<>>}, {ok, <<"hallo world">>}},
+ read({t_socket, <<" world">>, 60000, <<"hallo">>}, 65536)
+ )}
+ ]
+ }.
+
+
+read_exact(Socket, Bytes) -> thrift_socket_transport:read_exact(Socket, Bytes).
+
+read_exact_test_() ->
+ {setup,
+ fun() ->
+ meck:new(gen_tcp, [unstick, passthrough]),
+ meck:expect(gen_tcp, recv, fun(Bin, N, _) ->
+ case N of
+ 0 -> {ok, Bin};
+ 1 -> {ok, <<"h">>};
+ N when N > 2 -> {error, timeout}
+ end
+ end),
+ meck:expect(gen_tcp, close, fun(_) -> ok end)
+ end,
+ fun(_) -> meck:unload(gen_tcp) end,
+ [
+ {"read_exact zero bytes from empty socket", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read_exact({t_socket, <<>>, 60000, []}, 0)
+ )},
+ {"read_exact zero bytes from nonempty socket", ?_assertMatch(
+ {{t_socket, _, _, _}, {ok, <<>>}},
+ read_exact({t_socket, <<"hallo world">>, 60000, []}, 0)
+ )},
+ {"read_exact 1 byte from nonempty socket", ?_assertMatch(
+ {{t_socket, _, _, []}, {ok, <<"h">>}},
+ read_exact({t_socket, <<"hallo world">>, 60000, []}, 1)
+ )},
+ {"read_exact a zillion bytes from nonempty socket", ?_assertMatch(
+ {{t_socket, _, _, []}, {error, timeout}},
+ read_exact({t_socket, <<"hallo world">>, 60000, []}, 65536)
+ )},
+ {"read_exact 1 byte from previously buffered socket", ?_assertMatch(
+ {{t_socket, _, _, <<"allo">>}, {ok, <<"h">>}},
+ read_exact({t_socket, <<" world">>, 60000, <<"hallo">>}, 1)
+ )},
+ {"read_exact 6 byte from previously buffered socket", ?_assertMatch(
+ {{t_socket, _, _, []}, {ok, <<"more h">>}},
+ read_exact({t_socket, <<"hallo">>, 60000, <<"more ">>}, 6)
+ )},
+ {"read_exact a zillion bytes from previously buffered socket", ?_assertMatch(
+ {{t_socket, _, _, <<"hallo">>}, {error, timeout}},
+ read_exact({t_socket, <<" world">>, 60000, <<"hallo">>}, 65536)
+ )}
+ ]
+ }.
+
+
+write(Socket, Data) -> thrift_socket_transport:write(Socket, Data).
+
+write_test_() ->
+ {setup,
+ fun() ->
+ meck:new(gen_tcp, [unstick, passthrough]),
+ meck:expect(gen_tcp, send, fun(_, _) -> ok end)
+ end,
+ fun(_) -> meck:unload(gen_tcp) end,
+ [
+ {"write empty list to socket", ?_assertMatch(
+ {{t_socket, a_fake_socket, 60000, []}, ok},
+ write({t_socket, a_fake_socket, 60000, []}, [])
+ )},
+ {"write empty binary to socket", ?_assertMatch(
+ {{t_socket, a_fake_socket, 60000, []}, ok},
+ write({t_socket, a_fake_socket, 60000, []}, <<>>)
+ )},
+ {"write a list to socket", ?_assertMatch(
+ {{t_socket, a_fake_socket, 60000, []}, ok},
+ write({t_socket, a_fake_socket, 60000, []}, "hallo world")
+ )},
+ {"write a binary to socket", ?_assertMatch(
+ {{t_socket, a_fake_socket, 60000, []}, ok},
+ write({t_socket, a_fake_socket, 60000, []}, <<"hallo world">>)
+ )}
+ ]
+ }.
+
+
+flush(Transport) -> thrift_socket_transport:flush(Transport).
+
+flush_test_() ->
+ [
+ {"flush socket", ?_assertMatch(
+ {{t_socket, a_fake_socket, 60000, []}, ok},
+ flush({t_socket, a_fake_socket, 60000, []})
+ )}
+ ].
+
+
+close(Transport) -> thrift_socket_transport:close(Transport).
+
+close_test_() ->
+ {setup,
+ fun() ->
+ meck:new(gen_tcp, [unstick, passthrough]),
+ meck:expect(gen_tcp, close, fun(_) -> ok end)
+ end,
+ fun(_) -> meck:unload(gen_tcp) end,
+ [
+ {"close membuffer", ?_assertMatch(
+ {{t_socket, a_fake_socket, 60000, []}, ok},
+ close({t_socket, a_fake_socket, 60000, []})
+ )}
+ ]
+ }. \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/erl/test/thrift_socket_server_test.erl b/src/jaegertracing/thrift/lib/erl/test/thrift_socket_server_test.erl
new file mode 100644
index 000000000..0818b844e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/thrift_socket_server_test.erl
@@ -0,0 +1,49 @@
+-module (thrift_socket_server_test).
+
+-include_lib("eunit/include/eunit.hrl").
+
+-include ("thrift_constants.hrl").
+
+parse_handler_options_test_() ->
+ CorrectServiceHandlerOptionList = [{?MULTIPLEXED_ERROR_HANDLER_KEY, ?MODULE}, {"Service1", ?MODULE}, {"Service2", ?MODULE}],
+ MissingErrorHandlerOptionList = [{"Service1", ?MODULE}, {"Service2", ?MODULE}],
+ WrongService2HandlerOptionList = [{?MULTIPLEXED_ERROR_HANDLER_KEY, ?MODULE}, {"Service1", ?MODULE}, {"Service2", "Module"}],
+ WrongServiceKeyOptionList = [{?MULTIPLEXED_ERROR_HANDLER_KEY, ?MODULE}, {'service1', ?MODULE}, {"Service2", ?MODULE}],
+ CorrectHandlerTestFunction = fun() ->
+ ?assertMatch({thrift_socket_server,_,_,_,_,_,_,_,_,_,_,_,_,_}, thrift_socket_server:parse_options([{handler, CorrectServiceHandlerOptionList}])),
+ {thrift_socket_server,_,_, HandlerList,_,_,_,_,_,_,_,_,_,_} = thrift_socket_server:parse_options([{handler, CorrectServiceHandlerOptionList}]),
+ lists:foreach(fun
+ ({ServiceName, HandlerModule}) ->
+ ?assertMatch({ok, HandlerModule} when is_atom(HandlerModule), thrift_multiplexed_map_wrapper:find(ServiceName, HandlerList))
+ end, CorrectServiceHandlerOptionList)
+ end,
+ [
+ {"Bad argument for the handler option", ?_assertThrow(_, thrift_socket_server:parse_options([{handler, []}]))},
+ {"Try to parse the handler option twice", ?_assertThrow(_, thrift_socket_server:parse_options([{handler, ?MODULE}, {handler, CorrectServiceHandlerOptionList}]))},
+ {"Parse the handler option as a non multiplexed service handler", ?_assertMatch({thrift_socket_server,_,_,?MODULE,_,_,_,_,_,_,_,_,_,_}, thrift_socket_server:parse_options([{handler, ?MODULE}]))},
+ {"No error handler was defined", ?_assertThrow(_, thrift_socket_server:parse_options([{handler, MissingErrorHandlerOptionList}]))},
+ {"Bad handler module for Service2", ?_assertThrow(_, thrift_socket_server:parse_options([{handler, WrongService2HandlerOptionList}]))},
+ {"Bad service key for Service1", ?_assertThrow(_, thrift_socket_server:parse_options([{handler, WrongServiceKeyOptionList}]))},
+ {"Try to parse a correct handler option list", CorrectHandlerTestFunction}
+ ].
+
+parse_service_options_test_() ->
+ CorrectServiceModuleOptionList = [{"Service1", ?MODULE}, {"Service2", ?MODULE}],
+ WrongService2ModuleOptionList = [{"Service1", ?MODULE}, {"Service2", "thrift_service_module"}],
+ WrongServiceKeyOptionList = [{'service1', ?MODULE}, {"Service2", ?MODULE}],
+ CorrectServiceModuleTestFunction = fun() ->
+ ?assertMatch({thrift_socket_server,_,_,_,_,_,_,_,_,_,_,_,_,_}, thrift_socket_server:parse_options([{service, CorrectServiceModuleOptionList}])),
+ {thrift_socket_server,_, ServiceModuleList,_,_,_,_,_,_,_,_,_,_,_} = thrift_socket_server:parse_options([{service, CorrectServiceModuleOptionList}]),
+ lists:foreach(fun
+ ({ServiceName, ServiceModule}) ->
+ ?assertMatch({ok, ServiceModule} when is_atom(ServiceModule), thrift_multiplexed_map_wrapper:find(ServiceName, ServiceModuleList))
+ end, CorrectServiceModuleOptionList)
+ end,
+ [
+ {"Bad argument for the service option", ?_assertThrow(_, thrift_socket_server:parse_options([{service, []}]))},
+ {"Try to parse the service option twice", ?_assertThrow(_, thrift_socket_server:parse_options([{service, ?MODULE}, {service, CorrectServiceModuleOptionList}]))},
+ {"Parse a service module for a non multiplexed service", ?_assertMatch({thrift_socket_server,_,?MODULE,_,_,_,_,_,_,_,_,_,_,_}, thrift_socket_server:parse_options([{service, ?MODULE}]))},
+ {"Bad service module for Service2", ?_assertThrow(_, thrift_socket_server:parse_options([{service, WrongService2ModuleOptionList}]))},
+ {"Bad service key for Service1", ?_assertThrow(_, thrift_socket_server:parse_options([{service, WrongServiceKeyOptionList}]))},
+ {"Try to parse a correct service option list", CorrectServiceModuleTestFunction}
+ ].
diff --git a/src/jaegertracing/thrift/lib/erl/test/thrift_test_test.erl b/src/jaegertracing/thrift/lib/erl/test/thrift_test_test.erl
new file mode 100644
index 000000000..77df61d9a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/erl/test/thrift_test_test.erl
@@ -0,0 +1,643 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+% don't rename this thrift_test, it clobbers generated files
+-module(thrift_test_test).
+-compile(export_all).
+
+-include_lib("eunit/include/eunit.hrl").
+
+-include("gen-erl/thrift_test_constants.hrl").
+
+constant_test_() ->
+ [
+ {"myNumberz equals 1", ?_assertEqual(1, ?THRIFT_TEST_MYNUMBERZ)}
+ ].
+
+record_generation_test_() ->
+ [
+ {"Bonk record", ?_assertMatch(
+ {'thrift.test.Bonk', _, _},
+ #'thrift.test.Bonk'{message=null,type=null}
+ )},
+ {"Bools record", ?_assertMatch(
+ {'thrift.test.Bools', _, _},
+ #'thrift.test.Bools'{im_true=null,im_false=null}
+ )},
+ {"Xtruct record", ?_assertMatch(
+ {'thrift.test.Xtruct', _, _, _, _},
+ #'thrift.test.Xtruct'{string_thing=null,byte_thing=null,i32_thing=null,i64_thing=null}
+ )},
+ {"Xtruct2 record", ?_assertMatch(
+ {'thrift.test.Xtruct2', _, _, _},
+ #'thrift.test.Xtruct2'{byte_thing=null,struct_thing=null,i32_thing=null}
+ )},
+ {"Xtruct3 record", ?_assertMatch(
+ {'thrift.test.Xtruct3', _, _, _, _},
+ #'thrift.test.Xtruct3'{string_thing=null,changed=null,i32_thing=null,i64_thing=null}
+ )},
+ {"Insanity record", ?_assertMatch(
+ {'thrift.test.Insanity', _, _},
+ #'thrift.test.Insanity'{userMap=null,xtructs=null}
+ )},
+ {"CrazyNesting record", ?_assertMatch(
+ {'thrift.test.CrazyNesting', _, _, _, _},
+ #'thrift.test.CrazyNesting'{
+ string_field=null,
+ set_field=null,
+ list_field=null,
+ binary_field=null
+ }
+ )},
+ {"Xception record", ?_assertMatch(
+ {'thrift.test.Xception', _, _},
+ #'thrift.test.Xception'{errorCode=null,message=null}
+ )},
+ {"Xception2 record", ?_assertMatch(
+ {'thrift.test.Xception2', _, _},
+ #'thrift.test.Xception2'{errorCode=null,struct_thing=null}
+ )},
+ {"EmptyStruct record", ?_assertMatch({'thrift.test.EmptyStruct'}, #'thrift.test.EmptyStruct'{})},
+ {"OneField record", ?_assertMatch({'thrift.test.OneField', _}, #'thrift.test.OneField'{field=null})},
+ {"VersioningTestV1 record", ?_assertMatch(
+ {'thrift.test.VersioningTestV1', _, _, _},
+ #'thrift.test.VersioningTestV1'{begin_in_both=null,old_string=null,end_in_both=null}
+ )},
+ {"VersioningTestV2 record", ?_assertMatch(
+ {'thrift.test.VersioningTestV2', _, _, _, _, _, _, _, _, _, _, _, _},
+ #'thrift.test.VersioningTestV2'{
+ begin_in_both=null,
+ newint=null,
+ newbyte=null,
+ newshort=null,
+ newlong=null,
+ newdouble=null,
+ newstruct=null,
+ newlist=null,
+ newset=null,
+ newmap=null,
+ newstring=null,
+ end_in_both=null
+ }
+ )},
+ {"ListTypeVersioningV1 record", ?_assertMatch(
+ {'thrift.test.ListTypeVersioningV1', _, _},
+ #'thrift.test.ListTypeVersioningV1'{myints=null,hello=null}
+ )},
+ {"ListTypeVersioningV2 record", ?_assertMatch(
+ {'thrift.test.ListTypeVersioningV2', _, _},
+ #'thrift.test.ListTypeVersioningV2'{strings=null,hello=null}
+ )},
+ {"GuessProtocolStruct record", ?_assertMatch(
+ {'thrift.test.GuessProtocolStruct', _},
+ #'thrift.test.GuessProtocolStruct'{map_field=null}
+ )},
+ {"LargeDeltas record", ?_assertMatch(
+ {'thrift.test.LargeDeltas', _, _, _, _, _, _, _, _, _, _},
+ #'thrift.test.LargeDeltas'{
+ b1=null,
+ b10=null,
+ b100=null,
+ check_true=null,
+ b1000=null,
+ check_false=null,
+ vertwo2000=null,
+ a_set2500=null,
+ vertwo3000=null,
+ big_numbers=null
+ }
+ )},
+ {"NestedListsI32x2 record", ?_assertMatch(
+ {'thrift.test.NestedListsI32x2', _},
+ #'thrift.test.NestedListsI32x2'{integerlist=null}
+ )},
+ {"NestedListsI32x3 record", ?_assertMatch(
+ {'thrift.test.NestedListsI32x3', _},
+ #'thrift.test.NestedListsI32x3'{integerlist=null}
+ )},
+ {"NestedMixedx2 record", ?_assertMatch(
+ {'thrift.test.NestedMixedx2', _, _, _},
+ #'thrift.test.NestedMixedx2'{
+ int_set_list=null,
+ map_int_strset=null,
+ map_int_strset_list=null
+ }
+ )},
+ {"ListBonks record", ?_assertMatch({'thrift.test.ListBonks', _}, #'thrift.test.ListBonks'{bonk=null})},
+ {"NestedListsBonk record", ?_assertMatch(
+ {'thrift.test.NestedListsBonk', _},
+ #'thrift.test.NestedListsBonk'{bonk=null}
+ )},
+ {"BoolTest record", ?_assertMatch(
+ {'thrift.test.BoolTest', _, _},
+ #'thrift.test.BoolTest'{b=null,s=null}
+ )},
+ {"StructA record", ?_assertMatch({'thrift.test.StructA', _}, #'thrift.test.StructA'{s=null})},
+ {"StructB record", ?_assertMatch(
+ {'thrift.test.StructB', _, _},
+ #'thrift.test.StructB'{aa=null,ab=null}
+ )}
+ ].
+
+struct_info_test_() ->
+ [
+ {"Bonk definition (short version)", ?_assertEqual(
+ {struct, [{1, string}, {2, i32}]},
+ thrift_test_types:struct_info('thrift.test.Bonk')
+ )},
+ {"Bonk definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, string, message, undefined},
+ {2, undefined, i32, type, undefined}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.Bonk')
+ )},
+ {"Bools definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, bool, im_true, undefined},
+ {2, undefined, bool, im_false, undefined}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.Bools')
+ )},
+ {"Xtruct definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, string, string_thing, undefined},
+ {4, undefined, byte, byte_thing, undefined},
+ {9, undefined, i32, i32_thing, undefined},
+ {11, undefined, i64, i64_thing, undefined}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.Xtruct')
+ )},
+ {"Xtruct2 definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, byte, byte_thing, undefined},
+ {2, undefined, {struct, {'thrift_test_types', 'thrift.test.Xtruct'}}, struct_thing, #'thrift.test.Xtruct'{}},
+ {3, undefined, i32, i32_thing, undefined}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.Xtruct2')
+ )},
+ {"Xtruct3 definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, string, string_thing, undefined},
+ {4, undefined, i32, changed, undefined},
+ {9, undefined, i32, i32_thing, undefined},
+ {11, undefined, i64, i64_thing, undefined}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.Xtruct3')
+ )},
+ {"Insanity definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, {map, i32, i64}, userMap, dict:new()},
+ {2, undefined, {list, {struct, {'thrift_test_types', 'thrift.test.Xtruct'}}}, xtructs, []}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.Insanity')
+ )},
+ {"CrazyNesting definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, string, string_field, undefined},
+ {2, optional, {set, {struct, {'thrift_test_types', 'thrift.test.Insanity'}}}, set_field, sets:new()},
+ {3, required, {list, {map,
+ {set, i32},
+ {map, i32, {set, {list, {map, {struct, {'thrift_test_types', 'thrift.test.Insanity'}}, string}}}}
+ }}, list_field, []},
+ {4, undefined, string, binary_field, undefined}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.CrazyNesting')
+ )},
+ {"Xception definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, i32, errorCode, undefined},
+ {2, undefined, string, message, undefined}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.Xception')
+ )},
+ {"Xception2 definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, i32, errorCode, undefined},
+ {2, undefined, {struct, {'thrift_test_types', 'thrift.test.Xtruct'}}, struct_thing, #'thrift.test.Xtruct'{}}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.Xception2')
+ )},
+ {"EmptyStruct definition", ?_assertEqual(
+ {struct, []},
+ thrift_test_types:struct_info_ext('thrift.test.EmptyStruct')
+ )},
+ {"OneField definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, {struct, {'thrift_test_types', 'thrift.test.EmptyStruct'}}, field, #'thrift.test.EmptyStruct'{}}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.OneField')
+ )},
+ {"VersioningTestV1 definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, i32, begin_in_both, undefined},
+ {3, undefined, string, old_string, undefined},
+ {12, undefined, i32, end_in_both, undefined}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.VersioningTestV1')
+ )},
+ {"VersioningTestV2 definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, i32, begin_in_both, undefined},
+ {2, undefined, i32, newint, undefined},
+ {3, undefined, byte, newbyte, undefined},
+ {4, undefined, i16, newshort, undefined},
+ {5, undefined, i64, newlong, undefined},
+ {6, undefined, double, newdouble, undefined},
+ {7, undefined, {struct, {thrift_test_types, 'thrift.test.Bonk'}}, newstruct, #'thrift.test.Bonk'{}},
+ {8, undefined, {list, i32}, newlist, []},
+ {9, undefined, {set, i32}, newset, sets:new()},
+ {10, undefined, {map, i32, i32}, newmap, dict:new()},
+ {11, undefined, string, newstring, undefined},
+ {12, undefined, i32, end_in_both, undefined}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.VersioningTestV2')
+ )},
+ {"ListTypeVersioningV1 definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, {list, i32}, myints, []},
+ {2, undefined, string, hello, undefined}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.ListTypeVersioningV1')
+ )},
+ {"ListTypeVersioningV2 definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, {list, string}, strings, []},
+ {2, undefined, string, hello, undefined}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.ListTypeVersioningV2')
+ )},
+ {"GuessProtocolStruct definition", ?_assertEqual(
+ {struct, [
+ {7, undefined, {map, string, string}, map_field, dict:new()}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.GuessProtocolStruct')
+ )},
+ {"LargeDeltas definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, {struct, {thrift_test_types, 'thrift.test.Bools'}}, b1, #'thrift.test.Bools'{}},
+ {10, undefined, {struct, {thrift_test_types, 'thrift.test.Bools'}}, b10, #'thrift.test.Bools'{}},
+ {100, undefined, {struct, {thrift_test_types, 'thrift.test.Bools'}}, b100, #'thrift.test.Bools'{}},
+ {500, undefined, bool, check_true, undefined},
+ {1000, undefined, {struct, {thrift_test_types, 'thrift.test.Bools'}}, b1000, #'thrift.test.Bools'{}},
+ {1500, undefined, bool, check_false, undefined},
+ {2000, undefined, {struct, {thrift_test_types, 'thrift.test.VersioningTestV2'}}, vertwo2000, #'thrift.test.VersioningTestV2'{}},
+ {2500, undefined, {set, string}, a_set2500, sets:new()},
+ {3000, undefined, {struct, {thrift_test_types, 'thrift.test.VersioningTestV2'}}, vertwo3000, #'thrift.test.VersioningTestV2'{}},
+ {4000, undefined, {list, i32}, big_numbers, []}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.LargeDeltas')
+ )},
+ {"NestedListsI32x2 definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, {list, {list, i32}}, integerlist, []}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.NestedListsI32x2')
+ )},
+ {"NestedListsI32x3 definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, {list, {list, {list, i32}}}, integerlist, []}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.NestedListsI32x3')
+ )},
+ {"NestedMixedx2 definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, {list, {set, i32}}, int_set_list, []},
+ {2, undefined, {map, i32, {set, string}}, map_int_strset, dict:new()},
+ {3, undefined, {list, {map, i32, {set, string}}}, map_int_strset_list, []}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.NestedMixedx2')
+ )},
+ {"ListBonks definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, {list, {struct, {thrift_test_types, 'thrift.test.Bonk'}}}, bonk, []}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.ListBonks')
+ )},
+ {"NestedListsBonk definition", ?_assertEqual(
+ {struct, [
+ {1, undefined, {list, {list, {list, {struct, {thrift_test_types, 'thrift.test.Bonk'}}}}}, bonk, []}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.NestedListsBonk')
+ )},
+ {"BoolTest definition", ?_assertEqual(
+ {struct, [
+ {1, optional, bool, b, true},
+ {2, optional, string, s, "true"}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.BoolTest')
+ )},
+ {"StructA definition", ?_assertEqual(
+ {struct, [{1, required, string, s, undefined}]},
+ thrift_test_types:struct_info_ext('thrift.test.StructA')
+ )},
+ {"StructB definition", ?_assertEqual(
+ {struct, [
+ {1, optional, {struct, {thrift_test_types, 'thrift.test.StructA'}}, aa, #'thrift.test.StructA'{}},
+ {2, required, {struct, {thrift_test_types, 'thrift.test.StructA'}}, ab, #'thrift.test.StructA'{}}
+ ]},
+ thrift_test_types:struct_info_ext('thrift.test.StructB')
+ )}
+ ].
+
+service_info_test_() ->
+ [
+ {"testVoid params", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testVoid, params_type)
+ )},
+ {"testVoid reply", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testVoid, reply_type)
+ )},
+ {"testVoid exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testVoid, exceptions)
+ )},
+ {"testString params", ?_assertEqual(
+ {struct, [{1, string}]},
+ thrift_test_thrift:function_info(testString, params_type)
+ )},
+ {"testString reply", ?_assertEqual(
+ string,
+ thrift_test_thrift:function_info(testString, reply_type)
+ )},
+ {"testString exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testString, exceptions)
+ )},
+ {"testByte params", ?_assertEqual(
+ {struct, [{1, byte}]},
+ thrift_test_thrift:function_info(testByte, params_type)
+ )},
+ {"testByte reply", ?_assertEqual(
+ byte,
+ thrift_test_thrift:function_info(testByte, reply_type)
+ )},
+ {"testByte exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testByte, exceptions)
+ )},
+ {"testI32 params", ?_assertEqual(
+ {struct, [{1, i32}]},
+ thrift_test_thrift:function_info(testI32, params_type)
+ )},
+ {"testI32 reply", ?_assertEqual(
+ i32,
+ thrift_test_thrift:function_info(testI32, reply_type)
+ )},
+ {"testI32 exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testI32, exceptions)
+ )},
+ {"testI64 params", ?_assertEqual(
+ {struct, [{1, i64}]},
+ thrift_test_thrift:function_info(testI64, params_type)
+ )},
+ {"testI64 reply", ?_assertEqual(
+ i64,
+ thrift_test_thrift:function_info(testI64, reply_type)
+ )},
+ {"testI64 exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testI64, exceptions)
+ )},
+ {"testDouble params", ?_assertEqual(
+ {struct, [{1, double}]},
+ thrift_test_thrift:function_info(testDouble, params_type)
+ )},
+ {"testDouble reply", ?_assertEqual(
+ double,
+ thrift_test_thrift:function_info(testDouble, reply_type)
+ )},
+ {"testDouble exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testDouble, exceptions)
+ )},
+ {"testStruct params", ?_assertEqual(
+ {struct, [
+ {1, {struct, {thrift_test_types, 'thrift.test.Xtruct'}}}
+ ]},
+ thrift_test_thrift:function_info(testStruct, params_type)
+ )},
+ {"testStruct reply", ?_assertEqual(
+ {struct, {thrift_test_types, 'thrift.test.Xtruct'}},
+ thrift_test_thrift:function_info(testStruct, reply_type)
+ )},
+ {"testStruct exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testStruct, exceptions)
+ )},
+ {"testNest params", ?_assertEqual(
+ {struct, [
+ {1, {struct, {thrift_test_types, 'thrift.test.Xtruct2'}}}
+ ]},
+ thrift_test_thrift:function_info(testNest, params_type)
+ )},
+ {"testNest reply", ?_assertEqual(
+ {struct, {thrift_test_types, 'thrift.test.Xtruct2'}},
+ thrift_test_thrift:function_info(testNest, reply_type)
+ )},
+ {"testNest exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testNest, exceptions)
+ )},
+ {"testMap params", ?_assertEqual(
+ {struct, [
+ {1, {map, i32, i32}}
+ ]},
+ thrift_test_thrift:function_info(testMap, params_type)
+ )},
+ {"testMap reply", ?_assertEqual(
+ {map, i32, i32},
+ thrift_test_thrift:function_info(testMap, reply_type)
+ )},
+ {"testMap exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testMap, exceptions)
+ )},
+ {"testStringMap params", ?_assertEqual(
+ {struct, [
+ {1, {map, string, string}}
+ ]},
+ thrift_test_thrift:function_info(testStringMap, params_type)
+ )},
+ {"testStringMap reply", ?_assertEqual(
+ {map, string, string},
+ thrift_test_thrift:function_info(testStringMap, reply_type)
+ )},
+ {"testStringMap exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testStringMap, exceptions)
+ )},
+ {"testSet params", ?_assertEqual(
+ {struct, [
+ {1, {set, i32}}
+ ]},
+ thrift_test_thrift:function_info(testSet, params_type)
+ )},
+ {"testSet reply", ?_assertEqual(
+ {set, i32},
+ thrift_test_thrift:function_info(testSet, reply_type)
+ )},
+ {"testSet exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testSet, exceptions)
+ )},
+ {"testList params", ?_assertEqual(
+ {struct, [
+ {1, {list, i32}}
+ ]},
+ thrift_test_thrift:function_info(testList, params_type)
+ )},
+ {"testList reply", ?_assertEqual(
+ {list, i32},
+ thrift_test_thrift:function_info(testList, reply_type)
+ )},
+ {"testList exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testList, exceptions)
+ )},
+ {"testEnum params", ?_assertEqual(
+ {struct, [
+ {1, i32}
+ ]},
+ thrift_test_thrift:function_info(testEnum, params_type)
+ )},
+ {"testEnum reply", ?_assertEqual(
+ i32,
+ thrift_test_thrift:function_info(testEnum, reply_type)
+ )},
+ {"testEnum exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testEnum, exceptions)
+ )},
+ {"testTypedef params", ?_assertEqual(
+ {struct, [{1, i64}]},
+ thrift_test_thrift:function_info(testTypedef, params_type)
+ )},
+ {"testTypedef reply", ?_assertEqual(
+ i64,
+ thrift_test_thrift:function_info(testTypedef, reply_type)
+ )},
+ {"testTypedef exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testTypedef, exceptions)
+ )},
+ {"testMapMap params", ?_assertEqual(
+ {struct, [
+ {1, i32}
+ ]},
+ thrift_test_thrift:function_info(testMapMap, params_type)
+ )},
+ {"testMapMap reply", ?_assertEqual(
+ {map, i32, {map, i32,i32}},
+ thrift_test_thrift:function_info(testMapMap, reply_type)
+ )},
+ {"testMapMap exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testMapMap, exceptions)
+ )},
+ {"testInsanity params", ?_assertEqual(
+ {struct, [
+ {1, {struct, {thrift_test_types, 'thrift.test.Insanity'}}}
+ ]},
+ thrift_test_thrift:function_info(testInsanity, params_type)
+ )},
+ {"testInsanity reply", ?_assertEqual(
+ {map, i64, {map, i32, {struct, {'thrift_test_types', 'thrift.test.Insanity'}}}},
+ thrift_test_thrift:function_info(testInsanity, reply_type)
+ )},
+ {"testInsanity exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testInsanity, exceptions)
+ )},
+ {"testMulti params", ?_assertEqual(
+ {struct, [
+ {1, byte},
+ {2, i32},
+ {3, i64},
+ {4, {map, i16, string}},
+ {5, i32},
+ {6, i64}
+ ]},
+ thrift_test_thrift:function_info(testMulti, params_type)
+ )},
+ {"testMulti reply", ?_assertEqual(
+ {struct, {thrift_test_types, 'thrift.test.Xtruct'}},
+ thrift_test_thrift:function_info(testMulti, reply_type)
+ )},
+ {"testMulti exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testMulti, exceptions)
+ )},
+ {"testException params", ?_assertEqual(
+ {struct, [{1, string}]},
+ thrift_test_thrift:function_info(testException, params_type)
+ )},
+ {"testException reply", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testException, reply_type)
+ )},
+ {"testException exceptions", ?_assertEqual(
+ {struct, [
+ {1, {struct, {thrift_test_types, 'thrift.test.Xception'}}}
+ ]},
+ thrift_test_thrift:function_info(testException, exceptions)
+ )},
+ {"testMultiException params", ?_assertEqual(
+ {struct, [{1, string}, {2, string}]},
+ thrift_test_thrift:function_info(testMultiException, params_type)
+ )},
+ {"testMultiException reply", ?_assertEqual(
+ {struct, {thrift_test_types, 'thrift.test.Xtruct'}},
+ thrift_test_thrift:function_info(testMultiException, reply_type)
+ )},
+ {"testMultiException exceptions", ?_assertEqual(
+ {struct, [
+ {1, {struct, {thrift_test_types, 'thrift.test.Xception'}}},
+ {2, {struct, {thrift_test_types, 'thrift.test.Xception2'}}}
+ ]},
+ thrift_test_thrift:function_info(testMultiException, exceptions)
+ )},
+ {"testOneway params", ?_assertEqual(
+ {struct, [{1, i32}]},
+ thrift_test_thrift:function_info(testOneway, params_type)
+ )},
+ {"testOneway reply", ?_assertEqual(
+ oneway_void,
+ thrift_test_thrift:function_info(testOneway, reply_type)
+ )},
+ {"testOneway exceptions", ?_assertEqual(
+ {struct, []},
+ thrift_test_thrift:function_info(testOneway, exceptions)
+ )},
+ {"secondtestString params", ?_assertEqual(
+ {struct, [{1, string}]},
+ second_service_thrift:function_info(secondtestString, params_type)
+ )},
+ {"secondtestString reply", ?_assertEqual(
+ string,
+ second_service_thrift:function_info(secondtestString, reply_type)
+ )},
+ {"secondtestString exceptions", ?_assertEqual(
+ {struct, []},
+ second_service_thrift:function_info(secondtestString, exceptions)
+ )}
+ ].
diff --git a/src/jaegertracing/thrift/lib/go/Makefile.am b/src/jaegertracing/thrift/lib/go/Makefile.am
new file mode 100644
index 000000000..0dfa5fadc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/Makefile.am
@@ -0,0 +1,45 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+SUBDIRS = .
+
+if WITH_TESTS
+SUBDIRS += test
+endif
+
+install:
+ @echo '##############################################################'
+ @echo '##############################################################'
+ @echo 'The Go client library should be installed via "go get", please see /lib/go/README.md'
+ @echo '##############################################################'
+ @echo '##############################################################'
+
+check-local:
+ GOPATH=`pwd` $(GO) test -race ./thrift
+
+clean-local:
+ $(RM) -rf pkg
+
+all-local:
+ GOPATH=`pwd` $(GO) build ./thrift
+
+EXTRA_DIST = \
+ thrift \
+ coding_standards.md \
+ README.md
diff --git a/src/jaegertracing/thrift/lib/go/README.md b/src/jaegertracing/thrift/lib/go/README.md
new file mode 100644
index 000000000..ce6d5edc1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/README.md
@@ -0,0 +1,83 @@
+Thrift Go Software Library
+
+License
+=======
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+
+Using Thrift with Go
+====================
+
+Thrift supports Go 1.7+
+
+In following Go conventions, we recommend you use the 'go' tool to install
+Thrift for go.
+
+ $ go get github.com/apache/thrift/lib/go/thrift/...
+
+Will retrieve and install the most recent version of the package.
+
+
+A note about optional fields
+============================
+
+The thrift-to-Go compiler tries to represent thrift IDL structs as Go structs.
+We must be able to distinguish between optional fields that are set to their
+default value and optional values which are actually unset, so the generated
+code represents optional fields via pointers.
+
+This is generally intuitive and works well much of the time, but Go does not
+have a syntax for creating a pointer to a constant in a single expression. That
+is, given a struct like
+
+ struct SomeIDLType {
+ OptionalField *int32
+ }
+
+, the following will not compile:
+
+ x := &SomeIDLType{
+ OptionalField: &(3),
+ }
+
+(Nor is there any other syntax that's built in to the language)
+
+As such, we provide some helpers that do just this under lib/go/thrift/. E.g.,
+
+ x := &SomeIDLType{
+ OptionalField: thrift.Int32Ptr(3),
+ }
+
+And so on. The code generator also creates analogous helpers for user-defined
+typedefs and enums.
+
+Adding custom tags to generated Thrift structs
+==============================================
+
+You can add tags to the auto-generated thrift structs using the following format:
+
+ struct foo {
+ 1: required string Bar (go.tag = "some_tag:\"some_tag_value\"")
+ }
+
+which will generate:
+
+ type Foo struct {
+ Bar string `thrift:"bar,1,required" some_tag:"some_tag_value"`
+ }
diff --git a/src/jaegertracing/thrift/lib/go/coding_standards.md b/src/jaegertracing/thrift/lib/go/coding_standards.md
new file mode 100644
index 000000000..fa0390bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/coding_standards.md
@@ -0,0 +1 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
diff --git a/src/jaegertracing/thrift/lib/go/test/BinaryKeyTest.thrift b/src/jaegertracing/thrift/lib/go/test/BinaryKeyTest.thrift
new file mode 100644
index 000000000..71cb61472
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/BinaryKeyTest.thrift
@@ -0,0 +1,24 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Make sure that thrift produce compilable code for binary key
+struct testStruct {
+ 1: required map<binary,string> bin_to_string
+}
+
diff --git a/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceServiceTest.thrift b/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceServiceTest.thrift
new file mode 100644
index 000000000..aade3d7d6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceServiceTest.thrift
@@ -0,0 +1,25 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+namespace go conflict.context
+
+include "ConflictNamespaceTestD.thrift"
+
+service ConflictService {
+ ConflictNamespaceTestD.ThingD thingFunc()
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestA.thrift b/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestA.thrift
new file mode 100644
index 000000000..2749da90b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestA.thrift
@@ -0,0 +1,23 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+namespace go conflicta.common
+
+struct ThingA {
+ 1: bool value
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestB.thrift b/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestB.thrift
new file mode 100644
index 000000000..b1940ff9f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestB.thrift
@@ -0,0 +1,23 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+namespace go conflictb.common
+
+struct ThingB {
+ 1: bool value
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestC.thrift b/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestC.thrift
new file mode 100644
index 000000000..7d5ee2582
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestC.thrift
@@ -0,0 +1,23 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+namespace go common
+
+struct ThingC {
+ 1: bool value
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestD.thrift b/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestD.thrift
new file mode 100644
index 000000000..8fe7f5e01
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestD.thrift
@@ -0,0 +1,23 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+namespace go conflictd.context
+
+struct ThingD {
+ 1: bool value
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestSuperThing.thrift b/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestSuperThing.thrift
new file mode 100644
index 000000000..2348a621f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/ConflictNamespaceTestSuperThing.thrift
@@ -0,0 +1,29 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+include "ConflictNamespaceTestA.thrift"
+include "ConflictNamespaceTestB.thrift"
+include "ConflictNamespaceTestC.thrift"
+include "ConflictNamespaceTestD.thrift"
+
+struct SuperThing {
+ 1: ConflictNamespaceTestA.ThingA thing_a
+ 2: ConflictNamespaceTestB.ThingB thing_b
+ 3: ConflictNamespaceTestC.ThingC thing_c
+ 4: ConflictNamespaceTestD.ThingD thing_d
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/DontExportRWTest.thrift b/src/jaegertracing/thrift/lib/go/test/DontExportRWTest.thrift
new file mode 100644
index 000000000..39e5a6fc8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/DontExportRWTest.thrift
@@ -0,0 +1,27 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+struct InnerStruct {
+ 1: required string id
+}
+
+struct TestStruct {
+ 1: required string id
+ 2: required InnerStruct inner
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/ErrorTest.thrift b/src/jaegertracing/thrift/lib/go/test/ErrorTest.thrift
new file mode 100644
index 000000000..33b664434
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/ErrorTest.thrift
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+struct TestStruct
+{
+ 1: map<string, string> m,
+ 2: list<string> l,
+ 3: set<string> s,
+ 4: i32 i
+}
+
+service ErrorTest
+{
+ TestStruct testStruct(1: TestStruct thing)
+ string testString(1: string s)
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/GoTagTest.thrift b/src/jaegertracing/thrift/lib/go/test/GoTagTest.thrift
new file mode 100644
index 000000000..5667c6e54
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/GoTagTest.thrift
@@ -0,0 +1,25 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+struct tagged {
+ 1: string string_thing,
+ 2: i64 int_thing (go.tag = "json:\"int_thing,string\""),
+ 3: optional i64 optional_int_thing
+ 4: optional bool optional_bool_thing = false
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/IgnoreInitialismsTest.thrift b/src/jaegertracing/thrift/lib/go/test/IgnoreInitialismsTest.thrift
new file mode 100644
index 000000000..0b30e9ed5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/IgnoreInitialismsTest.thrift
@@ -0,0 +1,26 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+struct IgnoreInitialismsTest {
+ 1: i64 id,
+ 2: i64 my_id,
+ 3: i64 num_cpu,
+ 4: i64 num_gpu,
+ 5: i64 my_ID,
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/IncludesTest.thrift b/src/jaegertracing/thrift/lib/go/test/IncludesTest.thrift
new file mode 100644
index 000000000..3f60321f7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/IncludesTest.thrift
@@ -0,0 +1,67 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+include "ThriftTest.thrift"
+include "NamespacedTest.thrift"
+
+const ThriftTest.UserId USERID = 42
+const NamespacedTest.UserId USERID1 = 41
+const ThriftTest.MapType MAPCONSTANT = {'hello':{}, 'goodnight':{}}
+
+const i32 TWO = NamespacedTest.Stuff.TWO
+const i32 THREE = NamespacedTest.THREE
+
+struct testStruct {
+ 1: list<ThriftTest.Numberz> listNumbers
+}
+
+struct TestStruct2 {
+ 1: testStruct blah,
+ 2: ThriftTest.UserId id,
+ 3: NamespacedTest.Stuff stuff,
+}
+
+service testService extends ThriftTest.SecondService {
+ ThriftTest.CrazyNesting getCrazyNesting(
+ 1: ThriftTest.StructA a,
+ 2: ThriftTest.Numberz numbers
+ ) throws(1: ThriftTest.Xception err1),
+
+ void getSomeValue_DO_NOT_CALL(),
+}
+
+service ExtendedService extends testService {
+ void extendedMethod(),
+ NamespacedTest.StuffStruct extendedMethod2(),
+}
+
+service Extended2Service extends NamespacedTest.NamespacedService {
+ void extendedMethod3(),
+}
+
+typedef map<ThriftTest.UserId, map<NamespacedTest.UserId, list<TestStruct2> > > ComplexMapType
+
+struct ComplexMapStruct {
+ 1: ComplexMapType complex,
+}
+
+service ComplexMapService {
+ ComplexMapStruct transformMap(1: ComplexMapStruct input),
+}
+
diff --git a/src/jaegertracing/thrift/lib/go/test/InitialismsTest.thrift b/src/jaegertracing/thrift/lib/go/test/InitialismsTest.thrift
new file mode 100644
index 000000000..ad86ac19d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/InitialismsTest.thrift
@@ -0,0 +1,24 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+struct InitialismsTest {
+ 1: string user_id,
+ 2: string server_url,
+ 3: string id,
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/Makefile.am b/src/jaegertracing/thrift/lib/go/test/Makefile.am
new file mode 100644
index 000000000..244ddff1f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/Makefile.am
@@ -0,0 +1,131 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+THRIFTARGS = -out gopath/src/ --gen go:thrift_import=thrift$(COMPILER_EXTRAFLAG)
+THRIFTTEST = $(top_srcdir)/test/ThriftTest.thrift
+
+# Thrift for GO has problems with complex map keys: THRIFT-2063
+gopath: $(THRIFT) $(THRIFTTEST) \
+ IncludesTest.thrift \
+ NamespacedTest.thrift \
+ MultiplexedProtocolTest.thrift \
+ OnewayTest.thrift \
+ OptionalFieldsTest.thrift \
+ RequiredFieldTest.thrift \
+ ServicesTest.thrift \
+ GoTagTest.thrift \
+ TypedefFieldTest.thrift \
+ RefAnnotationFieldsTest.thrift \
+ UnionDefaultValueTest.thrift \
+ UnionBinaryTest.thrift \
+ ErrorTest.thrift \
+ NamesTest.thrift \
+ InitialismsTest.thrift \
+ DontExportRWTest.thrift \
+ dontexportrwtest/compile_test.go \
+ IgnoreInitialismsTest.thrift \
+ ConflictNamespaceTestA.thrift \
+ ConflictNamespaceTestB.thrift \
+ ConflictNamespaceTestC.thrift \
+ ConflictNamespaceTestD.thrift \
+ ConflictNamespaceTestSuperThing.thrift \
+ ConflictNamespaceServiceTest.thrift
+ mkdir -p gopath/src
+ grep -v list.*map.*list.*map $(THRIFTTEST) | grep -v 'set<Insanity>' > ThriftTest.thrift
+ $(THRIFT) $(THRIFTARGS) -r IncludesTest.thrift
+ $(THRIFT) $(THRIFTARGS) BinaryKeyTest.thrift
+ $(THRIFT) $(THRIFTARGS) MultiplexedProtocolTest.thrift
+ $(THRIFT) $(THRIFTARGS) OnewayTest.thrift
+ $(THRIFT) $(THRIFTARGS) OptionalFieldsTest.thrift
+ $(THRIFT) $(THRIFTARGS) RequiredFieldTest.thrift
+ $(THRIFT) $(THRIFTARGS) ServicesTest.thrift
+ $(THRIFT) $(THRIFTARGS) GoTagTest.thrift
+ $(THRIFT) $(THRIFTARGS) TypedefFieldTest.thrift
+ $(THRIFT) $(THRIFTARGS) RefAnnotationFieldsTest.thrift
+ $(THRIFT) $(THRIFTARGS) UnionDefaultValueTest.thrift
+ $(THRIFT) $(THRIFTARGS) UnionBinaryTest.thrift
+ $(THRIFT) $(THRIFTARGS) ErrorTest.thrift
+ $(THRIFT) $(THRIFTARGS) NamesTest.thrift
+ $(THRIFT) $(THRIFTARGS) InitialismsTest.thrift
+ $(THRIFT) $(THRIFTARGS),read_write_private DontExportRWTest.thrift
+ $(THRIFT) $(THRIFTARGS),ignore_initialisms IgnoreInitialismsTest.thrift
+ $(THRIFT) $(THRIFTARGS) ConflictNamespaceTestA.thrift
+ $(THRIFT) $(THRIFTARGS) ConflictNamespaceTestB.thrift
+ $(THRIFT) $(THRIFTARGS) ConflictNamespaceTestC.thrift
+ $(THRIFT) $(THRIFTARGS) ConflictNamespaceTestD.thrift
+ $(THRIFT) $(THRIFTARGS) ConflictNamespaceTestSuperThing.thrift
+ $(THRIFT) $(THRIFTARGS) ConflictNamespaceServiceTest.thrift
+ GOPATH=`pwd`/gopath $(GO) get github.com/golang/mock/gomock || true
+ sed -i 's/\"context\"/\"golang.org\/x\/net\/context\"/g' gopath/src/github.com/golang/mock/gomock/controller.go || true
+ GOPATH=`pwd`/gopath $(GO) get github.com/golang/mock/gomock
+ ln -nfs ../../../thrift gopath/src/thrift
+ ln -nfs ../../tests gopath/src/tests
+ cp -r ./dontexportrwtest gopath/src
+ touch gopath
+
+check: gopath
+ GOPATH=`pwd`/gopath $(GO) build \
+ includestest \
+ binarykeytest \
+ servicestest \
+ typedeffieldtest \
+ refannotationfieldstest \
+ errortest \
+ namestest \
+ initialismstest \
+ dontexportrwtest \
+ ignoreinitialismstest \
+ unionbinarytest \
+ conflictnamespacetestsuperthing \
+ conflict/context/conflict_service-remote
+ GOPATH=`pwd`/gopath $(GO) test thrift tests dontexportrwtest
+
+clean-local:
+ $(RM) -r gopath ThriftTest.thrift gen-go
+
+client: stubs
+ $(GO) run TestClient.go
+
+EXTRA_DIST = \
+ dontexportrwtest \
+ tests \
+ BinaryKeyTest.thrift \
+ GoTagTest.thrift \
+ IncludesTest.thrift \
+ MultiplexedProtocolTest.thrift \
+ NamespacedTest.thrift \
+ OnewayTest.thrift \
+ OptionalFieldsTest.thrift \
+ RequiredFieldTest.thrift \
+ RefAnnotationFieldsTest.thrift \
+ UnionDefaultValueTest.thrift \
+ UnionBinaryTest.thrift \
+ ServicesTest.thrift \
+ TypedefFieldTest.thrift \
+ ErrorTest.thrift \
+ NamesTest.thrift \
+ InitialismsTest.thrift \
+ DontExportRWTest.thrift \
+ IgnoreInitialismsTest.thrift \
+ ConflictNamespaceTestA.thrift \
+ ConflictNamespaceTestB.thrift \
+ ConflictNamespaceTestC.thrift \
+ ConflictNamespaceTestD.thrift \
+ ConflictNamespaceTestSuperThing.thrift
+ ConflictNamespaceServiceTest.thrift
diff --git a/src/jaegertracing/thrift/lib/go/test/MultiplexedProtocolTest.thrift b/src/jaegertracing/thrift/lib/go/test/MultiplexedProtocolTest.thrift
new file mode 100644
index 000000000..b263f5925
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/MultiplexedProtocolTest.thrift
@@ -0,0 +1,27 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+service First {
+ i64 returnOne();
+}
+
+service Second {
+ i64 returnTwo();
+}
+
diff --git a/src/jaegertracing/thrift/lib/go/test/NamesTest.thrift b/src/jaegertracing/thrift/lib/go/test/NamesTest.thrift
new file mode 100644
index 000000000..e7e9563e8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/NamesTest.thrift
@@ -0,0 +1,32 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+struct NamesTest {
+ 1: required string type
+}
+
+service NameCollisionOne
+{
+ void blahBlah()
+}
+
+service NameCollisionTwo
+{
+ void blahBlah()
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/NamespacedTest.thrift b/src/jaegertracing/thrift/lib/go/test/NamespacedTest.thrift
new file mode 100644
index 000000000..a91035062
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/NamespacedTest.thrift
@@ -0,0 +1,40 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+include "ThriftTest.thrift"
+
+namespace go lib.go.test.namespacedtest
+
+enum Stuff {
+ ONE = 1,
+ TWO = 2,
+}
+
+const i32 THREE = 3;
+
+typedef i64 UserId
+
+struct StuffStruct {
+ 2: Stuff stuff,
+}
+
+service NamespacedService {
+ ThriftTest.UserId getUserID(),
+}
+
diff --git a/src/jaegertracing/thrift/lib/go/test/OnewayTest.thrift b/src/jaegertracing/thrift/lib/go/test/OnewayTest.thrift
new file mode 100644
index 000000000..3242f80fd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/OnewayTest.thrift
@@ -0,0 +1,24 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+service OneWay {
+ oneway void hi(1: i64 i, 2: string s)
+ void emptyfunc()
+ i64 echo_int(1: i64 param)
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/OptionalFieldsTest.thrift b/src/jaegertracing/thrift/lib/go/test/OptionalFieldsTest.thrift
new file mode 100644
index 000000000..2afc157c7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/OptionalFieldsTest.thrift
@@ -0,0 +1,50 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+struct structA {
+ 1: required i64 sa_i
+}
+
+struct all_optional {
+ 1: optional string s = "DEFAULT",
+ 2: optional i64 i = 42,
+ 3: optional bool b = false,
+ 4: optional string s2,
+ 5: optional i64 i2,
+ 6: optional bool b2,
+ 7: optional structA aa,
+ 9: optional list<i64> l,
+ 10: optional list<i64> l2 = [1, 2],
+ 11: optional map<i64, i64> m,
+ 12: optional map<i64, i64> m2 = {1:2, 3:4},
+ 13: optional binary bin,
+ 14: optional binary bin2 = "asdf",
+}
+
+struct structB {
+ 1: required structA required_struct_thing
+ 2: optional structA optional_struct_thing
+}
+
+struct structC {
+ 1: string s,
+ 2: required i32 i,
+ 3: optional bool b,
+ 4: required string s2,
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/RefAnnotationFieldsTest.thrift b/src/jaegertracing/thrift/lib/go/test/RefAnnotationFieldsTest.thrift
new file mode 100644
index 000000000..b7f28c28f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/RefAnnotationFieldsTest.thrift
@@ -0,0 +1,58 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+struct structA {
+ 1: required i64 sa_i
+}
+
+struct all_referenced {
+ 1: optional string s = "DEFAULT" (cpp.ref = ""),
+ 2: optional i64 i = 42 (cpp.ref = ""),
+ 3: optional bool b = false (cpp.ref = ""),
+ 4: optional string s2 (cpp.ref = ""),
+ 5: optional i64 i2 (cpp.ref = ""),
+ 6: optional bool b2 (cpp.ref = ""),
+ 7: optional structA aa (cpp.ref = ""),
+ 9: optional list<i64> l (cpp.ref = ""),
+ 10: optional list<i64> l2 = [1, 2] (cpp.ref = ""),
+ 11: optional map<i64, i64> m (cpp.ref = ""),
+ 12: optional map<i64, i64> m2 = {1:2, 3:4} (cpp.ref = ""),
+ 13: optional binary bin (cpp.ref = ""),
+ 14: optional binary bin2 = "asdf" (cpp.ref = ""),
+
+ 15: required string ref_s = "DEFAULT" (cpp.ref = ""),
+ 16: required i64 ref_i = 42 (cpp.ref = ""),
+ 17: required bool ref_b = false (cpp.ref = ""),
+ 18: required string ref_s2 (cpp.ref = ""),
+ 19: required i64 ref_i2 (cpp.ref = ""),
+ 20: required bool ref_b2 (cpp.ref = ""),
+ 21: required structA ref_aa (cpp.ref = ""),
+ 22: required list<i64> ref_l (cpp.ref = ""),
+ 23: required list<i64> ref_l2 = [1, 2] (cpp.ref = ""),
+ 24: required map<i64, i64> ref_m (cpp.ref = ""),
+ 25: required map<i64, i64> ref_m2 = {1:2, 3:4} (cpp.ref = ""),
+ 26: required binary ref_bin (cpp.ref = ""),
+ 27: required binary ref_bin2 = "asdf" (cpp.ref = ""),
+
+}
+
+struct structB {
+ 1: required structA required_struct_thing
+ 2: optional structA optional_struct_thing
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/RequiredFieldTest.thrift b/src/jaegertracing/thrift/lib/go/test/RequiredFieldTest.thrift
new file mode 100644
index 000000000..4a2dcaeba
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/RequiredFieldTest.thrift
@@ -0,0 +1,7 @@
+struct RequiredField {
+ 1: required string name
+}
+
+struct OtherThing {
+ 1: required i16 value
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/ServicesTest.thrift b/src/jaegertracing/thrift/lib/go/test/ServicesTest.thrift
new file mode 100644
index 000000000..882b03acb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/ServicesTest.thrift
@@ -0,0 +1,111 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# We are only testing that generated code compiles, no correctness checking is done
+
+exception moderate_disaster {
+ 1: i32 errorCode,
+ 2: string message
+}
+
+exception total_disaster {
+ 1: string message
+ 2: optional bool president_was_woken_up = false
+}
+
+struct struct_a {
+ 1: required i64 whatever
+}
+
+service a_serv {
+ void voidfunc(),
+ void void_with_1ex() throws(1: moderate_disaster err1)
+ void void_with_2ex() throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ string stringfunc()
+ string stringfunc_1ex() throws(1: moderate_disaster err1)
+ string stringfunc_2ex() throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ i64 i64func()
+ i64 i64func_1ex() throws(1: moderate_disaster err1)
+ i64 i64func_2ex() throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ list<string> list_of_strings_func()
+ list<string> list_of_strings_func_1ex() throws(1: moderate_disaster err1)
+ list<string> list_of_strings_func_2ex() throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ map<i64,string> map_func()
+ map<i64,string> map_func_1ex() throws(1: moderate_disaster err1)
+ map<i64,string> map_func_2ex() throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ struct_a struct_a_func()
+ struct_a struct_a_func_1ex() throws(1: moderate_disaster err1)
+ struct_a struct_a_func_2ex() throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ void voidfunc_1int(1: i64 i),
+ void void_with_1ex_1int(1: i64 i) throws(1: moderate_disaster err1)
+ void void_with_2ex_1int(1: i64 i) throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ string stringfunc_1int(1: i64 i)
+ string stringfunc_1ex_1int(1: i64 i) throws(1: moderate_disaster err1)
+ string stringfunc_2ex_1int(1: i64 i) throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ i64 i64func_1int(1: i64 i)
+ i64 i64func_1ex_1int(1: i64 i) throws(1: moderate_disaster err1)
+ i64 i64func_2ex_1int(1: i64 i) throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ list<string> list_of_strings_func_1int(1: i64 i)
+ list<string> list_of_strings_func_1ex_1int(1: i64 i) throws(1: moderate_disaster err1)
+ list<string> list_of_strings_func_2ex_1int(1: i64 i) throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ map<i64,string> map_func_1int(1: i64 i)
+ map<i64,string> map_func_1ex_1int(1: i64 i) throws(1: moderate_disaster err1)
+ map<i64,string> map_func_2ex_1int(1: i64 i) throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ struct_a struct_a_func_1int(1: i64 i)
+ struct_a struct_a_func_1ex_1int(1: i64 i) throws(1: moderate_disaster err1)
+ struct_a struct_a_func_2ex_1int(1: i64 i) throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ void voidfunc_1int_1s(1: i64 i, 2: string s),
+ void void_with_1ex_1int_1s(1: i64 i, 2: string s) throws(1: moderate_disaster err1)
+ void void_with_2ex_1int_1s(1: i64 i, 2: string s) throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ string stringfunc_1int_1s(1: i64 i, 2: string s)
+ string stringfunc_1ex_1int_1s(1: i64 i, 2: string s) throws(1: moderate_disaster err1)
+ string stringfunc_2ex_1int_1s(1: i64 i, 2: string s) throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ i64 i64func_1int_1s(1: i64 i, 2: string s)
+ i64 i64func_1ex_1int_1s(1: i64 i, 2: string s) throws(1: moderate_disaster err1)
+ i64 i64func_2ex_1int_1s(1: i64 i, 2: string s) throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ list<string> list_of_strings_func_1int_1s(1: i64 i, 2: string s)
+ list<string> list_of_strings_func_1ex_1int_1s(1: i64 i, 2: string s) throws(1: moderate_disaster err1)
+ list<string> list_of_strings_func_2ex_1int_1s(1: i64 i, 2: string s) throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ map<i64,string> map_func_1int_1s(1: i64 i, 2: string s)
+ map<i64,string> map_func_1ex_1int_1s(1: i64 i, 2: string s) throws(1: moderate_disaster err1)
+ map<i64,string> map_func_2ex_1int_1s(1: i64 i, 2: string s) throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ struct_a struct_a_func_1int_1s(1: i64 i, 2: string s)
+ struct_a struct_a_func_1ex_1int_1s(1: i64 i, 2: string s) throws(1: moderate_disaster err1)
+ struct_a struct_a_func_2ex_1int_1s(1: i64 i, 2: string s) throws(1: moderate_disaster err1, 2:total_disaster err2)
+
+ struct_a struct_a_func_1struct_a(1: struct_a st)
+
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/TypedefFieldTest.thrift b/src/jaegertracing/thrift/lib/go/test/TypedefFieldTest.thrift
new file mode 100644
index 000000000..390e8c88e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/TypedefFieldTest.thrift
@@ -0,0 +1,39 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# We are only testing that generated code compiles, no correctness checking is done
+
+enum Details {
+ Everything = 0
+ StateOnly = 1
+ StateAndOptions = 2
+ SomethingElse = 3
+}
+
+typedef list< Details> DetailsWanted
+
+struct BaseRequest {
+ 1 : optional string RequestID
+}
+
+struct GetMyDetails {
+ 1 : required BaseRequest base_
+ 2 : required string ObjectID
+ 3 : optional DetailsWanted DetailsWanted
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/UnionBinaryTest.thrift b/src/jaegertracing/thrift/lib/go/test/UnionBinaryTest.thrift
new file mode 100644
index 000000000..f77112bda
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/UnionBinaryTest.thrift
@@ -0,0 +1,25 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# See https://issues.apache.org/jira/browse/THRIFT-4573
+union Sample {
+ 1: map<string, string> u1,
+ 2: binary u2,
+ 3: list<string> u3
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/UnionDefaultValueTest.thrift b/src/jaegertracing/thrift/lib/go/test/UnionDefaultValueTest.thrift
new file mode 100644
index 000000000..4c9348003
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/UnionDefaultValueTest.thrift
@@ -0,0 +1,34 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+struct Option1 {
+}
+
+struct Option2 {
+ 1: optional string name
+}
+
+union Descendant {
+ 1: Option1 option1
+ 2: Option2 option2
+}
+
+struct TestStruct {
+ 1: optional Descendant descendant = { "option1": {}}
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/dontexportrwtest/compile_test.go b/src/jaegertracing/thrift/lib/go/test/dontexportrwtest/compile_test.go
new file mode 100644
index 000000000..cf6763e29
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/dontexportrwtest/compile_test.go
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package dontexportrwtest
+
+import (
+ "testing"
+)
+
+// Make sure that thrift generates non-exported read/write methods if
+// read_write_private option is specified
+func TestReadWriteMethodsArePrivate(t *testing.T) {
+ // This will only compile if read/write methods exist
+ s := NewTestStruct()
+ _ = s.read
+ _ = s.write
+
+ is := NewInnerStruct()
+ _ = is.read
+ _ = is.write
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/binary_key_test.go b/src/jaegertracing/thrift/lib/go/test/tests/binary_key_test.go
new file mode 100644
index 000000000..aa9619391
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/binary_key_test.go
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ "binarykeytest"
+ "testing"
+)
+
+func TestBinaryMapKeyGeneratesString(t *testing.T) {
+ s := binarykeytest.NewTestStruct()
+ //This will only compile if BinToString has type of map[string]string
+ s.BinToString = make(map[string]string)
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/client_error_test.go b/src/jaegertracing/thrift/lib/go/test/tests/client_error_test.go
new file mode 100644
index 000000000..fdec4ea57
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/client_error_test.go
@@ -0,0 +1,873 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ "context"
+ "errors"
+ "errortest"
+ "testing"
+ "thrift"
+
+ "github.com/golang/mock/gomock"
+)
+
+// TestCase: Comprehensive call and reply workflow in the client.
+// Setup mock to fail at a certain position. Return true if position exists otherwise false.
+func prepareClientCallReply(protocol *MockTProtocol, failAt int, failWith error) bool {
+ var err error = nil
+
+ if failAt == 0 {
+ err = failWith
+ }
+ last := protocol.EXPECT().WriteMessageBegin("testStruct", thrift.CALL, int32(1)).Return(err)
+ if failAt == 0 {
+ return true
+ }
+ if failAt == 1 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteStructBegin("testStruct_args").Return(err).After(last)
+ if failAt == 1 {
+ return true
+ }
+ if failAt == 2 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteFieldBegin("thing", thrift.TType(thrift.STRUCT), int16(1)).Return(err).After(last)
+ if failAt == 2 {
+ return true
+ }
+ if failAt == 3 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteStructBegin("TestStruct").Return(err).After(last)
+ if failAt == 3 {
+ return true
+ }
+ if failAt == 4 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteFieldBegin("m", thrift.TType(thrift.MAP), int16(1)).Return(err).After(last)
+ if failAt == 4 {
+ return true
+ }
+ if failAt == 5 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteMapBegin(thrift.TType(thrift.STRING), thrift.TType(thrift.STRING), 0).Return(err).After(last)
+ if failAt == 5 {
+ return true
+ }
+ if failAt == 6 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteMapEnd().Return(err).After(last)
+ if failAt == 6 {
+ return true
+ }
+ if failAt == 7 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteFieldEnd().Return(err).After(last)
+ if failAt == 7 {
+ return true
+ }
+ if failAt == 8 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteFieldBegin("l", thrift.TType(thrift.LIST), int16(2)).Return(err).After(last)
+ if failAt == 8 {
+ return true
+ }
+ if failAt == 9 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteListBegin(thrift.TType(thrift.STRING), 0).Return(err).After(last)
+ if failAt == 9 {
+ return true
+ }
+ if failAt == 10 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteListEnd().Return(err).After(last)
+ if failAt == 10 {
+ return true
+ }
+ if failAt == 11 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteFieldEnd().Return(err).After(last)
+ if failAt == 11 {
+ return true
+ }
+ if failAt == 12 {
+ err = failWith
+ }
+
+ last = protocol.EXPECT().WriteFieldBegin("s", thrift.TType(thrift.SET), int16(3)).Return(err).After(last)
+ if failAt == 12 {
+ return true
+ }
+ if failAt == 13 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteSetBegin(thrift.TType(thrift.STRING), 0).Return(err).After(last)
+ if failAt == 13 {
+ return true
+ }
+ if failAt == 14 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteSetEnd().Return(err).After(last)
+ if failAt == 14 {
+ return true
+ }
+ if failAt == 15 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteFieldEnd().Return(err).After(last)
+ if failAt == 15 {
+ return true
+ }
+ if failAt == 16 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteFieldBegin("i", thrift.TType(thrift.I32), int16(4)).Return(err).After(last)
+ if failAt == 16 {
+ return true
+ }
+ if failAt == 17 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteI32(int32(3)).Return(err).After(last)
+ if failAt == 17 {
+ return true
+ }
+ if failAt == 18 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteFieldEnd().Return(err).After(last)
+ if failAt == 18 {
+ return true
+ }
+ if failAt == 19 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteFieldStop().Return(err).After(last)
+ if failAt == 19 {
+ return true
+ }
+ if failAt == 20 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteStructEnd().Return(err).After(last)
+ if failAt == 20 {
+ return true
+ }
+ if failAt == 21 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteFieldEnd().Return(err).After(last)
+ if failAt == 21 {
+ return true
+ }
+ if failAt == 22 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteFieldStop().Return(err).After(last)
+ if failAt == 22 {
+ return true
+ }
+ if failAt == 23 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteStructEnd().Return(err).After(last)
+ if failAt == 23 {
+ return true
+ }
+ if failAt == 24 {
+ err = failWith
+ }
+ last = protocol.EXPECT().WriteMessageEnd().Return(err).After(last)
+ if failAt == 24 {
+ return true
+ }
+ if failAt == 25 {
+ err = failWith
+ }
+ last = protocol.EXPECT().Flush(context.Background()).Return(err).After(last)
+ if failAt == 25 {
+ return true
+ }
+ if failAt == 26 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadMessageBegin().Return("testStruct", thrift.REPLY, int32(1), err).After(last)
+ if failAt == 26 {
+ return true
+ }
+ if failAt == 27 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadStructBegin().Return("testStruct_args", err).After(last)
+ if failAt == 27 {
+ return true
+ }
+ if failAt == 28 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadFieldBegin().Return("_", thrift.TType(thrift.STRUCT), int16(0), err).After(last)
+ if failAt == 28 {
+ return true
+ }
+ if failAt == 29 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadStructBegin().Return("TestStruct", err).After(last)
+ if failAt == 29 {
+ return true
+ }
+ if failAt == 30 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadFieldBegin().Return("m", thrift.TType(thrift.MAP), int16(1), err).After(last)
+ if failAt == 30 {
+ return true
+ }
+ if failAt == 31 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadMapBegin().Return(thrift.TType(thrift.STRING), thrift.TType(thrift.STRING), 0, err).After(last)
+ if failAt == 31 {
+ return true
+ }
+ if failAt == 32 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadMapEnd().Return(err).After(last)
+ if failAt == 32 {
+ return true
+ }
+ if failAt == 33 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadFieldEnd().Return(err).After(last)
+ if failAt == 33 {
+ return true
+ }
+ if failAt == 34 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadFieldBegin().Return("l", thrift.TType(thrift.LIST), int16(2), err).After(last)
+ if failAt == 34 {
+ return true
+ }
+ if failAt == 35 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadListBegin().Return(thrift.TType(thrift.STRING), 0, err).After(last)
+ if failAt == 35 {
+ return true
+ }
+ if failAt == 36 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadListEnd().Return(err).After(last)
+ if failAt == 36 {
+ return true
+ }
+ if failAt == 37 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadFieldEnd().Return(err).After(last)
+ if failAt == 37 {
+ return true
+ }
+ if failAt == 38 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadFieldBegin().Return("s", thrift.TType(thrift.SET), int16(3), err).After(last)
+ if failAt == 38 {
+ return true
+ }
+ if failAt == 39 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadSetBegin().Return(thrift.TType(thrift.STRING), 0, err).After(last)
+ if failAt == 39 {
+ return true
+ }
+ if failAt == 40 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadSetEnd().Return(err).After(last)
+ if failAt == 40 {
+ return true
+ }
+ if failAt == 41 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadFieldEnd().Return(err).After(last)
+ if failAt == 41 {
+ return true
+ }
+ if failAt == 42 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadFieldBegin().Return("i", thrift.TType(thrift.I32), int16(4), err).After(last)
+ if failAt == 42 {
+ return true
+ }
+ if failAt == 43 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadI32().Return(int32(3), err).After(last)
+ if failAt == 43 {
+ return true
+ }
+ if failAt == 44 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadFieldEnd().Return(err).After(last)
+ if failAt == 44 {
+ return true
+ }
+ if failAt == 45 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadFieldBegin().Return("_", thrift.TType(thrift.STOP), int16(5), err).After(last)
+ if failAt == 45 {
+ return true
+ }
+ if failAt == 46 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadStructEnd().Return(err).After(last)
+ if failAt == 46 {
+ return true
+ }
+ if failAt == 47 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadFieldEnd().Return(err).After(last)
+ if failAt == 47 {
+ return true
+ }
+ if failAt == 48 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadFieldBegin().Return("_", thrift.TType(thrift.STOP), int16(1), err).After(last)
+ if failAt == 48 {
+ return true
+ }
+ if failAt == 49 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadStructEnd().Return(err).After(last)
+ if failAt == 49 {
+ return true
+ }
+ if failAt == 50 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadMessageEnd().Return(err).After(last)
+ if failAt == 50 {
+ return true
+ }
+ return false
+}
+
+// TestCase: Comprehensive call and reply workflow in the client.
+// Expecting TTransportError on fail.
+func TestClientReportTTransportErrors(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+
+ thing := errortest.NewTestStruct()
+ thing.M = make(map[string]string)
+ thing.L = make([]string, 0)
+ thing.S = make([]string, 0)
+ thing.I = 3
+
+ err := thrift.NewTTransportException(thrift.TIMED_OUT, "test")
+ for i := 0; ; i++ {
+ protocol := NewMockTProtocol(mockCtrl)
+ if !prepareClientCallReply(protocol, i, err) {
+ return
+ }
+ client := errortest.NewErrorTestClient(thrift.NewTStandardClient(protocol, protocol))
+ _, retErr := client.TestStruct(defaultCtx, thing)
+ mockCtrl.Finish()
+ mockCtrl = gomock.NewController(t)
+ err2, ok := retErr.(thrift.TTransportException)
+ if !ok {
+ t.Fatal("Expected a TTrasportException")
+ }
+
+ if err2.TypeId() != thrift.TIMED_OUT {
+ t.Fatal("Expected TIMED_OUT error")
+ }
+ }
+}
+
+// TestCase: Comprehensive call and reply workflow in the client.
+// Expecting TTransportError on fail.
+// Similar to TestClientReportTTransportErrors, but using legacy client constructor.
+func TestClientReportTTransportErrorsLegacy(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ transport := thrift.NewTMemoryBuffer()
+ thing := errortest.NewTestStruct()
+ thing.M = make(map[string]string)
+ thing.L = make([]string, 0)
+ thing.S = make([]string, 0)
+ thing.I = 3
+
+ err := thrift.NewTTransportException(thrift.TIMED_OUT, "test")
+ for i := 0; ; i++ {
+ protocol := NewMockTProtocol(mockCtrl)
+ if !prepareClientCallReply(protocol, i, err) {
+ return
+ }
+ client := errortest.NewErrorTestClientProtocol(transport, protocol, protocol)
+ _, retErr := client.TestStruct(defaultCtx, thing)
+ mockCtrl.Finish()
+ mockCtrl = gomock.NewController(t)
+ err2, ok := retErr.(thrift.TTransportException)
+ if !ok {
+ t.Fatal("Expected a TTrasportException")
+ }
+
+ if err2.TypeId() != thrift.TIMED_OUT {
+ t.Fatal("Expected TIMED_OUT error")
+ }
+ }
+}
+
+// TestCase: Comprehensive call and reply workflow in the client.
+// Expecting TTProtocolErrors on fail.
+func TestClientReportTProtocolErrors(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+
+ thing := errortest.NewTestStruct()
+ thing.M = make(map[string]string)
+ thing.L = make([]string, 0)
+ thing.S = make([]string, 0)
+ thing.I = 3
+
+ err := thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, errors.New("test"))
+ for i := 0; ; i++ {
+ protocol := NewMockTProtocol(mockCtrl)
+ if !prepareClientCallReply(protocol, i, err) {
+ return
+ }
+ client := errortest.NewErrorTestClient(thrift.NewTStandardClient(protocol, protocol))
+ _, retErr := client.TestStruct(defaultCtx, thing)
+ mockCtrl.Finish()
+ mockCtrl = gomock.NewController(t)
+ err2, ok := retErr.(thrift.TProtocolException)
+ if !ok {
+ t.Fatal("Expected a TProtocolException")
+ }
+ if err2.TypeId() != thrift.INVALID_DATA {
+ t.Fatal("Expected INVALID_DATA error")
+ }
+ }
+}
+
+// TestCase: Comprehensive call and reply workflow in the client.
+// Expecting TTProtocolErrors on fail.
+// Similar to TestClientReportTProtocolErrors, but using legacy client constructor.
+func TestClientReportTProtocolErrorsLegacy(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ transport := thrift.NewTMemoryBuffer()
+ thing := errortest.NewTestStruct()
+ thing.M = make(map[string]string)
+ thing.L = make([]string, 0)
+ thing.S = make([]string, 0)
+ thing.I = 3
+
+ err := thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, errors.New("test"))
+ for i := 0; ; i++ {
+ protocol := NewMockTProtocol(mockCtrl)
+ if !prepareClientCallReply(protocol, i, err) {
+ return
+ }
+ client := errortest.NewErrorTestClientProtocol(transport, protocol, protocol)
+ _, retErr := client.TestStruct(defaultCtx, thing)
+ mockCtrl.Finish()
+ mockCtrl = gomock.NewController(t)
+ err2, ok := retErr.(thrift.TProtocolException)
+ if !ok {
+ t.Fatal("Expected a TProtocolException")
+ }
+ if err2.TypeId() != thrift.INVALID_DATA {
+ t.Fatal("Expected INVALID_DATA error")
+ }
+ }
+}
+
+// TestCase: call and reply with exception workflow in the client.
+// Setup mock to fail at a certain position. Return true if position exists otherwise false.
+func prepareClientCallException(protocol *MockTProtocol, failAt int, failWith error) bool {
+ var err error = nil
+
+ // No need to test failure in this block, because it is covered in other test cases
+ last := protocol.EXPECT().WriteMessageBegin("testString", thrift.CALL, int32(1))
+ last = protocol.EXPECT().WriteStructBegin("testString_args").After(last)
+ last = protocol.EXPECT().WriteFieldBegin("s", thrift.TType(thrift.STRING), int16(1)).After(last)
+ last = protocol.EXPECT().WriteString("test").After(last)
+ last = protocol.EXPECT().WriteFieldEnd().After(last)
+ last = protocol.EXPECT().WriteFieldStop().After(last)
+ last = protocol.EXPECT().WriteStructEnd().After(last)
+ last = protocol.EXPECT().WriteMessageEnd().After(last)
+ last = protocol.EXPECT().Flush(context.Background()).After(last)
+
+ // Reading the exception, might fail.
+ if failAt == 0 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadMessageBegin().Return("testString", thrift.EXCEPTION, int32(1), err).After(last)
+ if failAt == 0 {
+ return true
+ }
+ if failAt == 1 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadStructBegin().Return("TApplicationException", err).After(last)
+ if failAt == 1 {
+ return true
+ }
+ if failAt == 2 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadFieldBegin().Return("message", thrift.TType(thrift.STRING), int16(1), err).After(last)
+ if failAt == 2 {
+ return true
+ }
+ if failAt == 3 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadString().Return("test", err).After(last)
+ if failAt == 3 {
+ return true
+ }
+ if failAt == 4 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadFieldEnd().Return(err).After(last)
+ if failAt == 4 {
+ return true
+ }
+ if failAt == 5 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadFieldBegin().Return("type", thrift.TType(thrift.I32), int16(2), err).After(last)
+ if failAt == 5 {
+ return true
+ }
+ if failAt == 6 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadI32().Return(int32(thrift.PROTOCOL_ERROR), err).After(last)
+ if failAt == 6 {
+ return true
+ }
+ if failAt == 7 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadFieldEnd().Return(err).After(last)
+ if failAt == 7 {
+ return true
+ }
+ if failAt == 8 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadFieldBegin().Return("_", thrift.TType(thrift.STOP), int16(2), err).After(last)
+ if failAt == 8 {
+ return true
+ }
+ if failAt == 9 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadStructEnd().Return(err).After(last)
+ if failAt == 9 {
+ return true
+ }
+ if failAt == 10 {
+ err = failWith
+ }
+ last = protocol.EXPECT().ReadMessageEnd().Return(err).After(last)
+ if failAt == 10 {
+ return true
+ }
+
+ return false
+}
+
+// TestCase: call and reply with exception workflow in the client.
+func TestClientCallException(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+
+ err := thrift.NewTTransportException(thrift.TIMED_OUT, "test")
+ for i := 0; ; i++ {
+ protocol := NewMockTProtocol(mockCtrl)
+ willComplete := !prepareClientCallException(protocol, i, err)
+
+ client := errortest.NewErrorTestClient(thrift.NewTStandardClient(protocol, protocol))
+ _, retErr := client.TestString(defaultCtx, "test")
+ mockCtrl.Finish()
+ mockCtrl = gomock.NewController(t)
+
+ if !willComplete {
+ err2, ok := retErr.(thrift.TTransportException)
+ if !ok {
+ t.Fatal("Expected a TTransportException")
+ }
+ if err2.TypeId() != thrift.TIMED_OUT {
+ t.Fatal("Expected TIMED_OUT error")
+ }
+ } else {
+ err2, ok := retErr.(thrift.TApplicationException)
+ if !ok {
+ t.Fatal("Expected a TApplicationException")
+ }
+ if err2.TypeId() != thrift.PROTOCOL_ERROR {
+ t.Fatal("Expected PROTOCOL_ERROR error")
+ }
+ break
+ }
+ }
+}
+
+// TestCase: call and reply with exception workflow in the client.
+// Similar to TestClientCallException, but using legacy client constructor.
+func TestClientCallExceptionLegacy(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ transport := thrift.NewTMemoryBuffer()
+ err := thrift.NewTTransportException(thrift.TIMED_OUT, "test")
+ for i := 0; ; i++ {
+ protocol := NewMockTProtocol(mockCtrl)
+ willComplete := !prepareClientCallException(protocol, i, err)
+
+ client := errortest.NewErrorTestClientProtocol(transport, protocol, protocol)
+ _, retErr := client.TestString(defaultCtx, "test")
+ mockCtrl.Finish()
+ mockCtrl = gomock.NewController(t)
+
+ if !willComplete {
+ err2, ok := retErr.(thrift.TTransportException)
+ if !ok {
+ t.Fatal("Expected a TTransportException")
+ }
+ if err2.TypeId() != thrift.TIMED_OUT {
+ t.Fatal("Expected TIMED_OUT error")
+ }
+ } else {
+ err2, ok := retErr.(thrift.TApplicationException)
+ if !ok {
+ t.Fatal("Expected a TApplicationException")
+ }
+ if err2.TypeId() != thrift.PROTOCOL_ERROR {
+ t.Fatal("Expected PROTOCOL_ERROR error")
+ }
+ break
+ }
+ }
+}
+
+// TestCase: Mismatching sequence id has been received in the client.
+func TestClientSeqIdMismatch(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ protocol := NewMockTProtocol(mockCtrl)
+ gomock.InOrder(
+ protocol.EXPECT().WriteMessageBegin("testString", thrift.CALL, int32(1)),
+ protocol.EXPECT().WriteStructBegin("testString_args"),
+ protocol.EXPECT().WriteFieldBegin("s", thrift.TType(thrift.STRING), int16(1)),
+ protocol.EXPECT().WriteString("test"),
+ protocol.EXPECT().WriteFieldEnd(),
+ protocol.EXPECT().WriteFieldStop(),
+ protocol.EXPECT().WriteStructEnd(),
+ protocol.EXPECT().WriteMessageEnd(),
+ protocol.EXPECT().Flush(context.Background()),
+ protocol.EXPECT().ReadMessageBegin().Return("testString", thrift.REPLY, int32(2), nil),
+ )
+
+ client := errortest.NewErrorTestClient(thrift.NewTStandardClient(protocol, protocol))
+ _, err := client.TestString(defaultCtx, "test")
+ mockCtrl.Finish()
+ appErr, ok := err.(thrift.TApplicationException)
+ if !ok {
+ t.Fatal("Expected TApplicationException")
+ }
+ if appErr.TypeId() != thrift.BAD_SEQUENCE_ID {
+ t.Fatal("Expected BAD_SEQUENCE_ID error")
+ }
+}
+
+// TestCase: Mismatching sequence id has been received in the client.
+// Similar to TestClientSeqIdMismatch, but using legacy client constructor.
+func TestClientSeqIdMismatchLegeacy(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ transport := thrift.NewTMemoryBuffer()
+ protocol := NewMockTProtocol(mockCtrl)
+ gomock.InOrder(
+ protocol.EXPECT().WriteMessageBegin("testString", thrift.CALL, int32(1)),
+ protocol.EXPECT().WriteStructBegin("testString_args"),
+ protocol.EXPECT().WriteFieldBegin("s", thrift.TType(thrift.STRING), int16(1)),
+ protocol.EXPECT().WriteString("test"),
+ protocol.EXPECT().WriteFieldEnd(),
+ protocol.EXPECT().WriteFieldStop(),
+ protocol.EXPECT().WriteStructEnd(),
+ protocol.EXPECT().WriteMessageEnd(),
+ protocol.EXPECT().Flush(context.Background()),
+ protocol.EXPECT().ReadMessageBegin().Return("testString", thrift.REPLY, int32(2), nil),
+ )
+
+ client := errortest.NewErrorTestClientProtocol(transport, protocol, protocol)
+ _, err := client.TestString(defaultCtx, "test")
+ mockCtrl.Finish()
+ appErr, ok := err.(thrift.TApplicationException)
+ if !ok {
+ t.Fatal("Expected TApplicationException")
+ }
+ if appErr.TypeId() != thrift.BAD_SEQUENCE_ID {
+ t.Fatal("Expected BAD_SEQUENCE_ID error")
+ }
+}
+
+// TestCase: Wrong method name has been received in the client.
+func TestClientWrongMethodName(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ protocol := NewMockTProtocol(mockCtrl)
+ gomock.InOrder(
+ protocol.EXPECT().WriteMessageBegin("testString", thrift.CALL, int32(1)),
+ protocol.EXPECT().WriteStructBegin("testString_args"),
+ protocol.EXPECT().WriteFieldBegin("s", thrift.TType(thrift.STRING), int16(1)),
+ protocol.EXPECT().WriteString("test"),
+ protocol.EXPECT().WriteFieldEnd(),
+ protocol.EXPECT().WriteFieldStop(),
+ protocol.EXPECT().WriteStructEnd(),
+ protocol.EXPECT().WriteMessageEnd(),
+ protocol.EXPECT().Flush(context.Background()),
+ protocol.EXPECT().ReadMessageBegin().Return("unknown", thrift.REPLY, int32(1), nil),
+ )
+
+ client := errortest.NewErrorTestClient(thrift.NewTStandardClient(protocol, protocol))
+ _, err := client.TestString(defaultCtx, "test")
+ mockCtrl.Finish()
+ appErr, ok := err.(thrift.TApplicationException)
+ if !ok {
+ t.Fatal("Expected TApplicationException")
+ }
+ if appErr.TypeId() != thrift.WRONG_METHOD_NAME {
+ t.Fatal("Expected WRONG_METHOD_NAME error")
+ }
+}
+
+// TestCase: Wrong method name has been received in the client.
+// Similar to TestClientWrongMethodName, but using legacy client constructor.
+func TestClientWrongMethodNameLegacy(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ transport := thrift.NewTMemoryBuffer()
+ protocol := NewMockTProtocol(mockCtrl)
+ gomock.InOrder(
+ protocol.EXPECT().WriteMessageBegin("testString", thrift.CALL, int32(1)),
+ protocol.EXPECT().WriteStructBegin("testString_args"),
+ protocol.EXPECT().WriteFieldBegin("s", thrift.TType(thrift.STRING), int16(1)),
+ protocol.EXPECT().WriteString("test"),
+ protocol.EXPECT().WriteFieldEnd(),
+ protocol.EXPECT().WriteFieldStop(),
+ protocol.EXPECT().WriteStructEnd(),
+ protocol.EXPECT().WriteMessageEnd(),
+ protocol.EXPECT().Flush(context.Background()),
+ protocol.EXPECT().ReadMessageBegin().Return("unknown", thrift.REPLY, int32(1), nil),
+ )
+
+ client := errortest.NewErrorTestClientProtocol(transport, protocol, protocol)
+ _, err := client.TestString(defaultCtx, "test")
+ mockCtrl.Finish()
+ appErr, ok := err.(thrift.TApplicationException)
+ if !ok {
+ t.Fatal("Expected TApplicationException")
+ }
+ if appErr.TypeId() != thrift.WRONG_METHOD_NAME {
+ t.Fatal("Expected WRONG_METHOD_NAME error")
+ }
+}
+
+// TestCase: Wrong message type has been received in the client.
+func TestClientWrongMessageType(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ protocol := NewMockTProtocol(mockCtrl)
+ gomock.InOrder(
+ protocol.EXPECT().WriteMessageBegin("testString", thrift.CALL, int32(1)),
+ protocol.EXPECT().WriteStructBegin("testString_args"),
+ protocol.EXPECT().WriteFieldBegin("s", thrift.TType(thrift.STRING), int16(1)),
+ protocol.EXPECT().WriteString("test"),
+ protocol.EXPECT().WriteFieldEnd(),
+ protocol.EXPECT().WriteFieldStop(),
+ protocol.EXPECT().WriteStructEnd(),
+ protocol.EXPECT().WriteMessageEnd(),
+ protocol.EXPECT().Flush(context.Background()),
+ protocol.EXPECT().ReadMessageBegin().Return("testString", thrift.INVALID_TMESSAGE_TYPE, int32(1), nil),
+ )
+
+ client := errortest.NewErrorTestClient(thrift.NewTStandardClient(protocol, protocol))
+ _, err := client.TestString(defaultCtx, "test")
+ mockCtrl.Finish()
+ appErr, ok := err.(thrift.TApplicationException)
+ if !ok {
+ t.Fatal("Expected TApplicationException")
+ }
+ if appErr.TypeId() != thrift.INVALID_MESSAGE_TYPE_EXCEPTION {
+ t.Fatal("Expected INVALID_MESSAGE_TYPE_EXCEPTION error")
+ }
+}
+
+// TestCase: Wrong message type has been received in the client.
+// Similar to TestClientWrongMessageType, but using legacy client constructor.
+func TestClientWrongMessageTypeLegacy(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ transport := thrift.NewTMemoryBuffer()
+ protocol := NewMockTProtocol(mockCtrl)
+ gomock.InOrder(
+ protocol.EXPECT().WriteMessageBegin("testString", thrift.CALL, int32(1)),
+ protocol.EXPECT().WriteStructBegin("testString_args"),
+ protocol.EXPECT().WriteFieldBegin("s", thrift.TType(thrift.STRING), int16(1)),
+ protocol.EXPECT().WriteString("test"),
+ protocol.EXPECT().WriteFieldEnd(),
+ protocol.EXPECT().WriteFieldStop(),
+ protocol.EXPECT().WriteStructEnd(),
+ protocol.EXPECT().WriteMessageEnd(),
+ protocol.EXPECT().Flush(context.Background()),
+ protocol.EXPECT().ReadMessageBegin().Return("testString", thrift.INVALID_TMESSAGE_TYPE, int32(1), nil),
+ )
+
+ client := errortest.NewErrorTestClientProtocol(transport, protocol, protocol)
+ _, err := client.TestString(defaultCtx, "test")
+ mockCtrl.Finish()
+ appErr, ok := err.(thrift.TApplicationException)
+ if !ok {
+ t.Fatal("Expected TApplicationException")
+ }
+ if appErr.TypeId() != thrift.INVALID_MESSAGE_TYPE_EXCEPTION {
+ t.Fatal("Expected INVALID_MESSAGE_TYPE_EXCEPTION error")
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/context.go b/src/jaegertracing/thrift/lib/go/test/tests/context.go
new file mode 100644
index 000000000..a93a82b8f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/context.go
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ "context"
+)
+
+var defaultCtx = context.Background()
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/encoding_json_test.go b/src/jaegertracing/thrift/lib/go/test/tests/encoding_json_test.go
new file mode 100644
index 000000000..12d4566fb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/encoding_json_test.go
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ "encoding"
+ "encoding/json"
+ "testing"
+ "thrifttest"
+)
+
+func TestEnumIsTextMarshaller(t *testing.T) {
+ one := thrifttest.Numberz_ONE
+ var tm encoding.TextMarshaler = one
+ b, err := tm.MarshalText()
+ if err != nil {
+ t.Fatalf("Unexpected error from MarshalText: %s", err)
+ }
+ if string(b) != one.String() {
+ t.Errorf("MarshalText(%s) = %s, expected = %s", one, b, one)
+ }
+}
+
+func TestEnumIsTextUnmarshaller(t *testing.T) {
+ var tm encoding.TextUnmarshaler = thrifttest.NumberzPtr(thrifttest.Numberz_TWO)
+ err := tm.UnmarshalText([]byte("TWO"))
+ if err != nil {
+ t.Fatalf("Unexpected error from UnmarshalText(TWO): %s", err)
+ }
+ if *(tm.(*thrifttest.Numberz)) != thrifttest.Numberz_TWO {
+ t.Errorf("UnmarshalText(TWO) = %s", tm)
+ }
+
+ err = tm.UnmarshalText([]byte("NAN"))
+ if err == nil {
+ t.Errorf("Error from UnmarshalText(NAN)")
+ }
+}
+
+func TestJSONMarshalUnmarshal(t *testing.T) {
+ s1 := thrifttest.StructB{
+ Aa: &thrifttest.StructA{S: "Aa"},
+ Ab: &thrifttest.StructA{S: "Ab"},
+ }
+
+ b, err := json.Marshal(s1)
+ if err != nil {
+ t.Fatalf("Unexpected error from json.Marshal: %s", err)
+ }
+
+ s2 := thrifttest.StructB{}
+ err = json.Unmarshal(b, &s2)
+ if err != nil {
+ t.Fatalf("Unexpected error from json.Unmarshal: %s", err)
+ }
+
+ if *s1.Aa != *s2.Aa || *s1.Ab != *s2.Ab {
+ t.Logf("s1 = %+v", s1)
+ t.Logf("s2 = %+v", s2)
+ t.Errorf("json: Unmarshal(Marshal(s)) != s")
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/gotag_test.go b/src/jaegertracing/thrift/lib/go/test/tests/gotag_test.go
new file mode 100644
index 000000000..ff2f14ecf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/gotag_test.go
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ "gotagtest"
+ "reflect"
+ "testing"
+)
+
+func TestDefaultTag(t *testing.T) {
+ s := gotagtest.Tagged{}
+ st := reflect.TypeOf(s)
+ field, ok := st.FieldByName("StringThing")
+ if !ok || field.Tag.Get("json") != "string_thing" {
+ t.Error("Unexpected default tag value")
+ }
+}
+
+func TestCustomTag(t *testing.T) {
+ s := gotagtest.Tagged{}
+ st := reflect.TypeOf(s)
+ field, ok := st.FieldByName("IntThing")
+ if !ok || field.Tag.Get("json") != "int_thing,string" {
+ t.Error("Unexpected custom tag value")
+ }
+}
+
+func TestOptionalTag(t *testing.T) {
+ s := gotagtest.Tagged{}
+ st := reflect.TypeOf(s)
+ field, ok := st.FieldByName("OptionalIntThing")
+ if !ok || field.Tag.Get("json") != "optional_int_thing,omitempty" {
+ t.Error("Unexpected default tag value for optional field")
+ }
+}
+
+func TestOptionalTagWithDefaultValue(t *testing.T) {
+ s := gotagtest.Tagged{}
+ st := reflect.TypeOf(s)
+ field, ok := st.FieldByName("OptionalBoolThing")
+ if !ok || field.Tag.Get("json") != "optional_bool_thing" {
+ t.Error("Unexpected default tag value for optional field that has a default value")
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/ignoreinitialisms_test.go b/src/jaegertracing/thrift/lib/go/test/tests/ignoreinitialisms_test.go
new file mode 100644
index 000000000..3cd5f6509
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/ignoreinitialisms_test.go
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ "ignoreinitialismstest"
+ "reflect"
+ "testing"
+)
+
+func TestIgnoreInitialismsFlagIsHonoured(t *testing.T) {
+ s := ignoreinitialismstest.IgnoreInitialismsTest{}
+ st := reflect.TypeOf(s)
+ _, ok := st.FieldByName("Id")
+ if !ok {
+ t.Error("Id attribute is missing!")
+ }
+ _, ok = st.FieldByName("MyId")
+ if !ok {
+ t.Error("MyId attribute is missing!")
+ }
+ _, ok = st.FieldByName("NumCpu")
+ if !ok {
+ t.Error("NumCpu attribute is missing!")
+ }
+ _, ok = st.FieldByName("NumGpu")
+ if !ok {
+ t.Error("NumGpu attribute is missing!")
+ }
+ _, ok = st.FieldByName("My_ID")
+ if !ok {
+ t.Error("My_ID attribute is missing!")
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/initialisms_test.go b/src/jaegertracing/thrift/lib/go/test/tests/initialisms_test.go
new file mode 100644
index 000000000..40923d24c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/initialisms_test.go
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ "initialismstest"
+ "reflect"
+ "testing"
+)
+
+func TestThatCommonInitialismsAreFixed(t *testing.T) {
+ s := initialismstest.InitialismsTest{}
+ st := reflect.TypeOf(s)
+ _, ok := st.FieldByName("UserID")
+ if !ok {
+ t.Error("UserID attribute is missing!")
+ }
+ _, ok = st.FieldByName("ServerURL")
+ if !ok {
+ t.Error("ServerURL attribute is missing!")
+ }
+ _, ok = st.FieldByName("ID")
+ if !ok {
+ t.Error("ID attribute is missing!")
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/multiplexed_protocol_test.go b/src/jaegertracing/thrift/lib/go/test/tests/multiplexed_protocol_test.go
new file mode 100644
index 000000000..61ac62828
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/multiplexed_protocol_test.go
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ "context"
+ "multiplexedprotocoltest"
+ "net"
+ "testing"
+ "thrift"
+ "time"
+)
+
+func FindAvailableTCPServerPort() net.Addr {
+ if l, err := net.Listen("tcp", "127.0.0.1:0"); err != nil {
+ panic("Could not find available server port")
+ } else {
+ defer l.Close()
+ return l.Addr()
+ }
+}
+
+type FirstImpl struct{}
+
+func (f *FirstImpl) ReturnOne(ctx context.Context) (r int64, err error) {
+ return 1, nil
+}
+
+type SecondImpl struct{}
+
+func (s *SecondImpl) ReturnTwo(ctx context.Context) (r int64, err error) {
+ return 2, nil
+}
+
+func createTransport(addr net.Addr) (thrift.TTransport, error) {
+ socket := thrift.NewTSocketFromAddrTimeout(addr, TIMEOUT)
+ transport := thrift.NewTFramedTransport(socket)
+ err := transport.Open()
+ if err != nil {
+ return nil, err
+ }
+ return transport, nil
+}
+
+func TestMultiplexedProtocolFirst(t *testing.T) {
+ processor := thrift.NewTMultiplexedProcessor()
+ protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
+ transportFactory := thrift.NewTTransportFactory()
+ transportFactory = thrift.NewTFramedTransportFactory(transportFactory)
+ addr := FindAvailableTCPServerPort()
+ serverTransport, err := thrift.NewTServerSocketTimeout(addr.String(), TIMEOUT)
+ if err != nil {
+ t.Fatal("Unable to create server socket", err)
+ }
+ server = thrift.NewTSimpleServer4(processor, serverTransport, transportFactory, protocolFactory)
+
+ firstProcessor := multiplexedprotocoltest.NewFirstProcessor(&FirstImpl{})
+ processor.RegisterProcessor("FirstService", firstProcessor)
+
+ secondProcessor := multiplexedprotocoltest.NewSecondProcessor(&SecondImpl{})
+ processor.RegisterProcessor("SecondService", secondProcessor)
+
+ defer server.Stop()
+ go server.Serve()
+ time.Sleep(10 * time.Millisecond)
+
+ transport, err := createTransport(addr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer transport.Close()
+ protocol := thrift.NewTMultiplexedProtocol(thrift.NewTBinaryProtocolTransport(transport), "FirstService")
+
+ client := multiplexedprotocoltest.NewFirstClient(thrift.NewTStandardClient(protocol, protocol))
+
+ ret, err := client.ReturnOne(defaultCtx)
+ if err != nil {
+ t.Fatal("Unable to call first server:", err)
+ } else if ret != 1 {
+ t.Fatal("Unexpected result from server: ", ret)
+ }
+}
+
+func TestMultiplexedProtocolSecond(t *testing.T) {
+ processor := thrift.NewTMultiplexedProcessor()
+ protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
+ transportFactory := thrift.NewTTransportFactory()
+ transportFactory = thrift.NewTFramedTransportFactory(transportFactory)
+ addr := FindAvailableTCPServerPort()
+ serverTransport, err := thrift.NewTServerSocketTimeout(addr.String(), TIMEOUT)
+ if err != nil {
+ t.Fatal("Unable to create server socket", err)
+ }
+ server = thrift.NewTSimpleServer4(processor, serverTransport, transportFactory, protocolFactory)
+
+ firstProcessor := multiplexedprotocoltest.NewFirstProcessor(&FirstImpl{})
+ processor.RegisterProcessor("FirstService", firstProcessor)
+
+ secondProcessor := multiplexedprotocoltest.NewSecondProcessor(&SecondImpl{})
+ processor.RegisterProcessor("SecondService", secondProcessor)
+
+ defer server.Stop()
+ go server.Serve()
+ time.Sleep(10 * time.Millisecond)
+
+ transport, err := createTransport(addr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer transport.Close()
+ protocol := thrift.NewTMultiplexedProtocol(thrift.NewTBinaryProtocolTransport(transport), "SecondService")
+
+ client := multiplexedprotocoltest.NewSecondClient(thrift.NewTStandardClient(protocol, protocol))
+
+ ret, err := client.ReturnTwo(defaultCtx)
+ if err != nil {
+ t.Fatal("Unable to call second server:", err)
+ } else if ret != 2 {
+ t.Fatal("Unexpected result from server: ", ret)
+ }
+}
+
+func TestMultiplexedProtocolLegacy(t *testing.T) {
+ processor := thrift.NewTMultiplexedProcessor()
+ protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
+ transportFactory := thrift.NewTTransportFactory()
+ transportFactory = thrift.NewTFramedTransportFactory(transportFactory)
+ addr := FindAvailableTCPServerPort()
+ serverTransport, err := thrift.NewTServerSocketTimeout(addr.String(), TIMEOUT)
+ if err != nil {
+ t.Fatal("Unable to create server socket", err)
+ }
+ server = thrift.NewTSimpleServer4(processor, serverTransport, transportFactory, protocolFactory)
+
+ firstProcessor := multiplexedprotocoltest.NewFirstProcessor(&FirstImpl{})
+ processor.RegisterProcessor("FirstService", firstProcessor)
+
+ secondProcessor := multiplexedprotocoltest.NewSecondProcessor(&SecondImpl{})
+ processor.RegisterProcessor("SecondService", secondProcessor)
+
+ defer server.Stop()
+ go server.Serve()
+ time.Sleep(10 * time.Millisecond)
+
+ transport, err := createTransport(addr)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ defer transport.Close()
+
+ protocol := thrift.NewTBinaryProtocolTransport(transport)
+ client := multiplexedprotocoltest.NewSecondClient(thrift.NewTStandardClient(protocol, protocol))
+
+ ret, err := client.ReturnTwo(defaultCtx)
+ //expect error since default processor is not registered
+ if err == nil {
+ t.Fatal("Expecting error")
+ }
+
+ //register default processor and call again
+ processor.RegisterDefault(multiplexedprotocoltest.NewSecondProcessor(&SecondImpl{}))
+ transport, err = createTransport(addr)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ defer transport.Close()
+
+ protocol = thrift.NewTBinaryProtocolTransport(transport)
+ client = multiplexedprotocoltest.NewSecondClient(thrift.NewTStandardClient(protocol, protocol))
+
+ ret, err = client.ReturnTwo(defaultCtx)
+ if err != nil {
+ t.Fatal("Unable to call legacy server:", err)
+ }
+ if ret != 2 {
+ t.Fatal("Unexpected result from server: ", ret)
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/names_test.go b/src/jaegertracing/thrift/lib/go/test/tests/names_test.go
new file mode 100644
index 000000000..90b63a3b4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/names_test.go
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ "namestest"
+ "reflect"
+ "testing"
+)
+
+func TestThatAttributeNameSubstituionDoesNotOccur(t *testing.T) {
+ s := namestest.NamesTest{}
+ st := reflect.TypeOf(s)
+ _, ok := st.FieldByName("Type")
+ if !ok {
+ t.Error("Type attribute is missing!")
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/one_way_test.go b/src/jaegertracing/thrift/lib/go/test/tests/one_way_test.go
new file mode 100644
index 000000000..48d0bbe38
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/one_way_test.go
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ "context"
+ "fmt"
+ "net"
+ "onewaytest"
+ "testing"
+ "thrift"
+ "time"
+)
+
+func findPort() net.Addr {
+ if l, err := net.Listen("tcp", "127.0.0.1:0"); err != nil {
+ panic("Could not find available server port")
+ } else {
+ defer l.Close()
+ return l.Addr()
+ }
+}
+
+type impl struct{}
+
+func (i *impl) Hi(ctx context.Context, in int64, s string) (err error) { fmt.Println("Hi!"); return }
+func (i *impl) Emptyfunc(ctx context.Context) (err error) { return }
+func (i *impl) EchoInt(ctx context.Context, param int64) (r int64, err error) { return param, nil }
+
+const TIMEOUT = time.Second
+
+var addr net.Addr
+var server *thrift.TSimpleServer
+var client *onewaytest.OneWayClient
+
+func TestInitOneway(t *testing.T) {
+ var err error
+ addr = findPort()
+ serverTransport, err := thrift.NewTServerSocketTimeout(addr.String(), TIMEOUT)
+ if err != nil {
+ t.Fatal("Unable to create server socket", err)
+ }
+ processor := onewaytest.NewOneWayProcessor(&impl{})
+ server = thrift.NewTSimpleServer2(processor, serverTransport)
+
+ go server.Serve()
+ time.Sleep(10 * time.Millisecond)
+}
+
+func TestInitOnewayClient(t *testing.T) {
+ transport := thrift.NewTSocketFromAddrTimeout(addr, TIMEOUT)
+ protocol := thrift.NewTBinaryProtocolTransport(transport)
+ client = onewaytest.NewOneWayClient(thrift.NewTStandardClient(protocol, protocol))
+ err := transport.Open()
+ if err != nil {
+ t.Fatal("Unable to open client socket", err)
+ }
+}
+
+func TestCallOnewayServer(t *testing.T) {
+ //call oneway function
+ err := client.Hi(defaultCtx, 1, "")
+ if err != nil {
+ t.Fatal("Unexpected error: ", err)
+ }
+ //There is no way to detect protocol problems with single oneway call so we call it second time
+ i, err := client.EchoInt(defaultCtx, 42)
+ if err != nil {
+ t.Fatal("Unexpected error: ", err)
+ }
+ if i != 42 {
+ t.Fatal("Unexpected returned value: ", i)
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/optional_fields_test.go b/src/jaegertracing/thrift/lib/go/test/tests/optional_fields_test.go
new file mode 100644
index 000000000..34ad6605a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/optional_fields_test.go
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ "bytes"
+ gomock "github.com/golang/mock/gomock"
+ "optionalfieldstest"
+ "testing"
+ "thrift"
+)
+
+func TestIsSetReturnFalseOnCreation(t *testing.T) {
+ ao := optionalfieldstest.NewAllOptional()
+ if ao.IsSetS() {
+ t.Errorf("Optional field S is set on initialization")
+ }
+ if ao.IsSetI() {
+ t.Errorf("Optional field I is set on initialization")
+ }
+ if ao.IsSetB() {
+ t.Errorf("Optional field B is set on initialization")
+ }
+ if ao.IsSetS2() {
+ t.Errorf("Optional field S2 is set on initialization")
+ }
+ if ao.IsSetI2() {
+ t.Errorf("Optional field I2 is set on initialization")
+ }
+ if ao.IsSetB2() {
+ t.Errorf("Optional field B2 is set on initialization")
+ }
+ if ao.IsSetAa() {
+ t.Errorf("Optional field Aa is set on initialization")
+ }
+ if ao.IsSetL() {
+ t.Errorf("Optional field L is set on initialization")
+ }
+ if ao.IsSetL2() {
+ t.Errorf("Optional field L2 is set on initialization")
+ }
+ if ao.IsSetM() {
+ t.Errorf("Optional field M is set on initialization")
+ }
+ if ao.IsSetM2() {
+ t.Errorf("Optional field M2 is set on initialization")
+ }
+ if ao.IsSetBin() {
+ t.Errorf("Optional field Bin is set on initialization")
+ }
+ if ao.IsSetBin2() {
+ t.Errorf("Optional field Bin2 is set on initialization")
+ }
+}
+
+func TestDefaultValuesOnCreation(t *testing.T) {
+ ao := optionalfieldstest.NewAllOptional()
+ if ao.GetS() != "DEFAULT" {
+ t.Errorf("Unexpected default value %#v for field S", ao.GetS())
+ }
+ if ao.GetI() != 42 {
+ t.Errorf("Unexpected default value %#v for field I", ao.GetI())
+ }
+ if ao.GetB() != false {
+ t.Errorf("Unexpected default value %#v for field B", ao.GetB())
+ }
+ if ao.GetS2() != "" {
+ t.Errorf("Unexpected default value %#v for field S2", ao.GetS2())
+ }
+ if ao.GetI2() != 0 {
+ t.Errorf("Unexpected default value %#v for field I2", ao.GetI2())
+ }
+ if ao.GetB2() != false {
+ t.Errorf("Unexpected default value %#v for field B2", ao.GetB2())
+ }
+ if l := ao.GetL(); len(l) != 0 {
+ t.Errorf("Unexpected default value %#v for field L", l)
+ }
+ if l := ao.GetL2(); len(l) != 2 || l[0] != 1 || l[1] != 2 {
+ t.Errorf("Unexpected default value %#v for field L2", l)
+ }
+ //FIXME: should we return empty map here?
+ if m := ao.GetM(); m != nil {
+ t.Errorf("Unexpected default value %#v for field M", m)
+ }
+ if m := ao.GetM2(); len(m) != 2 || m[1] != 2 || m[3] != 4 {
+ t.Errorf("Unexpected default value %#v for field M2", m)
+ }
+ if bv := ao.GetBin(); bv != nil {
+ t.Errorf("Unexpected default value %#v for field Bin", bv)
+ }
+ if bv := ao.GetBin2(); !bytes.Equal(bv, []byte("asdf")) {
+ t.Errorf("Unexpected default value %#v for field Bin2", bv)
+ }
+}
+
+func TestInitialValuesOnCreation(t *testing.T) {
+ ao := optionalfieldstest.NewAllOptional()
+ if ao.S != "DEFAULT" {
+ t.Errorf("Unexpected initial value %#v for field S", ao.S)
+ }
+ if ao.I != 42 {
+ t.Errorf("Unexpected initial value %#v for field I", ao.I)
+ }
+ if ao.B != false {
+ t.Errorf("Unexpected initial value %#v for field B", ao.B)
+ }
+ if ao.S2 != nil {
+ t.Errorf("Unexpected initial value %#v for field S2", ao.S2)
+ }
+ if ao.I2 != nil {
+ t.Errorf("Unexpected initial value %#v for field I2", ao.I2)
+ }
+ if ao.B2 != nil {
+ t.Errorf("Unexpected initial value %#v for field B2", ao.B2)
+ }
+ if ao.L != nil || len(ao.L) != 0 {
+ t.Errorf("Unexpected initial value %#v for field L", ao.L)
+ }
+ if ao.L2 != nil {
+ t.Errorf("Unexpected initial value %#v for field L2", ao.L2)
+ }
+ if ao.M != nil {
+ t.Errorf("Unexpected initial value %#v for field M", ao.M)
+ }
+ if ao.M2 != nil {
+ t.Errorf("Unexpected initial value %#v for field M2", ao.M2)
+ }
+ if ao.Bin != nil || len(ao.Bin) != 0 {
+ t.Errorf("Unexpected initial value %#v for field Bin", ao.Bin)
+ }
+ if !bytes.Equal(ao.Bin2, []byte("asdf")) {
+ t.Errorf("Unexpected initial value %#v for field Bin2", ao.Bin2)
+ }
+}
+
+func TestIsSetReturnTrueAfterUpdate(t *testing.T) {
+ ao := optionalfieldstest.NewAllOptional()
+ ao.S = "somevalue"
+ ao.I = 123
+ ao.B = true
+ ao.Aa = optionalfieldstest.NewStructA()
+ if !ao.IsSetS() {
+ t.Errorf("Field S should be set")
+ }
+ if !ao.IsSetI() {
+ t.Errorf("Field I should be set")
+ }
+ if !ao.IsSetB() {
+ t.Errorf("Field B should be set")
+ }
+ if !ao.IsSetAa() {
+ t.Errorf("Field aa should be set")
+ }
+}
+
+func TestListNotEmpty(t *testing.T) {
+ ao := optionalfieldstest.NewAllOptional()
+ ao.L = []int64{1, 2, 3}
+ if !ao.IsSetL() {
+ t.Errorf("Field L should be set")
+ }
+}
+
+//Make sure that optional fields are not being serialized
+func TestNoOptionalUnsetFieldsOnWire(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ defer mockCtrl.Finish()
+ proto := NewMockTProtocol(mockCtrl)
+ gomock.InOrder(
+ proto.EXPECT().WriteStructBegin("all_optional").Return(nil),
+ proto.EXPECT().WriteFieldStop().Return(nil),
+ proto.EXPECT().WriteStructEnd().Return(nil),
+ )
+ ao := optionalfieldstest.NewAllOptional()
+ ao.Write(proto)
+}
+
+func TestNoSetToDefaultFieldsOnWire(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ defer mockCtrl.Finish()
+ proto := NewMockTProtocol(mockCtrl)
+ gomock.InOrder(
+ proto.EXPECT().WriteStructBegin("all_optional").Return(nil),
+ proto.EXPECT().WriteFieldStop().Return(nil),
+ proto.EXPECT().WriteStructEnd().Return(nil),
+ )
+ ao := optionalfieldstest.NewAllOptional()
+ ao.I = 42
+ ao.Write(proto)
+}
+
+//Make sure that only one field is being serialized when set to non-default
+func TestOneISetFieldOnWire(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ defer mockCtrl.Finish()
+ proto := NewMockTProtocol(mockCtrl)
+ gomock.InOrder(
+ proto.EXPECT().WriteStructBegin("all_optional").Return(nil),
+ proto.EXPECT().WriteFieldBegin("i", thrift.TType(thrift.I64), int16(2)).Return(nil),
+ proto.EXPECT().WriteI64(int64(123)).Return(nil),
+ proto.EXPECT().WriteFieldEnd().Return(nil),
+ proto.EXPECT().WriteFieldStop().Return(nil),
+ proto.EXPECT().WriteStructEnd().Return(nil),
+ )
+ ao := optionalfieldstest.NewAllOptional()
+ ao.I = 123
+ ao.Write(proto)
+}
+
+func TestOneLSetFieldOnWire(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ defer mockCtrl.Finish()
+ proto := NewMockTProtocol(mockCtrl)
+ gomock.InOrder(
+ proto.EXPECT().WriteStructBegin("all_optional").Return(nil),
+ proto.EXPECT().WriteFieldBegin("l", thrift.TType(thrift.LIST), int16(9)).Return(nil),
+ proto.EXPECT().WriteListBegin(thrift.TType(thrift.I64), 2).Return(nil),
+ proto.EXPECT().WriteI64(int64(1)).Return(nil),
+ proto.EXPECT().WriteI64(int64(2)).Return(nil),
+ proto.EXPECT().WriteListEnd().Return(nil),
+ proto.EXPECT().WriteFieldEnd().Return(nil),
+ proto.EXPECT().WriteFieldStop().Return(nil),
+ proto.EXPECT().WriteStructEnd().Return(nil),
+ )
+ ao := optionalfieldstest.NewAllOptional()
+ ao.L = []int64{1, 2}
+ ao.Write(proto)
+}
+
+func TestOneBinSetFieldOnWire(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ defer mockCtrl.Finish()
+ proto := NewMockTProtocol(mockCtrl)
+ gomock.InOrder(
+ proto.EXPECT().WriteStructBegin("all_optional").Return(nil),
+ proto.EXPECT().WriteFieldBegin("bin", thrift.TType(thrift.STRING), int16(13)).Return(nil),
+ proto.EXPECT().WriteBinary([]byte("somebytestring")).Return(nil),
+ proto.EXPECT().WriteFieldEnd().Return(nil),
+ proto.EXPECT().WriteFieldStop().Return(nil),
+ proto.EXPECT().WriteStructEnd().Return(nil),
+ )
+ ao := optionalfieldstest.NewAllOptional()
+ ao.Bin = []byte("somebytestring")
+ ao.Write(proto)
+}
+
+func TestOneEmptyBinSetFieldOnWire(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ defer mockCtrl.Finish()
+ proto := NewMockTProtocol(mockCtrl)
+ gomock.InOrder(
+ proto.EXPECT().WriteStructBegin("all_optional").Return(nil),
+ proto.EXPECT().WriteFieldBegin("bin", thrift.TType(thrift.STRING), int16(13)).Return(nil),
+ proto.EXPECT().WriteBinary([]byte{}).Return(nil),
+ proto.EXPECT().WriteFieldEnd().Return(nil),
+ proto.EXPECT().WriteFieldStop().Return(nil),
+ proto.EXPECT().WriteStructEnd().Return(nil),
+ )
+ ao := optionalfieldstest.NewAllOptional()
+ ao.Bin = []byte{}
+ ao.Write(proto)
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/protocol_mock.go b/src/jaegertracing/thrift/lib/go/test/tests/protocol_mock.go
new file mode 100644
index 000000000..51d7a02ff
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/protocol_mock.go
@@ -0,0 +1,513 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Automatically generated by MockGen. DO NOT EDIT!
+// Source: thrift (interfaces: TProtocol)
+
+package tests
+
+import (
+ "context"
+ thrift "thrift"
+
+ gomock "github.com/golang/mock/gomock"
+)
+
+// Mock of TProtocol interface
+type MockTProtocol struct {
+ ctrl *gomock.Controller
+ recorder *_MockTProtocolRecorder
+}
+
+// Recorder for MockTProtocol (not exported)
+type _MockTProtocolRecorder struct {
+ mock *MockTProtocol
+}
+
+func NewMockTProtocol(ctrl *gomock.Controller) *MockTProtocol {
+ mock := &MockTProtocol{ctrl: ctrl}
+ mock.recorder = &_MockTProtocolRecorder{mock}
+ return mock
+}
+
+func (_m *MockTProtocol) EXPECT() *_MockTProtocolRecorder {
+ return _m.recorder
+}
+
+func (_m *MockTProtocol) Flush(ctx context.Context) error {
+ ret := _m.ctrl.Call(_m, "Flush")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) Flush(ctx context.Context) *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "Flush")
+}
+
+func (_m *MockTProtocol) ReadBinary() ([]byte, error) {
+ ret := _m.ctrl.Call(_m, "ReadBinary")
+ ret0, _ := ret[0].([]byte)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+func (_mr *_MockTProtocolRecorder) ReadBinary() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadBinary")
+}
+
+func (_m *MockTProtocol) ReadBool() (bool, error) {
+ ret := _m.ctrl.Call(_m, "ReadBool")
+ ret0, _ := ret[0].(bool)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+func (_mr *_MockTProtocolRecorder) ReadBool() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadBool")
+}
+
+func (_m *MockTProtocol) ReadByte() (int8, error) {
+ ret := _m.ctrl.Call(_m, "ReadByte")
+ ret0, _ := ret[0].(int8)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+func (_mr *_MockTProtocolRecorder) ReadByte() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadByte")
+}
+
+func (_m *MockTProtocol) ReadDouble() (float64, error) {
+ ret := _m.ctrl.Call(_m, "ReadDouble")
+ ret0, _ := ret[0].(float64)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+func (_mr *_MockTProtocolRecorder) ReadDouble() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadDouble")
+}
+
+func (_m *MockTProtocol) ReadFieldBegin() (string, thrift.TType, int16, error) {
+ ret := _m.ctrl.Call(_m, "ReadFieldBegin")
+ ret0, _ := ret[0].(string)
+ ret1, _ := ret[1].(thrift.TType)
+ ret2, _ := ret[2].(int16)
+ ret3, _ := ret[3].(error)
+ return ret0, ret1, ret2, ret3
+}
+
+func (_mr *_MockTProtocolRecorder) ReadFieldBegin() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadFieldBegin")
+}
+
+func (_m *MockTProtocol) ReadFieldEnd() error {
+ ret := _m.ctrl.Call(_m, "ReadFieldEnd")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) ReadFieldEnd() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadFieldEnd")
+}
+
+func (_m *MockTProtocol) ReadI16() (int16, error) {
+ ret := _m.ctrl.Call(_m, "ReadI16")
+ ret0, _ := ret[0].(int16)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+func (_mr *_MockTProtocolRecorder) ReadI16() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadI16")
+}
+
+func (_m *MockTProtocol) ReadI32() (int32, error) {
+ ret := _m.ctrl.Call(_m, "ReadI32")
+ ret0, _ := ret[0].(int32)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+func (_mr *_MockTProtocolRecorder) ReadI32() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadI32")
+}
+
+func (_m *MockTProtocol) ReadI64() (int64, error) {
+ ret := _m.ctrl.Call(_m, "ReadI64")
+ ret0, _ := ret[0].(int64)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+func (_mr *_MockTProtocolRecorder) ReadI64() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadI64")
+}
+
+func (_m *MockTProtocol) ReadListBegin() (thrift.TType, int, error) {
+ ret := _m.ctrl.Call(_m, "ReadListBegin")
+ ret0, _ := ret[0].(thrift.TType)
+ ret1, _ := ret[1].(int)
+ ret2, _ := ret[2].(error)
+ return ret0, ret1, ret2
+}
+
+func (_mr *_MockTProtocolRecorder) ReadListBegin() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadListBegin")
+}
+
+func (_m *MockTProtocol) ReadListEnd() error {
+ ret := _m.ctrl.Call(_m, "ReadListEnd")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) ReadListEnd() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadListEnd")
+}
+
+func (_m *MockTProtocol) ReadMapBegin() (thrift.TType, thrift.TType, int, error) {
+ ret := _m.ctrl.Call(_m, "ReadMapBegin")
+ ret0, _ := ret[0].(thrift.TType)
+ ret1, _ := ret[1].(thrift.TType)
+ ret2, _ := ret[2].(int)
+ ret3, _ := ret[3].(error)
+ return ret0, ret1, ret2, ret3
+}
+
+func (_mr *_MockTProtocolRecorder) ReadMapBegin() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadMapBegin")
+}
+
+func (_m *MockTProtocol) ReadMapEnd() error {
+ ret := _m.ctrl.Call(_m, "ReadMapEnd")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) ReadMapEnd() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadMapEnd")
+}
+
+func (_m *MockTProtocol) ReadMessageBegin() (string, thrift.TMessageType, int32, error) {
+ ret := _m.ctrl.Call(_m, "ReadMessageBegin")
+ ret0, _ := ret[0].(string)
+ ret1, _ := ret[1].(thrift.TMessageType)
+ ret2, _ := ret[2].(int32)
+ ret3, _ := ret[3].(error)
+ return ret0, ret1, ret2, ret3
+}
+
+func (_mr *_MockTProtocolRecorder) ReadMessageBegin() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadMessageBegin")
+}
+
+func (_m *MockTProtocol) ReadMessageEnd() error {
+ ret := _m.ctrl.Call(_m, "ReadMessageEnd")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) ReadMessageEnd() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadMessageEnd")
+}
+
+func (_m *MockTProtocol) ReadSetBegin() (thrift.TType, int, error) {
+ ret := _m.ctrl.Call(_m, "ReadSetBegin")
+ ret0, _ := ret[0].(thrift.TType)
+ ret1, _ := ret[1].(int)
+ ret2, _ := ret[2].(error)
+ return ret0, ret1, ret2
+}
+
+func (_mr *_MockTProtocolRecorder) ReadSetBegin() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadSetBegin")
+}
+
+func (_m *MockTProtocol) ReadSetEnd() error {
+ ret := _m.ctrl.Call(_m, "ReadSetEnd")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) ReadSetEnd() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadSetEnd")
+}
+
+func (_m *MockTProtocol) ReadString() (string, error) {
+ ret := _m.ctrl.Call(_m, "ReadString")
+ ret0, _ := ret[0].(string)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+func (_mr *_MockTProtocolRecorder) ReadString() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadString")
+}
+
+func (_m *MockTProtocol) ReadStructBegin() (string, error) {
+ ret := _m.ctrl.Call(_m, "ReadStructBegin")
+ ret0, _ := ret[0].(string)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+func (_mr *_MockTProtocolRecorder) ReadStructBegin() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadStructBegin")
+}
+
+func (_m *MockTProtocol) ReadStructEnd() error {
+ ret := _m.ctrl.Call(_m, "ReadStructEnd")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) ReadStructEnd() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadStructEnd")
+}
+
+func (_m *MockTProtocol) Skip(_param0 thrift.TType) error {
+ ret := _m.ctrl.Call(_m, "Skip", _param0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) Skip(arg0 interface{}) *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "Skip", arg0)
+}
+
+func (_m *MockTProtocol) Transport() thrift.TTransport {
+ ret := _m.ctrl.Call(_m, "Transport")
+ ret0, _ := ret[0].(thrift.TTransport)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) Transport() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "Transport")
+}
+
+func (_m *MockTProtocol) WriteBinary(_param0 []byte) error {
+ ret := _m.ctrl.Call(_m, "WriteBinary", _param0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteBinary(arg0 interface{}) *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteBinary", arg0)
+}
+
+func (_m *MockTProtocol) WriteBool(_param0 bool) error {
+ ret := _m.ctrl.Call(_m, "WriteBool", _param0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteBool(arg0 interface{}) *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteBool", arg0)
+}
+
+func (_m *MockTProtocol) WriteByte(_param0 int8) error {
+ ret := _m.ctrl.Call(_m, "WriteByte", _param0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteByte(arg0 interface{}) *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteByte", arg0)
+}
+
+func (_m *MockTProtocol) WriteDouble(_param0 float64) error {
+ ret := _m.ctrl.Call(_m, "WriteDouble", _param0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteDouble(arg0 interface{}) *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteDouble", arg0)
+}
+
+func (_m *MockTProtocol) WriteFieldBegin(_param0 string, _param1 thrift.TType, _param2 int16) error {
+ ret := _m.ctrl.Call(_m, "WriteFieldBegin", _param0, _param1, _param2)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteFieldBegin(arg0, arg1, arg2 interface{}) *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteFieldBegin", arg0, arg1, arg2)
+}
+
+func (_m *MockTProtocol) WriteFieldEnd() error {
+ ret := _m.ctrl.Call(_m, "WriteFieldEnd")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteFieldEnd() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteFieldEnd")
+}
+
+func (_m *MockTProtocol) WriteFieldStop() error {
+ ret := _m.ctrl.Call(_m, "WriteFieldStop")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteFieldStop() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteFieldStop")
+}
+
+func (_m *MockTProtocol) WriteI16(_param0 int16) error {
+ ret := _m.ctrl.Call(_m, "WriteI16", _param0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteI16(arg0 interface{}) *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteI16", arg0)
+}
+
+func (_m *MockTProtocol) WriteI32(_param0 int32) error {
+ ret := _m.ctrl.Call(_m, "WriteI32", _param0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteI32(arg0 interface{}) *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteI32", arg0)
+}
+
+func (_m *MockTProtocol) WriteI64(_param0 int64) error {
+ ret := _m.ctrl.Call(_m, "WriteI64", _param0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteI64(arg0 interface{}) *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteI64", arg0)
+}
+
+func (_m *MockTProtocol) WriteListBegin(_param0 thrift.TType, _param1 int) error {
+ ret := _m.ctrl.Call(_m, "WriteListBegin", _param0, _param1)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteListBegin(arg0, arg1 interface{}) *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteListBegin", arg0, arg1)
+}
+
+func (_m *MockTProtocol) WriteListEnd() error {
+ ret := _m.ctrl.Call(_m, "WriteListEnd")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteListEnd() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteListEnd")
+}
+
+func (_m *MockTProtocol) WriteMapBegin(_param0 thrift.TType, _param1 thrift.TType, _param2 int) error {
+ ret := _m.ctrl.Call(_m, "WriteMapBegin", _param0, _param1, _param2)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteMapBegin(arg0, arg1, arg2 interface{}) *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteMapBegin", arg0, arg1, arg2)
+}
+
+func (_m *MockTProtocol) WriteMapEnd() error {
+ ret := _m.ctrl.Call(_m, "WriteMapEnd")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteMapEnd() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteMapEnd")
+}
+
+func (_m *MockTProtocol) WriteMessageBegin(_param0 string, _param1 thrift.TMessageType, _param2 int32) error {
+ ret := _m.ctrl.Call(_m, "WriteMessageBegin", _param0, _param1, _param2)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteMessageBegin(arg0, arg1, arg2 interface{}) *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteMessageBegin", arg0, arg1, arg2)
+}
+
+func (_m *MockTProtocol) WriteMessageEnd() error {
+ ret := _m.ctrl.Call(_m, "WriteMessageEnd")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteMessageEnd() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteMessageEnd")
+}
+
+func (_m *MockTProtocol) WriteSetBegin(_param0 thrift.TType, _param1 int) error {
+ ret := _m.ctrl.Call(_m, "WriteSetBegin", _param0, _param1)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteSetBegin(arg0, arg1 interface{}) *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteSetBegin", arg0, arg1)
+}
+
+func (_m *MockTProtocol) WriteSetEnd() error {
+ ret := _m.ctrl.Call(_m, "WriteSetEnd")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteSetEnd() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteSetEnd")
+}
+
+func (_m *MockTProtocol) WriteString(_param0 string) error {
+ ret := _m.ctrl.Call(_m, "WriteString", _param0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteString(arg0 interface{}) *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteString", arg0)
+}
+
+func (_m *MockTProtocol) WriteStructBegin(_param0 string) error {
+ ret := _m.ctrl.Call(_m, "WriteStructBegin", _param0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteStructBegin(arg0 interface{}) *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteStructBegin", arg0)
+}
+
+func (_m *MockTProtocol) WriteStructEnd() error {
+ ret := _m.ctrl.Call(_m, "WriteStructEnd")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+func (_mr *_MockTProtocolRecorder) WriteStructEnd() *gomock.Call {
+ return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteStructEnd")
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/protocols_test.go b/src/jaegertracing/thrift/lib/go/test/tests/protocols_test.go
new file mode 100644
index 000000000..cffd9c3f7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/protocols_test.go
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ "testing"
+ "thrift"
+ "thrifttest"
+)
+
+func RunSocketTestSuite(t *testing.T, protocolFactory thrift.TProtocolFactory,
+ transportFactory thrift.TTransportFactory) {
+ // server
+ var err error
+ addr = FindAvailableTCPServerPort()
+ serverTransport, err := thrift.NewTServerSocketTimeout(addr.String(), TIMEOUT)
+ if err != nil {
+ t.Fatal("Unable to create server socket", err)
+ }
+ processor := thrifttest.NewThriftTestProcessor(NewThriftTestHandler())
+ server = thrift.NewTSimpleServer4(processor, serverTransport, transportFactory, protocolFactory)
+ server.Listen()
+
+ go server.Serve()
+
+ // client
+ var transport thrift.TTransport = thrift.NewTSocketFromAddrTimeout(addr, TIMEOUT)
+ transport, err = transportFactory.GetTransport(transport)
+ if err != nil {
+ t.Fatal(err)
+ }
+ var protocol thrift.TProtocol = protocolFactory.GetProtocol(transport)
+ thriftTestClient := thrifttest.NewThriftTestClient(thrift.NewTStandardClient(protocol, protocol))
+ err = transport.Open()
+ if err != nil {
+ t.Fatal("Unable to open client socket", err)
+ }
+
+ driver := NewThriftTestDriver(t, thriftTestClient)
+ driver.Start()
+}
+
+// Run test suite using TJSONProtocol
+func TestTJSONProtocol(t *testing.T) {
+ RunSocketTestSuite(t,
+ thrift.NewTJSONProtocolFactory(),
+ thrift.NewTTransportFactory())
+ RunSocketTestSuite(t,
+ thrift.NewTJSONProtocolFactory(),
+ thrift.NewTBufferedTransportFactory(8912))
+ RunSocketTestSuite(t,
+ thrift.NewTJSONProtocolFactory(),
+ thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory()))
+}
+
+// Run test suite using TBinaryProtocol
+func TestTBinaryProtocol(t *testing.T) {
+ RunSocketTestSuite(t,
+ thrift.NewTBinaryProtocolFactoryDefault(),
+ thrift.NewTTransportFactory())
+ RunSocketTestSuite(t,
+ thrift.NewTBinaryProtocolFactoryDefault(),
+ thrift.NewTBufferedTransportFactory(8912))
+ RunSocketTestSuite(t,
+ thrift.NewTBinaryProtocolFactoryDefault(),
+ thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory()))
+}
+
+// Run test suite using TCompactBinaryProtocol
+func TestTCompactProtocol(t *testing.T) {
+ RunSocketTestSuite(t,
+ thrift.NewTCompactProtocolFactory(),
+ thrift.NewTTransportFactory())
+ RunSocketTestSuite(t,
+ thrift.NewTCompactProtocolFactory(),
+ thrift.NewTBufferedTransportFactory(8912))
+ RunSocketTestSuite(t,
+ thrift.NewTCompactProtocolFactory(),
+ thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory()))
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/required_fields_test.go b/src/jaegertracing/thrift/lib/go/test/tests/required_fields_test.go
new file mode 100644
index 000000000..3fa414ad8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/required_fields_test.go
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ "context"
+ "github.com/golang/mock/gomock"
+ "optionalfieldstest"
+ "requiredfieldtest"
+ "testing"
+ "thrift"
+)
+
+func TestRequiredField_SucecssWhenSet(t *testing.T) {
+ // create a new RequiredField instance with the required field set
+ source := &requiredfieldtest.RequiredField{Name: "this is a test"}
+ sourceData, err := thrift.NewTSerializer().Write(context.Background(), source)
+ if err != nil {
+ t.Fatalf("failed to serialize %T: %v", source, err)
+ }
+
+ d := thrift.NewTDeserializer()
+ err = d.Read(&requiredfieldtest.RequiredField{}, sourceData)
+ if err != nil {
+ t.Fatalf("Did not expect an error when trying to deserialize the requiredfieldtest.RequiredField: %v", err)
+ }
+}
+
+func TestRequiredField_ErrorWhenMissing(t *testing.T) {
+ // create a new OtherThing instance, without setting the required field
+ source := &requiredfieldtest.OtherThing{}
+ sourceData, err := thrift.NewTSerializer().Write(context.Background(), source)
+ if err != nil {
+ t.Fatalf("failed to serialize %T: %v", source, err)
+ }
+
+ // attempt to deserialize into a different type (which should fail)
+ d := thrift.NewTDeserializer()
+ err = d.Read(&requiredfieldtest.RequiredField{}, sourceData)
+ if err == nil {
+ t.Fatal("Expected an error when trying to deserialize an object which is missing a required field")
+ }
+}
+
+func TestStructReadRequiredFields(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ protocol := NewMockTProtocol(mockCtrl)
+ testStruct := optionalfieldstest.NewStructC()
+
+ // None of required fields are set
+ gomock.InOrder(
+ protocol.EXPECT().ReadStructBegin().Return("StructC", nil),
+ protocol.EXPECT().ReadFieldBegin().Return("_", thrift.TType(thrift.STOP), int16(1), nil),
+ protocol.EXPECT().ReadStructEnd().Return(nil),
+ )
+
+ err := testStruct.Read(protocol)
+ mockCtrl.Finish()
+ mockCtrl = gomock.NewController(t)
+ if err == nil {
+ t.Fatal("Expected read to fail")
+ }
+ err2, ok := err.(thrift.TProtocolException)
+ if !ok {
+ t.Fatal("Expected a TProtocolException")
+ }
+ if err2.TypeId() != thrift.INVALID_DATA {
+ t.Fatal("Expected INVALID_DATA TProtocolException")
+ }
+
+ // One of the required fields is set
+ gomock.InOrder(
+ protocol.EXPECT().ReadStructBegin().Return("StructC", nil),
+ protocol.EXPECT().ReadFieldBegin().Return("I", thrift.TType(thrift.I32), int16(2), nil),
+ protocol.EXPECT().ReadI32().Return(int32(1), nil),
+ protocol.EXPECT().ReadFieldEnd().Return(nil),
+ protocol.EXPECT().ReadFieldBegin().Return("_", thrift.TType(thrift.STOP), int16(1), nil),
+ protocol.EXPECT().ReadStructEnd().Return(nil),
+ )
+
+ err = testStruct.Read(protocol)
+ mockCtrl.Finish()
+ mockCtrl = gomock.NewController(t)
+ if err == nil {
+ t.Fatal("Expected read to fail")
+ }
+ err2, ok = err.(thrift.TProtocolException)
+ if !ok {
+ t.Fatal("Expected a TProtocolException")
+ }
+ if err2.TypeId() != thrift.INVALID_DATA {
+ t.Fatal("Expected INVALID_DATA TProtocolException")
+ }
+
+ // Both of the required fields are set
+ gomock.InOrder(
+ protocol.EXPECT().ReadStructBegin().Return("StructC", nil),
+ protocol.EXPECT().ReadFieldBegin().Return("i", thrift.TType(thrift.I32), int16(2), nil),
+ protocol.EXPECT().ReadI32().Return(int32(1), nil),
+ protocol.EXPECT().ReadFieldEnd().Return(nil),
+ protocol.EXPECT().ReadFieldBegin().Return("s2", thrift.TType(thrift.STRING), int16(4), nil),
+ protocol.EXPECT().ReadString().Return("test", nil),
+ protocol.EXPECT().ReadFieldEnd().Return(nil),
+ protocol.EXPECT().ReadFieldBegin().Return("_", thrift.TType(thrift.STOP), int16(1), nil),
+ protocol.EXPECT().ReadStructEnd().Return(nil),
+ )
+
+ err = testStruct.Read(protocol)
+ mockCtrl.Finish()
+ if err != nil {
+ t.Fatal("Expected read to succeed")
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/struct_args_rets_test.go b/src/jaegertracing/thrift/lib/go/test/tests/struct_args_rets_test.go
new file mode 100644
index 000000000..81e9b2658
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/struct_args_rets_test.go
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ st "servicestest"
+)
+
+//this function is never called, it will fail to compile if check is failed
+func staticCheckStructArgsResults() {
+ //Check that struct args and results are passed by reference
+ var sa *st.StructA = &st.StructA{}
+ var iface st.AServ
+ var err error
+
+ sa, err = iface.StructAFunc_1structA(defaultCtx, sa)
+ _ = err
+ _ = sa
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/thrifttest_driver.go b/src/jaegertracing/thrift/lib/go/test/tests/thrifttest_driver.go
new file mode 100644
index 000000000..de54cbcaa
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/thrifttest_driver.go
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * 'License'); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ "reflect"
+ "testing"
+ "thrifttest"
+)
+
+type ThriftTestDriver struct {
+ client thrifttest.ThriftTest
+ t *testing.T
+}
+
+func NewThriftTestDriver(t *testing.T, client thrifttest.ThriftTest) *ThriftTestDriver {
+ return &ThriftTestDriver{client, t}
+}
+
+func (p *ThriftTestDriver) Start() {
+ client := p.client
+ t := p.t
+
+ if client.TestVoid(defaultCtx) != nil {
+ t.Fatal("TestVoid failed")
+ }
+
+ if r, err := client.TestString(defaultCtx, "Test"); r != "Test" || err != nil {
+ t.Fatal("TestString with simple text failed")
+ }
+
+ if r, err := client.TestString(defaultCtx, ""); r != "" || err != nil {
+ t.Fatal("TestString with empty text failed")
+ }
+
+ stringTest := "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, " +
+ "Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, " +
+ "БеларуÑкаÑ, БеларуÑÐºÐ°Ñ (тарашкевіца), БългарÑки, Bamanankan, " +
+ "বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Ðохчийн, " +
+ "Cebuano, á£áŽ³áŽ©, ÄŒesky, СловѣÌньÑкъ / ⰔⰎⰑⰂⰡâ°â° â°”â°â°Ÿ, Чӑвашла, Cymraeg, " +
+ "Dansk, Zazaki, Þ‹Þ¨ÞˆÞ¬Þ€Þ¨Þ„Þ¦ÞÞ°, Ελληνικά, Emiliàn e rumagnòl, English, " +
+ "Esperanto, Español, Eesti, Euskara, Ùارسی, Suomi, Võro, Føroyskt, " +
+ "Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, " +
+ "Avañe'ẽ, ગà«àªœàª°àª¾àª¤à«€, Gaelg, עברית, हिनà¥à¤¦à¥€, Fiji Hindi, Hrvatski, " +
+ "Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia, " +
+ "Ilokano, Ido, Ãslenska, Italiano, 日本語, Lojban, Basa Jawa, " +
+ "ქáƒáƒ áƒ—ული, Kongo, Kalaallisut, ಕನà³à²¨à²¡, 한국어, Къарачай-Малкъар, " +
+ "Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, " +
+ "Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa " +
+ "Banyumasan, Malagasy, МакедонÑки, മലയാളം, मराठी, مازÙرونی, Bahasa " +
+ "Melayu, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪" +
+ "Norsk (nynorsk)‬, ‪Norsk (bokmål)‬, Nouormand, Diné bizaad, " +
+ "Occitan, Иронау, Papiamentu, Deitsch, Polski, پنجابی, پښتو, " +
+ "Norfuk / Pitkern, Português, Runa Simi, Rumantsch, Romani, Română, " +
+ "РуÑÑкий, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple " +
+ "English, SlovenÄina, SlovenÅ¡Äina, СрпÑки / Srpski, Seeltersk, " +
+ "Svenska, Kiswahili, தமிழà¯, తెలà±à°—à±, Тоҷикӣ, ไทย, Türkmençe, Tagalog, " +
+ "Türkçe, Татарча/Tatarça, УкраїнÑька, اردو, Tiếng Việt, Volapük, " +
+ "Walon, Winaray, å´è¯­, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, " +
+ "Bân-lâm-gú, 粵語"
+
+ if r, err := client.TestString(defaultCtx, stringTest); r != stringTest || err != nil {
+ t.Fatal("TestString with all languages failed")
+ }
+
+ specialCharacters := "quote: \" backslash:" +
+ " backspace: \b formfeed: \f newline: \n return: \r tab: " +
+ " now-all-of-them-together: '\\\b\n\r\t'" +
+ " now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><" +
+ " char-to-test-json-parsing: ]] \"]] \\\" }}}{ [[[ "
+
+ if r, err := client.TestString(defaultCtx, specialCharacters); r != specialCharacters || err != nil {
+ t.Fatal("TestString with specialCharacters failed")
+ }
+
+ if r, err := client.TestByte(defaultCtx, 1); r != 1 || err != nil {
+ t.Fatal("TestByte(1) failed")
+ }
+ if r, err := client.TestByte(defaultCtx, 0); r != 0 || err != nil {
+ t.Fatal("TestByte(0) failed")
+ }
+ if r, err := client.TestByte(defaultCtx, -1); r != -1 || err != nil {
+ t.Fatal("TestByte(-1) failed")
+ }
+ if r, err := client.TestByte(defaultCtx, -127); r != -127 || err != nil {
+ t.Fatal("TestByte(-127) failed")
+ }
+
+ if r, err := client.TestI32(defaultCtx, -1); r != -1 || err != nil {
+ t.Fatal("TestI32(-1) failed")
+ }
+ if r, err := client.TestI32(defaultCtx, 1); r != 1 || err != nil {
+ t.Fatal("TestI32(1) failed")
+ }
+
+ if r, err := client.TestI64(defaultCtx, -5); r != -5 || err != nil {
+ t.Fatal("TestI64(-5) failed")
+ }
+ if r, err := client.TestI64(defaultCtx, 5); r != 5 || err != nil {
+ t.Fatal("TestI64(5) failed")
+ }
+ if r, err := client.TestI64(defaultCtx, -34359738368); r != -34359738368 || err != nil {
+ t.Fatal("TestI64(-34359738368) failed")
+ }
+
+ if r, err := client.TestDouble(defaultCtx, -5.2098523); r != -5.2098523 || err != nil {
+ t.Fatal("TestDouble(-5.2098523) failed")
+ }
+ if r, err := client.TestDouble(defaultCtx, -7.012052175215044); r != -7.012052175215044 || err != nil {
+ t.Fatal("TestDouble(-7.012052175215044) failed")
+ }
+
+ // TODO: add testBinary() call
+
+ out := thrifttest.NewXtruct()
+ out.StringThing = "Zero"
+ out.ByteThing = 1
+ out.I32Thing = -3
+ out.I64Thing = 1000000
+ if r, err := client.TestStruct(defaultCtx, out); !reflect.DeepEqual(r, out) || err != nil {
+ t.Fatal("TestStruct failed")
+ }
+
+ out2 := thrifttest.NewXtruct2()
+ out2.ByteThing = 1
+ out2.StructThing = out
+ out2.I32Thing = 5
+ if r, err := client.TestNest(defaultCtx, out2); !reflect.DeepEqual(r, out2) || err != nil {
+ t.Fatal("TestNest failed")
+ }
+
+ mapout := make(map[int32]int32)
+ for i := int32(0); i < 5; i++ {
+ mapout[i] = i - 10
+ }
+ if r, err := client.TestMap(defaultCtx, mapout); !reflect.DeepEqual(r, mapout) || err != nil {
+ t.Fatal("TestMap failed")
+ }
+
+ mapTestInput := map[string]string{
+ "a": "123", "a b": "with spaces ", "same": "same", "0": "numeric key",
+ "longValue": stringTest, stringTest: "long key",
+ }
+ if r, err := client.TestStringMap(defaultCtx, mapTestInput); !reflect.DeepEqual(r, mapTestInput) || err != nil {
+ t.Fatal("TestStringMap failed")
+ }
+
+ setTestInput := []int32{1, 2, 3}
+ if r, err := client.TestSet(defaultCtx, setTestInput); !reflect.DeepEqual(r, setTestInput) || err != nil {
+ t.Fatal("TestSet failed")
+ }
+
+ listTest := []int32{1, 2, 3}
+ if r, err := client.TestList(defaultCtx, listTest); !reflect.DeepEqual(r, listTest) || err != nil {
+ t.Fatal("TestList failed")
+ }
+
+ if r, err := client.TestEnum(defaultCtx, thrifttest.Numberz_ONE); r != thrifttest.Numberz_ONE || err != nil {
+ t.Fatal("TestEnum failed")
+ }
+
+ if r, err := client.TestTypedef(defaultCtx, 69); r != 69 || err != nil {
+ t.Fatal("TestTypedef failed")
+ }
+
+ mapMapTest := map[int32]map[int32]int32{
+ 4: {1: 1, 2: 2, 3: 3, 4: 4},
+ -4: {-4: -4, -3: -3, -2: -2, -1: -1},
+ }
+ if r, err := client.TestMapMap(defaultCtx, 1); !reflect.DeepEqual(r, mapMapTest) || err != nil {
+ t.Fatal("TestMapMap failed")
+ }
+
+ crazyX1 := thrifttest.NewXtruct()
+ crazyX1.StringThing = "Goodbye4"
+ crazyX1.ByteThing = 4
+ crazyX1.I32Thing = 4
+ crazyX1.I64Thing = 4
+
+ crazyX2 := thrifttest.NewXtruct()
+ crazyX2.StringThing = "Hello2"
+ crazyX2.ByteThing = 2
+ crazyX2.I32Thing = 2
+ crazyX2.I64Thing = 2
+
+ crazy := thrifttest.NewInsanity()
+ crazy.UserMap = map[thrifttest.Numberz]thrifttest.UserId{5: 5, 8: 8}
+ crazy.Xtructs = []*thrifttest.Xtruct{crazyX1, crazyX2}
+
+ crazyEmpty := thrifttest.NewInsanity()
+ crazyEmpty.UserMap = map[thrifttest.Numberz]thrifttest.UserId{}
+ crazyEmpty.Xtructs = []*thrifttest.Xtruct{}
+
+ insanity := map[thrifttest.UserId]map[thrifttest.Numberz]*thrifttest.Insanity{
+ 1: {thrifttest.Numberz_TWO: crazy, thrifttest.Numberz_THREE: crazy},
+ 2: {thrifttest.Numberz_SIX: crazyEmpty},
+ }
+ if r, err := client.TestInsanity(defaultCtx, crazy); !reflect.DeepEqual(r, insanity) || err != nil {
+ t.Fatal("TestInsanity failed")
+ }
+
+ if err := client.TestException(defaultCtx, "TException"); err == nil {
+ t.Fatal("TestException TException failed")
+ }
+
+ if err, ok := client.TestException(defaultCtx, "Xception").(*thrifttest.Xception); ok == false || err == nil {
+ t.Fatal("TestException Xception failed")
+ } else if err.ErrorCode != 1001 || err.Message != "Xception" {
+ t.Fatal("TestException Xception failed")
+ }
+
+ if err := client.TestException(defaultCtx, "no Exception"); err != nil {
+ t.Fatal("TestException no Exception failed")
+ }
+
+ if err := client.TestOneway(defaultCtx, 0); err != nil {
+ t.Fatal("TestOneway failed")
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/thrifttest_handler.go b/src/jaegertracing/thrift/lib/go/test/tests/thrifttest_handler.go
new file mode 100644
index 000000000..31b9ee23e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/thrifttest_handler.go
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * 'License'); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ "context"
+ "errors"
+ "thrift"
+ "thrifttest"
+ "time"
+)
+
+type SecondServiceHandler struct {
+}
+
+func NewSecondServiceHandler() *SecondServiceHandler {
+ return &SecondServiceHandler{}
+}
+
+func (p *SecondServiceHandler) BlahBlah(ctx context.Context) (err error) {
+ return nil
+}
+
+func (p *SecondServiceHandler) SecondtestString(ctx context.Context, thing string) (r string, err error) {
+ return thing, nil
+}
+
+type ThriftTestHandler struct {
+}
+
+func NewThriftTestHandler() *ThriftTestHandler {
+ return &ThriftTestHandler{}
+}
+
+func (p *ThriftTestHandler) TestVoid(ctx context.Context) (err error) {
+ return nil
+}
+
+func (p *ThriftTestHandler) TestString(ctx context.Context, thing string) (r string, err error) {
+ return thing, nil
+}
+
+func (p *ThriftTestHandler) TestBool(ctx context.Context, thing bool) (r bool, err error) {
+ return thing, nil
+}
+
+func (p *ThriftTestHandler) TestByte(ctx context.Context, thing int8) (r int8, err error) {
+ return thing, nil
+}
+
+func (p *ThriftTestHandler) TestI32(ctx context.Context, thing int32) (r int32, err error) {
+ return thing, nil
+}
+
+func (p *ThriftTestHandler) TestI64(ctx context.Context, thing int64) (r int64, err error) {
+ return thing, nil
+}
+
+func (p *ThriftTestHandler) TestDouble(ctx context.Context, thing float64) (r float64, err error) {
+ return thing, nil
+}
+
+func (p *ThriftTestHandler) TestBinary(ctx context.Context, thing []byte) (r []byte, err error) {
+ return thing, nil
+}
+
+func (p *ThriftTestHandler) TestStruct(ctx context.Context, thing *thrifttest.Xtruct) (r *thrifttest.Xtruct, err error) {
+ return thing, nil
+}
+
+func (p *ThriftTestHandler) TestNest(ctx context.Context, thing *thrifttest.Xtruct2) (r *thrifttest.Xtruct2, err error) {
+ return thing, nil
+}
+
+func (p *ThriftTestHandler) TestMap(ctx context.Context, thing map[int32]int32) (r map[int32]int32, err error) {
+ return thing, nil
+}
+
+func (p *ThriftTestHandler) TestStringMap(ctx context.Context, thing map[string]string) (r map[string]string, err error) {
+ return thing, nil
+}
+
+func (p *ThriftTestHandler) TestSet(ctx context.Context, thing []int32) (r []int32, err error) {
+ return thing, nil
+}
+
+func (p *ThriftTestHandler) TestList(ctx context.Context, thing []int32) (r []int32, err error) {
+ return thing, nil
+}
+
+func (p *ThriftTestHandler) TestEnum(ctx context.Context, thing thrifttest.Numberz) (r thrifttest.Numberz, err error) {
+ return thing, nil
+}
+
+func (p *ThriftTestHandler) TestTypedef(ctx context.Context, thing thrifttest.UserId) (r thrifttest.UserId, err error) {
+ return thing, nil
+}
+
+func (p *ThriftTestHandler) TestMapMap(ctx context.Context, hello int32) (r map[int32]map[int32]int32, err error) {
+ r = make(map[int32]map[int32]int32)
+ pos := make(map[int32]int32)
+ neg := make(map[int32]int32)
+
+ for i := int32(1); i < 5; i++ {
+ pos[i] = i
+ neg[-i] = -i
+ }
+ r[4] = pos
+ r[-4] = neg
+
+ return r, nil
+}
+
+func (p *ThriftTestHandler) TestInsanity(ctx context.Context, argument *thrifttest.Insanity) (r map[thrifttest.UserId]map[thrifttest.Numberz]*thrifttest.Insanity, err error) {
+ hello := thrifttest.NewXtruct()
+ hello.StringThing = "Hello2"
+ hello.ByteThing = 2
+ hello.I32Thing = 2
+ hello.I64Thing = 2
+
+ goodbye := thrifttest.NewXtruct()
+ goodbye.StringThing = "Goodbye4"
+ goodbye.ByteThing = 4
+ goodbye.I32Thing = 4
+ goodbye.I64Thing = 4
+
+ crazy := thrifttest.NewInsanity()
+ crazy.UserMap = make(map[thrifttest.Numberz]thrifttest.UserId)
+ crazy.UserMap[thrifttest.Numberz_EIGHT] = 8
+ crazy.UserMap[thrifttest.Numberz_FIVE] = 5
+ crazy.Xtructs = []*thrifttest.Xtruct{goodbye, hello}
+
+ first_map := make(map[thrifttest.Numberz]*thrifttest.Insanity)
+ second_map := make(map[thrifttest.Numberz]*thrifttest.Insanity)
+
+ first_map[thrifttest.Numberz_TWO] = crazy
+ first_map[thrifttest.Numberz_THREE] = crazy
+
+ looney := thrifttest.NewInsanity()
+ second_map[thrifttest.Numberz_SIX] = looney
+
+ var insane = make(map[thrifttest.UserId]map[thrifttest.Numberz]*thrifttest.Insanity)
+ insane[1] = first_map
+ insane[2] = second_map
+
+ return insane, nil
+}
+
+func (p *ThriftTestHandler) TestMulti(ctx context.Context, arg0 int8, arg1 int32, arg2 int64, arg3 map[int16]string, arg4 thrifttest.Numberz, arg5 thrifttest.UserId) (r *thrifttest.Xtruct, err error) {
+ r = thrifttest.NewXtruct()
+ r.StringThing = "Hello2"
+ r.ByteThing = arg0
+ r.I32Thing = arg1
+ r.I64Thing = arg2
+ return r, nil
+}
+
+func (p *ThriftTestHandler) TestException(ctx context.Context, arg string) (err error) {
+ if arg == "Xception" {
+ x := thrifttest.NewXception()
+ x.ErrorCode = 1001
+ x.Message = arg
+ return x
+ } else if arg == "TException" {
+ return thrift.TException(errors.New(arg))
+ } else {
+ return nil
+ }
+}
+
+func (p *ThriftTestHandler) TestMultiException(ctx context.Context, arg0 string, arg1 string) (r *thrifttest.Xtruct, err error) {
+ if arg0 == "Xception" {
+ x := thrifttest.NewXception()
+ x.ErrorCode = 1001
+ x.Message = "This is an Xception"
+ return nil, x
+ } else if arg0 == "Xception2" {
+ x2 := thrifttest.NewXception2()
+ x2.ErrorCode = 2002
+ x2.StructThing = thrifttest.NewXtruct()
+ x2.StructThing.StringThing = "This is an Xception2"
+ return nil, x2
+ }
+
+ res := thrifttest.NewXtruct()
+ res.StringThing = arg1
+ return res, nil
+}
+
+func (p *ThriftTestHandler) TestOneway(ctx context.Context, secondsToSleep int32) (err error) {
+ time.Sleep(time.Second * time.Duration(secondsToSleep))
+ return nil
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/union_binary_test.go b/src/jaegertracing/thrift/lib/go/test/tests/union_binary_test.go
new file mode 100644
index 000000000..bdae2cb92
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/union_binary_test.go
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ "testing"
+ "unionbinarytest"
+)
+
+
+// See https://issues.apache.org/jira/browse/THRIFT-4573
+func TestUnionBinary(t *testing.T) {
+ s := unionbinarytest.NewSample()
+ s.U1 = map[string]string{}
+ s.U2 = []byte{}
+ if n := s.CountSetFieldsSample(); n != 2 {
+ t.Errorf("Expected 2 set fields, got %d!", n)
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/test/tests/union_default_value_test.go b/src/jaegertracing/thrift/lib/go/test/tests/union_default_value_test.go
new file mode 100644
index 000000000..2dcbf4e44
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/test/tests/union_default_value_test.go
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tests
+
+import (
+ "testing"
+ "uniondefaultvaluetest"
+)
+
+func TestUnionDefaultValue(t *testing.T) {
+ s := uniondefaultvaluetest.NewTestStruct()
+ d := s.GetDescendant()
+ if d == nil {
+ t.Error("Default Union value not set!")
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/application_exception.go b/src/jaegertracing/thrift/lib/go/thrift/application_exception.go
new file mode 100644
index 000000000..0023c57cf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/application_exception.go
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+const (
+ UNKNOWN_APPLICATION_EXCEPTION = 0
+ UNKNOWN_METHOD = 1
+ INVALID_MESSAGE_TYPE_EXCEPTION = 2
+ WRONG_METHOD_NAME = 3
+ BAD_SEQUENCE_ID = 4
+ MISSING_RESULT = 5
+ INTERNAL_ERROR = 6
+ PROTOCOL_ERROR = 7
+ INVALID_TRANSFORM = 8
+ INVALID_PROTOCOL = 9
+ UNSUPPORTED_CLIENT_TYPE = 10
+)
+
+var defaultApplicationExceptionMessage = map[int32]string{
+ UNKNOWN_APPLICATION_EXCEPTION: "unknown application exception",
+ UNKNOWN_METHOD: "unknown method",
+ INVALID_MESSAGE_TYPE_EXCEPTION: "invalid message type",
+ WRONG_METHOD_NAME: "wrong method name",
+ BAD_SEQUENCE_ID: "bad sequence ID",
+ MISSING_RESULT: "missing result",
+ INTERNAL_ERROR: "unknown internal error",
+ PROTOCOL_ERROR: "unknown protocol error",
+ INVALID_TRANSFORM: "Invalid transform",
+ INVALID_PROTOCOL: "Invalid protocol",
+ UNSUPPORTED_CLIENT_TYPE: "Unsupported client type",
+}
+
+// Application level Thrift exception
+type TApplicationException interface {
+ TException
+ TypeId() int32
+ Read(iprot TProtocol) error
+ Write(oprot TProtocol) error
+}
+
+type tApplicationException struct {
+ message string
+ type_ int32
+}
+
+func (e tApplicationException) Error() string {
+ if e.message != "" {
+ return e.message
+ }
+ return defaultApplicationExceptionMessage[e.type_]
+}
+
+func NewTApplicationException(type_ int32, message string) TApplicationException {
+ return &tApplicationException{message, type_}
+}
+
+func (p *tApplicationException) TypeId() int32 {
+ return p.type_
+}
+
+func (p *tApplicationException) Read(iprot TProtocol) error {
+ // TODO: this should really be generated by the compiler
+ _, err := iprot.ReadStructBegin()
+ if err != nil {
+ return err
+ }
+
+ message := ""
+ type_ := int32(UNKNOWN_APPLICATION_EXCEPTION)
+
+ for {
+ _, ttype, id, err := iprot.ReadFieldBegin()
+ if err != nil {
+ return err
+ }
+ if ttype == STOP {
+ break
+ }
+ switch id {
+ case 1:
+ if ttype == STRING {
+ if message, err = iprot.ReadString(); err != nil {
+ return err
+ }
+ } else {
+ if err = SkipDefaultDepth(iprot, ttype); err != nil {
+ return err
+ }
+ }
+ case 2:
+ if ttype == I32 {
+ if type_, err = iprot.ReadI32(); err != nil {
+ return err
+ }
+ } else {
+ if err = SkipDefaultDepth(iprot, ttype); err != nil {
+ return err
+ }
+ }
+ default:
+ if err = SkipDefaultDepth(iprot, ttype); err != nil {
+ return err
+ }
+ }
+ if err = iprot.ReadFieldEnd(); err != nil {
+ return err
+ }
+ }
+ if err := iprot.ReadStructEnd(); err != nil {
+ return err
+ }
+
+ p.message = message
+ p.type_ = type_
+
+ return nil
+}
+
+func (p *tApplicationException) Write(oprot TProtocol) (err error) {
+ err = oprot.WriteStructBegin("TApplicationException")
+ if len(p.Error()) > 0 {
+ err = oprot.WriteFieldBegin("message", STRING, 1)
+ if err != nil {
+ return
+ }
+ err = oprot.WriteString(p.Error())
+ if err != nil {
+ return
+ }
+ err = oprot.WriteFieldEnd()
+ if err != nil {
+ return
+ }
+ }
+ err = oprot.WriteFieldBegin("type", I32, 2)
+ if err != nil {
+ return
+ }
+ err = oprot.WriteI32(p.type_)
+ if err != nil {
+ return
+ }
+ err = oprot.WriteFieldEnd()
+ if err != nil {
+ return
+ }
+ err = oprot.WriteFieldStop()
+ if err != nil {
+ return
+ }
+ err = oprot.WriteStructEnd()
+ return
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/application_exception_test.go b/src/jaegertracing/thrift/lib/go/thrift/application_exception_test.go
new file mode 100644
index 000000000..77433575d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/application_exception_test.go
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "testing"
+)
+
+func TestTApplicationException(t *testing.T) {
+ exc := NewTApplicationException(UNKNOWN_APPLICATION_EXCEPTION, "")
+ if exc.Error() != defaultApplicationExceptionMessage[UNKNOWN_APPLICATION_EXCEPTION] {
+ t.Fatalf("Expected empty string for exception but found '%s'", exc.Error())
+ }
+ if exc.TypeId() != UNKNOWN_APPLICATION_EXCEPTION {
+ t.Fatalf("Expected type UNKNOWN for exception but found '%v'", exc.TypeId())
+ }
+ exc = NewTApplicationException(WRONG_METHOD_NAME, "junk_method")
+ if exc.Error() != "junk_method" {
+ t.Fatalf("Expected 'junk_method' for exception but found '%s'", exc.Error())
+ }
+ if exc.TypeId() != WRONG_METHOD_NAME {
+ t.Fatalf("Expected type WRONG_METHOD_NAME for exception but found '%v'", exc.TypeId())
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/binary_protocol.go b/src/jaegertracing/thrift/lib/go/thrift/binary_protocol.go
new file mode 100644
index 000000000..93ae898cf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/binary_protocol.go
@@ -0,0 +1,505 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "bytes"
+ "context"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "math"
+)
+
+type TBinaryProtocol struct {
+ trans TRichTransport
+ origTransport TTransport
+ strictRead bool
+ strictWrite bool
+ buffer [64]byte
+}
+
+type TBinaryProtocolFactory struct {
+ strictRead bool
+ strictWrite bool
+}
+
+func NewTBinaryProtocolTransport(t TTransport) *TBinaryProtocol {
+ return NewTBinaryProtocol(t, false, true)
+}
+
+func NewTBinaryProtocol(t TTransport, strictRead, strictWrite bool) *TBinaryProtocol {
+ p := &TBinaryProtocol{origTransport: t, strictRead: strictRead, strictWrite: strictWrite}
+ if et, ok := t.(TRichTransport); ok {
+ p.trans = et
+ } else {
+ p.trans = NewTRichTransport(t)
+ }
+ return p
+}
+
+func NewTBinaryProtocolFactoryDefault() *TBinaryProtocolFactory {
+ return NewTBinaryProtocolFactory(false, true)
+}
+
+func NewTBinaryProtocolFactory(strictRead, strictWrite bool) *TBinaryProtocolFactory {
+ return &TBinaryProtocolFactory{strictRead: strictRead, strictWrite: strictWrite}
+}
+
+func (p *TBinaryProtocolFactory) GetProtocol(t TTransport) TProtocol {
+ return NewTBinaryProtocol(t, p.strictRead, p.strictWrite)
+}
+
+/**
+ * Writing Methods
+ */
+
+func (p *TBinaryProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) error {
+ if p.strictWrite {
+ version := uint32(VERSION_1) | uint32(typeId)
+ e := p.WriteI32(int32(version))
+ if e != nil {
+ return e
+ }
+ e = p.WriteString(name)
+ if e != nil {
+ return e
+ }
+ e = p.WriteI32(seqId)
+ return e
+ } else {
+ e := p.WriteString(name)
+ if e != nil {
+ return e
+ }
+ e = p.WriteByte(int8(typeId))
+ if e != nil {
+ return e
+ }
+ e = p.WriteI32(seqId)
+ return e
+ }
+ return nil
+}
+
+func (p *TBinaryProtocol) WriteMessageEnd() error {
+ return nil
+}
+
+func (p *TBinaryProtocol) WriteStructBegin(name string) error {
+ return nil
+}
+
+func (p *TBinaryProtocol) WriteStructEnd() error {
+ return nil
+}
+
+func (p *TBinaryProtocol) WriteFieldBegin(name string, typeId TType, id int16) error {
+ e := p.WriteByte(int8(typeId))
+ if e != nil {
+ return e
+ }
+ e = p.WriteI16(id)
+ return e
+}
+
+func (p *TBinaryProtocol) WriteFieldEnd() error {
+ return nil
+}
+
+func (p *TBinaryProtocol) WriteFieldStop() error {
+ e := p.WriteByte(STOP)
+ return e
+}
+
+func (p *TBinaryProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error {
+ e := p.WriteByte(int8(keyType))
+ if e != nil {
+ return e
+ }
+ e = p.WriteByte(int8(valueType))
+ if e != nil {
+ return e
+ }
+ e = p.WriteI32(int32(size))
+ return e
+}
+
+func (p *TBinaryProtocol) WriteMapEnd() error {
+ return nil
+}
+
+func (p *TBinaryProtocol) WriteListBegin(elemType TType, size int) error {
+ e := p.WriteByte(int8(elemType))
+ if e != nil {
+ return e
+ }
+ e = p.WriteI32(int32(size))
+ return e
+}
+
+func (p *TBinaryProtocol) WriteListEnd() error {
+ return nil
+}
+
+func (p *TBinaryProtocol) WriteSetBegin(elemType TType, size int) error {
+ e := p.WriteByte(int8(elemType))
+ if e != nil {
+ return e
+ }
+ e = p.WriteI32(int32(size))
+ return e
+}
+
+func (p *TBinaryProtocol) WriteSetEnd() error {
+ return nil
+}
+
+func (p *TBinaryProtocol) WriteBool(value bool) error {
+ if value {
+ return p.WriteByte(1)
+ }
+ return p.WriteByte(0)
+}
+
+func (p *TBinaryProtocol) WriteByte(value int8) error {
+ e := p.trans.WriteByte(byte(value))
+ return NewTProtocolException(e)
+}
+
+func (p *TBinaryProtocol) WriteI16(value int16) error {
+ v := p.buffer[0:2]
+ binary.BigEndian.PutUint16(v, uint16(value))
+ _, e := p.trans.Write(v)
+ return NewTProtocolException(e)
+}
+
+func (p *TBinaryProtocol) WriteI32(value int32) error {
+ v := p.buffer[0:4]
+ binary.BigEndian.PutUint32(v, uint32(value))
+ _, e := p.trans.Write(v)
+ return NewTProtocolException(e)
+}
+
+func (p *TBinaryProtocol) WriteI64(value int64) error {
+ v := p.buffer[0:8]
+ binary.BigEndian.PutUint64(v, uint64(value))
+ _, err := p.trans.Write(v)
+ return NewTProtocolException(err)
+}
+
+func (p *TBinaryProtocol) WriteDouble(value float64) error {
+ return p.WriteI64(int64(math.Float64bits(value)))
+}
+
+func (p *TBinaryProtocol) WriteString(value string) error {
+ e := p.WriteI32(int32(len(value)))
+ if e != nil {
+ return e
+ }
+ _, err := p.trans.WriteString(value)
+ return NewTProtocolException(err)
+}
+
+func (p *TBinaryProtocol) WriteBinary(value []byte) error {
+ e := p.WriteI32(int32(len(value)))
+ if e != nil {
+ return e
+ }
+ _, err := p.trans.Write(value)
+ return NewTProtocolException(err)
+}
+
+/**
+ * Reading methods
+ */
+
+func (p *TBinaryProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) {
+ size, e := p.ReadI32()
+ if e != nil {
+ return "", typeId, 0, NewTProtocolException(e)
+ }
+ if size < 0 {
+ typeId = TMessageType(size & 0x0ff)
+ version := int64(int64(size) & VERSION_MASK)
+ if version != VERSION_1 {
+ return name, typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, fmt.Errorf("Bad version in ReadMessageBegin"))
+ }
+ name, e = p.ReadString()
+ if e != nil {
+ return name, typeId, seqId, NewTProtocolException(e)
+ }
+ seqId, e = p.ReadI32()
+ if e != nil {
+ return name, typeId, seqId, NewTProtocolException(e)
+ }
+ return name, typeId, seqId, nil
+ }
+ if p.strictRead {
+ return name, typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, fmt.Errorf("Missing version in ReadMessageBegin"))
+ }
+ name, e2 := p.readStringBody(size)
+ if e2 != nil {
+ return name, typeId, seqId, e2
+ }
+ b, e3 := p.ReadByte()
+ if e3 != nil {
+ return name, typeId, seqId, e3
+ }
+ typeId = TMessageType(b)
+ seqId, e4 := p.ReadI32()
+ if e4 != nil {
+ return name, typeId, seqId, e4
+ }
+ return name, typeId, seqId, nil
+}
+
+func (p *TBinaryProtocol) ReadMessageEnd() error {
+ return nil
+}
+
+func (p *TBinaryProtocol) ReadStructBegin() (name string, err error) {
+ return
+}
+
+func (p *TBinaryProtocol) ReadStructEnd() error {
+ return nil
+}
+
+func (p *TBinaryProtocol) ReadFieldBegin() (name string, typeId TType, seqId int16, err error) {
+ t, err := p.ReadByte()
+ typeId = TType(t)
+ if err != nil {
+ return name, typeId, seqId, err
+ }
+ if t != STOP {
+ seqId, err = p.ReadI16()
+ }
+ return name, typeId, seqId, err
+}
+
+func (p *TBinaryProtocol) ReadFieldEnd() error {
+ return nil
+}
+
+var invalidDataLength = NewTProtocolExceptionWithType(INVALID_DATA, errors.New("Invalid data length"))
+
+func (p *TBinaryProtocol) ReadMapBegin() (kType, vType TType, size int, err error) {
+ k, e := p.ReadByte()
+ if e != nil {
+ err = NewTProtocolException(e)
+ return
+ }
+ kType = TType(k)
+ v, e := p.ReadByte()
+ if e != nil {
+ err = NewTProtocolException(e)
+ return
+ }
+ vType = TType(v)
+ size32, e := p.ReadI32()
+ if e != nil {
+ err = NewTProtocolException(e)
+ return
+ }
+ if size32 < 0 {
+ err = invalidDataLength
+ return
+ }
+ size = int(size32)
+ return kType, vType, size, nil
+}
+
+func (p *TBinaryProtocol) ReadMapEnd() error {
+ return nil
+}
+
+func (p *TBinaryProtocol) ReadListBegin() (elemType TType, size int, err error) {
+ b, e := p.ReadByte()
+ if e != nil {
+ err = NewTProtocolException(e)
+ return
+ }
+ elemType = TType(b)
+ size32, e := p.ReadI32()
+ if e != nil {
+ err = NewTProtocolException(e)
+ return
+ }
+ if size32 < 0 {
+ err = invalidDataLength
+ return
+ }
+ size = int(size32)
+
+ return
+}
+
+func (p *TBinaryProtocol) ReadListEnd() error {
+ return nil
+}
+
+func (p *TBinaryProtocol) ReadSetBegin() (elemType TType, size int, err error) {
+ b, e := p.ReadByte()
+ if e != nil {
+ err = NewTProtocolException(e)
+ return
+ }
+ elemType = TType(b)
+ size32, e := p.ReadI32()
+ if e != nil {
+ err = NewTProtocolException(e)
+ return
+ }
+ if size32 < 0 {
+ err = invalidDataLength
+ return
+ }
+ size = int(size32)
+ return elemType, size, nil
+}
+
+func (p *TBinaryProtocol) ReadSetEnd() error {
+ return nil
+}
+
+func (p *TBinaryProtocol) ReadBool() (bool, error) {
+ b, e := p.ReadByte()
+ v := true
+ if b != 1 {
+ v = false
+ }
+ return v, e
+}
+
+func (p *TBinaryProtocol) ReadByte() (int8, error) {
+ v, err := p.trans.ReadByte()
+ return int8(v), err
+}
+
+func (p *TBinaryProtocol) ReadI16() (value int16, err error) {
+ buf := p.buffer[0:2]
+ err = p.readAll(buf)
+ value = int16(binary.BigEndian.Uint16(buf))
+ return value, err
+}
+
+func (p *TBinaryProtocol) ReadI32() (value int32, err error) {
+ buf := p.buffer[0:4]
+ err = p.readAll(buf)
+ value = int32(binary.BigEndian.Uint32(buf))
+ return value, err
+}
+
+func (p *TBinaryProtocol) ReadI64() (value int64, err error) {
+ buf := p.buffer[0:8]
+ err = p.readAll(buf)
+ value = int64(binary.BigEndian.Uint64(buf))
+ return value, err
+}
+
+func (p *TBinaryProtocol) ReadDouble() (value float64, err error) {
+ buf := p.buffer[0:8]
+ err = p.readAll(buf)
+ value = math.Float64frombits(binary.BigEndian.Uint64(buf))
+ return value, err
+}
+
+func (p *TBinaryProtocol) ReadString() (value string, err error) {
+ size, e := p.ReadI32()
+ if e != nil {
+ return "", e
+ }
+ if size < 0 {
+ err = invalidDataLength
+ return
+ }
+
+ return p.readStringBody(size)
+}
+
+func (p *TBinaryProtocol) ReadBinary() ([]byte, error) {
+ size, e := p.ReadI32()
+ if e != nil {
+ return nil, e
+ }
+ if size < 0 {
+ return nil, invalidDataLength
+ }
+
+ isize := int(size)
+ buf := make([]byte, isize)
+ _, err := io.ReadFull(p.trans, buf)
+ return buf, NewTProtocolException(err)
+}
+
+func (p *TBinaryProtocol) Flush(ctx context.Context) (err error) {
+ return NewTProtocolException(p.trans.Flush(ctx))
+}
+
+func (p *TBinaryProtocol) Skip(fieldType TType) (err error) {
+ return SkipDefaultDepth(p, fieldType)
+}
+
+func (p *TBinaryProtocol) Transport() TTransport {
+ return p.origTransport
+}
+
+func (p *TBinaryProtocol) readAll(buf []byte) error {
+ _, err := io.ReadFull(p.trans, buf)
+ return NewTProtocolException(err)
+}
+
+const readLimit = 32768
+
+func (p *TBinaryProtocol) readStringBody(size int32) (value string, err error) {
+ if size < 0 {
+ return "", nil
+ }
+
+ var (
+ buf bytes.Buffer
+ e error
+ b []byte
+ )
+
+ switch {
+ case int(size) <= len(p.buffer):
+ b = p.buffer[:size] // avoids allocation for small reads
+ case int(size) < readLimit:
+ b = make([]byte, size)
+ default:
+ b = make([]byte, readLimit)
+ }
+
+ for size > 0 {
+ _, e = io.ReadFull(p.trans, b)
+ buf.Write(b)
+ if e != nil {
+ break
+ }
+ size -= readLimit
+ if size < readLimit && size > 0 {
+ b = b[:size]
+ }
+ }
+ return buf.String(), NewTProtocolException(e)
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/binary_protocol_test.go b/src/jaegertracing/thrift/lib/go/thrift/binary_protocol_test.go
new file mode 100644
index 000000000..0462cc79d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/binary_protocol_test.go
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "testing"
+)
+
+func TestReadWriteBinaryProtocol(t *testing.T) {
+ ReadWriteProtocolTest(t, NewTBinaryProtocolFactoryDefault())
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/buffered_transport.go b/src/jaegertracing/thrift/lib/go/thrift/buffered_transport.go
new file mode 100644
index 000000000..96702061b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/buffered_transport.go
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "bufio"
+ "context"
+)
+
+type TBufferedTransportFactory struct {
+ size int
+}
+
+type TBufferedTransport struct {
+ bufio.ReadWriter
+ tp TTransport
+}
+
+func (p *TBufferedTransportFactory) GetTransport(trans TTransport) (TTransport, error) {
+ return NewTBufferedTransport(trans, p.size), nil
+}
+
+func NewTBufferedTransportFactory(bufferSize int) *TBufferedTransportFactory {
+ return &TBufferedTransportFactory{size: bufferSize}
+}
+
+func NewTBufferedTransport(trans TTransport, bufferSize int) *TBufferedTransport {
+ return &TBufferedTransport{
+ ReadWriter: bufio.ReadWriter{
+ Reader: bufio.NewReaderSize(trans, bufferSize),
+ Writer: bufio.NewWriterSize(trans, bufferSize),
+ },
+ tp: trans,
+ }
+}
+
+func (p *TBufferedTransport) IsOpen() bool {
+ return p.tp.IsOpen()
+}
+
+func (p *TBufferedTransport) Open() (err error) {
+ return p.tp.Open()
+}
+
+func (p *TBufferedTransport) Close() (err error) {
+ return p.tp.Close()
+}
+
+func (p *TBufferedTransport) Read(b []byte) (int, error) {
+ n, err := p.ReadWriter.Read(b)
+ if err != nil {
+ p.ReadWriter.Reader.Reset(p.tp)
+ }
+ return n, err
+}
+
+func (p *TBufferedTransport) Write(b []byte) (int, error) {
+ n, err := p.ReadWriter.Write(b)
+ if err != nil {
+ p.ReadWriter.Writer.Reset(p.tp)
+ }
+ return n, err
+}
+
+func (p *TBufferedTransport) Flush(ctx context.Context) error {
+ if err := p.ReadWriter.Flush(); err != nil {
+ p.ReadWriter.Writer.Reset(p.tp)
+ return err
+ }
+ return p.tp.Flush(ctx)
+}
+
+func (p *TBufferedTransport) RemainingBytes() (num_bytes uint64) {
+ return p.tp.RemainingBytes()
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/buffered_transport_test.go b/src/jaegertracing/thrift/lib/go/thrift/buffered_transport_test.go
new file mode 100644
index 000000000..95ec0cbd2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/buffered_transport_test.go
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "testing"
+)
+
+func TestBufferedTransport(t *testing.T) {
+ trans := NewTBufferedTransport(NewTMemoryBuffer(), 10240)
+ TransportTest(t, trans, trans)
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/client.go b/src/jaegertracing/thrift/lib/go/thrift/client.go
new file mode 100644
index 000000000..b073a952d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/client.go
@@ -0,0 +1,95 @@
+package thrift
+
+import (
+ "context"
+ "fmt"
+)
+
+type TClient interface {
+ Call(ctx context.Context, method string, args, result TStruct) error
+}
+
+type TStandardClient struct {
+ seqId int32
+ iprot, oprot TProtocol
+}
+
+// TStandardClient implements TClient, and uses the standard message format for Thrift.
+// It is not safe for concurrent use.
+func NewTStandardClient(inputProtocol, outputProtocol TProtocol) *TStandardClient {
+ return &TStandardClient{
+ iprot: inputProtocol,
+ oprot: outputProtocol,
+ }
+}
+
+func (p *TStandardClient) Send(ctx context.Context, oprot TProtocol, seqId int32, method string, args TStruct) error {
+ // Set headers from context object on THeaderProtocol
+ if headerProt, ok := oprot.(*THeaderProtocol); ok {
+ headerProt.ClearWriteHeaders()
+ for _, key := range GetWriteHeaderList(ctx) {
+ if value, ok := GetHeader(ctx, key); ok {
+ headerProt.SetWriteHeader(key, value)
+ }
+ }
+ }
+
+ if err := oprot.WriteMessageBegin(method, CALL, seqId); err != nil {
+ return err
+ }
+ if err := args.Write(oprot); err != nil {
+ return err
+ }
+ if err := oprot.WriteMessageEnd(); err != nil {
+ return err
+ }
+ return oprot.Flush(ctx)
+}
+
+func (p *TStandardClient) Recv(iprot TProtocol, seqId int32, method string, result TStruct) error {
+ rMethod, rTypeId, rSeqId, err := iprot.ReadMessageBegin()
+ if err != nil {
+ return err
+ }
+
+ if method != rMethod {
+ return NewTApplicationException(WRONG_METHOD_NAME, fmt.Sprintf("%s: wrong method name", method))
+ } else if seqId != rSeqId {
+ return NewTApplicationException(BAD_SEQUENCE_ID, fmt.Sprintf("%s: out of order sequence response", method))
+ } else if rTypeId == EXCEPTION {
+ var exception tApplicationException
+ if err := exception.Read(iprot); err != nil {
+ return err
+ }
+
+ if err := iprot.ReadMessageEnd(); err != nil {
+ return err
+ }
+
+ return &exception
+ } else if rTypeId != REPLY {
+ return NewTApplicationException(INVALID_MESSAGE_TYPE_EXCEPTION, fmt.Sprintf("%s: invalid message type", method))
+ }
+
+ if err := result.Read(iprot); err != nil {
+ return err
+ }
+
+ return iprot.ReadMessageEnd()
+}
+
+func (p *TStandardClient) Call(ctx context.Context, method string, args, result TStruct) error {
+ p.seqId++
+ seqId := p.seqId
+
+ if err := p.Send(ctx, p.oprot, seqId, method, args); err != nil {
+ return err
+ }
+
+ // method is oneway
+ if result == nil {
+ return nil
+ }
+
+ return p.Recv(p.iprot, seqId, method, result)
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/common_test.go b/src/jaegertracing/thrift/lib/go/thrift/common_test.go
new file mode 100644
index 000000000..93597ff8a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/common_test.go
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import "context"
+
+type mockProcessor struct {
+ ProcessFunc func(in, out TProtocol) (bool, TException)
+}
+
+func (m *mockProcessor) Process(ctx context.Context, in, out TProtocol) (bool, TException) {
+ return m.ProcessFunc(in, out)
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/compact_protocol.go b/src/jaegertracing/thrift/lib/go/thrift/compact_protocol.go
new file mode 100644
index 000000000..1900d50c3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/compact_protocol.go
@@ -0,0 +1,810 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "math"
+)
+
+const (
+ COMPACT_PROTOCOL_ID = 0x082
+ COMPACT_VERSION = 1
+ COMPACT_VERSION_MASK = 0x1f
+ COMPACT_TYPE_MASK = 0x0E0
+ COMPACT_TYPE_BITS = 0x07
+ COMPACT_TYPE_SHIFT_AMOUNT = 5
+)
+
+type tCompactType byte
+
+const (
+ COMPACT_BOOLEAN_TRUE = 0x01
+ COMPACT_BOOLEAN_FALSE = 0x02
+ COMPACT_BYTE = 0x03
+ COMPACT_I16 = 0x04
+ COMPACT_I32 = 0x05
+ COMPACT_I64 = 0x06
+ COMPACT_DOUBLE = 0x07
+ COMPACT_BINARY = 0x08
+ COMPACT_LIST = 0x09
+ COMPACT_SET = 0x0A
+ COMPACT_MAP = 0x0B
+ COMPACT_STRUCT = 0x0C
+)
+
+var (
+ ttypeToCompactType map[TType]tCompactType
+)
+
+func init() {
+ ttypeToCompactType = map[TType]tCompactType{
+ STOP: STOP,
+ BOOL: COMPACT_BOOLEAN_TRUE,
+ BYTE: COMPACT_BYTE,
+ I16: COMPACT_I16,
+ I32: COMPACT_I32,
+ I64: COMPACT_I64,
+ DOUBLE: COMPACT_DOUBLE,
+ STRING: COMPACT_BINARY,
+ LIST: COMPACT_LIST,
+ SET: COMPACT_SET,
+ MAP: COMPACT_MAP,
+ STRUCT: COMPACT_STRUCT,
+ }
+}
+
+type TCompactProtocolFactory struct{}
+
+func NewTCompactProtocolFactory() *TCompactProtocolFactory {
+ return &TCompactProtocolFactory{}
+}
+
+func (p *TCompactProtocolFactory) GetProtocol(trans TTransport) TProtocol {
+ return NewTCompactProtocol(trans)
+}
+
+type TCompactProtocol struct {
+ trans TRichTransport
+ origTransport TTransport
+
+ // Used to keep track of the last field for the current and previous structs,
+ // so we can do the delta stuff.
+ lastField []int
+ lastFieldId int
+
+ // If we encounter a boolean field begin, save the TField here so it can
+ // have the value incorporated.
+ booleanFieldName string
+ booleanFieldId int16
+ booleanFieldPending bool
+
+ // If we read a field header, and it's a boolean field, save the boolean
+ // value here so that readBool can use it.
+ boolValue bool
+ boolValueIsNotNull bool
+ buffer [64]byte
+}
+
+// Create a TCompactProtocol given a TTransport
+func NewTCompactProtocol(trans TTransport) *TCompactProtocol {
+ p := &TCompactProtocol{origTransport: trans, lastField: []int{}}
+ if et, ok := trans.(TRichTransport); ok {
+ p.trans = et
+ } else {
+ p.trans = NewTRichTransport(trans)
+ }
+
+ return p
+
+}
+
+//
+// Public Writing methods.
+//
+
+// Write a message header to the wire. Compact Protocol messages contain the
+// protocol version so we can migrate forwards in the future if need be.
+func (p *TCompactProtocol) WriteMessageBegin(name string, typeId TMessageType, seqid int32) error {
+ err := p.writeByteDirect(COMPACT_PROTOCOL_ID)
+ if err != nil {
+ return NewTProtocolException(err)
+ }
+ err = p.writeByteDirect((COMPACT_VERSION & COMPACT_VERSION_MASK) | ((byte(typeId) << COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_MASK))
+ if err != nil {
+ return NewTProtocolException(err)
+ }
+ _, err = p.writeVarint32(seqid)
+ if err != nil {
+ return NewTProtocolException(err)
+ }
+ e := p.WriteString(name)
+ return e
+
+}
+
+func (p *TCompactProtocol) WriteMessageEnd() error { return nil }
+
+// Write a struct begin. This doesn't actually put anything on the wire. We
+// use it as an opportunity to put special placeholder markers on the field
+// stack so we can get the field id deltas correct.
+func (p *TCompactProtocol) WriteStructBegin(name string) error {
+ p.lastField = append(p.lastField, p.lastFieldId)
+ p.lastFieldId = 0
+ return nil
+}
+
+// Write a struct end. This doesn't actually put anything on the wire. We use
+// this as an opportunity to pop the last field from the current struct off
+// of the field stack.
+func (p *TCompactProtocol) WriteStructEnd() error {
+ p.lastFieldId = p.lastField[len(p.lastField)-1]
+ p.lastField = p.lastField[:len(p.lastField)-1]
+ return nil
+}
+
+func (p *TCompactProtocol) WriteFieldBegin(name string, typeId TType, id int16) error {
+ if typeId == BOOL {
+ // we want to possibly include the value, so we'll wait.
+ p.booleanFieldName, p.booleanFieldId, p.booleanFieldPending = name, id, true
+ return nil
+ }
+ _, err := p.writeFieldBeginInternal(name, typeId, id, 0xFF)
+ return NewTProtocolException(err)
+}
+
+// The workhorse of writeFieldBegin. It has the option of doing a
+// 'type override' of the type header. This is used specifically in the
+// boolean field case.
+func (p *TCompactProtocol) writeFieldBeginInternal(name string, typeId TType, id int16, typeOverride byte) (int, error) {
+ // short lastField = lastField_.pop();
+
+ // if there's a type override, use that.
+ var typeToWrite byte
+ if typeOverride == 0xFF {
+ typeToWrite = byte(p.getCompactType(typeId))
+ } else {
+ typeToWrite = typeOverride
+ }
+ // check if we can use delta encoding for the field id
+ fieldId := int(id)
+ written := 0
+ if fieldId > p.lastFieldId && fieldId-p.lastFieldId <= 15 {
+ // write them together
+ err := p.writeByteDirect(byte((fieldId-p.lastFieldId)<<4) | typeToWrite)
+ if err != nil {
+ return 0, err
+ }
+ } else {
+ // write them separate
+ err := p.writeByteDirect(typeToWrite)
+ if err != nil {
+ return 0, err
+ }
+ err = p.WriteI16(id)
+ written = 1 + 2
+ if err != nil {
+ return 0, err
+ }
+ }
+
+ p.lastFieldId = fieldId
+ // p.lastField.Push(field.id);
+ return written, nil
+}
+
+func (p *TCompactProtocol) WriteFieldEnd() error { return nil }
+
+func (p *TCompactProtocol) WriteFieldStop() error {
+ err := p.writeByteDirect(STOP)
+ return NewTProtocolException(err)
+}
+
+func (p *TCompactProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error {
+ if size == 0 {
+ err := p.writeByteDirect(0)
+ return NewTProtocolException(err)
+ }
+ _, err := p.writeVarint32(int32(size))
+ if err != nil {
+ return NewTProtocolException(err)
+ }
+ err = p.writeByteDirect(byte(p.getCompactType(keyType))<<4 | byte(p.getCompactType(valueType)))
+ return NewTProtocolException(err)
+}
+
+func (p *TCompactProtocol) WriteMapEnd() error { return nil }
+
+// Write a list header.
+func (p *TCompactProtocol) WriteListBegin(elemType TType, size int) error {
+ _, err := p.writeCollectionBegin(elemType, size)
+ return NewTProtocolException(err)
+}
+
+func (p *TCompactProtocol) WriteListEnd() error { return nil }
+
+// Write a set header.
+func (p *TCompactProtocol) WriteSetBegin(elemType TType, size int) error {
+ _, err := p.writeCollectionBegin(elemType, size)
+ return NewTProtocolException(err)
+}
+
+func (p *TCompactProtocol) WriteSetEnd() error { return nil }
+
+func (p *TCompactProtocol) WriteBool(value bool) error {
+ v := byte(COMPACT_BOOLEAN_FALSE)
+ if value {
+ v = byte(COMPACT_BOOLEAN_TRUE)
+ }
+ if p.booleanFieldPending {
+ // we haven't written the field header yet
+ _, err := p.writeFieldBeginInternal(p.booleanFieldName, BOOL, p.booleanFieldId, v)
+ p.booleanFieldPending = false
+ return NewTProtocolException(err)
+ }
+ // we're not part of a field, so just write the value.
+ err := p.writeByteDirect(v)
+ return NewTProtocolException(err)
+}
+
+// Write a byte. Nothing to see here!
+func (p *TCompactProtocol) WriteByte(value int8) error {
+ err := p.writeByteDirect(byte(value))
+ return NewTProtocolException(err)
+}
+
+// Write an I16 as a zigzag varint.
+func (p *TCompactProtocol) WriteI16(value int16) error {
+ _, err := p.writeVarint32(p.int32ToZigzag(int32(value)))
+ return NewTProtocolException(err)
+}
+
+// Write an i32 as a zigzag varint.
+func (p *TCompactProtocol) WriteI32(value int32) error {
+ _, err := p.writeVarint32(p.int32ToZigzag(value))
+ return NewTProtocolException(err)
+}
+
+// Write an i64 as a zigzag varint.
+func (p *TCompactProtocol) WriteI64(value int64) error {
+ _, err := p.writeVarint64(p.int64ToZigzag(value))
+ return NewTProtocolException(err)
+}
+
+// Write a double to the wire as 8 bytes.
+func (p *TCompactProtocol) WriteDouble(value float64) error {
+ buf := p.buffer[0:8]
+ binary.LittleEndian.PutUint64(buf, math.Float64bits(value))
+ _, err := p.trans.Write(buf)
+ return NewTProtocolException(err)
+}
+
+// Write a string to the wire with a varint size preceding.
+func (p *TCompactProtocol) WriteString(value string) error {
+ _, e := p.writeVarint32(int32(len(value)))
+ if e != nil {
+ return NewTProtocolException(e)
+ }
+ if len(value) > 0 {
+ }
+ _, e = p.trans.WriteString(value)
+ return e
+}
+
+// Write a byte array, using a varint for the size.
+func (p *TCompactProtocol) WriteBinary(bin []byte) error {
+ _, e := p.writeVarint32(int32(len(bin)))
+ if e != nil {
+ return NewTProtocolException(e)
+ }
+ if len(bin) > 0 {
+ _, e = p.trans.Write(bin)
+ return NewTProtocolException(e)
+ }
+ return nil
+}
+
+//
+// Reading methods.
+//
+
+// Read a message header.
+func (p *TCompactProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) {
+
+ protocolId, err := p.readByteDirect()
+ if err != nil {
+ return
+ }
+
+ if protocolId != COMPACT_PROTOCOL_ID {
+ e := fmt.Errorf("Expected protocol id %02x but got %02x", COMPACT_PROTOCOL_ID, protocolId)
+ return "", typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, e)
+ }
+
+ versionAndType, err := p.readByteDirect()
+ if err != nil {
+ return
+ }
+
+ version := versionAndType & COMPACT_VERSION_MASK
+ typeId = TMessageType((versionAndType >> COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_BITS)
+ if version != COMPACT_VERSION {
+ e := fmt.Errorf("Expected version %02x but got %02x", COMPACT_VERSION, version)
+ err = NewTProtocolExceptionWithType(BAD_VERSION, e)
+ return
+ }
+ seqId, e := p.readVarint32()
+ if e != nil {
+ err = NewTProtocolException(e)
+ return
+ }
+ name, err = p.ReadString()
+ return
+}
+
+func (p *TCompactProtocol) ReadMessageEnd() error { return nil }
+
+// Read a struct begin. There's nothing on the wire for this, but it is our
+// opportunity to push a new struct begin marker onto the field stack.
+func (p *TCompactProtocol) ReadStructBegin() (name string, err error) {
+ p.lastField = append(p.lastField, p.lastFieldId)
+ p.lastFieldId = 0
+ return
+}
+
+// Doesn't actually consume any wire data, just removes the last field for
+// this struct from the field stack.
+func (p *TCompactProtocol) ReadStructEnd() error {
+ // consume the last field we read off the wire.
+ p.lastFieldId = p.lastField[len(p.lastField)-1]
+ p.lastField = p.lastField[:len(p.lastField)-1]
+ return nil
+}
+
+// Read a field header off the wire.
+func (p *TCompactProtocol) ReadFieldBegin() (name string, typeId TType, id int16, err error) {
+ t, err := p.readByteDirect()
+ if err != nil {
+ return
+ }
+
+ // if it's a stop, then we can return immediately, as the struct is over.
+ if (t & 0x0f) == STOP {
+ return "", STOP, 0, nil
+ }
+
+ // mask off the 4 MSB of the type header. it could contain a field id delta.
+ modifier := int16((t & 0xf0) >> 4)
+ if modifier == 0 {
+ // not a delta. look ahead for the zigzag varint field id.
+ id, err = p.ReadI16()
+ if err != nil {
+ return
+ }
+ } else {
+ // has a delta. add the delta to the last read field id.
+ id = int16(p.lastFieldId) + modifier
+ }
+ typeId, e := p.getTType(tCompactType(t & 0x0f))
+ if e != nil {
+ err = NewTProtocolException(e)
+ return
+ }
+
+ // if this happens to be a boolean field, the value is encoded in the type
+ if p.isBoolType(t) {
+ // save the boolean value in a special instance variable.
+ p.boolValue = (byte(t)&0x0f == COMPACT_BOOLEAN_TRUE)
+ p.boolValueIsNotNull = true
+ }
+
+ // push the new field onto the field stack so we can keep the deltas going.
+ p.lastFieldId = int(id)
+ return
+}
+
+func (p *TCompactProtocol) ReadFieldEnd() error { return nil }
+
+// Read a map header off the wire. If the size is zero, skip reading the key
+// and value type. This means that 0-length maps will yield TMaps without the
+// "correct" types.
+func (p *TCompactProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, err error) {
+ size32, e := p.readVarint32()
+ if e != nil {
+ err = NewTProtocolException(e)
+ return
+ }
+ if size32 < 0 {
+ err = invalidDataLength
+ return
+ }
+ size = int(size32)
+
+ keyAndValueType := byte(STOP)
+ if size != 0 {
+ keyAndValueType, err = p.readByteDirect()
+ if err != nil {
+ return
+ }
+ }
+ keyType, _ = p.getTType(tCompactType(keyAndValueType >> 4))
+ valueType, _ = p.getTType(tCompactType(keyAndValueType & 0xf))
+ return
+}
+
+func (p *TCompactProtocol) ReadMapEnd() error { return nil }
+
+// Read a list header off the wire. If the list size is 0-14, the size will
+// be packed into the element type header. If it's a longer list, the 4 MSB
+// of the element type header will be 0xF, and a varint will follow with the
+// true size.
+func (p *TCompactProtocol) ReadListBegin() (elemType TType, size int, err error) {
+ size_and_type, err := p.readByteDirect()
+ if err != nil {
+ return
+ }
+ size = int((size_and_type >> 4) & 0x0f)
+ if size == 15 {
+ size2, e := p.readVarint32()
+ if e != nil {
+ err = NewTProtocolException(e)
+ return
+ }
+ if size2 < 0 {
+ err = invalidDataLength
+ return
+ }
+ size = int(size2)
+ }
+ elemType, e := p.getTType(tCompactType(size_and_type))
+ if e != nil {
+ err = NewTProtocolException(e)
+ return
+ }
+ return
+}
+
+func (p *TCompactProtocol) ReadListEnd() error { return nil }
+
+// Read a set header off the wire. If the set size is 0-14, the size will
+// be packed into the element type header. If it's a longer set, the 4 MSB
+// of the element type header will be 0xF, and a varint will follow with the
+// true size.
+func (p *TCompactProtocol) ReadSetBegin() (elemType TType, size int, err error) {
+ return p.ReadListBegin()
+}
+
+func (p *TCompactProtocol) ReadSetEnd() error { return nil }
+
+// Read a boolean off the wire. If this is a boolean field, the value should
+// already have been read during readFieldBegin, so we'll just consume the
+// pre-stored value. Otherwise, read a byte.
+func (p *TCompactProtocol) ReadBool() (value bool, err error) {
+ if p.boolValueIsNotNull {
+ p.boolValueIsNotNull = false
+ return p.boolValue, nil
+ }
+ v, err := p.readByteDirect()
+ return v == COMPACT_BOOLEAN_TRUE, err
+}
+
+// Read a single byte off the wire. Nothing interesting here.
+func (p *TCompactProtocol) ReadByte() (int8, error) {
+ v, err := p.readByteDirect()
+ if err != nil {
+ return 0, NewTProtocolException(err)
+ }
+ return int8(v), err
+}
+
+// Read an i16 from the wire as a zigzag varint.
+func (p *TCompactProtocol) ReadI16() (value int16, err error) {
+ v, err := p.ReadI32()
+ return int16(v), err
+}
+
+// Read an i32 from the wire as a zigzag varint.
+func (p *TCompactProtocol) ReadI32() (value int32, err error) {
+ v, e := p.readVarint32()
+ if e != nil {
+ return 0, NewTProtocolException(e)
+ }
+ value = p.zigzagToInt32(v)
+ return value, nil
+}
+
+// Read an i64 from the wire as a zigzag varint.
+func (p *TCompactProtocol) ReadI64() (value int64, err error) {
+ v, e := p.readVarint64()
+ if e != nil {
+ return 0, NewTProtocolException(e)
+ }
+ value = p.zigzagToInt64(v)
+ return value, nil
+}
+
+// No magic here - just read a double off the wire.
+func (p *TCompactProtocol) ReadDouble() (value float64, err error) {
+ longBits := p.buffer[0:8]
+ _, e := io.ReadFull(p.trans, longBits)
+ if e != nil {
+ return 0.0, NewTProtocolException(e)
+ }
+ return math.Float64frombits(p.bytesToUint64(longBits)), nil
+}
+
+// Reads a []byte (via readBinary), and then UTF-8 decodes it.
+func (p *TCompactProtocol) ReadString() (value string, err error) {
+ length, e := p.readVarint32()
+ if e != nil {
+ return "", NewTProtocolException(e)
+ }
+ if length < 0 {
+ return "", invalidDataLength
+ }
+
+ if length == 0 {
+ return "", nil
+ }
+ var buf []byte
+ if length <= int32(len(p.buffer)) {
+ buf = p.buffer[0:length]
+ } else {
+ buf = make([]byte, length)
+ }
+ _, e = io.ReadFull(p.trans, buf)
+ return string(buf), NewTProtocolException(e)
+}
+
+// Read a []byte from the wire.
+func (p *TCompactProtocol) ReadBinary() (value []byte, err error) {
+ length, e := p.readVarint32()
+ if e != nil {
+ return nil, NewTProtocolException(e)
+ }
+ if length == 0 {
+ return []byte{}, nil
+ }
+ if length < 0 {
+ return nil, invalidDataLength
+ }
+
+ buf := make([]byte, length)
+ _, e = io.ReadFull(p.trans, buf)
+ return buf, NewTProtocolException(e)
+}
+
+func (p *TCompactProtocol) Flush(ctx context.Context) (err error) {
+ return NewTProtocolException(p.trans.Flush(ctx))
+}
+
+func (p *TCompactProtocol) Skip(fieldType TType) (err error) {
+ return SkipDefaultDepth(p, fieldType)
+}
+
+func (p *TCompactProtocol) Transport() TTransport {
+ return p.origTransport
+}
+
+//
+// Internal writing methods
+//
+
+// Abstract method for writing the start of lists and sets. List and sets on
+// the wire differ only by the type indicator.
+func (p *TCompactProtocol) writeCollectionBegin(elemType TType, size int) (int, error) {
+ if size <= 14 {
+ return 1, p.writeByteDirect(byte(int32(size<<4) | int32(p.getCompactType(elemType))))
+ }
+ err := p.writeByteDirect(0xf0 | byte(p.getCompactType(elemType)))
+ if err != nil {
+ return 0, err
+ }
+ m, err := p.writeVarint32(int32(size))
+ return 1 + m, err
+}
+
+// Write an i32 as a varint. Results in 1-5 bytes on the wire.
+// TODO(pomack): make a permanent buffer like writeVarint64?
+func (p *TCompactProtocol) writeVarint32(n int32) (int, error) {
+ i32buf := p.buffer[0:5]
+ idx := 0
+ for {
+ if (n & ^0x7F) == 0 {
+ i32buf[idx] = byte(n)
+ idx++
+ // p.writeByteDirect(byte(n));
+ break
+ // return;
+ } else {
+ i32buf[idx] = byte((n & 0x7F) | 0x80)
+ idx++
+ // p.writeByteDirect(byte(((n & 0x7F) | 0x80)));
+ u := uint32(n)
+ n = int32(u >> 7)
+ }
+ }
+ return p.trans.Write(i32buf[0:idx])
+}
+
+// Write an i64 as a varint. Results in 1-10 bytes on the wire.
+func (p *TCompactProtocol) writeVarint64(n int64) (int, error) {
+ varint64out := p.buffer[0:10]
+ idx := 0
+ for {
+ if (n & ^0x7F) == 0 {
+ varint64out[idx] = byte(n)
+ idx++
+ break
+ } else {
+ varint64out[idx] = byte((n & 0x7F) | 0x80)
+ idx++
+ u := uint64(n)
+ n = int64(u >> 7)
+ }
+ }
+ return p.trans.Write(varint64out[0:idx])
+}
+
+// Convert l into a zigzag long. This allows negative numbers to be
+// represented compactly as a varint.
+func (p *TCompactProtocol) int64ToZigzag(l int64) int64 {
+ return (l << 1) ^ (l >> 63)
+}
+
+// Convert l into a zigzag long. This allows negative numbers to be
+// represented compactly as a varint.
+func (p *TCompactProtocol) int32ToZigzag(n int32) int32 {
+ return (n << 1) ^ (n >> 31)
+}
+
+func (p *TCompactProtocol) fixedUint64ToBytes(n uint64, buf []byte) {
+ binary.LittleEndian.PutUint64(buf, n)
+}
+
+func (p *TCompactProtocol) fixedInt64ToBytes(n int64, buf []byte) {
+ binary.LittleEndian.PutUint64(buf, uint64(n))
+}
+
+// Writes a byte without any possibility of all that field header nonsense.
+// Used internally by other writing methods that know they need to write a byte.
+func (p *TCompactProtocol) writeByteDirect(b byte) error {
+ return p.trans.WriteByte(b)
+}
+
+// Writes a byte without any possibility of all that field header nonsense.
+func (p *TCompactProtocol) writeIntAsByteDirect(n int) (int, error) {
+ return 1, p.writeByteDirect(byte(n))
+}
+
+//
+// Internal reading methods
+//
+
+// Read an i32 from the wire as a varint. The MSB of each byte is set
+// if there is another byte to follow. This can read up to 5 bytes.
+func (p *TCompactProtocol) readVarint32() (int32, error) {
+ // if the wire contains the right stuff, this will just truncate the i64 we
+ // read and get us the right sign.
+ v, err := p.readVarint64()
+ return int32(v), err
+}
+
+// Read an i64 from the wire as a proper varint. The MSB of each byte is set
+// if there is another byte to follow. This can read up to 10 bytes.
+func (p *TCompactProtocol) readVarint64() (int64, error) {
+ shift := uint(0)
+ result := int64(0)
+ for {
+ b, err := p.readByteDirect()
+ if err != nil {
+ return 0, err
+ }
+ result |= int64(b&0x7f) << shift
+ if (b & 0x80) != 0x80 {
+ break
+ }
+ shift += 7
+ }
+ return result, nil
+}
+
+// Read a byte, unlike ReadByte that reads Thrift-byte that is i8.
+func (p *TCompactProtocol) readByteDirect() (byte, error) {
+ return p.trans.ReadByte()
+}
+
+//
+// encoding helpers
+//
+
+// Convert from zigzag int to int.
+func (p *TCompactProtocol) zigzagToInt32(n int32) int32 {
+ u := uint32(n)
+ return int32(u>>1) ^ -(n & 1)
+}
+
+// Convert from zigzag long to long.
+func (p *TCompactProtocol) zigzagToInt64(n int64) int64 {
+ u := uint64(n)
+ return int64(u>>1) ^ -(n & 1)
+}
+
+// Note that it's important that the mask bytes are long literals,
+// otherwise they'll default to ints, and when you shift an int left 56 bits,
+// you just get a messed up int.
+func (p *TCompactProtocol) bytesToInt64(b []byte) int64 {
+ return int64(binary.LittleEndian.Uint64(b))
+}
+
+// Note that it's important that the mask bytes are long literals,
+// otherwise they'll default to ints, and when you shift an int left 56 bits,
+// you just get a messed up int.
+func (p *TCompactProtocol) bytesToUint64(b []byte) uint64 {
+ return binary.LittleEndian.Uint64(b)
+}
+
+//
+// type testing and converting
+//
+
+func (p *TCompactProtocol) isBoolType(b byte) bool {
+ return (b&0x0f) == COMPACT_BOOLEAN_TRUE || (b&0x0f) == COMPACT_BOOLEAN_FALSE
+}
+
+// Given a tCompactType constant, convert it to its corresponding
+// TType value.
+func (p *TCompactProtocol) getTType(t tCompactType) (TType, error) {
+ switch byte(t) & 0x0f {
+ case STOP:
+ return STOP, nil
+ case COMPACT_BOOLEAN_FALSE, COMPACT_BOOLEAN_TRUE:
+ return BOOL, nil
+ case COMPACT_BYTE:
+ return BYTE, nil
+ case COMPACT_I16:
+ return I16, nil
+ case COMPACT_I32:
+ return I32, nil
+ case COMPACT_I64:
+ return I64, nil
+ case COMPACT_DOUBLE:
+ return DOUBLE, nil
+ case COMPACT_BINARY:
+ return STRING, nil
+ case COMPACT_LIST:
+ return LIST, nil
+ case COMPACT_SET:
+ return SET, nil
+ case COMPACT_MAP:
+ return MAP, nil
+ case COMPACT_STRUCT:
+ return STRUCT, nil
+ }
+ return STOP, TException(fmt.Errorf("don't know what type: %v", t&0x0f))
+}
+
+// Given a TType value, find the appropriate TCompactProtocol.Types constant.
+func (p *TCompactProtocol) getCompactType(t TType) tCompactType {
+ return ttypeToCompactType[t]
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/compact_protocol_test.go b/src/jaegertracing/thrift/lib/go/thrift/compact_protocol_test.go
new file mode 100644
index 000000000..65f77f2c4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/compact_protocol_test.go
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestReadWriteCompactProtocol(t *testing.T) {
+ ReadWriteProtocolTest(t, NewTCompactProtocolFactory())
+
+ transports := []TTransport{
+ NewTMemoryBuffer(),
+ NewStreamTransportRW(bytes.NewBuffer(make([]byte, 0, 16384))),
+ NewTFramedTransport(NewTMemoryBuffer()),
+ }
+
+ zlib0, _ := NewTZlibTransport(NewTMemoryBuffer(), 0)
+ zlib6, _ := NewTZlibTransport(NewTMemoryBuffer(), 6)
+ zlib9, _ := NewTZlibTransport(NewTFramedTransport(NewTMemoryBuffer()), 9)
+ transports = append(transports, zlib0, zlib6, zlib9)
+
+ for _, trans := range transports {
+ p := NewTCompactProtocol(trans)
+ ReadWriteBool(t, p, trans)
+ p = NewTCompactProtocol(trans)
+ ReadWriteByte(t, p, trans)
+ p = NewTCompactProtocol(trans)
+ ReadWriteI16(t, p, trans)
+ p = NewTCompactProtocol(trans)
+ ReadWriteI32(t, p, trans)
+ p = NewTCompactProtocol(trans)
+ ReadWriteI64(t, p, trans)
+ p = NewTCompactProtocol(trans)
+ ReadWriteDouble(t, p, trans)
+ p = NewTCompactProtocol(trans)
+ ReadWriteString(t, p, trans)
+ p = NewTCompactProtocol(trans)
+ ReadWriteBinary(t, p, trans)
+ trans.Close()
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/context.go b/src/jaegertracing/thrift/lib/go/thrift/context.go
new file mode 100644
index 000000000..d15c1bcf8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/context.go
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import "context"
+
+var defaultCtx = context.Background()
diff --git a/src/jaegertracing/thrift/lib/go/thrift/debug_protocol.go b/src/jaegertracing/thrift/lib/go/thrift/debug_protocol.go
new file mode 100644
index 000000000..57943e0f3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/debug_protocol.go
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+ "log"
+)
+
+type TDebugProtocol struct {
+ Delegate TProtocol
+ LogPrefix string
+}
+
+type TDebugProtocolFactory struct {
+ Underlying TProtocolFactory
+ LogPrefix string
+}
+
+func NewTDebugProtocolFactory(underlying TProtocolFactory, logPrefix string) *TDebugProtocolFactory {
+ return &TDebugProtocolFactory{
+ Underlying: underlying,
+ LogPrefix: logPrefix,
+ }
+}
+
+func (t *TDebugProtocolFactory) GetProtocol(trans TTransport) TProtocol {
+ return &TDebugProtocol{
+ Delegate: t.Underlying.GetProtocol(trans),
+ LogPrefix: t.LogPrefix,
+ }
+}
+
+func (tdp *TDebugProtocol) WriteMessageBegin(name string, typeId TMessageType, seqid int32) error {
+ err := tdp.Delegate.WriteMessageBegin(name, typeId, seqid)
+ log.Printf("%sWriteMessageBegin(name=%#v, typeId=%#v, seqid=%#v) => %#v", tdp.LogPrefix, name, typeId, seqid, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteMessageEnd() error {
+ err := tdp.Delegate.WriteMessageEnd()
+ log.Printf("%sWriteMessageEnd() => %#v", tdp.LogPrefix, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteStructBegin(name string) error {
+ err := tdp.Delegate.WriteStructBegin(name)
+ log.Printf("%sWriteStructBegin(name=%#v) => %#v", tdp.LogPrefix, name, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteStructEnd() error {
+ err := tdp.Delegate.WriteStructEnd()
+ log.Printf("%sWriteStructEnd() => %#v", tdp.LogPrefix, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteFieldBegin(name string, typeId TType, id int16) error {
+ err := tdp.Delegate.WriteFieldBegin(name, typeId, id)
+ log.Printf("%sWriteFieldBegin(name=%#v, typeId=%#v, id%#v) => %#v", tdp.LogPrefix, name, typeId, id, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteFieldEnd() error {
+ err := tdp.Delegate.WriteFieldEnd()
+ log.Printf("%sWriteFieldEnd() => %#v", tdp.LogPrefix, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteFieldStop() error {
+ err := tdp.Delegate.WriteFieldStop()
+ log.Printf("%sWriteFieldStop() => %#v", tdp.LogPrefix, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error {
+ err := tdp.Delegate.WriteMapBegin(keyType, valueType, size)
+ log.Printf("%sWriteMapBegin(keyType=%#v, valueType=%#v, size=%#v) => %#v", tdp.LogPrefix, keyType, valueType, size, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteMapEnd() error {
+ err := tdp.Delegate.WriteMapEnd()
+ log.Printf("%sWriteMapEnd() => %#v", tdp.LogPrefix, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteListBegin(elemType TType, size int) error {
+ err := tdp.Delegate.WriteListBegin(elemType, size)
+ log.Printf("%sWriteListBegin(elemType=%#v, size=%#v) => %#v", tdp.LogPrefix, elemType, size, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteListEnd() error {
+ err := tdp.Delegate.WriteListEnd()
+ log.Printf("%sWriteListEnd() => %#v", tdp.LogPrefix, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteSetBegin(elemType TType, size int) error {
+ err := tdp.Delegate.WriteSetBegin(elemType, size)
+ log.Printf("%sWriteSetBegin(elemType=%#v, size=%#v) => %#v", tdp.LogPrefix, elemType, size, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteSetEnd() error {
+ err := tdp.Delegate.WriteSetEnd()
+ log.Printf("%sWriteSetEnd() => %#v", tdp.LogPrefix, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteBool(value bool) error {
+ err := tdp.Delegate.WriteBool(value)
+ log.Printf("%sWriteBool(value=%#v) => %#v", tdp.LogPrefix, value, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteByte(value int8) error {
+ err := tdp.Delegate.WriteByte(value)
+ log.Printf("%sWriteByte(value=%#v) => %#v", tdp.LogPrefix, value, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteI16(value int16) error {
+ err := tdp.Delegate.WriteI16(value)
+ log.Printf("%sWriteI16(value=%#v) => %#v", tdp.LogPrefix, value, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteI32(value int32) error {
+ err := tdp.Delegate.WriteI32(value)
+ log.Printf("%sWriteI32(value=%#v) => %#v", tdp.LogPrefix, value, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteI64(value int64) error {
+ err := tdp.Delegate.WriteI64(value)
+ log.Printf("%sWriteI64(value=%#v) => %#v", tdp.LogPrefix, value, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteDouble(value float64) error {
+ err := tdp.Delegate.WriteDouble(value)
+ log.Printf("%sWriteDouble(value=%#v) => %#v", tdp.LogPrefix, value, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteString(value string) error {
+ err := tdp.Delegate.WriteString(value)
+ log.Printf("%sWriteString(value=%#v) => %#v", tdp.LogPrefix, value, err)
+ return err
+}
+func (tdp *TDebugProtocol) WriteBinary(value []byte) error {
+ err := tdp.Delegate.WriteBinary(value)
+ log.Printf("%sWriteBinary(value=%#v) => %#v", tdp.LogPrefix, value, err)
+ return err
+}
+
+func (tdp *TDebugProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqid int32, err error) {
+ name, typeId, seqid, err = tdp.Delegate.ReadMessageBegin()
+ log.Printf("%sReadMessageBegin() (name=%#v, typeId=%#v, seqid=%#v, err=%#v)", tdp.LogPrefix, name, typeId, seqid, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadMessageEnd() (err error) {
+ err = tdp.Delegate.ReadMessageEnd()
+ log.Printf("%sReadMessageEnd() err=%#v", tdp.LogPrefix, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadStructBegin() (name string, err error) {
+ name, err = tdp.Delegate.ReadStructBegin()
+ log.Printf("%sReadStructBegin() (name%#v, err=%#v)", tdp.LogPrefix, name, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadStructEnd() (err error) {
+ err = tdp.Delegate.ReadStructEnd()
+ log.Printf("%sReadStructEnd() err=%#v", tdp.LogPrefix, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadFieldBegin() (name string, typeId TType, id int16, err error) {
+ name, typeId, id, err = tdp.Delegate.ReadFieldBegin()
+ log.Printf("%sReadFieldBegin() (name=%#v, typeId=%#v, id=%#v, err=%#v)", tdp.LogPrefix, name, typeId, id, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadFieldEnd() (err error) {
+ err = tdp.Delegate.ReadFieldEnd()
+ log.Printf("%sReadFieldEnd() err=%#v", tdp.LogPrefix, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, err error) {
+ keyType, valueType, size, err = tdp.Delegate.ReadMapBegin()
+ log.Printf("%sReadMapBegin() (keyType=%#v, valueType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, keyType, valueType, size, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadMapEnd() (err error) {
+ err = tdp.Delegate.ReadMapEnd()
+ log.Printf("%sReadMapEnd() err=%#v", tdp.LogPrefix, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadListBegin() (elemType TType, size int, err error) {
+ elemType, size, err = tdp.Delegate.ReadListBegin()
+ log.Printf("%sReadListBegin() (elemType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, elemType, size, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadListEnd() (err error) {
+ err = tdp.Delegate.ReadListEnd()
+ log.Printf("%sReadListEnd() err=%#v", tdp.LogPrefix, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadSetBegin() (elemType TType, size int, err error) {
+ elemType, size, err = tdp.Delegate.ReadSetBegin()
+ log.Printf("%sReadSetBegin() (elemType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, elemType, size, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadSetEnd() (err error) {
+ err = tdp.Delegate.ReadSetEnd()
+ log.Printf("%sReadSetEnd() err=%#v", tdp.LogPrefix, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadBool() (value bool, err error) {
+ value, err = tdp.Delegate.ReadBool()
+ log.Printf("%sReadBool() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadByte() (value int8, err error) {
+ value, err = tdp.Delegate.ReadByte()
+ log.Printf("%sReadByte() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadI16() (value int16, err error) {
+ value, err = tdp.Delegate.ReadI16()
+ log.Printf("%sReadI16() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadI32() (value int32, err error) {
+ value, err = tdp.Delegate.ReadI32()
+ log.Printf("%sReadI32() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadI64() (value int64, err error) {
+ value, err = tdp.Delegate.ReadI64()
+ log.Printf("%sReadI64() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadDouble() (value float64, err error) {
+ value, err = tdp.Delegate.ReadDouble()
+ log.Printf("%sReadDouble() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadString() (value string, err error) {
+ value, err = tdp.Delegate.ReadString()
+ log.Printf("%sReadString() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
+ return
+}
+func (tdp *TDebugProtocol) ReadBinary() (value []byte, err error) {
+ value, err = tdp.Delegate.ReadBinary()
+ log.Printf("%sReadBinary() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
+ return
+}
+func (tdp *TDebugProtocol) Skip(fieldType TType) (err error) {
+ err = tdp.Delegate.Skip(fieldType)
+ log.Printf("%sSkip(fieldType=%#v) (err=%#v)", tdp.LogPrefix, fieldType, err)
+ return
+}
+func (tdp *TDebugProtocol) Flush(ctx context.Context) (err error) {
+ err = tdp.Delegate.Flush(ctx)
+ log.Printf("%sFlush() (err=%#v)", tdp.LogPrefix, err)
+ return
+}
+
+func (tdp *TDebugProtocol) Transport() TTransport {
+ return tdp.Delegate.Transport()
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/deserializer.go b/src/jaegertracing/thrift/lib/go/thrift/deserializer.go
new file mode 100644
index 000000000..91a0983a4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/deserializer.go
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+type TDeserializer struct {
+ Transport TTransport
+ Protocol TProtocol
+}
+
+func NewTDeserializer() *TDeserializer {
+ var transport TTransport
+ transport = NewTMemoryBufferLen(1024)
+
+ protocol := NewTBinaryProtocolFactoryDefault().GetProtocol(transport)
+
+ return &TDeserializer{
+ transport,
+ protocol}
+}
+
+func (t *TDeserializer) ReadString(msg TStruct, s string) (err error) {
+ err = nil
+ if _, err = t.Transport.Write([]byte(s)); err != nil {
+ return
+ }
+ if err = msg.Read(t.Protocol); err != nil {
+ return
+ }
+ return
+}
+
+func (t *TDeserializer) Read(msg TStruct, b []byte) (err error) {
+ err = nil
+ if _, err = t.Transport.Write(b); err != nil {
+ return
+ }
+ if err = msg.Read(t.Protocol); err != nil {
+ return
+ }
+ return
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/exception.go b/src/jaegertracing/thrift/lib/go/thrift/exception.go
new file mode 100644
index 000000000..ea8d6f661
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/exception.go
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "errors"
+)
+
+// Generic Thrift exception
+type TException interface {
+ error
+}
+
+// Prepends additional information to an error without losing the Thrift exception interface
+func PrependError(prepend string, err error) error {
+ if t, ok := err.(TTransportException); ok {
+ return NewTTransportException(t.TypeId(), prepend+t.Error())
+ }
+ if t, ok := err.(TProtocolException); ok {
+ return NewTProtocolExceptionWithType(t.TypeId(), errors.New(prepend+err.Error()))
+ }
+ if t, ok := err.(TApplicationException); ok {
+ return NewTApplicationException(t.TypeId(), prepend+t.Error())
+ }
+
+ return errors.New(prepend + err.Error())
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/exception_test.go b/src/jaegertracing/thrift/lib/go/thrift/exception_test.go
new file mode 100644
index 000000000..71f5e2c7e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/exception_test.go
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "errors"
+ "testing"
+)
+
+func TestPrependError(t *testing.T) {
+ err := NewTApplicationException(INTERNAL_ERROR, "original error")
+ err2, ok := PrependError("Prepend: ", err).(TApplicationException)
+ if !ok {
+ t.Fatal("Couldn't cast error TApplicationException")
+ }
+ if err2.Error() != "Prepend: original error" {
+ t.Fatal("Unexpected error string")
+ }
+ if err2.TypeId() != INTERNAL_ERROR {
+ t.Fatal("Unexpected type error")
+ }
+
+ err3 := NewTProtocolExceptionWithType(INVALID_DATA, errors.New("original error"))
+ err4, ok := PrependError("Prepend: ", err3).(TProtocolException)
+ if !ok {
+ t.Fatal("Couldn't cast error TProtocolException")
+ }
+ if err4.Error() != "Prepend: original error" {
+ t.Fatal("Unexpected error string")
+ }
+ if err4.TypeId() != INVALID_DATA {
+ t.Fatal("Unexpected type error")
+ }
+
+ err5 := NewTTransportException(TIMED_OUT, "original error")
+ err6, ok := PrependError("Prepend: ", err5).(TTransportException)
+ if !ok {
+ t.Fatal("Couldn't cast error TTransportException")
+ }
+ if err6.Error() != "Prepend: original error" {
+ t.Fatal("Unexpected error string")
+ }
+ if err6.TypeId() != TIMED_OUT {
+ t.Fatal("Unexpected type error")
+ }
+
+ err7 := errors.New("original error")
+ err8 := PrependError("Prepend: ", err7)
+ if err8.Error() != "Prepend: original error" {
+ t.Fatal("Unexpected error string")
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/field.go b/src/jaegertracing/thrift/lib/go/thrift/field.go
new file mode 100644
index 000000000..9d6652550
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/field.go
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+// Helper class that encapsulates field metadata.
+type field struct {
+ name string
+ typeId TType
+ id int
+}
+
+func newField(n string, t TType, i int) *field {
+ return &field{name: n, typeId: t, id: i}
+}
+
+func (p *field) Name() string {
+ if p == nil {
+ return ""
+ }
+ return p.name
+}
+
+func (p *field) TypeId() TType {
+ if p == nil {
+ return TType(VOID)
+ }
+ return p.typeId
+}
+
+func (p *field) Id() int {
+ if p == nil {
+ return -1
+ }
+ return p.id
+}
+
+func (p *field) String() string {
+ if p == nil {
+ return "<nil>"
+ }
+ return "<TField name:'" + p.name + "' type:" + string(p.typeId) + " field-id:" + string(p.id) + ">"
+}
+
+var ANONYMOUS_FIELD *field
+
+type fieldSlice []field
+
+func (p fieldSlice) Len() int {
+ return len(p)
+}
+
+func (p fieldSlice) Less(i, j int) bool {
+ return p[i].Id() < p[j].Id()
+}
+
+func (p fieldSlice) Swap(i, j int) {
+ p[i], p[j] = p[j], p[i]
+}
+
+func init() {
+ ANONYMOUS_FIELD = newField("", STOP, 0)
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/framed_transport.go b/src/jaegertracing/thrift/lib/go/thrift/framed_transport.go
new file mode 100644
index 000000000..34275b5f4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/framed_transport.go
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "encoding/binary"
+ "fmt"
+ "io"
+)
+
+const DEFAULT_MAX_LENGTH = 16384000
+
+type TFramedTransport struct {
+ transport TTransport
+ buf bytes.Buffer
+ reader *bufio.Reader
+ frameSize uint32 //Current remaining size of the frame. if ==0 read next frame header
+ buffer [4]byte
+ maxLength uint32
+}
+
+type tFramedTransportFactory struct {
+ factory TTransportFactory
+ maxLength uint32
+}
+
+func NewTFramedTransportFactory(factory TTransportFactory) TTransportFactory {
+ return &tFramedTransportFactory{factory: factory, maxLength: DEFAULT_MAX_LENGTH}
+}
+
+func NewTFramedTransportFactoryMaxLength(factory TTransportFactory, maxLength uint32) TTransportFactory {
+ return &tFramedTransportFactory{factory: factory, maxLength: maxLength}
+}
+
+func (p *tFramedTransportFactory) GetTransport(base TTransport) (TTransport, error) {
+ tt, err := p.factory.GetTransport(base)
+ if err != nil {
+ return nil, err
+ }
+ return NewTFramedTransportMaxLength(tt, p.maxLength), nil
+}
+
+func NewTFramedTransport(transport TTransport) *TFramedTransport {
+ return &TFramedTransport{transport: transport, reader: bufio.NewReader(transport), maxLength: DEFAULT_MAX_LENGTH}
+}
+
+func NewTFramedTransportMaxLength(transport TTransport, maxLength uint32) *TFramedTransport {
+ return &TFramedTransport{transport: transport, reader: bufio.NewReader(transport), maxLength: maxLength}
+}
+
+func (p *TFramedTransport) Open() error {
+ return p.transport.Open()
+}
+
+func (p *TFramedTransport) IsOpen() bool {
+ return p.transport.IsOpen()
+}
+
+func (p *TFramedTransport) Close() error {
+ return p.transport.Close()
+}
+
+func (p *TFramedTransport) Read(buf []byte) (l int, err error) {
+ if p.frameSize == 0 {
+ p.frameSize, err = p.readFrameHeader()
+ if err != nil {
+ return
+ }
+ }
+ if p.frameSize < uint32(len(buf)) {
+ frameSize := p.frameSize
+ tmp := make([]byte, p.frameSize)
+ l, err = p.Read(tmp)
+ copy(buf, tmp)
+ if err == nil {
+ // Note: It's important to only return an error when l
+ // is zero.
+ // In io.Reader.Read interface, it's perfectly fine to
+ // return partial data and nil error, which means
+ // "This is all the data we have right now without
+ // blocking. If you need the full data, call Read again
+ // or use io.ReadFull instead".
+ // Returning partial data with an error actually means
+ // there's no more data after the partial data just
+ // returned, which is not true in this case
+ // (it might be that the other end just haven't written
+ // them yet).
+ if l == 0 {
+ err = NewTTransportExceptionFromError(fmt.Errorf("Not enough frame size %d to read %d bytes", frameSize, len(buf)))
+ }
+ return
+ }
+ }
+ got, err := p.reader.Read(buf)
+ p.frameSize = p.frameSize - uint32(got)
+ //sanity check
+ if p.frameSize < 0 {
+ return 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "Negative frame size")
+ }
+ return got, NewTTransportExceptionFromError(err)
+}
+
+func (p *TFramedTransport) ReadByte() (c byte, err error) {
+ if p.frameSize == 0 {
+ p.frameSize, err = p.readFrameHeader()
+ if err != nil {
+ return
+ }
+ }
+ if p.frameSize < 1 {
+ return 0, NewTTransportExceptionFromError(fmt.Errorf("Not enough frame size %d to read %d bytes", p.frameSize, 1))
+ }
+ c, err = p.reader.ReadByte()
+ if err == nil {
+ p.frameSize--
+ }
+ return
+}
+
+func (p *TFramedTransport) Write(buf []byte) (int, error) {
+ n, err := p.buf.Write(buf)
+ return n, NewTTransportExceptionFromError(err)
+}
+
+func (p *TFramedTransport) WriteByte(c byte) error {
+ return p.buf.WriteByte(c)
+}
+
+func (p *TFramedTransport) WriteString(s string) (n int, err error) {
+ return p.buf.WriteString(s)
+}
+
+func (p *TFramedTransport) Flush(ctx context.Context) error {
+ size := p.buf.Len()
+ buf := p.buffer[:4]
+ binary.BigEndian.PutUint32(buf, uint32(size))
+ _, err := p.transport.Write(buf)
+ if err != nil {
+ p.buf.Truncate(0)
+ return NewTTransportExceptionFromError(err)
+ }
+ if size > 0 {
+ if n, err := p.buf.WriteTo(p.transport); err != nil {
+ print("Error while flushing write buffer of size ", size, " to transport, only wrote ", n, " bytes: ", err.Error(), "\n")
+ p.buf.Truncate(0)
+ return NewTTransportExceptionFromError(err)
+ }
+ }
+ err = p.transport.Flush(ctx)
+ return NewTTransportExceptionFromError(err)
+}
+
+func (p *TFramedTransport) readFrameHeader() (uint32, error) {
+ buf := p.buffer[:4]
+ if _, err := io.ReadFull(p.reader, buf); err != nil {
+ return 0, err
+ }
+ size := binary.BigEndian.Uint32(buf)
+ if size < 0 || size > p.maxLength {
+ return 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, fmt.Sprintf("Incorrect frame size (%d)", size))
+ }
+ return size, nil
+}
+
+func (p *TFramedTransport) RemainingBytes() (num_bytes uint64) {
+ return uint64(p.frameSize)
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/framed_transport_test.go b/src/jaegertracing/thrift/lib/go/thrift/framed_transport_test.go
new file mode 100644
index 000000000..8f683ef30
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/framed_transport_test.go
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "testing"
+)
+
+func TestFramedTransport(t *testing.T) {
+ trans := NewTFramedTransport(NewTMemoryBuffer())
+ TransportTest(t, trans, trans)
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/header_context.go b/src/jaegertracing/thrift/lib/go/thrift/header_context.go
new file mode 100644
index 000000000..21e880d66
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/header_context.go
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+)
+
+// See https://godoc.org/context#WithValue on why do we need the unexported typedefs.
+type (
+ headerKey string
+ headerKeyList int
+)
+
+// Values for headerKeyList.
+const (
+ headerKeyListRead headerKeyList = iota
+ headerKeyListWrite
+)
+
+// SetHeader sets a header in the context.
+func SetHeader(ctx context.Context, key, value string) context.Context {
+ return context.WithValue(
+ ctx,
+ headerKey(key),
+ value,
+ )
+}
+
+// GetHeader returns a value of the given header from the context.
+func GetHeader(ctx context.Context, key string) (value string, ok bool) {
+ if v := ctx.Value(headerKey(key)); v != nil {
+ value, ok = v.(string)
+ }
+ return
+}
+
+// SetReadHeaderList sets the key list of read THeaders in the context.
+func SetReadHeaderList(ctx context.Context, keys []string) context.Context {
+ return context.WithValue(
+ ctx,
+ headerKeyListRead,
+ keys,
+ )
+}
+
+// GetReadHeaderList returns the key list of read THeaders from the context.
+func GetReadHeaderList(ctx context.Context) []string {
+ if v := ctx.Value(headerKeyListRead); v != nil {
+ if value, ok := v.([]string); ok {
+ return value
+ }
+ }
+ return nil
+}
+
+// SetWriteHeaderList sets the key list of THeaders to write in the context.
+func SetWriteHeaderList(ctx context.Context, keys []string) context.Context {
+ return context.WithValue(
+ ctx,
+ headerKeyListWrite,
+ keys,
+ )
+}
+
+// GetWriteHeaderList returns the key list of THeaders to write from the context.
+func GetWriteHeaderList(ctx context.Context) []string {
+ if v := ctx.Value(headerKeyListWrite); v != nil {
+ if value, ok := v.([]string); ok {
+ return value
+ }
+ }
+ return nil
+}
+
+// AddReadTHeaderToContext adds the whole THeader headers into context.
+func AddReadTHeaderToContext(ctx context.Context, headers THeaderMap) context.Context {
+ keys := make([]string, 0, len(headers))
+ for key, value := range headers {
+ ctx = SetHeader(ctx, key, value)
+ keys = append(keys, key)
+ }
+ return SetReadHeaderList(ctx, keys)
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/header_context_test.go b/src/jaegertracing/thrift/lib/go/thrift/header_context_test.go
new file mode 100644
index 000000000..a1ea2d093
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/header_context_test.go
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+ "reflect"
+ "testing"
+)
+
+func TestSetGetHeader(t *testing.T) {
+ const (
+ key = "foo"
+ value = "bar"
+ )
+ ctx := context.Background()
+
+ ctx = SetHeader(ctx, key, value)
+
+ checkGet := func(t *testing.T, ctx context.Context) {
+ t.Helper()
+ got, ok := GetHeader(ctx, key)
+ if !ok {
+ t.Fatalf("Cannot get header %q back after setting it.", key)
+ }
+ if got != value {
+ t.Fatalf("Header value expected %q, got %q instead", value, got)
+ }
+ }
+
+ checkGet(t, ctx)
+
+ t.Run(
+ "NoConflicts",
+ func(t *testing.T) {
+ type otherType string
+ const otherValue = "bar2"
+
+ ctx = context.WithValue(ctx, otherType(key), otherValue)
+ checkGet(t, ctx)
+ },
+ )
+
+ t.Run(
+ "GetHeaderOnNonExistKey",
+ func(t *testing.T) {
+ const otherKey = "foo2"
+
+ if _, ok := GetHeader(ctx, otherKey); ok {
+ t.Errorf("GetHeader returned ok on non-existing key %q", otherKey)
+ }
+ },
+ )
+}
+
+func TestReadKeyList(t *testing.T) {
+ headers := THeaderMap{
+ "key1": "value1",
+ "key2": "value2",
+ }
+ ctx := context.Background()
+
+ ctx = AddReadTHeaderToContext(ctx, headers)
+
+ got := make(THeaderMap)
+ keys := GetReadHeaderList(ctx)
+ t.Logf("keys: %+v", keys)
+ for _, key := range keys {
+ value, ok := GetHeader(ctx, key)
+ if ok {
+ got[key] = value
+ } else {
+ t.Errorf("Cannot get key %q from context", key)
+ }
+ }
+
+ if !reflect.DeepEqual(headers, got) {
+ t.Errorf("Expected header map %+v, got %+v", headers, got)
+ }
+
+ writtenKeys := GetWriteHeaderList(ctx)
+ if len(writtenKeys) > 0 {
+ t.Errorf(
+ "Expected empty GetWriteHeaderList() result, got %+v",
+ writtenKeys,
+ )
+ }
+}
+
+func TestWriteKeyList(t *testing.T) {
+ keys := []string{
+ "key1",
+ "key2",
+ }
+ ctx := context.Background()
+
+ ctx = SetWriteHeaderList(ctx, keys)
+ got := GetWriteHeaderList(ctx)
+
+ if !reflect.DeepEqual(keys, got) {
+ t.Errorf("Expected header keys %+v, got %+v", keys, got)
+ }
+
+ readKeys := GetReadHeaderList(ctx)
+ if len(readKeys) > 0 {
+ t.Errorf(
+ "Expected empty GetReadHeaderList() result, got %+v",
+ readKeys,
+ )
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/header_protocol.go b/src/jaegertracing/thrift/lib/go/thrift/header_protocol.go
new file mode 100644
index 000000000..46205b28b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/header_protocol.go
@@ -0,0 +1,305 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+)
+
+// THeaderProtocol is a thrift protocol that implements THeader:
+// https://github.com/apache/thrift/blob/master/doc/specs/HeaderFormat.md
+//
+// It supports either binary or compact protocol as the wrapped protocol.
+//
+// Most of the THeader handlings are happening inside THeaderTransport.
+type THeaderProtocol struct {
+ transport *THeaderTransport
+
+ // Will be initialized on first read/write.
+ protocol TProtocol
+}
+
+// NewTHeaderProtocol creates a new THeaderProtocol from the underlying
+// transport. The passed in transport will be wrapped with THeaderTransport.
+//
+// Note that THeaderTransport handles frame and zlib by itself,
+// so the underlying transport should be a raw socket transports (TSocket or TSSLSocket),
+// instead of rich transports like TZlibTransport or TFramedTransport.
+func NewTHeaderProtocol(trans TTransport) *THeaderProtocol {
+ t := NewTHeaderTransport(trans)
+ p, _ := THeaderProtocolDefault.GetProtocol(t)
+ return &THeaderProtocol{
+ transport: t,
+ protocol: p,
+ }
+}
+
+type tHeaderProtocolFactory struct{}
+
+func (tHeaderProtocolFactory) GetProtocol(trans TTransport) TProtocol {
+ return NewTHeaderProtocol(trans)
+}
+
+// NewTHeaderProtocolFactory creates a factory for THeader.
+//
+// It's a wrapper for NewTHeaderProtocol
+func NewTHeaderProtocolFactory() TProtocolFactory {
+ return tHeaderProtocolFactory{}
+}
+
+// Transport returns the underlying transport.
+//
+// It's guaranteed to be of type *THeaderTransport.
+func (p *THeaderProtocol) Transport() TTransport {
+ return p.transport
+}
+
+// GetReadHeaders returns the THeaderMap read from transport.
+func (p *THeaderProtocol) GetReadHeaders() THeaderMap {
+ return p.transport.GetReadHeaders()
+}
+
+// SetWriteHeader sets a header for write.
+func (p *THeaderProtocol) SetWriteHeader(key, value string) {
+ p.transport.SetWriteHeader(key, value)
+}
+
+// ClearWriteHeaders clears all write headers previously set.
+func (p *THeaderProtocol) ClearWriteHeaders() {
+ p.transport.ClearWriteHeaders()
+}
+
+// AddTransform add a transform for writing.
+func (p *THeaderProtocol) AddTransform(transform THeaderTransformID) error {
+ return p.transport.AddTransform(transform)
+}
+
+func (p *THeaderProtocol) Flush(ctx context.Context) error {
+ return p.transport.Flush(ctx)
+}
+
+func (p *THeaderProtocol) WriteMessageBegin(name string, typeID TMessageType, seqID int32) error {
+ newProto, err := p.transport.Protocol().GetProtocol(p.transport)
+ if err != nil {
+ return err
+ }
+ p.protocol = newProto
+ p.transport.SequenceID = seqID
+ return p.protocol.WriteMessageBegin(name, typeID, seqID)
+}
+
+func (p *THeaderProtocol) WriteMessageEnd() error {
+ if err := p.protocol.WriteMessageEnd(); err != nil {
+ return err
+ }
+ return p.transport.Flush(context.Background())
+}
+
+func (p *THeaderProtocol) WriteStructBegin(name string) error {
+ return p.protocol.WriteStructBegin(name)
+}
+
+func (p *THeaderProtocol) WriteStructEnd() error {
+ return p.protocol.WriteStructEnd()
+}
+
+func (p *THeaderProtocol) WriteFieldBegin(name string, typeID TType, id int16) error {
+ return p.protocol.WriteFieldBegin(name, typeID, id)
+}
+
+func (p *THeaderProtocol) WriteFieldEnd() error {
+ return p.protocol.WriteFieldEnd()
+}
+
+func (p *THeaderProtocol) WriteFieldStop() error {
+ return p.protocol.WriteFieldStop()
+}
+
+func (p *THeaderProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error {
+ return p.protocol.WriteMapBegin(keyType, valueType, size)
+}
+
+func (p *THeaderProtocol) WriteMapEnd() error {
+ return p.protocol.WriteMapEnd()
+}
+
+func (p *THeaderProtocol) WriteListBegin(elemType TType, size int) error {
+ return p.protocol.WriteListBegin(elemType, size)
+}
+
+func (p *THeaderProtocol) WriteListEnd() error {
+ return p.protocol.WriteListEnd()
+}
+
+func (p *THeaderProtocol) WriteSetBegin(elemType TType, size int) error {
+ return p.protocol.WriteSetBegin(elemType, size)
+}
+
+func (p *THeaderProtocol) WriteSetEnd() error {
+ return p.protocol.WriteSetEnd()
+}
+
+func (p *THeaderProtocol) WriteBool(value bool) error {
+ return p.protocol.WriteBool(value)
+}
+
+func (p *THeaderProtocol) WriteByte(value int8) error {
+ return p.protocol.WriteByte(value)
+}
+
+func (p *THeaderProtocol) WriteI16(value int16) error {
+ return p.protocol.WriteI16(value)
+}
+
+func (p *THeaderProtocol) WriteI32(value int32) error {
+ return p.protocol.WriteI32(value)
+}
+
+func (p *THeaderProtocol) WriteI64(value int64) error {
+ return p.protocol.WriteI64(value)
+}
+
+func (p *THeaderProtocol) WriteDouble(value float64) error {
+ return p.protocol.WriteDouble(value)
+}
+
+func (p *THeaderProtocol) WriteString(value string) error {
+ return p.protocol.WriteString(value)
+}
+
+func (p *THeaderProtocol) WriteBinary(value []byte) error {
+ return p.protocol.WriteBinary(value)
+}
+
+// ReadFrame calls underlying THeaderTransport's ReadFrame function.
+func (p *THeaderProtocol) ReadFrame() error {
+ return p.transport.ReadFrame()
+}
+
+func (p *THeaderProtocol) ReadMessageBegin() (name string, typeID TMessageType, seqID int32, err error) {
+ if err = p.transport.ReadFrame(); err != nil {
+ return
+ }
+
+ var newProto TProtocol
+ newProto, err = p.transport.Protocol().GetProtocol(p.transport)
+ if err != nil {
+ tAppExc, ok := err.(TApplicationException)
+ if !ok {
+ return
+ }
+ if e := p.protocol.WriteMessageBegin("", EXCEPTION, seqID); e != nil {
+ return
+ }
+ if e := tAppExc.Write(p.protocol); e != nil {
+ return
+ }
+ if e := p.protocol.WriteMessageEnd(); e != nil {
+ return
+ }
+ if e := p.transport.Flush(context.Background()); e != nil {
+ return
+ }
+ return
+ }
+ p.protocol = newProto
+
+ return p.protocol.ReadMessageBegin()
+}
+
+func (p *THeaderProtocol) ReadMessageEnd() error {
+ return p.protocol.ReadMessageEnd()
+}
+
+func (p *THeaderProtocol) ReadStructBegin() (name string, err error) {
+ return p.protocol.ReadStructBegin()
+}
+
+func (p *THeaderProtocol) ReadStructEnd() error {
+ return p.protocol.ReadStructEnd()
+}
+
+func (p *THeaderProtocol) ReadFieldBegin() (name string, typeID TType, id int16, err error) {
+ return p.protocol.ReadFieldBegin()
+}
+
+func (p *THeaderProtocol) ReadFieldEnd() error {
+ return p.protocol.ReadFieldEnd()
+}
+
+func (p *THeaderProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, err error) {
+ return p.protocol.ReadMapBegin()
+}
+
+func (p *THeaderProtocol) ReadMapEnd() error {
+ return p.protocol.ReadMapEnd()
+}
+
+func (p *THeaderProtocol) ReadListBegin() (elemType TType, size int, err error) {
+ return p.protocol.ReadListBegin()
+}
+
+func (p *THeaderProtocol) ReadListEnd() error {
+ return p.protocol.ReadListEnd()
+}
+
+func (p *THeaderProtocol) ReadSetBegin() (elemType TType, size int, err error) {
+ return p.protocol.ReadSetBegin()
+}
+
+func (p *THeaderProtocol) ReadSetEnd() error {
+ return p.protocol.ReadSetEnd()
+}
+
+func (p *THeaderProtocol) ReadBool() (value bool, err error) {
+ return p.protocol.ReadBool()
+}
+
+func (p *THeaderProtocol) ReadByte() (value int8, err error) {
+ return p.protocol.ReadByte()
+}
+
+func (p *THeaderProtocol) ReadI16() (value int16, err error) {
+ return p.protocol.ReadI16()
+}
+
+func (p *THeaderProtocol) ReadI32() (value int32, err error) {
+ return p.protocol.ReadI32()
+}
+
+func (p *THeaderProtocol) ReadI64() (value int64, err error) {
+ return p.protocol.ReadI64()
+}
+
+func (p *THeaderProtocol) ReadDouble() (value float64, err error) {
+ return p.protocol.ReadDouble()
+}
+
+func (p *THeaderProtocol) ReadString() (value string, err error) {
+ return p.protocol.ReadString()
+}
+
+func (p *THeaderProtocol) ReadBinary() (value []byte, err error) {
+ return p.protocol.ReadBinary()
+}
+
+func (p *THeaderProtocol) Skip(fieldType TType) error {
+ return p.protocol.Skip(fieldType)
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/header_protocol_test.go b/src/jaegertracing/thrift/lib/go/thrift/header_protocol_test.go
new file mode 100644
index 000000000..9b6019bcf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/header_protocol_test.go
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "testing"
+)
+
+func TestReadWriteHeaderProtocol(t *testing.T) {
+ ReadWriteProtocolTest(t, NewTHeaderProtocolFactory())
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/header_transport.go b/src/jaegertracing/thrift/lib/go/thrift/header_transport.go
new file mode 100644
index 000000000..5343ccb46
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/header_transport.go
@@ -0,0 +1,723 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "bufio"
+ "bytes"
+ "compress/zlib"
+ "context"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+)
+
+// Size in bytes for 32-bit ints.
+const size32 = 4
+
+type headerMeta struct {
+ MagicFlags uint32
+ SequenceID int32
+ HeaderLength uint16
+}
+
+const headerMetaSize = 10
+
+type clientType int
+
+const (
+ clientUnknown clientType = iota
+ clientHeaders
+ clientFramedBinary
+ clientUnframedBinary
+ clientFramedCompact
+ clientUnframedCompact
+)
+
+// Constants defined in THeader format:
+// https://github.com/apache/thrift/blob/master/doc/specs/HeaderFormat.md
+const (
+ THeaderHeaderMagic uint32 = 0x0fff0000
+ THeaderHeaderMask uint32 = 0xffff0000
+ THeaderFlagsMask uint32 = 0x0000ffff
+ THeaderMaxFrameSize uint32 = 0x3fffffff
+)
+
+// THeaderMap is the type of the header map in THeader transport.
+type THeaderMap map[string]string
+
+// THeaderProtocolID is the wrapped protocol id used in THeader.
+type THeaderProtocolID int32
+
+// Supported THeaderProtocolID values.
+const (
+ THeaderProtocolBinary THeaderProtocolID = 0x00
+ THeaderProtocolCompact THeaderProtocolID = 0x02
+ THeaderProtocolDefault = THeaderProtocolBinary
+)
+
+// GetProtocol gets the corresponding TProtocol from the wrapped protocol id.
+func (id THeaderProtocolID) GetProtocol(trans TTransport) (TProtocol, error) {
+ switch id {
+ default:
+ return nil, NewTApplicationException(
+ INVALID_PROTOCOL,
+ fmt.Sprintf("THeader protocol id %d not supported", id),
+ )
+ case THeaderProtocolBinary:
+ return NewTBinaryProtocolFactoryDefault().GetProtocol(trans), nil
+ case THeaderProtocolCompact:
+ return NewTCompactProtocol(trans), nil
+ }
+}
+
+// THeaderTransformID defines the numeric id of the transform used.
+type THeaderTransformID int32
+
+// THeaderTransformID values
+const (
+ TransformNone THeaderTransformID = iota // 0, no special handling
+ TransformZlib // 1, zlib
+ // Rest of the values are not currently supported, namely HMAC and Snappy.
+)
+
+var supportedTransformIDs = map[THeaderTransformID]bool{
+ TransformNone: true,
+ TransformZlib: true,
+}
+
+// TransformReader is an io.ReadCloser that handles transforms reading.
+type TransformReader struct {
+ io.Reader
+
+ closers []io.Closer
+}
+
+var _ io.ReadCloser = (*TransformReader)(nil)
+
+// NewTransformReaderWithCapacity initializes a TransformReader with expected
+// closers capacity.
+//
+// If you don't know the closers capacity beforehand, just use
+//
+// &TransformReader{Reader: baseReader}
+//
+// instead would be sufficient.
+func NewTransformReaderWithCapacity(baseReader io.Reader, capacity int) *TransformReader {
+ return &TransformReader{
+ Reader: baseReader,
+ closers: make([]io.Closer, 0, capacity),
+ }
+}
+
+// Close calls the underlying closers in appropriate order,
+// stops at and returns the first error encountered.
+func (tr *TransformReader) Close() error {
+ // Call closers in reversed order
+ for i := len(tr.closers) - 1; i >= 0; i-- {
+ if err := tr.closers[i].Close(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// AddTransform adds a transform.
+func (tr *TransformReader) AddTransform(id THeaderTransformID) error {
+ switch id {
+ default:
+ return NewTApplicationException(
+ INVALID_TRANSFORM,
+ fmt.Sprintf("THeaderTransformID %d not supported", id),
+ )
+ case TransformNone:
+ // no-op
+ case TransformZlib:
+ readCloser, err := zlib.NewReader(tr.Reader)
+ if err != nil {
+ return err
+ }
+ tr.Reader = readCloser
+ tr.closers = append(tr.closers, readCloser)
+ }
+ return nil
+}
+
+// TransformWriter is an io.WriteCloser that handles transforms writing.
+type TransformWriter struct {
+ io.Writer
+
+ closers []io.Closer
+}
+
+var _ io.WriteCloser = (*TransformWriter)(nil)
+
+// NewTransformWriter creates a new TransformWriter with base writer and transforms.
+func NewTransformWriter(baseWriter io.Writer, transforms []THeaderTransformID) (io.WriteCloser, error) {
+ writer := &TransformWriter{
+ Writer: baseWriter,
+ closers: make([]io.Closer, 0, len(transforms)),
+ }
+ for _, id := range transforms {
+ if err := writer.AddTransform(id); err != nil {
+ return nil, err
+ }
+ }
+ return writer, nil
+}
+
+// Close calls the underlying closers in appropriate order,
+// stops at and returns the first error encountered.
+func (tw *TransformWriter) Close() error {
+ // Call closers in reversed order
+ for i := len(tw.closers) - 1; i >= 0; i-- {
+ if err := tw.closers[i].Close(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// AddTransform adds a transform.
+func (tw *TransformWriter) AddTransform(id THeaderTransformID) error {
+ switch id {
+ default:
+ return NewTApplicationException(
+ INVALID_TRANSFORM,
+ fmt.Sprintf("THeaderTransformID %d not supported", id),
+ )
+ case TransformNone:
+ // no-op
+ case TransformZlib:
+ writeCloser := zlib.NewWriter(tw.Writer)
+ tw.Writer = writeCloser
+ tw.closers = append(tw.closers, writeCloser)
+ }
+ return nil
+}
+
+// THeaderInfoType is the type id of the info headers.
+type THeaderInfoType int32
+
+// Supported THeaderInfoType values.
+const (
+ _ THeaderInfoType = iota // Skip 0
+ InfoKeyValue // 1
+ // Rest of the info types are not supported.
+)
+
+// THeaderTransport is a Transport mode that implements THeader.
+//
+// Note that THeaderTransport handles frame and zlib by itself,
+// so the underlying transport should be a raw socket transports (TSocket or TSSLSocket),
+// instead of rich transports like TZlibTransport or TFramedTransport.
+type THeaderTransport struct {
+ SequenceID int32
+ Flags uint32
+
+ transport TTransport
+
+ // THeaderMap for read and write
+ readHeaders THeaderMap
+ writeHeaders THeaderMap
+
+ // Reading related variables.
+ reader *bufio.Reader
+ // When frame is detected, we read the frame fully into frameBuffer.
+ frameBuffer bytes.Buffer
+ // When it's non-nil, Read should read from frameReader instead of
+ // reader, and EOF error indicates end of frame instead of end of all
+ // transport.
+ frameReader io.ReadCloser
+
+ // Writing related variables
+ writeBuffer bytes.Buffer
+ writeTransforms []THeaderTransformID
+
+ clientType clientType
+ protocolID THeaderProtocolID
+
+ // buffer is used in the following scenarios to avoid repetitive
+ // allocations, while 4 is big enough for all those scenarios:
+ //
+ // * header padding (max size 4)
+ // * write the frame size (size 4)
+ buffer [4]byte
+}
+
+var _ TTransport = (*THeaderTransport)(nil)
+
+// NewTHeaderTransport creates THeaderTransport from the underlying transport.
+//
+// Please note that THeaderTransport handles framing and zlib by itself,
+// so the underlying transport should be the raw socket transports (TSocket or TSSLSocket),
+// instead of rich transports like TZlibTransport or TFramedTransport.
+//
+// If trans is already a *THeaderTransport, it will be returned as is.
+func NewTHeaderTransport(trans TTransport) *THeaderTransport {
+ if ht, ok := trans.(*THeaderTransport); ok {
+ return ht
+ }
+ return &THeaderTransport{
+ transport: trans,
+ reader: bufio.NewReader(trans),
+ writeHeaders: make(THeaderMap),
+ protocolID: THeaderProtocolDefault,
+ }
+}
+
+// Open calls the underlying transport's Open function.
+func (t *THeaderTransport) Open() error {
+ return t.transport.Open()
+}
+
+// IsOpen calls the underlying transport's IsOpen function.
+func (t *THeaderTransport) IsOpen() bool {
+ return t.transport.IsOpen()
+}
+
+// ReadFrame tries to read the frame header, guess the client type, and handle
+// unframed clients.
+func (t *THeaderTransport) ReadFrame() error {
+ if !t.needReadFrame() {
+ // No need to read frame, skipping.
+ return nil
+ }
+ // Peek and handle the first 32 bits.
+ // They could either be the length field of a framed message,
+ // or the first bytes of an unframed message.
+ buf, err := t.reader.Peek(size32)
+ if err != nil {
+ return err
+ }
+ frameSize := binary.BigEndian.Uint32(buf)
+ if frameSize&VERSION_MASK == VERSION_1 {
+ t.clientType = clientUnframedBinary
+ return nil
+ }
+ if buf[0] == COMPACT_PROTOCOL_ID && buf[1]&COMPACT_VERSION_MASK == COMPACT_VERSION {
+ t.clientType = clientUnframedCompact
+ return nil
+ }
+
+ // At this point it should be a framed message,
+ // sanity check on frameSize then discard the peeked part.
+ if frameSize > THeaderMaxFrameSize {
+ return NewTProtocolExceptionWithType(
+ SIZE_LIMIT,
+ errors.New("frame too large"),
+ )
+ }
+ t.reader.Discard(size32)
+
+ // Read the frame fully into frameBuffer.
+ _, err = io.Copy(
+ &t.frameBuffer,
+ io.LimitReader(t.reader, int64(frameSize)),
+ )
+ if err != nil {
+ return err
+ }
+ t.frameReader = ioutil.NopCloser(&t.frameBuffer)
+
+ // Peek and handle the next 32 bits.
+ buf = t.frameBuffer.Bytes()[:size32]
+ version := binary.BigEndian.Uint32(buf)
+ if version&THeaderHeaderMask == THeaderHeaderMagic {
+ t.clientType = clientHeaders
+ return t.parseHeaders(frameSize)
+ }
+ if version&VERSION_MASK == VERSION_1 {
+ t.clientType = clientFramedBinary
+ return nil
+ }
+ if buf[0] == COMPACT_PROTOCOL_ID && buf[1]&COMPACT_VERSION_MASK == COMPACT_VERSION {
+ t.clientType = clientFramedCompact
+ return nil
+ }
+ if err := t.endOfFrame(); err != nil {
+ return err
+ }
+ return NewTProtocolExceptionWithType(
+ NOT_IMPLEMENTED,
+ errors.New("unsupported client transport type"),
+ )
+}
+
+// endOfFrame does end of frame handling.
+//
+// It closes frameReader, and also resets frame related states.
+func (t *THeaderTransport) endOfFrame() error {
+ defer func() {
+ t.frameBuffer.Reset()
+ t.frameReader = nil
+ }()
+ return t.frameReader.Close()
+}
+
+func (t *THeaderTransport) parseHeaders(frameSize uint32) error {
+ if t.clientType != clientHeaders {
+ return nil
+ }
+
+ var err error
+ var meta headerMeta
+ if err = binary.Read(&t.frameBuffer, binary.BigEndian, &meta); err != nil {
+ return err
+ }
+ frameSize -= headerMetaSize
+ t.Flags = meta.MagicFlags & THeaderFlagsMask
+ t.SequenceID = meta.SequenceID
+ headerLength := int64(meta.HeaderLength) * 4
+ if int64(frameSize) < headerLength {
+ return NewTProtocolExceptionWithType(
+ SIZE_LIMIT,
+ errors.New("header size is larger than the whole frame"),
+ )
+ }
+ headerBuf := NewTMemoryBuffer()
+ _, err = io.Copy(headerBuf, io.LimitReader(&t.frameBuffer, headerLength))
+ if err != nil {
+ return err
+ }
+ hp := NewTCompactProtocol(headerBuf)
+
+ // At this point the header is already read into headerBuf,
+ // and t.frameBuffer starts from the actual payload.
+ protoID, err := hp.readVarint32()
+ if err != nil {
+ return err
+ }
+ t.protocolID = THeaderProtocolID(protoID)
+ var transformCount int32
+ transformCount, err = hp.readVarint32()
+ if err != nil {
+ return err
+ }
+ if transformCount > 0 {
+ reader := NewTransformReaderWithCapacity(
+ &t.frameBuffer,
+ int(transformCount),
+ )
+ t.frameReader = reader
+ transformIDs := make([]THeaderTransformID, transformCount)
+ for i := 0; i < int(transformCount); i++ {
+ id, err := hp.readVarint32()
+ if err != nil {
+ return err
+ }
+ transformIDs[i] = THeaderTransformID(id)
+ }
+ // The transform IDs on the wire was added based on the order of
+ // writing, so on the reading side we need to reverse the order.
+ for i := transformCount - 1; i >= 0; i-- {
+ id := transformIDs[i]
+ if err := reader.AddTransform(id); err != nil {
+ return err
+ }
+ }
+ }
+
+ // The info part does not use the transforms yet, so it's
+ // important to continue using headerBuf.
+ headers := make(THeaderMap)
+ for {
+ infoType, err := hp.readVarint32()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return err
+ }
+ if THeaderInfoType(infoType) == InfoKeyValue {
+ count, err := hp.readVarint32()
+ if err != nil {
+ return err
+ }
+ for i := 0; i < int(count); i++ {
+ key, err := hp.ReadString()
+ if err != nil {
+ return err
+ }
+ value, err := hp.ReadString()
+ if err != nil {
+ return err
+ }
+ headers[key] = value
+ }
+ } else {
+ // Skip reading info section on the first
+ // unsupported info type.
+ break
+ }
+ }
+ t.readHeaders = headers
+
+ return nil
+}
+
+func (t *THeaderTransport) needReadFrame() bool {
+ if t.clientType == clientUnknown {
+ // This is a new connection that's never read before.
+ return true
+ }
+ if t.isFramed() && t.frameReader == nil {
+ // We just finished the last frame.
+ return true
+ }
+ return false
+}
+
+func (t *THeaderTransport) Read(p []byte) (read int, err error) {
+ err = t.ReadFrame()
+ if err != nil {
+ return
+ }
+ if t.frameReader != nil {
+ read, err = t.frameReader.Read(p)
+ if err == io.EOF {
+ err = t.endOfFrame()
+ if err != nil {
+ return
+ }
+ if read < len(p) {
+ var nextRead int
+ nextRead, err = t.Read(p[read:])
+ read += nextRead
+ }
+ }
+ return
+ }
+ return t.reader.Read(p)
+}
+
+// Write writes data to the write buffer.
+//
+// You need to call Flush to actually write them to the transport.
+func (t *THeaderTransport) Write(p []byte) (int, error) {
+ return t.writeBuffer.Write(p)
+}
+
+// Flush writes the appropriate header and the write buffer to the underlying transport.
+func (t *THeaderTransport) Flush(ctx context.Context) error {
+ if t.writeBuffer.Len() == 0 {
+ return nil
+ }
+
+ defer t.writeBuffer.Reset()
+
+ switch t.clientType {
+ default:
+ fallthrough
+ case clientUnknown:
+ t.clientType = clientHeaders
+ fallthrough
+ case clientHeaders:
+ headers := NewTMemoryBuffer()
+ hp := NewTCompactProtocol(headers)
+ if _, err := hp.writeVarint32(int32(t.protocolID)); err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+ if _, err := hp.writeVarint32(int32(len(t.writeTransforms))); err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+ for _, transform := range t.writeTransforms {
+ if _, err := hp.writeVarint32(int32(transform)); err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+ }
+ if len(t.writeHeaders) > 0 {
+ if _, err := hp.writeVarint32(int32(InfoKeyValue)); err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+ if _, err := hp.writeVarint32(int32(len(t.writeHeaders))); err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+ for key, value := range t.writeHeaders {
+ if err := hp.WriteString(key); err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+ if err := hp.WriteString(value); err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+ }
+ }
+ padding := 4 - headers.Len()%4
+ if padding < 4 {
+ buf := t.buffer[:padding]
+ for i := range buf {
+ buf[i] = 0
+ }
+ if _, err := headers.Write(buf); err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+ }
+
+ var payload bytes.Buffer
+ meta := headerMeta{
+ MagicFlags: THeaderHeaderMagic + t.Flags&THeaderFlagsMask,
+ SequenceID: t.SequenceID,
+ HeaderLength: uint16(headers.Len() / 4),
+ }
+ if err := binary.Write(&payload, binary.BigEndian, meta); err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+ if _, err := io.Copy(&payload, headers); err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+
+ writer, err := NewTransformWriter(&payload, t.writeTransforms)
+ if err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+ if _, err := io.Copy(writer, &t.writeBuffer); err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+ if err := writer.Close(); err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+
+ // First write frame length
+ buf := t.buffer[:size32]
+ binary.BigEndian.PutUint32(buf, uint32(payload.Len()))
+ if _, err := t.transport.Write(buf); err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+ // Then write the payload
+ if _, err := io.Copy(t.transport, &payload); err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+
+ case clientFramedBinary, clientFramedCompact:
+ buf := t.buffer[:size32]
+ binary.BigEndian.PutUint32(buf, uint32(t.writeBuffer.Len()))
+ if _, err := t.transport.Write(buf); err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+ fallthrough
+ case clientUnframedBinary, clientUnframedCompact:
+ if _, err := io.Copy(t.transport, &t.writeBuffer); err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+ }
+
+ select {
+ default:
+ case <-ctx.Done():
+ return NewTTransportExceptionFromError(ctx.Err())
+ }
+
+ return t.transport.Flush(ctx)
+}
+
+// Close closes the transport, along with its underlying transport.
+func (t *THeaderTransport) Close() error {
+ if err := t.Flush(context.Background()); err != nil {
+ return err
+ }
+ return t.transport.Close()
+}
+
+// RemainingBytes calls underlying transport's RemainingBytes.
+//
+// Even in framed cases, because of all the possible compression transforms
+// involved, the remaining frame size is likely to be different from the actual
+// remaining readable bytes, so we don't bother to keep tracking the remaining
+// frame size by ourselves and just use the underlying transport's
+// RemainingBytes directly.
+func (t *THeaderTransport) RemainingBytes() uint64 {
+ return t.transport.RemainingBytes()
+}
+
+// GetReadHeaders returns the THeaderMap read from transport.
+func (t *THeaderTransport) GetReadHeaders() THeaderMap {
+ return t.readHeaders
+}
+
+// SetWriteHeader sets a header for write.
+func (t *THeaderTransport) SetWriteHeader(key, value string) {
+ t.writeHeaders[key] = value
+}
+
+// ClearWriteHeaders clears all write headers previously set.
+func (t *THeaderTransport) ClearWriteHeaders() {
+ t.writeHeaders = make(THeaderMap)
+}
+
+// AddTransform add a transform for writing.
+func (t *THeaderTransport) AddTransform(transform THeaderTransformID) error {
+ if !supportedTransformIDs[transform] {
+ return NewTProtocolExceptionWithType(
+ NOT_IMPLEMENTED,
+ fmt.Errorf("THeaderTransformID %d not supported", transform),
+ )
+ }
+ t.writeTransforms = append(t.writeTransforms, transform)
+ return nil
+}
+
+// Protocol returns the wrapped protocol id used in this THeaderTransport.
+func (t *THeaderTransport) Protocol() THeaderProtocolID {
+ switch t.clientType {
+ default:
+ return t.protocolID
+ case clientFramedBinary, clientUnframedBinary:
+ return THeaderProtocolBinary
+ case clientFramedCompact, clientUnframedCompact:
+ return THeaderProtocolCompact
+ }
+}
+
+func (t *THeaderTransport) isFramed() bool {
+ switch t.clientType {
+ default:
+ return false
+ case clientHeaders, clientFramedBinary, clientFramedCompact:
+ return true
+ }
+}
+
+// THeaderTransportFactory is a TTransportFactory implementation to create
+// THeaderTransport.
+type THeaderTransportFactory struct {
+ // The underlying factory, could be nil.
+ Factory TTransportFactory
+}
+
+// NewTHeaderTransportFactory creates a new *THeaderTransportFactory.
+func NewTHeaderTransportFactory(factory TTransportFactory) TTransportFactory {
+ return &THeaderTransportFactory{
+ Factory: factory,
+ }
+}
+
+// GetTransport implements TTransportFactory.
+func (f *THeaderTransportFactory) GetTransport(trans TTransport) (TTransport, error) {
+ if f.Factory != nil {
+ t, err := f.Factory.GetTransport(trans)
+ if err != nil {
+ return nil, err
+ }
+ return NewTHeaderTransport(t), nil
+ }
+ return NewTHeaderTransport(trans), nil
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/header_transport_test.go b/src/jaegertracing/thrift/lib/go/thrift/header_transport_test.go
new file mode 100644
index 000000000..e30476802
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/header_transport_test.go
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+ "io"
+ "io/ioutil"
+ "testing"
+)
+
+func TestTHeaderHeadersReadWrite(t *testing.T) {
+ trans := NewTMemoryBuffer()
+ reader := NewTHeaderTransport(trans)
+ writer := NewTHeaderTransport(trans)
+
+ const key1 = "key1"
+ const value1 = "value1"
+ const key2 = "key2"
+ const value2 = "value2"
+ const payload1 = "hello, world1\n"
+ const payload2 = "hello, world2\n"
+
+ // Write
+ if err := writer.AddTransform(TransformZlib); err != nil {
+ t.Fatalf(
+ "writer.AddTransform(TransformZlib) returned error: %v",
+ err,
+ )
+ }
+ // Use double zlib to make sure that we close them in the right order.
+ if err := writer.AddTransform(TransformZlib); err != nil {
+ t.Fatalf(
+ "writer.AddTransform(TransformZlib) returned error: %v",
+ err,
+ )
+ }
+ if err := writer.AddTransform(TransformNone); err != nil {
+ t.Fatalf(
+ "writer.AddTransform(TransformNone) returned error: %v",
+ err,
+ )
+ }
+ writer.SetWriteHeader(key1, value1)
+ writer.SetWriteHeader(key2, value2)
+ if _, err := writer.Write([]byte(payload1)); err != nil {
+ t.Errorf("writer.Write returned error: %v", err)
+ }
+ if err := writer.Flush(context.Background()); err != nil {
+ t.Errorf("writer.Flush returned error: %v", err)
+ }
+ if _, err := writer.Write([]byte(payload2)); err != nil {
+ t.Errorf("writer.Write returned error: %v", err)
+ }
+ if err := writer.Flush(context.Background()); err != nil {
+ t.Errorf("writer.Flush returned error: %v", err)
+ }
+
+ // Read
+
+ // Make sure multiple calls to ReadFrame is fine.
+ if err := reader.ReadFrame(); err != nil {
+ t.Errorf("reader.ReadFrame returned error: %v", err)
+ }
+ if err := reader.ReadFrame(); err != nil {
+ t.Errorf("reader.ReadFrame returned error: %v", err)
+ }
+ read, err := ioutil.ReadAll(reader)
+ if err != nil {
+ t.Errorf("Read returned error: %v", err)
+ }
+ if err := reader.ReadFrame(); err != nil && err != io.EOF {
+ t.Errorf("reader.ReadFrame returned error: %v", err)
+ }
+ if string(read) != payload1+payload2 {
+ t.Errorf(
+ "Read content expected %q, got %q",
+ payload1+payload2,
+ read,
+ )
+ }
+ if prot := reader.Protocol(); prot != THeaderProtocolBinary {
+ t.Errorf(
+ "reader.Protocol() expected %d, got %d",
+ THeaderProtocolBinary,
+ prot,
+ )
+ }
+ if reader.clientType != clientHeaders {
+ t.Errorf(
+ "reader.clientType expected %d, got %d",
+ clientHeaders,
+ reader.clientType,
+ )
+ }
+ headers := reader.GetReadHeaders()
+ if len(headers) != 2 || headers[key1] != value1 || headers[key2] != value2 {
+ t.Errorf(
+ "reader.GetReadHeaders() expected size 2, actual content: %+v",
+ headers,
+ )
+ }
+}
+
+func TestTHeaderTransportNoDoubleWrapping(t *testing.T) {
+ trans := NewTMemoryBuffer()
+ orig := NewTHeaderTransport(trans)
+ wrapped := NewTHeaderTransport(orig)
+
+ if wrapped != orig {
+ t.Errorf("NewTHeaderTransport double wrapped THeaderTransport")
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/http_client.go b/src/jaegertracing/thrift/lib/go/thrift/http_client.go
new file mode 100644
index 000000000..5c82bf538
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/http_client.go
@@ -0,0 +1,242 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "bytes"
+ "context"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "strconv"
+)
+
+// Default to using the shared http client. Library users are
+// free to change this global client or specify one through
+// THttpClientOptions.
+var DefaultHttpClient *http.Client = http.DefaultClient
+
+type THttpClient struct {
+ client *http.Client
+ response *http.Response
+ url *url.URL
+ requestBuffer *bytes.Buffer
+ header http.Header
+ nsecConnectTimeout int64
+ nsecReadTimeout int64
+}
+
+type THttpClientTransportFactory struct {
+ options THttpClientOptions
+ url string
+}
+
+func (p *THttpClientTransportFactory) GetTransport(trans TTransport) (TTransport, error) {
+ if trans != nil {
+ t, ok := trans.(*THttpClient)
+ if ok && t.url != nil {
+ return NewTHttpClientWithOptions(t.url.String(), p.options)
+ }
+ }
+ return NewTHttpClientWithOptions(p.url, p.options)
+}
+
+type THttpClientOptions struct {
+ // If nil, DefaultHttpClient is used
+ Client *http.Client
+}
+
+func NewTHttpClientTransportFactory(url string) *THttpClientTransportFactory {
+ return NewTHttpClientTransportFactoryWithOptions(url, THttpClientOptions{})
+}
+
+func NewTHttpClientTransportFactoryWithOptions(url string, options THttpClientOptions) *THttpClientTransportFactory {
+ return &THttpClientTransportFactory{url: url, options: options}
+}
+
+func NewTHttpClientWithOptions(urlstr string, options THttpClientOptions) (TTransport, error) {
+ parsedURL, err := url.Parse(urlstr)
+ if err != nil {
+ return nil, err
+ }
+ buf := make([]byte, 0, 1024)
+ client := options.Client
+ if client == nil {
+ client = DefaultHttpClient
+ }
+ httpHeader := map[string][]string{"Content-Type": {"application/x-thrift"}}
+ return &THttpClient{client: client, url: parsedURL, requestBuffer: bytes.NewBuffer(buf), header: httpHeader}, nil
+}
+
+func NewTHttpClient(urlstr string) (TTransport, error) {
+ return NewTHttpClientWithOptions(urlstr, THttpClientOptions{})
+}
+
+// Set the HTTP Header for this specific Thrift Transport
+// It is important that you first assert the TTransport as a THttpClient type
+// like so:
+//
+// httpTrans := trans.(THttpClient)
+// httpTrans.SetHeader("User-Agent","Thrift Client 1.0")
+func (p *THttpClient) SetHeader(key string, value string) {
+ p.header.Add(key, value)
+}
+
+// Get the HTTP Header represented by the supplied Header Key for this specific Thrift Transport
+// It is important that you first assert the TTransport as a THttpClient type
+// like so:
+//
+// httpTrans := trans.(THttpClient)
+// hdrValue := httpTrans.GetHeader("User-Agent")
+func (p *THttpClient) GetHeader(key string) string {
+ return p.header.Get(key)
+}
+
+// Deletes the HTTP Header given a Header Key for this specific Thrift Transport
+// It is important that you first assert the TTransport as a THttpClient type
+// like so:
+//
+// httpTrans := trans.(THttpClient)
+// httpTrans.DelHeader("User-Agent")
+func (p *THttpClient) DelHeader(key string) {
+ p.header.Del(key)
+}
+
+func (p *THttpClient) Open() error {
+ // do nothing
+ return nil
+}
+
+func (p *THttpClient) IsOpen() bool {
+ return p.response != nil || p.requestBuffer != nil
+}
+
+func (p *THttpClient) closeResponse() error {
+ var err error
+ if p.response != nil && p.response.Body != nil {
+ // The docs specify that if keepalive is enabled and the response body is not
+ // read to completion the connection will never be returned to the pool and
+ // reused. Errors are being ignored here because if the connection is invalid
+ // and this fails for some reason, the Close() method will do any remaining
+ // cleanup.
+ io.Copy(ioutil.Discard, p.response.Body)
+
+ err = p.response.Body.Close()
+ }
+
+ p.response = nil
+ return err
+}
+
+func (p *THttpClient) Close() error {
+ if p.requestBuffer != nil {
+ p.requestBuffer.Reset()
+ p.requestBuffer = nil
+ }
+ return p.closeResponse()
+}
+
+func (p *THttpClient) Read(buf []byte) (int, error) {
+ if p.response == nil {
+ return 0, NewTTransportException(NOT_OPEN, "Response buffer is empty, no request.")
+ }
+ n, err := p.response.Body.Read(buf)
+ if n > 0 && (err == nil || err == io.EOF) {
+ return n, nil
+ }
+ return n, NewTTransportExceptionFromError(err)
+}
+
+func (p *THttpClient) ReadByte() (c byte, err error) {
+ return readByte(p.response.Body)
+}
+
+func (p *THttpClient) Write(buf []byte) (int, error) {
+ n, err := p.requestBuffer.Write(buf)
+ return n, err
+}
+
+func (p *THttpClient) WriteByte(c byte) error {
+ return p.requestBuffer.WriteByte(c)
+}
+
+func (p *THttpClient) WriteString(s string) (n int, err error) {
+ return p.requestBuffer.WriteString(s)
+}
+
+func (p *THttpClient) Flush(ctx context.Context) error {
+ // Close any previous response body to avoid leaking connections.
+ p.closeResponse()
+
+ req, err := http.NewRequest("POST", p.url.String(), p.requestBuffer)
+ if err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+ req.Header = p.header
+ if ctx != nil {
+ req = req.WithContext(ctx)
+ }
+ response, err := p.client.Do(req)
+ if err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+ if response.StatusCode != http.StatusOK {
+ // Close the response to avoid leaking file descriptors. closeResponse does
+ // more than just call Close(), so temporarily assign it and reuse the logic.
+ p.response = response
+ p.closeResponse()
+
+ // TODO(pomack) log bad response
+ return NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "HTTP Response code: "+strconv.Itoa(response.StatusCode))
+ }
+ p.response = response
+ return nil
+}
+
+func (p *THttpClient) RemainingBytes() (num_bytes uint64) {
+ len := p.response.ContentLength
+ if len >= 0 {
+ return uint64(len)
+ }
+
+ const maxSize = ^uint64(0)
+ return maxSize // the thruth is, we just don't know unless framed is used
+}
+
+// Deprecated: Use NewTHttpClientTransportFactory instead.
+func NewTHttpPostClientTransportFactory(url string) *THttpClientTransportFactory {
+ return NewTHttpClientTransportFactoryWithOptions(url, THttpClientOptions{})
+}
+
+// Deprecated: Use NewTHttpClientTransportFactoryWithOptions instead.
+func NewTHttpPostClientTransportFactoryWithOptions(url string, options THttpClientOptions) *THttpClientTransportFactory {
+ return NewTHttpClientTransportFactoryWithOptions(url, options)
+}
+
+// Deprecated: Use NewTHttpClientWithOptions instead.
+func NewTHttpPostClientWithOptions(urlstr string, options THttpClientOptions) (TTransport, error) {
+ return NewTHttpClientWithOptions(urlstr, options)
+}
+
+// Deprecated: Use NewTHttpClient instead.
+func NewTHttpPostClient(urlstr string) (TTransport, error) {
+ return NewTHttpClientWithOptions(urlstr, THttpClientOptions{})
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/http_client_test.go b/src/jaegertracing/thrift/lib/go/thrift/http_client_test.go
new file mode 100644
index 000000000..453680ace
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/http_client_test.go
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "net/http"
+ "testing"
+)
+
+func TestHttpClient(t *testing.T) {
+ l, addr := HttpClientSetupForTest(t)
+ if l != nil {
+ defer l.Close()
+ }
+ trans, err := NewTHttpPostClient("http://" + addr.String())
+ if err != nil {
+ l.Close()
+ t.Fatalf("Unable to connect to %s: %s", addr.String(), err)
+ }
+ TransportTest(t, trans, trans)
+}
+
+func TestHttpClientHeaders(t *testing.T) {
+ l, addr := HttpClientSetupForTest(t)
+ if l != nil {
+ defer l.Close()
+ }
+ trans, err := NewTHttpPostClient("http://" + addr.String())
+ if err != nil {
+ l.Close()
+ t.Fatalf("Unable to connect to %s: %s", addr.String(), err)
+ }
+ TransportHeaderTest(t, trans, trans)
+}
+
+func TestHttpCustomClient(t *testing.T) {
+ l, addr := HttpClientSetupForTest(t)
+ if l != nil {
+ defer l.Close()
+ }
+
+ httpTransport := &customHttpTransport{}
+
+ trans, err := NewTHttpPostClientWithOptions("http://"+addr.String(), THttpClientOptions{
+ Client: &http.Client{
+ Transport: httpTransport,
+ },
+ })
+ if err != nil {
+ l.Close()
+ t.Fatalf("Unable to connect to %s: %s", addr.String(), err)
+ }
+ TransportHeaderTest(t, trans, trans)
+
+ if !httpTransport.hit {
+ t.Fatalf("Custom client was not used")
+ }
+}
+
+func TestHttpCustomClientPackageScope(t *testing.T) {
+ l, addr := HttpClientSetupForTest(t)
+ if l != nil {
+ defer l.Close()
+ }
+ httpTransport := &customHttpTransport{}
+ DefaultHttpClient = &http.Client{
+ Transport: httpTransport,
+ }
+
+ trans, err := NewTHttpPostClient("http://" + addr.String())
+ if err != nil {
+ l.Close()
+ t.Fatalf("Unable to connect to %s: %s", addr.String(), err)
+ }
+ TransportHeaderTest(t, trans, trans)
+
+ if !httpTransport.hit {
+ t.Fatalf("Custom client was not used")
+ }
+}
+
+type customHttpTransport struct {
+ hit bool
+}
+
+func (c *customHttpTransport) RoundTrip(req *http.Request) (*http.Response, error) {
+ c.hit = true
+ return http.DefaultTransport.RoundTrip(req)
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/http_transport.go b/src/jaegertracing/thrift/lib/go/thrift/http_transport.go
new file mode 100644
index 000000000..66f0f388a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/http_transport.go
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "compress/gzip"
+ "io"
+ "net/http"
+ "strings"
+)
+
+// NewThriftHandlerFunc is a function that create a ready to use Apache Thrift Handler function
+func NewThriftHandlerFunc(processor TProcessor,
+ inPfactory, outPfactory TProtocolFactory) func(w http.ResponseWriter, r *http.Request) {
+
+ return gz(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Add("Content-Type", "application/x-thrift")
+
+ transport := NewStreamTransport(r.Body, w)
+ processor.Process(r.Context(), inPfactory.GetProtocol(transport), outPfactory.GetProtocol(transport))
+ })
+}
+
+// gz transparently compresses the HTTP response if the client supports it.
+func gz(handler http.HandlerFunc) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
+ handler(w, r)
+ return
+ }
+ w.Header().Set("Content-Encoding", "gzip")
+ gz := gzip.NewWriter(w)
+ defer gz.Close()
+ gzw := gzipResponseWriter{Writer: gz, ResponseWriter: w}
+ handler(gzw, r)
+ }
+}
+
+type gzipResponseWriter struct {
+ io.Writer
+ http.ResponseWriter
+}
+
+func (w gzipResponseWriter) Write(b []byte) (int, error) {
+ return w.Writer.Write(b)
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/iostream_transport.go b/src/jaegertracing/thrift/lib/go/thrift/iostream_transport.go
new file mode 100644
index 000000000..fea93bcef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/iostream_transport.go
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "bufio"
+ "context"
+ "io"
+)
+
+// StreamTransport is a Transport made of an io.Reader and/or an io.Writer
+type StreamTransport struct {
+ io.Reader
+ io.Writer
+ isReadWriter bool
+ closed bool
+}
+
+type StreamTransportFactory struct {
+ Reader io.Reader
+ Writer io.Writer
+ isReadWriter bool
+}
+
+func (p *StreamTransportFactory) GetTransport(trans TTransport) (TTransport, error) {
+ if trans != nil {
+ t, ok := trans.(*StreamTransport)
+ if ok {
+ if t.isReadWriter {
+ return NewStreamTransportRW(t.Reader.(io.ReadWriter)), nil
+ }
+ if t.Reader != nil && t.Writer != nil {
+ return NewStreamTransport(t.Reader, t.Writer), nil
+ }
+ if t.Reader != nil && t.Writer == nil {
+ return NewStreamTransportR(t.Reader), nil
+ }
+ if t.Reader == nil && t.Writer != nil {
+ return NewStreamTransportW(t.Writer), nil
+ }
+ return &StreamTransport{}, nil
+ }
+ }
+ if p.isReadWriter {
+ return NewStreamTransportRW(p.Reader.(io.ReadWriter)), nil
+ }
+ if p.Reader != nil && p.Writer != nil {
+ return NewStreamTransport(p.Reader, p.Writer), nil
+ }
+ if p.Reader != nil && p.Writer == nil {
+ return NewStreamTransportR(p.Reader), nil
+ }
+ if p.Reader == nil && p.Writer != nil {
+ return NewStreamTransportW(p.Writer), nil
+ }
+ return &StreamTransport{}, nil
+}
+
+func NewStreamTransportFactory(reader io.Reader, writer io.Writer, isReadWriter bool) *StreamTransportFactory {
+ return &StreamTransportFactory{Reader: reader, Writer: writer, isReadWriter: isReadWriter}
+}
+
+func NewStreamTransport(r io.Reader, w io.Writer) *StreamTransport {
+ return &StreamTransport{Reader: bufio.NewReader(r), Writer: bufio.NewWriter(w)}
+}
+
+func NewStreamTransportR(r io.Reader) *StreamTransport {
+ return &StreamTransport{Reader: bufio.NewReader(r)}
+}
+
+func NewStreamTransportW(w io.Writer) *StreamTransport {
+ return &StreamTransport{Writer: bufio.NewWriter(w)}
+}
+
+func NewStreamTransportRW(rw io.ReadWriter) *StreamTransport {
+ bufrw := bufio.NewReadWriter(bufio.NewReader(rw), bufio.NewWriter(rw))
+ return &StreamTransport{Reader: bufrw, Writer: bufrw, isReadWriter: true}
+}
+
+func (p *StreamTransport) IsOpen() bool {
+ return !p.closed
+}
+
+// implicitly opened on creation, can't be reopened once closed
+func (p *StreamTransport) Open() error {
+ if !p.closed {
+ return NewTTransportException(ALREADY_OPEN, "StreamTransport already open.")
+ } else {
+ return NewTTransportException(NOT_OPEN, "cannot reopen StreamTransport.")
+ }
+}
+
+// Closes both the input and output streams.
+func (p *StreamTransport) Close() error {
+ if p.closed {
+ return NewTTransportException(NOT_OPEN, "StreamTransport already closed.")
+ }
+ p.closed = true
+ closedReader := false
+ if p.Reader != nil {
+ c, ok := p.Reader.(io.Closer)
+ if ok {
+ e := c.Close()
+ closedReader = true
+ if e != nil {
+ return e
+ }
+ }
+ p.Reader = nil
+ }
+ if p.Writer != nil && (!closedReader || !p.isReadWriter) {
+ c, ok := p.Writer.(io.Closer)
+ if ok {
+ e := c.Close()
+ if e != nil {
+ return e
+ }
+ }
+ p.Writer = nil
+ }
+ return nil
+}
+
+// Flushes the underlying output stream if not null.
+func (p *StreamTransport) Flush(ctx context.Context) error {
+ if p.Writer == nil {
+ return NewTTransportException(NOT_OPEN, "Cannot flush null outputStream")
+ }
+ f, ok := p.Writer.(Flusher)
+ if ok {
+ err := f.Flush()
+ if err != nil {
+ return NewTTransportExceptionFromError(err)
+ }
+ }
+ return nil
+}
+
+func (p *StreamTransport) Read(c []byte) (n int, err error) {
+ n, err = p.Reader.Read(c)
+ if err != nil {
+ err = NewTTransportExceptionFromError(err)
+ }
+ return
+}
+
+func (p *StreamTransport) ReadByte() (c byte, err error) {
+ f, ok := p.Reader.(io.ByteReader)
+ if ok {
+ c, err = f.ReadByte()
+ } else {
+ c, err = readByte(p.Reader)
+ }
+ if err != nil {
+ err = NewTTransportExceptionFromError(err)
+ }
+ return
+}
+
+func (p *StreamTransport) Write(c []byte) (n int, err error) {
+ n, err = p.Writer.Write(c)
+ if err != nil {
+ err = NewTTransportExceptionFromError(err)
+ }
+ return
+}
+
+func (p *StreamTransport) WriteByte(c byte) (err error) {
+ f, ok := p.Writer.(io.ByteWriter)
+ if ok {
+ err = f.WriteByte(c)
+ } else {
+ err = writeByte(p.Writer, c)
+ }
+ if err != nil {
+ err = NewTTransportExceptionFromError(err)
+ }
+ return
+}
+
+func (p *StreamTransport) WriteString(s string) (n int, err error) {
+ f, ok := p.Writer.(stringWriter)
+ if ok {
+ n, err = f.WriteString(s)
+ } else {
+ n, err = p.Writer.Write([]byte(s))
+ }
+ if err != nil {
+ err = NewTTransportExceptionFromError(err)
+ }
+ return
+}
+
+func (p *StreamTransport) RemainingBytes() (num_bytes uint64) {
+ const maxSize = ^uint64(0)
+ return maxSize // the thruth is, we just don't know unless framed is used
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/iostream_transport_test.go b/src/jaegertracing/thrift/lib/go/thrift/iostream_transport_test.go
new file mode 100644
index 000000000..15a611642
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/iostream_transport_test.go
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestStreamTransport(t *testing.T) {
+ trans := NewStreamTransportRW(bytes.NewBuffer(make([]byte, 0, 1024)))
+ TransportTest(t, trans, trans)
+}
+
+func TestStreamTransportOpenClose(t *testing.T) {
+ trans := NewStreamTransportRW(bytes.NewBuffer(make([]byte, 0, 1024)))
+ if !trans.IsOpen() {
+ t.Fatal("StreamTransport should be already open")
+ }
+ if trans.Open() == nil {
+ t.Fatal("StreamTransport should return error when open twice")
+ }
+ if trans.Close() != nil {
+ t.Fatal("StreamTransport should not return error when closing open transport")
+ }
+ if trans.IsOpen() {
+ t.Fatal("StreamTransport should not be open after close")
+ }
+ if trans.Close() == nil {
+ t.Fatal("StreamTransport should return error when closing a non open transport")
+ }
+ if trans.Open() == nil {
+ t.Fatal("StreamTransport should not be able to reopen")
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/json_protocol.go b/src/jaegertracing/thrift/lib/go/thrift/json_protocol.go
new file mode 100644
index 000000000..800ac22c7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/json_protocol.go
@@ -0,0 +1,581 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+ "encoding/base64"
+ "fmt"
+)
+
+const (
+ THRIFT_JSON_PROTOCOL_VERSION = 1
+)
+
+// for references to _ParseContext see tsimplejson_protocol.go
+
+// JSON protocol implementation for thrift.
+// Utilizes Simple JSON protocol
+//
+type TJSONProtocol struct {
+ *TSimpleJSONProtocol
+}
+
+// Constructor
+func NewTJSONProtocol(t TTransport) *TJSONProtocol {
+ v := &TJSONProtocol{TSimpleJSONProtocol: NewTSimpleJSONProtocol(t)}
+ v.parseContextStack = append(v.parseContextStack, int(_CONTEXT_IN_TOPLEVEL))
+ v.dumpContext = append(v.dumpContext, int(_CONTEXT_IN_TOPLEVEL))
+ return v
+}
+
+// Factory
+type TJSONProtocolFactory struct{}
+
+func (p *TJSONProtocolFactory) GetProtocol(trans TTransport) TProtocol {
+ return NewTJSONProtocol(trans)
+}
+
+func NewTJSONProtocolFactory() *TJSONProtocolFactory {
+ return &TJSONProtocolFactory{}
+}
+
+func (p *TJSONProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) error {
+ p.resetContextStack() // THRIFT-3735
+ if e := p.OutputListBegin(); e != nil {
+ return e
+ }
+ if e := p.WriteI32(THRIFT_JSON_PROTOCOL_VERSION); e != nil {
+ return e
+ }
+ if e := p.WriteString(name); e != nil {
+ return e
+ }
+ if e := p.WriteByte(int8(typeId)); e != nil {
+ return e
+ }
+ if e := p.WriteI32(seqId); e != nil {
+ return e
+ }
+ return nil
+}
+
+func (p *TJSONProtocol) WriteMessageEnd() error {
+ return p.OutputListEnd()
+}
+
+func (p *TJSONProtocol) WriteStructBegin(name string) error {
+ if e := p.OutputObjectBegin(); e != nil {
+ return e
+ }
+ return nil
+}
+
+func (p *TJSONProtocol) WriteStructEnd() error {
+ return p.OutputObjectEnd()
+}
+
+func (p *TJSONProtocol) WriteFieldBegin(name string, typeId TType, id int16) error {
+ if e := p.WriteI16(id); e != nil {
+ return e
+ }
+ if e := p.OutputObjectBegin(); e != nil {
+ return e
+ }
+ s, e1 := p.TypeIdToString(typeId)
+ if e1 != nil {
+ return e1
+ }
+ if e := p.WriteString(s); e != nil {
+ return e
+ }
+ return nil
+}
+
+func (p *TJSONProtocol) WriteFieldEnd() error {
+ return p.OutputObjectEnd()
+}
+
+func (p *TJSONProtocol) WriteFieldStop() error { return nil }
+
+func (p *TJSONProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error {
+ if e := p.OutputListBegin(); e != nil {
+ return e
+ }
+ s, e1 := p.TypeIdToString(keyType)
+ if e1 != nil {
+ return e1
+ }
+ if e := p.WriteString(s); e != nil {
+ return e
+ }
+ s, e1 = p.TypeIdToString(valueType)
+ if e1 != nil {
+ return e1
+ }
+ if e := p.WriteString(s); e != nil {
+ return e
+ }
+ if e := p.WriteI64(int64(size)); e != nil {
+ return e
+ }
+ return p.OutputObjectBegin()
+}
+
+func (p *TJSONProtocol) WriteMapEnd() error {
+ if e := p.OutputObjectEnd(); e != nil {
+ return e
+ }
+ return p.OutputListEnd()
+}
+
+func (p *TJSONProtocol) WriteListBegin(elemType TType, size int) error {
+ return p.OutputElemListBegin(elemType, size)
+}
+
+func (p *TJSONProtocol) WriteListEnd() error {
+ return p.OutputListEnd()
+}
+
+func (p *TJSONProtocol) WriteSetBegin(elemType TType, size int) error {
+ return p.OutputElemListBegin(elemType, size)
+}
+
+func (p *TJSONProtocol) WriteSetEnd() error {
+ return p.OutputListEnd()
+}
+
+func (p *TJSONProtocol) WriteBool(b bool) error {
+ if b {
+ return p.WriteI32(1)
+ }
+ return p.WriteI32(0)
+}
+
+func (p *TJSONProtocol) WriteByte(b int8) error {
+ return p.WriteI32(int32(b))
+}
+
+func (p *TJSONProtocol) WriteI16(v int16) error {
+ return p.WriteI32(int32(v))
+}
+
+func (p *TJSONProtocol) WriteI32(v int32) error {
+ return p.OutputI64(int64(v))
+}
+
+func (p *TJSONProtocol) WriteI64(v int64) error {
+ return p.OutputI64(int64(v))
+}
+
+func (p *TJSONProtocol) WriteDouble(v float64) error {
+ return p.OutputF64(v)
+}
+
+func (p *TJSONProtocol) WriteString(v string) error {
+ return p.OutputString(v)
+}
+
+func (p *TJSONProtocol) WriteBinary(v []byte) error {
+ // JSON library only takes in a string,
+ // not an arbitrary byte array, to ensure bytes are transmitted
+ // efficiently we must convert this into a valid JSON string
+ // therefore we use base64 encoding to avoid excessive escaping/quoting
+ if e := p.OutputPreValue(); e != nil {
+ return e
+ }
+ if _, e := p.write(JSON_QUOTE_BYTES); e != nil {
+ return NewTProtocolException(e)
+ }
+ writer := base64.NewEncoder(base64.StdEncoding, p.writer)
+ if _, e := writer.Write(v); e != nil {
+ p.writer.Reset(p.trans) // THRIFT-3735
+ return NewTProtocolException(e)
+ }
+ if e := writer.Close(); e != nil {
+ return NewTProtocolException(e)
+ }
+ if _, e := p.write(JSON_QUOTE_BYTES); e != nil {
+ return NewTProtocolException(e)
+ }
+ return p.OutputPostValue()
+}
+
+// Reading methods.
+func (p *TJSONProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) {
+ p.resetContextStack() // THRIFT-3735
+ if isNull, err := p.ParseListBegin(); isNull || err != nil {
+ return name, typeId, seqId, err
+ }
+ version, err := p.ReadI32()
+ if err != nil {
+ return name, typeId, seqId, err
+ }
+ if version != THRIFT_JSON_PROTOCOL_VERSION {
+ e := fmt.Errorf("Unknown Protocol version %d, expected version %d", version, THRIFT_JSON_PROTOCOL_VERSION)
+ return name, typeId, seqId, NewTProtocolExceptionWithType(INVALID_DATA, e)
+
+ }
+ if name, err = p.ReadString(); err != nil {
+ return name, typeId, seqId, err
+ }
+ bTypeId, err := p.ReadByte()
+ typeId = TMessageType(bTypeId)
+ if err != nil {
+ return name, typeId, seqId, err
+ }
+ if seqId, err = p.ReadI32(); err != nil {
+ return name, typeId, seqId, err
+ }
+ return name, typeId, seqId, nil
+}
+
+func (p *TJSONProtocol) ReadMessageEnd() error {
+ err := p.ParseListEnd()
+ return err
+}
+
+func (p *TJSONProtocol) ReadStructBegin() (name string, err error) {
+ _, err = p.ParseObjectStart()
+ return "", err
+}
+
+func (p *TJSONProtocol) ReadStructEnd() error {
+ return p.ParseObjectEnd()
+}
+
+func (p *TJSONProtocol) ReadFieldBegin() (string, TType, int16, error) {
+ b, _ := p.reader.Peek(1)
+ if len(b) < 1 || b[0] == JSON_RBRACE[0] || b[0] == JSON_RBRACKET[0] {
+ return "", STOP, -1, nil
+ }
+ fieldId, err := p.ReadI16()
+ if err != nil {
+ return "", STOP, fieldId, err
+ }
+ if _, err = p.ParseObjectStart(); err != nil {
+ return "", STOP, fieldId, err
+ }
+ sType, err := p.ReadString()
+ if err != nil {
+ return "", STOP, fieldId, err
+ }
+ fType, err := p.StringToTypeId(sType)
+ return "", fType, fieldId, err
+}
+
+func (p *TJSONProtocol) ReadFieldEnd() error {
+ return p.ParseObjectEnd()
+}
+
+func (p *TJSONProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, e error) {
+ if isNull, e := p.ParseListBegin(); isNull || e != nil {
+ return VOID, VOID, 0, e
+ }
+
+ // read keyType
+ sKeyType, e := p.ReadString()
+ if e != nil {
+ return keyType, valueType, size, e
+ }
+ keyType, e = p.StringToTypeId(sKeyType)
+ if e != nil {
+ return keyType, valueType, size, e
+ }
+
+ // read valueType
+ sValueType, e := p.ReadString()
+ if e != nil {
+ return keyType, valueType, size, e
+ }
+ valueType, e = p.StringToTypeId(sValueType)
+ if e != nil {
+ return keyType, valueType, size, e
+ }
+
+ // read size
+ iSize, e := p.ReadI64()
+ if e != nil {
+ return keyType, valueType, size, e
+ }
+ size = int(iSize)
+
+ _, e = p.ParseObjectStart()
+ return keyType, valueType, size, e
+}
+
+func (p *TJSONProtocol) ReadMapEnd() error {
+ e := p.ParseObjectEnd()
+ if e != nil {
+ return e
+ }
+ return p.ParseListEnd()
+}
+
+func (p *TJSONProtocol) ReadListBegin() (elemType TType, size int, e error) {
+ return p.ParseElemListBegin()
+}
+
+func (p *TJSONProtocol) ReadListEnd() error {
+ return p.ParseListEnd()
+}
+
+func (p *TJSONProtocol) ReadSetBegin() (elemType TType, size int, e error) {
+ return p.ParseElemListBegin()
+}
+
+func (p *TJSONProtocol) ReadSetEnd() error {
+ return p.ParseListEnd()
+}
+
+func (p *TJSONProtocol) ReadBool() (bool, error) {
+ value, err := p.ReadI32()
+ return (value != 0), err
+}
+
+func (p *TJSONProtocol) ReadByte() (int8, error) {
+ v, err := p.ReadI64()
+ return int8(v), err
+}
+
+func (p *TJSONProtocol) ReadI16() (int16, error) {
+ v, err := p.ReadI64()
+ return int16(v), err
+}
+
+func (p *TJSONProtocol) ReadI32() (int32, error) {
+ v, err := p.ReadI64()
+ return int32(v), err
+}
+
+func (p *TJSONProtocol) ReadI64() (int64, error) {
+ v, _, err := p.ParseI64()
+ return v, err
+}
+
+func (p *TJSONProtocol) ReadDouble() (float64, error) {
+ v, _, err := p.ParseF64()
+ return v, err
+}
+
+func (p *TJSONProtocol) ReadString() (string, error) {
+ var v string
+ if err := p.ParsePreValue(); err != nil {
+ return v, err
+ }
+ f, _ := p.reader.Peek(1)
+ if len(f) > 0 && f[0] == JSON_QUOTE {
+ p.reader.ReadByte()
+ value, err := p.ParseStringBody()
+ v = value
+ if err != nil {
+ return v, err
+ }
+ } else if len(f) > 0 && f[0] == JSON_NULL[0] {
+ b := make([]byte, len(JSON_NULL))
+ _, err := p.reader.Read(b)
+ if err != nil {
+ return v, NewTProtocolException(err)
+ }
+ if string(b) != string(JSON_NULL) {
+ e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b))
+ return v, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ } else {
+ e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f))
+ return v, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ return v, p.ParsePostValue()
+}
+
+func (p *TJSONProtocol) ReadBinary() ([]byte, error) {
+ var v []byte
+ if err := p.ParsePreValue(); err != nil {
+ return nil, err
+ }
+ f, _ := p.reader.Peek(1)
+ if len(f) > 0 && f[0] == JSON_QUOTE {
+ p.reader.ReadByte()
+ value, err := p.ParseBase64EncodedBody()
+ v = value
+ if err != nil {
+ return v, err
+ }
+ } else if len(f) > 0 && f[0] == JSON_NULL[0] {
+ b := make([]byte, len(JSON_NULL))
+ _, err := p.reader.Read(b)
+ if err != nil {
+ return v, NewTProtocolException(err)
+ }
+ if string(b) != string(JSON_NULL) {
+ e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b))
+ return v, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ } else {
+ e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f))
+ return v, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+
+ return v, p.ParsePostValue()
+}
+
+func (p *TJSONProtocol) Flush(ctx context.Context) (err error) {
+ err = p.writer.Flush()
+ if err == nil {
+ err = p.trans.Flush(ctx)
+ }
+ return NewTProtocolException(err)
+}
+
+func (p *TJSONProtocol) Skip(fieldType TType) (err error) {
+ return SkipDefaultDepth(p, fieldType)
+}
+
+func (p *TJSONProtocol) Transport() TTransport {
+ return p.trans
+}
+
+func (p *TJSONProtocol) OutputElemListBegin(elemType TType, size int) error {
+ if e := p.OutputListBegin(); e != nil {
+ return e
+ }
+ s, e1 := p.TypeIdToString(elemType)
+ if e1 != nil {
+ return e1
+ }
+ if e := p.WriteString(s); e != nil {
+ return e
+ }
+ if e := p.WriteI64(int64(size)); e != nil {
+ return e
+ }
+ return nil
+}
+
+func (p *TJSONProtocol) ParseElemListBegin() (elemType TType, size int, e error) {
+ if isNull, e := p.ParseListBegin(); isNull || e != nil {
+ return VOID, 0, e
+ }
+ sElemType, err := p.ReadString()
+ if err != nil {
+ return VOID, size, err
+ }
+ elemType, err = p.StringToTypeId(sElemType)
+ if err != nil {
+ return elemType, size, err
+ }
+ nSize, err2 := p.ReadI64()
+ size = int(nSize)
+ return elemType, size, err2
+}
+
+func (p *TJSONProtocol) readElemListBegin() (elemType TType, size int, e error) {
+ if isNull, e := p.ParseListBegin(); isNull || e != nil {
+ return VOID, 0, e
+ }
+ sElemType, err := p.ReadString()
+ if err != nil {
+ return VOID, size, err
+ }
+ elemType, err = p.StringToTypeId(sElemType)
+ if err != nil {
+ return elemType, size, err
+ }
+ nSize, err2 := p.ReadI64()
+ size = int(nSize)
+ return elemType, size, err2
+}
+
+func (p *TJSONProtocol) writeElemListBegin(elemType TType, size int) error {
+ if e := p.OutputListBegin(); e != nil {
+ return e
+ }
+ s, e1 := p.TypeIdToString(elemType)
+ if e1 != nil {
+ return e1
+ }
+ if e := p.OutputString(s); e != nil {
+ return e
+ }
+ if e := p.OutputI64(int64(size)); e != nil {
+ return e
+ }
+ return nil
+}
+
+func (p *TJSONProtocol) TypeIdToString(fieldType TType) (string, error) {
+ switch byte(fieldType) {
+ case BOOL:
+ return "tf", nil
+ case BYTE:
+ return "i8", nil
+ case I16:
+ return "i16", nil
+ case I32:
+ return "i32", nil
+ case I64:
+ return "i64", nil
+ case DOUBLE:
+ return "dbl", nil
+ case STRING:
+ return "str", nil
+ case STRUCT:
+ return "rec", nil
+ case MAP:
+ return "map", nil
+ case SET:
+ return "set", nil
+ case LIST:
+ return "lst", nil
+ }
+
+ e := fmt.Errorf("Unknown fieldType: %d", int(fieldType))
+ return "", NewTProtocolExceptionWithType(INVALID_DATA, e)
+}
+
+func (p *TJSONProtocol) StringToTypeId(fieldType string) (TType, error) {
+ switch fieldType {
+ case "tf":
+ return TType(BOOL), nil
+ case "i8":
+ return TType(BYTE), nil
+ case "i16":
+ return TType(I16), nil
+ case "i32":
+ return TType(I32), nil
+ case "i64":
+ return TType(I64), nil
+ case "dbl":
+ return TType(DOUBLE), nil
+ case "str":
+ return TType(STRING), nil
+ case "rec":
+ return TType(STRUCT), nil
+ case "map":
+ return TType(MAP), nil
+ case "set":
+ return TType(SET), nil
+ case "lst":
+ return TType(LIST), nil
+ }
+
+ e := fmt.Errorf("Unknown type identifier: %s", fieldType)
+ return TType(STOP), NewTProtocolExceptionWithType(INVALID_DATA, e)
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/json_protocol_test.go b/src/jaegertracing/thrift/lib/go/thrift/json_protocol_test.go
new file mode 100644
index 000000000..59c4d64a2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/json_protocol_test.go
@@ -0,0 +1,650 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "math"
+ "strconv"
+ "testing"
+)
+
+func TestWriteJSONProtocolBool(t *testing.T) {
+ thetype := "boolean"
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ for _, value := range BOOL_VALUES {
+ if e := p.WriteBool(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error())
+ }
+ s := trans.String()
+ expected := ""
+ if value {
+ expected = "1"
+ } else {
+ expected = "0"
+ }
+ if s != expected {
+ t.Fatalf("Bad value for %s %v: %s expected", thetype, value, s)
+ }
+ v := -1
+ if err := json.Unmarshal([]byte(s), &v); err != nil || (v != 0) != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestReadJSONProtocolBool(t *testing.T) {
+ thetype := "boolean"
+ for _, value := range BOOL_VALUES {
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ if value {
+ trans.Write([]byte{'1'}) // not JSON_TRUE
+ } else {
+ trans.Write([]byte{'0'}) // not JSON_FALSE
+ }
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadBool()
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if v != value {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ vv := -1
+ if err := json.Unmarshal([]byte(s), &vv); err != nil || (vv != 0) != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, vv)
+ }
+ trans.Reset()
+ trans.Close()
+ }
+}
+
+func TestWriteJSONProtocolByte(t *testing.T) {
+ thetype := "byte"
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ for _, value := range BYTE_VALUES {
+ if e := p.WriteByte(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error())
+ }
+ s := trans.String()
+ if s != fmt.Sprint(value) {
+ t.Fatalf("Bad value for %s %v: %s", thetype, value, s)
+ }
+ v := int8(0)
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestReadJSONProtocolByte(t *testing.T) {
+ thetype := "byte"
+ for _, value := range BYTE_VALUES {
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ trans.WriteString(strconv.Itoa(int(value)))
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadByte()
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if v != value {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ trans.Close()
+ }
+}
+
+func TestWriteJSONProtocolI16(t *testing.T) {
+ thetype := "int16"
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ for _, value := range INT16_VALUES {
+ if e := p.WriteI16(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error())
+ }
+ s := trans.String()
+ if s != fmt.Sprint(value) {
+ t.Fatalf("Bad value for %s %v: %s", thetype, value, s)
+ }
+ v := int16(0)
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestReadJSONProtocolI16(t *testing.T) {
+ thetype := "int16"
+ for _, value := range INT16_VALUES {
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ trans.WriteString(strconv.Itoa(int(value)))
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadI16()
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if v != value {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ trans.Close()
+ }
+}
+
+func TestWriteJSONProtocolI32(t *testing.T) {
+ thetype := "int32"
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ for _, value := range INT32_VALUES {
+ if e := p.WriteI32(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error())
+ }
+ s := trans.String()
+ if s != fmt.Sprint(value) {
+ t.Fatalf("Bad value for %s %v: %s", thetype, value, s)
+ }
+ v := int32(0)
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestReadJSONProtocolI32(t *testing.T) {
+ thetype := "int32"
+ for _, value := range INT32_VALUES {
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ trans.WriteString(strconv.Itoa(int(value)))
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadI32()
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if v != value {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ trans.Close()
+ }
+}
+
+func TestWriteJSONProtocolI64(t *testing.T) {
+ thetype := "int64"
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ for _, value := range INT64_VALUES {
+ if e := p.WriteI64(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error())
+ }
+ s := trans.String()
+ if s != fmt.Sprint(value) {
+ t.Fatalf("Bad value for %s %v: %s", thetype, value, s)
+ }
+ v := int64(0)
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestReadJSONProtocolI64(t *testing.T) {
+ thetype := "int64"
+ for _, value := range INT64_VALUES {
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ trans.WriteString(strconv.FormatInt(value, 10))
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadI64()
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if v != value {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ trans.Close()
+ }
+}
+
+func TestWriteJSONProtocolDouble(t *testing.T) {
+ thetype := "double"
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ for _, value := range DOUBLE_VALUES {
+ if e := p.WriteDouble(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error())
+ }
+ s := trans.String()
+ if math.IsInf(value, 1) {
+ if s != jsonQuote(JSON_INFINITY) {
+ t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, jsonQuote(JSON_INFINITY))
+ }
+ } else if math.IsInf(value, -1) {
+ if s != jsonQuote(JSON_NEGATIVE_INFINITY) {
+ t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, jsonQuote(JSON_NEGATIVE_INFINITY))
+ }
+ } else if math.IsNaN(value) {
+ if s != jsonQuote(JSON_NAN) {
+ t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, jsonQuote(JSON_NAN))
+ }
+ } else {
+ if s != fmt.Sprint(value) {
+ t.Fatalf("Bad value for %s %v: %s", thetype, value, s)
+ }
+ v := float64(0)
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestReadJSONProtocolDouble(t *testing.T) {
+ thetype := "double"
+ for _, value := range DOUBLE_VALUES {
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ n := NewNumericFromDouble(value)
+ trans.WriteString(n.String())
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadDouble()
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if math.IsInf(value, 1) {
+ if !math.IsInf(v, 1) {
+ t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ } else if math.IsInf(value, -1) {
+ if !math.IsInf(v, -1) {
+ t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ } else if math.IsNaN(value) {
+ if !math.IsNaN(v) {
+ t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ } else {
+ if v != value {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ }
+ trans.Reset()
+ trans.Close()
+ }
+}
+
+func TestWriteJSONProtocolString(t *testing.T) {
+ thetype := "string"
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ for _, value := range STRING_VALUES {
+ if e := p.WriteString(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error())
+ }
+ s := trans.String()
+ if s[0] != '"' || s[len(s)-1] != '"' {
+ t.Fatalf("Bad value for %s '%v', wrote '%v', expected: %v", thetype, value, s, fmt.Sprint("\"", value, "\""))
+ }
+ v := new(string)
+ if err := json.Unmarshal([]byte(s), v); err != nil || *v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v)
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestReadJSONProtocolString(t *testing.T) {
+ thetype := "string"
+ for _, value := range STRING_VALUES {
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ trans.WriteString(jsonQuote(value))
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadString()
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if v != value {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ v1 := new(string)
+ if err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v1)
+ }
+ trans.Reset()
+ trans.Close()
+ }
+}
+
+func TestWriteJSONProtocolBinary(t *testing.T) {
+ thetype := "binary"
+ value := protocol_bdata
+ b64value := make([]byte, base64.StdEncoding.EncodedLen(len(protocol_bdata)))
+ base64.StdEncoding.Encode(b64value, value)
+ b64String := string(b64value)
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ if e := p.WriteBinary(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error())
+ }
+ s := trans.String()
+ expectedString := fmt.Sprint("\"", b64String, "\"")
+ if s != expectedString {
+ t.Fatalf("Bad value for %s %v\n wrote: \"%v\"\nexpected: \"%v\"", thetype, value, s, expectedString)
+ }
+ v1, err := p.ReadBinary()
+ if err != nil {
+ t.Fatalf("Unable to read binary: %s", err.Error())
+ }
+ if len(v1) != len(value) {
+ t.Fatalf("Invalid value for binary\nexpected: \"%v\"\n read: \"%v\"", value, v1)
+ }
+ for k, v := range value {
+ if v1[k] != v {
+ t.Fatalf("Invalid value for binary at %v\nexpected: \"%v\"\n read: \"%v\"", k, v, v1[k])
+ }
+ }
+ trans.Close()
+}
+
+func TestReadJSONProtocolBinary(t *testing.T) {
+ thetype := "binary"
+ value := protocol_bdata
+ b64value := make([]byte, base64.StdEncoding.EncodedLen(len(protocol_bdata)))
+ base64.StdEncoding.Encode(b64value, value)
+ b64String := string(b64value)
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ trans.WriteString(jsonQuote(b64String))
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadBinary()
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if len(v) != len(value) {
+ t.Fatalf("Bad value for %s value length %v, wrote: %v, received length: %v", thetype, len(value), s, len(v))
+ }
+ for i := 0; i < len(v); i++ {
+ if v[i] != value[i] {
+ t.Fatalf("Bad value for %s at index %d value %v, wrote: %v, received: %v", thetype, i, value[i], s, v[i])
+ }
+ }
+ v1 := new(string)
+ if err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != b64String {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v1)
+ }
+ trans.Reset()
+ trans.Close()
+}
+
+func TestWriteJSONProtocolList(t *testing.T) {
+ thetype := "list"
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ p.WriteListBegin(TType(DOUBLE), len(DOUBLE_VALUES))
+ for _, value := range DOUBLE_VALUES {
+ if e := p.WriteDouble(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ }
+ p.WriteListEnd()
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error())
+ }
+ str := trans.String()
+ str1 := new([]interface{})
+ err := json.Unmarshal([]byte(str), str1)
+ if err != nil {
+ t.Fatalf("Unable to decode %s, wrote: %s", thetype, str)
+ }
+ l := *str1
+ if len(l) < 2 {
+ t.Fatalf("List must be at least of length two to include metadata")
+ }
+ if l[0] != "dbl" {
+ t.Fatal("Invalid type for list, expected: ", STRING, ", but was: ", l[0])
+ }
+ if int(l[1].(float64)) != len(DOUBLE_VALUES) {
+ t.Fatal("Invalid length for list, expected: ", len(DOUBLE_VALUES), ", but was: ", l[1])
+ }
+ for k, value := range DOUBLE_VALUES {
+ s := l[k+2]
+ if math.IsInf(value, 1) {
+ if s.(string) != JSON_INFINITY {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_INFINITY), str)
+ }
+ } else if math.IsInf(value, 0) {
+ if s.(string) != JSON_NEGATIVE_INFINITY {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY), str)
+ }
+ } else if math.IsNaN(value) {
+ if s.(string) != JSON_NAN {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_NAN), str)
+ }
+ } else {
+ if s.(float64) != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s'", thetype, value, s)
+ }
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestWriteJSONProtocolSet(t *testing.T) {
+ thetype := "set"
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ p.WriteSetBegin(TType(DOUBLE), len(DOUBLE_VALUES))
+ for _, value := range DOUBLE_VALUES {
+ if e := p.WriteDouble(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ }
+ p.WriteSetEnd()
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error())
+ }
+ str := trans.String()
+ str1 := new([]interface{})
+ err := json.Unmarshal([]byte(str), str1)
+ if err != nil {
+ t.Fatalf("Unable to decode %s, wrote: %s", thetype, str)
+ }
+ l := *str1
+ if len(l) < 2 {
+ t.Fatalf("Set must be at least of length two to include metadata")
+ }
+ if l[0] != "dbl" {
+ t.Fatal("Invalid type for set, expected: ", DOUBLE, ", but was: ", l[0])
+ }
+ if int(l[1].(float64)) != len(DOUBLE_VALUES) {
+ t.Fatal("Invalid length for set, expected: ", len(DOUBLE_VALUES), ", but was: ", l[1])
+ }
+ for k, value := range DOUBLE_VALUES {
+ s := l[k+2]
+ if math.IsInf(value, 1) {
+ if s.(string) != JSON_INFINITY {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_INFINITY), str)
+ }
+ } else if math.IsInf(value, 0) {
+ if s.(string) != JSON_NEGATIVE_INFINITY {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY), str)
+ }
+ } else if math.IsNaN(value) {
+ if s.(string) != JSON_NAN {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_NAN), str)
+ }
+ } else {
+ if s.(float64) != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s'", thetype, value, s)
+ }
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestWriteJSONProtocolMap(t *testing.T) {
+ thetype := "map"
+ trans := NewTMemoryBuffer()
+ p := NewTJSONProtocol(trans)
+ p.WriteMapBegin(TType(I32), TType(DOUBLE), len(DOUBLE_VALUES))
+ for k, value := range DOUBLE_VALUES {
+ if e := p.WriteI32(int32(k)); e != nil {
+ t.Fatalf("Unable to write %s key int32 value %v due to error: %s", thetype, k, e.Error())
+ }
+ if e := p.WriteDouble(value); e != nil {
+ t.Fatalf("Unable to write %s value float64 value %v due to error: %s", thetype, value, e.Error())
+ }
+ }
+ p.WriteMapEnd()
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error())
+ }
+ str := trans.String()
+ if str[0] != '[' || str[len(str)-1] != ']' {
+ t.Fatalf("Bad value for %s, wrote: %v, in go: %v", thetype, str, DOUBLE_VALUES)
+ }
+ expectedKeyType, expectedValueType, expectedSize, err := p.ReadMapBegin()
+ if err != nil {
+ t.Fatalf("Error while reading map begin: %s", err.Error())
+ }
+ if expectedKeyType != I32 {
+ t.Fatal("Expected map key type ", I32, ", but was ", expectedKeyType)
+ }
+ if expectedValueType != DOUBLE {
+ t.Fatal("Expected map value type ", DOUBLE, ", but was ", expectedValueType)
+ }
+ if expectedSize != len(DOUBLE_VALUES) {
+ t.Fatal("Expected map size of ", len(DOUBLE_VALUES), ", but was ", expectedSize)
+ }
+ for k, value := range DOUBLE_VALUES {
+ ik, err := p.ReadI32()
+ if err != nil {
+ t.Fatalf("Bad key for %s index %v, wrote: %v, expected: %v, error: %s", thetype, k, ik, string(k), err.Error())
+ }
+ if int(ik) != k {
+ t.Fatalf("Bad key for %s index %v, wrote: %v, expected: %v", thetype, k, ik, k)
+ }
+ dv, err := p.ReadDouble()
+ if err != nil {
+ t.Fatalf("Bad value for %s index %v, wrote: %v, expected: %v, error: %s", thetype, k, dv, value, err.Error())
+ }
+ s := strconv.FormatFloat(dv, 'g', 10, 64)
+ if math.IsInf(value, 1) {
+ if !math.IsInf(dv, 1) {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, jsonQuote(JSON_INFINITY))
+ }
+ } else if math.IsInf(value, 0) {
+ if !math.IsInf(dv, 0) {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY))
+ }
+ } else if math.IsNaN(value) {
+ if !math.IsNaN(dv) {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, jsonQuote(JSON_NAN))
+ }
+ } else {
+ expected := strconv.FormatFloat(value, 'g', 10, 64)
+ if s != expected {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected %v", thetype, k, value, s, expected)
+ }
+ v := float64(0)
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ }
+ }
+ err = p.ReadMapEnd()
+ if err != nil {
+ t.Fatalf("Error while reading map end: %s", err.Error())
+ }
+ trans.Close()
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/lowlevel_benchmarks_test.go b/src/jaegertracing/thrift/lib/go/thrift/lowlevel_benchmarks_test.go
new file mode 100644
index 000000000..e1736557b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/lowlevel_benchmarks_test.go
@@ -0,0 +1,540 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "bytes"
+ "testing"
+)
+
+var binaryProtoF = NewTBinaryProtocolFactoryDefault()
+var compactProtoF = NewTCompactProtocolFactory()
+
+var buf = bytes.NewBuffer(make([]byte, 0, 1024))
+
+var tfv = []TTransportFactory{
+ NewTMemoryBufferTransportFactory(1024),
+ NewStreamTransportFactory(buf, buf, true),
+ NewTFramedTransportFactory(NewTMemoryBufferTransportFactory(1024)),
+}
+
+func BenchmarkBinaryBool_0(b *testing.B) {
+ trans, err := tfv[0].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteBool(b, p, trans)
+ }
+}
+
+func BenchmarkBinaryByte_0(b *testing.B) {
+ trans, err := tfv[0].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteByte(b, p, trans)
+ }
+}
+
+func BenchmarkBinaryI16_0(b *testing.B) {
+ trans, err := tfv[0].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI16(b, p, trans)
+ }
+}
+
+func BenchmarkBinaryI32_0(b *testing.B) {
+ trans, err := tfv[0].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI32(b, p, trans)
+ }
+}
+func BenchmarkBinaryI64_0(b *testing.B) {
+ trans, err := tfv[0].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI64(b, p, trans)
+ }
+}
+func BenchmarkBinaryDouble_0(b *testing.B) {
+ trans, err := tfv[0].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteDouble(b, p, trans)
+ }
+}
+func BenchmarkBinaryString_0(b *testing.B) {
+ trans, err := tfv[0].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteString(b, p, trans)
+ }
+}
+func BenchmarkBinaryBinary_0(b *testing.B) {
+ trans, err := tfv[0].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteBinary(b, p, trans)
+ }
+}
+
+func BenchmarkBinaryBool_1(b *testing.B) {
+ trans, err := tfv[1].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteBool(b, p, trans)
+ }
+}
+
+func BenchmarkBinaryByte_1(b *testing.B) {
+ trans, err := tfv[1].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteByte(b, p, trans)
+ }
+}
+
+func BenchmarkBinaryI16_1(b *testing.B) {
+ trans, err := tfv[1].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI16(b, p, trans)
+ }
+}
+
+func BenchmarkBinaryI32_1(b *testing.B) {
+ trans, err := tfv[1].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI32(b, p, trans)
+ }
+}
+func BenchmarkBinaryI64_1(b *testing.B) {
+ trans, err := tfv[1].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI64(b, p, trans)
+ }
+}
+func BenchmarkBinaryDouble_1(b *testing.B) {
+ trans, err := tfv[1].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteDouble(b, p, trans)
+ }
+}
+func BenchmarkBinaryString_1(b *testing.B) {
+ trans, err := tfv[1].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteString(b, p, trans)
+ }
+}
+func BenchmarkBinaryBinary_1(b *testing.B) {
+ trans, err := tfv[1].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteBinary(b, p, trans)
+ }
+}
+
+func BenchmarkBinaryBool_2(b *testing.B) {
+ trans, err := tfv[2].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteBool(b, p, trans)
+ }
+}
+
+func BenchmarkBinaryByte_2(b *testing.B) {
+ trans, err := tfv[2].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteByte(b, p, trans)
+ }
+}
+
+func BenchmarkBinaryI16_2(b *testing.B) {
+ trans, err := tfv[2].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI16(b, p, trans)
+ }
+}
+
+func BenchmarkBinaryI32_2(b *testing.B) {
+ trans, err := tfv[2].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI32(b, p, trans)
+ }
+}
+func BenchmarkBinaryI64_2(b *testing.B) {
+ trans, err := tfv[2].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI64(b, p, trans)
+ }
+}
+func BenchmarkBinaryDouble_2(b *testing.B) {
+ trans, err := tfv[2].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteDouble(b, p, trans)
+ }
+}
+func BenchmarkBinaryString_2(b *testing.B) {
+ trans, err := tfv[2].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteString(b, p, trans)
+ }
+}
+func BenchmarkBinaryBinary_2(b *testing.B) {
+ trans, err := tfv[2].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := binaryProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteBinary(b, p, trans)
+ }
+}
+
+func BenchmarkCompactBool_0(b *testing.B) {
+ trans, err := tfv[0].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteBool(b, p, trans)
+ }
+}
+
+func BenchmarkCompactByte_0(b *testing.B) {
+ trans, err := tfv[0].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteByte(b, p, trans)
+ }
+}
+
+func BenchmarkCompactI16_0(b *testing.B) {
+ trans, err := tfv[0].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI16(b, p, trans)
+ }
+}
+
+func BenchmarkCompactI32_0(b *testing.B) {
+ trans, err := tfv[0].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI32(b, p, trans)
+ }
+}
+func BenchmarkCompactI64_0(b *testing.B) {
+ trans, err := tfv[0].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI64(b, p, trans)
+ }
+}
+func BenchmarkCompactDouble0(b *testing.B) {
+ trans, err := tfv[0].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteDouble(b, p, trans)
+ }
+}
+func BenchmarkCompactString0(b *testing.B) {
+ trans, err := tfv[0].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteString(b, p, trans)
+ }
+}
+func BenchmarkCompactBinary0(b *testing.B) {
+ trans, err := tfv[0].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteBinary(b, p, trans)
+ }
+}
+
+func BenchmarkCompactBool_1(b *testing.B) {
+ trans, err := tfv[1].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteBool(b, p, trans)
+ }
+}
+
+func BenchmarkCompactByte_1(b *testing.B) {
+ trans, err := tfv[1].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteByte(b, p, trans)
+ }
+}
+
+func BenchmarkCompactI16_1(b *testing.B) {
+ trans, err := tfv[1].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI16(b, p, trans)
+ }
+}
+
+func BenchmarkCompactI32_1(b *testing.B) {
+ trans, err := tfv[1].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI32(b, p, trans)
+ }
+}
+func BenchmarkCompactI64_1(b *testing.B) {
+ trans, err := tfv[1].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI64(b, p, trans)
+ }
+}
+func BenchmarkCompactDouble1(b *testing.B) {
+ trans, err := tfv[1].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteDouble(b, p, trans)
+ }
+}
+func BenchmarkCompactString1(b *testing.B) {
+ trans, err := tfv[1].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteString(b, p, trans)
+ }
+}
+func BenchmarkCompactBinary1(b *testing.B) {
+ trans, err := tfv[1].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteBinary(b, p, trans)
+ }
+}
+
+func BenchmarkCompactBool_2(b *testing.B) {
+ trans, err := tfv[2].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteBool(b, p, trans)
+ }
+}
+
+func BenchmarkCompactByte_2(b *testing.B) {
+ trans, err := tfv[2].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteByte(b, p, trans)
+ }
+}
+
+func BenchmarkCompactI16_2(b *testing.B) {
+ trans, err := tfv[2].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI16(b, p, trans)
+ }
+}
+
+func BenchmarkCompactI32_2(b *testing.B) {
+ trans, err := tfv[2].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI32(b, p, trans)
+ }
+}
+func BenchmarkCompactI64_2(b *testing.B) {
+ trans, err := tfv[2].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteI64(b, p, trans)
+ }
+}
+func BenchmarkCompactDouble2(b *testing.B) {
+ trans, err := tfv[2].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteDouble(b, p, trans)
+ }
+}
+func BenchmarkCompactString2(b *testing.B) {
+ trans, err := tfv[2].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteString(b, p, trans)
+ }
+}
+func BenchmarkCompactBinary2(b *testing.B) {
+ trans, err := tfv[2].GetTransport(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ p := compactProtoF.GetProtocol(trans)
+ for i := 0; i < b.N; i++ {
+ ReadWriteBinary(b, p, trans)
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/memory_buffer.go b/src/jaegertracing/thrift/lib/go/thrift/memory_buffer.go
new file mode 100644
index 000000000..5936d2730
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/memory_buffer.go
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "bytes"
+ "context"
+)
+
+// Memory buffer-based implementation of the TTransport interface.
+type TMemoryBuffer struct {
+ *bytes.Buffer
+ size int
+}
+
+type TMemoryBufferTransportFactory struct {
+ size int
+}
+
+func (p *TMemoryBufferTransportFactory) GetTransport(trans TTransport) (TTransport, error) {
+ if trans != nil {
+ t, ok := trans.(*TMemoryBuffer)
+ if ok && t.size > 0 {
+ return NewTMemoryBufferLen(t.size), nil
+ }
+ }
+ return NewTMemoryBufferLen(p.size), nil
+}
+
+func NewTMemoryBufferTransportFactory(size int) *TMemoryBufferTransportFactory {
+ return &TMemoryBufferTransportFactory{size: size}
+}
+
+func NewTMemoryBuffer() *TMemoryBuffer {
+ return &TMemoryBuffer{Buffer: &bytes.Buffer{}, size: 0}
+}
+
+func NewTMemoryBufferLen(size int) *TMemoryBuffer {
+ buf := make([]byte, 0, size)
+ return &TMemoryBuffer{Buffer: bytes.NewBuffer(buf), size: size}
+}
+
+func (p *TMemoryBuffer) IsOpen() bool {
+ return true
+}
+
+func (p *TMemoryBuffer) Open() error {
+ return nil
+}
+
+func (p *TMemoryBuffer) Close() error {
+ p.Buffer.Reset()
+ return nil
+}
+
+// Flushing a memory buffer is a no-op
+func (p *TMemoryBuffer) Flush(ctx context.Context) error {
+ return nil
+}
+
+func (p *TMemoryBuffer) RemainingBytes() (num_bytes uint64) {
+ return uint64(p.Buffer.Len())
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/memory_buffer_test.go b/src/jaegertracing/thrift/lib/go/thrift/memory_buffer_test.go
new file mode 100644
index 000000000..af2e8bfe5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/memory_buffer_test.go
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "testing"
+)
+
+func TestMemoryBuffer(t *testing.T) {
+ trans := NewTMemoryBufferLen(1024)
+ TransportTest(t, trans, trans)
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/messagetype.go b/src/jaegertracing/thrift/lib/go/thrift/messagetype.go
new file mode 100644
index 000000000..25ab2e98a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/messagetype.go
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+// Message type constants in the Thrift protocol.
+type TMessageType int32
+
+const (
+ INVALID_TMESSAGE_TYPE TMessageType = 0
+ CALL TMessageType = 1
+ REPLY TMessageType = 2
+ EXCEPTION TMessageType = 3
+ ONEWAY TMessageType = 4
+)
diff --git a/src/jaegertracing/thrift/lib/go/thrift/multiplexed_protocol.go b/src/jaegertracing/thrift/lib/go/thrift/multiplexed_protocol.go
new file mode 100644
index 000000000..d028a30b3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/multiplexed_protocol.go
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+ "fmt"
+ "strings"
+)
+
+/*
+TMultiplexedProtocol is a protocol-independent concrete decorator
+that allows a Thrift client to communicate with a multiplexing Thrift server,
+by prepending the service name to the function name during function calls.
+
+NOTE: THIS IS NOT USED BY SERVERS. On the server, use TMultiplexedProcessor to handle request
+from a multiplexing client.
+
+This example uses a single socket transport to invoke two services:
+
+socket := thrift.NewTSocketFromAddrTimeout(addr, TIMEOUT)
+transport := thrift.NewTFramedTransport(socket)
+protocol := thrift.NewTBinaryProtocolTransport(transport)
+
+mp := thrift.NewTMultiplexedProtocol(protocol, "Calculator")
+service := Calculator.NewCalculatorClient(mp)
+
+mp2 := thrift.NewTMultiplexedProtocol(protocol, "WeatherReport")
+service2 := WeatherReport.NewWeatherReportClient(mp2)
+
+err := transport.Open()
+if err != nil {
+ t.Fatal("Unable to open client socket", err)
+}
+
+fmt.Println(service.Add(2,2))
+fmt.Println(service2.GetTemperature())
+*/
+
+type TMultiplexedProtocol struct {
+ TProtocol
+ serviceName string
+}
+
+const MULTIPLEXED_SEPARATOR = ":"
+
+func NewTMultiplexedProtocol(protocol TProtocol, serviceName string) *TMultiplexedProtocol {
+ return &TMultiplexedProtocol{
+ TProtocol: protocol,
+ serviceName: serviceName,
+ }
+}
+
+func (t *TMultiplexedProtocol) WriteMessageBegin(name string, typeId TMessageType, seqid int32) error {
+ if typeId == CALL || typeId == ONEWAY {
+ return t.TProtocol.WriteMessageBegin(t.serviceName+MULTIPLEXED_SEPARATOR+name, typeId, seqid)
+ } else {
+ return t.TProtocol.WriteMessageBegin(name, typeId, seqid)
+ }
+}
+
+/*
+TMultiplexedProcessor is a TProcessor allowing
+a single TServer to provide multiple services.
+
+To do so, you instantiate the processor and then register additional
+processors with it, as shown in the following example:
+
+var processor = thrift.NewTMultiplexedProcessor()
+
+firstProcessor :=
+processor.RegisterProcessor("FirstService", firstProcessor)
+
+processor.registerProcessor(
+ "Calculator",
+ Calculator.NewCalculatorProcessor(&CalculatorHandler{}),
+)
+
+processor.registerProcessor(
+ "WeatherReport",
+ WeatherReport.NewWeatherReportProcessor(&WeatherReportHandler{}),
+)
+
+serverTransport, err := thrift.NewTServerSocketTimeout(addr, TIMEOUT)
+if err != nil {
+ t.Fatal("Unable to create server socket", err)
+}
+server := thrift.NewTSimpleServer2(processor, serverTransport)
+server.Serve();
+*/
+
+type TMultiplexedProcessor struct {
+ serviceProcessorMap map[string]TProcessor
+ DefaultProcessor TProcessor
+}
+
+func NewTMultiplexedProcessor() *TMultiplexedProcessor {
+ return &TMultiplexedProcessor{
+ serviceProcessorMap: make(map[string]TProcessor),
+ }
+}
+
+func (t *TMultiplexedProcessor) RegisterDefault(processor TProcessor) {
+ t.DefaultProcessor = processor
+}
+
+func (t *TMultiplexedProcessor) RegisterProcessor(name string, processor TProcessor) {
+ if t.serviceProcessorMap == nil {
+ t.serviceProcessorMap = make(map[string]TProcessor)
+ }
+ t.serviceProcessorMap[name] = processor
+}
+
+func (t *TMultiplexedProcessor) Process(ctx context.Context, in, out TProtocol) (bool, TException) {
+ name, typeId, seqid, err := in.ReadMessageBegin()
+ if err != nil {
+ return false, err
+ }
+ if typeId != CALL && typeId != ONEWAY {
+ return false, fmt.Errorf("Unexpected message type %v", typeId)
+ }
+ //extract the service name
+ v := strings.SplitN(name, MULTIPLEXED_SEPARATOR, 2)
+ if len(v) != 2 {
+ if t.DefaultProcessor != nil {
+ smb := NewStoredMessageProtocol(in, name, typeId, seqid)
+ return t.DefaultProcessor.Process(ctx, smb, out)
+ }
+ return false, fmt.Errorf("Service name not found in message name: %s. Did you forget to use a TMultiplexProtocol in your client?", name)
+ }
+ actualProcessor, ok := t.serviceProcessorMap[v[0]]
+ if !ok {
+ return false, fmt.Errorf("Service name not found: %s. Did you forget to call registerProcessor()?", v[0])
+ }
+ smb := NewStoredMessageProtocol(in, v[1], typeId, seqid)
+ return actualProcessor.Process(ctx, smb, out)
+}
+
+//Protocol that use stored message for ReadMessageBegin
+type storedMessageProtocol struct {
+ TProtocol
+ name string
+ typeId TMessageType
+ seqid int32
+}
+
+func NewStoredMessageProtocol(protocol TProtocol, name string, typeId TMessageType, seqid int32) *storedMessageProtocol {
+ return &storedMessageProtocol{protocol, name, typeId, seqid}
+}
+
+func (s *storedMessageProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqid int32, err error) {
+ return s.name, s.typeId, s.seqid, nil
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/numeric.go b/src/jaegertracing/thrift/lib/go/thrift/numeric.go
new file mode 100644
index 000000000..aa8daa9b5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/numeric.go
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "math"
+ "strconv"
+)
+
+type Numeric interface {
+ Int64() int64
+ Int32() int32
+ Int16() int16
+ Byte() byte
+ Int() int
+ Float64() float64
+ Float32() float32
+ String() string
+ isNull() bool
+}
+
+type numeric struct {
+ iValue int64
+ dValue float64
+ sValue string
+ isNil bool
+}
+
+var (
+ INFINITY Numeric
+ NEGATIVE_INFINITY Numeric
+ NAN Numeric
+ ZERO Numeric
+ NUMERIC_NULL Numeric
+)
+
+func NewNumericFromDouble(dValue float64) Numeric {
+ if math.IsInf(dValue, 1) {
+ return INFINITY
+ }
+ if math.IsInf(dValue, -1) {
+ return NEGATIVE_INFINITY
+ }
+ if math.IsNaN(dValue) {
+ return NAN
+ }
+ iValue := int64(dValue)
+ sValue := strconv.FormatFloat(dValue, 'g', 10, 64)
+ isNil := false
+ return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil}
+}
+
+func NewNumericFromI64(iValue int64) Numeric {
+ dValue := float64(iValue)
+ sValue := string(iValue)
+ isNil := false
+ return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil}
+}
+
+func NewNumericFromI32(iValue int32) Numeric {
+ dValue := float64(iValue)
+ sValue := string(iValue)
+ isNil := false
+ return &numeric{iValue: int64(iValue), dValue: dValue, sValue: sValue, isNil: isNil}
+}
+
+func NewNumericFromString(sValue string) Numeric {
+ if sValue == INFINITY.String() {
+ return INFINITY
+ }
+ if sValue == NEGATIVE_INFINITY.String() {
+ return NEGATIVE_INFINITY
+ }
+ if sValue == NAN.String() {
+ return NAN
+ }
+ iValue, _ := strconv.ParseInt(sValue, 10, 64)
+ dValue, _ := strconv.ParseFloat(sValue, 64)
+ isNil := len(sValue) == 0
+ return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil}
+}
+
+func NewNumericFromJSONString(sValue string, isNull bool) Numeric {
+ if isNull {
+ return NewNullNumeric()
+ }
+ if sValue == JSON_INFINITY {
+ return INFINITY
+ }
+ if sValue == JSON_NEGATIVE_INFINITY {
+ return NEGATIVE_INFINITY
+ }
+ if sValue == JSON_NAN {
+ return NAN
+ }
+ iValue, _ := strconv.ParseInt(sValue, 10, 64)
+ dValue, _ := strconv.ParseFloat(sValue, 64)
+ return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNull}
+}
+
+func NewNullNumeric() Numeric {
+ return &numeric{iValue: 0, dValue: 0.0, sValue: "", isNil: true}
+}
+
+func (p *numeric) Int64() int64 {
+ return p.iValue
+}
+
+func (p *numeric) Int32() int32 {
+ return int32(p.iValue)
+}
+
+func (p *numeric) Int16() int16 {
+ return int16(p.iValue)
+}
+
+func (p *numeric) Byte() byte {
+ return byte(p.iValue)
+}
+
+func (p *numeric) Int() int {
+ return int(p.iValue)
+}
+
+func (p *numeric) Float64() float64 {
+ return p.dValue
+}
+
+func (p *numeric) Float32() float32 {
+ return float32(p.dValue)
+}
+
+func (p *numeric) String() string {
+ return p.sValue
+}
+
+func (p *numeric) isNull() bool {
+ return p.isNil
+}
+
+func init() {
+ INFINITY = &numeric{iValue: 0, dValue: math.Inf(1), sValue: "Infinity", isNil: false}
+ NEGATIVE_INFINITY = &numeric{iValue: 0, dValue: math.Inf(-1), sValue: "-Infinity", isNil: false}
+ NAN = &numeric{iValue: 0, dValue: math.NaN(), sValue: "NaN", isNil: false}
+ ZERO = &numeric{iValue: 0, dValue: 0, sValue: "0", isNil: false}
+ NUMERIC_NULL = &numeric{iValue: 0, dValue: 0, sValue: "0", isNil: true}
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/pointerize.go b/src/jaegertracing/thrift/lib/go/thrift/pointerize.go
new file mode 100644
index 000000000..fb564ea81
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/pointerize.go
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+///////////////////////////////////////////////////////////////////////////////
+// This file is home to helpers that convert from various base types to
+// respective pointer types. This is necessary because Go does not permit
+// references to constants, nor can a pointer type to base type be allocated
+// and initialized in a single expression.
+//
+// E.g., this is not allowed:
+//
+// var ip *int = &5
+//
+// But this *is* allowed:
+//
+// func IntPtr(i int) *int { return &i }
+// var ip *int = IntPtr(5)
+//
+// Since pointers to base types are commonplace as [optional] fields in
+// exported thrift structs, we factor such helpers here.
+///////////////////////////////////////////////////////////////////////////////
+
+func Float32Ptr(v float32) *float32 { return &v }
+func Float64Ptr(v float64) *float64 { return &v }
+func IntPtr(v int) *int { return &v }
+func Int8Ptr(v int8) *int8 { return &v }
+func Int16Ptr(v int16) *int16 { return &v }
+func Int32Ptr(v int32) *int32 { return &v }
+func Int64Ptr(v int64) *int64 { return &v }
+func StringPtr(v string) *string { return &v }
+func Uint32Ptr(v uint32) *uint32 { return &v }
+func Uint64Ptr(v uint64) *uint64 { return &v }
+func BoolPtr(v bool) *bool { return &v }
+func ByteSlicePtr(v []byte) *[]byte { return &v }
diff --git a/src/jaegertracing/thrift/lib/go/thrift/processor_factory.go b/src/jaegertracing/thrift/lib/go/thrift/processor_factory.go
new file mode 100644
index 000000000..e4b132b30
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/processor_factory.go
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import "context"
+
+// A processor is a generic object which operates upon an input stream and
+// writes to some output stream.
+type TProcessor interface {
+ Process(ctx context.Context, in, out TProtocol) (bool, TException)
+}
+
+type TProcessorFunction interface {
+ Process(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException)
+}
+
+// The default processor factory just returns a singleton
+// instance.
+type TProcessorFactory interface {
+ GetProcessor(trans TTransport) TProcessor
+}
+
+type tProcessorFactory struct {
+ processor TProcessor
+}
+
+func NewTProcessorFactory(p TProcessor) TProcessorFactory {
+ return &tProcessorFactory{processor: p}
+}
+
+func (p *tProcessorFactory) GetProcessor(trans TTransport) TProcessor {
+ return p.processor
+}
+
+/**
+ * The default processor factory just returns a singleton
+ * instance.
+ */
+type TProcessorFunctionFactory interface {
+ GetProcessorFunction(trans TTransport) TProcessorFunction
+}
+
+type tProcessorFunctionFactory struct {
+ processor TProcessorFunction
+}
+
+func NewTProcessorFunctionFactory(p TProcessorFunction) TProcessorFunctionFactory {
+ return &tProcessorFunctionFactory{processor: p}
+}
+
+func (p *tProcessorFunctionFactory) GetProcessorFunction(trans TTransport) TProcessorFunction {
+ return p.processor
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/protocol.go b/src/jaegertracing/thrift/lib/go/thrift/protocol.go
new file mode 100644
index 000000000..2e6bc4b16
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/protocol.go
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+ "errors"
+ "fmt"
+)
+
+const (
+ VERSION_MASK = 0xffff0000
+ VERSION_1 = 0x80010000
+)
+
+type TProtocol interface {
+ WriteMessageBegin(name string, typeId TMessageType, seqid int32) error
+ WriteMessageEnd() error
+ WriteStructBegin(name string) error
+ WriteStructEnd() error
+ WriteFieldBegin(name string, typeId TType, id int16) error
+ WriteFieldEnd() error
+ WriteFieldStop() error
+ WriteMapBegin(keyType TType, valueType TType, size int) error
+ WriteMapEnd() error
+ WriteListBegin(elemType TType, size int) error
+ WriteListEnd() error
+ WriteSetBegin(elemType TType, size int) error
+ WriteSetEnd() error
+ WriteBool(value bool) error
+ WriteByte(value int8) error
+ WriteI16(value int16) error
+ WriteI32(value int32) error
+ WriteI64(value int64) error
+ WriteDouble(value float64) error
+ WriteString(value string) error
+ WriteBinary(value []byte) error
+
+ ReadMessageBegin() (name string, typeId TMessageType, seqid int32, err error)
+ ReadMessageEnd() error
+ ReadStructBegin() (name string, err error)
+ ReadStructEnd() error
+ ReadFieldBegin() (name string, typeId TType, id int16, err error)
+ ReadFieldEnd() error
+ ReadMapBegin() (keyType TType, valueType TType, size int, err error)
+ ReadMapEnd() error
+ ReadListBegin() (elemType TType, size int, err error)
+ ReadListEnd() error
+ ReadSetBegin() (elemType TType, size int, err error)
+ ReadSetEnd() error
+ ReadBool() (value bool, err error)
+ ReadByte() (value int8, err error)
+ ReadI16() (value int16, err error)
+ ReadI32() (value int32, err error)
+ ReadI64() (value int64, err error)
+ ReadDouble() (value float64, err error)
+ ReadString() (value string, err error)
+ ReadBinary() (value []byte, err error)
+
+ Skip(fieldType TType) (err error)
+ Flush(ctx context.Context) (err error)
+
+ Transport() TTransport
+}
+
+// The maximum recursive depth the skip() function will traverse
+const DEFAULT_RECURSION_DEPTH = 64
+
+// Skips over the next data element from the provided input TProtocol object.
+func SkipDefaultDepth(prot TProtocol, typeId TType) (err error) {
+ return Skip(prot, typeId, DEFAULT_RECURSION_DEPTH)
+}
+
+// Skips over the next data element from the provided input TProtocol object.
+func Skip(self TProtocol, fieldType TType, maxDepth int) (err error) {
+
+ if maxDepth <= 0 {
+ return NewTProtocolExceptionWithType(DEPTH_LIMIT, errors.New("Depth limit exceeded"))
+ }
+
+ switch fieldType {
+ case BOOL:
+ _, err = self.ReadBool()
+ return
+ case BYTE:
+ _, err = self.ReadByte()
+ return
+ case I16:
+ _, err = self.ReadI16()
+ return
+ case I32:
+ _, err = self.ReadI32()
+ return
+ case I64:
+ _, err = self.ReadI64()
+ return
+ case DOUBLE:
+ _, err = self.ReadDouble()
+ return
+ case STRING:
+ _, err = self.ReadString()
+ return
+ case STRUCT:
+ if _, err = self.ReadStructBegin(); err != nil {
+ return err
+ }
+ for {
+ _, typeId, _, _ := self.ReadFieldBegin()
+ if typeId == STOP {
+ break
+ }
+ err := Skip(self, typeId, maxDepth-1)
+ if err != nil {
+ return err
+ }
+ self.ReadFieldEnd()
+ }
+ return self.ReadStructEnd()
+ case MAP:
+ keyType, valueType, size, err := self.ReadMapBegin()
+ if err != nil {
+ return err
+ }
+ for i := 0; i < size; i++ {
+ err := Skip(self, keyType, maxDepth-1)
+ if err != nil {
+ return err
+ }
+ self.Skip(valueType)
+ }
+ return self.ReadMapEnd()
+ case SET:
+ elemType, size, err := self.ReadSetBegin()
+ if err != nil {
+ return err
+ }
+ for i := 0; i < size; i++ {
+ err := Skip(self, elemType, maxDepth-1)
+ if err != nil {
+ return err
+ }
+ }
+ return self.ReadSetEnd()
+ case LIST:
+ elemType, size, err := self.ReadListBegin()
+ if err != nil {
+ return err
+ }
+ for i := 0; i < size; i++ {
+ err := Skip(self, elemType, maxDepth-1)
+ if err != nil {
+ return err
+ }
+ }
+ return self.ReadListEnd()
+ default:
+ return NewTProtocolExceptionWithType(INVALID_DATA, errors.New(fmt.Sprintf("Unknown data type %d", fieldType)))
+ }
+ return nil
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/protocol_exception.go b/src/jaegertracing/thrift/lib/go/thrift/protocol_exception.go
new file mode 100644
index 000000000..29ab75d92
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/protocol_exception.go
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "encoding/base64"
+)
+
+// Thrift Protocol exception
+type TProtocolException interface {
+ TException
+ TypeId() int
+}
+
+const (
+ UNKNOWN_PROTOCOL_EXCEPTION = 0
+ INVALID_DATA = 1
+ NEGATIVE_SIZE = 2
+ SIZE_LIMIT = 3
+ BAD_VERSION = 4
+ NOT_IMPLEMENTED = 5
+ DEPTH_LIMIT = 6
+)
+
+type tProtocolException struct {
+ typeId int
+ message string
+}
+
+func (p *tProtocolException) TypeId() int {
+ return p.typeId
+}
+
+func (p *tProtocolException) String() string {
+ return p.message
+}
+
+func (p *tProtocolException) Error() string {
+ return p.message
+}
+
+func NewTProtocolException(err error) TProtocolException {
+ if err == nil {
+ return nil
+ }
+ if e, ok := err.(TProtocolException); ok {
+ return e
+ }
+ if _, ok := err.(base64.CorruptInputError); ok {
+ return &tProtocolException{INVALID_DATA, err.Error()}
+ }
+ return &tProtocolException{UNKNOWN_PROTOCOL_EXCEPTION, err.Error()}
+}
+
+func NewTProtocolExceptionWithType(errType int, err error) TProtocolException {
+ if err == nil {
+ return nil
+ }
+ return &tProtocolException{errType, err.Error()}
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/protocol_factory.go b/src/jaegertracing/thrift/lib/go/thrift/protocol_factory.go
new file mode 100644
index 000000000..c40f796d8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/protocol_factory.go
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+// Factory interface for constructing protocol instances.
+type TProtocolFactory interface {
+ GetProtocol(trans TTransport) TProtocol
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/protocol_test.go b/src/jaegertracing/thrift/lib/go/thrift/protocol_test.go
new file mode 100644
index 000000000..944055c0b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/protocol_test.go
@@ -0,0 +1,517 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "bytes"
+ "context"
+ "io/ioutil"
+ "math"
+ "net"
+ "net/http"
+ "testing"
+)
+
+const PROTOCOL_BINARY_DATA_SIZE = 155
+
+var (
+ protocol_bdata []byte // test data for writing; same as data
+ BOOL_VALUES []bool
+ BYTE_VALUES []int8
+ INT16_VALUES []int16
+ INT32_VALUES []int32
+ INT64_VALUES []int64
+ DOUBLE_VALUES []float64
+ STRING_VALUES []string
+)
+
+func init() {
+ protocol_bdata = make([]byte, PROTOCOL_BINARY_DATA_SIZE)
+ for i := 0; i < PROTOCOL_BINARY_DATA_SIZE; i++ {
+ protocol_bdata[i] = byte((i + 'a') % 255)
+ }
+ BOOL_VALUES = []bool{false, true, false, false, true}
+ BYTE_VALUES = []int8{117, 0, 1, 32, 127, -128, -1}
+ INT16_VALUES = []int16{459, 0, 1, -1, -128, 127, 32767, -32768}
+ INT32_VALUES = []int32{459, 0, 1, -1, -128, 127, 32767, 2147483647, -2147483535}
+ INT64_VALUES = []int64{459, 0, 1, -1, -128, 127, 32767, 2147483647, -2147483535, 34359738481, -35184372088719, -9223372036854775808, 9223372036854775807}
+ DOUBLE_VALUES = []float64{459.3, 0.0, -1.0, 1.0, 0.5, 0.3333, 3.14159, 1.537e-38, 1.673e25, 6.02214179e23, -6.02214179e23, INFINITY.Float64(), NEGATIVE_INFINITY.Float64(), NAN.Float64()}
+ STRING_VALUES = []string{"", "a", "st[uf]f", "st,u:ff with spaces", "stuff\twith\nescape\\characters'...\"lots{of}fun</xml>"}
+}
+
+type HTTPEchoServer struct{}
+type HTTPHeaderEchoServer struct{}
+
+func (p *HTTPEchoServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ buf, err := ioutil.ReadAll(req.Body)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write(buf)
+ } else {
+ w.WriteHeader(http.StatusOK)
+ w.Write(buf)
+ }
+}
+
+func (p *HTTPHeaderEchoServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ buf, err := ioutil.ReadAll(req.Body)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write(buf)
+ } else {
+ w.WriteHeader(http.StatusOK)
+ w.Write(buf)
+ }
+}
+
+func HttpClientSetupForTest(t *testing.T) (net.Listener, net.Addr) {
+ addr, err := FindAvailableTCPServerPort(40000)
+ if err != nil {
+ t.Fatalf("Unable to find available tcp port addr: %s", err)
+ return nil, addr
+ }
+ l, err := net.Listen(addr.Network(), addr.String())
+ if err != nil {
+ t.Fatalf("Unable to setup tcp listener on %s: %s", addr.String(), err)
+ return l, addr
+ }
+ go http.Serve(l, &HTTPEchoServer{})
+ return l, addr
+}
+
+func HttpClientSetupForHeaderTest(t *testing.T) (net.Listener, net.Addr) {
+ addr, err := FindAvailableTCPServerPort(40000)
+ if err != nil {
+ t.Fatalf("Unable to find available tcp port addr: %s", err)
+ return nil, addr
+ }
+ l, err := net.Listen(addr.Network(), addr.String())
+ if err != nil {
+ t.Fatalf("Unable to setup tcp listener on %s: %s", addr.String(), err)
+ return l, addr
+ }
+ go http.Serve(l, &HTTPHeaderEchoServer{})
+ return l, addr
+}
+
+func ReadWriteProtocolTest(t *testing.T, protocolFactory TProtocolFactory) {
+ buf := bytes.NewBuffer(make([]byte, 0, 1024))
+ l, addr := HttpClientSetupForTest(t)
+ defer l.Close()
+ transports := []TTransportFactory{
+ NewTMemoryBufferTransportFactory(1024),
+ NewStreamTransportFactory(buf, buf, true),
+ NewTFramedTransportFactory(NewTMemoryBufferTransportFactory(1024)),
+ NewTZlibTransportFactoryWithFactory(0, NewTMemoryBufferTransportFactory(1024)),
+ NewTZlibTransportFactoryWithFactory(6, NewTMemoryBufferTransportFactory(1024)),
+ NewTZlibTransportFactoryWithFactory(9, NewTFramedTransportFactory(NewTMemoryBufferTransportFactory(1024))),
+ NewTHttpPostClientTransportFactory("http://" + addr.String()),
+ }
+ for _, tf := range transports {
+ trans, err := tf.GetTransport(nil)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ p := protocolFactory.GetProtocol(trans)
+ ReadWriteBool(t, p, trans)
+ trans.Close()
+ }
+ for _, tf := range transports {
+ trans, err := tf.GetTransport(nil)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ p := protocolFactory.GetProtocol(trans)
+ ReadWriteByte(t, p, trans)
+ trans.Close()
+ }
+ for _, tf := range transports {
+ trans, err := tf.GetTransport(nil)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ p := protocolFactory.GetProtocol(trans)
+ ReadWriteI16(t, p, trans)
+ trans.Close()
+ }
+ for _, tf := range transports {
+ trans, err := tf.GetTransport(nil)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ p := protocolFactory.GetProtocol(trans)
+ ReadWriteI32(t, p, trans)
+ trans.Close()
+ }
+ for _, tf := range transports {
+ trans, err := tf.GetTransport(nil)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ p := protocolFactory.GetProtocol(trans)
+ ReadWriteI64(t, p, trans)
+ trans.Close()
+ }
+ for _, tf := range transports {
+ trans, err := tf.GetTransport(nil)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ p := protocolFactory.GetProtocol(trans)
+ ReadWriteDouble(t, p, trans)
+ trans.Close()
+ }
+ for _, tf := range transports {
+ trans, err := tf.GetTransport(nil)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ p := protocolFactory.GetProtocol(trans)
+ ReadWriteString(t, p, trans)
+ trans.Close()
+ }
+ for _, tf := range transports {
+ trans, err := tf.GetTransport(nil)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ p := protocolFactory.GetProtocol(trans)
+ ReadWriteBinary(t, p, trans)
+ trans.Close()
+ }
+ for _, tf := range transports {
+ trans, err := tf.GetTransport(nil)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ p := protocolFactory.GetProtocol(trans)
+ ReadWriteI64(t, p, trans)
+ ReadWriteDouble(t, p, trans)
+ ReadWriteBinary(t, p, trans)
+ ReadWriteByte(t, p, trans)
+ trans.Close()
+ }
+}
+
+func ReadWriteBool(t testing.TB, p TProtocol, trans TTransport) {
+ thetype := TType(BOOL)
+ thelen := len(BOOL_VALUES)
+ err := p.WriteListBegin(thetype, thelen)
+ if err != nil {
+ t.Errorf("%s: %T %T %q Error writing list begin: %q", "ReadWriteBool", p, trans, err, thetype)
+ }
+ for k, v := range BOOL_VALUES {
+ err = p.WriteBool(v)
+ if err != nil {
+ t.Errorf("%s: %T %T %v Error writing bool in list at index %v: %v", "ReadWriteBool", p, trans, err, k, v)
+ }
+ }
+ p.WriteListEnd()
+ if err != nil {
+ t.Errorf("%s: %T %T %v Error writing list end: %v", "ReadWriteBool", p, trans, err, BOOL_VALUES)
+ }
+ p.Flush(context.Background())
+ thetype2, thelen2, err := p.ReadListBegin()
+ if err != nil {
+ t.Errorf("%s: %T %T %v Error reading list: %v", "ReadWriteBool", p, trans, err, BOOL_VALUES)
+ }
+ _, ok := p.(*TSimpleJSONProtocol)
+ if !ok {
+ if thetype != thetype2 {
+ t.Errorf("%s: %T %T type %s != type %s", "ReadWriteBool", p, trans, thetype, thetype2)
+ }
+ if thelen != thelen2 {
+ t.Errorf("%s: %T %T len %v != len %v", "ReadWriteBool", p, trans, thelen, thelen2)
+ }
+ }
+ for k, v := range BOOL_VALUES {
+ value, err := p.ReadBool()
+ if err != nil {
+ t.Errorf("%s: %T %T %v Error reading bool at index %v: %v", "ReadWriteBool", p, trans, err, k, v)
+ }
+ if v != value {
+ t.Errorf("%s: index %v %v %v %v != %v", "ReadWriteBool", k, p, trans, v, value)
+ }
+ }
+ err = p.ReadListEnd()
+ if err != nil {
+ t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteBool", p, trans, err)
+ }
+}
+
+func ReadWriteByte(t testing.TB, p TProtocol, trans TTransport) {
+ thetype := TType(BYTE)
+ thelen := len(BYTE_VALUES)
+ err := p.WriteListBegin(thetype, thelen)
+ if err != nil {
+ t.Errorf("%s: %T %T %q Error writing list begin: %q", "ReadWriteByte", p, trans, err, thetype)
+ }
+ for k, v := range BYTE_VALUES {
+ err = p.WriteByte(v)
+ if err != nil {
+ t.Errorf("%s: %T %T %q Error writing byte in list at index %d: %q", "ReadWriteByte", p, trans, err, k, v)
+ }
+ }
+ err = p.WriteListEnd()
+ if err != nil {
+ t.Errorf("%s: %T %T %q Error writing list end: %q", "ReadWriteByte", p, trans, err, BYTE_VALUES)
+ }
+ err = p.Flush(context.Background())
+ if err != nil {
+ t.Errorf("%s: %T %T %q Error flushing list of bytes: %q", "ReadWriteByte", p, trans, err, BYTE_VALUES)
+ }
+ thetype2, thelen2, err := p.ReadListBegin()
+ if err != nil {
+ t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteByte", p, trans, err, BYTE_VALUES)
+ }
+ _, ok := p.(*TSimpleJSONProtocol)
+ if !ok {
+ if thetype != thetype2 {
+ t.Errorf("%s: %T %T type %s != type %s", "ReadWriteByte", p, trans, thetype, thetype2)
+ }
+ if thelen != thelen2 {
+ t.Errorf("%s: %T %T len %v != len %v", "ReadWriteByte", p, trans, thelen, thelen2)
+ }
+ }
+ for k, v := range BYTE_VALUES {
+ value, err := p.ReadByte()
+ if err != nil {
+ t.Errorf("%s: %T %T %q Error reading byte at index %d: %q", "ReadWriteByte", p, trans, err, k, v)
+ }
+ if v != value {
+ t.Errorf("%s: %T %T %d != %d", "ReadWriteByte", p, trans, v, value)
+ }
+ }
+ err = p.ReadListEnd()
+ if err != nil {
+ t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteByte", p, trans, err)
+ }
+}
+
+func ReadWriteI16(t testing.TB, p TProtocol, trans TTransport) {
+ thetype := TType(I16)
+ thelen := len(INT16_VALUES)
+ p.WriteListBegin(thetype, thelen)
+ for _, v := range INT16_VALUES {
+ p.WriteI16(v)
+ }
+ p.WriteListEnd()
+ p.Flush(context.Background())
+ thetype2, thelen2, err := p.ReadListBegin()
+ if err != nil {
+ t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteI16", p, trans, err, INT16_VALUES)
+ }
+ _, ok := p.(*TSimpleJSONProtocol)
+ if !ok {
+ if thetype != thetype2 {
+ t.Errorf("%s: %T %T type %s != type %s", "ReadWriteI16", p, trans, thetype, thetype2)
+ }
+ if thelen != thelen2 {
+ t.Errorf("%s: %T %T len %v != len %v", "ReadWriteI16", p, trans, thelen, thelen2)
+ }
+ }
+ for k, v := range INT16_VALUES {
+ value, err := p.ReadI16()
+ if err != nil {
+ t.Errorf("%s: %T %T %q Error reading int16 at index %d: %q", "ReadWriteI16", p, trans, err, k, v)
+ }
+ if v != value {
+ t.Errorf("%s: %T %T %d != %d", "ReadWriteI16", p, trans, v, value)
+ }
+ }
+ err = p.ReadListEnd()
+ if err != nil {
+ t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteI16", p, trans, err)
+ }
+}
+
+func ReadWriteI32(t testing.TB, p TProtocol, trans TTransport) {
+ thetype := TType(I32)
+ thelen := len(INT32_VALUES)
+ p.WriteListBegin(thetype, thelen)
+ for _, v := range INT32_VALUES {
+ p.WriteI32(v)
+ }
+ p.WriteListEnd()
+ p.Flush(context.Background())
+ thetype2, thelen2, err := p.ReadListBegin()
+ if err != nil {
+ t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteI32", p, trans, err, INT32_VALUES)
+ }
+ _, ok := p.(*TSimpleJSONProtocol)
+ if !ok {
+ if thetype != thetype2 {
+ t.Errorf("%s: %T %T type %s != type %s", "ReadWriteI32", p, trans, thetype, thetype2)
+ }
+ if thelen != thelen2 {
+ t.Errorf("%s: %T %T len %v != len %v", "ReadWriteI32", p, trans, thelen, thelen2)
+ }
+ }
+ for k, v := range INT32_VALUES {
+ value, err := p.ReadI32()
+ if err != nil {
+ t.Errorf("%s: %T %T %q Error reading int32 at index %d: %q", "ReadWriteI32", p, trans, err, k, v)
+ }
+ if v != value {
+ t.Errorf("%s: %T %T %d != %d", "ReadWriteI32", p, trans, v, value)
+ }
+ }
+ if err != nil {
+ t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteI32", p, trans, err)
+ }
+}
+
+func ReadWriteI64(t testing.TB, p TProtocol, trans TTransport) {
+ thetype := TType(I64)
+ thelen := len(INT64_VALUES)
+ p.WriteListBegin(thetype, thelen)
+ for _, v := range INT64_VALUES {
+ p.WriteI64(v)
+ }
+ p.WriteListEnd()
+ p.Flush(context.Background())
+ thetype2, thelen2, err := p.ReadListBegin()
+ if err != nil {
+ t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteI64", p, trans, err, INT64_VALUES)
+ }
+ _, ok := p.(*TSimpleJSONProtocol)
+ if !ok {
+ if thetype != thetype2 {
+ t.Errorf("%s: %T %T type %s != type %s", "ReadWriteI64", p, trans, thetype, thetype2)
+ }
+ if thelen != thelen2 {
+ t.Errorf("%s: %T %T len %v != len %v", "ReadWriteI64", p, trans, thelen, thelen2)
+ }
+ }
+ for k, v := range INT64_VALUES {
+ value, err := p.ReadI64()
+ if err != nil {
+ t.Errorf("%s: %T %T %q Error reading int64 at index %d: %q", "ReadWriteI64", p, trans, err, k, v)
+ }
+ if v != value {
+ t.Errorf("%s: %T %T %q != %q", "ReadWriteI64", p, trans, v, value)
+ }
+ }
+ if err != nil {
+ t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteI64", p, trans, err)
+ }
+}
+
+func ReadWriteDouble(t testing.TB, p TProtocol, trans TTransport) {
+ thetype := TType(DOUBLE)
+ thelen := len(DOUBLE_VALUES)
+ p.WriteListBegin(thetype, thelen)
+ for _, v := range DOUBLE_VALUES {
+ p.WriteDouble(v)
+ }
+ p.WriteListEnd()
+ p.Flush(context.Background())
+ thetype2, thelen2, err := p.ReadListBegin()
+ if err != nil {
+ t.Errorf("%s: %T %T %v Error reading list: %v", "ReadWriteDouble", p, trans, err, DOUBLE_VALUES)
+ }
+ if thetype != thetype2 {
+ t.Errorf("%s: %T %T type %s != type %s", "ReadWriteDouble", p, trans, thetype, thetype2)
+ }
+ if thelen != thelen2 {
+ t.Errorf("%s: %T %T len %v != len %v", "ReadWriteDouble", p, trans, thelen, thelen2)
+ }
+ for k, v := range DOUBLE_VALUES {
+ value, err := p.ReadDouble()
+ if err != nil {
+ t.Errorf("%s: %T %T %q Error reading double at index %d: %v", "ReadWriteDouble", p, trans, err, k, v)
+ }
+ if math.IsNaN(v) {
+ if !math.IsNaN(value) {
+ t.Errorf("%s: %T %T math.IsNaN(%v) != math.IsNaN(%v)", "ReadWriteDouble", p, trans, v, value)
+ }
+ } else if v != value {
+ t.Errorf("%s: %T %T %v != %v", "ReadWriteDouble", p, trans, v, value)
+ }
+ }
+ err = p.ReadListEnd()
+ if err != nil {
+ t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteDouble", p, trans, err)
+ }
+}
+
+func ReadWriteString(t testing.TB, p TProtocol, trans TTransport) {
+ thetype := TType(STRING)
+ thelen := len(STRING_VALUES)
+ p.WriteListBegin(thetype, thelen)
+ for _, v := range STRING_VALUES {
+ p.WriteString(v)
+ }
+ p.WriteListEnd()
+ p.Flush(context.Background())
+ thetype2, thelen2, err := p.ReadListBegin()
+ if err != nil {
+ t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteString", p, trans, err, STRING_VALUES)
+ }
+ _, ok := p.(*TSimpleJSONProtocol)
+ if !ok {
+ if thetype != thetype2 {
+ t.Errorf("%s: %T %T type %s != type %s", "ReadWriteString", p, trans, thetype, thetype2)
+ }
+ if thelen != thelen2 {
+ t.Errorf("%s: %T %T len %v != len %v", "ReadWriteString", p, trans, thelen, thelen2)
+ }
+ }
+ for k, v := range STRING_VALUES {
+ value, err := p.ReadString()
+ if err != nil {
+ t.Errorf("%s: %T %T %q Error reading string at index %d: %q", "ReadWriteString", p, trans, err, k, v)
+ }
+ if v != value {
+ t.Errorf("%s: %T %T %v != %v", "ReadWriteString", p, trans, v, value)
+ }
+ }
+ if err != nil {
+ t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteString", p, trans, err)
+ }
+}
+
+func ReadWriteBinary(t testing.TB, p TProtocol, trans TTransport) {
+ v := protocol_bdata
+ p.WriteBinary(v)
+ p.Flush(context.Background())
+ value, err := p.ReadBinary()
+ if err != nil {
+ t.Errorf("%s: %T %T Unable to read binary: %s", "ReadWriteBinary", p, trans, err.Error())
+ }
+ if len(v) != len(value) {
+ t.Errorf("%s: %T %T len(v) != len(value)... %d != %d", "ReadWriteBinary", p, trans, len(v), len(value))
+ } else {
+ for i := 0; i < len(v); i++ {
+ if v[i] != value[i] {
+ t.Errorf("%s: %T %T %s != %s", "ReadWriteBinary", p, trans, v, value)
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/rich_transport.go b/src/jaegertracing/thrift/lib/go/thrift/rich_transport.go
new file mode 100644
index 000000000..4025bebea
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/rich_transport.go
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import "io"
+
+type RichTransport struct {
+ TTransport
+}
+
+// Wraps Transport to provide TRichTransport interface
+func NewTRichTransport(trans TTransport) *RichTransport {
+ return &RichTransport{trans}
+}
+
+func (r *RichTransport) ReadByte() (c byte, err error) {
+ return readByte(r.TTransport)
+}
+
+func (r *RichTransport) WriteByte(c byte) error {
+ return writeByte(r.TTransport, c)
+}
+
+func (r *RichTransport) WriteString(s string) (n int, err error) {
+ return r.Write([]byte(s))
+}
+
+func (r *RichTransport) RemainingBytes() (num_bytes uint64) {
+ return r.TTransport.RemainingBytes()
+}
+
+func readByte(r io.Reader) (c byte, err error) {
+ v := [1]byte{0}
+ n, err := r.Read(v[0:1])
+ if n > 0 && (err == nil || err == io.EOF) {
+ return v[0], nil
+ }
+ if n > 0 && err != nil {
+ return v[0], err
+ }
+ if err != nil {
+ return 0, err
+ }
+ return v[0], nil
+}
+
+func writeByte(w io.Writer, c byte) error {
+ v := [1]byte{c}
+ _, err := w.Write(v[0:1])
+ return err
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/rich_transport_test.go b/src/jaegertracing/thrift/lib/go/thrift/rich_transport_test.go
new file mode 100644
index 000000000..25c3fd5aa
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/rich_transport_test.go
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "reflect"
+ "testing"
+)
+
+func TestEnsureTransportsAreRich(t *testing.T) {
+ buf := bytes.NewBuffer(make([]byte, 0, 1024))
+
+ transports := []TTransportFactory{
+ NewTMemoryBufferTransportFactory(1024),
+ NewStreamTransportFactory(buf, buf, true),
+ NewTFramedTransportFactory(NewTMemoryBufferTransportFactory(1024)),
+ NewTHttpPostClientTransportFactory("http://127.0.0.1"),
+ }
+ for _, tf := range transports {
+ trans, err := tf.GetTransport(nil)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ _, ok := trans.(TRichTransport)
+ if !ok {
+ t.Errorf("Transport %s does not implement TRichTransport interface", reflect.ValueOf(trans))
+ }
+ }
+}
+
+// TestReadByte tests whether readByte handles error cases correctly.
+func TestReadByte(t *testing.T) {
+ for i, test := range readByteTests {
+ v, err := readByte(test.r)
+ if v != test.v {
+ t.Fatalf("TestReadByte %d: value differs. Expected %d, got %d", i, test.v, test.r.v)
+ }
+ if err != test.err {
+ t.Fatalf("TestReadByte %d: error differs. Expected %s, got %s", i, test.err, test.r.err)
+ }
+ }
+}
+
+var someError = errors.New("Some error")
+var readByteTests = []struct {
+ r *mockReader
+ v byte
+ err error
+}{
+ {&mockReader{0, 55, io.EOF}, 0, io.EOF}, // reader sends EOF w/o data
+ {&mockReader{0, 55, someError}, 0, someError}, // reader sends some other error
+ {&mockReader{1, 55, nil}, 55, nil}, // reader sends data w/o error
+ {&mockReader{1, 55, io.EOF}, 55, nil}, // reader sends data with EOF
+ {&mockReader{1, 55, someError}, 55, someError}, // reader sends data withsome error
+}
+
+type mockReader struct {
+ n int
+ v byte
+ err error
+}
+
+func (r *mockReader) Read(p []byte) (n int, err error) {
+ if r.n > 0 {
+ p[0] = r.v
+ }
+ return r.n, r.err
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/serializer.go b/src/jaegertracing/thrift/lib/go/thrift/serializer.go
new file mode 100644
index 000000000..1ff4d3754
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/serializer.go
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+)
+
+type TSerializer struct {
+ Transport *TMemoryBuffer
+ Protocol TProtocol
+}
+
+type TStruct interface {
+ Write(p TProtocol) error
+ Read(p TProtocol) error
+}
+
+func NewTSerializer() *TSerializer {
+ transport := NewTMemoryBufferLen(1024)
+ protocol := NewTBinaryProtocolFactoryDefault().GetProtocol(transport)
+
+ return &TSerializer{
+ transport,
+ protocol}
+}
+
+func (t *TSerializer) WriteString(ctx context.Context, msg TStruct) (s string, err error) {
+ t.Transport.Reset()
+
+ if err = msg.Write(t.Protocol); err != nil {
+ return
+ }
+
+ if err = t.Protocol.Flush(ctx); err != nil {
+ return
+ }
+ if err = t.Transport.Flush(ctx); err != nil {
+ return
+ }
+
+ return t.Transport.String(), nil
+}
+
+func (t *TSerializer) Write(ctx context.Context, msg TStruct) (b []byte, err error) {
+ t.Transport.Reset()
+
+ if err = msg.Write(t.Protocol); err != nil {
+ return
+ }
+
+ if err = t.Protocol.Flush(ctx); err != nil {
+ return
+ }
+
+ if err = t.Transport.Flush(ctx); err != nil {
+ return
+ }
+
+ b = append(b, t.Transport.Bytes()...)
+ return
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/serializer_test.go b/src/jaegertracing/thrift/lib/go/thrift/serializer_test.go
new file mode 100644
index 000000000..32227ef49
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/serializer_test.go
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "testing"
+)
+
+type ProtocolFactory interface {
+ GetProtocol(t TTransport) TProtocol
+}
+
+func compareStructs(m, m1 MyTestStruct) (bool, error) {
+ switch {
+ case m.On != m1.On:
+ return false, errors.New("Boolean not equal")
+ case m.B != m1.B:
+ return false, errors.New("Byte not equal")
+ case m.Int16 != m1.Int16:
+ return false, errors.New("Int16 not equal")
+ case m.Int32 != m1.Int32:
+ return false, errors.New("Int32 not equal")
+ case m.Int64 != m1.Int64:
+ return false, errors.New("Int64 not equal")
+ case m.D != m1.D:
+ return false, errors.New("Double not equal")
+ case m.St != m1.St:
+ return false, errors.New("String not equal")
+
+ case len(m.Bin) != len(m1.Bin):
+ return false, errors.New("Binary size not equal")
+ case len(m.Bin) == len(m1.Bin):
+ for i := range m.Bin {
+ if m.Bin[i] != m1.Bin[i] {
+ return false, errors.New("Binary not equal")
+ }
+ }
+ case len(m.StringMap) != len(m1.StringMap):
+ return false, errors.New("StringMap size not equal")
+ case len(m.StringList) != len(m1.StringList):
+ return false, errors.New("StringList size not equal")
+ case len(m.StringSet) != len(m1.StringSet):
+ return false, errors.New("StringSet size not equal")
+
+ case m.E != m1.E:
+ return false, errors.New("MyTestEnum not equal")
+
+ default:
+ return true, nil
+
+ }
+ return true, nil
+}
+
+func ProtocolTest1(test *testing.T, pf ProtocolFactory) (bool, error) {
+ t := NewTSerializer()
+ t.Protocol = pf.GetProtocol(t.Transport)
+ var m = MyTestStruct{}
+ m.On = true
+ m.B = int8(0)
+ m.Int16 = 1
+ m.Int32 = 2
+ m.Int64 = 3
+ m.D = 4.1
+ m.St = "Test"
+ m.Bin = make([]byte, 10)
+ m.StringMap = make(map[string]string, 5)
+ m.StringList = make([]string, 5)
+ m.StringSet = make(map[string]struct{}, 5)
+ m.E = 2
+
+ s, err := t.WriteString(context.Background(), &m)
+ if err != nil {
+ return false, errors.New(fmt.Sprintf("Unable to Serialize struct\n\t %s", err))
+ }
+
+ t1 := NewTDeserializer()
+ t1.Protocol = pf.GetProtocol(t1.Transport)
+ var m1 = MyTestStruct{}
+ if err = t1.ReadString(&m1, s); err != nil {
+ return false, errors.New(fmt.Sprintf("Unable to Deserialize struct\n\t %s", err))
+
+ }
+
+ return compareStructs(m, m1)
+
+}
+
+func ProtocolTest2(test *testing.T, pf ProtocolFactory) (bool, error) {
+ t := NewTSerializer()
+ t.Protocol = pf.GetProtocol(t.Transport)
+ var m = MyTestStruct{}
+ m.On = false
+ m.B = int8(0)
+ m.Int16 = 1
+ m.Int32 = 2
+ m.Int64 = 3
+ m.D = 4.1
+ m.St = "Test"
+ m.Bin = make([]byte, 10)
+ m.StringMap = make(map[string]string, 5)
+ m.StringList = make([]string, 5)
+ m.StringSet = make(map[string]struct{}, 5)
+ m.E = 2
+
+ s, err := t.WriteString(context.Background(), &m)
+ if err != nil {
+ return false, errors.New(fmt.Sprintf("Unable to Serialize struct\n\t %s", err))
+
+ }
+
+ t1 := NewTDeserializer()
+ t1.Protocol = pf.GetProtocol(t1.Transport)
+ var m1 = MyTestStruct{}
+ if err = t1.ReadString(&m1, s); err != nil {
+ return false, errors.New(fmt.Sprintf("Unable to Deserialize struct\n\t %s", err))
+
+ }
+
+ return compareStructs(m, m1)
+
+}
+
+func TestSerializer(t *testing.T) {
+
+ var protocol_factories map[string]ProtocolFactory
+ protocol_factories = make(map[string]ProtocolFactory)
+ protocol_factories["Binary"] = NewTBinaryProtocolFactoryDefault()
+ protocol_factories["Compact"] = NewTCompactProtocolFactory()
+ //protocol_factories["SimpleJSON"] = NewTSimpleJSONProtocolFactory() - write only, can't be read back by design
+ protocol_factories["JSON"] = NewTJSONProtocolFactory()
+
+ var tests map[string]func(*testing.T, ProtocolFactory) (bool, error)
+ tests = make(map[string]func(*testing.T, ProtocolFactory) (bool, error))
+ tests["Test 1"] = ProtocolTest1
+ tests["Test 2"] = ProtocolTest2
+ //tests["Test 3"] = ProtocolTest3 // Example of how to add additional tests
+
+ for name, pf := range protocol_factories {
+
+ for test, f := range tests {
+
+ if s, err := f(t, pf); !s || err != nil {
+ t.Errorf("%s Failed for %s protocol\n\t %s", test, name, err)
+ }
+
+ }
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/serializer_types_test.go b/src/jaegertracing/thrift/lib/go/thrift/serializer_types_test.go
new file mode 100644
index 000000000..e5472bbff
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/serializer_types_test.go
@@ -0,0 +1,633 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+// Autogenerated by Thrift Compiler (FIXME)
+// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+
+/* THE FOLLOWING THRIFT FILE WAS USED TO CREATE THIS
+
+enum MyTestEnum {
+ FIRST = 1,
+ SECOND = 2,
+ THIRD = 3,
+ FOURTH = 4,
+}
+
+struct MyTestStruct {
+ 1: bool on,
+ 2: byte b,
+ 3: i16 int16,
+ 4: i32 int32,
+ 5: i64 int64,
+ 6: double d,
+ 7: string st,
+ 8: binary bin,
+ 9: map<string, string> stringMap,
+ 10: list<string> stringList,
+ 11: set<string> stringSet,
+ 12: MyTestEnum e,
+}
+*/
+
+import (
+ "fmt"
+)
+
+// (needed to ensure safety because of naive import list construction.)
+var _ = ZERO
+var _ = fmt.Printf
+
+var GoUnusedProtection__ int
+
+type MyTestEnum int64
+
+const (
+ MyTestEnum_FIRST MyTestEnum = 1
+ MyTestEnum_SECOND MyTestEnum = 2
+ MyTestEnum_THIRD MyTestEnum = 3
+ MyTestEnum_FOURTH MyTestEnum = 4
+)
+
+func (p MyTestEnum) String() string {
+ switch p {
+ case MyTestEnum_FIRST:
+ return "FIRST"
+ case MyTestEnum_SECOND:
+ return "SECOND"
+ case MyTestEnum_THIRD:
+ return "THIRD"
+ case MyTestEnum_FOURTH:
+ return "FOURTH"
+ }
+ return "<UNSET>"
+}
+
+func MyTestEnumFromString(s string) (MyTestEnum, error) {
+ switch s {
+ case "FIRST":
+ return MyTestEnum_FIRST, nil
+ case "SECOND":
+ return MyTestEnum_SECOND, nil
+ case "THIRD":
+ return MyTestEnum_THIRD, nil
+ case "FOURTH":
+ return MyTestEnum_FOURTH, nil
+ }
+ return MyTestEnum(0), fmt.Errorf("not a valid MyTestEnum string")
+}
+
+func MyTestEnumPtr(v MyTestEnum) *MyTestEnum { return &v }
+
+type MyTestStruct struct {
+ On bool `thrift:"on,1" json:"on"`
+ B int8 `thrift:"b,2" json:"b"`
+ Int16 int16 `thrift:"int16,3" json:"int16"`
+ Int32 int32 `thrift:"int32,4" json:"int32"`
+ Int64 int64 `thrift:"int64,5" json:"int64"`
+ D float64 `thrift:"d,6" json:"d"`
+ St string `thrift:"st,7" json:"st"`
+ Bin []byte `thrift:"bin,8" json:"bin"`
+ StringMap map[string]string `thrift:"stringMap,9" json:"stringMap"`
+ StringList []string `thrift:"stringList,10" json:"stringList"`
+ StringSet map[string]struct{} `thrift:"stringSet,11" json:"stringSet"`
+ E MyTestEnum `thrift:"e,12" json:"e"`
+}
+
+func NewMyTestStruct() *MyTestStruct {
+ return &MyTestStruct{}
+}
+
+func (p *MyTestStruct) GetOn() bool {
+ return p.On
+}
+
+func (p *MyTestStruct) GetB() int8 {
+ return p.B
+}
+
+func (p *MyTestStruct) GetInt16() int16 {
+ return p.Int16
+}
+
+func (p *MyTestStruct) GetInt32() int32 {
+ return p.Int32
+}
+
+func (p *MyTestStruct) GetInt64() int64 {
+ return p.Int64
+}
+
+func (p *MyTestStruct) GetD() float64 {
+ return p.D
+}
+
+func (p *MyTestStruct) GetSt() string {
+ return p.St
+}
+
+func (p *MyTestStruct) GetBin() []byte {
+ return p.Bin
+}
+
+func (p *MyTestStruct) GetStringMap() map[string]string {
+ return p.StringMap
+}
+
+func (p *MyTestStruct) GetStringList() []string {
+ return p.StringList
+}
+
+func (p *MyTestStruct) GetStringSet() map[string]struct{} {
+ return p.StringSet
+}
+
+func (p *MyTestStruct) GetE() MyTestEnum {
+ return p.E
+}
+func (p *MyTestStruct) Read(iprot TProtocol) error {
+ if _, err := iprot.ReadStructBegin(); err != nil {
+ return PrependError(fmt.Sprintf("%T read error: ", p), err)
+ }
+ for {
+ _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()
+ if err != nil {
+ return PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err)
+ }
+ if fieldTypeId == STOP {
+ break
+ }
+ switch fieldId {
+ case 1:
+ if err := p.readField1(iprot); err != nil {
+ return err
+ }
+ case 2:
+ if err := p.readField2(iprot); err != nil {
+ return err
+ }
+ case 3:
+ if err := p.readField3(iprot); err != nil {
+ return err
+ }
+ case 4:
+ if err := p.readField4(iprot); err != nil {
+ return err
+ }
+ case 5:
+ if err := p.readField5(iprot); err != nil {
+ return err
+ }
+ case 6:
+ if err := p.readField6(iprot); err != nil {
+ return err
+ }
+ case 7:
+ if err := p.readField7(iprot); err != nil {
+ return err
+ }
+ case 8:
+ if err := p.readField8(iprot); err != nil {
+ return err
+ }
+ case 9:
+ if err := p.readField9(iprot); err != nil {
+ return err
+ }
+ case 10:
+ if err := p.readField10(iprot); err != nil {
+ return err
+ }
+ case 11:
+ if err := p.readField11(iprot); err != nil {
+ return err
+ }
+ case 12:
+ if err := p.readField12(iprot); err != nil {
+ return err
+ }
+ default:
+ if err := iprot.Skip(fieldTypeId); err != nil {
+ return err
+ }
+ }
+ if err := iprot.ReadFieldEnd(); err != nil {
+ return err
+ }
+ }
+ if err := iprot.ReadStructEnd(); err != nil {
+ return PrependError(fmt.Sprintf("%T read struct end error: ", p), err)
+ }
+ return nil
+}
+
+func (p *MyTestStruct) readField1(iprot TProtocol) error {
+ if v, err := iprot.ReadBool(); err != nil {
+ return PrependError("error reading field 1: ", err)
+ } else {
+ p.On = v
+ }
+ return nil
+}
+
+func (p *MyTestStruct) readField2(iprot TProtocol) error {
+ if v, err := iprot.ReadByte(); err != nil {
+ return PrependError("error reading field 2: ", err)
+ } else {
+ temp := int8(v)
+ p.B = temp
+ }
+ return nil
+}
+
+func (p *MyTestStruct) readField3(iprot TProtocol) error {
+ if v, err := iprot.ReadI16(); err != nil {
+ return PrependError("error reading field 3: ", err)
+ } else {
+ p.Int16 = v
+ }
+ return nil
+}
+
+func (p *MyTestStruct) readField4(iprot TProtocol) error {
+ if v, err := iprot.ReadI32(); err != nil {
+ return PrependError("error reading field 4: ", err)
+ } else {
+ p.Int32 = v
+ }
+ return nil
+}
+
+func (p *MyTestStruct) readField5(iprot TProtocol) error {
+ if v, err := iprot.ReadI64(); err != nil {
+ return PrependError("error reading field 5: ", err)
+ } else {
+ p.Int64 = v
+ }
+ return nil
+}
+
+func (p *MyTestStruct) readField6(iprot TProtocol) error {
+ if v, err := iprot.ReadDouble(); err != nil {
+ return PrependError("error reading field 6: ", err)
+ } else {
+ p.D = v
+ }
+ return nil
+}
+
+func (p *MyTestStruct) readField7(iprot TProtocol) error {
+ if v, err := iprot.ReadString(); err != nil {
+ return PrependError("error reading field 7: ", err)
+ } else {
+ p.St = v
+ }
+ return nil
+}
+
+func (p *MyTestStruct) readField8(iprot TProtocol) error {
+ if v, err := iprot.ReadBinary(); err != nil {
+ return PrependError("error reading field 8: ", err)
+ } else {
+ p.Bin = v
+ }
+ return nil
+}
+
+func (p *MyTestStruct) readField9(iprot TProtocol) error {
+ _, _, size, err := iprot.ReadMapBegin()
+ if err != nil {
+ return PrependError("error reading map begin: ", err)
+ }
+ tMap := make(map[string]string, size)
+ p.StringMap = tMap
+ for i := 0; i < size; i++ {
+ var _key0 string
+ if v, err := iprot.ReadString(); err != nil {
+ return PrependError("error reading field 0: ", err)
+ } else {
+ _key0 = v
+ }
+ var _val1 string
+ if v, err := iprot.ReadString(); err != nil {
+ return PrependError("error reading field 0: ", err)
+ } else {
+ _val1 = v
+ }
+ p.StringMap[_key0] = _val1
+ }
+ if err := iprot.ReadMapEnd(); err != nil {
+ return PrependError("error reading map end: ", err)
+ }
+ return nil
+}
+
+func (p *MyTestStruct) readField10(iprot TProtocol) error {
+ _, size, err := iprot.ReadListBegin()
+ if err != nil {
+ return PrependError("error reading list begin: ", err)
+ }
+ tSlice := make([]string, 0, size)
+ p.StringList = tSlice
+ for i := 0; i < size; i++ {
+ var _elem2 string
+ if v, err := iprot.ReadString(); err != nil {
+ return PrependError("error reading field 0: ", err)
+ } else {
+ _elem2 = v
+ }
+ p.StringList = append(p.StringList, _elem2)
+ }
+ if err := iprot.ReadListEnd(); err != nil {
+ return PrependError("error reading list end: ", err)
+ }
+ return nil
+}
+
+func (p *MyTestStruct) readField11(iprot TProtocol) error {
+ _, size, err := iprot.ReadSetBegin()
+ if err != nil {
+ return PrependError("error reading set begin: ", err)
+ }
+ tSet := make(map[string]struct{}, size)
+ p.StringSet = tSet
+ for i := 0; i < size; i++ {
+ var _elem3 string
+ if v, err := iprot.ReadString(); err != nil {
+ return PrependError("error reading field 0: ", err)
+ } else {
+ _elem3 = v
+ }
+ p.StringSet[_elem3] = struct{}{}
+ }
+ if err := iprot.ReadSetEnd(); err != nil {
+ return PrependError("error reading set end: ", err)
+ }
+ return nil
+}
+
+func (p *MyTestStruct) readField12(iprot TProtocol) error {
+ if v, err := iprot.ReadI32(); err != nil {
+ return PrependError("error reading field 12: ", err)
+ } else {
+ temp := MyTestEnum(v)
+ p.E = temp
+ }
+ return nil
+}
+
+func (p *MyTestStruct) Write(oprot TProtocol) error {
+ if err := oprot.WriteStructBegin("MyTestStruct"); err != nil {
+ return PrependError(fmt.Sprintf("%T write struct begin error: ", p), err)
+ }
+ if err := p.writeField1(oprot); err != nil {
+ return err
+ }
+ if err := p.writeField2(oprot); err != nil {
+ return err
+ }
+ if err := p.writeField3(oprot); err != nil {
+ return err
+ }
+ if err := p.writeField4(oprot); err != nil {
+ return err
+ }
+ if err := p.writeField5(oprot); err != nil {
+ return err
+ }
+ if err := p.writeField6(oprot); err != nil {
+ return err
+ }
+ if err := p.writeField7(oprot); err != nil {
+ return err
+ }
+ if err := p.writeField8(oprot); err != nil {
+ return err
+ }
+ if err := p.writeField9(oprot); err != nil {
+ return err
+ }
+ if err := p.writeField10(oprot); err != nil {
+ return err
+ }
+ if err := p.writeField11(oprot); err != nil {
+ return err
+ }
+ if err := p.writeField12(oprot); err != nil {
+ return err
+ }
+ if err := oprot.WriteFieldStop(); err != nil {
+ return PrependError("write field stop error: ", err)
+ }
+ if err := oprot.WriteStructEnd(); err != nil {
+ return PrependError("write struct stop error: ", err)
+ }
+ return nil
+}
+
+func (p *MyTestStruct) writeField1(oprot TProtocol) (err error) {
+ if err := oprot.WriteFieldBegin("on", BOOL, 1); err != nil {
+ return PrependError(fmt.Sprintf("%T write field begin error 1:on: ", p), err)
+ }
+ if err := oprot.WriteBool(bool(p.On)); err != nil {
+ return PrependError(fmt.Sprintf("%T.on (1) field write error: ", p), err)
+ }
+ if err := oprot.WriteFieldEnd(); err != nil {
+ return PrependError(fmt.Sprintf("%T write field end error 1:on: ", p), err)
+ }
+ return err
+}
+
+func (p *MyTestStruct) writeField2(oprot TProtocol) (err error) {
+ if err := oprot.WriteFieldBegin("b", BYTE, 2); err != nil {
+ return PrependError(fmt.Sprintf("%T write field begin error 2:b: ", p), err)
+ }
+ if err := oprot.WriteByte(int8(p.B)); err != nil {
+ return PrependError(fmt.Sprintf("%T.b (2) field write error: ", p), err)
+ }
+ if err := oprot.WriteFieldEnd(); err != nil {
+ return PrependError(fmt.Sprintf("%T write field end error 2:b: ", p), err)
+ }
+ return err
+}
+
+func (p *MyTestStruct) writeField3(oprot TProtocol) (err error) {
+ if err := oprot.WriteFieldBegin("int16", I16, 3); err != nil {
+ return PrependError(fmt.Sprintf("%T write field begin error 3:int16: ", p), err)
+ }
+ if err := oprot.WriteI16(int16(p.Int16)); err != nil {
+ return PrependError(fmt.Sprintf("%T.int16 (3) field write error: ", p), err)
+ }
+ if err := oprot.WriteFieldEnd(); err != nil {
+ return PrependError(fmt.Sprintf("%T write field end error 3:int16: ", p), err)
+ }
+ return err
+}
+
+func (p *MyTestStruct) writeField4(oprot TProtocol) (err error) {
+ if err := oprot.WriteFieldBegin("int32", I32, 4); err != nil {
+ return PrependError(fmt.Sprintf("%T write field begin error 4:int32: ", p), err)
+ }
+ if err := oprot.WriteI32(int32(p.Int32)); err != nil {
+ return PrependError(fmt.Sprintf("%T.int32 (4) field write error: ", p), err)
+ }
+ if err := oprot.WriteFieldEnd(); err != nil {
+ return PrependError(fmt.Sprintf("%T write field end error 4:int32: ", p), err)
+ }
+ return err
+}
+
+func (p *MyTestStruct) writeField5(oprot TProtocol) (err error) {
+ if err := oprot.WriteFieldBegin("int64", I64, 5); err != nil {
+ return PrependError(fmt.Sprintf("%T write field begin error 5:int64: ", p), err)
+ }
+ if err := oprot.WriteI64(int64(p.Int64)); err != nil {
+ return PrependError(fmt.Sprintf("%T.int64 (5) field write error: ", p), err)
+ }
+ if err := oprot.WriteFieldEnd(); err != nil {
+ return PrependError(fmt.Sprintf("%T write field end error 5:int64: ", p), err)
+ }
+ return err
+}
+
+func (p *MyTestStruct) writeField6(oprot TProtocol) (err error) {
+ if err := oprot.WriteFieldBegin("d", DOUBLE, 6); err != nil {
+ return PrependError(fmt.Sprintf("%T write field begin error 6:d: ", p), err)
+ }
+ if err := oprot.WriteDouble(float64(p.D)); err != nil {
+ return PrependError(fmt.Sprintf("%T.d (6) field write error: ", p), err)
+ }
+ if err := oprot.WriteFieldEnd(); err != nil {
+ return PrependError(fmt.Sprintf("%T write field end error 6:d: ", p), err)
+ }
+ return err
+}
+
+func (p *MyTestStruct) writeField7(oprot TProtocol) (err error) {
+ if err := oprot.WriteFieldBegin("st", STRING, 7); err != nil {
+ return PrependError(fmt.Sprintf("%T write field begin error 7:st: ", p), err)
+ }
+ if err := oprot.WriteString(string(p.St)); err != nil {
+ return PrependError(fmt.Sprintf("%T.st (7) field write error: ", p), err)
+ }
+ if err := oprot.WriteFieldEnd(); err != nil {
+ return PrependError(fmt.Sprintf("%T write field end error 7:st: ", p), err)
+ }
+ return err
+}
+
+func (p *MyTestStruct) writeField8(oprot TProtocol) (err error) {
+ if err := oprot.WriteFieldBegin("bin", STRING, 8); err != nil {
+ return PrependError(fmt.Sprintf("%T write field begin error 8:bin: ", p), err)
+ }
+ if err := oprot.WriteBinary(p.Bin); err != nil {
+ return PrependError(fmt.Sprintf("%T.bin (8) field write error: ", p), err)
+ }
+ if err := oprot.WriteFieldEnd(); err != nil {
+ return PrependError(fmt.Sprintf("%T write field end error 8:bin: ", p), err)
+ }
+ return err
+}
+
+func (p *MyTestStruct) writeField9(oprot TProtocol) (err error) {
+ if err := oprot.WriteFieldBegin("stringMap", MAP, 9); err != nil {
+ return PrependError(fmt.Sprintf("%T write field begin error 9:stringMap: ", p), err)
+ }
+ if err := oprot.WriteMapBegin(STRING, STRING, len(p.StringMap)); err != nil {
+ return PrependError("error writing map begin: ", err)
+ }
+ for k, v := range p.StringMap {
+ if err := oprot.WriteString(string(k)); err != nil {
+ return PrependError(fmt.Sprintf("%T. (0) field write error: ", p), err)
+ }
+ if err := oprot.WriteString(string(v)); err != nil {
+ return PrependError(fmt.Sprintf("%T. (0) field write error: ", p), err)
+ }
+ }
+ if err := oprot.WriteMapEnd(); err != nil {
+ return PrependError("error writing map end: ", err)
+ }
+ if err := oprot.WriteFieldEnd(); err != nil {
+ return PrependError(fmt.Sprintf("%T write field end error 9:stringMap: ", p), err)
+ }
+ return err
+}
+
+func (p *MyTestStruct) writeField10(oprot TProtocol) (err error) {
+ if err := oprot.WriteFieldBegin("stringList", LIST, 10); err != nil {
+ return PrependError(fmt.Sprintf("%T write field begin error 10:stringList: ", p), err)
+ }
+ if err := oprot.WriteListBegin(STRING, len(p.StringList)); err != nil {
+ return PrependError("error writing list begin: ", err)
+ }
+ for _, v := range p.StringList {
+ if err := oprot.WriteString(string(v)); err != nil {
+ return PrependError(fmt.Sprintf("%T. (0) field write error: ", p), err)
+ }
+ }
+ if err := oprot.WriteListEnd(); err != nil {
+ return PrependError("error writing list end: ", err)
+ }
+ if err := oprot.WriteFieldEnd(); err != nil {
+ return PrependError(fmt.Sprintf("%T write field end error 10:stringList: ", p), err)
+ }
+ return err
+}
+
+func (p *MyTestStruct) writeField11(oprot TProtocol) (err error) {
+ if err := oprot.WriteFieldBegin("stringSet", SET, 11); err != nil {
+ return PrependError(fmt.Sprintf("%T write field begin error 11:stringSet: ", p), err)
+ }
+ if err := oprot.WriteSetBegin(STRING, len(p.StringSet)); err != nil {
+ return PrependError("error writing set begin: ", err)
+ }
+ for v := range p.StringSet {
+ if err := oprot.WriteString(string(v)); err != nil {
+ return PrependError(fmt.Sprintf("%T. (0) field write error: ", p), err)
+ }
+ }
+ if err := oprot.WriteSetEnd(); err != nil {
+ return PrependError("error writing set end: ", err)
+ }
+ if err := oprot.WriteFieldEnd(); err != nil {
+ return PrependError(fmt.Sprintf("%T write field end error 11:stringSet: ", p), err)
+ }
+ return err
+}
+
+func (p *MyTestStruct) writeField12(oprot TProtocol) (err error) {
+ if err := oprot.WriteFieldBegin("e", I32, 12); err != nil {
+ return PrependError(fmt.Sprintf("%T write field begin error 12:e: ", p), err)
+ }
+ if err := oprot.WriteI32(int32(p.E)); err != nil {
+ return PrependError(fmt.Sprintf("%T.e (12) field write error: ", p), err)
+ }
+ if err := oprot.WriteFieldEnd(); err != nil {
+ return PrependError(fmt.Sprintf("%T write field end error 12:e: ", p), err)
+ }
+ return err
+}
+
+func (p *MyTestStruct) String() string {
+ if p == nil {
+ return "<nil>"
+ }
+ return fmt.Sprintf("MyTestStruct(%+v)", *p)
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/server.go b/src/jaegertracing/thrift/lib/go/thrift/server.go
new file mode 100644
index 000000000..f813fa353
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/server.go
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+type TServer interface {
+ ProcessorFactory() TProcessorFactory
+ ServerTransport() TServerTransport
+ InputTransportFactory() TTransportFactory
+ OutputTransportFactory() TTransportFactory
+ InputProtocolFactory() TProtocolFactory
+ OutputProtocolFactory() TProtocolFactory
+
+ // Starts the server
+ Serve() error
+ // Stops the server. This is optional on a per-implementation basis. Not
+ // all servers are required to be cleanly stoppable.
+ Stop() error
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/server_socket.go b/src/jaegertracing/thrift/lib/go/thrift/server_socket.go
new file mode 100644
index 000000000..7dd24ae36
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/server_socket.go
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "net"
+ "sync"
+ "time"
+)
+
+type TServerSocket struct {
+ listener net.Listener
+ addr net.Addr
+ clientTimeout time.Duration
+
+ // Protects the interrupted value to make it thread safe.
+ mu sync.RWMutex
+ interrupted bool
+}
+
+func NewTServerSocket(listenAddr string) (*TServerSocket, error) {
+ return NewTServerSocketTimeout(listenAddr, 0)
+}
+
+func NewTServerSocketTimeout(listenAddr string, clientTimeout time.Duration) (*TServerSocket, error) {
+ addr, err := net.ResolveTCPAddr("tcp", listenAddr)
+ if err != nil {
+ return nil, err
+ }
+ return &TServerSocket{addr: addr, clientTimeout: clientTimeout}, nil
+}
+
+// Creates a TServerSocket from a net.Addr
+func NewTServerSocketFromAddrTimeout(addr net.Addr, clientTimeout time.Duration) *TServerSocket {
+ return &TServerSocket{addr: addr, clientTimeout: clientTimeout}
+}
+
+func (p *TServerSocket) Listen() error {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ if p.IsListening() {
+ return nil
+ }
+ l, err := net.Listen(p.addr.Network(), p.addr.String())
+ if err != nil {
+ return err
+ }
+ p.listener = l
+ return nil
+}
+
+func (p *TServerSocket) Accept() (TTransport, error) {
+ p.mu.RLock()
+ interrupted := p.interrupted
+ p.mu.RUnlock()
+
+ if interrupted {
+ return nil, errTransportInterrupted
+ }
+
+ p.mu.Lock()
+ listener := p.listener
+ p.mu.Unlock()
+ if listener == nil {
+ return nil, NewTTransportException(NOT_OPEN, "No underlying server socket")
+ }
+
+ conn, err := listener.Accept()
+ if err != nil {
+ return nil, NewTTransportExceptionFromError(err)
+ }
+ return NewTSocketFromConnTimeout(conn, p.clientTimeout), nil
+}
+
+// Checks whether the socket is listening.
+func (p *TServerSocket) IsListening() bool {
+ return p.listener != nil
+}
+
+// Connects the socket, creating a new socket object if necessary.
+func (p *TServerSocket) Open() error {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ if p.IsListening() {
+ return NewTTransportException(ALREADY_OPEN, "Server socket already open")
+ }
+ if l, err := net.Listen(p.addr.Network(), p.addr.String()); err != nil {
+ return err
+ } else {
+ p.listener = l
+ }
+ return nil
+}
+
+func (p *TServerSocket) Addr() net.Addr {
+ if p.listener != nil {
+ return p.listener.Addr()
+ }
+ return p.addr
+}
+
+func (p *TServerSocket) Close() error {
+ var err error
+ p.mu.Lock()
+ if p.IsListening() {
+ err = p.listener.Close()
+ p.listener = nil
+ }
+ p.mu.Unlock()
+ return err
+}
+
+func (p *TServerSocket) Interrupt() error {
+ p.mu.Lock()
+ p.interrupted = true
+ p.mu.Unlock()
+ p.Close()
+
+ return nil
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/server_socket_test.go b/src/jaegertracing/thrift/lib/go/thrift/server_socket_test.go
new file mode 100644
index 000000000..f1e1983a9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/server_socket_test.go
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "fmt"
+ "testing"
+)
+
+func TestSocketIsntListeningAfterInterrupt(t *testing.T) {
+ host := "127.0.0.1"
+ port := 9090
+ addr := fmt.Sprintf("%s:%d", host, port)
+
+ socket := CreateServerSocket(t, addr)
+ socket.Listen()
+ socket.Interrupt()
+
+ newSocket := CreateServerSocket(t, addr)
+ err := newSocket.Listen()
+ defer newSocket.Interrupt()
+ if err != nil {
+ t.Fatalf("Failed to rebinds: %s", err)
+ }
+}
+
+func TestSocketConcurrency(t *testing.T) {
+ host := "127.0.0.1"
+ port := 9090
+ addr := fmt.Sprintf("%s:%d", host, port)
+
+ socket := CreateServerSocket(t, addr)
+ go func() { socket.Listen() }()
+ go func() { socket.Interrupt() }()
+}
+
+func CreateServerSocket(t *testing.T, addr string) *TServerSocket {
+ socket, err := NewTServerSocket(addr)
+ if err != nil {
+ t.Fatalf("Failed to create server socket: %s", err)
+ }
+ return socket
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/server_test.go b/src/jaegertracing/thrift/lib/go/thrift/server_test.go
new file mode 100644
index 000000000..ffaf45702
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/server_test.go
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "testing"
+)
+
+func TestNothing(t *testing.T) {
+
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/server_transport.go b/src/jaegertracing/thrift/lib/go/thrift/server_transport.go
new file mode 100644
index 000000000..51c40b64a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/server_transport.go
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+// Server transport. Object which provides client transports.
+type TServerTransport interface {
+ Listen() error
+ Accept() (TTransport, error)
+ Close() error
+
+ // Optional method implementation. This signals to the server transport
+ // that it should break out of any accept() or listen() that it is currently
+ // blocked on. This method, if implemented, MUST be thread safe, as it may
+ // be called from a different thread context than the other TServerTransport
+ // methods.
+ Interrupt() error
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/simple_json_protocol.go b/src/jaegertracing/thrift/lib/go/thrift/simple_json_protocol.go
new file mode 100644
index 000000000..f5e0c05d1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/simple_json_protocol.go
@@ -0,0 +1,1338 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "io"
+ "math"
+ "strconv"
+)
+
+type _ParseContext int
+
+const (
+ _CONTEXT_IN_TOPLEVEL _ParseContext = 1
+ _CONTEXT_IN_LIST_FIRST _ParseContext = 2
+ _CONTEXT_IN_LIST _ParseContext = 3
+ _CONTEXT_IN_OBJECT_FIRST _ParseContext = 4
+ _CONTEXT_IN_OBJECT_NEXT_KEY _ParseContext = 5
+ _CONTEXT_IN_OBJECT_NEXT_VALUE _ParseContext = 6
+)
+
+func (p _ParseContext) String() string {
+ switch p {
+ case _CONTEXT_IN_TOPLEVEL:
+ return "TOPLEVEL"
+ case _CONTEXT_IN_LIST_FIRST:
+ return "LIST-FIRST"
+ case _CONTEXT_IN_LIST:
+ return "LIST"
+ case _CONTEXT_IN_OBJECT_FIRST:
+ return "OBJECT-FIRST"
+ case _CONTEXT_IN_OBJECT_NEXT_KEY:
+ return "OBJECT-NEXT-KEY"
+ case _CONTEXT_IN_OBJECT_NEXT_VALUE:
+ return "OBJECT-NEXT-VALUE"
+ }
+ return "UNKNOWN-PARSE-CONTEXT"
+}
+
+// Simple JSON protocol implementation for thrift.
+//
+// This protocol produces/consumes a simple output format
+// suitable for parsing by scripting languages. It should not be
+// confused with the full-featured TJSONProtocol.
+//
+type TSimpleJSONProtocol struct {
+ trans TTransport
+
+ parseContextStack []int
+ dumpContext []int
+
+ writer *bufio.Writer
+ reader *bufio.Reader
+}
+
+// Constructor
+func NewTSimpleJSONProtocol(t TTransport) *TSimpleJSONProtocol {
+ v := &TSimpleJSONProtocol{trans: t,
+ writer: bufio.NewWriter(t),
+ reader: bufio.NewReader(t),
+ }
+ v.parseContextStack = append(v.parseContextStack, int(_CONTEXT_IN_TOPLEVEL))
+ v.dumpContext = append(v.dumpContext, int(_CONTEXT_IN_TOPLEVEL))
+ return v
+}
+
+// Factory
+type TSimpleJSONProtocolFactory struct{}
+
+func (p *TSimpleJSONProtocolFactory) GetProtocol(trans TTransport) TProtocol {
+ return NewTSimpleJSONProtocol(trans)
+}
+
+func NewTSimpleJSONProtocolFactory() *TSimpleJSONProtocolFactory {
+ return &TSimpleJSONProtocolFactory{}
+}
+
+var (
+ JSON_COMMA []byte
+ JSON_COLON []byte
+ JSON_LBRACE []byte
+ JSON_RBRACE []byte
+ JSON_LBRACKET []byte
+ JSON_RBRACKET []byte
+ JSON_QUOTE byte
+ JSON_QUOTE_BYTES []byte
+ JSON_NULL []byte
+ JSON_TRUE []byte
+ JSON_FALSE []byte
+ JSON_INFINITY string
+ JSON_NEGATIVE_INFINITY string
+ JSON_NAN string
+ JSON_INFINITY_BYTES []byte
+ JSON_NEGATIVE_INFINITY_BYTES []byte
+ JSON_NAN_BYTES []byte
+ json_nonbase_map_elem_bytes []byte
+)
+
+func init() {
+ JSON_COMMA = []byte{','}
+ JSON_COLON = []byte{':'}
+ JSON_LBRACE = []byte{'{'}
+ JSON_RBRACE = []byte{'}'}
+ JSON_LBRACKET = []byte{'['}
+ JSON_RBRACKET = []byte{']'}
+ JSON_QUOTE = '"'
+ JSON_QUOTE_BYTES = []byte{'"'}
+ JSON_NULL = []byte{'n', 'u', 'l', 'l'}
+ JSON_TRUE = []byte{'t', 'r', 'u', 'e'}
+ JSON_FALSE = []byte{'f', 'a', 'l', 's', 'e'}
+ JSON_INFINITY = "Infinity"
+ JSON_NEGATIVE_INFINITY = "-Infinity"
+ JSON_NAN = "NaN"
+ JSON_INFINITY_BYTES = []byte{'I', 'n', 'f', 'i', 'n', 'i', 't', 'y'}
+ JSON_NEGATIVE_INFINITY_BYTES = []byte{'-', 'I', 'n', 'f', 'i', 'n', 'i', 't', 'y'}
+ JSON_NAN_BYTES = []byte{'N', 'a', 'N'}
+ json_nonbase_map_elem_bytes = []byte{']', ',', '['}
+}
+
+func jsonQuote(s string) string {
+ b, _ := json.Marshal(s)
+ s1 := string(b)
+ return s1
+}
+
+func jsonUnquote(s string) (string, bool) {
+ s1 := new(string)
+ err := json.Unmarshal([]byte(s), s1)
+ return *s1, err == nil
+}
+
+func mismatch(expected, actual string) error {
+ return fmt.Errorf("Expected '%s' but found '%s' while parsing JSON.", expected, actual)
+}
+
+func (p *TSimpleJSONProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) error {
+ p.resetContextStack() // THRIFT-3735
+ if e := p.OutputListBegin(); e != nil {
+ return e
+ }
+ if e := p.WriteString(name); e != nil {
+ return e
+ }
+ if e := p.WriteByte(int8(typeId)); e != nil {
+ return e
+ }
+ if e := p.WriteI32(seqId); e != nil {
+ return e
+ }
+ return nil
+}
+
+func (p *TSimpleJSONProtocol) WriteMessageEnd() error {
+ return p.OutputListEnd()
+}
+
+func (p *TSimpleJSONProtocol) WriteStructBegin(name string) error {
+ if e := p.OutputObjectBegin(); e != nil {
+ return e
+ }
+ return nil
+}
+
+func (p *TSimpleJSONProtocol) WriteStructEnd() error {
+ return p.OutputObjectEnd()
+}
+
+func (p *TSimpleJSONProtocol) WriteFieldBegin(name string, typeId TType, id int16) error {
+ if e := p.WriteString(name); e != nil {
+ return e
+ }
+ return nil
+}
+
+func (p *TSimpleJSONProtocol) WriteFieldEnd() error {
+ //return p.OutputListEnd()
+ return nil
+}
+
+func (p *TSimpleJSONProtocol) WriteFieldStop() error { return nil }
+
+func (p *TSimpleJSONProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error {
+ if e := p.OutputListBegin(); e != nil {
+ return e
+ }
+ if e := p.WriteByte(int8(keyType)); e != nil {
+ return e
+ }
+ if e := p.WriteByte(int8(valueType)); e != nil {
+ return e
+ }
+ return p.WriteI32(int32(size))
+}
+
+func (p *TSimpleJSONProtocol) WriteMapEnd() error {
+ return p.OutputListEnd()
+}
+
+func (p *TSimpleJSONProtocol) WriteListBegin(elemType TType, size int) error {
+ return p.OutputElemListBegin(elemType, size)
+}
+
+func (p *TSimpleJSONProtocol) WriteListEnd() error {
+ return p.OutputListEnd()
+}
+
+func (p *TSimpleJSONProtocol) WriteSetBegin(elemType TType, size int) error {
+ return p.OutputElemListBegin(elemType, size)
+}
+
+func (p *TSimpleJSONProtocol) WriteSetEnd() error {
+ return p.OutputListEnd()
+}
+
+func (p *TSimpleJSONProtocol) WriteBool(b bool) error {
+ return p.OutputBool(b)
+}
+
+func (p *TSimpleJSONProtocol) WriteByte(b int8) error {
+ return p.WriteI32(int32(b))
+}
+
+func (p *TSimpleJSONProtocol) WriteI16(v int16) error {
+ return p.WriteI32(int32(v))
+}
+
+func (p *TSimpleJSONProtocol) WriteI32(v int32) error {
+ return p.OutputI64(int64(v))
+}
+
+func (p *TSimpleJSONProtocol) WriteI64(v int64) error {
+ return p.OutputI64(int64(v))
+}
+
+func (p *TSimpleJSONProtocol) WriteDouble(v float64) error {
+ return p.OutputF64(v)
+}
+
+func (p *TSimpleJSONProtocol) WriteString(v string) error {
+ return p.OutputString(v)
+}
+
+func (p *TSimpleJSONProtocol) WriteBinary(v []byte) error {
+ // JSON library only takes in a string,
+ // not an arbitrary byte array, to ensure bytes are transmitted
+ // efficiently we must convert this into a valid JSON string
+ // therefore we use base64 encoding to avoid excessive escaping/quoting
+ if e := p.OutputPreValue(); e != nil {
+ return e
+ }
+ if _, e := p.write(JSON_QUOTE_BYTES); e != nil {
+ return NewTProtocolException(e)
+ }
+ writer := base64.NewEncoder(base64.StdEncoding, p.writer)
+ if _, e := writer.Write(v); e != nil {
+ p.writer.Reset(p.trans) // THRIFT-3735
+ return NewTProtocolException(e)
+ }
+ if e := writer.Close(); e != nil {
+ return NewTProtocolException(e)
+ }
+ if _, e := p.write(JSON_QUOTE_BYTES); e != nil {
+ return NewTProtocolException(e)
+ }
+ return p.OutputPostValue()
+}
+
+// Reading methods.
+func (p *TSimpleJSONProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) {
+ p.resetContextStack() // THRIFT-3735
+ if isNull, err := p.ParseListBegin(); isNull || err != nil {
+ return name, typeId, seqId, err
+ }
+ if name, err = p.ReadString(); err != nil {
+ return name, typeId, seqId, err
+ }
+ bTypeId, err := p.ReadByte()
+ typeId = TMessageType(bTypeId)
+ if err != nil {
+ return name, typeId, seqId, err
+ }
+ if seqId, err = p.ReadI32(); err != nil {
+ return name, typeId, seqId, err
+ }
+ return name, typeId, seqId, nil
+}
+
+func (p *TSimpleJSONProtocol) ReadMessageEnd() error {
+ return p.ParseListEnd()
+}
+
+func (p *TSimpleJSONProtocol) ReadStructBegin() (name string, err error) {
+ _, err = p.ParseObjectStart()
+ return "", err
+}
+
+func (p *TSimpleJSONProtocol) ReadStructEnd() error {
+ return p.ParseObjectEnd()
+}
+
+func (p *TSimpleJSONProtocol) ReadFieldBegin() (string, TType, int16, error) {
+ if err := p.ParsePreValue(); err != nil {
+ return "", STOP, 0, err
+ }
+ b, _ := p.reader.Peek(1)
+ if len(b) > 0 {
+ switch b[0] {
+ case JSON_RBRACE[0]:
+ return "", STOP, 0, nil
+ case JSON_QUOTE:
+ p.reader.ReadByte()
+ name, err := p.ParseStringBody()
+ // simplejson is not meant to be read back into thrift
+ // - see http://wiki.apache.org/thrift/ThriftUsageJava
+ // - use JSON instead
+ if err != nil {
+ return name, STOP, 0, err
+ }
+ return name, STOP, -1, p.ParsePostValue()
+ /*
+ if err = p.ParsePostValue(); err != nil {
+ return name, STOP, 0, err
+ }
+ if isNull, err := p.ParseListBegin(); isNull || err != nil {
+ return name, STOP, 0, err
+ }
+ bType, err := p.ReadByte()
+ thetype := TType(bType)
+ if err != nil {
+ return name, thetype, 0, err
+ }
+ id, err := p.ReadI16()
+ return name, thetype, id, err
+ */
+ }
+ e := fmt.Errorf("Expected \"}\" or '\"', but found: '%s'", string(b))
+ return "", STOP, 0, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ return "", STOP, 0, NewTProtocolException(io.EOF)
+}
+
+func (p *TSimpleJSONProtocol) ReadFieldEnd() error {
+ return nil
+ //return p.ParseListEnd()
+}
+
+func (p *TSimpleJSONProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, e error) {
+ if isNull, e := p.ParseListBegin(); isNull || e != nil {
+ return VOID, VOID, 0, e
+ }
+
+ // read keyType
+ bKeyType, e := p.ReadByte()
+ keyType = TType(bKeyType)
+ if e != nil {
+ return keyType, valueType, size, e
+ }
+
+ // read valueType
+ bValueType, e := p.ReadByte()
+ valueType = TType(bValueType)
+ if e != nil {
+ return keyType, valueType, size, e
+ }
+
+ // read size
+ iSize, err := p.ReadI64()
+ size = int(iSize)
+ return keyType, valueType, size, err
+}
+
+func (p *TSimpleJSONProtocol) ReadMapEnd() error {
+ return p.ParseListEnd()
+}
+
+func (p *TSimpleJSONProtocol) ReadListBegin() (elemType TType, size int, e error) {
+ return p.ParseElemListBegin()
+}
+
+func (p *TSimpleJSONProtocol) ReadListEnd() error {
+ return p.ParseListEnd()
+}
+
+func (p *TSimpleJSONProtocol) ReadSetBegin() (elemType TType, size int, e error) {
+ return p.ParseElemListBegin()
+}
+
+func (p *TSimpleJSONProtocol) ReadSetEnd() error {
+ return p.ParseListEnd()
+}
+
+func (p *TSimpleJSONProtocol) ReadBool() (bool, error) {
+ var value bool
+
+ if err := p.ParsePreValue(); err != nil {
+ return value, err
+ }
+ f, _ := p.reader.Peek(1)
+ if len(f) > 0 {
+ switch f[0] {
+ case JSON_TRUE[0]:
+ b := make([]byte, len(JSON_TRUE))
+ _, err := p.reader.Read(b)
+ if err != nil {
+ return false, NewTProtocolException(err)
+ }
+ if string(b) == string(JSON_TRUE) {
+ value = true
+ } else {
+ e := fmt.Errorf("Expected \"true\" but found: %s", string(b))
+ return value, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ break
+ case JSON_FALSE[0]:
+ b := make([]byte, len(JSON_FALSE))
+ _, err := p.reader.Read(b)
+ if err != nil {
+ return false, NewTProtocolException(err)
+ }
+ if string(b) == string(JSON_FALSE) {
+ value = false
+ } else {
+ e := fmt.Errorf("Expected \"false\" but found: %s", string(b))
+ return value, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ break
+ case JSON_NULL[0]:
+ b := make([]byte, len(JSON_NULL))
+ _, err := p.reader.Read(b)
+ if err != nil {
+ return false, NewTProtocolException(err)
+ }
+ if string(b) == string(JSON_NULL) {
+ value = false
+ } else {
+ e := fmt.Errorf("Expected \"null\" but found: %s", string(b))
+ return value, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ default:
+ e := fmt.Errorf("Expected \"true\", \"false\", or \"null\" but found: %s", string(f))
+ return value, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ }
+ return value, p.ParsePostValue()
+}
+
+func (p *TSimpleJSONProtocol) ReadByte() (int8, error) {
+ v, err := p.ReadI64()
+ return int8(v), err
+}
+
+func (p *TSimpleJSONProtocol) ReadI16() (int16, error) {
+ v, err := p.ReadI64()
+ return int16(v), err
+}
+
+func (p *TSimpleJSONProtocol) ReadI32() (int32, error) {
+ v, err := p.ReadI64()
+ return int32(v), err
+}
+
+func (p *TSimpleJSONProtocol) ReadI64() (int64, error) {
+ v, _, err := p.ParseI64()
+ return v, err
+}
+
+func (p *TSimpleJSONProtocol) ReadDouble() (float64, error) {
+ v, _, err := p.ParseF64()
+ return v, err
+}
+
+func (p *TSimpleJSONProtocol) ReadString() (string, error) {
+ var v string
+ if err := p.ParsePreValue(); err != nil {
+ return v, err
+ }
+ f, _ := p.reader.Peek(1)
+ if len(f) > 0 && f[0] == JSON_QUOTE {
+ p.reader.ReadByte()
+ value, err := p.ParseStringBody()
+ v = value
+ if err != nil {
+ return v, err
+ }
+ } else if len(f) > 0 && f[0] == JSON_NULL[0] {
+ b := make([]byte, len(JSON_NULL))
+ _, err := p.reader.Read(b)
+ if err != nil {
+ return v, NewTProtocolException(err)
+ }
+ if string(b) != string(JSON_NULL) {
+ e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b))
+ return v, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ } else {
+ e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f))
+ return v, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ return v, p.ParsePostValue()
+}
+
+func (p *TSimpleJSONProtocol) ReadBinary() ([]byte, error) {
+ var v []byte
+ if err := p.ParsePreValue(); err != nil {
+ return nil, err
+ }
+ f, _ := p.reader.Peek(1)
+ if len(f) > 0 && f[0] == JSON_QUOTE {
+ p.reader.ReadByte()
+ value, err := p.ParseBase64EncodedBody()
+ v = value
+ if err != nil {
+ return v, err
+ }
+ } else if len(f) > 0 && f[0] == JSON_NULL[0] {
+ b := make([]byte, len(JSON_NULL))
+ _, err := p.reader.Read(b)
+ if err != nil {
+ return v, NewTProtocolException(err)
+ }
+ if string(b) != string(JSON_NULL) {
+ e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b))
+ return v, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ } else {
+ e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f))
+ return v, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+
+ return v, p.ParsePostValue()
+}
+
+func (p *TSimpleJSONProtocol) Flush(ctx context.Context) (err error) {
+ return NewTProtocolException(p.writer.Flush())
+}
+
+func (p *TSimpleJSONProtocol) Skip(fieldType TType) (err error) {
+ return SkipDefaultDepth(p, fieldType)
+}
+
+func (p *TSimpleJSONProtocol) Transport() TTransport {
+ return p.trans
+}
+
+func (p *TSimpleJSONProtocol) OutputPreValue() error {
+ cxt := _ParseContext(p.dumpContext[len(p.dumpContext)-1])
+ switch cxt {
+ case _CONTEXT_IN_LIST, _CONTEXT_IN_OBJECT_NEXT_KEY:
+ if _, e := p.write(JSON_COMMA); e != nil {
+ return NewTProtocolException(e)
+ }
+ break
+ case _CONTEXT_IN_OBJECT_NEXT_VALUE:
+ if _, e := p.write(JSON_COLON); e != nil {
+ return NewTProtocolException(e)
+ }
+ break
+ }
+ return nil
+}
+
+func (p *TSimpleJSONProtocol) OutputPostValue() error {
+ cxt := _ParseContext(p.dumpContext[len(p.dumpContext)-1])
+ switch cxt {
+ case _CONTEXT_IN_LIST_FIRST:
+ p.dumpContext = p.dumpContext[:len(p.dumpContext)-1]
+ p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_LIST))
+ break
+ case _CONTEXT_IN_OBJECT_FIRST:
+ p.dumpContext = p.dumpContext[:len(p.dumpContext)-1]
+ p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_VALUE))
+ break
+ case _CONTEXT_IN_OBJECT_NEXT_KEY:
+ p.dumpContext = p.dumpContext[:len(p.dumpContext)-1]
+ p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_VALUE))
+ break
+ case _CONTEXT_IN_OBJECT_NEXT_VALUE:
+ p.dumpContext = p.dumpContext[:len(p.dumpContext)-1]
+ p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_KEY))
+ break
+ }
+ return nil
+}
+
+func (p *TSimpleJSONProtocol) OutputBool(value bool) error {
+ if e := p.OutputPreValue(); e != nil {
+ return e
+ }
+ var v string
+ if value {
+ v = string(JSON_TRUE)
+ } else {
+ v = string(JSON_FALSE)
+ }
+ switch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) {
+ case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY:
+ v = jsonQuote(v)
+ default:
+ }
+ if e := p.OutputStringData(v); e != nil {
+ return e
+ }
+ return p.OutputPostValue()
+}
+
+func (p *TSimpleJSONProtocol) OutputNull() error {
+ if e := p.OutputPreValue(); e != nil {
+ return e
+ }
+ if _, e := p.write(JSON_NULL); e != nil {
+ return NewTProtocolException(e)
+ }
+ return p.OutputPostValue()
+}
+
+func (p *TSimpleJSONProtocol) OutputF64(value float64) error {
+ if e := p.OutputPreValue(); e != nil {
+ return e
+ }
+ var v string
+ if math.IsNaN(value) {
+ v = string(JSON_QUOTE) + JSON_NAN + string(JSON_QUOTE)
+ } else if math.IsInf(value, 1) {
+ v = string(JSON_QUOTE) + JSON_INFINITY + string(JSON_QUOTE)
+ } else if math.IsInf(value, -1) {
+ v = string(JSON_QUOTE) + JSON_NEGATIVE_INFINITY + string(JSON_QUOTE)
+ } else {
+ v = strconv.FormatFloat(value, 'g', -1, 64)
+ switch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) {
+ case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY:
+ v = string(JSON_QUOTE) + v + string(JSON_QUOTE)
+ default:
+ }
+ }
+ if e := p.OutputStringData(v); e != nil {
+ return e
+ }
+ return p.OutputPostValue()
+}
+
+func (p *TSimpleJSONProtocol) OutputI64(value int64) error {
+ if e := p.OutputPreValue(); e != nil {
+ return e
+ }
+ v := strconv.FormatInt(value, 10)
+ switch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) {
+ case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY:
+ v = jsonQuote(v)
+ default:
+ }
+ if e := p.OutputStringData(v); e != nil {
+ return e
+ }
+ return p.OutputPostValue()
+}
+
+func (p *TSimpleJSONProtocol) OutputString(s string) error {
+ if e := p.OutputPreValue(); e != nil {
+ return e
+ }
+ if e := p.OutputStringData(jsonQuote(s)); e != nil {
+ return e
+ }
+ return p.OutputPostValue()
+}
+
+func (p *TSimpleJSONProtocol) OutputStringData(s string) error {
+ _, e := p.write([]byte(s))
+ return NewTProtocolException(e)
+}
+
+func (p *TSimpleJSONProtocol) OutputObjectBegin() error {
+ if e := p.OutputPreValue(); e != nil {
+ return e
+ }
+ if _, e := p.write(JSON_LBRACE); e != nil {
+ return NewTProtocolException(e)
+ }
+ p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_FIRST))
+ return nil
+}
+
+func (p *TSimpleJSONProtocol) OutputObjectEnd() error {
+ if _, e := p.write(JSON_RBRACE); e != nil {
+ return NewTProtocolException(e)
+ }
+ p.dumpContext = p.dumpContext[:len(p.dumpContext)-1]
+ if e := p.OutputPostValue(); e != nil {
+ return e
+ }
+ return nil
+}
+
+func (p *TSimpleJSONProtocol) OutputListBegin() error {
+ if e := p.OutputPreValue(); e != nil {
+ return e
+ }
+ if _, e := p.write(JSON_LBRACKET); e != nil {
+ return NewTProtocolException(e)
+ }
+ p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_LIST_FIRST))
+ return nil
+}
+
+func (p *TSimpleJSONProtocol) OutputListEnd() error {
+ if _, e := p.write(JSON_RBRACKET); e != nil {
+ return NewTProtocolException(e)
+ }
+ p.dumpContext = p.dumpContext[:len(p.dumpContext)-1]
+ if e := p.OutputPostValue(); e != nil {
+ return e
+ }
+ return nil
+}
+
+func (p *TSimpleJSONProtocol) OutputElemListBegin(elemType TType, size int) error {
+ if e := p.OutputListBegin(); e != nil {
+ return e
+ }
+ if e := p.WriteByte(int8(elemType)); e != nil {
+ return e
+ }
+ if e := p.WriteI64(int64(size)); e != nil {
+ return e
+ }
+ return nil
+}
+
+func (p *TSimpleJSONProtocol) ParsePreValue() error {
+ if e := p.readNonSignificantWhitespace(); e != nil {
+ return NewTProtocolException(e)
+ }
+ cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1])
+ b, _ := p.reader.Peek(1)
+ switch cxt {
+ case _CONTEXT_IN_LIST:
+ if len(b) > 0 {
+ switch b[0] {
+ case JSON_RBRACKET[0]:
+ return nil
+ case JSON_COMMA[0]:
+ p.reader.ReadByte()
+ if e := p.readNonSignificantWhitespace(); e != nil {
+ return NewTProtocolException(e)
+ }
+ return nil
+ default:
+ e := fmt.Errorf("Expected \"]\" or \",\" in list context, but found \"%s\"", string(b))
+ return NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ }
+ break
+ case _CONTEXT_IN_OBJECT_NEXT_KEY:
+ if len(b) > 0 {
+ switch b[0] {
+ case JSON_RBRACE[0]:
+ return nil
+ case JSON_COMMA[0]:
+ p.reader.ReadByte()
+ if e := p.readNonSignificantWhitespace(); e != nil {
+ return NewTProtocolException(e)
+ }
+ return nil
+ default:
+ e := fmt.Errorf("Expected \"}\" or \",\" in object context, but found \"%s\"", string(b))
+ return NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ }
+ break
+ case _CONTEXT_IN_OBJECT_NEXT_VALUE:
+ if len(b) > 0 {
+ switch b[0] {
+ case JSON_COLON[0]:
+ p.reader.ReadByte()
+ if e := p.readNonSignificantWhitespace(); e != nil {
+ return NewTProtocolException(e)
+ }
+ return nil
+ default:
+ e := fmt.Errorf("Expected \":\" in object context, but found \"%s\"", string(b))
+ return NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ }
+ break
+ }
+ return nil
+}
+
+func (p *TSimpleJSONProtocol) ParsePostValue() error {
+ if e := p.readNonSignificantWhitespace(); e != nil {
+ return NewTProtocolException(e)
+ }
+ cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1])
+ switch cxt {
+ case _CONTEXT_IN_LIST_FIRST:
+ p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1]
+ p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_LIST))
+ break
+ case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY:
+ p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1]
+ p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_NEXT_VALUE))
+ break
+ case _CONTEXT_IN_OBJECT_NEXT_VALUE:
+ p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1]
+ p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_NEXT_KEY))
+ break
+ }
+ return nil
+}
+
+func (p *TSimpleJSONProtocol) readNonSignificantWhitespace() error {
+ for {
+ b, _ := p.reader.Peek(1)
+ if len(b) < 1 {
+ return nil
+ }
+ switch b[0] {
+ case ' ', '\r', '\n', '\t':
+ p.reader.ReadByte()
+ continue
+ default:
+ break
+ }
+ break
+ }
+ return nil
+}
+
+func (p *TSimpleJSONProtocol) ParseStringBody() (string, error) {
+ line, err := p.reader.ReadString(JSON_QUOTE)
+ if err != nil {
+ return "", NewTProtocolException(err)
+ }
+ l := len(line)
+ // count number of escapes to see if we need to keep going
+ i := 1
+ for ; i < l; i++ {
+ if line[l-i-1] != '\\' {
+ break
+ }
+ }
+ if i&0x01 == 1 {
+ v, ok := jsonUnquote(string(JSON_QUOTE) + line)
+ if !ok {
+ return "", NewTProtocolException(err)
+ }
+ return v, nil
+ }
+ s, err := p.ParseQuotedStringBody()
+ if err != nil {
+ return "", NewTProtocolException(err)
+ }
+ str := string(JSON_QUOTE) + line + s
+ v, ok := jsonUnquote(str)
+ if !ok {
+ e := fmt.Errorf("Unable to parse as JSON string %s", str)
+ return "", NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ return v, nil
+}
+
+func (p *TSimpleJSONProtocol) ParseQuotedStringBody() (string, error) {
+ line, err := p.reader.ReadString(JSON_QUOTE)
+ if err != nil {
+ return "", NewTProtocolException(err)
+ }
+ l := len(line)
+ // count number of escapes to see if we need to keep going
+ i := 1
+ for ; i < l; i++ {
+ if line[l-i-1] != '\\' {
+ break
+ }
+ }
+ if i&0x01 == 1 {
+ return line, nil
+ }
+ s, err := p.ParseQuotedStringBody()
+ if err != nil {
+ return "", NewTProtocolException(err)
+ }
+ v := line + s
+ return v, nil
+}
+
+func (p *TSimpleJSONProtocol) ParseBase64EncodedBody() ([]byte, error) {
+ line, err := p.reader.ReadBytes(JSON_QUOTE)
+ if err != nil {
+ return line, NewTProtocolException(err)
+ }
+ line2 := line[0 : len(line)-1]
+ l := len(line2)
+ if (l % 4) != 0 {
+ pad := 4 - (l % 4)
+ fill := [...]byte{'=', '=', '='}
+ line2 = append(line2, fill[:pad]...)
+ l = len(line2)
+ }
+ output := make([]byte, base64.StdEncoding.DecodedLen(l))
+ n, err := base64.StdEncoding.Decode(output, line2)
+ return output[0:n], NewTProtocolException(err)
+}
+
+func (p *TSimpleJSONProtocol) ParseI64() (int64, bool, error) {
+ if err := p.ParsePreValue(); err != nil {
+ return 0, false, err
+ }
+ var value int64
+ var isnull bool
+ if p.safePeekContains(JSON_NULL) {
+ p.reader.Read(make([]byte, len(JSON_NULL)))
+ isnull = true
+ } else {
+ num, err := p.readNumeric()
+ isnull = (num == nil)
+ if !isnull {
+ value = num.Int64()
+ }
+ if err != nil {
+ return value, isnull, err
+ }
+ }
+ return value, isnull, p.ParsePostValue()
+}
+
+func (p *TSimpleJSONProtocol) ParseF64() (float64, bool, error) {
+ if err := p.ParsePreValue(); err != nil {
+ return 0, false, err
+ }
+ var value float64
+ var isnull bool
+ if p.safePeekContains(JSON_NULL) {
+ p.reader.Read(make([]byte, len(JSON_NULL)))
+ isnull = true
+ } else {
+ num, err := p.readNumeric()
+ isnull = (num == nil)
+ if !isnull {
+ value = num.Float64()
+ }
+ if err != nil {
+ return value, isnull, err
+ }
+ }
+ return value, isnull, p.ParsePostValue()
+}
+
+func (p *TSimpleJSONProtocol) ParseObjectStart() (bool, error) {
+ if err := p.ParsePreValue(); err != nil {
+ return false, err
+ }
+ var b []byte
+ b, err := p.reader.Peek(1)
+ if err != nil {
+ return false, err
+ }
+ if len(b) > 0 && b[0] == JSON_LBRACE[0] {
+ p.reader.ReadByte()
+ p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_FIRST))
+ return false, nil
+ } else if p.safePeekContains(JSON_NULL) {
+ return true, nil
+ }
+ e := fmt.Errorf("Expected '{' or null, but found '%s'", string(b))
+ return false, NewTProtocolExceptionWithType(INVALID_DATA, e)
+}
+
+func (p *TSimpleJSONProtocol) ParseObjectEnd() error {
+ if isNull, err := p.readIfNull(); isNull || err != nil {
+ return err
+ }
+ cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1])
+ if (cxt != _CONTEXT_IN_OBJECT_FIRST) && (cxt != _CONTEXT_IN_OBJECT_NEXT_KEY) {
+ e := fmt.Errorf("Expected to be in the Object Context, but not in Object Context (%d)", cxt)
+ return NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ line, err := p.reader.ReadString(JSON_RBRACE[0])
+ if err != nil {
+ return NewTProtocolException(err)
+ }
+ for _, char := range line {
+ switch char {
+ default:
+ e := fmt.Errorf("Expecting end of object \"}\", but found: \"%s\"", line)
+ return NewTProtocolExceptionWithType(INVALID_DATA, e)
+ case ' ', '\n', '\r', '\t', '}':
+ break
+ }
+ }
+ p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1]
+ return p.ParsePostValue()
+}
+
+func (p *TSimpleJSONProtocol) ParseListBegin() (isNull bool, err error) {
+ if e := p.ParsePreValue(); e != nil {
+ return false, e
+ }
+ var b []byte
+ b, err = p.reader.Peek(1)
+ if err != nil {
+ return false, err
+ }
+ if len(b) >= 1 && b[0] == JSON_LBRACKET[0] {
+ p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_LIST_FIRST))
+ p.reader.ReadByte()
+ isNull = false
+ } else if p.safePeekContains(JSON_NULL) {
+ isNull = true
+ } else {
+ err = fmt.Errorf("Expected \"null\" or \"[\", received %q", b)
+ }
+ return isNull, NewTProtocolExceptionWithType(INVALID_DATA, err)
+}
+
+func (p *TSimpleJSONProtocol) ParseElemListBegin() (elemType TType, size int, e error) {
+ if isNull, e := p.ParseListBegin(); isNull || e != nil {
+ return VOID, 0, e
+ }
+ bElemType, err := p.ReadByte()
+ elemType = TType(bElemType)
+ if err != nil {
+ return elemType, size, err
+ }
+ nSize, err2 := p.ReadI64()
+ size = int(nSize)
+ return elemType, size, err2
+}
+
+func (p *TSimpleJSONProtocol) ParseListEnd() error {
+ if isNull, err := p.readIfNull(); isNull || err != nil {
+ return err
+ }
+ cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1])
+ if cxt != _CONTEXT_IN_LIST {
+ e := fmt.Errorf("Expected to be in the List Context, but not in List Context (%d)", cxt)
+ return NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ line, err := p.reader.ReadString(JSON_RBRACKET[0])
+ if err != nil {
+ return NewTProtocolException(err)
+ }
+ for _, char := range line {
+ switch char {
+ default:
+ e := fmt.Errorf("Expecting end of list \"]\", but found: \"%v\"", line)
+ return NewTProtocolExceptionWithType(INVALID_DATA, e)
+ case ' ', '\n', '\r', '\t', rune(JSON_RBRACKET[0]):
+ break
+ }
+ }
+ p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1]
+ if _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) == _CONTEXT_IN_TOPLEVEL {
+ return nil
+ }
+ return p.ParsePostValue()
+}
+
+func (p *TSimpleJSONProtocol) readSingleValue() (interface{}, TType, error) {
+ e := p.readNonSignificantWhitespace()
+ if e != nil {
+ return nil, VOID, NewTProtocolException(e)
+ }
+ b, e := p.reader.Peek(1)
+ if len(b) > 0 {
+ c := b[0]
+ switch c {
+ case JSON_NULL[0]:
+ buf := make([]byte, len(JSON_NULL))
+ _, e := p.reader.Read(buf)
+ if e != nil {
+ return nil, VOID, NewTProtocolException(e)
+ }
+ if string(JSON_NULL) != string(buf) {
+ e = mismatch(string(JSON_NULL), string(buf))
+ return nil, VOID, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ return nil, VOID, nil
+ case JSON_QUOTE:
+ p.reader.ReadByte()
+ v, e := p.ParseStringBody()
+ if e != nil {
+ return v, UTF8, NewTProtocolException(e)
+ }
+ if v == JSON_INFINITY {
+ return INFINITY, DOUBLE, nil
+ } else if v == JSON_NEGATIVE_INFINITY {
+ return NEGATIVE_INFINITY, DOUBLE, nil
+ } else if v == JSON_NAN {
+ return NAN, DOUBLE, nil
+ }
+ return v, UTF8, nil
+ case JSON_TRUE[0]:
+ buf := make([]byte, len(JSON_TRUE))
+ _, e := p.reader.Read(buf)
+ if e != nil {
+ return true, BOOL, NewTProtocolException(e)
+ }
+ if string(JSON_TRUE) != string(buf) {
+ e := mismatch(string(JSON_TRUE), string(buf))
+ return true, BOOL, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ return true, BOOL, nil
+ case JSON_FALSE[0]:
+ buf := make([]byte, len(JSON_FALSE))
+ _, e := p.reader.Read(buf)
+ if e != nil {
+ return false, BOOL, NewTProtocolException(e)
+ }
+ if string(JSON_FALSE) != string(buf) {
+ e := mismatch(string(JSON_FALSE), string(buf))
+ return false, BOOL, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ return false, BOOL, nil
+ case JSON_LBRACKET[0]:
+ _, e := p.reader.ReadByte()
+ return make([]interface{}, 0), LIST, NewTProtocolException(e)
+ case JSON_LBRACE[0]:
+ _, e := p.reader.ReadByte()
+ return make(map[string]interface{}), STRUCT, NewTProtocolException(e)
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'e', 'E', '.', '+', '-', JSON_INFINITY[0], JSON_NAN[0]:
+ // assume numeric
+ v, e := p.readNumeric()
+ return v, DOUBLE, e
+ default:
+ e := fmt.Errorf("Expected element in list but found '%s' while parsing JSON.", string(c))
+ return nil, VOID, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ }
+ e = fmt.Errorf("Cannot read a single element while parsing JSON.")
+ return nil, VOID, NewTProtocolExceptionWithType(INVALID_DATA, e)
+
+}
+
+func (p *TSimpleJSONProtocol) readIfNull() (bool, error) {
+ cont := true
+ for cont {
+ b, _ := p.reader.Peek(1)
+ if len(b) < 1 {
+ return false, nil
+ }
+ switch b[0] {
+ default:
+ return false, nil
+ case JSON_NULL[0]:
+ cont = false
+ break
+ case ' ', '\n', '\r', '\t':
+ p.reader.ReadByte()
+ break
+ }
+ }
+ if p.safePeekContains(JSON_NULL) {
+ p.reader.Read(make([]byte, len(JSON_NULL)))
+ return true, nil
+ }
+ return false, nil
+}
+
+func (p *TSimpleJSONProtocol) readQuoteIfNext() {
+ b, _ := p.reader.Peek(1)
+ if len(b) > 0 && b[0] == JSON_QUOTE {
+ p.reader.ReadByte()
+ }
+}
+
+func (p *TSimpleJSONProtocol) readNumeric() (Numeric, error) {
+ isNull, err := p.readIfNull()
+ if isNull || err != nil {
+ return NUMERIC_NULL, err
+ }
+ hasDecimalPoint := false
+ nextCanBeSign := true
+ hasE := false
+ MAX_LEN := 40
+ buf := bytes.NewBuffer(make([]byte, 0, MAX_LEN))
+ continueFor := true
+ inQuotes := false
+ for continueFor {
+ c, err := p.reader.ReadByte()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ return NUMERIC_NULL, NewTProtocolException(err)
+ }
+ switch c {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ buf.WriteByte(c)
+ nextCanBeSign = false
+ case '.':
+ if hasDecimalPoint {
+ e := fmt.Errorf("Unable to parse number with multiple decimal points '%s.'", buf.String())
+ return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ if hasE {
+ e := fmt.Errorf("Unable to parse number with decimal points in the exponent '%s.'", buf.String())
+ return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ buf.WriteByte(c)
+ hasDecimalPoint, nextCanBeSign = true, false
+ case 'e', 'E':
+ if hasE {
+ e := fmt.Errorf("Unable to parse number with multiple exponents '%s%c'", buf.String(), c)
+ return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ buf.WriteByte(c)
+ hasE, nextCanBeSign = true, true
+ case '-', '+':
+ if !nextCanBeSign {
+ e := fmt.Errorf("Negative sign within number")
+ return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ buf.WriteByte(c)
+ nextCanBeSign = false
+ case ' ', 0, '\t', '\n', '\r', JSON_RBRACE[0], JSON_RBRACKET[0], JSON_COMMA[0], JSON_COLON[0]:
+ p.reader.UnreadByte()
+ continueFor = false
+ case JSON_NAN[0]:
+ if buf.Len() == 0 {
+ buffer := make([]byte, len(JSON_NAN))
+ buffer[0] = c
+ _, e := p.reader.Read(buffer[1:])
+ if e != nil {
+ return NUMERIC_NULL, NewTProtocolException(e)
+ }
+ if JSON_NAN != string(buffer) {
+ e := mismatch(JSON_NAN, string(buffer))
+ return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ if inQuotes {
+ p.readQuoteIfNext()
+ }
+ return NAN, nil
+ } else {
+ e := fmt.Errorf("Unable to parse number starting with character '%c'", c)
+ return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ case JSON_INFINITY[0]:
+ if buf.Len() == 0 || (buf.Len() == 1 && buf.Bytes()[0] == '+') {
+ buffer := make([]byte, len(JSON_INFINITY))
+ buffer[0] = c
+ _, e := p.reader.Read(buffer[1:])
+ if e != nil {
+ return NUMERIC_NULL, NewTProtocolException(e)
+ }
+ if JSON_INFINITY != string(buffer) {
+ e := mismatch(JSON_INFINITY, string(buffer))
+ return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ if inQuotes {
+ p.readQuoteIfNext()
+ }
+ return INFINITY, nil
+ } else if buf.Len() == 1 && buf.Bytes()[0] == JSON_NEGATIVE_INFINITY[0] {
+ buffer := make([]byte, len(JSON_NEGATIVE_INFINITY))
+ buffer[0] = JSON_NEGATIVE_INFINITY[0]
+ buffer[1] = c
+ _, e := p.reader.Read(buffer[2:])
+ if e != nil {
+ return NUMERIC_NULL, NewTProtocolException(e)
+ }
+ if JSON_NEGATIVE_INFINITY != string(buffer) {
+ e := mismatch(JSON_NEGATIVE_INFINITY, string(buffer))
+ return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ if inQuotes {
+ p.readQuoteIfNext()
+ }
+ return NEGATIVE_INFINITY, nil
+ } else {
+ e := fmt.Errorf("Unable to parse number starting with character '%c' due to existing buffer %s", c, buf.String())
+ return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ case JSON_QUOTE:
+ if !inQuotes {
+ inQuotes = true
+ } else {
+ break
+ }
+ default:
+ e := fmt.Errorf("Unable to parse number starting with character '%c'", c)
+ return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ }
+ if buf.Len() == 0 {
+ e := fmt.Errorf("Unable to parse number from empty string ''")
+ return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)
+ }
+ return NewNumericFromJSONString(buf.String(), false), nil
+}
+
+// Safely peeks into the buffer, reading only what is necessary
+func (p *TSimpleJSONProtocol) safePeekContains(b []byte) bool {
+ for i := 0; i < len(b); i++ {
+ a, _ := p.reader.Peek(i + 1)
+ if len(a) < (i+1) || a[i] != b[i] {
+ return false
+ }
+ }
+ return true
+}
+
+// Reset the context stack to its initial state.
+func (p *TSimpleJSONProtocol) resetContextStack() {
+ p.parseContextStack = []int{int(_CONTEXT_IN_TOPLEVEL)}
+ p.dumpContext = []int{int(_CONTEXT_IN_TOPLEVEL)}
+}
+
+func (p *TSimpleJSONProtocol) write(b []byte) (int, error) {
+ n, err := p.writer.Write(b)
+ if err != nil {
+ p.writer.Reset(p.trans) // THRIFT-3735
+ }
+ return n, err
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/simple_json_protocol_test.go b/src/jaegertracing/thrift/lib/go/thrift/simple_json_protocol_test.go
new file mode 100644
index 000000000..0126da0a8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/simple_json_protocol_test.go
@@ -0,0 +1,738 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "math"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func TestWriteSimpleJSONProtocolBool(t *testing.T) {
+ thetype := "boolean"
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ for _, value := range BOOL_VALUES {
+ if e := p.WriteBool(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error())
+ }
+ s := trans.String()
+ if s != fmt.Sprint(value) {
+ t.Fatalf("Bad value for %s %v: %s", thetype, value, s)
+ }
+ v := false
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestReadSimpleJSONProtocolBool(t *testing.T) {
+ thetype := "boolean"
+ for _, value := range BOOL_VALUES {
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ if value {
+ trans.Write(JSON_TRUE)
+ } else {
+ trans.Write(JSON_FALSE)
+ }
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadBool()
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if v != value {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ trans.Close()
+ }
+}
+
+func TestWriteSimpleJSONProtocolByte(t *testing.T) {
+ thetype := "byte"
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ for _, value := range BYTE_VALUES {
+ if e := p.WriteByte(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error())
+ }
+ s := trans.String()
+ if s != fmt.Sprint(value) {
+ t.Fatalf("Bad value for %s %v: %s", thetype, value, s)
+ }
+ v := int8(0)
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestReadSimpleJSONProtocolByte(t *testing.T) {
+ thetype := "byte"
+ for _, value := range BYTE_VALUES {
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ trans.WriteString(strconv.Itoa(int(value)))
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadByte()
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if v != value {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ trans.Close()
+ }
+}
+
+func TestWriteSimpleJSONProtocolI16(t *testing.T) {
+ thetype := "int16"
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ for _, value := range INT16_VALUES {
+ if e := p.WriteI16(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error())
+ }
+ s := trans.String()
+ if s != fmt.Sprint(value) {
+ t.Fatalf("Bad value for %s %v: %s", thetype, value, s)
+ }
+ v := int16(0)
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestReadSimpleJSONProtocolI16(t *testing.T) {
+ thetype := "int16"
+ for _, value := range INT16_VALUES {
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ trans.WriteString(strconv.Itoa(int(value)))
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadI16()
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if v != value {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ trans.Close()
+ }
+}
+
+func TestWriteSimpleJSONProtocolI32(t *testing.T) {
+ thetype := "int32"
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ for _, value := range INT32_VALUES {
+ if e := p.WriteI32(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error())
+ }
+ s := trans.String()
+ if s != fmt.Sprint(value) {
+ t.Fatalf("Bad value for %s %v: %s", thetype, value, s)
+ }
+ v := int32(0)
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestReadSimpleJSONProtocolI32(t *testing.T) {
+ thetype := "int32"
+ for _, value := range INT32_VALUES {
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ trans.WriteString(strconv.Itoa(int(value)))
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadI32()
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if v != value {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ trans.Close()
+ }
+}
+
+func TestReadSimpleJSONProtocolI32Null(t *testing.T) {
+ thetype := "int32"
+ value := "null"
+
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ trans.WriteString(value)
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadI32()
+
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if v != 0 {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ trans.Reset()
+ trans.Close()
+}
+
+func TestWriteSimpleJSONProtocolI64(t *testing.T) {
+ thetype := "int64"
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ for _, value := range INT64_VALUES {
+ if e := p.WriteI64(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error())
+ }
+ s := trans.String()
+ if s != fmt.Sprint(value) {
+ t.Fatalf("Bad value for %s %v: %s", thetype, value, s)
+ }
+ v := int64(0)
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestReadSimpleJSONProtocolI64(t *testing.T) {
+ thetype := "int64"
+ for _, value := range INT64_VALUES {
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ trans.WriteString(strconv.FormatInt(value, 10))
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadI64()
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if v != value {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ trans.Reset()
+ trans.Close()
+ }
+}
+
+func TestReadSimpleJSONProtocolI64Null(t *testing.T) {
+ thetype := "int32"
+ value := "null"
+
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ trans.WriteString(value)
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadI64()
+
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if v != 0 {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ trans.Reset()
+ trans.Close()
+}
+
+func TestWriteSimpleJSONProtocolDouble(t *testing.T) {
+ thetype := "double"
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ for _, value := range DOUBLE_VALUES {
+ if e := p.WriteDouble(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error())
+ }
+ s := trans.String()
+ if math.IsInf(value, 1) {
+ if s != jsonQuote(JSON_INFINITY) {
+ t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, jsonQuote(JSON_INFINITY))
+ }
+ } else if math.IsInf(value, -1) {
+ if s != jsonQuote(JSON_NEGATIVE_INFINITY) {
+ t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, jsonQuote(JSON_NEGATIVE_INFINITY))
+ }
+ } else if math.IsNaN(value) {
+ if s != jsonQuote(JSON_NAN) {
+ t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, jsonQuote(JSON_NAN))
+ }
+ } else {
+ if s != fmt.Sprint(value) {
+ t.Fatalf("Bad value for %s %v: %s", thetype, value, s)
+ }
+ v := float64(0)
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestReadSimpleJSONProtocolDouble(t *testing.T) {
+ thetype := "double"
+ for _, value := range DOUBLE_VALUES {
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ n := NewNumericFromDouble(value)
+ trans.WriteString(n.String())
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadDouble()
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if math.IsInf(value, 1) {
+ if !math.IsInf(v, 1) {
+ t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ } else if math.IsInf(value, -1) {
+ if !math.IsInf(v, -1) {
+ t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ } else if math.IsNaN(value) {
+ if !math.IsNaN(v) {
+ t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ } else {
+ if v != value {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ }
+ trans.Reset()
+ trans.Close()
+ }
+}
+
+func TestWriteSimpleJSONProtocolString(t *testing.T) {
+ thetype := "string"
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ for _, value := range STRING_VALUES {
+ if e := p.WriteString(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error())
+ }
+ s := trans.String()
+ if s[0] != '"' || s[len(s)-1] != '"' {
+ t.Fatalf("Bad value for %s '%v', wrote '%v', expected: %v", thetype, value, s, fmt.Sprint("\"", value, "\""))
+ }
+ v := new(string)
+ if err := json.Unmarshal([]byte(s), v); err != nil || *v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v)
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestReadSimpleJSONProtocolString(t *testing.T) {
+ thetype := "string"
+ for _, value := range STRING_VALUES {
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ trans.WriteString(jsonQuote(value))
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadString()
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if v != value {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ v1 := new(string)
+ if err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v1)
+ }
+ trans.Reset()
+ trans.Close()
+ }
+}
+func TestReadSimpleJSONProtocolStringNull(t *testing.T) {
+ thetype := "string"
+ value := "null"
+
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ trans.WriteString(value)
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadString()
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if v != "" {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ trans.Reset()
+ trans.Close()
+}
+
+func TestWriteSimpleJSONProtocolBinary(t *testing.T) {
+ thetype := "binary"
+ value := protocol_bdata
+ b64value := make([]byte, base64.StdEncoding.EncodedLen(len(protocol_bdata)))
+ base64.StdEncoding.Encode(b64value, value)
+ b64String := string(b64value)
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ if e := p.WriteBinary(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error())
+ }
+ s := trans.String()
+ if s != fmt.Sprint("\"", b64String, "\"") {
+ t.Fatalf("Bad value for %s %v\n wrote: %v\nexpected: %v", thetype, value, s, "\""+b64String+"\"")
+ }
+ v1 := new(string)
+ if err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != b64String {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v1)
+ }
+ trans.Close()
+}
+
+func TestReadSimpleJSONProtocolBinary(t *testing.T) {
+ thetype := "binary"
+ value := protocol_bdata
+ b64value := make([]byte, base64.StdEncoding.EncodedLen(len(protocol_bdata)))
+ base64.StdEncoding.Encode(b64value, value)
+ b64String := string(b64value)
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ trans.WriteString(jsonQuote(b64String))
+ trans.Flush(context.Background())
+ s := trans.String()
+ v, e := p.ReadBinary()
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if len(v) != len(value) {
+ t.Fatalf("Bad value for %s value length %v, wrote: %v, received length: %v", thetype, len(value), s, len(v))
+ }
+ for i := 0; i < len(v); i++ {
+ if v[i] != value[i] {
+ t.Fatalf("Bad value for %s at index %d value %v, wrote: %v, received: %v", thetype, i, value[i], s, v[i])
+ }
+ }
+ v1 := new(string)
+ if err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != b64String {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v1)
+ }
+ trans.Reset()
+ trans.Close()
+}
+
+func TestReadSimpleJSONProtocolBinaryNull(t *testing.T) {
+ thetype := "binary"
+ value := "null"
+
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ trans.WriteString(value)
+ trans.Flush(context.Background())
+ s := trans.String()
+ b, e := p.ReadBinary()
+ v := string(b)
+
+ if e != nil {
+ t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ if v != "" {
+ t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v)
+ }
+ trans.Reset()
+ trans.Close()
+}
+
+func TestWriteSimpleJSONProtocolList(t *testing.T) {
+ thetype := "list"
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ p.WriteListBegin(TType(DOUBLE), len(DOUBLE_VALUES))
+ for _, value := range DOUBLE_VALUES {
+ if e := p.WriteDouble(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ }
+ p.WriteListEnd()
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error())
+ }
+ str := trans.String()
+ str1 := new([]interface{})
+ err := json.Unmarshal([]byte(str), str1)
+ if err != nil {
+ t.Fatalf("Unable to decode %s, wrote: %s", thetype, str)
+ }
+ l := *str1
+ if len(l) < 2 {
+ t.Fatalf("List must be at least of length two to include metadata")
+ }
+ if int(l[0].(float64)) != DOUBLE {
+ t.Fatal("Invalid type for list, expected: ", DOUBLE, ", but was: ", l[0])
+ }
+ if int(l[1].(float64)) != len(DOUBLE_VALUES) {
+ t.Fatal("Invalid length for list, expected: ", len(DOUBLE_VALUES), ", but was: ", l[1])
+ }
+ for k, value := range DOUBLE_VALUES {
+ s := l[k+2]
+ if math.IsInf(value, 1) {
+ if s.(string) != JSON_INFINITY {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_INFINITY), str)
+ }
+ } else if math.IsInf(value, 0) {
+ if s.(string) != JSON_NEGATIVE_INFINITY {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY), str)
+ }
+ } else if math.IsNaN(value) {
+ if s.(string) != JSON_NAN {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_NAN), str)
+ }
+ } else {
+ if s.(float64) != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s'", thetype, value, s)
+ }
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestWriteSimpleJSONProtocolSet(t *testing.T) {
+ thetype := "set"
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ p.WriteSetBegin(TType(DOUBLE), len(DOUBLE_VALUES))
+ for _, value := range DOUBLE_VALUES {
+ if e := p.WriteDouble(value); e != nil {
+ t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error())
+ }
+ }
+ p.WriteSetEnd()
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error())
+ }
+ str := trans.String()
+ str1 := new([]interface{})
+ err := json.Unmarshal([]byte(str), str1)
+ if err != nil {
+ t.Fatalf("Unable to decode %s, wrote: %s", thetype, str)
+ }
+ l := *str1
+ if len(l) < 2 {
+ t.Fatalf("Set must be at least of length two to include metadata")
+ }
+ if int(l[0].(float64)) != DOUBLE {
+ t.Fatal("Invalid type for set, expected: ", DOUBLE, ", but was: ", l[0])
+ }
+ if int(l[1].(float64)) != len(DOUBLE_VALUES) {
+ t.Fatal("Invalid length for set, expected: ", len(DOUBLE_VALUES), ", but was: ", l[1])
+ }
+ for k, value := range DOUBLE_VALUES {
+ s := l[k+2]
+ if math.IsInf(value, 1) {
+ if s.(string) != JSON_INFINITY {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_INFINITY), str)
+ }
+ } else if math.IsInf(value, 0) {
+ if s.(string) != JSON_NEGATIVE_INFINITY {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY), str)
+ }
+ } else if math.IsNaN(value) {
+ if s.(string) != JSON_NAN {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_NAN), str)
+ }
+ } else {
+ if s.(float64) != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s'", thetype, value, s)
+ }
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestWriteSimpleJSONProtocolMap(t *testing.T) {
+ thetype := "map"
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ p.WriteMapBegin(TType(I32), TType(DOUBLE), len(DOUBLE_VALUES))
+ for k, value := range DOUBLE_VALUES {
+ if e := p.WriteI32(int32(k)); e != nil {
+ t.Fatalf("Unable to write %s key int32 value %v due to error: %s", thetype, k, e.Error())
+ }
+ if e := p.WriteDouble(value); e != nil {
+ t.Fatalf("Unable to write %s value float64 value %v due to error: %s", thetype, value, e.Error())
+ }
+ }
+ p.WriteMapEnd()
+ if e := p.Flush(context.Background()); e != nil {
+ t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error())
+ }
+ str := trans.String()
+ if str[0] != '[' || str[len(str)-1] != ']' {
+ t.Fatalf("Bad value for %s, wrote: %v, in go: %v", thetype, str, DOUBLE_VALUES)
+ }
+ l := strings.Split(str[1:len(str)-1], ",")
+ if len(l) < 3 {
+ t.Fatal("Expected list of at least length 3 for map for metadata, but was of length ", len(l))
+ }
+ expectedKeyType, _ := strconv.Atoi(l[0])
+ expectedValueType, _ := strconv.Atoi(l[1])
+ expectedSize, _ := strconv.Atoi(l[2])
+ if expectedKeyType != I32 {
+ t.Fatal("Expected map key type ", I32, ", but was ", l[0])
+ }
+ if expectedValueType != DOUBLE {
+ t.Fatal("Expected map value type ", DOUBLE, ", but was ", l[1])
+ }
+ if expectedSize != len(DOUBLE_VALUES) {
+ t.Fatal("Expected map size of ", len(DOUBLE_VALUES), ", but was ", l[2])
+ }
+ for k, value := range DOUBLE_VALUES {
+ strk := l[k*2+3]
+ strv := l[k*2+4]
+ ik, err := strconv.Atoi(strk)
+ if err != nil {
+ t.Fatalf("Bad value for %s index %v, wrote: %v, expected: %v, error: %s", thetype, k, strk, string(k), err.Error())
+ }
+ if ik != k {
+ t.Fatalf("Bad value for %s index %v, wrote: %v, expected: %v", thetype, k, strk, k)
+ }
+ s := strv
+ if math.IsInf(value, 1) {
+ if s != jsonQuote(JSON_INFINITY) {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, jsonQuote(JSON_INFINITY))
+ }
+ } else if math.IsInf(value, 0) {
+ if s != jsonQuote(JSON_NEGATIVE_INFINITY) {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY))
+ }
+ } else if math.IsNaN(value) {
+ if s != jsonQuote(JSON_NAN) {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, jsonQuote(JSON_NAN))
+ }
+ } else {
+ expected := strconv.FormatFloat(value, 'g', 10, 64)
+ if s != expected {
+ t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected %v", thetype, k, value, s, expected)
+ }
+ v := float64(0)
+ if err := json.Unmarshal([]byte(s), &v); err != nil || v != value {
+ t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v)
+ }
+ }
+ trans.Reset()
+ }
+ trans.Close()
+}
+
+func TestWriteSimpleJSONProtocolSafePeek(t *testing.T) {
+ trans := NewTMemoryBuffer()
+ p := NewTSimpleJSONProtocol(trans)
+ trans.Write([]byte{'a', 'b'})
+ trans.Flush(context.Background())
+
+ test1 := p.safePeekContains([]byte{'a', 'b'})
+ if !test1 {
+ t.Fatalf("Should match at test 1")
+ }
+
+ test2 := p.safePeekContains([]byte{'a', 'b', 'c', 'd'})
+ if test2 {
+ t.Fatalf("Should not match at test 2")
+ }
+
+ test3 := p.safePeekContains([]byte{'x', 'y'})
+ if test3 {
+ t.Fatalf("Should not match at test 3")
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/simple_server.go b/src/jaegertracing/thrift/lib/go/thrift/simple_server.go
new file mode 100644
index 000000000..f8efbed91
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/simple_server.go
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "log"
+ "runtime/debug"
+ "sync"
+ "sync/atomic"
+)
+
+/*
+ * This is not a typical TSimpleServer as it is not blocked after accept a socket.
+ * It is more like a TThreadedServer that can handle different connections in different goroutines.
+ * This will work if golang user implements a conn-pool like thing in client side.
+ */
+type TSimpleServer struct {
+ closed int32
+ wg sync.WaitGroup
+ mu sync.Mutex
+
+ processorFactory TProcessorFactory
+ serverTransport TServerTransport
+ inputTransportFactory TTransportFactory
+ outputTransportFactory TTransportFactory
+ inputProtocolFactory TProtocolFactory
+ outputProtocolFactory TProtocolFactory
+
+ // Headers to auto forward in THeaderProtocol
+ forwardHeaders []string
+}
+
+func NewTSimpleServer2(processor TProcessor, serverTransport TServerTransport) *TSimpleServer {
+ return NewTSimpleServerFactory2(NewTProcessorFactory(processor), serverTransport)
+}
+
+func NewTSimpleServer4(processor TProcessor, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer {
+ return NewTSimpleServerFactory4(NewTProcessorFactory(processor),
+ serverTransport,
+ transportFactory,
+ protocolFactory,
+ )
+}
+
+func NewTSimpleServer6(processor TProcessor, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer {
+ return NewTSimpleServerFactory6(NewTProcessorFactory(processor),
+ serverTransport,
+ inputTransportFactory,
+ outputTransportFactory,
+ inputProtocolFactory,
+ outputProtocolFactory,
+ )
+}
+
+func NewTSimpleServerFactory2(processorFactory TProcessorFactory, serverTransport TServerTransport) *TSimpleServer {
+ return NewTSimpleServerFactory6(processorFactory,
+ serverTransport,
+ NewTTransportFactory(),
+ NewTTransportFactory(),
+ NewTBinaryProtocolFactoryDefault(),
+ NewTBinaryProtocolFactoryDefault(),
+ )
+}
+
+func NewTSimpleServerFactory4(processorFactory TProcessorFactory, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer {
+ return NewTSimpleServerFactory6(processorFactory,
+ serverTransport,
+ transportFactory,
+ transportFactory,
+ protocolFactory,
+ protocolFactory,
+ )
+}
+
+func NewTSimpleServerFactory6(processorFactory TProcessorFactory, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer {
+ return &TSimpleServer{
+ processorFactory: processorFactory,
+ serverTransport: serverTransport,
+ inputTransportFactory: inputTransportFactory,
+ outputTransportFactory: outputTransportFactory,
+ inputProtocolFactory: inputProtocolFactory,
+ outputProtocolFactory: outputProtocolFactory,
+ }
+}
+
+func (p *TSimpleServer) ProcessorFactory() TProcessorFactory {
+ return p.processorFactory
+}
+
+func (p *TSimpleServer) ServerTransport() TServerTransport {
+ return p.serverTransport
+}
+
+func (p *TSimpleServer) InputTransportFactory() TTransportFactory {
+ return p.inputTransportFactory
+}
+
+func (p *TSimpleServer) OutputTransportFactory() TTransportFactory {
+ return p.outputTransportFactory
+}
+
+func (p *TSimpleServer) InputProtocolFactory() TProtocolFactory {
+ return p.inputProtocolFactory
+}
+
+func (p *TSimpleServer) OutputProtocolFactory() TProtocolFactory {
+ return p.outputProtocolFactory
+}
+
+func (p *TSimpleServer) Listen() error {
+ return p.serverTransport.Listen()
+}
+
+// SetForwardHeaders sets the list of header keys that will be auto forwarded
+// while using THeaderProtocol.
+//
+// "forward" means that when the server is also a client to other upstream
+// thrift servers, the context object user gets in the processor functions will
+// have both read and write headers set, with write headers being forwarded.
+// Users can always override the write headers by calling SetWriteHeaderList
+// before calling thrift client functions.
+func (p *TSimpleServer) SetForwardHeaders(headers []string) {
+ size := len(headers)
+ if size == 0 {
+ p.forwardHeaders = nil
+ return
+ }
+
+ keys := make([]string, size)
+ copy(keys, headers)
+ p.forwardHeaders = keys
+}
+
+func (p *TSimpleServer) innerAccept() (int32, error) {
+ client, err := p.serverTransport.Accept()
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ closed := atomic.LoadInt32(&p.closed)
+ if closed != 0 {
+ return closed, nil
+ }
+ if err != nil {
+ return 0, err
+ }
+ if client != nil {
+ p.wg.Add(1)
+ go func() {
+ defer p.wg.Done()
+ if err := p.processRequests(client); err != nil {
+ log.Println("error processing request:", err)
+ }
+ }()
+ }
+ return 0, nil
+}
+
+func (p *TSimpleServer) AcceptLoop() error {
+ for {
+ closed, err := p.innerAccept()
+ if err != nil {
+ return err
+ }
+ if closed != 0 {
+ return nil
+ }
+ }
+}
+
+func (p *TSimpleServer) Serve() error {
+ err := p.Listen()
+ if err != nil {
+ return err
+ }
+ p.AcceptLoop()
+ return nil
+}
+
+func (p *TSimpleServer) Stop() error {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ if atomic.LoadInt32(&p.closed) != 0 {
+ return nil
+ }
+ atomic.StoreInt32(&p.closed, 1)
+ p.serverTransport.Interrupt()
+ p.wg.Wait()
+ return nil
+}
+
+func (p *TSimpleServer) processRequests(client TTransport) error {
+ processor := p.processorFactory.GetProcessor(client)
+ inputTransport, err := p.inputTransportFactory.GetTransport(client)
+ if err != nil {
+ return err
+ }
+ inputProtocol := p.inputProtocolFactory.GetProtocol(inputTransport)
+ var outputTransport TTransport
+ var outputProtocol TProtocol
+
+ // for THeaderProtocol, we must use the same protocol instance for
+ // input and output so that the response is in the same dialect that
+ // the server detected the request was in.
+ headerProtocol, ok := inputProtocol.(*THeaderProtocol)
+ if ok {
+ outputProtocol = inputProtocol
+ } else {
+ oTrans, err := p.outputTransportFactory.GetTransport(client)
+ if err != nil {
+ return err
+ }
+ outputTransport = oTrans
+ outputProtocol = p.outputProtocolFactory.GetProtocol(outputTransport)
+ }
+
+ defer func() {
+ if e := recover(); e != nil {
+ log.Printf("panic in processor: %s: %s", e, debug.Stack())
+ }
+ }()
+
+ if inputTransport != nil {
+ defer inputTransport.Close()
+ }
+ if outputTransport != nil {
+ defer outputTransport.Close()
+ }
+ for {
+ if atomic.LoadInt32(&p.closed) != 0 {
+ return nil
+ }
+
+ ctx := defaultCtx
+ if headerProtocol != nil {
+ // We need to call ReadFrame here, otherwise we won't
+ // get any headers on the AddReadTHeaderToContext call.
+ //
+ // ReadFrame is safe to be called multiple times so it
+ // won't break when it's called again later when we
+ // actually start to read the message.
+ if err := headerProtocol.ReadFrame(); err != nil {
+ return err
+ }
+ ctx = AddReadTHeaderToContext(defaultCtx, headerProtocol.GetReadHeaders())
+ ctx = SetWriteHeaderList(ctx, p.forwardHeaders)
+ }
+
+ ok, err := processor.Process(ctx, inputProtocol, outputProtocol)
+ if err, ok := err.(TTransportException); ok && err.TypeId() == END_OF_FILE {
+ return nil
+ } else if err != nil {
+ return err
+ }
+ if err, ok := err.(TApplicationException); ok && err.TypeId() == UNKNOWN_METHOD {
+ continue
+ }
+ if !ok {
+ break
+ }
+ }
+ return nil
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/simple_server_test.go b/src/jaegertracing/thrift/lib/go/thrift/simple_server_test.go
new file mode 100644
index 000000000..58149a8e6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/simple_server_test.go
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "testing"
+ "errors"
+ "runtime"
+)
+
+type mockServerTransport struct {
+ ListenFunc func() error
+ AcceptFunc func() (TTransport, error)
+ CloseFunc func() error
+ InterruptFunc func() error
+}
+
+func (m *mockServerTransport) Listen() error {
+ return m.ListenFunc()
+}
+
+func (m *mockServerTransport) Accept() (TTransport, error) {
+ return m.AcceptFunc()
+}
+
+func (m *mockServerTransport) Close() error {
+ return m.CloseFunc()
+}
+
+func (m *mockServerTransport) Interrupt() error {
+ return m.InterruptFunc()
+}
+
+type mockTTransport struct {
+ TTransport
+}
+
+func (m *mockTTransport) Close() error {
+ return nil
+}
+
+func TestMultipleStop(t *testing.T) {
+ proc := &mockProcessor{
+ ProcessFunc: func(in, out TProtocol) (bool, TException) {
+ return false, nil
+ },
+ }
+
+ var interruptCalled bool
+ c := make(chan struct{})
+ trans := &mockServerTransport{
+ ListenFunc: func() error {
+ return nil
+ },
+ AcceptFunc: func() (TTransport, error) {
+ <-c
+ return nil, nil
+ },
+ CloseFunc: func() error {
+ c <- struct{}{}
+ return nil
+ },
+ InterruptFunc: func() error {
+ interruptCalled = true
+ return nil
+ },
+ }
+
+ serv := NewTSimpleServer2(proc, trans)
+ go serv.Serve()
+ serv.Stop()
+ if !interruptCalled {
+ t.Error("first server transport should have been interrupted")
+ }
+
+ serv = NewTSimpleServer2(proc, trans)
+ interruptCalled = false
+ go serv.Serve()
+ serv.Stop()
+ if !interruptCalled {
+ t.Error("second server transport should have been interrupted")
+ }
+}
+
+func TestWaitRace(t *testing.T) {
+ proc := &mockProcessor{
+ ProcessFunc: func(in, out TProtocol) (bool, TException) {
+ return false, nil
+ },
+ }
+
+ trans := &mockServerTransport{
+ ListenFunc: func() error {
+ return nil
+ },
+ AcceptFunc: func() (TTransport, error) {
+ return &mockTTransport{}, nil
+ },
+ CloseFunc: func() error {
+ return nil
+ },
+ InterruptFunc: func() error {
+ return nil
+ },
+ }
+
+ serv := NewTSimpleServer2(proc, trans)
+ go serv.Serve()
+ runtime.Gosched()
+ serv.Stop()
+}
+
+func TestNoHangDuringStopFromDanglingLockAcquireDuringAcceptLoop(t *testing.T) {
+ proc := &mockProcessor{
+ ProcessFunc: func(in, out TProtocol) (bool, TException) {
+ return false, nil
+ },
+ }
+
+ trans := &mockServerTransport{
+ ListenFunc: func() error {
+ return nil
+ },
+ AcceptFunc: func() (TTransport, error) {
+ return nil, errors.New("no sir")
+ },
+ CloseFunc: func() error {
+ return nil
+ },
+ InterruptFunc: func() error {
+ return nil
+ },
+ }
+
+ serv := NewTSimpleServer2(proc, trans)
+ go serv.Serve()
+ runtime.Gosched()
+ serv.Stop()
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/socket.go b/src/jaegertracing/thrift/lib/go/thrift/socket.go
new file mode 100644
index 000000000..88b98f591
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/socket.go
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+ "net"
+ "time"
+)
+
+type TSocket struct {
+ conn net.Conn
+ addr net.Addr
+ timeout time.Duration
+}
+
+// NewTSocket creates a net.Conn-backed TTransport, given a host and port
+//
+// Example:
+// trans, err := thrift.NewTSocket("localhost:9090")
+func NewTSocket(hostPort string) (*TSocket, error) {
+ return NewTSocketTimeout(hostPort, 0)
+}
+
+// NewTSocketTimeout creates a net.Conn-backed TTransport, given a host and port
+// it also accepts a timeout as a time.Duration
+func NewTSocketTimeout(hostPort string, timeout time.Duration) (*TSocket, error) {
+ //conn, err := net.DialTimeout(network, address, timeout)
+ addr, err := net.ResolveTCPAddr("tcp", hostPort)
+ if err != nil {
+ return nil, err
+ }
+ return NewTSocketFromAddrTimeout(addr, timeout), nil
+}
+
+// Creates a TSocket from a net.Addr
+func NewTSocketFromAddrTimeout(addr net.Addr, timeout time.Duration) *TSocket {
+ return &TSocket{addr: addr, timeout: timeout}
+}
+
+// Creates a TSocket from an existing net.Conn
+func NewTSocketFromConnTimeout(conn net.Conn, timeout time.Duration) *TSocket {
+ return &TSocket{conn: conn, addr: conn.RemoteAddr(), timeout: timeout}
+}
+
+// Sets the socket timeout
+func (p *TSocket) SetTimeout(timeout time.Duration) error {
+ p.timeout = timeout
+ return nil
+}
+
+func (p *TSocket) pushDeadline(read, write bool) {
+ var t time.Time
+ if p.timeout > 0 {
+ t = time.Now().Add(time.Duration(p.timeout))
+ }
+ if read && write {
+ p.conn.SetDeadline(t)
+ } else if read {
+ p.conn.SetReadDeadline(t)
+ } else if write {
+ p.conn.SetWriteDeadline(t)
+ }
+}
+
+// Connects the socket, creating a new socket object if necessary.
+func (p *TSocket) Open() error {
+ if p.IsOpen() {
+ return NewTTransportException(ALREADY_OPEN, "Socket already connected.")
+ }
+ if p.addr == nil {
+ return NewTTransportException(NOT_OPEN, "Cannot open nil address.")
+ }
+ if len(p.addr.Network()) == 0 {
+ return NewTTransportException(NOT_OPEN, "Cannot open bad network name.")
+ }
+ if len(p.addr.String()) == 0 {
+ return NewTTransportException(NOT_OPEN, "Cannot open bad address.")
+ }
+ var err error
+ if p.conn, err = net.DialTimeout(p.addr.Network(), p.addr.String(), p.timeout); err != nil {
+ return NewTTransportException(NOT_OPEN, err.Error())
+ }
+ return nil
+}
+
+// Retrieve the underlying net.Conn
+func (p *TSocket) Conn() net.Conn {
+ return p.conn
+}
+
+// Returns true if the connection is open
+func (p *TSocket) IsOpen() bool {
+ if p.conn == nil {
+ return false
+ }
+ return true
+}
+
+// Closes the socket.
+func (p *TSocket) Close() error {
+ // Close the socket
+ if p.conn != nil {
+ err := p.conn.Close()
+ if err != nil {
+ return err
+ }
+ p.conn = nil
+ }
+ return nil
+}
+
+//Returns the remote address of the socket.
+func (p *TSocket) Addr() net.Addr {
+ return p.addr
+}
+
+func (p *TSocket) Read(buf []byte) (int, error) {
+ if !p.IsOpen() {
+ return 0, NewTTransportException(NOT_OPEN, "Connection not open")
+ }
+ p.pushDeadline(true, false)
+ n, err := p.conn.Read(buf)
+ return n, NewTTransportExceptionFromError(err)
+}
+
+func (p *TSocket) Write(buf []byte) (int, error) {
+ if !p.IsOpen() {
+ return 0, NewTTransportException(NOT_OPEN, "Connection not open")
+ }
+ p.pushDeadline(false, true)
+ return p.conn.Write(buf)
+}
+
+func (p *TSocket) Flush(ctx context.Context) error {
+ return nil
+}
+
+func (p *TSocket) Interrupt() error {
+ if !p.IsOpen() {
+ return nil
+ }
+ return p.conn.Close()
+}
+
+func (p *TSocket) RemainingBytes() (num_bytes uint64) {
+ const maxSize = ^uint64(0)
+ return maxSize // the truth is, we just don't know unless framed is used
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/ssl_server_socket.go b/src/jaegertracing/thrift/lib/go/thrift/ssl_server_socket.go
new file mode 100644
index 000000000..907afca32
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/ssl_server_socket.go
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "crypto/tls"
+ "net"
+ "time"
+)
+
+type TSSLServerSocket struct {
+ listener net.Listener
+ addr net.Addr
+ clientTimeout time.Duration
+ interrupted bool
+ cfg *tls.Config
+}
+
+func NewTSSLServerSocket(listenAddr string, cfg *tls.Config) (*TSSLServerSocket, error) {
+ return NewTSSLServerSocketTimeout(listenAddr, cfg, 0)
+}
+
+func NewTSSLServerSocketTimeout(listenAddr string, cfg *tls.Config, clientTimeout time.Duration) (*TSSLServerSocket, error) {
+ if cfg.MinVersion == 0 {
+ cfg.MinVersion = tls.VersionTLS10
+ }
+ addr, err := net.ResolveTCPAddr("tcp", listenAddr)
+ if err != nil {
+ return nil, err
+ }
+ return &TSSLServerSocket{addr: addr, clientTimeout: clientTimeout, cfg: cfg}, nil
+}
+
+func (p *TSSLServerSocket) Listen() error {
+ if p.IsListening() {
+ return nil
+ }
+ l, err := tls.Listen(p.addr.Network(), p.addr.String(), p.cfg)
+ if err != nil {
+ return err
+ }
+ p.listener = l
+ return nil
+}
+
+func (p *TSSLServerSocket) Accept() (TTransport, error) {
+ if p.interrupted {
+ return nil, errTransportInterrupted
+ }
+ if p.listener == nil {
+ return nil, NewTTransportException(NOT_OPEN, "No underlying server socket")
+ }
+ conn, err := p.listener.Accept()
+ if err != nil {
+ return nil, NewTTransportExceptionFromError(err)
+ }
+ return NewTSSLSocketFromConnTimeout(conn, p.cfg, p.clientTimeout), nil
+}
+
+// Checks whether the socket is listening.
+func (p *TSSLServerSocket) IsListening() bool {
+ return p.listener != nil
+}
+
+// Connects the socket, creating a new socket object if necessary.
+func (p *TSSLServerSocket) Open() error {
+ if p.IsListening() {
+ return NewTTransportException(ALREADY_OPEN, "Server socket already open")
+ }
+ if l, err := tls.Listen(p.addr.Network(), p.addr.String(), p.cfg); err != nil {
+ return err
+ } else {
+ p.listener = l
+ }
+ return nil
+}
+
+func (p *TSSLServerSocket) Addr() net.Addr {
+ return p.addr
+}
+
+func (p *TSSLServerSocket) Close() error {
+ defer func() {
+ p.listener = nil
+ }()
+ if p.IsListening() {
+ return p.listener.Close()
+ }
+ return nil
+}
+
+func (p *TSSLServerSocket) Interrupt() error {
+ p.interrupted = true
+ return nil
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/ssl_socket.go b/src/jaegertracing/thrift/lib/go/thrift/ssl_socket.go
new file mode 100644
index 000000000..ba6337726
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/ssl_socket.go
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+ "crypto/tls"
+ "net"
+ "time"
+)
+
+type TSSLSocket struct {
+ conn net.Conn
+ // hostPort contains host:port (e.g. "asdf.com:12345"). The field is
+ // only valid if addr is nil.
+ hostPort string
+ // addr is nil when hostPort is not "", and is only used when the
+ // TSSLSocket is constructed from a net.Addr.
+ addr net.Addr
+ timeout time.Duration
+ cfg *tls.Config
+}
+
+// NewTSSLSocket creates a net.Conn-backed TTransport, given a host and port and tls Configuration
+//
+// Example:
+// trans, err := thrift.NewTSSLSocket("localhost:9090", nil)
+func NewTSSLSocket(hostPort string, cfg *tls.Config) (*TSSLSocket, error) {
+ return NewTSSLSocketTimeout(hostPort, cfg, 0)
+}
+
+// NewTSSLSocketTimeout creates a net.Conn-backed TTransport, given a host and port
+// it also accepts a tls Configuration and a timeout as a time.Duration
+func NewTSSLSocketTimeout(hostPort string, cfg *tls.Config, timeout time.Duration) (*TSSLSocket, error) {
+ if cfg.MinVersion == 0 {
+ cfg.MinVersion = tls.VersionTLS10
+ }
+ return &TSSLSocket{hostPort: hostPort, timeout: timeout, cfg: cfg}, nil
+}
+
+// Creates a TSSLSocket from a net.Addr
+func NewTSSLSocketFromAddrTimeout(addr net.Addr, cfg *tls.Config, timeout time.Duration) *TSSLSocket {
+ return &TSSLSocket{addr: addr, timeout: timeout, cfg: cfg}
+}
+
+// Creates a TSSLSocket from an existing net.Conn
+func NewTSSLSocketFromConnTimeout(conn net.Conn, cfg *tls.Config, timeout time.Duration) *TSSLSocket {
+ return &TSSLSocket{conn: conn, addr: conn.RemoteAddr(), timeout: timeout, cfg: cfg}
+}
+
+// Sets the socket timeout
+func (p *TSSLSocket) SetTimeout(timeout time.Duration) error {
+ p.timeout = timeout
+ return nil
+}
+
+func (p *TSSLSocket) pushDeadline(read, write bool) {
+ var t time.Time
+ if p.timeout > 0 {
+ t = time.Now().Add(time.Duration(p.timeout))
+ }
+ if read && write {
+ p.conn.SetDeadline(t)
+ } else if read {
+ p.conn.SetReadDeadline(t)
+ } else if write {
+ p.conn.SetWriteDeadline(t)
+ }
+}
+
+// Connects the socket, creating a new socket object if necessary.
+func (p *TSSLSocket) Open() error {
+ var err error
+ // If we have a hostname, we need to pass the hostname to tls.Dial for
+ // certificate hostname checks.
+ if p.hostPort != "" {
+ if p.conn, err = tls.DialWithDialer(&net.Dialer{
+ Timeout: p.timeout}, "tcp", p.hostPort, p.cfg); err != nil {
+ return NewTTransportException(NOT_OPEN, err.Error())
+ }
+ } else {
+ if p.IsOpen() {
+ return NewTTransportException(ALREADY_OPEN, "Socket already connected.")
+ }
+ if p.addr == nil {
+ return NewTTransportException(NOT_OPEN, "Cannot open nil address.")
+ }
+ if len(p.addr.Network()) == 0 {
+ return NewTTransportException(NOT_OPEN, "Cannot open bad network name.")
+ }
+ if len(p.addr.String()) == 0 {
+ return NewTTransportException(NOT_OPEN, "Cannot open bad address.")
+ }
+ if p.conn, err = tls.DialWithDialer(&net.Dialer{
+ Timeout: p.timeout}, p.addr.Network(), p.addr.String(), p.cfg); err != nil {
+ return NewTTransportException(NOT_OPEN, err.Error())
+ }
+ }
+ return nil
+}
+
+// Retrieve the underlying net.Conn
+func (p *TSSLSocket) Conn() net.Conn {
+ return p.conn
+}
+
+// Returns true if the connection is open
+func (p *TSSLSocket) IsOpen() bool {
+ if p.conn == nil {
+ return false
+ }
+ return true
+}
+
+// Closes the socket.
+func (p *TSSLSocket) Close() error {
+ // Close the socket
+ if p.conn != nil {
+ err := p.conn.Close()
+ if err != nil {
+ return err
+ }
+ p.conn = nil
+ }
+ return nil
+}
+
+func (p *TSSLSocket) Read(buf []byte) (int, error) {
+ if !p.IsOpen() {
+ return 0, NewTTransportException(NOT_OPEN, "Connection not open")
+ }
+ p.pushDeadline(true, false)
+ n, err := p.conn.Read(buf)
+ return n, NewTTransportExceptionFromError(err)
+}
+
+func (p *TSSLSocket) Write(buf []byte) (int, error) {
+ if !p.IsOpen() {
+ return 0, NewTTransportException(NOT_OPEN, "Connection not open")
+ }
+ p.pushDeadline(false, true)
+ return p.conn.Write(buf)
+}
+
+func (p *TSSLSocket) Flush(ctx context.Context) error {
+ return nil
+}
+
+func (p *TSSLSocket) Interrupt() error {
+ if !p.IsOpen() {
+ return nil
+ }
+ return p.conn.Close()
+}
+
+func (p *TSSLSocket) RemainingBytes() (num_bytes uint64) {
+ const maxSize = ^uint64(0)
+ return maxSize // the thruth is, we just don't know unless framed is used
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/transport.go b/src/jaegertracing/thrift/lib/go/thrift/transport.go
new file mode 100644
index 000000000..ba2738a8d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/transport.go
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+ "errors"
+ "io"
+)
+
+var errTransportInterrupted = errors.New("Transport Interrupted")
+
+type Flusher interface {
+ Flush() (err error)
+}
+
+type ContextFlusher interface {
+ Flush(ctx context.Context) (err error)
+}
+
+type ReadSizeProvider interface {
+ RemainingBytes() (num_bytes uint64)
+}
+
+// Encapsulates the I/O layer
+type TTransport interface {
+ io.ReadWriteCloser
+ ContextFlusher
+ ReadSizeProvider
+
+ // Opens the transport for communication
+ Open() error
+
+ // Returns true if the transport is open
+ IsOpen() bool
+}
+
+type stringWriter interface {
+ WriteString(s string) (n int, err error)
+}
+
+// This is "enchanced" transport with extra capabilities. You need to use one of these
+// to construct protocol.
+// Notably, TSocket does not implement this interface, and it is always a mistake to use
+// TSocket directly in protocol.
+type TRichTransport interface {
+ io.ReadWriter
+ io.ByteReader
+ io.ByteWriter
+ stringWriter
+ ContextFlusher
+ ReadSizeProvider
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/transport_exception.go b/src/jaegertracing/thrift/lib/go/thrift/transport_exception.go
new file mode 100644
index 000000000..9505b4461
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/transport_exception.go
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "errors"
+ "io"
+)
+
+type timeoutable interface {
+ Timeout() bool
+}
+
+// Thrift Transport exception
+type TTransportException interface {
+ TException
+ TypeId() int
+ Err() error
+}
+
+const (
+ UNKNOWN_TRANSPORT_EXCEPTION = 0
+ NOT_OPEN = 1
+ ALREADY_OPEN = 2
+ TIMED_OUT = 3
+ END_OF_FILE = 4
+)
+
+type tTransportException struct {
+ typeId int
+ err error
+}
+
+func (p *tTransportException) TypeId() int {
+ return p.typeId
+}
+
+func (p *tTransportException) Error() string {
+ return p.err.Error()
+}
+
+func (p *tTransportException) Err() error {
+ return p.err
+}
+
+func NewTTransportException(t int, e string) TTransportException {
+ return &tTransportException{typeId: t, err: errors.New(e)}
+}
+
+func NewTTransportExceptionFromError(e error) TTransportException {
+ if e == nil {
+ return nil
+ }
+
+ if t, ok := e.(TTransportException); ok {
+ return t
+ }
+
+ switch v := e.(type) {
+ case TTransportException:
+ return v
+ case timeoutable:
+ if v.Timeout() {
+ return &tTransportException{typeId: TIMED_OUT, err: e}
+ }
+ }
+
+ if e == io.EOF {
+ return &tTransportException{typeId: END_OF_FILE, err: e}
+ }
+
+ return &tTransportException{typeId: UNKNOWN_TRANSPORT_EXCEPTION, err: e}
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/transport_exception_test.go b/src/jaegertracing/thrift/lib/go/thrift/transport_exception_test.go
new file mode 100644
index 000000000..b44314f49
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/transport_exception_test.go
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "fmt"
+ "io"
+
+ "testing"
+)
+
+type timeout struct{ timedout bool }
+
+func (t *timeout) Timeout() bool {
+ return t.timedout
+}
+
+func (t *timeout) Error() string {
+ return fmt.Sprintf("Timeout: %v", t.timedout)
+}
+
+func TestTExceptionTimeout(t *testing.T) {
+ timeout := &timeout{true}
+ exception := NewTTransportExceptionFromError(timeout)
+ if timeout.Error() != exception.Error() {
+ t.Fatalf("Error did not match: expected %q, got %q", timeout.Error(), exception.Error())
+ }
+
+ if exception.TypeId() != TIMED_OUT {
+ t.Fatalf("TypeId was not TIMED_OUT: expected %v, got %v", TIMED_OUT, exception.TypeId())
+ }
+}
+
+func TestTExceptionEOF(t *testing.T) {
+ exception := NewTTransportExceptionFromError(io.EOF)
+ if io.EOF.Error() != exception.Error() {
+ t.Fatalf("Error did not match: expected %q, got %q", io.EOF.Error(), exception.Error())
+ }
+
+ if exception.TypeId() != END_OF_FILE {
+ t.Fatalf("TypeId was not END_OF_FILE: expected %v, got %v", END_OF_FILE, exception.TypeId())
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/transport_factory.go b/src/jaegertracing/thrift/lib/go/thrift/transport_factory.go
new file mode 100644
index 000000000..c80580794
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/transport_factory.go
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+// Factory class used to create wrapped instance of Transports.
+// This is used primarily in servers, which get Transports from
+// a ServerTransport and then may want to mutate them (i.e. create
+// a BufferedTransport from the underlying base transport)
+type TTransportFactory interface {
+ GetTransport(trans TTransport) (TTransport, error)
+}
+
+type tTransportFactory struct{}
+
+// Return a wrapped instance of the base Transport.
+func (p *tTransportFactory) GetTransport(trans TTransport) (TTransport, error) {
+ return trans, nil
+}
+
+func NewTTransportFactory() TTransportFactory {
+ return &tTransportFactory{}
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/transport_test.go b/src/jaegertracing/thrift/lib/go/thrift/transport_test.go
new file mode 100644
index 000000000..01278038e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/transport_test.go
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+ "io"
+ "net"
+ "strconv"
+ "testing"
+)
+
+const TRANSPORT_BINARY_DATA_SIZE = 4096
+
+var (
+ transport_bdata []byte // test data for writing; same as data
+ transport_header map[string]string
+)
+
+func init() {
+ transport_bdata = make([]byte, TRANSPORT_BINARY_DATA_SIZE)
+ for i := 0; i < TRANSPORT_BINARY_DATA_SIZE; i++ {
+ transport_bdata[i] = byte((i + 'a') % 255)
+ }
+ transport_header = map[string]string{"key": "User-Agent",
+ "value": "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36"}
+}
+
+func TransportTest(t *testing.T, writeTrans TTransport, readTrans TTransport) {
+ buf := make([]byte, TRANSPORT_BINARY_DATA_SIZE)
+ if !writeTrans.IsOpen() {
+ t.Fatalf("Transport %T not open: %s", writeTrans, writeTrans)
+ }
+ if !readTrans.IsOpen() {
+ t.Fatalf("Transport %T not open: %s", readTrans, readTrans)
+ }
+ _, err := writeTrans.Write(transport_bdata)
+ if err != nil {
+ t.Fatalf("Transport %T cannot write binary data of length %d: %s", writeTrans, len(transport_bdata), err)
+ }
+ err = writeTrans.Flush(context.Background())
+ if err != nil {
+ t.Fatalf("Transport %T cannot flush write of binary data: %s", writeTrans, err)
+ }
+ n, err := io.ReadFull(readTrans, buf)
+ if err != nil {
+ t.Errorf("Transport %T cannot read binary data of length %d: %s", readTrans, TRANSPORT_BINARY_DATA_SIZE, err)
+ }
+ if n != TRANSPORT_BINARY_DATA_SIZE {
+ t.Errorf("Transport %T read only %d instead of %d bytes of binary data", readTrans, n, TRANSPORT_BINARY_DATA_SIZE)
+ }
+ for k, v := range buf {
+ if v != transport_bdata[k] {
+ t.Fatalf("Transport %T read %d instead of %d for index %d of binary data 2", readTrans, v, transport_bdata[k], k)
+ }
+ }
+ _, err = writeTrans.Write(transport_bdata)
+ if err != nil {
+ t.Fatalf("Transport %T cannot write binary data 2 of length %d: %s", writeTrans, len(transport_bdata), err)
+ }
+ err = writeTrans.Flush(context.Background())
+ if err != nil {
+ t.Fatalf("Transport %T cannot flush write binary data 2: %s", writeTrans, err)
+ }
+ buf = make([]byte, TRANSPORT_BINARY_DATA_SIZE)
+ read := 1
+ for n = 0; n < TRANSPORT_BINARY_DATA_SIZE && read != 0; {
+ read, err = readTrans.Read(buf[n:])
+ if err != nil {
+ t.Errorf("Transport %T cannot read binary data 2 of total length %d from offset %d: %s", readTrans, TRANSPORT_BINARY_DATA_SIZE, n, err)
+ }
+ n += read
+ }
+ if n != TRANSPORT_BINARY_DATA_SIZE {
+ t.Errorf("Transport %T read only %d instead of %d bytes of binary data 2", readTrans, n, TRANSPORT_BINARY_DATA_SIZE)
+ }
+ for k, v := range buf {
+ if v != transport_bdata[k] {
+ t.Fatalf("Transport %T read %d instead of %d for index %d of binary data 2", readTrans, v, transport_bdata[k], k)
+ }
+ }
+}
+
+func TransportHeaderTest(t *testing.T, writeTrans TTransport, readTrans TTransport) {
+ buf := make([]byte, TRANSPORT_BINARY_DATA_SIZE)
+ if !writeTrans.IsOpen() {
+ t.Fatalf("Transport %T not open: %s", writeTrans, writeTrans)
+ }
+ if !readTrans.IsOpen() {
+ t.Fatalf("Transport %T not open: %s", readTrans, readTrans)
+ }
+ // Need to assert type of TTransport to THttpClient to expose the Setter
+ httpWPostTrans := writeTrans.(*THttpClient)
+ httpWPostTrans.SetHeader(transport_header["key"], transport_header["value"])
+
+ _, err := writeTrans.Write(transport_bdata)
+ if err != nil {
+ t.Fatalf("Transport %T cannot write binary data of length %d: %s", writeTrans, len(transport_bdata), err)
+ }
+ err = writeTrans.Flush(context.Background())
+ if err != nil {
+ t.Fatalf("Transport %T cannot flush write of binary data: %s", writeTrans, err)
+ }
+ // Need to assert type of TTransport to THttpClient to expose the Getter
+ httpRPostTrans := readTrans.(*THttpClient)
+ readHeader := httpRPostTrans.GetHeader(transport_header["key"])
+ if err != nil {
+ t.Errorf("Transport %T cannot read HTTP Header Value", httpRPostTrans)
+ }
+
+ if transport_header["value"] != readHeader {
+ t.Errorf("Expected HTTP Header Value %s, got %s", transport_header["value"], readHeader)
+ }
+ n, err := io.ReadFull(readTrans, buf)
+ if err != nil {
+ t.Errorf("Transport %T cannot read binary data of length %d: %s", readTrans, TRANSPORT_BINARY_DATA_SIZE, err)
+ }
+ if n != TRANSPORT_BINARY_DATA_SIZE {
+ t.Errorf("Transport %T read only %d instead of %d bytes of binary data", readTrans, n, TRANSPORT_BINARY_DATA_SIZE)
+ }
+ for k, v := range buf {
+ if v != transport_bdata[k] {
+ t.Fatalf("Transport %T read %d instead of %d for index %d of binary data 2", readTrans, v, transport_bdata[k], k)
+ }
+ }
+}
+
+func CloseTransports(t *testing.T, readTrans TTransport, writeTrans TTransport) {
+ err := readTrans.Close()
+ if err != nil {
+ t.Errorf("Transport %T cannot close read transport: %s", readTrans, err)
+ }
+ if writeTrans != readTrans {
+ err = writeTrans.Close()
+ if err != nil {
+ t.Errorf("Transport %T cannot close write transport: %s", writeTrans, err)
+ }
+ }
+}
+
+func FindAvailableTCPServerPort(startPort int) (net.Addr, error) {
+ for i := startPort; i < 65535; i++ {
+ s := "127.0.0.1:" + strconv.Itoa(i)
+ l, err := net.Listen("tcp", s)
+ if err == nil {
+ l.Close()
+ return net.ResolveTCPAddr("tcp", s)
+ }
+ }
+ return nil, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "Could not find available server port")
+}
+
+func valueInSlice(value string, slice []string) bool {
+ for _, v := range slice {
+ if value == v {
+ return true
+ }
+ }
+ return false
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/type.go b/src/jaegertracing/thrift/lib/go/thrift/type.go
new file mode 100644
index 000000000..4292ffcad
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/type.go
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+// Type constants in the Thrift protocol
+type TType byte
+
+const (
+ STOP = 0
+ VOID = 1
+ BOOL = 2
+ BYTE = 3
+ I08 = 3
+ DOUBLE = 4
+ I16 = 6
+ I32 = 8
+ I64 = 10
+ STRING = 11
+ UTF7 = 11
+ STRUCT = 12
+ MAP = 13
+ SET = 14
+ LIST = 15
+ UTF8 = 16
+ UTF16 = 17
+ //BINARY = 18 wrong and unusued
+)
+
+var typeNames = map[int]string{
+ STOP: "STOP",
+ VOID: "VOID",
+ BOOL: "BOOL",
+ BYTE: "BYTE",
+ DOUBLE: "DOUBLE",
+ I16: "I16",
+ I32: "I32",
+ I64: "I64",
+ STRING: "STRING",
+ STRUCT: "STRUCT",
+ MAP: "MAP",
+ SET: "SET",
+ LIST: "LIST",
+ UTF8: "UTF8",
+ UTF16: "UTF16",
+}
+
+func (p TType) String() string {
+ if s, ok := typeNames[int(p)]; ok {
+ return s
+ }
+ return "Unknown"
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/zlib_transport.go b/src/jaegertracing/thrift/lib/go/thrift/zlib_transport.go
new file mode 100644
index 000000000..f3d42673a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/zlib_transport.go
@@ -0,0 +1,132 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+ */
+
+package thrift
+
+import (
+ "compress/zlib"
+ "context"
+ "io"
+ "log"
+)
+
+// TZlibTransportFactory is a factory for TZlibTransport instances
+type TZlibTransportFactory struct {
+ level int
+ factory TTransportFactory
+}
+
+// TZlibTransport is a TTransport implementation that makes use of zlib compression.
+type TZlibTransport struct {
+ reader io.ReadCloser
+ transport TTransport
+ writer *zlib.Writer
+}
+
+// GetTransport constructs a new instance of NewTZlibTransport
+func (p *TZlibTransportFactory) GetTransport(trans TTransport) (TTransport, error) {
+ if p.factory != nil {
+ // wrap other factory
+ var err error
+ trans, err = p.factory.GetTransport(trans)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return NewTZlibTransport(trans, p.level)
+}
+
+// NewTZlibTransportFactory constructs a new instance of NewTZlibTransportFactory
+func NewTZlibTransportFactory(level int) *TZlibTransportFactory {
+ return &TZlibTransportFactory{level: level, factory: nil}
+}
+
+// NewTZlibTransportFactory constructs a new instance of TZlibTransportFactory
+// as a wrapper over existing transport factory
+func NewTZlibTransportFactoryWithFactory(level int, factory TTransportFactory) *TZlibTransportFactory {
+ return &TZlibTransportFactory{level: level, factory: factory}
+}
+
+// NewTZlibTransport constructs a new instance of TZlibTransport
+func NewTZlibTransport(trans TTransport, level int) (*TZlibTransport, error) {
+ w, err := zlib.NewWriterLevel(trans, level)
+ if err != nil {
+ log.Println(err)
+ return nil, err
+ }
+
+ return &TZlibTransport{
+ writer: w,
+ transport: trans,
+ }, nil
+}
+
+// Close closes the reader and writer (flushing any unwritten data) and closes
+// the underlying transport.
+func (z *TZlibTransport) Close() error {
+ if z.reader != nil {
+ if err := z.reader.Close(); err != nil {
+ return err
+ }
+ }
+ if err := z.writer.Close(); err != nil {
+ return err
+ }
+ return z.transport.Close()
+}
+
+// Flush flushes the writer and its underlying transport.
+func (z *TZlibTransport) Flush(ctx context.Context) error {
+ if err := z.writer.Flush(); err != nil {
+ return err
+ }
+ return z.transport.Flush(ctx)
+}
+
+// IsOpen returns true if the transport is open
+func (z *TZlibTransport) IsOpen() bool {
+ return z.transport.IsOpen()
+}
+
+// Open opens the transport for communication
+func (z *TZlibTransport) Open() error {
+ return z.transport.Open()
+}
+
+func (z *TZlibTransport) Read(p []byte) (int, error) {
+ if z.reader == nil {
+ r, err := zlib.NewReader(z.transport)
+ if err != nil {
+ return 0, NewTTransportExceptionFromError(err)
+ }
+ z.reader = r
+ }
+
+ return z.reader.Read(p)
+}
+
+// RemainingBytes returns the size in bytes of the data that is still to be
+// read.
+func (z *TZlibTransport) RemainingBytes() uint64 {
+ return z.transport.RemainingBytes()
+}
+
+func (z *TZlibTransport) Write(p []byte) (int, error) {
+ return z.writer.Write(p)
+}
diff --git a/src/jaegertracing/thrift/lib/go/thrift/zlib_transport_test.go b/src/jaegertracing/thrift/lib/go/thrift/zlib_transport_test.go
new file mode 100644
index 000000000..3c6f11eb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/go/thrift/zlib_transport_test.go
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "compress/zlib"
+ "testing"
+)
+
+func TestZlibTransport(t *testing.T) {
+ trans, err := NewTZlibTransport(NewTMemoryBuffer(), zlib.BestCompression)
+ if err != nil {
+ t.Fatal(err)
+ }
+ TransportTest(t, trans, trans)
+}
+
+type DummyTransportFactory struct{}
+
+func (p *DummyTransportFactory) GetTransport(trans TTransport) (TTransport, error) {
+ return NewTMemoryBuffer(), nil
+}
+
+func TestZlibFactoryTransportWithFactory(t *testing.T) {
+ factory := NewTZlibTransportFactoryWithFactory(
+ zlib.BestCompression,
+ &DummyTransportFactory{},
+ )
+ buffer := NewTMemoryBuffer()
+ trans, err := factory.GetTransport(buffer)
+ if err != nil {
+ t.Fatal(err)
+ }
+ TransportTest(t, trans, trans)
+}
+
+func TestZlibFactoryTransportWithoutFactory(t *testing.T) {
+ factory := NewTZlibTransportFactoryWithFactory(zlib.BestCompression, nil)
+ buffer := NewTMemoryBuffer()
+ trans, err := factory.GetTransport(buffer)
+ if err != nil {
+ t.Fatal(err)
+ }
+ TransportTest(t, trans, trans)
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/README.md b/src/jaegertracing/thrift/lib/haxe/README.md
new file mode 100644
index 000000000..c9f74b578
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/README.md
@@ -0,0 +1,162 @@
+Thrift Haxe Software Library
+
+License
+=======
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+Using Thrift with Haxe
+========================
+
+Haxe setup
+---------------
+
+Thrift requires Haxe 3.1.3. Installers for Windows and OSX
+platforms are available at `http://haxe.org/download`.
+
+Depending on the desired targets, you may have to install the appropriate HaxeLibs
+after installing Haxe itself. For example, if you plan to target C#, Java and C++,
+enter the following commands after installing Haxe:
+
+ haxelib install hxcpp
+ haxelib install hxjava
+ haxelib install hxcs
+
+For other targets, please consult the Haxe documentation whether or not any additional
+target libraries need to be installed and how to achieve this.
+
+
+Haxe on Linux
+---------------
+
+For Linux platforms it is recommended to use the distro-specific package
+manager, where possible. More detailed information can be found at the
+Haxe Linux download section: http://haxe.org/download/linux
+
+If you run into the error message
+
+ Uncaught exception - load.c(237) : Failed to load library : /usr/lib/neko/regexp.ndll
+ (libpcre.so.3: cannot open shared object file: No such file or directory)
+
+this can be solved depending on your OSes bitness by either
+
+ sudo ln -sf /usr/lib/libpcre.so.1 /usr/lib/libpcre.so.3
+ sudo ldconfig
+
+or
+
+ sudo ln -sf /usr/lib64/libpcre.so.1 /usr/lib64/libpcre.so.3
+ sudo ldconfig
+
+Thrift Haxe bindings
+-------------------
+
+Thrift Haxe bindings can be set up via the `haxelib` tool
+either from the official ASF repo, or via the github mirror.
+
+- To set up any **stable version**, choose the appropriate branch (e.g. `0.12.0`):
+
+ - `haxelib git thrift https://github.com/apache/thrift.git 0.12.0 lib/haxe`
+
+- To set up the current **development version**, use the `master` branch:
+
+ - `haxelib git thrift https://github.com/apache/thrift.git master lib/haxe`
+
+As usual, the installed library can be updated using `haxelib upgrade`
+or `haxelib update thrift`.
+
+In order to work with Thrift, you will need to install the Thrift compiler
+or build from source, depending on your operating system. Appropriate
+downloads and more information can be found at http://thrift.apache.org
+
+To get started, visit the /tutorial/haxe and /test/haxe dirs for examples.
+If you are using HIDE or the FlashDevelop IDE, you'll find appropriate
+project files in these folders.
+
+
+Current status
+========================
+- tested with Haxe C++ target
+- tested with Haxe PHP target (console/web server, binary protocols)
+- transports: Socket, HTTP (servers run inside PHP server/PHP target only), Stream
+- protocols: Binary, JSON, Multiplex, Compact
+- tutorial client and server available
+- cross-test client and server available
+
+
+Further developments
+========================
+- improve to work with C#, Java and JavaScript Haxe/OpenFL targets
+- improve to work with more (ideally all) Haxe/OpenFL targets
+- add HTTP server, update tutorial and tests accordingly
+
+
+Known restrictions
+========================
+
+Although designed with maximum portability in mind, for technical reasons some platforms
+may only support parts of the library, or not be compatible at all.
+
+Javascript:
+- tutorial fails to build because of unsupported Sys.args
+
+PHP HTTP Server notes
+========================
+
+- you have to import PHP files generated by haxe into PHP
+```php
+require_once dirname(__FILE__) . '/bin/php-web-server/Main-debug.php';
+```
+
+- trace() by default outputs into stdout (http response), so you have to redirect it to stderr or you own logs, something like
+```haxe
+//remap trace to error log
+haxe.Log.trace = function(v:Dynamic, ?infos:haxe.PosInfos)
+{
+ //simulate normal trace https://github.com/HaxeFoundation/haxe/blob/development/std/haxe/Log.hx
+ var newValue : Dynamic;
+ if (infos != null && infos.customParams!=null) {
+ var extra:String = "";
+ for( v in infos.customParams )
+ extra += "," + v;
+ newValue = v + extra;
+ }
+ else {
+ newValue = v;
+ }
+ var msg = infos != null ? infos.fileName + ':' + infos.lineNumber + ': ' : '';
+ Sys.stderr().writeString('${msg}${newValue}\n');
+}
+```
+
+- to allow thrift server to read/write HTTP request/response, it should be pointed out to php streams
+```haxe
+transport = new TWrappingServerTransport(
+ new TStreamTransport(
+ new TFileStream("php://input", Read),
+ new TFileStream("php://output", Append)
+ )
+ );
+```
+
+- TSimpleServer doesn't stop after first call, so processor.process() should be called instead, or use runOnce property
+```haxe
+var server = new TSimpleServer( processor, transport, transfactory, protfactory);
+server.runOnce = true;
+```
+
diff --git a/src/jaegertracing/thrift/lib/haxe/coding_standards.md b/src/jaegertracing/thrift/lib/haxe/coding_standards.md
new file mode 100644
index 000000000..fa0390bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/coding_standards.md
@@ -0,0 +1 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
diff --git a/src/jaegertracing/thrift/lib/haxe/haxelib.json b/src/jaegertracing/thrift/lib/haxe/haxelib.json
new file mode 100644
index 000000000..b3c15308d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/haxelib.json
@@ -0,0 +1,12 @@
+{
+ "name": "thrift",
+ "url" : "http://thrift.apache.org",
+ "license": "Apache",
+ "tags": ["thrift", "rpc", "serialization", "cross", "framework"],
+ "description": "Haxe bindings for the Apache Thrift RPC and serialization framework",
+ "version": "0.13.0",
+ "releasenote": "Licensed under Apache License, Version 2.0. The Apache Thrift compiler needs to be installed separately.",
+ "contributors": ["Apache Software Foundation (ASF)"],
+ "dependencies": { },
+ "classPath": "src"
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/AbstractMethodError.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/AbstractMethodError.hx
new file mode 100644
index 000000000..54b81534a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/AbstractMethodError.hx
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+#if flash
+import flash.errors.IllegalOperationError;
+#else
+import org.apache.thrift.TException;
+#end
+
+class AbstractMethodError
+#if flash
+extends IllegalOperationError
+#else
+extends TException
+#end
+{
+
+ public function new(message : String="") {
+ super("Attempt to call an abstract method");
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/ArgumentError.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/ArgumentError.hx
new file mode 100644
index 000000000..3ca04fdc4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/ArgumentError.hx
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+#if ! flash
+// predefined for flash only
+class ArgumentError extends TException {
+ public function new(msg : String = "") {
+ super(msg);
+ }
+}
+#end
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/Limits.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/Limits.hx
new file mode 100644
index 000000000..44eec3a61
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/Limits.hx
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+class Limits {
+
+ // Haxe limits are not fixed values, they depend on the target platform
+ // For example, neko limits an int to 31 bits instead of 32. So we detect
+ // the values once during intialisation in order to
+ // (a) get the right values for the current platform, and
+ // (b) prevent us from dependecies to a bunch of defines
+
+ public static var I32_MAX = {
+ var last : Int = 0;
+ var next : Int = 0;
+ for(bit in 0 ... 32) {
+ last = next;
+ next = last | (1 << bit);
+ if(next < 0) {
+ break;
+ }
+ }
+ last; // final value
+ }
+
+ // add whatever you need
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TApplicationException.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TApplicationException.hx
new file mode 100644
index 000000000..7fe844f28
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TApplicationException.hx
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TField;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolUtil;
+import org.apache.thrift.protocol.TStruct;
+import org.apache.thrift.protocol.TType;
+
+ /**
+ * Application level exception
+ */
+class TApplicationException extends TException {
+
+ private static var TAPPLICATION_EXCEPTION_STRUCT = { new TStruct("TApplicationException"); };
+ private static var MESSAGE_FIELD = { new TField("message", TType.STRING, 1); };
+ private static var TYPE_FIELD = { new TField("type", TType.I32, 2); };
+
+ // WARNING: These are subject to be extended in the future, so we can't use enums
+ // with Haxe 3.1.3 because of https://github.com/HaxeFoundation/haxe/issues/3649
+ public static inline var UNKNOWN : Int = 0;
+ public static inline var UNKNOWN_METHOD : Int = 1;
+ public static inline var INVALID_MESSAGE_TYPE : Int = 2;
+ public static inline var WRONG_METHOD_NAME : Int = 3;
+ public static inline var BAD_SEQUENCE_ID : Int = 4;
+ public static inline var MISSING_RESULT : Int = 5;
+ public static inline var INTERNAL_ERROR : Int = 6;
+ public static inline var PROTOCOL_ERROR : Int = 7;
+ public static inline var INVALID_TRANSFORM : Int = 8;
+ public static inline var INVALID_PROTOCOL : Int = 9;
+ public static inline var UNSUPPORTED_CLIENT_TYPE : Int = 10;
+
+ public function new(type : Int = UNKNOWN, message : String = "") {
+ super(message, type);
+ }
+
+ public static function read(iprot:TProtocol) : TApplicationException {
+ var field:TField;
+ iprot.readStructBegin();
+
+ var message : String = null;
+ var type : Int = UNKNOWN;
+
+ while (true) {
+ field = iprot.readFieldBegin();
+ if (field.type == TType.STOP) {
+ break;
+ }
+ switch (field.id) {
+ case 1:
+ if (field.type == TType.STRING) {
+ message = iprot.readString();
+ }
+ else {
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ case 2:
+ if (field.type == TType.I32) {
+ type = iprot.readI32();
+ }
+ else {
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ default:
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ iprot.readFieldEnd();
+ }
+ iprot.readStructEnd();
+ return new TApplicationException(type, message);
+ }
+
+ public function write(oprot:TProtocol) : Void {
+ oprot.writeStructBegin(TAPPLICATION_EXCEPTION_STRUCT);
+ if (errorMsg != null) {
+ oprot.writeFieldBegin(MESSAGE_FIELD);
+ oprot.writeString(errorMsg);
+ oprot.writeFieldEnd();
+ }
+ oprot.writeFieldBegin(TYPE_FIELD);
+ oprot.writeI32(errorID);
+ oprot.writeFieldEnd();
+ oprot.writeFieldStop();
+ oprot.writeStructEnd();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TBase.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TBase.hx
new file mode 100644
index 000000000..ede0beb4a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TBase.hx
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+// Make sure we use at least 3.1.3
+// Some Linux platforms have waaaay too old packages in their repos
+// Pro Tip: Look at http://openfl.com for a good Linux install script
+#if( haxe_ver < 3.103)
+#error Haxe 3.1.3 or newer required, sorry!
+#end
+
+import org.apache.thrift.protocol.TProtocol;
+
+ /**
+ * Generic base interface for generated Thrift objects.
+ *
+ */
+interface TBase {
+
+ /**
+ * Reads the TObject from the given input protocol.
+ *
+ * @param iprot Input protocol
+ */
+ function read(iprot:TProtocol) : Void;
+
+ /**
+ * Writes the objects out to the protocol
+ *
+ * @param oprot Output protocol
+ */
+ function write(oprot:TProtocol) : Void;
+
+ /**
+ * Check if a field is currently set or unset.
+ *
+ * @param fieldId The field's id tag as found in the IDL.
+ */
+ function isSet(fieldId : Int) : Bool;
+
+ /**
+ * Get a field's value by id. Primitive types will be wrapped in the
+ * appropriate "boxed" types.
+ *
+ * @param fieldId The field's id tag as found in the IDL.
+ */
+ function getFieldValue(fieldId : Int) : Dynamic;
+
+ /**
+ * Set a field's value by id. Primitive types must be "boxed" in the
+ * appropriate object wrapper type.
+ *
+ * @param fieldId The field's id tag as found in the IDL.
+ */
+ function setFieldValue(fieldId : Int, value : Dynamic) : Void;
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TException.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TException.hx
new file mode 100644
index 000000000..54fa1ffef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TException.hx
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+class TException {
+
+ @:isVar
+ public var errorID(default,null) : Int;
+ @:isVar
+ public var errorMsg(default,null) : String;
+
+
+ public function new(msg : String = "", id : Int = 0) {
+ errorID = id;
+ errorMsg = msg;
+ }
+
+}
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TFieldRequirementType.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TFieldRequirementType.hx
new file mode 100644
index 000000000..039a2cf3a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TFieldRequirementType.hx
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+ /**
+ * Requirement type constants.
+ *
+ */
+@:enum
+abstract TFieldRequirementType(Int) from Int to Int {
+ public static inline var REQUIRED : Int = 1;
+ public static inline var OPTIONAL : Int = 2;
+ public static inline var DEFAULT : Int = 3;
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TProcessor.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TProcessor.hx
new file mode 100644
index 000000000..0cb6f7d46
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/TProcessor.hx
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TProtocol;
+
+/**
+ * A processor is a generic object which operates upon an input stream and
+ * writes to some output stream.
+ */
+interface TProcessor {
+ function process(input:TProtocol, output:TProtocol) : Bool;
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/BitConverter.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/BitConverter.hx
new file mode 100644
index 000000000..ee0aaa8e9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/BitConverter.hx
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.helper;
+
+import haxe.Int64;
+import haxe.io.Bytes;
+import haxe.io.BytesBuffer;
+
+class BitConverter {
+
+ public static function DoubleToInt64Bits( db : Float) : Int64 {
+ var buf = new BytesBuffer();
+ buf.addDouble( db);
+ return bytesToLong( buf.getBytes());
+ }
+
+
+ public static function Int64BitsToDouble( i64 : Int64) : Float {
+ var buf = new BytesBuffer();
+ buf.add( fixedLongToBytes( i64));
+ return buf.getBytes().getDouble(0);
+ }
+
+
+
+ /**
+ * Convert a long into little-endian bytes in buf starting at off and going
+ * until off+7.
+ */
+ public static function fixedLongToBytes( n : Int64) : Bytes {
+ var buf = Bytes.alloc(8);
+ #if( haxe_ver < 3.2)
+ buf.set( 0, Int64.getLow( Int64.and( n, Int64.make(0, 0xff))));
+ buf.set( 1, Int64.getLow( Int64.and( Int64.shr( n, 8), Int64.make(0, 0xff))));
+ buf.set( 2, Int64.getLow( Int64.and( Int64.shr( n, 16), Int64.make(0, 0xff))));
+ buf.set( 3, Int64.getLow( Int64.and( Int64.shr( n, 24), Int64.make(0, 0xff))));
+ buf.set( 4, Int64.getLow( Int64.and( Int64.shr( n, 32), Int64.make(0, 0xff))));
+ buf.set( 5, Int64.getLow( Int64.and( Int64.shr( n, 40), Int64.make(0, 0xff))));
+ buf.set( 6, Int64.getLow( Int64.and( Int64.shr( n, 48), Int64.make(0, 0xff))));
+ buf.set( 7, Int64.getLow( Int64.and( Int64.shr( n, 56), Int64.make(0, 0xff))));
+ #else
+ buf.set( 0, Int64.and( n, Int64.make(0, 0xff)).low);
+ buf.set( 1, Int64.and( Int64.shr( n, 8), Int64.make(0, 0xff)).low);
+ buf.set( 2, Int64.and( Int64.shr( n, 16), Int64.make(0, 0xff)).low);
+ buf.set( 3, Int64.and( Int64.shr( n, 24), Int64.make(0, 0xff)).low);
+ buf.set( 4, Int64.and( Int64.shr( n, 32), Int64.make(0, 0xff)).low);
+ buf.set( 5, Int64.and( Int64.shr( n, 40), Int64.make(0, 0xff)).low);
+ buf.set( 6, Int64.and( Int64.shr( n, 48), Int64.make(0, 0xff)).low);
+ buf.set( 7, Int64.and( Int64.shr( n, 56), Int64.make(0, 0xff)).low);
+ #end
+ return buf;
+ }
+
+ /**
+ * Note that it's important that the mask bytes are long literals,
+ * otherwise they'll default to ints, and when you shift an int left 56 bits,
+ * you just get a messed up int.
+ */
+ public static function bytesToLong( bytes : Bytes) : Int64 {
+ var result : Int64 = Int64.make(0, 0);
+ result = Int64.or( Int64.shl( result, 8), Int64.make( 0, bytes.get(7)));
+ result = Int64.or( Int64.shl( result, 8), Int64.make( 0, bytes.get(6)));
+ result = Int64.or( Int64.shl( result, 8), Int64.make( 0, bytes.get(5)));
+ result = Int64.or( Int64.shl( result, 8), Int64.make( 0, bytes.get(4)));
+ result = Int64.or( Int64.shl( result, 8), Int64.make( 0, bytes.get(3)));
+ result = Int64.or( Int64.shl( result, 8), Int64.make( 0, bytes.get(2)));
+ result = Int64.or( Int64.shl( result, 8), Int64.make( 0, bytes.get(1)));
+ result = Int64.or( Int64.shl( result, 8), Int64.make( 0, bytes.get(0)));
+ return result;
+ }
+
+
+ #if debug
+ private static function TestBTL( test : Int64) : Void {
+ var buf : Bytes = fixedLongToBytes( test);
+ var erg = bytesToLong(buf);
+ if ( Int64.compare( erg, test) != 0)
+ throw 'BitConverter.bytesToLongTest($test) failed: $erg';
+ }
+ #end
+
+
+ #if debug
+ private static function TestPair( a : Float, b : Int64) : Void {
+ var bx = DoubleToInt64Bits(a);
+ if ( Int64.compare( bx, b) != 0)
+ throw 'BitConverter.TestPair: DoubleToInt64Bits($a): expected $b, got $bx';
+ var ax = Int64BitsToDouble(b);
+ if( ax != a)
+ throw 'BitConverter.TestPair: Int64BitsToDouble($b: expected $a, got $ax';
+ }
+ #end
+
+
+ #if debug
+ public static function UnitTest() : Void {
+
+ // bytesToLong()
+ var i : Int;
+ TestBTL( Int64.make(0,0));
+ for ( i in 0 ... 62) {
+ TestBTL( Int64.shl( Int64.make(0,1), i));
+ TestBTL( Int64.sub( Int64.make(0,0), Int64.shl( Int64.make(0,1), i)));
+ }
+ TestBTL( Int64.make(0x7FFFFFFF,0xFFFFFFFF));
+ TestBTL( Int64.make(cast(0x80000000,Int),0x00000000));
+
+ // DoubleToInt64Bits;
+ TestPair( 1.0000000000000000E+000, Int64.make(cast(0x3FF00000,Int),cast(0x00000000,Int)));
+ TestPair( 1.5000000000000000E+001, Int64.make(cast(0x402E0000,Int),cast(0x00000000,Int)));
+ TestPair( 2.5500000000000000E+002, Int64.make(cast(0x406FE000,Int),cast(0x00000000,Int)));
+ TestPair( 4.2949672950000000E+009, Int64.make(cast(0x41EFFFFF,Int),cast(0xFFE00000,Int)));
+ TestPair( 3.9062500000000000E-003, Int64.make(cast(0x3F700000,Int),cast(0x00000000,Int)));
+ TestPair( 2.3283064365386963E-010, Int64.make(cast(0x3DF00000,Int),cast(0x00000000,Int)));
+ TestPair( 1.2345678901230000E-300, Int64.make(cast(0x01AA74FE,Int),cast(0x1C1E7E45,Int)));
+ TestPair( 1.2345678901234500E-150, Int64.make(cast(0x20D02A36,Int),cast(0x586DB4BB,Int)));
+ TestPair( 1.2345678901234565E+000, Int64.make(cast(0x3FF3C0CA,Int),cast(0x428C59FA,Int)));
+ TestPair( 1.2345678901234567E+000, Int64.make(cast(0x3FF3C0CA,Int),cast(0x428C59FB,Int)));
+ TestPair( 1.2345678901234569E+000, Int64.make(cast(0x3FF3C0CA,Int),cast(0x428C59FC,Int)));
+ TestPair( 1.2345678901234569E+150, Int64.make(cast(0x5F182344,Int),cast(0xCD3CDF9F,Int)));
+ TestPair( 1.2345678901234569E+300, Int64.make(cast(0x7E3D7EE8,Int),cast(0xBCBBD352,Int)));
+ TestPair( -1.7976931348623157E+308, Int64.make(cast(0xFFEFFFFF,Int),cast(0xFFFFFFFF,Int)));
+ TestPair( 1.7976931348623157E+308, Int64.make(cast(0x7FEFFFFF,Int),cast(0xFFFFFFFF,Int)));
+ TestPair( 4.9406564584124654E-324, Int64.make(cast(0x00000000,Int),cast(0x00000001,Int)));
+ TestPair( 0.0000000000000000E+000, Int64.make(cast(0x00000000,Int),cast(0x00000000,Int)));
+ TestPair( 4.94065645841247E-324, Int64.make(cast(0x00000000,Int),cast(0x00000001,Int)));
+ TestPair( 3.2378592100206092E-319, Int64.make(cast(0x00000000,Int),cast(0x0000FFFF,Int)));
+ TestPair( 1.3906711615669959E-309, Int64.make(cast(0x0000FFFF,Int),cast(0xFFFFFFFF,Int)));
+ TestPair( Math.NEGATIVE_INFINITY, Int64.make(cast(0xFFF00000,Int),cast(0x00000000,Int)));
+ TestPair( Math.POSITIVE_INFINITY, Int64.make(cast(0x7FF00000,Int),cast(0x00000000,Int)));
+
+ // NaN is special
+ var i64nan = DoubleToInt64Bits( Math.NaN);
+ var i64cmp = Int64.make(cast(0xFFF80000, Int), cast(0x00000000, Int));
+ if ( ! Math.isNaN( Int64BitsToDouble( i64cmp)))
+ throw 'BitConverter NaN-Test #1: expected NaN';
+
+ // For doubles, a quiet NaN is a bit pattern
+ // between 7FF8000000000000 and 7FFFFFFFFFFFFFFF
+ // or FFF8000000000000 and FFFFFFFFFFFFFFFF
+ var min1 = Int64.make( cast(0x7FF80000, Int), cast(0x00000000, Int));
+ var max1 = Int64.make( cast(0x7FFFFFFF, Int), cast(0xFFFFFFFF, Int));
+ var min2 = Int64.make( cast(0xFFF80000, Int), cast(0x00000000, Int));
+ var max2 = Int64.make( cast(0xFFFFFFFF, Int), cast(0xFFFFFFFF, Int));
+ var ok1 = (Int64.compare( min1, i64nan) <= 0) && (Int64.compare( i64nan, max1) <= 0);
+ var ok2 = (Int64.compare( min2, i64nan) <= 0) && (Int64.compare( i64nan, max2) <= 0);
+ if( ! (ok1 || ok2))
+ throw 'BitConverter NaN-Test #2: failed';
+ }
+ #end
+
+}
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/Int64Map.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/Int64Map.hx
new file mode 100644
index 000000000..8845fd095
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/Int64Map.hx
@@ -0,0 +1,295 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.helper;
+
+import Map;
+import haxe.Int64;
+import haxe.ds.IntMap;
+
+
+// Int64Map allows mapping of Int64 keys to arbitrary values.
+// ObjectMap<> cannot be used, since we want to compare by value, not address
+
+class Int64Map<T> implements IMap< Int64, T> {
+
+ private var SubMaps : IntMap< IntMap< T>>; // Hi -> Lo -> Value
+
+ public function new() : Void {
+ SubMaps = new IntMap< IntMap< T>>();
+ };
+
+ private function GetSubMap( hi : haxe.Int32, canCreate : Bool) : IntMap< T> {
+ if( SubMaps.exists(hi)) {
+ return SubMaps.get(hi);
+ }
+
+ if( ! canCreate) {
+ return null;
+ }
+
+ var lomap = new IntMap< T>();
+ SubMaps.set( hi, lomap);
+ return lomap;
+ }
+
+
+ private function GetLowMap( key : haxe.Int64, canCreate : Bool) : IntMap< T> {
+ #if( haxe_ver < 3.2)
+ return GetSubMap( Int64.getHigh(key), canCreate);
+ #else
+ return GetSubMap( key.high, canCreate);
+ #end
+ }
+
+
+ private function GetLowIndex( key : haxe.Int64) : haxe.Int32 {
+ #if( haxe_ver < 3.2)
+ return Int64.getLow(key);
+ #else
+ return key.low;
+ #end
+ }
+
+
+ private function NullCheck( key : haxe.Int64) : Bool {
+ #if( haxe_ver < 3.2)
+ return (key != null);
+ #else
+ return true; // Int64 is not nullable anymore (it never really was)
+ #end
+ };
+
+
+
+ /**
+ Maps `key` to `value`.
+ If `key` already has a mapping, the previous value disappears.
+ If `key` is null, the result is unspecified.
+ **/
+ public function set( key : Int64, value : T ) : Void {
+ if( ! NullCheck(key)) {
+ return;
+ }
+
+ var lomap = GetLowMap( key, true);
+ lomap.set( GetLowIndex(key), value);
+ }
+
+
+ /**
+ Returns the current mapping of `key`.
+ If no such mapping exists, null is returned.
+ If `key` is null, the result is unspecified.
+
+ Note that a check like `map.get(key) == null` can hold for two reasons:
+
+ 1. the map has no mapping for `key`
+ 2. the map has a mapping with a value of `null`
+
+ If it is important to distinguish these cases, `exists()` should be
+ used.
+
+ **/
+ public function get( key : Int64) : Null<T> {
+ if( ! NullCheck(key)) {
+ return null;
+ }
+
+ var lomap = GetLowMap( key, true);
+ if( lomap == null) {
+ return null;
+ }
+
+ return lomap.get( GetLowIndex(key));
+ }
+
+ /**
+ Returns true if `key` has a mapping, false otherwise.
+ If `key` is null, the result is unspecified.
+ **/
+ public function exists( key : Int64) : Bool {
+ if( ! NullCheck(key)) {
+ return false;
+ }
+
+ var lomap = GetLowMap( key, true);
+ if( lomap == null) {
+ return false;
+ }
+
+ return lomap.exists( GetLowIndex(key));
+ }
+
+ /**
+ Removes the mapping of `key` and returns true if such a mapping existed,
+ false otherwise. If `key` is null, the result is unspecified.
+ **/
+ public function remove( key : Int64) : Bool {
+ if( ! NullCheck(key)) {
+ return false;
+ }
+
+ var lomap = GetLowMap( key, true);
+ if( lomap == null) {
+ return false;
+ }
+
+ return lomap.remove( GetLowIndex(key));
+ }
+
+
+ /**
+ Returns an Iterator over the keys of `this` Map.
+ The order of keys is undefined.
+ **/
+ public function keys() : Iterator<Int64> {
+ return new Int64KeyIterator<T>(SubMaps);
+ }
+
+ /**
+ Returns an Iterator over the values of `this` Map.
+ The order of values is undefined.
+ **/
+ public function iterator() : Iterator<T> {
+ return new Int64ValueIterator<T>(SubMaps);
+ }
+
+ /**
+ Returns a String representation of `this` Map.
+ The exact representation depends on the platform and key-type.
+ **/
+ public function toString() : String {
+ var result : String = "{";
+
+ var first = true;
+ for( key in this.keys()) {
+ if( first) {
+ first = false;
+ } else {
+ result += ",";
+ }
+
+ result += " ";
+ var value = this.get(key);
+ result += Int64.toStr(key) + ' => $value';
+ }
+
+ return result + "}";
+ }
+
+}
+
+
+// internal helper class for Int64Map<T>
+// all class with matching methods can be used as iterator (duck typing)
+private class Int64MapIteratorBase<T> {
+
+ private var SubMaps : IntMap< IntMap< T>>; // Hi -> Lo -> Value
+
+ private var HiIterator : Iterator< Int> = null;
+ private var LoIterator : Iterator< Int> = null;
+ private var CurrentHi : Int = 0;
+
+ public function new( data : IntMap< IntMap< T>>) : Void {
+ SubMaps = data;
+ HiIterator = SubMaps.keys();
+ LoIterator = null;
+ CurrentHi = 0;
+ };
+
+ /**
+ Returns false if the iteration is complete, true otherwise.
+
+ Usually iteration is considered to be complete if all elements of the
+ underlying data structure were handled through calls to next(). However,
+ in custom iterators any logic may be used to determine the completion
+ state.
+ **/
+ public function hasNext() : Bool {
+
+ if( (LoIterator != null) && LoIterator.hasNext()) {
+ return true;
+ }
+
+ while( (HiIterator != null) && HiIterator.hasNext()) {
+ CurrentHi = HiIterator.next();
+ LoIterator = SubMaps.get(CurrentHi).keys();
+ if( (LoIterator != null) && LoIterator.hasNext()) {
+ return true;
+ }
+ }
+
+ HiIterator = null;
+ LoIterator = null;
+ return false;
+ }
+
+}
+
+
+// internal helper class for Int64Map<T>
+// all class with matching methods can be used as iterator (duck typing)
+private class Int64KeyIterator<T>extends Int64MapIteratorBase<T> {
+
+ public function new( data : IntMap< IntMap< T>>) : Void {
+ super(data);
+ };
+
+ /**
+ Returns the current item of the Iterator and advances to the next one.
+
+ This method is not required to check hasNext() first. A call to this
+ method while hasNext() is false yields unspecified behavior.
+ **/
+ public function next() : Int64 {
+ if( hasNext()) {
+ return Int64.make( CurrentHi, LoIterator.next());
+ } else {
+ throw "no more elements";
+ }
+ }
+}
+
+
+// internal helper class for Int64Map<T>
+// all class with matching methods can be used as iterator (duck typing)
+private class Int64ValueIterator<T> extends Int64MapIteratorBase<T> {
+
+ public function new( data : IntMap< IntMap< T>>) : Void {
+ super(data);
+ };
+
+ /**
+ Returns the current item of the Iterator and advances to the next one.
+
+ This method is not required to check hasNext() first. A call to this
+ method while hasNext() is false yields unspecified behavior.
+ **/
+ public function next() : T {
+ if( hasNext()) {
+ return SubMaps.get(CurrentHi).get(LoIterator.next());
+ } else {
+ throw "no more elements";
+ }
+ }
+}
+
+
+// EOF
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/IntSet.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/IntSet.hx
new file mode 100644
index 000000000..91e5d8e97
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/IntSet.hx
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.helper;
+
+import Map;
+
+
+class IntSet {
+
+ private var _elements = new haxe.ds.IntMap<Int>();
+ private var _size : Int = 0;
+ public var size(get,never) : Int;
+
+ public function new( values : Array<Int> = null) {
+ if ( values != null) {
+ for ( value in values) {
+ add(value);
+ }
+ }
+ }
+
+ public function iterator():Iterator<Int> {
+ return _elements.keys();
+ }
+
+ public function traceAll() : Void {
+ trace('$_size entries');
+ for(entry in this) {
+ var yes = contains(entry);
+ trace('- $entry, contains() = $yes');
+ }
+ }
+
+ public function add(o : Int) : Bool {
+ if( _elements.exists(o)) {
+ return false;
+ }
+ _size++;
+ _elements.set(o,_size);
+ return true;
+ }
+
+ public function clear() : Void {
+ while( _size > 0) {
+ remove( _elements.keys().next());
+ }
+ }
+
+ public function contains(o : Int) : Bool {
+ return _elements.exists(o);
+ }
+
+ public function isEmpty() : Bool {
+ return _size == 0;
+ }
+
+ public function remove(o : Int) : Bool {
+ if (contains(o)) {
+ _elements.remove(o);
+ _size--;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public function toArray() : Array<Int> {
+ var ret : Array<Int> = new Array<Int>();
+ for (key in _elements.keys()) {
+ ret.push(key);
+ }
+ return ret;
+ }
+
+ public function get_size() : Int {
+ return _size;
+ }
+}
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/ObjectSet.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/ObjectSet.hx
new file mode 100644
index 000000000..bcf72fb7f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/ObjectSet.hx
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.helper;
+
+import Map;
+
+
+class ObjectSet<K> {
+
+ private var _elements = new haxe.ds.ObjectMap<K,Int>();
+ private var _size : Int = 0;
+ public var size(get,never) : Int;
+
+ public function new( values : Array<K> = null) {
+ if ( values != null) {
+ for ( value in values) {
+ add(value);
+ }
+ }
+ }
+
+ public function iterator():Iterator<K> {
+ return _elements.keys();
+ }
+
+ public function traceAll() : Void {
+ trace('$_size entries');
+ for(entry in this) {
+ var yes = contains(entry);
+ trace('- $entry, contains() = $yes');
+ }
+ }
+
+ public function add(o : K) : Bool {
+ if( _elements.exists(o)) {
+ return false;
+ }
+ _size++;
+ _elements.set(o,_size);
+ return true;
+ }
+
+ public function clear() : Void {
+ while( _size > 0) {
+ remove( _elements.keys().next());
+ }
+ }
+
+ public function contains(o : K) : Bool {
+ return _elements.exists(o);
+ }
+
+ public function isEmpty() : Bool {
+ return _size == 0;
+ }
+
+ public function remove(o : K) : Bool {
+ if (contains(o)) {
+ _elements.remove(o);
+ _size--;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public function toArray() : Array<K> {
+ var ret : Array<K> = new Array<K>();
+ for (key in _elements.keys()) {
+ ret.push(key);
+ }
+ return ret;
+ }
+
+ public function get_size() : Int {
+ return _size;
+ }
+}
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/StringSet.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/StringSet.hx
new file mode 100644
index 000000000..d8c0d90af
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/StringSet.hx
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.helper;
+
+import Map;
+
+
+class StringSet {
+
+ private var _elements = new haxe.ds.StringMap<Int>();
+ private var _size : Int = 0;
+ public var size(get,never) : Int;
+
+ public function new( values : Array<String> = null) {
+ if ( values != null) {
+ for ( value in values) {
+ add(value);
+ }
+ }
+ }
+
+ public function iterator():Iterator<String> {
+ return _elements.keys();
+ }
+
+ public function traceAll() : Void {
+ trace('$_size entries');
+ for(entry in this) {
+ var yes = contains(entry);
+ trace('- $entry, contains() = $yes');
+ }
+ }
+
+ public function add(o : String) : Bool {
+ if( _elements.exists(o)) {
+ return false;
+ }
+ _size++;
+ _elements.set(o,_size);
+ return true;
+ }
+
+ public function clear() : Void {
+ while( _size > 0) {
+ remove( _elements.keys().next());
+ }
+ }
+
+ public function contains(o : String) : Bool {
+ return _elements.exists(o);
+ }
+
+ public function isEmpty() : Bool {
+ return _size == 0;
+ }
+
+ public function remove(o : String) : Bool {
+ if (contains(o)) {
+ _elements.remove(o);
+ _size--;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public function toArray() : Array<String> {
+ var ret : Array<String> = new Array<String>();
+ for (key in _elements.keys()) {
+ ret.push(key);
+ }
+ return ret;
+ }
+
+ public function get_size() : String {
+ return _size;
+ }
+}
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/ZigZag.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/ZigZag.hx
new file mode 100644
index 000000000..3f8601beb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/helper/ZigZag.hx
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.helper;
+
+import haxe.Int64;
+import haxe.Int32;
+
+class ZigZag {
+
+ /**
+ * Convert n into a zigzag int. This allows negative numbers to be
+ * represented compactly as a varint.
+ */
+ public static function FromInt( n : Int) : UInt {
+ #if php
+
+ return cast(cast(cast(n,Int32) << 1,Int32) ^ cast(cast(n,Int32) >> 31,Int32),UInt);
+
+ #else
+
+ return cast(n << 1,UInt) ^ cast(n >> 31,UInt);
+
+ #end
+ }
+
+
+ /**
+ * Convert from zigzag int to int.
+ */
+ public static function ToInt( n : UInt) : Int {
+ #if php
+
+ var a = (0x7FFFFFFF & cast(n >> 1,Int));
+ var b = (cast(n & 1,Int));
+ b = -b; // workaround for https://github.com/HaxeFoundation/haxe/issues/5288
+ return a ^ b;
+
+ #else
+
+ return (0x7FFFFFFF & cast(n >> 1,Int)) ^ (-cast(n & 1,Int));
+
+ #end
+ }
+
+
+ /**
+ * Convert l into a zigzag long. This allows negative numbers to be
+ * represented compactly as a varint.
+ */
+ public static function FromLong( n : Int64) : Int64 {
+ return Int64.xor( Int64.shl(n, 1), Int64.shr(n, 63));
+ }
+
+
+ /**
+ * Convert from zigzag long to long.
+ */
+ public static function ToLong( n : Int64) : Int64 {
+ return Int64.xor(
+ Int64.and(
+ Int64.shr(n, 1),
+ Int64.make(0x7FFFFFFF, 0xFFFFFFFF)),
+ Int64.sub(
+ Int64.make(0, 0),
+ Int64.and(n, Int64.make(0,1))));
+ }
+
+
+ #if debug
+ private static function Test32( test : Int) : Void {
+ var a : UInt = ZigZag.FromInt( test);
+ var b : Int = ZigZag.ToInt(a);
+ #if php
+ test = test & 0xFFFFFFFF; // workaround for https://github.com/HaxeFoundation/haxe/issues/5289
+ #end
+ if( test != b)
+ throw 'ZigZag.Test32($test) failed: a = $a, b = $b';
+ }
+ #end
+
+
+
+ #if debug
+ private static function Test64( test : haxe.Int64) : Void {
+ var a : Int64 = ZigZag.FromLong( test);
+ var b : Int64 = ZigZag.ToLong(a);
+ if( Int64.compare( test, b) != 0)
+ throw 'ZigZag.Test64($test) failed: a = $a, b = $b';
+ }
+ #end
+
+
+ #if debug
+ public static function UnitTest() : Void {
+ var u1 : UInt = 0xFFFFFFFE;
+ var u2 : UInt = 0xFFFFFFFF;
+ var i1 : Int = 2147483647;
+ var i2 : Int = -2147483648;
+
+ #if php
+ i2 = i2 & 0xFFFFFFFF; // workaround for https://github.com/HaxeFoundation/haxe/issues/5289
+ #end
+
+ // protobuf testcases
+ if( FromInt(0) != 0) throw 'pb #1 to ZigZag';
+ if( FromInt(-1) != 1) throw 'pb #2 to ZigZag';
+ if( FromInt(1) != 2) throw 'pb #3 to ZigZag';
+ if( FromInt(-2) != 3) throw 'pb #4 to ZigZag';
+ if( FromInt(i1) != u1) throw 'pb #5 to ZigZag';
+ if( FromInt(i2) != u2) throw 'pb #6 to ZigZag';
+
+ // protobuf testcases
+ if( ToInt(0) != 0) throw 'pb #1 from ZigZag';
+ if( ToInt(1) != -1) throw 'pb #2 from ZigZag';
+ if( ToInt(2) != 1) throw 'pb #3 from ZigZag';
+ if( ToInt(3) != -2) throw 'pb #4 from ZigZag';
+ if( ToInt(u1) != i1) throw 'pb #5 from ZigZag, got ${ToInt(u1)} expected $i1';
+ if( ToInt(u2) != i2) throw 'pb #6 from ZigZag, got ${ToInt(u2)} expected $i2';
+
+ // back and forth 32
+ Test32( 0);
+ for( i in 0 ... 30) {
+ Test32( 1 << i);
+ Test32( -(1 << i));
+ }
+ Test32( 0x7FFFFFFF);
+ Test32( cast(0x80000000,Int));
+
+ // back and forth 64
+ Test64( Int64.make(0,0));
+ for( i in 0 ... 62) {
+ Test64( Int64.shl( Int64.make(0,1), i));
+ Test64( Int64.sub( Int64.make(0,0), Int64.shl( Int64.make(0,1), i)));
+ }
+ Test64( Int64.make(0x7FFFFFFF,0xFFFFFFFF));
+ Test64( Int64.make(cast(0x80000000,Int),0x00000000));
+ }
+ #end
+}
+
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/FieldMetaData.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/FieldMetaData.hx
new file mode 100644
index 000000000..26db1134e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/FieldMetaData.hx
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+import flash.utils.Dictionary;
+
+/**
+* This class is used to store meta data about thrift fields. Every field in a
+* a struct should have a corresponding instance of this class describing it.
+*
+*/
+class FieldMetaData {
+
+ public var fieldName : String;
+ public var requirementType : Int;
+ public var valueMetaData:FieldValueMetaData;
+
+ private static var structMap:Dictionary = new Dictionary();
+
+ public function FieldMetaData(name : String, req : Int, vMetaData:FieldValueMetaData) {
+ this.fieldName = name;
+ this.requirementType = req;
+ this.valueMetaData = vMetaData;
+ }
+
+ public static function addStructMetaDataMap(sClass:Class, map:Dictionary) : Void{
+ structMap[sClass] = map;
+ }
+
+ /**
+ * Returns a map with metadata (i.e. instances of FieldMetaData) that
+ * describe the fields of the given class.
+ *
+ * @param sClass The TBase class for which the metadata map is requested
+ */
+ public static function getStructMetaDataMap(sClass:Class):Dictionary {
+ return structMap[sClass];
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/FieldValueMetaData.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/FieldValueMetaData.hx
new file mode 100644
index 000000000..8879d9156
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/FieldValueMetaData.hx
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+import org.apache.thrift.protocol.TType;
+
+/**
+ * FieldValueMetaData and collection of subclasses to store metadata about
+ * the value(s) of a field
+ */
+class FieldValueMetaData {
+
+ public var type : Int;
+
+ public function FieldValueMetaData(type : Int) {
+ this.type = type;
+ }
+
+ public function isStruct() : Bool {
+ return type == TType.STRUCT;
+ }
+
+ public function isContainer() : Bool {
+ return type == TType.LIST || type == TType.MAP || type == TType.SET;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/ListMetaData.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/ListMetaData.hx
new file mode 100644
index 000000000..40ca31bad
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/ListMetaData.hx
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+class ListMetaData extends FieldValueMetaData {
+
+ public var elemMetaData:FieldValueMetaData;
+
+ public function ListMetaData(type : Int, eMetaData:FieldValueMetaData) {
+ super(type);
+ this.elemMetaData = eMetaData;
+ }
+}
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/MapMetaData.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/MapMetaData.hx
new file mode 100644
index 000000000..5463e6276
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/MapMetaData.hx
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+class MapMetaData extends FieldValueMetaData {
+
+ public var keyMetaData:FieldValueMetaData;
+ public var valueMetaData:FieldValueMetaData;
+
+ public function MapMetaData(type : Int, kMetaData:FieldValueMetaData, vMetaData:FieldValueMetaData) {
+ super(type);
+ this.keyMetaData = kMetaData;
+ this.valueMetaData = vMetaData;
+ }
+}
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/SetMetaData.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/SetMetaData.hx
new file mode 100644
index 000000000..a3367f449
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/SetMetaData.hx
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+class SetMetaData extends FieldValueMetaData {
+
+ public var elemMetaData:FieldValueMetaData;
+
+ public function SetMetaData(type : Int, eMetaData:FieldValueMetaData) {
+ super(type);
+ this.elemMetaData = eMetaData;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/StructMetaData.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/StructMetaData.hx
new file mode 100644
index 000000000..1822dd37f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/meta_data/StructMetaData.hx
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+class StructMetaData extends FieldValueMetaData {
+
+ public var structClass:Class;
+
+ public function StructMetaData(type : Int, sClass:Class) {
+ super(type);
+ this.structClass = sClass;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TBinaryProtocol.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TBinaryProtocol.hx
new file mode 100644
index 000000000..7ef291c0e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TBinaryProtocol.hx
@@ -0,0 +1,301 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import haxe.io.Bytes;
+import haxe.io.BytesInput;
+import haxe.io.BytesOutput;
+import haxe.io.BytesBuffer;
+import haxe.Int64;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.transport.TTransport;
+
+/**
+* Binary protocol implementation for thrift.
+*/
+class TBinaryProtocol extends TRecursionTracker implements TProtocol {
+
+ private static var ANONYMOUS_STRUCT:TStruct = new TStruct();
+
+ private static inline var VERSION_MASK : haxe.Int32 = 0xffff0000;
+ private static inline var VERSION_1 : haxe.Int32 = 0x80010000;
+
+ private var strictRead_ : Bool = false;
+ private var strictWrite_ : Bool = true;
+ private var trans_ : TTransport;
+
+ /**
+ * Constructor
+ */
+ public function new(trans:TTransport, strictRead : Bool=false, strictWrite : Bool=true) {
+ trans_ = trans;
+ strictRead_ = strictRead;
+ strictWrite_ = strictWrite;
+ }
+
+ public function getTransport():TTransport {
+ return trans_;
+ }
+
+ public function writeMessageBegin(message:TMessage) : Void {
+ if (strictWrite_) {
+ var version : Int = VERSION_1 | message.type;
+ writeI32(version);
+ writeString(message.name);
+ writeI32(message.seqid);
+ } else {
+ writeString(message.name);
+ writeByte(message.type);
+ writeI32(message.seqid);
+ }
+ }
+
+ public function writeMessageEnd() : Void {}
+
+ public function writeStructBegin(struct:TStruct) : Void {}
+
+ public function writeStructEnd() : Void {}
+
+ public function writeFieldBegin(field:TField) : Void {
+ writeByte(field.type);
+ writeI16(field.id);
+ }
+
+ public function writeFieldEnd() : Void {}
+
+ public function writeFieldStop() : Void {
+ writeByte(TType.STOP);
+ }
+
+ public function writeMapBegin(map:TMap) : Void {
+ writeByte(map.keyType);
+ writeByte(map.valueType);
+ writeI32(map.size);
+ }
+
+ public function writeMapEnd() : Void {}
+
+ public function writeListBegin(list:TList) : Void {
+ writeByte(list.elemType);
+ writeI32(list.size);
+ }
+
+ public function writeListEnd() : Void {}
+
+ public function writeSetBegin(set:TSet) : Void {
+ writeByte(set.elemType);
+ writeI32(set.size);
+ }
+
+ public function writeSetEnd() : Void {}
+
+ public function writeBool(b : Bool) : Void {
+ writeByte(b ? 1 : 0);
+ }
+
+
+ public function writeByte(b : Int) : Void {
+ var out = new BytesOutput();
+ out.bigEndian = true;
+ out.writeByte(b);
+ trans_.write(out.getBytes(), 0, 1);
+ }
+
+ public function writeI16(i16 : Int) : Void {
+ var out = new BytesOutput();
+ out.bigEndian = true;
+ out.writeInt16(i16);
+ trans_.write(out.getBytes(), 0, 2);
+ }
+
+ public function writeI32(i32 : Int) : Void {
+ var out = new BytesOutput();
+ out.bigEndian = true;
+ out.writeInt32(i32);
+ trans_.write(out.getBytes(), 0, 4);
+ }
+
+ public function writeI64(i64 : haxe.Int64) : Void {
+ var out = new BytesOutput();
+ out.bigEndian = true;
+ #if( haxe_ver < 3.2)
+ var hi = Int64.getHigh(i64);
+ var lo = Int64.getLow(i64);
+ out.writeInt32(hi);
+ out.writeInt32(lo);
+ #else
+ out.writeInt32(i64.high);
+ out.writeInt32(i64.low);
+ #end
+ trans_.write(out.getBytes(), 0, 8);
+ }
+
+ public function writeDouble(dub:Float) : Void {
+ var out = new BytesOutput();
+ out.bigEndian = true;
+ out.writeDouble(dub);
+ trans_.write(out.getBytes(), 0, 8);
+ }
+
+ public function writeString(str : String) : Void {
+ var out = new BytesOutput();
+ out.bigEndian = true;
+ out.writeString(str);
+ var bytes = out.getBytes();
+ writeI32( bytes.length);
+ trans_.write( bytes, 0, bytes.length);
+ }
+
+ public function writeBinary(bin:Bytes) : Void {
+ writeI32(bin.length);
+ trans_.write(bin, 0, bin.length);
+ }
+
+ /**
+ * Reading methods.
+ */
+
+ public function readMessageBegin():TMessage {
+ var size : Int = readI32();
+ if (size < 0) {
+ var version : Int = size & VERSION_MASK;
+ if (version != VERSION_1) {
+ throw new TProtocolException(TProtocolException.BAD_VERSION, "Bad version in readMessageBegin");
+ }
+ return new TMessage(readString(), size & 0x000000ff, readI32());
+ } else {
+ if (strictRead_) {
+ throw new TProtocolException(TProtocolException.BAD_VERSION, "Missing version in readMessageBegin, old client?");
+ }
+ return new TMessage(readStringBody(size), readByte(), readI32());
+ }
+ }
+
+ public function readMessageEnd() : Void {}
+
+ public function readStructBegin():TStruct {
+ return ANONYMOUS_STRUCT;
+ }
+
+ public function readStructEnd() : Void {}
+
+ public function readFieldBegin() : TField {
+ var type : Int = readByte();
+ var id : Int = 0;
+ if (type != TType.STOP)
+ {
+ id = readI16();
+ }
+ return new TField("", type, id);
+ }
+
+ public function readFieldEnd() : Void {}
+
+ public function readMapBegin() : TMap {
+ return new TMap(readByte(), readByte(), readI32());
+ }
+
+ public function readMapEnd() : Void {}
+
+ public function readListBegin():TList {
+ return new TList(readByte(), readI32());
+ }
+
+ public function readListEnd() : Void {}
+
+ public function readSetBegin() : TSet {
+ return new TSet(readByte(), readI32());
+ }
+
+ public function readSetEnd() : Void {}
+
+ public function readBool() : Bool {
+ return (readByte() == 1);
+ }
+
+
+ public function readByte() : Int {
+ var buffer = new BytesBuffer();
+ var len = trans_.readAll( buffer, 0, 1);
+ var inp = new BytesInput( buffer.getBytes(), 0, 1);
+ inp.bigEndian = true;
+ return inp.readByte();
+ }
+
+ public function readI16() : Int {
+ var buffer = new BytesBuffer();
+ var len = trans_.readAll( buffer, 0, 2);
+ var inp = new BytesInput( buffer.getBytes(), 0, 2);
+ inp.bigEndian = true;
+ return inp.readInt16();
+ }
+
+ public function readI32() : Int {
+ var buffer = new BytesBuffer();
+ var len = trans_.readAll( buffer, 0, 4);
+ var inp = new BytesInput( buffer.getBytes(), 0, 4);
+ inp.bigEndian = true;
+ return inp.readInt32();
+ }
+
+ public function readI64() : haxe.Int64 {
+ var buffer = new BytesBuffer();
+ var len = trans_.readAll( buffer, 0, 8);
+ var inp = new BytesInput( buffer.getBytes(), 0, 8);
+ inp.bigEndian = true;
+ var hi = inp.readInt32();
+ var lo = inp.readInt32();
+ return Int64.make(hi,lo);
+ }
+
+ public function readDouble():Float {
+ var buffer = new BytesBuffer();
+ var len = trans_.readAll( buffer, 0, 8);
+ var inp = new BytesInput( buffer.getBytes(), 0, 8);
+ inp.bigEndian = true;
+ return inp.readDouble();
+ }
+
+ public function readString() : String {
+ return readStringBody( readI32());
+ }
+
+ public function readStringBody(len : Int) : String {
+ if( len > 0) {
+ var buffer = new BytesBuffer();
+ trans_.readAll( buffer, 0, len);
+ var inp = new BytesInput( buffer.getBytes(), 0, len);
+ inp.bigEndian = true;
+ return inp.readString(len);
+ } else {
+ return "";
+ }
+ }
+
+ public function readBinary() : Bytes {
+ var len : Int = readI32();
+ var buffer = new BytesBuffer();
+ trans_.readAll( buffer, 0, len);
+ return buffer.getBytes();
+ }
+
+}
+
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TBinaryProtocolFactory.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TBinaryProtocolFactory.hx
new file mode 100644
index 000000000..f4a9becef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TBinaryProtocolFactory.hx
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.transport.TTransport;
+
+
+/**
+* Binary Protocol Factory
+*/
+class TBinaryProtocolFactory implements TProtocolFactory {
+
+ private var strictRead_ : Bool = false;
+ private var strictWrite_ : Bool = true;
+
+ public function new( strictRead : Bool = false, strictWrite : Bool = true) {
+ strictRead_ = strictRead;
+ strictWrite_ = strictWrite;
+ }
+
+ public function getProtocol( trans : TTransport) : TProtocol {
+ return new TBinaryProtocol( trans, strictRead_, strictWrite_);
+ }
+}
+
+
+
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TCompactProtocol.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TCompactProtocol.hx
new file mode 100644
index 000000000..03b13e2f6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TCompactProtocol.hx
@@ -0,0 +1,718 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import haxe.io.Bytes;
+import haxe.io.BytesInput;
+import haxe.io.BytesOutput;
+import haxe.io.BytesBuffer;
+import haxe.ds.GenericStack;
+import haxe.Int32;
+import haxe.Int64;
+import haxe.Utf8;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.transport.TTransport;
+import org.apache.thrift.helper.ZigZag;
+import org.apache.thrift.helper.BitConverter;
+
+
+/**
+* Compact protocol implementation for thrift.
+*/
+class TCompactProtocol extends TRecursionTracker implements TProtocol {
+
+ private static var ANONYMOUS_STRUCT : TStruct = new TStruct("");
+ private static var TSTOP : TField = new TField("", TType.STOP, 0);
+
+ private static inline var PROTOCOL_ID : Int = 0x82;
+ private static inline var VERSION : Int = 1;
+ private static inline var VERSION_MASK : Int = 0x1f; // 0001 1111
+ private static inline var TYPE_MASK : Int = 0xE0; // 1110 0000
+ private static inline var TYPE_BITS : Int = 0x07; // 0000 0111
+ private static inline var TYPE_SHIFT_AMOUNT : Int = 5;
+
+
+ private static var ttypeToCompactType = [
+ TType.STOP => TCompactTypes.STOP,
+ TType.BOOL => TCompactTypes.BOOLEAN_TRUE,
+ TType.BYTE => TCompactTypes.BYTE,
+ TType.DOUBLE => TCompactTypes.DOUBLE,
+ TType.I16 => TCompactTypes.I16,
+ TType.I32 => TCompactTypes.I32,
+ TType.I64 => TCompactTypes.I64,
+ TType.STRING => TCompactTypes.BINARY,
+ TType.STRUCT => TCompactTypes.STRUCT,
+ TType.MAP => TCompactTypes.MAP,
+ TType.SET => TCompactTypes.SET,
+ TType.LIST => TCompactTypes.LIST
+ ];
+
+ private static var tcompactTypeToType = [
+ TCompactTypes.STOP => TType.STOP,
+ TCompactTypes.BOOLEAN_TRUE => TType.BOOL,
+ TCompactTypes.BOOLEAN_FALSE => TType.BOOL,
+ TCompactTypes.BYTE => TType.BYTE,
+ TCompactTypes.I16 => TType.I16,
+ TCompactTypes.I32 => TType.I32,
+ TCompactTypes.I64 => TType.I64,
+ TCompactTypes.DOUBLE => TType.DOUBLE,
+ TCompactTypes.BINARY => TType.STRING,
+ TCompactTypes.LIST => TType.LIST,
+ TCompactTypes.SET => TType.SET,
+ TCompactTypes.MAP => TType.MAP,
+ TCompactTypes.STRUCT => TType.STRUCT
+ ];
+
+
+ /**
+ * Used to keep track of the last field for the current and previous structs,
+ * so we can do the delta stuff.
+ */
+ private var lastField_ : GenericStack<Int> = new GenericStack<Int>();
+ private var lastFieldId_ : Int = 0;
+
+ /**
+ * If we encounter a boolean field begin, save the TField here so it can
+ * have the value incorporated.
+ */
+ private var booleanField_ : Null<TField>;
+
+ /**
+ * If we Read a field header, and it's a boolean field, save the boolean
+ * value here so that ReadBool can use it.
+ */
+ private var boolValue_ : Null<Bool>;
+
+
+ // whether the underlying system holds Strings as UTF-8
+ // http://old.haxe.org/manual/encoding
+ private static var utf8Strings = haxe.Utf8.validate("Ç-ß-Æ-Ю-Ш");
+
+ // the transport used
+ public var trans(default,null) : TTransport;
+
+
+ // TCompactProtocol Constructor
+ public function new( trans : TTransport) {
+ this.trans = trans;
+ }
+
+ public function getTransport() : TTransport {
+ return trans;
+ }
+
+
+ public function Reset() : Void{
+ while ( ! lastField_.isEmpty()) {
+ lastField_.pop();
+ }
+ lastFieldId_ = 0;
+ }
+
+
+ /**
+ * Writes a byte without any possibility of all that field header nonsense.
+ * Used internally by other writing methods that know they need to Write a byte.
+ */
+ private function WriteByteDirect( b : Int) : Void {
+ var buf = Bytes.alloc(1);
+ buf.set( 0, b);
+ trans.write( buf, 0, 1);
+ }
+
+ /**
+ * Write an i32 as a varint. Results in 1-5 bytes on the wire.
+ */
+ private function WriteVarint32( n : UInt) : Void {
+ var i32buf = new BytesBuffer();
+ while (true)
+ {
+ if ((n & ~0x7F) == 0)
+ {
+ i32buf.addByte( n & 0xFF);
+ break;
+ }
+ else
+ {
+ i32buf.addByte( (n & 0x7F) | 0x80);
+ n >>= 7;
+ }
+ }
+
+ var tmp = i32buf.getBytes();
+ trans.write( tmp, 0, tmp.length);
+ }
+
+ /**
+ * Write a message header to the wire. Compact Protocol messages contain the
+ * protocol version so we can migrate forwards in the future if need be.
+ */
+ public function writeMessageBegin( message : TMessage) : Void {
+ Reset();
+
+ var versionAndType : Int = (VERSION & VERSION_MASK) | ((message.type << TYPE_SHIFT_AMOUNT) & TYPE_MASK);
+ WriteByteDirect( PROTOCOL_ID);
+ WriteByteDirect( versionAndType);
+ WriteVarint32( cast( message.seqid, UInt));
+ writeString( message.name);
+ }
+
+ /**
+ * Write a struct begin. This doesn't actually put anything on the wire. We
+ * use it as an opportunity to put special placeholder markers on the field
+ * stack so we can get the field id deltas correct.
+ */
+ public function writeStructBegin(struct:TStruct) : Void {
+ lastField_.add( lastFieldId_);
+ lastFieldId_ = 0;
+ }
+
+ /**
+ * Write a struct end. This doesn't actually put anything on the wire. We use
+ * this as an opportunity to pop the last field from the current struct off
+ * of the field stack.
+ */
+ public function writeStructEnd() : Void {
+ lastFieldId_ = lastField_.pop();
+ }
+
+ /**
+ * Write a field header containing the field id and field type. If the
+ * difference between the current field id and the last one is small (< 15),
+ * then the field id will be encoded in the 4 MSB as a delta. Otherwise, the
+ * field id will follow the type header as a zigzag varint.
+ */
+ public function writeFieldBegin(field:TField) : Void {
+ if (field.type == TType.BOOL)
+ booleanField_ = field; // we want to possibly include the value, so we'll wait.
+ else
+ WriteFieldBeginInternal(field, 0xFF);
+ }
+
+ /**
+ * The workhorse of WriteFieldBegin. It has the option of doing a
+ * 'type override' of the type header. This is used specifically in the
+ * boolean field case.
+ */
+ private function WriteFieldBeginInternal( field : TField, typeOverride : Int) : Void {
+ // if there's a type override, use that.
+ var typeToWrite : Int;
+ if ( typeOverride == 0xFF)
+ typeToWrite = getCompactType( field.type);
+ else
+ typeToWrite = typeOverride;
+
+ // check if we can use delta encoding for the field id
+ if (field.id > lastFieldId_ && field.id - lastFieldId_ <= 15)
+ {
+ // Write them together
+ WriteByteDirect((field.id - lastFieldId_) << 4 | typeToWrite);
+ }
+ else
+ {
+ // Write them separate
+ WriteByteDirect(typeToWrite);
+ writeI16(field.id);
+ }
+
+ lastFieldId_ = field.id;
+ }
+
+ /**
+ * Write the STOP symbol so we know there are no more fields in this struct.
+ */
+ public function writeFieldStop() : Void {
+ WriteByteDirect( cast(TCompactTypes.STOP, Int));
+ }
+
+ /**
+ * Write a map header. If the map is empty, omit the key and value type
+ * headers, as we don't need any additional information to skip it.
+ */
+ public function writeMapBegin(map:TMap) : Void {
+ if (map.size == 0)
+ {
+ WriteByteDirect(0);
+ }
+ else
+ {
+ var kvtype = (getCompactType(map.keyType) << 4) | getCompactType(map.valueType);
+ WriteVarint32( cast( map.size, UInt));
+ WriteByteDirect( kvtype);
+ }
+ }
+
+ /**
+ * Write a list header.
+ */
+ public function writeListBegin( list : TList) : Void {
+ WriteCollectionBegin( list.elemType, list.size);
+ }
+
+ /**
+ * Write a set header.
+ */
+ public function writeSetBegin( set : TSet) : Void {
+ WriteCollectionBegin( set.elemType, set.size);
+ }
+
+ /**
+ * Write a boolean value. Potentially, this could be a boolean field, in
+ * which case the field header info isn't written yet. If so, decide what the
+ * right type header is for the value and then Write the field header.
+ * Otherwise, Write a single byte.
+ */
+ public function writeBool(b : Bool) : Void {
+ var bct : Int = b ? TCompactTypes.BOOLEAN_TRUE : TCompactTypes.BOOLEAN_FALSE;
+
+ if (booleanField_ != null)
+ {
+ // we haven't written the field header yet
+ WriteFieldBeginInternal( booleanField_, bct);
+ booleanField_ = null;
+ }
+ else
+ {
+ // we're not part of a field, so just Write the value.
+ WriteByteDirect( bct);
+ }
+ }
+
+ /**
+ * Write a byte. Nothing to see here!
+ */
+ public function writeByte( b : Int) : Void {
+ WriteByteDirect( b);
+ }
+
+ /**
+ * Write an I16 as a zigzag varint.
+ */
+ public function writeI16( i16 : Int) : Void {
+ WriteVarint32( ZigZag.FromInt( i16));
+ }
+
+ /**
+ * Write an i32 as a zigzag varint.
+ */
+ public function writeI32( i32 : Int) : Void {
+ WriteVarint32( ZigZag.FromInt( i32));
+ }
+
+ /**
+ * Write an i64 as a zigzag varint.
+ */
+ public function writeI64( i64 : haxe.Int64) : Void {
+ WriteVarint64( ZigZag.FromLong( i64));
+ }
+
+ /**
+ * Write a double to the wire as 8 bytes.
+ */
+ public function writeDouble( dub : Float) : Void {
+ var data = BitConverter.fixedLongToBytes( BitConverter.DoubleToInt64Bits(dub));
+ trans.write( data, 0, data.length);
+ }
+
+ /**
+ * Write a string to the wire with a varint size preceding.
+ */
+ public function writeString(str : String) : Void {
+ var buf = new BytesBuffer();
+ if( utf8Strings)
+ buf.addString( str); // no need to encode on UTF8 targets, the string is just fine
+ else
+ buf.addString( Utf8.encode( str));
+ var tmp = buf.getBytes();
+ writeBinary( tmp);
+ }
+
+ /**
+ * Write a byte array, using a varint for the size.
+ */
+ public function writeBinary( bin : Bytes) : Void {
+ WriteVarint32( cast(bin.length,UInt));
+ trans.write( bin, 0, bin.length);
+ }
+
+
+ // These methods are called by structs, but don't actually have any wire
+ // output or purpose.
+ public function writeMessageEnd() : Void { }
+ public function writeMapEnd() : Void { }
+ public function writeListEnd() : Void { }
+ public function writeSetEnd() : Void { }
+ public function writeFieldEnd() : Void { }
+
+ //
+ // Internal writing methods
+ //
+
+ /**
+ * Abstract method for writing the start of lists and sets. List and sets on
+ * the wire differ only by the type indicator.
+ */
+ private function WriteCollectionBegin( elemType : Int, size : Int) : Void {
+ if (size <= 14) {
+ WriteByteDirect( size << 4 | getCompactType(elemType));
+ }
+ else {
+ WriteByteDirect( 0xf0 | getCompactType(elemType));
+ WriteVarint32( cast(size, UInt));
+ }
+ }
+
+ /**
+ * Write an i64 as a varint. Results in 1-10 bytes on the wire.
+ */
+ private function WriteVarint64(n : haxe.Int64) : Void {
+ var varint64out = new BytesBuffer();
+ while (true)
+ {
+ if( Int64.isZero( Int64.and( n, Int64.neg(Int64.make(0,0x7F)))))
+ {
+ #if( haxe_ver < 3.2)
+ varint64out.addByte( Int64.getLow(n));
+ #else
+ varint64out.addByte( n.low);
+ #end
+ break;
+ }
+ else
+ {
+ #if ( haxe_ver < 3.2)
+ varint64out.addByte( (Int64.getLow(n) & 0x7F) | 0x80);
+ #else
+ varint64out.addByte( (n.low & 0x7F) | 0x80);
+ #end
+ n = Int64.shr( n, 7);
+ n = Int64.and( n, Int64.make(0x01FFFFFF,0xFFFFFFFF)); // clean out the shifted 7 bits
+ }
+ }
+ var tmp = varint64out.getBytes();
+ trans.write( tmp, 0, tmp.length);
+ }
+
+
+ /**
+ * Read a message header.
+ */
+ public function readMessageBegin():TMessage {
+ Reset();
+
+ var protocolId : Int = readByte();
+ if (protocolId != PROTOCOL_ID) {
+ throw new TProtocolException( TProtocolException.INVALID_DATA, "Expected protocol id " + StringTools.hex(PROTOCOL_ID,2) + " but got " + StringTools.hex(protocolId));
+ }
+
+ var versionAndType : Int = readByte();
+ var version : Int = (versionAndType & VERSION_MASK);
+ if (version != VERSION) {
+ throw new TProtocolException( TProtocolException.INVALID_DATA, "Expected version " + VERSION + " but got " + version);
+ }
+
+ var type : Int = ((versionAndType >> TYPE_SHIFT_AMOUNT) & TYPE_BITS);
+ var seqid : Int = cast( ReadVarint32(), Int);
+ var msgNm : String = readString();
+ return new TMessage( msgNm, type, seqid);
+ }
+
+ /**
+ * Read a struct begin. There's nothing on the wire for this, but it is our
+ * opportunity to push a new struct begin marker onto the field stack.
+ */
+ public function readStructBegin():TStruct {
+ lastField_.add(lastFieldId_);
+ lastFieldId_ = 0;
+ return ANONYMOUS_STRUCT;
+ }
+
+ /**
+ * Doesn't actually consume any wire data, just removes the last field for
+ * this struct from the field stack.
+ */
+ public function readStructEnd() : Void {
+ // consume the last field we Read off the wire.
+ lastFieldId_ = lastField_.pop();
+ }
+
+ /**
+ * Read a field header off the wire.
+ */
+ public function readFieldBegin() : TField {
+ var type : Int = readByte();
+
+ // if it's a stop, then we can return immediately, as the struct is over.
+ if (type == cast(TCompactTypes.STOP,Int)) {
+ return TSTOP;
+ }
+
+ var fieldId : Int;
+
+ // mask off the 4 MSB of the type header. it could contain a field id delta.
+ var modifier : Int = ((type & 0xf0) >> 4);
+ if (modifier == 0)
+ fieldId = readI16(); // not a delta. look ahead for the zigzag varint field id.
+ else
+ fieldId = lastFieldId_ + modifier; // add the delta to the last Read field id.
+
+ var field : TField = new TField( "", cast(getTType(type & 0x0f),Int), fieldId);
+
+ // if this happens to be a boolean field, the value is encoded in the type
+ if (isBoolType(type)) {
+ // save the boolean value in a special instance variable.
+ boolValue_ = ((type & 0x0f) == cast(TCompactTypes.BOOLEAN_TRUE,Int));
+ }
+
+ // push the new field onto the field stack so we can keep the deltas going.
+ lastFieldId_ = field.id;
+ return field;
+ }
+
+ /**
+ * Read a map header off the wire. If the size is zero, skip Reading the key
+ * and value type. This means that 0-length maps will yield TMaps without the
+ * "correct" types.
+ */
+ public function readMapBegin() : TMap {
+ var size : Int = cast( ReadVarint32(), Int);
+ var keyAndValueType : Int = ((size == 0) ? 0 : readByte());
+ var key : Int = cast( getTType( (keyAndValueType & 0xF0) >> 4), Int);
+ var val : Int = cast( getTType( keyAndValueType & 0x0F), Int);
+ return new TMap( key, val, size);
+ }
+
+ /**
+ * Read a list header off the wire. If the list size is 0-14, the size will
+ * be packed into the element type header. If it's a longer list, the 4 MSB
+ * of the element type header will be 0xF, and a varint will follow with the
+ * true size.
+ */
+ public function readListBegin():TList {
+ var size_and_type : Int = readByte();
+
+ var size : Int = ((size_and_type & 0xF0) >> 4) & 0x0F;
+ if (size == 15) {
+ size = cast( ReadVarint32(), Int);
+ }
+
+ var type = getTType(size_and_type);
+ return new TList( type, size);
+ }
+
+ /**
+ * Read a set header off the wire. If the set size is 0-14, the size will
+ * be packed into the element type header. If it's a longer set, the 4 MSB
+ * of the element type header will be 0xF, and a varint will follow with the
+ * true size.
+ */
+ public function readSetBegin() : TSet {
+ var size_and_type : Int = readByte();
+
+ var size : Int = ((size_and_type & 0xF0) >> 4) & 0x0F;
+ if (size == 15) {
+ size = cast( ReadVarint32(), Int);
+ }
+
+ var type = getTType(size_and_type);
+ return new TSet( type, size);
+ }
+
+ /**
+ * Read a boolean off the wire. If this is a boolean field, the value should
+ * already have been Read during ReadFieldBegin, so we'll just consume the
+ * pre-stored value. Otherwise, Read a byte.
+ */
+ public function readBool() : Bool {
+ if (boolValue_ != null) {
+ var result : Bool = boolValue_;
+ boolValue_ = null;
+ return result;
+ }
+
+ return (readByte() == cast(TCompactTypes.BOOLEAN_TRUE,Int));
+ }
+
+ /**
+ * Read a single byte off the wire. Nothing interesting here.
+ */
+ public function readByte() : Int {
+ var byteRawBuf = new BytesBuffer();
+ trans.readAll( byteRawBuf, 0, 1);
+ return byteRawBuf.getBytes().get(0);
+ }
+
+ /**
+ * Read an i16 from the wire as a zigzag varint.
+ */
+ public function readI16() : Int {
+ return ZigZag.ToInt( ReadVarint32());
+ }
+
+ /**
+ * Read an i32 from the wire as a zigzag varint.
+ */
+ public function readI32() : Int {
+ return ZigZag.ToInt( ReadVarint32());
+ }
+
+ /**
+ * Read an i64 from the wire as a zigzag varint.
+ */
+ public function readI64() : haxe.Int64 {
+ return ZigZag.ToLong( ReadVarint64());
+ }
+
+ /**
+ * No magic here - just Read a double off the wire.
+ */
+ public function readDouble():Float {
+ var longBits = new BytesBuffer();
+ trans.readAll( longBits, 0, 8);
+ return BitConverter.Int64BitsToDouble( BitConverter.bytesToLong( longBits.getBytes()));
+ }
+
+ /**
+ * Reads a byte[] (via ReadBinary), and then UTF-8 decodes it.
+ */
+ public function readString() : String {
+ var length : Int = cast( ReadVarint32(), Int);
+
+ if (length == 0) {
+ return "";
+ }
+
+ var buf = new BytesBuffer();
+ trans.readAll( buf, 0, length);
+
+ length = buf.length;
+ var inp = new BytesInput( buf.getBytes());
+ var str = inp.readString( length);
+ if( utf8Strings)
+ return str; // no need to decode on UTF8 targets, the string is just fine
+ else
+ return Utf8.decode( str);
+ }
+
+ /**
+ * Read a byte[] from the wire.
+ */
+ public function readBinary() : Bytes {
+ var length : Int = cast( ReadVarint32(), Int);
+ if (length == 0) {
+ return Bytes.alloc(0);
+ }
+
+ var buf = new BytesBuffer();
+ trans.readAll( buf, 0, length);
+ return buf.getBytes();
+ }
+
+
+ // These methods are here for the struct to call, but don't have any wire
+ // encoding.
+ public function readMessageEnd() : Void { }
+ public function readFieldEnd() : Void { }
+ public function readMapEnd() : Void { }
+ public function readListEnd() : Void { }
+ public function readSetEnd() : Void { }
+
+ //
+ // Internal Reading methods
+ //
+
+ /**
+ * Read an i32 from the wire as a varint. The MSB of each byte is set
+ * if there is another byte to follow. This can Read up to 5 bytes.
+ */
+ private function ReadVarint32() : UInt {
+ var result : UInt = 0;
+ var shift : Int = 0;
+ while (true) {
+ var b : Int = readByte();
+ result |= cast((b & 0x7f) << shift, UInt);
+ if ((b & 0x80) != 0x80) {
+ break;
+ }
+ shift += 7;
+ }
+ return result;
+ }
+
+ /**
+ * Read an i64 from the wire as a proper varint. The MSB of each byte is set
+ * if there is another byte to follow. This can Read up to 10 bytes.
+ */
+ private function ReadVarint64() : Int64 {
+ var shift : Int = 0;
+ var result : Int64 = Int64.make(0,0);
+ while (true) {
+ var b : Int = readByte();
+ result = Int64.or( result, Int64.shl( Int64.make(0,b & 0x7f), shift));
+ if ((b & 0x80) != 0x80) {
+ break;
+ }
+ shift += 7;
+ }
+
+ return result;
+ }
+
+
+ //
+ // type testing and converting
+ //
+
+ private function isBoolType( b : Int) : Bool {
+ var lowerNibble : Int = b & 0x0f;
+ switch(lowerNibble)
+ {
+ case TCompactTypes.BOOLEAN_TRUE: return true;
+ case TCompactTypes.BOOLEAN_FALSE: return true;
+ default: return false;
+ }
+ }
+
+
+ /**
+ * Given a TCompactProtocol.TCompactTypes constant, convert it to its corresponding
+ * TType value.
+ */
+ private function getTType( type : Int) : Int {
+ try
+ {
+ return tcompactTypeToType[type];
+ }
+ catch ( e : Dynamic)
+ {
+ var tt : Int = (type & 0x0f);
+ throw new TProtocolException( TProtocolException.UNKNOWN, 'don\'t know what type: $tt ($e)');
+ }
+ }
+
+ /**
+ * Given a TType value, find the appropriate TCompactProtocol.TCompactTypes constant.
+ */
+ private function getCompactType( ttype : Int) : Int
+ {
+ return cast( ttypeToCompactType[ttype], Int);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TCompactProtocolFactory.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TCompactProtocolFactory.hx
new file mode 100644
index 000000000..c5673b40c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TCompactProtocolFactory.hx
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.transport.TTransport;
+
+
+/**
+* Compact Protocol Factory
+*/
+class TCompactProtocolFactory implements TProtocolFactory {
+
+ public function new() {
+ }
+
+ public function getProtocol( trans : TTransport) : TProtocol {
+ return new TCompactProtocol( trans);
+ }
+}
+
+
+
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TCompactTypes.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TCompactTypes.hx
new file mode 100644
index 000000000..cdd3d874f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TCompactTypes.hx
@@ -0,0 +1,41 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * All of the on-wire type codes.
+ */
+@:enum
+abstract TCompactTypes(Int) from Int to Int {
+ public static inline var STOP = 0x00;
+ public static inline var BOOLEAN_TRUE = 0x01;
+ public static inline var BOOLEAN_FALSE = 0x02;
+ public static inline var BYTE = 0x03;
+ public static inline var I16 = 0x04;
+ public static inline var I32 = 0x05;
+ public static inline var I64 = 0x06;
+ public static inline var DOUBLE = 0x07;
+ public static inline var BINARY = 0x08;
+ public static inline var LIST = 0x09;
+ public static inline var SET = 0x0A;
+ public static inline var MAP = 0x0B;
+ public static inline var STRUCT = 0x0C;
+}
+
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TField.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TField.hx
new file mode 100644
index 000000000..3f5498d82
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TField.hx
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+class TField {
+
+ public var name : String;
+ public var type : Int;
+ public var id : Int;
+
+ public function new(n : String = "", t : Int = 0, i : Int = 0) {
+ name = n;
+ type = t;
+ id = i;
+ }
+
+ public function toString() : String {
+ return '<TField name:"$name" type:"$type" field-id:"$id">';
+ }
+
+ public function equals( otherField : TField) : Bool {
+ return (type == otherField.type)
+ && (id == otherField.id);
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TJSONProtocol.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TJSONProtocol.hx
new file mode 100644
index 000000000..e20ff33c5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TJSONProtocol.hx
@@ -0,0 +1,1073 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import haxe.io.Bytes;
+import haxe.io.BytesInput;
+import haxe.io.BytesOutput;
+import haxe.io.BytesBuffer;
+import haxe.ds.GenericStack;
+import haxe.Utf8;
+import haxe.crypto.Base64;
+import haxe.Int64;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.protocol.TMessage;
+import org.apache.thrift.protocol.TField;
+import org.apache.thrift.protocol.TMap;
+import org.apache.thrift.protocol.TSet;
+import org.apache.thrift.protocol.TList;
+import org.apache.thrift.transport.TTransport;
+
+
+
+/* JSON protocol implementation for thrift.
+* This is a full-featured protocol supporting Write and Read.
+*
+* Please see the C++ class header for a detailed description of the wire format.
+*
+* Adapted from the Java version.
+*/
+class TJSONProtocol extends TRecursionTracker implements TProtocol {
+
+ public var trans(default,null) : TTransport;
+
+ // Stack of nested contexts that we may be in
+ private var contextStack : GenericStack<JSONBaseContext> = new GenericStack<JSONBaseContext>();
+
+ // Current context that we are in
+ private var context : JSONBaseContext;
+
+ // Reader that manages a 1-byte buffer
+ private var reader : LookaheadReader;
+
+ // whether the underlying system holds Strings as UTF-8
+ // http://old.haxe.org/manual/encoding
+ private static var utf8Strings = haxe.Utf8.validate("Ç-ß-Æ-Ю-Ш");
+
+ // TJSONProtocol Constructor
+ public function new( trans : TTransport)
+ {
+ this.trans = trans;
+ this.context = new JSONBaseContext(this);
+ this.reader = new LookaheadReader(this);
+ }
+
+ public function getTransport() : TTransport {
+ return trans;
+ }
+
+ public function writeMessageBegin(message:TMessage) : Void {
+ WriteJSONArrayStart();
+ WriteJSONInteger( JSONConstants.VERSION);
+ WriteJSONString( BytesFromString(message.name));
+ WriteJSONInteger( message.type);
+ WriteJSONInteger( message.seqid);
+ }
+
+ public function writeMessageEnd() : Void {
+ WriteJSONArrayEnd();
+ }
+
+ public function writeStructBegin(struct:TStruct) : Void {
+ WriteJSONObjectStart();
+ }
+
+ public function writeStructEnd() : Void {
+ WriteJSONObjectEnd();
+ }
+
+ public function writeFieldBegin(field:TField) : Void {
+ WriteJSONInteger( field.id );
+ WriteJSONObjectStart();
+ WriteJSONString( BytesFromString( JSONConstants.GetTypeNameForTypeID( field.type)));
+ }
+
+ public function writeFieldEnd() : Void {
+ WriteJSONObjectEnd();
+ }
+
+ public function writeFieldStop() : Void { }
+
+ public function writeMapBegin(map:TMap) : Void {
+ WriteJSONArrayStart();
+ WriteJSONString( BytesFromString( JSONConstants.GetTypeNameForTypeID( map.keyType)));
+ WriteJSONString( BytesFromString( JSONConstants.GetTypeNameForTypeID( map.valueType)));
+ WriteJSONInteger( map.size);
+ WriteJSONObjectStart();
+ }
+
+ public function writeMapEnd() : Void {
+ WriteJSONObjectEnd();
+ WriteJSONArrayEnd();
+ }
+
+ public function writeListBegin(list:TList) : Void {
+ WriteJSONArrayStart();
+ WriteJSONString( BytesFromString( JSONConstants.GetTypeNameForTypeID( list.elemType )));
+ WriteJSONInteger( list.size);
+ }
+
+ public function writeListEnd() : Void {
+ WriteJSONArrayEnd();
+ }
+
+ public function writeSetBegin(set:TSet) : Void {
+ WriteJSONArrayStart();
+ WriteJSONString( BytesFromString( JSONConstants.GetTypeNameForTypeID( set.elemType)));
+ WriteJSONInteger( set.size);
+ }
+
+ public function writeSetEnd() : Void {
+ WriteJSONArrayEnd();
+ }
+
+ public function writeBool(b : Bool) : Void {
+ if( b)
+ WriteJSONInteger( 1);
+ else
+ WriteJSONInteger( 0);
+ }
+
+ public function writeByte(b : Int) : Void {
+ WriteJSONInteger( b);
+ }
+
+ public function writeI16(i16 : Int) : Void {
+ WriteJSONInteger( i16);
+ }
+
+ public function writeI32(i32 : Int) : Void {
+ WriteJSONInteger( i32);
+ }
+
+ public function writeI64(i64 : haxe.Int64) : Void {
+ WriteJSONInt64( i64);
+ }
+
+ public function writeDouble(dub:Float) : Void {
+ WriteJSONDouble(dub);
+ }
+
+ public function writeString(str : String) : Void {
+ WriteJSONString( BytesFromString(str));
+ }
+
+ public function writeBinary(bin:Bytes) : Void {
+ WriteJSONBase64(bin);
+ }
+
+ public function readMessageBegin():TMessage {
+ var message : TMessage = new TMessage();
+ ReadJSONArrayStart();
+ if (ReadJSONInteger() != JSONConstants.VERSION)
+ {
+ throw new TProtocolException(TProtocolException.BAD_VERSION,
+ "Message contained bad version.");
+ }
+
+ message.name = ReadJSONString(false);
+ message.type = ReadJSONInteger();
+ message.seqid = ReadJSONInteger();
+ return message;
+ }
+
+ public function readMessageEnd() : Void {
+ ReadJSONArrayEnd();
+ }
+
+ public function readStructBegin():TStruct {
+ ReadJSONObjectStart();
+ return new TStruct();
+ }
+
+ public function readStructEnd() : Void {
+ ReadJSONObjectEnd();
+ }
+
+ public function readFieldBegin() : TField {
+ var field : TField = new TField();
+ var ch = reader.Peek();
+ if (StringFromBytes(ch) == JSONConstants.RBRACE)
+ {
+ field.type = TType.STOP;
+ }
+ else
+ {
+ field.id = ReadJSONInteger();
+ ReadJSONObjectStart();
+ field.type = JSONConstants.GetTypeIDForTypeName( ReadJSONString(false));
+ }
+ return field;
+ }
+
+ public function readFieldEnd() : Void {
+ ReadJSONObjectEnd();
+ }
+
+ public function readMapBegin() : TMap {
+ ReadJSONArrayStart();
+ var KeyType = JSONConstants.GetTypeIDForTypeName( ReadJSONString(false));
+ var ValueType = JSONConstants.GetTypeIDForTypeName( ReadJSONString(false));
+ var Count : Int = ReadJSONInteger();
+ ReadJSONObjectStart();
+
+ var map = new TMap( KeyType, ValueType, Count);
+ return map;
+ }
+
+ public function readMapEnd() : Void {
+ ReadJSONObjectEnd();
+ ReadJSONArrayEnd();
+ }
+
+ public function readListBegin():TList {
+ ReadJSONArrayStart();
+ var ElementType = JSONConstants.GetTypeIDForTypeName( ReadJSONString(false));
+ var Count : Int = ReadJSONInteger();
+
+ var list = new TList( ElementType, Count);
+ return list;
+ }
+
+ public function readListEnd() : Void {
+ ReadJSONArrayEnd();
+ }
+
+ public function readSetBegin() : TSet {
+ ReadJSONArrayStart();
+ var ElementType = JSONConstants.GetTypeIDForTypeName( ReadJSONString(false));
+ var Count : Int = ReadJSONInteger();
+
+ var set = new TSet( ElementType, Count);
+ return set;
+ }
+
+ public function readSetEnd() : Void {
+ ReadJSONArrayEnd();
+ }
+
+ public function readBool() : Bool {
+ return (ReadJSONInteger() != 0);
+ }
+
+ public function readByte() : Int {
+ return ReadJSONInteger();
+ }
+
+ public function readI16() : Int {
+ return ReadJSONInteger();
+ }
+
+ public function readI32() : Int {
+ return ReadJSONInteger();
+ }
+
+ public function readI64() : haxe.Int64 {
+ return ReadJSONInt64();
+ }
+
+ public function readDouble():Float {
+ return ReadJSONDouble();
+ }
+
+ public function readString() : String {
+ return ReadJSONString(false);
+ }
+
+ public function readBinary() : Bytes {
+ return ReadJSONBase64();
+ }
+
+ // Push a new JSON context onto the stack.
+ private function PushContext(c : JSONBaseContext) : Void {
+ contextStack.add(context);
+ context = c;
+ }
+
+ // Pop the last JSON context off the stack
+ private function PopContext() : Void {
+ context = contextStack.pop();
+ }
+
+
+ // Write the bytes in array buf as a JSON characters, escaping as needed
+ private function WriteJSONString( b : Bytes) : Void {
+ context.Write();
+
+ var tmp = BytesFromString( JSONConstants.QUOTE);
+ trans.write( tmp, 0, tmp.length);
+
+ for (i in 0 ... b.length) {
+ var value = b.get(i);
+
+ if ((value & 0x00FF) >= 0x30)
+ {
+ if (String.fromCharCode(value) == JSONConstants.BACKSLASH.charAt(0))
+ {
+ tmp = BytesFromString( JSONConstants.BACKSLASH + JSONConstants.BACKSLASH);
+ trans.write( tmp, 0, tmp.length);
+ }
+ else
+ {
+ trans.write( b, i, 1);
+ }
+ }
+ else
+ {
+ var num = JSONConstants.JSON_CHAR_TABLE[value];
+ if (num == 1)
+ {
+ trans.write( b, i, 1);
+ }
+ else if (num > 1)
+ {
+ var buf = new BytesBuffer();
+ buf.addString( JSONConstants.BACKSLASH);
+ buf.addByte( num);
+ tmp = buf.getBytes();
+ trans.write( tmp, 0, tmp.length);
+ }
+ else
+ {
+ var buf = new BytesBuffer();
+ buf.addString( JSONConstants.ESCSEQ);
+ buf.addString( HexChar( (value & 0xFF000000) >> 12));
+ buf.addString( HexChar( (value & 0x00FF0000) >> 8));
+ buf.addString( HexChar( (value & 0x0000FF00) >> 4));
+ buf.addString( HexChar( value & 0x000000FF));
+ tmp = buf.getBytes();
+ trans.write( tmp, 0, tmp.length);
+ }
+ }
+ }
+
+ tmp = BytesFromString( JSONConstants.QUOTE);
+ trans.write( tmp, 0, tmp.length);
+ }
+
+ // Write out number as a JSON value. If the context dictates so,
+ // it will be wrapped in quotes to output as a JSON string.
+ private function WriteJSONInteger( num : Int) : Void {
+ context.Write();
+
+ var str : String = "";
+ var escapeNum : Bool = context.EscapeNumbers();
+
+ if (escapeNum) {
+ str += JSONConstants.QUOTE;
+ }
+
+ str += Std.string(num);
+
+ if (escapeNum) {
+ str += JSONConstants.QUOTE;
+ }
+
+ var tmp = BytesFromString( str);
+ trans.write( tmp, 0, tmp.length);
+ }
+
+ // Write out number as a JSON value. If the context dictates so,
+ // it will be wrapped in quotes to output as a JSON string.
+ private function WriteJSONInt64( num : Int64) : Void {
+ context.Write();
+
+ var str : String = "";
+ var escapeNum : Bool = context.EscapeNumbers();
+
+ if (escapeNum) {
+ str += JSONConstants.QUOTE;
+ }
+
+ str += Std.string(num);
+
+ if (escapeNum) {
+ str += JSONConstants.QUOTE;
+ }
+
+ var tmp = BytesFromString( str);
+ trans.write( tmp, 0, tmp.length);
+ }
+
+ // Write out a double as a JSON value. If it is NaN or infinity or if the
+ // context dictates escaping, Write out as JSON string.
+ private function WriteJSONDouble(num : Float) : Void {
+ context.Write();
+
+
+ var special : Bool = false;
+ var rendered : String = "";
+ if( Math.isNaN(num)) {
+ special = true;
+ rendered = JSONConstants.FLOAT_IS_NAN;
+ } else if (! Math.isFinite(num)) {
+ special = true;
+ if( num > 0) {
+ rendered = JSONConstants.FLOAT_IS_POS_INF;
+ } else {
+ rendered = JSONConstants.FLOAT_IS_NEG_INF;
+ }
+ } else {
+ rendered = Std.string(num); // plain and simple float number
+ }
+
+ // compose output
+ var escapeNum : Bool = special || context.EscapeNumbers();
+ var str : String = "";
+ if (escapeNum) {
+ str += JSONConstants.QUOTE;
+ }
+ str += rendered;
+ if (escapeNum) {
+ str += JSONConstants.QUOTE;
+ }
+
+ var tmp = BytesFromString( str);
+ trans.write( tmp, 0, tmp.length);
+ }
+
+ // Write out contents of byte array b as a JSON string with base-64 encoded data
+ private function WriteJSONBase64( b : Bytes) : Void {
+ context.Write();
+
+ var buf = new BytesBuffer();
+ buf.addString( JSONConstants.QUOTE);
+ buf.addString( Base64.encode(b));
+ buf.addString( JSONConstants.QUOTE);
+
+ var tmp = buf.getBytes();
+ trans.write( tmp, 0, tmp.length);
+ }
+
+ private function WriteJSONObjectStart() : Void {
+ context.Write();
+ var tmp = BytesFromString( JSONConstants.LBRACE);
+ trans.write( tmp, 0, tmp.length);
+ PushContext( new JSONPairContext(this));
+ }
+
+ private function WriteJSONObjectEnd() : Void {
+ PopContext();
+ var tmp = BytesFromString( JSONConstants.RBRACE);
+ trans.write( tmp, 0, tmp.length);
+ }
+
+ private function WriteJSONArrayStart() : Void {
+ context.Write();
+ var tmp = BytesFromString( JSONConstants.LBRACKET);
+ trans.write( tmp, 0, tmp.length);
+ PushContext( new JSONListContext(this));
+ }
+
+ private function WriteJSONArrayEnd() : Void {
+ PopContext();
+ var tmp = BytesFromString( JSONConstants.RBRACKET);
+ trans.write( tmp, 0, tmp.length);
+ }
+
+
+ /**
+ * Reading methods.
+ */
+
+ // Read a byte that must match char, otherwise an exception is thrown.
+ public function ReadJSONSyntaxChar( char : String) : Void {
+ var b = BytesFromString( char);
+
+ var ch = reader.Read();
+ if (ch.get(0) != b.get(0))
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ 'Unexpected character: $ch');
+ }
+ }
+
+ // Read in a JSON string, unescaping as appropriate.
+ // Skip Reading from the context if skipContext is true.
+ private function ReadJSONString(skipContext : Bool) : String
+ {
+ if (!skipContext)
+ {
+ context.Read();
+ }
+
+ var buffer : BytesBuffer = new BytesBuffer();
+
+ ReadJSONSyntaxChar( JSONConstants.QUOTE);
+ while (true)
+ {
+ var ch = reader.Read();
+
+ // end of string?
+ if (StringFromBytes(ch) == JSONConstants.QUOTE)
+ {
+ break;
+ }
+
+ // escaped?
+ if (StringFromBytes(ch) != JSONConstants.ESCSEQ.charAt(0))
+ {
+ buffer.addByte( ch.get(0));
+ continue;
+ }
+
+ // distinguish between \uXXXX (hex unicode) and \X (control chars)
+ ch = reader.Read();
+ if (StringFromBytes(ch) != JSONConstants.ESCSEQ.charAt(1))
+ {
+ var value = JSONConstants.ESCAPE_CHARS_TO_VALUES[ch.get(0)];
+ if( value == null)
+ {
+ throw new TProtocolException( TProtocolException.INVALID_DATA, "Expected control char");
+ }
+ buffer.addByte( value);
+ continue;
+ }
+
+
+ // it's \uXXXX
+ var hexbuf = new BytesBuffer();
+ var hexlen = trans.readAll( hexbuf, 0, 4);
+ if( hexlen != 4)
+ {
+ throw new TProtocolException( TProtocolException.INVALID_DATA, "Not enough data for \\uNNNN sequence");
+ }
+
+ var hexdigits = hexbuf.getBytes();
+ var charcode = 0;
+ charcode = (charcode << 4) + HexVal( String.fromCharCode(hexdigits.get(0)));
+ charcode = (charcode << 4) + HexVal( String.fromCharCode(hexdigits.get(1)));
+ charcode = (charcode << 4) + HexVal( String.fromCharCode(hexdigits.get(2)));
+ charcode = (charcode << 4) + HexVal( String.fromCharCode(hexdigits.get(3)));
+ buffer.addString( String.fromCharCode(charcode));
+ }
+
+ return StringFromBytes( buffer.getBytes());
+ }
+
+ // Return true if the given byte could be a valid part of a JSON number.
+ private function IsJSONNumeric(b : Int) : Bool {
+ switch (b)
+ {
+ case "+".code: return true;
+ case "-".code: return true;
+ case ".".code: return true;
+ case "0".code: return true;
+ case "1".code: return true;
+ case "2".code: return true;
+ case "3".code: return true;
+ case "4".code: return true;
+ case "5".code: return true;
+ case "6".code: return true;
+ case "7".code: return true;
+ case "8".code: return true;
+ case "9".code: return true;
+ case "E".code: return true;
+ case "e".code: return true;
+ }
+ return false;
+ }
+
+ // Read in a sequence of characters that are all valid in JSON numbers. Does
+ // not do a complete regex check to validate that this is actually a number.
+ private function ReadJSONNumericChars() : String
+ {
+ var buffer : BytesBuffer = new BytesBuffer();
+ while (true)
+ {
+ var ch = reader.Peek();
+ if( ! IsJSONNumeric( ch.get(0)))
+ {
+ break;
+ }
+ buffer.addByte( reader.Read().get(0));
+ }
+ return StringFromBytes( buffer.getBytes());
+ }
+
+ // Read in a JSON number. If the context dictates, Read in enclosing quotes.
+ private function ReadJSONInteger() : Int {
+ context.Read();
+
+ if (context.EscapeNumbers()) {
+ ReadJSONSyntaxChar( JSONConstants.QUOTE);
+ }
+
+ var str : String = ReadJSONNumericChars();
+
+ if (context.EscapeNumbers()) {
+ ReadJSONSyntaxChar( JSONConstants.QUOTE);
+ }
+
+ var value = Std.parseInt(str);
+ if( value == null) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, 'Bad numeric data: $str');
+ }
+
+ return value;
+ }
+
+ // Read in a JSON number. If the context dictates, Read in enclosing quotes.
+ private function ReadJSONInt64() : haxe.Int64 {
+ context.Read();
+
+ if (context.EscapeNumbers()) {
+ ReadJSONSyntaxChar( JSONConstants.QUOTE);
+ }
+
+ var str : String = ReadJSONNumericChars();
+ if( str.length == 0) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, 'Bad numeric data: $str');
+ }
+
+ if (context.EscapeNumbers()) {
+ ReadJSONSyntaxChar( JSONConstants.QUOTE);
+ }
+
+ // process sign
+ var bMinus = false;
+ var startAt = 0;
+ if( (str.charAt(0) == "+") || (str.charAt(0) == "-")) {
+ bMinus = (str.charAt(0) == "-");
+ startAt++;
+ }
+
+ // process digits
+ var value : Int64 = Int64.make(0,0);
+ var bGotDigits = false;
+ for( i in startAt ... str.length) {
+ var ch = str.charAt(i);
+ var digit = JSONConstants.DECIMAL_DIGITS[ch];
+ if( digit == null) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, 'Bad numeric data: $str');
+ }
+ bGotDigits = true;
+
+ // these are decimal digits
+ value = Int64.mul( value, Int64.make(0,10));
+ value = Int64.add( value, Int64.make(0,digit));
+ }
+
+ // process pending minus sign, if applicable
+ // this should also handle the edge case MIN_INT64 correctly
+ if( bMinus && (Int64.compare(value,Int64.make(0,0)) > 0)) {
+ value = Int64.neg( value);
+ bMinus = false;
+ }
+
+ if( ! bGotDigits) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, 'Bad numeric data: $str');
+ }
+
+ return value;
+ }
+
+ // Read in a JSON double value. Throw if the value is not wrapped in quotes
+ // when expected or if wrapped in quotes when not expected.
+ private function ReadJSONDouble() : Float {
+ context.Read();
+
+ var str : String = "";
+ if (StringFromBytes(reader.Peek()) == JSONConstants.QUOTE) {
+ str = ReadJSONString(true);
+
+ // special cases
+ if( str == JSONConstants.FLOAT_IS_NAN) {
+ return Math.NaN;
+ }
+ if( str == JSONConstants.FLOAT_IS_POS_INF) {
+ return Math.POSITIVE_INFINITY;
+ }
+ if( str == JSONConstants.FLOAT_IS_NEG_INF) {
+ return Math.NEGATIVE_INFINITY;
+ }
+
+ if( ! context.EscapeNumbers()) {
+ // throw - we should not be in a string in this case
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Numeric data unexpectedly quoted");
+ }
+ }
+ else
+ {
+ if( context.EscapeNumbers()) {
+ // This will throw - we should have had a quote if EscapeNumbers() == true
+ ReadJSONSyntaxChar( JSONConstants.QUOTE);
+ }
+
+ str = ReadJSONNumericChars();
+ }
+
+ // parse and check - we should have at least one valid digit
+ var dub = Std.parseFloat( str);
+ if( (str.length == 0) || Math.isNaN(dub)) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, 'Bad numeric data: $str');
+ }
+
+ return dub;
+ }
+
+ // Read in a JSON string containing base-64 encoded data and decode it.
+ private function ReadJSONBase64() : Bytes
+ {
+ var str = ReadJSONString(false);
+ return Base64.decode( str);
+ }
+
+ private function ReadJSONObjectStart() : Void {
+ context.Read();
+ ReadJSONSyntaxChar( JSONConstants.LBRACE);
+ PushContext(new JSONPairContext(this));
+ }
+
+ private function ReadJSONObjectEnd() : Void {
+ ReadJSONSyntaxChar( JSONConstants.RBRACE);
+ PopContext();
+ }
+
+ private function ReadJSONArrayStart() : Void {
+ context.Read();
+ ReadJSONSyntaxChar( JSONConstants.LBRACKET);
+ PushContext(new JSONListContext(this));
+ }
+
+ private function ReadJSONArrayEnd() : Void {
+ ReadJSONSyntaxChar( JSONConstants.RBRACKET);
+ PopContext();
+ }
+
+
+ public static function BytesFromString( str : String) : Bytes {
+ var buf = new BytesBuffer();
+ if( utf8Strings)
+ buf.addString( str); // no need to encode on UTF8 targets, the string is just fine
+ else
+ buf.addString( Utf8.encode( str));
+ return buf.getBytes();
+ }
+
+ public static function StringFromBytes( buf : Bytes) : String {
+ var inp = new BytesInput( buf);
+ if( buf.length == 0)
+ return ""; // readString() would return null in that case, which is wrong
+ var str = inp.readString( buf.length);
+ if( utf8Strings)
+ return str; // no need to decode on UTF8 targets, the string is just fine
+ else
+ return Utf8.decode( str);
+ }
+
+ // Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its corresponding hex value
+ private static function HexVal(char : String) : Int {
+ var value = JSONConstants.HEX_DIGITS[char];
+ if( value == null) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, 'Expected hex character: $char');
+ }
+ return value;
+ }
+
+ // Convert a byte containing a hex nibble to its corresponding hex character
+ private static function HexChar(nibble : Int) : String
+ {
+ return "0123456789abcdef".charAt(nibble & 0x0F);
+ }
+
+
+}
+
+
+@:allow(TJSONProtocol)
+class JSONConstants {
+ public static var COMMA = ",";
+ public static var COLON = ":";
+ public static var LBRACE = "{";
+ public static var RBRACE = "}";
+ public static var LBRACKET = "[";
+ public static var RBRACKET = "]";
+ public static var QUOTE = "\"";
+ public static var BACKSLASH = "\\";
+
+ public static var ESCSEQ = "\\u";
+
+ public static var FLOAT_IS_NAN = "NaN";
+ public static var FLOAT_IS_POS_INF = "Infinity";
+ public static var FLOAT_IS_NEG_INF = "-Infinity";
+
+ public static var VERSION = 1;
+ public static var JSON_CHAR_TABLE = [
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ "b".code, "t".code, "n".code, 0, "f".code, "r".code, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, "\"".code, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ ];
+
+ public static var ESCAPE_CHARS = ['"','\\','/','b','f','n','r','t'];
+ public static var ESCAPE_CHARS_TO_VALUES = [
+ "\"".code => 0x22,
+ "\\".code => 0x5C,
+ "/".code => 0x2F,
+ "b".code => 0x08,
+ "f".code => 0x0C,
+ "n".code => 0x0A,
+ "r".code => 0x0D,
+ "t".code => 0x09
+ ];
+
+ public static var DECIMAL_DIGITS = [
+ "0" => 0,
+ "1" => 1,
+ "2" => 2,
+ "3" => 3,
+ "4" => 4,
+ "5" => 5,
+ "6" => 6,
+ "7" => 7,
+ "8" => 8,
+ "9" => 9
+ ];
+
+ public static var HEX_DIGITS = [
+ "0" => 0,
+ "1" => 1,
+ "2" => 2,
+ "3" => 3,
+ "4" => 4,
+ "5" => 5,
+ "6" => 6,
+ "7" => 7,
+ "8" => 8,
+ "9" => 9,
+ "A" => 10,
+ "a" => 10,
+ "B" => 11,
+ "b" => 11,
+ "C" => 12,
+ "c" => 12,
+ "D" => 13,
+ "d" => 13,
+ "E" => 14,
+ "e" => 14,
+ "F" => 15,
+ "f" => 15
+ ];
+
+
+ public static var DEF_STRING_SIZE = 16;
+
+ public static var NAME_BOOL = 'tf';
+ public static var NAME_BYTE = 'i8';
+ public static var NAME_I16 = 'i16';
+ public static var NAME_I32 = 'i32';
+ public static var NAME_I64 = 'i64';
+ public static var NAME_DOUBLE = 'dbl';
+ public static var NAME_STRUCT = 'rec';
+ public static var NAME_STRING = 'str';
+ public static var NAME_MAP = 'map';
+ public static var NAME_LIST = 'lst';
+ public static var NAME_SET = 'set';
+
+ public static function GetTypeNameForTypeID(typeID : Int) : String {
+ switch (typeID)
+ {
+ case TType.BOOL: return NAME_BOOL;
+ case TType.BYTE: return NAME_BYTE;
+ case TType.I16: return NAME_I16;
+ case TType.I32: return NAME_I32;
+ case TType.I64: return NAME_I64;
+ case TType.DOUBLE: return NAME_DOUBLE;
+ case TType.STRING: return NAME_STRING;
+ case TType.STRUCT: return NAME_STRUCT;
+ case TType.MAP: return NAME_MAP;
+ case TType.SET: return NAME_SET;
+ case TType.LIST: return NAME_LIST;
+ }
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "Unrecognized type");
+ }
+
+ private static var NAMES_TO_TYPES = [
+ NAME_BOOL => TType.BOOL,
+ NAME_BYTE => TType.BYTE,
+ NAME_I16 => TType.I16,
+ NAME_I32 => TType.I32,
+ NAME_I64 => TType.I64,
+ NAME_DOUBLE => TType.DOUBLE,
+ NAME_STRING => TType.STRING,
+ NAME_STRUCT => TType.STRUCT,
+ NAME_MAP => TType.MAP,
+ NAME_SET => TType.SET,
+ NAME_LIST => TType.LIST
+ ];
+
+ public static function GetTypeIDForTypeName(name : String) : Int
+ {
+ var type = NAMES_TO_TYPES[name];
+ if( null != type) {
+ return type;
+ }
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "Unrecognized type");
+ }
+
+}
+
+
+// Base class for tracking JSON contexts that may require inserting/Reading
+// additional JSON syntax characters. This base context does nothing.
+@:allow(TJSONProtocol)
+class JSONBaseContext
+{
+ private var proto : TJSONProtocol;
+
+ public function new(proto : TJSONProtocol )
+ {
+ this.proto = proto;
+ }
+
+ public function Write() : Void { }
+ public function Read() : Void { }
+
+ public function EscapeNumbers() : Bool {
+ return false;
+ }
+}
+
+
+// Context for JSON lists.
+// Will insert/Read commas before each item except for the first one
+@:allow(TJSONProtocol)
+class JSONListContext extends JSONBaseContext
+{
+ public function new( proto : TJSONProtocol) {
+ super(proto);
+ }
+
+ private var first : Bool = true;
+
+ public override function Write() : Void {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ var buf = new BytesBuffer();
+ buf.addString( JSONConstants.COMMA);
+ var tmp = buf.getBytes();
+ proto.trans.write( tmp, 0, tmp.length);
+ }
+ }
+
+ public override function Read() : Void {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ proto.ReadJSONSyntaxChar( JSONConstants.COMMA);
+ }
+ }
+}
+
+
+// Context for JSON records.
+// Will insert/Read colons before the value portion of each record
+// pair, and commas before each key except the first. In addition,
+// will indicate that numbers in the key position need to be escaped
+// in quotes (since JSON keys must be strings).
+@:allow(TJSONProtocol)
+class JSONPairContext extends JSONBaseContext
+{
+ public function new( proto : TJSONProtocol ) {
+ super( proto);
+ }
+
+ private var first : Bool = true;
+ private var colon : Bool = true;
+
+ public override function Write() : Void {
+ if (first)
+ {
+ first = false;
+ colon = true;
+ }
+ else
+ {
+ var buf = new BytesBuffer();
+ buf.addString( colon ? JSONConstants.COLON : JSONConstants.COMMA);
+ var tmp = buf.getBytes();
+ proto.trans.write( tmp, 0, tmp.length);
+ colon = !colon;
+ }
+ }
+
+ public override function Read() : Void {
+ if (first)
+ {
+ first = false;
+ colon = true;
+ }
+ else
+ {
+ proto.ReadJSONSyntaxChar( colon ? JSONConstants.COLON : JSONConstants.COMMA);
+ colon = !colon;
+ }
+ }
+
+ public override function EscapeNumbers() : Bool
+ {
+ return colon;
+ }
+}
+
+// Holds up to one byte from the transport
+@:allow(TJSONProtocol)
+class LookaheadReader {
+
+ private var proto : TJSONProtocol;
+ private var data : Bytes;
+
+ public function new( proto : TJSONProtocol ) {
+ this.proto = proto;
+ data = null;
+ }
+
+
+ // Return and consume the next byte to be Read, either taking it from the
+ // data buffer if present or getting it from the transport otherwise.
+ public function Read() : Bytes {
+ var retval = Peek();
+ data = null;
+ return retval;
+ }
+
+ // Return the next byte to be Read without consuming, filling the data
+ // buffer if it has not been filled alReady.
+ public function Peek() : Bytes {
+ if (data == null) {
+ var buf = new BytesBuffer();
+ proto.trans.readAll(buf, 0, 1);
+ data = buf.getBytes();
+ }
+ return data;
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TJSONProtocolFactory.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TJSONProtocolFactory.hx
new file mode 100644
index 000000000..363558a1f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TJSONProtocolFactory.hx
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.transport.TTransport;
+
+
+/**
+* JSON Protocol Factory
+*/
+class TJSONProtocolFactory implements TProtocolFactory {
+
+ public function new() {
+ }
+
+ public function getProtocol( trans : TTransport) : TProtocol {
+ return new TJSONProtocol( trans);
+ }
+}
+
+
+
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TList.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TList.hx
new file mode 100644
index 000000000..5a1fb5500
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TList.hx
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+class TList {
+
+ public var elemType : Int;
+ public var size : Int;
+
+ public function new(t : Int = 0, s : Int = 0) {
+ elemType = t;
+ size = s;
+ }
+
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMap.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMap.hx
new file mode 100644
index 000000000..f4e6288e2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMap.hx
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+class TMap {
+
+ public var keyType : Int;
+ public var valueType : Int;
+ public var size : Int;
+
+ public function new(k : Int = 0, v : Int = 0, s : Int = 0) {
+ keyType = k;
+ valueType = v;
+ size = s;
+ }
+
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMessage.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMessage.hx
new file mode 100644
index 000000000..d99264a56
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMessage.hx
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+class TMessage {
+
+ public var name : String;
+ public var type : Int;
+ public var seqid : Int;
+
+ public function new(n : String = "", t : Int = 0, s : Int = 0) {
+ name = n;
+ type = t;
+ seqid = s;
+ }
+
+ public function toString() : String {
+ return "<TMessage name:'" + name + "' type: " + type + " seqid:" + seqid + ">";
+ }
+
+ public function equals(other:TMessage) : Bool {
+ return name == other.name && type == other.type && seqid == other.seqid;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMessageType.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMessageType.hx
new file mode 100644
index 000000000..706d3298b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMessageType.hx
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+@:enum
+abstract TMessageType(Int) from Int to Int {
+ public static inline var CALL : Int = 1;
+ public static inline var REPLY : Int = 2;
+ public static inline var EXCEPTION : Int = 3;
+ public static inline var ONEWAY : Int = 4;
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProcessor.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProcessor.hx
new file mode 100644
index 000000000..50aa3cdf0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProcessor.hx
@@ -0,0 +1,177 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import haxe.ds.StringMap;
+import org.apache.thrift.TApplicationException;
+import org.apache.thrift.TProcessor;
+
+import org.apache.thrift.transport.TTransport;
+
+
+/**
+ * TMultiplexedProcessor is a TProcessor allowing a single TServer to provide multiple services.
+ * To do so, you instantiate the processor and then register additional processors with it,
+ * as shown in the following example:
+ *
+ * TMultiplexedProcessor processor = new TMultiplexedProcessor();
+ *
+ * processor.registerProcessor(
+ * "Calculator",
+ * new Calculator.Processor(new CalculatorHandler()));
+ *
+ * processor.registerProcessor(
+ * "WeatherReport",
+ * new WeatherReport.Processor(new WeatherReportHandler()));
+ *
+ * TServerTransport t = new TServerSocket(9090);
+ * TSimpleServer server = new TSimpleServer(processor, t);
+ *
+ * server.serve();
+ */
+class TMultiplexedProcessor implements TProcessor
+{
+ private var serviceProcessorMap : StringMap<TProcessor> = new StringMap<TProcessor>();
+ private var defaultProcessor : TProcessor = null;
+
+ public function new() {
+ }
+
+ /**
+ * 'Register' a service with this TMultiplexedProcessor. This allows us to broker
+ * requests to individual services by using the service name to select them at request time.
+ *
+ * Args:
+ * - serviceName Name of a service, has to be identical to the name
+ * declared in the Thrift IDL, e.g. "WeatherReport".
+ * - processor Implementation of a service, usually referred to as "handlers",
+ * e.g. WeatherReportHandler implementing WeatherReport.Iface.
+ */
+ public function RegisterProcessor(serviceName : String, processor : TProcessor, asDefault : Bool = false) : Void {
+ serviceProcessorMap.set(serviceName, processor);
+ if ( asDefault) {
+ if( defaultProcessor != null) {
+ throw new TApplicationException( TApplicationException.UNKNOWN, "Can't have multiple default processors");
+ } else {
+ defaultProcessor = processor;
+ }
+ }
+ }
+
+
+ private function Fail( oprot : TProtocol, message : TMessage, extype : Int, etxt : String) : Void {
+ var appex = new TApplicationException( extype, etxt);
+
+ var newMessage = new TMessage(message.name, TMessageType.EXCEPTION, message.seqid);
+
+ oprot.writeMessageBegin(newMessage);
+ appex.write( oprot);
+ oprot.writeMessageEnd();
+ oprot.getTransport().flush();
+ }
+
+
+ /**
+ * This implementation of process performs the following steps:
+ *
+ * - Read the beginning of the message.
+ * - Extract the service name from the message.
+ * - Using the service name to locate the appropriate processor.
+ * - Dispatch to the processor, with a decorated instance of TProtocol
+ * that allows readMessageBegin() to return the original TMessage.
+ *
+ * Throws an exception if
+ * - the message type is not CALL or ONEWAY,
+ * - the service name was not found in the message, or
+ * - the service name has not been RegisterProcessor()ed.
+ */
+ public function process( iprot : TProtocol, oprot : TProtocol) : Bool {
+ /* Use the actual underlying protocol (e.g. TBinaryProtocol) to read the
+ message header. This pulls the message "off the wire", which we'll
+ deal with at the end of this method. */
+
+ var message : TMessage = iprot.readMessageBegin();
+ var methodName : String = "";
+
+ if ((message.type != TMessageType.CALL) && (message.type != TMessageType.ONEWAY))
+ {
+ Fail(oprot, message,
+ TApplicationException.INVALID_MESSAGE_TYPE,
+ "Message type CALL or ONEWAY expected");
+ return false;
+ }
+
+ // Extract the service name
+ var actualProcessor : TProcessor = null;
+ var index = message.name.indexOf(TMultiplexedProtocol.SEPARATOR);
+ if (index < 0) {
+ // fallback to default processor
+ methodName = message.name;
+ actualProcessor = defaultProcessor;
+ if( actualProcessor == null) {
+ Fail(oprot, message,
+ TApplicationException.INVALID_PROTOCOL,
+ "Service name not found in message name: " + message.name + " and no default processor defined. " +
+ "Did you forget to use a TMultiplexProtocol in your client?");
+ return false;
+ }
+
+ } else {
+ // service name given
+ var serviceName = message.name.substring(0, index);
+ methodName = message.name.substring( serviceName.length + TMultiplexedProtocol.SEPARATOR.length);
+ actualProcessor = serviceProcessorMap.get( serviceName);
+ if( actualProcessor == null) {
+ Fail(oprot, message,
+ TApplicationException.INTERNAL_ERROR,
+ "Service name not found: " + serviceName + ". " +
+ "Did you forget to call RegisterProcessor()?");
+ return false;
+ }
+ }
+
+ // Create a new TMessage, removing the service name
+ // Dispatch processing to the stored processor
+ var newMessage = new TMessage( methodName, message.type, message.seqid);
+ var storedMsg = new StoredMessageProtocol( iprot, newMessage);
+ return actualProcessor.process( storedMsg, oprot);
+ }
+}
+
+
+/**
+ * Our goal was to work with any protocol. In order to do that, we needed
+ * to allow them to call readMessageBegin() and get a TMessage in exactly
+ * the standard format, without the service name prepended to TMessage.name.
+ */
+class StoredMessageProtocol extends TProtocolDecorator
+{
+ private var messageBegin : TMessage;
+
+ public function new( protocol : TProtocol, messageBegin : TMessage) {
+ super( protocol);
+ this.messageBegin = messageBegin;
+ }
+
+ public override function readMessageBegin() : TMessage {
+ return messageBegin;
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProtocol.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProtocol.hx
new file mode 100644
index 000000000..cacd1d782
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProtocol.hx
@@ -0,0 +1,97 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.transport.TTransport;
+
+
+/**
+ * TMultiplexedProtocol is a protocol-independent concrete decorator that allows a Thrift
+ * client to communicate with a multiplexing Thrift server, by prepending the service name
+ * to the function name during function calls.
+ *
+ * NOTE: THIS IS NOT TO BE USED BY SERVERS.
+ * On the server, use TMultiplexedProcessor to handle requests from a multiplexing client.
+ *
+ * This example uses a single socket transport to invoke two services:
+ *
+ * TSocket transport = new TSocket("localhost", 9090);
+ * transport.open();
+ *
+ * TBinaryProtocol protocol = new TBinaryProtocol(transport);
+ *
+ * TMultiplexedProtocol mp = new TMultiplexedProtocol(protocol, "Calculator");
+ * Calculator.Client service = new Calculator.Client(mp);
+ *
+ * TMultiplexedProtocol mp2 = new TMultiplexedProtocol(protocol, "WeatherReport");
+ * WeatherReport.Client service2 = new WeatherReport.Client(mp2);
+ *
+ * System.out.println(service.add(2,2));
+ * System.out.println(service2.getTemperature());
+ *
+ */
+class TMultiplexedProtocol extends TProtocolDecorator {
+
+ /** Used to delimit the service name from the function name */
+ public static inline var SEPARATOR : String = ":";
+
+ private var service : String;
+
+ /**
+ * Wrap the specified protocol, allowing it to be used to communicate with a
+ * multiplexing server. The <code>serviceName</code> is required as it is
+ * prepended to the message header so that the multiplexing server can broker
+ * the function call to the proper service.
+ *
+ * Args:
+ * protocol Your communication protocol of choice, e.g. TBinaryProtocol
+ * serviceName The service name of the service communicating via this protocol.
+ */
+ public function new( protocol : TProtocol, serviceName : String) {
+ super( protocol);
+ service = serviceName;
+ }
+
+ /**
+ * Prepends the service name to the function name, separated by TMultiplexedProtocol.SEPARATOR.
+ * Args:
+ * tMessage The original message.
+ */
+ public override function writeMessageBegin( message : TMessage) : Void {
+ switch( message.type)
+ {
+ case TMessageType.CALL:
+ super.writeMessageBegin(new TMessage(
+ service + SEPARATOR + message.name,
+ message.type,
+ message.seqid));
+
+ case TMessageType.ONEWAY:
+ super.writeMessageBegin(new TMessage(
+ service + SEPARATOR + message.name,
+ message.type,
+ message.seqid));
+
+ default:
+ super.writeMessageBegin(message);
+ }
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocol.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocol.hx
new file mode 100644
index 000000000..b7f3842d0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocol.hx
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import haxe.io.Bytes;
+import org.apache.thrift.TException;
+import org.apache.thrift.transport.TTransport;
+
+/**
+* Protocol interface definition
+*/
+interface TProtocol {
+
+ function getTransport() : TTransport;
+
+ /**
+ * Writing methods.
+ */
+ function writeMessageBegin(message:TMessage) : Void;
+ function writeMessageEnd() : Void;
+ function writeStructBegin(struct:TStruct) : Void;
+ function writeStructEnd() : Void;
+ function writeFieldBegin(field:TField) : Void;
+ function writeFieldEnd() : Void;
+ function writeFieldStop() : Void;
+ function writeMapBegin(map:TMap) : Void;
+ function writeMapEnd() : Void;
+ function writeListBegin(list:TList) : Void;
+ function writeListEnd() : Void;
+ function writeSetBegin(set:TSet) : Void;
+ function writeSetEnd() : Void;
+ function writeBool(b : Bool) : Void;
+ function writeByte(b : Int) : Void;
+ function writeI16(i16 : Int) : Void;
+ function writeI32(i32 : Int) : Void;
+ function writeI64(i64 : haxe.Int64) : Void;
+ function writeDouble(dub : Float) : Void;
+ function writeString(str : String) : Void;
+ function writeBinary(bin : Bytes) : Void;
+
+ /**
+ * Reading methods.
+ */
+ function readMessageBegin():TMessage;
+ function readMessageEnd() : Void;
+ function readStructBegin():TStruct;
+ function readStructEnd() : Void;
+ function readFieldBegin():TField;
+ function readFieldEnd() : Void;
+ function readMapBegin():TMap;
+ function readMapEnd() : Void;
+ function readListBegin():TList;
+ function readListEnd() : Void;
+ function readSetBegin():TSet;
+ function readSetEnd() : Void;
+ function readBool() : Bool;
+ function readByte() : Int;
+ function readI16() : Int;
+ function readI32() : Int;
+ function readI64() : haxe.Int64;
+ function readDouble() : Float;
+ function readString() : String;
+ function readBinary() : Bytes;
+
+ // recursion tracking
+ function IncrementRecursionDepth() : Void;
+ function DecrementRecursionDepth() : Void;
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocolDecorator.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocolDecorator.hx
new file mode 100644
index 000000000..769e93cc5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocolDecorator.hx
@@ -0,0 +1,226 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import haxe.io.Bytes;
+import haxe.Int64;
+
+import org.apache.thrift.transport.TTransport;
+
+
+/**
+ * TProtocolDecorator forwards all requests to an enclosed TProtocol instance,
+ * providing a way to author concise concrete decorator subclasses. While it has
+ * no abstract methods, it is marked abstract as a reminder that by itself,
+ * it does not modify the behaviour of the enclosed TProtocol.
+ *
+ * See p.175 of Design Patterns (by Gamma et al.)
+ * See TMultiplexedProtocol
+ */
+class TProtocolDecorator implements TProtocol
+{
+ private var wrapped : TProtocol;
+
+ /**
+ * Encloses the specified protocol.
+ * @param protocol All operations will be forward to this protocol. Must be non-null.
+ */
+ private function new( protocol : TProtocol) // not to be instantiated, must derive a class
+ {
+ wrapped = protocol;
+ }
+
+ public function getTransport() : TTransport {
+ return wrapped.getTransport();
+ }
+
+ public function writeMessageBegin( value : TMessage) : Void {
+ wrapped.writeMessageBegin( value);
+ }
+
+ public function writeMessageEnd() : Void {
+ wrapped.writeMessageEnd();
+ }
+
+ public function writeStructBegin(value : TStruct) : Void {
+ wrapped.writeStructBegin( value);
+ }
+
+ public function writeStructEnd() : Void {
+ wrapped.writeStructEnd();
+ }
+
+ public function writeFieldBegin(value : TField) : Void {
+ wrapped.writeFieldBegin( value);
+ }
+
+ public function writeFieldEnd() : Void {
+ wrapped.writeFieldEnd();
+ }
+
+ public function writeFieldStop() : Void {
+ wrapped.writeFieldStop();
+ }
+
+ public function writeMapBegin( value : TMap) : Void {
+ wrapped.writeMapBegin( value);
+ }
+
+ public function writeMapEnd() : Void {
+ wrapped.writeMapEnd();
+ }
+
+ public function writeListBegin( value : TList) : Void {
+ wrapped.writeListBegin( value);
+ }
+
+ public function writeListEnd() : Void {
+ wrapped.writeListEnd();
+ }
+
+ public function writeSetBegin( value : TSet) : Void {
+ wrapped.writeSetBegin( value);
+ }
+
+ public function writeSetEnd() : Void {
+ wrapped.writeSetEnd();
+ }
+
+ public function writeBool(value : Bool) : Void {
+ wrapped.writeBool( value);
+ }
+
+ public function writeByte(value : Int) : Void {
+ wrapped.writeByte( value);
+ }
+
+ public function writeI16(value : Int) : Void {
+ wrapped.writeI16( value);
+ }
+
+ public function writeI32(value : Int) : Void {
+ wrapped.writeI32( value);
+ }
+
+ public function writeI64(value : haxe.Int64) : Void {
+ wrapped.writeI64( value);
+ }
+
+ public function writeDouble(value : Float) : Void {
+ wrapped.writeDouble( value);
+ }
+
+ public function writeString(value : String) : Void {
+ wrapped.writeString( value);
+ }
+
+ public function writeBinary(value : Bytes ) : Void {
+ wrapped.writeBinary( value);
+ }
+
+ public function readMessageBegin() : TMessage {
+ return wrapped.readMessageBegin();
+ }
+
+ public function readMessageEnd() : Void {
+ wrapped.readMessageEnd();
+ }
+
+ public function readStructBegin() : TStruct {
+ return wrapped.readStructBegin();
+ }
+
+ public function readStructEnd() : Void {
+ wrapped.readStructEnd();
+ }
+
+ public function readFieldBegin() : TField {
+ return wrapped.readFieldBegin();
+ }
+
+ public function readFieldEnd() : Void {
+ wrapped.readFieldEnd();
+ }
+
+ public function readMapBegin() : TMap {
+ return wrapped.readMapBegin();
+ }
+
+ public function readMapEnd() : Void {
+ wrapped.readMapEnd();
+ }
+
+ public function readListBegin() : TList {
+ return wrapped.readListBegin();
+ }
+
+ public function readListEnd() : Void {
+ wrapped.readListEnd();
+ }
+
+ public function readSetBegin() : TSet {
+ return wrapped.readSetBegin();
+ }
+
+ public function readSetEnd() : Void {
+ wrapped.readSetEnd();
+ }
+
+ public function readBool() : Bool
+ {
+ return wrapped.readBool();
+ }
+
+ public function readByte() : Int {
+ return wrapped.readByte();
+ }
+
+ public function readI16() : Int {
+ return wrapped.readI16();
+ }
+
+ public function readI32() : Int {
+ return wrapped.readI32();
+ }
+
+ public function readI64() : haxe.Int64 {
+ return wrapped.readI64();
+ }
+
+ public function readDouble() : Float {
+ return wrapped.readDouble();
+ }
+
+ public function readString() : String {
+ return wrapped.readString();
+ }
+
+ public function readBinary() : Bytes {
+ return wrapped.readBinary();
+ }
+
+ public function IncrementRecursionDepth() : Void {
+ return wrapped.IncrementRecursionDepth();
+ }
+
+ public function DecrementRecursionDepth() : Void {
+ return wrapped.DecrementRecursionDepth();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocolException.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocolException.hx
new file mode 100644
index 000000000..a3b37a570
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocolException.hx
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.TException;
+
+class TProtocolException extends TException {
+
+ // WARNING: These are subject to be extended in the future, so we can't use enums
+ // with Haxe 3.1.3 because of https://github.com/HaxeFoundation/haxe/issues/3649
+ public static inline var UNKNOWN : Int = 0;
+ public static inline var INVALID_DATA : Int = 1;
+ public static inline var NEGATIVE_SIZE : Int = 2;
+ public static inline var SIZE_LIMIT : Int = 3;
+ public static inline var BAD_VERSION : Int = 4;
+ public static inline var NOT_IMPLEMENTED : Int = 5;
+ public static inline var DEPTH_LIMIT : Int = 6;
+
+ public function new(error : Int = UNKNOWN, message : String = "") {
+ super(message, error);
+ }
+
+
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocolFactory.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocolFactory.hx
new file mode 100644
index 000000000..1c2d62e2d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocolFactory.hx
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.transport.TTransport;
+
+interface TProtocolFactory {
+ function getProtocol(trans:TTransport):TProtocol;
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocolUtil.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocolUtil.hx
new file mode 100644
index 000000000..001e40564
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TProtocolUtil.hx
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.*;
+
+ /**
+ * Utility class with static methods for interacting with protocol data
+ * streams.
+ *
+ */
+class TProtocolUtil {
+
+ /**
+ * Skips over the next data element from the provided input TProtocol object.
+ *
+ * @param prot the protocol object to read from
+ * @param type the next value will be intepreted as this TType value.
+ */
+ public static function skip(prot:TProtocol, type : Int) : Void {
+ prot.IncrementRecursionDepth();
+ try
+ {
+ switch (type) {
+ case TType.BOOL:
+ prot.readBool();
+
+ case TType.BYTE:
+ prot.readByte();
+
+ case TType.I16:
+ prot.readI16();
+
+ case TType.I32:
+ prot.readI32();
+
+ case TType.I64:
+ prot.readI64();
+
+ case TType.DOUBLE:
+ prot.readDouble();
+
+ case TType.STRING:
+ prot.readBinary();
+
+ case TType.STRUCT:
+ prot.readStructBegin();
+ while (true) {
+ var field:TField = prot.readFieldBegin();
+ if (field.type == TType.STOP) {
+ break;
+ }
+ skip(prot, field.type);
+ prot.readFieldEnd();
+ }
+ prot.readStructEnd();
+
+ case TType.MAP:
+ var map:TMap = prot.readMapBegin();
+ for (i in 0 ... map.size) {
+ skip(prot, map.keyType);
+ skip(prot, map.valueType);
+ }
+ prot.readMapEnd();
+
+ case TType.SET:
+ var set:TSet = prot.readSetBegin();
+ for (j in 0 ... set.size) {
+ skip(prot, set.elemType);
+ }
+ prot.readSetEnd();
+
+ case TType.LIST:
+ var list:TList = prot.readListBegin();
+ for (k in 0 ... list.size) {
+ skip(prot, list.elemType);
+ }
+ prot.readListEnd();
+
+ default:
+ throw new TProtocolException(TProtocolException.UNKNOWN, "Unknown field type ${type}");
+ }
+
+ prot.DecrementRecursionDepth();
+ }
+ catch(e:Dynamic)
+ {
+ prot.DecrementRecursionDepth();
+ throw e;
+ }
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TRecursionTracker.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TRecursionTracker.hx
new file mode 100644
index 000000000..cf0211b39
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TRecursionTracker.hx
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.*;
+
+
+class TRecursionTracker {
+
+ // default
+ private static inline var DEFAULT_RECURSION_DEPTH : Int = 64;
+
+ // limit and actual value
+ public var recursionLimit : Int = DEFAULT_RECURSION_DEPTH;
+ private var recursionDepth : Int = 0;
+
+ public function IncrementRecursionDepth() : Void
+ {
+ if (recursionDepth < recursionLimit)
+ ++recursionDepth;
+ else
+ throw new TProtocolException(TProtocolException.DEPTH_LIMIT, "Depth limit exceeded");
+ }
+
+ public function DecrementRecursionDepth() : Void
+ {
+ --recursionDepth;
+ }
+
+
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TSet.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TSet.hx
new file mode 100644
index 000000000..44eab36ca
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TSet.hx
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+class TSet {
+
+ public var elemType : Int;
+ public var size : Int;
+
+ public function new(t : Int = 0, s : Int = 0) {
+ elemType = t;
+ size = s;
+ }
+
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TStruct.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TStruct.hx
new file mode 100644
index 000000000..9e0b7ddba
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TStruct.hx
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+class TStruct {
+
+ public var name : String;
+
+ public function new(n : String = "") {
+ name = n;
+ }
+
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TType.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TType.hx
new file mode 100644
index 000000000..6abbc9685
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/protocol/TType.hx
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+@:enum
+abstract TType(Int) from Int to Int {
+ public static inline var STOP : Int = 0;
+ public static inline var VOID : Int = 1;
+ public static inline var BOOL : Int = 2;
+ public static inline var BYTE : Int = 3;
+ public static inline var DOUBLE : Int = 4;
+ public static inline var I16 : Int = 6;
+ public static inline var I32 : Int = 8;
+ public static inline var I64 : Int = 10;
+ public static inline var STRING : Int = 11;
+ public static inline var STRUCT : Int = 12;
+ public static inline var MAP : Int = 13;
+ public static inline var SET : Int = 14;
+ public static inline var LIST : Int = 15;
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/server/TServer.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/server/TServer.hx
new file mode 100644
index 000000000..56eee0add
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/server/TServer.hx
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.server;
+
+import org.apache.thrift.*;
+import org.apache.thrift.protocol.*;
+import org.apache.thrift.transport.*;
+import org.apache.thrift.meta_data.*;
+
+class TServer
+{
+ private var processor : TProcessor = null;
+ private var serverTransport : TServerTransport = null;
+ private var inputTransportFactory : TTransportFactory = null;
+ private var outputTransportFactory : TTransportFactory = null;
+ private var inputProtocolFactory : TProtocolFactory = null;
+ private var outputProtocolFactory : TProtocolFactory = null;
+
+ // server events
+ public var serverEventHandler : TServerEventHandler = null;
+
+ // Log delegation
+ private var _logDelegate : Dynamic->Void = null;
+ public var logDelegate(get,set) : Dynamic->Void;
+
+ public function new( processor : TProcessor,
+ serverTransport : TServerTransport,
+ inputTransportFactory : TTransportFactory = null,
+ outputTransportFactory : TTransportFactory = null,
+ inputProtocolFactory : TProtocolFactory = null,
+ outputProtocolFactory : TProtocolFactory = null,
+ logDelegate : Dynamic->Void = null)
+ {
+ this.processor = processor;
+ this.serverTransport = serverTransport;
+ this.inputTransportFactory = inputTransportFactory;
+ this.outputTransportFactory = outputTransportFactory;
+ this.inputProtocolFactory = inputProtocolFactory;
+ this.outputProtocolFactory = outputProtocolFactory;
+ this.logDelegate = logDelegate;
+
+ ApplyMissingDefaults();
+ }
+
+ private function ApplyMissingDefaults() {
+ if( processor == null)
+ throw "Invalid server configuration: processor missing";
+ if( serverTransport == null)
+ throw "Invalid server configuration: serverTransport missing";
+ if( inputTransportFactory == null)
+ inputTransportFactory = new TTransportFactory();
+ if( outputTransportFactory == null)
+ outputTransportFactory = new TTransportFactory();
+ if( inputProtocolFactory == null)
+ inputProtocolFactory = new TBinaryProtocolFactory();
+ if( outputProtocolFactory == null)
+ outputProtocolFactory= new TBinaryProtocolFactory();
+ if( logDelegate == null)
+ logDelegate = DefaultLogDelegate;
+ }
+
+
+ private function set_logDelegate(value : Dynamic->Void) : Dynamic->Void {
+ if(value != null) {
+ _logDelegate = value;
+ } else {
+ _logDelegate = DefaultLogDelegate;
+ }
+ return _logDelegate;
+ }
+
+
+ private function get_logDelegate() : Dynamic->Void {
+ return _logDelegate;
+ }
+
+
+ private function DefaultLogDelegate(value : Dynamic) : Void {
+ trace( value);
+ }
+
+
+
+ public function Serve() : Void {
+ throw new AbstractMethodError();
+ }
+
+
+ public function Stop() : Void {
+ throw new AbstractMethodError();
+ }
+
+}
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/server/TServerEventHandler.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/server/TServerEventHandler.hx
new file mode 100644
index 000000000..9bc992755
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/server/TServerEventHandler.hx
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.server;
+
+import org.apache.thrift.*;
+import org.apache.thrift.transport.*;
+import org.apache.thrift.protocol.*;
+
+
+// Interface implemented by server users to handle events from the server
+interface TServerEventHandler {
+
+ // Called before the server begins
+ function preServe() : Void;
+
+ // Called when a new client has connected and is about to being processing
+ function createContext( input : TProtocol, output : TProtocol) : Dynamic;
+
+ // Called when a client has finished request-handling to delete server context
+ function deleteContext( serverContext : Dynamic, input : TProtocol, output : TProtocol) : Void;
+
+ // Called when a client is about to call the processor
+ function processContext( serverContext : Dynamic, transport : TTransport) : Void;
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/server/TSimpleServer.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/server/TSimpleServer.hx
new file mode 100644
index 000000000..0600744d3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/server/TSimpleServer.hx
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.server;
+
+import org.apache.thrift.*;
+import org.apache.thrift.protocol.*;
+import org.apache.thrift.transport.*;
+import org.apache.thrift.meta_data.*;
+
+// Simple single-threaded server for testing
+class TSimpleServer extends TServer {
+
+ private var stop : Bool = false;
+
+ //stops just after input transport returns EOF
+ //useful for limited scenarios, like embeding into php server
+ public var runOnce : Bool = false;
+
+ public function new( processor : TProcessor,
+ serverTransport : TServerTransport,
+ transportFactory : TTransportFactory = null,
+ protocolFactory : TProtocolFactory = null,
+ logger : Dynamic->Void = null) {
+ super( processor, serverTransport,
+ transportFactory, transportFactory,
+ protocolFactory, protocolFactory,
+ logger);
+ }
+
+
+ public override function Serve() : Void
+ {
+ try
+ {
+ serverTransport.Listen();
+ }
+ catch (ttx : TTransportException)
+ {
+ logDelegate(ttx);
+ return;
+ }
+
+ // Fire the preServe server event when server is up,
+ // but before any client connections
+ if (serverEventHandler != null) {
+ serverEventHandler.preServe();
+ }
+
+ while( ! stop)
+ {
+ var client : TTransport = null;
+ var inputTransport : TTransport = null;
+ var outputTransport : TTransport = null;
+ var inputProtocol : TProtocol = null;
+ var outputProtocol : TProtocol = null;
+ var connectionContext : Dynamic = null;
+ try
+ {
+ client = serverTransport.Accept();
+ if (client != null) {
+ inputTransport = inputTransportFactory.getTransport( client);
+ outputTransport = outputTransportFactory.getTransport( client);
+ inputProtocol = inputProtocolFactory.getProtocol( inputTransport);
+ outputProtocol = outputProtocolFactory.getProtocol( outputTransport);
+
+ // Recover event handler (if any) and fire createContext
+ // server event when a client connects
+ if (serverEventHandler != null) {
+ connectionContext = serverEventHandler.createContext(inputProtocol, outputProtocol);
+ }
+
+ // Process client requests until client disconnects
+ while( true) {
+ // Fire processContext server event
+ // N.B. This is the pattern implemented in C++ and the event fires provisionally.
+ // That is to say it may be many minutes between the event firing and the client request
+ // actually arriving or the client may hang up without ever makeing a request.
+ if (serverEventHandler != null) {
+ serverEventHandler.processContext(connectionContext, inputTransport);
+ }
+
+ //Process client request (blocks until transport is readable)
+ if( ! processor.process( inputProtocol, outputProtocol)) {
+ break;
+ }
+ }
+ }
+ }
+ catch( ttx : TTransportException)
+ {
+ // Usually a client disconnect, expected
+ if(runOnce && ttx.errorID == TTransportException.END_OF_FILE) {
+ //input returns eof, exit
+ //follows lib/cpp/src/thrift/server/TServerFramework.cpp
+ Stop();
+ }
+ }
+ catch( pex : TProtocolException)
+ {
+ logDelegate('$pex ${pex.errorID} ${pex.errorMsg}'); // Unexpected
+ }
+ catch( e : Dynamic)
+ {
+ logDelegate(e); // Unexpected
+ }
+
+ if(client != null && !runOnce)
+ {
+ client.close();
+ }
+
+ // Fire deleteContext server event after client disconnects
+ if (serverEventHandler != null) {
+ serverEventHandler.deleteContext(connectionContext, inputProtocol, outputProtocol);
+ }
+ }
+ }
+
+ public override function Stop() : Void
+ {
+ stop = true;
+ serverTransport.Close();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TBufferedTransport.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TBufferedTransport.hx
new file mode 100644
index 000000000..4b33fcf86
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TBufferedTransport.hx
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.apache.thrift.transport.*;
+
+import haxe.io.Eof;
+import haxe.io.Bytes;
+import haxe.io.BytesBuffer;
+import haxe.io.BytesOutput;
+import haxe.io.BytesInput;
+
+
+class TBufferedTransport extends TTransport
+{
+ // constants
+ public static inline var DEFAULT_BUFSIZE : Int = 0x1000; // 4096 Bytes
+ public static inline var MIN_BUFSIZE : Int = 0x100; // 256 Bytes
+ public static inline var MAX_BUFSIZE : Int = 0x100000; // 1 MB
+
+ // Underlying transport
+ public var transport(default,null) : TTransport = null;
+
+ // Buffer for input/output
+ private var readBuffer_ : BytesInput = null;
+ private var writeBuffer_ : BytesOutput = null;
+ private var bufSize : Int;
+
+ // Constructor wraps around another transport
+ public function new( transport : TTransport, bufSize : Int = DEFAULT_BUFSIZE) {
+
+ // ensure buffer size is in the range
+ if ( bufSize < MIN_BUFSIZE)
+ bufSize = MIN_BUFSIZE;
+ else if( bufSize > MAX_BUFSIZE)
+ bufSize = MAX_BUFSIZE;
+
+ this.transport = transport;
+ this.bufSize = bufSize;
+ this.writeBuffer_ = new BytesOutput();
+ this.writeBuffer_.bigEndian = true;
+ }
+
+ public override function open() : Void {
+ transport.open();
+ }
+
+ public override function isOpen() : Bool {
+ return transport.isOpen();
+ }
+
+ public override function close() : Void {
+ transport.close();
+ }
+
+ public override function read(buf : BytesBuffer, off : Int, len : Int) : Int {
+ try {
+ var data = Bytes.alloc(len);
+
+ while( true) {
+ if ((readBuffer_ != null) && (readBuffer_.position < readBuffer_.length)) {
+ var got = readBuffer_.readBytes(data, 0, len);
+ if (got > 0) {
+ buf.addBytes(data, 0, got);
+ return got;
+ }
+ }
+
+ // there is no point in buffering whenever the
+ // remaining length exceeds the buffer size
+ if ( len >= bufSize) {
+ var got = transport.read( buf, off, len);
+ if (got > 0) {
+ buf.addBytes(data, 0, got);
+ return got;
+ }
+ }
+
+ // fill the buffer
+ if ( readChunk() <= 0)
+ break;
+ }
+
+ throw new TTransportException(TTransportException.END_OF_FILE, 'Can\'t read $len bytes!');
+ }
+ catch (eof : Eof) {
+ throw new TTransportException(TTransportException.END_OF_FILE, 'Can\'t read $len bytes!');
+ }
+ }
+
+ function readChunk() : Int {
+ var size = bufSize;
+ try {
+ var buffer = new BytesBuffer();
+ size = transport.read( buffer, 0, size);
+ readBuffer_ = new BytesInput( buffer.getBytes(), 0, size);
+ readBuffer_.bigEndian = true;
+ return size;
+ }
+ catch(eof : Eof) {
+ throw new TTransportException(TTransportException.END_OF_FILE, 'Can\'t read $size bytes!');
+ }
+ }
+
+ private function writeChunk(forceWrite : Bool) : Void {
+ if( writeBuffer_.length > 0) {
+ if ( forceWrite || (writeBuffer_.length >= bufSize)) {
+ var buf = writeBuffer_.getBytes();
+ writeBuffer_ = new BytesOutput();
+ writeBuffer_.bigEndian = true;
+ transport.write(buf, 0, buf.length);
+ }
+ }
+ }
+
+ public override function write(buf : Bytes, off : Int, len : Int) : Void {
+ var halfSize : Int = Std.int(bufSize / 2);
+
+ // No point in buffering if len exceeds the buffer size.
+ // However, if the buffer is less than half full we should still consider
+ // squashing all into one write, except when the actual write len is very large.
+ var huge_write : Bool = (len >= (2 * bufSize));
+ var exceeds_buf : Bool = huge_write || (len >= bufSize);
+ var write_thru : Bool = exceeds_buf && (writeBuffer_.length >= halfSize);
+ if ( write_thru) {
+ writeChunk(true); // force send whatever we have in there
+ transport.write(buf, off, len); // write thru
+ } else {
+ writeBuffer_.writeBytes(buf, off, len);
+ writeChunk(false);
+ }
+ }
+
+ public override function flush( callback : Dynamic->Void =null) : Void {
+ writeChunk(true);
+ transport.flush(callback);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TBufferedTransportFactory.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TBufferedTransportFactory.hx
new file mode 100644
index 000000000..539e72033
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TBufferedTransportFactory.hx
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.apache.thrift.transport.*;
+
+
+class TBufferedTransportFactory extends TTransportFactory {
+
+ private var bufSize : Int;
+
+ public function new(bufSize : Int = TBufferedTransport.DEFAULT_BUFSIZE) {
+ super();
+ this.bufSize = bufSize;
+ }
+
+ public override function getTransport(base : TTransport) : TTransport {
+ return new TBufferedTransport(base, bufSize);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TFileStream.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TFileStream.hx
new file mode 100644
index 000000000..cd8ad1776
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TFileStream.hx
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import haxe.io.Bytes;
+import haxe.io.BytesBuffer;
+import haxe.io.Input;
+import haxe.io.Output;
+
+
+enum TFileMode {
+ CreateNew;
+ Append;
+ Read;
+}
+
+
+class TFileStream implements TStream {
+
+ public var FileName(default,null) : String;
+
+ private var Input : sys.io.FileInput;
+ private var Output : sys.io.FileOutput;
+
+
+ public function new( fname : String, mode : TFileMode) {
+ FileName = fname;
+ switch ( mode)
+ {
+ case TFileMode.CreateNew:
+ Output = sys.io.File.write( fname, true);
+
+ case TFileMode.Append:
+ Output = sys.io.File.append( fname, true);
+
+ case TFileMode.Read:
+ Input = sys.io.File.read( fname, true);
+
+ default:
+ throw new TTransportException( TTransportException.UNKNOWN,
+ "Unsupported mode");
+ }
+
+ }
+
+ public function Close() : Void {
+ if( Input != null) {
+ Input.close();
+ Input = null;
+ }
+ if( Output != null) {
+ Output.close();
+ Output = null;
+ }
+ }
+
+ public function Peek() : Bool {
+ if( Input == null)
+ throw new TTransportException( TTransportException.NOT_OPEN, "File not open for input");
+
+ return (! Input.eof());
+ }
+
+ public function Read( buf : Bytes, offset : Int, count : Int) : Int {
+ if( Input == null)
+ throw new TTransportException( TTransportException.NOT_OPEN, "File not open for input");
+
+ return Input.readBytes( buf, offset, count);
+ }
+
+ public function Write( buf : Bytes, offset : Int, count : Int) : Void {
+ if( Output == null)
+ throw new TTransportException( TTransportException.NOT_OPEN, "File not open for output");
+
+ Output.writeBytes( buf, offset, count);
+ }
+
+ public function Flush() : Void {
+ if( Output != null)
+ Output.flush();
+ }
+
+}
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TFramedTransport.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TFramedTransport.hx
new file mode 100644
index 000000000..cef82ef61
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TFramedTransport.hx
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.apache.thrift.transport.*;
+
+import haxe.io.Eof;
+import haxe.io.Bytes;
+import haxe.io.BytesBuffer;
+import haxe.io.BytesOutput;
+import haxe.io.BytesInput;
+
+
+/**
+ * TFramedTransport is a buffered TTransport that ensures a fully read message
+ * every time by preceding messages with a 4-byte frame size.
+ */
+class TFramedTransport extends TTransport
+{
+ public static inline var DEFAULT_MAX_LENGTH = 16384000;
+
+ var maxLength_ : Int;
+
+ /**
+ * Underlying transport
+ */
+ var transport_ : TTransport = null;
+
+ /**
+ * Buffer for output
+ */
+ var writeBuffer_ : BytesOutput = new BytesOutput();
+
+ /**
+ * Buffer for input
+ */
+ var readBuffer_ : BytesInput = null;
+
+ /**
+ * Constructor wraps around another transport
+ */
+ public function new( transport : TTransport, maxLength : Int = DEFAULT_MAX_LENGTH) {
+ transport_ = transport;
+ maxLength_ = maxLength;
+ }
+
+ public override function open() : Void {
+ transport_.open();
+ }
+
+ public override function isOpen() : Bool {
+ return transport_.isOpen();
+ }
+
+ public override function close() : Void {
+ transport_.close();
+ }
+
+ public override function read(buf : BytesBuffer, off : Int, len : Int) : Int {
+ try {
+ var data = Bytes.alloc(len);
+
+ if ((readBuffer_ != null) && (readBuffer_.position < readBuffer_.length)) {
+ var got : Int = readBuffer_.readBytes(data, off, len);
+ if (got > 0) {
+ buf.addBytes(data,0,got);
+ return got;
+ };
+ };
+
+ // Read another frame of data
+ readFrame();
+
+ var got = readBuffer_.readBytes(data, off, len);
+ buf.addBytes(data,0,got);
+ return got;
+ }
+ catch (eof : Eof) {
+ throw new TTransportException(TTransportException.END_OF_FILE, 'Can\'t read $len bytes!');
+ }
+ }
+
+
+ function readFrameSize() : Int {
+ try {
+ var buffer = new BytesBuffer();
+ var len = transport_.readAll( buffer, 0, 4);
+ var inp = new BytesInput( buffer.getBytes(), 0, 4);
+ inp.bigEndian = true;
+ return inp.readInt32();
+ }
+ catch(eof : Eof) {
+ throw new TTransportException(TTransportException.END_OF_FILE, 'Can\'t read 4 bytes!');
+ }
+ }
+
+
+ function readFrame() : Void {
+ var size : Int = readFrameSize();
+
+ if (size < 0) {
+ throw new TTransportException(TTransportException.UNKNOWN, 'Read a negative frame size ($size)!');
+ };
+ if (size > maxLength_) {
+ throw new TTransportException(TTransportException.UNKNOWN, 'Frame size ($size) larger than max length ($maxLength_)!');
+ };
+
+ try {
+ var buffer = new BytesBuffer();
+ size = transport_.readAll( buffer, 0, size);
+ readBuffer_ = new BytesInput( buffer.getBytes(), 0, size);
+ readBuffer_.bigEndian = true;
+ }
+ catch(eof : Eof) {
+ throw new TTransportException(TTransportException.END_OF_FILE, 'Can\'t read $size bytes!');
+ }
+ }
+
+ public override function write(buf : Bytes, off : Int, len : Int) : Void {
+ writeBuffer_.writeBytes(buf, off, len);
+ }
+
+ function writeFrameSize(len : Int) : Void {
+ var out = new BytesOutput();
+ out.bigEndian = true;
+ out.writeInt32(len);
+ transport_.write(out.getBytes(), 0, 4);
+ }
+
+ public override function flush( callback : Dynamic->Void =null) : Void {
+ var buf : Bytes = writeBuffer_.getBytes();
+ var len : Int = buf.length;
+ writeBuffer_ = new BytesOutput();
+
+ writeFrameSize(len);
+ transport_.write(buf, 0, len);
+ transport_.flush(callback);
+ }
+}
+
+
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TFramedTransportFactory.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TFramedTransportFactory.hx
new file mode 100644
index 000000000..8d45a641a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TFramedTransportFactory.hx
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.apache.thrift.transport.*;
+
+
+class TFramedTransportFactory extends TTransportFactory {
+
+ var maxLength_ : Int;
+
+ public function new(maxLength : Int = TFramedTransport.DEFAULT_MAX_LENGTH) {
+ super();
+ maxLength_ = maxLength;
+ }
+
+ public override function getTransport(base : TTransport) : TTransport {
+ return new TFramedTransport(base, maxLength_);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TFullDuplexHttpClient.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TFullDuplexHttpClient.hx
new file mode 100644
index 000000000..1972853ef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TFullDuplexHttpClient.hx
@@ -0,0 +1,253 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import flash.errors.EOFError;
+import flash.events.Event;
+import flash.events.IOErrorEvent;
+import flash.events.ProgressEvent;
+import flash.events.SecurityErrorEvent;
+import flash.net.URLLoader;
+import flash.net.URLLoaderDataFormat;
+import flash.net.URLRequest;
+import flash.net.URLRequestMethod;
+import flash.utils.IDataInput;
+import flash.utils.IDataOutput;
+import haxe.io.Bytes;
+import flash.net.Socket;
+import flash.events.EventDispatcher;
+
+
+ /**
+ * HTTP implementation of the TTransport interface. Used for working with a
+ * Thrift web services implementation.
+ * Unlike Http Client, it uses a single POST, and chunk-encoding to transfer all messages.
+ */
+
+ public class TFullDuplexHttpClient extends TTransport
+ {
+ private var socket : Socket = null;
+ private var host : String;
+ private var port : Int;
+ private var resource : String;
+ private var stripped : Bool = false;
+ private var obuffer : Bytes = new Bytes();
+ private var input : IDataInput;
+ private var output : IDataOutput;
+ private var bytesInChunk : Int = 0;
+ private var CRLF : Bytes = new Bytes();
+ private var ioCallback : TException->Void = null;
+ private var eventDispatcher : EventDispatcher = new EventDispatcher();
+
+ public function new(host : String, port : Int, resource : String) : Void
+ {
+ CRLF.writeByte(13);
+ CRLF.writeByte(10);
+ this.host = host;
+ this.port = port;
+ this.resource = resource;
+ }
+
+ public override function close() : Void
+ {
+ this.input = null;
+ this.output = null;
+ this.stripped = false;
+ socket.close()
+ }
+
+ public override function peek() : Bool
+ {
+ if(socket.connected)
+ {
+ trace("Bytes remained:" + socket.bytesAvailable);
+ return socket.bytesAvailable>0;
+ }
+ return false;
+ }
+
+ public override function read(buf : Bytes, off : Int, len : Int) : Int
+ {
+ var n1 : Int = 0, n2 : Int = 0, n3 : Int = 0, n4 : Int = 0, cidx : Int = 2;
+ var chunkSize : Bytes = new Bytes();
+
+ try
+ {
+ while (!stripped)
+ {
+ n1 = n2;
+ n2 = n3;
+ n3 = n4;
+ n4 = input.readByte();
+ if ((n1 == 13) && (n2 == 10) && (n3 == 13) && (n4 == 10))
+ {
+ stripped = true;
+ }
+ }
+
+ // read chunk size
+ if (bytesInChunk == 0)
+ {
+ n1 = input.readByte();
+ n2 = input.readByte();
+
+ chunkSize.writeByte(n1);
+ chunkSize.writeByte(n2);
+
+ while (!((n1 == 13) && (n2 == 10)))
+ {
+ n1 = n2;
+ n2 = input.readByte();
+ chunkSize.writeByte(n2);
+ }
+
+ bytesInChunk = parseInt(chunkSize.toString(), 16);
+ }
+
+ input.readBytes(buf, off, len);
+ debugBuffer(buf);
+ bytesInChunk -= len;
+
+ if (bytesInChunk == 0)
+ {
+ // advance the : "\r\n"
+ input.readUTFBytes(2);
+ }
+ return len;
+ }
+ catch (e : EOFError)
+ {
+ trace(e);
+ throw new TTransportException(TTransportException.UNKNOWN, "No more data available.");
+ }
+ catch (e : TException)
+ {
+ trace('TException $e');
+ throw e;
+ }
+ catch (e : Error)
+ {
+ trace(e);
+ throw new TTransportException(TTransportException.UNKNOWN, 'Bad IO error: $e');
+ }
+ catch (e : Dynamic)
+ {
+ trace(e);
+ throw new TTransportException(TTransportException.UNKNOWN, 'Bad IO error: $e');
+ }
+ return 0;
+ }
+
+ public function debugBuffer(buf : Bytes) : Void
+ {
+ var debug : String = "BUFFER >>";
+ var i : Int;
+ for (i = 0; i < buf.length; i++)
+ {
+ debug += buf[i] as int;
+ debug += " ";
+ }
+
+ trace(debug + "<<");
+ }
+
+ public override function write(buf : Bytes, off : Int, len : Int) : Void
+ {
+ obuffer.writeBytes(buf, off, len);
+ }
+
+ public function addEventListener(type : String, listener : Function, useCapture : Bool = false, priority : Int = 0, useWeakReference : Bool = false) : Void
+ {
+ this.eventDispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
+ }
+
+ public override function open() : Void
+ {
+ this.socket = new Socket();
+ this.socket.addEventListener(Event.CONNECT, socketConnected);
+ this.socket.addEventListener(IOErrorEvent.IO_ERROR, socketError);
+ this.socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, socketSecurityError);
+ this.socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
+ this.socket.connect(host, port);
+ }
+
+ public function socketConnected(event : Event) : Void
+ {
+ this.output = this.socket;
+ this.input = this.socket;
+ this.output.writeUTF( "CONNECT " + resource + " HTTP/1.1\n"
+ + "Host : " + host + ":" + port + "\r\n"
+ + "User-Agent : Thrift/Haxe\r\n"
+ + "Transfer-Encoding : chunked\r\n"
+ + "content-type : application/x-thrift\r\n"
+ + "Accept : */*\r\n"
+ + "\r\n");
+ this.eventDispatcher.dispatchEvent(event);
+ }
+
+ public function socketError(event : IOErrorEvent) : Void
+ {
+ trace("Error Connecting:" + event);
+ this.close();
+ if (ioCallback == null)
+ {
+ return;
+ }
+ ioCallback(new TTransportException(TTransportException.UNKNOWN, "IOError : " + event.text));
+ this.eventDispatcher.dispatchEvent(event);
+ }
+
+ public function socketSecurityError(event : SecurityErrorEvent) : Void
+ {
+ trace("Security Error Connecting:" + event);
+ this.close();
+ this.eventDispatcher.dispatchEvent(event);
+ }
+
+ public function socketDataHandler(event : ProgressEvent) : Void
+ {
+ trace("Got Data call:" +ioCallback);
+ if (ioCallback != null)
+ {
+ ioCallback(null);
+ };
+ this.eventDispatcher.dispatchEvent(event);
+ }
+
+ public override function flush(callback : Error->Void = null) : Void
+ {
+ trace("set callback:" + callback);
+ this.ioCallback = callback;
+ this.output.writeUTF(this.obuffer.length.toString(16));
+ this.output.writeBytes(CRLF);
+ this.output.writeBytes(this.obuffer);
+ this.output.writeBytes(CRLF);
+ this.socket.flush();
+ // waiting for new Flex sdk 3.5
+ //this.obuffer.clear();
+ this.obuffer = new Bytes();
+ }
+
+ public override function isOpen() : Bool
+ {
+ return (this.socket == null ? false : this.socket.connected);
+ }
+
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/THttpClient.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/THttpClient.hx
new file mode 100644
index 000000000..79f86610d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/THttpClient.hx
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+
+import haxe.io.Bytes;
+import haxe.io.BytesBuffer;
+import haxe.io.BytesOutput;
+import haxe.io.BytesInput;
+
+import haxe.Http;
+
+
+
+/**
+* HTTP implementation of the TTransport interface. Used for working with a
+* Thrift web services implementation.
+*/
+
+class THttpClient extends TTransport {
+
+ private var requestBuffer_ : BytesOutput = new BytesOutput();
+ private var responseBuffer_ : BytesInput = null;
+
+ private var request_ : Http = null;
+
+
+ public function new( requestUrl : String) : Void {
+ request_ = new Http(requestUrl);
+ request_.addHeader( "contentType", "application/x-thrift");
+ }
+
+
+ public override function open() : Void {
+ }
+
+ public override function close() : Void {
+ }
+
+ public override function isOpen() : Bool {
+ return true;
+ }
+
+ public override function read(buf:BytesBuffer, off : Int, len : Int) : Int {
+ if (responseBuffer_ == null) {
+ throw new TTransportException(TTransportException.UNKNOWN, "Response buffer is empty, no request.");
+ }
+
+ var data =Bytes.alloc(len);
+ len = responseBuffer_.readBytes(data, off, len);
+ buf.addBytes(data,0,len);
+ return len;
+ }
+
+ public override function write(buf:Bytes, off : Int, len : Int) : Void {
+ requestBuffer_.writeBytes(buf, off, len);
+ }
+
+
+ public override function flush(callback:Dynamic->Void = null) : Void {
+ var buffer = requestBuffer_;
+ requestBuffer_ = new BytesOutput();
+ responseBuffer_ = null;
+
+ request_.onData = function(data : String) {
+ var tmp = new BytesBuffer();
+ tmp.addString(data);
+ responseBuffer_ = new BytesInput(tmp.getBytes());
+ if( callback != null) {
+ callback(null);
+ }
+ };
+
+ request_.onError = function(msg : String) {
+ if( callback != null) {
+ callback(new TTransportException(TTransportException.UNKNOWN, "IOError: " + msg));
+ }
+ };
+
+ request_.setPostData(buffer.getBytes().toString());
+ request_.request(true/*POST*/);
+ }
+
+}
+
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TServerSocket.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TServerSocket.hx
new file mode 100644
index 000000000..4badb2a1f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TServerSocket.hx
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import haxe.remoting.SocketProtocol;
+import haxe.io.Bytes;
+import haxe.io.BytesBuffer;
+import haxe.io.BytesInput;
+import haxe.io.BytesOutput;
+import haxe.io.Input;
+import haxe.io.Output;
+import haxe.io.Eof;
+
+//import flash.net.ServerSocket; - not yet available on Haxe 3.1.3
+#if ! (flash || html5)
+
+import sys.net.Host;
+
+
+class TServerSocket extends TServerTransport {
+
+ // Underlying server with socket
+ private var _socket : Socket= null;
+
+ // Timeout for client sockets from accept
+ private var _clientTimeout : Float = 5;
+
+ // Whether or not to wrap new TSocket connections in buffers
+ private var _useBufferedSockets : Bool = false;
+
+
+ public function new(?address : String = 'localhost', port : Int, clientTimeout : Float = 5, useBufferedSockets : Bool = false)
+ {
+ _clientTimeout = clientTimeout;
+ _useBufferedSockets = useBufferedSockets;
+
+ try
+ {
+ _socket = new Socket();
+ _socket.bind( new Host(address), port);
+ }
+ catch (e : Dynamic)
+ {
+ _socket = null;
+ throw new TTransportException( TTransportException.UNKNOWN, 'Could not create ServerSocket on port $port: $e');
+ }
+ }
+
+
+ public override function Listen() : Void
+ {
+ // Make sure not to block on accept
+ if (_socket != null) {
+ try
+ {
+ #if !php
+ _socket.listen(1);
+ #end
+ }
+ catch (e : Dynamic)
+ {
+ trace('Error $e');
+ throw new TTransportException( TTransportException.UNKNOWN, 'Could not accept on listening socket: $e');
+ }
+ }
+ }
+
+ private override function AcceptImpl() : TTransport
+ {
+ if (_socket == null) {
+ throw new TTransportException( TTransportException.NOT_OPEN, "No underlying server socket.");
+ }
+
+ try
+ {
+ var accepted = _socket.accept();
+ var result = TSocket.fromSocket(accepted);
+ result.setTimeout( _clientTimeout);
+
+ if( _useBufferedSockets)
+ {
+ throw "buffered transport not yet supported"; // TODO
+ //result = new TBufferedTransport(result);
+ }
+
+ return result;
+ }
+ catch (e : Dynamic)
+ {
+ trace('Error $e');
+ throw new TTransportException( TTransportException.UNKNOWN, '$e');
+ }
+ }
+
+ public override function Close() : Void
+ {
+ if (_socket != null)
+ {
+ try
+ {
+ _socket.close();
+ }
+ catch (e : Dynamic)
+ {
+ trace('Error $e');
+ throw new TTransportException( TTransportException.UNKNOWN, 'WARNING: Could not close server socket: $e');
+ }
+ _socket = null;
+ }
+ }
+}
+
+#end
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TServerTransport.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TServerTransport.hx
new file mode 100644
index 000000000..21899819e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TServerTransport.hx
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+class TServerTransport {
+
+ public function Accept() : TTransport {
+ var transport = AcceptImpl();
+ if (transport == null) {
+ throw new TTransportException( TTransportException.UNKNOWN, "accept() may not return NULL");
+ }
+ return transport;
+ }
+
+ public function Listen() : Void {
+ throw new AbstractMethodError();
+ }
+
+ public function Close() : Void {
+ throw new AbstractMethodError();
+ }
+
+ private function AcceptImpl() : TTransport {
+ throw new AbstractMethodError();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TSocket.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TSocket.hx
new file mode 100644
index 000000000..7941ab9fe
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TSocket.hx
@@ -0,0 +1,318 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+#if flash
+import flash.net.Socket;
+#elseif js
+import js.html.WebSocket;
+#else
+import haxe.remoting.SocketProtocol;
+#end
+
+import haxe.io.Bytes;
+import haxe.io.BytesBuffer;
+import haxe.io.BytesInput;
+import haxe.io.BytesOutput;
+import haxe.io.Input;
+import haxe.io.Output;
+import haxe.io.Eof;
+
+
+#if ! (flash || js)
+import sys.net.Host;
+#end
+
+
+ /**
+ * Socket implementation of the TTransport interface. Used for working with a
+ * Thrift Socket Server based implementations.
+ */
+
+class TSocket extends TTransport {
+
+ #if (flash || js)
+ private var host : String;
+ #else
+ private var host : Host;
+ #end
+
+ private var port : Int;
+
+ #if js
+ private var socket : WebSocket = null;
+ #else
+ private var socket : Socket = null;
+ #end
+
+ #if js
+ private var input : Dynamic = null;
+ private var output : WebSocket = null;
+ #elseif flash
+ private var input : Socket = null;
+ private var output : Socket = null;
+ #else
+ private var input : Input = null;
+ private var output : Output = null;
+ #end
+
+ private var timeout : Float = 30;
+
+ private var obuffer : BytesOutput = new BytesOutput();
+ private var ioCallback : TException->Void = null;
+ private var readCount : Int = 0;
+
+ public function new(host : String, port : Int) : Void {
+ #if (flash || js)
+ this.host = host;
+ #else
+ this.host = new Host(host);
+ #end
+
+ this.port = port;
+ }
+
+ #if ! (flash || js)
+ // used by TSocketServer
+ public static function fromSocket( socket : Socket) : TSocket {
+ var socketHost = socket.host();
+ var result = new TSocket(socketHost.host.toString(), socketHost.port);
+ result.assignSocket(socket);
+ return result;
+ }
+ #end
+
+ public override function close() : Void {
+ input = null;
+ output = null;
+ socket.close();
+ }
+
+ public override function peek() : Bool {
+ if( (input == null) || (socket == null)) {
+ return false;
+ } else {
+ #if flash
+ return (input.bytesAvailable > 0);
+ #elseif js
+ return true;
+ #else
+ var ready = Socket.select( [socket], null, null, 0);
+ return (ready.read.length > 0);
+ #end
+ }
+ }
+
+ // Reads up to len bytes into buffer buf, starting att offset off.
+ // May return less bytes than len required
+ public override function read( buf : BytesBuffer, off : Int, len : Int) : Int {
+ try
+ {
+ #if flash
+
+ var remaining = len;
+ while( remaining > 0) {
+ buf.addByte( input.readByte());
+ --remaining;
+ }
+ return len;
+
+ #elseif js
+
+ if( input == null) {
+ throw new TTransportException(TTransportException.UNKNOWN, "Still no data "); // don't block
+ }
+ var nr = len;
+ while( nr < len) {
+ buf.addByte( input.get(off+nr));
+ ++nr;
+ }
+ return len;
+
+ #else
+
+ //socket.waitForRead(); - no, this ignores timeout and blocks infinitely
+ if(readCount < off) {
+ input.read(off-readCount);
+ readCount = off;
+ }
+
+ var data = Bytes.alloc(len);
+ var got = input.readBytes(data, 0, len);
+ buf.addBytes( data, 0, got);
+ readCount += got;
+ return got;
+
+ #end
+ }
+ catch (e : Eof)
+ {
+ trace('Eof $e');
+ throw new TTransportException(TTransportException.END_OF_FILE, "No more data available.");
+ }
+ catch (e : TException)
+ {
+ trace('TException $e');
+ throw e;
+ }
+ catch (e : Dynamic)
+ {
+ trace('Error $e');
+ throw new TTransportException(TTransportException.UNKNOWN, 'Bad IO error : $e');
+ }
+ }
+
+
+ public override function write(buf : Bytes, off : Int, len : Int) : Void
+ {
+ obuffer.writeBytes(buf, off, len);
+ }
+
+
+
+ public override function flush(callback : Dynamic->Void = null) : Void
+ {
+ if( ! isOpen())
+ {
+ throw new TTransportException(TTransportException.NOT_OPEN, "Transport not open");
+ }
+
+ #if flash
+
+ var bytes = new flash.utils.ByteArray();
+ var data = obuffer.getBytes();
+ var len = 0;
+ while( len < data.length) {
+ bytes.writeByte(data.get(len));
+ ++len;
+ }
+
+ #elseif js
+
+ var data = obuffer.getBytes();
+ var outbuf = new js.html.Int8Array(data.length);
+ var len = 0;
+ while( len < data.length) {
+ outbuf.set( [data.get(len)], len);
+ ++len;
+ }
+ var bytes = outbuf.buffer;
+
+ #else
+
+ var bytes = obuffer.getBytes();
+ var len = bytes.length;
+
+ #end
+
+ obuffer = new BytesOutput();
+
+
+ ioCallback = callback;
+ try {
+ readCount = 0;
+
+ #if js
+ output.send( bytes);
+ #else
+ output.writeBytes( bytes, 0, bytes.length);
+ #end
+
+ if(ioCallback != null) {
+ ioCallback(null); // success call
+ }
+ }
+ catch (e : TException)
+ {
+ trace('TException $e, message : ${e.errorMsg}');
+ if(ioCallback != null) {
+ ioCallback(e);
+ }
+ }
+ catch (e : Dynamic) {
+ trace(e);
+ if(ioCallback != null) {
+ ioCallback(new TTransportException(TTransportException.UNKNOWN, 'Bad IO error : $e'));
+ }
+ }
+ }
+
+ public override function isOpen() : Bool
+ {
+ return (socket != null);
+ }
+
+ public override function open() : Void
+ {
+ #if js
+ var socket = new WebSocket();
+ socket.onmessage = function( event : js.html.MessageEvent) {
+ this.input = event.data;
+ }
+
+ #elseif flash
+ var socket = new Socket();
+ socket.connect(host, port);
+
+ #elseif php
+ var socket = new Socket();
+ socket.connect(host, port);
+ socket.setBlocking(true);
+ socket.setTimeout(timeout);
+
+ #else
+ var socket = new Socket();
+ socket.setBlocking(true);
+ socket.setFastSend(true);
+ socket.setTimeout(timeout);
+ socket.connect(host, port);
+
+ #end
+
+ assignSocket( socket);
+ }
+
+ #if js
+ private function assignSocket( socket : WebSocket) : Void
+ #else
+ private function assignSocket( socket : Socket) : Void
+ #end
+ {
+ this.socket = socket;
+
+ #if (flash || js)
+ output = socket;
+ input = socket;
+
+ #else
+ output = socket.output;
+ input = socket.input;
+
+ #end
+ }
+
+ public function setTimeout( timeout : Float ) : Void {
+ if(isOpen()) {
+ socket.setTimeout(timeout);
+ }
+ this.timeout = timeout;
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TStream.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TStream.hx
new file mode 100644
index 000000000..50b3ed3cc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TStream.hx
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import haxe.io.Bytes;
+import haxe.io.BytesBuffer;
+
+
+interface TStream {
+ function Close() : Void;
+ function Peek() : Bool;
+ function Read( buf : Bytes, offset : Int, count : Int) : Int;
+ function Write( buf : Bytes, offset : Int, count : Int) : Void;
+ function Flush() : Void;
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TStreamTransport.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TStreamTransport.hx
new file mode 100644
index 000000000..31a7c141e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TStreamTransport.hx
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.apache.thrift.transport.*;
+import org.apache.thrift.helper.*;
+
+import haxe.io.Bytes;
+import haxe.io.BytesBuffer;
+import haxe.io.BytesOutput;
+import haxe.io.BytesInput;
+
+
+class TStreamTransport extends TTransport {
+
+ public var InputStream(default,null) : TStream;
+ public var OutputStream(default,null) : TStream;
+
+
+ public function new( input : TStream, output : TStream) {
+ this.InputStream = input;
+ this.OutputStream = output;
+ }
+
+ public override function isOpen() : Bool {
+ return true;
+ }
+
+ public override function peek() : Bool {
+ return (InputStream != null);
+ }
+
+ public override function open() : Void {
+ }
+
+ public override function close() : Void {
+ if (InputStream != null)
+ {
+ InputStream.Close();
+ InputStream = null;
+ }
+ if (OutputStream != null)
+ {
+ OutputStream.Close();
+ OutputStream = null;
+ }
+ }
+
+ public override function read( buf : BytesBuffer, off : Int, len : Int) : Int {
+ if (InputStream == null)
+ {
+ throw new TTransportException( TTransportException.NOT_OPEN,
+ "Cannot read from null InputStream");
+ }
+
+ var data : Bytes = Bytes.alloc(len);
+ var size = InputStream.Read( data, off, len);
+ buf.addBytes( data, 0, size);
+ return size;
+ }
+
+ public override function write(buf:Bytes, off : Int, len : Int) : Void {
+ if (OutputStream == null)
+ {
+ throw new TTransportException( TTransportException.NOT_OPEN,
+ "Cannot write to null OutputStream");
+ }
+
+ OutputStream.Write(buf, off, len);
+ }
+
+ public override function flush(callback:Dynamic->Void =null) : Void {
+ if (OutputStream == null)
+ {
+ var err = new TTransportException( TTransportException.NOT_OPEN,
+ "Cannot flush null OutputStream");
+ if(callback != null)
+ callback(err);
+ else
+ throw err;
+ }
+
+ OutputStream.Flush();
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TTransport.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TTransport.hx
new file mode 100644
index 000000000..e6b31791f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TTransport.hx
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import haxe.io.Eof;
+import haxe.io.Bytes;
+import haxe.io.BytesBuffer;
+import org.apache.thrift.AbstractMethodError;
+
+class TTransport {
+
+ /**
+ * Queries whether the transport is open.
+ *
+ * @return True if the transport is open.
+ */
+ public function isOpen() : Bool {
+ throw new AbstractMethodError();
+ }
+
+ /**
+ * Is there more data to be read?
+ *
+ * @return True if the remote side is still alive and feeding us
+ */
+ public function peek() : Bool {
+ return isOpen();
+ }
+
+ /**
+ * Opens the transport for reading/writing.
+ *
+ * @throws TTransportException if the transport could not be opened
+ */
+ public function open() : Void {
+ throw new AbstractMethodError();
+ }
+
+ /**
+ * Closes the transport.
+ */
+ public function close() : Void {
+ throw new AbstractMethodError();
+ };
+
+ /**
+ * Reads up to len bytes into buffer buf, starting att offset off.
+ *
+ * @param buf Array to read into
+ * @param off Index to start reading at
+ * @param len Maximum number of bytes to read
+ * @return The bytes count actually read
+ * @throws TTransportException if there was an error reading data
+ */
+ public function read( buf : BytesBuffer, off : Int, len : Int) : Int {
+ throw new AbstractMethodError();
+ }
+
+ /**
+ * Guarantees that all of len bytes are actually read off the transport.
+ *
+ * @param buf Array to read into
+ * @param off Index to start reading at
+ * @param len Maximum number of bytes to read
+ * @return The number of bytes actually read, which must be equal to len
+ * @throws TTransportException if there was an error reading data
+ */
+ public function readAll(buf : BytesBuffer, off : Int, len : Int) : Int {
+ var got : Int = 0;
+ var ret : Int = 0;
+ while (got < len) {
+ try {
+ ret = read(buf, off+got, len-got);
+ if (ret <= 0) {
+ throw new TTransportException(TTransportException.UNKNOWN,
+ "Cannot read. Remote side has closed. Tried to read "
+ + len + " bytes, but only got " + got + " bytes.");
+ }
+ }
+ catch (eof : Eof) {
+ throw new TTransportException(TTransportException.END_OF_FILE, 'Can\'t read $len bytes!');
+ }
+ got += ret;
+ }
+ return got;
+ }
+
+ /**
+ * Writes the buffer to the output
+ *
+ * @param buf The output data buffer
+ * @throws TTransportException if an error occurs writing data
+ */
+ public function writeAll(buf:Bytes) : Void {
+ write(buf, 0, buf.length);
+ }
+
+ /**
+ * Writes up to len bytes from the buffer.
+ *
+ * @param buf The output data buffer
+ * @param off The offset to start writing from
+ * @param len The number of bytes to write
+ * @throws TTransportException if there was an error writing data
+ */
+ public function write(buf:Bytes, off : Int, len : Int) : Void {
+ throw new AbstractMethodError();
+ }
+
+ /**
+ * Flush any pending data out of a transport buffer.
+ *
+ * @throws TTransportException if there was an error writing out data.
+ */
+ public function flush(callback:Dynamic->Void =null) : Void {
+ if(callback != null)
+ callback(new AbstractMethodError());
+ else
+ throw new AbstractMethodError();
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TTransportException.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TTransportException.hx
new file mode 100644
index 000000000..ad028dd86
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TTransportException.hx
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.apache.thrift.TException;
+
+class TTransportException extends TException {
+
+ // WARNING: These are subject to be extended in the future, so we can't use enums
+ // with Haxe 3.1.3 because of https://github.com/HaxeFoundation/haxe/issues/3649
+ public static inline var UNKNOWN : Int = 0;
+ public static inline var NOT_OPEN : Int = 1;
+ public static inline var ALREADY_OPEN : Int = 2;
+ public static inline var TIMED_OUT : Int = 3;
+ public static inline var END_OF_FILE : Int = 4;
+
+ public function new(error : Int = UNKNOWN, message : String = "") {
+ super(message, error);
+ }
+
+}
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TTransportFactory.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TTransportFactory.hx
new file mode 100644
index 000000000..361f35f10
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TTransportFactory.hx
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+/**
+ * Factory class used to create wrapped instance of Transports.
+ * This is used primarily in servers, which get Transports from
+ * a ServerTransport and then may want to mutate them (i.e. create
+ * a BufferedTransport from the underlying base transport)
+ *
+ */
+class TTransportFactory {
+
+ public function new() {
+ }
+
+ /**
+ * Return a wrapped instance of the base Transport.
+ *
+ * @param trans The base transport
+ * @return Wrapped Transport
+ */
+ public function getTransport( trans : TTransport) : TTransport {
+ return trans;
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TWrappingServerTransport.hx b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TWrappingServerTransport.hx
new file mode 100644
index 000000000..b2272f387
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/src/org/apache/thrift/transport/TWrappingServerTransport.hx
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+
+/*
+ wraps real transport, provided by constructor
+*/
+class TWrappingServerTransport extends TServerTransport {
+
+ private var transport(default,null) : TTransport;
+
+ public function new(transport : TTransport) {
+ this.transport = transport;
+ }
+
+ public override function Listen() : Void
+ {
+ }
+
+ private override function AcceptImpl() : TTransport
+ {
+ return transport;
+ }
+
+ public override function Close() : Void
+ {
+
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/haxe/test/HaxeTests.hxproj b/src/jaegertracing/thrift/lib/haxe/test/HaxeTests.hxproj
new file mode 100644
index 000000000..3beed8244
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/test/HaxeTests.hxproj
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<project version="2">
+ <!-- Output SWF options -->
+ <output>
+ <movie outputType="Application" />
+ <movie input="" />
+ <movie path="bin/HaxeTests" />
+ <movie fps="30" />
+ <movie width="800" />
+ <movie height="600" />
+ <movie version="1" />
+ <movie minorVersion="0" />
+ <movie platform="C++" />
+ <movie background="#FFFFFF" />
+ </output>
+ <!-- Other classes to be compiled into your SWF -->
+ <classpaths>
+ <class path="src" />
+ <class path="gen-haxe" />
+ <class path="../src" />
+ </classpaths>
+ <!-- Build options -->
+ <build>
+ <option directives="" />
+ <option flashStrict="False" />
+ <option noInlineOnDebug="False" />
+ <option mainClass="Main" />
+ <option enabledebug="False" />
+ <option additional="" />
+ </build>
+ <!-- haxelib libraries -->
+ <haxelib>
+ <!-- example: <library name="..." /> -->
+ </haxelib>
+ <!-- Class files to compile (other referenced classes will automatically be included) -->
+ <compileTargets>
+ <!-- example: <compile path="..." /> -->
+ </compileTargets>
+ <!-- Paths to exclude from the Project Explorer tree -->
+ <hiddenPaths>
+ <hidden path="obj" />
+ <hidden path="cpp.hxml" />
+ <hidden path="csharp.hxml" />
+ <hidden path="flash.hxml" />
+ <hidden path="java.hxml" />
+ <hidden path="javascript.hxml" />
+ <hidden path="make_all.bat" />
+ <hidden path="make_all.sh" />
+ <hidden path="Makefile.am" />
+ <hidden path="neko.hxml" />
+ <hidden path="php.hxml" />
+ <hidden path="project.hide" />
+ <hidden path="python.hxml" />
+ </hiddenPaths>
+ <!-- Executed before build -->
+ <preBuildCommand>thrift -r -gen haxe ../../../test/ThriftTest.thrift
+thrift -r -gen haxe ../../../contrib/async-test/aggr.thrift
+thrift -r -gen haxe ../../../lib/rb/benchmark/Benchmark.thrift</preBuildCommand>
+ <!-- Executed after build -->
+ <postBuildCommand alwaysRun="False" />
+ <!-- Other project options -->
+ <options>
+ <option showHiddenPaths="False" />
+ <option testMovie="Custom" />
+ <option testMovieCommand="bin/HaxeTests/Main.exe server multiplex" />
+ </options>
+ <!-- Plugin storage -->
+ <storage />
+</project> \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/test/Makefile.am b/src/jaegertracing/thrift/lib/haxe/test/Makefile.am
new file mode 100644
index 000000000..2b8b24524
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/test/Makefile.am
@@ -0,0 +1,85 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+THRIFTCMD = $(THRIFT) --gen haxe -r
+THRIFTTEST = $(top_srcdir)/test/ThriftTest.thrift
+AGGR = $(top_srcdir)/contrib/async-test/aggr.thrift
+BENCHMARK = $(top_srcdir)/lib/rb/benchmark/Benchmark.thrift
+
+BIN_CPP = bin/Main-debug
+BIN_PHP = bin/php/Main-debug.php
+
+gen-haxe/thrift/test/ThriftTest.hx: $(THRIFTTEST)
+ $(THRIFTCMD) $(THRIFTTEST)
+
+gen-haxe/thrift/test/Aggr.hx: $(AGGR)
+ $(THRIFTCMD) $(AGGR)
+
+gen-haxe/thrift/test/BenchmarkService.hx: $(BENCHMARK)
+ $(THRIFTCMD) $(BENCHMARK)
+
+all-local: $(BIN_CPP) $(BIN_PHP)
+
+$(BIN_CPP): \
+ src/*.hx \
+ ../src/org/apache/thrift/**/*.hx \
+ gen-haxe/thrift/test/ThriftTest.hx \
+ gen-haxe/thrift/test/Aggr.hx \
+ gen-haxe/thrift/test/BenchmarkService.hx
+ $(HAXE) --cwd . cpp.hxml
+
+$(BIN_PHP): \
+ src/*.hx \
+ ../src/org/apache/thrift/**/*.hx \
+ gen-haxe/thrift/test/ThriftTest.hx \
+ gen-haxe/thrift/test/Aggr.hx \
+ gen-haxe/thrift/test/BenchmarkService.hx
+ $(HAXE) --cwd . php.hxml
+
+
+#TODO: other haxe targets
+# $(HAXE) --cwd . csharp
+# $(HAXE) --cwd . flash
+# $(HAXE) --cwd . java
+# $(HAXE) --cwd . javascript
+# $(HAXE) --cwd . neko
+# $(HAXE) --cwd . python # needs Haxe 3.2.0
+
+
+clean-local:
+ $(RM) -r gen-haxe bin
+
+check: $(BIN_CPP) $(BIN_PHP)
+ $(BIN_CPP)
+ php -f $(BIN_PHP)
+
+EXTRA_DIST = \
+ src \
+ cpp.hxml \
+ csharp.hxml \
+ flash.hxml \
+ java.hxml \
+ javascript.hxml \
+ neko.hxml \
+ php.hxml \
+ python.hxml \
+ project.hide \
+ HaxeTests.hxproj \
+ make_all.bat \
+ make_all.sh
diff --git a/src/jaegertracing/thrift/lib/haxe/test/cpp.hxml b/src/jaegertracing/thrift/lib/haxe/test/cpp.hxml
new file mode 100644
index 000000000..73848a8bc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/test/cpp.hxml
@@ -0,0 +1,41 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#integrate files to classpath
+-cp src
+-cp ../src
+-cp gen-haxe
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#CPP target
+-cpp bin
+
+#To produce 64 bit binaries the file should define the HXCPP_M64 compile variable:
+#-D HXCPP_M64
+
+#Add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/test/csharp.hxml b/src/jaegertracing/thrift/lib/haxe/test/csharp.hxml
new file mode 100644
index 000000000..4c34b0d94
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/test/csharp.hxml
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#integrate files to classpath
+-cp src
+-cp ../src
+-cp gen-haxe
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#CSHARP target
+-cs bin/Test.exe
+
+#Add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/test/flash.hxml b/src/jaegertracing/thrift/lib/haxe/test/flash.hxml
new file mode 100644
index 000000000..8b1763190
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/test/flash.hxml
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#integrate files to classpath
+-cp src
+-cp ../src
+-cp gen-haxe
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#Flash target
+-swf bin/Test.swf
+
+#Add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/test/java.hxml b/src/jaegertracing/thrift/lib/haxe/test/java.hxml
new file mode 100644
index 000000000..c9471597c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/test/java.hxml
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#integrate files to classpath
+-cp src
+-cp ../src
+-cp gen-haxe
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#Java target
+-java bin/Test.jar
+
+#Add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/test/javascript.hxml b/src/jaegertracing/thrift/lib/haxe/test/javascript.hxml
new file mode 100644
index 000000000..18d9964c2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/test/javascript.hxml
@@ -0,0 +1,44 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#integrate files to classpath
+-cp src
+-cp ../src
+-cp gen-haxe
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#JavaScript target
+-js bin/Test.js
+
+#You can use -D source-map-content (requires Haxe 3.1+) to have the .hx
+#files directly embedded into the map file, this way you only have to
+#upload it, and it will be always in sync with the compiled .js even if
+#you modify your .hx files.
+-D source-map-content
+
+#Generate source map and add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/test/make_all.bat b/src/jaegertracing/thrift/lib/haxe/test/make_all.bat
new file mode 100644
index 000000000..0314e18a3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/test/make_all.bat
@@ -0,0 +1,70 @@
+@echo off
+rem /*
+rem * Licensed to the Apache Software Foundation (ASF) under one
+rem * or more contributor license agreements. See the NOTICE file
+rem * distributed with this work for additional information
+rem * regarding copyright ownership. The ASF licenses this file
+rem * to you under the Apache License, Version 2.0 (the
+rem * "License"); you may not use this file except in compliance
+rem * with the License. You may obtain a copy of the License at
+rem *
+rem * http://www.apache.org/licenses/LICENSE-2.0
+rem *
+rem * Unless required by applicable law or agreed to in writing,
+rem * software distributed under the License is distributed on an
+rem * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem * KIND, either express or implied. See the License for the
+rem * specific language governing permissions and limitations
+rem * under the License.
+rem */
+
+setlocal
+if "%HOMEDRIVE%"=="" goto MISSINGVARS
+if "%HOMEPATH%"=="" goto MISSINGVARS
+if "%HAXEPATH%"=="" goto NOTINSTALLED
+
+set path=%HAXEPATH%;%HAXEPATH%\..\neko;%path%
+
+rem # invoke Thrift comnpiler
+thrift -r -gen haxe ..\..\..\test\ThriftTest.thrift
+thrift -r -gen haxe ..\..\..\contrib\async-test\aggr.thrift
+thrift -r -gen haxe ..\..\..\lib\rb\benchmark\Benchmark.thrift
+if errorlevel 1 goto STOP
+
+rem # invoke Haxe compiler for all targets
+for %%a in (*.hxml) do (
+ rem * filter Python, as it is not supported by Haxe 3.1.3 (but will be in 3.1.4)
+ if not "%%a"=="python.hxml" (
+ echo --------------------------
+ echo Building %%a ...
+ echo --------------------------
+ haxe --cwd . %%a
+ )
+)
+
+
+echo.
+echo done.
+pause
+goto eof
+
+:NOTINSTALLED
+echo FATAL: Either Haxe is not installed, or the HAXEPATH variable is not set.
+pause
+goto eof
+
+:MISSINGVARS
+echo FATAL: Unable to locate home folder.
+echo.
+echo Both HOMEDRIVE and HOMEPATH need to be set to point to your Home folder.
+echo The current values are:
+echo HOMEDRIVE=%HOMEDRIVE%
+echo HOMEPATH=%HOMEPATH%
+pause
+goto eof
+
+:STOP
+pause
+goto eof
+
+:eof
diff --git a/src/jaegertracing/thrift/lib/haxe/test/make_all.sh b/src/jaegertracing/thrift/lib/haxe/test/make_all.sh
new file mode 100644
index 000000000..512f5ec03
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/test/make_all.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# invoke Thrift comnpiler
+thrift -r -gen haxe ../../../test/ThriftTest.thrift
+thrift -r -gen haxe ../../../contrib/async-test/aggr.thrift
+thrift -r -gen haxe ../../../lib/rb/benchmark/Benchmark.thrift
+
+# output folder
+if [ ! -d bin ]; then
+ mkdir bin
+fi
+
+# invoke Haxe compiler
+for target in *.hxml; do
+ echo --------------------------
+ echo Building ${target} ...
+ echo --------------------------
+ if [ ! -d bin/${target} ]; then
+ mkdir bin/${target}
+ fi
+ haxe --cwd . ${target}
+done
+
+
+#eof
diff --git a/src/jaegertracing/thrift/lib/haxe/test/neko.hxml b/src/jaegertracing/thrift/lib/haxe/test/neko.hxml
new file mode 100644
index 000000000..2db70c8e6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/test/neko.hxml
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#integrate files to classpath
+-cp src
+-cp ../src
+-cp gen-haxe
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#neko target
+-neko bin/Test.n
+
+#Add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/test/php.hxml b/src/jaegertracing/thrift/lib/haxe/test/php.hxml
new file mode 100644
index 000000000..14f2b2d01
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/test/php.hxml
@@ -0,0 +1,39 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#integrate files to classpath
+-cp src
+-cp ../src
+-cp gen-haxe
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#PHP target
+-php bin/php/
+--php-front Main-debug.php
+
+#Add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full
diff --git a/src/jaegertracing/thrift/lib/haxe/test/project.hide b/src/jaegertracing/thrift/lib/haxe/test/project.hide
new file mode 100644
index 000000000..16ef98e98
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/test/project.hide
@@ -0,0 +1,67 @@
+{
+ "type" : 0
+ ,"target" : 4
+ ,"name" : "Test"
+ ,"main" : null
+ ,"projectPackage" : ""
+ ,"company" : ""
+ ,"license" : ""
+ ,"url" : ""
+ ,"targetData" : [
+ {
+ "pathToHxml" : "flash.hxml"
+ ,"runActionType" : 1
+ ,"runActionText" : "bin/Test.swf"
+ }
+ ,{
+ "pathToHxml" : "javascript.hxml"
+ ,"runActionType" : 1
+ ,"runActionText" : "bin\\index.html"
+ }
+ ,{
+ "pathToHxml" : "neko.hxml"
+ ,"runActionType" : 2
+ ,"runActionText" : "neko bin/Test.n"
+ }
+ ,{
+ "pathToHxml" : "php.hxml"
+ }
+ ,{
+ "pathToHxml" : "cpp.hxml"
+ ,"runActionType" : 2
+ ,"runActionText" : "bin/Main-debug.exe"
+ }
+ ,{
+ "pathToHxml" : "java.hxml"
+ }
+ ,{
+ "pathToHxml" : "csharp.hxml"
+ }
+ ,{
+ "pathToHxml" : "python.hxml"
+ ,"runActionType" : 2
+ ,"runActionText" : "python bin/Test.py"
+ }
+ ]
+ ,"files" : [
+ {
+ "path" : "src\\Main.hx"
+ ,"useTabs" : true
+ ,"indentSize" : 4
+ ,"foldedRegions" : [
+
+ ]
+ ,"activeLine" : 13
+ }
+ ]
+ ,"activeFile" : "src\\Main.hx"
+ ,"openFLTarget" : null
+ ,"openFLBuildMode" : "Debug"
+ ,"runActionType" : null
+ ,"runActionText" : null
+ ,"buildActionCommand" : null
+ ,"hiddenItems" : [
+
+ ]
+ ,"showHiddenItems" : false
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/test/python.hxml b/src/jaegertracing/thrift/lib/haxe/test/python.hxml
new file mode 100644
index 000000000..4d6a133d6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/test/python.hxml
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#integrate files to classpath
+-cp src
+-cp ../src
+-cp gen-haxe
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#Python target
+-python bin/Test.py
+
+#Add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/test/src/Main.hx b/src/jaegertracing/thrift/lib/haxe/test/src/Main.hx
new file mode 100644
index 000000000..6c262d78f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/test/src/Main.hx
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package;
+
+import org.apache.thrift.*;
+import org.apache.thrift.protocol.*;
+import org.apache.thrift.transport.*;
+import org.apache.thrift.server.*;
+import org.apache.thrift.meta_data.*;
+
+import thrift.test.*; // generated code
+
+
+enum WhatTests {
+ Normal;
+ Multiplex;
+}
+
+class Main
+{
+ static private var tests : WhatTests = Normal;
+ static private var server : Bool = false;
+
+ static private inline var CMDLINEHELP : String
+ = "\nHaxeTests [client|server] [multiplex]\n"
+ + " client|server ... determines run mode for some tests, default is client\n"
+ + " multiplex ........ run multiplex test server or client\n";
+
+ static private function ParseArgs() {
+ #if sys
+
+ var args = Sys.args();
+ if ( args != null) {
+ for ( arg in args) {
+ switch(arg.toLowerCase()) {
+ case "client":
+ server = false;
+ case "server" :
+ server = true;
+ case "multiplex" :
+ tests = Multiplex;
+ default:
+ throw 'Invalid argument "$arg"\n'+CMDLINEHELP;
+ }
+ }
+ }
+
+ #end
+ }
+
+ static public function main()
+ {
+ try
+ {
+ ParseArgs();
+
+ switch( tests) {
+ case Normal:
+ StreamTest.Run(server);
+ case Multiplex:
+ MultiplexTest.Run(server);
+ default:
+ throw "Unhandled test mode $tests";
+ }
+
+ trace("All tests completed.");
+ }
+ catch( e: Dynamic)
+ {
+ trace('$e');
+ #if sys
+ Sys.exit(1); // indicate error
+ #end
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/haxe/test/src/MultiplexTest.hx b/src/jaegertracing/thrift/lib/haxe/test/src/MultiplexTest.hx
new file mode 100644
index 000000000..3818b6609
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/test/src/MultiplexTest.hx
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package;
+
+import haxe.Int64;
+import haxe.Int32;
+
+import org.apache.thrift.*;
+import org.apache.thrift.protocol.*;
+import org.apache.thrift.transport.*;
+import org.apache.thrift.server.*;
+import org.apache.thrift.meta_data.*;
+
+// debug only
+import org.apache.thrift.protocol.TProtocolDecorator;
+import org.apache.thrift.protocol.TMultiplexedProtocol;
+import org.apache.thrift.protocol.TMultiplexedProcessor;
+
+// generated code imports
+import Aggr;
+import AggrImpl;
+import AggrProcessor;
+import BenchmarkService;
+import BenchmarkServiceImpl;
+import BenchmarkServiceProcessor;
+import Error;
+
+
+class BenchmarkServiceHandler implements BenchmarkService
+{
+ public function new() {
+ }
+
+ public function fibonacci(n : haxe.Int32) : haxe.Int32 {
+ trace('Benchmark.fibonacci($n)');
+ var next : Int;
+ var prev = 0;
+ var result = 1;
+ while( n > 0)
+ {
+ next = result + prev;
+ prev = result;
+ result = next;
+ --n;
+ }
+ return result;
+ }
+}
+
+
+class AggrServiceHandler implements Aggr
+{
+ private var values : List<haxe.Int32> = new List<haxe.Int32>();
+
+ public function new() {
+ }
+
+ public function addValue(value : haxe.Int32) : Void {
+ trace('Aggr.addValue($value)');
+ values.add( value);
+ }
+
+ public function getValues() : List< haxe.Int32> {
+ trace('Aggr.getValues()');
+ return values;
+ }
+}
+
+
+
+class MultiplexTest extends TestBase {
+
+ private inline static var NAME_BENCHMARKSERVICE : String = "BenchmarkService";
+ private inline static var NAME_AGGR : String = "Aggr";
+
+
+ public static override function Run(server : Bool) : Void {
+ if ( server) {
+ RunMultiplexServer();
+ } else {
+ RunMultiplexClient();
+ RunDefaultClient();
+ }
+ }
+
+
+ // run the multiplex server
+ public static override function RunMultiplexServer() : Void {
+ try
+ {
+ var benchHandler : BenchmarkService = new BenchmarkServiceHandler();
+ var benchProcessor : TProcessor = new BenchmarkServiceProcessor( benchHandler);
+
+ var aggrHandler : Aggr = new AggrServiceHandler();
+ var aggrProcessor : TProcessor = new AggrProcessor( aggrHandler);
+
+ var multiplex : TMultiplexedProcessor = new TMultiplexedProcessor();
+ multiplex.RegisterProcessor( NAME_BENCHMARKSERVICE, benchProcessor, true); // default
+ multiplex.RegisterProcessor( NAME_AGGR, aggrProcessor);
+
+ // protocol+transport stack
+ var protfact : TProtocolFactory = new TBinaryProtocolFactory(true,true);
+ var servertrans : TServerTransport = new TServerSocket( 9090, 5, false);
+ var transfact : TTransportFactory = new TFramedTransportFactory();
+
+ var server : TServer = new TSimpleServer( multiplex, servertrans, transfact, protfact);
+
+ trace("Starting the server ...");
+ server.Serve();
+ }
+ catch( e : TApplicationException)
+ {
+ TestBase.Expect(false,'${e.errorID} ${e.errorMsg}');
+ }
+ catch( e : TException)
+ {
+ TestBase.Expect(false,'$e');
+ }
+ }
+
+
+ // run multiplex client against multiplex server
+ public static override function RunMultiplexClient() : Void {
+ try
+ {
+ var trans : TTransport;
+ trans = new TSocket("localhost", 9090);
+ trans = new TFramedTransport(trans);
+ trans.open();
+
+ var protocol : TProtocol = new TBinaryProtocol(trans,true,true);
+ var multiplex : TMultiplexedProtocol;
+
+ multiplex = new TMultiplexedProtocol( protocol, NAME_BENCHMARKSERVICE);
+ var bench = new BenchmarkServiceImpl( multiplex);
+
+ multiplex = new TMultiplexedProtocol( protocol, NAME_AGGR);
+ var aggr = new AggrImpl( multiplex);
+
+ trace('calling aggr.add( bench.fibo())...');
+ for( i in 1 ... 10)
+ {
+ trace('$i');
+ aggr.addValue( bench.fibonacci(i));
+ }
+
+ trace('calling aggr ...');
+ var i = 1;
+ var values = aggr.getValues();
+ TestBase.Expect(values != null,'aggr.getValues() == null');
+ for( k in values)
+ {
+ trace('fib($i) = $k');
+ ++i;
+ }
+
+ trans.close();
+ trace('done.');
+
+ }
+ catch( e : TApplicationException)
+ {
+ TestBase.Expect(false,'${e.errorID} ${e.errorMsg}');
+ }
+ catch( e : TException)
+ {
+ TestBase.Expect(false,'$e');
+ }
+ }
+
+
+ // run non-multiplex client against multiplex server to test default fallback
+ public static override function RunDefaultClient() : Void {
+ try
+ {
+ var trans : TTransport;
+ trans = new TSocket("localhost", 9090);
+ trans = new TFramedTransport(trans);
+ trans.open();
+
+ var protocol : TProtocol = new TBinaryProtocol(trans,true,true);
+
+ var bench = new BenchmarkServiceImpl( protocol);
+
+ trace('calling bench (via default) ...');
+ for( i in 1 ... 10)
+ {
+ var k = bench.fibonacci(i);
+ trace('fib($i) = $k');
+ }
+
+ trans.close();
+ trace('done.');
+ }
+ catch( e : TApplicationException)
+ {
+ TestBase.Expect(false,'${e.errorID} ${e.errorMsg}');
+ }
+ catch( e : TException)
+ {
+ TestBase.Expect(false,'$e');
+ }
+ }
+
+}
+
+
diff --git a/src/jaegertracing/thrift/lib/haxe/test/src/StreamTest.hx b/src/jaegertracing/thrift/lib/haxe/test/src/StreamTest.hx
new file mode 100644
index 000000000..244f1ea9d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/test/src/StreamTest.hx
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package;
+
+import haxe.Int64;
+import sys.FileSystem;
+
+import org.apache.thrift.*;
+import org.apache.thrift.protocol.*;
+import org.apache.thrift.transport.*;
+import org.apache.thrift.server.*;
+import org.apache.thrift.meta_data.*;
+
+import thrift.test.*; // generated code
+
+
+class StreamTest extends TestBase {
+
+
+ private inline static var tmpfile : String = "data.tmp";
+
+
+ private static function MakeTestData() : Xtruct {
+ var data : Xtruct = new Xtruct();
+ data.string_thing = "Streamtest";
+ data.byte_thing = -128;
+ data.i32_thing = 4711;
+ data.i64_thing = Int64.make(0x12345678,0x9ABCDEF0);
+ return data;
+ }
+
+ public static function WriteData() : Xtruct
+ {
+ var stream : TStream = new TFileStream( tmpfile, CreateNew);
+ var trans : TTransport = new TStreamTransport( null, stream);
+ var prot = new TJSONProtocol( trans);
+
+ var data = MakeTestData();
+ data.write(prot);
+ trans.close();
+
+ return data;
+ }
+
+ public static function ReadData() : Xtruct
+ {
+ var stream : TStream = new TFileStream( tmpfile, Read);
+ var trans : TTransport = new TStreamTransport( stream, null);
+ var prot = new TJSONProtocol( trans);
+
+ var data : Xtruct = new Xtruct();
+ data.read(prot);
+ trans.close();
+
+ return data;
+ }
+
+ public static override function Run(server : Bool) : Void
+ {
+ try {
+ var written = WriteData();
+ var read = ReadData();
+ FileSystem.deleteFile(tmpfile);
+
+ TestBase.Expect( read.string_thing == written.string_thing, "string data");
+ TestBase.Expect( read.byte_thing == written.byte_thing, "byte data");
+ TestBase.Expect( read.i32_thing == written.i32_thing, "i32 data");
+ TestBase.Expect( Int64.compare( read.i64_thing, written.i64_thing) == 0, "i64 data");
+
+ } catch(e:Dynamic) {
+ FileSystem.deleteFile(tmpfile);
+ throw e;
+ }
+ }
+
+}
+
+
diff --git a/src/jaegertracing/thrift/lib/haxe/test/src/TestBase.hx b/src/jaegertracing/thrift/lib/haxe/test/src/TestBase.hx
new file mode 100644
index 000000000..12327737a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/haxe/test/src/TestBase.hx
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package;
+
+import org.apache.thrift.*;
+import org.apache.thrift.protocol.*;
+import org.apache.thrift.transport.*;
+import org.apache.thrift.server.*;
+import org.apache.thrift.meta_data.*;
+
+
+class TestBase {
+
+ private function new() {
+ // override, if necessary
+ }
+
+ public static function Run(server : Bool) : Void {
+ throw new AbstractMethodError();
+ }
+
+ public static function Expect( expr : Bool, info : String, ?pos : haxe.PosInfos) : Void {
+ if( ! expr) {
+ throw ('Test "$info" failed at '+pos.methodName+' in '+pos.fileName+':'+pos.lineNumber);
+ }
+ }
+
+}
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/hs/CMakeLists.txt b/src/jaegertracing/thrift/lib/hs/CMakeLists.txt
new file mode 100644
index 000000000..1a5b8fd32
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/CMakeLists.txt
@@ -0,0 +1,93 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Rebuild when any of these files changes
+set(haskell_sources
+ src/Thrift.hs
+ src/Thrift/Arbitraries.hs
+ src/Thrift/Protocol.hs
+ src/Thrift/Protocol/Binary.hs
+ src/Thrift/Protocol/Compact.hs
+ src/Thrift/Protocol/JSON.hs
+ src/Thrift/Server.hs
+ src/Thrift/Transport.hs
+ src/Thrift/Transport/Empty.hs
+ src/Thrift/Transport/Framed.hs
+ src/Thrift/Transport/Handle.hs
+ src/Thrift/Transport/HttpClient.hs
+ src/Thrift/Transport/IOBuffer.hs
+ src/Thrift/Types.hs
+ thrift.cabal
+)
+
+if(BUILD_TESTING)
+ list(APPEND haskell_soruces
+ test/Spec.hs
+ test/BinarySpec.hs
+ test/CompactSpec.hs
+ test/JSONSpec.hs
+ )
+ set(hs_enable_test "--enable-tests")
+endif()
+
+set(haskell_artifacts thrift_cabal.stamp)
+# Adding *.hi files so that any missing file triggers the build
+foreach(SRC ${haskell_sources})
+ get_filename_component(EX ${SRC} EXT)
+ if(${EX} STREQUAL ".hs")
+ file(RELATIVE_PATH REL ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/${SRC})
+ get_filename_component(DIR ${REL} DIRECTORY)
+ get_filename_component(BASE ${REL} NAME_WE)
+ list(APPEND haskell_artifacts dist/build/${DIR}/${BASE}.hi)
+ endif()
+endforeach()
+
+if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ set(hs_optimize -O0)
+else()
+ set(hs_optimize -O1)
+endif()
+
+add_custom_command(
+ OUTPUT ${haskell_artifacts}
+ COMMAND ${CABAL} update
+ # Build dependencies first without --builddir, otherwise it fails.
+ COMMAND ${CABAL} install --only-dependencies ${hs_enable_test}
+ COMMAND ${CABAL} configure ${hs_optimize} ${hs_enable_test} --builddir=${CMAKE_CURRENT_BINARY_DIR}/dist
+ COMMAND ${CABAL} build --builddir=${CMAKE_CURRENT_BINARY_DIR}/dist
+ COMMAND ${CABAL} install --builddir=${CMAKE_CURRENT_BINARY_DIR}/dist
+ COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/thrift_cabal.stamp
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ DEPENDS ${haskell_sources}
+ COMMENT "Building Haskell library")
+
+add_custom_target(haskell_library ALL
+ DEPENDS ${haskell_artifacts})
+
+if(BUILD_TESTING)
+ add_test(NAME HaskellCabalCheck
+ COMMAND ${CABAL} check
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+ add_test(NAME HaskellCabalTest
+ # Cabal fails to find built executable when --builddir is specified.
+ # So we invoke the executable directly.
+ # COMMAND ${CABAL} test --builddir=${CMAKE_CURRENT_BINARY_DIR}/dist
+ # WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+ COMMAND dist/build/spec/spec)
+endif()
diff --git a/src/jaegertracing/thrift/lib/hs/LICENSE b/src/jaegertracing/thrift/lib/hs/LICENSE
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/src/jaegertracing/thrift/lib/hs/Makefile.am b/src/jaegertracing/thrift/lib/hs/Makefile.am
new file mode 100644
index 000000000..ba156a130
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/Makefile.am
@@ -0,0 +1,53 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+EXTRA_DIST = \
+ coding_standards.md \
+ CMakeLists.txt \
+ LICENSE \
+ README.md \
+ Setup.lhs \
+ TODO \
+ thrift.cabal \
+ src \
+ test
+
+all-local:
+ $(CABAL) update
+ $(CABAL) install
+
+install-exec-hook:
+ $(CABAL) install
+
+# Make sure this doesn't fail if Haskell is not configured.
+clean-local:
+ $(CABAL) clean
+
+dist-local:
+ $(CABAL) sdist
+
+maintainer-clean-local:
+ $(CABAL) clean
+
+check-local:
+ $(CABAL) check
+ $(CABAL) install --only-dependencies --enable-tests
+ $(CABAL) configure --enable-tests
+ $(CABAL) build
+ $(CABAL) test
diff --git a/src/jaegertracing/thrift/lib/hs/README.md b/src/jaegertracing/thrift/lib/hs/README.md
new file mode 100644
index 000000000..10bdeff1e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/README.md
@@ -0,0 +1,113 @@
+Haskell Thrift Bindings
+
+License
+=======
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+Compile
+=======
+
+Use Cabal to compile and install; ./configure uses Cabal underneath, and that
+path is not yet well tested. Thrift's library and generated code should compile
+with pretty much any GHC extensions or warnings you enable (or disable).
+Please report this not being the case as a bug on
+https://issues.apache.org/jira/secure/CreateIssue!default.jspa
+
+Chances you'll need to muck a bit with Cabal flags to install Thrift:
+
+CABAL_CONFIGURE_FLAGS="--user" ./configure
+
+Base Types
+==========
+
+The mapping from Thrift types to Haskell's is:
+
+ * double -> Double
+ * byte -> Data.Int.Int8
+ * i16 -> Data.Int.Int16
+ * i32 -> Data.Int.Int32
+ * i64 -> Data.Int.Int64
+ * string -> Text
+ * binary -> Data.ByteString.Lazy
+ * bool -> Boolean
+
+Enums
+=====
+
+Become Haskell 'data' types. Use fromEnum to get out the int value.
+
+Lists
+=====
+
+Become Data.Vector.Vector from the vector package.
+
+Maps and Sets
+=============
+
+Become Data.HashMap.Strict.Map and Data.HashSet.Set from the
+unordered-containers package.
+
+Structs
+=======
+
+Become records. Field labels are ugly, of the form f_STRUCTNAME_FIELDNAME. All
+fields are Maybe types.
+
+Exceptions
+==========
+
+Identical to structs. Use them with throw and catch from Control.Exception.
+
+Client
+======
+
+Just a bunch of functions. You may have to import a bunch of client files to
+deal with inheritance.
+
+Interface
+=========
+
+You should only have to import the last one in the chain of inheritors. To make
+an interface, declare a label:
+
+ data MyIface = MyIface
+
+and then declare it an instance of each iface class, starting with the superest
+class and proceeding down (all the while defining the methods). Then pass your
+label to process as the handler.
+
+Processor
+=========
+
+Just a function that takes a handler label, protocols. It calls the
+superclasses process if there is a superclass.
+
+Releasing to Hackage
+====================
+
+Using the [Docker Container for Ubuntu Bionic](../../build/docker/README.md), run:
+
+ root@e941f5311545:/thrift/src# ./bootstrap.sh && ./configure
+ root@e941f5311545:/thrift/src# cd lib/hs && make dist-local
+
+This will produce a `lib/hs/dist/thrift-<version>.tar.gz` file. Take this
+file and upload it as a Haskell Hackage
+[package candidate](https://hackage.haskell.org/upload#candidates) and
+check to make sure all the information is correct. Assuming all is satisfactory,
+you can upload the package as official using the link at the top of the page.
diff --git a/src/jaegertracing/thrift/lib/hs/Setup.lhs b/src/jaegertracing/thrift/lib/hs/Setup.lhs
new file mode 100755
index 000000000..d52ae9455
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/Setup.lhs
@@ -0,0 +1,21 @@
+#!/usr/bin/env runhaskell
+
+> -- Licensed to the Apache Software Foundation (ASF) under one
+> -- or more contributor license agreements. See the NOTICE file
+> -- distributed with this work for additional information
+> -- regarding copyright ownership. The ASF licenses this file
+> -- to you under the Apache License, Version 2.0 (the
+> -- "License"); you may not use this file except in compliance
+> -- with the License. You may obtain a copy of the License at
+> --
+> -- http://www.apache.org/licenses/LICENSE-2.0
+> --
+> -- Unless required by applicable law or agreed to in writing,
+> -- software distributed under the License is distributed on an
+> -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+> -- KIND, either express or implied. See the License for the
+> -- specific language governing permissions and limitations
+> -- under the License.
+
+> import Distribution.Simple
+> main = defaultMain
diff --git a/src/jaegertracing/thrift/lib/hs/TODO b/src/jaegertracing/thrift/lib/hs/TODO
new file mode 100644
index 000000000..136817321
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/TODO
@@ -0,0 +1,2 @@
+The library could stand to be built up more.
+Many modules need export lists.
diff --git a/src/jaegertracing/thrift/lib/hs/coding_standards.md b/src/jaegertracing/thrift/lib/hs/coding_standards.md
new file mode 100644
index 000000000..fa0390bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/coding_standards.md
@@ -0,0 +1 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
diff --git a/src/jaegertracing/thrift/lib/hs/src/Thrift.hs b/src/jaegertracing/thrift/lib/hs/src/Thrift.hs
new file mode 100644
index 000000000..658020991
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/src/Thrift.hs
@@ -0,0 +1,114 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes #-}
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+module Thrift
+ ( module Thrift.Transport
+ , module Thrift.Protocol
+ , AppExnType(..)
+ , AppExn(..)
+ , readAppExn
+ , writeAppExn
+ , ThriftException(..)
+ ) where
+
+import Control.Exception
+
+import Data.Int
+import Data.Text.Lazy ( Text, pack, unpack )
+import Data.Text.Lazy.Encoding
+import Data.Typeable ( Typeable )
+import qualified Data.HashMap.Strict as Map
+
+import Thrift.Protocol
+import Thrift.Transport
+import Thrift.Types
+
+data ThriftException = ThriftException
+ deriving ( Show, Typeable )
+instance Exception ThriftException
+
+data AppExnType
+ = AE_UNKNOWN
+ | AE_UNKNOWN_METHOD
+ | AE_INVALID_MESSAGE_TYPE
+ | AE_WRONG_METHOD_NAME
+ | AE_BAD_SEQUENCE_ID
+ | AE_MISSING_RESULT
+ | AE_INTERNAL_ERROR
+ | AE_PROTOCOL_ERROR
+ | AE_INVALID_TRANSFORM
+ | AE_INVALID_PROTOCOL
+ | AE_UNSUPPORTED_CLIENT_TYPE
+ deriving ( Eq, Show, Typeable )
+
+instance Enum AppExnType where
+ toEnum 0 = AE_UNKNOWN
+ toEnum 1 = AE_UNKNOWN_METHOD
+ toEnum 2 = AE_INVALID_MESSAGE_TYPE
+ toEnum 3 = AE_WRONG_METHOD_NAME
+ toEnum 4 = AE_BAD_SEQUENCE_ID
+ toEnum 5 = AE_MISSING_RESULT
+ toEnum 6 = AE_INTERNAL_ERROR
+ toEnum 7 = AE_PROTOCOL_ERROR
+ toEnum 8 = AE_INVALID_TRANSFORM
+ toEnum 9 = AE_INVALID_PROTOCOL
+ toEnum 10 = AE_UNSUPPORTED_CLIENT_TYPE
+ toEnum t = error $ "Invalid AppExnType " ++ show t
+
+ fromEnum AE_UNKNOWN = 0
+ fromEnum AE_UNKNOWN_METHOD = 1
+ fromEnum AE_INVALID_MESSAGE_TYPE = 2
+ fromEnum AE_WRONG_METHOD_NAME = 3
+ fromEnum AE_BAD_SEQUENCE_ID = 4
+ fromEnum AE_MISSING_RESULT = 5
+ fromEnum AE_INTERNAL_ERROR = 6
+ fromEnum AE_PROTOCOL_ERROR = 7
+ fromEnum AE_INVALID_TRANSFORM = 8
+ fromEnum AE_INVALID_PROTOCOL = 9
+ fromEnum AE_UNSUPPORTED_CLIENT_TYPE = 10
+
+data AppExn = AppExn { ae_type :: AppExnType, ae_message :: String }
+ deriving ( Show, Typeable )
+instance Exception AppExn
+
+writeAppExn :: Protocol p => p -> AppExn -> IO ()
+writeAppExn pt ae = writeVal pt $ TStruct $ Map.fromList
+ [ (1, ("message", TString $ encodeUtf8 $ pack $ ae_message ae))
+ , (2, ("type", TI32 $ fromIntegral $ fromEnum (ae_type ae)))
+ ]
+
+readAppExn :: Protocol p => p -> IO AppExn
+readAppExn pt = do
+ let typemap = Map.fromList [(1,("message",T_STRING)),(2,("type",T_I32))]
+ TStruct fields <- readVal pt $ T_STRUCT typemap
+ return $ readAppExnFields fields
+
+readAppExnFields :: Map.HashMap Int16 (Text, ThriftVal) -> AppExn
+readAppExnFields fields = AppExn{
+ ae_message = maybe undefined unwrapMessage $ Map.lookup 1 fields,
+ ae_type = maybe undefined unwrapType $ Map.lookup 2 fields
+ }
+ where
+ unwrapMessage (_, TString s) = unpack $ decodeUtf8 s
+ unwrapMessage _ = undefined
+ unwrapType (_, TI32 i) = toEnum $ fromIntegral i
+ unwrapType _ = undefined
diff --git a/src/jaegertracing/thrift/lib/hs/src/Thrift/Arbitraries.hs b/src/jaegertracing/thrift/lib/hs/src/Thrift/Arbitraries.hs
new file mode 100644
index 000000000..e9c0fc3ee
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/src/Thrift/Arbitraries.hs
@@ -0,0 +1,55 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module Thrift.Arbitraries where
+
+import Data.Bits()
+
+import Test.QuickCheck.Arbitrary
+
+import Control.Applicative ((<$>))
+import Data.Map (Map)
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+import qualified Data.Vector as Vector
+import qualified Data.Text.Lazy as Text
+import qualified Data.HashSet as HSet
+import qualified Data.HashMap.Strict as HMap
+import Data.Hashable (Hashable)
+
+import Data.ByteString.Lazy (ByteString)
+import qualified Data.ByteString.Lazy as BS
+
+-- String has an Arbitrary instance already
+-- Bool has an Arbitrary instance already
+-- A Thrift 'list' is a Vector.
+
+instance Arbitrary ByteString where
+ arbitrary = BS.pack . filter (/= 0) <$> arbitrary
+
+instance (Arbitrary k) => Arbitrary (Vector.Vector k) where
+ arbitrary = Vector.fromList <$> arbitrary
+
+instance Arbitrary Text.Text where
+ arbitrary = Text.pack . filter (/= '\0') <$> arbitrary
+
+instance (Eq k, Hashable k, Arbitrary k) => Arbitrary (HSet.HashSet k) where
+ arbitrary = HSet.fromList <$> arbitrary
+
+instance (Eq k, Hashable k, Arbitrary k, Arbitrary v) =>
+ Arbitrary (HMap.HashMap k v) where
+ arbitrary = HMap.fromList <$> arbitrary
+
+{-
+ To handle Thrift 'enum' we would ideally use something like:
+
+instance (Enum a, Bounded a) => Arbitrary a
+ where arbitrary = elements (enumFromTo minBound maxBound)
+
+Unfortunately this doesn't play nicely with the type system.
+Instead we'll generate an arbitrary instance along with the code.
+-}
+
+{-
+ There might be some way to introspect on the Haskell structure of a
+ Thrift 'struct' or 'exception' but generating the code directly is simpler.
+-}
diff --git a/src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol.hs b/src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol.hs
new file mode 100644
index 000000000..67a9175cb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol.hs
@@ -0,0 +1,136 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE OverloadedStrings #-}
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+module Thrift.Protocol
+ ( Protocol(..)
+ , StatelessProtocol(..)
+ , ProtocolExn(..)
+ , ProtocolExnType(..)
+ , getTypeOf
+ , runParser
+ , bsToDouble
+ , bsToDoubleLE
+ ) where
+
+import Control.Exception
+import Data.Attoparsec.ByteString
+import Data.Bits
+import Data.ByteString.Unsafe
+import Data.Functor ((<$>))
+import Data.Int
+import Data.Monoid (mempty)
+import Data.Text.Lazy (Text)
+import Data.Typeable (Typeable)
+import Data.Word
+import Foreign.Ptr (castPtr)
+import Foreign.Storable (peek, poke)
+import System.IO.Unsafe
+import qualified Data.ByteString as BS
+import qualified Data.HashMap.Strict as Map
+import qualified Data.ByteString.Lazy as LBS
+
+import Thrift.Transport
+import Thrift.Types
+
+class Protocol a where
+ readByte :: a -> IO LBS.ByteString
+ readVal :: a -> ThriftType -> IO ThriftVal
+ readMessage :: a -> ((Text, MessageType, Int32) -> IO b) -> IO b
+
+ writeVal :: a -> ThriftVal -> IO ()
+ writeMessage :: a -> (Text, MessageType, Int32) -> IO () -> IO ()
+
+class Protocol a => StatelessProtocol a where
+ serializeVal :: a -> ThriftVal -> LBS.ByteString
+ deserializeVal :: a -> ThriftType -> LBS.ByteString -> ThriftVal
+
+data ProtocolExnType
+ = PE_UNKNOWN
+ | PE_INVALID_DATA
+ | PE_NEGATIVE_SIZE
+ | PE_SIZE_LIMIT
+ | PE_BAD_VERSION
+ | PE_NOT_IMPLEMENTED
+ | PE_MISSING_REQUIRED_FIELD
+ deriving ( Eq, Show, Typeable )
+
+data ProtocolExn = ProtocolExn ProtocolExnType String
+ deriving ( Show, Typeable )
+instance Exception ProtocolExn
+
+getTypeOf :: ThriftVal -> ThriftType
+getTypeOf v = case v of
+ TStruct{} -> T_STRUCT Map.empty
+ TMap{} -> T_MAP T_VOID T_VOID
+ TList{} -> T_LIST T_VOID
+ TSet{} -> T_SET T_VOID
+ TBool{} -> T_BOOL
+ TByte{} -> T_BYTE
+ TI16{} -> T_I16
+ TI32{} -> T_I32
+ TI64{} -> T_I64
+ TString{} -> T_STRING
+ TBinary{} -> T_BINARY
+ TDouble{} -> T_DOUBLE
+
+runParser :: (Protocol p, Show a) => p -> Parser a -> IO a
+runParser prot p = refill >>= getResult . parse p
+ where
+ refill = handle handleEOF $ LBS.toStrict <$> readByte prot
+ getResult (Done _ a) = return a
+ getResult (Partial k) = refill >>= getResult . k
+ getResult f = throw $ ProtocolExn PE_INVALID_DATA (show f)
+
+handleEOF :: SomeException -> IO BS.ByteString
+handleEOF = const $ return mempty
+
+-- | Converts a ByteString to a Floating point number
+-- The ByteString is assumed to be encoded in network order (Big Endian)
+-- therefore the behavior of this function varies based on whether the local
+-- machine is big endian or little endian.
+bsToDouble :: BS.ByteString -> Double
+bsToDoubleLE :: BS.ByteString -> Double
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+bsToDouble bs = unsafeDupablePerformIO $ unsafeUseAsCString bs castBsSwapped
+bsToDoubleLE bs = unsafeDupablePerformIO $ unsafeUseAsCString bs castBs
+#else
+bsToDouble bs = unsafeDupablePerformIO $ unsafeUseAsCString bs castBs
+bsToDoubleLE bs = unsafeDupablePerformIO $ unsafeUseAsCString bs castBsSwapped
+#endif
+
+
+castBsSwapped chrPtr = do
+ w <- peek (castPtr chrPtr)
+ poke (castPtr chrPtr) (byteSwap w)
+ peek (castPtr chrPtr)
+castBs = peek . castPtr
+
+-- | Swap endianness of a 64-bit word
+byteSwap :: Word64 -> Word64
+byteSwap w = (w `shiftL` 56 .&. 0xFF00000000000000) .|.
+ (w `shiftL` 40 .&. 0x00FF000000000000) .|.
+ (w `shiftL` 24 .&. 0x0000FF0000000000) .|.
+ (w `shiftL` 8 .&. 0x000000FF00000000) .|.
+ (w `shiftR` 8 .&. 0x00000000FF000000) .|.
+ (w `shiftR` 24 .&. 0x0000000000FF0000) .|.
+ (w `shiftR` 40 .&. 0x000000000000FF00) .|.
+ (w `shiftR` 56 .&. 0x00000000000000FF)
diff --git a/src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol/Binary.hs b/src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol/Binary.hs
new file mode 100644
index 000000000..7b0acd9d4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol/Binary.hs
@@ -0,0 +1,212 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Thrift.Protocol.Binary
+ ( module Thrift.Protocol
+ , BinaryProtocol(..)
+ , versionMask
+ , version1
+ ) where
+
+import Control.Exception ( throw )
+import Control.Monad
+import Data.Bits
+import Data.ByteString.Lazy.Builder
+import Data.Functor
+import Data.Int
+import Data.Monoid
+import Data.Text.Lazy.Encoding ( decodeUtf8, encodeUtf8 )
+import Data.Word
+
+import Thrift.Protocol
+import Thrift.Transport
+import Thrift.Types
+
+import qualified Data.Attoparsec.ByteString as P
+import qualified Data.Attoparsec.ByteString.Lazy as LP
+import qualified Data.Binary as Binary
+import qualified Data.ByteString.Lazy as LBS
+import qualified Data.HashMap.Strict as Map
+import qualified Data.Text.Lazy as LT
+
+versionMask :: Int32
+versionMask = fromIntegral (0xffff0000 :: Word32)
+
+version1 :: Int32
+version1 = fromIntegral (0x80010000 :: Word32)
+
+data BinaryProtocol a = Transport a => BinaryProtocol a
+
+getTransport :: Transport t => BinaryProtocol t -> t
+getTransport (BinaryProtocol t) = t
+
+-- NOTE: Reading and Writing functions rely on Builders and Data.Binary to
+-- encode and decode data. Data.Binary assumes that the binary values it is
+-- encoding to and decoding from are in BIG ENDIAN format, and converts the
+-- endianness as necessary to match the local machine.
+instance Transport t => Protocol (BinaryProtocol t) where
+ readByte p = tReadAll (getTransport p) 1
+ -- flushTransport p = tFlush (getTransport p)
+ writeMessage p (n, t, s) f = do
+ tWrite (getTransport p) messageBegin
+ f
+ tFlush $ getTransport p
+ where
+ messageBegin = toLazyByteString $
+ buildBinaryValue (TI32 (version1 .|. fromIntegral (fromEnum t))) <>
+ buildBinaryValue (TString $ encodeUtf8 n) <>
+ buildBinaryValue (TI32 s)
+
+ readMessage p = (readMessageBegin p >>=)
+ where
+ readMessageBegin p = runParser p $ do
+ TI32 ver <- parseBinaryValue T_I32
+ if ver .&. versionMask /= version1
+ then throw $ ProtocolExn PE_BAD_VERSION "Missing version identifier"
+ else do
+ TString s <- parseBinaryValue T_STRING
+ TI32 sz <- parseBinaryValue T_I32
+ return (decodeUtf8 s, toEnum $ fromIntegral $ ver .&. 0xFF, sz)
+
+ writeVal p = tWrite (getTransport p) . toLazyByteString . buildBinaryValue
+ readVal p = runParser p . parseBinaryValue
+
+instance Transport t => StatelessProtocol (BinaryProtocol t) where
+ serializeVal _ = toLazyByteString . buildBinaryValue
+ deserializeVal _ ty bs =
+ case LP.eitherResult $ LP.parse (parseBinaryValue ty) bs of
+ Left s -> error s
+ Right val -> val
+
+-- | Writing Functions
+buildBinaryValue :: ThriftVal -> Builder
+buildBinaryValue (TStruct fields) = buildBinaryStruct fields <> buildType T_STOP
+buildBinaryValue (TMap ky vt entries) =
+ buildType ky <>
+ buildType vt <>
+ int32BE (fromIntegral (length entries)) <>
+ buildBinaryMap entries
+buildBinaryValue (TList ty entries) =
+ buildType ty <>
+ int32BE (fromIntegral (length entries)) <>
+ buildBinaryList entries
+buildBinaryValue (TSet ty entries) =
+ buildType ty <>
+ int32BE (fromIntegral (length entries)) <>
+ buildBinaryList entries
+buildBinaryValue (TBool b) =
+ word8 $ toEnum $ if b then 1 else 0
+buildBinaryValue (TByte b) = int8 b
+buildBinaryValue (TI16 i) = int16BE i
+buildBinaryValue (TI32 i) = int32BE i
+buildBinaryValue (TI64 i) = int64BE i
+buildBinaryValue (TDouble d) = doubleBE d
+buildBinaryValue (TString s) = int32BE len <> lazyByteString s
+ where
+ len :: Int32 = fromIntegral (LBS.length s)
+buildBinaryValue (TBinary s) = buildBinaryValue (TString s)
+
+buildBinaryStruct :: Map.HashMap Int16 (LT.Text, ThriftVal) -> Builder
+buildBinaryStruct = Map.foldrWithKey combine mempty
+ where
+ combine fid (_,val) s =
+ buildTypeOf val <> int16BE fid <> buildBinaryValue val <> s
+
+buildBinaryMap :: [(ThriftVal, ThriftVal)] -> Builder
+buildBinaryMap = foldl combine mempty
+ where
+ combine s (key, val) = s <> buildBinaryValue key <> buildBinaryValue val
+
+buildBinaryList :: [ThriftVal] -> Builder
+buildBinaryList = foldr (mappend . buildBinaryValue) mempty
+
+-- | Reading Functions
+parseBinaryValue :: ThriftType -> P.Parser ThriftVal
+parseBinaryValue (T_STRUCT tmap) = TStruct <$> parseBinaryStruct tmap
+parseBinaryValue (T_MAP _ _) = do
+ kt <- parseType
+ vt <- parseType
+ n <- Binary.decode . LBS.fromStrict <$> P.take 4
+ TMap kt vt <$> parseBinaryMap kt vt n
+parseBinaryValue (T_LIST _) = do
+ t <- parseType
+ n <- Binary.decode . LBS.fromStrict <$> P.take 4
+ TList t <$> parseBinaryList t n
+parseBinaryValue (T_SET _) = do
+ t <- parseType
+ n <- Binary.decode . LBS.fromStrict <$> P.take 4
+ TSet t <$> parseBinaryList t n
+parseBinaryValue T_BOOL = TBool . (/=0) <$> P.anyWord8
+parseBinaryValue T_BYTE = TByte . Binary.decode . LBS.fromStrict <$> P.take 1
+parseBinaryValue T_I16 = TI16 . Binary.decode . LBS.fromStrict <$> P.take 2
+parseBinaryValue T_I32 = TI32 . Binary.decode . LBS.fromStrict <$> P.take 4
+parseBinaryValue T_I64 = TI64 . Binary.decode . LBS.fromStrict <$> P.take 8
+parseBinaryValue T_DOUBLE = TDouble . bsToDouble <$> P.take 8
+parseBinaryValue T_STRING = parseBinaryString TString
+parseBinaryValue T_BINARY = parseBinaryString TBinary
+parseBinaryValue ty = error $ "Cannot read value of type " ++ show ty
+
+parseBinaryString ty = do
+ i :: Int32 <- Binary.decode . LBS.fromStrict <$> P.take 4
+ ty . LBS.fromStrict <$> P.take (fromIntegral i)
+
+parseBinaryStruct :: TypeMap -> P.Parser (Map.HashMap Int16 (LT.Text, ThriftVal))
+parseBinaryStruct tmap = Map.fromList <$> P.manyTill parseField (matchType T_STOP)
+ where
+ parseField = do
+ t <- parseType
+ n <- Binary.decode . LBS.fromStrict <$> P.take 2
+ v <- case (t, Map.lookup n tmap) of
+ (T_STRING, Just (_, T_BINARY)) -> parseBinaryValue T_BINARY
+ _ -> parseBinaryValue t
+ return (n, ("", v))
+
+parseBinaryMap :: ThriftType -> ThriftType -> Int32 -> P.Parser [(ThriftVal, ThriftVal)]
+parseBinaryMap kt vt n | n <= 0 = return []
+ | otherwise = do
+ k <- parseBinaryValue kt
+ v <- parseBinaryValue vt
+ ((k,v) :) <$> parseBinaryMap kt vt (n-1)
+
+parseBinaryList :: ThriftType -> Int32 -> P.Parser [ThriftVal]
+parseBinaryList ty n | n <= 0 = return []
+ | otherwise = liftM2 (:) (parseBinaryValue ty)
+ (parseBinaryList ty (n-1))
+
+
+
+-- | Write a type as a byte
+buildType :: ThriftType -> Builder
+buildType t = word8 $ fromIntegral $ fromEnum t
+
+-- | Write type of a ThriftVal as a byte
+buildTypeOf :: ThriftVal -> Builder
+buildTypeOf = buildType . getTypeOf
+
+-- | Read a byte as though it were a ThriftType
+parseType :: P.Parser ThriftType
+parseType = toEnum . fromIntegral <$> P.anyWord8
+
+matchType :: ThriftType -> P.Parser ThriftType
+matchType t = t <$ P.word8 (fromIntegral $ fromEnum t)
diff --git a/src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol/Compact.hs b/src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol/Compact.hs
new file mode 100644
index 000000000..f23970a82
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol/Compact.hs
@@ -0,0 +1,311 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Thrift.Protocol.Compact
+ ( module Thrift.Protocol
+ , CompactProtocol(..)
+ , parseVarint
+ , buildVarint
+ ) where
+
+import Control.Applicative
+import Control.Monad
+import Data.Attoparsec.ByteString as P
+import Data.Attoparsec.ByteString.Lazy as LP
+import Data.Bits
+import Data.ByteString.Lazy.Builder as B
+import Data.Int
+import Data.List as List
+import Data.Monoid
+import Data.Word
+import Data.Text.Lazy.Encoding ( decodeUtf8, encodeUtf8 )
+
+import Thrift.Protocol
+import Thrift.Transport
+import Thrift.Types
+
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as LBS
+import qualified Data.HashMap.Strict as Map
+import qualified Data.Text.Lazy as LT
+
+-- | the Compact Protocol implements the standard Thrift 'TCompactProcotol'
+-- which is similar to the 'TBinaryProtocol', but takes less space on the wire.
+-- Integral types are encoded using as varints.
+data CompactProtocol a = CompactProtocol a
+ -- ^ Constuct a 'CompactProtocol' with a 'Transport'
+
+protocolID, version, versionMask, typeMask, typeBits :: Word8
+protocolID = 0x82 -- 1000 0010
+version = 0x01
+versionMask = 0x1f -- 0001 1111
+typeMask = 0xe0 -- 1110 0000
+typeBits = 0x07 -- 0000 0111
+typeShiftAmount :: Int
+typeShiftAmount = 5
+
+getTransport :: Transport t => CompactProtocol t -> t
+getTransport (CompactProtocol t) = t
+
+instance Transport t => Protocol (CompactProtocol t) where
+ readByte p = tReadAll (getTransport p) 1
+ writeMessage p (n, t, s) f = do
+ tWrite (getTransport p) messageBegin
+ f
+ tFlush $ getTransport p
+ where
+ messageBegin = toLazyByteString $
+ B.word8 protocolID <>
+ B.word8 ((version .&. versionMask) .|.
+ (((fromIntegral $ fromEnum t) `shiftL`
+ typeShiftAmount) .&. typeMask)) <>
+ buildVarint (i32ToZigZag s) <>
+ buildCompactValue (TString $ encodeUtf8 n)
+
+ readMessage p f = readMessageBegin >>= f
+ where
+ readMessageBegin = runParser p $ do
+ pid <- fromIntegral <$> P.anyWord8
+ when (pid /= protocolID) $ error "Bad Protocol ID"
+ w <- fromIntegral <$> P.anyWord8
+ let ver = w .&. versionMask
+ when (ver /= version) $ error "Bad Protocol version"
+ let typ = (w `shiftR` typeShiftAmount) .&. typeBits
+ seqId <- parseVarint zigZagToI32
+ TString name <- parseCompactValue T_STRING
+ return (decodeUtf8 name, toEnum $ fromIntegral $ typ, seqId)
+
+ writeVal p = tWrite (getTransport p) . toLazyByteString . buildCompactValue
+ readVal p ty = runParser p $ parseCompactValue ty
+
+instance Transport t => StatelessProtocol (CompactProtocol t) where
+ serializeVal _ = toLazyByteString . buildCompactValue
+ deserializeVal _ ty bs =
+ case LP.eitherResult $ LP.parse (parseCompactValue ty) bs of
+ Left s -> error s
+ Right val -> val
+
+-- | Writing Functions
+buildCompactValue :: ThriftVal -> Builder
+buildCompactValue (TStruct fields) = buildCompactStruct fields
+buildCompactValue (TMap kt vt entries) =
+ let len = fromIntegral $ length entries :: Word32 in
+ if len == 0
+ then B.word8 0x00
+ else buildVarint len <>
+ B.word8 (fromTType kt `shiftL` 4 .|. fromTType vt) <>
+ buildCompactMap entries
+buildCompactValue (TList ty entries) =
+ let len = length entries in
+ (if len < 15
+ then B.word8 $ (fromIntegral len `shiftL` 4) .|. fromTType ty
+ else B.word8 (0xF0 .|. fromTType ty) <>
+ buildVarint (fromIntegral len :: Word32)) <>
+ buildCompactList entries
+buildCompactValue (TSet ty entries) = buildCompactValue (TList ty entries)
+buildCompactValue (TBool b) =
+ B.word8 $ toEnum $ if b then 1 else 0
+buildCompactValue (TByte b) = int8 b
+buildCompactValue (TI16 i) = buildVarint $ i16ToZigZag i
+buildCompactValue (TI32 i) = buildVarint $ i32ToZigZag i
+buildCompactValue (TI64 i) = buildVarint $ i64ToZigZag i
+buildCompactValue (TDouble d) = doubleLE d
+buildCompactValue (TString s) = buildVarint len <> lazyByteString s
+ where
+ len = fromIntegral (LBS.length s) :: Word32
+buildCompactValue (TBinary s) = buildCompactValue (TString s)
+
+buildCompactStruct :: Map.HashMap Int16 (LT.Text, ThriftVal) -> Builder
+buildCompactStruct = flip (loop 0) mempty . Map.toList
+ where
+ loop _ [] acc = acc <> B.word8 (fromTType T_STOP)
+ loop lastId ((fid, (_,val)) : fields) acc = loop fid fields $ acc <>
+ (if fid > lastId && fid - lastId <= 15
+ then B.word8 $ fromIntegral ((fid - lastId) `shiftL` 4) .|. typeOf val
+ else B.word8 (typeOf val) <> buildVarint (i16ToZigZag fid)) <>
+ (if typeOf val > 0x02 -- Not a T_BOOL
+ then buildCompactValue val
+ else mempty) -- T_BOOLs are encoded in the type
+buildCompactMap :: [(ThriftVal, ThriftVal)] -> Builder
+buildCompactMap = foldl combine mempty
+ where
+ combine s (key, val) = buildCompactValue key <> buildCompactValue val <> s
+
+buildCompactList :: [ThriftVal] -> Builder
+buildCompactList = foldr (mappend . buildCompactValue) mempty
+
+-- | Reading Functions
+parseCompactValue :: ThriftType -> Parser ThriftVal
+parseCompactValue (T_STRUCT tmap) = TStruct <$> parseCompactStruct tmap
+parseCompactValue (T_MAP kt' vt') = do
+ n <- parseVarint id
+ if n == 0
+ then return $ TMap kt' vt' []
+ else do
+ w <- P.anyWord8
+ let kt = typeFrom $ w `shiftR` 4
+ vt = typeFrom $ w .&. 0x0F
+ TMap kt vt <$> parseCompactMap kt vt n
+parseCompactValue (T_LIST ty) = TList ty <$> parseCompactList
+parseCompactValue (T_SET ty) = TSet ty <$> parseCompactList
+parseCompactValue T_BOOL = TBool . (/=0) <$> P.anyWord8
+parseCompactValue T_BYTE = TByte . fromIntegral <$> P.anyWord8
+parseCompactValue T_I16 = TI16 <$> parseVarint zigZagToI16
+parseCompactValue T_I32 = TI32 <$> parseVarint zigZagToI32
+parseCompactValue T_I64 = TI64 <$> parseVarint zigZagToI64
+parseCompactValue T_DOUBLE = TDouble . bsToDoubleLE <$> P.take 8
+parseCompactValue T_STRING = parseCompactString TString
+parseCompactValue T_BINARY = parseCompactString TBinary
+parseCompactValue ty = error $ "Cannot read value of type " ++ show ty
+
+parseCompactString ty = do
+ len :: Word32 <- parseVarint id
+ ty . LBS.fromStrict <$> P.take (fromIntegral len)
+
+parseCompactStruct :: TypeMap -> Parser (Map.HashMap Int16 (LT.Text, ThriftVal))
+parseCompactStruct tmap = Map.fromList <$> parseFields 0
+ where
+ parseFields :: Int16 -> Parser [(Int16, (LT.Text, ThriftVal))]
+ parseFields lastId = do
+ w <- P.anyWord8
+ if w == 0x00
+ then return []
+ else do
+ let ty = typeFrom (w .&. 0x0F)
+ modifier = (w .&. 0xF0) `shiftR` 4
+ fid <- if modifier /= 0
+ then return (lastId + fromIntegral modifier)
+ else parseVarint zigZagToI16
+ val <- if ty == T_BOOL
+ then return (TBool $ (w .&. 0x0F) == 0x01)
+ else case (ty, Map.lookup fid tmap) of
+ (T_STRING, Just (_, T_BINARY)) -> parseCompactValue T_BINARY
+ _ -> parseCompactValue ty
+ ((fid, (LT.empty, val)) : ) <$> parseFields fid
+
+parseCompactMap :: ThriftType -> ThriftType -> Int32 ->
+ Parser [(ThriftVal, ThriftVal)]
+parseCompactMap kt vt n | n <= 0 = return []
+ | otherwise = do
+ k <- parseCompactValue kt
+ v <- parseCompactValue vt
+ ((k,v) :) <$> parseCompactMap kt vt (n-1)
+
+parseCompactList :: Parser [ThriftVal]
+parseCompactList = do
+ w <- P.anyWord8
+ let ty = typeFrom $ w .&. 0x0F
+ lsize = w `shiftR` 4
+ size <- if lsize == 0xF
+ then parseVarint id
+ else return $ fromIntegral lsize
+ loop ty size
+ where
+ loop :: ThriftType -> Int32 -> Parser [ThriftVal]
+ loop ty n | n <= 0 = return []
+ | otherwise = liftM2 (:) (parseCompactValue ty)
+ (loop ty (n-1))
+
+-- Signed numbers must be converted to "Zig Zag" format before they can be
+-- serialized in the Varint format
+i16ToZigZag :: Int16 -> Word16
+i16ToZigZag n = fromIntegral $ (n `shiftL` 1) `xor` (n `shiftR` 15)
+
+zigZagToI16 :: Word16 -> Int16
+zigZagToI16 n = fromIntegral $ (n `shiftR` 1) `xor` negate (n .&. 0x1)
+
+i32ToZigZag :: Int32 -> Word32
+i32ToZigZag n = fromIntegral $ (n `shiftL` 1) `xor` (n `shiftR` 31)
+
+zigZagToI32 :: Word32 -> Int32
+zigZagToI32 n = fromIntegral $ (n `shiftR` 1) `xor` negate (n .&. 0x1)
+
+i64ToZigZag :: Int64 -> Word64
+i64ToZigZag n = fromIntegral $ (n `shiftL` 1) `xor` (n `shiftR` 63)
+
+zigZagToI64 :: Word64 -> Int64
+zigZagToI64 n = fromIntegral $ (n `shiftR` 1) `xor` negate (n .&. 0x1)
+
+buildVarint :: (Bits a, Integral a) => a -> Builder
+buildVarint n | n .&. complement 0x7F == 0 = B.word8 $ fromIntegral n
+ | otherwise = B.word8 (0x80 .|. (fromIntegral n .&. 0x7F)) <>
+ buildVarint (n `shiftR` 7)
+
+parseVarint :: (Bits a, Integral a, Ord a) => (a -> b) -> Parser b
+parseVarint fromZigZag = do
+ bytestemp <- BS.unpack <$> P.takeTill (not . flip testBit 7)
+ lsb <- P.anyWord8
+ let bytes = lsb : List.reverse bytestemp
+ return $ fromZigZag $ List.foldl' combine 0x00 bytes
+ where combine a b = (a `shiftL` 7) .|. (fromIntegral b .&. 0x7f)
+
+-- | Compute the Compact Type
+fromTType :: ThriftType -> Word8
+fromTType ty = case ty of
+ T_STOP -> 0x00
+ T_BOOL -> 0x01
+ T_BYTE -> 0x03
+ T_I16 -> 0x04
+ T_I32 -> 0x05
+ T_I64 -> 0x06
+ T_DOUBLE -> 0x07
+ T_STRING -> 0x08
+ T_BINARY -> 0x08
+ T_LIST{} -> 0x09
+ T_SET{} -> 0x0A
+ T_MAP{} -> 0x0B
+ T_STRUCT{} -> 0x0C
+ T_VOID -> error "No Compact type for T_VOID"
+
+typeOf :: ThriftVal -> Word8
+typeOf v = case v of
+ TBool True -> 0x01
+ TBool False -> 0x02
+ TByte _ -> 0x03
+ TI16 _ -> 0x04
+ TI32 _ -> 0x05
+ TI64 _ -> 0x06
+ TDouble _ -> 0x07
+ TString _ -> 0x08
+ TBinary _ -> 0x08
+ TList{} -> 0x09
+ TSet{} -> 0x0A
+ TMap{} -> 0x0B
+ TStruct{} -> 0x0C
+
+typeFrom :: Word8 -> ThriftType
+typeFrom w = case w of
+ 0x01 -> T_BOOL
+ 0x02 -> T_BOOL
+ 0x03 -> T_BYTE
+ 0x04 -> T_I16
+ 0x05 -> T_I32
+ 0x06 -> T_I64
+ 0x07 -> T_DOUBLE
+ 0x08 -> T_STRING
+ 0x09 -> T_LIST T_VOID
+ 0x0A -> T_SET T_VOID
+ 0x0B -> T_MAP T_VOID T_VOID
+ 0x0C -> T_STRUCT Map.empty
+ n -> error $ "typeFrom: " ++ show n ++ " is not a compact type"
diff --git a/src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol/Header.hs b/src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol/Header.hs
new file mode 100644
index 000000000..5f42db45d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol/Header.hs
@@ -0,0 +1,141 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+
+module Thrift.Protocol.Header
+ ( module Thrift.Protocol
+ , HeaderProtocol(..)
+ , getProtocolType
+ , setProtocolType
+ , getHeaders
+ , getWriteHeaders
+ , setHeader
+ , setHeaders
+ , createHeaderProtocol
+ , createHeaderProtocol1
+ ) where
+
+import Thrift.Protocol
+import Thrift.Protocol.Binary
+import Thrift.Protocol.JSON
+import Thrift.Protocol.Compact
+import Thrift.Transport
+import Thrift.Transport.Header
+import Data.IORef
+import qualified Data.Map as Map
+
+data ProtocolWrap = forall a. (Protocol a) => ProtocolWrap(a)
+
+instance Protocol ProtocolWrap where
+ readByte (ProtocolWrap p) = readByte p
+ readVal (ProtocolWrap p) = readVal p
+ readMessage (ProtocolWrap p) = readMessage p
+ writeVal (ProtocolWrap p) = writeVal p
+ writeMessage (ProtocolWrap p) = writeMessage p
+
+data HeaderProtocol i o = (Transport i, Transport o) => HeaderProtocol {
+ trans :: HeaderTransport i o,
+ wrappedProto :: IORef ProtocolWrap
+ }
+
+createProtocolWrap :: Transport t => ProtocolType -> t -> ProtocolWrap
+createProtocolWrap typ t =
+ case typ of
+ TBinary -> ProtocolWrap $ BinaryProtocol t
+ TCompact -> ProtocolWrap $ CompactProtocol t
+ TJSON -> ProtocolWrap $ JSONProtocol t
+
+createHeaderProtocol :: (Transport i, Transport o) => i -> o -> IO(HeaderProtocol i o)
+createHeaderProtocol i o = do
+ t <- openHeaderTransport i o
+ pid <- readIORef $ protocolType t
+ proto <- newIORef $ createProtocolWrap pid t
+ return $ HeaderProtocol { trans = t, wrappedProto = proto }
+
+createHeaderProtocol1 :: Transport t => t -> IO(HeaderProtocol t t)
+createHeaderProtocol1 t = createHeaderProtocol t t
+
+resetProtocol :: (Transport i, Transport o) => HeaderProtocol i o -> IO ()
+resetProtocol p = do
+ pid <- readIORef $ protocolType $ trans p
+ writeIORef (wrappedProto p) $ createProtocolWrap pid $ trans p
+
+getWrapped = readIORef . wrappedProto
+
+setTransport :: (Transport i, Transport o) => HeaderProtocol i o -> HeaderTransport i o -> HeaderProtocol i o
+setTransport p t = p { trans = t }
+
+updateTransport :: (Transport i, Transport o) => HeaderProtocol i o -> (HeaderTransport i o -> HeaderTransport i o)-> HeaderProtocol i o
+updateTransport p f = setTransport p (f $ trans p)
+
+type Headers = Map.Map String String
+
+-- TODO: we want to set headers without recreating client...
+setHeader :: (Transport i, Transport o) => HeaderProtocol i o -> String -> String -> HeaderProtocol i o
+setHeader p k v = updateTransport p $ \t -> t { writeHeaders = Map.insert k v $ writeHeaders t }
+
+setHeaders :: (Transport i, Transport o) => HeaderProtocol i o -> Headers -> HeaderProtocol i o
+setHeaders p h = updateTransport p $ \t -> t { writeHeaders = h }
+
+-- TODO: make it public once we have first transform implementation for Haskell
+setTransforms :: (Transport i, Transport o) => HeaderProtocol i o -> [TransformType] -> HeaderProtocol i o
+setTransforms p trs = updateTransport p $ \t -> t { writeTransforms = trs }
+
+setTransform :: (Transport i, Transport o) => HeaderProtocol i o -> TransformType -> HeaderProtocol i o
+setTransform p tr = updateTransport p $ \t -> t { writeTransforms = tr:(writeTransforms t) }
+
+getWriteHeaders :: (Transport i, Transport o) => HeaderProtocol i o -> Headers
+getWriteHeaders = writeHeaders . trans
+
+getHeaders :: (Transport i, Transport o) => HeaderProtocol i o -> IO [(String, String)]
+getHeaders = readIORef . headers . trans
+
+getProtocolType :: (Transport i, Transport o) => HeaderProtocol i o -> IO ProtocolType
+getProtocolType p = readIORef $ protocolType $ trans p
+
+setProtocolType :: (Transport i, Transport o) => HeaderProtocol i o -> ProtocolType -> IO ()
+setProtocolType p typ = do
+ typ0 <- getProtocolType p
+ if typ == typ0
+ then return ()
+ else do
+ tSetProtocol (trans p) typ
+ resetProtocol p
+
+instance (Transport i, Transport o) => Protocol (HeaderProtocol i o) where
+ readByte p = tReadAll (trans p) 1
+
+ readVal p tp = do
+ proto <- getWrapped p
+ readVal proto tp
+
+ readMessage p f = do
+ tResetProtocol (trans p)
+ resetProtocol p
+ proto <- getWrapped p
+ readMessage proto f
+
+ writeVal p v = do
+ proto <- getWrapped p
+ writeVal proto v
+
+ writeMessage p x f = do
+ proto <- getWrapped p
+ writeMessage proto x f
+
diff --git a/src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol/JSON.hs b/src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol/JSON.hs
new file mode 100644
index 000000000..839eddc84
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/src/Thrift/Protocol/JSON.hs
@@ -0,0 +1,362 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TupleSections #-}
+
+module Thrift.Protocol.JSON
+ ( module Thrift.Protocol
+ , JSONProtocol(..)
+ ) where
+
+import Control.Applicative
+import Control.Exception (bracket)
+import Control.Monad
+import Data.Attoparsec.ByteString as P
+import Data.Attoparsec.ByteString.Char8 as PC
+import Data.Attoparsec.ByteString.Lazy as LP
+import Data.ByteString.Base64.Lazy as B64C
+import Data.ByteString.Lazy.Builder as B
+import Data.ByteString.Internal (c2w, w2c)
+import Data.Functor
+import Data.Int
+import Data.List
+import Data.Maybe (catMaybes)
+import Data.Monoid
+import Data.Text.Lazy.Encoding
+import Data.Word
+import qualified Data.HashMap.Strict as Map
+
+import Thrift.Protocol
+import Thrift.Transport
+import Thrift.Types
+
+import qualified Data.ByteString.Lazy as LBS
+import qualified Data.ByteString.Lazy.Char8 as LBSC
+import qualified Data.Text.Lazy as LT
+
+-- | The JSON Protocol data uses the standard 'TJSONProtocol'. Data is
+-- encoded as a JSON 'ByteString'
+data JSONProtocol t = JSONProtocol t
+ -- ^ Construct a 'JSONProtocol' with a 'Transport'
+getTransport :: Transport t => JSONProtocol t -> t
+getTransport (JSONProtocol t) = t
+
+instance Transport t => Protocol (JSONProtocol t) where
+ readByte p = tReadAll (getTransport p) 1
+
+ writeMessage (JSONProtocol t) (s, ty, sq) = bracket readMessageBegin readMessageEnd . const
+ where
+ readMessageBegin = tWrite t $ toLazyByteString $
+ B.char8 '[' <> buildShowable (1 :: Int32) <>
+ B.string8 ",\"" <> escape (encodeUtf8 s) <> B.char8 '\"' <>
+ B.char8 ',' <> buildShowable (fromEnum ty) <>
+ B.char8 ',' <> buildShowable sq <>
+ B.char8 ','
+ readMessageEnd _ = do
+ tWrite t "]"
+ tFlush t
+
+ readMessage p = bracket readMessageBegin readMessageEnd
+ where
+ readMessageBegin = runParser p $ skipSpace *> do
+ _ver :: Int32 <- lexeme (PC.char8 '[') *> lexeme (signed decimal)
+ bs <- lexeme (PC.char8 ',') *> lexeme escapedString
+ case decodeUtf8' bs of
+ Left _ -> fail "readMessage: invalid text encoding"
+ Right str -> do
+ ty <- toEnum <$> (lexeme (PC.char8 ',') *> lexeme (signed decimal))
+ seqNum <- lexeme (PC.char8 ',') *> lexeme (signed decimal)
+ _ <- PC.char8 ','
+ return (str, ty, seqNum)
+ readMessageEnd _ = void $ runParser p (PC.char8 ']')
+
+ writeVal p = tWrite (getTransport p) . toLazyByteString . buildJSONValue
+ readVal p ty = runParser p $ skipSpace *> parseJSONValue ty
+
+instance Transport t => StatelessProtocol (JSONProtocol t) where
+ serializeVal _ = toLazyByteString . buildJSONValue
+ deserializeVal _ ty bs =
+ case LP.eitherResult $ LP.parse (parseJSONValue ty) bs of
+ Left s -> error s
+ Right val -> val
+
+-- Writing Functions
+
+buildJSONValue :: ThriftVal -> Builder
+buildJSONValue (TStruct fields) = B.char8 '{' <> buildJSONStruct fields <> B.char8 '}'
+buildJSONValue (TMap kt vt entries) =
+ B.char8 '[' <> B.char8 '"' <> getTypeName kt <> B.char8 '"' <>
+ B.char8 ',' <> B.char8 '"' <> getTypeName vt <> B.char8 '"' <>
+ B.char8 ',' <> buildShowable (length entries) <>
+ B.char8 ',' <> B.char8 '{' <> buildJSONMap entries <> B.char8 '}' <>
+ B.char8 ']'
+buildJSONValue (TList ty entries) =
+ B.char8 '[' <> B.char8 '"' <> getTypeName ty <> B.char8 '"' <>
+ B.char8 ',' <> buildShowable (length entries) <>
+ (if length entries > 0
+ then B.char8 ',' <> buildJSONList entries
+ else mempty) <>
+ B.char8 ']'
+buildJSONValue (TSet ty entries) = buildJSONValue (TList ty entries)
+buildJSONValue (TBool b) = if b then B.char8 '1' else B.char8 '0'
+buildJSONValue (TByte b) = buildShowable b
+buildJSONValue (TI16 i) = buildShowable i
+buildJSONValue (TI32 i) = buildShowable i
+buildJSONValue (TI64 i) = buildShowable i
+buildJSONValue (TDouble d) = buildShowable d
+buildJSONValue (TString s) = B.char8 '\"' <> escape s <> B.char8 '\"'
+buildJSONValue (TBinary s) = B.char8 '\"' <> (B.lazyByteString . B64C.encode $ s) <> B.char8 '\"'
+
+buildJSONStruct :: Map.HashMap Int16 (LT.Text, ThriftVal) -> Builder
+buildJSONStruct = mconcat . intersperse (B.char8 ',') . Map.foldrWithKey buildField []
+ where
+ buildField fid (_,val) = (:) $
+ B.char8 '"' <> buildShowable fid <> B.string8 "\":" <>
+ B.char8 '{' <>
+ B.char8 '"' <> getTypeName (getTypeOf val) <> B.string8 "\":" <>
+ buildJSONValue val <>
+ B.char8 '}'
+
+buildJSONMap :: [(ThriftVal, ThriftVal)] -> Builder
+buildJSONMap = mconcat . intersperse (B.char8 ',') . map buildKV
+ where
+ buildKV (key@(TString _), val) =
+ buildJSONValue key <> B.char8 ':' <> buildJSONValue val
+ buildKV (key, val) =
+ B.char8 '\"' <> buildJSONValue key <> B.string8 "\":" <> buildJSONValue val
+buildJSONList :: [ThriftVal] -> Builder
+buildJSONList = mconcat . intersperse (B.char8 ',') . map buildJSONValue
+
+buildShowable :: Show a => a -> Builder
+buildShowable = B.string8 . show
+
+-- Reading Functions
+
+parseJSONValue :: ThriftType -> Parser ThriftVal
+parseJSONValue (T_STRUCT tmap) =
+ TStruct <$> (lexeme (PC.char8 '{') *> parseJSONStruct tmap <* PC.char8 '}')
+parseJSONValue (T_MAP kt vt) = fmap (TMap kt vt) $
+ between '[' ']' $
+ lexeme escapedString *> lexeme (PC.char8 ',') *>
+ lexeme escapedString *> lexeme (PC.char8 ',') *>
+ lexeme decimal *> lexeme (PC.char8 ',') *>
+ between '{' '}' (parseJSONMap kt vt)
+parseJSONValue (T_LIST ty) = fmap (TList ty) $
+ between '[' ']' $ do
+ len <- lexeme escapedString *> lexeme (PC.char8 ',') *> lexeme decimal
+ if len > 0
+ then lexeme (PC.char8 ',') *> parseJSONList ty
+ else return []
+parseJSONValue (T_SET ty) = fmap (TSet ty) $
+ between '[' ']' $ do
+ len <- lexeme escapedString *> lexeme (PC.char8 ',') *> lexeme decimal
+ if len > 0
+ then lexeme (PC.char8 ',') *> parseJSONList ty
+ else return []
+parseJSONValue T_BOOL =
+ (TBool True <$ PC.char8 '1') <|> (TBool False <$ PC.char8 '0')
+parseJSONValue T_BYTE = TByte <$> signed decimal
+parseJSONValue T_I16 = TI16 <$> signed decimal
+parseJSONValue T_I32 = TI32 <$> signed decimal
+parseJSONValue T_I64 = TI64 <$> signed decimal
+parseJSONValue T_DOUBLE = TDouble <$> double
+parseJSONValue T_STRING = TString <$> escapedString
+parseJSONValue T_BINARY = TBinary <$> base64String
+parseJSONValue T_STOP = fail "parseJSONValue: cannot parse type T_STOP"
+parseJSONValue T_VOID = fail "parseJSONValue: cannot parse type T_VOID"
+
+parseAnyValue :: Parser ()
+parseAnyValue = choice $
+ skipBetween '{' '}' :
+ skipBetween '[' ']' :
+ map (void . parseJSONValue)
+ [ T_BOOL
+ , T_I16
+ , T_I32
+ , T_I64
+ , T_DOUBLE
+ , T_STRING
+ , T_BINARY
+ ]
+ where
+ skipBetween :: Char -> Char -> Parser ()
+ skipBetween a b = between a b $ void (PC.satisfy (\c -> c /= a && c /= b))
+ <|> skipBetween a b
+
+parseJSONStruct :: TypeMap -> Parser (Map.HashMap Int16 (LT.Text, ThriftVal))
+parseJSONStruct tmap = Map.fromList . catMaybes <$> parseField
+ `sepBy` lexeme (PC.char8 ',')
+ where
+ parseField = do
+ fid <- lexeme (between '"' '"' decimal) <* lexeme (PC.char8 ':')
+ case Map.lookup fid tmap of
+ Just (str, ftype) -> between '{' '}' $ do
+ _ <- lexeme (escapedString) *> lexeme (PC.char8 ':')
+ val <- lexeme (parseJSONValue ftype)
+ return $ Just (fid, (str, val))
+ Nothing -> lexeme parseAnyValue *> return Nothing
+
+parseJSONMap :: ThriftType -> ThriftType -> Parser [(ThriftVal, ThriftVal)]
+parseJSONMap kt vt =
+ ((,) <$> lexeme (parseJSONKey kt) <*>
+ (lexeme (PC.char8 ':') *> lexeme (parseJSONValue vt))) `sepBy`
+ lexeme (PC.char8 ',')
+ where
+ parseJSONKey T_STRING = parseJSONValue T_STRING
+ parseJSONKey T_BINARY = parseJSONValue T_BINARY
+ parseJSONKey kt = PC.char8 '"' *> parseJSONValue kt <* PC.char8 '"'
+
+parseJSONList :: ThriftType -> Parser [ThriftVal]
+parseJSONList ty = lexeme (parseJSONValue ty) `sepBy` lexeme (PC.char8 ',')
+
+escapedString :: Parser LBS.ByteString
+escapedString = PC.char8 '"' *>
+ (LBS.pack <$> P.many' (escapedChar <|> notChar8 '"')) <*
+ PC.char8 '"'
+
+base64String :: Parser LBS.ByteString
+base64String = PC.char8 '"' *>
+ (decodeBase64 . LBSC.pack <$> P.many' (PC.notChar '"')) <*
+ PC.char8 '"'
+ where
+ decodeBase64 b =
+ let padded = case (LBS.length b) `mod` 4 of
+ 2 -> LBS.append b "=="
+ 3 -> LBS.append b "="
+ _ -> b in
+ case B64C.decode padded of
+ Right s -> s
+ Left x -> error x
+
+escapedChar :: Parser Word8
+escapedChar = PC.char8 '\\' *> (c2w <$> choice
+ [ '\SOH' <$ P.string "u0001"
+ , '\STX' <$ P.string "u0002"
+ , '\ETX' <$ P.string "u0003"
+ , '\EOT' <$ P.string "u0004"
+ , '\ENQ' <$ P.string "u0005"
+ , '\ACK' <$ P.string "u0006"
+ , '\BEL' <$ P.string "u0007"
+ , '\BS' <$ P.string "u0008"
+ , '\VT' <$ P.string "u000b"
+ , '\FF' <$ P.string "u000c"
+ , '\CR' <$ P.string "u000d"
+ , '\SO' <$ P.string "u000e"
+ , '\SI' <$ P.string "u000f"
+ , '\DLE' <$ P.string "u0010"
+ , '\DC1' <$ P.string "u0011"
+ , '\DC2' <$ P.string "u0012"
+ , '\DC3' <$ P.string "u0013"
+ , '\DC4' <$ P.string "u0014"
+ , '\NAK' <$ P.string "u0015"
+ , '\SYN' <$ P.string "u0016"
+ , '\ETB' <$ P.string "u0017"
+ , '\CAN' <$ P.string "u0018"
+ , '\EM' <$ P.string "u0019"
+ , '\SUB' <$ P.string "u001a"
+ , '\ESC' <$ P.string "u001b"
+ , '\FS' <$ P.string "u001c"
+ , '\GS' <$ P.string "u001d"
+ , '\RS' <$ P.string "u001e"
+ , '\US' <$ P.string "u001f"
+ , '\DEL' <$ P.string "u007f"
+ , '\0' <$ PC.char '0'
+ , '\a' <$ PC.char 'a'
+ , '\b' <$ PC.char 'b'
+ , '\f' <$ PC.char 'f'
+ , '\n' <$ PC.char 'n'
+ , '\r' <$ PC.char 'r'
+ , '\t' <$ PC.char 't'
+ , '\v' <$ PC.char 'v'
+ , '\"' <$ PC.char '"'
+ , '\'' <$ PC.char '\''
+ , '\\' <$ PC.char '\\'
+ , '/' <$ PC.char '/'
+ ])
+
+escape :: LBS.ByteString -> Builder
+escape = LBS.foldl' escapeChar mempty
+ where
+ escapeChar b w = b <> (B.lazyByteString $ case w2c w of
+ '\0' -> "\\0"
+ '\b' -> "\\b"
+ '\f' -> "\\f"
+ '\n' -> "\\n"
+ '\r' -> "\\r"
+ '\t' -> "\\t"
+ '\"' -> "\\\""
+ '\\' -> "\\\\"
+ '\SOH' -> "\\u0001"
+ '\STX' -> "\\u0002"
+ '\ETX' -> "\\u0003"
+ '\EOT' -> "\\u0004"
+ '\ENQ' -> "\\u0005"
+ '\ACK' -> "\\u0006"
+ '\BEL' -> "\\u0007"
+ '\VT' -> "\\u000b"
+ '\SO' -> "\\u000e"
+ '\SI' -> "\\u000f"
+ '\DLE' -> "\\u0010"
+ '\DC1' -> "\\u0011"
+ '\DC2' -> "\\u0012"
+ '\DC3' -> "\\u0013"
+ '\DC4' -> "\\u0014"
+ '\NAK' -> "\\u0015"
+ '\SYN' -> "\\u0016"
+ '\ETB' -> "\\u0017"
+ '\CAN' -> "\\u0018"
+ '\EM' -> "\\u0019"
+ '\SUB' -> "\\u001a"
+ '\ESC' -> "\\u001b"
+ '\FS' -> "\\u001c"
+ '\GS' -> "\\u001d"
+ '\RS' -> "\\u001e"
+ '\US' -> "\\u001f"
+ '\DEL' -> "\\u007f"
+ _ -> LBS.singleton w)
+
+lexeme :: Parser a -> Parser a
+lexeme = (<* skipSpace)
+
+notChar8 :: Char -> Parser Word8
+notChar8 c = P.satisfy (/= c2w c)
+
+between :: Char -> Char -> Parser a -> Parser a
+between a b p = lexeme (PC.char8 a) *> lexeme p <* lexeme (PC.char8 b)
+
+getTypeName :: ThriftType -> Builder
+getTypeName ty = B.string8 $ case ty of
+ T_STRUCT _ -> "rec"
+ T_MAP _ _ -> "map"
+ T_LIST _ -> "lst"
+ T_SET _ -> "set"
+ T_BOOL -> "tf"
+ T_BYTE -> "i8"
+ T_I16 -> "i16"
+ T_I32 -> "i32"
+ T_I64 -> "i64"
+ T_DOUBLE -> "dbl"
+ T_STRING -> "str"
+ T_BINARY -> "str"
+ _ -> error "Unrecognized Type"
+
diff --git a/src/jaegertracing/thrift/lib/hs/src/Thrift/Server.hs b/src/jaegertracing/thrift/lib/hs/src/Thrift/Server.hs
new file mode 100644
index 000000000..543f33850
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/src/Thrift/Server.hs
@@ -0,0 +1,66 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+module Thrift.Server
+ ( runBasicServer
+ , runThreadedServer
+ ) where
+
+import Control.Concurrent ( forkIO )
+import Control.Exception
+import Control.Monad ( forever, when )
+
+import Network
+
+import System.IO
+
+import Thrift
+import Thrift.Transport.Handle()
+import Thrift.Protocol.Binary
+
+
+-- | A threaded sever that is capable of using any Transport or Protocol
+-- instances.
+runThreadedServer :: (Protocol i, Protocol o)
+ => (Socket -> IO (i, o))
+ -> h
+ -> (h -> (i, o) -> IO Bool)
+ -> PortID
+ -> IO a
+runThreadedServer accepter hand proc_ port = do
+ socket <- listenOn port
+ acceptLoop (accepter socket) (proc_ hand)
+
+-- | A basic threaded binary protocol socket server.
+runBasicServer :: h
+ -> (h -> (BinaryProtocol Handle, BinaryProtocol Handle) -> IO Bool)
+ -> PortNumber
+ -> IO a
+runBasicServer hand proc_ port = runThreadedServer binaryAccept hand proc_ (PortNumber port)
+ where binaryAccept s = do
+ (h, _, _) <- accept s
+ return (BinaryProtocol h, BinaryProtocol h)
+
+acceptLoop :: IO t -> (t -> IO Bool) -> IO a
+acceptLoop accepter proc_ = forever $
+ do ps <- accepter
+ forkIO $ handle (\(_ :: SomeException) -> return ())
+ (loop $ proc_ ps)
+ where loop m = do { continue <- m; when continue (loop m) }
diff --git a/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport.hs b/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport.hs
new file mode 100644
index 000000000..306edc208
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport.hs
@@ -0,0 +1,65 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+module Thrift.Transport
+ ( Transport(..)
+ , TransportExn(..)
+ , TransportExnType(..)
+ ) where
+
+import Control.Monad ( when )
+import Control.Exception ( Exception, throw )
+import Data.Functor ( (<$>) )
+import Data.Typeable ( Typeable )
+import Data.Word
+
+import qualified Data.ByteString.Lazy as LBS
+import Data.Monoid
+
+class Transport a where
+ tIsOpen :: a -> IO Bool
+ tClose :: a -> IO ()
+ tRead :: a -> Int -> IO LBS.ByteString
+ tPeek :: a -> IO (Maybe Word8)
+ tWrite :: a -> LBS.ByteString -> IO ()
+ tFlush :: a -> IO ()
+ tReadAll :: a -> Int -> IO LBS.ByteString
+
+ tReadAll _ 0 = return mempty
+ tReadAll a len = do
+ result <- tRead a len
+ let rlen = fromIntegral $ LBS.length result
+ when (rlen == 0) (throw $ TransportExn "Cannot read. Remote side has closed." TE_UNKNOWN)
+ if len <= rlen
+ then return result
+ else (result `mappend`) <$> tReadAll a (len - rlen)
+
+data TransportExn = TransportExn String TransportExnType
+ deriving ( Show, Typeable )
+instance Exception TransportExn
+
+data TransportExnType
+ = TE_UNKNOWN
+ | TE_NOT_OPEN
+ | TE_ALREADY_OPEN
+ | TE_TIMED_OUT
+ | TE_END_OF_FILE
+ deriving ( Eq, Show, Typeable )
diff --git a/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Empty.hs b/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Empty.hs
new file mode 100644
index 000000000..47af5fe88
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Empty.hs
@@ -0,0 +1,36 @@
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE OverloadedStrings #-}
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+module Thrift.Transport.Empty
+ ( EmptyTransport(..)
+ ) where
+
+import Thrift.Transport
+
+data EmptyTransport = EmptyTransport
+
+instance Transport EmptyTransport where
+ tIsOpen = const $ return False
+ tClose = const $ return ()
+ tRead _ _ = return ""
+ tPeek = const $ return Nothing
+ tWrite _ _ = return ()
+ tFlush = const$ return ()
diff --git a/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Framed.hs b/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Framed.hs
new file mode 100644
index 000000000..42fc43f39
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Framed.hs
@@ -0,0 +1,99 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+module Thrift.Transport.Framed
+ ( module Thrift.Transport
+ , FramedTransport
+ , openFramedTransport
+ ) where
+
+import Thrift.Transport
+import Thrift.Transport.IOBuffer
+
+import Data.Int (Int32)
+import qualified Data.Binary as B
+import qualified Data.ByteString.Lazy as LBS
+
+
+-- | FramedTransport wraps a given transport in framed mode.
+data FramedTransport t = FramedTransport {
+ wrappedTrans :: t, -- ^ Underlying transport.
+ writeBuffer :: WriteBuffer, -- ^ Write buffer.
+ readBuffer :: ReadBuffer -- ^ Read buffer.
+ }
+
+-- | Create a new framed transport which wraps the given transport.
+openFramedTransport :: Transport t => t -> IO (FramedTransport t)
+openFramedTransport trans = do
+ wbuf <- newWriteBuffer
+ rbuf <- newReadBuffer
+ return FramedTransport{ wrappedTrans = trans, writeBuffer = wbuf, readBuffer = rbuf }
+
+instance Transport t => Transport (FramedTransport t) where
+
+ tClose = tClose . wrappedTrans
+
+ tRead trans n = do
+ -- First, check the read buffer for any data.
+ bs <- readBuf (readBuffer trans) n
+ if LBS.null bs
+ then
+ -- When the buffer is empty, read another frame from the
+ -- underlying transport.
+ do len <- readFrame trans
+ if len > 0
+ then tRead trans n
+ else return bs
+ else return bs
+ tPeek trans = do
+ mw <- peekBuf (readBuffer trans)
+ case mw of
+ Just _ -> return mw
+ Nothing -> do
+ len <- readFrame trans
+ if len > 0
+ then tPeek trans
+ else return Nothing
+
+ tWrite = writeBuf . writeBuffer
+
+ tFlush trans = do
+ bs <- flushBuf (writeBuffer trans)
+ let szBs = B.encode $ (fromIntegral $ LBS.length bs :: Int32)
+ tWrite (wrappedTrans trans) szBs
+ tWrite (wrappedTrans trans) bs
+ tFlush (wrappedTrans trans)
+
+ tIsOpen = tIsOpen . wrappedTrans
+
+readFrame :: Transport t => FramedTransport t -> IO Int
+readFrame trans = do
+ -- Read and decode the frame size.
+ szBs <- tRead (wrappedTrans trans) 4
+ let sz = fromIntegral (B.decode szBs :: Int32)
+
+ -- Read the frame and stuff it into the read buffer.
+ bs <- tRead (wrappedTrans trans) sz
+ fillBuf (readBuffer trans) bs
+
+ -- Return the frame size so that the caller knows whether to expect
+ -- something in the read buffer or not.
+ return sz
diff --git a/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Handle.hs b/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Handle.hs
new file mode 100644
index 000000000..ff6295b67
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Handle.hs
@@ -0,0 +1,78 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+module Thrift.Transport.Handle
+ ( module Thrift.Transport
+ , HandleSource(..)
+ ) where
+
+import Control.Exception ( catch, throw )
+import Data.ByteString.Internal (c2w)
+import Data.Functor
+
+import Network
+
+import System.IO
+import System.IO.Error ( isEOFError )
+
+import Thrift.Transport
+
+import qualified Data.ByteString.Lazy as LBS
+import Data.Monoid
+
+instance Transport Handle where
+ tIsOpen = hIsOpen
+ tClose = hClose
+ tRead h n = read `Control.Exception.catch` handleEOF mempty
+ where
+ read = do
+ hLookAhead h
+ LBS.hGetNonBlocking h n
+ tReadAll _ 0 = return mempty
+ tReadAll h n = LBS.hGet h n `Control.Exception.catch` throwTransportExn
+ tPeek h = (Just . c2w <$> hLookAhead h) `Control.Exception.catch` handleEOF Nothing
+ tWrite = LBS.hPut
+ tFlush = hFlush
+
+
+-- | Type class for all types that can open a Handle. This class is used to
+-- replace tOpen in the Transport type class.
+class HandleSource s where
+ hOpen :: s -> IO Handle
+
+instance HandleSource FilePath where
+ hOpen s = openFile s ReadWriteMode
+
+instance HandleSource (HostName, PortID) where
+ hOpen = uncurry connectTo
+
+throwTransportExn :: IOError -> IO a
+throwTransportExn e = if isEOFError e
+ then throw $ TransportExn "Cannot read. Remote side has closed." TE_UNKNOWN
+ else throw $ TransportExn "Handle tReadAll: Could not read" TE_UNKNOWN
+
+handleEOF :: a -> IOError -> IO a
+handleEOF a e = if isEOFError e
+ then return a
+ else throw $ TransportExn "Handle: Could not read" TE_UNKNOWN
diff --git a/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Header.hs b/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Header.hs
new file mode 100644
index 000000000..2dacad25f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Header.hs
@@ -0,0 +1,354 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+module Thrift.Transport.Header
+ ( module Thrift.Transport
+ , HeaderTransport(..)
+ , openHeaderTransport
+ , ProtocolType(..)
+ , TransformType(..)
+ , ClientType(..)
+ , tResetProtocol
+ , tSetProtocol
+ ) where
+
+import Thrift.Transport
+import Thrift.Protocol.Compact
+import Control.Applicative
+import Control.Exception ( throw )
+import Control.Monad
+import Data.Bits
+import Data.IORef
+import Data.Int
+import Data.Monoid
+import Data.Word
+
+import qualified Data.Attoparsec.ByteString as P
+import qualified Data.Binary as Binary
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Char8 as C
+import qualified Data.ByteString.Lazy as LBS
+import qualified Data.ByteString.Lazy.Builder as B
+import qualified Data.Map as Map
+
+data ProtocolType = TBinary | TCompact | TJSON deriving (Enum, Eq)
+data ClientType = HeaderClient | Framed | Unframed deriving (Enum, Eq)
+
+infoIdKeyValue = 1
+
+type Headers = Map.Map String String
+
+data TransformType = ZlibTransform deriving (Enum, Eq)
+
+fromTransportType :: TransformType -> Int16
+fromTransportType ZlibTransform = 1
+
+toTransportType :: Int16 -> TransformType
+toTransportType 1 = ZlibTransform
+toTransportType _ = throw $ TransportExn "HeaderTransport: Unknown transform ID" TE_UNKNOWN
+
+data HeaderTransport i o = (Transport i, Transport o) => HeaderTransport
+ { readBuffer :: IORef LBS.ByteString
+ , writeBuffer :: IORef B.Builder
+ , inTrans :: i
+ , outTrans :: o
+ , clientType :: IORef ClientType
+ , protocolType :: IORef ProtocolType
+ , headers :: IORef [(String, String)]
+ , writeHeaders :: Headers
+ , transforms :: IORef [TransformType]
+ , writeTransforms :: [TransformType]
+ }
+
+openHeaderTransport :: (Transport i, Transport o) => i -> o -> IO (HeaderTransport i o)
+openHeaderTransport i o = do
+ pid <- newIORef TCompact
+ rBuf <- newIORef LBS.empty
+ wBuf <- newIORef mempty
+ cType <- newIORef HeaderClient
+ h <- newIORef []
+ trans <- newIORef []
+ return HeaderTransport
+ { readBuffer = rBuf
+ , writeBuffer = wBuf
+ , inTrans = i
+ , outTrans = o
+ , clientType = cType
+ , protocolType = pid
+ , headers = h
+ , writeHeaders = Map.empty
+ , transforms = trans
+ , writeTransforms = []
+ }
+
+isFramed t = (/= Unframed) <$> readIORef (clientType t)
+
+readFrame :: (Transport i, Transport o) => HeaderTransport i o -> IO Bool
+readFrame t = do
+ let input = inTrans t
+ let rBuf = readBuffer t
+ let cType = clientType t
+ lsz <- tRead input 4
+ let sz = LBS.toStrict lsz
+ case P.parseOnly P.endOfInput sz of
+ Right _ -> do return False
+ Left _ -> do
+ case parseBinaryMagic sz of
+ Right _ -> do
+ writeIORef rBuf $ lsz
+ writeIORef cType Unframed
+ writeIORef (protocolType t) TBinary
+ return True
+ Left _ -> do
+ case parseCompactMagic sz of
+ Right _ -> do
+ writeIORef rBuf $ lsz
+ writeIORef cType Unframed
+ writeIORef (protocolType t) TCompact
+ return True
+ Left _ -> do
+ let len = Binary.decode lsz :: Int32
+ lbuf <- tReadAll input $ fromIntegral len
+ let buf = LBS.toStrict lbuf
+ case parseBinaryMagic buf of
+ Right _ -> do
+ writeIORef cType Framed
+ writeIORef (protocolType t) TBinary
+ writeIORef rBuf lbuf
+ return True
+ Left _ -> do
+ case parseCompactMagic buf of
+ Right _ -> do
+ writeIORef cType Framed
+ writeIORef (protocolType t) TCompact
+ writeIORef rBuf lbuf
+ return True
+ Left _ -> do
+ case parseHeaderMagic buf of
+ Right flags -> do
+ let (flags, seqNum, header, body) = extractHeader buf
+ writeIORef cType HeaderClient
+ handleHeader t header
+ payload <- untransform t body
+ writeIORef rBuf $ LBS.fromStrict $ payload
+ return True
+ Left _ ->
+ throw $ TransportExn "HeaderTransport: unkonwn client type" TE_UNKNOWN
+
+parseBinaryMagic = P.parseOnly $ P.word8 0x80 *> P.word8 0x01 *> P.word8 0x00 *> P.anyWord8
+parseCompactMagic = P.parseOnly $ P.word8 0x82 *> P.satisfy (\b -> b .&. 0x1f == 0x01)
+parseHeaderMagic = P.parseOnly $ P.word8 0x0f *> P.word8 0xff *> (P.count 2 P.anyWord8)
+
+parseI32 :: P.Parser Int32
+parseI32 = Binary.decode . LBS.fromStrict <$> P.take 4
+parseI16 :: P.Parser Int16
+parseI16 = Binary.decode . LBS.fromStrict <$> P.take 2
+
+extractHeader :: BS.ByteString -> (Int16, Int32, BS.ByteString, BS.ByteString)
+extractHeader bs =
+ case P.parse extractHeader_ bs of
+ P.Done remain (flags, seqNum, header) -> (flags, seqNum, header, remain)
+ _ -> throw $ TransportExn "HeaderTransport: Invalid header" TE_UNKNOWN
+ where
+ extractHeader_ = do
+ magic <- P.word8 0x0f *> P.word8 0xff
+ flags <- parseI16
+ seqNum <- parseI32
+ (headerSize :: Int) <- (* 4) . fromIntegral <$> parseI16
+ header <- P.take headerSize
+ return (flags, seqNum, header)
+
+handleHeader t header =
+ case P.parseOnly parseHeader header of
+ Right (pType, trans, info) -> do
+ writeIORef (protocolType t) pType
+ writeIORef (transforms t) trans
+ writeIORef (headers t) info
+ _ -> throw $ TransportExn "HeaderTransport: Invalid header" TE_UNKNOWN
+
+
+iw16 :: Int16 -> Word16
+iw16 = fromIntegral
+iw32 :: Int32 -> Word32
+iw32 = fromIntegral
+wi16 :: Word16 -> Int16
+wi16 = fromIntegral
+wi32 :: Word32 -> Int32
+wi32 = fromIntegral
+
+parseHeader :: P.Parser (ProtocolType, [TransformType], [(String, String)])
+parseHeader = do
+ protocolType <- toProtocolType <$> parseVarint wi16
+ numTrans <- fromIntegral <$> parseVarint wi16
+ trans <- replicateM numTrans parseTransform
+ info <- parseInfo
+ return (protocolType, trans, info)
+
+toProtocolType :: Int16 -> ProtocolType
+toProtocolType 0 = TBinary
+toProtocolType 1 = TJSON
+toProtocolType 2 = TCompact
+
+fromProtocolType :: ProtocolType -> Int16
+fromProtocolType TBinary = 0
+fromProtocolType TJSON = 1
+fromProtocolType TCompact = 2
+
+parseTransform :: P.Parser TransformType
+parseTransform = toTransportType <$> parseVarint wi16
+
+parseInfo :: P.Parser [(String, String)]
+parseInfo = do
+ n <- P.eitherP P.endOfInput (parseVarint wi32)
+ case n of
+ Left _ -> return []
+ Right n0 ->
+ replicateM (fromIntegral n0) $ do
+ klen <- parseVarint wi16
+ k <- P.take $ fromIntegral klen
+ vlen <- parseVarint wi16
+ v <- P.take $ fromIntegral vlen
+ return (C.unpack k, C.unpack v)
+
+parseString :: P.Parser BS.ByteString
+parseString = parseVarint wi32 >>= (P.take . fromIntegral)
+
+buildHeader :: HeaderTransport i o -> IO B.Builder
+buildHeader t = do
+ pType <- readIORef $ protocolType t
+ let pId = buildVarint $ iw16 $ fromProtocolType pType
+ let headerContent = pId <> (buildTransforms t) <> (buildInfo t)
+ let len = fromIntegral $ LBS.length $ B.toLazyByteString headerContent
+ -- TODO: length limit check
+ let padding = mconcat $ replicate (mod len 4) $ B.word8 0
+ let codedLen = B.int16BE (fromIntegral $ (quot (len - 1) 4) + 1)
+ let flags = 0
+ let seqNum = 0
+ return $ B.int16BE 0x0fff <> B.int16BE flags <> B.int32BE seqNum <> codedLen <> headerContent <> padding
+
+buildTransforms :: HeaderTransport i o -> B.Builder
+-- TODO: check length limit
+buildTransforms t =
+ let trans = writeTransforms t in
+ (buildVarint $ iw16 $ fromIntegral $ length trans) <>
+ (mconcat $ map (buildVarint . iw16 . fromTransportType) trans)
+
+buildInfo :: HeaderTransport i o -> B.Builder
+buildInfo t =
+ let h = Map.assocs $ writeHeaders t in
+ -- TODO: check length limit
+ case length h of
+ 0 -> mempty
+ len -> (buildVarint $ iw16 $ fromIntegral $ len) <> (mconcat $ map buildInfoEntry h)
+ where
+ buildInfoEntry (k, v) = buildVarStr k <> buildVarStr v
+ -- TODO: check length limit
+ buildVarStr s = (buildVarint $ iw16 $ fromIntegral $ length s) <> B.string8 s
+
+tResetProtocol :: (Transport i, Transport o) => HeaderTransport i o -> IO Bool
+tResetProtocol t = do
+ rBuf <- readIORef $ readBuffer t
+ writeIORef (clientType t) HeaderClient
+ readFrame t
+
+tSetProtocol :: (Transport i, Transport o) => HeaderTransport i o -> ProtocolType -> IO ()
+tSetProtocol t = writeIORef (protocolType t)
+
+transform :: HeaderTransport i o -> LBS.ByteString -> LBS.ByteString
+transform t bs =
+ foldr applyTransform bs $ writeTransforms t
+ where
+ -- applyTransform bs ZlibTransform =
+ -- throw $ TransportExn "HeaderTransport: not implemented: ZlibTransform " TE_UNKNOWN
+ applyTransform bs _ =
+ throw $ TransportExn "HeaderTransport: Unknown transform" TE_UNKNOWN
+
+untransform :: HeaderTransport i o -> BS.ByteString -> IO BS.ByteString
+untransform t bs = do
+ trans <- readIORef $ transforms t
+ return $ foldl unapplyTransform bs trans
+ where
+ -- unapplyTransform bs ZlibTransform =
+ -- throw $ TransportExn "HeaderTransport: not implemented: ZlibTransform " TE_UNKNOWN
+ unapplyTransform bs _ =
+ throw $ TransportExn "HeaderTransport: Unknown transform" TE_UNKNOWN
+
+instance (Transport i, Transport o) => Transport (HeaderTransport i o) where
+ tIsOpen t = do
+ tIsOpen (inTrans t)
+ tIsOpen (outTrans t)
+
+ tClose t = do
+ tClose(outTrans t)
+ tClose(inTrans t)
+
+ tRead t len = do
+ rBuf <- readIORef $ readBuffer t
+ if not $ LBS.null rBuf
+ then do
+ let (consumed, remain) = LBS.splitAt (fromIntegral len) rBuf
+ writeIORef (readBuffer t) remain
+ return consumed
+ else do
+ framed <- isFramed t
+ if not framed
+ then tRead (inTrans t) len
+ else do
+ ok <- readFrame t
+ if ok
+ then tRead t len
+ else return LBS.empty
+
+ tPeek t = do
+ rBuf <- readIORef (readBuffer t)
+ if not $ LBS.null rBuf
+ then return $ Just $ LBS.head rBuf
+ else do
+ framed <- isFramed t
+ if not framed
+ then tPeek (inTrans t)
+ else do
+ ok <- readFrame t
+ if ok
+ then tPeek t
+ else return Nothing
+
+ tWrite t buf = do
+ let wBuf = writeBuffer t
+ framed <- isFramed t
+ if framed
+ then modifyIORef wBuf (<> B.lazyByteString buf)
+ else
+ -- TODO: what should we do when switched to unframed in the middle ?
+ tWrite(outTrans t) buf
+
+ tFlush t = do
+ cType <- readIORef $ clientType t
+ case cType of
+ Unframed -> tFlush $ outTrans t
+ Framed -> flushBuffer t id mempty
+ HeaderClient -> buildHeader t >>= flushBuffer t (transform t)
+ where
+ flushBuffer t f header = do
+ wBuf <- readIORef $ writeBuffer t
+ writeIORef (writeBuffer t) mempty
+ let payload = B.toLazyByteString (header <> wBuf)
+ tWrite (outTrans t) $ Binary.encode (fromIntegral $ LBS.length payload :: Int32)
+ tWrite (outTrans t) $ f payload
+ tFlush (outTrans t)
diff --git a/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/HttpClient.hs b/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/HttpClient.hs
new file mode 100644
index 000000000..edeb3208d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/HttpClient.hs
@@ -0,0 +1,101 @@
+{-# LANGUAGE FlexibleInstances #-}
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+module Thrift.Transport.HttpClient
+ ( module Thrift.Transport
+ , HttpClient (..)
+ , openHttpClient
+) where
+
+import Thrift.Transport
+import Thrift.Transport.IOBuffer
+import Network.URI
+import Network.HTTP hiding (port, host)
+
+import Data.Maybe (fromJust)
+import Data.Monoid (mempty)
+import Control.Exception (throw)
+import qualified Data.ByteString.Lazy as LBS
+
+
+-- | 'HttpClient', or THttpClient implements the Thrift Transport
+-- | Layer over http or https.
+data HttpClient =
+ HttpClient {
+ hstream :: HandleStream LBS.ByteString,
+ uri :: URI,
+ writeBuffer :: WriteBuffer,
+ readBuffer :: ReadBuffer
+ }
+
+uriAuth :: URI -> URIAuth
+uriAuth = fromJust . uriAuthority
+
+host :: URI -> String
+host = uriRegName . uriAuth
+
+port :: URI -> Int
+port uri_ =
+ if portStr == mempty then
+ httpPort
+ else
+ read portStr
+ where
+ portStr = dropWhile (== ':') $ uriPort $ uriAuth uri_
+ httpPort = 80
+
+-- | Use 'openHttpClient' to create an HttpClient connected to @uri@
+openHttpClient :: URI -> IO HttpClient
+openHttpClient uri_ = do
+ stream <- openTCPConnection (host uri_) (port uri_)
+ wbuf <- newWriteBuffer
+ rbuf <- newReadBuffer
+ return $ HttpClient stream uri_ wbuf rbuf
+
+instance Transport HttpClient where
+
+ tClose = close . hstream
+
+ tPeek = peekBuf . readBuffer
+
+ tRead = readBuf . readBuffer
+
+ tWrite = writeBuf . writeBuffer
+
+ tFlush hclient = do
+ body <- flushBuf $ writeBuffer hclient
+ let request = Request {
+ rqURI = uri hclient,
+ rqHeaders = [
+ mkHeader HdrContentType "application/x-thrift",
+ mkHeader HdrContentLength $ show $ LBS.length body],
+ rqMethod = POST,
+ rqBody = body
+ }
+
+ res <- sendHTTP (hstream hclient) request
+ case res of
+ Right response ->
+ fillBuf (readBuffer hclient) (rspBody response)
+ Left _ ->
+ throw $ TransportExn "THttpConnection: HTTP failure from server" TE_UNKNOWN
+ return ()
+
+ tIsOpen _ = return True
diff --git a/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/IOBuffer.hs b/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/IOBuffer.hs
new file mode 100644
index 000000000..7ebd7d899
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/IOBuffer.hs
@@ -0,0 +1,69 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+module Thrift.Transport.IOBuffer
+ ( WriteBuffer
+ , newWriteBuffer
+ , writeBuf
+ , flushBuf
+ , ReadBuffer
+ , newReadBuffer
+ , fillBuf
+ , readBuf
+ , peekBuf
+ ) where
+
+import Data.ByteString.Lazy.Builder
+import Data.Functor
+import Data.IORef
+import Data.Monoid
+import Data.Word
+
+import qualified Data.ByteString.Lazy as LBS
+
+type WriteBuffer = IORef Builder
+type ReadBuffer = IORef LBS.ByteString
+
+newWriteBuffer :: IO WriteBuffer
+newWriteBuffer = newIORef mempty
+
+writeBuf :: WriteBuffer -> LBS.ByteString -> IO ()
+writeBuf w s = modifyIORef w ( <> lazyByteString s)
+
+flushBuf :: WriteBuffer -> IO LBS.ByteString
+flushBuf w = do
+ buf <- readIORef w
+ writeIORef w mempty
+ return $ toLazyByteString buf
+
+newReadBuffer :: IO ReadBuffer
+newReadBuffer = newIORef mempty
+
+fillBuf :: ReadBuffer -> LBS.ByteString -> IO ()
+fillBuf = writeIORef
+
+readBuf :: ReadBuffer -> Int -> IO LBS.ByteString
+readBuf r n = do
+ bs <- readIORef r
+ let (hd, tl) = LBS.splitAt (fromIntegral n) bs
+ writeIORef r tl
+ return hd
+
+peekBuf :: ReadBuffer -> IO (Maybe Word8)
+peekBuf r = (fmap fst . LBS.uncons) <$> readIORef r
diff --git a/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Memory.hs b/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Memory.hs
new file mode 100644
index 000000000..1c93af695
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/src/Thrift/Transport/Memory.hs
@@ -0,0 +1,77 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+module Thrift.Transport.Memory
+ ( openMemoryBuffer
+ , MemoryBuffer(..)
+ ) where
+
+import Data.ByteString.Lazy.Builder
+import Data.Functor
+import Data.IORef
+import Data.Monoid
+import qualified Data.ByteString.Lazy as LBS
+
+import Thrift.Transport
+
+
+data MemoryBuffer = MemoryBuffer {
+ writeBuffer :: IORef Builder,
+ readBuffer :: IORef LBS.ByteString
+}
+
+openMemoryBuffer :: IO MemoryBuffer
+openMemoryBuffer = do
+ wbuf <- newIORef mempty
+ rbuf <- newIORef mempty
+ return MemoryBuffer {
+ writeBuffer = wbuf,
+ readBuffer = rbuf
+ }
+
+instance Transport MemoryBuffer where
+ tIsOpen = const $ return False
+ tClose = const $ return ()
+ tFlush trans = do
+ let wBuf = writeBuffer trans
+ wb <- readIORef wBuf
+ modifyIORef (readBuffer trans) $ \rb -> mappend rb $ toLazyByteString wb
+ writeIORef wBuf mempty
+
+ tRead _ 0 = return mempty
+ tRead trans n = do
+ let rbuf = readBuffer trans
+ rb <- readIORef rbuf
+ let len = fromIntegral $ LBS.length rb
+ if len == 0
+ then do
+ tFlush trans
+ rb2 <- readIORef (readBuffer trans)
+ if (fromIntegral $ LBS.length rb2) == 0
+ then return mempty
+ else tRead trans n
+ else do
+ let (ret, remain) = LBS.splitAt (fromIntegral n) rb
+ writeIORef rbuf remain
+ return ret
+
+ tPeek trans = (fmap fst . LBS.uncons) <$> readIORef (readBuffer trans)
+
+ tWrite trans v = do
+ modifyIORef (writeBuffer trans) (<> lazyByteString v)
diff --git a/src/jaegertracing/thrift/lib/hs/src/Thrift/Types.hs b/src/jaegertracing/thrift/lib/hs/src/Thrift/Types.hs
new file mode 100644
index 000000000..2a200253d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/src/Thrift/Types.hs
@@ -0,0 +1,130 @@
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module Thrift.Types where
+
+import Data.Foldable (foldl')
+import Data.Hashable ( Hashable, hashWithSalt )
+import Data.Int
+import Test.QuickCheck.Arbitrary
+import Test.QuickCheck.Gen (elements)
+import Data.Text.Lazy (Text)
+import qualified Data.ByteString.Lazy as LBS
+import qualified Data.HashMap.Strict as Map
+import qualified Data.HashSet as Set
+import qualified Data.Vector as Vector
+
+instance (Hashable a) => Hashable (Vector.Vector a) where
+ hashWithSalt = Vector.foldl' hashWithSalt
+
+
+type TypeMap = Map.HashMap Int16 (Text, ThriftType)
+
+data ThriftVal = TStruct (Map.HashMap Int16 (Text, ThriftVal))
+ | TMap ThriftType ThriftType [(ThriftVal, ThriftVal)]
+ | TList ThriftType [ThriftVal]
+ | TSet ThriftType [ThriftVal]
+ | TBool Bool
+ | TByte Int8
+ | TI16 Int16
+ | TI32 Int32
+ | TI64 Int64
+ | TString LBS.ByteString
+ | TBinary LBS.ByteString
+ | TDouble Double
+ deriving (Eq, Show)
+
+-- Information is needed here for collection types (ie T_STRUCT, T_MAP,
+-- T_LIST, and T_SET) so that we know what types those collections are
+-- parameterized by. In most protocols, this cannot be discerned directly
+-- from the data being read.
+data ThriftType
+ = T_STOP
+ | T_VOID
+ | T_BOOL
+ | T_BYTE
+ | T_DOUBLE
+ | T_I16
+ | T_I32
+ | T_I64
+ | T_STRING
+ | T_BINARY
+ | T_STRUCT TypeMap
+ | T_MAP ThriftType ThriftType
+ | T_SET ThriftType
+ | T_LIST ThriftType
+ deriving ( Eq, Show )
+
+-- NOTE: when using toEnum information about parametized types is NOT preserved.
+-- This design choice is consistent woth the Thrift implementation in other
+-- languages
+instance Enum ThriftType where
+ fromEnum T_STOP = 0
+ fromEnum T_VOID = 1
+ fromEnum T_BOOL = 2
+ fromEnum T_BYTE = 3
+ fromEnum T_DOUBLE = 4
+ fromEnum T_I16 = 6
+ fromEnum T_I32 = 8
+ fromEnum T_I64 = 10
+ fromEnum T_STRING = 11
+ fromEnum T_BINARY = 11
+ fromEnum (T_STRUCT _) = 12
+ fromEnum (T_MAP _ _) = 13
+ fromEnum (T_SET _) = 14
+ fromEnum (T_LIST _) = 15
+
+ toEnum 0 = T_STOP
+ toEnum 1 = T_VOID
+ toEnum 2 = T_BOOL
+ toEnum 3 = T_BYTE
+ toEnum 4 = T_DOUBLE
+ toEnum 6 = T_I16
+ toEnum 8 = T_I32
+ toEnum 10 = T_I64
+ toEnum 11 = T_STRING
+ -- toEnum 11 = T_BINARY
+ toEnum 12 = T_STRUCT Map.empty
+ toEnum 13 = T_MAP T_VOID T_VOID
+ toEnum 14 = T_SET T_VOID
+ toEnum 15 = T_LIST T_VOID
+ toEnum t = error $ "Invalid ThriftType " ++ show t
+
+data MessageType
+ = M_CALL
+ | M_REPLY
+ | M_EXCEPTION
+ | M_ONEWAY
+ deriving ( Eq, Show )
+
+instance Enum MessageType where
+ fromEnum M_CALL = 1
+ fromEnum M_REPLY = 2
+ fromEnum M_EXCEPTION = 3
+ fromEnum M_ONEWAY = 4
+
+ toEnum 1 = M_CALL
+ toEnum 2 = M_REPLY
+ toEnum 3 = M_EXCEPTION
+ toEnum 4 = M_ONEWAY
+ toEnum t = error $ "Invalid MessageType " ++ show t
+
+instance Arbitrary MessageType where
+ arbitrary = elements [M_CALL, M_REPLY, M_EXCEPTION, M_ONEWAY]
diff --git a/src/jaegertracing/thrift/lib/hs/test/BinarySpec.hs b/src/jaegertracing/thrift/lib/hs/test/BinarySpec.hs
new file mode 100644
index 000000000..d692fabe3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/test/BinarySpec.hs
@@ -0,0 +1,91 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+module BinarySpec where
+
+import Test.Hspec
+import Test.Hspec.QuickCheck (prop)
+
+import qualified Data.ByteString.Lazy as LBS
+import qualified Data.ByteString.Lazy.Char8 as C
+
+import Thrift.Types
+import Thrift.Transport
+import Thrift.Transport.Memory
+import Thrift.Protocol
+import Thrift.Protocol.Binary
+
+spec :: Spec
+spec = do
+ describe "BinaryProtocol" $ do
+ describe "double" $ do
+ it "writes in big endian order" $ do
+ let val = 2 ** 53
+ trans <- openMemoryBuffer
+ let proto = BinaryProtocol trans
+ writeVal proto (TDouble val)
+ bin <- tRead trans 8
+ (LBS.unpack bin) `shouldBe`[67, 64, 0, 0, 0, 0, 0, 0]
+
+ it "reads in big endian order" $ do
+ let bin = LBS.pack [67, 64, 0, 0, 0, 0, 0, 0]
+ trans <- openMemoryBuffer
+ let proto = BinaryProtocol trans
+ tWrite trans bin
+ val <- readVal proto T_DOUBLE
+ val `shouldBe` (TDouble $ 2 ** 53)
+
+ prop "round trip" $ \val -> do
+ trans <- openMemoryBuffer
+ let proto = BinaryProtocol trans
+ writeVal proto $ TDouble val
+ val2 <- readVal proto T_DOUBLE
+ val2 `shouldBe` (TDouble val)
+
+ describe "string" $ do
+ it "writes" $ do
+ let val = C.pack "aaa"
+ trans <- openMemoryBuffer
+ let proto = BinaryProtocol trans
+ writeVal proto (TString val)
+ bin <- tRead trans 7
+ (LBS.unpack bin) `shouldBe` [0, 0, 0, 3, 97, 97, 97]
+
+ describe "binary" $ do
+ it "writes" $ do
+ trans <- openMemoryBuffer
+ let proto = BinaryProtocol trans
+ writeVal proto (TBinary $ LBS.pack [42, 43, 44])
+ bin <- tRead trans 100
+ (LBS.unpack bin) `shouldBe` [0, 0, 0, 3, 42, 43, 44]
+
+ it "reads" $ do
+ trans <- openMemoryBuffer
+ let proto = BinaryProtocol trans
+ tWrite trans $ LBS.pack [0, 0, 0, 3, 42, 43, 44]
+ val <- readVal proto (T_BINARY)
+ val `shouldBe` (TBinary $ LBS.pack [42, 43, 44])
+
+ prop "round trip" $ \val -> do
+ trans <- openMemoryBuffer
+ let proto = BinaryProtocol trans
+ writeVal proto (TBinary $ LBS.pack val)
+ val2 <- readVal proto (T_BINARY)
+ val2 `shouldBe` (TBinary $ LBS.pack val)
+
diff --git a/src/jaegertracing/thrift/lib/hs/test/CompactSpec.hs b/src/jaegertracing/thrift/lib/hs/test/CompactSpec.hs
new file mode 100644
index 000000000..5540e7b5e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/test/CompactSpec.hs
@@ -0,0 +1,81 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+module CompactSpec where
+
+import Test.Hspec
+import Test.Hspec.QuickCheck (prop)
+
+import qualified Data.ByteString.Lazy as LBS
+
+import Thrift.Types
+import Thrift.Transport
+import Thrift.Transport.Memory
+import Thrift.Protocol
+import Thrift.Protocol.Compact
+
+spec :: Spec
+spec = do
+ describe "CompactProtocol" $ do
+ describe "double" $ do
+ it "writes in little endian order" $ do
+ let val = 2 ** 53
+ trans <- openMemoryBuffer
+ let proto = CompactProtocol trans
+ writeVal proto (TDouble val)
+ bin <- tReadAll trans 8
+ (LBS.unpack bin) `shouldBe`[0, 0, 0, 0, 0, 0, 64, 67]
+
+ it "reads in little endian order" $ do
+ let bin = LBS.pack [0, 0, 0, 0, 0, 0, 64, 67]
+ trans <- openMemoryBuffer
+ let proto = CompactProtocol trans
+ tWrite trans bin
+ val <- readVal proto T_DOUBLE
+ val `shouldBe` (TDouble $ 2 ** 53)
+
+ prop "round trip" $ \val -> do
+ trans <- openMemoryBuffer
+ let proto = CompactProtocol trans
+ writeVal proto $ TDouble val
+ val2 <- readVal proto T_DOUBLE
+ val2 `shouldBe` (TDouble val)
+
+ describe "binary" $ do
+ it "writes" $ do
+ trans <- openMemoryBuffer
+ let proto = CompactProtocol trans
+ writeVal proto (TBinary $ LBS.pack [42, 43, 44])
+ bin <- tRead trans 100
+ (LBS.unpack bin) `shouldBe` [3, 42, 43, 44]
+
+ it "reads" $ do
+ trans <- openMemoryBuffer
+ let proto = CompactProtocol trans
+ tWrite trans $ LBS.pack [3, 42, 43, 44]
+ val <- readVal proto (T_BINARY)
+ val `shouldBe` (TBinary $ LBS.pack [42, 43, 44])
+
+ prop "round trip" $ \val -> do
+ trans <- openMemoryBuffer
+ let proto = CompactProtocol trans
+ writeVal proto (TBinary $ LBS.pack val)
+ val2 <- readVal proto (T_BINARY)
+ val2 `shouldBe` (TBinary $ LBS.pack val)
+
diff --git a/src/jaegertracing/thrift/lib/hs/test/JSONSpec.hs b/src/jaegertracing/thrift/lib/hs/test/JSONSpec.hs
new file mode 100644
index 000000000..022c8265e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/test/JSONSpec.hs
@@ -0,0 +1,225 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+module JSONSpec where
+
+import Test.Hspec
+import Test.Hspec.QuickCheck (prop)
+
+import qualified Data.ByteString.Lazy as LBS
+import qualified Data.ByteString.Lazy.Char8 as C
+
+import Thrift.Types
+import Thrift.Transport
+import Thrift.Transport.Memory
+import Thrift.Protocol
+import Thrift.Protocol.JSON
+
+tString :: [Char] -> ThriftVal
+tString = TString . C.pack
+
+spec :: Spec
+spec = do
+ describe "JSONProtocol" $ do
+ describe "bool" $ do
+ it "writes true as 1" $ do
+ let val = True
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ writeVal proto (TBool val)
+ bin <-tRead trans 100
+ (C.unpack bin) `shouldBe` ['1']
+
+ it "writes false as 0" $ do
+ let val = False
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ writeVal proto (TBool val)
+ bin <- tRead trans 100
+ (C.unpack bin) `shouldBe` ['0']
+
+ prop "round trip" $ \val -> do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ writeVal proto $ TBool val
+ val2 <- readVal proto T_BOOL
+ val2 `shouldBe` (TBool val)
+
+ describe "string" $ do
+ it "writes" $ do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ writeVal proto (TString $ C.pack "\"a")
+ bin <- tRead trans 100
+ (C.unpack bin) `shouldBe` "\"\\\"a\""
+
+ it "reads" $ do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ tWrite trans $ C.pack "\"\\\"a\""
+ val <- readVal proto (T_STRING)
+ val `shouldBe` (TString $ C.pack "\"a")
+
+ prop "round trip" $ \val -> do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ writeVal proto (TString $ C.pack val)
+ val2 <- readVal proto (T_STRING)
+ val2 `shouldBe` (TString $ C.pack val)
+
+ describe "binary" $ do
+ it "writes with padding" $ do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ writeVal proto (TBinary $ LBS.pack [1])
+ bin <- tRead trans 100
+ (C.unpack bin) `shouldBe` "\"AQ==\""
+
+ it "reads with padding" $ do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ tWrite trans $ C.pack "\"AQ==\""
+ val <- readVal proto (T_BINARY)
+ val `shouldBe` (TBinary $ LBS.pack [1])
+
+ it "reads without padding" $ do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ tWrite trans $ C.pack "\"AQ\""
+ val <- readVal proto (T_BINARY)
+ val `shouldBe` (TBinary $ LBS.pack [1])
+
+ prop "round trip" $ \val -> do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ writeVal proto (TBinary $ LBS.pack val)
+ val2 <- readVal proto (T_BINARY)
+ val2 `shouldBe` (TBinary $ LBS.pack val)
+
+ describe "list" $ do
+ it "writes empty list" $ do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ writeVal proto (TList T_BYTE [])
+ bin <- tRead trans 100
+ (C.unpack bin) `shouldBe` "[\"i8\",0]"
+
+ it "reads empty" $ do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ tWrite trans (C.pack "[\"i8\",0]")
+ val <- readVal proto (T_LIST T_BYTE)
+ val `shouldBe` (TList T_BYTE [])
+
+ it "writes single element" $ do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ writeVal proto (TList T_BYTE [TByte 0])
+ bin <- tRead trans 100
+ (C.unpack bin) `shouldBe` "[\"i8\",1,0]"
+
+ it "reads single element" $ do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ tWrite trans (C.pack "[\"i8\",1,0]")
+ val <- readVal proto (T_LIST T_BYTE)
+ val `shouldBe` (TList T_BYTE [TByte 0])
+
+ it "reads elements" $ do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ tWrite trans (C.pack "[\"i8\",2,42, 43]")
+ val <- readVal proto (T_LIST T_BYTE)
+ val `shouldBe` (TList T_BYTE [TByte 42, TByte 43])
+
+ prop "round trip" $ \val -> do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ writeVal proto $ (TList T_STRING $ map tString val)
+ val2 <- readVal proto $ T_LIST T_STRING
+ val2 `shouldBe` (TList T_STRING $ map tString val)
+
+ describe "set" $ do
+ it "writes empty" $ do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ writeVal proto (TSet T_BYTE [])
+ bin <- tRead trans 100
+ (C.unpack bin) `shouldBe` "[\"i8\",0]"
+
+ it "reads empty" $ do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ tWrite trans (C.pack "[\"i8\",0]")
+ val <- readVal proto (T_SET T_BYTE)
+ val `shouldBe` (TSet T_BYTE [])
+
+ it "reads single element" $ do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ tWrite trans (C.pack "[\"i8\",1,0]")
+ val <- readVal proto (T_SET T_BYTE)
+ val `shouldBe` (TSet T_BYTE [TByte 0])
+
+ it "reads elements" $ do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ tWrite trans (C.pack "[\"i8\",2,42, 43]")
+ val <- readVal proto (T_SET T_BYTE)
+ val `shouldBe` (TSet T_BYTE [TByte 42, TByte 43])
+
+ prop "round trip" $ \val -> do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ writeVal proto $ (TSet T_STRING $ map tString val)
+ val2 <- readVal proto $ T_SET T_STRING
+ val2 `shouldBe` (TSet T_STRING $ map tString val)
+
+ describe "map" $ do
+ it "writes empty" $ do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ writeVal proto (TMap T_BYTE T_BYTE [])
+ bin <- tRead trans 100
+ (C.unpack bin) `shouldBe`"[\"i8\",\"i8\",0,{}]"
+
+ it "reads empty" $ do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ tWrite trans (C.pack "[\"i8\",\"i8\",0,{}]")
+ val <- readVal proto (T_MAP T_BYTE T_BYTE)
+ val `shouldBe` (TMap T_BYTE T_BYTE [])
+
+ it "reads string-string" $ do
+ let bin = "[\"str\",\"str\",2,{\"a\":\"2\",\"b\":\"blah\"}]"
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ tWrite trans (C.pack bin)
+ val <- readVal proto (T_MAP T_STRING T_STRING)
+ val`shouldBe` (TMap T_STRING T_STRING [(tString "a", tString "2"), (tString "b", tString "blah")])
+
+ prop "round trip" $ \val -> do
+ trans <- openMemoryBuffer
+ let proto = JSONProtocol trans
+ writeVal proto $ (TMap T_STRING T_STRING $ map toKV val)
+ val2 <- readVal proto $ T_MAP T_STRING T_STRING
+ val2 `shouldBe` (TMap T_STRING T_STRING $ map toKV val)
+ where
+ toKV v = (tString v, tString v)
+
diff --git a/src/jaegertracing/thrift/lib/hs/test/Spec.hs b/src/jaegertracing/thrift/lib/hs/test/Spec.hs
new file mode 100644
index 000000000..7ec9a990b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/test/Spec.hs
@@ -0,0 +1,38 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+-- Our CI does not work well with auto discover.
+-- Need to add build-time PATH variable to hspec-discover dir from CMake
+-- or install hspec system-wide for the following to work.
+-- {-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+
+import Test.Hspec
+
+import qualified BinarySpec
+import qualified CompactSpec
+import qualified JSONSpec
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec = do
+ describe "Binary" BinarySpec.spec
+ describe "Compact" CompactSpec.spec
+ describe "JSON" JSONSpec.spec
diff --git a/src/jaegertracing/thrift/lib/hs/thrift.cabal b/src/jaegertracing/thrift/lib/hs/thrift.cabal
new file mode 100644
index 000000000..dd30d89f1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/hs/thrift.cabal
@@ -0,0 +1,84 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+Name: thrift
+Version: 0.13.0
+Cabal-Version: 1.24
+License: Apache
+Category: Foreign
+Build-Type: Simple
+Synopsis: Haskell bindings for the Apache Thrift RPC system
+Homepage: http://thrift.apache.org
+Bug-Reports: https://issues.apache.org/jira/browse/THRIFT
+Maintainer: dev@thrift.apache.org
+License-File: LICENSE
+
+Description:
+ Haskell bindings for the Apache Thrift RPC system. Requires the use of the thrift code generator.
+
+flag network-uri
+ description: Get Network.URI from the network-uri package
+ default: True
+
+Library
+ Hs-Source-Dirs:
+ src
+ Build-Depends:
+ base >= 4, base < 5, containers, ghc-prim, attoparsec, binary, bytestring >= 0.10, base64-bytestring, hashable, HTTP, text, hspec-core > 2.4.0, unordered-containers >= 0.2.6, vector >= 0.10.12.2, QuickCheck >= 2.8.2, split
+ if flag(network-uri)
+ build-depends: network-uri >= 2.6, network >= 2.6 && < 3.0
+ else
+ build-depends: network < 2.6
+ Exposed-Modules:
+ Thrift,
+ Thrift.Arbitraries
+ Thrift.Protocol,
+ Thrift.Protocol.Header,
+ Thrift.Protocol.Binary,
+ Thrift.Protocol.Compact,
+ Thrift.Protocol.JSON,
+ Thrift.Server,
+ Thrift.Transport,
+ Thrift.Transport.Empty,
+ Thrift.Transport.Framed,
+ Thrift.Transport.Handle,
+ Thrift.Transport.Header,
+ Thrift.Transport.HttpClient,
+ Thrift.Transport.IOBuffer,
+ Thrift.Transport.Memory,
+ Thrift.Types
+ Default-Language: Haskell2010
+ Default-Extensions:
+ DeriveDataTypeable,
+ ExistentialQuantification,
+ FlexibleInstances,
+ KindSignatures,
+ MagicHash,
+ RankNTypes,
+ RecordWildCards,
+ ScopedTypeVariables,
+ TypeSynonymInstances
+
+Test-Suite spec
+ Type: exitcode-stdio-1.0
+ Hs-Source-Dirs: test
+ Ghc-Options: -Wall
+ main-is: Spec.hs
+ Build-Depends: base, thrift, hspec, QuickCheck >= 2.8.2, bytestring >= 0.10, unordered-containers >= 0.2.6
+ Default-Language: Haskell2010
diff --git a/src/jaegertracing/thrift/lib/java/CMakeLists.txt b/src/jaegertracing/thrift/lib/java/CMakeLists.txt
new file mode 100644
index 000000000..a67845aba
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/CMakeLists.txt
@@ -0,0 +1,94 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+if(ANDROID)
+
+ set(THRIFT_AAR outputs/aar/thrift-debug.aar outputs/aar/thrift-release.aar)
+ add_custom_command(
+ OUTPUT ${THRIFT_AAR}
+ COMMAND ${GRADLE_EXECUTABLE}
+ -p "${CMAKE_CURRENT_SOURCE_DIR}/android"
+ "-PbuildDir=${CMAKE_CURRENT_BINARY_DIR}/android/build" assemble
+ )
+ add_custom_target(thrift_aar ALL DEPENDS ${THRIFT_AAR})
+
+else()
+
+ if(IS_ABSOLUTE "${LIB_INSTALL_DIR}")
+ set(JAVA_INSTALL_DIR "${LIB_INSTALL_DIR}/java")
+ else()
+ set(JAVA_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/java")
+ endif()
+
+ if(IS_ABSOLUTE "${DOC_INSTALL_DIR}")
+ set(JAVA_DOC_INSTALL_DIR "${DOC_INSTALL_DIR}/java")
+ else()
+ set(JAVA_DOC_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${DOC_INSTALL_DIR}/java")
+ endif()
+
+ set(PRELEASE "true")
+ if (CMAKE_BUILD_TYPE MATCHES DEBUG)
+ set(PRELEASE "false")
+ endif ()
+
+ add_custom_target(ThriftJava ALL
+ COMMENT "Building Java library using Gradle Wrapper"
+ COMMAND ${GRADLEW_EXECUTABLE} ${GRADLE_OPTS} assemble
+ --console=plain --no-daemon
+ -Prelease=${PRELEASE}
+ -Pthrift.version=${thrift_VERSION}
+ "-Pbuild.dir=${CMAKE_CURRENT_BINARY_DIR}/build"
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+
+ # Enable publishing from CMake if the publishing information is provided
+ add_custom_target(MavenPublish
+ COMMENT "Publishing Java Library to Apache Maven staging"
+ COMMAND ${GRADLEW_EXECUTABLE} ${GRADLE_OPTS} clean uploadArchives
+ --console=plain --no-daemon
+ -Prelease=${PRELEASE}
+ -Pthrift.version=${thrift_VERSION}
+ "-Pbuild.dir=${CMAKE_CURRENT_BINARY_DIR}/build"
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+
+ # Hook the CMake install process to the results from make ALL.
+ # This works best when 'make all && sudo make install/fast' is used.
+ # Using slash to end the source location to avoid copying the directory path.
+ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build/libs/
+ DESTINATION ${JAVA_INSTALL_DIR}
+ FILES_MATCHING PATTERN "libthrift-${thrift_VERSION}.jar")
+ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build/deps/
+ DESTINATION ${JAVA_INSTALL_DIR}
+ FILES_MATCHING PATTERN "*.jar")
+ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build/docs/javadoc/
+ DESTINATION ${JAVA_DOC_INSTALL_DIR})
+
+ if(BUILD_TESTING)
+ add_test(NAME JavaTest
+ COMMAND ${GRADLEW_EXECUTABLE} ${GRADLE_OPTS} test
+ --console=plain --no-daemon
+ -Prelease=${PRELEASE}
+ -Pthrift.version=${thrift_VERSION}
+ "-Pbuild.dir=${CMAKE_CURRENT_BINARY_DIR}/build"
+ "-Pthrift.compiler=${THRIFT_COMPILER}"
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+ endif()
+
+endif()
diff --git a/src/jaegertracing/thrift/lib/java/Makefile.am b/src/jaegertracing/thrift/lib/java/Makefile.am
new file mode 100644
index 000000000..65981ca68
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/Makefile.am
@@ -0,0 +1,72 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+export CLASSPATH
+
+all-local:
+ ./gradlew $(GRADLE_OPTS) assemble \
+ -Prelease=true \
+ -Pthrift.version=$(PACKAGE_VERSION) \
+ --console=plain
+
+install-exec-hook:
+ ./gradlew $(GRADLE_OPTS) install \
+ -Prelease=true \
+ -Pinstall.path=$(DESTDIR)$(JAVA_PREFIX) \
+ -Pinstall.javadoc.path=$(DESTDIR)$(docdir)/java \
+ -Pthrift.version=$(PACKAGE_VERSION) \
+ --console=plain
+
+clean-local:
+ ./gradlew $(GRADLE_OPTS) clean --console=plain
+
+precross: $(THRIFT)
+ ./gradlew $(GRADLE_OPTS) shadowJar \
+ -Prelease=true \
+ -Pthrift.version=$(PACKAGE_VERSION) \
+ -Pthrift.compiler=$(THRIFT) \
+ --console=plain
+
+check-local: $(THRIFT)
+ ./gradlew $(GRADLE_OPTS) test \
+ -Prelease=true \
+ -Pthrift.version=$(PACKAGE_VERSION) \
+ -Pthrift.compiler=$(THRIFT) \
+ --console=plain
+
+maven-publish:
+ ./gradlew $(GRADLE_OPTS) uploadArchives \
+ -Prelease=true \
+ -Pthrift.version=$(PACKAGE_VERSION) \
+ --console=plain
+
+EXTRA_DIST = \
+ build.gradle \
+ gradle.properties \
+ settings.gradle \
+ gradle \
+ gradlew \
+ gradlew.bat \
+ CMakeLists.txt \
+ coding_standards.md \
+ android \
+ src \
+ test \
+ code_quality_tools \
+ README.md
diff --git a/src/jaegertracing/thrift/lib/java/README.md b/src/jaegertracing/thrift/lib/java/README.md
new file mode 100644
index 000000000..c8ec1c37c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/README.md
@@ -0,0 +1,188 @@
+Thrift Java Software Library
+
+License
+=======
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+Building and installing from source
+===================================
+
+When using a CMake build from the source distribution on Linux the
+easiest way to build and install is this simple command line:
+
+ make all && sudo make install/fast
+
+It is important to use the install/fast option to eliminate
+the automatic rebuild by dependency that causes issues because
+the build tooling is designed to work with cached files in the
+user home directory during the build process. Instead this builds
+the code in the expected local build tree and then uses CMake
+install code to copy to the target destination.
+
+Building Thrift with Gradle without CMake/Autoconf
+==================================================
+
+The Thrift Java source is not build using the GNU tools, but rather uses
+the Gradle build system, which tends to be predominant amongst Java
+developers.
+
+To compile the Java Thrift libraries, simply do the following:
+
+ ./gradlew
+
+Yep, that's easy. Look for libthrift-<version>.jar in the build/libs directory.
+
+The default build will run the unit tests which expect a usable
+Thrift compiler to exist on the system. You have two choices for
+that.
+
+* Build the Thrift executable from source at the default
+ location in the source tree. The project is configured
+ to look for it there.
+* Install the published binary distribution to have Thrift
+ executable in a known location and add the path to the
+ ~/.gradle/gradle.properties file using the property name
+ "thrift.compiler". For example this would set the path in
+ a Windows box if Thrift was installed under C:\Thrift
+
+ thrift.compiler=C:/Thrift/thrift.exe
+
+To just build the library without running unit tests you simply do this.
+
+ ./gradlew assemble
+
+To install the library in the local Maven repository location
+where other Maven or Gradle builds can reference it simply do this.
+
+ ./gradlew install
+
+The library will be placed in your home directory under .m2/repository
+
+To include Thrift in your applications simply add libthrift.jar to your
+classpath, or install if in your default system classpath of choice.
+
+
+Build Thrift behind a proxy:
+
+ ./gradlew -Dhttp.proxyHost=myproxyhost -Dhttp.proxyPort=8080 -Dhttp.proxyUser=thriftuser -Dhttp.proxyPassword=topsecret
+
+or via
+
+ ./configure --with-java GRADLE_OPTS='-Dhttp.proxyHost=myproxyhost -Dhttp.proxyPort=8080 -Dhttp.proxyUser=thriftuser -Dhttp.proxyPassword=topsecret'
+
+
+Unit Test HTML Reports
+======================
+
+The build will automatically generate an HTML Unit Test report. This can be found
+under build/reports/tests/test/index.html. It can be viewed with a browser
+directly from that location.
+
+
+Clover Code Coverage for Thrift
+===============================
+
+The build will optionally generate Clover Code coverage if the Gradle property
+`cloverEnabled=true` is set in ~/.gradle/gradle.properties or on the command line
+via `-PcloverEnabled=true`. The generated report can be found under the location
+build/reports/clover/html/index.html. It can be viewed with a browser
+directly from that location. Additionally, a PDF report is generated and is found
+under the location build/reports/clover/clover.pdf.
+
+The following command will build, unit test, and generate Clover reports:
+
+ ./gradlew -PcloverEnabled=true
+
+
+Publishing Maven Artifacts to Maven Central
+===========================================
+
+The Automake build generates a Makefile that provides the correct parameters
+when you run the build provided the configure.ac has been set with the correct
+version number. The Gradle build will receive the correct value for the build.
+The same applies to the CMake build, the value from the configure.ac file will
+be used if you execute these commands:
+
+ make maven-publish -- This is for an Automake Linux build
+ make MavenPublish -- This is for a CMake generated build
+
+The uploadArchives task in Gradle is preconfigured with all necessary details
+to sign and publish the artifacts from the build to the Apache Maven staging
+repository. The task requires the following externally provided properties to
+authenticate to the repository and sign the artifacts. The preferred approach
+is to create or edit the ~/.gradle/gradle.properties file and add the following
+properties to it.
+
+ # Signing key information for artifacts PGP signature (values are examples)
+ signing.keyId=24875D73
+ signing.password=secret
+ signing.secretKeyRingFile=/Users/me/.gnupg/secring.gpg
+
+ # Apache Maven staging repository user credentials
+ mavenUser=meMyselfAndI
+ mavenPassword=MySuperAwesomeSecretPassword
+
+NOTE: If you do not have a secring.gpg file, see the
+[gradle signing docs](https://docs.gradle.org/current/userguide/signing_plugin.html)
+for instructions on how to generate it.
+
+It is also possible to manually publish using the Gradle build directly.
+With the key information and credentials in place the following will generate
+if needed the build artifacts and proceed to publish the results.
+
+ ./gradlew -Prelease=true uploadArchives
+
+It is also possible to override the target repository for the Maven Publication
+by using a Gradle property, for example you can publish signed JAR files to your
+company internal server if you add this to the command line or in the
+~/.gradle/gradle.properties file. The URL below assumes a Nexus Repository.
+
+ maven-repository-url=https://my.company.com/service/local/staging/deploy/maven2
+
+Or the same on the command line:
+
+ ./gradlew -Pmaven-repository-url=https://my.company.com/service/local/staging/deploy/maven2 -Prelease=true -Pthrift.version=0.11.0 uploadArchives
+
+
+Dependencies
+============
+
+Gradle
+http://gradle.org/
+
+# Breaking Changes
+
+## 0.13.0
+
+* The signature of the 'process' method in TAsyncProcessor and TProcessor has
+changed to remove the boolean return type and instead rely on Exceptions.
+
+* Per THRIFT-4805, TSaslTransportException has been removed. The same condition
+is now covered by TTansportException, where `TTransportException.getType() == END_OF_FILE`.
+
+## 0.12.0
+
+The access modifier of the AutoExpandingBuffer class has been changed from
+public to default (package) and will no longer be accessible by third-party
+libraries.
+
+The access modifier of the ShortStack class has been changed from
+public to default (package) and will no longer be accessible by third-party
+libraries.
+
diff --git a/src/jaegertracing/thrift/lib/java/android/build.gradle b/src/jaegertracing/thrift/lib/java/android/build.gradle
new file mode 100644
index 000000000..c9984237b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/android/build.gradle
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.1"
+ useLibrary 'org.apache.http.legacy'
+ sourceSets.main.java {
+ srcDir '../src'
+ exclude 'org/apache/thrift/transport/TSaslClientTransport.java'
+ exclude 'org/apache/thrift/transport/TSaslServerTransport.java'
+ exclude 'org/apache/thrift/transport/TSaslTransport.java'
+ }
+}
+
+repositories {
+ mavenCentral()
+}
+dependencies {
+ compile 'org.slf4j:slf4j-api:1.7.13'
+ compile 'javax.servlet:servlet-api:2.5'
+ compile 'org.apache.httpcomponents:httpcore:4.4.4'
+}
+
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.5.0'
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/android/settings.gradle b/src/jaegertracing/thrift/lib/java/android/settings.gradle
new file mode 100644
index 000000000..75e97be41
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/android/settings.gradle
@@ -0,0 +1 @@
+rootProject.name='thrift'
diff --git a/src/jaegertracing/thrift/lib/java/android/src/main/AndroidManifest.xml b/src/jaegertracing/thrift/lib/java/android/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..43abdb758
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/android/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest package="org.apache.thrift">
+ <application />
+</manifest>
diff --git a/src/jaegertracing/thrift/lib/java/build.gradle b/src/jaegertracing/thrift/lib/java/build.gradle
new file mode 100644
index 000000000..5f0d2782b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/build.gradle
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Using the legacy plugin classpath for Clover so it can be loaded optionally
+buildscript {
+ repositories {
+ mavenCentral()
+ google()
+ jcenter()
+ gradlePluginPortal()
+ }
+
+ dependencies {
+ classpath 'com.bmuschko:gradle-clover-plugin:2.2.1'
+ }
+}
+
+plugins {
+ id 'java'
+ id 'maven'
+ id 'signing'
+ id 'com.github.johnrengelman.shadow' version '4.0.4'
+}
+
+description = 'Apache Thrift Java Library'
+
+defaultTasks 'build'
+
+// Version components for this project
+group = property('thrift.groupid')
+
+if (Boolean.parseBoolean(project.release)) {
+ version = property('thrift.version')
+} else {
+ version = property('thrift.version') + '-SNAPSHOT'
+}
+
+// Keeping the rest of the build logic in functional named scripts for clarity
+apply from: 'gradle/environment.gradle'
+apply from: 'gradle/sourceConfiguration.gradle'
+apply from: 'gradle/additionalArtifacts.gradle'
+apply from: 'gradle/generateTestThrift.gradle'
+apply from: 'gradle/unitTests.gradle'
+apply from: 'gradle/cloverCoverage.gradle'
+apply from: 'gradle/functionalTests.gradle'
+apply from: 'gradle/publishing.gradle'
+apply from: 'gradle/codeQualityChecks.gradle'
diff --git a/src/jaegertracing/thrift/lib/java/code_quality_tools/findbugs-filter.xml b/src/jaegertracing/thrift/lib/java/code_quality_tools/findbugs-filter.xml
new file mode 100644
index 000000000..8a93b0ad0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/code_quality_tools/findbugs-filter.xml
@@ -0,0 +1,51 @@
+<FindBugsFilter>
+ <!--
+ This file controls filtering some of the more obnoxious findbugs reports.
+ Some may be worthy of examination and resolution, others are too nit-picky.
+ -->
+ <Match>
+ <Or>
+ <!-- Filter the missing serialVersionUID errors -->
+ <Bug code="SnVI" />
+ <!-- Filter Malicious code vulnerability Warnings -->
+ <Bug code="EI,EI2" />
+ <!-- Filter Unchecked/unconfirmed cast -->
+ <Bug code="BC" />
+ <!-- Filter Should return a zero length array rather than null? -->
+ <Bug code="PZLA" />
+ <!-- Filter Redundant nullcheck -->
+ <Bug code="RCN" />
+ <!-- Filter Exception is caught when Exception is not thrown -->
+ <Bug code="REC" />
+ <!-- Filter Switch statement found where default case is missing -->
+ <Bug code="SF" />
+ <!-- Filter Unread public/protected field -->
+ <Bug code="UrF" />
+ <!-- Filter Field not initialized in constructor and dereferenced -->
+ <Bug code="UwF" />
+ </Or>
+ </Match>
+ <Match>
+ <!-- Filter method invokes System.exit(...), which shuts down the entire virtual machine -->
+ <Class name="org.apache.thrift.transport.TFileTransport" />
+ <Method name="printUsage" />
+ <Bug code="Dm" />
+ </Match>
+ <Match>
+ <!-- Filter method might ignore java.lang.Exception -->
+ <Class name="org.apache.thrift.transport.TSimpleFileTransport" />
+ <Method name="close" />
+ <Bug code="DE" />
+ </Match>
+ <Match>
+ <!-- Filter method might ignore java.lang.Exception -->
+ <Class name="org.apache.thrift.TNonblockingMultiFetchClient$MultiFetch" />
+ <Method name="run" />
+ <Bug code="DE" />
+ </Match>
+ <Match>
+ <!-- Filter Class defines non-transient non-serializable instance field -->
+ <Class name="org.apache.thrift.server.TServlet" />
+ <Bug code="Se" />
+ </Match>
+</FindBugsFilter>
diff --git a/src/jaegertracing/thrift/lib/java/coding_standards.md b/src/jaegertracing/thrift/lib/java/coding_standards.md
new file mode 100644
index 000000000..fa0390bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/coding_standards.md
@@ -0,0 +1 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
diff --git a/src/jaegertracing/thrift/lib/java/gradle.properties b/src/jaegertracing/thrift/lib/java/gradle.properties
new file mode 100644
index 000000000..081165910
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/gradle.properties
@@ -0,0 +1,34 @@
+# This file is shared currently between this Gradle build and the
+# Ant builds for fd303 and JavaScript. Keep the dotted notation for
+# the properties to minimize the changes in the dependencies.
+thrift.version=0.13.0
+thrift.groupid=org.apache.thrift
+release=false
+
+# Local Install paths
+install.path=/usr/local/lib
+install.javadoc.path=/usr/local/lib
+
+# Test execution properties
+testPort=9090
+
+# Test with Clover Code coverage (disabled by default)
+cloverEnabled=false
+
+# Maven dependency download locations
+mvn.repo=http://repo1.maven.org/maven2
+apache.repo=https://repository.apache.org/content/repositories/releases
+
+# Apache Maven publish
+license=http://www.apache.org/licenses/LICENSE-2.0.txt
+maven-repository-url=https://repository.apache.org/service/local/staging/deploy/maven2
+maven-repository-id=apache.releases.https
+
+# Dependency versions
+httpclient.version=4.5.6
+httpcore.version=4.4.1
+slf4j.version=1.7.25
+servlet.version=2.5
+junit.version=4.12
+mockito.version=1.9.5
+javax.annotation.version=1.3.2
diff --git a/src/jaegertracing/thrift/lib/java/gradle/additionalArtifacts.gradle b/src/jaegertracing/thrift/lib/java/gradle/additionalArtifacts.gradle
new file mode 100644
index 000000000..201469da1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/gradle/additionalArtifacts.gradle
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Following Gradle best practices to keep build logic organized
+
+task sourcesJar(type: Jar, group: 'Build') {
+ description = 'Assembles a jar archive containing the main Java sources.'
+
+ classifier 'sources'
+ from sourceSets.main.allSource
+}
+
+task javadocJar(type: Jar, dependsOn: javadoc, group: 'Build') {
+ description = 'Assembles a jar archive containing the JavaDoc.'
+
+ classifier 'javadoc'
+ from javadoc.destinationDir
+}
+
+artifacts {
+ archives sourcesJar
+ archives javadocJar
+}
+
diff --git a/src/jaegertracing/thrift/lib/java/gradle/cloverCoverage.gradle b/src/jaegertracing/thrift/lib/java/gradle/cloverCoverage.gradle
new file mode 100644
index 000000000..cef0e79b1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/gradle/cloverCoverage.gradle
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Following Gradle best practices to keep build logic organized
+
+// Keep this as an optional feature for now, disabled by default
+if (Boolean.parseBoolean(project.cloverEnabled)) {
+ apply plugin: 'com.bmuschko.clover'
+
+ dependencies {
+ clover 'org.openclover:clover:4.2.+'
+ }
+
+ clover {
+
+ testIncludes = ['**/Test*.java']
+ // Exclude the generated test code from code coverage
+ testExcludes = ['thrift/test/Test*.java']
+
+ compiler {
+ encoding = 'UTF-8'
+ debug = true
+ }
+
+ report {
+ html = true
+ pdf = true
+ }
+ }
+
+ build.dependsOn cloverGenerateReport
+}
diff --git a/src/jaegertracing/thrift/lib/java/gradle/codeQualityChecks.gradle b/src/jaegertracing/thrift/lib/java/gradle/codeQualityChecks.gradle
new file mode 100644
index 000000000..1ff1c297d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/gradle/codeQualityChecks.gradle
@@ -0,0 +1,39 @@
+
+// =================================================================
+// Configure the Gradle code quality plugins here.
+//
+
+apply plugin: 'findbugs'
+
+findbugs {
+ ignoreFailures = true
+ toolVersion = '3.0.1'
+ sourceSets = [ sourceSets.main ]
+ effort = 'max'
+ reportLevel = 'low'
+ excludeFilter = file('code_quality_tools/findbugs-filter.xml')
+}
+
+tasks.withType(FindBugs) {
+ reports {
+ text.enabled = false
+ html.enabled = true
+ xml.enabled = false
+ }
+}
+
+apply plugin: 'pmd'
+
+pmd {
+ ignoreFailures = true
+ toolVersion = '6.0.0'
+ sourceSets = [ sourceSets.main ]
+ ruleSets = [ 'java-basic' ]
+}
+
+tasks.withType(Pmd) {
+ reports {
+ html.enabled = true
+ xml.enabled = false
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/gradle/environment.gradle b/src/jaegertracing/thrift/lib/java/gradle/environment.gradle
new file mode 100644
index 000000000..45fa63a17
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/gradle/environment.gradle
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Following Gradle best practices to keep build logic organized
+
+// Override the build directory if CMake is used (allows for out-of-tree-builds)
+if (hasProperty('build.dir')) {
+ buildDir = file(property('build.dir'))
+}
+
+// In order to remain compatible with other Ant based builds in the system
+// we convert the gradle.properties into DSL friendly camelCased properties
+ext.installPath = property('install.path')
+ext.installJavadocPath = property('install.javadoc.path')
+
+ext.thriftRoot = file('../..')
+
+if (hasProperty('thrift.compiler')) {
+ ext.thriftCompiler = property('thrift.compiler')
+} else {
+ ext.thriftCompiler = "$thriftRoot/compiler/cpp/thrift"
+}
+
+ext.mvnRepo = property('mvn.repo')
+ext.apacheRepo = property('apache.repo')
+ext.mavenRepositoryUrl = property('maven-repository-url')
+
+// Versions used in this project
+ext.httpclientVersion = property('httpclient.version')
+ext.httpcoreVersion = property('httpcore.version')
+ext.servletVersion = property('servlet.version')
+ext.slf4jVersion = property('slf4j.version')
+ext.junitVersion = property('junit.version')
+ext.mockitoVersion = property('mockito.version')
+ext.javaxAnnotationVersion = property('javax.annotation.version')
+
+// In this section you declare where to find the dependencies of your project
+repositories {
+ maven {
+ name 'Maven Central Repository'
+ url mvnRepo
+ }
+ maven {
+ name 'Apache Maven Repository'
+ url apacheRepo
+ }
+}
+
+dependencies {
+ compile "org.slf4j:slf4j-api:${slf4jVersion}"
+ compile "org.apache.httpcomponents:httpclient:${httpclientVersion}"
+ compile "org.apache.httpcomponents:httpcore:${httpcoreVersion}"
+ compile "javax.servlet:servlet-api:${servletVersion}"
+ compile "javax.annotation:javax.annotation-api:${javaxAnnotationVersion}"
+
+ testCompile "junit:junit:${junitVersion}"
+ testCompile "org.mockito:mockito-all:${mockitoVersion}"
+ testRuntime "org.slf4j:slf4j-log4j12:${slf4jVersion}"
+}
diff --git a/src/jaegertracing/thrift/lib/java/gradle/functionalTests.gradle b/src/jaegertracing/thrift/lib/java/gradle/functionalTests.gradle
new file mode 100644
index 000000000..c420d122c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/gradle/functionalTests.gradle
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Following Gradle best practices to keep build logic organized
+
+// ----------------------------------------------------------------------------
+// Functional testing harness creation. This helps run the cross-check tests.
+// The Makefile precross target invokes the shadowJar task and the tests.json
+// code is changed to call runclient or runserver as needed.
+
+// ----------------------------------------------------------------------------
+// Cross Test sources are separated in their own sourceSet
+//
+sourceSets {
+ crossTest {
+ java {
+ srcDir 'test'
+ include '**/test/TestClient.java'
+ include '**/test/TestServer.java'
+ include '**/test/TestNonblockingServer.java'
+ }
+ }
+}
+
+configurations {
+ crossTestCompile { extendsFrom testCompile }
+ crossTestRuntime { extendsFrom crossTestCompile, testRuntime }
+}
+
+dependencies {
+ crossTestCompile sourceSets.main.output
+ crossTestCompile sourceSets.test.output
+}
+
+// I am using shadow plugin to make a self contained functional test Uber JAR that
+// eliminates startup problems with wrapping the cross-check harness in Gradle.
+// This is used by the runner scripts as the single classpath entry which
+// allows the process to be as lightweight as it can.
+shadowJar {
+ description = 'Assemble a test JAR file for cross-check execution'
+ // make sure the runners are created when this runs
+ dependsOn 'generateRunnerScriptForClient', 'generateRunnerScriptForServer', 'generateRunnerScriptForNonblockingServer'
+
+ baseName = 'functionalTest'
+ destinationDir = file("$buildDir/functionalTestJar")
+ classifier = null
+
+ // We do not need a version number for this internal jar
+ version = null
+
+ // Bundle the complete set of unit test classes including generated code
+ // and the runtime dependencies in one JAR to expedite execution.
+ from sourceSets.test.output
+ from sourceSets.crossTest.output
+ configurations = [project.configurations.testRuntime]
+}
+
+// Common script runner configuration elements
+def scriptExt = ''
+def execExt = ''
+def scriptHead = '#!/bin/bash'
+def args = '$*'
+
+// Although this is marked internal it is an available and stable interface
+if (org.gradle.internal.os.OperatingSystem.current().windows) {
+ scriptExt = '.bat'
+ execExt = '.exe'
+ scriptHead = '@echo off'
+ args = '%*'
+}
+
+// The Java executable to use with the runner scripts
+def javaExe = file("${System.getProperty('java.home')}/bin/java${execExt}").canonicalPath
+// The common Uber jar path
+def jarPath = shadowJar.archivePath.canonicalPath
+def trustStore = file('test/.truststore').canonicalPath
+def keyStore = file('test/.keystore').canonicalPath
+
+task generateRunnerScriptForClient(group: 'Build') {
+ description = 'Generate a runner script for cross-check tests with TestClient'
+
+ def clientFile = file("$buildDir/runclient${scriptExt}")
+
+ def runClientText = """\
+${scriptHead}
+
+"${javaExe}" -cp "$jarPath" "-Djavax.net.ssl.trustStore=$trustStore" -Djavax.net.ssl.trustStorePassword=thrift org.apache.thrift.test.TestClient $args
+"""
+ inputs.property 'runClientText', runClientText
+ outputs.file clientFile
+
+ doLast {
+ clientFile.parentFile.mkdirs()
+ clientFile.text = runClientText
+ clientFile.setExecutable(true, false)
+ }
+}
+
+task generateRunnerScriptForServer(group: 'Build') {
+ description = 'Generate a runner script for cross-check tests with TestServer'
+
+ def serverFile = file("$buildDir/runserver${scriptExt}")
+
+ def runServerText = """\
+${scriptHead}
+
+"${javaExe}" -cp "$jarPath" "-Djavax.net.ssl.keyStore=$keyStore" -Djavax.net.ssl.keyStorePassword=thrift org.apache.thrift.test.TestServer $args
+"""
+
+ inputs.property 'runServerText', runServerText
+ outputs.file serverFile
+
+ doLast {
+ serverFile.parentFile.mkdirs()
+ serverFile.text = runServerText
+ serverFile.setExecutable(true, false)
+ }
+}
+
+task generateRunnerScriptForNonblockingServer(group: 'Build') {
+ description = 'Generate a runner script for cross-check tests with TestNonblockingServer'
+
+ def serverFile = file("$buildDir/runnonblockingserver${scriptExt}")
+
+ def runServerText = """\
+${scriptHead}
+
+"${javaExe}" -cp "$jarPath" "-Djavax.net.ssl.keyStore=$keyStore" -Djavax.net.ssl.keyStorePassword=thrift org.apache.thrift.test.TestNonblockingServer $args
+"""
+
+ inputs.property 'runServerText', runServerText
+ outputs.file serverFile
+
+ doLast {
+ serverFile.parentFile.mkdirs()
+ serverFile.text = runServerText
+ serverFile.setExecutable(true, false)
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/gradle/generateTestThrift.gradle b/src/jaegertracing/thrift/lib/java/gradle/generateTestThrift.gradle
new file mode 100644
index 000000000..121bf537d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/gradle/generateTestThrift.gradle
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Following Gradle best practices to keep build logic organized
+
+// Generated code locations for Unit tests
+ext.genSrc = file("$buildDir/gen-java")
+ext.genBeanSrc = file("$buildDir/gen-javabean")
+ext.genReuseSrc = file("$buildDir/gen-javareuse")
+ext.genFullCamelSrc = file("$buildDir/gen-fullcamel")
+ext.genUnsafeSrc = file("$buildDir/gen-unsafe")
+
+// Add the generated code directories to the test source set
+sourceSets {
+ test.java.srcDirs genSrc, genBeanSrc, genReuseSrc, genFullCamelSrc, genUnsafeSrc
+}
+
+// ----------------------------------------------------------------------------
+// Code generation for Unit Testing
+
+// A callable closure to make this easier
+ext.thriftCompile = { Task task, String thriftFileName, String generator = 'java', File outputDir = genSrc ->
+ def thriftFile = file("$thriftRoot/test/$thriftFileName")
+ assert thriftFile.exists()
+
+ task.inputs.file thriftFile
+ task.outputs.dir outputDir
+
+ task.doLast {
+ outputDir.mkdirs()
+ def result = exec {
+ executable file(thriftCompiler)
+ args '--gen', generator
+ args '-out', outputDir
+ args thriftFile
+ standardOutput = task.outputBuffer
+ errorOutput = task.outputBuffer
+ ignoreExitValue = true
+ }
+ if (result.exitValue != 0) {
+ // Only show the Thrift compiler output on failures, cuts down on noise!
+ println task.outputBuffer.toString()
+ result.rethrowFailure()
+ }
+ }
+}
+
+task generate(group: 'Build') {
+ description = 'Generate all unit test Thrift sources'
+ compileTestJava.dependsOn it
+}
+
+task generateJava(group: 'Build') {
+ description = 'Generate the thrift gen-java source'
+ generate.dependsOn it
+
+ ext.outputBuffer = new ByteArrayOutputStream()
+
+ thriftCompile(it, 'ThriftTest.thrift')
+ thriftCompile(it, 'JavaTypes.thrift')
+ thriftCompile(it, 'DebugProtoTest.thrift')
+ thriftCompile(it, 'DoubleConstantsTest.thrift')
+ thriftCompile(it, 'OptionalRequiredTest.thrift')
+ thriftCompile(it, 'ManyOptionals.thrift')
+ thriftCompile(it, 'JavaDeepCopyTest.thrift')
+ thriftCompile(it, 'EnumContainersTest.thrift')
+ thriftCompile(it, 'JavaBinaryDefault.thrift')
+}
+
+task generateBeanJava(group: 'Build') {
+ description = 'Generate the thrift gen-javabean source'
+ generate.dependsOn it
+
+ ext.outputBuffer = new ByteArrayOutputStream()
+
+ thriftCompile(it, 'JavaBeansTest.thrift', 'java:beans,nocamel', genBeanSrc)
+}
+
+task generateReuseJava(group: 'Build') {
+ description = 'Generate the thrift gen-javareuse source'
+ generate.dependsOn it
+
+ ext.outputBuffer = new ByteArrayOutputStream()
+
+ thriftCompile(it, 'FullCamelTest.thrift', 'java:fullcamel', genFullCamelSrc)
+}
+
+task generateFullCamelJava(group: 'Build') {
+ description = 'Generate the thrift gen-fullcamel source'
+ generate.dependsOn it
+
+ ext.outputBuffer = new ByteArrayOutputStream()
+
+ thriftCompile(it, 'ReuseObjects.thrift', 'java:reuse-objects', genReuseSrc)
+}
+
+task generateUnsafeBinariesJava(group: 'Build') {
+ description = 'Generate the thrift gen-unsafebinaries source'
+ generate.dependsOn it
+
+ ext.outputBuffer = new ByteArrayOutputStream()
+
+ thriftCompile(it, 'UnsafeTypes.thrift', 'java:unsafe_binaries', genUnsafeSrc)
+}
diff --git a/src/jaegertracing/thrift/lib/java/gradle/publishing.gradle b/src/jaegertracing/thrift/lib/java/gradle/publishing.gradle
new file mode 100644
index 000000000..029bff93d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/gradle/publishing.gradle
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Following Gradle best practices to keep build logic organized
+
+// ----------------------------------------------------------------------------
+// Installation subtasks, not used currently, we use "make install/fast"
+task installDist(type: Copy, group: 'Install') {
+ description = "Copy Thrift JAR and dependencies into $installPath location"
+
+ destinationDir = file(installPath)
+
+ from jar
+ from configurations.compile
+}
+
+task installJavadoc(type: Copy, group: 'Install', dependsOn: javadoc) {
+ description = "Install Thrift JavaDoc into $installJavadocPath location"
+
+ destinationDir = file(installJavadocPath)
+
+ from javadoc.destinationDir
+}
+
+// This is not needed by Gradle builds but the remaining Ant builds seem to
+// need access to the generated test classes for Thrift unit tests so we
+// assist them to use it this way.
+task copyDependencies(type: Copy, group: 'Build') {
+ description = 'Copy runtime dependencies in a common location for other Ant based projects'
+ project.assemble.dependsOn it
+
+ destinationDir = file("$buildDir/deps")
+ from configurations.testRuntime
+ // exclude some very specific unit test dependencies
+ exclude '**/junit*.jar', '**/mockito*.jar', '**/hamcrest*.jar'
+}
+
+// ----------------------------------------------------------------------------
+// Allow this configuration to be shared between install and uploadArchives tasks
+def configurePom(pom) {
+ pom.project {
+ name 'Apache Thrift'
+ description 'Thrift is a software framework for scalable cross-language services development.'
+ packaging 'jar'
+ url 'http://thrift.apache.org'
+
+ scm {
+ url 'https://github.com/apache/thrift'
+ connection 'scm:git:https://github.com/apache/thrift.git'
+ developerConnection 'scm:git:git@github.com:apache/thrift.git'
+ }
+
+ licenses {
+ license {
+ name 'The Apache Software License, Version 2.0'
+ url "${project.license}"
+ distribution 'repo'
+ }
+ }
+
+ developers {
+ developer {
+ id 'dev'
+ name 'Apache Thrift Developers'
+ email 'dev@thrift.apache.org'
+ }
+ }
+ }
+
+ pom.whenConfigured {
+ // Fixup the scope for servlet-api to be 'provided' instead of 'compile'
+ dependencies.find { dep -> dep.groupId == 'javax.servlet' && dep.artifactId == 'servlet-api' }.with {
+ // it.optional = true
+ it.scope = 'provided'
+ }
+ }
+}
+
+install {
+ repositories.mavenInstaller {
+ configurePom(pom)
+ }
+}
+
+uploadArchives {
+ dependsOn test // make sure we run unit tests when publishing
+ repositories.mavenDeployer {
+ // signPom will silently do nothing when no signing information is provided
+ beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
+ repository(url: project.mavenRepositoryUrl) {
+ if (project.hasProperty('mavenUser') && project.hasProperty('mavenPassword')) {
+ authentication(userName: mavenUser, password: mavenPassword)
+ }
+ }
+ configurePom(pom)
+ }
+}
+
+// Signing configuration, optional, only when release and uploadArchives is activated
+signing {
+ required { !version.endsWith("SNAPSHOT") && gradle.taskGraph.hasTask("uploadArchives") }
+ sign configurations.archives
+}
diff --git a/src/jaegertracing/thrift/lib/java/gradle/sourceConfiguration.gradle b/src/jaegertracing/thrift/lib/java/gradle/sourceConfiguration.gradle
new file mode 100644
index 000000000..8dd0331f7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/gradle/sourceConfiguration.gradle
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Following Gradle best practices to keep build logic organized
+
+// ----------------------------------------------------------------------------
+// source sets for main and test sources
+sourceSets {
+ main {
+ java {
+ srcDir 'src'
+ }
+ }
+ test {
+ java {
+ srcDir 'test'
+ // see functionalTests.gradle for these files
+ exclude '**/test/TestClient.java'
+ exclude '**/test/TestServer.java'
+ exclude '**/test/TestNonblockingServer.java'
+ }
+ resources {
+ srcDir 'test'
+ include 'log4j.properties'
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Compiler configuration details
+
+sourceCompatibility = '1.8'
+targetCompatibility = '1.8'
+
+tasks.withType(JavaCompile) {
+ options.encoding = 'UTF-8'
+ options.debug = true
+ options.deprecation = true
+ // options.compilerArgs.addAll('-Xlint:unchecked')
+}
+
+// ----------------------------------------------------------------------------
+// Jar packaging details
+processResources {
+ into('META-INF') {
+ from "$thriftRoot/LICENSE"
+ from "$thriftRoot/NOTICE"
+ rename('(.+)', '$1.txt')
+ }
+}
+
+jar {
+ project.test.dependsOn it
+ manifest {
+ attributes([
+ "Implementation-Version": "${project.version}",
+ "Bundle-ManifestVersion": "2",
+ "Bundle-SymbolicName": "${project.group}",
+ "Bundle-Name": "Apache Thrift",
+ "Bundle-Version": "${project.version}",
+ "Bundle-Description": "Apache Thrift library",
+ "Bundle-License": "${project.license}",
+ "Bundle-ActivationPolicy": "lazy",
+ "Export-Package": "${project.group}.async;uses:=\"${project.group}.protocol,${project.group}.transport,org.slf4j,${project.group}\";version=\"${version}\",${project.group}.protocol;uses:=\"${project.group}.transport,${project.group},${project.group}.scheme\";version=\"${version}\",${project.group}.server;uses:=\"${project.group}.transport,${project.group}.protocol,${project.group},org.slf4j,javax.servlet,javax.servlet.http\";version=\"${version}\",${project.group}.transport;uses:=\"${project.group}.protocol,${project.group},org.apache.http.client,org.apache.http.params,org.apache.http.entity,org.apache.http.client.methods,org.apache.http,org.slf4j,javax.net.ssl,javax.net,javax.security.sasl,javax.security.auth.callback\";version=\"${version}\",${project.group};uses:=\"${project.group}.protocol,${project.group}.async,${project.group}.server,${project.group}.transport,org.slf4j,org.apache.log4j,${project.group}.scheme\";version=\"${version}\",${project.group}.meta_data;uses:=\"${project.group}\";version=\"${version}\",${project.group}.scheme;uses:=\"${project.group}.protocol,${project.group}\";version=\"${version}\"",
+ "Import-Package": "javax.net,javax.net.ssl,javax.security.auth.callback,javax.security.sasl,javax.servlet;resolution:=optional,javax.servlet.http;resolution:=optional,org.slf4j;resolution:=optional;version=\"[1.4,2)\",org.apache.http.client;resolution:=optional,org.apache.http.params;resolution:=optional,org.apache.http.entity;resolution:=optional,org.apache.http.client.methods;resolution:=optional,org.apache.http;resolution:=optional"
+ ])
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/gradle/unitTests.gradle b/src/jaegertracing/thrift/lib/java/gradle/unitTests.gradle
new file mode 100644
index 000000000..61f2fbdeb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/gradle/unitTests.gradle
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Following Gradle best practices to keep build logic organized
+
+// Bundle the test classes in a JAR for other Ant based builds
+task testJar(type: Jar, group: 'Build') {
+ description = 'Assembles a jar archive containing the test classes.'
+ project.test.dependsOn it
+
+ classifier 'test'
+ from sourceSets.test.output
+}
+
+// ----------------------------------------------------------------------------
+// Unit test tasks and configurations
+
+// Help the up to date algorithm to make these tests done
+ext.markTaskDone = { task ->
+ def buildFile = file("$buildDir/${task.name}.flag")
+ task.inputs.files task.classpath
+ task.outputs.file buildFile
+ task.doLast {
+ buildFile.text = 'Passed!'
+ }
+}
+
+task deprecatedEqualityTest(type: JavaExec, group: 'Verification') {
+ description = 'Run the non-JUnit test suite '
+ classpath = sourceSets.test.runtimeClasspath
+ main 'org.apache.thrift.test.EqualityTest'
+ markTaskDone(it)
+}
+
+task deprecatedJavaBeansTest(type: JavaExec, group: 'Verification') {
+ description = 'Run the non-JUnit test suite '
+ classpath = sourceSets.test.runtimeClasspath
+ main 'org.apache.thrift.test.JavaBeansTest'
+ markTaskDone(it)
+}
+
+// Main Unit Test task configuration
+test {
+ description="Run the full test suite"
+ dependsOn deprecatedEqualityTest, deprecatedJavaBeansTest
+
+ // Allow repeating tests even after successful execution
+ if (project.hasProperty('rerunTests')) {
+ outputs.upToDateWhen { false }
+ }
+
+ include '**/Test*.class'
+ exclude '**/Test*\$*.class'
+
+ maxHeapSize = '512m'
+ forkEvery = 1
+
+ systemProperties = [
+ 'build.test': "${compileTestJava.destinationDir}",
+ 'test.port': "${testPort}",
+ 'javax.net.ssl.trustStore': "${projectDir}/test/.truststore",
+ 'javax.net.ssl.trustStorePassword': 'thrift',
+ 'javax.net.ssl.keyStore': "${projectDir}/test/.keystore",
+ 'javax.net.ssl.keyStorePassword': 'thrift'
+ ]
+}
diff --git a/src/jaegertracing/thrift/lib/java/gradle/wrapper/gradle-wrapper.jar b/src/jaegertracing/thrift/lib/java/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..5c2d1cf01
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/src/jaegertracing/thrift/lib/java/gradle/wrapper/gradle-wrapper.properties b/src/jaegertracing/thrift/lib/java/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..7c4388a92
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/src/jaegertracing/thrift/lib/java/gradlew b/src/jaegertracing/thrift/lib/java/gradlew
new file mode 100755
index 000000000..83f2acfdc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/src/jaegertracing/thrift/lib/java/gradlew.bat b/src/jaegertracing/thrift/lib/java/gradlew.bat
new file mode 100644
index 000000000..9618d8d96
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/gradlew.bat
@@ -0,0 +1,100 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/src/jaegertracing/thrift/lib/java/settings.gradle b/src/jaegertracing/thrift/lib/java/settings.gradle
new file mode 100644
index 000000000..c9bd8bc0e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/settings.gradle
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+rootProject.name = 'libthrift'
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/AsyncProcessFunction.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/AsyncProcessFunction.java
new file mode 100644
index 000000000..483c8d054
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/AsyncProcessFunction.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift;
+
+import org.apache.thrift.async.AsyncMethodCallback;
+import org.apache.thrift.protocol.TMessage;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.server.AbstractNonblockingServer;
+
+public abstract class AsyncProcessFunction<I, T extends TBase, R> {
+ final String methodName;
+
+ public AsyncProcessFunction(String methodName) {
+ this.methodName = methodName;
+ }
+
+ protected abstract boolean isOneway();
+
+ public abstract void start(I iface, T args, AsyncMethodCallback<R> resultHandler) throws TException;
+
+ public abstract T getEmptyArgsInstance();
+
+ public abstract AsyncMethodCallback<R> getResultHandler(final AbstractNonblockingServer.AsyncFrameBuffer fb, int seqid);
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public void sendResponse(final AbstractNonblockingServer.AsyncFrameBuffer fb, final TSerializable result, final byte type, final int seqid) throws TException {
+ TProtocol oprot = fb.getOutputProtocol();
+
+ oprot.writeMessageBegin(new TMessage(getMethodName(), type, seqid));
+ result.write(oprot);
+ oprot.writeMessageEnd();
+ oprot.getTransport().flush();
+
+ fb.responseReady();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/EncodingUtils.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/EncodingUtils.java
new file mode 100644
index 000000000..bf14ef57e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/EncodingUtils.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+/**
+ * Utility methods for use when encoding/decoding raw data as byte arrays.
+ */
+public class EncodingUtils {
+
+ /**
+ * Encode <code>integer</code> as a series of 4 bytes into <code>buf</code>
+ * starting at position 0 within that buffer.
+ *
+ * @param integer
+ * The integer to encode.
+ * @param buf
+ * The buffer to write to.
+ */
+ public static final void encodeBigEndian(final int integer, final byte[] buf) {
+ encodeBigEndian(integer, buf, 0);
+ }
+
+ /**
+ * Encode <code>integer</code> as a series of 4 bytes into <code>buf</code>
+ * starting at position <code>offset</code>.
+ *
+ * @param integer
+ * The integer to encode.
+ * @param buf
+ * The buffer to write to.
+ * @param offset
+ * The offset within <code>buf</code> to start the encoding.
+ */
+ public static final void encodeBigEndian(final int integer, final byte[] buf, int offset) {
+ buf[offset] = (byte) (0xff & (integer >> 24));
+ buf[offset + 1] = (byte) (0xff & (integer >> 16));
+ buf[offset + 2] = (byte) (0xff & (integer >> 8));
+ buf[offset + 3] = (byte) (0xff & (integer));
+ }
+
+ /**
+ * Decode a series of 4 bytes from <code>buf</code>, starting at position 0,
+ * and interpret them as an integer.
+ *
+ * @param buf
+ * The buffer to read from.
+ * @return An integer, as read from the buffer.
+ */
+ public static final int decodeBigEndian(final byte[] buf) {
+ return decodeBigEndian(buf, 0);
+ }
+
+ /**
+ * Decode a series of 4 bytes from <code>buf</code>, start at
+ * <code>offset</code>, and interpret them as an integer.
+ *
+ * @param buf
+ * The buffer to read from.
+ * @param offset
+ * The offset with <code>buf</code> to start the decoding.
+ * @return An integer, as read from the buffer.
+ */
+ public static final int decodeBigEndian(final byte[] buf, int offset) {
+ return ((buf[offset] & 0xff) << 24) | ((buf[offset + 1] & 0xff) << 16)
+ | ((buf[offset + 2] & 0xff) << 8) | ((buf[offset + 3] & 0xff));
+ }
+
+ /**
+ * Bitfield utilities.
+ * Returns true if the bit at position is set in v.
+ */
+ public static final boolean testBit(byte v, int position) {
+ return testBit((int)v, position);
+ }
+
+ public static final boolean testBit(short v, int position) {
+ return testBit((int)v, position);
+ }
+
+ public static final boolean testBit(int v, int position) {
+ return (v & (1 << position)) != 0;
+ }
+
+ public static final boolean testBit(long v, int position) {
+ return (v & (1L << position)) != 0L;
+ }
+
+ /**
+ * Returns v, with the bit at position set to zero.
+ */
+ public static final byte clearBit(byte v, int position) {
+ return (byte)clearBit((int)v, position);
+ }
+
+ public static final short clearBit(short v, int position) {
+ return (short)clearBit((int)v, position);
+ }
+
+ public static final int clearBit(int v, int position) {
+ return v & ~(1 << position);
+ }
+
+ public static final long clearBit(long v, int position) {
+ return v & ~(1L << position);
+ }
+
+ /**
+ * Returns v, with the bit at position set to 1 or 0 depending on value.
+ */
+ public static final byte setBit(byte v, int position, boolean value) {
+ return (byte)setBit((int)v, position, value);
+ }
+
+ public static final short setBit(short v, int position, boolean value) {
+ return (short)setBit((int)v, position, value);
+ }
+
+ public static final int setBit(int v, int position, boolean value) {
+ if(value)
+ return v | (1 << position);
+ else
+ return clearBit(v, position);
+ }
+
+ public static final long setBit(long v, int position, boolean value) {
+ if(value)
+ return v | (1L << position);
+ else
+ return clearBit(v, position);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/Option.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/Option.java
new file mode 100644
index 000000000..d5cd309e8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/Option.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+/**
+ * Implementation of the Option type pattern
+ */
+public abstract class Option<T> {
+
+ @SuppressWarnings("rawtypes")
+ private static final Option NONE = new None();
+
+ /**
+ * Whether the Option is defined or not
+ * @return
+ * true if the Option is defined (of type Some)
+ * false if the Option is not defined (of type None)
+ */
+ public abstract boolean isDefined();
+
+ /**
+ * Get the value of the Option (if it is defined)
+ * @return the value
+ * @throws IllegalStateException if called on a None
+ */
+ public abstract T get();
+
+ /**
+ * Get the contained value (if defined) or else return a default value
+ * @param other what to return if the value is not defined (a None)
+ * @return either the value, or other if the value is not defined
+ */
+ public T or(T other) {
+ if (isDefined()) {
+ return get();
+ } else {
+ return other;
+ }
+ }
+ /**
+ * The None type, representing an absent value (instead of "null")
+ */
+ public static class None<T> extends Option<T> {
+ public boolean isDefined() {
+ return false;
+ }
+
+ public T get() {
+ throw new IllegalStateException("Cannot call get() on None");
+ }
+
+ public String toString() {
+ return "None";
+ }
+ }
+
+ /**
+ * The Some type, representing an existence of some value
+ * @param <T> The type of value
+ */
+ public static class Some<T> extends Option<T> {
+ private final T value;
+ public Some(T value) {
+ this.value = value;
+ }
+
+ public boolean isDefined() {
+ return true;
+ }
+
+ public T get() {
+ return value;
+ }
+
+ public String toString() {
+ return "Some(" + value + ")";
+ }
+ }
+
+ /**
+ * Wraps value in an Option type, depending on whether or not value is null
+ * @param value
+ * @param <T> type of value
+ * @return Some(value) if value is not null, None if value is null
+ */
+ public static <T> Option<T> fromNullable(T value) {
+ if (value != null) {
+ return some(value);
+ } else {
+ return none();
+ }
+ }
+
+ /**
+ * Wrap value in a Some type (NB! value must not be null!)
+ * @param value
+ * @param <T> type of value
+ * @return a new Some(value)
+ */
+ public static <T> Some<T> some(T value) {
+ return new Some<T>(value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> None<T> none() {
+ return (None<T>) NONE;
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/ProcessFunction.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/ProcessFunction.java
new file mode 100644
index 000000000..e6213dfa1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/ProcessFunction.java
@@ -0,0 +1,88 @@
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TMessage;
+import org.apache.thrift.protocol.TMessageType;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolException;
+import org.apache.thrift.transport.TTransportException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class ProcessFunction<I, T extends TBase> {
+ private final String methodName;
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ProcessFunction.class.getName());
+
+ public ProcessFunction(String methodName) {
+ this.methodName = methodName;
+ }
+
+ public final void process(int seqid, TProtocol iprot, TProtocol oprot, I iface) throws TException {
+ T args = getEmptyArgsInstance();
+ try {
+ args.read(iprot);
+ } catch (TProtocolException e) {
+ iprot.readMessageEnd();
+ TApplicationException x = new TApplicationException(TApplicationException.PROTOCOL_ERROR, e.getMessage());
+ oprot.writeMessageBegin(new TMessage(getMethodName(), TMessageType.EXCEPTION, seqid));
+ x.write(oprot);
+ oprot.writeMessageEnd();
+ oprot.getTransport().flush();
+ return;
+ }
+ iprot.readMessageEnd();
+ TSerializable result = null;
+ byte msgType = TMessageType.REPLY;
+
+ try {
+ result = getResult(iface, args);
+ } catch (TTransportException ex) {
+ LOGGER.error("Transport error while processing " + getMethodName(), ex);
+ throw ex;
+ } catch (TApplicationException ex) {
+ LOGGER.error("Internal application error processing " + getMethodName(), ex);
+ result = ex;
+ msgType = TMessageType.EXCEPTION;
+ } catch (Exception ex) {
+ LOGGER.error("Internal error processing " + getMethodName(), ex);
+ if(rethrowUnhandledExceptions()) throw new RuntimeException(ex.getMessage(), ex);
+ if(!isOneway()) {
+ result = new TApplicationException(TApplicationException.INTERNAL_ERROR,
+ "Internal error processing " + getMethodName());
+ msgType = TMessageType.EXCEPTION;
+ }
+ }
+
+ if(!isOneway()) {
+ oprot.writeMessageBegin(new TMessage(getMethodName(), msgType, seqid));
+ result.write(oprot);
+ oprot.writeMessageEnd();
+ oprot.getTransport().flush();
+ }
+ }
+
+ private void handleException(int seqid, TProtocol oprot) throws TException {
+ if (!isOneway()) {
+ TApplicationException x = new TApplicationException(TApplicationException.INTERNAL_ERROR,
+ "Internal error processing " + getMethodName());
+ oprot.writeMessageBegin(new TMessage(getMethodName(), TMessageType.EXCEPTION, seqid));
+ x.write(oprot);
+ oprot.writeMessageEnd();
+ oprot.getTransport().flush();
+ }
+ }
+
+ protected boolean rethrowUnhandledExceptions(){
+ return false;
+ }
+
+ protected abstract boolean isOneway();
+
+ public abstract TBase getResult(I iface, T args) throws TException;
+
+ public abstract T getEmptyArgsInstance();
+
+ public String getMethodName() {
+ return methodName;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TApplicationException.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TApplicationException.java
new file mode 100644
index 000000000..4d693d9ce
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TApplicationException.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TField;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolUtil;
+import org.apache.thrift.protocol.TStruct;
+import org.apache.thrift.protocol.TType;
+
+/**
+ * Application level exception
+ *
+ */
+public class TApplicationException extends TException implements TSerializable {
+
+ private static final TStruct TAPPLICATION_EXCEPTION_STRUCT = new TStruct("TApplicationException");
+ private static final TField MESSAGE_FIELD = new TField("message", TType.STRING, (short)1);
+ private static final TField TYPE_FIELD = new TField("type", TType.I32, (short)2);
+
+ private static final long serialVersionUID = 1L;
+
+ public static final int UNKNOWN = 0;
+ public static final int UNKNOWN_METHOD = 1;
+ public static final int INVALID_MESSAGE_TYPE = 2;
+ public static final int WRONG_METHOD_NAME = 3;
+ public static final int BAD_SEQUENCE_ID = 4;
+ public static final int MISSING_RESULT = 5;
+ public static final int INTERNAL_ERROR = 6;
+ public static final int PROTOCOL_ERROR = 7;
+ public static final int INVALID_TRANSFORM = 8;
+ public static final int INVALID_PROTOCOL = 9;
+ public static final int UNSUPPORTED_CLIENT_TYPE = 10;
+
+ protected int type_ = UNKNOWN;
+ private String message_ = null;
+
+ public TApplicationException() {
+ super();
+ }
+
+ public TApplicationException(int type) {
+ super();
+ type_ = type;
+ }
+
+ public TApplicationException(int type, String message) {
+ super(message);
+ type_ = type;
+ }
+
+ public TApplicationException(String message) {
+ super(message);
+ }
+
+ public int getType() {
+ return type_;
+ }
+
+ @Override
+ public String getMessage() {
+ if (message_ == null) {
+ return super.getMessage();
+ }
+ else {
+ return message_;
+ }
+ }
+
+ public void read(TProtocol iprot) throws TException
+ {
+ TField field;
+ iprot.readStructBegin();
+
+ String message = null;
+ int type = UNKNOWN;
+
+ while (true) {
+ field = iprot.readFieldBegin();
+ if (field.type == TType.STOP) {
+ break;
+ }
+ switch (field.id) {
+ case 1:
+ if (field.type == TType.STRING) {
+ message = iprot.readString();
+ } else {
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ break;
+ case 2:
+ if (field.type == TType.I32) {
+ type = iprot.readI32();
+ } else {
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ break;
+ default:
+ TProtocolUtil.skip(iprot, field.type);
+ break;
+ }
+ iprot.readFieldEnd();
+ }
+ iprot.readStructEnd();
+ type_ = type;
+ message_ = message;
+ }
+
+ /**
+ * Convenience factory method for constructing a TApplicationException given a TProtocol input
+ */
+ public static TApplicationException readFrom(TProtocol iprot) throws TException
+ {
+ TApplicationException result = new TApplicationException();
+ result.read(iprot);
+ return result;
+ }
+
+ public void write(TProtocol oprot) throws TException
+ {
+ oprot.writeStructBegin(TAPPLICATION_EXCEPTION_STRUCT);
+ if (getMessage() != null) {
+ oprot.writeFieldBegin(MESSAGE_FIELD);
+ oprot.writeString(getMessage());
+ oprot.writeFieldEnd();
+ }
+ oprot.writeFieldBegin(TYPE_FIELD);
+ oprot.writeI32(type_);
+ oprot.writeFieldEnd();
+ oprot.writeFieldStop();
+ oprot.writeStructEnd();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TAsyncProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TAsyncProcessor.java
new file mode 100644
index 000000000..66f768896
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TAsyncProcessor.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift;
+
+import org.apache.thrift.server.AbstractNonblockingServer.AsyncFrameBuffer;
+
+public interface TAsyncProcessor {
+ /**
+ * Process a single frame.
+
+ * <b>Note:</b> Implementations must call fb.responseReady() once processing
+ * is complete
+ *
+ * @throws TException if the frame cannot be processed
+ */
+ public void process(final AsyncFrameBuffer fb) throws TException;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBase.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBase.java
new file mode 100644
index 000000000..e1489d530
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBase.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.io.Serializable;
+
+import org.apache.thrift.protocol.TProtocol;
+
+/**
+ * Generic base interface for generated Thrift objects.
+ *
+ */
+public interface TBase<T extends TBase<T,F>, F extends TFieldIdEnum> extends Comparable<T>, TSerializable, Serializable {
+
+ /**
+ * Get the F instance that corresponds to fieldId.
+ */
+ public F fieldForId(int fieldId);
+
+ /**
+ * Check if a field is currently set or unset.
+ *
+ * @param field
+ */
+ public boolean isSet(F field);
+
+ /**
+ * Get a field's value by field variable. Primitive types will be wrapped in
+ * the appropriate "boxed" types.
+ *
+ * @param field
+ */
+ public Object getFieldValue(F field);
+
+ /**
+ * Set a field's value by field variable. Primitive types must be "boxed" in
+ * the appropriate object wrapper type.
+ *
+ * @param field
+ */
+ public void setFieldValue(F field, Object value);
+
+ public T deepCopy();
+
+ /**
+ * Return to the state of having just been initialized, as though you had just
+ * called the default constructor.
+ */
+ public void clear();
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseAsyncProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseAsyncProcessor.java
new file mode 100644
index 000000000..f13f068ef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseAsyncProcessor.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.*;
+import org.apache.thrift.async.AsyncMethodCallback;
+
+import org.apache.thrift.server.AbstractNonblockingServer.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.Map;
+
+public class TBaseAsyncProcessor<I> implements TAsyncProcessor, TProcessor {
+ protected final Logger LOGGER = LoggerFactory.getLogger(getClass().getName());
+
+ final I iface;
+ final Map<String,AsyncProcessFunction<I, ? extends TBase,?>> processMap;
+
+ public TBaseAsyncProcessor(I iface, Map<String, AsyncProcessFunction<I, ? extends TBase,?>> processMap) {
+ this.iface = iface;
+ this.processMap = processMap;
+ }
+
+ public Map<String,AsyncProcessFunction<I, ? extends TBase,?>> getProcessMapView() {
+ return Collections.unmodifiableMap(processMap);
+ }
+
+ public void process(final AsyncFrameBuffer fb) throws TException {
+
+ final TProtocol in = fb.getInputProtocol();
+ final TProtocol out = fb.getOutputProtocol();
+
+ //Find processing function
+ final TMessage msg = in.readMessageBegin();
+ AsyncProcessFunction fn = processMap.get(msg.name);
+ if (fn == null) {
+ TProtocolUtil.skip(in, TType.STRUCT);
+ in.readMessageEnd();
+
+ TApplicationException x = new TApplicationException(TApplicationException.UNKNOWN_METHOD,
+ "Invalid method name: '" + msg.name + "'");
+ LOGGER.debug("Invalid method name", x);
+
+ // this means it is a two-way request, so we can send a reply
+ if (msg.type == TMessageType.CALL) {
+ out.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid));
+ x.write(out);
+ out.writeMessageEnd();
+ out.getTransport().flush();
+ }
+ fb.responseReady();
+ return;
+ }
+
+ //Get Args
+ TBase args = fn.getEmptyArgsInstance();
+
+ try {
+ args.read(in);
+ } catch (TProtocolException e) {
+ in.readMessageEnd();
+
+ TApplicationException x = new TApplicationException(TApplicationException.PROTOCOL_ERROR,
+ e.getMessage());
+ LOGGER.debug("Could not retrieve function arguments", x);
+
+ if (!fn.isOneway()) {
+ out.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid));
+ x.write(out);
+ out.writeMessageEnd();
+ out.getTransport().flush();
+ }
+ fb.responseReady();
+ return;
+ }
+ in.readMessageEnd();
+
+ if (fn.isOneway()) {
+ fb.responseReady();
+ }
+
+ //start off processing function
+ AsyncMethodCallback resultHandler = fn.getResultHandler(fb, msg.seqid);
+ try {
+ fn.start(iface, args, resultHandler);
+ } catch (Exception e) {
+ LOGGER.debug("Exception handling function", e);
+ resultHandler.onError(e);
+ }
+ return;
+ }
+
+ @Override
+ public void process(TProtocol in, TProtocol out) throws TException {
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseHelper.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseHelper.java
new file mode 100644
index 000000000..6f6c6ebf5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseHelper.java
@@ -0,0 +1,297 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.thrift;
+
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+public final class TBaseHelper {
+
+ private TBaseHelper(){}
+
+ private static final Comparator comparator = new NestedStructureComparator();
+
+ public static int compareTo(Object o1, Object o2) {
+ if (o1 instanceof Comparable) {
+ return compareTo((Comparable)o1, (Comparable)o2);
+ } else if (o1 instanceof List) {
+ return compareTo((List)o1, (List)o2);
+ } else if (o1 instanceof Set) {
+ return compareTo((Set)o1, (Set)o2);
+ } else if (o1 instanceof Map) {
+ return compareTo((Map)o1, (Map)o2);
+ } else if (o1 instanceof byte[]) {
+ return compareTo((byte[])o1, (byte[])o2);
+ } else {
+ throw new IllegalArgumentException("Cannot compare objects of type " + o1.getClass());
+ }
+ }
+
+ public static int compareTo(boolean a, boolean b) {
+ return Boolean.compare(a, b);
+ }
+
+ public static int compareTo(byte a, byte b) {
+ return Byte.compare(a, b);
+ }
+
+ public static int compareTo(short a, short b) {
+ return Short.compare(a,b);
+ }
+
+ public static int compareTo(int a, int b) {
+ return Integer.compare(a, b);
+ }
+
+ public static int compareTo(long a, long b) {
+ return Long.compare(a, b);
+ }
+
+ public static int compareTo(double a, double b) {
+ return Double.compare(a, b);
+ }
+
+ public static int compareTo(String a, String b) {
+ return a.compareTo(b);
+ }
+
+ public static int compareTo(byte[] a, byte[] b) {
+ int compare = compareTo(a.length, b.length);
+ if (compare == 0) {
+ for (int i = 0; i < a.length; i++) {
+ compare = compareTo(a[i], b[i]);
+ if (compare != 0) {
+ break;
+ }
+ }
+ }
+ return compare;
+ }
+
+ public static int compareTo(Comparable a, Comparable b) {
+ return a.compareTo(b);
+ }
+
+ public static int compareTo(List a, List b) {
+ int compare = compareTo(a.size(), b.size());
+ if (compare == 0) {
+ for (int i = 0; i < a.size(); i++) {
+ compare = comparator.compare(a.get(i), b.get(i));
+ if (compare != 0) {
+ break;
+ }
+ }
+ }
+ return compare;
+ }
+
+ public static int compareTo(Set a, Set b) {
+ int compare = compareTo(a.size(), b.size());
+ if (compare == 0) {
+ ArrayList sortedA = new ArrayList(a);
+ ArrayList sortedB = new ArrayList(b);
+
+ Collections.sort(sortedA, comparator);
+ Collections.sort(sortedB, comparator);
+
+ Iterator iterA = sortedA.iterator();
+ Iterator iterB = sortedB.iterator();
+
+ // Compare each item.
+ while (iterA.hasNext() && iterB.hasNext()) {
+ compare = comparator.compare(iterA.next(), iterB.next());
+ if (compare != 0) {
+ break;
+ }
+ }
+ }
+ return compare;
+ }
+
+ public static int compareTo(Map a, Map b) {
+ int lastComparison = compareTo(a.size(), b.size());
+ if (lastComparison != 0) {
+ return lastComparison;
+ }
+
+ // Sort a and b so we can compare them.
+ SortedMap sortedA = new TreeMap(comparator);
+ sortedA.putAll(a);
+ Iterator<Map.Entry> iterA = sortedA.entrySet().iterator();
+ SortedMap sortedB = new TreeMap(comparator);
+ sortedB.putAll(b);
+ Iterator<Map.Entry> iterB = sortedB.entrySet().iterator();
+
+ // Compare each item.
+ while (iterA.hasNext() && iterB.hasNext()) {
+ Map.Entry entryA = iterA.next();
+ Map.Entry entryB = iterB.next();
+ lastComparison = comparator.compare(entryA.getKey(), entryB.getKey());
+ if (lastComparison != 0) {
+ return lastComparison;
+ }
+ lastComparison = comparator.compare(entryA.getValue(), entryB.getValue());
+ if (lastComparison != 0) {
+ return lastComparison;
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Comparator to compare items inside a structure (e.g. a list, set, or map).
+ */
+ private static class NestedStructureComparator implements Comparator, Serializable {
+ public int compare(Object oA, Object oB) {
+ if (oA == null && oB == null) {
+ return 0;
+ } else if (oA == null) {
+ return -1;
+ } else if (oB == null) {
+ return 1;
+ } else if (oA instanceof List) {
+ return compareTo((List)oA, (List)oB);
+ } else if (oA instanceof Set) {
+ return compareTo((Set)oA, (Set)oB);
+ } else if (oA instanceof Map) {
+ return compareTo((Map)oA, (Map)oB);
+ } else if (oA instanceof byte[]) {
+ return compareTo((byte[])oA, (byte[])oB);
+ } else {
+ return compareTo((Comparable)oA, (Comparable)oB);
+ }
+ }
+ }
+
+ public static void toString(Collection<ByteBuffer> bbs, StringBuilder sb) {
+ Iterator<ByteBuffer> it = bbs.iterator();
+ if (!it.hasNext()) {
+ sb.append("[]");
+ } else {
+ sb.append("[");
+ while (true) {
+ ByteBuffer bb = it.next();
+ org.apache.thrift.TBaseHelper.toString(bb, sb);
+ if (!it.hasNext()) {
+ sb.append("]");
+ return;
+ } else {
+ sb.append(", ");
+ }
+ }
+ }
+ }
+
+ public static void toString(ByteBuffer bb, StringBuilder sb) {
+ byte[] buf = bb.array();
+
+ int arrayOffset = bb.arrayOffset();
+ int offset = arrayOffset + bb.position();
+ int origLimit = arrayOffset + bb.limit();
+ int limit = (origLimit - offset > 128) ? offset + 128 : origLimit;
+
+ for (int i = offset; i < limit; i++) {
+ if (i > offset) {
+ sb.append(" ");
+ }
+ sb.append(paddedByteString(buf[i]));
+ }
+ if (origLimit != limit) {
+ sb.append("...");
+ }
+ }
+
+ public static String paddedByteString(byte b) {
+ int extended = (b | 0x100) & 0x1ff;
+ return Integer.toHexString(extended).toUpperCase().substring(1);
+ }
+
+ public static byte[] byteBufferToByteArray(ByteBuffer byteBuffer) {
+ if (wrapsFullArray(byteBuffer)) {
+ return byteBuffer.array();
+ }
+ byte[] target = new byte[byteBuffer.remaining()];
+ byteBufferToByteArray(byteBuffer, target, 0);
+ return target;
+ }
+
+ public static boolean wrapsFullArray(ByteBuffer byteBuffer) {
+ return byteBuffer.hasArray()
+ && byteBuffer.position() == 0
+ && byteBuffer.arrayOffset() == 0
+ && byteBuffer.remaining() == byteBuffer.capacity();
+ }
+
+ public static int byteBufferToByteArray(ByteBuffer byteBuffer, byte[] target, int offset) {
+ int remaining = byteBuffer.remaining();
+ System.arraycopy(byteBuffer.array(),
+ byteBuffer.arrayOffset() + byteBuffer.position(),
+ target,
+ offset,
+ remaining);
+ return remaining;
+ }
+
+ public static ByteBuffer rightSize(ByteBuffer in) {
+ if (in == null) {
+ return null;
+ }
+ if (wrapsFullArray(in)) {
+ return in;
+ }
+ return ByteBuffer.wrap(byteBufferToByteArray(in));
+ }
+
+ public static ByteBuffer copyBinary(final ByteBuffer orig) {
+ if (orig == null) {
+ return null;
+ }
+ ByteBuffer copy = ByteBuffer.wrap(new byte[orig.remaining()]);
+ if (orig.hasArray()) {
+ System.arraycopy(orig.array(), orig.arrayOffset() + orig.position(), copy.array(), 0, orig.remaining());
+ } else {
+ orig.slice().get(copy.array());
+ }
+
+ return copy;
+ }
+
+ public static byte[] copyBinary(final byte[] orig) {
+ return (orig == null) ? null : Arrays.copyOf(orig, orig.length);
+ }
+
+ public static int hashCode(long value) {
+ return Long.hashCode(value);
+ }
+
+ public static int hashCode(double value) {
+ return Double.hashCode(value);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseProcessor.java
new file mode 100644
index 000000000..55a0f15d3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseProcessor.java
@@ -0,0 +1,41 @@
+package org.apache.thrift;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.thrift.protocol.TMessage;
+import org.apache.thrift.protocol.TMessageType;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolUtil;
+import org.apache.thrift.protocol.TType;
+
+public abstract class TBaseProcessor<I> implements TProcessor {
+ private final I iface;
+ private final Map<String,ProcessFunction<I, ? extends TBase>> processMap;
+
+ protected TBaseProcessor(I iface, Map<String, ProcessFunction<I, ? extends TBase>> processFunctionMap) {
+ this.iface = iface;
+ this.processMap = processFunctionMap;
+ }
+
+ public Map<String,ProcessFunction<I, ? extends TBase>> getProcessMapView() {
+ return Collections.unmodifiableMap(processMap);
+ }
+
+ @Override
+ public void process(TProtocol in, TProtocol out) throws TException {
+ TMessage msg = in.readMessageBegin();
+ ProcessFunction fn = processMap.get(msg.name);
+ if (fn == null) {
+ TProtocolUtil.skip(in, TType.STRUCT);
+ in.readMessageEnd();
+ TApplicationException x = new TApplicationException(TApplicationException.UNKNOWN_METHOD, "Invalid method name: '"+msg.name+"'");
+ out.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid));
+ x.write(out);
+ out.writeMessageEnd();
+ out.getTransport().flush();
+ } else {
+ fn.process(msg.seqid, in, out, iface);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TByteArrayOutputStream.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TByteArrayOutputStream.java
new file mode 100644
index 000000000..3a2d56c88
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TByteArrayOutputStream.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.Charset;
+
+/**
+ * Class that allows access to the underlying buf without doing deep
+ * copies on it.
+ *
+ */
+public class TByteArrayOutputStream extends ByteArrayOutputStream {
+
+ private final int initialSize;
+
+ public TByteArrayOutputStream(int size) {
+ super(size);
+ this.initialSize = size;
+ }
+
+ public TByteArrayOutputStream() {
+ this(32);
+ }
+
+ public byte[] get() {
+ return buf;
+ }
+
+ public void reset() {
+ super.reset();
+ if (buf.length > initialSize) {
+ buf = new byte[initialSize];
+ }
+ }
+
+ public int len() {
+ return count;
+ }
+
+ public String toString(Charset charset) {
+ return new String(buf, 0, count, charset);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TDeserializer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TDeserializer.java
new file mode 100644
index 000000000..d1d396609
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TDeserializer.java
@@ -0,0 +1,339 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TField;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.protocol.TProtocolUtil;
+import org.apache.thrift.protocol.TType;
+import org.apache.thrift.transport.TMemoryInputTransport;
+
+/**
+ * Generic utility for easily deserializing objects from a byte array or Java
+ * String.
+ *
+ */
+public class TDeserializer {
+ private final TProtocol protocol_;
+ private final TMemoryInputTransport trans_;
+
+ /**
+ * Create a new TDeserializer that uses the TBinaryProtocol by default.
+ */
+ public TDeserializer() {
+ this(new TBinaryProtocol.Factory());
+ }
+
+ /**
+ * Create a new TDeserializer. It will use the TProtocol specified by the
+ * factory that is passed in.
+ *
+ * @param protocolFactory Factory to create a protocol
+ */
+ public TDeserializer(TProtocolFactory protocolFactory) {
+ trans_ = new TMemoryInputTransport();
+ protocol_ = protocolFactory.getProtocol(trans_);
+ }
+
+ /**
+ * Deserialize the Thrift object from a byte array.
+ *
+ * @param base The object to read into
+ * @param bytes The array to read from
+ */
+ public void deserialize(TBase base, byte[] bytes) throws TException {
+ deserialize(base, bytes, 0, bytes.length);
+ }
+
+ /**
+ * Deserialize the Thrift object from a byte array.
+ *
+ * @param base The object to read into
+ * @param bytes The array to read from
+ * @param offset The offset into {@code bytes}
+ * @param length The length to read from {@code bytes}
+ */
+ public void deserialize(TBase base, byte[] bytes, int offset, int length) throws TException {
+ try {
+ trans_.reset(bytes, offset, length);
+ base.read(protocol_);
+ } finally {
+ trans_.clear();
+ protocol_.reset();
+ }
+ }
+
+ /**
+ * Deserialize the Thrift object from a Java string, using a specified
+ * character set for decoding.
+ *
+ * @param base The object to read into
+ * @param data The string to read from
+ * @param charset Valid JVM charset
+ */
+ public void deserialize(TBase base, String data, String charset) throws TException {
+ try {
+ deserialize(base, data.getBytes(charset));
+ } catch (UnsupportedEncodingException uex) {
+ throw new TException("JVM DOES NOT SUPPORT ENCODING: " + charset);
+ } finally {
+ protocol_.reset();
+ }
+ }
+
+ /**
+ * Deserialize only a single Thrift object (addressed by recursively using field id)
+ * from a byte record.
+ * @param tb The object to read into
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path tb
+ * @param fieldIdPathRest The rest FieldId's that define a path tb
+ * @throws TException
+ */
+ public void partialDeserialize(TBase tb, byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ try {
+ if (locateField(bytes, fieldIdPathFirst, fieldIdPathRest) != null) {
+ // if this line is reached, iprot will be positioned at the start of tb.
+ tb.read(protocol_);
+ }
+ } catch (Exception e) {
+ throw new TException(e);
+ } finally {
+ trans_.clear();
+ protocol_.reset();
+ }
+ }
+
+ /**
+ * Deserialize only a boolean field (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to a boolean field
+ * @param fieldIdPathRest The rest FieldId's that define a path to a boolean field
+ * @throws TException
+ */
+ public Boolean partialDeserializeBool(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ return (Boolean) partialDeserializeField(TType.BOOL, bytes, fieldIdPathFirst, fieldIdPathRest);
+ }
+
+ /**
+ * Deserialize only a byte field (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to a byte field
+ * @param fieldIdPathRest The rest FieldId's that define a path to a byte field
+ * @throws TException
+ */
+ public Byte partialDeserializeByte(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ return (Byte) partialDeserializeField(TType.BYTE, bytes, fieldIdPathFirst, fieldIdPathRest);
+ }
+
+ /**
+ * Deserialize only a double field (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to a double field
+ * @param fieldIdPathRest The rest FieldId's that define a path to a double field
+ * @throws TException
+ */
+ public Double partialDeserializeDouble(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ return (Double) partialDeserializeField(TType.DOUBLE, bytes, fieldIdPathFirst, fieldIdPathRest);
+ }
+
+ /**
+ * Deserialize only an i16 field (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to an i16 field
+ * @param fieldIdPathRest The rest FieldId's that define a path to an i16 field
+ * @throws TException
+ */
+ public Short partialDeserializeI16(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ return (Short) partialDeserializeField(TType.I16, bytes, fieldIdPathFirst, fieldIdPathRest);
+ }
+
+ /**
+ * Deserialize only an i32 field (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to an i32 field
+ * @param fieldIdPathRest The rest FieldId's that define a path to an i32 field
+ * @throws TException
+ */
+ public Integer partialDeserializeI32(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ return (Integer) partialDeserializeField(TType.I32, bytes, fieldIdPathFirst, fieldIdPathRest);
+ }
+
+ /**
+ * Deserialize only an i64 field (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to an i64 field
+ * @param fieldIdPathRest The rest FieldId's that define a path to an i64 field
+ * @throws TException
+ */
+ public Long partialDeserializeI64(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ return (Long) partialDeserializeField(TType.I64, bytes, fieldIdPathFirst, fieldIdPathRest);
+ }
+
+ /**
+ * Deserialize only a string field (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to a string field
+ * @param fieldIdPathRest The rest FieldId's that define a path to a string field
+ * @throws TException
+ */
+ public String partialDeserializeString(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ return (String) partialDeserializeField(TType.STRING, bytes, fieldIdPathFirst, fieldIdPathRest);
+ }
+
+ /**
+ * Deserialize only a binary field (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to a binary field
+ * @param fieldIdPathRest The rest FieldId's that define a path to a binary field
+ * @throws TException
+ */
+ public ByteBuffer partialDeserializeByteArray(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ // TType does not have binary, so we use the arbitrary num 100
+ return (ByteBuffer) partialDeserializeField((byte)100, bytes, fieldIdPathFirst, fieldIdPathRest);
+ }
+
+ /**
+ * Deserialize only the id of the field set in a TUnion (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to a TUnion
+ * @param fieldIdPathRest The rest FieldId's that define a path to a TUnion
+ * @throws TException
+ */
+ public Short partialDeserializeSetFieldIdInUnion(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ try {
+ TField field = locateField(bytes, fieldIdPathFirst, fieldIdPathRest);
+ if (field != null){
+ protocol_.readStructBegin(); // The Union
+ return protocol_.readFieldBegin().id; // The field set in the union
+ }
+ return null;
+ } catch (Exception e) {
+ throw new TException(e);
+ } finally {
+ trans_.clear();
+ protocol_.reset();
+ }
+ }
+
+ private Object partialDeserializeField(byte ttype, byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ try {
+ TField field = locateField(bytes, fieldIdPathFirst, fieldIdPathRest);
+ if (field != null) {
+ if (ttype == field.type) {
+ // if this point is reached, iprot will be positioned at the start of
+ // the field
+ switch (ttype) {
+ case TType.BOOL:
+ return protocol_.readBool();
+ case TType.BYTE:
+ return protocol_.readByte();
+ case TType.DOUBLE:
+ return protocol_.readDouble();
+ case TType.I16:
+ return protocol_.readI16();
+ case TType.I32:
+ return protocol_.readI32();
+ case TType.I64:
+ return protocol_.readI64();
+ case TType.STRING:
+ return protocol_.readString();
+ default:
+ return null;
+ }
+ }
+ // hack to differentiate between string and binary
+ if (ttype == 100 && field.type == TType.STRING) {
+ return protocol_.readBinary();
+ }
+ }
+ return null;
+ } catch (Exception e) {
+ throw new TException(e);
+ } finally {
+ trans_.clear();
+ protocol_.reset();
+ }
+ }
+
+ private TField locateField(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ trans_.reset(bytes);
+
+ TFieldIdEnum[] fieldIdPath = new TFieldIdEnum[fieldIdPathRest.length + 1];
+ fieldIdPath[0] = fieldIdPathFirst;
+ System.arraycopy(fieldIdPathRest, 0, fieldIdPath, 1, fieldIdPathRest.length);
+
+ // index into field ID path being currently searched for
+ int curPathIndex = 0;
+
+ // this will be the located field, or null if it is not located
+ TField field = null;
+
+ protocol_.readStructBegin();
+
+ while (curPathIndex < fieldIdPath.length) {
+ field = protocol_.readFieldBegin();
+ // we can stop searching if we either see a stop or we go past the field
+ // id we're looking for (since fields should now be serialized in asc
+ // order).
+ if (field.type == TType.STOP || field.id > fieldIdPath[curPathIndex].getThriftFieldId()) {
+ return null;
+ }
+
+ if (field.id != fieldIdPath[curPathIndex].getThriftFieldId()) {
+ // Not the field we're looking for. Skip field.
+ TProtocolUtil.skip(protocol_, field.type);
+ protocol_.readFieldEnd();
+ } else {
+ // This field is the next step in the path. Step into field.
+ curPathIndex++;
+ if (curPathIndex < fieldIdPath.length) {
+ protocol_.readStructBegin();
+ }
+ }
+ }
+ return field;
+ }
+
+ /**
+ * Deserialize the Thrift object from a Java string, using the default JVM
+ * charset encoding.
+ *
+ * @param base The object to read into
+ * @param data The string to read from
+ */
+ public void fromString(TBase base, String data) throws TException {
+ deserialize(base, data.getBytes());
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnum.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnum.java
new file mode 100644
index 000000000..325fdece7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnum.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+public interface TEnum {
+ public int getValue();
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnumHelper.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnumHelper.java
new file mode 100644
index 000000000..fbc778751
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnumHelper.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.lang.NoSuchMethodException;
+import java.lang.IllegalAccessException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Utility class with static methods for interacting with TEnum
+ */
+public class TEnumHelper {
+
+ /**
+ * Given a TEnum class and integer value, this method will return
+ * the associated constant from the given TEnum class.
+ * This method MUST be modified should the name of the 'findByValue' method
+ * change.
+ *
+ * @param enumClass TEnum from which to return a matching constant.
+ * @param value Value for which to return the constant.
+ *
+ * @return The constant in 'enumClass' whose value is 'value' or null if
+ * something went wrong.
+ */
+ public static TEnum getByValue(Class<? extends TEnum> enumClass, int value) {
+ try {
+ Method method = enumClass.getMethod("findByValue", int.class);
+ return (TEnum) method.invoke(null, value);
+ } catch (NoSuchMethodException nsme) {
+ return null;
+ } catch (IllegalAccessException iae) {
+ return null;
+ } catch (InvocationTargetException ite) {
+ return null;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TException.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TException.java
new file mode 100644
index 000000000..f84f4812e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TException.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+/**
+ * Generic exception class for Thrift.
+ *
+ */
+public class TException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public TException() {
+ super();
+ }
+
+ public TException(String message) {
+ super(message);
+ }
+
+ public TException(Throwable cause) {
+ super(cause);
+ }
+
+ public TException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldIdEnum.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldIdEnum.java
new file mode 100644
index 000000000..2956fba0b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldIdEnum.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift;
+
+/**
+ * Interface for all generated struct Fields objects.
+ */
+public interface TFieldIdEnum {
+ /**
+ * Get the Thrift field id for the named field.
+ */
+ public short getThriftFieldId();
+
+ /**
+ * Get the field's name, exactly as in the IDL.
+ */
+ public String getFieldName();
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldRequirementType.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldRequirementType.java
new file mode 100644
index 000000000..74bac4eff
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldRequirementType.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+/**
+ * Requirement type constants.
+ *
+ */
+public final class TFieldRequirementType {
+ public static final byte REQUIRED = 1;
+ public static final byte OPTIONAL = 2;
+ public static final byte DEFAULT = 3;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TMultiplexedProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TMultiplexedProcessor.java
new file mode 100644
index 000000000..c49486217
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TMultiplexedProcessor.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.*;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * <code>TMultiplexedProcessor</code> is a <code>TProcessor</code> allowing
+ * a single <code>TServer</code> to provide multiple services.
+ *
+ * <p>To do so, you instantiate the processor and then register additional
+ * processors with it, as shown in the following example:</p>
+ *
+ * <blockquote><code>
+ * TMultiplexedProcessor processor = new TMultiplexedProcessor();
+ *
+ * processor.registerProcessor(
+ * "Calculator",
+ * new Calculator.Processor(new CalculatorHandler()));
+ *
+ * processor.registerProcessor(
+ * "WeatherReport",
+ * new WeatherReport.Processor(new WeatherReportHandler()));
+ *
+ * TServerTransport t = new TServerSocket(9090);
+ * TSimpleServer server = new TSimpleServer(processor, t);
+ *
+ * server.serve();
+ * </code></blockquote>
+ */
+public class TMultiplexedProcessor implements TProcessor {
+
+ private final Map<String,TProcessor> SERVICE_PROCESSOR_MAP
+ = new HashMap<String,TProcessor>();
+ private TProcessor defaultProcessor;
+
+ /**
+ * 'Register' a service with this <code>TMultiplexedProcessor</code>. This
+ * allows us to broker requests to individual services by using the service
+ * name to select them at request time.
+ *
+ * @param serviceName Name of a service, has to be identical to the name
+ * declared in the Thrift IDL, e.g. "WeatherReport".
+ * @param processor Implementation of a service, usually referred to
+ * as "handlers", e.g. WeatherReportHandler implementing WeatherReport.Iface.
+ */
+ public void registerProcessor(String serviceName, TProcessor processor) {
+ SERVICE_PROCESSOR_MAP.put(serviceName, processor);
+ }
+
+ /**
+ * Register a service to be called to process queries without service name
+ * @param processor
+ */
+ public void registerDefault(TProcessor processor) {
+ defaultProcessor = processor;
+ }
+
+ /**
+ * This implementation of <code>process</code> performs the following steps:
+ *
+ * <ol>
+ * <li>Read the beginning of the message.</li>
+ * <li>Extract the service name from the message.</li>
+ * <li>Using the service name to locate the appropriate processor.</li>
+ * <li>Dispatch to the processor, with a decorated instance of TProtocol
+ * that allows readMessageBegin() to return the original TMessage.</li>
+ * </ol>
+ *
+ * @throws TProtocolException If the message type is not CALL or ONEWAY, if
+ * the service name was not found in the message, or if the service
+ * name was not found in the service map. You called {@link #registerProcessor(String, TProcessor) registerProcessor}
+ * during initialization, right? :)
+ */
+ public void process(TProtocol iprot, TProtocol oprot) throws TException {
+ /*
+ Use the actual underlying protocol (e.g. TBinaryProtocol) to read the
+ message header. This pulls the message "off the wire", which we'll
+ deal with at the end of this method.
+ */
+ TMessage message = iprot.readMessageBegin();
+
+ if (message.type != TMessageType.CALL && message.type != TMessageType.ONEWAY) {
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
+ "This should not have happened!?");
+ }
+
+ // Extract the service name
+ int index = message.name.indexOf(TMultiplexedProtocol.SEPARATOR);
+ if (index < 0) {
+ if (defaultProcessor != null) {
+ // Dispatch processing to the stored processor
+ defaultProcessor.process(new StoredMessageProtocol(iprot, message), oprot);
+ return;
+ }
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
+ "Service name not found in message name: " + message.name + ". Did you " +
+ "forget to use a TMultiplexProtocol in your client?");
+ }
+
+ // Create a new TMessage, something that can be consumed by any TProtocol
+ String serviceName = message.name.substring(0, index);
+ TProcessor actualProcessor = SERVICE_PROCESSOR_MAP.get(serviceName);
+ if (actualProcessor == null) {
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
+ "Service name not found: " + serviceName + ". Did you forget " +
+ "to call registerProcessor()?");
+ }
+
+ // Create a new TMessage, removing the service name
+ TMessage standardMessage = new TMessage(
+ message.name.substring(serviceName.length()+TMultiplexedProtocol.SEPARATOR.length()),
+ message.type,
+ message.seqid
+ );
+
+ // Dispatch processing to the stored processor
+ actualProcessor.process(new StoredMessageProtocol(iprot, standardMessage), oprot);
+ }
+
+ /**
+ * Our goal was to work with any protocol. In order to do that, we needed
+ * to allow them to call readMessageBegin() and get a TMessage in exactly
+ * the standard format, without the service name prepended to TMessage.name.
+ */
+ private static class StoredMessageProtocol extends TProtocolDecorator {
+ TMessage messageBegin;
+ public StoredMessageProtocol(TProtocol protocol, TMessage messageBegin) {
+ super(protocol);
+ this.messageBegin = messageBegin;
+ }
+ @Override
+ public TMessage readMessageBegin() throws TException {
+ return messageBegin;
+ }
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchClient.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchClient.java
new file mode 100644
index 000000000..382d978db
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchClient.java
@@ -0,0 +1,399 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.thrift;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+
+/**
+ * This class uses a single thread to set up non-blocking sockets to a set
+ * of remote servers (hostname and port pairs), and sends a same request to
+ * all these servers. It then fetches responses from servers.
+ *
+ * Parameters:
+ * int maxRecvBufBytesPerServer - an upper limit for receive buffer size
+ * per server (in byte). If a response from a server exceeds this limit, the
+ * client will not allocate memory or read response data for it.
+ *
+ * int fetchTimeoutSeconds - time limit for fetching responses from all
+ * servers (in second). After the timeout, the fetch job is stopped and
+ * available responses are returned.
+ *
+ * ByteBuffer requestBuf - request message that is sent to all servers.
+ *
+ * Output:
+ * Responses are stored in an array of ByteBuffers. Index of elements in
+ * this array corresponds to index of servers in the server list. Content in
+ * a ByteBuffer may be in one of the following forms:
+ * 1. First 4 bytes form an integer indicating length of following data,
+ * then followed by the data.
+ * 2. First 4 bytes form an integer indicating length of following data,
+ * then followed by nothing - this happens when the response data size
+ * exceeds maxRecvBufBytesPerServer, and the client will not read any
+ * response data.
+ * 3. No data in the ByteBuffer - this happens when the server does not
+ * return any response within fetchTimeoutSeconds.
+ *
+ * In some special cases (no servers are given, fetchTimeoutSeconds less
+ * than or equal to 0, requestBuf is null), the return is null.
+ *
+ * Note:
+ * It assumes all remote servers are TNonblockingServers and use
+ * TFramedTransport.
+ *
+ */
+public class TNonblockingMultiFetchClient {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(
+ TNonblockingMultiFetchClient.class.getName()
+ );
+
+ // if the size of the response msg exceeds this limit (in byte), we will
+ // not read the msg
+ private int maxRecvBufBytesPerServer;
+
+ // time limit for fetching data from all servers (in second)
+ private int fetchTimeoutSeconds;
+
+ // store request that will be sent to servers
+ private ByteBuffer requestBuf;
+ private ByteBuffer requestBufDuplication;
+
+ // a list of remote servers
+ private List<InetSocketAddress> servers;
+
+ // store fetch results
+ private TNonblockingMultiFetchStats stats;
+ private ByteBuffer[] recvBuf;
+
+ public TNonblockingMultiFetchClient(int maxRecvBufBytesPerServer,
+ int fetchTimeoutSeconds, ByteBuffer requestBuf,
+ List<InetSocketAddress> servers) {
+ this.maxRecvBufBytesPerServer = maxRecvBufBytesPerServer;
+ this.fetchTimeoutSeconds = fetchTimeoutSeconds;
+ this.requestBuf = requestBuf;
+ this.servers = servers;
+
+ stats = new TNonblockingMultiFetchStats();
+ recvBuf = null;
+ }
+
+ public synchronized int getMaxRecvBufBytesPerServer() {
+ return maxRecvBufBytesPerServer;
+ }
+
+ public synchronized int getFetchTimeoutSeconds() {
+ return fetchTimeoutSeconds;
+ }
+
+ /**
+ * return a duplication of requestBuf, so that requestBuf will not
+ * be modified by others.
+ */
+ public synchronized ByteBuffer getRequestBuf() {
+ if (requestBuf == null) {
+ return null;
+ } else {
+ if (requestBufDuplication == null) {
+ requestBufDuplication = requestBuf.duplicate();
+ }
+ return requestBufDuplication;
+ }
+ }
+
+ public synchronized List<InetSocketAddress> getServerList() {
+ if (servers == null) {
+ return null;
+ }
+ return Collections.unmodifiableList(servers);
+ }
+
+ public synchronized TNonblockingMultiFetchStats getFetchStats() {
+ return stats;
+ }
+
+ /**
+ * main entry function for fetching from servers
+ */
+ public synchronized ByteBuffer[] fetch() {
+ // clear previous results
+ recvBuf = null;
+ stats.clear();
+
+ if (servers == null || servers.size() == 0 ||
+ requestBuf == null || fetchTimeoutSeconds <= 0) {
+ return recvBuf;
+ }
+
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ MultiFetch multiFetch = new MultiFetch();
+ FutureTask<?> task = new FutureTask(multiFetch, null);
+ executor.execute(task);
+ try {
+ task.get(fetchTimeoutSeconds, TimeUnit.SECONDS);
+ } catch(InterruptedException ie) {
+ // attempt to cancel execution of the task.
+ task.cancel(true);
+ LOGGER.error("interrupted during fetch: "+ie.toString());
+ } catch(ExecutionException ee) {
+ // attempt to cancel execution of the task.
+ task.cancel(true);
+ LOGGER.error("exception during fetch: "+ee.toString());
+ } catch(TimeoutException te) {
+ // attempt to cancel execution of the task.
+ task.cancel(true);
+ LOGGER.error("timeout for fetch: "+te.toString());
+ }
+
+ executor.shutdownNow();
+ multiFetch.close();
+ return recvBuf;
+ }
+
+ /**
+ * Private class that does real fetch job.
+ * Users are not allowed to directly use this class, as its run()
+ * function may run forever.
+ */
+ private class MultiFetch implements Runnable {
+ private Selector selector;
+
+ /**
+ * main entry function for fetching.
+ *
+ * Server responses are stored in TNonblocingMultiFetchClient.recvBuf,
+ * and fetch statistics is in TNonblockingMultiFetchClient.stats.
+ *
+ * Sanity check for parameters has been done in
+ * TNonblockingMultiFetchClient before calling this function.
+ */
+ public void run() {
+ long t1 = System.currentTimeMillis();
+
+ int numTotalServers = servers.size();
+ stats.setNumTotalServers(numTotalServers);
+
+ // buffer for receiving response from servers
+ recvBuf = new ByteBuffer[numTotalServers];
+ // buffer for sending request
+ ByteBuffer sendBuf[] = new ByteBuffer[numTotalServers];
+ long numBytesRead[] = new long[numTotalServers];
+ int frameSize[] = new int[numTotalServers];
+ boolean hasReadFrameSize[] = new boolean[numTotalServers];
+
+ try {
+ selector = Selector.open();
+ } catch (IOException e) {
+ LOGGER.error("selector opens error: "+e.toString());
+ return;
+ }
+
+ for (int i = 0; i < numTotalServers; i++) {
+ // create buffer to send request to server.
+ sendBuf[i] = requestBuf.duplicate();
+ // create buffer to read response's frame size from server
+ recvBuf[i] = ByteBuffer.allocate(4);
+ stats.incTotalRecvBufBytes(4);
+
+ InetSocketAddress server = servers.get(i);
+ SocketChannel s = null;
+ SelectionKey key = null;
+ try {
+ s = SocketChannel.open();
+ s.configureBlocking(false);
+ // now this method is non-blocking
+ s.connect(server);
+ key = s.register(selector, s.validOps());
+ // attach index of the key
+ key.attach(i);
+ } catch (Exception e) {
+ stats.incNumConnectErrorServers();
+ String err = String.format("set up socket to server %s error: %s",
+ server.toString(), e.toString());
+ LOGGER.error(err);
+ // free resource
+ if (s != null) {
+ try {s.close();} catch (Exception ex) {}
+ }
+ if (key != null) {
+ key.cancel();
+ }
+ }
+ }
+
+ // wait for events
+ while (stats.getNumReadCompletedServers() +
+ stats.getNumConnectErrorServers() < stats.getNumTotalServers()) {
+ // if the thread is interrupted (e.g., task is cancelled)
+ if (Thread.currentThread().isInterrupted()) {
+ return;
+ }
+
+ try{
+ selector.select();
+ } catch (Exception e) {
+ LOGGER.error("selector selects error: "+e.toString());
+ continue;
+ }
+
+ Iterator<SelectionKey> it = selector.selectedKeys().iterator();
+ while (it.hasNext()) {
+ SelectionKey selKey = it.next();
+ it.remove();
+
+ // get previously attached index
+ int index = (Integer)selKey.attachment();
+
+ if (selKey.isValid() && selKey.isConnectable()) {
+ // if this socket throws an exception (e.g., connection refused),
+ // print error msg and skip it.
+ try {
+ SocketChannel sChannel = (SocketChannel)selKey.channel();
+ sChannel.finishConnect();
+ } catch (Exception e) {
+ stats.incNumConnectErrorServers();
+ String err = String.format("socket %d connects to server %s " +
+ "error: %s",
+ index, servers.get(index).toString(), e.toString());
+ LOGGER.error(err);
+ }
+ }
+
+ if (selKey.isValid() && selKey.isWritable()) {
+ if (sendBuf[index].hasRemaining()) {
+ // if this socket throws an exception, print error msg and
+ // skip it.
+ try {
+ SocketChannel sChannel = (SocketChannel)selKey.channel();
+ sChannel.write(sendBuf[index]);
+ } catch (Exception e) {
+ String err = String.format("socket %d writes to server %s " +
+ "error: %s",
+ index, servers.get(index).toString(), e.toString());
+ LOGGER.error(err);
+ }
+ }
+ }
+
+ if (selKey.isValid() && selKey.isReadable()) {
+ // if this socket throws an exception, print error msg and
+ // skip it.
+ try {
+ SocketChannel sChannel = (SocketChannel)selKey.channel();
+ int bytesRead = sChannel.read(recvBuf[index]);
+
+ if (bytesRead > 0) {
+ numBytesRead[index] += bytesRead;
+
+ if (!hasReadFrameSize[index] &&
+ recvBuf[index].remaining()==0) {
+ // if the frame size has been read completely, then prepare
+ // to read the actual frame.
+ frameSize[index] = recvBuf[index].getInt(0);
+
+ if (frameSize[index] <= 0) {
+ stats.incNumInvalidFrameSize();
+ String err = String.format("Read an invalid frame size %d"
+ + " from %s. Does the server use TFramedTransport? ",
+ frameSize[index], servers.get(index).toString());
+ LOGGER.error(err);
+ sChannel.close();
+ continue;
+ }
+
+ if (frameSize[index] + 4 > stats.getMaxResponseBytes()) {
+ stats.setMaxResponseBytes(frameSize[index]+4);
+ }
+
+ if (frameSize[index] + 4 > maxRecvBufBytesPerServer) {
+ stats.incNumOverflowedRecvBuf();
+ String err = String.format("Read frame size %d from %s,"
+ + " total buffer size would exceed limit %d",
+ frameSize[index], servers.get(index).toString(),
+ maxRecvBufBytesPerServer);
+ LOGGER.error(err);
+ sChannel.close();
+ continue;
+ }
+
+ // reallocate buffer for actual frame data
+ recvBuf[index] = ByteBuffer.allocate(frameSize[index] + 4);
+ recvBuf[index].putInt(frameSize[index]);
+
+ stats.incTotalRecvBufBytes(frameSize[index]);
+ hasReadFrameSize[index] = true;
+ }
+
+ if (hasReadFrameSize[index] &&
+ numBytesRead[index] >= frameSize[index]+4) {
+ // has read all data
+ sChannel.close();
+ stats.incNumReadCompletedServers();
+ long t2 = System.currentTimeMillis();
+ stats.setReadTime(t2-t1);
+ }
+ }
+ } catch (Exception e) {
+ String err = String.format("socket %d reads from server %s " +
+ "error: %s",
+ index, servers.get(index).toString(), e.toString());
+ LOGGER.error(err);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * dispose any resource allocated
+ */
+ public void close() {
+ try {
+ if (selector.isOpen()) {
+ Iterator<SelectionKey> it = selector.keys().iterator();
+ while (it.hasNext()) {
+ SelectionKey selKey = it.next();
+ SocketChannel sChannel = (SocketChannel)selKey.channel();
+ sChannel.close();
+ }
+
+ selector.close();
+ }
+ } catch (IOException e) {
+ LOGGER.error("free resource error: "+e.toString());
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchStats.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchStats.java
new file mode 100644
index 000000000..90b86208b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchStats.java
@@ -0,0 +1,80 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.thrift;
+
+/**
+ * This class keeps track of statistics for TNonblockinMultiFetchClient.
+ */
+public class TNonblockingMultiFetchStats {
+ private int numTotalServers;
+ private int numReadCompletedServers;
+ private int numConnectErrorServers;
+ private int totalRecvBufBytes;
+ private int maxResponseBytes;
+ private int numOverflowedRecvBuf;
+ private int numInvalidFrameSize;
+ // time from the beginning of fetch() function to the reading finish
+ // time of the last socket (in milli-second)
+ private long readTime;
+
+ public TNonblockingMultiFetchStats() {
+ clear();
+ }
+
+ public void clear() {
+ numTotalServers = 0;
+ numReadCompletedServers = 0;
+ numConnectErrorServers = 0;
+ totalRecvBufBytes = 0;
+ maxResponseBytes = 0;
+ numOverflowedRecvBuf = 0;
+ numInvalidFrameSize = 0;
+ readTime = 0;
+ }
+
+ public String toString() {
+ String stats = String.format("numTotalServers=%d, " +
+ "numReadCompletedServers=%d, numConnectErrorServers=%d, " +
+ "numUnresponsiveServers=%d, totalRecvBufBytes=%fM, " +
+ "maxResponseBytes=%d, numOverflowedRecvBuf=%d, " +
+ "numInvalidFrameSize=%d, readTime=%dms",
+ numTotalServers, numReadCompletedServers, numConnectErrorServers,
+ (numTotalServers-numReadCompletedServers-numConnectErrorServers),
+ totalRecvBufBytes/1024.0/1024, maxResponseBytes, numOverflowedRecvBuf,
+ numInvalidFrameSize, readTime);
+ return stats;
+ }
+
+ public void setNumTotalServers(int val) { numTotalServers = val; }
+ public void setMaxResponseBytes(int val) { maxResponseBytes = val; }
+ public void setReadTime(long val) { readTime = val; }
+ public void incNumReadCompletedServers() { numReadCompletedServers++; }
+ public void incNumConnectErrorServers() { numConnectErrorServers++; }
+ public void incNumOverflowedRecvBuf() { numOverflowedRecvBuf++; }
+ public void incTotalRecvBufBytes(int val) { totalRecvBufBytes += val; }
+ public void incNumInvalidFrameSize() { numInvalidFrameSize++; }
+
+ public int getMaxResponseBytes() { return maxResponseBytes; }
+ public int getNumReadCompletedServers() { return numReadCompletedServers; }
+ public int getNumConnectErrorServers() { return numConnectErrorServers; }
+ public int getNumTotalServers() { return numTotalServers; }
+ public int getNumOverflowedRecvBuf() { return numOverflowedRecvBuf;}
+ public int getTotalRecvBufBytes() { return totalRecvBufBytes;}
+ public int getNumInvalidFrameSize() { return numInvalidFrameSize; }
+ public long getReadTime() { return readTime; }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessor.java
new file mode 100644
index 000000000..15ba9c0fe
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessor.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TProtocol;
+
+/**
+ * A processor is a generic object which operates upon an input stream and
+ * writes to some output stream.
+ */
+public interface TProcessor {
+ public void process(TProtocol in, TProtocol out) throws TException;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessorFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessorFactory.java
new file mode 100644
index 000000000..81933a211
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessorFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * The default processor factory just returns a singleton
+ * instance.
+ */
+public class TProcessorFactory {
+
+ private final TProcessor processor_;
+
+ public TProcessorFactory(TProcessor processor) {
+ processor_ = processor;
+ }
+
+ public TProcessor getProcessor(TTransport trans) {
+ return processor_;
+ }
+
+ public boolean isAsyncProcessor() {
+ return processor_ instanceof TAsyncProcessor;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializable.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializable.java
new file mode 100644
index 000000000..80002c761
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializable.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TProtocol;
+
+/**
+ * Generic base interface for generated Thrift objects.
+ *
+ */
+public interface TSerializable {
+
+ /**
+ * Reads the TObject from the given input protocol.
+ *
+ * @param iprot Input protocol
+ */
+ public void read(TProtocol iprot) throws TException;
+
+ /**
+ * Writes the objects out to the protocol
+ *
+ * @param oprot Output protocol
+ */
+ public void write(TProtocol oprot) throws TException;
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializer.java
new file mode 100644
index 000000000..4e1ce6129
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializer.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TIOStreamTransport;
+
+/**
+ * Generic utility for easily serializing objects into a byte array or Java
+ * String.
+ *
+ */
+public class TSerializer {
+
+ /**
+ * This is the byte array that data is actually serialized into
+ */
+ private final ByteArrayOutputStream baos_ = new ByteArrayOutputStream();
+
+ /**
+ * This transport wraps that byte array
+ */
+ private final TIOStreamTransport transport_ = new TIOStreamTransport(baos_);
+
+ /**
+ * Internal protocol used for serializing objects.
+ */
+ private TProtocol protocol_;
+
+ /**
+ * Create a new TSerializer that uses the TBinaryProtocol by default.
+ */
+ public TSerializer() {
+ this(new TBinaryProtocol.Factory());
+ }
+
+ /**
+ * Create a new TSerializer. It will use the TProtocol specified by the
+ * factory that is passed in.
+ *
+ * @param protocolFactory Factory to create a protocol
+ */
+ public TSerializer(TProtocolFactory protocolFactory) {
+ protocol_ = protocolFactory.getProtocol(transport_);
+ }
+
+ /**
+ * Serialize the Thrift object into a byte array. The process is simple,
+ * just clear the byte array output, write the object into it, and grab the
+ * raw bytes.
+ *
+ * @param base The object to serialize
+ * @return Serialized object in byte[] format
+ */
+ public byte[] serialize(TBase base) throws TException {
+ baos_.reset();
+ base.write(protocol_);
+ return baos_.toByteArray();
+ }
+
+ /**
+ * Serialize the Thrift object into a Java string, using a specified
+ * character set for encoding.
+ *
+ * @param base The object to serialize
+ * @param charset Valid JVM charset
+ * @return Serialized object as a String
+ */
+ public String toString(TBase base, String charset) throws TException {
+ try {
+ return new String(serialize(base), charset);
+ } catch (UnsupportedEncodingException uex) {
+ throw new TException("JVM DOES NOT SUPPORT ENCODING: " + charset);
+ }
+ }
+
+ /**
+ * Serialize the Thrift object into a Java string, using the default JVM
+ * charset encoding.
+ *
+ * @param base The object to serialize
+ * @return Serialized object as a String
+ */
+ public String toString(TBase base) throws TException {
+ return new String(serialize(base));
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClient.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClient.java
new file mode 100644
index 000000000..00a36ee7f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClient.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TMessage;
+import org.apache.thrift.protocol.TMessageType;
+import org.apache.thrift.protocol.TProtocol;
+
+/**
+ * A TServiceClient is used to communicate with a TService implementation
+ * across protocols and transports.
+ */
+public abstract class TServiceClient {
+ public TServiceClient(TProtocol prot) {
+ this(prot, prot);
+ }
+
+ public TServiceClient(TProtocol iprot, TProtocol oprot) {
+ iprot_ = iprot;
+ oprot_ = oprot;
+ }
+
+ protected TProtocol iprot_;
+ protected TProtocol oprot_;
+
+ protected int seqid_;
+
+ /**
+ * Get the TProtocol being used as the input (read) protocol.
+ * @return the TProtocol being used as the input (read) protocol.
+ */
+ public TProtocol getInputProtocol() {
+ return this.iprot_;
+ }
+
+ /**
+ * Get the TProtocol being used as the output (write) protocol.
+ * @return the TProtocol being used as the output (write) protocol.
+ */
+ public TProtocol getOutputProtocol() {
+ return this.oprot_;
+ }
+
+ protected void sendBase(String methodName, TBase<?,?> args) throws TException {
+ sendBase(methodName, args, TMessageType.CALL);
+ }
+
+ protected void sendBaseOneway(String methodName, TBase<?,?> args) throws TException {
+ sendBase(methodName, args, TMessageType.ONEWAY);
+ }
+
+ private void sendBase(String methodName, TBase<?,?> args, byte type) throws TException {
+ oprot_.writeMessageBegin(new TMessage(methodName, type, ++seqid_));
+ args.write(oprot_);
+ oprot_.writeMessageEnd();
+ oprot_.getTransport().flush();
+ }
+
+ protected void receiveBase(TBase<?,?> result, String methodName) throws TException {
+ TMessage msg = iprot_.readMessageBegin();
+ if (msg.type == TMessageType.EXCEPTION) {
+ TApplicationException x = new TApplicationException();
+ x.read(iprot_);
+ iprot_.readMessageEnd();
+ throw x;
+ }
+ if (msg.seqid != seqid_) {
+ throw new TApplicationException(TApplicationException.BAD_SEQUENCE_ID,
+ String.format("%s failed: out of sequence response: expected %d but got %d", methodName, seqid_, msg.seqid));
+ }
+ result.read(iprot_);
+ iprot_.readMessageEnd();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClientFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClientFactory.java
new file mode 100644
index 000000000..988e65591
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClientFactory.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TProtocol;
+
+/**
+ * A TServiceClientFactory provides a general way to get a TServiceClient
+ * connected to a remote TService via a protocol.
+ * @param <T>
+ */
+public interface TServiceClientFactory<T extends TServiceClient> {
+ /**
+ * Get a brand-new T using <i>prot</i> as both the input and output protocol.
+ * @param prot
+ * @return A brand-new T using <i>prot</i> as both the input and output protocol.
+ */
+ public T getClient(TProtocol prot);
+
+ /**
+ * Get a brand new T using the specified input and output protocols. The
+ * input and output protocols may be the same instance.
+ * @param iprot
+ * @param oprot
+ * @return a brand new T using the specified input and output protocols
+ */
+ public T getClient(TProtocol iprot, TProtocol oprot);
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TUnion.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TUnion.java
new file mode 100644
index 000000000..1ef11df49
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TUnion.java
@@ -0,0 +1,279 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.thrift;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.thrift.protocol.TField;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolException;
+import org.apache.thrift.protocol.TStruct;
+import org.apache.thrift.scheme.IScheme;
+import org.apache.thrift.scheme.SchemeFactory;
+import org.apache.thrift.scheme.StandardScheme;
+import org.apache.thrift.scheme.TupleScheme;
+
+public abstract class TUnion<T extends TUnion<T,F>, F extends TFieldIdEnum> implements TBase<T, F> {
+
+ protected Object value_;
+ protected F setField_;
+
+ protected TUnion() {
+ setField_ = null;
+ value_ = null;
+ }
+
+ private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
+ static {
+ schemes.put(StandardScheme.class, new TUnionStandardSchemeFactory());
+ schemes.put(TupleScheme.class, new TUnionTupleSchemeFactory());
+ }
+
+ protected TUnion(F setField, Object value) {
+ setFieldValue(setField, value);
+ }
+
+ protected TUnion(TUnion<T, F> other) {
+ if (!other.getClass().equals(this.getClass())) {
+ throw new ClassCastException();
+ }
+ setField_ = other.setField_;
+ value_ = deepCopyObject(other.value_);
+ }
+
+ private static Object deepCopyObject(Object o) {
+ if (o instanceof TBase) {
+ return ((TBase)o).deepCopy();
+ } else if (o instanceof ByteBuffer) {
+ return TBaseHelper.copyBinary((ByteBuffer)o);
+ } else if (o instanceof List) {
+ return deepCopyList((List)o);
+ } else if (o instanceof Set) {
+ return deepCopySet((Set)o);
+ } else if (o instanceof Map) {
+ return deepCopyMap((Map)o);
+ } else {
+ return o;
+ }
+ }
+
+ private static Map deepCopyMap(Map<Object, Object> map) {
+ Map copy = new HashMap(map.size());
+ for (Map.Entry<Object, Object> entry : map.entrySet()) {
+ copy.put(deepCopyObject(entry.getKey()), deepCopyObject(entry.getValue()));
+ }
+ return copy;
+ }
+
+ private static Set deepCopySet(Set set) {
+ Set copy = new HashSet(set.size());
+ for (Object o : set) {
+ copy.add(deepCopyObject(o));
+ }
+ return copy;
+ }
+
+ private static List deepCopyList(List list) {
+ List copy = new ArrayList(list.size());
+ for (Object o : list) {
+ copy.add(deepCopyObject(o));
+ }
+ return copy;
+ }
+
+ public F getSetField() {
+ return setField_;
+ }
+
+ public Object getFieldValue() {
+ return value_;
+ }
+
+ public Object getFieldValue(F fieldId) {
+ if (fieldId != setField_) {
+ throw new IllegalArgumentException("Cannot get the value of field " + fieldId + " because union's set field is " + setField_);
+ }
+
+ return getFieldValue();
+ }
+
+ public Object getFieldValue(int fieldId) {
+ return getFieldValue(enumForId((short)fieldId));
+ }
+
+ public boolean isSet() {
+ return setField_ != null;
+ }
+
+ public boolean isSet(F fieldId) {
+ return setField_ == fieldId;
+ }
+
+ public boolean isSet(int fieldId) {
+ return isSet(enumForId((short)fieldId));
+ }
+
+ public void read(TProtocol iprot) throws TException {
+ schemes.get(iprot.getScheme()).getScheme().read(iprot, this);
+ }
+
+ public void setFieldValue(F fieldId, Object value) {
+ checkType(fieldId, value);
+ setField_ = fieldId;
+ value_ = value;
+ }
+
+ public void setFieldValue(int fieldId, Object value) {
+ setFieldValue(enumForId((short)fieldId), value);
+ }
+
+ public void write(TProtocol oprot) throws TException {
+ schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
+ }
+
+ /**
+ * Implementation should be generated so that we can efficiently type check
+ * various values.
+ * @param setField
+ * @param value
+ */
+ protected abstract void checkType(F setField, Object value) throws ClassCastException;
+
+ /**
+ * Implementation should be generated to read the right stuff from the wire
+ * based on the field header.
+ * @param field
+ * @return read Object based on the field header, as specified by the argument.
+ */
+ protected abstract Object standardSchemeReadValue(TProtocol iprot, TField field) throws TException;
+ protected abstract void standardSchemeWriteValue(TProtocol oprot) throws TException;
+
+ protected abstract Object tupleSchemeReadValue(TProtocol iprot, short fieldID) throws TException;
+ protected abstract void tupleSchemeWriteValue(TProtocol oprot) throws TException;
+
+ protected abstract TStruct getStructDesc();
+
+ protected abstract TField getFieldDesc(F setField);
+
+ protected abstract F enumForId(short id);
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("<");
+ sb.append(this.getClass().getSimpleName());
+ sb.append(" ");
+
+ if (getSetField() != null) {
+ Object v = getFieldValue();
+ sb.append(getFieldDesc(getSetField()).name);
+ sb.append(":");
+ if(v instanceof ByteBuffer) {
+ TBaseHelper.toString((ByteBuffer)v, sb);
+ } else {
+ sb.append(v.toString());
+ }
+ }
+ sb.append(">");
+ return sb.toString();
+ }
+
+ public final void clear() {
+ this.setField_ = null;
+ this.value_ = null;
+ }
+
+ private static class TUnionStandardSchemeFactory implements SchemeFactory {
+ public TUnionStandardScheme getScheme() {
+ return new TUnionStandardScheme();
+ }
+ }
+
+ private static class TUnionStandardScheme extends StandardScheme<TUnion> {
+
+ @Override
+ public void read(TProtocol iprot, TUnion struct) throws TException {
+ struct.setField_ = null;
+ struct.value_ = null;
+
+ iprot.readStructBegin();
+
+ TField field = iprot.readFieldBegin();
+
+ struct.value_ = struct.standardSchemeReadValue(iprot, field);
+ if (struct.value_ != null) {
+ struct.setField_ = struct.enumForId(field.id);
+ }
+
+ iprot.readFieldEnd();
+ // this is so that we will eat the stop byte. we could put a check here to
+ // make sure that it actually *is* the stop byte, but it's faster to do it
+ // this way.
+ iprot.readFieldBegin();
+ iprot.readStructEnd();
+ }
+
+ @Override
+ public void write(TProtocol oprot, TUnion struct) throws TException {
+ if (struct.getSetField() == null || struct.getFieldValue() == null) {
+ throw new TProtocolException("Cannot write a TUnion with no set value!");
+ }
+ oprot.writeStructBegin(struct.getStructDesc());
+ oprot.writeFieldBegin(struct.getFieldDesc(struct.setField_));
+ struct.standardSchemeWriteValue(oprot);
+ oprot.writeFieldEnd();
+ oprot.writeFieldStop();
+ oprot.writeStructEnd();
+ }
+ }
+
+ private static class TUnionTupleSchemeFactory implements SchemeFactory {
+ public TUnionTupleScheme getScheme() {
+ return new TUnionTupleScheme();
+ }
+ }
+
+ private static class TUnionTupleScheme extends TupleScheme<TUnion> {
+
+ @Override
+ public void read(TProtocol iprot, TUnion struct) throws TException {
+ struct.setField_ = null;
+ struct.value_ = null;
+ short fieldID = iprot.readI16();
+ struct.value_ = struct.tupleSchemeReadValue(iprot, fieldID);
+ if (struct.value_ != null) {
+ struct.setField_ = struct.enumForId(fieldID);
+ }
+ }
+
+ @Override
+ public void write(TProtocol oprot, TUnion struct) throws TException {
+ if (struct.getSetField() == null || struct.getFieldValue() == null) {
+ throw new TProtocolException("Cannot write a TUnion with no set value!");
+ }
+ oprot.writeI16(struct.setField_.getThriftFieldId());
+ struct.tupleSchemeWriteValue(oprot);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/annotation/Nullable.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/annotation/Nullable.java
new file mode 100644
index 000000000..a34b01ebb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/annotation/Nullable.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Annotation indicating a field, method return, or method parameter may be {@code null}.
+ * We package our own annotation to avoid a mandatory third-party dependency.
+ */
+@Retention(RetentionPolicy.CLASS)
+public @interface Nullable {
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/AsyncMethodCallback.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/AsyncMethodCallback.java
new file mode 100644
index 000000000..4ebde0741
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/AsyncMethodCallback.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.async;
+
+/**
+ * A handler interface asynchronous clients can implement to receive future
+ * notice of the results of an asynchronous method call.
+ *
+ * @param <T> The return type of the asynchronously invoked method.
+ */
+public interface AsyncMethodCallback<T> {
+ /**
+ * This method will be called when the remote side has completed invoking
+ * your method call and the result is fully read. For {@code oneway} method
+ * calls, this method will be called as soon as we have completed writing out
+ * the request.
+ *
+ * @param response The return value of the asynchronously invoked method;
+ * {@code null} for void methods which includes
+ * {@code oneway} methods.
+ */
+ void onComplete(T response);
+
+ /**
+ * This method will be called when there is either an unexpected client-side
+ * exception like an IOException or else when the remote method raises an
+ * exception, either declared in the IDL or due to an unexpected server-side
+ * error.
+ *
+ * @param exception The exception encountered processing the the asynchronous
+ * method call, may be a local exception or an unmarshalled
+ * remote exception.
+ */
+ void onError(Exception exception);
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClient.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClient.java
new file mode 100644
index 000000000..005018a83
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClient.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.async;
+
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TNonblockingTransport;
+
+public abstract class TAsyncClient {
+ protected final TProtocolFactory ___protocolFactory;
+ protected final TNonblockingTransport ___transport;
+ protected final TAsyncClientManager ___manager;
+ protected TAsyncMethodCall ___currentMethod;
+ private Exception ___error;
+ private long ___timeout;
+
+ public TAsyncClient(TProtocolFactory protocolFactory, TAsyncClientManager manager, TNonblockingTransport transport) {
+ this(protocolFactory, manager, transport, 0);
+ }
+
+ public TAsyncClient(TProtocolFactory protocolFactory, TAsyncClientManager manager, TNonblockingTransport transport, long timeout) {
+ this.___protocolFactory = protocolFactory;
+ this.___manager = manager;
+ this.___transport = transport;
+ this.___timeout = timeout;
+ }
+
+ public TProtocolFactory getProtocolFactory() {
+ return ___protocolFactory;
+ }
+
+ public long getTimeout() {
+ return ___timeout;
+ }
+
+ public boolean hasTimeout() {
+ return ___timeout > 0;
+ }
+
+ public void setTimeout(long timeout) {
+ this.___timeout = timeout;
+ }
+
+ /**
+ * Is the client in an error state?
+ * @return If client in an error state?
+ */
+ public boolean hasError() {
+ return ___error != null;
+ }
+
+ /**
+ * Get the client's error - returns null if no error
+ * @return Get the client's error. <p> returns null if no error
+ */
+ public Exception getError() {
+ return ___error;
+ }
+
+ protected void checkReady() {
+ // Ensure we are not currently executing a method
+ if (___currentMethod != null) {
+ throw new IllegalStateException("Client is currently executing another method: " + ___currentMethod.getClass().getName());
+ }
+
+ // Ensure we're not in an error state
+ if (___error != null) {
+ throw new IllegalStateException("Client has an error!", ___error);
+ }
+ }
+
+ /**
+ * Called by delegate method when finished
+ */
+ protected void onComplete() {
+ ___currentMethod = null;
+ }
+
+ /**
+ * Called by delegate method on error
+ */
+ protected void onError(Exception exception) {
+ ___transport.close();
+ ___currentMethod = null;
+ ___error = exception;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientFactory.java
new file mode 100644
index 000000000..28feb73d1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientFactory.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.async;
+
+import org.apache.thrift.transport.TNonblockingTransport;
+
+public interface TAsyncClientFactory<T extends TAsyncClient> {
+ public T getAsyncClient(TNonblockingTransport transport);
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientManager.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientManager.java
new file mode 100644
index 000000000..c07ccd540
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientManager.java
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.async;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.nio.channels.ClosedSelectorException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.thrift.TException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Contains selector thread which transitions method call objects
+ */
+public class TAsyncClientManager {
+ private static final Logger LOGGER = LoggerFactory.getLogger(TAsyncClientManager.class.getName());
+
+ private final SelectThread selectThread;
+ private final ConcurrentLinkedQueue<TAsyncMethodCall> pendingCalls = new ConcurrentLinkedQueue<TAsyncMethodCall>();
+
+ public TAsyncClientManager() throws IOException {
+ this.selectThread = new SelectThread();
+ selectThread.start();
+ }
+
+ public void call(TAsyncMethodCall method) throws TException {
+ if (!isRunning()) {
+ throw new TException("SelectThread is not running");
+ }
+ method.prepareMethodCall();
+ pendingCalls.add(method);
+ selectThread.getSelector().wakeup();
+ }
+
+ public void stop() {
+ selectThread.finish();
+ }
+
+ public boolean isRunning() {
+ return selectThread.isAlive();
+ }
+
+ private class SelectThread extends Thread {
+ private final Selector selector;
+ private volatile boolean running;
+ private final TreeSet<TAsyncMethodCall> timeoutWatchSet = new TreeSet<TAsyncMethodCall>(new TAsyncMethodCallTimeoutComparator());
+
+ public SelectThread() throws IOException {
+ this.selector = SelectorProvider.provider().openSelector();
+ this.running = true;
+ this.setName("TAsyncClientManager#SelectorThread " + this.getId());
+
+ // We don't want to hold up the JVM when shutting down
+ setDaemon(true);
+ }
+
+ public Selector getSelector() {
+ return selector;
+ }
+
+ public void finish() {
+ running = false;
+ selector.wakeup();
+ }
+
+ public void run() {
+ while (running) {
+ try {
+ try {
+ if (timeoutWatchSet.size() == 0) {
+ // No timeouts, so select indefinitely
+ selector.select();
+ } else {
+ // We have a timeout pending, so calculate the time until then and select appropriately
+ long nextTimeout = timeoutWatchSet.first().getTimeoutTimestamp();
+ long selectTime = nextTimeout - System.currentTimeMillis();
+ if (selectTime > 0) {
+ // Next timeout is in the future, select and wake up then
+ selector.select(selectTime);
+ } else {
+ // Next timeout is now or in past, select immediately so we can time out
+ selector.selectNow();
+ }
+ }
+ } catch (IOException e) {
+ LOGGER.error("Caught IOException in TAsyncClientManager!", e);
+ }
+ transitionMethods();
+ timeoutMethods();
+ startPendingMethods();
+ } catch (Exception exception) {
+ LOGGER.error("Ignoring uncaught exception in SelectThread", exception);
+ }
+ }
+
+ try {
+ selector.close();
+ } catch (IOException ex) {
+ LOGGER.warn("Could not close selector. This may result in leaked resources!", ex);
+ }
+ }
+
+ // Transition methods for ready keys
+ private void transitionMethods() {
+ try {
+ Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
+ while (keys.hasNext()) {
+ SelectionKey key = keys.next();
+ keys.remove();
+ if (!key.isValid()) {
+ // this can happen if the method call experienced an error and the
+ // key was cancelled. can also happen if we timeout a method, which
+ // results in a channel close.
+ // just skip
+ continue;
+ }
+ TAsyncMethodCall methodCall = (TAsyncMethodCall)key.attachment();
+ methodCall.transition(key);
+
+ // If done or error occurred, remove from timeout watch set
+ if (methodCall.isFinished() || methodCall.getClient().hasError()) {
+ timeoutWatchSet.remove(methodCall);
+ }
+ }
+ } catch (ClosedSelectorException e) {
+ LOGGER.error("Caught ClosedSelectorException in TAsyncClientManager!", e);
+ }
+ }
+
+ // Timeout any existing method calls
+ private void timeoutMethods() {
+ Iterator<TAsyncMethodCall> iterator = timeoutWatchSet.iterator();
+ long currentTime = System.currentTimeMillis();
+ while (iterator.hasNext()) {
+ TAsyncMethodCall methodCall = iterator.next();
+ if (currentTime >= methodCall.getTimeoutTimestamp()) {
+ iterator.remove();
+ methodCall.onError(new TimeoutException("Operation " + methodCall.getClass() + " timed out after " + (currentTime - methodCall.getStartTime()) + " ms."));
+ } else {
+ break;
+ }
+ }
+ }
+
+ // Start any new calls
+ private void startPendingMethods() {
+ TAsyncMethodCall methodCall;
+ while ((methodCall = pendingCalls.poll()) != null) {
+ // Catch registration errors. method will catch transition errors and cleanup.
+ try {
+ methodCall.start(selector);
+
+ // If timeout specified and first transition went smoothly, add to timeout watch set
+ TAsyncClient client = methodCall.getClient();
+ if (client.hasTimeout() && !client.hasError()) {
+ timeoutWatchSet.add(methodCall);
+ }
+ } catch (Exception exception) {
+ LOGGER.warn("Caught exception in TAsyncClientManager!", exception);
+ methodCall.onError(exception);
+ }
+ }
+ }
+ }
+
+ /** Comparator used in TreeSet */
+ private static class TAsyncMethodCallTimeoutComparator implements Comparator<TAsyncMethodCall>, Serializable {
+ public int compare(TAsyncMethodCall left, TAsyncMethodCall right) {
+ if (left.getTimeoutTimestamp() == right.getTimeoutTimestamp()) {
+ return (int)(left.getSequenceId() - right.getSequenceId());
+ } else {
+ return (int)(left.getTimeoutTimestamp() - right.getTimeoutTimestamp());
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncMethodCall.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncMethodCall.java
new file mode 100644
index 000000000..3bf1747f4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncMethodCall.java
@@ -0,0 +1,284 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.async;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TFramedTransport;
+import org.apache.thrift.transport.TMemoryBuffer;
+import org.apache.thrift.transport.TNonblockingTransport;
+import org.apache.thrift.transport.TTransportException;
+
+/**
+ * Encapsulates an async method call.
+ * <p>
+ * Need to generate:
+ * <ul>
+ * <li>protected abstract void write_args(TProtocol protocol)</li>
+ * <li>protected abstract T getResult() throws &lt;Exception_1&gt;, &lt;Exception_2&gt;, ...</li>
+ * </ul>
+ *
+ * @param <T> The return type of the encapsulated method call.
+ */
+public abstract class TAsyncMethodCall<T> {
+
+ private static final int INITIAL_MEMORY_BUFFER_SIZE = 128;
+ private static AtomicLong sequenceIdCounter = new AtomicLong(0);
+
+ public static enum State {
+ CONNECTING,
+ WRITING_REQUEST_SIZE,
+ WRITING_REQUEST_BODY,
+ READING_RESPONSE_SIZE,
+ READING_RESPONSE_BODY,
+ RESPONSE_READ,
+ ERROR;
+ }
+
+ /**
+ * Next step in the call, initialized by start()
+ */
+ private State state = null;
+
+ protected final TNonblockingTransport transport;
+ private final TProtocolFactory protocolFactory;
+ protected final TAsyncClient client;
+ private final AsyncMethodCallback<T> callback;
+ private final boolean isOneway;
+ private long sequenceId;
+ private final long timeout;
+
+ private ByteBuffer sizeBuffer;
+ private final byte[] sizeBufferArray = new byte[4];
+ private ByteBuffer frameBuffer;
+
+ private long startTime = System.currentTimeMillis();
+
+ protected TAsyncMethodCall(TAsyncClient client, TProtocolFactory protocolFactory, TNonblockingTransport transport, AsyncMethodCallback<T> callback, boolean isOneway) {
+ this.transport = transport;
+ this.callback = callback;
+ this.protocolFactory = protocolFactory;
+ this.client = client;
+ this.isOneway = isOneway;
+ this.sequenceId = TAsyncMethodCall.sequenceIdCounter.getAndIncrement();
+ this.timeout = client.getTimeout();
+ }
+
+ protected State getState() {
+ return state;
+ }
+
+ protected boolean isFinished() {
+ return state == State.RESPONSE_READ;
+ }
+
+ protected long getStartTime() {
+ return startTime;
+ }
+
+ protected long getSequenceId() {
+ return sequenceId;
+ }
+
+ public TAsyncClient getClient() {
+ return client;
+ }
+
+ public boolean hasTimeout() {
+ return timeout > 0;
+ }
+
+ public long getTimeoutTimestamp() {
+ return timeout + startTime;
+ }
+
+ protected abstract void write_args(TProtocol protocol) throws TException;
+
+ protected abstract T getResult() throws Exception;
+
+ /**
+ * Initialize buffers.
+ * @throws TException if buffer initialization fails
+ */
+ protected void prepareMethodCall() throws TException {
+ TMemoryBuffer memoryBuffer = new TMemoryBuffer(INITIAL_MEMORY_BUFFER_SIZE);
+ TProtocol protocol = protocolFactory.getProtocol(memoryBuffer);
+ write_args(protocol);
+
+ int length = memoryBuffer.length();
+ frameBuffer = ByteBuffer.wrap(memoryBuffer.getArray(), 0, length);
+
+ TFramedTransport.encodeFrameSize(length, sizeBufferArray);
+ sizeBuffer = ByteBuffer.wrap(sizeBufferArray);
+ }
+
+ /**
+ * Register with selector and start first state, which could be either connecting or writing.
+ * @throws IOException if register or starting fails
+ */
+ void start(Selector sel) throws IOException {
+ SelectionKey key;
+ if (transport.isOpen()) {
+ state = State.WRITING_REQUEST_SIZE;
+ key = transport.registerSelector(sel, SelectionKey.OP_WRITE);
+ } else {
+ state = State.CONNECTING;
+ key = transport.registerSelector(sel, SelectionKey.OP_CONNECT);
+
+ // non-blocking connect can complete immediately,
+ // in which case we should not expect the OP_CONNECT
+ if (transport.startConnect()) {
+ registerForFirstWrite(key);
+ }
+ }
+
+ key.attach(this);
+ }
+
+ void registerForFirstWrite(SelectionKey key) throws IOException {
+ state = State.WRITING_REQUEST_SIZE;
+ key.interestOps(SelectionKey.OP_WRITE);
+ }
+
+ protected ByteBuffer getFrameBuffer() {
+ return frameBuffer;
+ }
+
+ /**
+ * Transition to next state, doing whatever work is required. Since this
+ * method is only called by the selector thread, we can make changes to our
+ * select interests without worrying about concurrency.
+ * @param key
+ */
+ void transition(SelectionKey key) {
+ // Ensure key is valid
+ if (!key.isValid()) {
+ key.cancel();
+ Exception e = new TTransportException("Selection key not valid!");
+ onError(e);
+ return;
+ }
+
+ // Transition function
+ try {
+ switch (state) {
+ case CONNECTING:
+ doConnecting(key);
+ break;
+ case WRITING_REQUEST_SIZE:
+ doWritingRequestSize();
+ break;
+ case WRITING_REQUEST_BODY:
+ doWritingRequestBody(key);
+ break;
+ case READING_RESPONSE_SIZE:
+ doReadingResponseSize();
+ break;
+ case READING_RESPONSE_BODY:
+ doReadingResponseBody(key);
+ break;
+ default: // RESPONSE_READ, ERROR, or bug
+ throw new IllegalStateException("Method call in state " + state
+ + " but selector called transition method. Seems like a bug...");
+ }
+ } catch (Exception e) {
+ key.cancel();
+ key.attach(null);
+ onError(e);
+ }
+ }
+
+ protected void onError(Exception e) {
+ client.onError(e);
+ callback.onError(e);
+ state = State.ERROR;
+ }
+
+ private void doReadingResponseBody(SelectionKey key) throws IOException {
+ if (transport.read(frameBuffer) < 0) {
+ throw new IOException("Read call frame failed");
+ }
+ if (frameBuffer.remaining() == 0) {
+ cleanUpAndFireCallback(key);
+ }
+ }
+
+ private void cleanUpAndFireCallback(SelectionKey key) {
+ state = State.RESPONSE_READ;
+ key.interestOps(0);
+ // this ensures that the TAsyncMethod instance doesn't hang around
+ key.attach(null);
+ try {
+ T result = this.getResult();
+ client.onComplete();
+ callback.onComplete(result);
+ } catch (Exception e) {
+ key.cancel();
+ onError(e);
+ }
+ }
+
+ private void doReadingResponseSize() throws IOException {
+ if (transport.read(sizeBuffer) < 0) {
+ throw new IOException("Read call frame size failed");
+ }
+ if (sizeBuffer.remaining() == 0) {
+ state = State.READING_RESPONSE_BODY;
+ frameBuffer = ByteBuffer.allocate(TFramedTransport.decodeFrameSize(sizeBufferArray));
+ }
+ }
+
+ private void doWritingRequestBody(SelectionKey key) throws IOException {
+ if (transport.write(frameBuffer) < 0) {
+ throw new IOException("Write call frame failed");
+ }
+ if (frameBuffer.remaining() == 0) {
+ if (isOneway) {
+ cleanUpAndFireCallback(key);
+ } else {
+ state = State.READING_RESPONSE_SIZE;
+ sizeBuffer.rewind(); // Prepare to read incoming frame size
+ key.interestOps(SelectionKey.OP_READ);
+ }
+ }
+ }
+
+ private void doWritingRequestSize() throws IOException {
+ if (transport.write(sizeBuffer) < 0) {
+ throw new IOException("Write call frame size failed");
+ }
+ if (sizeBuffer.remaining() == 0) {
+ state = State.WRITING_REQUEST_BODY;
+ }
+ }
+
+ private void doConnecting(SelectionKey key) throws IOException {
+ if (!key.isConnectable() || !transport.finishConnect()) {
+ throw new IOException("not connectable or finishConnect returned false after we got an OP_CONNECT");
+ }
+ registerForFirstWrite(key);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/EnumMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/EnumMetaData.java
new file mode 100644
index 000000000..be49cb949
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/EnumMetaData.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+import org.apache.thrift.TEnum;
+
+public class EnumMetaData extends FieldValueMetaData {
+ public final Class<? extends TEnum> enumClass;
+
+ public EnumMetaData(byte type, Class<? extends TEnum> sClass){
+ super(type);
+ this.enumClass = sClass;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldMetaData.java
new file mode 100644
index 000000000..445f7e474
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldMetaData.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.thrift.TBase;
+import org.apache.thrift.TFieldIdEnum;
+
+/**
+ * This class is used to store meta data about thrift fields. Every field in a
+ * a struct should have a corresponding instance of this class describing it.
+ *
+ */
+public class FieldMetaData implements java.io.Serializable {
+ public final String fieldName;
+ public final byte requirementType;
+ public final FieldValueMetaData valueMetaData;
+ private static Map<Class<? extends TBase>, Map<? extends TFieldIdEnum, FieldMetaData>> structMap;
+
+ static {
+ structMap = new HashMap<Class<? extends TBase>, Map<? extends TFieldIdEnum, FieldMetaData>>();
+ }
+
+ public FieldMetaData(String name, byte req, FieldValueMetaData vMetaData){
+ this.fieldName = name;
+ this.requirementType = req;
+ this.valueMetaData = vMetaData;
+ }
+
+ public static synchronized void addStructMetaDataMap(Class<? extends TBase> sClass, Map<? extends TFieldIdEnum, FieldMetaData> map){
+ structMap.put(sClass, map);
+ }
+
+ /**
+ * Returns a map with metadata (i.e. instances of FieldMetaData) that
+ * describe the fields of the given class.
+ *
+ * @param sClass The TBase class for which the metadata map is requested
+ */
+ public static synchronized Map<? extends TFieldIdEnum, FieldMetaData> getStructMetaDataMap(Class<? extends TBase> sClass){
+ if (!structMap.containsKey(sClass)){ // Load class if it hasn't been loaded
+ try{
+ sClass.newInstance();
+ } catch (InstantiationException e){
+ throw new RuntimeException("InstantiationException for TBase class: " + sClass.getName() + ", message: " + e.getMessage());
+ } catch (IllegalAccessException e){
+ throw new RuntimeException("IllegalAccessException for TBase class: " + sClass.getName() + ", message: " + e.getMessage());
+ }
+ }
+ return structMap.get(sClass);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldValueMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldValueMetaData.java
new file mode 100644
index 000000000..2180b089b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldValueMetaData.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+import org.apache.thrift.protocol.TType;
+
+/**
+ * FieldValueMetaData and collection of subclasses to store metadata about
+ * the value(s) of a field
+ */
+public class FieldValueMetaData implements java.io.Serializable {
+ public final byte type;
+
+ private final boolean isTypedefType;
+ private final String typedefName;
+ private final boolean isBinary;
+
+ public FieldValueMetaData(byte type, boolean binary) {
+ this.type = type;
+ this.isTypedefType = false;
+ this.typedefName = null;
+ this.isBinary = binary;
+ }
+
+ public FieldValueMetaData(byte type) {
+ this(type, false);
+ }
+
+ public FieldValueMetaData(byte type, String typedefName) {
+ this.type = type;
+ this.isTypedefType = true;
+ this.typedefName = typedefName;
+ this.isBinary = false;
+ }
+
+ public boolean isTypedef() {
+ return isTypedefType;
+ }
+
+ public String getTypedefName() {
+ return typedefName;
+ }
+
+ public boolean isStruct() {
+ return type == TType.STRUCT;
+ }
+
+ public boolean isContainer() {
+ return type == TType.LIST || type == TType.MAP || type == TType.SET;
+ }
+
+ public boolean isBinary() {
+ return isBinary;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/ListMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/ListMetaData.java
new file mode 100644
index 000000000..8e7073bf5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/ListMetaData.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+public class ListMetaData extends FieldValueMetaData {
+ public final FieldValueMetaData elemMetaData;
+
+ public ListMetaData(byte type, FieldValueMetaData eMetaData){
+ super(type);
+ this.elemMetaData = eMetaData;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/MapMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/MapMetaData.java
new file mode 100644
index 000000000..e7c408c78
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/MapMetaData.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+public class MapMetaData extends FieldValueMetaData {
+ public final FieldValueMetaData keyMetaData;
+ public final FieldValueMetaData valueMetaData;
+
+ public MapMetaData(byte type, FieldValueMetaData kMetaData, FieldValueMetaData vMetaData){
+ super(type);
+ this.keyMetaData = kMetaData;
+ this.valueMetaData = vMetaData;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/SetMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/SetMetaData.java
new file mode 100644
index 000000000..cf4b96aab
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/SetMetaData.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+public class SetMetaData extends FieldValueMetaData {
+ public final FieldValueMetaData elemMetaData;
+
+ public SetMetaData(byte type, FieldValueMetaData eMetaData){
+ super(type);
+ this.elemMetaData = eMetaData;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/StructMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/StructMetaData.java
new file mode 100644
index 000000000..b37d21dab
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/StructMetaData.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+import org.apache.thrift.TBase;
+
+public class StructMetaData extends FieldValueMetaData {
+ public final Class<? extends TBase> structClass;
+
+ public StructMetaData(byte type, Class<? extends TBase> sClass){
+ super(type);
+ this.structClass = sClass;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/ShortStack.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/ShortStack.java
new file mode 100644
index 000000000..9e6593074
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/ShortStack.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.protocol;
+
+import java.util.Arrays;
+
+/**
+ * ShortStack is a short-specific Stack implementation written for the express
+ * purpose of very fast operations on TCompactProtocol's field id stack. This
+ * implementation performs at least 10x faster than java.util.Stack.
+ */
+class ShortStack {
+
+ private short[] vector;
+
+ /** Always points to the next location */
+ private int top = 0;
+
+ public ShortStack(int initialCapacity) {
+ vector = new short[initialCapacity];
+ }
+
+ public short pop() {
+ return vector[--top];
+ }
+
+ public void push(short pushed) {
+ if (vector.length == top) {
+ grow();
+ }
+ vector[top++] = pushed;
+ }
+
+ private void grow() {
+ vector = Arrays.copyOf(vector, vector.length << 1);
+ }
+
+ public void clear() {
+ top = 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("<ShortStack vector:[");
+ for (int i = 0; i < vector.length; i++) {
+ boolean isTop = (i == (top - 1));
+ short value = vector[i];
+ if (i != 0) {
+ sb.append(' ');
+ }
+ if (isTop) {
+ sb.append(">>").append(value).append("<<");
+ } else {
+ sb.append(value);
+ }
+ }
+ sb.append("]>");
+ return sb.toString();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBase64Utils.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBase64Utils.java
new file mode 100644
index 000000000..abfc965b7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBase64Utils.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Class for encoding and decoding Base64 data.
+ *
+ * This class is kept at package level because the interface does no input
+ * validation and is therefore too low-level for generalized reuse.
+ *
+ * Note also that the encoding does not pad with equal signs , as discussed in
+ * section 2.2 of the RFC (http://www.faqs.org/rfcs/rfc3548.html). Furthermore,
+ * bad data encountered when decoding is neither rejected or ignored but simply
+ * results in bad decoded data -- this is not in compliance with the RFC but is
+ * done in the interest of performance.
+ *
+ */
+class TBase64Utils {
+
+ private static final String ENCODE_TABLE =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ /**
+ * Encode len bytes of data in src at offset srcOff, storing the result into
+ * dst at offset dstOff. len must be 1, 2, or 3. dst must have at least len+1
+ * bytes of space at dstOff. src and dst should not be the same object. This
+ * method does no validation of the input values in the interest of
+ * performance.
+ *
+ * @param src the source of bytes to encode
+ * @param srcOff the offset into the source to read the unencoded bytes
+ * @param len the number of bytes to encode (must be 1, 2, or 3).
+ * @param dst the destination for the encoding
+ * @param dstOff the offset into the destination to place the encoded bytes
+ */
+ static final void encode(byte[] src, int srcOff, int len, byte[] dst,
+ int dstOff) {
+ dst[dstOff] = (byte)ENCODE_TABLE.charAt((src[srcOff] >> 2) & 0x3F);
+ if (len == 3) {
+ dst[dstOff + 1] =
+ (byte)ENCODE_TABLE.charAt(
+ ((src[srcOff] << 4) & 0x30) | ((src[srcOff+1] >> 4) & 0x0F));
+ dst[dstOff + 2] =
+ (byte)ENCODE_TABLE.charAt(
+ ((src[srcOff+1] << 2) & 0x3C) | ((src[srcOff+2] >> 6) & 0x03));
+ dst[dstOff + 3] =
+ (byte)ENCODE_TABLE.charAt(src[srcOff+2] & 0x3F);
+ }
+ else if (len == 2) {
+ dst[dstOff+1] =
+ (byte)ENCODE_TABLE.charAt(
+ ((src[srcOff] << 4) & 0x30) | ((src[srcOff+1] >> 4) & 0x0F));
+ dst[dstOff + 2] =
+ (byte)ENCODE_TABLE.charAt((src[srcOff+1] << 2) & 0x3C);
+ }
+ else { // len == 1) {
+ dst[dstOff + 1] =
+ (byte)ENCODE_TABLE.charAt((src[srcOff] << 4) & 0x30);
+ }
+ }
+
+ private static final byte[] DECODE_TABLE = {
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
+ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
+ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
+ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
+ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ };
+
+ /**
+ * Decode len bytes of data in src at offset srcOff, storing the result into
+ * dst at offset dstOff. len must be 2, 3, or 4. dst must have at least len-1
+ * bytes of space at dstOff. src and dst may be the same object as long as
+ * dstoff <= srcOff. This method does no validation of the input values in
+ * the interest of performance.
+ *
+ * @param src the source of bytes to decode
+ * @param srcOff the offset into the source to read the encoded bytes
+ * @param len the number of bytes to decode (must be 2, 3, or 4)
+ * @param dst the destination for the decoding
+ * @param dstOff the offset into the destination to place the decoded bytes
+ */
+ static final void decode(byte[] src, int srcOff, int len, byte[] dst,
+ int dstOff) {
+ dst[dstOff] = (byte)
+ ((DECODE_TABLE[src[srcOff] & 0x0FF] << 2) |
+ (DECODE_TABLE[src[srcOff+1] & 0x0FF] >> 4));
+ if (len > 2) {
+ dst[dstOff+1] = (byte)
+ (((DECODE_TABLE[src[srcOff+1] & 0x0FF] << 4) & 0xF0) |
+ (DECODE_TABLE[src[srcOff+2] & 0x0FF] >> 2));
+ if (len > 3) {
+ dst[dstOff+2] = (byte)
+ (((DECODE_TABLE[src[srcOff+2] & 0x0FF] << 6) & 0xC0) |
+ DECODE_TABLE[src[srcOff+3] & 0x0FF]);
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBinaryProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBinaryProtocol.java
new file mode 100644
index 000000000..7924e2fe6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBinaryProtocol.java
@@ -0,0 +1,457 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * Binary protocol implementation for thrift.
+ *
+ */
+public class TBinaryProtocol extends TProtocol {
+ private static final TStruct ANONYMOUS_STRUCT = new TStruct();
+ private static final long NO_LENGTH_LIMIT = -1;
+
+ protected static final int VERSION_MASK = 0xffff0000;
+ protected static final int VERSION_1 = 0x80010000;
+
+ /**
+ * The maximum number of bytes to read from the transport for
+ * variable-length fields (such as strings or binary) or {@link #NO_LENGTH_LIMIT} for
+ * unlimited.
+ */
+ private final long stringLengthLimit_;
+
+ /**
+ * The maximum number of elements to read from the network for
+ * containers (maps, sets, lists), or {@link #NO_LENGTH_LIMIT} for unlimited.
+ */
+ private final long containerLengthLimit_;
+
+ protected boolean strictRead_;
+ protected boolean strictWrite_;
+
+ private final byte[] inoutTemp = new byte[8];
+
+ /**
+ * Factory
+ */
+ public static class Factory implements TProtocolFactory {
+ protected long stringLengthLimit_;
+ protected long containerLengthLimit_;
+ protected boolean strictRead_;
+ protected boolean strictWrite_;
+
+ public Factory() {
+ this(false, true);
+ }
+
+ public Factory(boolean strictRead, boolean strictWrite) {
+ this(strictRead, strictWrite, NO_LENGTH_LIMIT, NO_LENGTH_LIMIT);
+ }
+
+ public Factory(long stringLengthLimit, long containerLengthLimit) {
+ this(false, true, stringLengthLimit, containerLengthLimit);
+ }
+
+ public Factory(boolean strictRead, boolean strictWrite, long stringLengthLimit, long containerLengthLimit) {
+ stringLengthLimit_ = stringLengthLimit;
+ containerLengthLimit_ = containerLengthLimit;
+ strictRead_ = strictRead;
+ strictWrite_ = strictWrite;
+ }
+
+ public TProtocol getProtocol(TTransport trans) {
+ return new TBinaryProtocol(trans, stringLengthLimit_, containerLengthLimit_, strictRead_, strictWrite_);
+ }
+ }
+
+ /**
+ * Constructor
+ */
+ public TBinaryProtocol(TTransport trans) {
+ this(trans, false, true);
+ }
+
+ public TBinaryProtocol(TTransport trans, boolean strictRead, boolean strictWrite) {
+ this(trans, NO_LENGTH_LIMIT, NO_LENGTH_LIMIT, strictRead, strictWrite);
+ }
+
+ public TBinaryProtocol(TTransport trans, long stringLengthLimit, long containerLengthLimit) {
+ this(trans, stringLengthLimit, containerLengthLimit, false, true);
+ }
+
+ public TBinaryProtocol(TTransport trans, long stringLengthLimit, long containerLengthLimit, boolean strictRead, boolean strictWrite) {
+ super(trans);
+ stringLengthLimit_ = stringLengthLimit;
+ containerLengthLimit_ = containerLengthLimit;
+ strictRead_ = strictRead;
+ strictWrite_ = strictWrite;
+ }
+
+ @Override
+ public void writeMessageBegin(TMessage message) throws TException {
+ if (strictWrite_) {
+ int version = VERSION_1 | message.type;
+ writeI32(version);
+ writeString(message.name);
+ writeI32(message.seqid);
+ } else {
+ writeString(message.name);
+ writeByte(message.type);
+ writeI32(message.seqid);
+ }
+ }
+
+ @Override
+ public void writeMessageEnd() throws TException {}
+
+ @Override
+ public void writeStructBegin(TStruct struct) throws TException {}
+
+ @Override
+ public void writeStructEnd() throws TException {}
+
+ @Override
+ public void writeFieldBegin(TField field) throws TException {
+ writeByte(field.type);
+ writeI16(field.id);
+ }
+
+ @Override
+ public void writeFieldEnd() throws TException {}
+
+ @Override
+ public void writeFieldStop() throws TException {
+ writeByte(TType.STOP);
+ }
+
+ @Override
+ public void writeMapBegin(TMap map) throws TException {
+ writeByte(map.keyType);
+ writeByte(map.valueType);
+ writeI32(map.size);
+ }
+
+ @Override
+ public void writeMapEnd() throws TException {}
+
+ @Override
+ public void writeListBegin(TList list) throws TException {
+ writeByte(list.elemType);
+ writeI32(list.size);
+ }
+
+ @Override
+ public void writeListEnd() throws TException {}
+
+ @Override
+ public void writeSetBegin(TSet set) throws TException {
+ writeByte(set.elemType);
+ writeI32(set.size);
+ }
+
+ @Override
+ public void writeSetEnd() throws TException {}
+
+ @Override
+ public void writeBool(boolean b) throws TException {
+ writeByte(b ? (byte)1 : (byte)0);
+ }
+
+ @Override
+ public void writeByte(byte b) throws TException {
+ inoutTemp[0] = b;
+ trans_.write(inoutTemp, 0, 1);
+ }
+
+ @Override
+ public void writeI16(short i16) throws TException {
+ inoutTemp[0] = (byte)(0xff & (i16 >> 8));
+ inoutTemp[1] = (byte)(0xff & (i16));
+ trans_.write(inoutTemp, 0, 2);
+ }
+
+ @Override
+ public void writeI32(int i32) throws TException {
+ inoutTemp[0] = (byte)(0xff & (i32 >> 24));
+ inoutTemp[1] = (byte)(0xff & (i32 >> 16));
+ inoutTemp[2] = (byte)(0xff & (i32 >> 8));
+ inoutTemp[3] = (byte)(0xff & (i32));
+ trans_.write(inoutTemp, 0, 4);
+ }
+
+ @Override
+ public void writeI64(long i64) throws TException {
+ inoutTemp[0] = (byte)(0xff & (i64 >> 56));
+ inoutTemp[1] = (byte)(0xff & (i64 >> 48));
+ inoutTemp[2] = (byte)(0xff & (i64 >> 40));
+ inoutTemp[3] = (byte)(0xff & (i64 >> 32));
+ inoutTemp[4] = (byte)(0xff & (i64 >> 24));
+ inoutTemp[5] = (byte)(0xff & (i64 >> 16));
+ inoutTemp[6] = (byte)(0xff & (i64 >> 8));
+ inoutTemp[7] = (byte)(0xff & (i64));
+ trans_.write(inoutTemp, 0, 8);
+ }
+
+ @Override
+ public void writeDouble(double dub) throws TException {
+ writeI64(Double.doubleToLongBits(dub));
+ }
+
+ @Override
+ public void writeString(String str) throws TException {
+ byte[] dat = str.getBytes(StandardCharsets.UTF_8);
+ writeI32(dat.length);
+ trans_.write(dat, 0, dat.length);
+ }
+
+ @Override
+ public void writeBinary(ByteBuffer bin) throws TException {
+ int length = bin.limit() - bin.position();
+ writeI32(length);
+ trans_.write(bin.array(), bin.position() + bin.arrayOffset(), length);
+ }
+
+ /**
+ * Reading methods.
+ */
+
+ @Override
+ public TMessage readMessageBegin() throws TException {
+ int size = readI32();
+ if (size < 0) {
+ int version = size & VERSION_MASK;
+ if (version != VERSION_1) {
+ throw new TProtocolException(TProtocolException.BAD_VERSION, "Bad version in readMessageBegin");
+ }
+ return new TMessage(readString(), (byte)(size & 0x000000ff), readI32());
+ } else {
+ if (strictRead_) {
+ throw new TProtocolException(TProtocolException.BAD_VERSION, "Missing version in readMessageBegin, old client?");
+ }
+ return new TMessage(readStringBody(size), readByte(), readI32());
+ }
+ }
+
+ @Override
+ public void readMessageEnd() throws TException {}
+
+ @Override
+ public TStruct readStructBegin() throws TException {
+ return ANONYMOUS_STRUCT;
+ }
+
+ @Override
+ public void readStructEnd() throws TException {}
+
+ @Override
+ public TField readFieldBegin() throws TException {
+ byte type = readByte();
+ short id = type == TType.STOP ? 0 : readI16();
+ return new TField("", type, id);
+ }
+
+ @Override
+ public void readFieldEnd() throws TException {}
+
+ @Override
+ public TMap readMapBegin() throws TException {
+ TMap map = new TMap(readByte(), readByte(), readI32());
+ checkContainerReadLength(map.size);
+ return map;
+ }
+
+ @Override
+ public void readMapEnd() throws TException {}
+
+ @Override
+ public TList readListBegin() throws TException {
+ TList list = new TList(readByte(), readI32());
+ checkContainerReadLength(list.size);
+ return list;
+ }
+
+ @Override
+ public void readListEnd() throws TException {}
+
+ @Override
+ public TSet readSetBegin() throws TException {
+ TSet set = new TSet(readByte(), readI32());
+ checkContainerReadLength(set.size);
+ return set;
+ }
+
+ @Override
+ public void readSetEnd() throws TException {}
+
+ @Override
+ public boolean readBool() throws TException {
+ return (readByte() == 1);
+ }
+
+ @Override
+ public byte readByte() throws TException {
+ if (trans_.getBytesRemainingInBuffer() >= 1) {
+ byte b = trans_.getBuffer()[trans_.getBufferPosition()];
+ trans_.consumeBuffer(1);
+ return b;
+ }
+ readAll(inoutTemp, 0, 1);
+ return inoutTemp[0];
+ }
+
+ @Override
+ public short readI16() throws TException {
+ byte[] buf = inoutTemp;
+ int off = 0;
+
+ if (trans_.getBytesRemainingInBuffer() >= 2) {
+ buf = trans_.getBuffer();
+ off = trans_.getBufferPosition();
+ trans_.consumeBuffer(2);
+ } else {
+ readAll(inoutTemp, 0, 2);
+ }
+
+ return
+ (short)
+ (((buf[off] & 0xff) << 8) |
+ ((buf[off+1] & 0xff)));
+ }
+
+ @Override
+ public int readI32() throws TException {
+ byte[] buf = inoutTemp;
+ int off = 0;
+
+ if (trans_.getBytesRemainingInBuffer() >= 4) {
+ buf = trans_.getBuffer();
+ off = trans_.getBufferPosition();
+ trans_.consumeBuffer(4);
+ } else {
+ readAll(inoutTemp, 0, 4);
+ }
+ return
+ ((buf[off] & 0xff) << 24) |
+ ((buf[off+1] & 0xff) << 16) |
+ ((buf[off+2] & 0xff) << 8) |
+ ((buf[off+3] & 0xff));
+ }
+
+ @Override
+ public long readI64() throws TException {
+ byte[] buf = inoutTemp;
+ int off = 0;
+
+ if (trans_.getBytesRemainingInBuffer() >= 8) {
+ buf = trans_.getBuffer();
+ off = trans_.getBufferPosition();
+ trans_.consumeBuffer(8);
+ } else {
+ readAll(inoutTemp, 0, 8);
+ }
+
+ return
+ ((long)(buf[off] & 0xff) << 56) |
+ ((long)(buf[off+1] & 0xff) << 48) |
+ ((long)(buf[off+2] & 0xff) << 40) |
+ ((long)(buf[off+3] & 0xff) << 32) |
+ ((long)(buf[off+4] & 0xff) << 24) |
+ ((long)(buf[off+5] & 0xff) << 16) |
+ ((long)(buf[off+6] & 0xff) << 8) |
+ ((long)(buf[off+7] & 0xff));
+ }
+
+ @Override
+ public double readDouble() throws TException {
+ return Double.longBitsToDouble(readI64());
+ }
+
+ @Override
+ public String readString() throws TException {
+ int size = readI32();
+
+ checkStringReadLength(size);
+
+ if (trans_.getBytesRemainingInBuffer() >= size) {
+ String s = new String(trans_.getBuffer(), trans_.getBufferPosition(),
+ size, StandardCharsets.UTF_8);
+ trans_.consumeBuffer(size);
+ return s;
+ }
+
+ return readStringBody(size);
+ }
+
+ public String readStringBody(int size) throws TException {
+ checkStringReadLength(size);
+ byte[] buf = new byte[size];
+ trans_.readAll(buf, 0, size);
+ return new String(buf, StandardCharsets.UTF_8);
+ }
+
+ @Override
+ public ByteBuffer readBinary() throws TException {
+ int size = readI32();
+
+ checkStringReadLength(size);
+
+ if (trans_.getBytesRemainingInBuffer() >= size) {
+ ByteBuffer bb = ByteBuffer.wrap(trans_.getBuffer(), trans_.getBufferPosition(), size);
+ trans_.consumeBuffer(size);
+ return bb;
+ }
+
+ byte[] buf = new byte[size];
+ trans_.readAll(buf, 0, size);
+ return ByteBuffer.wrap(buf);
+ }
+
+ private void checkStringReadLength(int length) throws TProtocolException {
+ if (length < 0) {
+ throw new TProtocolException(TProtocolException.NEGATIVE_SIZE,
+ "Negative length: " + length);
+ }
+ if (stringLengthLimit_ != NO_LENGTH_LIMIT && length > stringLengthLimit_) {
+ throw new TProtocolException(TProtocolException.SIZE_LIMIT,
+ "Length exceeded max allowed: " + length);
+ }
+ }
+
+ private void checkContainerReadLength(int length) throws TProtocolException {
+ if (length < 0) {
+ throw new TProtocolException(TProtocolException.NEGATIVE_SIZE,
+ "Negative length: " + length);
+ }
+ if (containerLengthLimit_ != NO_LENGTH_LIMIT && length > containerLengthLimit_) {
+ throw new TProtocolException(TProtocolException.SIZE_LIMIT,
+ "Length exceeded max allowed: " + length);
+ }
+ }
+
+ private int readAll(byte[] buf, int off, int len) throws TException {
+ return trans_.readAll(buf, off, len);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java
new file mode 100644
index 000000000..ee0586945
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java
@@ -0,0 +1,904 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.thrift.protocol;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * TCompactProtocol2 is the Java implementation of the compact protocol specified
+ * in THRIFT-110. The fundamental approach to reducing the overhead of
+ * structures is a) use variable-length integers all over the place and b) make
+ * use of unused bits wherever possible. Your savings will obviously vary
+ * based on the specific makeup of your structs, but in general, the more
+ * fields, nested structures, short strings and collections, and low-value i32
+ * and i64 fields you have, the more benefit you'll see.
+ */
+public class TCompactProtocol extends TProtocol {
+ private final static byte[] EMPTY_BYTES = new byte[0];
+ private final static ByteBuffer EMPTY_BUFFER = ByteBuffer.wrap(EMPTY_BYTES);
+
+ private final static long NO_LENGTH_LIMIT = -1;
+
+ private final static TStruct ANONYMOUS_STRUCT = new TStruct("");
+ private final static TField TSTOP = new TField("", TType.STOP, (short)0);
+
+ private final static byte[] ttypeToCompactType = new byte[16];
+
+ static {
+ ttypeToCompactType[TType.STOP] = TType.STOP;
+ ttypeToCompactType[TType.BOOL] = Types.BOOLEAN_TRUE;
+ ttypeToCompactType[TType.BYTE] = Types.BYTE;
+ ttypeToCompactType[TType.I16] = Types.I16;
+ ttypeToCompactType[TType.I32] = Types.I32;
+ ttypeToCompactType[TType.I64] = Types.I64;
+ ttypeToCompactType[TType.DOUBLE] = Types.DOUBLE;
+ ttypeToCompactType[TType.STRING] = Types.BINARY;
+ ttypeToCompactType[TType.LIST] = Types.LIST;
+ ttypeToCompactType[TType.SET] = Types.SET;
+ ttypeToCompactType[TType.MAP] = Types.MAP;
+ ttypeToCompactType[TType.STRUCT] = Types.STRUCT;
+ }
+
+ /**
+ * TProtocolFactory that produces TCompactProtocols.
+ */
+ public static class Factory implements TProtocolFactory {
+ private final long stringLengthLimit_;
+ private final long containerLengthLimit_;
+
+ public Factory() {
+ this(NO_LENGTH_LIMIT, NO_LENGTH_LIMIT);
+ }
+
+ public Factory(long stringLengthLimit) {
+ this(stringLengthLimit, NO_LENGTH_LIMIT);
+ }
+
+ public Factory(long stringLengthLimit, long containerLengthLimit) {
+ this.containerLengthLimit_ = containerLengthLimit;
+ this.stringLengthLimit_ = stringLengthLimit;
+ }
+
+ public TProtocol getProtocol(TTransport trans) {
+ return new TCompactProtocol(trans, stringLengthLimit_, containerLengthLimit_);
+ }
+ }
+
+ private static final byte PROTOCOL_ID = (byte)0x82;
+ private static final byte VERSION = 1;
+ private static final byte VERSION_MASK = 0x1f; // 0001 1111
+ private static final byte TYPE_MASK = (byte)0xE0; // 1110 0000
+ private static final byte TYPE_BITS = 0x07; // 0000 0111
+ private static final int TYPE_SHIFT_AMOUNT = 5;
+
+ /**
+ * All of the on-wire type codes.
+ */
+ private static class Types {
+ public static final byte BOOLEAN_TRUE = 0x01;
+ public static final byte BOOLEAN_FALSE = 0x02;
+ public static final byte BYTE = 0x03;
+ public static final byte I16 = 0x04;
+ public static final byte I32 = 0x05;
+ public static final byte I64 = 0x06;
+ public static final byte DOUBLE = 0x07;
+ public static final byte BINARY = 0x08;
+ public static final byte LIST = 0x09;
+ public static final byte SET = 0x0A;
+ public static final byte MAP = 0x0B;
+ public static final byte STRUCT = 0x0C;
+ }
+
+ /**
+ * Used to keep track of the last field for the current and previous structs,
+ * so we can do the delta stuff.
+ */
+ private ShortStack lastField_ = new ShortStack(15);
+
+ private short lastFieldId_ = 0;
+
+ /**
+ * If we encounter a boolean field begin, save the TField here so it can
+ * have the value incorporated.
+ */
+ private TField booleanField_ = null;
+
+ /**
+ * If we read a field header, and it's a boolean field, save the boolean
+ * value here so that readBool can use it.
+ */
+ private Boolean boolValue_ = null;
+
+ /**
+ * The maximum number of bytes to read from the transport for
+ * variable-length fields (such as strings or binary) or {@link #NO_LENGTH_LIMIT} for
+ * unlimited.
+ */
+ private final long stringLengthLimit_;
+
+ /**
+ * The maximum number of elements to read from the network for
+ * containers (maps, sets, lists), or {@link #NO_LENGTH_LIMIT} for unlimited.
+ */
+ private final long containerLengthLimit_;
+
+ /**
+ * Temporary buffer used for various operations that would otherwise require a
+ * small allocation.
+ */
+ private final byte[] temp = new byte[10];
+
+ /**
+ * Create a TCompactProtocol.
+ *
+ * @param transport the TTransport object to read from or write to.
+ * @param stringLengthLimit the maximum number of bytes to read for
+ * variable-length fields.
+ * @param containerLengthLimit the maximum number of elements to read
+ * for containers.
+ */
+ public TCompactProtocol(TTransport transport, long stringLengthLimit, long containerLengthLimit) {
+ super(transport);
+ this.stringLengthLimit_ = stringLengthLimit;
+ this.containerLengthLimit_ = containerLengthLimit;
+ }
+
+ /**
+ * Create a TCompactProtocol.
+ *
+ * @param transport the TTransport object to read from or write to.
+ * @param stringLengthLimit the maximum number of bytes to read for
+ * variable-length fields.
+ * @deprecated Use constructor specifying both string limit and container limit instead
+ */
+ @Deprecated
+ public TCompactProtocol(TTransport transport, long stringLengthLimit) {
+ this(transport, stringLengthLimit, NO_LENGTH_LIMIT);
+ }
+
+ /**
+ * Create a TCompactProtocol.
+ *
+ * @param transport the TTransport object to read from or write to.
+ */
+ public TCompactProtocol(TTransport transport) {
+ this(transport, NO_LENGTH_LIMIT, NO_LENGTH_LIMIT);
+ }
+
+ @Override
+ public void reset() {
+ lastField_.clear();
+ lastFieldId_ = 0;
+ }
+
+ //
+ // Public Writing methods.
+ //
+
+ /**
+ * Write a message header to the wire. Compact Protocol messages contain the
+ * protocol version so we can migrate forwards in the future if need be.
+ */
+ @Override
+ public void writeMessageBegin(TMessage message) throws TException {
+ writeByteDirect(PROTOCOL_ID);
+ writeByteDirect((VERSION & VERSION_MASK) | ((message.type << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
+ writeVarint32(message.seqid);
+ writeString(message.name);
+ }
+
+ /**
+ * Write a struct begin. This doesn't actually put anything on the wire. We
+ * use it as an opportunity to put special placeholder markers on the field
+ * stack so we can get the field id deltas correct.
+ */
+ @Override
+ public void writeStructBegin(TStruct struct) throws TException {
+ lastField_.push(lastFieldId_);
+ lastFieldId_ = 0;
+ }
+
+ /**
+ * Write a struct end. This doesn't actually put anything on the wire. We use
+ * this as an opportunity to pop the last field from the current struct off
+ * of the field stack.
+ */
+ public void writeStructEnd() throws TException {
+ lastFieldId_ = lastField_.pop();
+ }
+
+ /**
+ * Write a field header containing the field id and field type. If the
+ * difference between the current field id and the last one is small (&lt; 15),
+ * then the field id will be encoded in the 4 MSB as a delta. Otherwise, the
+ * field id will follow the type header as a zigzag varint.
+ */
+ public void writeFieldBegin(TField field) throws TException {
+ if (field.type == TType.BOOL) {
+ // we want to possibly include the value, so we'll wait.
+ booleanField_ = field;
+ } else {
+ writeFieldBeginInternal(field, (byte)-1);
+ }
+ }
+
+ /**
+ * The workhorse of writeFieldBegin. It has the option of doing a
+ * 'type override' of the type header. This is used specifically in the
+ * boolean field case.
+ */
+ private void writeFieldBeginInternal(TField field, byte typeOverride) throws TException {
+ // short lastField = lastField_.pop();
+
+ // if there's a type override, use that.
+ byte typeToWrite = typeOverride == -1 ? getCompactType(field.type) : typeOverride;
+
+ // check if we can use delta encoding for the field id
+ if (field.id > lastFieldId_ && field.id - lastFieldId_ <= 15) {
+ // write them together
+ writeByteDirect((field.id - lastFieldId_) << 4 | typeToWrite);
+ } else {
+ // write them separate
+ writeByteDirect(typeToWrite);
+ writeI16(field.id);
+ }
+
+ lastFieldId_ = field.id;
+ // lastField_.push(field.id);
+ }
+
+ /**
+ * Write the STOP symbol so we know there are no more fields in this struct.
+ */
+ public void writeFieldStop() throws TException {
+ writeByteDirect(TType.STOP);
+ }
+
+ /**
+ * Write a map header. If the map is empty, omit the key and value type
+ * headers, as we don't need any additional information to skip it.
+ */
+ public void writeMapBegin(TMap map) throws TException {
+ if (map.size == 0) {
+ writeByteDirect(0);
+ } else {
+ writeVarint32(map.size);
+ writeByteDirect(getCompactType(map.keyType) << 4 | getCompactType(map.valueType));
+ }
+ }
+
+ /**
+ * Write a list header.
+ */
+ public void writeListBegin(TList list) throws TException {
+ writeCollectionBegin(list.elemType, list.size);
+ }
+
+ /**
+ * Write a set header.
+ */
+ public void writeSetBegin(TSet set) throws TException {
+ writeCollectionBegin(set.elemType, set.size);
+ }
+
+ /**
+ * Write a boolean value. Potentially, this could be a boolean field, in
+ * which case the field header info isn't written yet. If so, decide what the
+ * right type header is for the value and then write the field header.
+ * Otherwise, write a single byte.
+ */
+ public void writeBool(boolean b) throws TException {
+ if (booleanField_ != null) {
+ // we haven't written the field header yet
+ writeFieldBeginInternal(booleanField_, b ? Types.BOOLEAN_TRUE : Types.BOOLEAN_FALSE);
+ booleanField_ = null;
+ } else {
+ // we're not part of a field, so just write the value.
+ writeByteDirect(b ? Types.BOOLEAN_TRUE : Types.BOOLEAN_FALSE);
+ }
+ }
+
+ /**
+ * Write a byte. Nothing to see here!
+ */
+ public void writeByte(byte b) throws TException {
+ writeByteDirect(b);
+ }
+
+ /**
+ * Write an I16 as a zigzag varint.
+ */
+ public void writeI16(short i16) throws TException {
+ writeVarint32(intToZigZag(i16));
+ }
+
+ /**
+ * Write an i32 as a zigzag varint.
+ */
+ public void writeI32(int i32) throws TException {
+ writeVarint32(intToZigZag(i32));
+ }
+
+ /**
+ * Write an i64 as a zigzag varint.
+ */
+ public void writeI64(long i64) throws TException {
+ writeVarint64(longToZigzag(i64));
+ }
+
+ /**
+ * Write a double to the wire as 8 bytes.
+ */
+ public void writeDouble(double dub) throws TException {
+ fixedLongToBytes(Double.doubleToLongBits(dub), temp, 0);
+ trans_.write(temp, 0, 8);
+ }
+
+ /**
+ * Write a string to the wire with a varint size preceding.
+ */
+ public void writeString(String str) throws TException {
+ byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
+ writeBinary(bytes, 0, bytes.length);
+ }
+
+ /**
+ * Write a byte array, using a varint for the size.
+ */
+ public void writeBinary(ByteBuffer bin) throws TException {
+ int length = bin.limit() - bin.position();
+ writeBinary(bin.array(), bin.position() + bin.arrayOffset(), length);
+ }
+
+ private void writeBinary(byte[] buf, int offset, int length) throws TException {
+ writeVarint32(length);
+ trans_.write(buf, offset, length);
+ }
+
+ //
+ // These methods are called by structs, but don't actually have any wire
+ // output or purpose.
+ //
+
+ public void writeMessageEnd() throws TException {}
+ public void writeMapEnd() throws TException {}
+ public void writeListEnd() throws TException {}
+ public void writeSetEnd() throws TException {}
+ public void writeFieldEnd() throws TException {}
+
+ //
+ // Internal writing methods
+ //
+
+ /**
+ * Abstract method for writing the start of lists and sets. List and sets on
+ * the wire differ only by the type indicator.
+ */
+ protected void writeCollectionBegin(byte elemType, int size) throws TException {
+ if (size <= 14) {
+ writeByteDirect(size << 4 | getCompactType(elemType));
+ } else {
+ writeByteDirect(0xf0 | getCompactType(elemType));
+ writeVarint32(size);
+ }
+ }
+
+ /**
+ * Write an i32 as a varint. Results in 1-5 bytes on the wire.
+ * TODO: make a permanent buffer like writeVarint64?
+ */
+ private void writeVarint32(int n) throws TException {
+ int idx = 0;
+ while (true) {
+ if ((n & ~0x7F) == 0) {
+ temp[idx++] = (byte)n;
+ // writeByteDirect((byte)n);
+ break;
+ // return;
+ } else {
+ temp[idx++] = (byte)((n & 0x7F) | 0x80);
+ // writeByteDirect((byte)((n & 0x7F) | 0x80));
+ n >>>= 7;
+ }
+ }
+ trans_.write(temp, 0, idx);
+ }
+
+ /**
+ * Write an i64 as a varint. Results in 1-10 bytes on the wire.
+ */
+ private void writeVarint64(long n) throws TException {
+ int idx = 0;
+ while (true) {
+ if ((n & ~0x7FL) == 0) {
+ temp[idx++] = (byte)n;
+ break;
+ } else {
+ temp[idx++] = ((byte)((n & 0x7F) | 0x80));
+ n >>>= 7;
+ }
+ }
+ trans_.write(temp, 0, idx);
+ }
+
+ /**
+ * Convert l into a zigzag long. This allows negative numbers to be
+ * represented compactly as a varint.
+ */
+ private long longToZigzag(long l) {
+ return (l << 1) ^ (l >> 63);
+ }
+
+ /**
+ * Convert n into a zigzag int. This allows negative numbers to be
+ * represented compactly as a varint.
+ */
+ private int intToZigZag(int n) {
+ return (n << 1) ^ (n >> 31);
+ }
+
+ /**
+ * Convert a long into little-endian bytes in buf starting at off and going
+ * until off+7.
+ */
+ private void fixedLongToBytes(long n, byte[] buf, int off) {
+ buf[off+0] = (byte)( n & 0xff);
+ buf[off+1] = (byte)((n >> 8 ) & 0xff);
+ buf[off+2] = (byte)((n >> 16) & 0xff);
+ buf[off+3] = (byte)((n >> 24) & 0xff);
+ buf[off+4] = (byte)((n >> 32) & 0xff);
+ buf[off+5] = (byte)((n >> 40) & 0xff);
+ buf[off+6] = (byte)((n >> 48) & 0xff);
+ buf[off+7] = (byte)((n >> 56) & 0xff);
+ }
+
+ /**
+ * Writes a byte without any possibility of all that field header nonsense.
+ * Used internally by other writing methods that know they need to write a byte.
+ */
+ private void writeByteDirect(byte b) throws TException {
+ temp[0] = b;
+ trans_.write(temp, 0, 1);
+ }
+
+ /**
+ * Writes a byte without any possibility of all that field header nonsense.
+ */
+ private void writeByteDirect(int n) throws TException {
+ writeByteDirect((byte)n);
+ }
+
+
+ //
+ // Reading methods.
+ //
+
+ /**
+ * Read a message header.
+ */
+ public TMessage readMessageBegin() throws TException {
+ byte protocolId = readByte();
+ if (protocolId != PROTOCOL_ID) {
+ throw new TProtocolException("Expected protocol id " + Integer.toHexString(PROTOCOL_ID) + " but got " + Integer.toHexString(protocolId));
+ }
+ byte versionAndType = readByte();
+ byte version = (byte)(versionAndType & VERSION_MASK);
+ if (version != VERSION) {
+ throw new TProtocolException("Expected version " + VERSION + " but got " + version);
+ }
+ byte type = (byte)((versionAndType >> TYPE_SHIFT_AMOUNT) & TYPE_BITS);
+ int seqid = readVarint32();
+ String messageName = readString();
+ return new TMessage(messageName, type, seqid);
+ }
+
+ /**
+ * Read a struct begin. There's nothing on the wire for this, but it is our
+ * opportunity to push a new struct begin marker onto the field stack.
+ */
+ public TStruct readStructBegin() throws TException {
+ lastField_.push(lastFieldId_);
+ lastFieldId_ = 0;
+ return ANONYMOUS_STRUCT;
+ }
+
+ /**
+ * Doesn't actually consume any wire data, just removes the last field for
+ * this struct from the field stack.
+ */
+ public void readStructEnd() throws TException {
+ // consume the last field we read off the wire.
+ lastFieldId_ = lastField_.pop();
+ }
+
+ /**
+ * Read a field header off the wire.
+ */
+ public TField readFieldBegin() throws TException {
+ byte type = readByte();
+
+ // if it's a stop, then we can return immediately, as the struct is over.
+ if (type == TType.STOP) {
+ return TSTOP;
+ }
+
+ short fieldId;
+
+ // mask off the 4 MSB of the type header. it could contain a field id delta.
+ short modifier = (short)((type & 0xf0) >> 4);
+ if (modifier == 0) {
+ // not a delta. look ahead for the zigzag varint field id.
+ fieldId = readI16();
+ } else {
+ // has a delta. add the delta to the last read field id.
+ fieldId = (short)(lastFieldId_ + modifier);
+ }
+
+ TField field = new TField("", getTType((byte)(type & 0x0f)), fieldId);
+
+ // if this happens to be a boolean field, the value is encoded in the type
+ if (isBoolType(type)) {
+ // save the boolean value in a special instance variable.
+ boolValue_ = (byte)(type & 0x0f) == Types.BOOLEAN_TRUE ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ // push the new field onto the field stack so we can keep the deltas going.
+ lastFieldId_ = field.id;
+ return field;
+ }
+
+ /**
+ * Read a map header off the wire. If the size is zero, skip reading the key
+ * and value type. This means that 0-length maps will yield TMaps without the
+ * "correct" types.
+ */
+ public TMap readMapBegin() throws TException {
+ int size = readVarint32();
+ checkContainerReadLength(size);
+ byte keyAndValueType = size == 0 ? 0 : readByte();
+ return new TMap(getTType((byte)(keyAndValueType >> 4)), getTType((byte)(keyAndValueType & 0xf)), size);
+ }
+
+ /**
+ * Read a list header off the wire. If the list size is 0-14, the size will
+ * be packed into the element type header. If it's a longer list, the 4 MSB
+ * of the element type header will be 0xF, and a varint will follow with the
+ * true size.
+ */
+ public TList readListBegin() throws TException {
+ byte size_and_type = readByte();
+ int size = (size_and_type >> 4) & 0x0f;
+ if (size == 15) {
+ size = readVarint32();
+ }
+ checkContainerReadLength(size);
+ byte type = getTType(size_and_type);
+ return new TList(type, size);
+ }
+
+ /**
+ * Read a set header off the wire. If the set size is 0-14, the size will
+ * be packed into the element type header. If it's a longer set, the 4 MSB
+ * of the element type header will be 0xF, and a varint will follow with the
+ * true size.
+ */
+ public TSet readSetBegin() throws TException {
+ return new TSet(readListBegin());
+ }
+
+ /**
+ * Read a boolean off the wire. If this is a boolean field, the value should
+ * already have been read during readFieldBegin, so we'll just consume the
+ * pre-stored value. Otherwise, read a byte.
+ */
+ public boolean readBool() throws TException {
+ if (boolValue_ != null) {
+ boolean result = boolValue_.booleanValue();
+ boolValue_ = null;
+ return result;
+ }
+ return readByte() == Types.BOOLEAN_TRUE;
+ }
+
+ /**
+ * Read a single byte off the wire. Nothing interesting here.
+ */
+ public byte readByte() throws TException {
+ byte b;
+ if (trans_.getBytesRemainingInBuffer() > 0) {
+ b = trans_.getBuffer()[trans_.getBufferPosition()];
+ trans_.consumeBuffer(1);
+ } else {
+ trans_.readAll(temp, 0, 1);
+ b = temp[0];
+ }
+ return b;
+ }
+
+ /**
+ * Read an i16 from the wire as a zigzag varint.
+ */
+ public short readI16() throws TException {
+ return (short)zigzagToInt(readVarint32());
+ }
+
+ /**
+ * Read an i32 from the wire as a zigzag varint.
+ */
+ public int readI32() throws TException {
+ return zigzagToInt(readVarint32());
+ }
+
+ /**
+ * Read an i64 from the wire as a zigzag varint.
+ */
+ public long readI64() throws TException {
+ return zigzagToLong(readVarint64());
+ }
+
+ /**
+ * No magic here - just read a double off the wire.
+ */
+ public double readDouble() throws TException {
+ trans_.readAll(temp, 0, 8);
+ return Double.longBitsToDouble(bytesToLong(temp));
+ }
+
+ /**
+ * Reads a byte[] (via readBinary), and then UTF-8 decodes it.
+ */
+ public String readString() throws TException {
+ int length = readVarint32();
+ checkStringReadLength(length);
+
+ if (length == 0) {
+ return "";
+ }
+
+ final String str;
+ if (trans_.getBytesRemainingInBuffer() >= length) {
+ str = new String(trans_.getBuffer(), trans_.getBufferPosition(),
+ length, StandardCharsets.UTF_8);
+ trans_.consumeBuffer(length);
+ } else {
+ str = new String(readBinary(length), StandardCharsets.UTF_8);
+ }
+ return str;
+ }
+
+ /**
+ * Read a byte[] from the wire.
+ */
+ public ByteBuffer readBinary() throws TException {
+ int length = readVarint32();
+ checkStringReadLength(length);
+ if (length == 0) return EMPTY_BUFFER;
+
+ if (trans_.getBytesRemainingInBuffer() >= length) {
+ ByteBuffer bb = ByteBuffer.wrap(trans_.getBuffer(), trans_.getBufferPosition(), length);
+ trans_.consumeBuffer(length);
+ return bb;
+ }
+
+ byte[] buf = new byte[length];
+ trans_.readAll(buf, 0, length);
+ return ByteBuffer.wrap(buf);
+ }
+
+ /**
+ * Read a byte[] of a known length from the wire.
+ */
+ private byte[] readBinary(int length) throws TException {
+ if (length == 0) return EMPTY_BYTES;
+
+ byte[] buf = new byte[length];
+ trans_.readAll(buf, 0, length);
+ return buf;
+ }
+
+ private void checkStringReadLength(int length) throws TProtocolException {
+ if (length < 0) {
+ throw new TProtocolException(TProtocolException.NEGATIVE_SIZE,
+ "Negative length: " + length);
+ }
+ if (stringLengthLimit_ != NO_LENGTH_LIMIT && length > stringLengthLimit_) {
+ throw new TProtocolException(TProtocolException.SIZE_LIMIT,
+ "Length exceeded max allowed: " + length);
+ }
+ }
+
+ private void checkContainerReadLength(int length) throws TProtocolException {
+ if (length < 0) {
+ throw new TProtocolException(TProtocolException.NEGATIVE_SIZE,
+ "Negative length: " + length);
+ }
+ if (containerLengthLimit_ != NO_LENGTH_LIMIT && length > containerLengthLimit_) {
+ throw new TProtocolException(TProtocolException.SIZE_LIMIT,
+ "Length exceeded max allowed: " + length);
+ }
+ }
+
+ //
+ // These methods are here for the struct to call, but don't have any wire
+ // encoding.
+ //
+ public void readMessageEnd() throws TException {}
+ public void readFieldEnd() throws TException {}
+ public void readMapEnd() throws TException {}
+ public void readListEnd() throws TException {}
+ public void readSetEnd() throws TException {}
+
+ //
+ // Internal reading methods
+ //
+
+ /**
+ * Read an i32 from the wire as a varint. The MSB of each byte is set
+ * if there is another byte to follow. This can read up to 5 bytes.
+ */
+ private int readVarint32() throws TException {
+ int result = 0;
+ int shift = 0;
+ if (trans_.getBytesRemainingInBuffer() >= 5) {
+ byte[] buf = trans_.getBuffer();
+ int pos = trans_.getBufferPosition();
+ int off = 0;
+ while (true) {
+ byte b = buf[pos+off];
+ result |= (int) (b & 0x7f) << shift;
+ if ((b & 0x80) != 0x80) break;
+ shift += 7;
+ off++;
+ }
+ trans_.consumeBuffer(off+1);
+ } else {
+ while (true) {
+ byte b = readByte();
+ result |= (int) (b & 0x7f) << shift;
+ if ((b & 0x80) != 0x80) break;
+ shift += 7;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Read an i64 from the wire as a proper varint. The MSB of each byte is set
+ * if there is another byte to follow. This can read up to 10 bytes.
+ */
+ private long readVarint64() throws TException {
+ int shift = 0;
+ long result = 0;
+ if (trans_.getBytesRemainingInBuffer() >= 10) {
+ byte[] buf = trans_.getBuffer();
+ int pos = trans_.getBufferPosition();
+ int off = 0;
+ while (true) {
+ byte b = buf[pos+off];
+ result |= (long) (b & 0x7f) << shift;
+ if ((b & 0x80) != 0x80) break;
+ shift += 7;
+ off++;
+ }
+ trans_.consumeBuffer(off+1);
+ } else {
+ while (true) {
+ byte b = readByte();
+ result |= (long) (b & 0x7f) << shift;
+ if ((b & 0x80) != 0x80) break;
+ shift +=7;
+ }
+ }
+ return result;
+ }
+
+ //
+ // encoding helpers
+ //
+
+ /**
+ * Convert from zigzag int to int.
+ */
+ private int zigzagToInt(int n) {
+ return (n >>> 1) ^ -(n & 1);
+ }
+
+ /**
+ * Convert from zigzag long to long.
+ */
+ private long zigzagToLong(long n) {
+ return (n >>> 1) ^ -(n & 1);
+ }
+
+ /**
+ * Note that it's important that the mask bytes are long literals,
+ * otherwise they'll default to ints, and when you shift an int left 56 bits,
+ * you just get a messed up int.
+ */
+ private long bytesToLong(byte[] bytes) {
+ return
+ ((bytes[7] & 0xffL) << 56) |
+ ((bytes[6] & 0xffL) << 48) |
+ ((bytes[5] & 0xffL) << 40) |
+ ((bytes[4] & 0xffL) << 32) |
+ ((bytes[3] & 0xffL) << 24) |
+ ((bytes[2] & 0xffL) << 16) |
+ ((bytes[1] & 0xffL) << 8) |
+ ((bytes[0] & 0xffL));
+ }
+
+ //
+ // type testing and converting
+ //
+
+ private boolean isBoolType(byte b) {
+ int lowerNibble = b & 0x0f;
+ return lowerNibble == Types.BOOLEAN_TRUE || lowerNibble == Types.BOOLEAN_FALSE;
+ }
+
+ /**
+ * Given a TCompactProtocol.Types constant, convert it to its corresponding
+ * TType value.
+ */
+ private byte getTType(byte type) throws TProtocolException {
+ switch ((byte)(type & 0x0f)) {
+ case TType.STOP:
+ return TType.STOP;
+ case Types.BOOLEAN_FALSE:
+ case Types.BOOLEAN_TRUE:
+ return TType.BOOL;
+ case Types.BYTE:
+ return TType.BYTE;
+ case Types.I16:
+ return TType.I16;
+ case Types.I32:
+ return TType.I32;
+ case Types.I64:
+ return TType.I64;
+ case Types.DOUBLE:
+ return TType.DOUBLE;
+ case Types.BINARY:
+ return TType.STRING;
+ case Types.LIST:
+ return TType.LIST;
+ case Types.SET:
+ return TType.SET;
+ case Types.MAP:
+ return TType.MAP;
+ case Types.STRUCT:
+ return TType.STRUCT;
+ default:
+ throw new TProtocolException("don't know what type: " + (byte)(type & 0x0f));
+ }
+ }
+
+ /**
+ * Given a TType value, find the appropriate TCompactProtocol.Types constant.
+ */
+ private byte getCompactType(byte ttype) {
+ return ttypeToCompactType[ttype];
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TField.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TField.java
new file mode 100644
index 000000000..3872b008f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TField.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates field metadata.
+ * <p>Two fields are considered equal if they have the same type and id.</p>
+ */
+public class TField {
+ public TField() {
+ this("", TType.STOP, (short)0);
+ }
+
+ public TField(String n, byte t, short i) {
+ name = n;
+ type = t;
+ id = i;
+ }
+
+ public final String name;
+ public final byte type;
+ public final short id;
+
+ public String toString() {
+ return "<TField name:'" + name + "' type:" + type + " field-id:" + id + ">";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + id;
+ result = prime * result + type;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ TField otherField = (TField) obj;
+ return type == otherField.type && id == otherField.id;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TJSONProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TJSONProtocol.java
new file mode 100644
index 000000000..d37c4937f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TJSONProtocol.java
@@ -0,0 +1,973 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Stack;
+
+import org.apache.thrift.TByteArrayOutputStream;
+import org.apache.thrift.TException;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * JSON protocol implementation for thrift.
+ *
+ * This is a full-featured protocol supporting write and read.
+ *
+ * Please see the C++ class header for a detailed description of the
+ * protocol's wire format.
+ *
+ */
+public class TJSONProtocol extends TProtocol {
+
+ /**
+ * Factory for JSON protocol objects
+ */
+ public static class Factory implements TProtocolFactory {
+ protected boolean fieldNamesAsString_ = false;
+
+ public Factory() {}
+
+ public Factory(boolean fieldNamesAsString) {
+ fieldNamesAsString_ = fieldNamesAsString;
+ }
+
+ public TProtocol getProtocol(TTransport trans) {
+ return new TJSONProtocol(trans, fieldNamesAsString_);
+ }
+
+ }
+
+ private static final byte[] COMMA = new byte[] {','};
+ private static final byte[] COLON = new byte[] {':'};
+ private static final byte[] LBRACE = new byte[] {'{'};
+ private static final byte[] RBRACE = new byte[] {'}'};
+ private static final byte[] LBRACKET = new byte[] {'['};
+ private static final byte[] RBRACKET = new byte[] {']'};
+ private static final byte[] QUOTE = new byte[] {'"'};
+ private static final byte[] BACKSLASH = new byte[] {'\\'};
+ private static final byte[] ZERO = new byte[] {'0'};
+
+ private static final byte[] ESCSEQ = new byte[] {'\\','u','0','0'};
+
+ private static final long VERSION = 1;
+
+ private static final byte[] JSON_CHAR_TABLE = {
+ /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ 0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
+ 1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
+ };
+
+ private static final String ESCAPE_CHARS = "\"\\/bfnrt";
+
+ private static final byte[] ESCAPE_CHAR_VALS = {
+ '"', '\\', '/', '\b', '\f', '\n', '\r', '\t',
+ };
+
+ private static final int DEF_STRING_SIZE = 16;
+
+ private static final byte[] NAME_BOOL = new byte[] {'t', 'f'};
+ private static final byte[] NAME_BYTE = new byte[] {'i','8'};
+ private static final byte[] NAME_I16 = new byte[] {'i','1','6'};
+ private static final byte[] NAME_I32 = new byte[] {'i','3','2'};
+ private static final byte[] NAME_I64 = new byte[] {'i','6','4'};
+ private static final byte[] NAME_DOUBLE = new byte[] {'d','b','l'};
+ private static final byte[] NAME_STRUCT = new byte[] {'r','e','c'};
+ private static final byte[] NAME_STRING = new byte[] {'s','t','r'};
+ private static final byte[] NAME_MAP = new byte[] {'m','a','p'};
+ private static final byte[] NAME_LIST = new byte[] {'l','s','t'};
+ private static final byte[] NAME_SET = new byte[] {'s','e','t'};
+
+ private static final TStruct ANONYMOUS_STRUCT = new TStruct();
+
+ private static final byte[] getTypeNameForTypeID(byte typeID)
+ throws TException {
+ switch (typeID) {
+ case TType.BOOL:
+ return NAME_BOOL;
+ case TType.BYTE:
+ return NAME_BYTE;
+ case TType.I16:
+ return NAME_I16;
+ case TType.I32:
+ return NAME_I32;
+ case TType.I64:
+ return NAME_I64;
+ case TType.DOUBLE:
+ return NAME_DOUBLE;
+ case TType.STRING:
+ return NAME_STRING;
+ case TType.STRUCT:
+ return NAME_STRUCT;
+ case TType.MAP:
+ return NAME_MAP;
+ case TType.SET:
+ return NAME_SET;
+ case TType.LIST:
+ return NAME_LIST;
+ default:
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
+ "Unrecognized type");
+ }
+ }
+
+ private static final byte getTypeIDForTypeName(byte[] name)
+ throws TException {
+ byte result = TType.STOP;
+ if (name.length > 1) {
+ switch (name[0]) {
+ case 'd':
+ result = TType.DOUBLE;
+ break;
+ case 'i':
+ switch (name[1]) {
+ case '8':
+ result = TType.BYTE;
+ break;
+ case '1':
+ result = TType.I16;
+ break;
+ case '3':
+ result = TType.I32;
+ break;
+ case '6':
+ result = TType.I64;
+ break;
+ }
+ break;
+ case 'l':
+ result = TType.LIST;
+ break;
+ case 'm':
+ result = TType.MAP;
+ break;
+ case 'r':
+ result = TType.STRUCT;
+ break;
+ case 's':
+ if (name[1] == 't') {
+ result = TType.STRING;
+ }
+ else if (name[1] == 'e') {
+ result = TType.SET;
+ }
+ break;
+ case 't':
+ result = TType.BOOL;
+ break;
+ }
+ }
+ if (result == TType.STOP) {
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
+ "Unrecognized type");
+ }
+ return result;
+ }
+
+ // Base class for tracking JSON contexts that may require inserting/reading
+ // additional JSON syntax characters
+ // This base context does nothing.
+ protected class JSONBaseContext {
+ protected void write() throws TException {}
+
+ protected void read() throws TException {}
+
+ protected boolean escapeNum() { return false; }
+ }
+
+ // Context for JSON lists. Will insert/read commas before each item except
+ // for the first one
+ protected class JSONListContext extends JSONBaseContext {
+ private boolean first_ = true;
+
+ @Override
+ protected void write() throws TException {
+ if (first_) {
+ first_ = false;
+ } else {
+ trans_.write(COMMA);
+ }
+ }
+
+ @Override
+ protected void read() throws TException {
+ if (first_) {
+ first_ = false;
+ } else {
+ readJSONSyntaxChar(COMMA);
+ }
+ }
+ }
+
+ // Context for JSON records. Will insert/read colons before the value portion
+ // of each record pair, and commas before each key except the first. In
+ // addition, will indicate that numbers in the key position need to be
+ // escaped in quotes (since JSON keys must be strings).
+ protected class JSONPairContext extends JSONBaseContext {
+ private boolean first_ = true;
+ private boolean colon_ = true;
+
+ @Override
+ protected void write() throws TException {
+ if (first_) {
+ first_ = false;
+ colon_ = true;
+ } else {
+ trans_.write(colon_ ? COLON : COMMA);
+ colon_ = !colon_;
+ }
+ }
+
+ @Override
+ protected void read() throws TException {
+ if (first_) {
+ first_ = false;
+ colon_ = true;
+ } else {
+ readJSONSyntaxChar(colon_ ? COLON : COMMA);
+ colon_ = !colon_;
+ }
+ }
+
+ @Override
+ protected boolean escapeNum() {
+ return colon_;
+ }
+ }
+
+ // Holds up to one byte from the transport
+ protected class LookaheadReader {
+
+ private boolean hasData_;
+ private byte[] data_ = new byte[1];
+
+ // Return and consume the next byte to be read, either taking it from the
+ // data buffer if present or getting it from the transport otherwise.
+ protected byte read() throws TException {
+ if (hasData_) {
+ hasData_ = false;
+ }
+ else {
+ trans_.readAll(data_, 0, 1);
+ }
+ return data_[0];
+ }
+
+ // Return the next byte to be read without consuming, filling the data
+ // buffer if it has not been filled already.
+ protected byte peek() throws TException {
+ if (!hasData_) {
+ trans_.readAll(data_, 0, 1);
+ }
+ hasData_ = true;
+ return data_[0];
+ }
+ }
+
+ // Stack of nested contexts that we may be in
+ private Stack<JSONBaseContext> contextStack_ = new Stack<JSONBaseContext>();
+
+ // Current context that we are in
+ private JSONBaseContext context_ = new JSONBaseContext();
+
+ // Reader that manages a 1-byte buffer
+ private LookaheadReader reader_ = new LookaheadReader();
+
+ // Write out the TField names as a string instead of the default integer value
+ private boolean fieldNamesAsString_ = false;
+
+ // Push a new JSON context onto the stack.
+ private void pushContext(JSONBaseContext c) {
+ contextStack_.push(context_);
+ context_ = c;
+ }
+
+ // Pop the last JSON context off the stack
+ private void popContext() {
+ context_ = contextStack_.pop();
+ }
+
+ // Reset the context stack to its initial state
+ private void resetContext() {
+ while (!contextStack_.isEmpty()) {
+ popContext();
+ }
+ }
+
+ /**
+ * Constructor
+ */
+ public TJSONProtocol(TTransport trans) {
+ super(trans);
+ }
+
+ public TJSONProtocol(TTransport trans, boolean fieldNamesAsString) {
+ super(trans);
+ fieldNamesAsString_ = fieldNamesAsString;
+ }
+
+ @Override
+ public void reset() {
+ contextStack_.clear();
+ context_ = new JSONBaseContext();
+ reader_ = new LookaheadReader();
+ }
+
+ // Temporary buffer used by several methods
+ private byte[] tmpbuf_ = new byte[4];
+
+ // Read a byte that must match b[0]; otherwise an exception is thrown.
+ // Marked protected to avoid synthetic accessor in JSONListContext.read
+ // and JSONPairContext.read
+ protected void readJSONSyntaxChar(byte[] b) throws TException {
+ byte ch = reader_.read();
+ if (ch != b[0]) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Unexpected character:" + (char)ch);
+ }
+ }
+
+ // Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its
+ // corresponding hex value
+ private static final byte hexVal(byte ch) throws TException {
+ if ((ch >= '0') && (ch <= '9')) {
+ return (byte)((char)ch - '0');
+ }
+ else if ((ch >= 'a') && (ch <= 'f')) {
+ return (byte)((char)ch - 'a' + 10);
+ }
+ else {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected hex character");
+ }
+ }
+
+ // Convert a byte containing a hex value to its corresponding hex character
+ private static final byte hexChar(byte val) {
+ val &= 0x0F;
+ if (val < 10) {
+ return (byte)((char)val + '0');
+ }
+ else {
+ return (byte)((char)(val - 10) + 'a');
+ }
+ }
+
+ // Write the bytes in array buf as a JSON characters, escaping as needed
+ private void writeJSONString(byte[] b) throws TException {
+ context_.write();
+ trans_.write(QUOTE);
+ int len = b.length;
+ for (int i = 0; i < len; i++) {
+ if ((b[i] & 0x00FF) >= 0x30) {
+ if (b[i] == BACKSLASH[0]) {
+ trans_.write(BACKSLASH);
+ trans_.write(BACKSLASH);
+ }
+ else {
+ trans_.write(b, i, 1);
+ }
+ }
+ else {
+ tmpbuf_[0] = JSON_CHAR_TABLE[b[i]];
+ if (tmpbuf_[0] == 1) {
+ trans_.write(b, i, 1);
+ }
+ else if (tmpbuf_[0] > 1) {
+ trans_.write(BACKSLASH);
+ trans_.write(tmpbuf_, 0, 1);
+ }
+ else {
+ trans_.write(ESCSEQ);
+ tmpbuf_[0] = hexChar((byte)(b[i] >> 4));
+ tmpbuf_[1] = hexChar(b[i]);
+ trans_.write(tmpbuf_, 0, 2);
+ }
+ }
+ }
+ trans_.write(QUOTE);
+ }
+
+ // Write out number as a JSON value. If the context dictates so, it will be
+ // wrapped in quotes to output as a JSON string.
+ private void writeJSONInteger(long num) throws TException {
+ context_.write();
+ String str = Long.toString(num);
+ boolean escapeNum = context_.escapeNum();
+ if (escapeNum) {
+ trans_.write(QUOTE);
+ }
+ byte[] buf = str.getBytes(StandardCharsets.UTF_8);
+ trans_.write(buf);
+ if (escapeNum) {
+ trans_.write(QUOTE);
+ }
+ }
+
+ // Write out a double as a JSON value. If it is NaN or infinity or if the
+ // context dictates escaping, write out as JSON string.
+ private void writeJSONDouble(double num) throws TException {
+ context_.write();
+ String str = Double.toString(num);
+ boolean special = false;
+ switch (str.charAt(0)) {
+ case 'N': // NaN
+ case 'I': // Infinity
+ special = true;
+ break;
+ case '-':
+ if (str.charAt(1) == 'I') { // -Infinity
+ special = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ boolean escapeNum = special || context_.escapeNum();
+ if (escapeNum) {
+ trans_.write(QUOTE);
+ }
+ byte[] b = str.getBytes(StandardCharsets.UTF_8);
+ trans_.write(b, 0, b.length);
+ if (escapeNum) {
+ trans_.write(QUOTE);
+ }
+ }
+
+ // Write out contents of byte array b as a JSON string with base-64 encoded
+ // data
+ private void writeJSONBase64(byte[] b, int offset, int length) throws TException {
+ context_.write();
+ trans_.write(QUOTE);
+ int len = length;
+ int off = offset;
+ while (len >= 3) {
+ // Encode 3 bytes at a time
+ TBase64Utils.encode(b, off, 3, tmpbuf_, 0);
+ trans_.write(tmpbuf_, 0, 4);
+ off += 3;
+ len -= 3;
+ }
+ if (len > 0) {
+ // Encode remainder
+ TBase64Utils.encode(b, off, len, tmpbuf_, 0);
+ trans_.write(tmpbuf_, 0, len + 1);
+ }
+ trans_.write(QUOTE);
+ }
+
+ private void writeJSONObjectStart() throws TException {
+ context_.write();
+ trans_.write(LBRACE);
+ pushContext(new JSONPairContext());
+ }
+
+ private void writeJSONObjectEnd() throws TException {
+ popContext();
+ trans_.write(RBRACE);
+ }
+
+ private void writeJSONArrayStart() throws TException {
+ context_.write();
+ trans_.write(LBRACKET);
+ pushContext(new JSONListContext());
+ }
+
+ private void writeJSONArrayEnd() throws TException {
+ popContext();
+ trans_.write(RBRACKET);
+ }
+
+ @Override
+ public void writeMessageBegin(TMessage message) throws TException {
+ resetContext(); // THRIFT-3743
+ writeJSONArrayStart();
+ writeJSONInteger(VERSION);
+ byte[] b = message.name.getBytes(StandardCharsets.UTF_8);
+ writeJSONString(b);
+ writeJSONInteger(message.type);
+ writeJSONInteger(message.seqid);
+ }
+
+ @Override
+ public void writeMessageEnd() throws TException {
+ writeJSONArrayEnd();
+ }
+
+ @Override
+ public void writeStructBegin(TStruct struct) throws TException {
+ writeJSONObjectStart();
+ }
+
+ @Override
+ public void writeStructEnd() throws TException {
+ writeJSONObjectEnd();
+ }
+
+ @Override
+ public void writeFieldBegin(TField field) throws TException {
+ if (fieldNamesAsString_) {
+ writeString(field.name);
+ } else {
+ writeJSONInteger(field.id);
+ }
+ writeJSONObjectStart();
+ writeJSONString(getTypeNameForTypeID(field.type));
+ }
+
+ @Override
+ public void writeFieldEnd() throws TException {
+ writeJSONObjectEnd();
+ }
+
+ @Override
+ public void writeFieldStop() {}
+
+ @Override
+ public void writeMapBegin(TMap map) throws TException {
+ writeJSONArrayStart();
+ writeJSONString(getTypeNameForTypeID(map.keyType));
+ writeJSONString(getTypeNameForTypeID(map.valueType));
+ writeJSONInteger(map.size);
+ writeJSONObjectStart();
+ }
+
+ @Override
+ public void writeMapEnd() throws TException {
+ writeJSONObjectEnd();
+ writeJSONArrayEnd();
+ }
+
+ @Override
+ public void writeListBegin(TList list) throws TException {
+ writeJSONArrayStart();
+ writeJSONString(getTypeNameForTypeID(list.elemType));
+ writeJSONInteger(list.size);
+ }
+
+ @Override
+ public void writeListEnd() throws TException {
+ writeJSONArrayEnd();
+ }
+
+ @Override
+ public void writeSetBegin(TSet set) throws TException {
+ writeJSONArrayStart();
+ writeJSONString(getTypeNameForTypeID(set.elemType));
+ writeJSONInteger(set.size);
+ }
+
+ @Override
+ public void writeSetEnd() throws TException {
+ writeJSONArrayEnd();
+ }
+
+ @Override
+ public void writeBool(boolean b) throws TException {
+ writeJSONInteger(b ? (long)1 : (long)0);
+ }
+
+ @Override
+ public void writeByte(byte b) throws TException {
+ writeJSONInteger((long)b);
+ }
+
+ @Override
+ public void writeI16(short i16) throws TException {
+ writeJSONInteger((long)i16);
+ }
+
+ @Override
+ public void writeI32(int i32) throws TException {
+ writeJSONInteger((long)i32);
+ }
+
+ @Override
+ public void writeI64(long i64) throws TException {
+ writeJSONInteger(i64);
+ }
+
+ @Override
+ public void writeDouble(double dub) throws TException {
+ writeJSONDouble(dub);
+ }
+
+ @Override
+ public void writeString(String str) throws TException {
+ byte[] b = str.getBytes(StandardCharsets.UTF_8);
+ writeJSONString(b);
+ }
+
+ @Override
+ public void writeBinary(ByteBuffer bin) throws TException {
+ writeJSONBase64(bin.array(), bin.position() + bin.arrayOffset(), bin.limit() - bin.position() - bin.arrayOffset());
+ }
+
+ /**
+ * Reading methods.
+ */
+
+ // Read in a JSON string, unescaping as appropriate.. Skip reading from the
+ // context if skipContext is true.
+ private TByteArrayOutputStream readJSONString(boolean skipContext)
+ throws TException {
+ TByteArrayOutputStream arr = new TByteArrayOutputStream(DEF_STRING_SIZE);
+ ArrayList<Character> codeunits = new ArrayList<Character>();
+ if (!skipContext) {
+ context_.read();
+ }
+ readJSONSyntaxChar(QUOTE);
+ while (true) {
+ byte ch = reader_.read();
+ if (ch == QUOTE[0]) {
+ break;
+ }
+ if (ch == ESCSEQ[0]) {
+ ch = reader_.read();
+ if (ch == ESCSEQ[1]) {
+ trans_.readAll(tmpbuf_, 0, 4);
+ short cu = (short)(
+ ((short)hexVal(tmpbuf_[0]) << 12) +
+ ((short)hexVal(tmpbuf_[1]) << 8) +
+ ((short)hexVal(tmpbuf_[2]) << 4) +
+ (short)hexVal(tmpbuf_[3]));
+ try {
+ if (Character.isHighSurrogate((char)cu)) {
+ if (codeunits.size() > 0) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected low surrogate char");
+ }
+ codeunits.add((char)cu);
+ }
+ else if (Character.isLowSurrogate((char)cu)) {
+ if (codeunits.size() == 0) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected high surrogate char");
+ }
+
+ codeunits.add((char)cu);
+ arr.write(
+ (new String(new int[] { codeunits.get(0), codeunits.get(1) },
+ 0, 2)).getBytes(StandardCharsets.UTF_8));
+ codeunits.clear();
+ }
+ else {
+ arr.write((new String(new int[] { cu }, 0, 1))
+ .getBytes(StandardCharsets.UTF_8));
+ }
+ continue;
+ } catch (IOException ex) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Invalid unicode sequence");
+ }
+ }
+ else {
+ int off = ESCAPE_CHARS.indexOf(ch);
+ if (off == -1) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected control char");
+ }
+ ch = ESCAPE_CHAR_VALS[off];
+ }
+ }
+ arr.write(ch);
+ }
+ return arr;
+ }
+
+ // Return true if the given byte could be a valid part of a JSON number.
+ private boolean isJSONNumeric(byte b) {
+ switch (b) {
+ case '+':
+ case '-':
+ case '.':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'E':
+ case 'e':
+ return true;
+ }
+ return false;
+ }
+
+ // Read in a sequence of characters that are all valid in JSON numbers. Does
+ // not do a complete regex check to validate that this is actually a number.
+ private String readJSONNumericChars() throws TException {
+ StringBuilder strbld = new StringBuilder();
+ while (true) {
+ byte ch = reader_.peek();
+ if (!isJSONNumeric(ch)) {
+ break;
+ }
+ strbld.append((char)reader_.read());
+ }
+ return strbld.toString();
+ }
+
+ // Read in a JSON number. If the context dictates, read in enclosing quotes.
+ private long readJSONInteger() throws TException {
+ context_.read();
+ if (context_.escapeNum()) {
+ readJSONSyntaxChar(QUOTE);
+ }
+ String str = readJSONNumericChars();
+ if (context_.escapeNum()) {
+ readJSONSyntaxChar(QUOTE);
+ }
+ try {
+ return Long.valueOf(str);
+ }
+ catch (NumberFormatException ex) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Bad data encounted in numeric data");
+ }
+ }
+
+ // Read in a JSON double value. Throw if the value is not wrapped in quotes
+ // when expected or if wrapped in quotes when not expected.
+ private double readJSONDouble() throws TException {
+ context_.read();
+ if (reader_.peek() == QUOTE[0]) {
+ TByteArrayOutputStream arr = readJSONString(true);
+ double dub = Double.valueOf(arr.toString(StandardCharsets.UTF_8));
+ if (!context_.escapeNum() && !Double.isNaN(dub)
+ && !Double.isInfinite(dub)) {
+ // Throw exception -- we should not be in a string in this case
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Numeric data unexpectedly quoted");
+ }
+ return dub;
+ }
+ else {
+ if (context_.escapeNum()) {
+ // This will throw - we should have had a quote if escapeNum == true
+ readJSONSyntaxChar(QUOTE);
+ }
+ try {
+ return Double.valueOf(readJSONNumericChars());
+ }
+ catch (NumberFormatException ex) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Bad data encounted in numeric data");
+ }
+ }
+ }
+
+ // Read in a JSON string containing base-64 encoded data and decode it.
+ private byte[] readJSONBase64() throws TException {
+ TByteArrayOutputStream arr = readJSONString(false);
+ byte[] b = arr.get();
+ int len = arr.len();
+ int off = 0;
+ int size = 0;
+ // Ignore padding
+ int bound = len >= 2 ? len - 2 : 0;
+ for (int i = len - 1; i >= bound && b[i] == '='; --i) {
+ --len;
+ }
+ while (len >= 4) {
+ // Decode 4 bytes at a time
+ TBase64Utils.decode(b, off, 4, b, size); // NB: decoded in place
+ off += 4;
+ len -= 4;
+ size += 3;
+ }
+ // Don't decode if we hit the end or got a single leftover byte (invalid
+ // base64 but legal for skip of regular string type)
+ if (len > 1) {
+ // Decode remainder
+ TBase64Utils.decode(b, off, len, b, size); // NB: decoded in place
+ size += len - 1;
+ }
+ // Sadly we must copy the byte[] (any way around this?)
+ byte [] result = new byte[size];
+ System.arraycopy(b, 0, result, 0, size);
+ return result;
+ }
+
+ private void readJSONObjectStart() throws TException {
+ context_.read();
+ readJSONSyntaxChar(LBRACE);
+ pushContext(new JSONPairContext());
+ }
+
+ private void readJSONObjectEnd() throws TException {
+ readJSONSyntaxChar(RBRACE);
+ popContext();
+ }
+
+ private void readJSONArrayStart() throws TException {
+ context_.read();
+ readJSONSyntaxChar(LBRACKET);
+ pushContext(new JSONListContext());
+ }
+
+ private void readJSONArrayEnd() throws TException {
+ readJSONSyntaxChar(RBRACKET);
+ popContext();
+ }
+
+ @Override
+ public TMessage readMessageBegin() throws TException {
+ resetContext(); // THRIFT-3743
+ readJSONArrayStart();
+ if (readJSONInteger() != VERSION) {
+ throw new TProtocolException(TProtocolException.BAD_VERSION,
+ "Message contained bad version.");
+ }
+ String name = readJSONString(false).toString(StandardCharsets.UTF_8);
+ byte type = (byte) readJSONInteger();
+ int seqid = (int) readJSONInteger();
+ return new TMessage(name, type, seqid);
+ }
+
+ @Override
+ public void readMessageEnd() throws TException {
+ readJSONArrayEnd();
+ }
+
+ @Override
+ public TStruct readStructBegin() throws TException {
+ readJSONObjectStart();
+ return ANONYMOUS_STRUCT;
+ }
+
+ @Override
+ public void readStructEnd() throws TException {
+ readJSONObjectEnd();
+ }
+
+ @Override
+ public TField readFieldBegin() throws TException {
+ byte ch = reader_.peek();
+ byte type;
+ short id = 0;
+ if (ch == RBRACE[0]) {
+ type = TType.STOP;
+ }
+ else {
+ id = (short) readJSONInteger();
+ readJSONObjectStart();
+ type = getTypeIDForTypeName(readJSONString(false).get());
+ }
+ return new TField("", type, id);
+ }
+
+ @Override
+ public void readFieldEnd() throws TException {
+ readJSONObjectEnd();
+ }
+
+ @Override
+ public TMap readMapBegin() throws TException {
+ readJSONArrayStart();
+ byte keyType = getTypeIDForTypeName(readJSONString(false).get());
+ byte valueType = getTypeIDForTypeName(readJSONString(false).get());
+ int size = (int)readJSONInteger();
+ readJSONObjectStart();
+ return new TMap(keyType, valueType, size);
+ }
+
+ @Override
+ public void readMapEnd() throws TException {
+ readJSONObjectEnd();
+ readJSONArrayEnd();
+ }
+
+ @Override
+ public TList readListBegin() throws TException {
+ readJSONArrayStart();
+ byte elemType = getTypeIDForTypeName(readJSONString(false).get());
+ int size = (int)readJSONInteger();
+ return new TList(elemType, size);
+ }
+
+ @Override
+ public void readListEnd() throws TException {
+ readJSONArrayEnd();
+ }
+
+ @Override
+ public TSet readSetBegin() throws TException {
+ readJSONArrayStart();
+ byte elemType = getTypeIDForTypeName(readJSONString(false).get());
+ int size = (int)readJSONInteger();
+ return new TSet(elemType, size);
+ }
+
+ @Override
+ public void readSetEnd() throws TException {
+ readJSONArrayEnd();
+ }
+
+ @Override
+ public boolean readBool() throws TException {
+ return (readJSONInteger() == 0 ? false : true);
+ }
+
+ @Override
+ public byte readByte() throws TException {
+ return (byte) readJSONInteger();
+ }
+
+ @Override
+ public short readI16() throws TException {
+ return (short) readJSONInteger();
+ }
+
+ @Override
+ public int readI32() throws TException {
+ return (int) readJSONInteger();
+ }
+
+ @Override
+ public long readI64() throws TException {
+ return (long) readJSONInteger();
+ }
+
+ @Override
+ public double readDouble() throws TException {
+ return readJSONDouble();
+ }
+
+ @Override
+ public String readString() throws TException {
+ return readJSONString(false).toString(StandardCharsets.UTF_8);
+ }
+
+ @Override
+ public ByteBuffer readBinary() throws TException {
+ return ByteBuffer.wrap(readJSONBase64());
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TList.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TList.java
new file mode 100644
index 000000000..0d36e83d9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TList.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates list metadata.
+ *
+ */
+public final class TList {
+ public TList() {
+ this(TType.STOP, 0);
+ }
+
+ public TList(byte t, int s) {
+ elemType = t;
+ size = s;
+ }
+
+ public final byte elemType;
+ public final int size;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMap.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMap.java
new file mode 100644
index 000000000..20881f7ac
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMap.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates map metadata.
+ *
+ */
+public final class TMap {
+ public TMap() {
+ this(TType.STOP, TType.STOP, 0);
+ }
+
+ public TMap(byte k, byte v, int s) {
+ keyType = k;
+ valueType = v;
+ size = s;
+ }
+
+ public final byte keyType;
+ public final byte valueType;
+ public final int size;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessage.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessage.java
new file mode 100644
index 000000000..f13b8ca50
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessage.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates struct metadata.
+ *
+ */
+public final class TMessage {
+ public TMessage() {
+ this("", TType.STOP, 0);
+ }
+
+ public TMessage(String n, byte t, int s) {
+ name = n;
+ type = t;
+ seqid = s;
+ }
+
+ public final String name;
+ public final byte type;
+ public final int seqid;
+
+ @Override
+ public String toString() {
+ return "<TMessage name:'" + name + "' type: " + type + " seqid:" + seqid + ">";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + seqid;
+ result = prime * result + type;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ TMessage other = (TMessage) obj;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (seqid != other.seqid)
+ return false;
+ if (type != other.type)
+ return false;
+ return true;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessageType.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessageType.java
new file mode 100644
index 000000000..aa3f93177
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessageType.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Message type constants in the Thrift protocol.
+ *
+ */
+public final class TMessageType {
+ public static final byte CALL = 1;
+ public static final byte REPLY = 2;
+ public static final byte EXCEPTION = 3;
+ public static final byte ONEWAY = 4;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMultiplexedProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMultiplexedProtocol.java
new file mode 100644
index 000000000..0ea566ba6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMultiplexedProtocol.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.TException;
+
+/**
+ * <code>TMultiplexedProtocol</code> is a protocol-independent concrete decorator
+ * that allows a Thrift client to communicate with a multiplexing Thrift server,
+ * by prepending the service name to the function name during function calls.
+ *
+ * <p>NOTE: THIS IS NOT USED BY SERVERS. On the server, use {@link org.apache.thrift.TMultiplexedProcessor TMultiplexedProcessor} to handle requests
+ * from a multiplexing client.
+ *
+ * <p>This example uses a single socket transport to invoke two services:
+ *
+ * <pre>
+ * {@code
+ * TSocket transport = new TSocket("localhost", 9090);
+ * transport.open();
+ *
+ * TBinaryProtocol protocol = new TBinaryProtocol(transport);
+ *
+ * TMultiplexedProtocol mp = new TMultiplexedProtocol(protocol, "Calculator");
+ * Calculator.Client service = new Calculator.Client(mp);
+ *
+ * TMultiplexedProtocol mp2 = new TMultiplexedProtocol(protocol, "WeatherReport");
+ * WeatherReport.Client service2 = new WeatherReport.Client(mp2);
+ *
+ * System.out.println(service.add(2,2));
+ * System.out.println(service2.getTemperature());
+ * }
+ * </pre>
+ *
+ * @see org.apache.thrift.protocol.TProtocolDecorator
+ */
+public class TMultiplexedProtocol extends TProtocolDecorator {
+
+ /** Used to delimit the service name from the function name */
+ public static final String SEPARATOR = ":";
+
+ private final String SERVICE_NAME;
+
+ /**
+ * Wrap the specified protocol, allowing it to be used to communicate with a
+ * multiplexing server. The <code>serviceName</code> is required as it is
+ * prepended to the message header so that the multiplexing server can broker
+ * the function call to the proper service.
+ *
+ * @param protocol Your communication protocol of choice, e.g. <code>TBinaryProtocol</code>.
+ * @param serviceName The service name of the service communicating via this protocol.
+ */
+ public TMultiplexedProtocol(TProtocol protocol, String serviceName) {
+ super(protocol);
+ SERVICE_NAME = serviceName;
+ }
+
+ /**
+ * Prepends the service name to the function name, separated by TMultiplexedProtocol.SEPARATOR.
+ *
+ * @param tMessage The original message.
+ * @throws TException Passed through from wrapped <code>TProtocol</code> instance.
+ */
+ @Override
+ public void writeMessageBegin(TMessage tMessage) throws TException {
+ if (tMessage.type == TMessageType.CALL || tMessage.type == TMessageType.ONEWAY) {
+ super.writeMessageBegin(new TMessage(
+ SERVICE_NAME + SEPARATOR + tMessage.name,
+ tMessage.type,
+ tMessage.seqid
+ ));
+ } else {
+ super.writeMessageBegin(tMessage);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocol.java
new file mode 100644
index 000000000..0e96368d4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocol.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import java.nio.ByteBuffer;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.scheme.IScheme;
+import org.apache.thrift.scheme.StandardScheme;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * Protocol interface definition.
+ *
+ */
+public abstract class TProtocol {
+
+ /**
+ * Prevent direct instantiation
+ */
+ @SuppressWarnings("unused")
+ private TProtocol() {}
+
+ /**
+ * Transport
+ */
+ protected TTransport trans_;
+
+ /**
+ * Constructor
+ */
+ protected TProtocol(TTransport trans) {
+ trans_ = trans;
+ }
+
+ /**
+ * Transport accessor
+ */
+ public TTransport getTransport() {
+ return trans_;
+ }
+
+ /**
+ * Writing methods.
+ */
+
+ public abstract void writeMessageBegin(TMessage message) throws TException;
+
+ public abstract void writeMessageEnd() throws TException;
+
+ public abstract void writeStructBegin(TStruct struct) throws TException;
+
+ public abstract void writeStructEnd() throws TException;
+
+ public abstract void writeFieldBegin(TField field) throws TException;
+
+ public abstract void writeFieldEnd() throws TException;
+
+ public abstract void writeFieldStop() throws TException;
+
+ public abstract void writeMapBegin(TMap map) throws TException;
+
+ public abstract void writeMapEnd() throws TException;
+
+ public abstract void writeListBegin(TList list) throws TException;
+
+ public abstract void writeListEnd() throws TException;
+
+ public abstract void writeSetBegin(TSet set) throws TException;
+
+ public abstract void writeSetEnd() throws TException;
+
+ public abstract void writeBool(boolean b) throws TException;
+
+ public abstract void writeByte(byte b) throws TException;
+
+ public abstract void writeI16(short i16) throws TException;
+
+ public abstract void writeI32(int i32) throws TException;
+
+ public abstract void writeI64(long i64) throws TException;
+
+ public abstract void writeDouble(double dub) throws TException;
+
+ public abstract void writeString(String str) throws TException;
+
+ public abstract void writeBinary(ByteBuffer buf) throws TException;
+
+ /**
+ * Reading methods.
+ */
+
+ public abstract TMessage readMessageBegin() throws TException;
+
+ public abstract void readMessageEnd() throws TException;
+
+ public abstract TStruct readStructBegin() throws TException;
+
+ public abstract void readStructEnd() throws TException;
+
+ public abstract TField readFieldBegin() throws TException;
+
+ public abstract void readFieldEnd() throws TException;
+
+ public abstract TMap readMapBegin() throws TException;
+
+ public abstract void readMapEnd() throws TException;
+
+ public abstract TList readListBegin() throws TException;
+
+ public abstract void readListEnd() throws TException;
+
+ public abstract TSet readSetBegin() throws TException;
+
+ public abstract void readSetEnd() throws TException;
+
+ public abstract boolean readBool() throws TException;
+
+ public abstract byte readByte() throws TException;
+
+ public abstract short readI16() throws TException;
+
+ public abstract int readI32() throws TException;
+
+ public abstract long readI64() throws TException;
+
+ public abstract double readDouble() throws TException;
+
+ public abstract String readString() throws TException;
+
+ public abstract ByteBuffer readBinary() throws TException;
+
+ /**
+ * Reset any internal state back to a blank slate. This method only needs to
+ * be implemented for stateful protocols.
+ */
+ public void reset() {}
+
+ /**
+ * Scheme accessor
+ */
+ public Class<? extends IScheme> getScheme() {
+ return StandardScheme.class;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolDecorator.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolDecorator.java
new file mode 100644
index 000000000..2d29cd231
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolDecorator.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.TException;
+
+import java.nio.ByteBuffer;
+
+/**
+ * <code>TProtocolDecorator</code> forwards all requests to an enclosed
+ * <code>TProtocol</code> instance, providing a way to author concise
+ * concrete decorator subclasses. While it has no abstract methods, it
+ * is marked abstract as a reminder that by itself, it does not modify
+ * the behaviour of the enclosed <code>TProtocol</code>.
+ *
+ * <p>See p.175 of Design Patterns (by Gamma et al.)</p>
+ *
+ * @see org.apache.thrift.protocol.TMultiplexedProtocol
+ */
+public abstract class TProtocolDecorator extends TProtocol {
+
+ private final TProtocol concreteProtocol;
+
+ /**
+ * Encloses the specified protocol.
+ * @param protocol All operations will be forward to this protocol. Must be non-null.
+ */
+ public TProtocolDecorator(TProtocol protocol) {
+ super(protocol.getTransport());
+ concreteProtocol = protocol;
+ }
+
+ public void writeMessageBegin(TMessage tMessage) throws TException {
+ concreteProtocol.writeMessageBegin(tMessage);
+ }
+
+ public void writeMessageEnd() throws TException {
+ concreteProtocol.writeMessageEnd();
+ }
+
+ public void writeStructBegin(TStruct tStruct) throws TException {
+ concreteProtocol.writeStructBegin(tStruct);
+ }
+
+ public void writeStructEnd() throws TException {
+ concreteProtocol.writeStructEnd();
+ }
+
+ public void writeFieldBegin(TField tField) throws TException {
+ concreteProtocol.writeFieldBegin(tField);
+ }
+
+ public void writeFieldEnd() throws TException {
+ concreteProtocol.writeFieldEnd();
+ }
+
+ public void writeFieldStop() throws TException {
+ concreteProtocol.writeFieldStop();
+ }
+
+ public void writeMapBegin(TMap tMap) throws TException {
+ concreteProtocol.writeMapBegin(tMap);
+ }
+
+ public void writeMapEnd() throws TException {
+ concreteProtocol.writeMapEnd();
+ }
+
+ public void writeListBegin(TList tList) throws TException {
+ concreteProtocol.writeListBegin(tList);
+ }
+
+ public void writeListEnd() throws TException {
+ concreteProtocol.writeListEnd();
+ }
+
+ public void writeSetBegin(TSet tSet) throws TException {
+ concreteProtocol.writeSetBegin(tSet);
+ }
+
+ public void writeSetEnd() throws TException {
+ concreteProtocol.writeSetEnd();
+ }
+
+ public void writeBool(boolean b) throws TException {
+ concreteProtocol.writeBool(b);
+ }
+
+ public void writeByte(byte b) throws TException {
+ concreteProtocol.writeByte(b);
+ }
+
+ public void writeI16(short i) throws TException {
+ concreteProtocol.writeI16(i);
+ }
+
+ public void writeI32(int i) throws TException {
+ concreteProtocol.writeI32(i);
+ }
+
+ public void writeI64(long l) throws TException {
+ concreteProtocol.writeI64(l);
+ }
+
+ public void writeDouble(double v) throws TException {
+ concreteProtocol.writeDouble(v);
+ }
+
+ public void writeString(String s) throws TException {
+ concreteProtocol.writeString(s);
+ }
+
+ public void writeBinary(ByteBuffer buf) throws TException {
+ concreteProtocol.writeBinary(buf);
+ }
+
+ public TMessage readMessageBegin() throws TException {
+ return concreteProtocol.readMessageBegin();
+ }
+
+ public void readMessageEnd() throws TException {
+ concreteProtocol.readMessageEnd();
+ }
+
+ public TStruct readStructBegin() throws TException {
+ return concreteProtocol.readStructBegin();
+ }
+
+ public void readStructEnd() throws TException {
+ concreteProtocol.readStructEnd();
+ }
+
+ public TField readFieldBegin() throws TException {
+ return concreteProtocol.readFieldBegin();
+ }
+
+ public void readFieldEnd() throws TException {
+ concreteProtocol.readFieldEnd();
+ }
+
+ public TMap readMapBegin() throws TException {
+ return concreteProtocol.readMapBegin();
+ }
+
+ public void readMapEnd() throws TException {
+ concreteProtocol.readMapEnd();
+ }
+
+ public TList readListBegin() throws TException {
+ return concreteProtocol.readListBegin();
+ }
+
+ public void readListEnd() throws TException {
+ concreteProtocol.readListEnd();
+ }
+
+ public TSet readSetBegin() throws TException {
+ return concreteProtocol.readSetBegin();
+ }
+
+ public void readSetEnd() throws TException {
+ concreteProtocol.readSetEnd();
+ }
+
+ public boolean readBool() throws TException {
+ return concreteProtocol.readBool();
+ }
+
+ public byte readByte() throws TException {
+ return concreteProtocol.readByte();
+ }
+
+ public short readI16() throws TException {
+ return concreteProtocol.readI16();
+ }
+
+ public int readI32() throws TException {
+ return concreteProtocol.readI32();
+ }
+
+ public long readI64() throws TException {
+ return concreteProtocol.readI64();
+ }
+
+ public double readDouble() throws TException {
+ return concreteProtocol.readDouble();
+ }
+
+ public String readString() throws TException {
+ return concreteProtocol.readString();
+ }
+
+ public ByteBuffer readBinary() throws TException {
+ return concreteProtocol.readBinary();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolException.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolException.java
new file mode 100644
index 000000000..870f1b939
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolException.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.TException;
+
+/**
+ * Protocol exceptions.
+ *
+ */
+public class TProtocolException extends TException {
+
+
+ private static final long serialVersionUID = 1L;
+ public static final int UNKNOWN = 0;
+ public static final int INVALID_DATA = 1;
+ public static final int NEGATIVE_SIZE = 2;
+ public static final int SIZE_LIMIT = 3;
+ public static final int BAD_VERSION = 4;
+ public static final int NOT_IMPLEMENTED = 5;
+ public static final int DEPTH_LIMIT = 6;
+
+ protected int type_ = UNKNOWN;
+
+ public TProtocolException() {
+ super();
+ }
+
+ public TProtocolException(int type) {
+ super();
+ type_ = type;
+ }
+
+ public TProtocolException(int type, String message) {
+ super(message);
+ type_ = type;
+ }
+
+ public TProtocolException(String message) {
+ super(message);
+ }
+
+ public TProtocolException(int type, Throwable cause) {
+ super(cause);
+ type_ = type;
+ }
+
+ public TProtocolException(Throwable cause) {
+ super(cause);
+ }
+
+ public TProtocolException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public TProtocolException(int type, String message, Throwable cause) {
+ super(message, cause);
+ type_ = type;
+ }
+
+ public int getType() {
+ return type_;
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolFactory.java
new file mode 100644
index 000000000..b72e87b38
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolFactory.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import java.io.Serializable;
+
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * Factory interface for constructing protocol instances.
+ */
+public interface TProtocolFactory extends Serializable {
+ public TProtocol getProtocol(TTransport trans);
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolUtil.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolUtil.java
new file mode 100644
index 000000000..cdaa30b87
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolUtil.java
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.TException;
+
+/**
+ * Utility class with static methods for interacting with protocol data
+ * streams.
+ *
+ */
+public class TProtocolUtil {
+
+ /**
+ * The maximum recursive depth the skip() function will traverse before
+ * throwing a TException.
+ */
+ private static int maxSkipDepth = Integer.MAX_VALUE;
+
+ /**
+ * Specifies the maximum recursive depth that the skip function will
+ * traverse before throwing a TException. This is a global setting, so
+ * any call to skip in this JVM will enforce this value.
+ *
+ * @param depth the maximum recursive depth. A value of 2 would allow
+ * the skip function to skip a structure or collection with basic children,
+ * but it would not permit skipping a struct that had a field containing
+ * a child struct. A value of 1 would only allow skipping of simple
+ * types and empty structs/collections.
+ */
+ public static void setMaxSkipDepth(int depth) {
+ maxSkipDepth = depth;
+ }
+
+ /**
+ * Skips over the next data element from the provided input TProtocol object.
+ *
+ * @param prot the protocol object to read from
+ * @param type the next value will be interpreted as this TType value.
+ */
+ public static void skip(TProtocol prot, byte type)
+ throws TException {
+ skip(prot, type, maxSkipDepth);
+ }
+
+ /**
+ * Skips over the next data element from the provided input TProtocol object.
+ *
+ * @param prot the protocol object to read from
+ * @param type the next value will be interpreted as this TType value.
+ * @param maxDepth this function will only skip complex objects to this
+ * recursive depth, to prevent Java stack overflow.
+ */
+ public static void skip(TProtocol prot, byte type, int maxDepth)
+ throws TException {
+ if (maxDepth <= 0) {
+ throw new TException("Maximum skip depth exceeded");
+ }
+ switch (type) {
+ case TType.BOOL:
+ prot.readBool();
+ break;
+
+ case TType.BYTE:
+ prot.readByte();
+ break;
+
+ case TType.I16:
+ prot.readI16();
+ break;
+
+ case TType.I32:
+ prot.readI32();
+ break;
+
+ case TType.I64:
+ prot.readI64();
+ break;
+
+ case TType.DOUBLE:
+ prot.readDouble();
+ break;
+
+ case TType.STRING:
+ prot.readBinary();
+ break;
+
+ case TType.STRUCT:
+ prot.readStructBegin();
+ while (true) {
+ TField field = prot.readFieldBegin();
+ if (field.type == TType.STOP) {
+ break;
+ }
+ skip(prot, field.type, maxDepth - 1);
+ prot.readFieldEnd();
+ }
+ prot.readStructEnd();
+ break;
+
+ case TType.MAP:
+ TMap map = prot.readMapBegin();
+ for (int i = 0; i < map.size; i++) {
+ skip(prot, map.keyType, maxDepth - 1);
+ skip(prot, map.valueType, maxDepth - 1);
+ }
+ prot.readMapEnd();
+ break;
+
+ case TType.SET:
+ TSet set = prot.readSetBegin();
+ for (int i = 0; i < set.size; i++) {
+ skip(prot, set.elemType, maxDepth - 1);
+ }
+ prot.readSetEnd();
+ break;
+
+ case TType.LIST:
+ TList list = prot.readListBegin();
+ for (int i = 0; i < list.size; i++) {
+ skip(prot, list.elemType, maxDepth - 1);
+ }
+ prot.readListEnd();
+ break;
+
+ default:
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Unrecognized type " + type);
+ }
+ }
+
+ /**
+ * Attempt to determine the protocol used to serialize some data.
+ *
+ * The guess is based on known specificities of supported protocols.
+ * In some cases, no guess can be done, in that case we return the
+ * fallback TProtocolFactory.
+ * To be certain to correctly detect the protocol, the first encoded
+ * field should have a field id &lt; 256
+ *
+ * @param data The serialized data to guess the protocol for.
+ * @param fallback The TProtocol to return if no guess can be made.
+ * @return a Class implementing TProtocolFactory which can be used to create a deserializer.
+ */
+ public static TProtocolFactory guessProtocolFactory(byte[] data, TProtocolFactory fallback) {
+ //
+ // If the first and last bytes are opening/closing curly braces we guess the protocol as
+ // being TJSONProtocol.
+ // It could not be a TCompactBinary encoding for a field of type 0xb (Map)
+ // with delta id 7 as the last byte for TCompactBinary is always 0.
+ //
+
+ if ('{' == data[0] && '}' == data[data.length - 1]) {
+ return new TJSONProtocol.Factory();
+ }
+
+ //
+ // If the last byte is not 0, then it cannot be TCompactProtocol, it must be
+ // TBinaryProtocol.
+ //
+
+ if (data[data.length - 1] != 0) {
+ return new TBinaryProtocol.Factory();
+ }
+
+ //
+ // A first byte of value > 16 indicates TCompactProtocol was used, and the first byte
+ // encodes a delta field id (id <= 15) and a field type.
+ //
+
+ if (data[0] > 0x10) {
+ return new TCompactProtocol.Factory();
+ }
+
+ //
+ // If the second byte is 0 then it is a field id < 256 encoded by TBinaryProtocol.
+ // It cannot possibly be TCompactProtocol since a value of 0 would imply a field id
+ // of 0 as the zig zag varint encoding would end.
+ //
+
+ if (data.length > 1 && 0 == data[1]) {
+ return new TBinaryProtocol.Factory();
+ }
+
+ //
+ // If bit 7 of the first byte of the field id is set then we have two choices:
+ // 1. A field id > 63 was encoded with TCompactProtocol.
+ // 2. A field id > 0x7fff (32767) was encoded with TBinaryProtocol and the last byte of the
+ // serialized data is 0.
+ // Option 2 is impossible since field ids are short and thus limited to 32767.
+ //
+
+ if (data.length > 1 && (data[1] & 0x80) != 0) {
+ return new TCompactProtocol.Factory();
+ }
+
+ //
+ // The remaining case is either a field id <= 63 encoded as TCompactProtocol,
+ // one >= 256 encoded with TBinaryProtocol with a last byte at 0, or an empty structure.
+ // As we cannot really decide, we return the fallback protocol.
+ //
+ return fallback;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSet.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSet.java
new file mode 100644
index 000000000..38be9a991
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSet.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates set metadata.
+ *
+ */
+public final class TSet {
+ public TSet() {
+ this(TType.STOP, 0);
+ }
+
+ public TSet(byte t, int s) {
+ elemType = t;
+ size = s;
+ }
+
+ public TSet(TList list) {
+ this(list.elemType, list.size);
+ }
+
+ public final byte elemType;
+ public final int size;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSimpleJSONProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSimpleJSONProtocol.java
new file mode 100644
index 000000000..eb7e23bf9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSimpleJSONProtocol.java
@@ -0,0 +1,483 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Stack;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * JSON protocol implementation for thrift.
+ *
+ * This protocol is write-only and produces a simple output format
+ * suitable for parsing by scripting languages. It should not be
+ * confused with the full-featured TJSONProtocol.
+ *
+ */
+public class TSimpleJSONProtocol extends TProtocol {
+
+ /**
+ * Factory
+ */
+ public static class Factory implements TProtocolFactory {
+ public TProtocol getProtocol(TTransport trans) {
+ return new TSimpleJSONProtocol(trans);
+ }
+ }
+
+ private static final byte[] COMMA = new byte[] {','};
+ private static final byte[] COLON = new byte[] {':'};
+ private static final byte[] LBRACE = new byte[] {'{'};
+ private static final byte[] RBRACE = new byte[] {'}'};
+ private static final byte[] LBRACKET = new byte[] {'['};
+ private static final byte[] RBRACKET = new byte[] {']'};
+ private static final char QUOTE = '"';
+
+ private static final TStruct ANONYMOUS_STRUCT = new TStruct();
+ private static final TField ANONYMOUS_FIELD = new TField();
+ private static final TMessage EMPTY_MESSAGE = new TMessage();
+ private static final TSet EMPTY_SET = new TSet();
+ private static final TList EMPTY_LIST = new TList();
+ private static final TMap EMPTY_MAP = new TMap();
+ private static final String LIST = "list";
+ private static final String SET = "set";
+ private static final String MAP = "map";
+
+ protected class Context {
+ protected void write() throws TException {}
+
+ /**
+ * Returns whether the current value is a key in a map
+ */
+ protected boolean isMapKey() { return false; }
+ }
+
+ protected class ListContext extends Context {
+ protected boolean first_ = true;
+
+ protected void write() throws TException {
+ if (first_) {
+ first_ = false;
+ } else {
+ trans_.write(COMMA);
+ }
+ }
+ }
+
+ protected class StructContext extends Context {
+ protected boolean first_ = true;
+ protected boolean colon_ = true;
+
+ protected void write() throws TException {
+ if (first_) {
+ first_ = false;
+ colon_ = true;
+ } else {
+ trans_.write(colon_ ? COLON : COMMA);
+ colon_ = !colon_;
+ }
+ }
+ }
+
+ protected class MapContext extends StructContext {
+ protected boolean isKey = true;
+
+ @Override
+ protected void write() throws TException {
+ super.write();
+ isKey = !isKey;
+ }
+
+ protected boolean isMapKey() {
+ // we want to coerce map keys to json strings regardless
+ // of their type
+ return isKey;
+ }
+ }
+
+ protected final Context BASE_CONTEXT = new Context();
+
+ /**
+ * Stack of nested contexts that we may be in.
+ */
+ protected Stack<Context> writeContextStack_ = new Stack<Context>();
+
+ /**
+ * Current context that we are in
+ */
+ protected Context writeContext_ = BASE_CONTEXT;
+
+ /**
+ * Push a new write context onto the stack.
+ */
+ protected void pushWriteContext(Context c) {
+ writeContextStack_.push(writeContext_);
+ writeContext_ = c;
+ }
+
+ /**
+ * Pop the last write context off the stack
+ */
+ protected void popWriteContext() {
+ writeContext_ = writeContextStack_.pop();
+ }
+
+ /**
+ * Reset the write context stack to its initial state.
+ */
+ protected void resetWriteContext() {
+ while (!writeContextStack_.isEmpty()) {
+ popWriteContext();
+ }
+ }
+
+ /**
+ * Used to make sure that we are not encountering a map whose keys are containers
+ */
+ protected void assertContextIsNotMapKey(String invalidKeyType) throws CollectionMapKeyException {
+ if (writeContext_.isMapKey()) {
+ throw new CollectionMapKeyException("Cannot serialize a map with keys that are of type " + invalidKeyType);
+ }
+ }
+
+ /**
+ * Constructor
+ */
+ public TSimpleJSONProtocol(TTransport trans) {
+ super(trans);
+ }
+
+ @Override
+ public void writeMessageBegin(TMessage message) throws TException {
+ resetWriteContext(); // THRIFT-3743
+ trans_.write(LBRACKET);
+ pushWriteContext(new ListContext());
+ writeString(message.name);
+ writeByte(message.type);
+ writeI32(message.seqid);
+ }
+
+ @Override
+ public void writeMessageEnd() throws TException {
+ popWriteContext();
+ trans_.write(RBRACKET);
+ }
+
+ @Override
+ public void writeStructBegin(TStruct struct) throws TException {
+ writeContext_.write();
+ trans_.write(LBRACE);
+ pushWriteContext(new StructContext());
+ }
+
+ @Override
+ public void writeStructEnd() throws TException {
+ popWriteContext();
+ trans_.write(RBRACE);
+ }
+
+ @Override
+ public void writeFieldBegin(TField field) throws TException {
+ // Note that extra type information is omitted in JSON!
+ writeString(field.name);
+ }
+
+ @Override
+ public void writeFieldEnd() throws TException {}
+
+ @Override
+ public void writeFieldStop() throws TException {}
+
+ @Override
+ public void writeMapBegin(TMap map) throws TException {
+ assertContextIsNotMapKey(MAP);
+ writeContext_.write();
+ trans_.write(LBRACE);
+ pushWriteContext(new MapContext());
+ // No metadata!
+ }
+
+ @Override
+ public void writeMapEnd() throws TException {
+ popWriteContext();
+ trans_.write(RBRACE);
+ }
+
+ @Override
+ public void writeListBegin(TList list) throws TException {
+ assertContextIsNotMapKey(LIST);
+ writeContext_.write();
+ trans_.write(LBRACKET);
+ pushWriteContext(new ListContext());
+ // No metadata!
+ }
+
+ @Override
+ public void writeListEnd() throws TException {
+ popWriteContext();
+ trans_.write(RBRACKET);
+ }
+
+ @Override
+ public void writeSetBegin(TSet set) throws TException {
+ assertContextIsNotMapKey(SET);
+ writeContext_.write();
+ trans_.write(LBRACKET);
+ pushWriteContext(new ListContext());
+ // No metadata!
+ }
+
+ @Override
+ public void writeSetEnd() throws TException {
+ popWriteContext();
+ trans_.write(RBRACKET);
+ }
+
+ @Override
+ public void writeBool(boolean b) throws TException {
+ writeByte(b ? (byte)1 : (byte)0);
+ }
+
+ @Override
+ public void writeByte(byte b) throws TException {
+ writeI32(b);
+ }
+
+ @Override
+ public void writeI16(short i16) throws TException {
+ writeI32(i16);
+ }
+
+ @Override
+ public void writeI32(int i32) throws TException {
+ if(writeContext_.isMapKey()) {
+ writeString(Integer.toString(i32));
+ } else {
+ writeContext_.write();
+ _writeStringData(Integer.toString(i32));
+ }
+ }
+
+ public void _writeStringData(String s) throws TException {
+ byte[] b = s.getBytes(StandardCharsets.UTF_8);
+ trans_.write(b);
+ }
+
+ @Override
+ public void writeI64(long i64) throws TException {
+ if(writeContext_.isMapKey()) {
+ writeString(Long.toString(i64));
+ } else {
+ writeContext_.write();
+ _writeStringData(Long.toString(i64));
+ }
+ }
+
+ @Override
+ public void writeDouble(double dub) throws TException {
+ if(writeContext_.isMapKey()) {
+ writeString(Double.toString(dub));
+ } else {
+ writeContext_.write();
+ _writeStringData(Double.toString(dub));
+ }
+ }
+
+ @Override
+ public void writeString(String str) throws TException {
+ writeContext_.write();
+ int length = str.length();
+ StringBuffer escape = new StringBuffer(length + 16);
+ escape.append(QUOTE);
+ for (int i = 0; i < length; ++i) {
+ char c = str.charAt(i);
+ switch (c) {
+ case '"':
+ case '\\':
+ escape.append('\\');
+ escape.append(c);
+ break;
+ case '\b':
+ escape.append('\\');
+ escape.append('b');
+ break;
+ case '\f':
+ escape.append('\\');
+ escape.append('f');
+ break;
+ case '\n':
+ escape.append('\\');
+ escape.append('n');
+ break;
+ case '\r':
+ escape.append('\\');
+ escape.append('r');
+ break;
+ case '\t':
+ escape.append('\\');
+ escape.append('t');
+ break;
+ default:
+ // Control characters! According to JSON RFC u0020 (space)
+ if (c < ' ') {
+ String hex = Integer.toHexString(c);
+ escape.append('\\');
+ escape.append('u');
+ for (int j = 4; j > hex.length(); --j) {
+ escape.append('0');
+ }
+ escape.append(hex);
+ } else {
+ escape.append(c);
+ }
+ break;
+ }
+ }
+ escape.append(QUOTE);
+ _writeStringData(escape.toString());
+ }
+
+ @Override
+ public void writeBinary(ByteBuffer bin) throws TException {
+ // TODO(mcslee): Fix this
+ writeString(new String(bin.array(), bin.position() + bin.arrayOffset(),
+ bin.limit() - bin.position() - bin.arrayOffset(),
+ StandardCharsets.UTF_8));
+ }
+
+ /**
+ * Reading methods.
+ */
+
+ @Override
+ public TMessage readMessageBegin() throws TException {
+ // TODO(mcslee): implement
+ return EMPTY_MESSAGE;
+ }
+
+ @Override
+ public void readMessageEnd() throws TException {}
+
+ @Override
+ public TStruct readStructBegin() throws TException {
+ // TODO(mcslee): implement
+ return ANONYMOUS_STRUCT;
+ }
+
+ @Override
+ public void readStructEnd() throws TException {}
+
+ @Override
+ public TField readFieldBegin() throws TException {
+ // TODO(mcslee): implement
+ return ANONYMOUS_FIELD;
+ }
+
+ @Override
+ public void readFieldEnd() throws TException {}
+
+ @Override
+ public TMap readMapBegin() throws TException {
+ // TODO(mcslee): implement
+ return EMPTY_MAP;
+ }
+
+ @Override
+ public void readMapEnd() throws TException {}
+
+ @Override
+ public TList readListBegin() throws TException {
+ // TODO(mcslee): implement
+ return EMPTY_LIST;
+ }
+
+ @Override
+ public void readListEnd() throws TException {}
+
+ @Override
+ public TSet readSetBegin() throws TException {
+ // TODO(mcslee): implement
+ return EMPTY_SET;
+ }
+
+ @Override
+ public void readSetEnd() throws TException {}
+
+ @Override
+ public boolean readBool() throws TException {
+ return (readByte() == 1);
+ }
+
+ @Override
+ public byte readByte() throws TException {
+ // TODO(mcslee): implement
+ return 0;
+ }
+
+ @Override
+ public short readI16() throws TException {
+ // TODO(mcslee): implement
+ return 0;
+ }
+
+ @Override
+ public int readI32() throws TException {
+ // TODO(mcslee): implement
+ return 0;
+ }
+
+ @Override
+ public long readI64() throws TException {
+ // TODO(mcslee): implement
+ return 0;
+ }
+
+ @Override
+ public double readDouble() throws TException {
+ // TODO(mcslee): implement
+ return 0;
+ }
+
+ @Override
+ public String readString() throws TException {
+ // TODO(mcslee): implement
+ return "";
+ }
+
+ public String readStringBody(int size) throws TException {
+ // TODO(mcslee): implement
+ return "";
+ }
+
+ @Override
+ public ByteBuffer readBinary() throws TException {
+ // TODO(mcslee): implement
+ return ByteBuffer.wrap(new byte[0]);
+ }
+
+ public static class CollectionMapKeyException extends TException {
+ public CollectionMapKeyException(String message) {
+ super(message);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TStruct.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TStruct.java
new file mode 100644
index 000000000..a0f79012a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TStruct.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates struct metadata.
+ *
+ */
+public final class TStruct {
+ public TStruct() {
+ this("");
+ }
+
+ public TStruct(String n) {
+ name = n;
+ }
+
+ public final String name;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java
new file mode 100644
index 000000000..74f5226c8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.protocol;
+
+import java.util.BitSet;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.scheme.IScheme;
+import org.apache.thrift.scheme.TupleScheme;
+import org.apache.thrift.transport.TTransport;
+
+public final class TTupleProtocol extends TCompactProtocol {
+ public static class Factory implements TProtocolFactory {
+ public Factory() {}
+
+ public TProtocol getProtocol(TTransport trans) {
+ return new TTupleProtocol(trans);
+ }
+ }
+
+ public TTupleProtocol(TTransport transport) {
+ super(transport);
+ }
+
+ @Override
+ public Class<? extends IScheme> getScheme() {
+ return TupleScheme.class;
+ }
+
+ public void writeBitSet(BitSet bs, int vectorWidth) throws TException {
+ byte[] bytes = toByteArray(bs, vectorWidth);
+ for (byte b : bytes) {
+ writeByte(b);
+ }
+ }
+
+ public BitSet readBitSet(int i) throws TException {
+ int length = (int) Math.ceil(i/8.0);
+ byte[] bytes = new byte[length];
+ for (int j = 0; j < length; j++) {
+ bytes[j] = readByte();
+ }
+ BitSet bs = fromByteArray(bytes);
+ return bs;
+ }
+
+ /**
+ * Returns a bitset containing the values in bytes. The byte-ordering must be
+ * big-endian.
+ */
+ public static BitSet fromByteArray(byte[] bytes) {
+ BitSet bits = new BitSet();
+ for (int i = 0; i < bytes.length * 8; i++) {
+ if ((bytes[bytes.length - i / 8 - 1] & (1 << (i % 8))) > 0) {
+ bits.set(i);
+ }
+ }
+ return bits;
+ }
+
+ /**
+ * Returns a byte array of at least length 1. The most significant bit in the
+ * result is guaranteed not to be a 1 (since BitSet does not support sign
+ * extension). The byte-ordering of the result is big-endian which means the
+ * most significant bit is in element 0. The bit at index 0 of the bit set is
+ * assumed to be the least significant bit.
+ *
+ * @param bits
+ * @param vectorWidth
+ * @return a byte array of at least length 1
+ */
+ public static byte[] toByteArray(BitSet bits, int vectorWidth) {
+ byte[] bytes = new byte[(int) Math.ceil(vectorWidth/8.0)];
+ for (int i = 0; i < bits.length(); i++) {
+ if (bits.get(i)) {
+ bytes[bytes.length - i / 8 - 1] |= 1 << (i % 8);
+ }
+ }
+ return bytes;
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TType.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TType.java
new file mode 100644
index 000000000..c3c1a0abd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TType.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Type constants in the Thrift protocol.
+ */
+public final class TType {
+ public static final byte STOP = 0;
+ public static final byte VOID = 1;
+ public static final byte BOOL = 2;
+ public static final byte BYTE = 3;
+ public static final byte DOUBLE = 4;
+ public static final byte I16 = 6;
+ public static final byte I32 = 8;
+ public static final byte I64 = 10;
+ public static final byte STRING = 11;
+ public static final byte STRUCT = 12;
+ public static final byte MAP = 13;
+ public static final byte SET = 14;
+ public static final byte LIST = 15;
+ public static final byte ENUM = 16;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/IScheme.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/IScheme.java
new file mode 100644
index 000000000..aa3550705
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/IScheme.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.scheme;
+
+import org.apache.thrift.TBase;
+
+public interface IScheme<T extends TBase> {
+
+ public void read(org.apache.thrift.protocol.TProtocol iproto, T struct) throws org.apache.thrift.TException;
+
+ public void write(org.apache.thrift.protocol.TProtocol oproto, T struct) throws org.apache.thrift.TException;
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/SchemeFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/SchemeFactory.java
new file mode 100644
index 000000000..006a66805
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/SchemeFactory.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.scheme;
+
+public interface SchemeFactory {
+
+ public <S extends IScheme> S getScheme();
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/StandardScheme.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/StandardScheme.java
new file mode 100644
index 000000000..ffab04db6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/StandardScheme.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.scheme;
+
+import org.apache.thrift.TBase;
+
+public abstract class StandardScheme<T extends TBase> implements IScheme<T> {
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/TupleScheme.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/TupleScheme.java
new file mode 100644
index 000000000..365242b11
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/TupleScheme.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.scheme;
+
+import org.apache.thrift.TBase;
+
+public abstract class TupleScheme<T extends TBase> implements IScheme<T> {
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java
new file mode 100644
index 000000000..8c206e427
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java
@@ -0,0 +1,618 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.server;
+
+import org.apache.thrift.TAsyncProcessor;
+import org.apache.thrift.TByteArrayOutputStream;
+import org.apache.thrift.TException;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.transport.TFramedTransport;
+import org.apache.thrift.transport.TIOStreamTransport;
+import org.apache.thrift.transport.TMemoryInputTransport;
+import org.apache.thrift.transport.TNonblockingServerTransport;
+import org.apache.thrift.transport.TNonblockingTransport;
+import org.apache.thrift.transport.TTransport;
+import org.apache.thrift.transport.TTransportException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Provides common methods and classes used by nonblocking TServer
+ * implementations.
+ */
+public abstract class AbstractNonblockingServer extends TServer {
+ protected final Logger LOGGER = LoggerFactory.getLogger(getClass().getName());
+
+ public static abstract class AbstractNonblockingServerArgs<T extends AbstractNonblockingServerArgs<T>> extends AbstractServerArgs<T> {
+ public long maxReadBufferBytes = 256 * 1024 * 1024;
+
+ public AbstractNonblockingServerArgs(TNonblockingServerTransport transport) {
+ super(transport);
+ transportFactory(new TFramedTransport.Factory());
+ }
+ }
+
+ /**
+ * The maximum amount of memory we will allocate to client IO buffers at a
+ * time. Without this limit, the server will gladly allocate client buffers
+ * right into an out of memory exception, rather than waiting.
+ */
+ final long MAX_READ_BUFFER_BYTES;
+
+ /**
+ * How many bytes are currently allocated to read buffers.
+ */
+ final AtomicLong readBufferBytesAllocated = new AtomicLong(0);
+
+ public AbstractNonblockingServer(AbstractNonblockingServerArgs args) {
+ super(args);
+ MAX_READ_BUFFER_BYTES = args.maxReadBufferBytes;
+ }
+
+ /**
+ * Begin accepting connections and processing invocations.
+ */
+ public void serve() {
+ // start any IO threads
+ if (!startThreads()) {
+ return;
+ }
+
+ // start listening, or exit
+ if (!startListening()) {
+ return;
+ }
+
+ setServing(true);
+
+ // this will block while we serve
+ waitForShutdown();
+
+ setServing(false);
+
+ // do a little cleanup
+ stopListening();
+ }
+
+ /**
+ * Starts any threads required for serving.
+ *
+ * @return true if everything went ok, false if threads could not be started.
+ */
+ protected abstract boolean startThreads();
+
+ /**
+ * A method that will block until when threads handling the serving have been
+ * shut down.
+ */
+ protected abstract void waitForShutdown();
+
+ /**
+ * Have the server transport start accepting connections.
+ *
+ * @return true if we started listening successfully, false if something went
+ * wrong.
+ */
+ protected boolean startListening() {
+ try {
+ serverTransport_.listen();
+ return true;
+ } catch (TTransportException ttx) {
+ LOGGER.error("Failed to start listening on server socket!", ttx);
+ return false;
+ }
+ }
+
+ /**
+ * Stop listening for connections.
+ */
+ protected void stopListening() {
+ serverTransport_.close();
+ }
+
+ /**
+ * Perform an invocation. This method could behave several different ways -
+ * invoke immediately inline, queue for separate execution, etc.
+ *
+ * @return true if invocation was successfully requested, which is not a
+ * guarantee that invocation has completed. False if the request
+ * failed.
+ */
+ protected abstract boolean requestInvoke(FrameBuffer frameBuffer);
+
+ /**
+ * An abstract thread that handles selecting on a set of transports and
+ * {@link FrameBuffer FrameBuffers} associated with selected keys
+ * corresponding to requests.
+ */
+ protected abstract class AbstractSelectThread extends Thread {
+ protected Selector selector;
+
+ // List of FrameBuffers that want to change their selection interests.
+ protected final Set<FrameBuffer> selectInterestChanges = new HashSet<FrameBuffer>();
+
+ public AbstractSelectThread() throws IOException {
+ this.selector = SelectorProvider.provider().openSelector();
+ }
+
+ /**
+ * If the selector is blocked, wake it up.
+ */
+ public void wakeupSelector() {
+ selector.wakeup();
+ }
+
+ /**
+ * Add FrameBuffer to the list of select interest changes and wake up the
+ * selector if it's blocked. When the select() call exits, it'll give the
+ * FrameBuffer a chance to change its interests.
+ */
+ public void requestSelectInterestChange(FrameBuffer frameBuffer) {
+ synchronized (selectInterestChanges) {
+ selectInterestChanges.add(frameBuffer);
+ }
+ // wakeup the selector, if it's currently blocked.
+ selector.wakeup();
+ }
+
+ /**
+ * Check to see if there are any FrameBuffers that have switched their
+ * interest type from read to write or vice versa.
+ */
+ protected void processInterestChanges() {
+ synchronized (selectInterestChanges) {
+ for (FrameBuffer fb : selectInterestChanges) {
+ fb.changeSelectInterests();
+ }
+ selectInterestChanges.clear();
+ }
+ }
+
+ /**
+ * Do the work required to read from a readable client. If the frame is
+ * fully read, then invoke the method call.
+ */
+ protected void handleRead(SelectionKey key) {
+ FrameBuffer buffer = (FrameBuffer) key.attachment();
+ if (!buffer.read()) {
+ cleanupSelectionKey(key);
+ return;
+ }
+
+ // if the buffer's frame read is complete, invoke the method.
+ if (buffer.isFrameFullyRead()) {
+ if (!requestInvoke(buffer)) {
+ cleanupSelectionKey(key);
+ }
+ }
+ }
+
+ /**
+ * Let a writable client get written, if there's data to be written.
+ */
+ protected void handleWrite(SelectionKey key) {
+ FrameBuffer buffer = (FrameBuffer) key.attachment();
+ if (!buffer.write()) {
+ cleanupSelectionKey(key);
+ }
+ }
+
+ /**
+ * Do connection-close cleanup on a given SelectionKey.
+ */
+ protected void cleanupSelectionKey(SelectionKey key) {
+ // remove the records from the two maps
+ FrameBuffer buffer = (FrameBuffer) key.attachment();
+ if (buffer != null) {
+ // close the buffer
+ buffer.close();
+ }
+ // cancel the selection key
+ key.cancel();
+ }
+ } // SelectThread
+
+ /**
+ * Possible states for the FrameBuffer state machine.
+ */
+ private enum FrameBufferState {
+ // in the midst of reading the frame size off the wire
+ READING_FRAME_SIZE,
+ // reading the actual frame data now, but not all the way done yet
+ READING_FRAME,
+ // completely read the frame, so an invocation can now happen
+ READ_FRAME_COMPLETE,
+ // waiting to get switched to listening for write events
+ AWAITING_REGISTER_WRITE,
+ // started writing response data, not fully complete yet
+ WRITING,
+ // another thread wants this framebuffer to go back to reading
+ AWAITING_REGISTER_READ,
+ // we want our transport and selection key invalidated in the selector
+ // thread
+ AWAITING_CLOSE
+ }
+
+ /**
+ * Class that implements a sort of state machine around the interaction with a
+ * client and an invoker. It manages reading the frame size and frame data,
+ * getting it handed off as wrapped transports, and then the writing of
+ * response data back to the client. In the process it manages flipping the
+ * read and write bits on the selection key for its client.
+ */
+ public class FrameBuffer {
+ private final Logger LOGGER = LoggerFactory.getLogger(getClass().getName());
+
+ // the actual transport hooked up to the client.
+ protected final TNonblockingTransport trans_;
+
+ // the SelectionKey that corresponds to our transport
+ protected final SelectionKey selectionKey_;
+
+ // the SelectThread that owns the registration of our transport
+ protected final AbstractSelectThread selectThread_;
+
+ // where in the process of reading/writing are we?
+ protected FrameBufferState state_ = FrameBufferState.READING_FRAME_SIZE;
+
+ // the ByteBuffer we'll be using to write and read, depending on the state
+ protected ByteBuffer buffer_;
+
+ protected final TByteArrayOutputStream response_;
+
+ // the frame that the TTransport should wrap.
+ protected final TMemoryInputTransport frameTrans_;
+
+ // the transport that should be used to connect to clients
+ protected final TTransport inTrans_;
+
+ protected final TTransport outTrans_;
+
+ // the input protocol to use on frames
+ protected final TProtocol inProt_;
+
+ // the output protocol to use on frames
+ protected final TProtocol outProt_;
+
+ // context associated with this connection
+ protected final ServerContext context_;
+
+ public FrameBuffer(final TNonblockingTransport trans,
+ final SelectionKey selectionKey,
+ final AbstractSelectThread selectThread) {
+ trans_ = trans;
+ selectionKey_ = selectionKey;
+ selectThread_ = selectThread;
+ buffer_ = ByteBuffer.allocate(4);
+
+ frameTrans_ = new TMemoryInputTransport();
+ response_ = new TByteArrayOutputStream();
+ inTrans_ = inputTransportFactory_.getTransport(frameTrans_);
+ outTrans_ = outputTransportFactory_.getTransport(new TIOStreamTransport(response_));
+ inProt_ = inputProtocolFactory_.getProtocol(inTrans_);
+ outProt_ = outputProtocolFactory_.getProtocol(outTrans_);
+
+ if (eventHandler_ != null) {
+ context_ = eventHandler_.createContext(inProt_, outProt_);
+ } else {
+ context_ = null;
+ }
+ }
+
+ /**
+ * Give this FrameBuffer a chance to read. The selector loop should have
+ * received a read event for this FrameBuffer.
+ *
+ * @return true if the connection should live on, false if it should be
+ * closed
+ */
+ public boolean read() {
+ if (state_ == FrameBufferState.READING_FRAME_SIZE) {
+ // try to read the frame size completely
+ if (!internalRead()) {
+ return false;
+ }
+
+ // if the frame size has been read completely, then prepare to read the
+ // actual frame.
+ if (buffer_.remaining() == 0) {
+ // pull out the frame size as an integer.
+ int frameSize = buffer_.getInt(0);
+ if (frameSize <= 0) {
+ LOGGER.error("Read an invalid frame size of " + frameSize
+ + ". Are you using TFramedTransport on the client side?");
+ return false;
+ }
+
+ // if this frame will always be too large for this server, log the
+ // error and close the connection.
+ if (frameSize > MAX_READ_BUFFER_BYTES) {
+ LOGGER.error("Read a frame size of " + frameSize
+ + ", which is bigger than the maximum allowable buffer size for ALL connections.");
+ return false;
+ }
+
+ // if this frame will push us over the memory limit, then return.
+ // with luck, more memory will free up the next time around.
+ if (readBufferBytesAllocated.get() + frameSize > MAX_READ_BUFFER_BYTES) {
+ return true;
+ }
+
+ // increment the amount of memory allocated to read buffers
+ readBufferBytesAllocated.addAndGet(frameSize + 4);
+
+ // reallocate the readbuffer as a frame-sized buffer
+ buffer_ = ByteBuffer.allocate(frameSize + 4);
+ buffer_.putInt(frameSize);
+
+ state_ = FrameBufferState.READING_FRAME;
+ } else {
+ // this skips the check of READING_FRAME state below, since we can't
+ // possibly go on to that state if there's data left to be read at
+ // this one.
+ return true;
+ }
+ }
+
+ // it is possible to fall through from the READING_FRAME_SIZE section
+ // to READING_FRAME if there's already some frame data available once
+ // READING_FRAME_SIZE is complete.
+
+ if (state_ == FrameBufferState.READING_FRAME) {
+ if (!internalRead()) {
+ return false;
+ }
+
+ // since we're already in the select loop here for sure, we can just
+ // modify our selection key directly.
+ if (buffer_.remaining() == 0) {
+ // get rid of the read select interests
+ selectionKey_.interestOps(0);
+ state_ = FrameBufferState.READ_FRAME_COMPLETE;
+ }
+
+ return true;
+ }
+
+ // if we fall through to this point, then the state must be invalid.
+ LOGGER.error("Read was called but state is invalid (" + state_ + ")");
+ return false;
+ }
+
+ /**
+ * Give this FrameBuffer a chance to write its output to the final client.
+ */
+ public boolean write() {
+ if (state_ == FrameBufferState.WRITING) {
+ try {
+ if (trans_.write(buffer_) < 0) {
+ return false;
+ }
+ } catch (IOException e) {
+ LOGGER.warn("Got an IOException during write!", e);
+ return false;
+ }
+
+ // we're done writing. now we need to switch back to reading.
+ if (buffer_.remaining() == 0) {
+ prepareRead();
+ }
+ return true;
+ }
+
+ LOGGER.error("Write was called, but state is invalid (" + state_ + ")");
+ return false;
+ }
+
+ /**
+ * Give this FrameBuffer a chance to set its interest to write, once data
+ * has come in.
+ */
+ public void changeSelectInterests() {
+ switch (state_) {
+ case AWAITING_REGISTER_WRITE:
+ // set the OP_WRITE interest
+ selectionKey_.interestOps(SelectionKey.OP_WRITE);
+ state_ = FrameBufferState.WRITING;
+ break;
+ case AWAITING_REGISTER_READ:
+ prepareRead();
+ break;
+ case AWAITING_CLOSE:
+ close();
+ selectionKey_.cancel();
+ break;
+ default:
+ LOGGER.error(
+ "changeSelectInterest was called, but state is invalid ({})",
+ state_);
+ }
+ }
+
+ /**
+ * Shut the connection down.
+ */
+ public void close() {
+ // if we're being closed due to an error, we might have allocated a
+ // buffer that we need to subtract for our memory accounting.
+ if (state_ == FrameBufferState.READING_FRAME ||
+ state_ == FrameBufferState.READ_FRAME_COMPLETE ||
+ state_ == FrameBufferState.AWAITING_CLOSE) {
+ readBufferBytesAllocated.addAndGet(-buffer_.array().length);
+ }
+ trans_.close();
+ if (eventHandler_ != null) {
+ eventHandler_.deleteContext(context_, inProt_, outProt_);
+ }
+ }
+
+ /**
+ * Check if this FrameBuffer has a full frame read.
+ */
+ public boolean isFrameFullyRead() {
+ return state_ == FrameBufferState.READ_FRAME_COMPLETE;
+ }
+
+ /**
+ * After the processor has processed the invocation, whatever thread is
+ * managing invocations should call this method on this FrameBuffer so we
+ * know it's time to start trying to write again. Also, if it turns out that
+ * there actually isn't any data in the response buffer, we'll skip trying
+ * to write and instead go back to reading.
+ */
+ public void responseReady() {
+ // the read buffer is definitely no longer in use, so we will decrement
+ // our read buffer count. we do this here as well as in close because
+ // we'd like to free this read memory up as quickly as possible for other
+ // clients.
+ readBufferBytesAllocated.addAndGet(-buffer_.array().length);
+
+ if (response_.len() == 0) {
+ // go straight to reading again. this was probably an oneway method
+ state_ = FrameBufferState.AWAITING_REGISTER_READ;
+ buffer_ = null;
+ } else {
+ buffer_ = ByteBuffer.wrap(response_.get(), 0, response_.len());
+
+ // set state that we're waiting to be switched to write. we do this
+ // asynchronously through requestSelectInterestChange() because there is
+ // a possibility that we're not in the main thread, and thus currently
+ // blocked in select(). (this functionality is in place for the sake of
+ // the HsHa server.)
+ state_ = FrameBufferState.AWAITING_REGISTER_WRITE;
+ }
+ requestSelectInterestChange();
+ }
+
+ /**
+ * Actually invoke the method signified by this FrameBuffer.
+ */
+ public void invoke() {
+ frameTrans_.reset(buffer_.array());
+ response_.reset();
+
+ try {
+ if (eventHandler_ != null) {
+ eventHandler_.processContext(context_, inTrans_, outTrans_);
+ }
+ processorFactory_.getProcessor(inTrans_).process(inProt_, outProt_);
+ responseReady();
+ return;
+ } catch (TException te) {
+ LOGGER.warn("Exception while invoking!", te);
+ } catch (Throwable t) {
+ LOGGER.error("Unexpected throwable while invoking!", t);
+ }
+ // This will only be reached when there is a throwable.
+ state_ = FrameBufferState.AWAITING_CLOSE;
+ requestSelectInterestChange();
+ }
+
+ /**
+ * Perform a read into buffer.
+ *
+ * @return true if the read succeeded, false if there was an error or the
+ * connection closed.
+ */
+ private boolean internalRead() {
+ try {
+ if (trans_.read(buffer_) < 0) {
+ return false;
+ }
+ return true;
+ } catch (IOException e) {
+ LOGGER.warn("Got an IOException in internalRead!", e);
+ return false;
+ }
+ }
+
+ /**
+ * We're done writing, so reset our interest ops and change state
+ * accordingly.
+ */
+ private void prepareRead() {
+ // we can set our interest directly without using the queue because
+ // we're in the select thread.
+ selectionKey_.interestOps(SelectionKey.OP_READ);
+ // get ready for another go-around
+ buffer_ = ByteBuffer.allocate(4);
+ state_ = FrameBufferState.READING_FRAME_SIZE;
+ }
+
+ /**
+ * When this FrameBuffer needs to change its select interests and execution
+ * might not be in its select thread, then this method will make sure the
+ * interest change gets done when the select thread wakes back up. When the
+ * current thread is this FrameBuffer's select thread, then it just does the
+ * interest change immediately.
+ */
+ protected void requestSelectInterestChange() {
+ if (Thread.currentThread() == this.selectThread_) {
+ changeSelectInterests();
+ } else {
+ this.selectThread_.requestSelectInterestChange(this);
+ }
+ }
+ } // FrameBuffer
+
+ public class AsyncFrameBuffer extends FrameBuffer {
+ public AsyncFrameBuffer(TNonblockingTransport trans, SelectionKey selectionKey, AbstractSelectThread selectThread) {
+ super(trans, selectionKey, selectThread);
+ }
+
+ public TProtocol getInputProtocol() {
+ return inProt_;
+ }
+
+ public TProtocol getOutputProtocol() {
+ return outProt_;
+ }
+
+
+ public void invoke() {
+ frameTrans_.reset(buffer_.array());
+ response_.reset();
+
+ try {
+ if (eventHandler_ != null) {
+ eventHandler_.processContext(context_, inTrans_, outTrans_);
+ }
+ ((TAsyncProcessor)processorFactory_.getProcessor(inTrans_)).process(this);
+ return;
+ } catch (TException te) {
+ LOGGER.warn("Exception while invoking!", te);
+ } catch (Throwable t) {
+ LOGGER.error("Unexpected throwable while invoking!", t);
+ }
+ // This will only be reached when there is a throwable.
+ state_ = FrameBufferState.AWAITING_CLOSE;
+ requestSelectInterestChange();
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/Invocation.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/Invocation.java
new file mode 100644
index 000000000..e8210f419
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/Invocation.java
@@ -0,0 +1,20 @@
+package org.apache.thrift.server;
+
+import org.apache.thrift.server.AbstractNonblockingServer.FrameBuffer;
+
+/**
+ * An Invocation represents a method call that is prepared to execute, given
+ * an idle worker thread. It contains the input and output protocols the
+ * thread's processor should use to perform the usual Thrift invocation.
+ */
+class Invocation implements Runnable {
+ private final FrameBuffer frameBuffer;
+
+ public Invocation(final FrameBuffer frameBuffer) {
+ this.frameBuffer = frameBuffer;
+ }
+
+ public void run() {
+ frameBuffer.invoke();
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/ServerContext.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/ServerContext.java
new file mode 100644
index 000000000..9b0b99eea
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/ServerContext.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Interface for storing server's connection context
+ */
+
+package org.apache.thrift.server;
+
+public interface ServerContext {}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TExtensibleServlet.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TExtensibleServlet.java
new file mode 100644
index 000000000..75082c0f7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TExtensibleServlet.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.server;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TIOStreamTransport;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * Servlet implementation class ThriftServer, that allows {@link TProcessor} and
+ * {@link TProtocolFactory} to be supplied after the {@link #init()} method has
+ * finished. <br>
+ * Subclasses must implement the abstract methods that return the TProcessor and
+ * two TProtocolFactory. Those methods are guaranteed to be called exactly once,
+ * and that {@link ServletContext} is available.
+ */
+public abstract class TExtensibleServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ private TProcessor processor;
+
+ private TProtocolFactory inFactory;
+
+ private TProtocolFactory outFactory;
+
+ private Collection<Map.Entry<String, String>> customHeaders;
+
+ /**
+ * Returns the appropriate {@link TProcessor}. This will be called <b>once</b> just
+ * after the {@link #init()} method
+ *
+ * @return the appropriate {@link TProcessor}
+ */
+ protected abstract TProcessor getProcessor();
+
+ /**
+ * Returns the appropriate in {@link TProtocolFactory}. This will be called
+ * <b>once</b> just after the {@link #init()} method
+ *
+ * @return the appropriate in {@link TProtocolFactory}
+ */
+ protected abstract TProtocolFactory getInProtocolFactory();
+
+ /**
+ * Returns the appropriate out {@link TProtocolFactory}. This will be called
+ * <b>once</b> just after the {@link #init()} method
+ *
+ * @return the appropriate out {@link TProtocolFactory}
+ */
+ protected abstract TProtocolFactory getOutProtocolFactory();
+
+ @Override
+ public final void init(ServletConfig config) throws ServletException {
+ super.init(config); //no-args init() happens here
+ this.processor = getProcessor();
+ this.inFactory = getInProtocolFactory();
+ this.outFactory = getOutProtocolFactory();
+ this.customHeaders = new ArrayList<Map.Entry<String, String>>();
+
+ if (processor == null) {
+ throw new ServletException("processor must be set");
+ }
+ if (inFactory == null) {
+ throw new ServletException("inFactory must be set");
+ }
+ if (outFactory == null) {
+ throw new ServletException("outFactory must be set");
+ }
+ }
+
+ /**
+ * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
+ * response)
+ */
+ @Override
+ protected void doPost(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ TTransport inTransport = null;
+ TTransport outTransport = null;
+
+ try {
+ response.setContentType("application/x-thrift");
+
+ if (null != this.customHeaders) {
+ for (Map.Entry<String, String> header : this.customHeaders) {
+ response.addHeader(header.getKey(), header.getValue());
+ }
+ }
+
+ InputStream in = request.getInputStream();
+ OutputStream out = response.getOutputStream();
+
+ TTransport transport = new TIOStreamTransport(in, out);
+ inTransport = transport;
+ outTransport = transport;
+
+ TProtocol inProtocol = inFactory.getProtocol(inTransport);
+ TProtocol outProtocol = inFactory.getProtocol(outTransport);
+
+ processor.process(inProtocol, outProtocol);
+ out.flush();
+ } catch (TException te) {
+ throw new ServletException(te);
+ }
+ }
+
+ /**
+ * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
+ * response)
+ */
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ doPost(req, resp);
+ }
+
+ public void addCustomHeader(final String key, final String value) {
+ this.customHeaders.add(new Map.Entry<String, String>() {
+ public String getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String setValue(String value) {
+ return null;
+ }
+ });
+ }
+
+ public void setCustomHeaders(Collection<Map.Entry<String, String>> headers) {
+ this.customHeaders.clear();
+ this.customHeaders.addAll(headers);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/THsHaServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/THsHaServer.java
new file mode 100644
index 000000000..4c5d7b5b5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/THsHaServer.java
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.thrift.server;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.thrift.transport.TNonblockingServerTransport;
+
+/**
+ * An extension of the TNonblockingServer to a Half-Sync/Half-Async server.
+ * Like TNonblockingServer, it relies on the use of TFramedTransport.
+ */
+public class THsHaServer extends TNonblockingServer {
+
+ public static class Args extends AbstractNonblockingServerArgs<Args> {
+ public int minWorkerThreads = 5;
+ public int maxWorkerThreads = Integer.MAX_VALUE;
+ private int stopTimeoutVal = 60;
+ private TimeUnit stopTimeoutUnit = TimeUnit.SECONDS;
+ private ExecutorService executorService = null;
+
+ public Args(TNonblockingServerTransport transport) {
+ super(transport);
+ }
+
+
+ /**
+ * Sets the min and max threads.
+ *
+ * @deprecated use {@link #minWorkerThreads(int)} and {@link #maxWorkerThreads(int)} instead.
+ */
+ @Deprecated
+ public Args workerThreads(int n) {
+ minWorkerThreads = n;
+ maxWorkerThreads = n;
+ return this;
+ }
+
+ /**
+ * @return what the min threads was set to.
+ * @deprecated use {@link #getMinWorkerThreads()} and {@link #getMaxWorkerThreads()} instead.
+ */
+ @Deprecated
+ public int getWorkerThreads() {
+ return minWorkerThreads;
+ }
+
+ public Args minWorkerThreads(int n) {
+ minWorkerThreads = n;
+ return this;
+ }
+
+ public Args maxWorkerThreads(int n) {
+ maxWorkerThreads = n;
+ return this;
+ }
+
+ public int getMinWorkerThreads() {
+ return minWorkerThreads;
+ }
+
+ public int getMaxWorkerThreads() {
+ return maxWorkerThreads;
+ }
+
+ public int getStopTimeoutVal() {
+ return stopTimeoutVal;
+ }
+
+ public Args stopTimeoutVal(int stopTimeoutVal) {
+ this.stopTimeoutVal = stopTimeoutVal;
+ return this;
+ }
+
+ public TimeUnit getStopTimeoutUnit() {
+ return stopTimeoutUnit;
+ }
+
+ public Args stopTimeoutUnit(TimeUnit stopTimeoutUnit) {
+ this.stopTimeoutUnit = stopTimeoutUnit;
+ return this;
+ }
+
+ public ExecutorService getExecutorService() {
+ return executorService;
+ }
+
+ public Args executorService(ExecutorService executorService) {
+ this.executorService = executorService;
+ return this;
+ }
+ }
+
+
+ // This wraps all the functionality of queueing and thread pool management
+ // for the passing of Invocations from the Selector to workers.
+ private final ExecutorService invoker;
+
+ private final Args args;
+
+ /**
+ * Create the server with the specified Args configuration
+ */
+ public THsHaServer(Args args) {
+ super(args);
+
+ invoker = args.executorService == null ? createInvokerPool(args) : args.executorService;
+ this.args = args;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void waitForShutdown() {
+ joinSelector();
+ gracefullyShutdownInvokerPool();
+ }
+
+ /**
+ * Helper to create an invoker pool
+ */
+ protected static ExecutorService createInvokerPool(Args options) {
+ int minWorkerThreads = options.minWorkerThreads;
+ int maxWorkerThreads = options.maxWorkerThreads;
+ int stopTimeoutVal = options.stopTimeoutVal;
+ TimeUnit stopTimeoutUnit = options.stopTimeoutUnit;
+
+ LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
+ ExecutorService invoker = new ThreadPoolExecutor(minWorkerThreads,
+ maxWorkerThreads, stopTimeoutVal, stopTimeoutUnit, queue);
+
+ return invoker;
+ }
+
+ protected ExecutorService getInvoker() {
+ return invoker;
+ }
+
+ protected void gracefullyShutdownInvokerPool() {
+ // try to gracefully shut down the executor service
+ invoker.shutdown();
+
+ // Loop until awaitTermination finally does return without a interrupted
+ // exception. If we don't do this, then we'll shut down prematurely. We want
+ // to let the executorService clear it's task queue, closing client sockets
+ // appropriately.
+ long timeoutMS = args.stopTimeoutUnit.toMillis(args.stopTimeoutVal);
+ long now = System.currentTimeMillis();
+ while (timeoutMS >= 0) {
+ try {
+ invoker.awaitTermination(timeoutMS, TimeUnit.MILLISECONDS);
+ break;
+ } catch (InterruptedException ix) {
+ long newnow = System.currentTimeMillis();
+ timeoutMS -= (newnow - now);
+ now = newnow;
+ }
+ }
+ }
+
+ /**
+ * We override the standard invoke method here to queue the invocation for
+ * invoker service instead of immediately invoking. The thread pool takes care
+ * of the rest.
+ */
+ @Override
+ protected boolean requestInvoke(FrameBuffer frameBuffer) {
+ try {
+ Runnable invocation = getRunnable(frameBuffer);
+ invoker.execute(invocation);
+ return true;
+ } catch (RejectedExecutionException rx) {
+ LOGGER.warn("ExecutorService rejected execution!", rx);
+ return false;
+ }
+ }
+
+ protected Runnable getRunnable(FrameBuffer frameBuffer){
+ return new Invocation(frameBuffer);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TNonblockingServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TNonblockingServer.java
new file mode 100644
index 000000000..79610b0f7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TNonblockingServer.java
@@ -0,0 +1,247 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.thrift.server;
+
+import org.apache.thrift.transport.TNonblockingServerTransport;
+import org.apache.thrift.transport.TNonblockingTransport;
+import org.apache.thrift.transport.TTransportException;
+
+import java.io.IOException;
+import java.nio.channels.SelectionKey;
+import java.util.Iterator;
+
+/**
+ * A nonblocking TServer implementation. This allows for fairness amongst all
+ * connected clients in terms of invocations.
+ *
+ * This server is inherently single-threaded. If you want a limited thread pool
+ * coupled with invocation-fairness, see THsHaServer.
+ *
+ * To use this server, you MUST use a TFramedTransport at the outermost
+ * transport, otherwise this server will be unable to determine when a whole
+ * method call has been read off the wire. Clients must also use TFramedTransport.
+ */
+public class TNonblockingServer extends AbstractNonblockingServer {
+
+ public static class Args extends AbstractNonblockingServerArgs<Args> {
+ public Args(TNonblockingServerTransport transport) {
+ super(transport);
+ }
+ }
+
+ private SelectAcceptThread selectAcceptThread_;
+
+ public TNonblockingServer(AbstractNonblockingServerArgs args) {
+ super(args);
+ }
+
+
+ /**
+ * Start the selector thread to deal with accepts and client messages.
+ *
+ * @return true if everything went ok, false if we couldn't start for some
+ * reason.
+ */
+ @Override
+ protected boolean startThreads() {
+ // start the selector
+ try {
+ selectAcceptThread_ = new SelectAcceptThread((TNonblockingServerTransport)serverTransport_);
+ selectAcceptThread_.start();
+ return true;
+ } catch (IOException e) {
+ LOGGER.error("Failed to start selector thread!", e);
+ return false;
+ }
+ }
+
+ @Override
+ protected void waitForShutdown() {
+ joinSelector();
+ }
+
+ /**
+ * Block until the selector thread exits.
+ */
+ protected void joinSelector() {
+ // wait until the selector thread exits
+ try {
+ selectAcceptThread_.join();
+ } catch (InterruptedException e) {
+ LOGGER.debug("Interrupted while waiting for accept thread", e);
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ /**
+ * Stop serving and shut everything down.
+ */
+ @Override
+ public void stop() {
+ stopped_ = true;
+ if (selectAcceptThread_ != null) {
+ selectAcceptThread_.wakeupSelector();
+ }
+ }
+
+ /**
+ * Perform an invocation. This method could behave several different ways
+ * - invoke immediately inline, queue for separate execution, etc.
+ */
+ @Override
+ protected boolean requestInvoke(FrameBuffer frameBuffer) {
+ frameBuffer.invoke();
+ return true;
+ }
+
+
+ public boolean isStopped() {
+ return selectAcceptThread_.isStopped();
+ }
+
+ /**
+ * The thread that will be doing all the selecting, managing new connections
+ * and those that still need to be read.
+ */
+ protected class SelectAcceptThread extends AbstractSelectThread {
+
+ // The server transport on which new client transports will be accepted
+ private final TNonblockingServerTransport serverTransport;
+
+ /**
+ * Set up the thread that will handle the non-blocking accepts, reads, and
+ * writes.
+ */
+ public SelectAcceptThread(final TNonblockingServerTransport serverTransport)
+ throws IOException {
+ this.serverTransport = serverTransport;
+ serverTransport.registerSelector(selector);
+ }
+
+ public boolean isStopped() {
+ return stopped_;
+ }
+
+ /**
+ * The work loop. Handles both selecting (all IO operations) and managing
+ * the selection preferences of all existing connections.
+ */
+ public void run() {
+ try {
+ if (eventHandler_ != null) {
+ eventHandler_.preServe();
+ }
+
+ while (!stopped_) {
+ select();
+ processInterestChanges();
+ }
+ for (SelectionKey selectionKey : selector.keys()) {
+ cleanupSelectionKey(selectionKey);
+ }
+ } catch (Throwable t) {
+ LOGGER.error("run() exiting due to uncaught error", t);
+ } finally {
+ try {
+ selector.close();
+ } catch (IOException e) {
+ LOGGER.error("Got an IOException while closing selector!", e);
+ }
+ stopped_ = true;
+ }
+ }
+
+ /**
+ * Select and process IO events appropriately:
+ * If there are connections to be accepted, accept them.
+ * If there are existing connections with data waiting to be read, read it,
+ * buffering until a whole frame has been read.
+ * If there are any pending responses, buffer them until their target client
+ * is available, and then send the data.
+ */
+ private void select() {
+ try {
+ // wait for io events.
+ selector.select();
+
+ // process the io events we received
+ Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
+ while (!stopped_ && selectedKeys.hasNext()) {
+ SelectionKey key = selectedKeys.next();
+ selectedKeys.remove();
+
+ // skip if not valid
+ if (!key.isValid()) {
+ cleanupSelectionKey(key);
+ continue;
+ }
+
+ // if the key is marked Accept, then it has to be the server
+ // transport.
+ if (key.isAcceptable()) {
+ handleAccept();
+ } else if (key.isReadable()) {
+ // deal with reads
+ handleRead(key);
+ } else if (key.isWritable()) {
+ // deal with writes
+ handleWrite(key);
+ } else {
+ LOGGER.warn("Unexpected state in select! " + key.interestOps());
+ }
+ }
+ } catch (IOException e) {
+ LOGGER.warn("Got an IOException while selecting!", e);
+ }
+ }
+
+ protected FrameBuffer createFrameBuffer(final TNonblockingTransport trans,
+ final SelectionKey selectionKey,
+ final AbstractSelectThread selectThread) {
+ return processorFactory_.isAsyncProcessor() ?
+ new AsyncFrameBuffer(trans, selectionKey, selectThread) :
+ new FrameBuffer(trans, selectionKey, selectThread);
+ }
+
+ /**
+ * Accept a new connection.
+ */
+ private void handleAccept() throws IOException {
+ SelectionKey clientKey = null;
+ TNonblockingTransport client = null;
+ try {
+ // accept the connection
+ client = (TNonblockingTransport)serverTransport.accept();
+ clientKey = client.registerSelector(selector, SelectionKey.OP_READ);
+
+ // add this key to the map
+ FrameBuffer frameBuffer = createFrameBuffer(client, clientKey, SelectAcceptThread.this);
+
+ clientKey.attach(frameBuffer);
+ } catch (TTransportException tte) {
+ // something went wrong accepting.
+ LOGGER.warn("Exception trying to accept!", tte);
+ if (clientKey != null) cleanupSelectionKey(clientKey);
+ if (client != null) client.close();
+ }
+ }
+ } // SelectAcceptThread
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServer.java
new file mode 100644
index 000000000..bac06b26b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServer.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.server;
+
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.TProcessorFactory;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TServerTransport;
+import org.apache.thrift.transport.TTransportFactory;
+
+/**
+ * Generic interface for a Thrift server.
+ *
+ */
+public abstract class TServer {
+
+ public static class Args extends AbstractServerArgs<Args> {
+ public Args(TServerTransport transport) {
+ super(transport);
+ }
+ }
+
+ public static abstract class AbstractServerArgs<T extends AbstractServerArgs<T>> {
+ final TServerTransport serverTransport;
+ TProcessorFactory processorFactory;
+ TTransportFactory inputTransportFactory = new TTransportFactory();
+ TTransportFactory outputTransportFactory = new TTransportFactory();
+ TProtocolFactory inputProtocolFactory = new TBinaryProtocol.Factory();
+ TProtocolFactory outputProtocolFactory = new TBinaryProtocol.Factory();
+
+ public AbstractServerArgs(TServerTransport transport) {
+ serverTransport = transport;
+ }
+
+ public T processorFactory(TProcessorFactory factory) {
+ this.processorFactory = factory;
+ return (T) this;
+ }
+
+ public T processor(TProcessor processor) {
+ this.processorFactory = new TProcessorFactory(processor);
+ return (T) this;
+ }
+
+ public T transportFactory(TTransportFactory factory) {
+ this.inputTransportFactory = factory;
+ this.outputTransportFactory = factory;
+ return (T) this;
+ }
+
+ public T inputTransportFactory(TTransportFactory factory) {
+ this.inputTransportFactory = factory;
+ return (T) this;
+ }
+
+ public T outputTransportFactory(TTransportFactory factory) {
+ this.outputTransportFactory = factory;
+ return (T) this;
+ }
+
+ public T protocolFactory(TProtocolFactory factory) {
+ this.inputProtocolFactory = factory;
+ this.outputProtocolFactory = factory;
+ return (T) this;
+ }
+
+ public T inputProtocolFactory(TProtocolFactory factory) {
+ this.inputProtocolFactory = factory;
+ return (T) this;
+ }
+
+ public T outputProtocolFactory(TProtocolFactory factory) {
+ this.outputProtocolFactory = factory;
+ return (T) this;
+ }
+ }
+
+ /**
+ * Core processor
+ */
+ protected TProcessorFactory processorFactory_;
+
+ /**
+ * Server transport
+ */
+ protected TServerTransport serverTransport_;
+
+ /**
+ * Input Transport Factory
+ */
+ protected TTransportFactory inputTransportFactory_;
+
+ /**
+ * Output Transport Factory
+ */
+ protected TTransportFactory outputTransportFactory_;
+
+ /**
+ * Input Protocol Factory
+ */
+ protected TProtocolFactory inputProtocolFactory_;
+
+ /**
+ * Output Protocol Factory
+ */
+ protected TProtocolFactory outputProtocolFactory_;
+
+ private volatile boolean isServing;
+
+ protected TServerEventHandler eventHandler_;
+
+ // Flag for stopping the server
+ // Please see THRIFT-1795 for the usage of this flag
+ protected volatile boolean stopped_ = false;
+
+ protected TServer(AbstractServerArgs args) {
+ processorFactory_ = args.processorFactory;
+ serverTransport_ = args.serverTransport;
+ inputTransportFactory_ = args.inputTransportFactory;
+ outputTransportFactory_ = args.outputTransportFactory;
+ inputProtocolFactory_ = args.inputProtocolFactory;
+ outputProtocolFactory_ = args.outputProtocolFactory;
+ }
+
+ /**
+ * The run method fires up the server and gets things going.
+ */
+ public abstract void serve();
+
+ /**
+ * Stop the server. This is optional on a per-implementation basis. Not
+ * all servers are required to be cleanly stoppable.
+ */
+ public void stop() {}
+
+ public boolean isServing() {
+ return isServing;
+ }
+
+ protected void setServing(boolean serving) {
+ isServing = serving;
+ }
+
+ public void setServerEventHandler(TServerEventHandler eventHandler) {
+ eventHandler_ = eventHandler;
+ }
+
+ public TServerEventHandler getEventHandler() {
+ return eventHandler_;
+ }
+
+ public boolean getShouldStop() {
+ return this.stopped_;
+ }
+
+ public void setShouldStop(boolean shouldStop) {
+ this.stopped_ = shouldStop;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServerEventHandler.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServerEventHandler.java
new file mode 100644
index 000000000..f069b9bfb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServerEventHandler.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.server;
+
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * Interface that can handle events from the server core. To
+ * use this you should subclass it and implement the methods that you care
+ * about. Your subclass can also store local data that you may care about,
+ * such as additional "arguments" to these methods (stored in the object
+ * instance's state).
+ */
+public interface TServerEventHandler {
+
+ /**
+ * Called before the server begins.
+ */
+ void preServe();
+
+ /**
+ * Called when a new client has connected and is about to being processing.
+ */
+ ServerContext createContext(TProtocol input,
+ TProtocol output);
+
+ /**
+ * Called when a client has finished request-handling to delete server
+ * context.
+ */
+ void deleteContext(ServerContext serverContext,
+ TProtocol input,
+ TProtocol output);
+
+ /**
+ * Called when a client is about to call the processor.
+ */
+ void processContext(ServerContext serverContext,
+ TTransport inputTransport, TTransport outputTransport);
+
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServlet.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServlet.java
new file mode 100644
index 000000000..c1ab9df55
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServlet.java
@@ -0,0 +1,119 @@
+package org.apache.thrift.server;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TIOStreamTransport;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * Servlet implementation class ThriftServer
+ */
+public class TServlet extends HttpServlet {
+
+ private final TProcessor processor;
+
+ private final TProtocolFactory inProtocolFactory;
+
+ private final TProtocolFactory outProtocolFactory;
+
+ private final Collection<Map.Entry<String, String>> customHeaders;
+
+ /**
+ * @see HttpServlet#HttpServlet()
+ */
+ public TServlet(TProcessor processor, TProtocolFactory inProtocolFactory,
+ TProtocolFactory outProtocolFactory) {
+ super();
+ this.processor = processor;
+ this.inProtocolFactory = inProtocolFactory;
+ this.outProtocolFactory = outProtocolFactory;
+ this.customHeaders = new ArrayList<Map.Entry<String, String>>();
+ }
+
+ /**
+ * @see HttpServlet#HttpServlet()
+ */
+ public TServlet(TProcessor processor, TProtocolFactory protocolFactory) {
+ this(processor, protocolFactory, protocolFactory);
+ }
+
+ /**
+ * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
+ * response)
+ */
+ @Override
+ protected void doPost(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+
+ TTransport inTransport = null;
+ TTransport outTransport = null;
+
+ try {
+ response.setContentType("application/x-thrift");
+
+ if (null != this.customHeaders) {
+ for (Map.Entry<String, String> header : this.customHeaders) {
+ response.addHeader(header.getKey(), header.getValue());
+ }
+ }
+ InputStream in = request.getInputStream();
+ OutputStream out = response.getOutputStream();
+
+ TTransport transport = new TIOStreamTransport(in, out);
+ inTransport = transport;
+ outTransport = transport;
+
+ TProtocol inProtocol = inProtocolFactory.getProtocol(inTransport);
+ TProtocol outProtocol = outProtocolFactory.getProtocol(outTransport);
+
+ processor.process(inProtocol, outProtocol);
+ out.flush();
+ } catch (TException te) {
+ throw new ServletException(te);
+ }
+ }
+
+ /**
+ * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
+ * response)
+ */
+ protected void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ doPost(request, response);
+ }
+
+ public void addCustomHeader(final String key, final String value) {
+ this.customHeaders.add(new Map.Entry<String, String>() {
+ public String getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String setValue(String value) {
+ return null;
+ }
+ });
+ }
+
+ public void setCustomHeaders(Collection<Map.Entry<String, String>> headers) {
+ this.customHeaders.clear();
+ this.customHeaders.addAll(headers);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TSimpleServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TSimpleServer.java
new file mode 100644
index 000000000..13501efc6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TSimpleServer.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.server;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.transport.TTransport;
+import org.apache.thrift.transport.TTransportException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Simple singlethreaded server for testing.
+ *
+ */
+public class TSimpleServer extends TServer {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TSimpleServer.class.getName());
+
+ public TSimpleServer(AbstractServerArgs args) {
+ super(args);
+ }
+
+ public void serve() {
+ try {
+ serverTransport_.listen();
+ } catch (TTransportException ttx) {
+ LOGGER.error("Error occurred during listening.", ttx);
+ return;
+ }
+
+ // Run the preServe event
+ if (eventHandler_ != null) {
+ eventHandler_.preServe();
+ }
+
+ setServing(true);
+
+ while (!stopped_) {
+ TTransport client = null;
+ TProcessor processor = null;
+ TTransport inputTransport = null;
+ TTransport outputTransport = null;
+ TProtocol inputProtocol = null;
+ TProtocol outputProtocol = null;
+ ServerContext connectionContext = null;
+ try {
+ client = serverTransport_.accept();
+ if (client != null) {
+ processor = processorFactory_.getProcessor(client);
+ inputTransport = inputTransportFactory_.getTransport(client);
+ outputTransport = outputTransportFactory_.getTransport(client);
+ inputProtocol = inputProtocolFactory_.getProtocol(inputTransport);
+ outputProtocol = outputProtocolFactory_.getProtocol(outputTransport);
+ if (eventHandler_ != null) {
+ connectionContext = eventHandler_.createContext(inputProtocol, outputProtocol);
+ }
+ while (true) {
+ if (eventHandler_ != null) {
+ eventHandler_.processContext(connectionContext, inputTransport, outputTransport);
+ }
+ processor.process(inputProtocol, outputProtocol);
+ }
+ }
+ } catch (TTransportException ttx) {
+ // Client died, just move on
+ } catch (TException tx) {
+ if (!stopped_) {
+ LOGGER.error("Thrift error occurred during processing of message.", tx);
+ }
+ } catch (Exception x) {
+ if (!stopped_) {
+ LOGGER.error("Error occurred during processing of message.", x);
+ }
+ }
+
+ if (eventHandler_ != null) {
+ eventHandler_.deleteContext(connectionContext, inputProtocol, outputProtocol);
+ }
+
+ if (inputTransport != null) {
+ inputTransport.close();
+ }
+
+ if (outputTransport != null) {
+ outputTransport.close();
+ }
+
+ }
+ setServing(false);
+ }
+
+ public void stop() {
+ stopped_ = true;
+ serverTransport_.interrupt();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadPoolServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadPoolServer.java
new file mode 100644
index 000000000..87e873381
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadPoolServer.java
@@ -0,0 +1,359 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.server;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.transport.TServerTransport;
+import org.apache.thrift.transport.TTransport;
+import org.apache.thrift.transport.TTransportException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Server which uses Java's built in ThreadPool management to spawn off
+ * a worker pool that
+ *
+ */
+public class TThreadPoolServer extends TServer {
+ private static final Logger LOGGER = LoggerFactory.getLogger(TThreadPoolServer.class.getName());
+
+ public static class Args extends AbstractServerArgs<Args> {
+ public int minWorkerThreads = 5;
+ public int maxWorkerThreads = Integer.MAX_VALUE;
+ public ExecutorService executorService;
+ public int stopTimeoutVal = 60;
+ public TimeUnit stopTimeoutUnit = TimeUnit.SECONDS;
+ public int requestTimeout = 20;
+ public TimeUnit requestTimeoutUnit = TimeUnit.SECONDS;
+ public int beBackoffSlotLength = 100;
+ public TimeUnit beBackoffSlotLengthUnit = TimeUnit.MILLISECONDS;
+
+ public Args(TServerTransport transport) {
+ super(transport);
+ }
+
+ public Args minWorkerThreads(int n) {
+ minWorkerThreads = n;
+ return this;
+ }
+
+ public Args maxWorkerThreads(int n) {
+ maxWorkerThreads = n;
+ return this;
+ }
+
+ public Args stopTimeoutVal(int n) {
+ stopTimeoutVal = n;
+ return this;
+ }
+
+ public Args stopTimeoutUnit(TimeUnit tu) {
+ stopTimeoutUnit = tu;
+ return this;
+ }
+
+ public Args requestTimeout(int n) {
+ requestTimeout = n;
+ return this;
+ }
+
+ public Args requestTimeoutUnit(TimeUnit tu) {
+ requestTimeoutUnit = tu;
+ return this;
+ }
+ //Binary exponential backoff slot length
+ public Args beBackoffSlotLength(int n) {
+ beBackoffSlotLength = n;
+ return this;
+ }
+
+ //Binary exponential backoff slot time unit
+ public Args beBackoffSlotLengthUnit(TimeUnit tu) {
+ beBackoffSlotLengthUnit = tu;
+ return this;
+ }
+
+ public Args executorService(ExecutorService executorService) {
+ this.executorService = executorService;
+ return this;
+ }
+ }
+
+ // Executor service for handling client connections
+ private ExecutorService executorService_;
+
+ private final TimeUnit stopTimeoutUnit;
+
+ private final long stopTimeoutVal;
+
+ private final TimeUnit requestTimeoutUnit;
+
+ private final long requestTimeout;
+
+ private final long beBackoffSlotInMillis;
+
+ private Random random = new Random(System.currentTimeMillis());
+
+ public TThreadPoolServer(Args args) {
+ super(args);
+
+ stopTimeoutUnit = args.stopTimeoutUnit;
+ stopTimeoutVal = args.stopTimeoutVal;
+ requestTimeoutUnit = args.requestTimeoutUnit;
+ requestTimeout = args.requestTimeout;
+ beBackoffSlotInMillis = args.beBackoffSlotLengthUnit.toMillis(args.beBackoffSlotLength);
+
+ executorService_ = args.executorService != null ?
+ args.executorService : createDefaultExecutorService(args);
+ }
+
+ private static ExecutorService createDefaultExecutorService(Args args) {
+ SynchronousQueue<Runnable> executorQueue =
+ new SynchronousQueue<Runnable>();
+ return new ThreadPoolExecutor(args.minWorkerThreads,
+ args.maxWorkerThreads,
+ args.stopTimeoutVal,
+ args.stopTimeoutUnit,
+ executorQueue);
+ }
+
+ protected ExecutorService getExecutorService() {
+ return executorService_;
+ }
+
+ protected boolean preServe() {
+ try {
+ serverTransport_.listen();
+ } catch (TTransportException ttx) {
+ LOGGER.error("Error occurred during listening.", ttx);
+ return false;
+ }
+
+ // Run the preServe event
+ if (eventHandler_ != null) {
+ eventHandler_.preServe();
+ }
+ stopped_ = false;
+ setServing(true);
+
+ return true;
+ }
+
+ public void serve() {
+ if (!preServe()) {
+ return;
+ }
+
+ execute();
+ waitForShutdown();
+
+ setServing(false);
+ }
+
+ protected void execute() {
+ int failureCount = 0;
+ while (!stopped_) {
+ try {
+ TTransport client = serverTransport_.accept();
+ WorkerProcess wp = new WorkerProcess(client);
+
+ int retryCount = 0;
+ long remainTimeInMillis = requestTimeoutUnit.toMillis(requestTimeout);
+ while(true) {
+ try {
+ executorService_.execute(wp);
+ break;
+ } catch(Throwable t) {
+ if (t instanceof RejectedExecutionException) {
+ retryCount++;
+ try {
+ if (remainTimeInMillis > 0) {
+ //do a truncated 20 binary exponential backoff sleep
+ long sleepTimeInMillis = ((long) (random.nextDouble() *
+ (1L << Math.min(retryCount, 20)))) * beBackoffSlotInMillis;
+ sleepTimeInMillis = Math.min(sleepTimeInMillis, remainTimeInMillis);
+ TimeUnit.MILLISECONDS.sleep(sleepTimeInMillis);
+ remainTimeInMillis = remainTimeInMillis - sleepTimeInMillis;
+ } else {
+ client.close();
+ wp = null;
+ LOGGER.warn("Task has been rejected by ExecutorService " + retryCount
+ + " times till timedout, reason: " + t);
+ break;
+ }
+ } catch (InterruptedException e) {
+ LOGGER.warn("Interrupted while waiting to place client on executor queue.");
+ Thread.currentThread().interrupt();
+ break;
+ }
+ } else if (t instanceof Error) {
+ LOGGER.error("ExecutorService threw error: " + t, t);
+ throw (Error)t;
+ } else {
+ //for other possible runtime errors from ExecutorService, should also not kill serve
+ LOGGER.warn("ExecutorService threw error: " + t, t);
+ break;
+ }
+ }
+ }
+ } catch (TTransportException ttx) {
+ if (!stopped_) {
+ ++failureCount;
+ LOGGER.warn("Transport error occurred during acceptance of message.", ttx);
+ }
+ }
+ }
+ }
+
+ protected void waitForShutdown() {
+ executorService_.shutdown();
+
+ // Loop until awaitTermination finally does return without a interrupted
+ // exception. If we don't do this, then we'll shut down prematurely. We want
+ // to let the executorService clear it's task queue, closing client sockets
+ // appropriately.
+ long timeoutMS = stopTimeoutUnit.toMillis(stopTimeoutVal);
+ long now = System.currentTimeMillis();
+ while (timeoutMS >= 0) {
+ try {
+ executorService_.awaitTermination(timeoutMS, TimeUnit.MILLISECONDS);
+ break;
+ } catch (InterruptedException ix) {
+ long newnow = System.currentTimeMillis();
+ timeoutMS -= (newnow - now);
+ now = newnow;
+ }
+ }
+ }
+
+ public void stop() {
+ stopped_ = true;
+ serverTransport_.interrupt();
+ }
+
+ private class WorkerProcess implements Runnable {
+
+ /**
+ * Client that this services.
+ */
+ private TTransport client_;
+
+ /**
+ * Default constructor.
+ *
+ * @param client Transport to process
+ */
+ private WorkerProcess(TTransport client) {
+ client_ = client;
+ }
+
+ /**
+ * Loops on processing a client forever
+ */
+ public void run() {
+ TProcessor processor = null;
+ TTransport inputTransport = null;
+ TTransport outputTransport = null;
+ TProtocol inputProtocol = null;
+ TProtocol outputProtocol = null;
+
+ TServerEventHandler eventHandler = null;
+ ServerContext connectionContext = null;
+
+ try {
+ processor = processorFactory_.getProcessor(client_);
+ inputTransport = inputTransportFactory_.getTransport(client_);
+ outputTransport = outputTransportFactory_.getTransport(client_);
+ inputProtocol = inputProtocolFactory_.getProtocol(inputTransport);
+ outputProtocol = outputProtocolFactory_.getProtocol(outputTransport);
+
+ eventHandler = getEventHandler();
+ if (eventHandler != null) {
+ connectionContext = eventHandler.createContext(inputProtocol, outputProtocol);
+ }
+ // we check stopped_ first to make sure we're not supposed to be shutting
+ // down. this is necessary for graceful shutdown.
+ while (true) {
+
+ if (eventHandler != null) {
+ eventHandler.processContext(connectionContext, inputTransport, outputTransport);
+ }
+
+ if (stopped_) {
+ break;
+ }
+ processor.process(inputProtocol, outputProtocol);
+ }
+ } catch (Exception x) {
+ // We'll usually receive RuntimeException types here
+ // Need to unwrap to ascertain real causing exception before we choose to ignore
+ // Ignore err-logging all transport-level/type exceptions
+ if (!isIgnorableException(x)) {
+ // Log the exception at error level and continue
+ LOGGER.error((x instanceof TException? "Thrift " : "") + "Error occurred during processing of message.", x);
+ }
+ } finally {
+ if (eventHandler != null) {
+ eventHandler.deleteContext(connectionContext, inputProtocol, outputProtocol);
+ }
+ if (inputTransport != null) {
+ inputTransport.close();
+ }
+ if (outputTransport != null) {
+ outputTransport.close();
+ }
+ if (client_.isOpen()) {
+ client_.close();
+ }
+ }
+ }
+
+ private boolean isIgnorableException(Exception x) {
+ TTransportException tTransportException = null;
+
+ if (x instanceof TTransportException) {
+ tTransportException = (TTransportException)x;
+ }
+ else if (x.getCause() instanceof TTransportException) {
+ tTransportException = (TTransportException)x.getCause();
+ }
+
+ if (tTransportException != null) {
+ switch(tTransportException.getType()) {
+ case TTransportException.END_OF_FILE:
+ case TTransportException.TIMED_OUT:
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadedSelectorServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadedSelectorServer.java
new file mode 100644
index 000000000..038507e9c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadedSelectorServer.java
@@ -0,0 +1,744 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.server;
+
+import org.apache.thrift.transport.TNonblockingServerTransport;
+import org.apache.thrift.transport.TNonblockingTransport;
+import org.apache.thrift.transport.TTransportException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A Half-Sync/Half-Async server with a separate pool of threads to handle
+ * non-blocking I/O. Accepts are handled on a single thread, and a configurable
+ * number of nonblocking selector threads manage reading and writing of client
+ * connections. A synchronous worker thread pool handles processing of requests.
+ *
+ * Performs better than TNonblockingServer/THsHaServer in multi-core
+ * environments when the the bottleneck is CPU on the single selector thread
+ * handling I/O. In addition, because the accept handling is decoupled from
+ * reads/writes and invocation, the server has better ability to handle back-
+ * pressure from new connections (e.g. stop accepting when busy).
+ *
+ * Like TNonblockingServer, it relies on the use of TFramedTransport.
+ */
+public class TThreadedSelectorServer extends AbstractNonblockingServer {
+ private static final Logger LOGGER = LoggerFactory.getLogger(TThreadedSelectorServer.class.getName());
+
+ public static class Args extends AbstractNonblockingServerArgs<Args> {
+
+ /** The number of threads for selecting on already-accepted connections */
+ public int selectorThreads = 2;
+ /**
+ * The size of the executor service (if none is specified) that will handle
+ * invocations. This may be set to 0, in which case invocations will be
+ * handled directly on the selector threads (as is in TNonblockingServer)
+ */
+ private int workerThreads = 5;
+ /** Time to wait for server to stop gracefully */
+ private int stopTimeoutVal = 60;
+ private TimeUnit stopTimeoutUnit = TimeUnit.SECONDS;
+ /** The ExecutorService for handling dispatched requests */
+ private ExecutorService executorService = null;
+ /**
+ * The size of the blocking queue per selector thread for passing accepted
+ * connections to the selector thread
+ */
+ private int acceptQueueSizePerThread = 4;
+
+ /**
+ * Determines the strategy for handling new accepted connections.
+ */
+ public static enum AcceptPolicy {
+ /**
+ * Require accepted connection registration to be handled by the executor.
+ * If the worker pool is saturated, further accepts will be closed
+ * immediately. Slightly increases latency due to an extra scheduling.
+ */
+ FAIR_ACCEPT,
+ /**
+ * Handle the accepts as fast as possible, disregarding the status of the
+ * executor service.
+ */
+ FAST_ACCEPT
+ }
+
+ private AcceptPolicy acceptPolicy = AcceptPolicy.FAST_ACCEPT;
+
+ public Args(TNonblockingServerTransport transport) {
+ super(transport);
+ }
+
+ public Args selectorThreads(int i) {
+ selectorThreads = i;
+ return this;
+ }
+
+ public int getSelectorThreads() {
+ return selectorThreads;
+ }
+
+ public Args workerThreads(int i) {
+ workerThreads = i;
+ return this;
+ }
+
+ public int getWorkerThreads() {
+ return workerThreads;
+ }
+
+ public int getStopTimeoutVal() {
+ return stopTimeoutVal;
+ }
+
+ public Args stopTimeoutVal(int stopTimeoutVal) {
+ this.stopTimeoutVal = stopTimeoutVal;
+ return this;
+ }
+
+ public TimeUnit getStopTimeoutUnit() {
+ return stopTimeoutUnit;
+ }
+
+ public Args stopTimeoutUnit(TimeUnit stopTimeoutUnit) {
+ this.stopTimeoutUnit = stopTimeoutUnit;
+ return this;
+ }
+
+ public ExecutorService getExecutorService() {
+ return executorService;
+ }
+
+ public Args executorService(ExecutorService executorService) {
+ this.executorService = executorService;
+ return this;
+ }
+
+ public int getAcceptQueueSizePerThread() {
+ return acceptQueueSizePerThread;
+ }
+
+ public Args acceptQueueSizePerThread(int acceptQueueSizePerThread) {
+ this.acceptQueueSizePerThread = acceptQueueSizePerThread;
+ return this;
+ }
+
+ public AcceptPolicy getAcceptPolicy() {
+ return acceptPolicy;
+ }
+
+ public Args acceptPolicy(AcceptPolicy acceptPolicy) {
+ this.acceptPolicy = acceptPolicy;
+ return this;
+ }
+
+ public void validate() {
+ if (selectorThreads <= 0) {
+ throw new IllegalArgumentException("selectorThreads must be positive.");
+ }
+ if (workerThreads < 0) {
+ throw new IllegalArgumentException("workerThreads must be non-negative.");
+ }
+ if (acceptQueueSizePerThread <= 0) {
+ throw new IllegalArgumentException("acceptQueueSizePerThread must be positive.");
+ }
+ }
+ }
+
+ // The thread handling all accepts
+ private AcceptThread acceptThread;
+
+ // Threads handling events on client transports
+ private final Set<SelectorThread> selectorThreads = new HashSet<SelectorThread>();
+
+ // This wraps all the functionality of queueing and thread pool management
+ // for the passing of Invocations from the selector thread(s) to the workers
+ // (if any).
+ private final ExecutorService invoker;
+
+ private final Args args;
+
+ /**
+ * Create the server with the specified Args configuration
+ */
+ public TThreadedSelectorServer(Args args) {
+ super(args);
+ args.validate();
+ invoker = args.executorService == null ? createDefaultExecutor(args) : args.executorService;
+ this.args = args;
+ }
+
+ /**
+ * Start the accept and selector threads running to deal with clients.
+ *
+ * @return true if everything went ok, false if we couldn't start for some
+ * reason.
+ */
+ @Override
+ protected boolean startThreads() {
+ try {
+ for (int i = 0; i < args.selectorThreads; ++i) {
+ selectorThreads.add(new SelectorThread(args.acceptQueueSizePerThread));
+ }
+ acceptThread = new AcceptThread((TNonblockingServerTransport) serverTransport_,
+ createSelectorThreadLoadBalancer(selectorThreads));
+ for (SelectorThread thread : selectorThreads) {
+ thread.start();
+ }
+ acceptThread.start();
+ return true;
+ } catch (IOException e) {
+ LOGGER.error("Failed to start threads!", e);
+ return false;
+ }
+ }
+
+ /**
+ * Joins the accept and selector threads and shuts down the executor service.
+ */
+ @Override
+ protected void waitForShutdown() {
+ try {
+ joinThreads();
+ } catch (InterruptedException e) {
+ // Non-graceful shutdown occurred
+ LOGGER.error("Interrupted while joining threads!", e);
+ }
+ gracefullyShutdownInvokerPool();
+ }
+
+ protected void joinThreads() throws InterruptedException {
+ // wait until the io threads exit
+ acceptThread.join();
+ for (SelectorThread thread : selectorThreads) {
+ thread.join();
+ }
+ }
+
+ /**
+ * Stop serving and shut everything down.
+ */
+ @Override
+ public void stop() {
+ stopped_ = true;
+
+ // Stop queuing connect attempts asap
+ stopListening();
+
+ if (acceptThread != null) {
+ acceptThread.wakeupSelector();
+ }
+ if (selectorThreads != null) {
+ for (SelectorThread thread : selectorThreads) {
+ if (thread != null)
+ thread.wakeupSelector();
+ }
+ }
+ }
+
+ protected void gracefullyShutdownInvokerPool() {
+ // try to gracefully shut down the executor service
+ invoker.shutdown();
+
+ // Loop until awaitTermination finally does return without a interrupted
+ // exception. If we don't do this, then we'll shut down prematurely. We want
+ // to let the executorService clear it's task queue, closing client sockets
+ // appropriately.
+ long timeoutMS = args.stopTimeoutUnit.toMillis(args.stopTimeoutVal);
+ long now = System.currentTimeMillis();
+ while (timeoutMS >= 0) {
+ try {
+ invoker.awaitTermination(timeoutMS, TimeUnit.MILLISECONDS);
+ break;
+ } catch (InterruptedException ix) {
+ long newnow = System.currentTimeMillis();
+ timeoutMS -= (newnow - now);
+ now = newnow;
+ }
+ }
+ }
+
+ /**
+ * We override the standard invoke method here to queue the invocation for
+ * invoker service instead of immediately invoking. If there is no thread
+ * pool, handle the invocation inline on this thread
+ */
+ @Override
+ protected boolean requestInvoke(FrameBuffer frameBuffer) {
+ Runnable invocation = getRunnable(frameBuffer);
+ if (invoker != null) {
+ try {
+ invoker.execute(invocation);
+ return true;
+ } catch (RejectedExecutionException rx) {
+ LOGGER.warn("ExecutorService rejected execution!", rx);
+ return false;
+ }
+ } else {
+ // Invoke on the caller's thread
+ invocation.run();
+ return true;
+ }
+ }
+
+ protected Runnable getRunnable(FrameBuffer frameBuffer) {
+ return new Invocation(frameBuffer);
+ }
+
+ /**
+ * Helper to create the invoker if one is not specified
+ */
+ protected static ExecutorService createDefaultExecutor(Args options) {
+ return (options.workerThreads > 0) ? Executors.newFixedThreadPool(options.workerThreads) : null;
+ }
+
+ private static BlockingQueue<TNonblockingTransport> createDefaultAcceptQueue(int queueSize) {
+ if (queueSize == 0) {
+ // Unbounded queue
+ return new LinkedBlockingQueue<TNonblockingTransport>();
+ }
+ return new ArrayBlockingQueue<TNonblockingTransport>(queueSize);
+ }
+
+ /**
+ * The thread that selects on the server transport (listen socket) and accepts
+ * new connections to hand off to the IO selector threads
+ */
+ protected class AcceptThread extends Thread {
+
+ // The listen socket to accept on
+ private final TNonblockingServerTransport serverTransport;
+ private final Selector acceptSelector;
+
+ private final SelectorThreadLoadBalancer threadChooser;
+
+ /**
+ * Set up the AcceptThead
+ *
+ * @throws IOException
+ */
+ public AcceptThread(TNonblockingServerTransport serverTransport,
+ SelectorThreadLoadBalancer threadChooser) throws IOException {
+ this.serverTransport = serverTransport;
+ this.threadChooser = threadChooser;
+ this.acceptSelector = SelectorProvider.provider().openSelector();
+ this.serverTransport.registerSelector(acceptSelector);
+ }
+
+ /**
+ * The work loop. Selects on the server transport and accepts. If there was
+ * a server transport that had blocking accepts, and returned on blocking
+ * client transports, that should be used instead
+ */
+ public void run() {
+ try {
+ if (eventHandler_ != null) {
+ eventHandler_.preServe();
+ }
+
+ while (!stopped_) {
+ select();
+ }
+ } catch (Throwable t) {
+ LOGGER.error("run() on AcceptThread exiting due to uncaught error", t);
+ } finally {
+ try {
+ acceptSelector.close();
+ } catch (IOException e) {
+ LOGGER.error("Got an IOException while closing accept selector!", e);
+ }
+ // This will wake up the selector threads
+ TThreadedSelectorServer.this.stop();
+ }
+ }
+
+ /**
+ * If the selector is blocked, wake it up.
+ */
+ public void wakeupSelector() {
+ acceptSelector.wakeup();
+ }
+
+ /**
+ * Select and process IO events appropriately: If there are connections to
+ * be accepted, accept them.
+ */
+ private void select() {
+ try {
+ // wait for connect events.
+ acceptSelector.select();
+
+ // process the io events we received
+ Iterator<SelectionKey> selectedKeys = acceptSelector.selectedKeys().iterator();
+ while (!stopped_ && selectedKeys.hasNext()) {
+ SelectionKey key = selectedKeys.next();
+ selectedKeys.remove();
+
+ // skip if not valid
+ if (!key.isValid()) {
+ continue;
+ }
+
+ if (key.isAcceptable()) {
+ handleAccept();
+ } else {
+ LOGGER.warn("Unexpected state in select! " + key.interestOps());
+ }
+ }
+ } catch (IOException e) {
+ LOGGER.warn("Got an IOException while selecting!", e);
+ }
+ }
+
+ /**
+ * Accept a new connection.
+ */
+ private void handleAccept() {
+ final TNonblockingTransport client = doAccept();
+ if (client != null) {
+ // Pass this connection to a selector thread
+ final SelectorThread targetThread = threadChooser.nextThread();
+
+ if (args.acceptPolicy == Args.AcceptPolicy.FAST_ACCEPT || invoker == null) {
+ doAddAccept(targetThread, client);
+ } else {
+ // FAIR_ACCEPT
+ try {
+ invoker.submit(new Runnable() {
+ public void run() {
+ doAddAccept(targetThread, client);
+ }
+ });
+ } catch (RejectedExecutionException rx) {
+ LOGGER.warn("ExecutorService rejected accept registration!", rx);
+ // close immediately
+ client.close();
+ }
+ }
+ }
+ }
+
+ private TNonblockingTransport doAccept() {
+ try {
+ return (TNonblockingTransport) serverTransport.accept();
+ } catch (TTransportException tte) {
+ // something went wrong accepting.
+ LOGGER.warn("Exception trying to accept!", tte);
+ return null;
+ }
+ }
+
+ private void doAddAccept(SelectorThread thread, TNonblockingTransport client) {
+ if (!thread.addAcceptedConnection(client)) {
+ client.close();
+ }
+ }
+ } // AcceptThread
+
+ /**
+ * The SelectorThread(s) will be doing all the selecting on accepted active
+ * connections.
+ */
+ protected class SelectorThread extends AbstractSelectThread {
+
+ // Accepted connections added by the accept thread.
+ private final BlockingQueue<TNonblockingTransport> acceptedQueue;
+ private int SELECTOR_AUTO_REBUILD_THRESHOLD = 512;
+ private long MONITOR_PERIOD = 1000L;
+ private int jvmBug = 0;
+
+ /**
+ * Set up the SelectorThread with an unbounded queue for incoming accepts.
+ *
+ * @throws IOException
+ * if a selector cannot be created
+ */
+ public SelectorThread() throws IOException {
+ this(new LinkedBlockingQueue<TNonblockingTransport>());
+ }
+
+ /**
+ * Set up the SelectorThread with an bounded queue for incoming accepts.
+ *
+ * @throws IOException
+ * if a selector cannot be created
+ */
+ public SelectorThread(int maxPendingAccepts) throws IOException {
+ this(createDefaultAcceptQueue(maxPendingAccepts));
+ }
+
+ /**
+ * Set up the SelectorThread with a specified queue for connections.
+ *
+ * @param acceptedQueue
+ * The BlockingQueue implementation for holding incoming accepted
+ * connections.
+ * @throws IOException
+ * if a selector cannot be created.
+ */
+ public SelectorThread(BlockingQueue<TNonblockingTransport> acceptedQueue) throws IOException {
+ this.acceptedQueue = acceptedQueue;
+ }
+
+ /**
+ * Hands off an accepted connection to be handled by this thread. This
+ * method will block if the queue for new connections is at capacity.
+ *
+ * @param accepted
+ * The connection that has been accepted.
+ * @return true if the connection has been successfully added.
+ */
+ public boolean addAcceptedConnection(TNonblockingTransport accepted) {
+ try {
+ acceptedQueue.put(accepted);
+ } catch (InterruptedException e) {
+ LOGGER.warn("Interrupted while adding accepted connection!", e);
+ return false;
+ }
+ selector.wakeup();
+ return true;
+ }
+
+ /**
+ * The work loop. Handles selecting (read/write IO), dispatching, and
+ * managing the selection preferences of all existing connections.
+ */
+ public void run() {
+ try {
+ while (!stopped_) {
+ select();
+ processAcceptedConnections();
+ processInterestChanges();
+ }
+ for (SelectionKey selectionKey : selector.keys()) {
+ cleanupSelectionKey(selectionKey);
+ }
+ } catch (Throwable t) {
+ LOGGER.error("run() on SelectorThread exiting due to uncaught error", t);
+ } finally {
+ try {
+ selector.close();
+ } catch (IOException e) {
+ LOGGER.error("Got an IOException while closing selector!", e);
+ }
+ // This will wake up the accept thread and the other selector threads
+ TThreadedSelectorServer.this.stop();
+ }
+ }
+
+ /**
+ * Select and process IO events appropriately: If there are existing
+ * connections with data waiting to be read, read it, buffering until a
+ * whole frame has been read. If there are any pending responses, buffer
+ * them until their target client is available, and then send the data.
+ */
+ private void select() {
+ try {
+
+ doSelect();
+
+ // process the io events we received
+ Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
+ while (!stopped_ && selectedKeys.hasNext()) {
+ SelectionKey key = selectedKeys.next();
+ selectedKeys.remove();
+
+ // skip if not valid
+ if (!key.isValid()) {
+ cleanupSelectionKey(key);
+ continue;
+ }
+
+ if (key.isReadable()) {
+ // deal with reads
+ handleRead(key);
+ } else if (key.isWritable()) {
+ // deal with writes
+ handleWrite(key);
+ } else {
+ LOGGER.warn("Unexpected state in select! " + key.interestOps());
+ }
+ }
+ } catch (IOException e) {
+ LOGGER.warn("Got an IOException while selecting!", e);
+ }
+ }
+
+ /**
+ * Do select and judge epoll bug happen.
+ * See : https://issues.apache.org/jira/browse/THRIFT-4251
+ */
+ private void doSelect() throws IOException {
+ long beforeSelect = System.currentTimeMillis();
+ int selectedNums = selector.select();
+ long afterSelect = System.currentTimeMillis();
+
+ if (selectedNums == 0) {
+ jvmBug++;
+ } else {
+ jvmBug = 0;
+ }
+
+ long selectedTime = afterSelect - beforeSelect;
+ if (selectedTime >= MONITOR_PERIOD) {
+ jvmBug = 0;
+ } else if (jvmBug > SELECTOR_AUTO_REBUILD_THRESHOLD) {
+ LOGGER.warn("In {} ms happen {} times jvm bug; rebuilding selector.", MONITOR_PERIOD, jvmBug);
+ rebuildSelector();
+ selector.selectNow();
+ jvmBug = 0;
+ }
+
+ }
+
+ /**
+ * Replaces the current Selector of this SelectorThread with newly created Selector to work
+ * around the infamous epoll 100% CPU bug.
+ */
+ private synchronized void rebuildSelector() {
+ final Selector oldSelector = selector;
+ if (oldSelector == null) {
+ return;
+ }
+ Selector newSelector = null;
+ try {
+ newSelector = Selector.open();
+ LOGGER.warn("Created new Selector.");
+ } catch (IOException e) {
+ LOGGER.error("Create new Selector error.", e);
+ }
+
+ for (SelectionKey key : oldSelector.selectedKeys()) {
+ if (!key.isValid() && key.readyOps() == 0)
+ continue;
+ SelectableChannel channel = key.channel();
+ Object attachment = key.attachment();
+
+ try {
+ if (attachment == null) {
+ channel.register(newSelector, key.readyOps());
+ } else {
+ channel.register(newSelector, key.readyOps(), attachment);
+ }
+ } catch (ClosedChannelException e) {
+ LOGGER.error("Register new selector key error.", e);
+ }
+
+ }
+
+ selector = newSelector;
+ try {
+ oldSelector.close();
+ } catch (IOException e) {
+ LOGGER.error("Close old selector error.", e);
+ }
+ LOGGER.warn("Replace new selector success.");
+ }
+
+ private void processAcceptedConnections() {
+ // Register accepted connections
+ while (!stopped_) {
+ TNonblockingTransport accepted = acceptedQueue.poll();
+ if (accepted == null) {
+ break;
+ }
+ registerAccepted(accepted);
+ }
+ }
+
+ protected FrameBuffer createFrameBuffer(final TNonblockingTransport trans,
+ final SelectionKey selectionKey,
+ final AbstractSelectThread selectThread) {
+ return processorFactory_.isAsyncProcessor() ?
+ new AsyncFrameBuffer(trans, selectionKey, selectThread) :
+ new FrameBuffer(trans, selectionKey, selectThread);
+ }
+
+ private void registerAccepted(TNonblockingTransport accepted) {
+ SelectionKey clientKey = null;
+ try {
+ clientKey = accepted.registerSelector(selector, SelectionKey.OP_READ);
+
+ FrameBuffer frameBuffer = createFrameBuffer(accepted, clientKey, SelectorThread.this);
+
+ clientKey.attach(frameBuffer);
+ } catch (IOException e) {
+ LOGGER.warn("Failed to register accepted connection to selector!", e);
+ if (clientKey != null) {
+ cleanupSelectionKey(clientKey);
+ }
+ accepted.close();
+ }
+ }
+ } // SelectorThread
+
+ /**
+ * Creates a SelectorThreadLoadBalancer to be used by the accept thread for
+ * assigning newly accepted connections across the threads.
+ */
+ protected SelectorThreadLoadBalancer createSelectorThreadLoadBalancer(Collection<? extends SelectorThread> threads) {
+ return new SelectorThreadLoadBalancer(threads);
+ }
+
+ /**
+ * A round robin load balancer for choosing selector threads for new
+ * connections.
+ */
+ protected static class SelectorThreadLoadBalancer {
+ private final Collection<? extends SelectorThread> threads;
+ private Iterator<? extends SelectorThread> nextThreadIterator;
+
+ public <T extends SelectorThread> SelectorThreadLoadBalancer(Collection<T> threads) {
+ if (threads.isEmpty()) {
+ throw new IllegalArgumentException("At least one selector thread is required");
+ }
+ this.threads = Collections.unmodifiableList(new ArrayList<T>(threads));
+ nextThreadIterator = this.threads.iterator();
+ }
+
+ public SelectorThread nextThread() {
+ // Choose a selector thread (round robin)
+ if (!nextThreadIterator.hasNext()) {
+ nextThreadIterator = threads.iterator();
+ }
+ return nextThreadIterator.next();
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBuffer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBuffer.java
new file mode 100644
index 000000000..fc3aa92df
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBuffer.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+import java.util.Arrays;
+
+/**
+ * Helper class that wraps a byte[] so that it can expand and be reused. Users
+ * should call resizeIfNecessary to make sure the buffer has suitable capacity,
+ * and then use the array as needed. Note that the internal array will grow at a
+ * rate slightly faster than the requested capacity with the (untested)
+ * objective of avoiding expensive buffer allocations and copies.
+ */
+class AutoExpandingBuffer {
+ private byte[] array;
+
+ public AutoExpandingBuffer(int initialCapacity) {
+ this.array = new byte[initialCapacity];
+ }
+
+ public void resizeIfNecessary(int size) {
+ final int currentCapacity = this.array.length;
+ if (currentCapacity < size) {
+ // Increase by a factor of 1.5x
+ int growCapacity = currentCapacity + (currentCapacity >> 1);
+ int newCapacity = Math.max(growCapacity, size);
+ this.array = Arrays.copyOf(array, newCapacity);
+ }
+ }
+
+ public byte[] array() {
+ return this.array;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java
new file mode 100644
index 000000000..a28d25485
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+/**
+ * TTransport for reading from an AutoExpandingBuffer.
+ */
+public class AutoExpandingBufferReadTransport extends TTransport {
+
+ private final AutoExpandingBuffer buf;
+
+ private int pos = 0;
+ private int limit = 0;
+
+ public AutoExpandingBufferReadTransport(int initialCapacity) {
+ this.buf = new AutoExpandingBuffer(initialCapacity);
+ }
+
+ public void fill(TTransport inTrans, int length) throws TTransportException {
+ buf.resizeIfNecessary(length);
+ inTrans.readAll(buf.array(), 0, length);
+ pos = 0;
+ limit = length;
+ }
+
+ @Override
+ public void close() {}
+
+ @Override
+ public boolean isOpen() { return true; }
+
+ @Override
+ public void open() throws TTransportException {}
+
+ @Override
+ public final int read(byte[] target, int off, int len) throws TTransportException {
+ int amtToRead = Math.min(len, getBytesRemainingInBuffer());
+ System.arraycopy(buf.array(), pos, target, off, amtToRead);
+ consumeBuffer(amtToRead);
+ return amtToRead;
+ }
+
+ @Override
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public final void consumeBuffer(int len) {
+ pos += len;
+ }
+
+ @Override
+ public final byte[] getBuffer() {
+ return buf.array();
+ }
+
+ @Override
+ public final int getBufferPosition() {
+ return pos;
+ }
+
+ @Override
+ public final int getBytesRemainingInBuffer() {
+ return limit - pos;
+ }
+}
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferWriteTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferWriteTransport.java
new file mode 100644
index 000000000..ec7e7d45a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferWriteTransport.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+/**
+ * TTransport for writing to an AutoExpandingBuffer.
+ */
+public final class AutoExpandingBufferWriteTransport extends TTransport {
+
+ private final AutoExpandingBuffer buf;
+ private int pos;
+ private int res;
+
+ /**
+ * Constructor.
+ * @param initialCapacity the initial capacity of the buffer
+ * @param frontReserve space, if any, to reserve at the beginning such
+ * that the first write is after this reserve.
+ * This allows framed transport to reserve space
+ * for the frame buffer length.
+ * @throws IllegalArgumentException if initialCapacity is less than one
+ * @throws IllegalArgumentException if frontReserve is less than zero
+ * @throws IllegalArgumentException if frontReserve is greater than initialCapacity
+ */
+ public AutoExpandingBufferWriteTransport(int initialCapacity, int frontReserve) {
+ if (initialCapacity < 1) {
+ throw new IllegalArgumentException("initialCapacity");
+ }
+ if (frontReserve < 0 || initialCapacity < frontReserve) {
+ throw new IllegalArgumentException("frontReserve");
+ }
+ this.buf = new AutoExpandingBuffer(initialCapacity);
+ this.pos = frontReserve;
+ this.res = frontReserve;
+ }
+
+ @Override
+ public void close() {}
+
+ @Override
+ public boolean isOpen() {return true;}
+
+ @Override
+ public void open() throws TTransportException {}
+
+ @Override
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void write(byte[] toWrite, int off, int len) throws TTransportException {
+ buf.resizeIfNecessary(pos + len);
+ System.arraycopy(toWrite, off, buf.array(), pos, len);
+ pos += len;
+ }
+
+ public AutoExpandingBuffer getBuf() {
+ return buf;
+ }
+
+ /**
+ * @return length of the buffer, including any front reserve
+ */
+ public int getLength() {
+ return pos;
+ }
+
+ public void reset() {
+ pos = res;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TByteBuffer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TByteBuffer.java
new file mode 100644
index 000000000..b6b065748
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TByteBuffer.java
@@ -0,0 +1,87 @@
+package org.apache.thrift.transport;
+
+import java.nio.BufferOverflowException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+/**
+ * ByteBuffer-backed implementation of TTransport.
+ */
+public final class TByteBuffer extends TTransport {
+ private final ByteBuffer byteBuffer;
+
+ /**
+ * Creates a new TByteBuffer wrapping a given NIO ByteBuffer.
+ */
+ public TByteBuffer(ByteBuffer byteBuffer) {
+ this.byteBuffer = byteBuffer;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return true;
+ }
+
+ @Override
+ public void open() {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ final int n = Math.min(byteBuffer.remaining(), len);
+ if (n > 0) {
+ try {
+ byteBuffer.get(buf, off, n);
+ } catch (BufferUnderflowException e) {
+ throw new TTransportException("Unexpected end of input buffer", e);
+ }
+ }
+ return n;
+ }
+
+ @Override
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ try {
+ byteBuffer.put(buf, off, len);
+ } catch (BufferOverflowException e) {
+ throw new TTransportException("Not enough room in output buffer", e);
+ }
+ }
+
+ /**
+ * Get the underlying NIO ByteBuffer.
+ */
+ public ByteBuffer getByteBuffer() {
+ return byteBuffer;
+ }
+
+ /**
+ * Convenience method to call clear() on the underlying NIO ByteBuffer.
+ */
+ public TByteBuffer clear() {
+ byteBuffer.clear();
+ return this;
+ }
+
+ /**
+ * Convenience method to call flip() on the underlying NIO ByteBuffer.
+ */
+ public TByteBuffer flip() {
+ byteBuffer.flip();
+ return this;
+ }
+
+ /**
+ * Convenience method to convert the underlying NIO ByteBuffer to a
+ * plain old byte array.
+ */
+ public byte[] toByteArray() {
+ final byte[] data = new byte[byteBuffer.remaining()];
+ byteBuffer.slice().get(data);
+ return data;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFastFramedTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFastFramedTransport.java
new file mode 100644
index 000000000..a1fd2490a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFastFramedTransport.java
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+/**
+ * This transport is wire compatible with {@link TFramedTransport}, but makes
+ * use of reusable, expanding read and write buffers in order to avoid
+ * allocating new byte[]s all the time. Since the buffers only expand, you
+ * should probably only use this transport if your messages are not too variably
+ * large, unless the persistent memory cost is not an issue.
+ *
+ * This implementation is NOT threadsafe.
+ */
+public class TFastFramedTransport extends TTransport {
+
+ public static class Factory extends TTransportFactory {
+ private final int initialCapacity;
+ private final int maxLength;
+
+ public Factory() {
+ this(DEFAULT_BUF_CAPACITY, DEFAULT_MAX_LENGTH);
+ }
+
+ public Factory(int initialCapacity) {
+ this(initialCapacity, DEFAULT_MAX_LENGTH);
+ }
+
+ public Factory(int initialCapacity, int maxLength) {
+ this.initialCapacity = initialCapacity;
+ this.maxLength = maxLength;
+ }
+
+ @Override
+ public TTransport getTransport(TTransport trans) {
+ return new TFastFramedTransport(trans,
+ initialCapacity,
+ maxLength);
+ }
+ }
+
+ /**
+ * How big should the default read and write buffers be?
+ */
+ public static final int DEFAULT_BUF_CAPACITY = 1024;
+ /**
+ * How big is the largest allowable frame? Defaults to 16MB.
+ */
+ public static final int DEFAULT_MAX_LENGTH = 16384000;
+
+ private final TTransport underlying;
+ private final AutoExpandingBufferWriteTransport writeBuffer;
+ private AutoExpandingBufferReadTransport readBuffer;
+ private final int initialBufferCapacity;
+ private final byte[] i32buf = new byte[4];
+ private final int maxLength;
+
+ /**
+ * Create a new {@link TFastFramedTransport}. Use the defaults
+ * for initial buffer size and max frame length.
+ * @param underlying Transport that real reads and writes will go through to.
+ */
+ public TFastFramedTransport(TTransport underlying) {
+ this(underlying, DEFAULT_BUF_CAPACITY, DEFAULT_MAX_LENGTH);
+ }
+
+ /**
+ * Create a new {@link TFastFramedTransport}. Use the specified
+ * initial buffer capacity and the default max frame length.
+ * @param underlying Transport that real reads and writes will go through to.
+ * @param initialBufferCapacity The initial size of the read and write buffers.
+ * In practice, it's not critical to set this unless you know in advance that
+ * your messages are going to be very large.
+ */
+ public TFastFramedTransport(TTransport underlying, int initialBufferCapacity) {
+ this(underlying, initialBufferCapacity, DEFAULT_MAX_LENGTH);
+ }
+
+ /**
+ *
+ * @param underlying Transport that real reads and writes will go through to.
+ * @param initialBufferCapacity The initial size of the read and write buffers.
+ * In practice, it's not critical to set this unless you know in advance that
+ * your messages are going to be very large. (You can pass
+ * TFramedTransportWithReusableBuffer.DEFAULT_BUF_CAPACITY if you're only
+ * using this constructor because you want to set the maxLength.)
+ * @param maxLength The max frame size you are willing to read. You can use
+ * this parameter to limit how much memory can be allocated.
+ */
+ public TFastFramedTransport(TTransport underlying, int initialBufferCapacity, int maxLength) {
+ this.underlying = underlying;
+ this.maxLength = maxLength;
+ this.initialBufferCapacity = initialBufferCapacity;
+ readBuffer = new AutoExpandingBufferReadTransport(initialBufferCapacity);
+ writeBuffer = new AutoExpandingBufferWriteTransport(initialBufferCapacity, 4);
+ }
+
+ @Override
+ public void close() {
+ underlying.close();
+ }
+
+ @Override
+ public boolean isOpen() {
+ return underlying.isOpen();
+ }
+
+ @Override
+ public void open() throws TTransportException {
+ underlying.open();
+ }
+
+ @Override
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ int got = readBuffer.read(buf, off, len);
+ if (got > 0) {
+ return got;
+ }
+
+ // Read another frame of data
+ readFrame();
+
+ return readBuffer.read(buf, off, len);
+ }
+
+ private void readFrame() throws TTransportException {
+ underlying.readAll(i32buf , 0, 4);
+ int size = TFramedTransport.decodeFrameSize(i32buf);
+
+ if (size < 0) {
+ close();
+ throw new TTransportException(TTransportException.CORRUPTED_DATA, "Read a negative frame size (" + size + ")!");
+ }
+
+ if (size > maxLength) {
+ close();
+ throw new TTransportException(TTransportException.CORRUPTED_DATA,
+ "Frame size (" + size + ") larger than max length (" + maxLength + ")!");
+ }
+
+ readBuffer.fill(underlying, size);
+ }
+
+ @Override
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ writeBuffer.write(buf, off, len);
+ }
+
+ @Override
+ public void consumeBuffer(int len) {
+ readBuffer.consumeBuffer(len);
+ }
+
+ /**
+ * Only clears the read buffer!
+ */
+ public void clear() {
+ readBuffer = new AutoExpandingBufferReadTransport(initialBufferCapacity);
+ }
+
+ @Override
+ public void flush() throws TTransportException {
+ int payloadLength = writeBuffer.getLength() - 4;
+ byte[] data = writeBuffer.getBuf().array();
+ TFramedTransport.encodeFrameSize(payloadLength, data);
+ underlying.write(data, 0, payloadLength + 4);
+ writeBuffer.reset();
+ underlying.flush();
+ }
+
+ @Override
+ public byte[] getBuffer() {
+ return readBuffer.getBuffer();
+ }
+
+ @Override
+ public int getBufferPosition() {
+ return readBuffer.getBufferPosition();
+ }
+
+ @Override
+ public int getBytesRemainingInBuffer() {
+ return readBuffer.getBytesRemainingInBuffer();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileProcessor.java
new file mode 100644
index 000000000..96087d1a3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileProcessor.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.TException;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+
+/**
+ * FileProcessor: helps in processing files generated by TFileTransport.
+ * Port of original cpp implementation
+ */
+public class TFileProcessor {
+
+ private TProcessor processor_;
+ private TProtocolFactory inputProtocolFactory_;
+ private TProtocolFactory outputProtocolFactory_;
+ private TFileTransport inputTransport_;
+ private TTransport outputTransport_;
+
+ public TFileProcessor(TProcessor processor, TProtocolFactory protocolFactory,
+ TFileTransport inputTransport,
+ TTransport outputTransport) {
+ processor_ = processor;
+ inputProtocolFactory_ = outputProtocolFactory_ = protocolFactory;
+ inputTransport_ = inputTransport;
+ outputTransport_ = outputTransport;
+ }
+
+ public TFileProcessor(TProcessor processor,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ TFileTransport inputTransport,
+ TTransport outputTransport) {
+ processor_ = processor;
+ inputProtocolFactory_ = inputProtocolFactory;
+ outputProtocolFactory_ = outputProtocolFactory;
+ inputTransport_ = inputTransport;
+ outputTransport_ = outputTransport;
+ }
+
+ private void processUntil(int lastChunk) throws TException {
+ TProtocol ip = inputProtocolFactory_.getProtocol(inputTransport_);
+ TProtocol op = outputProtocolFactory_.getProtocol(outputTransport_);
+ int curChunk = inputTransport_.getCurChunk();
+
+ try {
+ while (lastChunk >= curChunk) {
+ processor_.process(ip, op);
+ int newChunk = inputTransport_.getCurChunk();
+ curChunk = newChunk;
+ }
+ } catch (TTransportException e) {
+ // if we are processing the last chunk - we could have just hit EOF
+ // on EOF - trap the error and stop processing.
+ if(e.getType() != TTransportException.END_OF_FILE)
+ throw e;
+ else {
+ return;
+ }
+ }
+ }
+
+ /**
+ * Process from start to last chunk both inclusive where chunks begin from 0
+
+ * @param startChunkNum first chunk to be processed
+ * @param endChunkNum last chunk to be processed
+ */
+ public void processChunk(int startChunkNum, int endChunkNum) throws TException {
+ int numChunks = inputTransport_.getNumChunks();
+ if(endChunkNum < 0)
+ endChunkNum += numChunks;
+
+ if(startChunkNum < 0)
+ startChunkNum += numChunks;
+
+ if(endChunkNum < startChunkNum)
+ throw new TException("endChunkNum " + endChunkNum + " is less than " + startChunkNum);
+
+ inputTransport_.seekToChunk(startChunkNum);
+ processUntil(endChunkNum);
+ }
+
+ /**
+ * Process a single chunk
+ *
+ * @param chunkNum chunk to be processed
+ */
+ public void processChunk(int chunkNum) throws TException {
+ processChunk(chunkNum, chunkNum);
+ }
+
+ /**
+ * Process a current chunk
+ */
+ public void processChunk() throws TException {
+ processChunk(inputTransport_.getCurChunk());
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileTransport.java
new file mode 100644
index 000000000..88b73e54d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileTransport.java
@@ -0,0 +1,624 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.util.Random;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * FileTransport implementation of the TTransport interface.
+ * Currently this is a straightforward port of the cpp implementation
+ *
+ * It may make better sense to provide a basic stream access on top of the framed file format
+ * The FileTransport can then be a user of this framed file format with some additional logic
+ * for chunking.
+ */
+public class TFileTransport extends TTransport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TFileTransport.class.getName());
+
+ public static class TruncableBufferedInputStream extends BufferedInputStream {
+ public void trunc() {
+ pos = count = 0;
+ }
+ public TruncableBufferedInputStream(InputStream in) {
+ super(in);
+ }
+ public TruncableBufferedInputStream(InputStream in, int size) {
+ super(in, size);
+ }
+ }
+
+
+ public static class Event {
+ private byte[] buf_;
+ private int nread_;
+ private int navailable_;
+
+ /**
+ * Initialize an event. Initially, it has no valid contents
+ *
+ * @param buf byte array buffer to store event
+ */
+ public Event(byte[] buf) {
+ buf_ = buf;
+ nread_ = navailable_ = 0;
+ }
+
+ public byte[] getBuf() { return buf_;}
+ public int getSize() { return buf_.length; }
+
+
+ public void setAvailable(int sz) { nread_ = 0; navailable_=sz;}
+ public int getRemaining() { return (navailable_ - nread_); }
+
+ public int emit(byte[] buf, int offset, int ndesired) {
+ if((ndesired == 0) || (ndesired > getRemaining()))
+ ndesired = getRemaining();
+
+ if(ndesired <= 0)
+ return (ndesired);
+
+ System.arraycopy(buf_, nread_, buf, offset, ndesired);
+ nread_ += ndesired;
+
+ return(ndesired);
+ }
+ };
+
+ public static class ChunkState {
+ /**
+ * Chunk Size. Must be same across all implementations
+ */
+ public static final int DEFAULT_CHUNK_SIZE = 16 * 1024 * 1024;
+
+ private int chunk_size_ = DEFAULT_CHUNK_SIZE;
+ private long offset_ = 0;
+
+ public ChunkState() {}
+ public ChunkState(int chunk_size) { chunk_size_ = chunk_size; }
+
+ public void skip(int size) {offset_ += size; }
+ public void seek(long offset) {offset_ = offset;}
+
+ public int getChunkSize() { return chunk_size_;}
+ public int getChunkNum() { return ((int)(offset_/chunk_size_));}
+ public int getRemaining() { return (chunk_size_ - ((int)(offset_ % chunk_size_)));}
+ public long getOffset() { return (offset_);}
+ }
+
+ public static enum TailPolicy {
+
+ NOWAIT(0, 0),
+ WAIT_FOREVER(500, -1);
+
+ /**
+ * Time in milliseconds to sleep before next read
+ * If 0, no sleep
+ */
+ public final int timeout_;
+
+ /**
+ * Number of retries before giving up
+ * if 0, no retries
+ * if -1, retry forever
+ */
+ public final int retries_;
+
+ /**
+ * ctor for policy
+ *
+ * @param timeout sleep time for this particular policy
+ * @param retries number of retries
+ */
+
+ TailPolicy(int timeout, int retries) {
+ timeout_ = timeout;
+ retries_ = retries;
+ }
+ }
+
+ /**
+ * Current tailing policy
+ */
+ TailPolicy currentPolicy_ = TailPolicy.NOWAIT;
+
+
+ /**
+ * Underlying file being read
+ */
+ protected TSeekableFile inputFile_ = null;
+
+ /**
+ * Underlying outputStream
+ */
+ protected OutputStream outputStream_ = null;
+
+
+ /**
+ * Event currently read in
+ */
+ Event currentEvent_ = null;
+
+ /**
+ * InputStream currently being used for reading
+ */
+ InputStream inputStream_ = null;
+
+ /**
+ * current Chunk state
+ */
+ ChunkState cs = null;
+
+ /**
+ * is read only?
+ */
+ private boolean readOnly_ = false;
+
+ /**
+ * Get File Tailing Policy
+ *
+ * @return current read policy
+ */
+ public TailPolicy getTailPolicy() {
+ return (currentPolicy_);
+ }
+
+ /**
+ * Set file Tailing Policy
+ *
+ * @param policy New policy to set
+ * @return Old policy
+ */
+ public TailPolicy setTailPolicy(TailPolicy policy) {
+ TailPolicy old = currentPolicy_;
+ currentPolicy_ = policy;
+ return (old);
+ }
+
+
+ /**
+ * Initialize read input stream
+ *
+ * @return input stream to read from file
+ */
+ private InputStream createInputStream() throws TTransportException {
+ InputStream is;
+ try {
+ if(inputStream_ != null) {
+ ((TruncableBufferedInputStream)inputStream_).trunc();
+ is = inputStream_;
+ } else {
+ is = new TruncableBufferedInputStream(inputFile_.getInputStream());
+ }
+ } catch (IOException iox) {
+ throw new TTransportException(iox.getMessage(), iox);
+ }
+ return(is);
+ }
+
+ /**
+ * Read (potentially tailing) an input stream
+ *
+ * @param is InputStream to read from
+ * @param buf Buffer to read into
+ * @param off Offset in buffer to read into
+ * @param len Number of bytes to read
+ * @param tp policy to use if we hit EOF
+ *
+ * @return number of bytes read
+ */
+ private int tailRead(InputStream is, byte[] buf,
+ int off, int len, TailPolicy tp) throws TTransportException {
+ int orig_len = len;
+ try {
+ int retries = 0;
+ while(len > 0) {
+ int cnt = is.read(buf, off, len);
+ if(cnt > 0) {
+ off += cnt;
+ len -= cnt;
+ retries = 0;
+ cs.skip(cnt); // remember that we read so many bytes
+ } else if (cnt == -1) {
+ // EOF
+ retries++;
+
+ if((tp.retries_ != -1) && tp.retries_ < retries)
+ return (orig_len - len);
+
+ if(tp.timeout_ > 0) {
+ try {Thread.sleep(tp.timeout_);} catch(InterruptedException e) {}
+ }
+ } else {
+ // either non-zero or -1 is what the contract says!
+ throw new
+ TTransportException("Unexpected return from InputStream.read = "
+ + cnt);
+ }
+ }
+ } catch (IOException iox) {
+ throw new TTransportException(iox.getMessage(), iox);
+ }
+
+ return(orig_len - len);
+ }
+
+ /**
+ * Event is corrupted. Do recovery
+ *
+ * @return true if recovery could be performed and we can read more data
+ * false is returned only when nothing more can be read
+ */
+ private boolean performRecovery() throws TTransportException {
+ int numChunks = getNumChunks();
+ int curChunk = cs.getChunkNum();
+
+ if(curChunk >= (numChunks-1)) {
+ return false;
+ }
+ seekToChunk(curChunk+1);
+ return true;
+ }
+
+ /**
+ * Read event from underlying file
+ *
+ * @return true if event could be read, false otherwise (on EOF)
+ */
+ private boolean readEvent() throws TTransportException {
+ byte[] ebytes = new byte[4];
+ int esize;
+ int nread;
+ int nrequested;
+
+ retry:
+ do {
+ // corner case. read to end of chunk
+ nrequested = cs.getRemaining();
+ if(nrequested < 4) {
+ nread = tailRead(inputStream_, ebytes, 0, nrequested, currentPolicy_);
+ if(nread != nrequested) {
+ return(false);
+ }
+ }
+
+ // assuming serialized on little endian machine
+ nread = tailRead(inputStream_, ebytes, 0, 4, currentPolicy_);
+ if(nread != 4) {
+ return(false);
+ }
+
+ esize=0;
+ for(int i=3; i>=0; i--) {
+ int val = (0x000000ff & (int)ebytes[i]);
+ esize |= (val << (i*8));
+ }
+
+ // check if event is corrupted and do recovery as required
+ if(esize > cs.getRemaining()) {
+ throw new TTransportException("FileTransport error: bad event size");
+ /*
+ if(performRecovery()) {
+ esize=0;
+ } else {
+ return false;
+ }
+ */
+ }
+ } while (esize == 0);
+
+ // reset existing event or get a larger one
+ if(currentEvent_.getSize() < esize)
+ currentEvent_ = new Event(new byte [esize]);
+
+ // populate the event
+ byte[] buf = currentEvent_.getBuf();
+ nread = tailRead(inputStream_, buf, 0, esize, currentPolicy_);
+ if(nread != esize) {
+ return(false);
+ }
+ currentEvent_.setAvailable(esize);
+ return(true);
+ }
+
+ /**
+ * open if both input/output open unless readonly
+ *
+ * @return true
+ */
+ public boolean isOpen() {
+ return ((inputStream_ != null) && (readOnly_ || (outputStream_ != null)));
+ }
+
+
+ /**
+ * Diverging from the cpp model and sticking to the TSocket model
+ * Files are not opened in ctor - but in explicit open call
+ */
+ public void open() throws TTransportException {
+ if (isOpen())
+ throw new TTransportException(TTransportException.ALREADY_OPEN);
+
+ try {
+ inputStream_ = createInputStream();
+ cs = new ChunkState();
+ currentEvent_ = new Event(new byte [256]);
+
+ if(!readOnly_)
+ outputStream_ = new BufferedOutputStream(inputFile_.getOutputStream());
+ } catch (IOException iox) {
+ throw new TTransportException(TTransportException.NOT_OPEN, iox);
+ }
+ }
+
+ /**
+ * Closes the transport.
+ */
+ public void close() {
+ if (inputFile_ != null) {
+ try {
+ inputFile_.close();
+ } catch (IOException iox) {
+ LOGGER.warn("WARNING: Error closing input file: " +
+ iox.getMessage());
+ }
+ inputFile_ = null;
+ }
+ if (outputStream_ != null) {
+ try {
+ outputStream_.close();
+ } catch (IOException iox) {
+ LOGGER.warn("WARNING: Error closing output stream: " +
+ iox.getMessage());
+ }
+ outputStream_ = null;
+ }
+ }
+
+
+ /**
+ * File Transport ctor
+ *
+ * @param path File path to read and write from
+ * @param readOnly Whether this is a read-only transport
+ */
+ public TFileTransport(final String path, boolean readOnly) throws IOException {
+ inputFile_ = new TStandardFile(path);
+ readOnly_ = readOnly;
+ }
+
+ /**
+ * File Transport ctor
+ *
+ * @param inputFile open TSeekableFile to read/write from
+ * @param readOnly Whether this is a read-only transport
+ */
+ public TFileTransport(TSeekableFile inputFile, boolean readOnly) {
+ inputFile_ = inputFile;
+ readOnly_ = readOnly;
+ }
+
+
+ /**
+ * Cloned from TTransport.java:readAll(). Only difference is throwing an EOF exception
+ * where one is detected.
+ */
+ public int readAll(byte[] buf, int off, int len)
+ throws TTransportException {
+ int got = 0;
+ int ret = 0;
+ while (got < len) {
+ ret = read(buf, off+got, len-got);
+ if (ret < 0) {
+ throw new TTransportException("Error in reading from file");
+ }
+ if(ret == 0) {
+ throw new TTransportException(TTransportException.END_OF_FILE,
+ "End of File reached");
+ }
+ got += ret;
+ }
+ return got;
+ }
+
+
+ /**
+ * Reads up to len bytes into buffer buf, starting at offset off.
+ *
+ * @param buf Array to read into
+ * @param off Index to start reading at
+ * @param len Maximum number of bytes to read
+ * @return The number of bytes actually read
+ * @throws TTransportException if there was an error reading data
+ */
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ if(!isOpen())
+ throw new TTransportException(TTransportException.NOT_OPEN,
+ "Must open before reading");
+
+ if(currentEvent_.getRemaining() == 0) {
+ if(!readEvent())
+ return(0);
+ }
+
+ int nread = currentEvent_.emit(buf, off, len);
+ return nread;
+ }
+
+ public int getNumChunks() throws TTransportException {
+ if(!isOpen())
+ throw new TTransportException(TTransportException.NOT_OPEN,
+ "Must open before getNumChunks");
+ try {
+ long len = inputFile_.length();
+ if(len == 0)
+ return 0;
+ else
+ return (((int)(len/cs.getChunkSize())) + 1);
+
+ } catch (IOException iox) {
+ throw new TTransportException(iox.getMessage(), iox);
+ }
+ }
+
+ public int getCurChunk() throws TTransportException {
+ if(!isOpen())
+ throw new TTransportException(TTransportException.NOT_OPEN,
+ "Must open before getCurChunk");
+ return (cs.getChunkNum());
+
+ }
+
+
+ public void seekToChunk(int chunk) throws TTransportException {
+ if(!isOpen())
+ throw new TTransportException(TTransportException.NOT_OPEN,
+ "Must open before seeking");
+
+ int numChunks = getNumChunks();
+
+ // file is empty, seeking to chunk is pointless
+ if (numChunks == 0) {
+ return;
+ }
+
+ // negative indicates reverse seek (from the end)
+ if (chunk < 0) {
+ chunk += numChunks;
+ }
+
+ // too large a value for reverse seek, just seek to beginning
+ if (chunk < 0) {
+ chunk = 0;
+ }
+
+ long eofOffset=0;
+ boolean seekToEnd = (chunk >= numChunks);
+ if(seekToEnd) {
+ chunk = chunk - 1;
+ try { eofOffset = inputFile_.length(); }
+ catch (IOException iox) {throw new TTransportException(iox.getMessage(),
+ iox);}
+ }
+
+ if(chunk*cs.getChunkSize() != cs.getOffset()) {
+ try { inputFile_.seek((long)chunk*cs.getChunkSize()); }
+ catch (IOException iox) {
+ throw new TTransportException("Seek to chunk " +
+ chunk + " " +iox.getMessage(), iox);
+ }
+
+ cs.seek((long)chunk*cs.getChunkSize());
+ currentEvent_.setAvailable(0);
+ inputStream_ = createInputStream();
+ }
+
+ if(seekToEnd) {
+ // waiting forever here - otherwise we can hit EOF and end up
+ // having consumed partial data from the data stream.
+ TailPolicy old = setTailPolicy(TailPolicy.WAIT_FOREVER);
+ while(cs.getOffset() < eofOffset) { readEvent(); }
+ currentEvent_.setAvailable(0);
+ setTailPolicy(old);
+ }
+ }
+
+ public void seekToEnd() throws TTransportException {
+ if(!isOpen())
+ throw new TTransportException(TTransportException.NOT_OPEN,
+ "Must open before seeking");
+ seekToChunk(getNumChunks());
+ }
+
+
+ /**
+ * Writes up to len bytes from the buffer.
+ *
+ * @param buf The output data buffer
+ * @param off The offset to start writing from
+ * @param len The number of bytes to write
+ * @throws TTransportException if there was an error writing data
+ */
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ throw new TTransportException("Not Supported");
+ }
+
+ /**
+ * Flush any pending data out of a transport buffer.
+ *
+ * @throws TTransportException if there was an error writing out data.
+ */
+ public void flush() throws TTransportException {
+ throw new TTransportException("Not Supported");
+ }
+
+ /**
+ * test program
+ *
+ */
+ public static void main(String[] args) throws Exception {
+
+ int num_chunks = 10;
+
+ if((args.length < 1) || args[0].equals("--help")
+ || args[0].equals("-h") || args[0].equals("-?")) {
+ printUsage();
+ }
+
+ if(args.length > 1) {
+ try {
+ num_chunks = Integer.parseInt(args[1]);
+ } catch (Exception e) {
+ LOGGER.error("Cannot parse " + args[1]);
+ printUsage();
+ }
+ }
+
+ TFileTransport t = new TFileTransport(args[0], true);
+ t.open();
+ LOGGER.info("NumChunks="+t.getNumChunks());
+
+ Random r = new Random();
+ for(int j=0; j<num_chunks; j++) {
+ byte[] buf = new byte[4096];
+ int cnum = r.nextInt(t.getNumChunks()-1);
+ LOGGER.info("Reading chunk "+cnum);
+ t.seekToChunk(cnum);
+ for(int i=0; i<4096; i++) {
+ t.read(buf, 0, 4096);
+ }
+ }
+ }
+
+ private static void printUsage() {
+ LOGGER.error("Usage: TFileTransport <filename> [num_chunks]");
+ LOGGER.error(" (Opens and reads num_chunks chunks from file randomly)");
+ System.exit(1);
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFramedTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFramedTransport.java
new file mode 100644
index 000000000..a006c3a6a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFramedTransport.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.apache.thrift.TByteArrayOutputStream;
+
+/**
+ * TFramedTransport is a buffered TTransport that ensures a fully read message
+ * every time by preceding messages with a 4-byte frame size.
+ */
+public class TFramedTransport extends TTransport {
+
+ protected static final int DEFAULT_MAX_LENGTH = 16384000;
+
+ private int maxLength_;
+
+ /**
+ * Underlying transport
+ */
+ private TTransport transport_ = null;
+
+ /**
+ * Buffer for output
+ */
+ private final TByteArrayOutputStream writeBuffer_ =
+ new TByteArrayOutputStream(1024);
+
+ /**
+ * Buffer for input
+ */
+ private final TMemoryInputTransport readBuffer_ =
+ new TMemoryInputTransport(new byte[0]);
+
+ public static class Factory extends TTransportFactory {
+ private int maxLength_;
+
+ public Factory() {
+ maxLength_ = TFramedTransport.DEFAULT_MAX_LENGTH;
+ }
+
+ public Factory(int maxLength) {
+ maxLength_ = maxLength;
+ }
+
+ @Override
+ public TTransport getTransport(TTransport base) {
+ return new TFramedTransport(base, maxLength_);
+ }
+ }
+
+ /**
+ * Something to fill in the first four bytes of the buffer
+ * to make room for the frame size. This allows the
+ * implementation to write once instead of twice.
+ */
+ private static final byte[] sizeFiller_ = new byte[] { 0x00, 0x00, 0x00, 0x00 };
+
+ /**
+ * Constructor wraps around another transport
+ */
+ public TFramedTransport(TTransport transport, int maxLength) {
+ transport_ = transport;
+ maxLength_ = maxLength;
+ writeBuffer_.write(sizeFiller_, 0, 4);
+ }
+
+ public TFramedTransport(TTransport transport) {
+ transport_ = transport;
+ maxLength_ = TFramedTransport.DEFAULT_MAX_LENGTH;
+ writeBuffer_.write(sizeFiller_, 0, 4);
+ }
+
+ public void open() throws TTransportException {
+ transport_.open();
+ }
+
+ public boolean isOpen() {
+ return transport_.isOpen();
+ }
+
+ public void close() {
+ transport_.close();
+ }
+
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ int got = readBuffer_.read(buf, off, len);
+ if (got > 0) {
+ return got;
+ }
+
+ // Read another frame of data
+ readFrame();
+
+ return readBuffer_.read(buf, off, len);
+ }
+
+ @Override
+ public byte[] getBuffer() {
+ return readBuffer_.getBuffer();
+ }
+
+ @Override
+ public int getBufferPosition() {
+ return readBuffer_.getBufferPosition();
+ }
+
+ @Override
+ public int getBytesRemainingInBuffer() {
+ return readBuffer_.getBytesRemainingInBuffer();
+ }
+
+ @Override
+ public void consumeBuffer(int len) {
+ readBuffer_.consumeBuffer(len);
+ }
+
+ public void clear() {
+ readBuffer_.clear();
+ }
+
+ private final byte[] i32buf = new byte[4];
+
+ private void readFrame() throws TTransportException {
+ transport_.readAll(i32buf, 0, 4);
+ int size = decodeFrameSize(i32buf);
+
+ if (size < 0) {
+ close();
+ throw new TTransportException(TTransportException.CORRUPTED_DATA, "Read a negative frame size (" + size + ")!");
+ }
+
+ if (size > maxLength_) {
+ close();
+ throw new TTransportException(TTransportException.CORRUPTED_DATA,
+ "Frame size (" + size + ") larger than max length (" + maxLength_ + ")!");
+ }
+
+ byte[] buff = new byte[size];
+ transport_.readAll(buff, 0, size);
+ readBuffer_.reset(buff);
+ }
+
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ writeBuffer_.write(buf, off, len);
+ }
+
+ @Override
+ public void flush() throws TTransportException {
+ byte[] buf = writeBuffer_.get();
+ int len = writeBuffer_.len() - 4; // account for the prepended frame size
+ writeBuffer_.reset();
+ writeBuffer_.write(sizeFiller_, 0, 4); // make room for the next frame's size data
+
+ encodeFrameSize(len, buf); // this is the frame length without the filler
+ transport_.write(buf, 0, len + 4); // we have to write the frame size and frame data
+ transport_.flush();
+ }
+
+ public static final void encodeFrameSize(final int frameSize, final byte[] buf) {
+ buf[0] = (byte)(0xff & (frameSize >> 24));
+ buf[1] = (byte)(0xff & (frameSize >> 16));
+ buf[2] = (byte)(0xff & (frameSize >> 8));
+ buf[3] = (byte)(0xff & (frameSize));
+ }
+
+ public static final int decodeFrameSize(final byte[] buf) {
+ return
+ ((buf[0] & 0xff) << 24) |
+ ((buf[1] & 0xff) << 16) |
+ ((buf[2] & 0xff) << 8) |
+ ((buf[3] & 0xff));
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/THttpClient.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/THttpClient.java
new file mode 100644
index 000000000..c3063fe43
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/THttpClient.java
@@ -0,0 +1,362 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+import java.net.URL;
+import java.net.HttpURLConnection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.params.CoreConnectionPNames;
+
+/**
+ * HTTP implementation of the TTransport interface. Used for working with a
+ * Thrift web services implementation (using for example TServlet).
+ *
+ * This class offers two implementations of the HTTP transport.
+ * One uses HttpURLConnection instances, the other HttpClient from Apache
+ * Http Components.
+ * The chosen implementation depends on the constructor used to
+ * create the THttpClient instance.
+ * Using the THttpClient(String url) constructor or passing null as the
+ * HttpClient to THttpClient(String url, HttpClient client) will create an
+ * instance which will use HttpURLConnection.
+ *
+ * When using HttpClient, the following configuration leads to 5-15%
+ * better performance than the HttpURLConnection implementation:
+ *
+ * http.protocol.version=HttpVersion.HTTP_1_1
+ * http.protocol.content-charset=UTF-8
+ * http.protocol.expect-continue=false
+ * http.connection.stalecheck=false
+ *
+ * Also note that under high load, the HttpURLConnection implementation
+ * may exhaust the open file descriptor limit.
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/THRIFT-970">THRIFT-970</a>
+ */
+
+public class THttpClient extends TTransport {
+
+ private URL url_ = null;
+
+ private final ByteArrayOutputStream requestBuffer_ = new ByteArrayOutputStream();
+
+ private InputStream inputStream_ = null;
+
+ private int connectTimeout_ = 0;
+
+ private int readTimeout_ = 0;
+
+ private Map<String,String> customHeaders_ = null;
+
+ private final HttpHost host;
+
+ private final HttpClient client;
+
+ public static class Factory extends TTransportFactory {
+
+ private final String url;
+ private final HttpClient client;
+
+ public Factory(String url) {
+ this.url = url;
+ this.client = null;
+ }
+
+ public Factory(String url, HttpClient client) {
+ this.url = url;
+ this.client = client;
+ }
+
+ @Override
+ public TTransport getTransport(TTransport trans) {
+ try {
+ if (null != client) {
+ return new THttpClient(url, client);
+ } else {
+ return new THttpClient(url);
+ }
+ } catch (TTransportException tte) {
+ return null;
+ }
+ }
+ }
+
+ public THttpClient(String url) throws TTransportException {
+ try {
+ url_ = new URL(url);
+ this.client = null;
+ this.host = null;
+ } catch (IOException iox) {
+ throw new TTransportException(iox);
+ }
+ }
+
+ public THttpClient(String url, HttpClient client) throws TTransportException {
+ try {
+ url_ = new URL(url);
+ this.client = client;
+ this.host = new HttpHost(url_.getHost(), -1 == url_.getPort() ? url_.getDefaultPort() : url_.getPort(), url_.getProtocol());
+ } catch (IOException iox) {
+ throw new TTransportException(iox);
+ }
+ }
+
+ public void setConnectTimeout(int timeout) {
+ connectTimeout_ = timeout;
+ if (null != this.client) {
+ // WARNING, this modifies the HttpClient params, this might have an impact elsewhere if the
+ // same HttpClient is used for something else.
+ client.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, connectTimeout_);
+ }
+ }
+
+ public void setReadTimeout(int timeout) {
+ readTimeout_ = timeout;
+ if (null != this.client) {
+ // WARNING, this modifies the HttpClient params, this might have an impact elsewhere if the
+ // same HttpClient is used for something else.
+ client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, readTimeout_);
+ }
+ }
+
+ public void setCustomHeaders(Map<String,String> headers) {
+ customHeaders_ = headers;
+ }
+
+ public void setCustomHeader(String key, String value) {
+ if (customHeaders_ == null) {
+ customHeaders_ = new HashMap<String, String>();
+ }
+ customHeaders_.put(key, value);
+ }
+
+ public void open() {}
+
+ public void close() {
+ if (null != inputStream_) {
+ try {
+ inputStream_.close();
+ } catch (IOException ioe) {
+ ;
+ }
+ inputStream_ = null;
+ }
+ }
+
+ public boolean isOpen() {
+ return true;
+ }
+
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ if (inputStream_ == null) {
+ throw new TTransportException("Response buffer is empty, no request.");
+ }
+ try {
+ int ret = inputStream_.read(buf, off, len);
+ if (ret == -1) {
+ throw new TTransportException("No more data available.");
+ }
+ return ret;
+ } catch (IOException iox) {
+ throw new TTransportException(iox);
+ }
+ }
+
+ public void write(byte[] buf, int off, int len) {
+ requestBuffer_.write(buf, off, len);
+ }
+
+ /**
+ * copy from org.apache.http.util.EntityUtils#consume. Android has it's own httpcore
+ * that doesn't have a consume.
+ */
+ private static void consume(final HttpEntity entity) throws IOException {
+ if (entity == null) {
+ return;
+ }
+ if (entity.isStreaming()) {
+ InputStream instream = entity.getContent();
+ if (instream != null) {
+ instream.close();
+ }
+ }
+ }
+
+ private void flushUsingHttpClient() throws TTransportException {
+
+ if (null == this.client) {
+ throw new TTransportException("Null HttpClient, aborting.");
+ }
+
+ // Extract request and reset buffer
+ byte[] data = requestBuffer_.toByteArray();
+ requestBuffer_.reset();
+
+ HttpPost post = null;
+
+ InputStream is = null;
+
+ try {
+ // Set request to path + query string
+ post = new HttpPost(this.url_.getFile());
+
+ //
+ // Headers are added to the HttpPost instance, not
+ // to HttpClient.
+ //
+
+ post.setHeader("Content-Type", "application/x-thrift");
+ post.setHeader("Accept", "application/x-thrift");
+ post.setHeader("User-Agent", "Java/THttpClient/HC");
+
+ if (null != customHeaders_) {
+ for (Map.Entry<String, String> header : customHeaders_.entrySet()) {
+ post.setHeader(header.getKey(), header.getValue());
+ }
+ }
+
+ post.setEntity(new ByteArrayEntity(data));
+
+ HttpResponse response = this.client.execute(this.host, post);
+ int responseCode = response.getStatusLine().getStatusCode();
+
+ //
+ // Retrieve the inputstream BEFORE checking the status code so
+ // resources get freed in the finally clause.
+ //
+
+ is = response.getEntity().getContent();
+
+ if (responseCode != HttpStatus.SC_OK) {
+ throw new TTransportException("HTTP Response code: " + responseCode);
+ }
+
+ // Read the responses into a byte array so we can release the connection
+ // early. This implies that the whole content will have to be read in
+ // memory, and that momentarily we might use up twice the memory (while the
+ // thrift struct is being read up the chain).
+ // Proceeding differently might lead to exhaustion of connections and thus
+ // to app failure.
+
+ byte[] buf = new byte[1024];
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ int len = 0;
+ do {
+ len = is.read(buf);
+ if (len > 0) {
+ baos.write(buf, 0, len);
+ }
+ } while (-1 != len);
+
+ try {
+ // Indicate we're done with the content.
+ consume(response.getEntity());
+ } catch (IOException ioe) {
+ // We ignore this exception, it might only mean the server has no
+ // keep-alive capability.
+ }
+
+ inputStream_ = new ByteArrayInputStream(baos.toByteArray());
+ } catch (IOException ioe) {
+ // Abort method so the connection gets released back to the connection manager
+ if (null != post) {
+ post.abort();
+ }
+ throw new TTransportException(ioe);
+ } finally {
+ if (null != is) {
+ // Close the entity's input stream, this will release the underlying connection
+ try {
+ is.close();
+ } catch (IOException ioe) {
+ throw new TTransportException(ioe);
+ }
+ }
+ if (post != null) {
+ post.releaseConnection();
+ }
+ }
+ }
+
+ public void flush() throws TTransportException {
+
+ if (null != this.client) {
+ flushUsingHttpClient();
+ return;
+ }
+
+ // Extract request and reset buffer
+ byte[] data = requestBuffer_.toByteArray();
+ requestBuffer_.reset();
+
+ try {
+ // Create connection object
+ HttpURLConnection connection = (HttpURLConnection)url_.openConnection();
+
+ // Timeouts, only if explicitly set
+ if (connectTimeout_ > 0) {
+ connection.setConnectTimeout(connectTimeout_);
+ }
+ if (readTimeout_ > 0) {
+ connection.setReadTimeout(readTimeout_);
+ }
+
+ // Make the request
+ connection.setRequestMethod("POST");
+ connection.setRequestProperty("Content-Type", "application/x-thrift");
+ connection.setRequestProperty("Accept", "application/x-thrift");
+ connection.setRequestProperty("User-Agent", "Java/THttpClient");
+ if (customHeaders_ != null) {
+ for (Map.Entry<String, String> header : customHeaders_.entrySet()) {
+ connection.setRequestProperty(header.getKey(), header.getValue());
+ }
+ }
+ connection.setDoOutput(true);
+ connection.connect();
+ connection.getOutputStream().write(data);
+
+ int responseCode = connection.getResponseCode();
+ if (responseCode != HttpURLConnection.HTTP_OK) {
+ throw new TTransportException("HTTP Response code: " + responseCode);
+ }
+
+ // Read the responses
+ inputStream_ = connection.getInputStream();
+
+ } catch (IOException iox) {
+ throw new TTransportException(iox);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TIOStreamTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TIOStreamTransport.java
new file mode 100644
index 000000000..2d31f392f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TIOStreamTransport.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * This is the most commonly used base transport. It takes an InputStream
+ * and an OutputStream and uses those to perform all transport operations.
+ * This allows for compatibility with all the nice constructs Java already
+ * has to provide a variety of types of streams.
+ *
+ */
+public class TIOStreamTransport extends TTransport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TIOStreamTransport.class.getName());
+
+ /** Underlying inputStream */
+ protected InputStream inputStream_ = null;
+
+ /** Underlying outputStream */
+ protected OutputStream outputStream_ = null;
+
+ /**
+ * Subclasses can invoke the default constructor and then assign the input
+ * streams in the open method.
+ */
+ protected TIOStreamTransport() {}
+
+ /**
+ * Input stream constructor.
+ *
+ * @param is Input stream to read from
+ */
+ public TIOStreamTransport(InputStream is) {
+ inputStream_ = is;
+ }
+
+ /**
+ * Output stream constructor.
+ *
+ * @param os Output stream to read from
+ */
+ public TIOStreamTransport(OutputStream os) {
+ outputStream_ = os;
+ }
+
+ /**
+ * Two-way stream constructor.
+ *
+ * @param is Input stream to read from
+ * @param os Output stream to read from
+ */
+ public TIOStreamTransport(InputStream is, OutputStream os) {
+ inputStream_ = is;
+ outputStream_ = os;
+ }
+
+ /**
+ *
+ * @return false after close is called.
+ */
+ public boolean isOpen() {
+ return inputStream_ != null && outputStream_ != null;
+ }
+
+ /**
+ * The streams must already be open. This method does nothing.
+ */
+ public void open() throws TTransportException {}
+
+ /**
+ * Closes both the input and output streams.
+ */
+ public void close() {
+ if (inputStream_ != null) {
+ try {
+ inputStream_.close();
+ } catch (IOException iox) {
+ LOGGER.warn("Error closing input stream.", iox);
+ }
+ inputStream_ = null;
+ }
+ if (outputStream_ != null) {
+ try {
+ outputStream_.close();
+ } catch (IOException iox) {
+ LOGGER.warn("Error closing output stream.", iox);
+ }
+ outputStream_ = null;
+ }
+ }
+
+ /**
+ * Reads from the underlying input stream if not null.
+ */
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ if (inputStream_ == null) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "Cannot read from null inputStream");
+ }
+ int bytesRead;
+ try {
+ bytesRead = inputStream_.read(buf, off, len);
+ } catch (IOException iox) {
+ throw new TTransportException(TTransportException.UNKNOWN, iox);
+ }
+ if (bytesRead < 0) {
+ throw new TTransportException(TTransportException.END_OF_FILE, "Socket is closed by peer.");
+ }
+ return bytesRead;
+ }
+
+ /**
+ * Writes to the underlying output stream if not null.
+ */
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ if (outputStream_ == null) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "Cannot write to null outputStream");
+ }
+ try {
+ outputStream_.write(buf, off, len);
+ } catch (IOException iox) {
+ throw new TTransportException(TTransportException.UNKNOWN, iox);
+ }
+ }
+
+ /**
+ * Flushes the underlying output stream if not null.
+ */
+ public void flush() throws TTransportException {
+ if (outputStream_ == null) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "Cannot flush null outputStream");
+ }
+ try {
+ outputStream_.flush();
+ } catch (IOException iox) {
+ throw new TTransportException(TTransportException.UNKNOWN, iox);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryBuffer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryBuffer.java
new file mode 100644
index 000000000..b19ac86d2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryBuffer.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.apache.thrift.TByteArrayOutputStream;
+import java.nio.charset.Charset;
+
+/**
+ * Memory buffer-based implementation of the TTransport interface.
+ */
+public class TMemoryBuffer extends TTransport {
+ /**
+ * Create a TMemoryBuffer with an initial buffer size of <i>size</i>. The
+ * internal buffer will grow as necessary to accommodate the size of the data
+ * being written to it.
+ *
+ * @param size the initial size of the buffer
+ */
+ public TMemoryBuffer(int size) {
+ arr_ = new TByteArrayOutputStream(size);
+ }
+
+ @Override
+ public boolean isOpen() {
+ return true;
+ }
+
+ @Override
+ public void open() {
+ /* Do nothing */
+ }
+
+ @Override
+ public void close() {
+ /* Do nothing */
+ }
+
+ @Override
+ public int read(byte[] buf, int off, int len) {
+ byte[] src = arr_.get();
+ int amtToRead = (len > arr_.len() - pos_ ? arr_.len() - pos_ : len);
+ if (amtToRead > 0) {
+ System.arraycopy(src, pos_, buf, off, amtToRead);
+ pos_ += amtToRead;
+ }
+ return amtToRead;
+ }
+
+ @Override
+ public void write(byte[] buf, int off, int len) {
+ arr_.write(buf, off, len);
+ }
+
+ /**
+ * Output the contents of the memory buffer as a String, using the supplied
+ * encoding
+ * @param charset the encoding to use
+ * @return the contents of the memory buffer as a String
+ */
+ public String toString(Charset charset) {
+ return arr_.toString(charset);
+ }
+
+ public String inspect() {
+ StringBuilder buf = new StringBuilder();
+ byte[] bytes = arr_.toByteArray();
+ for (int i = 0; i < bytes.length; i++) {
+ buf.append(pos_ == i ? "==>" : "" ).append(Integer.toHexString(bytes[i] & 0xff)).append(" ");
+ }
+ return buf.toString();
+ }
+
+ // The contents of the buffer
+ private TByteArrayOutputStream arr_;
+
+ // Position to read next byte from
+ private int pos_;
+
+ public int length() {
+ return arr_.size();
+ }
+
+ public byte[] getArray() {
+ return arr_.get();
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryInputTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryInputTransport.java
new file mode 100644
index 000000000..2530dcc36
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryInputTransport.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+public final class TMemoryInputTransport extends TTransport {
+
+ private byte[] buf_;
+ private int pos_;
+ private int endPos_;
+
+ public TMemoryInputTransport() {
+ }
+
+ public TMemoryInputTransport(byte[] buf) {
+ reset(buf);
+ }
+
+ public TMemoryInputTransport(byte[] buf, int offset, int length) {
+ reset(buf, offset, length);
+ }
+
+ public void reset(byte[] buf) {
+ reset(buf, 0, buf.length);
+ }
+
+ public void reset(byte[] buf, int offset, int length) {
+ buf_ = buf;
+ pos_ = offset;
+ endPos_ = offset + length;
+ }
+
+ public void clear() {
+ buf_ = null;
+ }
+
+ @Override
+ public void close() {}
+
+ @Override
+ public boolean isOpen() {
+ return true;
+ }
+
+ @Override
+ public void open() throws TTransportException {}
+
+ @Override
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ int bytesRemaining = getBytesRemainingInBuffer();
+ int amtToRead = (len > bytesRemaining ? bytesRemaining : len);
+ if (amtToRead > 0) {
+ System.arraycopy(buf_, pos_, buf, off, amtToRead);
+ consumeBuffer(amtToRead);
+ }
+ return amtToRead;
+ }
+
+ @Override
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ throw new UnsupportedOperationException("No writing allowed!");
+ }
+
+ @Override
+ public byte[] getBuffer() {
+ return buf_;
+ }
+
+ public int getBufferPosition() {
+ return pos_;
+ }
+
+ public int getBytesRemainingInBuffer() {
+ return endPos_ - pos_;
+ }
+
+ public void consumeBuffer(int len) {
+ pos_ += len;
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerSocket.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerSocket.java
new file mode 100644
index 000000000..df37cb06e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerSocket.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.thrift.transport;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.SocketException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Wrapper around ServerSocketChannel
+ */
+public class TNonblockingServerSocket extends TNonblockingServerTransport {
+ private static final Logger LOGGER = LoggerFactory.getLogger(TNonblockingServerSocket.class.getName());
+
+ /**
+ * This channel is where all the nonblocking magic happens.
+ */
+ private ServerSocketChannel serverSocketChannel = null;
+
+ /**
+ * Underlying ServerSocket object
+ */
+ private ServerSocket serverSocket_ = null;
+
+ /**
+ * Timeout for client sockets from accept
+ */
+ private int clientTimeout_ = 0;
+
+ public static class NonblockingAbstractServerSocketArgs extends
+ AbstractServerTransportArgs<NonblockingAbstractServerSocketArgs> {}
+
+ /**
+ * Creates just a port listening server socket
+ */
+ public TNonblockingServerSocket(int port) throws TTransportException {
+ this(port, 0);
+ }
+
+ /**
+ * Creates just a port listening server socket
+ */
+ public TNonblockingServerSocket(int port, int clientTimeout) throws TTransportException {
+ this(new NonblockingAbstractServerSocketArgs().port(port).clientTimeout(clientTimeout));
+ }
+
+ public TNonblockingServerSocket(InetSocketAddress bindAddr) throws TTransportException {
+ this(bindAddr, 0);
+ }
+
+ public TNonblockingServerSocket(InetSocketAddress bindAddr, int clientTimeout) throws TTransportException {
+ this(new NonblockingAbstractServerSocketArgs().bindAddr(bindAddr).clientTimeout(clientTimeout));
+ }
+
+ public TNonblockingServerSocket(NonblockingAbstractServerSocketArgs args) throws TTransportException {
+ clientTimeout_ = args.clientTimeout;
+ try {
+ serverSocketChannel = ServerSocketChannel.open();
+ serverSocketChannel.configureBlocking(false);
+
+ // Make server socket
+ serverSocket_ = serverSocketChannel.socket();
+ // Prevent 2MSL delay problem on server restarts
+ serverSocket_.setReuseAddress(true);
+ // Bind to listening port
+ serverSocket_.bind(args.bindAddr, args.backlog);
+ } catch (IOException ioe) {
+ serverSocket_ = null;
+ throw new TTransportException("Could not create ServerSocket on address " + args.bindAddr.toString() + ".", ioe);
+ }
+ }
+
+ public void listen() throws TTransportException {
+ // Make sure not to block on accept
+ if (serverSocket_ != null) {
+ try {
+ serverSocket_.setSoTimeout(0);
+ } catch (SocketException sx) {
+ LOGGER.error("Socket exception while setting socket timeout", sx);
+ }
+ }
+ }
+
+ protected TNonblockingSocket acceptImpl() throws TTransportException {
+ if (serverSocket_ == null) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "No underlying server socket.");
+ }
+ try {
+ SocketChannel socketChannel = serverSocketChannel.accept();
+ if (socketChannel == null) {
+ return null;
+ }
+
+ TNonblockingSocket tsocket = new TNonblockingSocket(socketChannel);
+ tsocket.setTimeout(clientTimeout_);
+ return tsocket;
+ } catch (IOException iox) {
+ throw new TTransportException(iox);
+ }
+ }
+
+ public void registerSelector(Selector selector) {
+ try {
+ // Register the server socket channel, indicating an interest in
+ // accepting new connections
+ serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
+ } catch (ClosedChannelException e) {
+ // this shouldn't happen, ideally...
+ // TODO: decide what to do with this.
+ }
+ }
+
+ public void close() {
+ if (serverSocket_ != null) {
+ try {
+ serverSocket_.close();
+ } catch (IOException iox) {
+ LOGGER.warn("WARNING: Could not close server socket: " + iox.getMessage());
+ }
+ serverSocket_ = null;
+ }
+ }
+
+ public void interrupt() {
+ // The thread-safeness of this is dubious, but Java documentation suggests
+ // that it is safe to do this from a different thread context
+ close();
+ }
+
+ public int getPort() {
+ if (serverSocket_ == null)
+ return -1;
+ return serverSocket_.getLocalPort();
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerTransport.java
new file mode 100644
index 000000000..ba45b09dc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerTransport.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.thrift.transport;
+
+import java.nio.channels.Selector;
+
+/**
+ * Server transport that can be operated in a nonblocking fashion.
+ */
+public abstract class TNonblockingServerTransport extends TServerTransport {
+
+ public abstract void registerSelector(Selector selector);
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingSocket.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingSocket.java
new file mode 100644
index 000000000..f86a48b42
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingSocket.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.thrift.transport;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Transport for use with async client.
+ */
+public class TNonblockingSocket extends TNonblockingTransport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TNonblockingSocket.class.getName());
+
+ /**
+ * Host and port if passed in, used for lazy non-blocking connect.
+ */
+ private final SocketAddress socketAddress_;
+
+ private final SocketChannel socketChannel_;
+
+ public TNonblockingSocket(String host, int port) throws IOException {
+ this(host, port, 0);
+ }
+
+ /**
+ * Create a new nonblocking socket transport that will be connected to host:port.
+ * @param host
+ * @param port
+ * @throws IOException
+ */
+ public TNonblockingSocket(String host, int port, int timeout) throws IOException {
+ this(SocketChannel.open(), timeout, new InetSocketAddress(host, port));
+ }
+
+ /**
+ * Constructor that takes an already created socket.
+ *
+ * @param socketChannel Already created SocketChannel object
+ * @throws IOException if there is an error setting up the streams
+ */
+ public TNonblockingSocket(SocketChannel socketChannel) throws IOException {
+ this(socketChannel, 0, null);
+ if (!socketChannel.isConnected()) throw new IOException("Socket must already be connected");
+ }
+
+ private TNonblockingSocket(SocketChannel socketChannel, int timeout, SocketAddress socketAddress)
+ throws IOException {
+ socketChannel_ = socketChannel;
+ socketAddress_ = socketAddress;
+
+ // make it a nonblocking channel
+ socketChannel.configureBlocking(false);
+
+ // set options
+ Socket socket = socketChannel.socket();
+ socket.setSoLinger(false, 0);
+ socket.setTcpNoDelay(true);
+ socket.setKeepAlive(true);
+ setTimeout(timeout);
+ }
+
+ /**
+ * Register the new SocketChannel with our Selector, indicating
+ * we'd like to be notified when it's ready for I/O.
+ *
+ * @param selector
+ * @return the selection key for this socket.
+ */
+ public SelectionKey registerSelector(Selector selector, int interests) throws IOException {
+ return socketChannel_.register(selector, interests);
+ }
+
+ /**
+ * Sets the socket timeout, although this implementation never uses blocking operations so it is unused.
+ *
+ * @param timeout Milliseconds timeout
+ */
+ public void setTimeout(int timeout) {
+ try {
+ socketChannel_.socket().setSoTimeout(timeout);
+ } catch (SocketException sx) {
+ LOGGER.warn("Could not set socket timeout.", sx);
+ }
+ }
+
+ /**
+ * Returns a reference to the underlying SocketChannel.
+ */
+ public SocketChannel getSocketChannel() {
+ return socketChannel_;
+ }
+
+ /**
+ * Checks whether the socket is connected.
+ */
+ public boolean isOpen() {
+ // isConnected() does not return false after close(), but isOpen() does
+ return socketChannel_.isOpen() && socketChannel_.isConnected();
+ }
+
+ /**
+ * Do not call, the implementation provides its own lazy non-blocking connect.
+ */
+ public void open() throws TTransportException {
+ throw new RuntimeException("open() is not implemented for TNonblockingSocket");
+ }
+
+ /**
+ * Perform a nonblocking read into buffer.
+ */
+ public int read(ByteBuffer buffer) throws IOException {
+ return socketChannel_.read(buffer);
+ }
+
+
+ /**
+ * Reads from the underlying input stream if not null.
+ */
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ if ((socketChannel_.validOps() & SelectionKey.OP_READ) != SelectionKey.OP_READ) {
+ throw new TTransportException(TTransportException.NOT_OPEN,
+ "Cannot read from write-only socket channel");
+ }
+ try {
+ return socketChannel_.read(ByteBuffer.wrap(buf, off, len));
+ } catch (IOException iox) {
+ throw new TTransportException(TTransportException.UNKNOWN, iox);
+ }
+ }
+
+ /**
+ * Perform a nonblocking write of the data in buffer;
+ */
+ public int write(ByteBuffer buffer) throws IOException {
+ return socketChannel_.write(buffer);
+ }
+
+ /**
+ * Writes to the underlying output stream if not null.
+ */
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ if ((socketChannel_.validOps() & SelectionKey.OP_WRITE) != SelectionKey.OP_WRITE) {
+ throw new TTransportException(TTransportException.NOT_OPEN,
+ "Cannot write to write-only socket channel");
+ }
+ try {
+ socketChannel_.write(ByteBuffer.wrap(buf, off, len));
+ } catch (IOException iox) {
+ throw new TTransportException(TTransportException.UNKNOWN, iox);
+ }
+ }
+
+ /**
+ * Noop.
+ */
+ public void flush() throws TTransportException {
+ // Not supported by SocketChannel.
+ }
+
+ /**
+ * Closes the socket.
+ */
+ public void close() {
+ try {
+ socketChannel_.close();
+ } catch (IOException iox) {
+ LOGGER.warn("Could not close socket.", iox);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public boolean startConnect() throws IOException {
+ return socketChannel_.connect(socketAddress_);
+ }
+
+ /** {@inheritDoc} */
+ public boolean finishConnect() throws IOException {
+ return socketChannel_.finishConnect();
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingTransport.java
new file mode 100644
index 000000000..43c130688
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingTransport.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+
+public abstract class TNonblockingTransport extends TTransport {
+
+ /**
+ * Non-blocking connection initialization.
+ * @see java.nio.channels.SocketChannel#connect(SocketAddress remote)
+ */
+ public abstract boolean startConnect() throws IOException;
+
+ /**
+ * Non-blocking connection completion.
+ * @see java.nio.channels.SocketChannel#finishConnect()
+ */
+ public abstract boolean finishConnect() throws IOException;
+
+ public abstract SelectionKey registerSelector(Selector selector, int interests) throws IOException;
+
+ public abstract int read(ByteBuffer buffer) throws IOException;
+
+ public abstract int write(ByteBuffer buffer) throws IOException;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java
new file mode 100644
index 000000000..73dfaaf18
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java
@@ -0,0 +1,447 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.security.KeyStore;
+import java.util.Arrays;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A Factory for providing and setting up Client and Server SSL wrapped
+ * TSocket and TServerSocket
+ */
+public class TSSLTransportFactory {
+
+ private static final Logger LOGGER =
+ LoggerFactory.getLogger(TSSLTransportFactory.class);
+
+ /**
+ * Get a SSL wrapped TServerSocket bound to the specified port. In this
+ * configuration the default settings are used. Default settings are retrieved
+ * from System properties that are set.
+ *
+ * Example system properties:
+ * -Djavax.net.ssl.trustStore=&lt;truststore location&gt;
+ * -Djavax.net.ssl.trustStorePassword=password
+ * -Djavax.net.ssl.keyStore=&lt;keystore location&gt;
+ * -Djavax.net.ssl.keyStorePassword=password
+ *
+ * @param port
+ * @return A SSL wrapped TServerSocket
+ * @throws TTransportException
+ */
+ public static TServerSocket getServerSocket(int port) throws TTransportException {
+ return getServerSocket(port, 0);
+ }
+
+ /**
+ * Get a default SSL wrapped TServerSocket bound to the specified port
+ *
+ * @param port
+ * @param clientTimeout
+ * @return A SSL wrapped TServerSocket
+ * @throws TTransportException
+ */
+ public static TServerSocket getServerSocket(int port, int clientTimeout) throws TTransportException {
+ return getServerSocket(port, clientTimeout, false, null);
+ }
+
+ /**
+ * Get a default SSL wrapped TServerSocket bound to the specified port and interface
+ *
+ * @param port
+ * @param clientTimeout
+ * @param ifAddress
+ * @return A SSL wrapped TServerSocket
+ * @throws TTransportException
+ */
+ public static TServerSocket getServerSocket(int port, int clientTimeout, boolean clientAuth, InetAddress ifAddress) throws TTransportException {
+ SSLServerSocketFactory factory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
+ return createServer(factory, port, clientTimeout, clientAuth, ifAddress, null);
+ }
+
+ /**
+ * Get a configured SSL wrapped TServerSocket bound to the specified port and interface.
+ * Here the TSSLTransportParameters are used to set the values for the algorithms, keystore,
+ * truststore and other settings
+ *
+ * @param port
+ * @param clientTimeout
+ * @param ifAddress
+ * @param params
+ * @return A SSL wrapped TServerSocket
+ * @throws TTransportException
+ */
+ public static TServerSocket getServerSocket(int port, int clientTimeout, InetAddress ifAddress, TSSLTransportParameters params) throws TTransportException {
+ if (params == null || !(params.isKeyStoreSet || params.isTrustStoreSet)) {
+ throw new TTransportException("Either one of the KeyStore or TrustStore must be set for SSLTransportParameters");
+ }
+
+ SSLContext ctx = createSSLContext(params);
+ return createServer(ctx.getServerSocketFactory(), port, clientTimeout, params.clientAuth, ifAddress, params);
+ }
+
+ private static TServerSocket createServer(SSLServerSocketFactory factory, int port, int timeout, boolean clientAuth,
+ InetAddress ifAddress, TSSLTransportParameters params) throws TTransportException {
+ try {
+ SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(port, 100, ifAddress);
+ serverSocket.setSoTimeout(timeout);
+ serverSocket.setNeedClientAuth(clientAuth);
+ if (params != null && params.cipherSuites != null) {
+ serverSocket.setEnabledCipherSuites(params.cipherSuites);
+ }
+ return new TServerSocket(new TServerSocket.ServerSocketTransportArgs().
+ serverSocket(serverSocket).clientTimeout(timeout));
+ } catch (Exception e) {
+ throw new TTransportException("Could not bind to port " + port, e);
+ }
+ }
+
+ /**
+ * Get a default SSL wrapped TSocket connected to the specified host and port. All
+ * the client methods return a bound connection. So there is no need to call open() on the
+ * TTransport.
+ *
+ * @param host
+ * @param port
+ * @param timeout
+ * @return A SSL wrapped TSocket
+ * @throws TTransportException
+ */
+ public static TSocket getClientSocket(String host, int port, int timeout) throws TTransportException {
+ SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
+ return createClient(factory, host, port, timeout);
+ }
+
+ /**
+ * Get a default SSL wrapped TSocket connected to the specified host and port.
+ *
+ * @param host
+ * @param port
+ * @return A SSL wrapped TSocket
+ * @throws TTransportException
+ */
+ public static TSocket getClientSocket(String host, int port) throws TTransportException {
+ return getClientSocket(host, port, 0);
+ }
+
+ /**
+ * Get a custom configured SSL wrapped TSocket. The SSL settings are obtained from the
+ * passed in TSSLTransportParameters.
+ *
+ * @param host
+ * @param port
+ * @param timeout
+ * @param params
+ * @return A SSL wrapped TSocket
+ * @throws TTransportException
+ */
+ public static TSocket getClientSocket(String host, int port, int timeout, TSSLTransportParameters params) throws TTransportException {
+ if (params == null || !(params.isKeyStoreSet || params.isTrustStoreSet)) {
+ throw new TTransportException("Either one of the KeyStore or TrustStore must be set for SSLTransportParameters");
+ }
+
+ SSLContext ctx = createSSLContext(params);
+ return createClient(ctx.getSocketFactory(), host, port, timeout);
+ }
+
+ private static SSLContext createSSLContext(TSSLTransportParameters params) throws TTransportException {
+ SSLContext ctx;
+ InputStream in = null;
+ InputStream is = null;
+
+ try {
+ ctx = SSLContext.getInstance(params.protocol);
+ TrustManagerFactory tmf = null;
+ KeyManagerFactory kmf = null;
+
+ if (params.isTrustStoreSet) {
+ tmf = TrustManagerFactory.getInstance(params.trustManagerType);
+ KeyStore ts = KeyStore.getInstance(params.trustStoreType);
+ if (params.trustStoreStream != null) {
+ in = params.trustStoreStream;
+ } else {
+ in = getStoreAsStream(params.trustStore);
+ }
+ ts.load(in,
+ (params.trustPass != null ? params.trustPass.toCharArray() : null));
+ tmf.init(ts);
+ }
+
+ if (params.isKeyStoreSet) {
+ kmf = KeyManagerFactory.getInstance(params.keyManagerType);
+ KeyStore ks = KeyStore.getInstance(params.keyStoreType);
+ if (params.keyStoreStream != null) {
+ is = params.keyStoreStream;
+ } else {
+ is = getStoreAsStream(params.keyStore);
+ }
+ ks.load(is, params.keyPass.toCharArray());
+ kmf.init(ks, params.keyPass.toCharArray());
+ }
+
+ if (params.isKeyStoreSet && params.isTrustStoreSet) {
+ ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+ }
+ else if (params.isKeyStoreSet) {
+ ctx.init(kmf.getKeyManagers(), null, null);
+ }
+ else {
+ ctx.init(null, tmf.getTrustManagers(), null);
+ }
+
+ } catch (Exception e) {
+ throw new TTransportException("Error creating the transport", e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ LOGGER.warn("Unable to close stream", e);
+ }
+ }
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ LOGGER.warn("Unable to close stream", e);
+ }
+ }
+ }
+
+ return ctx;
+ }
+
+ private static InputStream getStoreAsStream(String store) throws IOException {
+ try {
+ return new FileInputStream(store);
+ } catch(FileNotFoundException e) {
+ }
+
+ InputStream storeStream = null;
+ try {
+ storeStream = new URL(store).openStream();
+ if (storeStream != null) {
+ return storeStream;
+ }
+ } catch(MalformedURLException e) {
+ }
+
+ storeStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(store);
+
+ if (storeStream != null) {
+ return storeStream;
+ } else {
+ throw new IOException("Could not load file: " + store);
+ }
+ }
+
+ private static TSocket createClient(SSLSocketFactory factory, String host, int port, int timeout) throws TTransportException {
+ try {
+ SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
+ socket.setSoTimeout(timeout);
+ return new TSocket(socket);
+ } catch (Exception e) {
+ throw new TTransportException("Could not connect to " + host + " on port " + port, e);
+ }
+ }
+
+
+ /**
+ * A Class to hold all the SSL parameters
+ */
+ public static class TSSLTransportParameters {
+ protected String protocol = "TLS";
+ protected String keyStore;
+ protected InputStream keyStoreStream;
+ protected String keyPass;
+ protected String keyManagerType = KeyManagerFactory.getDefaultAlgorithm();
+ protected String keyStoreType = "JKS";
+ protected String trustStore;
+ protected InputStream trustStoreStream;
+ protected String trustPass;
+ protected String trustManagerType = TrustManagerFactory.getDefaultAlgorithm();
+ protected String trustStoreType = "JKS";
+ protected String[] cipherSuites;
+ protected boolean clientAuth = false;
+ protected boolean isKeyStoreSet = false;
+ protected boolean isTrustStoreSet = false;
+
+ public TSSLTransportParameters() {}
+
+ /**
+ * Create parameters specifying the protocol and cipher suites
+ *
+ * @param protocol The specific protocol (TLS/SSL) can be specified with versions
+ * @param cipherSuites
+ */
+ public TSSLTransportParameters(String protocol, String[] cipherSuites) {
+ this(protocol, cipherSuites, false);
+ }
+
+ /**
+ * Create parameters specifying the protocol, cipher suites and if client authentication
+ * is required
+ *
+ * @param protocol The specific protocol (TLS/SSL) can be specified with versions
+ * @param cipherSuites
+ * @param clientAuth
+ */
+ public TSSLTransportParameters(String protocol, String[] cipherSuites, boolean clientAuth) {
+ if (protocol != null) {
+ this.protocol = protocol;
+ }
+ this.cipherSuites = cipherSuites != null ? Arrays.copyOf(cipherSuites, cipherSuites.length) : null;
+ this.clientAuth = clientAuth;
+ }
+
+ /**
+ * Set the keystore, password, certificate type and the store type
+ *
+ * @param keyStore Location of the Keystore on disk
+ * @param keyPass Keystore password
+ * @param keyManagerType The default is X509
+ * @param keyStoreType The default is JKS
+ */
+ public void setKeyStore(String keyStore, String keyPass, String keyManagerType, String keyStoreType) {
+ this.keyStore = keyStore;
+ this.keyPass = keyPass;
+ if (keyManagerType != null) {
+ this.keyManagerType = keyManagerType;
+ }
+ if (keyStoreType != null) {
+ this.keyStoreType = keyStoreType;
+ }
+ isKeyStoreSet = true;
+ }
+
+ /**
+ * Set the keystore, password, certificate type and the store type
+ *
+ * @param keyStoreStream Keystore content input stream
+ * @param keyPass Keystore password
+ * @param keyManagerType The default is X509
+ * @param keyStoreType The default is JKS
+ */
+ public void setKeyStore(InputStream keyStoreStream, String keyPass, String keyManagerType, String keyStoreType) {
+ this.keyStoreStream = keyStoreStream;
+ setKeyStore("", keyPass, keyManagerType, keyStoreType);
+ }
+
+ /**
+ * Set the keystore and password
+ *
+ * @param keyStore Location of the Keystore on disk
+ * @param keyPass Keystore password
+ */
+ public void setKeyStore(String keyStore, String keyPass) {
+ setKeyStore(keyStore, keyPass, null, null);
+ }
+
+ /**
+ * Set the keystore and password
+ *
+ * @param keyStoreStream Keystore content input stream
+ * @param keyPass Keystore password
+ */
+ public void setKeyStore(InputStream keyStoreStream, String keyPass) {
+ setKeyStore(keyStoreStream, keyPass, null, null);
+ }
+
+ /**
+ * Set the truststore, password, certificate type and the store type
+ *
+ * @param trustStore Location of the Truststore on disk
+ * @param trustPass Truststore password
+ * @param trustManagerType The default is X509
+ * @param trustStoreType The default is JKS
+ */
+ public void setTrustStore(String trustStore, String trustPass, String trustManagerType, String trustStoreType) {
+ this.trustStore = trustStore;
+ this.trustPass = trustPass;
+ if (trustManagerType != null) {
+ this.trustManagerType = trustManagerType;
+ }
+ if (trustStoreType != null) {
+ this.trustStoreType = trustStoreType;
+ }
+ isTrustStoreSet = true;
+ }
+
+ /**
+ * Set the truststore, password, certificate type and the store type
+ *
+ * @param trustStoreStream Truststore content input stream
+ * @param trustPass Truststore password
+ * @param trustManagerType The default is X509
+ * @param trustStoreType The default is JKS
+ */
+ public void setTrustStore(InputStream trustStoreStream, String trustPass, String trustManagerType, String trustStoreType) {
+ this.trustStoreStream = trustStoreStream;
+ setTrustStore("", trustPass, trustManagerType, trustStoreType);
+ }
+
+ /**
+ * Set the truststore and password
+ *
+ * @param trustStore Location of the Truststore on disk
+ * @param trustPass Truststore password
+ */
+ public void setTrustStore(String trustStore, String trustPass) {
+ setTrustStore(trustStore, trustPass, null, null);
+ }
+
+ /**
+ * Set the truststore and password
+ *
+ * @param trustStoreStream Truststore content input stream
+ * @param trustPass Truststore password
+ */
+ public void setTrustStore(InputStream trustStoreStream, String trustPass) {
+ setTrustStore(trustStoreStream, trustPass, null, null);
+ }
+
+ /**
+ * Set if client authentication is required
+ *
+ * @param clientAuth
+ */
+ public void requireClientAuth(boolean clientAuth) {
+ this.clientAuth = clientAuth;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslClientTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslClientTransport.java
new file mode 100644
index 000000000..4b1ca0a94
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslClientTransport.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Wraps another Thrift <code>TTransport</code>, but performs SASL client
+ * negotiation on the call to <code>open()</code>. This class will wrap ensuing
+ * communication over it, if a SASL QOP is negotiated with the other party.
+ */
+public class TSaslClientTransport extends TSaslTransport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TSaslClientTransport.class);
+
+ /**
+ * The name of the mechanism this client supports.
+ */
+ private final String mechanism;
+
+ /**
+ * Uses the given <code>SaslClient</code>.
+ *
+ * @param saslClient
+ * The <code>SaslClient</code> to use for the subsequent SASL
+ * negotiation.
+ * @param transport
+ * Transport underlying this one.
+ */
+ public TSaslClientTransport(SaslClient saslClient, TTransport transport) {
+ super(saslClient, transport);
+ mechanism = saslClient.getMechanismName();
+ }
+
+ /**
+ * Creates a <code>SaslClient</code> using the given SASL-specific parameters.
+ * See the Java documentation for <code>Sasl.createSaslClient</code> for the
+ * details of the parameters.
+ *
+ * @param transport
+ * The underlying Thrift transport.
+ * @throws SaslException
+ */
+ public TSaslClientTransport(String mechanism, String authorizationId, String protocol,
+ String serverName, Map<String, String> props, CallbackHandler cbh, TTransport transport)
+ throws SaslException {
+ super(Sasl.createSaslClient(new String[] { mechanism }, authorizationId, protocol, serverName,
+ props, cbh), transport);
+ this.mechanism = mechanism;
+ }
+
+
+ @Override
+ protected SaslRole getRole() {
+ return SaslRole.CLIENT;
+ }
+
+ /**
+ * Performs the client side of the initial portion of the Thrift SASL
+ * protocol. Generates and sends the initial response to the server, including
+ * which mechanism this client wants to use.
+ */
+ @Override
+ protected void handleSaslStartMessage() throws TTransportException, SaslException {
+ SaslClient saslClient = getSaslClient();
+
+ byte[] initialResponse = new byte[0];
+ if (saslClient.hasInitialResponse())
+ initialResponse = saslClient.evaluateChallenge(initialResponse);
+
+ LOGGER.debug("Sending mechanism name {} and initial response of length {}", mechanism,
+ initialResponse.length);
+
+ byte[] mechanismBytes = mechanism.getBytes(StandardCharsets.UTF_8);
+ sendSaslMessage(NegotiationStatus.START,
+ mechanismBytes);
+ // Send initial response
+ sendSaslMessage(saslClient.isComplete() ? NegotiationStatus.COMPLETE : NegotiationStatus.OK,
+ initialResponse);
+ underlyingTransport.flush();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslServerTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslServerTransport.java
new file mode 100644
index 000000000..39b81ca43
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslServerTransport.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.lang.ref.WeakReference;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Wraps another Thrift <code>TTransport</code>, but performs SASL server
+ * negotiation on the call to <code>open()</code>. This class will wrap ensuing
+ * communication over it, if a SASL QOP is negotiated with the other party.
+ */
+public class TSaslServerTransport extends TSaslTransport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TSaslServerTransport.class);
+
+ /**
+ * Mapping from SASL mechanism name -> all the parameters required to
+ * instantiate a SASL server.
+ */
+ private Map<String, TSaslServerDefinition> serverDefinitionMap = new HashMap<String, TSaslServerDefinition>();
+
+ /**
+ * Contains all the parameters used to define a SASL server implementation.
+ */
+ private static class TSaslServerDefinition {
+ public String mechanism;
+ public String protocol;
+ public String serverName;
+ public Map<String, String> props;
+ public CallbackHandler cbh;
+
+ public TSaslServerDefinition(String mechanism, String protocol, String serverName,
+ Map<String, String> props, CallbackHandler cbh) {
+ this.mechanism = mechanism;
+ this.protocol = protocol;
+ this.serverName = serverName;
+ this.props = props;
+ this.cbh = cbh;
+ }
+ }
+
+ /**
+ * Uses the given underlying transport. Assumes that addServerDefinition is
+ * called later.
+ *
+ * @param transport
+ * Transport underlying this one.
+ */
+ public TSaslServerTransport(TTransport transport) {
+ super(transport);
+ }
+
+ /**
+ * Creates a <code>SaslServer</code> using the given SASL-specific parameters.
+ * See the Java documentation for <code>Sasl.createSaslServer</code> for the
+ * details of the parameters.
+ *
+ * @param transport
+ * The underlying Thrift transport.
+ */
+ public TSaslServerTransport(String mechanism, String protocol, String serverName,
+ Map<String, String> props, CallbackHandler cbh, TTransport transport) {
+ super(transport);
+ addServerDefinition(mechanism, protocol, serverName, props, cbh);
+ }
+
+ private TSaslServerTransport(Map<String, TSaslServerDefinition> serverDefinitionMap, TTransport transport) {
+ super(transport);
+ this.serverDefinitionMap.putAll(serverDefinitionMap);
+ }
+
+ /**
+ * Add a supported server definition to this transport. See the Java
+ * documentation for <code>Sasl.createSaslServer</code> for the details of the
+ * parameters.
+ */
+ public void addServerDefinition(String mechanism, String protocol, String serverName,
+ Map<String, String> props, CallbackHandler cbh) {
+ serverDefinitionMap.put(mechanism, new TSaslServerDefinition(mechanism, protocol, serverName,
+ props, cbh));
+ }
+
+ @Override
+ protected SaslRole getRole() {
+ return SaslRole.SERVER;
+ }
+
+ /**
+ * Performs the server side of the initial portion of the Thrift SASL protocol.
+ * Receives the initial response from the client, creates a SASL server using
+ * the mechanism requested by the client (if this server supports it), and
+ * sends the first challenge back to the client.
+ */
+ @Override
+ protected void handleSaslStartMessage() throws TTransportException, SaslException {
+ SaslResponse message = receiveSaslMessage();
+
+ LOGGER.debug("Received start message with status {}", message.status);
+ if (message.status != NegotiationStatus.START) {
+ throw sendAndThrowMessage(NegotiationStatus.ERROR, "Expecting START status, received " + message.status);
+ }
+
+ // Get the mechanism name.
+ String mechanismName = new String(message.payload, StandardCharsets.UTF_8);
+ TSaslServerDefinition serverDefinition = serverDefinitionMap.get(mechanismName);
+ LOGGER.debug("Received mechanism name '{}'", mechanismName);
+
+ if (serverDefinition == null) {
+ throw sendAndThrowMessage(NegotiationStatus.BAD, "Unsupported mechanism type " + mechanismName);
+ }
+ SaslServer saslServer = Sasl.createSaslServer(serverDefinition.mechanism,
+ serverDefinition.protocol, serverDefinition.serverName, serverDefinition.props,
+ serverDefinition.cbh);
+ setSaslServer(saslServer);
+ }
+
+ /**
+ * <code>TTransportFactory</code> to create
+ * <code>TSaslServerTransports</code>. Ensures that a given
+ * underlying <code>TTransport</code> instance receives the same
+ * <code>TSaslServerTransport</code>. This is kind of an awful hack to work
+ * around the fact that Thrift is designed assuming that
+ * <code>TTransport</code> instances are stateless, and thus the existing
+ * <code>TServers</code> use different <code>TTransport</code> instances for
+ * input and output.
+ */
+ public static class Factory extends TTransportFactory {
+
+ /**
+ * This is the implementation of the awful hack described above.
+ * <code>WeakHashMap</code> is used to ensure that we don't leak memory.
+ */
+ private static Map<TTransport, WeakReference<TSaslServerTransport>> transportMap =
+ Collections.synchronizedMap(new WeakHashMap<TTransport, WeakReference<TSaslServerTransport>>());
+
+ /**
+ * Mapping from SASL mechanism name -> all the parameters required to
+ * instantiate a SASL server.
+ */
+ private Map<String, TSaslServerDefinition> serverDefinitionMap = new HashMap<String, TSaslServerDefinition>();
+
+ /**
+ * Create a new Factory. Assumes that <code>addServerDefinition</code> will
+ * be called later.
+ */
+ public Factory() {
+ super();
+ }
+
+ /**
+ * Create a new <code>Factory</code>, initially with the single server
+ * definition given. You may still call <code>addServerDefinition</code>
+ * later. See the Java documentation for <code>Sasl.createSaslServer</code>
+ * for the details of the parameters.
+ */
+ public Factory(String mechanism, String protocol, String serverName,
+ Map<String, String> props, CallbackHandler cbh) {
+ super();
+ addServerDefinition(mechanism, protocol, serverName, props, cbh);
+ }
+
+ /**
+ * Add a supported server definition to the transports created by this
+ * factory. See the Java documentation for
+ * <code>Sasl.createSaslServer</code> for the details of the parameters.
+ */
+ public void addServerDefinition(String mechanism, String protocol, String serverName,
+ Map<String, String> props, CallbackHandler cbh) {
+ serverDefinitionMap.put(mechanism, new TSaslServerDefinition(mechanism, protocol, serverName,
+ props, cbh));
+ }
+
+ /**
+ * Get a new <code>TSaslServerTransport</code> instance, or reuse the
+ * existing one if a <code>TSaslServerTransport</code> has already been
+ * created before using the given <code>TTransport</code> as an underlying
+ * transport. This ensures that a given underlying transport instance
+ * receives the same <code>TSaslServerTransport</code>.
+ */
+ @Override
+ public TTransport getTransport(TTransport base) {
+ WeakReference<TSaslServerTransport> ret = transportMap.get(base);
+ if (ret == null || ret.get() == null) {
+ LOGGER.debug("transport map does not contain key", base);
+ ret = new WeakReference<TSaslServerTransport>(new TSaslServerTransport(serverDefinitionMap, base));
+ try {
+ ret.get().open();
+ } catch (TTransportException e) {
+ LOGGER.debug("failed to open server transport", e);
+ throw new RuntimeException(e);
+ }
+ transportMap.put(base, ret); // No need for putIfAbsent().
+ // Concurrent calls to getTransport() will pass in different TTransports.
+ } else {
+ LOGGER.debug("transport map does contain key {}", base);
+ }
+ return ret.get();
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslTransport.java
new file mode 100644
index 000000000..4a453b68f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslTransport.java
@@ -0,0 +1,575 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+import org.apache.thrift.EncodingUtils;
+import org.apache.thrift.TByteArrayOutputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A superclass for SASL client/server thrift transports. A subclass need only
+ * implement the <code>open</code> method.
+ */
+abstract class TSaslTransport extends TTransport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TSaslTransport.class);
+
+ protected static final int DEFAULT_MAX_LENGTH = 0x7FFFFFFF;
+
+ protected static final int MECHANISM_NAME_BYTES = 1;
+ protected static final int STATUS_BYTES = 1;
+ protected static final int PAYLOAD_LENGTH_BYTES = 4;
+
+ protected static enum SaslRole {
+ SERVER, CLIENT;
+ }
+
+ /**
+ * Status bytes used during the initial Thrift SASL handshake.
+ */
+ protected static enum NegotiationStatus {
+ START((byte)0x01),
+ OK((byte)0x02),
+ BAD((byte)0x03),
+ ERROR((byte)0x04),
+ COMPLETE((byte)0x05);
+
+ private final byte value;
+
+ private static final Map<Byte, NegotiationStatus> reverseMap =
+ new HashMap<Byte, NegotiationStatus>();
+ static {
+ for (NegotiationStatus s : NegotiationStatus.class.getEnumConstants()) {
+ reverseMap.put(s.getValue(), s);
+ }
+ }
+
+ private NegotiationStatus(byte val) {
+ this.value = val;
+ }
+
+ public byte getValue() {
+ return value;
+ }
+
+ public static NegotiationStatus byValue(byte val) {
+ return reverseMap.get(val);
+ }
+ }
+
+ /**
+ * Transport underlying this one.
+ */
+ protected TTransport underlyingTransport;
+
+ /**
+ * Either a SASL client or a SASL server.
+ */
+ private SaslParticipant sasl;
+
+ /**
+ * Whether or not we should wrap/unwrap reads/writes. Determined by whether or
+ * not a QOP is negotiated during the SASL handshake.
+ */
+ private boolean shouldWrap = false;
+
+ /**
+ * Buffer for input.
+ */
+ private TMemoryInputTransport readBuffer = new TMemoryInputTransport();
+
+ /**
+ * Buffer for output.
+ */
+ private final TByteArrayOutputStream writeBuffer = new TByteArrayOutputStream(1024);
+
+ /**
+ * Create a TSaslTransport. It's assumed that setSaslServer will be called
+ * later to initialize the SASL endpoint underlying this transport.
+ *
+ * @param underlyingTransport
+ * The thrift transport which this transport is wrapping.
+ */
+ protected TSaslTransport(TTransport underlyingTransport) {
+ this.underlyingTransport = underlyingTransport;
+ }
+
+ /**
+ * Create a TSaslTransport which acts as a client.
+ *
+ * @param saslClient
+ * The <code>SaslClient</code> which this transport will use for SASL
+ * negotiation.
+ * @param underlyingTransport
+ * The thrift transport which this transport is wrapping.
+ */
+ protected TSaslTransport(SaslClient saslClient, TTransport underlyingTransport) {
+ sasl = new SaslParticipant(saslClient);
+ this.underlyingTransport = underlyingTransport;
+ }
+
+ protected void setSaslServer(SaslServer saslServer) {
+ sasl = new SaslParticipant(saslServer);
+ }
+
+ // Used to read the status byte and payload length.
+ private final byte[] messageHeader = new byte[STATUS_BYTES + PAYLOAD_LENGTH_BYTES];
+
+ /**
+ * Send a complete Thrift SASL message.
+ *
+ * @param status
+ * The status to send.
+ * @param payload
+ * The data to send as the payload of this message.
+ * @throws TTransportException
+ */
+ protected void sendSaslMessage(NegotiationStatus status, byte[] payload) throws TTransportException {
+ if (payload == null)
+ payload = new byte[0];
+
+ messageHeader[0] = status.getValue();
+ EncodingUtils.encodeBigEndian(payload.length, messageHeader, STATUS_BYTES);
+
+ LOGGER.debug("{}: Writing message with status {} and payload length {}",
+ getRole(), status, payload.length);
+
+ underlyingTransport.write(messageHeader);
+ underlyingTransport.write(payload);
+ underlyingTransport.flush();
+ }
+
+ /**
+ * Read a complete Thrift SASL message.
+ *
+ * @return The SASL status and payload from this message.
+ * @throws TTransportException
+ * Thrown if there is a failure reading from the underlying
+ * transport, or if a status code of BAD or ERROR is encountered.
+ */
+ protected SaslResponse receiveSaslMessage() throws TTransportException {
+ underlyingTransport.readAll(messageHeader, 0, messageHeader.length);
+
+ byte statusByte = messageHeader[0];
+
+ NegotiationStatus status = NegotiationStatus.byValue(statusByte);
+ if (status == null) {
+ throw sendAndThrowMessage(NegotiationStatus.ERROR, "Invalid status " + statusByte);
+ }
+
+ int payloadBytes = EncodingUtils.decodeBigEndian(messageHeader, STATUS_BYTES);
+ if (payloadBytes < 0 || payloadBytes > 104857600 /* 100 MB */) {
+ throw sendAndThrowMessage(
+ NegotiationStatus.ERROR, "Invalid payload header length: " + payloadBytes);
+ }
+
+ byte[] payload = new byte[payloadBytes];
+ underlyingTransport.readAll(payload, 0, payload.length);
+
+ if (status == NegotiationStatus.BAD || status == NegotiationStatus.ERROR) {
+ String remoteMessage = new String(payload, StandardCharsets.UTF_8);
+ throw new TTransportException("Peer indicated failure: " + remoteMessage);
+ }
+ LOGGER.debug("{}: Received message with status {} and payload length {}",
+ getRole(), status, payload.length);
+ return new SaslResponse(status, payload);
+ }
+
+ /**
+ * Send a Thrift SASL message with the given status (usually BAD or ERROR) and
+ * string message, and then throw a TTransportException with the given
+ * message.
+ *
+ * @param status
+ * The Thrift SASL status code to send. Usually BAD or ERROR.
+ * @param message
+ * The optional message to send to the other side.
+ * @throws TTransportException
+ * Always thrown with the message provided.
+ * @return always throws TTransportException but declares return type to allow
+ * throw sendAndThrowMessage(...) to inform compiler control flow
+ */
+ protected TTransportException sendAndThrowMessage(NegotiationStatus status, String message) throws TTransportException {
+ try {
+ sendSaslMessage(status, message.getBytes(StandardCharsets.UTF_8));
+ } catch (Exception e) {
+ LOGGER.warn("Could not send failure response", e);
+ message += "\nAlso, could not send response: " + e.toString();
+ }
+ throw new TTransportException(message);
+ }
+
+ /**
+ * Implemented by subclasses to start the Thrift SASL handshake process. When
+ * this method completes, the <code>SaslParticipant</code> in this class is
+ * assumed to be initialized.
+ *
+ * @throws TTransportException
+ * @throws SaslException
+ */
+ abstract protected void handleSaslStartMessage() throws TTransportException, SaslException;
+
+ protected abstract SaslRole getRole();
+
+ /**
+ * Opens the underlying transport if it's not already open and then performs
+ * SASL negotiation. If a QOP is negotiated during this SASL handshake, it used
+ * for all communication on this transport after this call is complete.
+ */
+ @Override
+ public void open() throws TTransportException {
+ /*
+ * readSaslHeader is used to tag whether the SASL header has been read properly.
+ * If there is a problem in reading the header, there might not be any
+ * data in the stream, possibly a TCP health check from load balancer.
+ */
+ boolean readSaslHeader = false;
+
+ LOGGER.debug("opening transport {}", this);
+ if (sasl != null && sasl.isComplete())
+ throw new TTransportException("SASL transport already open");
+
+ if (!underlyingTransport.isOpen())
+ underlyingTransport.open();
+
+ try {
+ // Negotiate a SASL mechanism. The client also sends its
+ // initial response, or an empty one.
+ handleSaslStartMessage();
+ readSaslHeader = true;
+ LOGGER.debug("{}: Start message handled", getRole());
+
+ SaslResponse message = null;
+ while (!sasl.isComplete()) {
+ message = receiveSaslMessage();
+ if (message.status != NegotiationStatus.COMPLETE &&
+ message.status != NegotiationStatus.OK) {
+ throw new TTransportException("Expected COMPLETE or OK, got " + message.status);
+ }
+
+ byte[] challenge = sasl.evaluateChallengeOrResponse(message.payload);
+
+ // If we are the client, and the server indicates COMPLETE, we don't need to
+ // send back any further response.
+ if (message.status == NegotiationStatus.COMPLETE &&
+ getRole() == SaslRole.CLIENT) {
+ LOGGER.debug("{}: All done!", getRole());
+ continue;
+ }
+
+ sendSaslMessage(sasl.isComplete() ? NegotiationStatus.COMPLETE : NegotiationStatus.OK,
+ challenge);
+ }
+ LOGGER.debug("{}: Main negotiation loop complete", getRole());
+
+ // If we're the client, and we're complete, but the server isn't
+ // complete yet, we need to wait for its response. This will occur
+ // with ANONYMOUS auth, for example, where we send an initial response
+ // and are immediately complete.
+ if (getRole() == SaslRole.CLIENT &&
+ (message == null || message.status == NegotiationStatus.OK)) {
+ LOGGER.debug("{}: SASL Client receiving last message", getRole());
+ message = receiveSaslMessage();
+ if (message.status != NegotiationStatus.COMPLETE) {
+ throw new TTransportException(
+ "Expected SASL COMPLETE, but got " + message.status);
+ }
+ }
+ } catch (SaslException e) {
+ try {
+ LOGGER.error("SASL negotiation failure", e);
+ throw sendAndThrowMessage(NegotiationStatus.BAD, e.getMessage());
+ } finally {
+ underlyingTransport.close();
+ }
+ } catch (TTransportException e) {
+ // If there is no-data or no-sasl header in the stream,
+ // log the failure, and clean up the underlying transport.
+ if (!readSaslHeader && e.getType() == TTransportException.END_OF_FILE) {
+ underlyingTransport.close();
+ LOGGER.debug("No data or no sasl data in the stream during negotiation");
+ }
+ throw e;
+ }
+
+ String qop = (String) sasl.getNegotiatedProperty(Sasl.QOP);
+ if (qop != null && !qop.equalsIgnoreCase("auth"))
+ shouldWrap = true;
+ }
+
+ /**
+ * Get the underlying <code>SaslClient</code>.
+ *
+ * @return The <code>SaslClient</code>, or <code>null</code> if this transport
+ * is backed by a <code>SaslServer</code>.
+ */
+ public SaslClient getSaslClient() {
+ return sasl.saslClient;
+ }
+
+ /**
+ * Get the underlying transport that Sasl is using.
+ * @return The <code>TTransport</code> transport
+ */
+ public TTransport getUnderlyingTransport() {
+ return underlyingTransport;
+ }
+
+ /**
+ * Get the underlying <code>SaslServer</code>.
+ *
+ * @return The <code>SaslServer</code>, or <code>null</code> if this transport
+ * is backed by a <code>SaslClient</code>.
+ */
+ public SaslServer getSaslServer() {
+ return sasl.saslServer;
+ }
+
+ /**
+ * Read a 4-byte word from the underlying transport and interpret it as an
+ * integer.
+ *
+ * @return The length prefix of the next SASL message to read.
+ * @throws TTransportException
+ * Thrown if reading from the underlying transport fails.
+ */
+ protected int readLength() throws TTransportException {
+ byte[] lenBuf = new byte[4];
+ underlyingTransport.readAll(lenBuf, 0, lenBuf.length);
+ return EncodingUtils.decodeBigEndian(lenBuf);
+ }
+
+ /**
+ * Write the given integer as 4 bytes to the underlying transport.
+ *
+ * @param length
+ * The length prefix of the next SASL message to write.
+ * @throws TTransportException
+ * Thrown if writing to the underlying transport fails.
+ */
+ protected void writeLength(int length) throws TTransportException {
+ byte[] lenBuf = new byte[4];
+ TFramedTransport.encodeFrameSize(length, lenBuf);
+ underlyingTransport.write(lenBuf);
+ }
+
+ // Below is the SASL implementation of the TTransport interface.
+
+ /**
+ * Closes the underlying transport and disposes of the SASL implementation
+ * underlying this transport.
+ */
+ @Override
+ public void close() {
+ underlyingTransport.close();
+ try {
+ sasl.dispose();
+ } catch (SaslException e) {
+ // Not much we can do here.
+ }
+ }
+
+ /**
+ * True if the underlying transport is open and the SASL handshake is
+ * complete.
+ */
+ @Override
+ public boolean isOpen() {
+ return underlyingTransport.isOpen() && sasl != null && sasl.isComplete();
+ }
+
+ /**
+ * Read from the underlying transport. Unwraps the contents if a QOP was
+ * negotiated during the SASL handshake.
+ */
+ @Override
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ if (!isOpen())
+ throw new TTransportException("SASL authentication not complete");
+
+ int got = readBuffer.read(buf, off, len);
+ if (got > 0) {
+ return got;
+ }
+
+ // Read another frame of data
+ try {
+ readFrame();
+ } catch (SaslException e) {
+ throw new TTransportException(e);
+ } catch (TTransportException transportException) {
+ // If there is no-data or no-sasl header in the stream, log the failure, and rethrow.
+ if (transportException.getType() == TTransportException.END_OF_FILE) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("No data or no sasl data in the stream during negotiation");
+ }
+ }
+ throw transportException;
+ }
+
+ return readBuffer.read(buf, off, len);
+ }
+
+ /**
+ * Read a single frame of data from the underlying transport, unwrapping if
+ * necessary.
+ *
+ * @throws TTransportException
+ * Thrown if there's an error reading from the underlying transport.
+ * @throws SaslException
+ * Thrown if there's an error unwrapping the data.
+ */
+ private void readFrame() throws TTransportException, SaslException {
+ int dataLength = readLength();
+
+ if (dataLength < 0)
+ throw new TTransportException("Read a negative frame size (" + dataLength + ")!");
+
+ byte[] buff = new byte[dataLength];
+ LOGGER.debug("{}: reading data length: {}", getRole(), dataLength);
+ underlyingTransport.readAll(buff, 0, dataLength);
+ if (shouldWrap) {
+ buff = sasl.unwrap(buff, 0, buff.length);
+ LOGGER.debug("data length after unwrap: {}", buff.length);
+ }
+ readBuffer.reset(buff);
+ }
+
+ /**
+ * Write to the underlying transport.
+ */
+ @Override
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ if (!isOpen())
+ throw new TTransportException("SASL authentication not complete");
+
+ writeBuffer.write(buf, off, len);
+ }
+
+ /**
+ * Flushes to the underlying transport. Wraps the contents if a QOP was
+ * negotiated during the SASL handshake.
+ */
+ @Override
+ public void flush() throws TTransportException {
+ byte[] buf = writeBuffer.get();
+ int dataLength = writeBuffer.len();
+ writeBuffer.reset();
+
+ if (shouldWrap) {
+ LOGGER.debug("data length before wrap: {}", dataLength);
+ try {
+ buf = sasl.wrap(buf, 0, dataLength);
+ } catch (SaslException e) {
+ throw new TTransportException(e);
+ }
+ dataLength = buf.length;
+ }
+ LOGGER.debug("writing data length: {}", dataLength);
+ writeLength(dataLength);
+ underlyingTransport.write(buf, 0, dataLength);
+ underlyingTransport.flush();
+ }
+
+ /**
+ * Used exclusively by readSaslMessage to return both a status and data.
+ */
+ protected static class SaslResponse {
+ public NegotiationStatus status;
+ public byte[] payload;
+
+ public SaslResponse(NegotiationStatus status, byte[] payload) {
+ this.status = status;
+ this.payload = payload;
+ }
+ }
+
+ /**
+ * Used to abstract over the <code>SaslServer</code> and
+ * <code>SaslClient</code> classes, which share a lot of their interface, but
+ * unfortunately don't share a common superclass.
+ */
+ private static class SaslParticipant {
+ // One of these will always be null.
+ public SaslServer saslServer;
+ public SaslClient saslClient;
+
+ public SaslParticipant(SaslServer saslServer) {
+ this.saslServer = saslServer;
+ }
+
+ public SaslParticipant(SaslClient saslClient) {
+ this.saslClient = saslClient;
+ }
+
+ public byte[] evaluateChallengeOrResponse(byte[] challengeOrResponse) throws SaslException {
+ if (saslClient != null) {
+ return saslClient.evaluateChallenge(challengeOrResponse);
+ } else {
+ return saslServer.evaluateResponse(challengeOrResponse);
+ }
+ }
+
+ public boolean isComplete() {
+ if (saslClient != null)
+ return saslClient.isComplete();
+ else
+ return saslServer.isComplete();
+ }
+
+ public void dispose() throws SaslException {
+ if (saslClient != null)
+ saslClient.dispose();
+ else
+ saslServer.dispose();
+ }
+
+ public byte[] unwrap(byte[] buf, int off, int len) throws SaslException {
+ if (saslClient != null)
+ return saslClient.unwrap(buf, off, len);
+ else
+ return saslServer.unwrap(buf, off, len);
+ }
+
+ public byte[] wrap(byte[] buf, int off, int len) throws SaslException {
+ if (saslClient != null)
+ return saslClient.wrap(buf, off, len);
+ else
+ return saslServer.wrap(buf, off, len);
+ }
+
+ public Object getNegotiatedProperty(String propName) {
+ if (saslClient != null)
+ return saslClient.getNegotiatedProperty(propName);
+ else
+ return saslServer.getNegotiatedProperty(propName);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSeekableFile.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSeekableFile.java
new file mode 100644
index 000000000..e02d36f6c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSeekableFile.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+public interface TSeekableFile {
+
+ public InputStream getInputStream() throws IOException;
+ public OutputStream getOutputStream() throws IOException;
+ public void close() throws IOException;
+ public long length() throws IOException;
+ public void seek(long pos) throws IOException;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerSocket.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerSocket.java
new file mode 100644
index 000000000..79f7b7f49
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerSocket.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+
+/**
+ * Wrapper around ServerSocket for Thrift.
+ *
+ */
+public class TServerSocket extends TServerTransport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TServerSocket.class.getName());
+
+ /**
+ * Underlying ServerSocket object
+ */
+ private ServerSocket serverSocket_ = null;
+
+ /**
+ * Timeout for client sockets from accept
+ */
+ private int clientTimeout_ = 0;
+
+ public static class ServerSocketTransportArgs extends AbstractServerTransportArgs<ServerSocketTransportArgs> {
+ ServerSocket serverSocket;
+
+ public ServerSocketTransportArgs serverSocket(ServerSocket serverSocket) {
+ this.serverSocket = serverSocket;
+ return this;
+ }
+ }
+
+ /**
+ * Creates a server socket from underlying socket object
+ */
+ public TServerSocket(ServerSocket serverSocket) throws TTransportException {
+ this(serverSocket, 0);
+ }
+
+ /**
+ * Creates a server socket from underlying socket object
+ */
+ public TServerSocket(ServerSocket serverSocket, int clientTimeout) throws TTransportException {
+ this(new ServerSocketTransportArgs().serverSocket(serverSocket).clientTimeout(clientTimeout));
+ }
+
+ /**
+ * Creates just a port listening server socket
+ */
+ public TServerSocket(int port) throws TTransportException {
+ this(port, 0);
+ }
+
+ /**
+ * Creates just a port listening server socket
+ */
+ public TServerSocket(int port, int clientTimeout) throws TTransportException {
+ this(new InetSocketAddress(port), clientTimeout);
+ }
+
+ public TServerSocket(InetSocketAddress bindAddr) throws TTransportException {
+ this(bindAddr, 0);
+ }
+
+ public TServerSocket(InetSocketAddress bindAddr, int clientTimeout) throws TTransportException {
+ this(new ServerSocketTransportArgs().bindAddr(bindAddr).clientTimeout(clientTimeout));
+ }
+
+ public TServerSocket(ServerSocketTransportArgs args) throws TTransportException {
+ clientTimeout_ = args.clientTimeout;
+ if (args.serverSocket != null) {
+ this.serverSocket_ = args.serverSocket;
+ return;
+ }
+ try {
+ // Make server socket
+ serverSocket_ = new ServerSocket();
+ // Prevent 2MSL delay problem on server restarts
+ serverSocket_.setReuseAddress(true);
+ // Bind to listening port
+ serverSocket_.bind(args.bindAddr, args.backlog);
+ } catch (IOException ioe) {
+ close();
+ throw new TTransportException("Could not create ServerSocket on address " + args.bindAddr.toString() + ".", ioe);
+ }
+ }
+
+ public void listen() throws TTransportException {
+ // Make sure to block on accept
+ if (serverSocket_ != null) {
+ try {
+ serverSocket_.setSoTimeout(0);
+ } catch (SocketException sx) {
+ LOGGER.error("Could not set socket timeout.", sx);
+ }
+ }
+ }
+
+ protected TSocket acceptImpl() throws TTransportException {
+ if (serverSocket_ == null) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "No underlying server socket.");
+ }
+ try {
+ Socket result = serverSocket_.accept();
+ TSocket result2 = new TSocket(result);
+ result2.setTimeout(clientTimeout_);
+ return result2;
+ } catch (IOException iox) {
+ throw new TTransportException(iox);
+ }
+ }
+
+ public void close() {
+ if (serverSocket_ != null) {
+ try {
+ serverSocket_.close();
+ } catch (IOException iox) {
+ LOGGER.warn("Could not close server socket.", iox);
+ }
+ serverSocket_ = null;
+ }
+ }
+
+ public void interrupt() {
+ // The thread-safeness of this is dubious, but Java documentation suggests
+ // that it is safe to do this from a different thread context
+ close();
+ }
+
+ public ServerSocket getServerSocket() {
+ return serverSocket_;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerTransport.java
new file mode 100644
index 000000000..424e4faaa
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerTransport.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.Closeable;
+import java.net.InetSocketAddress;
+
+/**
+ * Server transport. Object which provides client transports.
+ *
+ */
+public abstract class TServerTransport implements Closeable {
+
+ public static abstract class AbstractServerTransportArgs<T extends AbstractServerTransportArgs<T>> {
+ int backlog = 0; // A value of 0 means the default value will be used (currently set at 50)
+ int clientTimeout = 0;
+ InetSocketAddress bindAddr;
+
+ public T backlog(int backlog) {
+ this.backlog = backlog;
+ return (T) this;
+ }
+
+ public T clientTimeout(int clientTimeout) {
+ this.clientTimeout = clientTimeout;
+ return (T) this;
+ }
+
+ public T port(int port) {
+ this.bindAddr = new InetSocketAddress(port);
+ return (T) this;
+ }
+
+ public T bindAddr(InetSocketAddress bindAddr) {
+ this.bindAddr = bindAddr;
+ return (T) this;
+ }
+ }
+
+ public abstract void listen() throws TTransportException;
+
+ public final TTransport accept() throws TTransportException {
+ TTransport transport = acceptImpl();
+ if (transport == null) {
+ throw new TTransportException("accept() may not return NULL");
+ }
+ return transport;
+ }
+
+ public abstract void close();
+
+ protected abstract TTransport acceptImpl() throws TTransportException;
+
+ /**
+ * Optional method implementation. This signals to the server transport
+ * that it should break out of any accept() or listen() that it is currently
+ * blocked on. This method, if implemented, MUST be thread safe, as it may
+ * be called from a different thread context than the other TServerTransport
+ * methods.
+ */
+ public void interrupt() {}
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSimpleFileTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSimpleFileTransport.java
new file mode 100644
index 000000000..42102d9e8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSimpleFileTransport.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+
+/**
+ * Basic file support for the TTransport interface
+ */
+public final class TSimpleFileTransport extends TTransport {
+
+ private RandomAccessFile file = null;
+ private boolean readable;
+ private boolean writable;
+ private String path_;
+
+
+ /**
+ * Create a transport backed by a simple file
+ *
+ * @param path the path to the file to open/create
+ * @param read true to support read operations
+ * @param write true to support write operations
+ * @param openFile true to open the file on construction
+ * @throws TTransportException if file open fails
+ */
+ public TSimpleFileTransport(String path, boolean read,
+ boolean write, boolean openFile)
+ throws TTransportException {
+ if (path.length() <= 0) {
+ throw new TTransportException("No path specified");
+ }
+ if (!read && !write) {
+ throw new TTransportException("Neither READ nor WRITE specified");
+ }
+ readable = read;
+ writable = write;
+ path_ = path;
+ if (openFile) {
+ open();
+ }
+ }
+
+ /**
+ * Create a transport backed by a simple file
+ * Implicitly opens file to conform to C++ behavior.
+ *
+ * @param path the path to the file to open/create
+ * @param read true to support read operations
+ * @param write true to support write operations
+ * @throws TTransportException if file open fails
+ */
+ public TSimpleFileTransport(String path, boolean read, boolean write)
+ throws TTransportException {
+ this(path, read, write, true);
+ }
+
+ /**
+ * Create a transport backed by a simple read only disk file (implicitly opens
+ * file)
+ *
+ * @param path the path to the file to open/create
+ * @throws TTransportException if file open fails
+ */
+ public TSimpleFileTransport(String path) throws TTransportException {
+ this(path, true, false, true);
+ }
+
+ /**
+ * Test file status
+ *
+ * @return true if open, otherwise false
+ */
+ @Override
+ public boolean isOpen() {
+ return (file != null);
+ }
+
+ /**
+ * Open file if not previously opened.
+ *
+ * @throws TTransportException if open fails
+ */
+ @Override
+ public void open() throws TTransportException {
+ if (file == null){
+ try {
+ String access = "r"; //RandomAccessFile objects must be readable
+ if (writable) {
+ access += "w";
+ }
+ file = new RandomAccessFile(path_, access);
+ } catch (IOException ioe) {
+ file = null;
+ throw new TTransportException(ioe.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Close file, subsequent read/write activity will throw exceptions
+ */
+ @Override
+ public void close() {
+ if (file != null) {
+ try {
+ file.close();
+ } catch (Exception e) {
+ //Nothing to do
+ }
+ file = null;
+ }
+ }
+
+ /**
+ * Read up to len many bytes into buf at offset
+ *
+ * @param buf houses bytes read
+ * @param off offset into buff to begin writing to
+ * @param len maximum number of bytes to read
+ * @return number of bytes actually read
+ * @throws TTransportException on read failure
+ */
+ @Override
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ if (!readable) {
+ throw new TTransportException("Read operation on write only file");
+ }
+ int iBytesRead = 0;
+ try {
+ iBytesRead = file.read(buf, off, len);
+ } catch (IOException ioe) {
+ file = null;
+ throw new TTransportException(ioe.getMessage());
+ }
+ return iBytesRead;
+ }
+
+ /**
+ * Write len many bytes from buff starting at offset
+ *
+ * @param buf buffer containing bytes to write
+ * @param off offset into buffer to begin writing from
+ * @param len number of bytes to write
+ * @throws TTransportException on write failure
+ */
+ @Override
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ try {
+ file.write(buf, off, len);
+ } catch (IOException ioe) {
+ file = null;
+ throw new TTransportException(ioe.getMessage());
+ }
+ }
+
+ /**
+ * Move file pointer to specified offset, new read/write calls will act here
+ *
+ * @param offset bytes from beginning of file to move pointer to
+ * @throws TTransportException is seek fails
+ */
+ public void seek(long offset) throws TTransportException {
+ try {
+ file.seek(offset);
+ } catch (IOException ex) {
+ throw new TTransportException(ex.getMessage());
+ }
+ }
+
+ /**
+ * Return the length of the file in bytes
+ *
+ * @return length of the file in bytes
+ * @throws TTransportException if file access fails
+ */
+ public long length() throws TTransportException {
+ try {
+ return file.length();
+ } catch (IOException ex) {
+ throw new TTransportException(ex.getMessage());
+ }
+ }
+
+ /**
+ * Return current file pointer position in bytes from beginning of file
+ *
+ * @return file pointer position
+ * @throws TTransportException if file access fails
+ */
+ public long getFilePointer() throws TTransportException {
+ try {
+ return file.getFilePointer();
+ } catch (IOException ex) {
+ throw new TTransportException(ex.getMessage());
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSocket.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSocket.java
new file mode 100644
index 000000000..b20b32b78
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSocket.java
@@ -0,0 +1,248 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketException;
+
+/**
+ * Socket implementation of the TTransport interface. To be commented soon!
+ *
+ */
+public class TSocket extends TIOStreamTransport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TSocket.class.getName());
+
+ /**
+ * Wrapped Socket object
+ */
+ private Socket socket_;
+
+ /**
+ * Remote host
+ */
+ private String host_;
+
+ /**
+ * Remote port
+ */
+ private int port_;
+
+ /**
+ * Socket timeout - read timeout on the socket
+ */
+ private int socketTimeout_;
+
+ /**
+ * Connection timeout
+ */
+ private int connectTimeout_;
+
+ /**
+ * Constructor that takes an already created socket.
+ *
+ * @param socket Already created socket object
+ * @throws TTransportException if there is an error setting up the streams
+ */
+ public TSocket(Socket socket) throws TTransportException {
+ socket_ = socket;
+ try {
+ socket_.setSoLinger(false, 0);
+ socket_.setTcpNoDelay(true);
+ socket_.setKeepAlive(true);
+ } catch (SocketException sx) {
+ LOGGER.warn("Could not configure socket.", sx);
+ }
+
+ if (isOpen()) {
+ try {
+ inputStream_ = new BufferedInputStream(socket_.getInputStream());
+ outputStream_ = new BufferedOutputStream(socket_.getOutputStream());
+ } catch (IOException iox) {
+ close();
+ throw new TTransportException(TTransportException.NOT_OPEN, iox);
+ }
+ }
+ }
+
+ /**
+ * Creates a new unconnected socket that will connect to the given host
+ * on the given port.
+ *
+ * @param host Remote host
+ * @param port Remote port
+ */
+ public TSocket(String host, int port) {
+ this(host, port, 0);
+ }
+
+ /**
+ * Creates a new unconnected socket that will connect to the given host
+ * on the given port.
+ *
+ * @param host Remote host
+ * @param port Remote port
+ * @param timeout Socket timeout and connection timeout
+ */
+ public TSocket(String host, int port, int timeout) {
+ this(host, port, timeout, timeout);
+ }
+
+ /**
+ * Creates a new unconnected socket that will connect to the given host
+ * on the given port, with a specific connection timeout and a
+ * specific socket timeout.
+ *
+ * @param host Remote host
+ * @param port Remote port
+ * @param socketTimeout Socket timeout
+ * @param connectTimeout Connection timeout
+ */
+ public TSocket(String host, int port, int socketTimeout, int connectTimeout) {
+ host_ = host;
+ port_ = port;
+ socketTimeout_ = socketTimeout;
+ connectTimeout_ = connectTimeout;
+ initSocket();
+ }
+
+ /**
+ * Initializes the socket object
+ */
+ private void initSocket() {
+ socket_ = new Socket();
+ try {
+ socket_.setSoLinger(false, 0);
+ socket_.setTcpNoDelay(true);
+ socket_.setKeepAlive(true);
+ socket_.setSoTimeout(socketTimeout_);
+ } catch (SocketException sx) {
+ LOGGER.error("Could not configure socket.", sx);
+ }
+ }
+
+ /**
+ * Sets the socket timeout and connection timeout.
+ *
+ * @param timeout Milliseconds timeout
+ */
+ public void setTimeout(int timeout) {
+ this.setConnectTimeout(timeout);
+ this.setSocketTimeout(timeout);
+ }
+
+ /**
+ * Sets the time after which the connection attempt will time out
+ *
+ * @param timeout Milliseconds timeout
+ */
+ public void setConnectTimeout(int timeout) {
+ connectTimeout_ = timeout;
+ }
+
+ /**
+ * Sets the socket timeout
+ *
+ * @param timeout Milliseconds timeout
+ */
+ public void setSocketTimeout(int timeout) {
+ socketTimeout_ = timeout;
+ try {
+ socket_.setSoTimeout(timeout);
+ } catch (SocketException sx) {
+ LOGGER.warn("Could not set socket timeout.", sx);
+ }
+ }
+
+ /**
+ * Returns a reference to the underlying socket.
+ */
+ public Socket getSocket() {
+ if (socket_ == null) {
+ initSocket();
+ }
+ return socket_;
+ }
+
+ /**
+ * Checks whether the socket is connected.
+ */
+ public boolean isOpen() {
+ if (socket_ == null) {
+ return false;
+ }
+ return socket_.isConnected();
+ }
+
+ /**
+ * Connects the socket, creating a new socket object if necessary.
+ */
+ public void open() throws TTransportException {
+ if (isOpen()) {
+ throw new TTransportException(TTransportException.ALREADY_OPEN, "Socket already connected.");
+ }
+
+ if (host_ == null || host_.length() == 0) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "Cannot open null host.");
+ }
+ if (port_ <= 0 || port_ > 65535) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "Invalid port " + port_);
+ }
+
+ if (socket_ == null) {
+ initSocket();
+ }
+
+ try {
+ socket_.connect(new InetSocketAddress(host_, port_), connectTimeout_);
+ inputStream_ = new BufferedInputStream(socket_.getInputStream());
+ outputStream_ = new BufferedOutputStream(socket_.getOutputStream());
+ } catch (IOException iox) {
+ close();
+ throw new TTransportException(TTransportException.NOT_OPEN, iox);
+ }
+ }
+
+ /**
+ * Closes the socket.
+ */
+ public void close() {
+ // Close the underlying streams
+ super.close();
+
+ // Close the socket
+ if (socket_ != null) {
+ try {
+ socket_.close();
+ } catch (IOException iox) {
+ LOGGER.warn("Could not close socket.", iox);
+ }
+ socket_ = null;
+ }
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TStandardFile.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TStandardFile.java
new file mode 100644
index 000000000..7a33af8ee
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TStandardFile.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+public class TStandardFile implements TSeekableFile {
+
+ protected String path_ = null;
+ protected RandomAccessFile inputFile_ = null;
+
+ public TStandardFile(String path) throws IOException {
+ path_ = path;
+ inputFile_ = new RandomAccessFile(path_, "r");
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return new FileInputStream(inputFile_.getFD());
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ return new FileOutputStream(path_);
+ }
+
+ public void close() throws IOException {
+ if(inputFile_ != null) {
+ inputFile_.close();
+ }
+ }
+
+ public long length() throws IOException {
+ return inputFile_.length();
+ }
+
+ public void seek(long pos) throws IOException {
+ inputFile_.seek(pos);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransport.java
new file mode 100644
index 000000000..73ad730ce
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransport.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.Closeable;
+
+/**
+ * Generic class that encapsulates the I/O layer. This is basically a thin
+ * wrapper around the combined functionality of Java input/output streams.
+ *
+ */
+public abstract class TTransport implements Closeable {
+
+ /**
+ * Queries whether the transport is open.
+ *
+ * @return True if the transport is open.
+ */
+ public abstract boolean isOpen();
+
+ /**
+ * Is there more data to be read?
+ *
+ * @return True if the remote side is still alive and feeding us
+ */
+ public boolean peek() {
+ return isOpen();
+ }
+
+ /**
+ * Opens the transport for reading/writing.
+ *
+ * @throws TTransportException if the transport could not be opened
+ */
+ public abstract void open()
+ throws TTransportException;
+
+ /**
+ * Closes the transport.
+ */
+ public abstract void close();
+
+ /**
+ * Reads up to len bytes into buffer buf, starting at offset off.
+ *
+ * @param buf Array to read into
+ * @param off Index to start reading at
+ * @param len Maximum number of bytes to read
+ * @return The number of bytes actually read
+ * @throws TTransportException if there was an error reading data
+ */
+ public abstract int read(byte[] buf, int off, int len)
+ throws TTransportException;
+
+ /**
+ * Guarantees that all of len bytes are actually read off the transport.
+ *
+ * @param buf Array to read into
+ * @param off Index to start reading at
+ * @param len Maximum number of bytes to read
+ * @return The number of bytes actually read, which must be equal to len
+ * @throws TTransportException if there was an error reading data
+ */
+ public int readAll(byte[] buf, int off, int len)
+ throws TTransportException {
+ int got = 0;
+ int ret = 0;
+ while (got < len) {
+ ret = read(buf, off+got, len-got);
+ if (ret <= 0) {
+ throw new TTransportException(
+ "Cannot read. Remote side has closed. Tried to read "
+ + len
+ + " bytes, but only got "
+ + got
+ + " bytes. (This is often indicative of an internal error on the server side. Please check your server logs.)");
+ }
+ got += ret;
+ }
+ return got;
+ }
+
+ /**
+ * Writes the buffer to the output
+ *
+ * @param buf The output data buffer
+ * @throws TTransportException if an error occurs writing data
+ */
+ public void write(byte[] buf) throws TTransportException {
+ write(buf, 0, buf.length);
+ }
+
+ /**
+ * Writes up to len bytes from the buffer.
+ *
+ * @param buf The output data buffer
+ * @param off The offset to start writing from
+ * @param len The number of bytes to write
+ * @throws TTransportException if there was an error writing data
+ */
+ public abstract void write(byte[] buf, int off, int len)
+ throws TTransportException;
+
+ /**
+ * Flush any pending data out of a transport buffer.
+ *
+ * @throws TTransportException if there was an error writing out data.
+ */
+ public void flush()
+ throws TTransportException {}
+
+ /**
+ * Access the protocol's underlying buffer directly. If this is not a
+ * buffered transport, return null.
+ * @return protocol's Underlying buffer
+ */
+ public byte[] getBuffer() {
+ return null;
+ }
+
+ /**
+ * Return the index within the underlying buffer that specifies the next spot
+ * that should be read from.
+ * @return index within the underlying buffer that specifies the next spot
+ * that should be read from
+ */
+ public int getBufferPosition() {
+ return 0;
+ }
+
+ /**
+ * Get the number of bytes remaining in the underlying buffer. Returns -1 if
+ * this is a non-buffered transport.
+ * @return the number of bytes remaining in the underlying buffer. <br> Returns -1 if
+ * this is a non-buffered transport.
+ */
+ public int getBytesRemainingInBuffer() {
+ return -1;
+ }
+
+ /**
+ * Consume len bytes from the underlying buffer.
+ * @param len
+ */
+ public void consumeBuffer(int len) {}
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportException.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportException.java
new file mode 100644
index 000000000..b886bc269
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportException.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.apache.thrift.TException;
+
+/**
+ * Transport exceptions.
+ *
+ */
+public class TTransportException extends TException {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final int UNKNOWN = 0;
+ public static final int NOT_OPEN = 1;
+ public static final int ALREADY_OPEN = 2;
+ public static final int TIMED_OUT = 3;
+ public static final int END_OF_FILE = 4;
+ public static final int CORRUPTED_DATA = 5;
+
+ protected int type_ = UNKNOWN;
+
+ public TTransportException() {
+ super();
+ }
+
+ public TTransportException(int type) {
+ super();
+ type_ = type;
+ }
+
+ public TTransportException(int type, String message) {
+ super(message);
+ type_ = type;
+ }
+
+ public TTransportException(String message) {
+ super(message);
+ }
+
+ public TTransportException(int type, Throwable cause) {
+ super(cause);
+ type_ = type;
+ }
+
+ public TTransportException(Throwable cause) {
+ super(cause);
+ }
+
+ public TTransportException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public TTransportException(int type, String message, Throwable cause) {
+ super(message, cause);
+ type_ = type;
+ }
+
+ public int getType() {
+ return type_;
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportFactory.java
new file mode 100644
index 000000000..3e71630ae
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportFactory.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+/**
+ * Factory class used to create wrapped instance of Transports.
+ * This is used primarily in servers, which get Transports from
+ * a ServerTransport and then may want to mutate them (i.e. create
+ * a BufferedTransport from the underlying base transport)
+ *
+ */
+public class TTransportFactory {
+
+ /**
+ * Return a wrapped instance of the base Transport.
+ *
+ * @param trans The base transport
+ * @return Wrapped Transport
+ */
+ public TTransport getTransport(TTransport trans) {
+ return trans;
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TZlibTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TZlibTransport.java
new file mode 100644
index 000000000..e755aa532
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TZlibTransport.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+
+/**
+ * TZlibTransport deflates on write and inflates on read.
+ */
+public class TZlibTransport extends TIOStreamTransport {
+
+ private TTransport transport_ = null;
+
+ public static class Factory extends TTransportFactory {
+ public Factory() {
+ }
+
+ @Override
+ public TTransport getTransport(TTransport base) {
+ return new TZlibTransport(base);
+ }
+ }
+
+ /**
+ * Constructs a new TZlibTransport instance.
+ * @param transport the underlying transport to read from and write to
+ */
+ public TZlibTransport(TTransport transport) {
+ this(transport, Deflater.BEST_COMPRESSION);
+ }
+
+ /**
+ * Constructs a new TZlibTransport instance.
+ * @param transport the underlying transport to read from and write to
+ * @param compressionLevel 0 for no compression, 9 for maximum compression
+ */
+ public TZlibTransport(TTransport transport, int compressionLevel) {
+ transport_ = transport;
+ inputStream_ = new InflaterInputStream(new TTransportInputStream(transport_), new Inflater());
+ outputStream_ = new DeflaterOutputStream(new TTransportOutputStream(transport_), new Deflater(compressionLevel, false), true);
+ }
+
+ @Override
+ public boolean isOpen() {
+ return transport_.isOpen();
+ }
+
+ @Override
+ public void open() throws TTransportException {
+ transport_.open();
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ if (transport_.isOpen()) {
+ transport_.close();
+ }
+ }
+}
+
+class TTransportInputStream extends InputStream {
+
+ private TTransport transport = null;
+
+ public TTransportInputStream(TTransport transport) {
+ this.transport = transport;
+ }
+
+ @Override
+ public int read() throws IOException {
+ try {
+ byte[] buf = new byte[1];
+ transport.read(buf, 0, 1);
+ return buf[0];
+ } catch (TTransportException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public int read(byte b[], int off, int len) throws IOException {
+ try {
+ return transport.read(b, off, len);
+ } catch (TTransportException e) {
+ throw new IOException(e);
+ }
+ }
+}
+
+class TTransportOutputStream extends OutputStream {
+
+ private TTransport transport = null;
+
+ public TTransportOutputStream(TTransport transport) {
+ this.transport = transport;
+ }
+
+ @Override
+ public void write(final int b) throws IOException {
+ try {
+ transport.write(new byte[]{(byte) b});
+ } catch (TTransportException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public void write(byte b[], int off, int len) throws IOException {
+ try {
+ transport.write(b, off, len);
+ } catch (TTransportException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ try {
+ transport.flush();
+ } catch (TTransportException e) {
+ throw new IOException(e);
+ }
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/java/test/.keystore b/src/jaegertracing/thrift/lib/java/test/.keystore
new file mode 100644
index 000000000..4dd66ac07
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/.keystore
Binary files differ
diff --git a/src/jaegertracing/thrift/lib/java/test/.truststore b/src/jaegertracing/thrift/lib/java/test/.truststore
new file mode 100644
index 000000000..26fbd195f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/.truststore
Binary files differ
diff --git a/src/jaegertracing/thrift/lib/java/test/log4j.properties b/src/jaegertracing/thrift/lib/java/test/log4j.properties
new file mode 100644
index 000000000..ab9bebafe
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/log4j.properties
@@ -0,0 +1,6 @@
+# log4j configuration used during build and unit tests
+log4j.rootLogger=debug,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p %c{2} (%F:%M(%L)) - %m%n
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/Fixtures.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/Fixtures.java
new file mode 100644
index 000000000..61f40a590
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/Fixtures.java
@@ -0,0 +1,340 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+import thrift.test.Bonk;
+import thrift.test.CompactProtoTestStruct;
+import thrift.test.HolyMoley;
+import thrift.test.Nesting;
+import thrift.test.OneOfEach;
+
+public class Fixtures {
+ public static final OneOfEach oneOfEach;
+ public static final Nesting nesting;
+ public static final HolyMoley holyMoley;
+ public static final CompactProtoTestStruct compactProtoTestStruct;
+
+ // These byte arrays are serialized versions of the above structs.
+ // They were serialized in binary protocol using thrift 0.6.x and are used to
+ // test backwards compatibility with respect to the standard scheme.
+ public static final byte[] persistentBytesOneOfEach = new byte[] { (byte) 0x02, (byte) 0x00,
+ (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x00, (byte) 0x02,
+ (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x03, (byte) 0xD6,
+ (byte) 0x06, (byte) 0x00, (byte) 0x04, (byte) 0x69, (byte) 0x78,
+ (byte) 0x08, (byte) 0x00, (byte) 0x05, (byte) 0x01, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x0A, (byte) 0x00, (byte) 0x06,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x65,
+ (byte) 0xA0, (byte) 0xBC, (byte) 0x00, (byte) 0x04, (byte) 0x00,
+ (byte) 0x07, (byte) 0x40, (byte) 0x09, (byte) 0x21, (byte) 0xFB,
+ (byte) 0x54, (byte) 0x44, (byte) 0x2D, (byte) 0x18, (byte) 0x0B,
+ (byte) 0x00, (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x0D, (byte) 0x4A, (byte) 0x53, (byte) 0x4F, (byte) 0x4E,
+ (byte) 0x20, (byte) 0x54, (byte) 0x48, (byte) 0x49, (byte) 0x53,
+ (byte) 0x21, (byte) 0x20, (byte) 0x22, (byte) 0x01, (byte) 0x0B,
+ (byte) 0x00, (byte) 0x09, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x2E, (byte) 0xD3, (byte) 0x80, (byte) 0xE2, (byte) 0x85,
+ (byte) 0xAE, (byte) 0xCE, (byte) 0x9D, (byte) 0x20, (byte) 0xD0,
+ (byte) 0x9D, (byte) 0xCE, (byte) 0xBF, (byte) 0xE2, (byte) 0x85,
+ (byte) 0xBF, (byte) 0xD0, (byte) 0xBE, (byte) 0xC9, (byte) 0xA1,
+ (byte) 0xD0, (byte) 0xB3, (byte) 0xD0, (byte) 0xB0, (byte) 0xCF,
+ (byte) 0x81, (byte) 0xE2, (byte) 0x84, (byte) 0x8E, (byte) 0x20,
+ (byte) 0xCE, (byte) 0x91, (byte) 0x74, (byte) 0x74, (byte) 0xCE,
+ (byte) 0xB1, (byte) 0xE2, (byte) 0x85, (byte) 0xBD, (byte) 0xCE,
+ (byte) 0xBA, (byte) 0xEF, (byte) 0xBF, (byte) 0xBD, (byte) 0xE2,
+ (byte) 0x80, (byte) 0xBC, (byte) 0x02, (byte) 0x00, (byte) 0x0A,
+ (byte) 0x00, (byte) 0x0B, (byte) 0x00, (byte) 0x0B, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x62, (byte) 0x61,
+ (byte) 0x73, (byte) 0x65, (byte) 0x36, (byte) 0x34, (byte) 0x0F,
+ (byte) 0x00, (byte) 0x0C, (byte) 0x03, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x03, (byte) 0x01, (byte) 0x02, (byte) 0x03,
+ (byte) 0x0F, (byte) 0x00, (byte) 0x0D, (byte) 0x06, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x01,
+ (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x03, (byte) 0x0F,
+ (byte) 0x00, (byte) 0x0E, (byte) 0x0A, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x03, (byte) 0x00 };
+ public static final byte[] persistentBytesNesting = new byte[] { (byte) 0x0C, (byte) 0x00,
+ (byte) 0x01, (byte) 0x08, (byte) 0x00, (byte) 0x01, (byte) 0x00,
+ (byte) 0x00, (byte) 0x7A, (byte) 0x69, (byte) 0x0B, (byte) 0x00,
+ (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x13,
+ (byte) 0x49, (byte) 0x20, (byte) 0x61, (byte) 0x6D, (byte) 0x20,
+ (byte) 0x61, (byte) 0x20, (byte) 0x62, (byte) 0x6F, (byte) 0x6E,
+ (byte) 0x6B, (byte) 0x2E, (byte) 0x2E, (byte) 0x2E, (byte) 0x20,
+ (byte) 0x78, (byte) 0x6F, (byte) 0x72, (byte) 0x21, (byte) 0x00,
+ (byte) 0x0C, (byte) 0x00, (byte) 0x02, (byte) 0x02, (byte) 0x00,
+ (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x00, (byte) 0x02,
+ (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x03, (byte) 0xD6,
+ (byte) 0x06, (byte) 0x00, (byte) 0x04, (byte) 0x69, (byte) 0x78,
+ (byte) 0x08, (byte) 0x00, (byte) 0x05, (byte) 0x01, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x0A, (byte) 0x00, (byte) 0x06,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x65,
+ (byte) 0xA0, (byte) 0xBC, (byte) 0x00, (byte) 0x04, (byte) 0x00,
+ (byte) 0x07, (byte) 0x40, (byte) 0x09, (byte) 0x21, (byte) 0xFB,
+ (byte) 0x54, (byte) 0x44, (byte) 0x2D, (byte) 0x18, (byte) 0x0B,
+ (byte) 0x00, (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x0D, (byte) 0x4A, (byte) 0x53, (byte) 0x4F, (byte) 0x4E,
+ (byte) 0x20, (byte) 0x54, (byte) 0x48, (byte) 0x49, (byte) 0x53,
+ (byte) 0x21, (byte) 0x20, (byte) 0x22, (byte) 0x01, (byte) 0x0B,
+ (byte) 0x00, (byte) 0x09, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x2E, (byte) 0xD3, (byte) 0x80, (byte) 0xE2, (byte) 0x85,
+ (byte) 0xAE, (byte) 0xCE, (byte) 0x9D, (byte) 0x20, (byte) 0xD0,
+ (byte) 0x9D, (byte) 0xCE, (byte) 0xBF, (byte) 0xE2, (byte) 0x85,
+ (byte) 0xBF, (byte) 0xD0, (byte) 0xBE, (byte) 0xC9, (byte) 0xA1,
+ (byte) 0xD0, (byte) 0xB3, (byte) 0xD0, (byte) 0xB0, (byte) 0xCF,
+ (byte) 0x81, (byte) 0xE2, (byte) 0x84, (byte) 0x8E, (byte) 0x20,
+ (byte) 0xCE, (byte) 0x91, (byte) 0x74, (byte) 0x74, (byte) 0xCE,
+ (byte) 0xB1, (byte) 0xE2, (byte) 0x85, (byte) 0xBD, (byte) 0xCE,
+ (byte) 0xBA, (byte) 0xEF, (byte) 0xBF, (byte) 0xBD, (byte) 0xE2,
+ (byte) 0x80, (byte) 0xBC, (byte) 0x02, (byte) 0x00, (byte) 0x0A,
+ (byte) 0x00, (byte) 0x0B, (byte) 0x00, (byte) 0x0B, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x62, (byte) 0x61,
+ (byte) 0x73, (byte) 0x65, (byte) 0x36, (byte) 0x34, (byte) 0x0F,
+ (byte) 0x00, (byte) 0x0C, (byte) 0x03, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x03, (byte) 0x01, (byte) 0x02, (byte) 0x03,
+ (byte) 0x0F, (byte) 0x00, (byte) 0x0D, (byte) 0x06, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x01,
+ (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x03, (byte) 0x0F,
+ (byte) 0x00, (byte) 0x0E, (byte) 0x0A, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x03, (byte) 0x00, (byte) 0x00 };
+ public static final byte[] persistentBytesHolyMoley = new byte[] { (byte) 0x0F, (byte) 0x00,
+ (byte) 0x01, (byte) 0x0C, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x02, (byte) 0x02, (byte) 0x00, (byte) 0x01, (byte) 0x01,
+ (byte) 0x02, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x03,
+ (byte) 0x00, (byte) 0x03, (byte) 0x23, (byte) 0x06, (byte) 0x00,
+ (byte) 0x04, (byte) 0x69, (byte) 0x78, (byte) 0x08, (byte) 0x00,
+ (byte) 0x05, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x0A, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x01, (byte) 0x65, (byte) 0xA0, (byte) 0xBC,
+ (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0x07, (byte) 0x40,
+ (byte) 0x09, (byte) 0x21, (byte) 0xFB, (byte) 0x54, (byte) 0x44,
+ (byte) 0x2D, (byte) 0x18, (byte) 0x0B, (byte) 0x00, (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0D, (byte) 0x4A,
+ (byte) 0x53, (byte) 0x4F, (byte) 0x4E, (byte) 0x20, (byte) 0x54,
+ (byte) 0x48, (byte) 0x49, (byte) 0x53, (byte) 0x21, (byte) 0x20,
+ (byte) 0x22, (byte) 0x01, (byte) 0x0B, (byte) 0x00, (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x2E, (byte) 0xD3,
+ (byte) 0x80, (byte) 0xE2, (byte) 0x85, (byte) 0xAE, (byte) 0xCE,
+ (byte) 0x9D, (byte) 0x20, (byte) 0xD0, (byte) 0x9D, (byte) 0xCE,
+ (byte) 0xBF, (byte) 0xE2, (byte) 0x85, (byte) 0xBF, (byte) 0xD0,
+ (byte) 0xBE, (byte) 0xC9, (byte) 0xA1, (byte) 0xD0, (byte) 0xB3,
+ (byte) 0xD0, (byte) 0xB0, (byte) 0xCF, (byte) 0x81, (byte) 0xE2,
+ (byte) 0x84, (byte) 0x8E, (byte) 0x20, (byte) 0xCE, (byte) 0x91,
+ (byte) 0x74, (byte) 0x74, (byte) 0xCE, (byte) 0xB1, (byte) 0xE2,
+ (byte) 0x85, (byte) 0xBD, (byte) 0xCE, (byte) 0xBA, (byte) 0xEF,
+ (byte) 0xBF, (byte) 0xBD, (byte) 0xE2, (byte) 0x80, (byte) 0xBC,
+ (byte) 0x02, (byte) 0x00, (byte) 0x0A, (byte) 0x00, (byte) 0x0B,
+ (byte) 0x00, (byte) 0x0B, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x06, (byte) 0x62, (byte) 0x61, (byte) 0x73, (byte) 0x65,
+ (byte) 0x36, (byte) 0x34, (byte) 0x0F, (byte) 0x00, (byte) 0x0C,
+ (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03,
+ (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x0F, (byte) 0x00,
+ (byte) 0x0D, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x03, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x02,
+ (byte) 0x00, (byte) 0x03, (byte) 0x0F, (byte) 0x00, (byte) 0x0E,
+ (byte) 0x0A, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x00,
+ (byte) 0x02, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x02,
+ (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x03, (byte) 0x00,
+ (byte) 0x03, (byte) 0xD6, (byte) 0x06, (byte) 0x00, (byte) 0x04,
+ (byte) 0x69, (byte) 0x78, (byte) 0x08, (byte) 0x00, (byte) 0x05,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0A,
+ (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x65, (byte) 0xA0, (byte) 0xBC, (byte) 0x00,
+ (byte) 0x04, (byte) 0x00, (byte) 0x07, (byte) 0x40, (byte) 0x09,
+ (byte) 0x21, (byte) 0xFB, (byte) 0x54, (byte) 0x44, (byte) 0x2D,
+ (byte) 0x18, (byte) 0x0B, (byte) 0x00, (byte) 0x08, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x0D, (byte) 0x4A, (byte) 0x53,
+ (byte) 0x4F, (byte) 0x4E, (byte) 0x20, (byte) 0x54, (byte) 0x48,
+ (byte) 0x49, (byte) 0x53, (byte) 0x21, (byte) 0x20, (byte) 0x22,
+ (byte) 0x01, (byte) 0x0B, (byte) 0x00, (byte) 0x09, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x2E, (byte) 0xD3, (byte) 0x80,
+ (byte) 0xE2, (byte) 0x85, (byte) 0xAE, (byte) 0xCE, (byte) 0x9D,
+ (byte) 0x20, (byte) 0xD0, (byte) 0x9D, (byte) 0xCE, (byte) 0xBF,
+ (byte) 0xE2, (byte) 0x85, (byte) 0xBF, (byte) 0xD0, (byte) 0xBE,
+ (byte) 0xC9, (byte) 0xA1, (byte) 0xD0, (byte) 0xB3, (byte) 0xD0,
+ (byte) 0xB0, (byte) 0xCF, (byte) 0x81, (byte) 0xE2, (byte) 0x84,
+ (byte) 0x8E, (byte) 0x20, (byte) 0xCE, (byte) 0x91, (byte) 0x74,
+ (byte) 0x74, (byte) 0xCE, (byte) 0xB1, (byte) 0xE2, (byte) 0x85,
+ (byte) 0xBD, (byte) 0xCE, (byte) 0xBA, (byte) 0xEF, (byte) 0xBF,
+ (byte) 0xBD, (byte) 0xE2, (byte) 0x80, (byte) 0xBC, (byte) 0x02,
+ (byte) 0x00, (byte) 0x0A, (byte) 0x00, (byte) 0x0B, (byte) 0x00,
+ (byte) 0x0B, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x06,
+ (byte) 0x62, (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x36,
+ (byte) 0x34, (byte) 0x0F, (byte) 0x00, (byte) 0x0C, (byte) 0x03,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x01,
+ (byte) 0x02, (byte) 0x03, (byte) 0x0F, (byte) 0x00, (byte) 0x0D,
+ (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03,
+ (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x00,
+ (byte) 0x03, (byte) 0x0F, (byte) 0x00, (byte) 0x0E, (byte) 0x0A,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x0E,
+ (byte) 0x00, (byte) 0x02, (byte) 0x0F, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x03, (byte) 0x0B, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x0B, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x0F, (byte) 0x74, (byte) 0x68, (byte) 0x65, (byte) 0x6E,
+ (byte) 0x20, (byte) 0x61, (byte) 0x20, (byte) 0x6F, (byte) 0x6E,
+ (byte) 0x65, (byte) 0x2C, (byte) 0x20, (byte) 0x74, (byte) 0x77,
+ (byte) 0x6F, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x06,
+ (byte) 0x74, (byte) 0x68, (byte) 0x72, (byte) 0x65, (byte) 0x65,
+ (byte) 0x21, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x06,
+ (byte) 0x46, (byte) 0x4F, (byte) 0x55, (byte) 0x52, (byte) 0x21,
+ (byte) 0x21, (byte) 0x0B, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x09,
+ (byte) 0x61, (byte) 0x6E, (byte) 0x64, (byte) 0x20, (byte) 0x61,
+ (byte) 0x20, (byte) 0x6F, (byte) 0x6E, (byte) 0x65, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x09, (byte) 0x61, (byte) 0x6E,
+ (byte) 0x64, (byte) 0x20, (byte) 0x61, (byte) 0x20, (byte) 0x74,
+ (byte) 0x77, (byte) 0x6F, (byte) 0x0D, (byte) 0x00, (byte) 0x03,
+ (byte) 0x0B, (byte) 0x0F, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03,
+ (byte) 0x74, (byte) 0x77, (byte) 0x6F, (byte) 0x0C, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x08, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01,
+ (byte) 0x0B, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x05, (byte) 0x57, (byte) 0x61, (byte) 0x69,
+ (byte) 0x74, (byte) 0x2E, (byte) 0x00, (byte) 0x08, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02,
+ (byte) 0x0B, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x05, (byte) 0x57, (byte) 0x68, (byte) 0x61,
+ (byte) 0x74, (byte) 0x3F, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x05, (byte) 0x74, (byte) 0x68, (byte) 0x72,
+ (byte) 0x65, (byte) 0x65, (byte) 0x0C, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x04, (byte) 0x7A, (byte) 0x65, (byte) 0x72, (byte) 0x6F,
+ (byte) 0x0C, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00 };
+
+ private static final byte[] kUnicodeBytes = { (byte) 0xd3, (byte) 0x80,
+ (byte) 0xe2, (byte) 0x85, (byte) 0xae, (byte) 0xce, (byte) 0x9d,
+ (byte) 0x20, (byte) 0xd0, (byte) 0x9d, (byte) 0xce, (byte) 0xbf,
+ (byte) 0xe2, (byte) 0x85, (byte) 0xbf, (byte) 0xd0, (byte) 0xbe,
+ (byte) 0xc9, (byte) 0xa1, (byte) 0xd0, (byte) 0xb3, (byte) 0xd0,
+ (byte) 0xb0, (byte) 0xcf, (byte) 0x81, (byte) 0xe2, (byte) 0x84,
+ (byte) 0x8e, (byte) 0x20, (byte) 0xce, (byte) 0x91, (byte) 0x74,
+ (byte) 0x74, (byte) 0xce, (byte) 0xb1, (byte) 0xe2, (byte) 0x85,
+ (byte) 0xbd, (byte) 0xce, (byte) 0xba, (byte) 0x83, (byte) 0xe2,
+ (byte) 0x80, (byte) 0xbc };
+
+ static {
+ try {
+ oneOfEach = new OneOfEach();
+ oneOfEach.setIm_true(true);
+ oneOfEach.setIm_false(false);
+ oneOfEach.setA_bite((byte) 0xd6);
+ oneOfEach.setInteger16((short) 27000);
+ oneOfEach.setInteger32(1 << 24);
+ oneOfEach.setInteger64((long) 6000 * 1000 * 1000);
+ oneOfEach.setDouble_precision(Math.PI);
+ oneOfEach.setSome_characters("JSON THIS! \"\1");
+ oneOfEach.setZomg_unicode(new String(kUnicodeBytes, StandardCharsets.UTF_8));
+ oneOfEach.setBase64(ByteBuffer.wrap("base64".getBytes()));
+ // byte, i16, and i64 lists are populated by default constructor
+
+ Bonk bonk = new Bonk();
+ bonk.setType(31337);
+ bonk.setMessage("I am a bonk... xor!");
+ nesting = new Nesting(bonk, oneOfEach);
+
+ holyMoley = new HolyMoley();
+ List<OneOfEach> big = new ArrayList<OneOfEach>();
+ big.add(new OneOfEach(oneOfEach));
+ big.add(nesting.my_ooe);
+ holyMoley.setBig(big);
+ holyMoley.getBig().get(0).setA_bite((byte) 0x22);
+ holyMoley.getBig().get(0).setA_bite((byte) 0x23);
+
+ holyMoley.setContain(new HashSet<List<String>>());
+ ArrayList<String> stage1 = new ArrayList<String>(2);
+ stage1.add("and a one");
+ stage1.add("and a two");
+ holyMoley.getContain().add(stage1);
+ stage1 = new ArrayList<String>(3);
+ stage1.add("then a one, two");
+ stage1.add("three!");
+ stage1.add("FOUR!!");
+ holyMoley.getContain().add(stage1);
+ stage1 = new ArrayList<String>(0);
+ holyMoley.getContain().add(stage1);
+
+ ArrayList<Bonk> stage2 = new ArrayList<Bonk>();
+ holyMoley.setBonks(new HashMap<String, List<Bonk>>());
+ // one empty
+ holyMoley.getBonks().put("zero", stage2);
+
+ // one with two
+ stage2 = new ArrayList<Bonk>();
+ Bonk b = new Bonk();
+ b.setType(1);
+ b.setMessage("Wait.");
+ stage2.add(b);
+ b = new Bonk();
+ b.setType(2);
+ b.setMessage("What?");
+ stage2.add(b);
+ holyMoley.getBonks().put("two", stage2);
+
+ // one with three
+ stage2 = new ArrayList<Bonk>();
+ b = new Bonk();
+ b.setType(3);
+ b.setMessage("quoth");
+ b = new Bonk();
+ b.setType(4);
+ b.setMessage("the raven");
+ b = new Bonk();
+ b.setType(5);
+ b.setMessage("nevermore");
+ holyMoley.getBonks().put("three", stage2);
+
+ // superhuge compact proto test struct
+ compactProtoTestStruct = new CompactProtoTestStruct(
+ thrift.test.DebugProtoTestConstants.COMPACT_TEST);
+ compactProtoTestStruct.setA_binary(ByteBuffer.wrap(new byte[] { 0, 1, 2,
+ 3, 4, 5, 6, 7, 8 }));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestDeepCopy.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestDeepCopy.java
new file mode 100644
index 000000000..acafaef10
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestDeepCopy.java
@@ -0,0 +1,34 @@
+package org.apache.thrift;
+
+import junit.framework.TestCase;
+import thrift.test.DeepCopyBar;
+import thrift.test.DeepCopyFoo;
+
+public class TestDeepCopy extends TestCase {
+
+ public void testDeepCopy() throws Exception {
+ final DeepCopyFoo foo = new DeepCopyFoo();
+
+ foo.addToL(new DeepCopyBar());
+ foo.addToS(new DeepCopyBar());
+ foo.putToM("test 3", new DeepCopyBar());
+
+ foo.addToLi(new thrift.test.Object());
+ foo.addToSi(new thrift.test.Object());
+ foo.putToMi("test 3", new thrift.test.Object());
+
+ foo.setBar(new DeepCopyBar());
+
+ final DeepCopyFoo deepCopyFoo = foo.deepCopy();
+
+ assertNotSame(foo.getBar(), deepCopyFoo.getBar());
+
+ assertNotSame(foo.getL().get(0), deepCopyFoo.getL().get(0));
+ assertNotSame(foo.getS().toArray(new DeepCopyBar[0])[0], deepCopyFoo.getS().toArray(new DeepCopyBar[0])[0]);
+ assertNotSame(foo.getM().get("test 3"), deepCopyFoo.getM().get("test 3"));
+
+ assertNotSame(foo.getLi().get(0), deepCopyFoo.getLi().get(0));
+ assertNotSame(foo.getSi().toArray(new thrift.test.Object[0])[0], deepCopyFoo.getSi().toArray(new thrift.test.Object[0])[0]);
+ assertNotSame(foo.getMi().get("test 3"), deepCopyFoo.getMi().get("test 3"));
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestEnumContainers.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestEnumContainers.java
new file mode 100644
index 000000000..683246ba6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestEnumContainers.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import junit.framework.TestCase;
+import thrift.test.enumcontainers.EnumContainersTestConstants;
+import thrift.test.enumcontainers.GodBean;
+import thrift.test.enumcontainers.GreekGodGoddess;
+
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+
+public class TestEnumContainers extends TestCase {
+
+ public void testEnumContainers() throws Exception {
+ final GodBean b1 = new GodBean();
+ b1.addToGoddess(GreekGodGoddess.HERA);
+ b1.getGoddess().add(GreekGodGoddess.APHRODITE);
+ b1.putToPower(GreekGodGoddess.ZEUS, 1000);
+ b1.getPower().put(GreekGodGoddess.HERA, 333);
+ b1.putToByAlias("Mr. Z", GreekGodGoddess.ZEUS);
+ b1.addToImages("Baths of Aphrodite 01.jpeg");
+
+ final GodBean b2 = new GodBean(b1);
+
+ final GodBean b3 = new GodBean();
+ {
+ final TSerializer serializer = new TSerializer();
+ final TDeserializer deserializer = new TDeserializer();
+
+ final byte[] bytes = serializer.serialize(b1);
+ deserializer.deserialize(b3, bytes);
+ }
+
+ assertTrue(b1.getGoddess() != b2.getGoddess());
+ assertTrue(b1.getPower() != b2.getPower());
+
+ assertTrue(b1.getGoddess() != b3.getGoddess());
+ assertTrue(b1.getPower() != b3.getPower());
+
+ for (GodBean each : new GodBean[]{b1, b2, b3}) {
+ assertTrue(each.getGoddess().contains(GreekGodGoddess.HERA));
+ assertFalse(each.getGoddess().contains(GreekGodGoddess.POSEIDON));
+ assertTrue(each.getGoddess() instanceof EnumSet);
+
+ assertEquals(Integer.valueOf(1000), each.getPower().get(GreekGodGoddess.ZEUS));
+ assertEquals(Integer.valueOf(333), each.getPower().get(GreekGodGoddess.HERA));
+ assertTrue(each.getPower() instanceof EnumMap);
+
+ assertTrue(each.getByAlias() instanceof HashMap);
+ assertTrue(each.getImages() instanceof HashSet);
+ }
+ }
+
+ public void testEnumConstants() {
+ assertEquals("lightning bolt", EnumContainersTestConstants.ATTRIBUTES.get(GreekGodGoddess.ZEUS));
+ assertTrue(EnumContainersTestConstants.ATTRIBUTES instanceof EnumMap);
+
+ assertTrue(EnumContainersTestConstants.BEAUTY.contains(GreekGodGoddess.APHRODITE));
+ assertTrue(EnumContainersTestConstants.BEAUTY instanceof EnumSet);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestFullCamel.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestFullCamel.java
new file mode 100644
index 000000000..fc9889890
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestFullCamel.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.util.HashSet;
+
+import junit.framework.TestCase;
+
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TType;
+
+import thrift.test.fullcamel.OneOfEachZZ;
+import thrift.test.fullcamel.UnderscoreSrv;
+
+// Sanity check for the code generated by 'fullcamel'.
+//
+public class TestFullCamel extends TestCase {
+
+ public void testCamelCaseSyntax() throws Exception {
+ TSerializer binarySerializer = new TSerializer(new TBinaryProtocol.Factory());
+ TDeserializer binaryDeserializer = new TDeserializer(new TBinaryProtocol.Factory());
+
+ OneOfEachZZ obj = new OneOfEachZZ();
+ obj.setABite((byte) 0xae);
+ obj.setImFalse(true);
+ byte[] serBytes = binarySerializer.serialize(obj);
+ binaryDeserializer.deserialize(obj, serBytes);
+ assertTrue( obj.getABite() == (byte) 0xae );
+ assertTrue( obj.isImFalse() == true );
+ }
+
+ public void testCamelCaseRpcMethods() throws Exception {
+ final UnderscoreSrv.Iface srv = new UnderscoreSrv.Iface() {
+ @Override
+ public long someRpcCall(String message) {
+ return 1l;
+ }
+ };
+ assertTrue(1l == srv.someRpcCall("test"));
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestMultiplexedProcessor.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestMultiplexedProcessor.java
new file mode 100644
index 000000000..85e7966bf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestMultiplexedProcessor.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.apache.thrift.protocol.TMessage;
+import org.apache.thrift.protocol.TMessageType;
+import org.apache.thrift.protocol.TProtocol;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestMultiplexedProcessor {
+ private TMultiplexedProcessor mp;
+ private TProtocol iprot;
+ private TProtocol oprot;
+
+ @Before
+ public void setUp() throws Exception {
+ mp = new TMultiplexedProcessor();
+ iprot = mock(TProtocol.class);
+ oprot = mock(TProtocol.class);
+ }
+
+ @Test(expected = TException.class)
+ public void testWrongMessageType() throws TException {
+ when (iprot.readMessageBegin()).thenReturn(new TMessage("service:func", TMessageType.REPLY, 42));
+ mp.process(iprot, oprot);
+ }
+
+ @Test(expected = TException.class)
+ public void testNoSuchService() throws TException {
+ when(iprot.readMessageBegin()).thenReturn(new TMessage("service:func", TMessageType.CALL, 42));
+
+ mp.process(iprot, oprot);
+ }
+
+ static class StubProcessor implements TProcessor {
+ @Override
+ public void process(TProtocol in, TProtocol out) throws TException {
+ TMessage msg = in.readMessageBegin();
+ if (!"func".equals(msg.name) || msg.type!=TMessageType.CALL || msg.seqid!=42) {
+ throw new TException("incorrect parameters");
+ }
+ out.writeMessageBegin(new TMessage("func", TMessageType.REPLY, 42));
+ }
+ }
+
+ @Test
+ public void testExistingService() throws TException {
+ when(iprot.readMessageBegin()).thenReturn(new TMessage("service:func", TMessageType.CALL, 42));
+ mp.registerProcessor("service", new StubProcessor());
+ mp.process(iprot, oprot);
+ verify(oprot).writeMessageBegin(any(TMessage.class));
+ }
+
+ @Test
+ public void testDefaultService() throws TException {
+ when(iprot.readMessageBegin()).thenReturn(new TMessage("func", TMessageType.CALL, 42));
+ mp.registerDefault(new StubProcessor());
+ mp.process(iprot, oprot);
+ verify(oprot).writeMessageBegin(any(TMessage.class));
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestOptionType.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestOptionType.java
new file mode 100644
index 000000000..f70285ffa
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestOptionType.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import junit.framework.TestCase;
+import org.apache.thrift.Option;
+
+// Tests and documents behavior for the "Option<T>" type
+public class TestOptionType extends TestCase {
+ public void testSome() throws Exception {
+ String name = "Chuck Norris";
+ Option<String> option = Option.fromNullable(name);
+
+ assertTrue(option instanceof Option.Some);
+ assertTrue(option.isDefined());
+ assertEquals("Some(Chuck Norris)", option.toString());
+ assertEquals(option.or("default value"), "Chuck Norris");
+ assertEquals(option.get(),"Chuck Norris");
+ }
+
+ public void testNone() throws Exception {
+ String name = null;
+ Option<String> option = Option.fromNullable(name);
+
+ assertTrue(option instanceof Option.None);
+ assertFalse(option.isDefined());
+ assertEquals("None", option.toString());
+ assertEquals(option.or("default value"), "default value");
+ // Expect exception
+ try {
+ Object value = option.get();
+ fail("Expected IllegalStateException, got no exception");
+ } catch (IllegalStateException ex) {
+
+ } catch(Exception ex) {
+ fail("Expected IllegalStateException, got some other exception: "+ex.toString());
+ }
+ }
+
+ public void testMakeSome() throws Exception {
+ Option<String> some = Option.some("wee");
+ assertTrue(some instanceof Option.Some);
+ }
+
+ public void testMakeNone() throws Exception {
+ Option<Integer> none = Option.none();
+ assertTrue(none instanceof Option.None);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestOptionals.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestOptionals.java
new file mode 100644
index 000000000..d1591ee2c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestOptionals.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import junit.framework.TestCase;
+
+import thrift.test.Opt4;
+import thrift.test.Opt30;
+import thrift.test.Opt64;
+import thrift.test.Opt80;
+
+// Exercises the isSet methods using structs from from ManyOptionals.thrift
+public class TestOptionals extends TestCase {
+ public void testEncodingUtils() throws Exception {
+ assertEquals((short)0x8, EncodingUtils.setBit((short)0, 3, true));
+ assertEquals((short)0, EncodingUtils.setBit((short)0x8, 3, false));
+ assertEquals(true, EncodingUtils.testBit((short)0x8, 3));
+ assertEquals(false, EncodingUtils.testBit((short)0x8, 4));
+
+ assertEquals((short)Short.MIN_VALUE, EncodingUtils.setBit((short)0, 15, true));
+ assertEquals((short)0, EncodingUtils.setBit((short)Short.MIN_VALUE, 15, false));
+ assertEquals(true, EncodingUtils.testBit(Short.MIN_VALUE, 15));
+ assertEquals(false, EncodingUtils.testBit(Short.MIN_VALUE, 14));
+ }
+
+ public void testOpt4() throws Exception {
+ Opt4 x = new Opt4();
+ assertEquals(false, x.isSetDef1());
+ x.setDef1(3);
+ assertEquals(true, x.isSetDef1());
+ assertEquals(false, x.isSetDef2());
+
+ Opt4 copy = new Opt4(x);
+ assertEquals(true, copy.isSetDef1());
+ copy.unsetDef1();
+ assertEquals(false, copy.isSetDef1());
+ assertEquals(true, x.isSetDef1());
+ }
+
+ public void testOpt30() throws Exception {
+ Opt30 x = new Opt30();
+ assertEquals(false, x.isSetDef1());
+ x.setDef1(3);
+ assertEquals(true, x.isSetDef1());
+ assertEquals(false, x.isSetDef2());
+ }
+
+ public void testOpt64() throws Exception {
+ Opt64 x = new Opt64();
+ assertEquals(false, x.isSetDef1());
+ x.setDef1(3);
+ assertEquals(true, x.isSetDef1());
+ assertEquals(false, x.isSetDef2());
+ x.setDef64(22);
+ assertEquals(true, x.isSetDef64());
+ assertEquals(false, x.isSetDef63());
+ }
+
+ public void testOpt80() throws Exception {
+ Opt80 x = new Opt80();
+ assertEquals(false, x.isSetDef1());
+ x.setDef1(3);
+ assertEquals(true, x.isSetDef1());
+ assertEquals(false, x.isSetDef2());
+
+ Opt80 copy = new Opt80(x);
+ copy.unsetDef1();
+ assertEquals(false, copy.isSetDef1());
+ assertEquals(true, x.isSetDef1());
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestRenderedDoubleConstants.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestRenderedDoubleConstants.java
new file mode 100644
index 000000000..d691fe356
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestRenderedDoubleConstants.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.util.List;
+import junit.framework.TestCase;
+import static org.junit.Assert.*;
+import org.junit.Test;
+import thrift.test.DoubleConstantsTestConstants;
+
+public class TestRenderedDoubleConstants extends TestCase {
+ private static final double EPSILON = 0.0000001;
+ private static final String ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST =
+ "failed to verify a double constant generated by Thrift (expected = %f, got = %f)";
+ private static final String ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_LIST_TEST =
+ "failed to verify a list item by Thrift (expected = %f, got = %f)";
+ private static final String ASSERTION_MESSAGE_FOR_TYPE_CHECKS =
+ "the rendered variable with name %s is not of double type";
+
+ // to make sure lists containing doubles are generated correctly
+ public void testRenderedDoubleList() throws Exception {
+ final double[] EXPECTED_LIST =
+ {1d,-100d,100d,9223372036854775807d,-9223372036854775807d,3.14159265359,1000000.1,-1000000.1,1.7e+308,
+ -1.7e+308,9223372036854775816.43,-9223372036854775816.43};
+ assertEquals(EXPECTED_LIST.length, DoubleConstantsTestConstants.DOUBLE_LIST_TEST.size());
+ for (int i = 0; i < EXPECTED_LIST.length; ++i) {
+ assertEquals(
+ String.format(
+ ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_LIST_TEST,
+ EXPECTED_LIST[i],
+ DoubleConstantsTestConstants.DOUBLE_LIST_TEST.get(i)),
+ EXPECTED_LIST[i], DoubleConstantsTestConstants.DOUBLE_LIST_TEST.get(i), EPSILON);
+ }
+ }
+
+ // to make sure the variables inside Thrift files are generated correctly
+ public void testRenderedDoubleConstants() throws Exception {
+ final double EXPECTED_DOUBLE_ASSIGNED_TO_INT_CONSTANT = 1.0;
+ final double EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT = -100.0;
+ final double EXPECTED_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT = 9223372036854775807.0;
+ final double EXPECTED_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT = -9223372036854775807.0;
+ final double EXPECTED_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS = 3.14159265359;
+ final double EXPECTED_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE = 1000000.1;
+ final double EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE = -1000000.1;
+ final double EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE = 1.7e+308;
+ final double EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE = 9223372036854775816.43;
+ final double EXPECTED_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE = -1.7e+308;
+ final double EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE = -9223372036854775816.43;
+ assertEquals(
+ String.format(
+ ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_INT_CONSTANT,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST),
+ EXPECTED_DOUBLE_ASSIGNED_TO_INT_CONSTANT,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST, EPSILON);
+ assertEquals(
+ String.format(
+ ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST),
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST, EPSILON);
+ assertEquals(
+ String.format(
+ ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST),
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST, EPSILON);
+ assertEquals(
+ String.format(
+ ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST),
+ EXPECTED_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST, EPSILON);
+ assertEquals(
+ String.format(
+ ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST),
+ EXPECTED_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST, EPSILON);
+ assertEquals(
+ String.format(
+ ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST),
+ EXPECTED_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST, EPSILON);
+ assertEquals(
+ String.format(
+ ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST),
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST, EPSILON);
+ assertEquals(
+ String.format(
+ ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST),
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST, EPSILON);
+ assertEquals(
+ String.format(
+ ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST),
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST, EPSILON);
+ assertEquals(
+ String.format(
+ ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST),
+ EXPECTED_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST, EPSILON);
+ assertEquals(
+ String.format(
+ ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST),
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE,
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST, EPSILON);
+ assertTrue(
+ String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST"),
+ Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST));
+ assertTrue(
+ String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST"),
+ Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST));
+ assertTrue(
+ String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST"),
+ Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST));
+ assertTrue(
+ String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST"),
+ Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST));
+ assertTrue(
+ String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST"),
+ Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST));
+ assertTrue(
+ String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST"),
+ Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST));
+ assertTrue(
+ String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST"),
+ Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST));
+ //assertTrue(
+ // String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST"),
+ // Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST));
+ assertTrue(
+ String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST"),
+ Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST));
+ //assertTrue(
+ // String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST"),
+ // Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST));
+ assertTrue(
+ String.format(
+ ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST"),
+ Double.class.isInstance(
+ DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST));
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestReuse.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestReuse.java
new file mode 100644
index 000000000..b44abd0d2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestReuse.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.util.HashSet;
+
+import org.apache.thrift.protocol.TBinaryProtocol;
+
+import thrift.test.Reuse;
+
+// Tests reusing objects for deserialization.
+//
+public class TestReuse extends TestStruct {
+
+ public void testReuseObject() throws Exception {
+ TSerializer binarySerializer = new TSerializer(new TBinaryProtocol.Factory());
+ TDeserializer binaryDeserializer = new TDeserializer(new TBinaryProtocol.Factory());
+
+ Reuse ru1 = new Reuse();
+ HashSet<String> hs1 = new HashSet<String>();
+ byte[] serBytes;
+ String st1 = new String("string1");
+ String st2 = new String("string2");
+
+ ru1.setVal1(11);
+ ru1.setVal2(hs1);
+ ru1.addToVal2(st1);
+
+ serBytes = binarySerializer.serialize(ru1);
+
+ // update hash set after serialization
+ hs1.add(st2);
+
+ binaryDeserializer.deserialize(ru1, serBytes);
+
+ assertTrue( ru1.getVal2() == hs1 );
+ assertTrue( hs1.size() == 2 );
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestStruct.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestStruct.java
new file mode 100644
index 000000000..3379ed120
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestStruct.java
@@ -0,0 +1,396 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.thrift.meta_data.FieldMetaData;
+import org.apache.thrift.meta_data.ListMetaData;
+import org.apache.thrift.meta_data.MapMetaData;
+import org.apache.thrift.meta_data.SetMetaData;
+import org.apache.thrift.meta_data.StructMetaData;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TType;
+
+import thrift.test.Bonk;
+import thrift.test.CrazyNesting;
+import thrift.test.HolyMoley;
+import thrift.test.Insanity;
+import thrift.test.JavaTestHelper;
+import thrift.test.Nesting;
+import thrift.test.Numberz;
+import thrift.test.OneOfEach;
+import thrift.test.StructA;
+import thrift.test.StructB;
+import thrift.test.Xtruct;
+
+public class TestStruct extends TestCase {
+ public void testIdentity() throws Exception {
+ TSerializer binarySerializer = new TSerializer(new TBinaryProtocol.Factory());
+ TDeserializer binaryDeserializer = new TDeserializer(new TBinaryProtocol.Factory());
+
+ OneOfEach ooe = Fixtures.oneOfEach;
+
+ Nesting n = new Nesting();
+ n.my_ooe = ooe;
+ n.my_ooe.integer16 = 16;
+ n.my_ooe.integer32 = 32;
+ n.my_ooe.integer64 = 64;
+ n.my_ooe.double_precision = (Math.sqrt(5)+1)/2;
+ n.my_ooe.some_characters = ":R (me going \"rrrr\")";
+ n.my_ooe.zomg_unicode = "\u04c0\u216e\u039d\u0020\u041d\u03bf\u217f"+
+ "\u043e\u0261\u0433\u0430\u03c1\u210e\u0020"+
+ "\u0391\u0074\u0074\u03b1\u217d\u03ba\u01c3"+
+ "\u203c";
+ n.my_bonk = Fixtures.nesting.my_bonk;
+
+ HolyMoley hm = Fixtures.holyMoley;
+
+ OneOfEach ooe2 = new OneOfEach();
+ binaryDeserializer.deserialize(
+ ooe2,
+ binarySerializer.serialize(ooe));
+
+ assertEquals(ooe, ooe2);
+ assertEquals(ooe.hashCode(), ooe2.hashCode());
+
+
+ Nesting n2 = new Nesting();
+ binaryDeserializer.deserialize(
+ n2,
+ binarySerializer.serialize(n));
+
+ assertEquals(n, n2);
+ assertEquals(n.hashCode(), n2.hashCode());
+
+ HolyMoley hm2 = new HolyMoley();
+ binaryDeserializer.deserialize(
+ hm2,
+ binarySerializer.serialize(hm));
+
+ assertEquals(hm, hm2);
+ assertEquals(hm.hashCode(), hm2.hashCode());
+ }
+
+ public void testDeepCopy() throws Exception {
+ TSerializer binarySerializer = new TSerializer(new TBinaryProtocol.Factory());
+ TDeserializer binaryDeserializer = new TDeserializer(new TBinaryProtocol.Factory());
+
+ HolyMoley hm = Fixtures.holyMoley;
+
+ byte[] binaryCopy = binarySerializer.serialize(hm);
+ HolyMoley hmCopy = new HolyMoley();
+ binaryDeserializer.deserialize(hmCopy, binaryCopy);
+ HolyMoley hmCopy2 = new HolyMoley(hm);
+
+ assertEquals(hm, hmCopy);
+ assertEquals(hmCopy, hmCopy2);
+
+ // change binary value in original object
+ hm.big.get(0).base64.array()[0]++;
+ // make sure the change didn't propagate to the copied object
+ assertFalse(hm.equals(hmCopy2));
+ hm.big.get(0).base64.array()[0]--; // undo change
+
+ hmCopy2.bonks.get("two").get(1).message = "What else?";
+
+ assertFalse(hm.equals(hmCopy2));
+ }
+
+ public void testCompareTo() throws Exception {
+ Bonk bonk1 = new Bonk();
+ Bonk bonk2 = new Bonk();
+
+ // Compare empty thrift objects.
+ assertEquals(0, bonk1.compareTo(bonk2));
+
+ bonk1.setMessage("m");
+
+ // Compare one thrift object with a filled in field and another without it.
+ assertTrue(bonk1.compareTo(bonk2) > 0);
+ assertTrue(bonk2.compareTo(bonk1) < 0);
+
+ // Compare both have filled-in fields.
+ bonk2.setMessage("z");
+ assertTrue(bonk1.compareTo(bonk2) < 0);
+ assertTrue(bonk2.compareTo(bonk1) > 0);
+
+ // Compare bonk1 has a field filled in that bonk2 doesn't.
+ bonk1.setType(123);
+ assertTrue(bonk1.compareTo(bonk2) > 0);
+ assertTrue(bonk2.compareTo(bonk1) < 0);
+
+ // Compare bonk1 and bonk2 equal.
+ bonk2.setType(123);
+ bonk2.setMessage("m");
+ assertEquals(0, bonk1.compareTo(bonk2));
+ }
+
+ public void testCompareToWithDataStructures() {
+ Insanity insanity1 = new Insanity();
+ Insanity insanity2 = new Insanity();
+
+ // Both empty.
+ expectEquals(insanity1, insanity2);
+
+ insanity1.setUserMap(new HashMap<Numberz, Long>());
+ // insanity1.map = {}, insanity2.map = null
+ expectGreaterThan(insanity1, insanity2);
+
+ // insanity1.map = {2:1}, insanity2.map = null
+ insanity1.getUserMap().put(Numberz.TWO, 1l);
+ expectGreaterThan(insanity1, insanity2);
+
+ // insanity1.map = {2:1}, insanity2.map = {}
+ insanity2.setUserMap(new HashMap<Numberz, Long>());
+ expectGreaterThan(insanity1, insanity2);
+
+ // insanity1.map = {2:1}, insanity2.map = {2:2}
+ insanity2.getUserMap().put(Numberz.TWO, 2l);
+ expectLessThan(insanity1, insanity2);
+
+ // insanity1.map = {2:1, 3:5}, insanity2.map = {2:2}
+ insanity1.getUserMap().put(Numberz.THREE, 5l);
+ expectGreaterThan(insanity1, insanity2);
+
+ // insanity1.map = {2:1, 3:5}, insanity2.map = {2:1, 4:5}
+ insanity2.getUserMap().put(Numberz.TWO, 1l);
+ insanity2.getUserMap().put(Numberz.FIVE, 5l);
+ expectLessThan(insanity1, insanity2);
+ }
+
+ private void expectLessThan(Insanity insanity1, Insanity insanity2) {
+ int compareTo = insanity1.compareTo(insanity2);
+ assertTrue(insanity1 + " should be less than " + insanity2 + ", but is: " + compareTo, compareTo < 0);
+ }
+
+ private void expectGreaterThan(Insanity insanity1, Insanity insanity2) {
+ int compareTo = insanity1.compareTo(insanity2);
+ assertTrue(insanity1 + " should be greater than " + insanity2 + ", but is: " + compareTo, compareTo > 0);
+ }
+
+ private void expectEquals(Insanity insanity1, Insanity insanity2) {
+ int compareTo = insanity1.compareTo(insanity2);
+ assertEquals(insanity1 + " should be equal to " + insanity2 + ", but is: " + compareTo, 0, compareTo);
+ }
+
+ public void testMetaData() throws Exception {
+ Map<CrazyNesting._Fields, FieldMetaData> mdMap = CrazyNesting.metaDataMap;
+
+ // Check for struct fields existence
+ assertEquals(4, mdMap.size());
+ assertTrue(mdMap.containsKey(CrazyNesting._Fields.SET_FIELD));
+ assertTrue(mdMap.containsKey(CrazyNesting._Fields.LIST_FIELD));
+ assertTrue(mdMap.containsKey(CrazyNesting._Fields.STRING_FIELD));
+ assertTrue(mdMap.containsKey(CrazyNesting._Fields.BINARY_FIELD));
+
+ // Check for struct fields contents
+ assertEquals("string_field", mdMap.get(CrazyNesting._Fields.STRING_FIELD).fieldName);
+ assertEquals("list_field", mdMap.get(CrazyNesting._Fields.LIST_FIELD).fieldName);
+ assertEquals("set_field", mdMap.get(CrazyNesting._Fields.SET_FIELD).fieldName);
+ assertEquals("binary_field", mdMap.get(CrazyNesting._Fields.BINARY_FIELD).fieldName);
+
+ assertEquals(TFieldRequirementType.DEFAULT, mdMap.get(CrazyNesting._Fields.STRING_FIELD).requirementType);
+ assertEquals(TFieldRequirementType.REQUIRED, mdMap.get(CrazyNesting._Fields.LIST_FIELD).requirementType);
+ assertEquals(TFieldRequirementType.OPTIONAL, mdMap.get(CrazyNesting._Fields.SET_FIELD).requirementType);
+
+ assertEquals(TType.STRING, mdMap.get(CrazyNesting._Fields.STRING_FIELD).valueMetaData.type);
+ assertFalse(mdMap.get(CrazyNesting._Fields.STRING_FIELD).valueMetaData.isBinary());
+ assertEquals(TType.LIST, mdMap.get(CrazyNesting._Fields.LIST_FIELD).valueMetaData.type);
+ assertEquals(TType.SET, mdMap.get(CrazyNesting._Fields.SET_FIELD).valueMetaData.type);
+ assertEquals(TType.STRING, mdMap.get(CrazyNesting._Fields.BINARY_FIELD).valueMetaData.type);
+ assertTrue(mdMap.get(CrazyNesting._Fields.BINARY_FIELD).valueMetaData.isBinary());
+
+ // Check nested structures
+ assertTrue(mdMap.get(CrazyNesting._Fields.LIST_FIELD).valueMetaData.isContainer());
+
+ assertFalse(mdMap.get(CrazyNesting._Fields.LIST_FIELD).valueMetaData.isStruct());
+
+ assertEquals(TType.STRUCT, ((MapMetaData)((ListMetaData)((SetMetaData)((MapMetaData)((MapMetaData)((ListMetaData)mdMap.get(CrazyNesting._Fields.LIST_FIELD).valueMetaData).elemMetaData).valueMetaData).valueMetaData).elemMetaData).elemMetaData).keyMetaData.type);
+
+ assertEquals(Insanity.class, ((StructMetaData)((MapMetaData)((ListMetaData)((SetMetaData)((MapMetaData)((MapMetaData)((ListMetaData)mdMap.get(CrazyNesting._Fields.LIST_FIELD).valueMetaData).elemMetaData).valueMetaData).valueMetaData).elemMetaData).elemMetaData).keyMetaData).structClass);
+
+ // Check that FieldMetaData contains a map with metadata for all generated struct classes
+ assertNotNull(FieldMetaData.getStructMetaDataMap(CrazyNesting.class));
+ assertNotNull(FieldMetaData.getStructMetaDataMap(Insanity.class));
+ assertNotNull(FieldMetaData.getStructMetaDataMap(Xtruct.class));
+
+ assertEquals(CrazyNesting.metaDataMap, FieldMetaData.getStructMetaDataMap(CrazyNesting.class));
+ assertEquals(Insanity.metaDataMap, FieldMetaData.getStructMetaDataMap(Insanity.class));
+
+ for (Map.Entry<? extends TFieldIdEnum, FieldMetaData> mdEntry : mdMap.entrySet()) {
+ assertEquals(mdEntry.getKey(), CrazyNesting._Fields.findByName(mdEntry.getValue().fieldName));
+ }
+
+ MapMetaData vmd = (MapMetaData)Insanity.metaDataMap.get(Insanity._Fields.USER_MAP).valueMetaData;
+ assertTrue(vmd.valueMetaData.isTypedef());
+ assertFalse(vmd.keyMetaData.isTypedef());
+ }
+
+ public void testToString() throws Exception {
+ JavaTestHelper object = new JavaTestHelper();
+ object.req_int = 0;
+ object.req_obj = "";
+
+ object.req_bin = ByteBuffer.wrap(new byte[] {
+ 0, -1, 2, -3, 4, -5, 6, -7, 8, -9, 10, -11, 12, -13, 14, -15,
+ 16, -17, 18, -19, 20, -21, 22, -23, 24, -25, 26, -27, 28, -29,
+ 30, -31, 32, -33, 34, -35, 36, -37, 38, -39, 40, -41, 42, -43, 44,
+ -45, 46, -47, 48, -49, 50, -51, 52, -53, 54, -55, 56, -57, 58, -59,
+ 60, -61, 62, -63, 64, -65, 66, -67, 68, -69, 70, -71, 72, -73, 74,
+ -75, 76, -77, 78, -79, 80, -81, 82, -83, 84, -85, 86, -87, 88, -89,
+ 90, -91, 92, -93, 94, -95, 96, -97, 98, -99, 100, -101, 102, -103,
+ 104, -105, 106, -107, 108, -109, 110, -111, 112, -113, 114, -115,
+ 116, -117, 118, -119, 120, -121, 122, -123, 124, -125, 126, -127,
+ });
+
+ assertEquals("JavaTestHelper(req_int:0, req_obj:, req_bin:"+
+ "00 FF 02 FD 04 FB 06 F9 08 F7 0A F5 0C F3 0E F1 10 EF 12 ED 14 "+
+ "EB 16 E9 18 E7 1A E5 1C E3 1E E1 20 DF 22 DD 24 DB 26 D9 28 D7 "+
+ "2A D5 2C D3 2E D1 30 CF 32 CD 34 CB 36 C9 38 C7 3A C5 3C C3 3E "+
+ "C1 40 BF 42 BD 44 BB 46 B9 48 B7 4A B5 4C B3 4E B1 50 AF 52 AD "+
+ "54 AB 56 A9 58 A7 5A A5 5C A3 5E A1 60 9F 62 9D 64 9B 66 99 68 "+
+ "97 6A 95 6C 93 6E 91 70 8F 72 8D 74 8B 76 89 78 87 7A 85 7C 83 "+
+ "7E 81)",
+ object.toString());
+
+ object.req_bin = ByteBuffer.wrap(new byte[] {
+ 0, -1, 2, -3, 4, -5, 6, -7, 8, -9, 10, -11, 12, -13, 14, -15,
+ 16, -17, 18, -19, 20, -21, 22, -23, 24, -25, 26, -27, 28, -29,
+ 30, -31, 32, -33, 34, -35, 36, -37, 38, -39, 40, -41, 42, -43, 44,
+ -45, 46, -47, 48, -49, 50, -51, 52, -53, 54, -55, 56, -57, 58, -59,
+ 60, -61, 62, -63, 64, -65, 66, -67, 68, -69, 70, -71, 72, -73, 74,
+ -75, 76, -77, 78, -79, 80, -81, 82, -83, 84, -85, 86, -87, 88, -89,
+ 90, -91, 92, -93, 94, -95, 96, -97, 98, -99, 100, -101, 102, -103,
+ 104, -105, 106, -107, 108, -109, 110, -111, 112, -113, 114, -115,
+ 116, -117, 118, -119, 120, -121, 122, -123, 124, -125, 126, -127,
+ 0,
+ });
+
+ assertEquals("JavaTestHelper(req_int:0, req_obj:, req_bin:"+
+ "00 FF 02 FD 04 FB 06 F9 08 F7 0A F5 0C F3 0E F1 10 EF 12 ED 14 "+
+ "EB 16 E9 18 E7 1A E5 1C E3 1E E1 20 DF 22 DD 24 DB 26 D9 28 D7 "+
+ "2A D5 2C D3 2E D1 30 CF 32 CD 34 CB 36 C9 38 C7 3A C5 3C C3 3E "+
+ "C1 40 BF 42 BD 44 BB 46 B9 48 B7 4A B5 4C B3 4E B1 50 AF 52 AD "+
+ "54 AB 56 A9 58 A7 5A A5 5C A3 5E A1 60 9F 62 9D 64 9B 66 99 68 "+
+ "97 6A 95 6C 93 6E 91 70 8F 72 8D 74 8B 76 89 78 87 7A 85 7C 83 "+
+ "7E 81...)",
+ object.toString());
+
+ object.req_bin = ByteBuffer.wrap(new byte[] {});
+ object.setOpt_binIsSet(true);
+
+ assertEquals("JavaTestHelper(req_int:0, req_obj:, req_bin:)",
+ object.toString());
+ }
+
+ private static void assertArrayEquals(byte[] expected, byte[] actual) {
+ if (!java.util.Arrays.equals(expected, actual)) {
+ fail("Expected byte array did not match actual.");
+ }
+ }
+
+ public void testBytesBufferFeatures() throws Exception {
+
+ final String testString = "testBytesBufferFeatures";
+ final JavaTestHelper o = new JavaTestHelper();
+
+ o.setReq_bin((ByteBuffer)null);
+ assertNull(o.getReq_bin());
+
+ o.setReq_bin(ByteBuffer.wrap(testString.getBytes()));
+ assertArrayEquals(testString.getBytes(), o.getReq_bin());
+
+ o.setReq_bin((byte[])null);
+ assertNull(o.getReq_bin());
+
+ o.setReq_bin(testString.getBytes());
+ assertArrayEquals(testString.getBytes(), o.getReq_bin());
+
+ o.setFieldValue(JavaTestHelper._Fields.REQ_BIN, null);
+ assertNull(o.getReq_bin());
+
+ o.setFieldValue(JavaTestHelper._Fields.REQ_BIN, testString.getBytes());
+ assertArrayEquals(testString.getBytes(), o.getReq_bin());
+
+ o.setFieldValue(JavaTestHelper._Fields.REQ_BIN, null);
+ assertNull(o.getReq_bin());
+
+ o.setFieldValue(JavaTestHelper._Fields.REQ_BIN, ByteBuffer.wrap(testString.getBytes()));
+ assertArrayEquals(testString.getBytes(), o.getReq_bin());
+ }
+
+ public void testJavaSerializable() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+
+ OneOfEach ooe = Fixtures.oneOfEach;
+
+ // Serialize ooe the Java way...
+ oos.writeObject(ooe);
+ byte[] serialized = baos.toByteArray();
+
+ // Attempt to deserialize it
+ ByteArrayInputStream bais = new ByteArrayInputStream(serialized);
+ ObjectInputStream ois = new ObjectInputStream(bais);
+ OneOfEach ooe2 = (OneOfEach) ois.readObject();
+
+ assertEquals(ooe, ooe2);
+ }
+
+ public void testSubStructValidation() throws Exception {
+ StructA valid = new StructA("valid");
+ StructA invalid = new StructA();
+
+ StructB b = new StructB();
+ try {
+ b.validate();
+ fail();
+ } catch (TException e) {
+ // expected
+ }
+
+ b = new StructB().setAb(valid);
+ b.validate();
+
+ b = new StructB().setAb(invalid);
+ try {
+ b.validate();
+ fail();
+ } catch (TException e) {
+ // expected
+ }
+
+ b = new StructB().setAb(valid).setAa(invalid);
+ try {
+ b.validate();
+ fail();
+ } catch (TException e) {
+ // expected
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTBaseHelper.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTBaseHelper.java
new file mode 100644
index 000000000..8b0855525
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTBaseHelper.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+public class TestTBaseHelper extends TestCase {
+ public void testByteArrayComparison() {
+ assertTrue(TBaseHelper.compareTo(new byte[]{'a','b'}, new byte[]{'a','c'}) < 0);
+ }
+
+ public void testSets() {
+ Set<String> a = new HashSet<String>();
+ Set<String> b = new HashSet<String>();
+
+ assertTrue(TBaseHelper.compareTo(a, b) == 0);
+
+ a.add("test");
+
+ assertTrue(TBaseHelper.compareTo(a, b) > 0);
+
+ b.add("test");
+
+ assertTrue(TBaseHelper.compareTo(a, b) == 0);
+
+ b.add("aardvark");
+
+ assertTrue(TBaseHelper.compareTo(a, b) < 0);
+
+ a.add("test2");
+
+ assertTrue(TBaseHelper.compareTo(a, b) > 0);
+ }
+
+ public void testNestedStructures() {
+ Set<List<String>> a = new HashSet<List<String>>();
+ Set<List<String>> b = new HashSet<List<String>>();
+
+ a.add(Arrays.asList(new String[] {"a","b"}));
+ b.add(Arrays.asList(new String[] {"a","b", "c"}));
+ a.add(Arrays.asList(new String[] {"a","b"}));
+ b.add(Arrays.asList(new String[] {"a","b", "c"}));
+
+ assertTrue(TBaseHelper.compareTo(a, b) < 0);
+ }
+
+ public void testMapsInSets() {
+ Set<Map<String, Long>> a = new HashSet<Map<String, Long>>();
+ Set<Map<String, Long>> b = new HashSet<Map<String, Long>>();
+
+ assertTrue(TBaseHelper.compareTo(a, b) == 0);
+
+ Map<String, Long> innerA = new HashMap<String, Long>();
+ Map<String, Long> innerB = new HashMap<String, Long>();
+ a.add(innerA);
+ b.add(innerB);
+
+ innerA.put("a", 1l);
+ innerB.put("a", 2l);
+
+ assertTrue(TBaseHelper.compareTo(a, b) < 0);
+ }
+
+ public void testByteArraysInMaps() {
+ Map<byte[], Long> a = new HashMap<byte[], Long>();
+ Map<byte[], Long> b = new HashMap<byte[], Long>();
+
+ assertTrue(TBaseHelper.compareTo(a, b) == 0);
+
+ a.put(new byte[]{'a','b'}, 1000L);
+ b.put(new byte[]{'a','b'}, 1000L);
+ a.put(new byte[]{'a','b', 'd'}, 1000L);
+ b.put(new byte[]{'a','b', 'a'}, 1000L);
+ assertTrue(TBaseHelper.compareTo(a, b) > 0);
+ }
+
+ public void testMapsWithNulls() {
+ Map<String, String> a = new HashMap<String, String>();
+ Map<String, String> b = new HashMap<String, String>();
+ a.put("a", null);
+ a.put("b", null);
+ b.put("a", null);
+ b.put("b", null);
+
+ assertTrue(TBaseHelper.compareTo(a, b) == 0);
+ }
+
+ public void testMapKeyComparison() {
+ Map<String, String> a = new HashMap<String, String>();
+ Map<String, String> b = new HashMap<String, String>();
+ a.put("a", "a");
+ b.put("b", "a");
+
+ assertTrue(TBaseHelper.compareTo(a, b) < 0);
+ }
+
+ public void testMapValueComparison() {
+ Map<String, String> a = new HashMap<String, String>();
+ Map<String, String> b = new HashMap<String, String>();
+ a.put("a", "b");
+ b.put("a", "a");
+
+ assertTrue(TBaseHelper.compareTo(a, b) > 0);
+ }
+
+ public void testByteArraysInSets() {
+ Set<byte[]> a = new HashSet<byte[]>();
+ Set<byte[]> b = new HashSet<byte[]>();
+
+ if (TBaseHelper.compareTo(a, b) != 0)
+ throw new RuntimeException("Set compare failed:" + a + " vs. " + b);
+
+ a.add(new byte[]{'a','b'});
+ b.add(new byte[]{'a','b'});
+ a.add(new byte[]{'a','b', 'd'});
+ b.add(new byte[]{'a','b', 'a'});
+ assertTrue(TBaseHelper.compareTo(a, b) > 0);
+ }
+
+ public void testByteBufferToByteArray() throws Exception {
+ byte[] b1 = {10,9,8,7,6,5,4,3,2,1,0};
+ byte[] b2 = TBaseHelper.byteBufferToByteArray(ByteBuffer.wrap(b1));
+ assertEquals("b1 and b2 should be the exact same array (identity) due to fast path", b1, b2);
+
+ byte[] b3 = TBaseHelper.byteBufferToByteArray(ByteBuffer.wrap(b1, 1, 3));
+ assertEquals(3, b3.length);
+ assertEquals(ByteBuffer.wrap(b1, 1, 3), ByteBuffer.wrap(b3));
+ }
+
+ public void testRightSize() throws Exception {
+ assertNull(TBaseHelper.rightSize(null));
+ }
+
+ public void testByteBufferToString() {
+ byte[] array = new byte[]{1, 2, 3};
+ ByteBuffer bb = ByteBuffer.wrap(array, 1, 2);
+ StringBuilder sb = new StringBuilder();
+ TBaseHelper.toString(bb, sb);
+ assertEquals("02 03", sb.toString());
+ bb = ByteBuffer.wrap(array, 0, array.length);
+ bb.position(1);
+ bb = bb.slice();
+ assertEquals(1, bb.arrayOffset());
+ assertEquals(0, bb.position());
+ assertEquals(2, bb.limit());
+ sb = new StringBuilder();
+ TBaseHelper.toString(bb, sb);
+ assertEquals("02 03", sb.toString());
+ }
+
+ public void testCopyBinaryWithByteBuffer() throws Exception {
+ byte[] bytes = new byte[]{0, 1, 2, 3, 4, 5};
+ ByteBuffer b = ByteBuffer.wrap(bytes);
+ ByteBuffer bCopy = TBaseHelper.copyBinary(b);
+ assertEquals(b, bCopy);
+ assertEquals(0, b.position());
+
+ b = ByteBuffer.allocateDirect(6);
+ b.put(bytes);
+ b.position(0);
+ bCopy = TBaseHelper.copyBinary(b);
+ assertEquals(6, b.remaining());
+ assertEquals(0, b.position());
+ assertEquals(b, bCopy);
+
+ b.mark();
+ b.get();
+ bCopy = TBaseHelper.copyBinary(b);
+ assertEquals(ByteBuffer.wrap(bytes, 1, 5), bCopy);
+ assertEquals(1, b.position());
+ b.reset();
+ assertEquals(0, b.position());
+
+ assertNull(TBaseHelper.copyBinary((ByteBuffer)null));
+ }
+
+ public void testCopyBinaryWithByteArray() throws Exception {
+ byte[] bytes = new byte[]{0, 1, 2, 3, 4, 5};
+ byte[] copy = TBaseHelper.copyBinary(bytes);
+ assertEquals(ByteBuffer.wrap(bytes), ByteBuffer.wrap(copy));
+ assertNotSame(bytes, copy);
+
+ assertNull(TBaseHelper.copyBinary((byte[])null));
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTDeserializer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTDeserializer.java
new file mode 100644
index 000000000..a4a353d6c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTDeserializer.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift;
+
+import java.nio.ByteBuffer;
+
+import junit.framework.TestCase;
+
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TCompactProtocol;
+import org.apache.thrift.protocol.TJSONProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+
+import thrift.test.Backwards;
+import thrift.test.OneOfEach;
+import thrift.test.PrimitiveThenStruct;
+import thrift.test.StructWithAUnion;
+import thrift.test.TestUnion;
+
+public class TestTDeserializer extends TestCase {
+
+ private static final TProtocolFactory[] PROTOCOLS = new TProtocolFactory[] {
+ new TBinaryProtocol.Factory(),
+ new TCompactProtocol.Factory(),
+ new TJSONProtocol.Factory()
+ };
+
+ public void testPartialDeserialize() throws Exception {
+ //Root:StructWithAUnion
+ // 1:Union
+ // 1.3:OneOfEach
+ OneOfEach level3OneOfEach = Fixtures.oneOfEach;
+ TestUnion level2TestUnion = new TestUnion(TestUnion._Fields.STRUCT_FIELD, level3OneOfEach);
+ StructWithAUnion level1SWU = new StructWithAUnion(level2TestUnion);
+
+ Backwards bw = new Backwards(2, 1);
+ PrimitiveThenStruct pts = new PrimitiveThenStruct(12345, 67890, bw);
+
+ for (TProtocolFactory factory : PROTOCOLS) {
+
+ //Level 2 test
+ testPartialDeserialize(factory, level1SWU, new TestUnion(), level2TestUnion, StructWithAUnion._Fields.TEST_UNION);
+
+ //Level 3 on 3rd field test
+ testPartialDeserialize(factory, level1SWU, new OneOfEach(), level3OneOfEach, StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD);
+
+ //Test early termination when traversed path Field.id exceeds the one being searched for
+ testPartialDeserialize(factory, level1SWU, new OneOfEach(), new OneOfEach(), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.I32_FIELD);
+
+ //Test that readStructBegin isn't called on primitive
+ testPartialDeserialize(factory, pts, new Backwards(), bw, PrimitiveThenStruct._Fields.BW);
+
+ //Test primitive types
+ TDeserializer deserializer = new TDeserializer(factory);
+
+ Boolean expectedBool = level3OneOfEach.isIm_true();
+ Boolean resultBool = deserializer.partialDeserializeBool(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.IM_TRUE);
+ assertEquals(expectedBool, resultBool);
+
+ Byte expectedByte = level3OneOfEach.getA_bite();
+ Byte resultByte = deserializer.partialDeserializeByte(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.A_BITE);
+ assertEquals(expectedByte, resultByte);
+
+ Double expectedDouble = level3OneOfEach.getDouble_precision();
+ Double resultDouble = deserializer.partialDeserializeDouble(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.DOUBLE_PRECISION);
+ assertEquals(expectedDouble, resultDouble);
+
+ Short expectedI16 = level3OneOfEach.getInteger16();
+ Short resultI16 = deserializer.partialDeserializeI16(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.INTEGER16);
+ assertEquals(expectedI16, resultI16);
+
+ Integer expectedI32 = level3OneOfEach.getInteger32();
+ Integer resultI32 = deserializer.partialDeserializeI32(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.INTEGER32);
+ assertEquals(expectedI32, resultI32);
+
+ Long expectedI64 = level3OneOfEach.getInteger64();
+ Long resultI64= deserializer.partialDeserializeI64(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.INTEGER64);
+ assertEquals(expectedI64, resultI64);
+
+ String expectedString = level3OneOfEach.getSome_characters();
+ String resultString = deserializer.partialDeserializeString(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.SOME_CHARACTERS);
+ assertEquals(expectedString, resultString);
+
+ byte[] expectedBinary = level3OneOfEach.getBase64();
+ ByteBuffer resultBinary = deserializer.partialDeserializeByteArray(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.BASE64);
+ assertEquals(expectedBinary.length, resultBinary.limit() - resultBinary.position() - resultBinary.arrayOffset());
+ assertEquals(ByteBuffer.wrap(expectedBinary), resultBinary);
+
+ // Test field id in Union
+ short id = deserializer.partialDeserializeSetFieldIdInUnion(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION);
+ assertEquals(level2TestUnion.getSetField().getThriftFieldId(), id);
+ }
+ }
+
+ public static void testPartialDeserialize(TProtocolFactory protocolFactory, TBase input, TBase output, TBase expected, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ byte[] record = serialize(input, protocolFactory);
+ TDeserializer deserializer = new TDeserializer(protocolFactory);
+ for (int i = 0; i < 2; i++) {
+ TBase outputCopy = output.deepCopy();
+ deserializer.partialDeserialize(outputCopy, record, fieldIdPathFirst, fieldIdPathRest);
+ assertEquals("on attempt " + i + ", with " + protocolFactory.toString()
+ + ", expected " + expected + " but got " + outputCopy,
+ expected, outputCopy);
+ }
+ }
+
+ private static byte[] serialize(TBase input, TProtocolFactory protocolFactory) throws TException{
+ return new TSerializer(protocolFactory).serialize(input);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTEnumHelper.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTEnumHelper.java
new file mode 100644
index 000000000..e2cca4039
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTEnumHelper.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift;
+
+import thrift.test.Numberz;
+
+import junit.framework.TestCase;
+
+public class TestTEnumHelper extends TestCase {
+
+ public void testGetByValue_ValidValues() {
+ for (Numberz n: Numberz.values()) {
+ int value = n.getValue();
+ assertEquals(n, TEnumHelper.getByValue(Numberz.class, value));
+ }
+ }
+
+ public void testGetByValue_InvalidValue() {
+ assertEquals(null, TEnumHelper.getByValue(Numberz.class, 0));
+ }
+
+ public void testGetByValue_InvalidClass() {
+ assertEquals(null, TEnumHelper.getByValue(TEnum.class, 0));
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTUnion.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTUnion.java
new file mode 100644
index 000000000..f1e6f0e1f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTUnion.java
@@ -0,0 +1,268 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TTupleProtocol;
+import org.apache.thrift.transport.TMemoryBuffer;
+
+import thrift.test.ComparableUnion;
+import thrift.test.Empty;
+import thrift.test.RandomStuff;
+import thrift.test.SomeEnum;
+import thrift.test.StructWithAUnion;
+import thrift.test.TestUnion;
+import thrift.test.TestUnionMinusStringField;
+
+public class TestTUnion extends TestCase {
+
+ public void testBasic() throws Exception {
+ TestUnion union = new TestUnion();
+
+ assertFalse(union.isSet());
+ assertFalse(union.isSetI32_field());
+ assertNull(union.getFieldValue());
+
+ union = new TestUnion(TestUnion._Fields.I32_FIELD, 25);
+
+ assertEquals(Integer.valueOf(25), union.getFieldValue());
+
+ assertEquals(Integer.valueOf(25), union.getFieldValue(TestUnion._Fields.I32_FIELD));
+
+ assertTrue(union.isSetI32_field());
+
+ try {
+ union.getFieldValue(TestUnion._Fields.STRING_FIELD);
+ fail("should have thrown an exception");
+ } catch (IllegalArgumentException e) {
+ // cool!
+ }
+
+ union = new TestUnion();
+
+ // should not throw an exception here
+ union.hashCode();
+
+ union.setI32_field(1);
+ assertEquals(1, union.getI32_field());
+ union.hashCode();
+
+ assertFalse(union.isSetString_field());
+
+ try {
+ union.getString_field();
+ fail("should have thrown an exception");
+ } catch (Exception e) {
+ // sweet
+ }
+
+ union = TestUnion.i32_field(1);
+
+ assertFalse(union.equals((TestUnion)null));
+
+ union = TestUnion.enum_field(SomeEnum.ONE);
+ union.hashCode();
+
+ union = new TestUnion();
+ // should not throw an exception
+ union.toString();
+ }
+
+ public void testCompareTo() throws Exception {
+ ComparableUnion cu = ComparableUnion.string_field("a");
+ ComparableUnion cu2 = ComparableUnion.string_field("b");
+
+ assertTrue(cu.compareTo(cu) == 0);
+ assertTrue(cu2.compareTo(cu2) == 0);
+
+ assertTrue(cu.compareTo(cu2) < 0);
+ assertTrue(cu2.compareTo(cu) > 0);
+
+ cu2 = ComparableUnion.binary_field(ByteBuffer.wrap(new byte[]{2}));
+
+ assertTrue(cu.compareTo(cu2) < 0);
+ assertTrue(cu2.compareTo(cu) > 0);
+
+ cu = ComparableUnion.binary_field(ByteBuffer.wrap(new byte[]{1}));
+
+ assertTrue(cu.compareTo(cu2) < 0);
+ assertTrue(cu2.compareTo(cu) > 0);
+
+ TestUnion union1 = new TestUnion(TestUnion._Fields.STRUCT_LIST, new ArrayList<RandomStuff>());
+ TestUnion union2 = new TestUnion(TestUnion._Fields.STRUCT_LIST, new ArrayList<RandomStuff>());
+ assertTrue(union1.compareTo(union2) == 0);
+
+ TestUnion union3 = new TestUnion(TestUnion._Fields.I32_SET, new HashSet<Integer>());
+ Set<Integer> i32_set = new HashSet<Integer>();
+ i32_set.add(1);
+ TestUnion union4 = new TestUnion(TestUnion._Fields.I32_SET, i32_set);
+ assertTrue(union3.compareTo(union4) < 0);
+
+ Map<Integer, Integer> i32_map = new HashMap<Integer, Integer>();
+ i32_map.put(1,1);
+ TestUnion union5 = new TestUnion(TestUnion._Fields.I32_MAP, i32_map);
+ TestUnion union6 = new TestUnion(TestUnion._Fields.I32_MAP, new HashMap<Integer, Integer>());
+ assertTrue(union5.compareTo(union6) > 0);
+ }
+
+ public void testEquality() throws Exception {
+ TestUnion union = new TestUnion(TestUnion._Fields.I32_FIELD, 25);
+
+ TestUnion otherUnion = new TestUnion(TestUnion._Fields.STRING_FIELD, "blah!!!");
+
+ assertFalse(union.equals(otherUnion));
+
+ otherUnion = new TestUnion(TestUnion._Fields.I32_FIELD, 400);
+
+ assertFalse(union.equals(otherUnion));
+
+ otherUnion = new TestUnion(TestUnion._Fields.OTHER_I32_FIELD, 25);
+
+ assertFalse(union.equals(otherUnion));
+ }
+
+ public void testSerialization() throws Exception {
+ TestUnion union = new TestUnion(TestUnion._Fields.I32_FIELD, 25);
+ union.setI32_set(Collections.singleton(42));
+
+ TMemoryBuffer buf = new TMemoryBuffer(0);
+ TProtocol proto = new TBinaryProtocol(buf);
+
+ union.write(proto);
+
+ TestUnion u2 = new TestUnion();
+
+ u2.read(proto);
+
+ assertEquals(u2, union);
+
+ StructWithAUnion swau = new StructWithAUnion(u2);
+
+ buf = new TMemoryBuffer(0);
+ proto = new TBinaryProtocol(buf);
+
+ swau.write(proto);
+
+ StructWithAUnion swau2 = new StructWithAUnion();
+ assertFalse(swau2.equals(swau));
+ swau2.read(proto);
+ assertEquals(swau2, swau);
+
+ // this should NOT throw an exception.
+ buf = new TMemoryBuffer(0);
+ proto = new TBinaryProtocol(buf);
+
+ swau.write(proto);
+ new Empty().read(proto);
+ }
+
+ public void testTupleProtocolSerialization () throws Exception {
+ TestUnion union = new TestUnion(TestUnion._Fields.I32_FIELD, 25);
+ union.setI32_set(Collections.singleton(42));
+
+ TMemoryBuffer buf = new TMemoryBuffer(0);
+ TProtocol proto = new TTupleProtocol(buf);
+
+ union.write(proto);
+
+ TestUnion u2 = new TestUnion();
+
+ u2.read(proto);
+
+ assertEquals(u2, union);
+
+ StructWithAUnion swau = new StructWithAUnion(u2);
+
+ buf = new TMemoryBuffer(0);
+ proto = new TBinaryProtocol(buf);
+
+ swau.write(proto);
+
+ StructWithAUnion swau2 = new StructWithAUnion();
+ assertFalse(swau2.equals(swau));
+ swau2.read(proto);
+ assertEquals(swau2, swau);
+
+ // this should NOT throw an exception.
+ buf = new TMemoryBuffer(0);
+ proto = new TTupleProtocol(buf);
+
+ swau.write(proto);
+ new Empty().read(proto);
+ }
+
+ public void testSkip() throws Exception {
+ TestUnion tu = TestUnion.string_field("string");
+ byte[] tuSerialized = new TSerializer().serialize(tu);
+ TestUnionMinusStringField tums = new TestUnionMinusStringField();
+ new TDeserializer().deserialize(tums, tuSerialized);
+ assertNull(tums.getSetField());
+ assertNull(tums.getFieldValue());
+ }
+
+ public void testDeepCopy() throws Exception {
+ byte[] bytes = {1, 2, 3};
+ ByteBuffer value = ByteBuffer.wrap(bytes);
+ ComparableUnion cu = ComparableUnion.binary_field(value);
+ ComparableUnion copy = cu.deepCopy();
+ assertEquals(cu, copy);
+ assertNotSame(cu.bufferForBinary_field().array(), copy.bufferForBinary_field().array());
+ }
+
+ public void testToString() throws Exception {
+ byte[] bytes = {1, 2, 3};
+ ByteBuffer value = ByteBuffer.wrap(bytes);
+ ComparableUnion cu = ComparableUnion.binary_field(value);
+ String expectedString = "<ComparableUnion binary_field:01 02 03>";
+ assertEquals(expectedString, cu.toString());
+ }
+
+ public void testJavaSerializable() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+
+ TestUnion tu = TestUnion.string_field("string");
+
+ // Serialize tu the Java way...
+ oos.writeObject(tu);
+ byte[] serialized = baos.toByteArray();
+
+ // Attempt to deserialize it
+ ByteArrayInputStream bais = new ByteArrayInputStream(serialized);
+ ObjectInputStream ois = new ObjectInputStream(bais);
+ TestUnion tu2 = (TestUnion) ois.readObject();
+
+ assertEquals(tu, tu2);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestUnsafeBinaries.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestUnsafeBinaries.java
new file mode 100644
index 000000000..d1fc21368
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestUnsafeBinaries.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import thrift.test.SafeBytes;
+import thrift.test.UnsafeBytes;
+
+// test generating types with un-copied byte[]/ByteBuffer input/output
+//
+public class TestUnsafeBinaries extends TestStruct {
+
+ private static byte[] input() {
+ return new byte[]{1, 1};
+ }
+
+ //
+ // verify that the unsafe_binaries option modifies behavior
+ //
+
+ // constructor doesn't copy
+ public void testUnsafeConstructor() throws Exception {
+
+ byte[] input = input();
+ UnsafeBytes struct = new UnsafeBytes(ByteBuffer.wrap(input));
+
+ input[0] = 2;
+
+ assertTrue(Arrays.equals(
+ new byte[]{2, 1},
+ struct.getBytes())
+ );
+
+ }
+
+ // getter doesn't copy
+ // note: this behavior is the same with/without the flag, but if this default ever changes, the current behavior
+ // should be retained when using this flag
+ public void testUnsafeGetter(){
+ UnsafeBytes struct = new UnsafeBytes(ByteBuffer.wrap(input()));
+
+ byte[] val = struct.getBytes();
+ val[0] = 2;
+
+ assertTrue(Arrays.equals(
+ new byte[]{2, 1},
+ struct.getBytes())
+ );
+
+ }
+
+ // setter doesn't copy
+ public void testUnsafeSetter(){
+ UnsafeBytes struct = new UnsafeBytes();
+
+ byte[] val = input();
+ struct.setBytes(val);
+
+ val[0] = 2;
+
+ assertTrue(Arrays.equals(
+ new byte[]{2, 1},
+ struct.getBytes())
+ );
+
+ }
+
+ // buffer doens't copy
+ public void testUnsafeBufferFor(){
+ UnsafeBytes struct = new UnsafeBytes(ByteBuffer.wrap(input()));
+
+ ByteBuffer val = struct.bufferForBytes();
+ val.array()[0] = 2;
+
+ assertTrue(Arrays.equals(
+ new byte[]{2, 1},
+ struct.getBytes())
+ );
+
+ }
+
+ //
+ // verify that the default generator does not change behavior
+ //
+
+ public void testSafeConstructor() {
+
+ byte[] input = input();
+ SafeBytes struct = new SafeBytes(ByteBuffer.wrap(input));
+
+ input[0] = 2;
+
+ assertTrue(Arrays.equals(
+ new byte[]{1, 1},
+ struct.getBytes())
+ );
+
+ }
+
+ public void testSafeSetter() {
+
+ byte[] input = input();
+ SafeBytes struct = new SafeBytes(ByteBuffer.wrap(input));
+
+ input[0] = 2;
+
+ assertTrue(Arrays.equals(
+ new byte[]{1, 1},
+ struct.getBytes())
+ );
+
+ }
+
+ public void testSafeBufferFor(){
+ SafeBytes struct = new SafeBytes(ByteBuffer.wrap(input()));
+
+ ByteBuffer val = struct.bufferForBytes();
+ val.array()[0] = 2;
+
+ assertTrue(Arrays.equals(
+ new byte[]{1, 1},
+ struct.getBytes())
+ );
+
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/async/TestTAsyncClient.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/async/TestTAsyncClient.java
new file mode 100644
index 000000000..392ca22d4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/async/TestTAsyncClient.java
@@ -0,0 +1,28 @@
+package org.apache.thrift.async;
+
+import junit.framework.TestCase;
+
+import org.apache.thrift.TException;
+
+import thrift.test.Srv;
+import thrift.test.Srv.AsyncClient;
+
+public class TestTAsyncClient extends TestCase {
+ public void testRaisesExceptionWhenUsedConcurrently() throws Exception {
+ TAsyncClientManager mockClientManager = new TAsyncClientManager() {
+ @Override
+ public void call(TAsyncMethodCall method) throws TException {
+ // do nothing
+ }
+ };
+
+ Srv.AsyncClient c = new AsyncClient(null, mockClientManager, null);
+ c.Janky(0, null);
+ try {
+ c.checkReady();
+ fail("should have hit an exception");
+ } catch (Exception e) {
+ // awesome
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/async/TestTAsyncClientManager.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/async/TestTAsyncClientManager.java
new file mode 100644
index 000000000..c483cf24e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/async/TestTAsyncClientManager.java
@@ -0,0 +1,378 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.async;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import junit.framework.TestCase;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.server.ServerTestBase;
+import org.apache.thrift.server.THsHaServer;
+import org.apache.thrift.server.THsHaServer.Args;
+import org.apache.thrift.transport.TNonblockingServerSocket;
+import org.apache.thrift.transport.TNonblockingSocket;
+
+import thrift.test.CompactProtoTestStruct;
+import thrift.test.ExceptionWithAMap;
+import thrift.test.Srv;
+import thrift.test.Srv.Iface;
+
+public class TestTAsyncClientManager extends TestCase {
+
+ private THsHaServer server_;
+ private Thread serverThread_;
+ private TAsyncClientManager clientManager_;
+
+ public void setUp() throws Exception {
+ server_ = new THsHaServer(new Args(new TNonblockingServerSocket(
+ new TNonblockingServerSocket.NonblockingAbstractServerSocketArgs().port(ServerTestBase.PORT))).
+ processor(new Srv.Processor(new SrvHandler())));
+ serverThread_ = new Thread(new Runnable() {
+ public void run() {
+ server_.serve();
+ }
+ });
+ serverThread_.start();
+ clientManager_ = new TAsyncClientManager();
+ Thread.sleep(500);
+ }
+
+ public void tearDown() throws Exception {
+ server_.stop();
+ clientManager_.stop();
+ serverThread_.join();
+ }
+
+ public void testBasicCall() throws Exception {
+ Srv.AsyncClient client = getClient();
+ basicCall(client);
+ }
+
+ public void testBasicCallWithTimeout() throws Exception {
+ Srv.AsyncClient client = getClient();
+ client.setTimeout(5000);
+ basicCall(client);
+ }
+
+ private static abstract class ErrorCallTest<C extends TAsyncClient, R> {
+ final void runTest() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final AtomicReference<Exception> error = new AtomicReference<Exception>();
+ C client = executeErroringCall(new AsyncMethodCallback<R>() {
+ @Override
+ public void onComplete(R response) {
+ latch.countDown();
+ }
+
+ @Override
+ public void onError(Exception exception) {
+ error.set(exception);
+ latch.countDown();
+ }
+ });
+ latch.await(2, TimeUnit.SECONDS);
+ assertTrue(client.hasError());
+ Exception exception = error.get();
+ assertNotNull(exception);
+ assertSame(exception, client.getError());
+ validateError(client, exception);
+ }
+
+ /**
+ * Executes a call that is expected to raise an exception.
+ *
+ * @param callback The testing callback that should be installed.
+ * @return The client the call was made against.
+ * @throws Exception if there was a problem setting up the client or making the call.
+ */
+ abstract C executeErroringCall(AsyncMethodCallback<R> callback) throws Exception;
+
+ /**
+ * Further validates the properties of the error raised in the remote call and the state of the
+ * client after that call.
+ *
+ * @param client The client returned from {@link #executeErroringCall(AsyncMethodCallback)}.
+ * @param error The exception raised by the remote call.
+ */
+ abstract void validateError(C client, Exception error);
+ }
+
+ public void testUnexpectedRemoteExceptionCall() throws Exception {
+ new ErrorCallTest<Srv.AsyncClient, Boolean>() {
+ @Override
+ Srv.AsyncClient executeErroringCall(AsyncMethodCallback<Boolean> callback) throws Exception {
+ Srv.AsyncClient client = getClient();
+ client.declaredExceptionMethod(false, callback);
+ return client;
+ }
+
+ @Override
+ void validateError(Srv.AsyncClient client, Exception error) {
+ assertFalse(client.hasTimeout());
+ assertTrue(error instanceof TException);
+ }
+ }.runTest();
+ }
+
+ public void testDeclaredRemoteExceptionCall() throws Exception {
+ new ErrorCallTest<Srv.AsyncClient, Boolean>() {
+ @Override
+ Srv.AsyncClient executeErroringCall(AsyncMethodCallback<Boolean> callback) throws Exception {
+ Srv.AsyncClient client = getClient();
+ client.declaredExceptionMethod(true, callback);
+ return client;
+ }
+
+ @Override
+ void validateError(Srv.AsyncClient client, Exception error) {
+ assertFalse(client.hasTimeout());
+ assertEquals(ExceptionWithAMap.class, error.getClass());
+ ExceptionWithAMap exceptionWithAMap = (ExceptionWithAMap) error;
+ assertEquals("blah", exceptionWithAMap.getBlah());
+ assertEquals(new HashMap<String, String>(), exceptionWithAMap.getMap_field());
+ }
+ }.runTest();
+ }
+
+ public void testTimeoutCall() throws Exception {
+ new ErrorCallTest<Srv.AsyncClient, Integer>() {
+ @Override
+ Srv.AsyncClient executeErroringCall(AsyncMethodCallback<Integer> callback) throws Exception {
+ Srv.AsyncClient client = getClient();
+ client.setTimeout(100);
+ client.primitiveMethod(callback);
+ return client;
+ }
+
+ @Override
+ void validateError(Srv.AsyncClient client, Exception error) {
+ assertTrue(client.hasTimeout());
+ assertTrue(error instanceof TimeoutException);
+ }
+ }.runTest();
+ }
+
+ public void testVoidCall() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final AtomicBoolean returned = new AtomicBoolean(false);
+ Srv.AsyncClient client = getClient();
+ client.voidMethod(new FailureLessCallback<Void>() {
+ @Override
+ public void onComplete(Void response) {
+ returned.set(true);
+ latch.countDown();
+ }
+ });
+ latch.await(1, TimeUnit.SECONDS);
+ assertTrue(returned.get());
+ }
+
+ public void testOnewayCall() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final AtomicBoolean returned = new AtomicBoolean(false);
+ Srv.AsyncClient client = getClient();
+ client.onewayMethod(new FailureLessCallback<Void>() {
+ @Override
+ public void onComplete(Void response) {
+ returned.set(true);
+ latch.countDown();
+ }
+ });
+ latch.await(1, TimeUnit.SECONDS);
+ assertTrue(returned.get());
+ }
+
+ public void testParallelCalls() throws Exception {
+ // make multiple calls with deserialization in the selector thread (repro Eric's issue)
+ int numThreads = 50;
+ int numCallsPerThread = 100;
+ List<JankyRunnable> runnables = new ArrayList<JankyRunnable>();
+ List<Thread> threads = new ArrayList<Thread>();
+ for (int i = 0; i < numThreads; i++) {
+ JankyRunnable runnable = new JankyRunnable(numCallsPerThread);
+ Thread thread = new Thread(runnable);
+ thread.start();
+ threads.add(thread);
+ runnables.add(runnable);
+ }
+ for (Thread thread : threads) {
+ thread.join();
+ }
+ int numSuccesses = 0;
+ for (JankyRunnable runnable : runnables) {
+ numSuccesses += runnable.getNumSuccesses();
+ }
+ assertEquals(numThreads * numCallsPerThread, numSuccesses);
+ }
+
+ private Srv.AsyncClient getClient() throws IOException {
+ TNonblockingSocket clientSocket = new TNonblockingSocket(ServerTestBase.HOST, ServerTestBase.PORT);
+ return new Srv.AsyncClient(new TBinaryProtocol.Factory(), clientManager_, clientSocket);
+ }
+
+ private void basicCall(Srv.AsyncClient client) throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final AtomicBoolean returned = new AtomicBoolean(false);
+ client.Janky(1, new FailureLessCallback<Integer>() {
+ @Override
+ public void onComplete(Integer response) {
+ assertEquals(3, response.intValue());
+ returned.set(true);
+ latch.countDown();
+ }
+
+ @Override
+ public void onError(Exception exception) {
+ try {
+ StringWriter sink = new StringWriter();
+ exception.printStackTrace(new PrintWriter(sink, true));
+ fail("unexpected onError with exception " + sink.toString());
+ } finally {
+ latch.countDown();
+ }
+ }
+ });
+ latch.await(100, TimeUnit.SECONDS);
+ assertTrue(returned.get());
+ }
+
+ public class SrvHandler implements Iface {
+ // Use this method for a standard call testing
+ @Override
+ public int Janky(int arg) throws TException {
+ assertEquals(1, arg);
+ return 3;
+ }
+
+ // Using this method for timeout testing - sleeps for 1 second before returning
+ @Override
+ public int primitiveMethod() throws TException {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return 0;
+ }
+
+ @Override
+ public void methodWithDefaultArgs(int something) throws TException { }
+
+ @Override
+ public CompactProtoTestStruct structMethod() throws TException {
+ return null;
+ }
+
+ @Override
+ public void voidMethod() throws TException {
+ }
+
+ @Override
+ public void onewayMethod() throws TException {
+ }
+
+ @Override
+ public boolean declaredExceptionMethod(boolean shouldThrowDeclared) throws TException {
+ if (shouldThrowDeclared) {
+ throw new ExceptionWithAMap("blah", new HashMap<String, String>());
+ } else {
+ throw new TException("Unexpected!");
+ }
+ }
+ }
+
+ private static abstract class FailureLessCallback<T> implements AsyncMethodCallback<T> {
+ @Override
+ public void onError(Exception exception) {
+ fail(exception);
+ }
+ }
+
+ private static void fail(Exception exception) {
+ StringWriter sink = new StringWriter();
+ exception.printStackTrace(new PrintWriter(sink, true));
+ fail("unexpected error " + sink.toString());
+ }
+
+ private class JankyRunnable implements Runnable {
+ private int numCalls_;
+ private int numSuccesses_ = 0;
+ private Srv.AsyncClient client_;
+
+ public JankyRunnable(int numCalls) throws Exception {
+ numCalls_ = numCalls;
+ client_ = getClient();
+ client_.setTimeout(20000);
+ }
+
+ public int getNumSuccesses() {
+ return numSuccesses_;
+ }
+
+ public void run() {
+ for (int i = 0; i < numCalls_ && !client_.hasError(); i++) {
+ final int iteration = i;
+ try {
+ // connect an async client
+ final CountDownLatch latch = new CountDownLatch(1);
+ final AtomicBoolean returned = new AtomicBoolean(false);
+ client_.Janky(1, new AsyncMethodCallback<Integer>() {
+
+ @Override
+ public void onComplete(Integer result) {
+ assertEquals(3, result.intValue());
+ returned.set(true);
+ latch.countDown();
+ }
+
+ @Override
+ public void onError(Exception exception) {
+ try {
+ StringWriter sink = new StringWriter();
+ exception.printStackTrace(new PrintWriter(sink, true));
+ fail("unexpected onError on iteration " + iteration + ": " + sink.toString());
+ } finally {
+ latch.countDown();
+ }
+ }
+ });
+
+ boolean calledBack = latch.await(30, TimeUnit.SECONDS);
+ assertTrue("wasn't called back in time on iteration " + iteration, calledBack);
+ assertTrue("onComplete not called on iteration " + iteration, returned.get());
+ this.numSuccesses_++;
+ } catch (Exception e) {
+ fail(e);
+ }
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/BenchmarkProtocols.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/BenchmarkProtocols.java
new file mode 100644
index 000000000..e88160759
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/BenchmarkProtocols.java
@@ -0,0 +1,88 @@
+package org.apache.thrift.protocol;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.thrift.Fixtures;
+import org.apache.thrift.TException;
+import org.apache.thrift.transport.TMemoryBuffer;
+
+public class BenchmarkProtocols {
+
+ private static final Set<TProtocolFactory> FACTORIES = new LinkedHashSet<TProtocolFactory>(){{
+ add(new TTupleProtocol.Factory());
+ add(new TCompactProtocol.Factory());
+ add(new TBinaryProtocol.Factory());
+ }};
+
+ private static final int NUM_REPS = 100000;
+ private static final int NUM_TRIALS = 10;
+
+ public static void main(String[] args) throws TException {
+ Map<TProtocolFactory, List<Long>> timesByFactory = new HashMap<TProtocolFactory, List<Long>>();
+
+ for (int trial = 0; trial < NUM_TRIALS; trial++) {
+ for (int i = 0; i < 16; i++) {
+ System.gc();
+ }
+// TProtocol proto = factory.getProtocol(new TTransport() {
+// @Override
+// public void write(byte[] buf, int off, int len) throws TTransportException {
+// }
+//
+// @Override
+// public int read(byte[] buf, int off, int len) throws TTransportException {
+// return 0;
+// }
+//
+// @Override
+// public void open() throws TTransportException {
+// }
+//
+// @Override
+// public boolean isOpen() {
+// return true;
+// }
+//
+// @Override
+// public void close() {
+// }
+// });
+
+
+ for (TProtocolFactory factory : FACTORIES) {
+ if (timesByFactory.get(factory) == null) {
+ timesByFactory.put(factory, new ArrayList<Long>());
+ }
+
+ long start = System.currentTimeMillis();
+ for (int rep = 0; rep < NUM_REPS; rep++) {
+ TProtocol proto = factory.getProtocol(new TMemoryBuffer(128*1024));
+ Fixtures.compactProtoTestStruct.write(proto);
+ Fixtures.nesting.write(proto);
+ }
+ long end = System.currentTimeMillis();
+ timesByFactory.get(factory).add(end-start);
+ }
+ }
+
+ for (TProtocolFactory factory : FACTORIES) {
+ List<Long> times = timesByFactory.get(factory);
+// System.out.println("raw times pre-drop: " + times );
+ times.remove(Collections.max(times));
+ long total = 0;
+ for (long t : times) {
+ total += t;
+ }
+ Collections.sort(times);
+ System.out.println(factory.getClass().getName() + " average time: " + (total / times.size()) + "ms");
+ System.out.println("raw times: " + times);
+ }
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/ProtocolTestBase.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/ProtocolTestBase.java
new file mode 100644
index 000000000..0386d8393
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/ProtocolTestBase.java
@@ -0,0 +1,427 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.protocol;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.thrift.Fixtures;
+import org.apache.thrift.TBase;
+import org.apache.thrift.TDeserializer;
+import org.apache.thrift.TException;
+import org.apache.thrift.TSerializer;
+import org.apache.thrift.transport.TMemoryBuffer;
+
+import thrift.test.CompactProtoTestStruct;
+import thrift.test.HolyMoley;
+import thrift.test.Nesting;
+import thrift.test.OneOfEach;
+import thrift.test.Srv;
+
+public abstract class ProtocolTestBase extends TestCase {
+
+ /** Does it make sense to call methods like writeI32 directly on your protocol? */
+ protected abstract boolean canBeUsedNaked();
+
+ /** The protocol factory for the protocol being tested. */
+ protected abstract TProtocolFactory getFactory();
+
+ public void testDouble() throws Exception {
+ if (canBeUsedNaked()) {
+ TMemoryBuffer buf = new TMemoryBuffer(1000);
+ TProtocol proto = getFactory().getProtocol(buf);
+ proto.writeDouble(123.456);
+ assertEquals(123.456, proto.readDouble());
+ }
+
+ internalTestStructField(new StructFieldTestCase(TType.DOUBLE, (short)15) {
+ @Override
+ public void readMethod(TProtocol proto) throws TException {
+ assertEquals(123.456, proto.readDouble());
+ }
+
+ @Override
+ public void writeMethod(TProtocol proto) throws TException {
+ proto.writeDouble(123.456);
+ }
+ });
+ }
+
+ public void testSerialization() throws Exception {
+ internalTestSerialization(OneOfEach.class, Fixtures.oneOfEach);
+ internalTestSerialization(Nesting.class, Fixtures.nesting);
+ internalTestSerialization(HolyMoley.class, Fixtures.holyMoley);
+ internalTestSerialization(CompactProtoTestStruct.class, Fixtures.compactProtoTestStruct);
+ }
+
+ public void testBinary() throws Exception {
+ for (byte[] b : Arrays.asList(new byte[0],
+ new byte[]{0,1,2,3,4,5,6,7,8,9,10},
+ new byte[]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14},
+ new byte[]{0x5D},
+ new byte[]{(byte)0xD5,(byte)0x5D},
+ new byte[]{(byte)0xFF,(byte)0xD5,(byte)0x5D},
+ new byte[128])) {
+ if (canBeUsedNaked()) {
+ internalTestNakedBinary(b);
+ }
+ internalTestBinaryField(b);
+ }
+
+ if (canBeUsedNaked()) {
+ byte[] data = {1, 2, 3, 4, 5, 6};
+
+ TMemoryBuffer buf = new TMemoryBuffer(0);
+ TProtocol proto = getFactory().getProtocol(buf);
+ ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.get();
+ proto.writeBinary(bb.slice());
+ assertEquals(ByteBuffer.wrap(data, 1, 5), proto.readBinary());
+ }
+ }
+
+ public void testString() throws Exception {
+ for (String s : Arrays.asList("", "short", "borderlinetiny", "a bit longer than the smallest possible")) {
+ if (canBeUsedNaked()) {
+ internalTestNakedString(s);
+ }
+ internalTestStringField(s);
+ }
+ }
+
+ public void testLong() throws Exception {
+ if (canBeUsedNaked()) {
+ internalTestNakedI64(0);
+ }
+ internalTestI64Field(0);
+ for (int i = 0; i < 62; i++) {
+ if (canBeUsedNaked()) {
+ internalTestNakedI64(1L << i);
+ internalTestNakedI64(-(1L << i));
+ }
+ internalTestI64Field(1L << i);
+ internalTestI64Field(-(1L << i));
+ }
+ }
+
+ public void testInt() throws Exception {
+ for (int i : Arrays.asList(0, 1, 7, 150, 15000, 31337, 0xffff, 0xffffff, -1, -7, -150, -15000, -0xffff, -0xffffff)) {
+ if (canBeUsedNaked()) {
+ internalTestNakedI32(i);
+ }
+ internalTestI32Field(i);
+ }
+ }
+
+ public void testShort() throws Exception {
+ for (int s : Arrays.asList(0, 1, 7, 150, 15000, 0x7fff, -1, -7, -150, -15000, -0x7fff)) {
+ if (canBeUsedNaked()) {
+ internalTestNakedI16((short)s);
+ }
+ internalTestI16Field((short)s);
+ }
+ }
+
+ public void testByte() throws Exception {
+ if (canBeUsedNaked()) {
+ internalTestNakedByte();
+ }
+ for (int i = 0; i < 128; i++) {
+ internalTestByteField((byte)i);
+ internalTestByteField((byte)-i);
+ }
+ }
+
+ private void internalTestNakedByte() throws Exception {
+ TMemoryBuffer buf = new TMemoryBuffer(1000);
+ TProtocol proto = getFactory().getProtocol(buf);
+ proto.writeByte((byte)123);
+ assertEquals((byte) 123, proto.readByte());
+ }
+
+ private void internalTestByteField(final byte b) throws Exception {
+ internalTestStructField(new StructFieldTestCase(TType.BYTE, (short)15) {
+ public void writeMethod(TProtocol proto) throws TException {
+ proto.writeByte(b);
+ }
+
+ public void readMethod(TProtocol proto) throws TException {
+ assertEquals((byte)b, proto.readByte());
+ }
+ });
+ }
+
+ private void internalTestNakedI16(short n) throws Exception {
+ TMemoryBuffer buf = new TMemoryBuffer(0);
+ TProtocol proto = getFactory().getProtocol(buf);
+ proto.writeI16(n);
+ assertEquals(n, proto.readI16());
+ }
+
+ private void internalTestI16Field(final short n) throws Exception {
+ internalTestStructField(new StructFieldTestCase(TType.I16, (short)15) {
+ public void writeMethod(TProtocol proto) throws TException {
+ proto.writeI16(n);
+ }
+
+ public void readMethod(TProtocol proto) throws TException {
+ assertEquals(n, proto.readI16());
+ }
+ });
+ }
+
+ private void internalTestNakedI32(int n) throws Exception {
+ TMemoryBuffer buf = new TMemoryBuffer(0);
+ TProtocol proto = getFactory().getProtocol(buf);
+ proto.writeI32(n);
+ assertEquals(n, proto.readI32());
+ }
+
+ private void internalTestI32Field(final int n) throws Exception {
+ internalTestStructField(new StructFieldTestCase(TType.I32, (short)15) {
+ public void writeMethod(TProtocol proto) throws TException {
+ proto.writeI32(n);
+ }
+
+ public void readMethod(TProtocol proto) throws TException {
+ assertEquals(n, proto.readI32());
+ }
+ });
+ }
+
+ private void internalTestNakedI64(long n) throws Exception {
+ TMemoryBuffer buf = new TMemoryBuffer(0);
+ TProtocol proto = getFactory().getProtocol(buf);
+ proto.writeI64(n);
+ assertEquals(n, proto.readI64());
+ }
+
+ private void internalTestI64Field(final long n) throws Exception {
+ internalTestStructField(new StructFieldTestCase(TType.I64, (short)15) {
+ public void writeMethod(TProtocol proto) throws TException {
+ proto.writeI64(n);
+ }
+
+ public void readMethod(TProtocol proto) throws TException {
+ assertEquals(n, proto.readI64());
+ }
+ });
+ }
+
+ private void internalTestNakedString(String str) throws Exception {
+ TMemoryBuffer buf = new TMemoryBuffer(0);
+ TProtocol proto = getFactory().getProtocol(buf);
+ proto.writeString(str);
+ assertEquals(str, proto.readString());
+ }
+
+ private void internalTestStringField(final String str) throws Exception {
+ internalTestStructField(new StructFieldTestCase(TType.STRING, (short)15) {
+ public void writeMethod(TProtocol proto) throws TException {
+ proto.writeString(str);
+ }
+
+ public void readMethod(TProtocol proto) throws TException {
+ assertEquals(str, proto.readString());
+ }
+ });
+ }
+
+ private void internalTestNakedBinary(byte[] data) throws Exception {
+ TMemoryBuffer buf = new TMemoryBuffer(0);
+ TProtocol proto = getFactory().getProtocol(buf);
+ proto.writeBinary(ByteBuffer.wrap(data));
+ assertEquals(ByteBuffer.wrap(data), proto.readBinary());
+ }
+
+ private void internalTestBinaryField(final byte[] data) throws Exception {
+ internalTestStructField(new StructFieldTestCase(TType.STRING, (short)15) {
+ public void writeMethod(TProtocol proto) throws TException {
+ proto.writeBinary(ByteBuffer.wrap(data));
+ }
+
+ public void readMethod(TProtocol proto) throws TException {
+ assertEquals(ByteBuffer.wrap(data), proto.readBinary());
+ }
+ });
+ }
+
+ private <T extends TBase> void internalTestSerialization(Class<T> klass, T expected) throws Exception {
+ TMemoryBuffer buf = new TMemoryBuffer(0);
+ TBinaryProtocol binproto = new TBinaryProtocol(buf);
+
+ expected.write(binproto);
+
+ buf = new TMemoryBuffer(0);
+ TProtocol proto = getFactory().getProtocol(buf);
+
+ expected.write(proto);
+ System.out.println("Size in " + proto.getClass().getSimpleName() + ": " + buf.length());
+
+ T actual = klass.newInstance();
+ actual.read(proto);
+ assertEquals(expected, actual);
+ }
+
+ public void testMessage() throws Exception {
+ List<TMessage> msgs = Arrays.asList(new TMessage[]{
+ new TMessage("short message name", TMessageType.CALL, 0),
+ new TMessage("1", TMessageType.REPLY, 12345),
+ new TMessage("loooooooooooooooooooooooooooooooooong", TMessageType.EXCEPTION, 1 << 16),
+ new TMessage("Janky", TMessageType.CALL, 0),
+ });
+
+ for (TMessage msg : msgs) {
+ TMemoryBuffer buf = new TMemoryBuffer(0);
+ TProtocol proto = getFactory().getProtocol(buf);
+ TMessage output = null;
+
+ proto.writeMessageBegin(msg);
+ proto.writeMessageEnd();
+
+ output = proto.readMessageBegin();
+
+ assertEquals(msg, output);
+ }
+ }
+
+ public void testServerRequest() throws Exception {
+ Srv.Iface handler = new Srv.Iface() {
+ public int Janky(int i32arg) throws TException {
+ return i32arg * 2;
+ }
+
+ public int primitiveMethod() throws TException {
+ return 0;
+ }
+
+ public CompactProtoTestStruct structMethod() throws TException {
+ return null;
+ }
+
+ public void voidMethod() throws TException {
+ }
+
+ public void methodWithDefaultArgs(int something) throws TException {
+ }
+
+ @Override
+ public void onewayMethod() throws TException {
+ }
+
+ @Override
+ public boolean declaredExceptionMethod(boolean shouldThrow) throws TException {
+ return shouldThrow;
+ }
+ };
+
+ Srv.Processor testProcessor = new Srv.Processor(handler);
+
+ TMemoryBuffer clientOutTrans = new TMemoryBuffer(0);
+ TProtocol clientOutProto = getFactory().getProtocol(clientOutTrans);
+ TMemoryBuffer clientInTrans = new TMemoryBuffer(0);
+ TProtocol clientInProto = getFactory().getProtocol(clientInTrans);
+
+ Srv.Client testClient = new Srv.Client(clientInProto, clientOutProto);
+
+ testClient.send_Janky(1);
+ // System.out.println(clientOutTrans.inspect());
+ testProcessor.process(clientOutProto, clientInProto);
+ // System.out.println(clientInTrans.inspect());
+ assertEquals(2, testClient.recv_Janky());
+ }
+
+ public void testTDeserializer() throws TException {
+ TSerializer ser = new TSerializer(getFactory());
+ byte[] bytes = ser.serialize(Fixtures.compactProtoTestStruct);
+
+ TDeserializer deser = new TDeserializer(getFactory());
+ CompactProtoTestStruct cpts = new CompactProtoTestStruct();
+ deser.deserialize(cpts, bytes);
+
+ assertEquals(Fixtures.compactProtoTestStruct, cpts);
+ }
+
+ //
+ // Helper methods
+ //
+
+ private void internalTestStructField(StructFieldTestCase testCase) throws Exception {
+ TMemoryBuffer buf = new TMemoryBuffer(0);
+ TProtocol proto = getFactory().getProtocol(buf);
+
+ TField field = new TField("test_field", testCase.type_, testCase.id_);
+ proto.writeStructBegin(new TStruct("test_struct"));
+ proto.writeFieldBegin(field);
+ testCase.writeMethod(proto);
+ proto.writeFieldEnd();
+ proto.writeStructEnd();
+
+ proto.readStructBegin();
+ TField readField = proto.readFieldBegin();
+ assertEquals(testCase.id_, readField.id);
+ assertEquals(testCase.type_, readField.type);
+ testCase.readMethod(proto);
+ proto.readStructEnd();
+ }
+
+ private static abstract class StructFieldTestCase {
+ byte type_;
+ short id_;
+ public StructFieldTestCase(byte type, short id) {
+ type_ = type;
+ id_ = id;
+ }
+
+ public abstract void writeMethod(TProtocol proto) throws TException;
+ public abstract void readMethod(TProtocol proto) throws TException;
+ }
+
+ private static final int NUM_TRIALS = 5;
+ private static final int NUM_REPS = 10000;
+
+ protected void benchmark() throws Exception {
+ for (int trial = 0; trial < NUM_TRIALS; trial++) {
+ TSerializer ser = new TSerializer(getFactory());
+ byte[] serialized = null;
+ long serStart = System.currentTimeMillis();
+ for (int rep = 0; rep < NUM_REPS; rep++) {
+ serialized = ser.serialize(Fixtures.holyMoley);
+ }
+ long serEnd = System.currentTimeMillis();
+ long serElapsed = serEnd - serStart;
+ System.out.println("Ser:\t" + serElapsed + "ms\t"
+ + ((double)serElapsed / NUM_REPS) + "ms per serialization");
+
+ HolyMoley cpts = new HolyMoley();
+ TDeserializer deser = new TDeserializer(getFactory());
+ long deserStart = System.currentTimeMillis();
+ for (int rep = 0; rep < NUM_REPS; rep++) {
+ deser.deserialize(cpts, serialized);
+ }
+ long deserEnd = System.currentTimeMillis();
+ long deserElapsed = deserEnd - deserStart;
+ System.out.println("Des:\t" + deserElapsed + "ms\t"
+ + ((double)deserElapsed / NUM_REPS) + "ms per deserialization");
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestShortStack.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestShortStack.java
new file mode 100644
index 000000000..c8e78eee6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestShortStack.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.protocol;
+
+import junit.framework.TestCase;
+
+public class TestShortStack extends TestCase {
+
+ public void testOps() throws Exception {
+ ShortStack s = new ShortStack(1);
+ s.push((short)10);
+ s.push((short)11);
+ s.push((short)12);
+ assertEquals((short)12, s.pop());
+ assertEquals((short)11, s.pop());
+ s.push((short)40);
+ assertEquals((short)40, s.pop());
+ assertEquals((short)10, s.pop());
+ try {
+ s.pop();
+ fail("should have thrown an exception!");
+ } catch (Exception e) {
+ // yay
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTCompactProtocol.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTCompactProtocol.java
new file mode 100644
index 000000000..b4c0888a4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTCompactProtocol.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.TDeserializer;
+import org.apache.thrift.TException;
+
+import thrift.test.Bonk;
+
+public class TestTCompactProtocol extends ProtocolTestBase {
+ @Override
+ protected TProtocolFactory getFactory() {
+ return new TCompactProtocol.Factory();
+ }
+
+ @Override
+ protected boolean canBeUsedNaked() {
+ return true;
+ }
+
+ public void testOOMDenialOfService() throws Exception {
+ // Struct header, Integer.MAX_VALUE length, and only one real
+ // byte of data
+ byte [] bytes = {24, -1, -1, -1, -17, 49};
+ TDeserializer deser = new TDeserializer(new TCompactProtocol
+ .Factory(1000));
+ Bonk bonk = new Bonk();
+ try {
+ deser.deserialize(bonk, bytes);
+ } catch (TException e) {
+ // Ignore as we are only checking for OOM in the failure case
+ }
+ }
+
+ public static void main(String args[]) throws Exception {
+ new TestTCompactProtocol().benchmark();
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTField.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTField.java
new file mode 100644
index 000000000..f72c25972
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTField.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.protocol;
+
+import junit.framework.TestCase;
+import static org.junit.Assert.assertNotEquals;
+
+public abstract class TestTField extends TestCase {
+
+ public void testConstructor() {
+ TField uut = new TField();
+ assertEquals("", uut.name);
+ assertEquals(TType.STOP, uut.type);
+ assertEquals(0, uut.id);
+
+ uut = new TField("foo", TType.VOID, (short)42);
+ assertEquals("foo", uut.name);
+ assertEquals(TType.VOID, uut.type);
+ assertEquals(42, uut.id);
+ }
+
+ public void testEquality() {
+ TField uut1 = new TField();
+ TField uut2 = new TField();
+ assertEquals(uut1, uut2);
+ assertEquals(uut1.hashCode(), uut2.hashCode());
+
+ uut1 = new TField("foo", TType.I32, (short)1);
+ uut2 = new TField("foo", TType.I32, (short)2);
+ assertNotEquals(uut1, uut2);
+ assertNotEquals(uut1.hashCode(), uut2.hashCode());
+
+ uut1 = new TField("foo", TType.VOID, (short)1);
+ uut2 = new TField("foo", TType.I32, (short)1);
+ assertNotEquals(uut1, uut2);
+ assertNotEquals(uut1.hashCode(), uut2.hashCode());
+
+ uut1 = new TField("foo", TType.VOID, (short)5);
+ uut2 = new TField("bar", TType.I32, (short)5);
+ assertEquals(uut1, uut2); // name field is ignored
+ assertEquals(uut1.hashCode(), uut2.hashCode());
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTJSONProtocol.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTJSONProtocol.java
new file mode 100644
index 000000000..c2ca1fa7a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTJSONProtocol.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.protocol;
+
+import java.nio.charset.StandardCharsets;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.protocol.TJSONProtocol;
+import org.apache.thrift.transport.TMemoryBuffer;
+
+public class TestTJSONProtocol extends ProtocolTestBase {
+ @Override
+ protected TProtocolFactory getFactory() {
+ return new TJSONProtocol.Factory();
+ }
+
+ @Override
+ protected boolean canBeUsedNaked() {
+ return false;
+ }
+
+ public void testEscapedUnicode() throws TException {
+ String jsonString = "\"hello unicode \\u0e01\\ud834\\udd1e world\"";
+ String expectedString = "hello unicode \u0e01\ud834\udd1e world";
+
+ TMemoryBuffer buffer = new TMemoryBuffer(1000);
+ TJSONProtocol protocol = new TJSONProtocol(buffer);
+ buffer.write(jsonString.getBytes(StandardCharsets.UTF_8));
+
+ assertEquals(expectedString, protocol.readString());
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTProtocolUtil.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTProtocolUtil.java
new file mode 100644
index 000000000..89cf5366e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTProtocolUtil.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.protocol;
+
+import junit.framework.TestCase;
+
+import org.apache.thrift.TSerializer;
+
+import thrift.test.GuessProtocolStruct;
+
+public class TestTProtocolUtil extends TestCase {
+
+ public void testGuessProtocolFactory_JSON() throws Exception {
+
+ byte[] data = "{foo}".getBytes();
+ TProtocolFactory factory = TProtocolUtil.guessProtocolFactory(data, new TCompactProtocol.Factory());
+ assertTrue(factory instanceof TJSONProtocol.Factory);
+
+ // Make sure data serialized with TCompact and which starts with '{'
+ // is not mistakenly guessed as serialized with JSON.
+
+ GuessProtocolStruct s = new GuessProtocolStruct();
+ s.putToMap_field("}","}");
+ byte[] ser = new TSerializer(new TCompactProtocol.Factory()).serialize(s);
+ factory = TProtocolUtil.guessProtocolFactory(ser, new TCompactProtocol.Factory());
+ assertFalse(factory instanceof TJSONProtocol.Factory);
+ }
+
+ public void testGuessProtocolFactory_Binary() throws Exception {
+ // Check that a last byte != 0 is correctly reported as Binary
+
+ byte[] buf = new byte[1];
+ for (int i = 1; i < 256; i++) {
+ buf[0] = (byte) i;
+ TProtocolFactory factory = TProtocolUtil.guessProtocolFactory(buf, new TCompactProtocol.Factory());
+ assertTrue(factory instanceof TBinaryProtocol.Factory);
+ }
+
+ // Check that a second byte set to 0 is reported as Binary
+ buf = new byte[2];
+ TProtocolFactory factory = TProtocolUtil.guessProtocolFactory(buf, new TCompactProtocol.Factory());
+ assertTrue(factory instanceof TBinaryProtocol.Factory);
+ }
+
+ public void testGuessProtocolFactory_Compact() throws Exception {
+ // Check that a first byte > 0x10 is reported as Compact
+ byte[] buf = new byte[3];
+ buf[0] = 0x11;
+ TProtocolFactory factory = TProtocolUtil.guessProtocolFactory(buf, new TBinaryProtocol.Factory());
+ assertTrue(factory instanceof TCompactProtocol.Factory);
+
+ // Check that second byte >= 0x80 is reported as Compact
+ buf[0] = 0;
+ for (int i = 0x80; i < 0x100; i++) {
+ buf[1] = (byte) i;
+ factory = TProtocolUtil.guessProtocolFactory(buf, new TBinaryProtocol.Factory());
+ assertTrue(factory instanceof TCompactProtocol.Factory);
+ }
+ }
+
+ public void testGuessProtocolFactory_Undecided() throws Exception {
+ byte[] buf = new byte[3];
+ buf[1] = 0x7e;
+ TProtocolFactory factory = TProtocolUtil.guessProtocolFactory(buf, new TSimpleJSONProtocol.Factory());
+ assertTrue(factory instanceof TSimpleJSONProtocol.Factory);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTSimpleJSONProtocol.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTSimpleJSONProtocol.java
new file mode 100644
index 000000000..171a487ea
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTSimpleJSONProtocol.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.protocol;
+
+import java.nio.charset.StandardCharsets;
+
+import junit.framework.TestCase;
+
+import org.apache.thrift.Fixtures;
+import org.apache.thrift.TException;
+import org.apache.thrift.transport.TMemoryBuffer;
+
+import thrift.test.CompactProtoTestStruct;
+import thrift.test.HolyMoley;
+
+public class TestTSimpleJSONProtocol extends TestCase {
+ private TMemoryBuffer buf;
+ private TSimpleJSONProtocol proto;
+
+ @Override
+ protected void setUp() throws Exception {
+ buf = new TMemoryBuffer(1000);
+ proto = new TSimpleJSONProtocol(buf);
+ }
+
+ private String bufToString() {
+ return buf.toString(StandardCharsets.UTF_8);
+ }
+
+ public void testHolyMoley() throws TException {
+ final HolyMoley holyMoley = Fixtures.holyMoley.deepCopy();
+ // unset sets that produce inconsistent ordering between JDK7/8
+ holyMoley.unsetBonks();
+ holyMoley.unsetContain();
+ holyMoley.write(proto);
+ assertEquals("{\"big\":[{\"im_true\":1,\"im_false\":0,\"a_bite\":35,\"integer16\":27000,\"integer32\":16777216,\"integer64\":6000000000,\"double_precision\":3.141592653589793,\"some_characters\":\"JSON THIS! \\\"\\u0001\",\"zomg_unicode\":\"ӀⅮΠÐοⅿоɡгаÏâ„Ž Αttαⅽκ�‼\",\"what_who\":0,\"base64\":\"base64\",\"byte_list\":[1,2,3],\"i16_list\":[1,2,3],\"i64_list\":[1,2,3]},{\"im_true\":1,\"im_false\":0,\"a_bite\":-42,\"integer16\":27000,\"integer32\":16777216,\"integer64\":6000000000,\"double_precision\":3.141592653589793,\"some_characters\":\"JSON THIS! \\\"\\u0001\",\"zomg_unicode\":\"ӀⅮΠÐοⅿоɡгаÏâ„Ž Αttαⅽκ�‼\",\"what_who\":0,\"base64\":\"base64\",\"byte_list\":[1,2,3],\"i16_list\":[1,2,3],\"i64_list\":[1,2,3]}]}", bufToString());
+ }
+
+ public void testNesting() throws TException {
+ Fixtures.nesting.write(proto);
+ assertEquals("{\"my_bonk\":{\"type\":31337,\"message\":\"I am a bonk... xor!\"},\"my_ooe\":{\"im_true\":1,\"im_false\":0,\"a_bite\":-42,\"integer16\":27000,\"integer32\":16777216,\"integer64\":6000000000,\"double_precision\":3.141592653589793,\"some_characters\":\"JSON THIS! \\\"\\u0001\",\"zomg_unicode\":\"ӀⅮΠÐοⅿоɡгаÏâ„Ž Αttαⅽκ�‼\",\"what_who\":0,\"base64\":\"base64\",\"byte_list\":[1,2,3],\"i16_list\":[1,2,3],\"i64_list\":[1,2,3]}}", bufToString());
+ }
+
+ public void testOneOfEach() throws TException {
+ Fixtures.oneOfEach.write(proto);
+ assertEquals("{\"im_true\":1,\"im_false\":0,\"a_bite\":-42,\"integer16\":27000,\"integer32\":16777216,\"integer64\":6000000000,\"double_precision\":3.141592653589793,\"some_characters\":\"JSON THIS! \\\"\\u0001\",\"zomg_unicode\":\"ӀⅮΠÐοⅿоɡгаÏâ„Ž Αttαⅽκ�‼\",\"what_who\":0,\"base64\":\"base64\",\"byte_list\":[1,2,3],\"i16_list\":[1,2,3],\"i64_list\":[1,2,3]}", bufToString());
+ }
+
+ public void testSanePartsOfCompactProtoTestStruct() throws TException {
+ // unset all the maps with container keys
+ CompactProtoTestStruct struct = Fixtures.compactProtoTestStruct.deepCopy();
+ struct.unsetList_byte_map();
+ struct.unsetSet_byte_map();
+ struct.unsetMap_byte_map();
+ // unset sets and maps that produce inconsistent ordering between JDK7/8
+ struct.unsetByte_set();
+ struct.unsetI16_set();
+ struct.unsetI64_set();
+ struct.unsetDouble_set();
+ struct.unsetString_set();
+ struct.unsetI16_byte_map();
+ struct.unsetI32_byte_map();
+ struct.unsetI64_byte_map();
+ struct.unsetDouble_byte_map();
+ struct.unsetString_byte_map();
+ struct.write(proto);
+ assertEquals("{\"a_byte\":127,\"a_i16\":32000,\"a_i32\":1000000000,\"a_i64\":1099511627775,\"a_double\":5.6789,\"a_string\":\"my string\",\"a_binary\":\"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\",\"true_field\":1,\"false_field\":0,\"empty_struct_field\":{},\"byte_list\":[-127,-1,0,1,127],\"i16_list\":[-1,0,1,32767],\"i32_list\":[-1,0,255,65535,16777215,2147483647],\"i64_list\":[-1,0,255,65535,16777215,4294967295,1099511627775,281474976710655,72057594037927935,9223372036854775807],\"double_list\":[0.1,0.2,0.3],\"string_list\":[\"first\",\"second\",\"third\"],\"boolean_list\":[1,1,1,0,0,0],\"struct_list\":[{},{}],\"i32_set\":[1,2,3],\"boolean_set\":[0,1],\"struct_set\":[{}],\"byte_byte_map\":{\"1\":2},\"boolean_byte_map\":{\"0\":0,\"1\":1},\"byte_i16_map\":{\"1\":1,\"2\":-1,\"3\":32767},\"byte_i32_map\":{\"1\":1,\"2\":-1,\"3\":2147483647},\"byte_i64_map\":{\"1\":1,\"2\":-1,\"3\":9223372036854775807},\"byte_double_map\":{\"1\":0.1,\"2\":-0.1,\"3\":1000000.1},\"byte_string_map\":{\"1\":\"\",\"2\":\"blah\",\"3\":\"loooooooooooooong string\"},\"byte_boolean_map\":{\"1\":1,\"2\":0},\"byte_map_map\":{\"0\":{},\"1\":{\"1\":1},\"2\":{\"1\":1,\"2\":2}},\"byte_set_map\":{\"0\":[],\"1\":[1],\"2\":[1,2]},\"byte_list_map\":{\"0\":[],\"1\":[1],\"2\":[1,2]},\"field500\":500,\"field5000\":5000,\"field20000\":20000}", bufToString());
+ }
+
+ public void testThrowsOnCollectionKeys() throws TException {
+ try {
+ Fixtures.compactProtoTestStruct.write(proto);
+ fail("this should throw a CollectionMapKeyException");
+ } catch (TSimpleJSONProtocol.CollectionMapKeyException e) {
+ //
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTTupleProtocol.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTTupleProtocol.java
new file mode 100644
index 000000000..b654db3f8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTTupleProtocol.java
@@ -0,0 +1,27 @@
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.TDeserializer;
+import org.apache.thrift.TSerializer;
+
+import thrift.test.TupleProtocolTestStruct;
+
+
+public class TestTTupleProtocol extends ProtocolTestBase {
+
+ @Override
+ protected boolean canBeUsedNaked() {
+ return false;
+ }
+
+ @Override
+ protected TProtocolFactory getFactory() {
+ return new TTupleProtocol.Factory();
+ }
+
+ public void testBitsetLengthIssue() throws Exception {
+ final TupleProtocolTestStruct t1 = new TupleProtocolTestStruct();
+ t1.setField1(0);
+ t1.setField2(12);
+ new TDeserializer(new TTupleProtocol.Factory()).deserialize(new TupleProtocolTestStruct(), new TSerializer(new TTupleProtocol.Factory()).serialize(t1));
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/scheme/TestStandardScheme.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/scheme/TestStandardScheme.java
new file mode 100644
index 000000000..33f229e88
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/scheme/TestStandardScheme.java
@@ -0,0 +1,40 @@
+package org.apache.thrift.scheme;
+
+import junit.framework.TestCase;
+
+import org.apache.thrift.Fixtures;
+import org.apache.thrift.TBase;
+import org.apache.thrift.TDeserializer;
+import org.apache.thrift.TException;
+import org.apache.thrift.TSerializer;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.transport.TMemoryBuffer;
+import org.apache.thrift.transport.TTransport;
+
+import thrift.test.HolyMoley;
+import thrift.test.Nesting;
+import thrift.test.OneOfEach;
+
+public class TestStandardScheme extends TestCase {
+ TSerializer serializer = new TSerializer();
+ TDeserializer deserializer = new TDeserializer();
+
+ /**
+ * This tests whether the Standard Scheme properly reads structs serialized
+ * using an older version of thrift.
+ */
+ public void testPersistentStructs() throws TException {
+ readAndCompare(new OneOfEach(), Fixtures.oneOfEach, Fixtures.persistentBytesOneOfEach);
+ readAndCompare(new HolyMoley(), Fixtures.holyMoley, Fixtures.persistentBytesHolyMoley);
+ readAndCompare(new Nesting(), Fixtures.nesting, Fixtures.persistentBytesNesting);
+ }
+
+ public void readAndCompare(TBase struct, TBase fixture, byte[] inputBytes) throws TException {
+ TTransport trans = new TMemoryBuffer(0);
+ trans.write(inputBytes, 0, inputBytes.length);
+ TProtocol iprot = new TBinaryProtocol(trans);
+ struct.read(iprot);
+ assertEquals(fixture, struct);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/ServerTestBase.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/ServerTestBase.java
new file mode 100644
index 000000000..8348cbc3d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/ServerTestBase.java
@@ -0,0 +1,715 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.server;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import java.nio.ByteBuffer;
+
+import junit.framework.TestCase;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.async.AsyncMethodCallback;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TCompactProtocol;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TFramedTransport;
+import org.apache.thrift.transport.TSocket;
+import org.apache.thrift.transport.TTransport;
+import org.apache.thrift.transport.TTransportFactory;
+import org.apache.thrift.transport.TFramedTransport.Factory;
+
+import thrift.test.Insanity;
+import thrift.test.Numberz;
+import thrift.test.ThriftTest;
+import thrift.test.Xception;
+import thrift.test.Xception2;
+import thrift.test.Xtruct;
+import thrift.test.Xtruct2;
+
+public abstract class ServerTestBase extends TestCase {
+
+ public static class TestHandler implements ThriftTest.Iface {
+
+ public TestHandler() {}
+
+ public void testVoid() {
+ System.out.print("testVoid()\n");
+ }
+
+ public String testString(String thing) {
+ System.out.print("testString(\"" + thing + "\")\n");
+ return thing;
+ }
+
+ public boolean testBool(boolean thing) {
+ System.out.print("testBool(" + thing + ")\n");
+ return thing;
+ }
+
+ public byte testByte(byte thing) {
+ System.out.print("testByte(" + thing + ")\n");
+ return thing;
+ }
+
+ public int testI32(int thing) {
+ System.out.print("testI32(" + thing + ")\n");
+ return thing;
+ }
+
+ public long testI64(long thing) {
+ System.out.print("testI64(" + thing + ")\n");
+ return thing;
+ }
+
+ public double testDouble(double thing) {
+ System.out.print("testDouble(" + thing + ")\n");
+ return thing;
+ }
+
+ public ByteBuffer testBinary(ByteBuffer thing) {
+ StringBuilder sb = new StringBuilder(thing.remaining() * 3);
+ thing.mark();
+ int limit = 0; // limit output to keep the log size sane
+ while ((thing.remaining() > 0) && (++limit < 1024)) {
+ sb.append(String.format("%02X ", thing.get()));
+ }
+ if(thing.remaining() > 0) {
+ sb.append("..."); // indicate we have more date
+ }
+ System.out.print("testBinary(" + sb.toString() + ")\n");
+ thing.reset();
+ return thing;
+ }
+
+ public Xtruct testStruct(Xtruct thing) {
+ System.out.print("testStruct({" +
+ "\"" + thing.string_thing + "\", " +
+ thing.byte_thing + ", " +
+ thing.i32_thing + ", " +
+ thing.i64_thing + "})\n");
+ return thing;
+ }
+
+ public Xtruct2 testNest(Xtruct2 nest) {
+ Xtruct thing = nest.struct_thing;
+ System.out.print("testNest({" +
+ nest.byte_thing + ", {" +
+ "\"" + thing.string_thing + "\", " +
+ thing.byte_thing + ", " +
+ thing.i32_thing + ", " +
+ thing.i64_thing + "}, " +
+ nest.i32_thing + "})\n");
+ return nest;
+ }
+
+ public Map<Integer,Integer> testMap(Map<Integer,Integer> thing) {
+ System.out.print("testMap({");
+ System.out.print(thing);
+ System.out.print("})\n");
+ return thing;
+ }
+
+ public Map<String,String> testStringMap(Map<String,String> thing) {
+ System.out.print("testStringMap({");
+ System.out.print(thing);
+ System.out.print("})\n");
+ return thing;
+ }
+
+ public Set<Integer> testSet(Set<Integer> thing) {
+ System.out.print("testSet({");
+ boolean first = true;
+ for (int elem : thing) {
+ if (first) {
+ first = false;
+ } else {
+ System.out.print(", ");
+ }
+ System.out.print(elem);
+ }
+ System.out.print("})\n");
+ return thing;
+ }
+
+ public List<Integer> testList(List<Integer> thing) {
+ System.out.print("testList({");
+ boolean first = true;
+ for (int elem : thing) {
+ if (first) {
+ first = false;
+ } else {
+ System.out.print(", ");
+ }
+ System.out.print(elem);
+ }
+ System.out.print("})\n");
+ return thing;
+ }
+
+ public Numberz testEnum(Numberz thing) {
+ System.out.print("testEnum(" + thing + ")\n");
+ return thing;
+ }
+
+ public long testTypedef(long thing) {
+ System.out.print("testTypedef(" + thing + ")\n");
+ return thing;
+ }
+
+ public Map<Integer,Map<Integer,Integer>> testMapMap(int hello) {
+ System.out.print("testMapMap(" + hello + ")\n");
+ Map<Integer,Map<Integer,Integer>> mapmap =
+ new HashMap<Integer,Map<Integer,Integer>>();
+
+ HashMap<Integer,Integer> pos = new HashMap<Integer,Integer>();
+ HashMap<Integer,Integer> neg = new HashMap<Integer,Integer>();
+ for (int i = 1; i < 5; i++) {
+ pos.put(i, i);
+ neg.put(-i, -i);
+ }
+
+ mapmap.put(4, pos);
+ mapmap.put(-4, neg);
+
+ return mapmap;
+ }
+
+ public Map<Long, Map<Numberz,Insanity>> testInsanity(Insanity argument) {
+ System.out.print("testInsanity()\n");
+
+ HashMap<Numberz,Insanity> first_map = new HashMap<Numberz, Insanity>();
+ HashMap<Numberz,Insanity> second_map = new HashMap<Numberz, Insanity>();;
+
+ first_map.put(Numberz.TWO, argument);
+ first_map.put(Numberz.THREE, argument);
+
+ Insanity looney = new Insanity();
+ second_map.put(Numberz.SIX, looney);
+
+ Map<Long,Map<Numberz,Insanity>> insane =
+ new HashMap<Long, Map<Numberz,Insanity>>();
+ insane.put((long)1, first_map);
+ insane.put((long)2, second_map);
+
+ return insane;
+ }
+
+ public Xtruct testMulti(byte arg0, int arg1, long arg2, Map<Short,String> arg3, Numberz arg4, long arg5) {
+ System.out.print("testMulti()\n");
+
+ Xtruct hello = new Xtruct();;
+ hello.string_thing = "Hello2";
+ hello.byte_thing = arg0;
+ hello.i32_thing = arg1;
+ hello.i64_thing = arg2;
+ return hello;
+ }
+
+ public void testException(String arg) throws Xception, TException {
+ System.out.print("testException("+arg+")\n");
+ if ("Xception".equals(arg)) {
+ Xception x = new Xception();
+ x.errorCode = 1001;
+ x.message = arg;
+ throw x;
+ } else if ("TException".equals(arg)) {
+ // Unspecified exception should yield a TApplicationException on client side
+ throw new RuntimeException(arg);
+ } else {
+ Xtruct result = new Xtruct();
+ result.string_thing = arg;
+ }
+ return;
+ }
+
+ public Xtruct testMultiException(String arg0, String arg1) throws Xception, Xception2 {
+ System.out.print("testMultiException(" + arg0 + ", " + arg1 + ")\n");
+ if (arg0.equals("Xception")) {
+ Xception x = new Xception();
+ x.errorCode = 1001;
+ x.message = "This is an Xception";
+ throw x;
+ } else if (arg0.equals("Xception2")) {
+ Xception2 x = new Xception2();
+ x.errorCode = 2002;
+ x.struct_thing = new Xtruct();
+ x.struct_thing.string_thing = "This is an Xception2";
+ throw x;
+ }
+
+ Xtruct result = new Xtruct();
+ result.string_thing = arg1;
+ return result;
+ }
+
+ public void testOneway(int sleepFor) {
+ System.out.println("testOneway(" + Integer.toString(sleepFor) +
+ ") => sleeping...");
+ try {
+ Thread.sleep(sleepFor * SLEEP_DELAY);
+ System.out.println("Done sleeping!");
+ } catch (InterruptedException ie) {
+ throw new RuntimeException(ie);
+ }
+ }
+ } // class TestHandler
+
+ private static final List<TProtocolFactory> PROTOCOLS = Arrays.asList(
+ new TBinaryProtocol.Factory(),
+ new TCompactProtocol.Factory());
+
+ public static final String HOST = "localhost";
+ public static final int PORT = Integer.valueOf(
+ System.getProperty("test.port", "9090"));
+ protected static final int SLEEP_DELAY = 1000;
+ protected static final int SOCKET_TIMEOUT = 1500;
+ private static final Xtruct XSTRUCT = new Xtruct("Zero", (byte) 1, -3, -5);
+ private static final Xtruct2 XSTRUCT2 = new Xtruct2((byte)1, XSTRUCT, 5);
+
+ public void startServer(TProcessor processor, TProtocolFactory protoFactory) throws Exception{
+ startServer(processor, protoFactory, null);
+ }
+
+ public abstract void startServer(TProcessor processor, TProtocolFactory protoFactory, TTransportFactory factory) throws Exception;
+
+ public abstract void stopServer() throws Exception;
+
+ public abstract TTransport getClientTransport(TTransport underlyingTransport) throws Exception;
+
+ private void testBool(ThriftTest.Client testClient) throws TException {
+ boolean t = testClient.testBool(true);
+ assertEquals(true, t);
+ boolean f = testClient.testBool(false);
+ assertEquals(false, f);
+ }
+
+ private void testByte(ThriftTest.Client testClient) throws TException {
+ byte i8 = testClient.testByte((byte)1);
+ assertEquals(1, i8);
+ }
+
+ private void testDouble(ThriftTest.Client testClient) throws TException {
+ double dub = testClient.testDouble(5.325098235);
+ assertEquals(5.325098235, dub);
+ }
+
+ private void testEnum(ThriftTest.Client testClient) throws TException {
+ assertEquals(Numberz.ONE, testClient.testEnum(Numberz.ONE));
+ assertEquals(Numberz.TWO, testClient.testEnum(Numberz.TWO));
+ assertEquals(Numberz.THREE, testClient.testEnum(Numberz.THREE));
+ assertEquals(Numberz.FIVE, testClient.testEnum(Numberz.FIVE));
+ assertEquals(Numberz.EIGHT, testClient.testEnum(Numberz.EIGHT));
+ }
+
+ private void testI32(ThriftTest.Client testClient) throws TException {
+ int i32 = testClient.testI32(-1);
+ assertEquals(i32, -1);
+ }
+
+ private void testI64(ThriftTest.Client testClient) throws TException {
+ long i64 = testClient.testI64(-34359738368L);
+ assertEquals(i64, -34359738368L);
+ }
+
+ // todo: add assertions
+ private void testInsanity(ThriftTest.Client testClient) throws TException {
+ Insanity insane;
+
+ insane = new Insanity();
+ insane.userMap = new HashMap<Numberz, Long>();
+ insane.userMap.put(Numberz.FIVE, (long)5000);
+ Xtruct truck = new Xtruct();
+ truck.string_thing = "Truck";
+ truck.byte_thing = (byte)8;
+ truck.i32_thing = 8;
+ truck.i64_thing = 8;
+ insane.xtructs = new ArrayList<Xtruct>();
+ insane.xtructs.add(truck);
+ System.out.print("testInsanity()");
+ Map<Long,Map<Numberz,Insanity>> whoa =
+ testClient.testInsanity(insane);
+ System.out.print(" = {");
+ for (long key : whoa.keySet()) {
+ Map<Numberz,Insanity> val = whoa.get(key);
+ System.out.print(key + " => {");
+
+ for (Numberz k2 : val.keySet()) {
+ Insanity v2 = val.get(k2);
+ System.out.print(k2 + " => {");
+ Map<Numberz, Long> userMap = v2.userMap;
+ System.out.print("{");
+ if (userMap != null) {
+ for (Numberz k3 : userMap.keySet()) {
+ System.out.print(k3 + " => " + userMap.get(k3) + ", ");
+ }
+ }
+ System.out.print("}, ");
+
+ List<Xtruct> xtructs = v2.xtructs;
+ System.out.print("{");
+ if (xtructs != null) {
+ for (Xtruct x : xtructs) {
+ System.out.print("{" + "\"" + x.string_thing + "\", " + x.byte_thing + ", " + x.i32_thing + ", "+ x.i64_thing + "}, ");
+ }
+ }
+ System.out.print("}");
+
+ System.out.print("}, ");
+ }
+ System.out.print("}, ");
+ }
+ System.out.print("}\n");
+ }
+
+ public boolean useAsyncProcessor() {
+ return false;
+ }
+
+ public void testIt() throws Exception {
+
+ for (TProtocolFactory protoFactory : getProtocols()) {
+ TProcessor processor = useAsyncProcessor() ? new ThriftTest.AsyncProcessor<AsyncTestHandler>(new AsyncTestHandler()) : new ThriftTest.Processor<TestHandler>(new TestHandler());
+
+ startServer(processor, protoFactory);
+
+ TSocket socket = new TSocket(HOST, PORT);
+ socket.setTimeout(SOCKET_TIMEOUT);
+ TTransport transport = getClientTransport(socket);
+
+ TProtocol protocol = protoFactory.getProtocol(transport);
+ ThriftTest.Client testClient = new ThriftTest.Client(protocol);
+
+ open(transport);
+ testVoid(testClient);
+ testString(testClient);
+ testBool(testClient);
+ testByte(testClient);
+ testI32(testClient);
+ testI64(testClient);
+ testDouble(testClient);
+ testStruct(testClient);
+ testNestedStruct(testClient);
+ testMap(testClient);
+ testStringMap(testClient);
+ testSet(testClient);
+ testList(testClient);
+ testEnum(testClient);
+ testTypedef(testClient);
+ testNestedMap(testClient);
+ testInsanity(testClient);
+ testException(testClient);
+ testOneway(testClient);
+ testI32(testClient);
+ transport.close();
+ socket.close();
+
+ stopServer();
+ }
+ }
+
+ public void open(TTransport transport) throws Exception {
+ transport.open();
+ }
+
+ public List<TProtocolFactory> getProtocols() {
+ return PROTOCOLS;
+ }
+
+ private void testList(ThriftTest.Client testClient) throws TException {
+ List<Integer> listout = new ArrayList<Integer>();
+ for (int i = -2; i < 3; ++i) {
+ listout.add(i);
+ }
+ List<Integer> listin = testClient.testList(listout);
+ assertEquals(listout, listin);
+ }
+
+ private void testMap(ThriftTest.Client testClient) throws TException {
+ Map<Integer,Integer> mapout = new HashMap<Integer,Integer>();
+ for (int i = 0; i < 5; ++i) {
+ mapout.put(i, i-10);
+ }
+ Map<Integer,Integer> mapin = testClient.testMap(mapout);
+ assertEquals(mapout, mapin);
+ }
+
+ private void testStringMap(ThriftTest.Client testClient) throws TException {
+ Map<String,String> mapout = new HashMap<String,String>();
+ mapout.put("a", "123");
+ mapout.put(" x y ", " with spaces ");
+ mapout.put("same", "same");
+ mapout.put("0", "numeric key");
+ Map<String,String> mapin = testClient.testStringMap(mapout);
+ assertEquals(mapout, mapin);
+ }
+
+ private void testNestedMap(ThriftTest.Client testClient) throws TException {
+ Map<Integer,Map<Integer,Integer>> mm =
+ testClient.testMapMap(1);
+ Map<Integer,Map<Integer,Integer>> mapmap =
+ new HashMap<Integer,Map<Integer,Integer>>();
+
+ HashMap<Integer,Integer> pos = new HashMap<Integer,Integer>();
+ HashMap<Integer,Integer> neg = new HashMap<Integer,Integer>();
+ for (int i = 1; i < 5; i++) {
+ pos.put(i, i);
+ neg.put(-i, -i);
+ }
+
+ mapmap.put(4, pos);
+ mapmap.put(-4, neg);
+ assertEquals(mapmap, mm);
+ }
+
+ private void testNestedStruct(ThriftTest.Client testClient) throws TException {
+ Xtruct2 in2 = testClient.testNest(XSTRUCT2);
+ assertEquals(XSTRUCT2, in2);
+ }
+
+ private void testOneway(ThriftTest.Client testClient) throws Exception {
+ long begin = System.currentTimeMillis();
+ testClient.testOneway(1);
+ long elapsed = System.currentTimeMillis() - begin;
+ assertTrue(elapsed < 500);
+ }
+
+ private void testSet(ThriftTest.Client testClient) throws TException {
+ Set<Integer> setout = new HashSet<Integer>();
+ for (int i = -2; i < 3; ++i) {
+ setout.add(i);
+ }
+ Set<Integer> setin = testClient.testSet(setout);
+ assertEquals(setout, setin);
+ }
+
+ private void testString(ThriftTest.Client testClient) throws TException {
+ String s = testClient.testString("Test");
+ assertEquals("Test", s);
+ }
+
+ private void testStruct(ThriftTest.Client testClient) throws TException {
+ assertEquals(XSTRUCT, testClient.testStruct(XSTRUCT));
+ }
+
+ private void testTypedef(ThriftTest.Client testClient) throws TException {
+ assertEquals(309858235082523L, testClient.testTypedef(309858235082523L));
+ }
+
+ private void testVoid(ThriftTest.Client testClient) throws TException {
+ testClient.testVoid();
+ }
+
+ private static class CallCountingTransportFactory extends TTransportFactory {
+ public int count = 0;
+ private final Factory factory;
+
+ public CallCountingTransportFactory(Factory factory) {
+ this.factory = factory;
+ }
+
+ @Override
+ public TTransport getTransport(TTransport trans) {
+ count++;
+ return factory.getTransport(trans);
+ }
+ }
+
+ public void testTransportFactory() throws Exception {
+ for (TProtocolFactory protoFactory : getProtocols()) {
+ TestHandler handler = new TestHandler();
+ ThriftTest.Processor<TestHandler> processor = new ThriftTest.Processor<TestHandler>(handler);
+
+ final CallCountingTransportFactory factory = new CallCountingTransportFactory(new TFramedTransport.Factory());
+
+ startServer(processor, protoFactory, factory);
+ assertEquals(0, factory.count);
+
+ TSocket socket = new TSocket(HOST, PORT);
+ socket.setTimeout(SOCKET_TIMEOUT);
+ TTransport transport = getClientTransport(socket);
+ open(transport);
+
+ TProtocol protocol = protoFactory.getProtocol(transport);
+ ThriftTest.Client testClient = new ThriftTest.Client(protocol);
+ assertEquals(0, testClient.testByte((byte) 0));
+ assertEquals(2, factory.count);
+ socket.close();
+ stopServer();
+ }
+ }
+
+ private void testException(ThriftTest.Client testClient) throws TException, Xception {
+ try {
+ testClient.testException("Xception");
+ assert false;
+ } catch(Xception e) {
+ assertEquals(e.message, "Xception");
+ assertEquals(e.errorCode, 1001);
+ }
+ try {
+ testClient.testException("TException");
+ assert false;
+ } catch(TException e) {
+ }
+ testClient.testException("no Exception");
+ }
+
+
+ public static class AsyncTestHandler implements ThriftTest.AsyncIface {
+
+ TestHandler handler = new TestHandler();
+
+ @Override
+ public void testVoid(AsyncMethodCallback<Void> resultHandler) throws TException {
+ resultHandler.onComplete(null);
+ }
+
+ @Override
+ public void testString(String thing, AsyncMethodCallback<String> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testString(thing));
+ }
+
+ @Override
+ public void testBool(boolean thing, AsyncMethodCallback<Boolean> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testBool(thing));
+ }
+
+ @Override
+ public void testByte(byte thing, AsyncMethodCallback<Byte> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testByte(thing));
+ }
+
+ @Override
+ public void testI32(int thing, AsyncMethodCallback<Integer> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testI32(thing));
+ }
+
+ @Override
+ public void testI64(long thing, AsyncMethodCallback<Long> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testI64(thing));
+ }
+
+ @Override
+ public void testDouble(double thing, AsyncMethodCallback<Double> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testDouble(thing));
+ }
+
+ @Override
+ public void testBinary(ByteBuffer thing, AsyncMethodCallback<ByteBuffer> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testBinary(thing));
+ }
+
+ @Override
+ public void testStruct(Xtruct thing, AsyncMethodCallback<Xtruct> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testStruct(thing));
+ }
+
+ @Override
+ public void testNest(Xtruct2 thing, AsyncMethodCallback<Xtruct2> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testNest(thing));
+ }
+
+ @Override
+ public void testMap(Map<Integer, Integer> thing, AsyncMethodCallback<Map<Integer, Integer>> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testMap(thing));
+ }
+
+ @Override
+ public void testStringMap(Map<String, String> thing, AsyncMethodCallback<Map<String, String>> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testStringMap(thing));
+ }
+
+ @Override
+ public void testSet(Set<Integer> thing, AsyncMethodCallback<Set<Integer>> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testSet(thing));
+ }
+
+ @Override
+ public void testList(List<Integer> thing, AsyncMethodCallback<List<Integer>> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testList(thing));
+ }
+
+ @Override
+ public void testEnum(Numberz thing, AsyncMethodCallback<Numberz> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testEnum(thing));
+ }
+
+ @Override
+ public void testTypedef(long thing, AsyncMethodCallback<Long> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testTypedef(thing));
+ }
+
+ @Override
+ public void testMapMap(int hello, AsyncMethodCallback<Map<Integer,Map<Integer,Integer>>> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testMapMap(hello));
+ }
+
+ @Override
+ public void testInsanity(Insanity argument, AsyncMethodCallback<Map<Long, Map<Numberz,Insanity>>> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testInsanity(argument));
+ }
+
+ @Override
+ public void testMulti(byte arg0, int arg1, long arg2, Map<Short, String> arg3, Numberz arg4, long arg5, AsyncMethodCallback<Xtruct> resultHandler) throws TException {
+ resultHandler.onComplete(handler.testMulti(arg0,arg1,arg2,arg3,arg4,arg5));
+ }
+
+ @Override
+ public void testException(String arg, AsyncMethodCallback<Void> resultHandler) throws TException {
+ System.out.print("testException("+arg+")\n");
+ if ("Xception".equals(arg)) {
+ Xception x = new Xception();
+ x.errorCode = 1001;
+ x.message = arg;
+ // throw and onError yield the same result.
+ // throw x;
+ resultHandler.onError(x);
+ return;
+ } else if ("TException".equals(arg)) {
+ // throw and onError yield the same result.
+ // resultHandler.onError(new TException(arg));
+ // return;
+ // Unspecified exception should yield a TApplicationException on client side
+ throw new RuntimeException(arg);
+ }
+ resultHandler.onComplete(null);
+ }
+
+ @Override
+ public void testMultiException(String arg0, String arg1, AsyncMethodCallback<Xtruct> resultHandler) throws TException {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void testOneway(int secondsToSleep, AsyncMethodCallback<Void> resultHandler) throws TException {
+ handler.testOneway(secondsToSleep);
+ resultHandler.onComplete(null);
+ }
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestAsyncServer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestAsyncServer.java
new file mode 100644
index 000000000..29c54cbbc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestAsyncServer.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.server;
+
+public class TestAsyncServer extends TestNonblockingServer {
+
+ @Override
+ public boolean useAsyncProcessor(){
+ return true;
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestHsHaServer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestHsHaServer.java
new file mode 100644
index 000000000..6638a333f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestHsHaServer.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.server;
+
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.server.THsHaServer.Args;
+import org.apache.thrift.transport.TNonblockingServerSocket;
+
+public class TestHsHaServer extends TestNonblockingServer {
+ protected TServer getServer(TProcessor processor, TNonblockingServerSocket socket, TProtocolFactory protoFactory) {
+ return new THsHaServer(new Args(socket).processor(processor).protocolFactory(protoFactory));
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestNonblockingServer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestNonblockingServer.java
new file mode 100644
index 000000000..3df3bd827
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestNonblockingServer.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.server;
+
+
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.server.TNonblockingServer.Args;
+import org.apache.thrift.transport.TFramedTransport;
+import org.apache.thrift.transport.TNonblockingServerSocket;
+import org.apache.thrift.transport.TSocket;
+import org.apache.thrift.transport.TTransport;
+import org.apache.thrift.transport.TTransportException;
+import org.apache.thrift.transport.TTransportFactory;
+
+import thrift.test.ThriftTest;
+
+public class TestNonblockingServer extends ServerTestBase {
+
+ private Thread serverThread;
+ private TServer server;
+ private static final int NUM_QUERIES = 1000;
+
+ protected TServer getServer(TProcessor processor, TNonblockingServerSocket socket, TProtocolFactory protoFactory, TTransportFactory factory) {
+ final Args args = new Args(socket).processor(processor).protocolFactory(protoFactory);
+ if (factory != null) {
+ args.transportFactory(factory);
+ }
+ return new TNonblockingServer(args);
+ }
+
+ @Override
+ public void startServer(final TProcessor processor, final TProtocolFactory protoFactory, final TTransportFactory factory) throws Exception {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ // Transport
+ TNonblockingServerSocket tServerSocket =
+ new TNonblockingServerSocket(new TNonblockingServerSocket.NonblockingAbstractServerSocketArgs().port(PORT));
+
+ server = getServer(processor, tServerSocket, protoFactory, factory);
+
+ // Run it
+ System.out.println("Starting the server on port " + PORT + "...");
+ server.serve();
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail();
+ }
+ }
+ };
+ serverThread.start();
+ Thread.sleep(1000);
+ }
+
+ @Override
+ public void stopServer() throws Exception {
+ server.stop();
+ try {
+ serverThread.join();
+ } catch (InterruptedException e) {}
+ }
+
+ @Override
+ public TTransport getClientTransport(TTransport underlyingTransport) throws Exception {
+ return new TFramedTransport(underlyingTransport);
+ }
+
+
+ public void testCleanupAllSelectionKeys() throws Exception {
+ for (TProtocolFactory protoFactory : getProtocols()) {
+ TestHandler handler = new TestHandler();
+ ThriftTest.Processor processor = new ThriftTest.Processor(handler);
+
+ startServer(processor, protoFactory);
+
+ TSocket socket = new TSocket(HOST, PORT);
+ socket.setTimeout(SOCKET_TIMEOUT);
+ TTransport transport = getClientTransport(socket);
+
+ TProtocol protocol = protoFactory.getProtocol(transport);
+ ThriftTest.Client testClient = new ThriftTest.Client(protocol);
+
+ open(transport);
+
+ for (int i = 0; i < NUM_QUERIES; ++i) {
+ testClient.testI32(1);
+ }
+ server.stop();
+ for (int i = 0; i < NUM_QUERIES; ++i) {
+ try {
+ testClient.testI32(1);
+ } catch(TTransportException e) {
+ System.err.println(e);
+ e.printStackTrace();
+ if (e.getCause() instanceof java.net.SocketTimeoutException) {
+ fail("timed out when it should have thrown another kind of error!");
+ }
+ }
+ }
+
+ transport.close();
+ stopServer();
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestThreadedSelectorServer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestThreadedSelectorServer.java
new file mode 100644
index 000000000..ed729a296
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestThreadedSelectorServer.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.server;
+
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.server.TThreadedSelectorServer.Args;
+import org.apache.thrift.transport.TNonblockingServerSocket;
+
+public class TestThreadedSelectorServer extends TestNonblockingServer {
+ protected TServer getServer(TProcessor processor, TNonblockingServerSocket socket, TProtocolFactory protoFactory) {
+ return new TThreadedSelectorServer(new Args(socket).processor(processor).protocolFactory(protoFactory));
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/EqualityTest.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/EqualityTest.java
new file mode 100644
index 000000000..94ba543a8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/EqualityTest.java
@@ -0,0 +1,663 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+This program was generated by the following Python script:
+
+#!/usr/bin/python2.5
+
+# Remove this when Python 2.6 hits the streets.
+from __future__ import with_statement
+
+import sys
+import os.path
+
+
+# Quines the easy way.
+with open(sys.argv[0], 'r') as handle:
+ source = handle.read()
+
+with open(os.path.join(os.path.dirname(sys.argv[0]), 'EqualityTest.java'), 'w') as out:
+ print >> out, ("/""*" r"""
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ """ "*""/")
+ print >> out
+ print >> out, "/""*"
+ print >> out, "This program was generated by the following Python script:"
+ print >> out
+ out.write(source)
+ print >> out, "*""/"
+
+ print >> out, r'''
+package org.apache.thrift.test;
+
+// Generated code
+import thrift.test.*;
+
+/'''r'''**
+ *'''r'''/
+public class EqualityTest {
+ public static void main(String[] args) throws Exception {
+ JavaTestHelper lhs, rhs;
+'''
+
+ vals = {
+ 'int': ("1", "2"),
+ 'obj': ("\"foo\"", "\"bar\""),
+ 'bin': ("new byte[]{1,2}", "new byte[]{3,4}"),
+ }
+ matrix = (
+ (False,False),
+ (False,True ),
+ (True ,False),
+ (True ,True ),
+ )
+
+ for type in ('int', 'obj', 'bin'):
+ for option in ('req', 'opt'):
+ nulls = matrix[0:1] if type == 'int' else matrix[-1::-1]
+ issets = matrix
+ for is_null in nulls:
+ for is_set in issets:
+ # isset is implied for non-primitives, so only consider the case
+ # where isset and non-null match.
+ if type != 'int' and list(is_set) != [ not null for null in is_null ]:
+ continue
+ for equal in (True, False):
+ print >> out
+ print >> out, " lhs = new JavaTestHelper();"
+ print >> out, " rhs = new JavaTestHelper();"
+ print >> out, " lhs." + option + "_" + type, "=", vals[type][0] + ";"
+ print >> out, " rhs." + option + "_" + type, "=", vals[type][0 if equal else 1] + ";"
+ isset_setter = "set" + option[0].upper() + option[1:] + "_" + type + "IsSet"
+ if (type == 'int' and is_set[0]): print >> out, " lhs." + isset_setter + "(true);"
+ if (type == 'int' and is_set[1]): print >> out, " rhs." + isset_setter + "(true);"
+ if (is_null[0]): print >> out, " lhs." + option + "_" + type, "= null;"
+ if (is_null[1]): print >> out, " rhs." + option + "_" + type, "= null;"
+ this_present = not is_null[0] and (option == 'req' or is_set[0])
+ that_present = not is_null[1] and (option == 'req' or is_set[1])
+ print >> out, " // this_present = " + repr(this_present)
+ print >> out, " // that_present = " + repr(that_present)
+ is_equal = \
+ (not this_present and not that_present) or \
+ (this_present and that_present and equal)
+ eq_str = 'true' if is_equal else 'false'
+
+ print >> out, " if (lhs.equals(rhs) != "+eq_str+")"
+ print >> out, " throw new RuntimeException(\"Failure\");"
+ if is_equal:
+ print >> out, " if (lhs.hashCode() != rhs.hashCode())"
+ print >> out, " throw new RuntimeException(\"Failure\");"
+
+ print >> out, r'''
+ }
+}
+'''
+*/
+
+package org.apache.thrift.test;
+
+// Generated code
+import java.nio.ByteBuffer;
+
+import thrift.test.JavaTestHelper;
+
+/**
+ */
+public class EqualityTest {
+ public static void main(String[] args) throws Exception {
+ JavaTestHelper lhs, rhs;
+
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_int = 1;
+ rhs.req_int = 1;
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_int = 1;
+ rhs.req_int = 2;
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_int = 1;
+ rhs.req_int = 1;
+ rhs.setReq_intIsSet(true);
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_int = 1;
+ rhs.req_int = 2;
+ rhs.setReq_intIsSet(true);
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_int = 1;
+ rhs.req_int = 1;
+ lhs.setReq_intIsSet(true);
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_int = 1;
+ rhs.req_int = 2;
+ lhs.setReq_intIsSet(true);
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_int = 1;
+ rhs.req_int = 1;
+ lhs.setReq_intIsSet(true);
+ rhs.setReq_intIsSet(true);
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_int = 1;
+ rhs.req_int = 2;
+ lhs.setReq_intIsSet(true);
+ rhs.setReq_intIsSet(true);
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_int = 1;
+ rhs.opt_int = 1;
+ // this_present = False
+ // that_present = False
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_int = 1;
+ rhs.opt_int = 2;
+ // this_present = False
+ // that_present = False
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_int = 1;
+ rhs.opt_int = 1;
+ rhs.setOpt_intIsSet(true);
+ // this_present = False
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_int = 1;
+ rhs.opt_int = 2;
+ rhs.setOpt_intIsSet(true);
+ // this_present = False
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_int = 1;
+ rhs.opt_int = 1;
+ lhs.setOpt_intIsSet(true);
+ // this_present = True
+ // that_present = False
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_int = 1;
+ rhs.opt_int = 2;
+ lhs.setOpt_intIsSet(true);
+ // this_present = True
+ // that_present = False
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_int = 1;
+ rhs.opt_int = 1;
+ lhs.setOpt_intIsSet(true);
+ rhs.setOpt_intIsSet(true);
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_int = 1;
+ rhs.opt_int = 2;
+ lhs.setOpt_intIsSet(true);
+ rhs.setOpt_intIsSet(true);
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_obj = "foo";
+ rhs.req_obj = "foo";
+ lhs.req_obj = null;
+ rhs.req_obj = null;
+ // this_present = False
+ // that_present = False
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_obj = "foo";
+ rhs.req_obj = "bar";
+ lhs.req_obj = null;
+ rhs.req_obj = null;
+ // this_present = False
+ // that_present = False
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_obj = "foo";
+ rhs.req_obj = "foo";
+ lhs.req_obj = null;
+ // this_present = False
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_obj = "foo";
+ rhs.req_obj = "bar";
+ lhs.req_obj = null;
+ // this_present = False
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_obj = "foo";
+ rhs.req_obj = "foo";
+ rhs.req_obj = null;
+ // this_present = True
+ // that_present = False
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_obj = "foo";
+ rhs.req_obj = "bar";
+ rhs.req_obj = null;
+ // this_present = True
+ // that_present = False
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_obj = "foo";
+ rhs.req_obj = "foo";
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_obj = "foo";
+ rhs.req_obj = "bar";
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_obj = "foo";
+ rhs.opt_obj = "foo";
+ lhs.opt_obj = null;
+ rhs.opt_obj = null;
+ // this_present = False
+ // that_present = False
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_obj = "foo";
+ rhs.opt_obj = "bar";
+ lhs.opt_obj = null;
+ rhs.opt_obj = null;
+ // this_present = False
+ // that_present = False
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_obj = "foo";
+ rhs.opt_obj = "foo";
+ lhs.opt_obj = null;
+ // this_present = False
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_obj = "foo";
+ rhs.opt_obj = "bar";
+ lhs.opt_obj = null;
+ // this_present = False
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_obj = "foo";
+ rhs.opt_obj = "foo";
+ rhs.opt_obj = null;
+ // this_present = True
+ // that_present = False
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_obj = "foo";
+ rhs.opt_obj = "bar";
+ rhs.opt_obj = null;
+ // this_present = True
+ // that_present = False
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_obj = "foo";
+ rhs.opt_obj = "foo";
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_obj = "foo";
+ rhs.opt_obj = "bar";
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.req_bin = ByteBuffer.wrap(new byte[]{1,2});
+ lhs.req_bin = null;
+ rhs.req_bin = null;
+ // this_present = False
+ // that_present = False
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.req_bin = ByteBuffer.wrap(new byte[]{3,4});
+ lhs.req_bin = null;
+ rhs.req_bin = null;
+ // this_present = False
+ // that_present = False
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.req_bin = ByteBuffer.wrap(new byte[]{1,2});
+ lhs.req_bin = null;
+ // this_present = False
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.req_bin = ByteBuffer.wrap(new byte[]{3,4});
+ lhs.req_bin = null;
+ // this_present = False
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.req_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.req_bin = null;
+ // this_present = True
+ // that_present = False
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.req_bin = ByteBuffer.wrap(new byte[]{3,4});
+ rhs.req_bin = null;
+ // this_present = True
+ // that_present = False
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.req_bin = ByteBuffer.wrap(new byte[]{1,2});
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.req_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.req_bin = ByteBuffer.wrap(new byte[]{3,4});
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2});
+ lhs.opt_bin = null;
+ rhs.opt_bin = null;
+ // this_present = False
+ // that_present = False
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.opt_bin = ByteBuffer.wrap(new byte[]{3,4});
+ lhs.opt_bin = null;
+ rhs.opt_bin = null;
+ // this_present = False
+ // that_present = False
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2});
+ lhs.opt_bin = null;
+ // this_present = False
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.opt_bin = ByteBuffer.wrap(new byte[]{3,4});
+ lhs.opt_bin = null;
+ // this_present = False
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.opt_bin = null;
+ // this_present = True
+ // that_present = False
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.opt_bin = ByteBuffer.wrap(new byte[]{3,4});
+ rhs.opt_bin = null;
+ // this_present = True
+ // that_present = False
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2});
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != true)
+ throw new RuntimeException("Failure");
+ if (lhs.hashCode() != rhs.hashCode())
+ throw new RuntimeException("Failure");
+
+ lhs = new JavaTestHelper();
+ rhs = new JavaTestHelper();
+ lhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2});
+ rhs.opt_bin = ByteBuffer.wrap(new byte[]{3,4});
+ // this_present = True
+ // that_present = True
+ if (lhs.equals(rhs) != false)
+ throw new RuntimeException("Failure");
+
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/JavaBeansTest.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/JavaBeansTest.java
new file mode 100644
index 000000000..6a2a0ed0c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/JavaBeansTest.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.test;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+
+import thrift.test.OneOfEachBeans;
+
+public class JavaBeansTest {
+ public static void main(String[] args) throws Exception {
+ // Test isSet methods
+ OneOfEachBeans ooe = new OneOfEachBeans();
+
+ // Nothing should be set
+ if (ooe.is_set_a_bite())
+ throw new RuntimeException("isSet method error: unset field returned as set!");
+ if (ooe.is_set_base64())
+ throw new RuntimeException("isSet method error: unset field returned as set!");
+ if (ooe.is_set_byte_list())
+ throw new RuntimeException("isSet method error: unset field returned as set!");
+ if (ooe.is_set_double_precision())
+ throw new RuntimeException("isSet method error: unset field returned as set!");
+ if (ooe.is_set_i16_list())
+ throw new RuntimeException("isSet method error: unset field returned as set!");
+ if (ooe.is_set_i64_list())
+ throw new RuntimeException("isSet method error: unset field returned as set!");
+ if (ooe.is_set_boolean_field())
+ throw new RuntimeException("isSet method error: unset field returned as set!");
+ if (ooe.is_set_integer16())
+ throw new RuntimeException("isSet method error: unset field returned as set!");
+ if (ooe.is_set_integer32())
+ throw new RuntimeException("isSet method error: unset field returned as set!");
+ if (ooe.is_set_integer64())
+ throw new RuntimeException("isSet method error: unset field returned as set!");
+ if (ooe.is_set_some_characters())
+ throw new RuntimeException("isSet method error: unset field returned as set!");
+
+ for (int i = 1; i < 12; i++){
+ if (ooe.isSet(ooe.fieldForId(i)))
+ throw new RuntimeException("isSet method error: unset field " + i + " returned as set!");
+ }
+
+ // Everything is set
+ ooe.set_a_bite((byte) 1);
+ ooe.set_base64(ByteBuffer.wrap("bytes".getBytes()));
+ ooe.set_byte_list(new LinkedList<Byte>());
+ ooe.set_double_precision(1);
+ ooe.set_i16_list(new LinkedList<Short>());
+ ooe.set_i64_list(new LinkedList<Long>());
+ ooe.set_boolean_field(true);
+ ooe.set_integer16((short) 1);
+ ooe.set_integer32(1);
+ ooe.set_integer64(1);
+ ooe.set_some_characters("string");
+
+ if (!ooe.is_set_a_bite())
+ throw new RuntimeException("isSet method error: set field returned as unset!");
+ if (!ooe.is_set_base64())
+ throw new RuntimeException("isSet method error: set field returned as unset!");
+ if (!ooe.is_set_byte_list())
+ throw new RuntimeException("isSet method error: set field returned as unset!");
+ if (!ooe.is_set_double_precision())
+ throw new RuntimeException("isSet method error: set field returned as unset!");
+ if (!ooe.is_set_i16_list())
+ throw new RuntimeException("isSet method error: set field returned as unset!");
+ if (!ooe.is_set_i64_list())
+ throw new RuntimeException("isSet method error: set field returned as unset!");
+ if (!ooe.is_set_boolean_field())
+ throw new RuntimeException("isSet method error: set field returned as unset!");
+ if (!ooe.is_set_integer16())
+ throw new RuntimeException("isSet method error: set field returned as unset!");
+ if (!ooe.is_set_integer32())
+ throw new RuntimeException("isSet method error: set field returned as unset!");
+ if (!ooe.is_set_integer64())
+ throw new RuntimeException("isSet method error: set field returned as unset!");
+ if (!ooe.is_set_some_characters())
+ throw new RuntimeException("isSet method error: set field returned as unset!");
+
+ for (int i = 1; i < 12; i++){
+ if (!ooe.isSet(ooe.fieldForId(i)))
+ throw new RuntimeException("isSet method error: set field " + i + " returned as unset!");
+ }
+
+ // Should throw exception when field doesn't exist
+ boolean exceptionThrown = false;
+ try{
+ if (ooe.isSet(ooe.fieldForId(100)));
+ } catch (IllegalArgumentException e){
+ exceptionThrown = true;
+ }
+ if (!exceptionThrown)
+ throw new RuntimeException("isSet method error: non-existent field provided as agument but no exception thrown!");
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/ReadStruct.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/ReadStruct.java
new file mode 100644
index 000000000..7e3b091d4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/ReadStruct.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.test;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+
+import org.apache.thrift.Fixtures;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TIOStreamTransport;
+import org.apache.thrift.transport.TTransport;
+
+import thrift.test.CompactProtoTestStruct;
+
+public class ReadStruct {
+ public static void main(String[] args) throws Exception {
+ if (args.length != 2) {
+ System.out.println("usage: java -cp build/classes org.apache.thrift.test.ReadStruct filename proto_factory_class");
+ System.out.println("Read in an instance of CompactProtocolTestStruct from 'file', making sure that it is equivalent to Fixtures.compactProtoTestStruct. Use a protocol from 'proto_factory_class'.");
+ }
+
+ TTransport trans = new TIOStreamTransport(new BufferedInputStream(new FileInputStream(args[0])));
+
+ TProtocolFactory factory = (TProtocolFactory)Class.forName(args[1]).newInstance();
+
+ TProtocol proto = factory.getProtocol(trans);
+
+ CompactProtoTestStruct cpts = new CompactProtoTestStruct();
+
+ for (CompactProtoTestStruct._Fields fid : CompactProtoTestStruct.metaDataMap.keySet()) {
+ cpts.setFieldValue(fid, null);
+ }
+
+ cpts.read(proto);
+
+ if (cpts.equals(Fixtures.compactProtoTestStruct)) {
+ System.out.println("Object verified successfully!");
+ } else {
+ System.out.println("Object failed verification!");
+ System.out.println("Expected: " + Fixtures.compactProtoTestStruct + " but got " + cpts);
+ }
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/SerializationBenchmark.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/SerializationBenchmark.java
new file mode 100644
index 000000000..2b0db3132
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/SerializationBenchmark.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.thrift.test;
+
+import org.apache.thrift.Fixtures;
+import org.apache.thrift.TBase;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TMemoryBuffer;
+import org.apache.thrift.transport.TMemoryInputTransport;
+import org.apache.thrift.transport.TTransport;
+import org.apache.thrift.transport.TTransportException;
+
+import thrift.test.OneOfEach;
+
+public class SerializationBenchmark {
+ private final static int HOW_MANY = 10000000;
+
+ public static void main(String[] args) throws Exception {
+ TProtocolFactory factory = new TBinaryProtocol.Factory();
+
+ testSerialization(factory, Fixtures.oneOfEach);
+ testDeserialization(factory, Fixtures.oneOfEach, OneOfEach.class);
+ }
+
+ public static void testSerialization(TProtocolFactory factory, TBase object) throws Exception {
+ TTransport trans = new TTransport() {
+ public void write(byte[] bin, int x, int y) throws TTransportException {}
+ public int read(byte[] bin, int x, int y) throws TTransportException {return 0;}
+ public void close() {}
+ public void open() {}
+ public boolean isOpen() {return true;}
+ };
+
+ TProtocol proto = factory.getProtocol(trans);
+
+ long startTime = System.currentTimeMillis();
+ for (int i = 0; i < HOW_MANY; i++) {
+ object.write(proto);
+ }
+ long endTime = System.currentTimeMillis();
+
+ System.out.println("Serialization test time: " + (endTime - startTime) + " ms");
+ }
+
+ public static <T extends TBase> void testDeserialization(TProtocolFactory factory, T object, Class<T> klass) throws Exception {
+ TMemoryBuffer buf = new TMemoryBuffer(0);
+ object.write(factory.getProtocol(buf));
+ byte[] serialized = new byte[100*1024];
+ buf.read(serialized, 0, 100*1024);
+
+ long startTime = System.currentTimeMillis();
+ for (int i = 0; i < HOW_MANY; i++) {
+ T o2 = klass.newInstance();
+ o2.read(factory.getProtocol(new TMemoryInputTransport(serialized)));
+ }
+ long endTime = System.currentTimeMillis();
+
+ System.out.println("Deserialization test time: " + (endTime - startTime) + " ms");
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestClient.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestClient.java
new file mode 100644
index 000000000..84410cea0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestClient.java
@@ -0,0 +1,811 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.test;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.thrift.TApplicationException;
+import org.apache.thrift.TException;
+import org.apache.thrift.TSerializer;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TCompactProtocol;
+import org.apache.thrift.protocol.TJSONProtocol;
+import org.apache.thrift.protocol.TMultiplexedProtocol;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TSimpleJSONProtocol;
+import org.apache.thrift.transport.TFastFramedTransport;
+import org.apache.thrift.transport.TFramedTransport;
+import org.apache.thrift.transport.THttpClient;
+import org.apache.thrift.transport.TSSLTransportFactory;
+import org.apache.thrift.transport.TSocket;
+import org.apache.thrift.transport.TTransport;
+import org.apache.thrift.transport.TTransportException;
+
+// Generated code
+import thrift.test.Insanity;
+import thrift.test.Numberz;
+import thrift.test.SecondService;
+import thrift.test.ThriftTest;
+import thrift.test.Xception;
+import thrift.test.Xception2;
+import thrift.test.Xtruct;
+import thrift.test.Xtruct2;
+
+/**
+ * Test Java client for thrift. Essentially just a copy of the C++ version,
+ * this makes a variety of requests to enable testing for both performance and
+ * correctness of the output.
+ *
+ */
+public class TestClient {
+
+ private static int ERR_BASETYPES = 1;
+ private static int ERR_STRUCTS = 2;
+ private static int ERR_CONTAINERS = 4;
+ private static int ERR_EXCEPTIONS = 8;
+ private static int ERR_PROTOCOLS = 16;
+ private static int ERR_UNKNOWN = 64;
+
+ public static void main(String [] args) {
+ String host = "localhost";
+ int port = 9090;
+ int numTests = 1;
+ String protocol_type = "binary";
+ String transport_type = "buffered";
+ boolean ssl = false;
+
+ int socketTimeout = 1000;
+
+ try {
+ for (int i = 0; i < args.length; ++i) {
+ if (args[i].startsWith("--host")) {
+ host = args[i].split("=")[1];
+ host.trim();
+ } else if (args[i].startsWith("--port")) {
+ port = Integer.valueOf(args[i].split("=")[1]);
+ } else if (args[i].startsWith("--n") ||
+ args[i].startsWith("--testloops")){
+ numTests = Integer.valueOf(args[i].split("=")[1]);
+ } else if (args[i].equals("--timeout")) {
+ socketTimeout = Integer.valueOf(args[i].split("=")[1]);
+ } else if (args[i].startsWith("--protocol")) {
+ protocol_type = args[i].split("=")[1];
+ protocol_type.trim();
+ } else if (args[i].startsWith("--transport")) {
+ transport_type = args[i].split("=")[1];
+ transport_type.trim();
+ } else if (args[i].equals("--ssl")) {
+ ssl = true;
+ } else if (args[i].equals("--help")) {
+ System.out.println("Allowed options:");
+ System.out.println(" --help\t\t\tProduce help message");
+ System.out.println(" --host=arg (=" + host + ")\tHost to connect");
+ System.out.println(" --port=arg (=" + port + ")\tPort number to connect");
+ System.out.println(" --transport=arg (=" + transport_type + ")\n\t\t\t\tTransport: buffered, framed, fastframed, http");
+ System.out.println(" --protocol=arg (=" + protocol_type + ")\tProtocol: binary, compact, json, multi, multic, multij");
+ System.out.println(" --ssl\t\t\tEncrypted Transport using SSL");
+ System.out.println(" --testloops[--n]=arg (=" + numTests + ")\tNumber of Tests");
+ System.exit(0);
+ }
+ }
+ } catch (Exception x) {
+ System.err.println("Can not parse arguments! See --help");
+ System.exit(ERR_UNKNOWN);
+ }
+
+ try {
+ if (protocol_type.equals("binary")) {
+ } else if (protocol_type.equals("compact")) {
+ } else if (protocol_type.equals("json")) {
+ } else if (protocol_type.equals("multi")) {
+ } else if (protocol_type.equals("multic")) {
+ } else if (protocol_type.equals("multij")) {
+ } else {
+ throw new Exception("Unknown protocol type! " + protocol_type);
+ }
+ if (transport_type.equals("buffered")) {
+ } else if (transport_type.equals("framed")) {
+ } else if (transport_type.equals("fastframed")) {
+ } else if (transport_type.equals("http")) {
+ } else {
+ throw new Exception("Unknown transport type! " + transport_type);
+ }
+ if (transport_type.equals("http") && ssl == true) {
+ throw new Exception("SSL is not supported over http.");
+ }
+ } catch (Exception e) {
+ System.err.println("Error: " + e.getMessage());
+ System.exit(ERR_UNKNOWN);
+ }
+
+ TTransport transport = null;
+
+ try {
+ if (transport_type.equals("http")) {
+ String url = "http://" + host + ":" + port + "/service";
+ transport = new THttpClient(url);
+ } else {
+ TSocket socket = null;
+ if (ssl == true) {
+ socket = TSSLTransportFactory.getClientSocket(host, port, 0);
+ } else {
+ socket = new TSocket(host, port);
+ }
+ socket.setTimeout(socketTimeout);
+ transport = socket;
+ if (transport_type.equals("buffered")) {
+ } else if (transport_type.equals("framed")) {
+ transport = new TFramedTransport(transport);
+ } else if (transport_type.equals("fastframed")) {
+ transport = new TFastFramedTransport(transport);
+ }
+ }
+ } catch (Exception x) {
+ x.printStackTrace();
+ System.exit(ERR_UNKNOWN);
+ }
+
+ TProtocol tProtocol = null;
+ TProtocol tProtocol2 = null;
+ if (protocol_type.equals("json") || protocol_type.equals("multij")) {
+ tProtocol = new TJSONProtocol(transport);
+ } else if (protocol_type.equals("compact") || protocol_type.equals("multic")) {
+ tProtocol = new TCompactProtocol(transport);
+ } else {
+ tProtocol = new TBinaryProtocol(transport);
+ }
+
+ if (protocol_type.startsWith("multi")) {
+ tProtocol2 = new TMultiplexedProtocol(tProtocol, "SecondService");
+ tProtocol = new TMultiplexedProtocol(tProtocol, "ThriftTest");
+ }
+
+ ThriftTest.Client testClient = new ThriftTest.Client(tProtocol);
+ Insanity insane = new Insanity();
+
+ long timeMin = 0;
+ long timeMax = 0;
+ long timeTot = 0;
+
+ int returnCode = 0;
+ for (int test = 0; test < numTests; ++test) {
+ try {
+ /**
+ * CONNECT TEST
+ */
+ System.out.println("Test #" + (test+1) + ", " + "connect " + host + ":" + port);
+
+ if (transport.isOpen() == false) {
+ try {
+ transport.open();
+ } catch (TTransportException ttx) {
+ ttx.printStackTrace();
+ System.out.println("Connect failed: " + ttx.getMessage());
+ System.exit(ERR_UNKNOWN);
+ }
+ }
+
+ long start = System.nanoTime();
+
+ /**
+ * VOID TEST
+ */
+ try {
+ System.out.print("testVoid()");
+ testClient.testVoid();
+ System.out.print(" = void\n");
+ } catch (TApplicationException tax) {
+ tax.printStackTrace();
+ returnCode |= ERR_BASETYPES;
+ }
+
+ /**
+ * STRING TEST
+ */
+ System.out.print("testString(\"Test\")");
+ String s = testClient.testString("Test");
+ System.out.print(" = \"" + s + "\"\n");
+ if (!s.equals("Test")) {
+ returnCode |= ERR_BASETYPES;
+ System.out.println("*** FAILURE ***\n");
+ }
+
+ /**
+ * Multiplexed test
+ */
+ if (protocol_type.startsWith("multi")) {
+ SecondService.Client secondClient = new SecondService.Client(tProtocol2);
+ System.out.print("secondtestString(\"Test2\")");
+ s = secondClient.secondtestString("Test2");
+ System.out.print(" = \"" + s + "\"\n");
+ if (!s.equals("testString(\"Test2\")")) {
+ returnCode |= ERR_PROTOCOLS;
+ System.out.println("*** FAILURE ***\n");
+ }
+ }
+ /**
+ * BYTE TEST
+ */
+ System.out.print("testByte(1)");
+ byte i8 = testClient.testByte((byte)1);
+ System.out.print(" = " + i8 + "\n");
+ if (i8 != 1) {
+ returnCode |= ERR_BASETYPES;
+ System.out.println("*** FAILURE ***\n");
+ }
+
+ /**
+ * I32 TEST
+ */
+ System.out.print("testI32(-1)");
+ int i32 = testClient.testI32(-1);
+ System.out.print(" = " + i32 + "\n");
+ if (i32 != -1) {
+ returnCode |= ERR_BASETYPES;
+ System.out.println("*** FAILURE ***\n");
+ }
+
+ /**
+ * I64 TEST
+ */
+ System.out.print("testI64(-34359738368)");
+ long i64 = testClient.testI64(-34359738368L);
+ System.out.print(" = " + i64 + "\n");
+ if (i64 != -34359738368L) {
+ returnCode |= ERR_BASETYPES;
+ System.out.println("*** FAILURE ***\n");
+ }
+
+ /**
+ * DOUBLE TEST
+ */
+ System.out.print("testDouble(-5.325098235)");
+ double dub = testClient.testDouble(-5.325098235);
+ System.out.print(" = " + dub + "\n");
+ if (Math.abs(dub - (-5.325098235)) > 0.001) {
+ returnCode |= ERR_BASETYPES;
+ System.out.println("*** FAILURE ***\n");
+ }
+
+ /**
+ * BINARY TEST
+ */
+ try {
+ System.out.print("testBinary(-128...127) = ");
+ byte[] data = new byte[] {-128, -127, -126, -125, -124, -123, -122, -121, -120, -119, -118, -117, -116, -115, -114, -113, -112, -111, -110, -109, -108, -107, -106, -105, -104, -103, -102, -101, -100, -99, -98, -97, -96, -95, -94, -93, -92, -91, -90, -89, -88, -87, -86, -85, -84, -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, -73, -72, -71, -70, -69, -68, -67, -66, -65, -64, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127};
+ ByteBuffer bin = testClient.testBinary(ByteBuffer.wrap(data));
+ bin.mark();
+ byte[] bytes = new byte[bin.limit() - bin.position()];
+ bin.get(bytes);
+ bin.reset();
+ System.out.print("{");
+ boolean first = true;
+ for (int i = 0; i < bytes.length; ++i) {
+ if (first)
+ first = false;
+ else
+ System.out.print(", ");
+ System.out.print(bytes[i]);
+ }
+ System.out.println("}");
+ if (!ByteBuffer.wrap(data).equals(bin)) {
+ returnCode |= ERR_BASETYPES;
+ System.out.println("*** FAILURE ***\n");
+ }
+ } catch (Exception ex) {
+ returnCode |= ERR_BASETYPES;
+ System.out.println("\n*** FAILURE ***\n");
+ ex.printStackTrace(System.out);
+ }
+
+ /**
+ * STRUCT TEST
+ */
+ System.out.print("testStruct({\"Zero\", 1, -3, -5})");
+ Xtruct out = new Xtruct();
+ out.string_thing = "Zero";
+ out.byte_thing = (byte) 1;
+ out.i32_thing = -3;
+ out.i64_thing = -5;
+ Xtruct in = testClient.testStruct(out);
+ System.out.print(" = {" + "\"" +
+ in.string_thing + "\"," +
+ in.byte_thing + ", " +
+ in.i32_thing + ", " +
+ in.i64_thing + "}\n");
+ if (!in.equals(out)) {
+ returnCode |= ERR_STRUCTS;
+ System.out.println("*** FAILURE ***\n");
+ }
+
+ /**
+ * NESTED STRUCT TEST
+ */
+ System.out.print("testNest({1, {\"Zero\", 1, -3, -5}), 5}");
+ Xtruct2 out2 = new Xtruct2();
+ out2.byte_thing = (short)1;
+ out2.struct_thing = out;
+ out2.i32_thing = 5;
+ Xtruct2 in2 = testClient.testNest(out2);
+ in = in2.struct_thing;
+ System.out.print(" = {" + in2.byte_thing + ", {" + "\"" +
+ in.string_thing + "\", " +
+ in.byte_thing + ", " +
+ in.i32_thing + ", " +
+ in.i64_thing + "}, " +
+ in2.i32_thing + "}\n");
+ if (!in2.equals(out2)) {
+ returnCode |= ERR_STRUCTS;
+ System.out.println("*** FAILURE ***\n");
+ }
+
+ /**
+ * MAP TEST
+ */
+ Map<Integer,Integer> mapout = new HashMap<Integer,Integer>();
+ for (int i = 0; i < 5; ++i) {
+ mapout.put(i, i-10);
+ }
+ System.out.print("testMap({");
+ boolean first = true;
+ for (int key : mapout.keySet()) {
+ if (first) {
+ first = false;
+ } else {
+ System.out.print(", ");
+ }
+ System.out.print(key + " => " + mapout.get(key));
+ }
+ System.out.print("})");
+ Map<Integer,Integer> mapin = testClient.testMap(mapout);
+ System.out.print(" = {");
+ first = true;
+ for (int key : mapin.keySet()) {
+ if (first) {
+ first = false;
+ } else {
+ System.out.print(", ");
+ }
+ System.out.print(key + " => " + mapout.get(key));
+ }
+ System.out.print("}\n");
+ if (!mapout.equals(mapin)) {
+ returnCode |= ERR_CONTAINERS;
+ System.out.println("*** FAILURE ***\n");
+ }
+
+ /**
+ * STRING MAP TEST
+ */
+ try {
+ Map<String, String> smapout = new HashMap<String, String>();
+ smapout.put("a", "2");
+ smapout.put("b", "blah");
+ smapout.put("some", "thing");
+ for (String key : smapout.keySet()) {
+ if (first) {
+ first = false;
+ } else {
+ System.out.print(", ");
+ }
+ System.out.print(key + " => " + smapout.get(key));
+ }
+ System.out.print("})");
+ Map<String, String> smapin = testClient.testStringMap(smapout);
+ System.out.print(" = {");
+ first = true;
+ for (String key : smapin.keySet()) {
+ if (first) {
+ first = false;
+ } else {
+ System.out.print(", ");
+ }
+ System.out.print(key + " => " + smapout.get(key));
+ }
+ System.out.print("}\n");
+ if (!smapout.equals(smapin)) {
+ returnCode |= ERR_CONTAINERS;
+ System.out.println("*** FAILURE ***\n");
+ }
+ } catch (Exception ex) {
+ returnCode |= ERR_CONTAINERS;
+ System.out.println("*** FAILURE ***\n");
+ ex.printStackTrace(System.out);
+ }
+
+ /**
+ * SET TEST
+ */
+ Set<Integer> setout = new HashSet<Integer>();
+ for (int i = -2; i < 3; ++i) {
+ setout.add(i);
+ }
+ System.out.print("testSet({");
+ first = true;
+ for (int elem : setout) {
+ if (first) {
+ first = false;
+ } else {
+ System.out.print(", ");
+ }
+ System.out.print(elem);
+ }
+ System.out.print("})");
+ Set<Integer> setin = testClient.testSet(setout);
+ System.out.print(" = {");
+ first = true;
+ for (int elem : setin) {
+ if (first) {
+ first = false;
+ } else {
+ System.out.print(", ");
+ }
+ System.out.print(elem);
+ }
+ System.out.print("}\n");
+ if (!setout.equals(setin)) {
+ returnCode |= ERR_CONTAINERS;
+ System.out.println("*** FAILURE ***\n");
+ }
+
+ /**
+ * LIST TEST
+ */
+ List<Integer> listout = new ArrayList<Integer>();
+ for (int i = -2; i < 3; ++i) {
+ listout.add(i);
+ }
+ System.out.print("testList({");
+ first = true;
+ for (int elem : listout) {
+ if (first) {
+ first = false;
+ } else {
+ System.out.print(", ");
+ }
+ System.out.print(elem);
+ }
+ System.out.print("})");
+ List<Integer> listin = testClient.testList(listout);
+ System.out.print(" = {");
+ first = true;
+ for (int elem : listin) {
+ if (first) {
+ first = false;
+ } else {
+ System.out.print(", ");
+ }
+ System.out.print(elem);
+ }
+ System.out.print("}\n");
+ if (!listout.equals(listin)) {
+ returnCode |= ERR_CONTAINERS;
+ System.out.println("*** FAILURE ***\n");
+ }
+
+ /**
+ * ENUM TEST
+ */
+ System.out.print("testEnum(ONE)");
+ Numberz ret = testClient.testEnum(Numberz.ONE);
+ System.out.print(" = " + ret + "\n");
+ if (ret != Numberz.ONE) {
+ returnCode |= ERR_STRUCTS;
+ System.out.println("*** FAILURE ***\n");
+ }
+
+ System.out.print("testEnum(TWO)");
+ ret = testClient.testEnum(Numberz.TWO);
+ System.out.print(" = " + ret + "\n");
+ if (ret != Numberz.TWO) {
+ returnCode |= ERR_STRUCTS;
+ System.out.println("*** FAILURE ***\n");
+ }
+
+ System.out.print("testEnum(THREE)");
+ ret = testClient.testEnum(Numberz.THREE);
+ System.out.print(" = " + ret + "\n");
+ if (ret != Numberz.THREE) {
+ returnCode |= ERR_STRUCTS;
+ System.out.println("*** FAILURE ***\n");
+ }
+
+ System.out.print("testEnum(FIVE)");
+ ret = testClient.testEnum(Numberz.FIVE);
+ System.out.print(" = " + ret + "\n");
+ if (ret != Numberz.FIVE) {
+ returnCode |= ERR_STRUCTS;
+ System.out.println("*** FAILURE ***\n");
+ }
+
+ System.out.print("testEnum(EIGHT)");
+ ret = testClient.testEnum(Numberz.EIGHT);
+ System.out.print(" = " + ret + "\n");
+ if (ret != Numberz.EIGHT) {
+ returnCode |= ERR_STRUCTS;
+ System.out.println("*** FAILURE ***\n");
+ }
+
+ /**
+ * TYPEDEF TEST
+ */
+ System.out.print("testTypedef(309858235082523)");
+ long uid = testClient.testTypedef(309858235082523L);
+ System.out.print(" = " + uid + "\n");
+ if (uid != 309858235082523L) {
+ returnCode |= ERR_BASETYPES;
+ System.out.println("*** FAILURE ***\n");
+ }
+
+ /**
+ * NESTED MAP TEST
+ */
+ System.out.print("testMapMap(1)");
+ Map<Integer,Map<Integer,Integer>> mm =
+ testClient.testMapMap(1);
+ System.out.print(" = {");
+ for (int key : mm.keySet()) {
+ System.out.print(key + " => {");
+ Map<Integer,Integer> m2 = mm.get(key);
+ for (int k2 : m2.keySet()) {
+ System.out.print(k2 + " => " + m2.get(k2) + ", ");
+ }
+ System.out.print("}, ");
+ }
+ System.out.print("}\n");
+ if (mm.size() != 2 || !mm.containsKey(4) || !mm.containsKey(-4)) {
+ returnCode |= ERR_CONTAINERS;
+ System.out.println("*** FAILURE ***\n");
+ } else {
+ Map<Integer, Integer> m1 = mm.get(4);
+ Map<Integer, Integer> m2 = mm.get(-4);
+ if (m1.get(1) != 1 || m1.get(2) != 2 || m1.get(3) != 3 || m1.get(4) != 4 ||
+ m2.get(-1) != -1 || m2.get(-2) != -2 || m2.get(-3) != -3 || m2.get(-4) != -4) {
+ returnCode |= ERR_CONTAINERS;
+ System.out.println("*** FAILURE ***\n");
+ }
+ }
+
+ /**
+ * INSANITY TEST
+ */
+
+ boolean insanityFailed = true;
+ try {
+ Xtruct hello = new Xtruct();
+ hello.string_thing = "Hello2";
+ hello.byte_thing = 2;
+ hello.i32_thing = 2;
+ hello.i64_thing = 2;
+
+ Xtruct goodbye = new Xtruct();
+ goodbye.string_thing = "Goodbye4";
+ goodbye.byte_thing = (byte)4;
+ goodbye.i32_thing = 4;
+ goodbye.i64_thing = (long)4;
+
+ insane.userMap = new HashMap<Numberz, Long>();
+ insane.userMap.put(Numberz.EIGHT, (long)8);
+ insane.userMap.put(Numberz.FIVE, (long)5);
+ insane.xtructs = new ArrayList<Xtruct>();
+ insane.xtructs.add(goodbye);
+ insane.xtructs.add(hello);
+
+ System.out.print("testInsanity()");
+ Map<Long,Map<Numberz,Insanity>> whoa =
+ testClient.testInsanity(insane);
+ System.out.print(" = {");
+ for (long key : whoa.keySet()) {
+ Map<Numberz,Insanity> val = whoa.get(key);
+ System.out.print(key + " => {");
+
+ for (Numberz k2 : val.keySet()) {
+ Insanity v2 = val.get(k2);
+ System.out.print(k2 + " => {");
+ Map<Numberz, Long> userMap = v2.userMap;
+ System.out.print("{");
+ if (userMap != null) {
+ for (Numberz k3 : userMap.keySet()) {
+ System.out.print(k3 + " => " + userMap.get(k3) + ", ");
+ }
+ }
+ System.out.print("}, ");
+
+ List<Xtruct> xtructs = v2.xtructs;
+ System.out.print("{");
+ if (xtructs != null) {
+ for (Xtruct x : xtructs) {
+ System.out.print("{" + "\"" + x.string_thing + "\", " + x.byte_thing + ", " + x.i32_thing + ", "+ x.i64_thing + "}, ");
+ }
+ }
+ System.out.print("}");
+
+ System.out.print("}, ");
+ }
+ System.out.print("}, ");
+ }
+ System.out.print("}\n");
+ if (whoa.size() == 2 && whoa.containsKey(1L) && whoa.containsKey(2L)) {
+ Map<Numberz, Insanity> first_map = whoa.get(1L);
+ Map<Numberz, Insanity> second_map = whoa.get(2L);
+ if (first_map.size() == 2 &&
+ first_map.containsKey(Numberz.TWO) &&
+ first_map.containsKey(Numberz.THREE) &&
+ second_map.size() == 1 &&
+ second_map.containsKey(Numberz.SIX) &&
+ insane.equals(first_map.get(Numberz.TWO)) &&
+ insane.equals(first_map.get(Numberz.THREE))) {
+ Insanity six =second_map.get(Numberz.SIX);
+ // Cannot use "new Insanity().equals(six)" because as of now, struct/container
+ // fields with default requiredness have isset=false for local instances and yet
+ // received empty values from other languages like C++ have isset=true .
+ if (six.getUserMapSize() == 0 && six.getXtructsSize() == 0) {
+ // OK
+ insanityFailed = false;
+ }
+ }
+ }
+ } catch (Exception ex) {
+ returnCode |= ERR_STRUCTS;
+ System.out.println("*** FAILURE ***\n");
+ ex.printStackTrace(System.out);
+ insanityFailed = false;
+ }
+ if (insanityFailed) {
+ returnCode |= ERR_STRUCTS;
+ System.out.println("*** FAILURE ***\n");
+ }
+
+ /**
+ * EXECPTION TEST
+ */
+ try {
+ System.out.print("testClient.testException(\"Xception\") =>");
+ testClient.testException("Xception");
+ System.out.print(" void\n*** FAILURE ***\n");
+ returnCode |= ERR_EXCEPTIONS;
+ } catch(Xception e) {
+ System.out.printf(" {%d, \"%s\"}\n", e.errorCode, e.message);
+ }
+
+ try {
+ System.out.print("testClient.testException(\"TException\") =>");
+ testClient.testException("TException");
+ System.out.print(" void\n*** FAILURE ***\n");
+ returnCode |= ERR_EXCEPTIONS;
+ } catch(TException e) {
+ System.out.printf(" {\"%s\"}\n", e.getMessage());
+ }
+
+ try {
+ System.out.print("testClient.testException(\"success\") =>");
+ testClient.testException("success");
+ System.out.print(" void\n");
+ }catch(Exception e) {
+ System.out.printf(" exception\n*** FAILURE ***\n");
+ returnCode |= ERR_EXCEPTIONS;
+ }
+
+
+ /**
+ * MULTI EXCEPTION TEST
+ */
+
+ try {
+ System.out.printf("testClient.testMultiException(\"Xception\", \"test 1\") =>");
+ testClient.testMultiException("Xception", "test 1");
+ System.out.print(" result\n*** FAILURE ***\n");
+ returnCode |= ERR_EXCEPTIONS;
+ } catch(Xception e) {
+ System.out.printf(" {%d, \"%s\"}\n", e.errorCode, e.message);
+ }
+
+ try {
+ System.out.printf("testClient.testMultiException(\"Xception2\", \"test 2\") =>");
+ testClient.testMultiException("Xception2", "test 2");
+ System.out.print(" result\n*** FAILURE ***\n");
+ returnCode |= ERR_EXCEPTIONS;
+ } catch(Xception2 e) {
+ System.out.printf(" {%d, {\"%s\"}}\n", e.errorCode, e.struct_thing.string_thing);
+ }
+
+ try {
+ System.out.print("testClient.testMultiException(\"success\", \"test 3\") =>");
+ Xtruct result;
+ result = testClient.testMultiException("success", "test 3");
+ System.out.printf(" {{\"%s\"}}\n", result.string_thing);
+ } catch(Exception e) {
+ System.out.printf(" exception\n*** FAILURE ***\n");
+ returnCode |= ERR_EXCEPTIONS;
+ }
+
+
+
+ /**
+ * ONEWAY TEST
+ */
+ System.out.print("testOneway(3)...");
+ long startOneway = System.nanoTime();
+ testClient.testOneway(3);
+ long onewayElapsedMillis = (System.nanoTime() - startOneway) / 1000000;
+ if (onewayElapsedMillis > 200) {
+ System.out.println("Oneway test took too long to execute failed: took " +
+ Long.toString(onewayElapsedMillis) +
+ "ms");
+ System.out.println("oneway calls are 'fire and forget' and therefore should not cause blocking.");
+ System.out.println("Some transports (HTTP) have a required response, and typically this failure");
+ System.out.println("means the transport response was delayed until after the execution");
+ System.out.println("of the RPC. The server should post the transport response immediately and");
+ System.out.println("before executing the RPC.");
+ System.out.println("*** FAILURE ***");
+ returnCode |= ERR_BASETYPES;
+ } else {
+ System.out.println("Success - fire and forget only took " +
+ Long.toString(onewayElapsedMillis) +
+ "ms");
+ }
+
+
+ long stop = System.nanoTime();
+ long tot = stop-start;
+
+ System.out.println("Total time: " + tot/1000 + "us");
+
+ if (timeMin == 0 || tot < timeMin) {
+ timeMin = tot;
+ }
+ if (tot > timeMax) {
+ timeMax = tot;
+ }
+ timeTot += tot;
+
+ transport.close();
+ } catch (Exception x) {
+ System.out.printf("*** FAILURE ***\n");
+ x.printStackTrace();
+ returnCode |= ERR_UNKNOWN;
+ }
+ }
+
+ long timeAvg = timeTot / numTests;
+
+ System.out.println("Min time: " + timeMin/1000 + "us");
+ System.out.println("Max time: " + timeMax/1000 + "us");
+ System.out.println("Avg time: " + timeAvg/1000 + "us");
+
+ try {
+ String json = (new TSerializer(new TSimpleJSONProtocol.Factory())).toString(insane);
+ System.out.println("\nSample TSimpleJSONProtocol output:\n" + json);
+ } catch (TException x) {
+ System.out.println("*** FAILURE ***");
+ x.printStackTrace();
+ returnCode |= ERR_BASETYPES;
+ }
+
+
+ System.exit(returnCode);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestNonblockingServer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestNonblockingServer.java
new file mode 100644
index 000000000..41c4b6500
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestNonblockingServer.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.test;
+
+import org.apache.thrift.server.THsHaServer;
+import org.apache.thrift.server.TNonblockingServer;
+import org.apache.thrift.server.TServer;
+import org.apache.thrift.server.THsHaServer.Args;
+import org.apache.thrift.transport.TNonblockingServerSocket;
+import org.apache.thrift.server.ServerTestBase.TestHandler;
+
+import thrift.test.ThriftTest;
+
+
+public class TestNonblockingServer extends TestServer {
+ public static void main(String [] args) {
+ try {
+ int port = 9090;
+ boolean hsha = false;
+
+ for (int i = 0; i < args.length; i++) {
+ if (args[i].equals("-p")) {
+ port = Integer.valueOf(args[i++]);
+ } else if (args[i].equals("-hsha")) {
+ hsha = true;
+ }
+ }
+ //@TODO add other protocol and transport types
+
+ // Processor
+ TestHandler testHandler =
+ new TestHandler();
+ ThriftTest.Processor testProcessor =
+ new ThriftTest.Processor(testHandler);
+
+ // Transport
+ TNonblockingServerSocket tServerSocket =
+ new TNonblockingServerSocket(new TNonblockingServerSocket.NonblockingAbstractServerSocketArgs().port(port));
+
+ TServer serverEngine;
+
+ if (hsha) {
+ // HsHa Server
+ serverEngine = new THsHaServer(new Args(tServerSocket).processor(testProcessor));
+ } else {
+ // Nonblocking Server
+ serverEngine = new TNonblockingServer(new Args(tServerSocket).processor(testProcessor));
+ }
+
+ // Run it
+ System.out.println("Starting the server on port " + port + "...");
+ serverEngine.serve();
+
+ } catch (Exception x) {
+ x.printStackTrace();
+ }
+ System.out.println("done.");
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestServer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestServer.java
new file mode 100644
index 000000000..1f3e555d9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestServer.java
@@ -0,0 +1,310 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TCompactProtocol;
+import org.apache.thrift.protocol.TJSONProtocol;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.protocol.TMultiplexedProtocol;
+import org.apache.thrift.server.ServerContext;
+import org.apache.thrift.server.TServer;
+import org.apache.thrift.server.TServer.Args;
+import org.apache.thrift.server.TSimpleServer;
+import org.apache.thrift.server.TThreadPoolServer;
+import org.apache.thrift.server.ServerTestBase.TestHandler;
+import org.apache.thrift.server.TServerEventHandler;
+import org.apache.thrift.server.TThreadedSelectorServer;
+import org.apache.thrift.server.TNonblockingServer;
+import org.apache.thrift.transport.TFramedTransport;
+import org.apache.thrift.transport.TFastFramedTransport;
+import org.apache.thrift.transport.TServerSocket;
+import org.apache.thrift.transport.TSSLTransportFactory;
+import org.apache.thrift.transport.TTransport;
+import org.apache.thrift.transport.TTransportFactory;
+import org.apache.thrift.transport.TNonblockingServerSocket;
+import org.apache.thrift.TMultiplexedProcessor;
+
+import thrift.test.Insanity;
+import thrift.test.Numberz;
+import thrift.test.SecondService;
+import thrift.test.ThriftTest;
+import thrift.test.Xception;
+import thrift.test.Xception2;
+import thrift.test.Xtruct;
+import thrift.test.Xtruct2;
+
+public class TestServer {
+
+ // Multiplexed Protocol Support Details:
+ //
+ // For multiplexed testing we always use binary protocol underneath.
+ //
+ // "ThriftTest" named service implements "ThriftTest" from ThriftTest.thrift
+ // "SecondService" named service implements "SecondService" from ThriftTest.thrift
+ // In addition, to support older non-multiplexed clients using the same concrete protocol
+ // the multiplexed processor is taught to use "ThriftTest" if the incoming request has no
+ // multiplexed call name decoration.
+
+ static class SecondHandler implements thrift.test.SecondService.Iface {
+
+ @Override
+ public java.lang.String secondtestString(java.lang.String thing) throws org.apache.thrift.TException
+ { return "testString(\"" + thing + "\")"; }
+
+ }
+
+ static class TestServerContext implements ServerContext {
+
+ int connectionId;
+
+ public TestServerContext(int connectionId) {
+ this.connectionId = connectionId;
+ }
+
+ public int getConnectionId() {
+ return connectionId;
+ }
+
+ public void setConnectionId(int connectionId) {
+ this.connectionId = connectionId;
+ }
+
+ }
+
+ static class TestServerEventHandler implements TServerEventHandler {
+
+ private int nextConnectionId = 1;
+
+ public void preServe() {
+ System.out.println("TServerEventHandler.preServe - called only once before server starts accepting connections");
+ }
+
+ public ServerContext createContext(TProtocol input, TProtocol output) {
+ //we can create some connection level data which is stored while connection is alive & served
+ TestServerContext ctx = new TestServerContext(nextConnectionId++);
+ System.out.println("TServerEventHandler.createContext - connection #"+ctx.getConnectionId()+" established");
+ return ctx;
+ }
+
+ public void deleteContext(ServerContext serverContext, TProtocol input, TProtocol output) {
+ TestServerContext ctx = (TestServerContext)serverContext;
+ System.out.println("TServerEventHandler.deleteContext - connection #"+ctx.getConnectionId()+" terminated");
+ }
+
+ public void processContext(ServerContext serverContext, TTransport inputTransport, TTransport outputTransport) {
+ TestServerContext ctx = (TestServerContext)serverContext;
+ System.out.println("TServerEventHandler.processContext - connection #"+ctx.getConnectionId()+" is ready to process next request");
+ }
+
+ }
+
+ public static void main(String [] args) {
+ try {
+ int port = 9090;
+ boolean ssl = false;
+ String transport_type = "buffered";
+ String protocol_type = "binary";
+ String server_type = "thread-pool";
+ String domain_socket = "";
+ int string_limit = -1;
+ int container_limit = -1;
+ try {
+ for (int i = 0; i < args.length; i++) {
+ if (args[i].startsWith("--port")) {
+ port = Integer.valueOf(args[i].split("=")[1]);
+ } else if (args[i].startsWith("--server-type")) {
+ server_type = args[i].split("=")[1];
+ server_type.trim();
+ } else if (args[i].startsWith("--port")) {
+ port=Integer.parseInt(args[i].split("=")[1]);
+ } else if (args[i].startsWith("--protocol")) {
+ protocol_type = args[i].split("=")[1];
+ protocol_type.trim();
+ } else if (args[i].startsWith("--transport")) {
+ transport_type = args[i].split("=")[1];
+ transport_type.trim();
+ } else if (args[i].equals("--ssl")) {
+ ssl = true;
+ } else if (args[i].startsWith("--string-limit")) {
+ string_limit = Integer.valueOf(args[i].split("=")[1]);
+ } else if (args[i].startsWith("--container-limit")) {
+ container_limit = Integer.valueOf(args[i].split("=")[1]);
+ } else if (args[i].equals("--help")) {
+ System.out.println("Allowed options:");
+ System.out.println(" --help\t\t\tProduce help message");
+ System.out.println(" --port=arg (=" + port + ")\tPort number to connect");
+ System.out.println(" --transport=arg (=" + transport_type + ")\n\t\t\t\tTransport: buffered, framed, fastframed");
+ System.out.println(" --protocol=arg (=" + protocol_type + ")\tProtocol: binary, compact, json, multi, multic, multij");
+ System.out.println(" --ssl\t\t\tEncrypted Transport using SSL");
+ System.out.println(" --server-type=arg (=" + server_type +")\n\t\t\t\tType of server: simple, thread-pool, nonblocking, threaded-selector");
+ System.out.println(" --string-limit=arg (=" + string_limit + ")\tString read length limit");
+ System.out.println(" --container-limit=arg (=" + container_limit + ")\tContainer read length limit");
+ System.exit(0);
+ }
+ }
+ } catch (Exception e) {
+ System.err.println("Can not parse arguments! See --help");
+ System.exit(1);
+ }
+
+ try {
+ if (server_type.equals("simple")) {
+ } else if (server_type.equals("thread-pool")) {
+ } else if (server_type.equals("nonblocking")) {
+ if (ssl == true) {
+ throw new Exception("SSL is not supported over nonblocking servers!");
+ }
+ } else if (server_type.equals("threaded-selector")) {
+ if (ssl == true) {
+ throw new Exception("SSL is not supported over nonblocking servers!");
+ }
+ } else {
+ throw new Exception("Unknown server type! " + server_type);
+ }
+ if (protocol_type.equals("binary")) {
+ } else if (protocol_type.equals("compact")) {
+ } else if (protocol_type.equals("json")) {
+ } else if (protocol_type.equals("multi")) {
+ } else if (protocol_type.equals("multic")) {
+ } else if (protocol_type.equals("multij")) {
+ } else {
+ throw new Exception("Unknown protocol type! " + protocol_type);
+ }
+ if (transport_type.equals("buffered")) {
+ } else if (transport_type.equals("framed")) {
+ } else if (transport_type.equals("fastframed")) {
+ } else {
+ throw new Exception("Unknown transport type! " + transport_type);
+ }
+ } catch (Exception e) {
+ System.err.println("Error: " + e.getMessage());
+ System.exit(1);
+ }
+
+ // Processors
+ TestHandler testHandler = new TestHandler();
+ ThriftTest.Processor testProcessor = new ThriftTest.Processor(testHandler);
+
+ SecondHandler secondHandler = new SecondHandler();
+ SecondService.Processor secondProcessor = new SecondService.Processor(secondHandler);
+
+ // Protocol factory
+ TProtocolFactory tProtocolFactory = null;
+ if (protocol_type.equals("json") || protocol_type.equals("multij")) {
+ tProtocolFactory = new TJSONProtocol.Factory();
+ } else if (protocol_type.equals("compact") || protocol_type.equals("multic")) {
+ tProtocolFactory = new TCompactProtocol.Factory(string_limit, container_limit);
+ } else { // also covers multi
+ tProtocolFactory = new TBinaryProtocol.Factory(string_limit, container_limit);
+ }
+
+ TTransportFactory tTransportFactory = null;
+
+ if (transport_type.equals("framed")) {
+ tTransportFactory = new TFramedTransport.Factory();
+ } else if (transport_type.equals("fastframed")) {
+ tTransportFactory = new TFastFramedTransport.Factory();
+ } else { // .equals("buffered") => default value
+ tTransportFactory = new TTransportFactory();
+ }
+
+ TServer serverEngine = null;
+
+ // If we are multiplexing services in one server...
+ TMultiplexedProcessor multiplexedProcessor = new TMultiplexedProcessor();
+ multiplexedProcessor.registerDefault (testProcessor);
+ multiplexedProcessor.registerProcessor("ThriftTest", testProcessor);
+ multiplexedProcessor.registerProcessor("SecondService", secondProcessor);
+
+ if (server_type.equals("nonblocking") ||
+ server_type.equals("threaded-selector")) {
+ // Nonblocking servers
+ TNonblockingServerSocket tNonblockingServerSocket =
+ new TNonblockingServerSocket(new TNonblockingServerSocket.NonblockingAbstractServerSocketArgs().port(port));
+
+ if (server_type.contains("nonblocking")) {
+ // Nonblocking Server
+ TNonblockingServer.Args tNonblockingServerArgs
+ = new TNonblockingServer.Args(tNonblockingServerSocket);
+ tNonblockingServerArgs.processor(protocol_type.startsWith("multi") ? multiplexedProcessor : testProcessor);
+ tNonblockingServerArgs.protocolFactory(tProtocolFactory);
+ tNonblockingServerArgs.transportFactory(tTransportFactory);
+ serverEngine = new TNonblockingServer(tNonblockingServerArgs);
+ } else { // server_type.equals("threaded-selector")
+ // ThreadedSelector Server
+ TThreadedSelectorServer.Args tThreadedSelectorServerArgs
+ = new TThreadedSelectorServer.Args(tNonblockingServerSocket);
+ tThreadedSelectorServerArgs.processor(protocol_type.startsWith("multi") ? multiplexedProcessor : testProcessor);
+ tThreadedSelectorServerArgs.protocolFactory(tProtocolFactory);
+ tThreadedSelectorServerArgs.transportFactory(tTransportFactory);
+ serverEngine = new TThreadedSelectorServer(tThreadedSelectorServerArgs);
+ }
+ } else {
+ // Blocking servers
+
+ // SSL socket
+ TServerSocket tServerSocket = null;
+ if (ssl) {
+ tServerSocket = TSSLTransportFactory.getServerSocket(port, 0);
+ } else {
+ tServerSocket = new TServerSocket(new TServerSocket.ServerSocketTransportArgs().port(port));
+ }
+
+ if (server_type.equals("simple")) {
+ // Simple Server
+ TServer.Args tServerArgs = new TServer.Args(tServerSocket);
+ tServerArgs.processor(protocol_type.startsWith("multi") ? multiplexedProcessor : testProcessor);
+ tServerArgs.protocolFactory(tProtocolFactory);
+ tServerArgs.transportFactory(tTransportFactory);
+ serverEngine = new TSimpleServer(tServerArgs);
+ } else { // server_type.equals("threadpool")
+ // ThreadPool Server
+ TThreadPoolServer.Args tThreadPoolServerArgs
+ = new TThreadPoolServer.Args(tServerSocket);
+ tThreadPoolServerArgs.processor(protocol_type.startsWith("multi") ? multiplexedProcessor : testProcessor);
+ tThreadPoolServerArgs.protocolFactory(tProtocolFactory);
+ tThreadPoolServerArgs.transportFactory(tTransportFactory);
+ serverEngine = new TThreadPoolServer(tThreadPoolServerArgs);
+ }
+ }
+
+ // Set server event handler
+ serverEngine.setServerEventHandler(new TestServerEventHandler());
+
+ // Run it
+ System.out.println("Starting the " + (ssl ? "ssl server" : "server") +
+ " [" + protocol_type + "/" + transport_type + "/" + server_type + "] on " +
+ ((domain_socket == "") ? ("port " + port) : ("unix socket " + domain_socket)));
+ serverEngine.serve();
+
+ } catch (Exception x) {
+ x.printStackTrace();
+ }
+ System.out.println("done.");
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/WriteStruct.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/WriteStruct.java
new file mode 100644
index 000000000..a0013a93a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/WriteStruct.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.test;
+
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+
+import org.apache.thrift.Fixtures;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TIOStreamTransport;
+import org.apache.thrift.transport.TTransport;
+
+public class WriteStruct {
+ public static void main(String[] args) throws Exception {
+ if (args.length != 2) {
+ System.out.println("usage: java -cp build/classes org.apache.thrift.test.WriteStruct filename proto_factory_class");
+ System.out.println("Write out an instance of Fixtures.compactProtocolTestStruct to 'file'. Use a protocol from 'proto_factory_class'.");
+ }
+
+ TTransport trans = new TIOStreamTransport(new BufferedOutputStream(new FileOutputStream(args[0])));
+
+ TProtocolFactory factory = (TProtocolFactory)Class.forName(args[1]).newInstance();
+
+ TProtocol proto = factory.getProtocol(trans);
+
+ Fixtures.compactProtoTestStruct.write(proto);
+ trans.flush();
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/ReadCountingTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/ReadCountingTransport.java
new file mode 100644
index 000000000..3c749f9fb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/ReadCountingTransport.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+
+public class ReadCountingTransport extends TTransport {
+ public int readCount = 0;
+ private TTransport trans;
+ private boolean open = true;
+
+ public ReadCountingTransport(TTransport underlying) {
+ trans = underlying;
+ }
+
+ @Override
+ public void close() {
+ open = false;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return open;
+ }
+
+ @Override
+ public void open() throws TTransportException {
+ open = true;
+ }
+
+ @Override
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ if (!isOpen()) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "Transport is closed");
+ }
+ readCount++;
+ return trans.read(buf, off, len);
+ }
+
+ @Override
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ if (!isOpen()) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "Transport is closed");
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBuffer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBuffer.java
new file mode 100644
index 000000000..c35348953
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBuffer.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+import junit.framework.TestCase;
+
+public class TestAutoExpandingBuffer extends TestCase {
+ public void testExpands() throws Exception {
+ // has expected initial capacity
+ AutoExpandingBuffer b = new AutoExpandingBuffer(10);
+ assertEquals(10, b.array().length);
+
+ // doesn't shrink
+ b.resizeIfNecessary(8);
+ assertEquals(10, b.array().length);
+
+ // grows when more capacity is needed
+ b.resizeIfNecessary(100);
+ assertTrue(b.array().length >= 100);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferReadTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferReadTransport.java
new file mode 100644
index 000000000..83ebc2d4c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferReadTransport.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+import java.nio.ByteBuffer;
+
+import junit.framework.TestCase;
+
+public class TestAutoExpandingBufferReadTransport extends TestCase {
+ private static final byte[] HUNDRED_BYTES = new byte[100];
+
+ static {
+ for (byte i = 0; i < 100; i++) {
+ HUNDRED_BYTES[i] = i;
+ }
+ }
+
+ public void testIt() throws Exception {
+ AutoExpandingBufferReadTransport t = new AutoExpandingBufferReadTransport(150);
+
+ TMemoryInputTransport membuf = new TMemoryInputTransport(HUNDRED_BYTES);
+
+ t.fill(membuf, 100);
+ assertEquals(100, t.getBytesRemainingInBuffer());
+ assertEquals(0, t.getBufferPosition());
+
+ byte[] target = new byte[10];
+ assertEquals(10, t.read(target, 0, 10));
+ assertEquals(ByteBuffer.wrap(HUNDRED_BYTES, 0, 10), ByteBuffer.wrap(target));
+
+ assertEquals(90, t.getBytesRemainingInBuffer());
+ assertEquals(10, t.getBufferPosition());
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferWriteTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferWriteTransport.java
new file mode 100644
index 000000000..86b5b0d0f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferWriteTransport.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+import java.nio.ByteBuffer;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class TestAutoExpandingBufferWriteTransport {
+
+ @Test
+ public void testIt() throws Exception {
+ AutoExpandingBufferWriteTransport t = new AutoExpandingBufferWriteTransport(1, 0);
+ assertEquals(0, t.getLength());
+ assertEquals(1, t.getBuf().array().length);
+ byte[] b1 = new byte[]{1,2,3};
+ t.write(b1);
+ assertEquals(3, t.getLength());
+ assertTrue(t.getBuf().array().length >= 3);
+ assertEquals(ByteBuffer.wrap(b1), ByteBuffer.wrap(t.getBuf().array(), 0, 3));
+
+ t.reset();
+ assertEquals(0, t.getLength());
+ assertTrue(t.getBuf().array().length >= 3);
+ byte[] b2 = new byte[]{4,5};
+ t.write(b2);
+ assertEquals(2, t.getLength());
+ assertEquals(ByteBuffer.wrap(b2), ByteBuffer.wrap(t.getBuf().array(), 0, 2));
+
+ AutoExpandingBufferWriteTransport uut = new AutoExpandingBufferWriteTransport(8, 4);
+ assertEquals(4, uut.getLength());
+ assertEquals(8, uut.getBuf().array().length);
+ uut.write(b1);
+ assertEquals(7, uut.getLength());
+ assertEquals(8, uut.getBuf().array().length);
+ assertEquals(ByteBuffer.wrap(b1), ByteBuffer.wrap(uut.getBuf().array(), 4, 3));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBadInitialSize() throws IllegalArgumentException {
+ new AutoExpandingBufferWriteTransport(0, 0);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBadFrontReserveSize() throws IllegalArgumentException {
+ new AutoExpandingBufferWriteTransport(4, -1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testTooSmallFrontReserveSize() throws IllegalArgumentException {
+ new AutoExpandingBufferWriteTransport(4, 5);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTByteBuffer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTByteBuffer.java
new file mode 100644
index 000000000..bdc0a848a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTByteBuffer.java
@@ -0,0 +1,36 @@
+package org.apache.thrift.transport;
+
+import junit.framework.TestCase;
+import java.nio.charset.StandardCharsets;
+import org.apache.thrift.TException;
+
+import java.nio.ByteBuffer;
+
+public class TestTByteBuffer extends TestCase {
+ public void testReadWrite() throws Exception {
+ final TByteBuffer byteBuffer = new TByteBuffer(ByteBuffer.allocate(16));
+ byteBuffer.write("Hello World".getBytes(StandardCharsets.UTF_8));
+ assertEquals("Hello World", new String(byteBuffer.flip().toByteArray(), StandardCharsets.UTF_8));
+ }
+
+ public void testReuseReadWrite() throws Exception {
+ final TByteBuffer byteBuffer = new TByteBuffer(ByteBuffer.allocate(16));
+ byteBuffer.write("Hello World".getBytes(StandardCharsets.UTF_8));
+ assertEquals("Hello World", new String(byteBuffer.flip().toByteArray(), StandardCharsets.UTF_8));
+
+ byteBuffer.clear();
+
+ byteBuffer.write("Goodbye Horses".getBytes(StandardCharsets.UTF_8));
+ assertEquals("Goodbye Horses", new String(byteBuffer.flip().toByteArray(), StandardCharsets.UTF_8));
+ }
+
+ public void testOverflow() throws Exception {
+ final TByteBuffer byteBuffer = new TByteBuffer(ByteBuffer.allocate(4));
+ try {
+ byteBuffer.write("Hello World".getBytes(StandardCharsets.UTF_8));
+ fail("Expected write operation to fail with TTransportException");
+ } catch (TTransportException e) {
+ assertEquals("Not enough room in output buffer", e.getMessage());
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTFastFramedTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTFastFramedTransport.java
new file mode 100644
index 000000000..06ee20666
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTFastFramedTransport.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+public class TestTFastFramedTransport extends TestTFramedTransport {
+ protected final static int INITIAL_CAPACITY = 50;
+
+ @Override
+ protected TTransport getTransport(TTransport underlying) {
+ return new TFastFramedTransport(underlying, INITIAL_CAPACITY, 10 * 1024 * 1024);
+ }
+
+ @Override
+ protected TTransport getTransport(TTransport underlying, int maxLength) {
+ return new TFastFramedTransport(underlying, INITIAL_CAPACITY, maxLength);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTFramedTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTFramedTransport.java
new file mode 100644
index 000000000..e30d74b07
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTFramedTransport.java
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+public class TestTFramedTransport extends TestCase {
+
+ protected TTransport getTransport(TTransport underlying) {
+ return new TFramedTransport(underlying);
+ }
+
+ protected TTransport getTransport(TTransport underlying, int maxLength) {
+ return new TFramedTransport(underlying, maxLength);
+ }
+
+ public static byte[] byteSequence(int start, int end) {
+ byte[] result = new byte[end-start+1];
+ for (int i = 0; i <= (end-start); i++) {
+ result[i] = (byte)(start+i);
+ }
+ return result;
+ }
+
+ public void testRead() throws IOException, TTransportException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ dos.writeInt(50);
+ dos.write(byteSequence(0, 49));
+
+ dos.writeInt(220);
+ dos.write(byteSequence(0, 219));
+
+ TMemoryBuffer membuf = new TMemoryBuffer(0);
+ membuf.write(baos.toByteArray());
+
+ ReadCountingTransport countTrans = new ReadCountingTransport(membuf);
+ TTransport trans = getTransport(countTrans);
+
+ byte[] readBuf = new byte[10];
+ trans.read(readBuf, 0, 10);
+ assertTrue(Arrays.equals(readBuf, byteSequence(0,9)));
+ assertEquals(2, countTrans.readCount);
+
+ trans.read(readBuf, 0, 10);
+ assertTrue(Arrays.equals(readBuf, byteSequence(10,19)));
+ assertEquals(2, countTrans.readCount);
+
+ assertEquals(30, trans.read(new byte[30], 0, 30));
+ assertEquals(2, countTrans.readCount);
+
+ readBuf = new byte[220];
+ assertEquals(220, trans.read(readBuf, 0, 220));
+ assertTrue(Arrays.equals(readBuf, byteSequence(0, 219)));
+ assertEquals(4, countTrans.readCount);
+ }
+
+ public void testInvalidFrameSize() throws IOException, TTransportException {
+ int maxLength = 128;
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ dos.writeInt(130);
+ dos.write(byteSequence(0, 129));
+
+ TMemoryBuffer membuf = new TMemoryBuffer(0);
+ membuf.write(baos.toByteArray());
+
+ ReadCountingTransport countTrans = new ReadCountingTransport(membuf);
+ TTransport trans = getTransport(countTrans, maxLength);
+
+ byte[] readBuf = new byte[10];
+ try {
+ trans.read(readBuf, 0, 4);
+ fail("Expected a TTransportException");
+ } catch (TTransportException e) {
+ // We expect this exception because the frame we're trying to read is larger than our max frame length
+ assertEquals(TTransportException.CORRUPTED_DATA, e.getType());
+ }
+
+ assertFalse(trans.isOpen());
+
+ try {
+ trans.read(readBuf, 0, 4);
+ fail("Expected a TTransportException");
+ } catch (TTransportException e) {
+ // This time we get an exception indicating the connection was closed
+ assertEquals(TTransportException.NOT_OPEN, e.getType());
+ }
+ }
+
+ public void testWrite() throws TTransportException, IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ WriteCountingTransport countingTrans = new WriteCountingTransport(new TIOStreamTransport(new BufferedOutputStream(baos)));
+ TTransport trans = getTransport(countingTrans);
+
+ trans.write(byteSequence(0,100));
+ assertEquals(0, countingTrans.writeCount);
+ trans.write(byteSequence(101,200));
+ trans.write(byteSequence(201,255));
+ assertEquals(0, countingTrans.writeCount);
+
+ trans.flush();
+ assertEquals(1, countingTrans.writeCount);
+
+ trans.write(byteSequence(0, 245));
+ trans.flush();
+ assertEquals(2, countingTrans.writeCount);
+
+ DataInputStream din = new DataInputStream(new ByteArrayInputStream(baos.toByteArray()));
+ assertEquals(256, din.readInt());
+
+ byte[] buf = new byte[256];
+ din.read(buf, 0, 256);
+ assertTrue(Arrays.equals(byteSequence(0,255), buf));
+
+ assertEquals(246, din.readInt());
+ buf = new byte[246];
+ din.read(buf, 0, 246);
+ assertTrue(Arrays.equals(byteSequence(0,245), buf));
+ }
+
+ public void testDirectRead() throws IOException, TTransportException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ dos.writeInt(50);
+ dos.write(byteSequence(0, 49));
+ dos.writeInt(75);
+ dos.write(byteSequence(125, 200));
+
+ TMemoryBuffer membuf = new TMemoryBuffer(0);
+ membuf.write(baos.toByteArray());
+
+ ReadCountingTransport countTrans = new ReadCountingTransport(membuf);
+ TTransport trans = getTransport(countTrans);
+
+ assertEquals(0, trans.getBytesRemainingInBuffer());
+
+ byte[] readBuf = new byte[10];
+ trans.read(readBuf, 0, 10);
+ assertTrue(Arrays.equals(readBuf, byteSequence(0,9)));
+
+ assertEquals(40, trans.getBytesRemainingInBuffer());
+ assertEquals(10, trans.getBufferPosition());
+
+ trans.consumeBuffer(5);
+ assertEquals(35, trans.getBytesRemainingInBuffer());
+ assertEquals(15, trans.getBufferPosition());
+
+ assertEquals(2, countTrans.readCount);
+
+ assertEquals(35, trans.read(new byte[35], 0, 35));
+ assertEquals(0, trans.getBytesRemainingInBuffer());
+ assertEquals(50, trans.getBufferPosition());
+
+ trans.read(readBuf, 0, 10);
+ assertEquals(4, countTrans.readCount);
+ assertTrue(Arrays.equals(readBuf, byteSequence(125,134)));
+ assertEquals(65, trans.getBytesRemainingInBuffer());
+ assertEquals(10, trans.getBufferPosition());
+ }
+
+ public void testClear() throws IOException, TTransportException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ dos.writeInt(220);
+ dos.write(byteSequence(0, 219));
+
+ TMemoryBuffer membuf = new TMemoryBuffer(0);
+ membuf.write(baos.toByteArray());
+
+ ReadCountingTransport countTrans = new ReadCountingTransport(membuf);
+ TTransport trans = getTransport(countTrans);
+
+ byte[] readBuf = new byte[220];
+ trans.read(readBuf, 0, 220);
+ assertTrue(Arrays.equals(readBuf, byteSequence(0,219)));
+
+ assertTrue(trans instanceof TFramedTransport || trans instanceof TFastFramedTransport);
+ if (trans instanceof TFramedTransport) {
+ assertTrue(trans.getBuffer() != null && trans.getBuffer().length > 0);
+ ((TFramedTransport) trans).clear();
+ assertTrue(trans.getBuffer() == null);
+ } else if (trans instanceof TFastFramedTransport) {
+ assertTrue(trans.getBuffer().length > TestTFastFramedTransport.INITIAL_CAPACITY);
+ ((TFastFramedTransport) trans).clear();
+ assertTrue(trans.getBuffer().length == TestTFastFramedTransport.INITIAL_CAPACITY);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTMemoryInputTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTMemoryInputTransport.java
new file mode 100644
index 000000000..273145bd2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTMemoryInputTransport.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+public class TestTMemoryInputTransport extends TestCase {
+ public void testFresh() throws Exception {
+ byte[] input_buf = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ TMemoryInputTransport trans = new TMemoryInputTransport(input_buf);
+ assertEquals(0, trans.getBufferPosition());
+ assertEquals(input_buf, trans.getBuffer());
+ assertEquals(10, trans.getBytesRemainingInBuffer());
+
+ byte[] buf1 = new byte[4];
+ trans.readAll(buf1, 0, 4);
+ assertTrue(Arrays.equals(new byte[]{1, 2, 3, 4}, buf1));
+ assertEquals(4, trans.getBufferPosition());
+ assertEquals(6, trans.getBytesRemainingInBuffer());
+
+ trans.consumeBuffer(2);
+
+ assertEquals(6, trans.getBufferPosition());
+ assertEquals(4, trans.getBytesRemainingInBuffer());
+
+ trans.readAll(buf1, 0, 4);
+ assertTrue(Arrays.equals(new byte[]{7, 8, 9, 10}, buf1));
+ assertEquals(10, trans.getBufferPosition());
+ assertEquals(0, trans.getBytesRemainingInBuffer());
+ }
+
+ public void testReused() throws Exception {
+ byte[] input_buf = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ TMemoryInputTransport trans = new TMemoryInputTransport(input_buf);
+ assertEquals(0, trans.getBufferPosition());
+ assertEquals(input_buf, trans.getBuffer());
+ assertEquals(10, trans.getBytesRemainingInBuffer());
+
+ byte[] new_buf = new byte[]{10, 9, 8};
+ trans.reset(new_buf);
+ assertEquals(0, trans.getBufferPosition());
+ assertEquals(new_buf, trans.getBuffer());
+ assertEquals(3, trans.getBytesRemainingInBuffer());
+ }
+
+ public void testWithOffsetAndLength() throws Exception {
+ byte[] input_buf = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ TMemoryInputTransport trans = new TMemoryInputTransport(input_buf, 1, 3);
+ assertEquals(1, trans.getBufferPosition());
+ assertEquals(3, trans.getBytesRemainingInBuffer());
+ byte[] readBuffer = new byte[3];
+ trans.readAll(readBuffer, 0, 3);
+ assertTrue(Arrays.equals(new byte[]{2, 3, 4}, readBuffer));
+
+ try {
+ assertEquals(0, trans.readAll(readBuffer, 0, 3));
+ fail("should have thrown an exception");
+ } catch (Exception e) {
+ // yay
+ }
+
+ trans.reset(input_buf, 3, 4);
+ readBuffer = new byte[4];
+ trans.readAll(readBuffer, 0, 4);
+ assertTrue(Arrays.equals(new byte[]{4, 5, 6, 7}, readBuffer));
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactory.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactory.java
new file mode 100644
index 000000000..032c2eb71
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactory.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.server.ServerTestBase;
+import org.apache.thrift.server.TServer;
+import org.apache.thrift.server.TSimpleServer;
+import org.apache.thrift.server.TServer.Args;
+
+public class TestTSSLTransportFactory extends ServerTestBase {
+ private Thread serverThread;
+ private TServer server;
+
+ private static final List<TProtocolFactory> protocols = new ArrayList<TProtocolFactory>();
+ static {
+ // TODO: Only supported on TBinaryProtocol. Doesn't work for TCompactProtocol
+ protocols.add(new TBinaryProtocol.Factory());
+ }
+
+ @Override
+ public TTransport getClientTransport(TTransport underlyingTransport)
+ throws Exception {
+ return TSSLTransportFactory.getClientSocket(HOST, PORT);
+ }
+
+ protected TServerSocket getServerTransport() throws Exception {
+ return TSSLTransportFactory.getServerSocket(PORT);
+ }
+
+ @Override
+ public void startServer(final TProcessor processor, final TProtocolFactory protoFactory, final TTransportFactory factory)
+ throws Exception {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ TServerTransport serverTransport = getServerTransport();
+ final Args args = new Args(serverTransport).processor(processor);
+ server = new TSimpleServer(args);
+ server.serve();
+ } catch (Exception e) {
+ e.printStackTrace();
+ assert false;
+ }
+ }
+ };
+
+ serverThread.start();
+ Thread.sleep(SLEEP_DELAY);
+ }
+
+ @Override
+ public void stopServer() throws Exception {
+ server.stop();
+ serverThread.join();
+ }
+
+ @Override
+ public void open(TTransport transport) throws Exception {}
+
+ @Override
+ public List<TProtocolFactory> getProtocols() {
+ return protocols;
+ }
+
+ @Override
+ public void testTransportFactory() throws Exception {
+ // this test doesn't really apply to this suite, so let's skip it.
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryCustomClient1.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryCustomClient1.java
new file mode 100644
index 000000000..da1659f9d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryCustomClient1.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+public class TestTSSLTransportFactoryCustomClient1 extends TestTSSLTransportFactory {
+
+ @Override
+ public TTransport getClientTransport(TTransport underlyingTransport)
+ throws Exception {
+ TSSLTransportFactory.TSSLTransportParameters params = new
+ TSSLTransportFactory.TSSLTransportParameters();
+
+ params.setTrustStore(System.getProperty("javax.net.ssl.trustStore"),
+ System.getProperty("javax.net.ssl.trustStorePassword"));
+
+ return TSSLTransportFactory.getClientSocket(HOST, PORT, 0/*timeout*/, params);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryCustomClient2.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryCustomClient2.java
new file mode 100644
index 000000000..eaed46057
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryCustomClient2.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+public class TestTSSLTransportFactoryCustomClient2 extends TestTSSLTransportFactory {
+
+ @Override
+ public TTransport getClientTransport(TTransport underlyingTransport)
+ throws Exception {
+ TSSLTransportFactory.TSSLTransportParameters params = new
+ TSSLTransportFactory.TSSLTransportParameters();
+
+ params.setTrustStore(System.getProperty("javax.net.ssl.trustStore"), null);
+
+ return TSSLTransportFactory.getClientSocket(HOST, PORT, 0/*timeout*/, params);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryStreamedStore.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryStreamedStore.java
new file mode 100644
index 000000000..25bf5cebb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryStreamedStore.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.FileInputStream;
+import java.net.InetAddress;
+
+public class TestTSSLTransportFactoryStreamedStore extends TestTSSLTransportFactory {
+ private static String keyStoreLocation = System.getProperty("javax.net.ssl.keyStore");
+ private static String trustStoreLocation = System.getProperty("javax.net.ssl.trustStore");
+
+ public TestTSSLTransportFactoryStreamedStore() {
+ super();
+
+ /**
+ * Override system properties to be able to test passing
+ * the trustStore and keyStore as input stream
+ */
+ System.setProperty("javax.net.ssl.trustStore", "");
+ System.setProperty("javax.net.ssl.keyStore", "");
+ }
+
+ @Override
+ public TTransport getClientTransport(TTransport underlyingTransport)
+ throws Exception {
+ TSSLTransportFactory.TSSLTransportParameters params = new
+ TSSLTransportFactory.TSSLTransportParameters();
+
+ params.setTrustStore(new FileInputStream(trustStoreLocation),
+ System.getProperty("javax.net.ssl.trustStorePassword"));
+
+ return TSSLTransportFactory.getClientSocket(HOST, PORT, 0/*timeout*/, params);
+ }
+
+ @Override
+ protected TServerSocket getServerTransport() throws Exception {
+ TSSLTransportFactory.TSSLTransportParameters params = new
+ TSSLTransportFactory.TSSLTransportParameters();
+
+ params.setKeyStore(new FileInputStream(keyStoreLocation),
+ System.getProperty("javax.net.ssl.keyStorePassword"));
+
+ return TSSLTransportFactory.getServerSocket(PORT, 0/*timeout*/, InetAddress.getByName(HOST), params);
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSaslTransports.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSaslTransports.java
new file mode 100644
index 000000000..36a06e9e5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSaslTransports.java
@@ -0,0 +1,471 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.sasl.AuthorizeCallback;
+import javax.security.sasl.RealmCallback;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslClientFactory;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+import javax.security.sasl.SaslServerFactory;
+
+import junit.framework.TestCase;
+
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.server.ServerTestBase;
+import org.apache.thrift.server.TServer;
+import org.apache.thrift.server.TSimpleServer;
+import org.apache.thrift.server.TServer.Args;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TestTSaslTransports extends TestCase {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TestTSaslTransports.class);
+
+ private static final String HOST = "localhost";
+ private static final String SERVICE = "thrift-test";
+ private static final String PRINCIPAL = "thrift-test-principal";
+ private static final String PASSWORD = "super secret password";
+ private static final String REALM = "thrift-test-realm";
+
+ private static final String UNWRAPPED_MECHANISM = "CRAM-MD5";
+ private static final Map<String, String> UNWRAPPED_PROPS = null;
+
+ private static final String WRAPPED_MECHANISM = "DIGEST-MD5";
+ private static final Map<String, String> WRAPPED_PROPS = new HashMap<String, String>();
+
+ static {
+ WRAPPED_PROPS.put(Sasl.QOP, "auth-int");
+ WRAPPED_PROPS.put("com.sun.security.sasl.digest.realm", REALM);
+ }
+
+ private static final String testMessage1 = "Hello, world! Also, four "
+ + "score and seven years ago our fathers brought forth on this "
+ + "continent a new nation, conceived in liberty, and dedicated to the "
+ + "proposition that all men are created equal.";
+
+ private static final String testMessage2 = "I have a dream that one day "
+ + "this nation will rise up and live out the true meaning of its creed: "
+ + "'We hold these truths to be self-evident, that all men are created equal.'";
+
+
+ private static class TestSaslCallbackHandler implements CallbackHandler {
+ private final String password;
+
+ public TestSaslCallbackHandler(String password) {
+ this.password = password;
+ }
+
+ @Override
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+ for (Callback c : callbacks) {
+ if (c instanceof NameCallback) {
+ ((NameCallback) c).setName(PRINCIPAL);
+ } else if (c instanceof PasswordCallback) {
+ ((PasswordCallback) c).setPassword(password.toCharArray());
+ } else if (c instanceof AuthorizeCallback) {
+ ((AuthorizeCallback) c).setAuthorized(true);
+ } else if (c instanceof RealmCallback) {
+ ((RealmCallback) c).setText(REALM);
+ } else {
+ throw new UnsupportedCallbackException(c);
+ }
+ }
+ }
+ }
+
+ private class ServerThread extends Thread {
+ final String mechanism;
+ final Map<String, String> props;
+ volatile Throwable thrown;
+
+ public ServerThread(String mechanism, Map<String, String> props) {
+ this.mechanism = mechanism;
+ this.props = props;
+ }
+
+ public void run() {
+ try {
+ internalRun();
+ } catch (Throwable t) {
+ thrown = t;
+ }
+ }
+
+ private void internalRun() throws Exception {
+ TServerSocket serverSocket = new TServerSocket(
+ new TServerSocket.ServerSocketTransportArgs().
+ port(ServerTestBase.PORT));
+ try {
+ acceptAndWrite(serverSocket);
+ } finally {
+ serverSocket.close();
+ }
+ }
+
+ private void acceptAndWrite(TServerSocket serverSocket)
+ throws Exception {
+ TTransport serverTransport = serverSocket.accept();
+ TTransport saslServerTransport = new TSaslServerTransport(
+ mechanism, SERVICE, HOST,
+ props, new TestSaslCallbackHandler(PASSWORD), serverTransport);
+
+ saslServerTransport.open();
+
+ byte[] inBuf = new byte[testMessage1.getBytes().length];
+ // Deliberately read less than the full buffer to ensure
+ // that TSaslTransport is correctly buffering reads. This
+ // will fail for the WRAPPED test, if it doesn't work.
+ saslServerTransport.readAll(inBuf, 0, 5);
+ saslServerTransport.readAll(inBuf, 5, 10);
+ saslServerTransport.readAll(inBuf, 15, inBuf.length - 15);
+ LOGGER.debug("server got: {}", new String(inBuf));
+ assertEquals(new String(inBuf), testMessage1);
+
+ LOGGER.debug("server writing: {}", testMessage2);
+ saslServerTransport.write(testMessage2.getBytes());
+ saslServerTransport.flush();
+
+ saslServerTransport.close();
+ }
+ }
+
+ private void testSaslOpen(final String mechanism, final Map<String, String> props)
+ throws Exception {
+ ServerThread serverThread = new ServerThread(mechanism, props);
+ serverThread.start();
+
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // Ah well.
+ }
+
+ try {
+ TSocket clientSocket = new TSocket(HOST, ServerTestBase.PORT);
+ TTransport saslClientTransport = new TSaslClientTransport(mechanism,
+ PRINCIPAL, SERVICE, HOST, props, new TestSaslCallbackHandler(PASSWORD), clientSocket);
+ saslClientTransport.open();
+ LOGGER.debug("client writing: {}", testMessage1);
+ saslClientTransport.write(testMessage1.getBytes());
+ saslClientTransport.flush();
+
+ byte[] inBuf = new byte[testMessage2.getBytes().length];
+ saslClientTransport.readAll(inBuf, 0, inBuf.length);
+ LOGGER.debug("client got: {}", new String(inBuf));
+ assertEquals(new String(inBuf), testMessage2);
+
+ TTransportException expectedException = null;
+ try {
+ saslClientTransport.open();
+ } catch (TTransportException e) {
+ expectedException = e;
+ }
+ assertNotNull(expectedException);
+
+ saslClientTransport.close();
+ } catch (Exception e) {
+ LOGGER.warn("Exception caught", e);
+ throw e;
+ } finally {
+ serverThread.interrupt();
+ try {
+ serverThread.join();
+ } catch (InterruptedException e) {
+ // Ah well.
+ }
+ assertNull(serverThread.thrown);
+ }
+ }
+
+ public void testUnwrappedOpen() throws Exception {
+ testSaslOpen(UNWRAPPED_MECHANISM, UNWRAPPED_PROPS);
+ }
+
+ public void testWrappedOpen() throws Exception {
+ testSaslOpen(WRAPPED_MECHANISM, WRAPPED_PROPS);
+ }
+
+ public void testAnonymousOpen() throws Exception {
+ testSaslOpen("ANONYMOUS", null);
+ }
+
+ /**
+ * Test that we get the proper exceptions thrown back the server when
+ * the client provides invalid password.
+ */
+ public void testBadPassword() throws Exception {
+ ServerThread serverThread = new ServerThread(UNWRAPPED_MECHANISM, UNWRAPPED_PROPS);
+ serverThread.start();
+
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // Ah well.
+ }
+
+ boolean clientSidePassed = true;
+
+ try {
+ TSocket clientSocket = new TSocket(HOST, ServerTestBase.PORT);
+ TTransport saslClientTransport = new TSaslClientTransport(
+ UNWRAPPED_MECHANISM, PRINCIPAL, SERVICE, HOST, UNWRAPPED_PROPS,
+ new TestSaslCallbackHandler("NOT THE PASSWORD"), clientSocket);
+ saslClientTransport.open();
+ clientSidePassed = false;
+ fail("Was able to open transport with bad password");
+ } catch (TTransportException tte) {
+ LOGGER.error("Exception for bad password", tte);
+ assertNotNull(tte.getMessage());
+ assertTrue(tte.getMessage().contains("Invalid response"));
+
+ } finally {
+ serverThread.interrupt();
+ serverThread.join();
+
+ if (clientSidePassed) {
+ assertNotNull(serverThread.thrown);
+ assertTrue(serverThread.thrown.getMessage().contains("Invalid response"));
+ }
+ }
+ }
+
+ public void testWithServer() throws Exception {
+ new TestTSaslTransportsWithServer().testIt();
+ }
+
+ private static class TestTSaslTransportsWithServer extends ServerTestBase {
+
+ private Thread serverThread;
+ private TServer server;
+
+ @Override
+ public TTransport getClientTransport(TTransport underlyingTransport) throws Exception {
+ return new TSaslClientTransport(
+ WRAPPED_MECHANISM, PRINCIPAL, SERVICE, HOST, WRAPPED_PROPS,
+ new TestSaslCallbackHandler(PASSWORD), underlyingTransport);
+ }
+
+ @Override
+ public void startServer(final TProcessor processor, final TProtocolFactory protoFactory, final TTransportFactory factory) throws Exception {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ // Transport
+ TServerSocket socket = new TServerSocket(new TServerSocket.ServerSocketTransportArgs().port(PORT));
+
+ TTransportFactory factory = new TSaslServerTransport.Factory(
+ WRAPPED_MECHANISM, SERVICE, HOST, WRAPPED_PROPS,
+ new TestSaslCallbackHandler(PASSWORD));
+ server = new TSimpleServer(new Args(socket).processor(processor).transportFactory(factory).protocolFactory(protoFactory));
+
+ // Run it
+ LOGGER.debug("Starting the server on port {}", PORT);
+ server.serve();
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail();
+ }
+ }
+ };
+ serverThread.start();
+ Thread.sleep(1000);
+ }
+
+ @Override
+ public void stopServer() throws Exception {
+ server.stop();
+ try {
+ serverThread.join();
+ } catch (InterruptedException e) {}
+ }
+
+ }
+
+
+ /**
+ * Implementation of SASL ANONYMOUS, used for testing client-side
+ * initial responses.
+ */
+ private static class AnonymousClient implements SaslClient {
+ private final String username;
+ private boolean hasProvidedInitialResponse;
+
+ public AnonymousClient(String username) {
+ this.username = username;
+ }
+
+ public String getMechanismName() { return "ANONYMOUS"; }
+ public boolean hasInitialResponse() { return true; }
+ public byte[] evaluateChallenge(byte[] challenge) throws SaslException {
+ if (hasProvidedInitialResponse) {
+ throw new SaslException("Already complete!");
+ }
+
+ hasProvidedInitialResponse = true;
+ return username.getBytes(StandardCharsets.UTF_8);
+ }
+ public boolean isComplete() { return hasProvidedInitialResponse; }
+ public byte[] unwrap(byte[] incoming, int offset, int len) {
+ throw new UnsupportedOperationException();
+ }
+ public byte[] wrap(byte[] outgoing, int offset, int len) {
+ throw new UnsupportedOperationException();
+ }
+ public Object getNegotiatedProperty(String propName) { return null; }
+ public void dispose() {}
+ }
+
+ private static class AnonymousServer implements SaslServer {
+ private String user;
+ public String getMechanismName() { return "ANONYMOUS"; }
+ public byte[] evaluateResponse(byte[] response) throws SaslException {
+ this.user = new String(response, StandardCharsets.UTF_8);
+ return null;
+ }
+ public boolean isComplete() { return user != null; }
+ public String getAuthorizationID() { return user; }
+ public byte[] unwrap(byte[] incoming, int offset, int len) {
+ throw new UnsupportedOperationException();
+ }
+ public byte[] wrap(byte[] outgoing, int offset, int len) {
+ throw new UnsupportedOperationException();
+ }
+ public Object getNegotiatedProperty(String propName) { return null; }
+ public void dispose() {}
+
+ }
+
+ public static class SaslAnonymousFactory
+ implements SaslClientFactory, SaslServerFactory {
+
+ public SaslClient createSaslClient(
+ String[] mechanisms, String authorizationId, String protocol,
+ String serverName, Map<String,?> props, CallbackHandler cbh)
+ {
+ for (String mech : mechanisms) {
+ if ("ANONYMOUS".equals(mech)) {
+ return new AnonymousClient(authorizationId);
+ }
+ }
+ return null;
+ }
+
+ public SaslServer createSaslServer(
+ String mechanism, String protocol, String serverName, Map<String,?> props, CallbackHandler cbh)
+ {
+ if ("ANONYMOUS".equals(mechanism)) {
+ return new AnonymousServer();
+ }
+ return null;
+ }
+ public String[] getMechanismNames(Map<String, ?> props) {
+ return new String[] { "ANONYMOUS" };
+ }
+ }
+
+ static {
+ java.security.Security.addProvider(new SaslAnonymousProvider());
+ }
+ public static class SaslAnonymousProvider extends java.security.Provider {
+ public SaslAnonymousProvider() {
+ super("ThriftSaslAnonymous", 1.0, "Thrift Anonymous SASL provider");
+ put("SaslClientFactory.ANONYMOUS", SaslAnonymousFactory.class.getName());
+ put("SaslServerFactory.ANONYMOUS", SaslAnonymousFactory.class.getName());
+ }
+ }
+
+ private static class MockTTransport extends TTransport {
+
+ byte[] badHeader = null;
+ private TMemoryInputTransport readBuffer = new TMemoryInputTransport();
+
+ public MockTTransport(int mode) {
+ if (mode==1) {
+ // Invalid status byte
+ badHeader = new byte[] { (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x05 };
+ } else if (mode == 2) {
+ // Valid status byte, negative payload length
+ badHeader = new byte[] { (byte)0x01, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF };
+ } else if (mode == 3) {
+ // Valid status byte, excessively large, bogus payload length
+ badHeader = new byte[] { (byte)0x01, (byte)0x64, (byte)0x00, (byte)0x00, (byte)0x00 };
+ }
+ readBuffer.reset(badHeader);
+ }
+
+ @Override
+ public boolean isOpen() {
+ return true;
+ }
+
+ @Override
+ public void open() throws TTransportException {}
+
+ @Override
+ public void close() {}
+
+ @Override
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ return readBuffer.read(buf, off, len);
+ }
+
+ @Override
+ public void write(byte[] buf, int off, int len) throws TTransportException {}
+ }
+
+ public void testBadHeader() {
+ TSaslTransport saslTransport = new TSaslServerTransport(new MockTTransport(1));
+ try {
+ saslTransport.receiveSaslMessage();
+ fail("Should have gotten an error due to incorrect status byte value.");
+ } catch (TTransportException e) {
+ assertEquals(e.getMessage(), "Invalid status -1");
+ }
+ saslTransport = new TSaslServerTransport(new MockTTransport(2));
+ try {
+ saslTransport.receiveSaslMessage();
+ fail("Should have gotten an error due to negative payload length.");
+ } catch (TTransportException e) {
+ assertEquals(e.getMessage(), "Invalid payload header length: -1");
+ }
+ saslTransport = new TSaslServerTransport(new MockTTransport(3));
+ try {
+ saslTransport.receiveSaslMessage();
+ fail("Should have gotten an error due to bogus (large) payload length.");
+ } catch (TTransportException e) {
+ assertEquals(e.getMessage(), "Invalid payload header length: 1677721600");
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSimpleFileTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSimpleFileTransport.java
new file mode 100644
index 000000000..7b880f499
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSimpleFileTransport.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import junit.framework.TestCase;
+
+public class TestTSimpleFileTransport extends TestCase {
+ public void testFresh() throws Exception {
+ //Test write side
+ Path tempFilePathName = Files.createTempFile("TSimpleFileTransportTest", null);
+ Files.delete(tempFilePathName);
+ byte[] input_buf = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ TSimpleFileTransport trans_write = new TSimpleFileTransport(tempFilePathName.toString(),false, true, false);
+ assert (!trans_write.isOpen());
+ trans_write.open();
+ assert(trans_write.isOpen());
+ trans_write.write(input_buf);
+ trans_write.write(input_buf,2,2);
+ trans_write.flush();
+ trans_write.close();
+
+ //Test read side
+ TSimpleFileTransport trans = new TSimpleFileTransport(tempFilePathName.toString(),true, false);
+ assert(trans.isOpen());
+
+ //Simple file trans provides no buffer access
+ assert(0 == trans.getBufferPosition());
+ assert(null == trans.getBuffer());
+ assert(-1 == trans.getBytesRemainingInBuffer());
+
+ //Test file pointer operations
+ assert(0 == trans.getFilePointer());
+ assert(12 == trans.length());
+
+ final int BUFSIZ = 4;
+ byte[] buf1 = new byte[BUFSIZ];
+ trans.readAll(buf1, 0, BUFSIZ);
+ assert(BUFSIZ == trans.getFilePointer());
+ assert(Arrays.equals(new byte[]{1, 2, 3, 4}, buf1));
+
+ int bytesRead = trans.read(buf1, 0, BUFSIZ);
+ assert(bytesRead > 0);
+ for (int i = 0; i < bytesRead; ++i) {
+ assert(buf1[i] == i+5);
+ }
+
+ trans.seek(0);
+ assert(0 == trans.getFilePointer());
+ trans.readAll(buf1, 0, BUFSIZ);
+ assert(Arrays.equals(new byte[]{1, 2, 3, 4}, buf1));
+ assert(BUFSIZ == trans.getFilePointer());
+ trans.close();
+ Files.delete(tempFilePathName);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTZlibTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTZlibTransport.java
new file mode 100644
index 000000000..3d7f9c1c9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTZlibTransport.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.zip.DataFormatException;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.InflaterInputStream;
+
+import junit.framework.TestCase;
+
+public class TestTZlibTransport extends TestCase {
+
+ protected TTransport getTransport(TTransport underlying) {
+ return new TZlibTransport(underlying);
+ }
+
+ public static byte[] byteSequence(int start, int end) {
+ byte[] result = new byte[end-start+1];
+ for (int i = 0; i <= (end-start); i++) {
+ result[i] = (byte)(start+i);
+ }
+ return result;
+ }
+
+ public void testClose() throws TTransportException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ WriteCountingTransport countingTrans = new WriteCountingTransport(new TIOStreamTransport(new BufferedOutputStream
+ (baos)));
+ TTransport trans = getTransport(countingTrans);
+ trans.write(byteSequence(0, 245));
+ countingTrans.close();
+ trans.close();
+ }
+
+ public void testCloseOpen() throws TTransportException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ TTransport trans = getTransport(new TIOStreamTransport(baos));
+ byte[] uncompressed = byteSequence(0, 245);
+ trans.write(uncompressed);
+ trans.close();
+ final byte[] compressed = baos.toByteArray();
+
+ final byte[] buf = new byte[255];
+ TTransport transRead = getTransport(new TIOStreamTransport(new ByteArrayInputStream(compressed)));
+ int readBytes = transRead.read(buf, 0, buf.length);
+ assertEquals(uncompressed.length, readBytes);
+ transRead.close();
+ }
+
+ public void testRead() throws IOException, TTransportException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(baos);
+ DataOutputStream dos = new DataOutputStream(deflaterOutputStream);
+ dos.write(byteSequence(0, 49));
+ dos.write(byteSequence(0, 219));
+
+ deflaterOutputStream.finish();
+
+ TMemoryBuffer membuf = new TMemoryBuffer(0);
+ membuf.write(baos.toByteArray());
+
+ ReadCountingTransport countTrans = new ReadCountingTransport(membuf);
+ TTransport trans = getTransport(countTrans);
+
+ byte[] readBuf = new byte[10];
+ trans.read(readBuf, 0, 10);
+ assertTrue(Arrays.equals(readBuf, byteSequence(0,9)));
+ assertEquals(1, countTrans.readCount);
+
+ trans.read(readBuf, 0, 10);
+ assertTrue(Arrays.equals(readBuf, byteSequence(10,19)));
+ assertEquals(1, countTrans.readCount);
+
+ assertEquals(30, trans.read(new byte[30], 0, 30));
+ assertEquals(1, countTrans.readCount);
+
+ readBuf = new byte[220];
+ assertEquals(220, trans.read(readBuf, 0, 220));
+ assertTrue(Arrays.equals(readBuf, byteSequence(0, 219)));
+ assertEquals(1, countTrans.readCount);
+ }
+
+ public void testWrite() throws TTransportException, IOException, DataFormatException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ WriteCountingTransport countingTrans = new WriteCountingTransport(new TIOStreamTransport(new BufferedOutputStream(baos)));
+ TTransport trans = getTransport(countingTrans);
+
+ trans.write(byteSequence(0, 100));
+ assertEquals(1, countingTrans.writeCount);
+ trans.write(byteSequence(101, 200));
+ trans.write(byteSequence(201, 255));
+ assertEquals(1, countingTrans.writeCount);
+
+ trans.flush();
+ assertEquals(2, countingTrans.writeCount);
+
+ trans.write(byteSequence(0, 245));
+ trans.flush();
+ assertEquals(3, countingTrans.writeCount);
+
+ DataInputStream din = new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(baos.toByteArray())));
+ byte[] buf = new byte[256];
+ int n = din.read(buf, 0, 256);
+ assertEquals(n, 256);
+ assertTrue(Arrays.equals(byteSequence(0, 255), buf));
+
+ buf = new byte[246];
+ n = din.read(buf, 0, 246);
+ assertEquals(n, 246);
+ for (int i = 0; i<buf.length; i++) {
+ assertEquals("for "+i, byteSequence(0,245)[i], buf[i]);
+ }
+
+ assertTrue(Arrays.equals(byteSequence(0,245), buf));
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/WriteCountingTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/WriteCountingTransport.java
new file mode 100644
index 000000000..358f5c6eb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/WriteCountingTransport.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+
+public class WriteCountingTransport extends TTransport {
+ public int writeCount = 0;
+ private final TTransport trans;
+
+ public WriteCountingTransport(TTransport underlying) {
+ trans = underlying;
+ }
+
+ @Override
+ public void close() {}
+
+ @Override
+ public boolean isOpen() {return true;}
+
+ @Override
+ public void open() throws TTransportException {}
+
+ @Override
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ return 0;
+ }
+
+ @Override
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ writeCount ++;
+ trans.write(buf, off, len);
+ }
+
+ @Override
+ public void flush() throws TTransportException {
+ trans.flush();
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/javame/coding_standards.md b/src/jaegertracing/thrift/lib/javame/coding_standards.md
new file mode 100644
index 000000000..fa0390bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/coding_standards.md
@@ -0,0 +1 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TApplicationException.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TApplicationException.java
new file mode 100644
index 000000000..2f8612af3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TApplicationException.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TField;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolUtil;
+import org.apache.thrift.protocol.TStruct;
+import org.apache.thrift.protocol.TType;
+
+/**
+ * Application level exception
+ *
+ */
+public class TApplicationException extends TException {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final int UNKNOWN = 0;
+ public static final int UNKNOWN_METHOD = 1;
+ public static final int INVALID_MESSAGE_TYPE = 2;
+ public static final int WRONG_METHOD_NAME = 3;
+ public static final int BAD_SEQUENCE_ID = 4;
+ public static final int MISSING_RESULT = 5;
+ public static final int INTERNAL_ERROR = 6;
+ public static final int PROTOCOL_ERROR = 7;
+ public static final int INVALID_TRANSFORM = 8;
+ public static final int INVALID_PROTOCOL = 9;
+ public static final int UNSUPPORTED_CLIENT_TYPE = 10;
+
+ protected int type_ = UNKNOWN;
+
+ public TApplicationException() {
+ super();
+ }
+
+ public TApplicationException(int type) {
+ super();
+ type_ = type;
+ }
+
+ public TApplicationException(int type, String message) {
+ super(message);
+ type_ = type;
+ }
+
+ public TApplicationException(String message) {
+ super(message);
+ }
+
+ public int getType() {
+ return type_;
+ }
+
+ public static TApplicationException read(TProtocol iprot) throws TException {
+ TField field;
+ iprot.readStructBegin();
+
+ String message = null;
+ int type = UNKNOWN;
+
+ while (true) {
+ field = iprot.readFieldBegin();
+ if (field.type == TType.STOP) {
+ break;
+ }
+ switch (field.id) {
+ case 1:
+ if (field.type == TType.STRING) {
+ message = iprot.readString();
+ } else {
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ break;
+ case 2:
+ if (field.type == TType.I32) {
+ type = iprot.readI32();
+ } else {
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ break;
+ default:
+ TProtocolUtil.skip(iprot, field.type);
+ break;
+ }
+ iprot.readFieldEnd();
+ }
+ iprot.readStructEnd();
+
+ return new TApplicationException(type, message);
+ }
+
+ public void write(TProtocol oprot) throws TException {
+ TStruct struct = new TStruct("TApplicationException");
+ TField field = new TField();
+ oprot.writeStructBegin(struct);
+ if (getMessage() != null) {
+ field.name = "message";
+ field.type = TType.STRING;
+ field.id = 1;
+ oprot.writeFieldBegin(field);
+ oprot.writeString(getMessage());
+ oprot.writeFieldEnd();
+ }
+ field.name = "type";
+ field.type = TType.I32;
+ field.id = 2;
+ oprot.writeFieldBegin(field);
+ oprot.writeI32(type_);
+ oprot.writeFieldEnd();
+ oprot.writeFieldStop();
+ oprot.writeStructEnd();
+
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TBase.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TBase.java
new file mode 100644
index 000000000..13ea024e2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TBase.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TProtocol;
+
+/**
+ * Generic base interface for generated Thrift objects.
+ *
+ */
+public interface TBase {
+
+ /**
+ * Reads the TObject from the given input protocol.
+ *
+ * @param iprot Input protocol
+ */
+ public void read(TProtocol iprot) throws TException;
+
+ /**
+ * Writes the objects out to the protocol
+ *
+ * @param oprot Output protocol
+ */
+ public void write(TProtocol oprot) throws TException;
+
+ public int compareTo(Object other);
+
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TBaseHelper.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TBaseHelper.java
new file mode 100644
index 000000000..2f31d311c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TBaseHelper.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift;
+
+import java.util.Vector;
+import java.util.Hashtable;
+import java.util.Enumeration;
+
+public class TBaseHelper {
+
+ public static int compareTo(boolean a, boolean b) {
+ return (a == b) ? 0 : (a ? 1 : -1);
+
+
+ }
+ public static int compareTo(Boolean a, Boolean b) {
+ return (a.booleanValue() == b.booleanValue()) ? 0 : (a.booleanValue() ? 1 : -1);
+
+
+ }
+ public static int compareTo(Boolean a, boolean b) {
+ return (a.booleanValue() == b) ? 0 : (a.booleanValue() ? 1 : -1);
+
+
+ }
+
+ public static Boolean booleanValueOf(boolean b) {
+ return (b ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ public static int compareTo(byte a, byte b) {
+ if (a < b) {
+ return -1;
+ } else if (b < a) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ public static int compareTo(short a, short b) {
+ if (a < b) {
+ return -1;
+ } else if (b < a) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ public static int compareTo(int a, int b) {
+ if (a < b) {
+ return -1;
+ } else if (b < a) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ public static int compareTo(long a, long b) {
+ if (a < b) {
+ return -1;
+ } else if (b < a) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ public static int compareTo(double a, double b) {
+ if (a < b) {
+ return -1;
+ } else if (b < a) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ public static int compareTo(String a, String b) {
+ return a.compareTo(b);
+ }
+
+ public static int compareTo(byte[] a, byte[] b) {
+ int sizeCompare = compareTo(a.length, b.length);
+ if (sizeCompare != 0) {
+ return sizeCompare;
+ }
+ for (int i = 0; i < a.length; i++) {
+ int byteCompare = compareTo(a, b);
+ if (byteCompare != 0) {
+ return byteCompare;
+ }
+ }
+ return 0;
+ }
+
+ public static int compareTo(Object a, Object b) {
+ if (a instanceof Vector) {
+ return compareTo((Vector)a, (Vector)b);
+ } if (a instanceof Hashtable) {
+ return compareTo((Hashtable)a, (Hashtable)b);
+ } else {
+ return ((TBase)a).compareTo(b);
+ }
+ }
+
+ public static int compareTo(Vector a, Vector b) {
+ int lastComparison = compareTo(a.size(), b.size());
+ if (lastComparison != 0) {
+ return lastComparison;
+ }
+ for (int i = 0; i < a.size(); i++) {
+ Object oA = a.elementAt(i);
+ Object oB = b.elementAt(i);
+ lastComparison = compareTo(oA, oB);
+ if (lastComparison != 0) {
+ return lastComparison;
+ }
+
+ }
+ return 0;
+ }
+
+ public static int compareTo(Hashtable a, Hashtable b) {
+ int lastComparison = compareTo(a.size(), b.size());
+ if (lastComparison != 0) {
+ return lastComparison;
+ }
+ Enumeration enumA = a.keys();
+ Enumeration enumB = b.keys();
+ while (lastComparison == 0 && enumA.hasMoreElements()) {
+ Object keyA = enumA.nextElement();
+ Object keyB = enumB.nextElement();
+ lastComparison = compareTo(keyA, keyB);
+ if (lastComparison == 0) {
+ lastComparison = compareTo(a.get(keyA), b.get(keyB));
+ }
+ }
+ return lastComparison;
+ }
+
+ public static int compareTo(TEnum a, TEnum b) {
+ return compareTo(a.getValue(), b.getValue());
+ }
+
+ /*
+ public static int compareTo(List a, List b) {
+ int lastComparison = compareTo(a.size(), b.size());
+ if (lastComparison != 0) {
+ return lastComparison;
+ }
+ for (int i = 0; i < a.size(); i++) {
+ Object oA = a.get(i);
+ Object oB = b.get(i);
+ if (oA instanceof List) {
+ lastComparison = compareTo((List) oA, (List) oB);
+ } else {
+ lastComparison = compareTo((Comparable) oA, (Comparable) oB);
+ }
+ if (lastComparison != 0) {
+ return lastComparison;
+ }
+ }
+ return 0;
+ }
+ */
+
+ public static void toString(byte[] bytes, StringBuffer sb) {
+ toString(bytes, 0, bytes.length, sb);
+ }
+
+ public static void toString(byte[] buf, int arrayOffset, int origLimit, StringBuffer sb) {
+ int limit = (origLimit - arrayOffset > 128) ? arrayOffset + 128 : origLimit;
+
+ for (int i = arrayOffset; i < limit; i++) {
+ if (i > arrayOffset) {
+ sb.append(" ");
+ }
+ sb.append(paddedByteString(buf[i]));
+ }
+ if (origLimit != limit) {
+ sb.append("...");
+ }
+ }
+
+ public static String paddedByteString(byte b) {
+ int extended = (b | 0x100) & 0x1ff;
+ return Integer.toHexString(extended).toUpperCase().substring(1);
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TByteArrayOutputStream.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TByteArrayOutputStream.java
new file mode 100644
index 000000000..077c2e6f7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TByteArrayOutputStream.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Class that allows access to the underlying buf without doing deep
+ * copies on it.
+ *
+ */
+public class TByteArrayOutputStream extends ByteArrayOutputStream {
+ public TByteArrayOutputStream(int size) {
+ super(size);
+ }
+
+ public byte[] get() {
+ return buf;
+ }
+
+ public int len() {
+ return count;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TDeserializer.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TDeserializer.java
new file mode 100644
index 000000000..8eceae717
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TDeserializer.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.io.ByteArrayInputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TIOStreamTransport;
+
+/**
+ * Generic utility for easily deserializing objects from a byte array or Java
+ * String.
+ *
+ */
+public class TDeserializer {
+ private final TProtocolFactory protocolFactory_;
+
+ /**
+ * Create a new TDeserializer that uses the TBinaryProtocol by default.
+ */
+ public TDeserializer() {
+ this(new TBinaryProtocol.Factory());
+ }
+
+ /**
+ * Create a new TDeserializer. It will use the TProtocol specified by the
+ * factory that is passed in.
+ *
+ * @param protocolFactory Factory to create a protocol
+ */
+ public TDeserializer(TProtocolFactory protocolFactory) {
+ protocolFactory_ = protocolFactory;
+ }
+
+ /**
+ * Deserialize the Thrift object from a byte array.
+ *
+ * @param base The object to read into
+ * @param bytes The array to read from
+ */
+ public void deserialize(TBase base, byte[] bytes) throws TException {
+ base.read(
+ protocolFactory_.getProtocol(
+ new TIOStreamTransport(
+ new ByteArrayInputStream(bytes))));
+ }
+
+ /**
+ * Deserialize the Thrift object from a Java string, using a specified
+ * character set for decoding.
+ *
+ * @param base The object to read into
+ * @param data The string to read from
+ * @param charset Valid JVM charset
+ */
+ public void deserialize(TBase base, String data, String charset) throws TException {
+ try {
+ deserialize(base, data.getBytes(charset));
+ } catch (UnsupportedEncodingException uex) {
+ throw new TException("JVM DOES NOT SUPPORT ENCODING: " + charset);
+ }
+ }
+
+ /**
+ * Deerialize the Thrift object from a Java string, using the default JVM
+ * charset encoding.
+ *
+ * @param base The object to read into
+ * @param data The string to read from
+ * @return Serialized object as a String
+ */
+ public void toString(TBase base, String data) throws TException {
+ deserialize(base, data.getBytes());
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TEnum.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TEnum.java
new file mode 100644
index 000000000..325fdece7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TEnum.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+public interface TEnum {
+ public int getValue();
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TException.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TException.java
new file mode 100644
index 000000000..7f375b813
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TException.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+/**
+ * Generic exception class for Thrift.
+ *
+ */
+public class TException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public TException() {
+ super();
+ }
+
+ public TException(String message) {
+ super(message);
+ }
+
+ public TException(Throwable cause) {
+ super(cause.getMessage());
+ }
+
+ public TException(String message, Throwable cause) {
+ super(message);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TFieldRequirementType.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TFieldRequirementType.java
new file mode 100644
index 000000000..104a391d8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TFieldRequirementType.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+/**
+ * Requirement type constants.
+ *
+ */
+public final class TFieldRequirementType {
+ public static final byte REQUIRED = 1;
+ public static final byte OPTIONAL = 2;
+ public static final byte DEFAULT = 3;
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TProcessor.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TProcessor.java
new file mode 100644
index 000000000..d79522c3e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TProcessor.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TProtocol;
+
+/**
+ * A processor is a generic object which operates upon an input stream and
+ * writes to some output stream.
+ *
+ */
+public interface TProcessor {
+ public boolean process(TProtocol in, TProtocol out)
+ throws TException;
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TProcessorFactory.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TProcessorFactory.java
new file mode 100644
index 000000000..bcd8a38fd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TProcessorFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * The default processor factory just returns a singleton
+ * instance.
+ */
+public class TProcessorFactory {
+
+ private final TProcessor processor_;
+
+ public TProcessorFactory(TProcessor processor) {
+ processor_ = processor;
+ }
+
+ public TProcessor getProcessor(TTransport trans) {
+ return processor_;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TSerializer.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TSerializer.java
new file mode 100644
index 000000000..4e1ce6129
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TSerializer.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TIOStreamTransport;
+
+/**
+ * Generic utility for easily serializing objects into a byte array or Java
+ * String.
+ *
+ */
+public class TSerializer {
+
+ /**
+ * This is the byte array that data is actually serialized into
+ */
+ private final ByteArrayOutputStream baos_ = new ByteArrayOutputStream();
+
+ /**
+ * This transport wraps that byte array
+ */
+ private final TIOStreamTransport transport_ = new TIOStreamTransport(baos_);
+
+ /**
+ * Internal protocol used for serializing objects.
+ */
+ private TProtocol protocol_;
+
+ /**
+ * Create a new TSerializer that uses the TBinaryProtocol by default.
+ */
+ public TSerializer() {
+ this(new TBinaryProtocol.Factory());
+ }
+
+ /**
+ * Create a new TSerializer. It will use the TProtocol specified by the
+ * factory that is passed in.
+ *
+ * @param protocolFactory Factory to create a protocol
+ */
+ public TSerializer(TProtocolFactory protocolFactory) {
+ protocol_ = protocolFactory.getProtocol(transport_);
+ }
+
+ /**
+ * Serialize the Thrift object into a byte array. The process is simple,
+ * just clear the byte array output, write the object into it, and grab the
+ * raw bytes.
+ *
+ * @param base The object to serialize
+ * @return Serialized object in byte[] format
+ */
+ public byte[] serialize(TBase base) throws TException {
+ baos_.reset();
+ base.write(protocol_);
+ return baos_.toByteArray();
+ }
+
+ /**
+ * Serialize the Thrift object into a Java string, using a specified
+ * character set for encoding.
+ *
+ * @param base The object to serialize
+ * @param charset Valid JVM charset
+ * @return Serialized object as a String
+ */
+ public String toString(TBase base, String charset) throws TException {
+ try {
+ return new String(serialize(base), charset);
+ } catch (UnsupportedEncodingException uex) {
+ throw new TException("JVM DOES NOT SUPPORT ENCODING: " + charset);
+ }
+ }
+
+ /**
+ * Serialize the Thrift object into a Java string, using the default JVM
+ * charset encoding.
+ *
+ * @param base The object to serialize
+ * @return Serialized object as a String
+ */
+ public String toString(TBase base) throws TException {
+ return new String(serialize(base));
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TServiceClient.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TServiceClient.java
new file mode 100644
index 000000000..ee07b7821
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TServiceClient.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TProtocol;
+
+/**
+ * A TServiceClient is used to communicate with a TService implementation
+ * across protocols and transports.
+ */
+public interface TServiceClient {
+ /**
+ * Get the TProtocol being used as the input (read) protocol.
+ * @return
+ */
+ public TProtocol getInputProtocol();
+ /**
+ * Get the TProtocol being used as the output (write) protocol.
+ * @return
+ */
+ public TProtocol getOutputProtocol();
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/FieldMetaData.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/FieldMetaData.java
new file mode 100644
index 000000000..bce02c724
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/FieldMetaData.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+import org.apache.thrift.TBase;
+
+import java.util.Hashtable;
+
+
+/**
+ * This class is used to store meta data about thrift fields. Every field in a
+ * a struct should have a corresponding instance of this class describing it.
+ *
+ */
+public class FieldMetaData {
+ public final String fieldName;
+ public final byte requirementType;
+ public final FieldValueMetaData valueMetaData;
+ private static Hashtable structMap;
+
+ static {
+ structMap = new Hashtable();
+ }
+
+ public FieldMetaData(String name, byte req, FieldValueMetaData vMetaData){
+ this.fieldName = name;
+ this.requirementType = req;
+ this.valueMetaData = vMetaData;
+ }
+
+ public static synchronized void addStructMetaDataMap(Class sClass, Hashtable map){
+ structMap.put(sClass, map);
+ }
+
+ /**
+ * Returns a map with metadata (i.e. instances of FieldMetaData) that
+ * describe the fields of the given class.
+ *
+ * @param sClass The TBase class for which the metadata map is requested
+ */
+ public static synchronized Hashtable getStructMetaDataMap(Class sClass){
+ if (!structMap.containsKey(sClass)){ // Load class if it hasn't been loaded
+ try{
+ sClass.newInstance();
+ } catch (InstantiationException e){
+ throw new RuntimeException("InstantiationException for TBase class: " + sClass.getName() + ", message: " + e.getMessage());
+ } catch (IllegalAccessException e){
+ throw new RuntimeException("IllegalAccessException for TBase class: " + sClass.getName() + ", message: " + e.getMessage());
+ }
+ }
+ return (Hashtable) structMap.get(sClass);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/FieldValueMetaData.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/FieldValueMetaData.java
new file mode 100644
index 000000000..4c017b6bf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/FieldValueMetaData.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+import org.apache.thrift.protocol.TType;
+
+
+/**
+ * FieldValueMetaData and collection of subclasses to store metadata about
+ * the value(s) of a field
+ */
+public class FieldValueMetaData {
+ public final byte type;
+
+ public FieldValueMetaData(byte type){
+ this.type = type;
+ }
+
+ public boolean isStruct() {
+ return type == TType.STRUCT;
+ }
+
+ public boolean isContainer() {
+ return type == TType.LIST || type == TType.MAP || type == TType.SET;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/ListMetaData.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/ListMetaData.java
new file mode 100644
index 000000000..5cd719423
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/ListMetaData.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+import org.apache.thrift.meta_data.FieldValueMetaData;
+
+public class ListMetaData extends FieldValueMetaData {
+ public final FieldValueMetaData elemMetaData;
+
+ public ListMetaData(byte type, FieldValueMetaData eMetaData){
+ super(type);
+ this.elemMetaData = eMetaData;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/MapMetaData.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/MapMetaData.java
new file mode 100644
index 000000000..0d1d6f659
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/MapMetaData.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+import org.apache.thrift.meta_data.FieldValueMetaData;
+
+public class MapMetaData extends FieldValueMetaData {
+ public final FieldValueMetaData keyMetaData;
+ public final FieldValueMetaData valueMetaData;
+
+ public MapMetaData(byte type, FieldValueMetaData kMetaData, FieldValueMetaData vMetaData){
+ super(type);
+ this.keyMetaData = kMetaData;
+ this.valueMetaData = vMetaData;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/SetMetaData.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/SetMetaData.java
new file mode 100644
index 000000000..3ec6868a1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/SetMetaData.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+public class SetMetaData extends FieldValueMetaData {
+ public final FieldValueMetaData elemMetaData;
+
+ public SetMetaData(byte type, FieldValueMetaData eMetaData){
+ super(type);
+ this.elemMetaData = eMetaData;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/StructMetaData.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/StructMetaData.java
new file mode 100644
index 000000000..e30d90814
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/StructMetaData.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+import org.apache.thrift.TBase;
+import org.apache.thrift.meta_data.FieldValueMetaData;
+
+
+public class StructMetaData extends FieldValueMetaData {
+ public final Class structClass;
+
+ public StructMetaData(byte type, Class sClass){
+ super(type);
+ this.structClass = sClass;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TBase64Utils.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TBase64Utils.java
new file mode 100644
index 000000000..abfc965b7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TBase64Utils.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Class for encoding and decoding Base64 data.
+ *
+ * This class is kept at package level because the interface does no input
+ * validation and is therefore too low-level for generalized reuse.
+ *
+ * Note also that the encoding does not pad with equal signs , as discussed in
+ * section 2.2 of the RFC (http://www.faqs.org/rfcs/rfc3548.html). Furthermore,
+ * bad data encountered when decoding is neither rejected or ignored but simply
+ * results in bad decoded data -- this is not in compliance with the RFC but is
+ * done in the interest of performance.
+ *
+ */
+class TBase64Utils {
+
+ private static final String ENCODE_TABLE =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ /**
+ * Encode len bytes of data in src at offset srcOff, storing the result into
+ * dst at offset dstOff. len must be 1, 2, or 3. dst must have at least len+1
+ * bytes of space at dstOff. src and dst should not be the same object. This
+ * method does no validation of the input values in the interest of
+ * performance.
+ *
+ * @param src the source of bytes to encode
+ * @param srcOff the offset into the source to read the unencoded bytes
+ * @param len the number of bytes to encode (must be 1, 2, or 3).
+ * @param dst the destination for the encoding
+ * @param dstOff the offset into the destination to place the encoded bytes
+ */
+ static final void encode(byte[] src, int srcOff, int len, byte[] dst,
+ int dstOff) {
+ dst[dstOff] = (byte)ENCODE_TABLE.charAt((src[srcOff] >> 2) & 0x3F);
+ if (len == 3) {
+ dst[dstOff + 1] =
+ (byte)ENCODE_TABLE.charAt(
+ ((src[srcOff] << 4) & 0x30) | ((src[srcOff+1] >> 4) & 0x0F));
+ dst[dstOff + 2] =
+ (byte)ENCODE_TABLE.charAt(
+ ((src[srcOff+1] << 2) & 0x3C) | ((src[srcOff+2] >> 6) & 0x03));
+ dst[dstOff + 3] =
+ (byte)ENCODE_TABLE.charAt(src[srcOff+2] & 0x3F);
+ }
+ else if (len == 2) {
+ dst[dstOff+1] =
+ (byte)ENCODE_TABLE.charAt(
+ ((src[srcOff] << 4) & 0x30) | ((src[srcOff+1] >> 4) & 0x0F));
+ dst[dstOff + 2] =
+ (byte)ENCODE_TABLE.charAt((src[srcOff+1] << 2) & 0x3C);
+ }
+ else { // len == 1) {
+ dst[dstOff + 1] =
+ (byte)ENCODE_TABLE.charAt((src[srcOff] << 4) & 0x30);
+ }
+ }
+
+ private static final byte[] DECODE_TABLE = {
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
+ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
+ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
+ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
+ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ };
+
+ /**
+ * Decode len bytes of data in src at offset srcOff, storing the result into
+ * dst at offset dstOff. len must be 2, 3, or 4. dst must have at least len-1
+ * bytes of space at dstOff. src and dst may be the same object as long as
+ * dstoff <= srcOff. This method does no validation of the input values in
+ * the interest of performance.
+ *
+ * @param src the source of bytes to decode
+ * @param srcOff the offset into the source to read the encoded bytes
+ * @param len the number of bytes to decode (must be 2, 3, or 4)
+ * @param dst the destination for the decoding
+ * @param dstOff the offset into the destination to place the decoded bytes
+ */
+ static final void decode(byte[] src, int srcOff, int len, byte[] dst,
+ int dstOff) {
+ dst[dstOff] = (byte)
+ ((DECODE_TABLE[src[srcOff] & 0x0FF] << 2) |
+ (DECODE_TABLE[src[srcOff+1] & 0x0FF] >> 4));
+ if (len > 2) {
+ dst[dstOff+1] = (byte)
+ (((DECODE_TABLE[src[srcOff+1] & 0x0FF] << 4) & 0xF0) |
+ (DECODE_TABLE[src[srcOff+2] & 0x0FF] >> 2));
+ if (len > 3) {
+ dst[dstOff+2] = (byte)
+ (((DECODE_TABLE[src[srcOff+2] & 0x0FF] << 6) & 0xC0) |
+ DECODE_TABLE[src[srcOff+3] & 0x0FF]);
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TBinaryProtocol.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TBinaryProtocol.java
new file mode 100644
index 000000000..f6fe76543
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TBinaryProtocol.java
@@ -0,0 +1,329 @@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import java.io.UnsupportedEncodingException;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * Binary protocol implementation for thrift.
+ *
+ */
+public class TBinaryProtocol extends TProtocol {
+
+ protected static final int VERSION_MASK = 0xffff0000;
+ protected static final int VERSION_1 = 0x80010000;
+
+ protected boolean strictRead_ = false;
+ protected boolean strictWrite_ = true;
+
+ /**
+ * Factory
+ */
+ public static class Factory implements TProtocolFactory {
+ protected boolean strictRead_ = false;
+ protected boolean strictWrite_ = true;
+
+ public Factory() {
+ this(false, true);
+ }
+
+ public Factory(boolean strictRead, boolean strictWrite) {
+ strictRead_ = strictRead;
+ strictWrite_ = strictWrite;
+ }
+
+ public TProtocol getProtocol(TTransport trans) {
+ return new TBinaryProtocol(trans, strictRead_, strictWrite_);
+ }
+ }
+
+ /**
+ * Constructor
+ */
+ public TBinaryProtocol(TTransport trans) {
+ this(trans, false, true);
+ }
+
+ public TBinaryProtocol(TTransport trans, boolean strictRead, boolean strictWrite) {
+ super(trans);
+ strictRead_ = strictRead;
+ strictWrite_ = strictWrite;
+ }
+
+ public void writeMessageBegin(TMessage message) throws TException {
+ if (strictWrite_) {
+ int version = VERSION_1 | message.type;
+ writeI32(version);
+ writeString(message.name);
+ writeI32(message.seqid);
+ } else {
+ writeString(message.name);
+ writeByte(message.type);
+ writeI32(message.seqid);
+ }
+ }
+
+ public void writeMessageEnd() {}
+
+ public void writeStructBegin(TStruct struct) {}
+
+ public void writeStructEnd() {}
+
+ public void writeFieldBegin(TField field) throws TException {
+ writeByte(field.type);
+ writeI16(field.id);
+ }
+
+ public void writeFieldEnd() {}
+
+ public void writeFieldStop() throws TException {
+ writeByte(TType.STOP);
+ }
+
+ public void writeMapBegin(TMap map) throws TException {
+ writeByte(map.keyType);
+ writeByte(map.valueType);
+ writeI32(map.size);
+ }
+
+ public void writeMapEnd() {}
+
+ public void writeListBegin(TList list) throws TException {
+ writeByte(list.elemType);
+ writeI32(list.size);
+ }
+
+ public void writeListEnd() {}
+
+ public void writeSetBegin(TSet set) throws TException {
+ writeByte(set.elemType);
+ writeI32(set.size);
+ }
+
+ public void writeSetEnd() {}
+
+ public void writeBool(boolean b) throws TException {
+ writeByte(b ? (byte)1 : (byte)0);
+ }
+
+ private byte [] bout = new byte[1];
+ public void writeByte(byte b) throws TException {
+ bout[0] = b;
+ trans_.write(bout, 0, 1);
+ }
+
+ private byte[] i16out = new byte[2];
+ public void writeI16(short i16) throws TException {
+ i16out[0] = (byte)(0xff & (i16 >> 8));
+ i16out[1] = (byte)(0xff & (i16));
+ trans_.write(i16out, 0, 2);
+ }
+
+ private byte[] i32out = new byte[4];
+ public void writeI32(int i32) throws TException {
+ i32out[0] = (byte)(0xff & (i32 >> 24));
+ i32out[1] = (byte)(0xff & (i32 >> 16));
+ i32out[2] = (byte)(0xff & (i32 >> 8));
+ i32out[3] = (byte)(0xff & (i32));
+ trans_.write(i32out, 0, 4);
+ }
+
+ private byte[] i64out = new byte[8];
+ public void writeI64(long i64) throws TException {
+ i64out[0] = (byte)(0xff & (i64 >> 56));
+ i64out[1] = (byte)(0xff & (i64 >> 48));
+ i64out[2] = (byte)(0xff & (i64 >> 40));
+ i64out[3] = (byte)(0xff & (i64 >> 32));
+ i64out[4] = (byte)(0xff & (i64 >> 24));
+ i64out[5] = (byte)(0xff & (i64 >> 16));
+ i64out[6] = (byte)(0xff & (i64 >> 8));
+ i64out[7] = (byte)(0xff & (i64));
+ trans_.write(i64out, 0, 8);
+ }
+
+ public void writeDouble(double dub) throws TException {
+ writeI64(Double.doubleToLongBits(dub));
+ }
+
+ public void writeString(String str) throws TException {
+ try {
+ byte[] dat = str.getBytes("UTF-8");
+ writeI32(dat.length);
+ trans_.write(dat, 0, dat.length);
+ } catch (UnsupportedEncodingException uex) {
+ throw new TException("JVM DOES NOT SUPPORT UTF-8");
+ }
+ }
+
+ public void writeBinary(byte[] bin) throws TException {
+ writeI32(bin.length);
+ trans_.write(bin, 0, bin.length);
+ }
+
+ /**
+ * Reading methods.
+ */
+
+ public TMessage readMessageBegin() throws TException {
+ TMessage message = new TMessage();
+
+ int size = readI32();
+ if (size < 0) {
+ int version = size & VERSION_MASK;
+ if (version != VERSION_1) {
+ throw new TProtocolException(TProtocolException.BAD_VERSION, "Bad version in readMessageBegin");
+ }
+ message.type = (byte)(size & 0x000000ff);
+ message.name = readString();
+ message.seqid = readI32();
+ } else {
+ if (strictRead_) {
+ throw new TProtocolException(TProtocolException.BAD_VERSION, "Missing version in readMessageBegin, old client?");
+ }
+ message.name = readStringBody(size);
+ message.type = readByte();
+ message.seqid = readI32();
+ }
+ return message;
+ }
+
+ public void readMessageEnd() {}
+
+ public TStruct readStructBegin() {
+ return new TStruct();
+ }
+
+ public void readStructEnd() {}
+
+ public TField readFieldBegin() throws TException {
+ TField field = new TField();
+ field.type = readByte();
+ if (field.type != TType.STOP) {
+ field.id = readI16();
+ }
+ return field;
+ }
+
+ public void readFieldEnd() {}
+
+ public TMap readMapBegin() throws TException {
+ TMap map = new TMap();
+ map.keyType = readByte();
+ map.valueType = readByte();
+ map.size = readI32();
+ return map;
+ }
+
+ public void readMapEnd() {}
+
+ public TList readListBegin() throws TException {
+ TList list = new TList();
+ list.elemType = readByte();
+ list.size = readI32();
+ return list;
+ }
+
+ public void readListEnd() {}
+
+ public TSet readSetBegin() throws TException {
+ TSet set = new TSet();
+ set.elemType = readByte();
+ set.size = readI32();
+ return set;
+ }
+
+ public void readSetEnd() {}
+
+ public boolean readBool() throws TException {
+ return (readByte() == 1);
+ }
+
+ private byte[] bin = new byte[1];
+ public byte readByte() throws TException {
+ readAll(bin, 0, 1);
+ return bin[0];
+ }
+
+ private byte[] i16rd = new byte[2];
+ public short readI16() throws TException {
+ readAll(i16rd, 0, 2);
+ return
+ (short)
+ (((i16rd[0] & 0xff) << 8) |
+ ((i16rd[1] & 0xff)));
+ }
+
+ private byte[] i32rd = new byte[4];
+ public int readI32() throws TException {
+ readAll(i32rd, 0, 4);
+ return
+ ((i32rd[0] & 0xff) << 24) |
+ ((i32rd[1] & 0xff) << 16) |
+ ((i32rd[2] & 0xff) << 8) |
+ ((i32rd[3] & 0xff));
+ }
+
+ private byte[] i64rd = new byte[8];
+ public long readI64() throws TException {
+ readAll(i64rd, 0, 8);
+ return
+ ((long)(i64rd[0] & 0xff) << 56) |
+ ((long)(i64rd[1] & 0xff) << 48) |
+ ((long)(i64rd[2] & 0xff) << 40) |
+ ((long)(i64rd[3] & 0xff) << 32) |
+ ((long)(i64rd[4] & 0xff) << 24) |
+ ((long)(i64rd[5] & 0xff) << 16) |
+ ((long)(i64rd[6] & 0xff) << 8) |
+ ((long)(i64rd[7] & 0xff));
+ }
+
+ public double readDouble() throws TException {
+ return Double.longBitsToDouble(readI64());
+ }
+
+ public String readString() throws TException {
+ int size = readI32();
+ return readStringBody(size);
+ }
+
+ public String readStringBody(int size) throws TException {
+ try {
+ byte[] buf = new byte[size];
+ trans_.readAll(buf, 0, size);
+ return new String(buf, "UTF-8");
+ } catch (UnsupportedEncodingException uex) {
+ throw new TException("JVM DOES NOT SUPPORT UTF-8");
+ }
+ }
+
+ public byte[] readBinary() throws TException {
+ int size = readI32();
+ byte[] buf = new byte[size];
+ trans_.readAll(buf, 0, size);
+ return buf;
+ }
+
+ private int readAll(byte[] buf, int off, int len) throws TException {
+ return trans_.readAll(buf, off, len);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TField.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TField.java
new file mode 100644
index 000000000..061140ff8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TField.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates field metadata.
+ *
+ */
+public class TField {
+ public TField() {}
+
+ public TField(String n, byte t, short i) {
+ name = n;
+ type = t;
+ id = i;
+ }
+
+ public String name = "";
+ public byte type = TType.STOP;
+ public short id = 0;
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TJSONProtocol.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TJSONProtocol.java
new file mode 100644
index 000000000..d3916863b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TJSONProtocol.java
@@ -0,0 +1,973 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Stack;
+
+import org.apache.thrift.TByteArrayOutputStream;
+import org.apache.thrift.TException;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * JSON protocol implementation for thrift.
+ * This is a full-featured protocol supporting write and read.
+ * Please see the C++ class header for a detailed description of the
+ * protocol's wire format.
+ */
+public class TJSONProtocol extends TProtocol {
+
+ /**
+ * Factory for JSON protocol objects
+ */
+ public static class Factory implements TProtocolFactory {
+
+ public TProtocol getProtocol(TTransport trans) {
+ return new TJSONProtocol(trans);
+ }
+
+ }
+
+ private static final byte[] COMMA = new byte[] { ',' };
+ private static final byte[] COLON = new byte[] { ':' };
+ private static final byte[] LBRACE = new byte[] { '{' };
+ private static final byte[] RBRACE = new byte[] { '}' };
+ private static final byte[] LBRACKET = new byte[] { '[' };
+ private static final byte[] RBRACKET = new byte[] { ']' };
+ private static final byte[] QUOTE = new byte[] { '"' };
+ private static final byte[] BACKSLASH = new byte[] { '\\' };
+ private static final byte[] ZERO = new byte[] { '0' };
+
+ private static final byte[] ESCSEQ = new byte[] { '\\', 'u', '0', '0' };
+
+ private static final long VERSION = 1;
+
+ private static final byte[] JSON_CHAR_TABLE = {
+ /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
+ 1, 1, '"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
+ };
+
+ private static final String ESCAPE_CHARS = "\"\\/bfnrt";
+
+ private static final byte[] ESCAPE_CHAR_VALS = {
+ '"', '\\', '/', '\b', '\f', '\n', '\r', '\t',
+ };
+
+ private static final int DEF_STRING_SIZE = 16;
+
+ private static final byte[] NAME_BOOL = new byte[] { 't', 'f' };
+ private static final byte[] NAME_BYTE = new byte[] { 'i', '8' };
+ private static final byte[] NAME_I16 = new byte[] { 'i', '1', '6' };
+ private static final byte[] NAME_I32 = new byte[] { 'i', '3', '2' };
+ private static final byte[] NAME_I64 = new byte[] { 'i', '6', '4' };
+ private static final byte[] NAME_DOUBLE = new byte[] { 'd', 'b', 'l' };
+ private static final byte[] NAME_STRUCT = new byte[] { 'r', 'e', 'c' };
+ private static final byte[] NAME_STRING = new byte[] { 's', 't', 'r' };
+ private static final byte[] NAME_MAP = new byte[] { 'm', 'a', 'p' };
+ private static final byte[] NAME_LIST = new byte[] { 'l', 's', 't' };
+ private static final byte[] NAME_SET = new byte[] { 's', 'e', 't' };
+
+ private static final TStruct ANONYMOUS_STRUCT = new TStruct();
+
+ private static final byte[] getTypeNameForTypeID(byte typeID)
+ throws TException {
+ switch (typeID) {
+ case TType.BOOL:
+ return NAME_BOOL;
+ case TType.BYTE:
+ return NAME_BYTE;
+ case TType.I16:
+ return NAME_I16;
+ case TType.I32:
+ return NAME_I32;
+ case TType.I64:
+ return NAME_I64;
+ case TType.DOUBLE:
+ return NAME_DOUBLE;
+ case TType.STRING:
+ return NAME_STRING;
+ case TType.STRUCT:
+ return NAME_STRUCT;
+ case TType.MAP:
+ return NAME_MAP;
+ case TType.SET:
+ return NAME_SET;
+ case TType.LIST:
+ return NAME_LIST;
+ default:
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
+ "Unrecognized type");
+ }
+ }
+
+ private static final byte getTypeIDForTypeName(byte[] name)
+ throws TException {
+ byte result = TType.STOP;
+ if (name.length > 1) {
+ switch (name[0]) {
+ case 'd':
+ result = TType.DOUBLE;
+ break;
+ case 'i':
+ switch (name[1]) {
+ case '8':
+ result = TType.BYTE;
+ break;
+ case '1':
+ result = TType.I16;
+ break;
+ case '3':
+ result = TType.I32;
+ break;
+ case '6':
+ result = TType.I64;
+ break;
+ }
+ break;
+ case 'l':
+ result = TType.LIST;
+ break;
+ case 'm':
+ result = TType.MAP;
+ break;
+ case 'r':
+ result = TType.STRUCT;
+ break;
+ case 's':
+ if (name[1] == 't') {
+ result = TType.STRING;
+ }
+ else if (name[1] == 'e') {
+ result = TType.SET;
+ }
+ break;
+ case 't':
+ result = TType.BOOL;
+ break;
+ }
+ }
+ if (result == TType.STOP) {
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
+ "Unrecognized type");
+ }
+ return result;
+ }
+
+ // Base class for tracking JSON contexts that may require inserting/reading
+ // additional JSON syntax characters
+ // This base context does nothing.
+ protected class JSONBaseContext {
+ /**
+ * @throws TException
+ */
+ protected void write() throws TException {}
+
+ /**
+ * @throws TException
+ */
+ protected void read() throws TException {}
+
+ protected boolean escapeNum() {
+ return false;
+ }
+ }
+
+ // Context for JSON lists. Will insert/read commas before each item except
+ // for the first one
+ protected class JSONListContext extends JSONBaseContext {
+ private boolean first_ = true;
+
+ protected void write() throws TException {
+ if (first_) {
+ first_ = false;
+ } else {
+ trans_.write(COMMA);
+ }
+ }
+
+ protected void read() throws TException {
+ if (first_) {
+ first_ = false;
+ } else {
+ readJSONSyntaxChar(COMMA);
+ }
+ }
+ }
+
+ // Context for JSON records. Will insert/read colons before the value portion
+ // of each record pair, and commas before each key except the first. In
+ // addition, will indicate that numbers in the key position need to be
+ // escaped in quotes (since JSON keys must be strings).
+ protected class JSONPairContext extends JSONBaseContext {
+ private boolean first_ = true;
+ private boolean colon_ = true;
+
+ protected void write() throws TException {
+ if (first_) {
+ first_ = false;
+ colon_ = true;
+ } else {
+ trans_.write(colon_ ? COLON : COMMA);
+ colon_ = !colon_;
+ }
+ }
+
+ protected void read() throws TException {
+ if (first_) {
+ first_ = false;
+ colon_ = true;
+ } else {
+ readJSONSyntaxChar(colon_ ? COLON : COMMA);
+ colon_ = !colon_;
+ }
+ }
+
+ protected boolean escapeNum() {
+ return colon_;
+ }
+ }
+
+ // Holds up to one byte from the transport
+ protected class LookaheadReader {
+
+ private boolean hasData_;
+ private byte[] data_ = new byte[1];
+
+ // Return and consume the next byte to be read, either taking it from the
+ // data buffer if present or getting it from the transport otherwise.
+ protected byte read() throws TException {
+ if (hasData_) {
+ hasData_ = false;
+ }
+ else {
+ trans_.readAll(data_, 0, 1);
+ }
+ return data_[0];
+ }
+
+ // Return the next byte to be read without consuming, filling the data
+ // buffer if it has not been filled already.
+ protected byte peek() throws TException {
+ if (!hasData_) {
+ trans_.readAll(data_, 0, 1);
+ }
+ hasData_ = true;
+ return data_[0];
+ }
+ }
+
+ // Stack of nested contexts of type JSONBaseContext that we may be in
+ private Stack contextStack_ = new Stack();
+
+ // Current context that we are in
+ private JSONBaseContext context_ = new JSONBaseContext();
+
+ // Reader that manages a 1-byte buffer
+ private LookaheadReader reader_ = new LookaheadReader();
+
+ // Push a new JSON context onto the stack.
+ private void pushContext(JSONBaseContext c) {
+ contextStack_.push(context_);
+ context_ = c;
+ }
+
+ // Pop the last JSON context off the stack
+ private void popContext() {
+ context_ = (JSONBaseContext)contextStack_.pop();
+ }
+
+ /**
+ * Constructor
+ */
+ public TJSONProtocol(TTransport trans) {
+ super(trans);
+ }
+
+ public void reset() {
+ contextStack_.clear();
+ context_ = new JSONBaseContext();
+ reader_ = new LookaheadReader();
+ }
+
+ // Temporary buffer used by several methods
+ private byte[] tmpbuf_ = new byte[4];
+
+ // Read a byte that must match b[0]; otherwise an exception is thrown.
+ // Marked protected to avoid synthetic accessor in JSONListContext.read
+ // and JSONPairContext.read
+ protected void readJSONSyntaxChar(byte[] b) throws TException {
+ byte ch = reader_.read();
+ if (ch != b[0]) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Unexpected character:" + (char)ch);
+ }
+ }
+
+ // Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its
+ // corresponding hex value
+ private static final byte hexVal(byte ch) throws TException {
+ if ((ch >= '0') && (ch <= '9')) {
+ return (byte)((char)ch - '0');
+ }
+ else if ((ch >= 'a') && (ch <= 'f')) {
+ return (byte)((char)ch - 'a' + 10);
+ }
+ else {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected hex character");
+ }
+ }
+
+ // Convert a byte containing a hex value to its corresponding hex character
+ private static final byte hexChar(byte val) {
+ val &= 0x0F;
+ if (val < 10) {
+ return (byte)((char)val + '0');
+ }
+ else {
+ return (byte)((char)(val - 10) + 'a');
+ }
+ }
+
+ private static boolean isHighSurrogate(char c) {
+ return c >= '\uD800' && c <= '\uDBFF';
+ }
+
+ private static boolean isLowSurrogate(char c) {
+ return c >= '\uDC00' && c <= '\uDFFF';
+ }
+
+ private static byte[] toUTF8(int codepoint) {
+ final int[] FIRST_BYTE_MASK = { 0, 0xc0, 0xe0, 0xf0 };
+ int length = 0;
+ if (codepoint <= 0x7f) length = 1;
+ else if (codepoint <= 0x7ff) length = 2;
+ else if (codepoint <= 0xffff) length = 3;
+ else if (codepoint <= 0x1fffff) length = 4;
+ else throw new RuntimeException("Code point over U+1FFFFF is not supported");
+
+ byte[] bytes = new byte[length];
+ switch (length) {
+ case 4:
+ bytes[3] = (byte)((codepoint & 0x3f) | 0x80);
+ codepoint >>= 6;
+ case 3:
+ bytes[2] = (byte)((codepoint & 0x3f) | 0x80);
+ codepoint >>= 6;
+ case 2:
+ bytes[1] = (byte)((codepoint & 0x3f) | 0x80);
+ codepoint >>= 6;
+ case 1:
+ bytes[0] = (byte)(codepoint | FIRST_BYTE_MASK[length - 1]);
+ }
+
+ return bytes;
+ }
+
+ private static byte[] toUTF8(int high, int low) {
+ int codepoint = (1 << 16) + ((high & 0x3ff) << 10);
+ codepoint += low & 0x3ff;
+ return toUTF8(codepoint);
+ }
+
+ // Write the bytes in array buf as a JSON characters, escaping as needed
+ private void writeJSONString(byte[] b) throws TException {
+ context_.write();
+ trans_.write(QUOTE);
+ int len = b.length;
+ for (int i = 0; i < len; i++) {
+ if ((b[i] & 0x00FF) >= 0x30) {
+ if (b[i] == BACKSLASH[0]) {
+ trans_.write(BACKSLASH);
+ trans_.write(BACKSLASH);
+ }
+ else {
+ trans_.write(b, i, 1);
+ }
+ }
+ else {
+ tmpbuf_[0] = JSON_CHAR_TABLE[b[i]];
+ if (tmpbuf_[0] == 1) {
+ trans_.write(b, i, 1);
+ }
+ else if (tmpbuf_[0] > 1) {
+ trans_.write(BACKSLASH);
+ trans_.write(tmpbuf_, 0, 1);
+ }
+ else {
+ trans_.write(ESCSEQ);
+ tmpbuf_[0] = hexChar((byte)(b[i] >> 4));
+ tmpbuf_[1] = hexChar(b[i]);
+ trans_.write(tmpbuf_, 0, 2);
+ }
+ }
+ }
+ trans_.write(QUOTE);
+ }
+
+ // Write out number as a JSON value. If the context dictates so, it will be
+ // wrapped in quotes to output as a JSON string.
+ private void writeJSONInteger(long num) throws TException {
+ context_.write();
+ String str = Long.toString(num);
+ boolean escapeNum = context_.escapeNum();
+ if (escapeNum) {
+ trans_.write(QUOTE);
+ }
+ try {
+ byte[] buf = str.getBytes("UTF-8");
+ trans_.write(buf);
+ } catch (UnsupportedEncodingException uex) {
+ throw new TException("JVM DOES NOT SUPPORT UTF-8");
+ }
+ if (escapeNum) {
+ trans_.write(QUOTE);
+ }
+ }
+
+ // Write out a double as a JSON value. If it is NaN or infinity or if the
+ // context dictates escaping, write out as JSON string.
+ private void writeJSONDouble(double num) throws TException {
+ context_.write();
+ String str = Double.toString(num);
+ boolean special = false;
+ switch (str.charAt(0)) {
+ case 'N': // NaN
+ case 'I': // Infinity
+ special = true;
+ break;
+ case '-':
+ if (str.charAt(1) == 'I') { // -Infinity
+ special = true;
+ }
+ break;
+ }
+
+ boolean escapeNum = special || context_.escapeNum();
+ if (escapeNum) {
+ trans_.write(QUOTE);
+ }
+ try {
+ byte[] b = str.getBytes("UTF-8");
+ trans_.write(b, 0, b.length);
+ } catch (UnsupportedEncodingException uex) {
+ throw new TException("JVM DOES NOT SUPPORT UTF-8");
+ }
+ if (escapeNum) {
+ trans_.write(QUOTE);
+ }
+ }
+
+ // Write out contents of byte array b as a JSON string with base-64 encoded
+ // data
+ private void writeJSONBase64(byte[] b, int offset, int length) throws TException {
+ context_.write();
+ trans_.write(QUOTE);
+ int len = length;
+ int off = offset;
+ while (len >= 3) {
+ // Encode 3 bytes at a time
+ TBase64Utils.encode(b, off, 3, tmpbuf_, 0);
+ trans_.write(tmpbuf_, 0, 4);
+ off += 3;
+ len -= 3;
+ }
+ if (len > 0) {
+ // Encode remainder
+ TBase64Utils.encode(b, off, len, tmpbuf_, 0);
+ trans_.write(tmpbuf_, 0, len + 1);
+ }
+ trans_.write(QUOTE);
+ }
+
+ private void writeJSONObjectStart() throws TException {
+ context_.write();
+ trans_.write(LBRACE);
+ pushContext(new JSONPairContext());
+ }
+
+ private void writeJSONObjectEnd() throws TException {
+ popContext();
+ trans_.write(RBRACE);
+ }
+
+ private void writeJSONArrayStart() throws TException {
+ context_.write();
+ trans_.write(LBRACKET);
+ pushContext(new JSONListContext());
+ }
+
+ private void writeJSONArrayEnd() throws TException {
+ popContext();
+ trans_.write(RBRACKET);
+ }
+
+ public void writeMessageBegin(TMessage message) throws TException {
+ writeJSONArrayStart();
+ writeJSONInteger(VERSION);
+ try {
+ byte[] b = message.name.getBytes("UTF-8");
+ writeJSONString(b);
+ } catch (UnsupportedEncodingException uex) {
+ throw new TException("JVM DOES NOT SUPPORT UTF-8");
+ }
+ writeJSONInteger(message.type);
+ writeJSONInteger(message.seqid);
+ }
+
+ public void writeMessageEnd() throws TException {
+ writeJSONArrayEnd();
+ }
+
+ public void writeStructBegin(TStruct struct) throws TException {
+ writeJSONObjectStart();
+ }
+
+ public void writeStructEnd() throws TException {
+ writeJSONObjectEnd();
+ }
+
+ public void writeFieldBegin(TField field) throws TException {
+ writeJSONInteger(field.id);
+ writeJSONObjectStart();
+ writeJSONString(getTypeNameForTypeID(field.type));
+ }
+
+ public void writeFieldEnd() throws TException {
+ writeJSONObjectEnd();
+ }
+
+ public void writeFieldStop() {}
+
+ public void writeMapBegin(TMap map) throws TException {
+ writeJSONArrayStart();
+ writeJSONString(getTypeNameForTypeID(map.keyType));
+ writeJSONString(getTypeNameForTypeID(map.valueType));
+ writeJSONInteger(map.size);
+ writeJSONObjectStart();
+ }
+
+ public void writeMapEnd() throws TException {
+ writeJSONObjectEnd();
+ writeJSONArrayEnd();
+ }
+
+ public void writeListBegin(TList list) throws TException {
+ writeJSONArrayStart();
+ writeJSONString(getTypeNameForTypeID(list.elemType));
+ writeJSONInteger(list.size);
+ }
+
+ public void writeListEnd() throws TException {
+ writeJSONArrayEnd();
+ }
+
+ public void writeSetBegin(TSet set) throws TException {
+ writeJSONArrayStart();
+ writeJSONString(getTypeNameForTypeID(set.elemType));
+ writeJSONInteger(set.size);
+ }
+
+ public void writeSetEnd() throws TException {
+ writeJSONArrayEnd();
+ }
+
+ public void writeBool(boolean b) throws TException {
+ writeJSONInteger(b ? (long)1 : (long)0);
+ }
+
+ public void writeByte(byte b) throws TException {
+ writeJSONInteger(b);
+ }
+
+ public void writeI16(short i16) throws TException {
+ writeJSONInteger(i16);
+ }
+
+ public void writeI32(int i32) throws TException {
+ writeJSONInteger(i32);
+ }
+
+ public void writeI64(long i64) throws TException {
+ writeJSONInteger(i64);
+ }
+
+ public void writeDouble(double dub) throws TException {
+ writeJSONDouble(dub);
+ }
+
+ public void writeString(String str) throws TException {
+ try {
+ byte[] b = str.getBytes("UTF-8");
+ writeJSONString(b);
+ } catch (UnsupportedEncodingException uex) {
+ throw new TException("JVM DOES NOT SUPPORT UTF-8");
+ }
+ }
+
+ public void writeBinary(byte[] bin) throws TException {
+ writeJSONBase64(bin, 0, bin.length);
+ }
+
+ /**
+ * Reading methods.
+ */
+
+ // Read in a JSON string, unescaping as appropriate.. Skip reading from the
+ // context if skipContext is true.
+ private TByteArrayOutputStream readJSONString(boolean skipContext)
+ throws TException {
+ TByteArrayOutputStream arr = new TByteArrayOutputStream(DEF_STRING_SIZE);
+ int highSurrogate = 0;
+ if (!skipContext) {
+ context_.read();
+ }
+ readJSONSyntaxChar(QUOTE);
+ while (true) {
+ byte ch = reader_.read();
+ if (ch == QUOTE[0]) {
+ break;
+ }
+ if (ch == ESCSEQ[0]) {
+ ch = reader_.read();
+ if (ch == ESCSEQ[1]) {
+ trans_.readAll(tmpbuf_, 0, 4);
+ short cu = (short)(
+ ((short)hexVal(tmpbuf_[0]) << 12) +
+ ((short)hexVal(tmpbuf_[1]) << 8) +
+ ((short)hexVal(tmpbuf_[2]) << 4) +
+ (short)hexVal(tmpbuf_[3]));
+ try {
+ if (isHighSurrogate((char)cu)) {
+ if (highSurrogate != 0) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected low surrogate char");
+ }
+ highSurrogate = cu;
+ }
+ else if (isLowSurrogate((char)cu)) {
+ if (highSurrogate == 0) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected high surrogate char");
+ }
+
+ arr.write(toUTF8(highSurrogate, cu));
+ highSurrogate = 0;
+ }
+ else {
+ arr.write(toUTF8(cu));
+ }
+ continue;
+ }
+ catch (UnsupportedEncodingException ex) {
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
+ "JVM does not support UTF-8");
+ }
+ catch (IOException ex) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Invalid unicode sequence");
+ }
+ }
+ else {
+ int off = ESCAPE_CHARS.indexOf(ch);
+ if (off == -1) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected control char");
+ }
+ ch = ESCAPE_CHAR_VALS[off];
+ }
+ }
+ arr.write(ch);
+ }
+
+ if (highSurrogate != 0) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected low surrogate char");
+ }
+ return arr;
+ }
+
+ // Return true if the given byte could be a valid part of a JSON number.
+ private boolean isJSONNumeric(byte b) {
+ switch (b) {
+ case '+':
+ case '-':
+ case '.':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'E':
+ case 'e':
+ return true;
+ }
+ return false;
+ }
+
+ // Read in a sequence of characters that are all valid in JSON numbers. Does
+ // not do a complete regex check to validate that this is actually a number.
+ private String readJSONNumericChars() throws TException {
+ StringBuffer strbuf = new StringBuffer();
+ while (true) {
+ byte ch = reader_.peek();
+ if (!isJSONNumeric(ch)) {
+ break;
+ }
+ strbuf.append((char)reader_.read());
+ }
+ return strbuf.toString();
+ }
+
+ // Read in a JSON number. If the context dictates, read in enclosing quotes.
+ private long readJSONInteger() throws TException {
+ context_.read();
+ if (context_.escapeNum()) {
+ readJSONSyntaxChar(QUOTE);
+ }
+ String str = readJSONNumericChars();
+ if (context_.escapeNum()) {
+ readJSONSyntaxChar(QUOTE);
+ }
+ try {
+ return Long.valueOf(str).longValue();
+ } catch (NumberFormatException ex) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Bad data encounted in numeric data");
+ }
+ }
+
+ // Read in a JSON double value. Throw if the value is not wrapped in quotes
+ // when expected or if wrapped in quotes when not expected.
+ private double readJSONDouble() throws TException {
+ context_.read();
+ if (reader_.peek() == QUOTE[0]) {
+ TByteArrayOutputStream arr = readJSONString(true);
+ try {
+ double dub = Double.valueOf(arr.toString("UTF-8")).doubleValue();
+ if (!context_.escapeNum() && !Double.isNaN(dub) &&
+ !Double.isInfinite(dub)) {
+ // Throw exception -- we should not be in a string in this case
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Numeric data unexpectedly quoted");
+ }
+ return dub;
+ } catch (UnsupportedEncodingException ex) {
+ throw new TException("JVM DOES NOT SUPPORT UTF-8");
+ }
+ }
+ else {
+ if (context_.escapeNum()) {
+ // This will throw - we should have had a quote if escapeNum == true
+ readJSONSyntaxChar(QUOTE);
+ }
+ try {
+ return Double.valueOf(readJSONNumericChars()).doubleValue();
+ } catch (NumberFormatException ex) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Bad data encounted in numeric data");
+ }
+ }
+ }
+
+ // Read in a JSON string containing base-64 encoded data and decode it.
+ private byte[] readJSONBase64() throws TException {
+ TByteArrayOutputStream arr = readJSONString(false);
+ byte[] b = arr.get();
+ int len = arr.len();
+ int off = 0;
+ int size = 0;
+ // Ignore padding
+ int bound = len >= 2 ? len - 2 : 0;
+ for (int i = len - 1; i >= bound && b[i] == '='; --i) {
+ --len;
+ }
+ while (len >= 4) {
+ // Decode 4 bytes at a time
+ TBase64Utils.decode(b, off, 4, b, size); // NB: decoded in place
+ off += 4;
+ len -= 4;
+ size += 3;
+ }
+ // Don't decode if we hit the end or got a single leftover byte (invalid
+ // base64 but legal for skip of regular string type)
+ if (len > 1) {
+ // Decode remainder
+ TBase64Utils.decode(b, off, len, b, size); // NB: decoded in place
+ size += len - 1;
+ }
+ // Sadly we must copy the byte[] (any way around this?)
+ byte[] result = new byte[size];
+ System.arraycopy(b, 0, result, 0, size);
+ return result;
+ }
+
+ private void readJSONObjectStart() throws TException {
+ context_.read();
+ readJSONSyntaxChar(LBRACE);
+ pushContext(new JSONPairContext());
+ }
+
+ private void readJSONObjectEnd() throws TException {
+ readJSONSyntaxChar(RBRACE);
+ popContext();
+ }
+
+ private void readJSONArrayStart() throws TException {
+ context_.read();
+ readJSONSyntaxChar(LBRACKET);
+ pushContext(new JSONListContext());
+ }
+
+ private void readJSONArrayEnd() throws TException {
+ readJSONSyntaxChar(RBRACKET);
+ popContext();
+ }
+
+ public TMessage readMessageBegin() throws TException {
+ readJSONArrayStart();
+ if (readJSONInteger() != VERSION) {
+ throw new TProtocolException(TProtocolException.BAD_VERSION,
+ "Message contained bad version.");
+ }
+ String name;
+ try {
+ name = readJSONString(false).toString("UTF-8");
+ } catch (UnsupportedEncodingException ex) {
+ throw new TException("JVM DOES NOT SUPPORT UTF-8");
+ }
+ byte type = (byte)readJSONInteger();
+ int seqid = (int)readJSONInteger();
+ return new TMessage(name, type, seqid);
+ }
+
+ public void readMessageEnd() throws TException {
+ readJSONArrayEnd();
+ }
+
+ public TStruct readStructBegin() throws TException {
+ readJSONObjectStart();
+ return ANONYMOUS_STRUCT;
+ }
+
+ public void readStructEnd() throws TException {
+ readJSONObjectEnd();
+ }
+
+ public TField readFieldBegin() throws TException {
+ byte ch = reader_.peek();
+ byte type;
+ short id = 0;
+ if (ch == RBRACE[0]) {
+ type = TType.STOP;
+ }
+ else {
+ id = (short)readJSONInteger();
+ readJSONObjectStart();
+ type = getTypeIDForTypeName(readJSONString(false).get());
+ }
+ return new TField("", type, id);
+ }
+
+ public void readFieldEnd() throws TException {
+ readJSONObjectEnd();
+ }
+
+ public TMap readMapBegin() throws TException {
+ readJSONArrayStart();
+ byte keyType = getTypeIDForTypeName(readJSONString(false).get());
+ byte valueType = getTypeIDForTypeName(readJSONString(false).get());
+ int size = (int)readJSONInteger();
+ readJSONObjectStart();
+ return new TMap(keyType, valueType, size);
+ }
+
+ public void readMapEnd() throws TException {
+ readJSONObjectEnd();
+ readJSONArrayEnd();
+ }
+
+ public TList readListBegin() throws TException {
+ readJSONArrayStart();
+ byte elemType = getTypeIDForTypeName(readJSONString(false).get());
+ int size = (int)readJSONInteger();
+ return new TList(elemType, size);
+ }
+
+ public void readListEnd() throws TException {
+ readJSONArrayEnd();
+ }
+
+ public TSet readSetBegin() throws TException {
+ readJSONArrayStart();
+ byte elemType = getTypeIDForTypeName(readJSONString(false).get());
+ int size = (int)readJSONInteger();
+ return new TSet(elemType, size);
+ }
+
+ public void readSetEnd() throws TException {
+ readJSONArrayEnd();
+ }
+
+ public boolean readBool() throws TException {
+ return (readJSONInteger() == 0 ? false : true);
+ }
+
+ public byte readByte() throws TException {
+ return (byte)readJSONInteger();
+ }
+
+ public short readI16() throws TException {
+ return (short)readJSONInteger();
+ }
+
+ public int readI32() throws TException {
+ return (int)readJSONInteger();
+ }
+
+ public long readI64() throws TException {
+ return readJSONInteger();
+ }
+
+ public double readDouble() throws TException {
+ return readJSONDouble();
+ }
+
+ public String readString() throws TException {
+ try {
+ return readJSONString(false).toString("UTF-8");
+ } catch (UnsupportedEncodingException ex) {
+ throw new TException("JVM DOES NOT SUPPORT UTF-8");
+ }
+ }
+
+ public byte[] readBinary() throws TException {
+ return readJSONBase64();
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TList.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TList.java
new file mode 100644
index 000000000..88f678561
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TList.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates list metadata.
+ *
+ */
+public class TList {
+ public TList() {}
+
+ public TList(byte t, int s) {
+ elemType = t;
+ size = s;
+ }
+
+ public byte elemType = TType.STOP;
+ public int size = 0;
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMap.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMap.java
new file mode 100644
index 000000000..718a1193e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMap.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates map metadata.
+ *
+ */
+public class TMap {
+ public TMap() {}
+
+ public TMap(byte k, byte v, int s) {
+ keyType = k;
+ valueType = v;
+ size = s;
+ }
+
+ public byte keyType = TType.STOP;
+ public byte valueType = TType.STOP;
+ public int size = 0;
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMessage.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMessage.java
new file mode 100644
index 000000000..408b05a9a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMessage.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates struct metadata.
+ *
+ */
+public class TMessage {
+ public TMessage() {}
+
+ public TMessage(String n, byte t, int s) {
+ name = n;
+ type = t;
+ seqid = s;
+ }
+
+ public String name = "";
+ public byte type;
+ public int seqid;
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMessageType.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMessageType.java
new file mode 100644
index 000000000..714ea7091
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMessageType.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Message type constants in the Thrift protocol.
+ *
+ */
+public final class TMessageType {
+ public static final byte CALL = 1;
+ public static final byte REPLY = 2;
+ public static final byte EXCEPTION = 3;
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocol.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocol.java
new file mode 100644
index 000000000..710e6d4c1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocol.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * Protocol interface definition.
+ *
+ */
+public abstract class TProtocol {
+
+ /**
+ * Prevent direct instantiation
+ */
+ private TProtocol() {}
+
+ /**
+ * Transport
+ */
+ protected TTransport trans_;
+
+ /**
+ * Constructor
+ */
+ protected TProtocol(TTransport trans) {
+ trans_ = trans;
+ }
+
+ /**
+ * Transport accessor
+ */
+ public TTransport getTransport() {
+ return trans_;
+ }
+
+ /**
+ * Writing methods.
+ */
+
+ public abstract void writeMessageBegin(TMessage message) throws TException;
+
+ public abstract void writeMessageEnd() throws TException;
+
+ public abstract void writeStructBegin(TStruct struct) throws TException;
+
+ public abstract void writeStructEnd() throws TException;
+
+ public abstract void writeFieldBegin(TField field) throws TException;
+
+ public abstract void writeFieldEnd() throws TException;
+
+ public abstract void writeFieldStop() throws TException;
+
+ public abstract void writeMapBegin(TMap map) throws TException;
+
+ public abstract void writeMapEnd() throws TException;
+
+ public abstract void writeListBegin(TList list) throws TException;
+
+ public abstract void writeListEnd() throws TException;
+
+ public abstract void writeSetBegin(TSet set) throws TException;
+
+ public abstract void writeSetEnd() throws TException;
+
+ public abstract void writeBool(boolean b) throws TException;
+
+ public void writeBool(Boolean b) throws TException {
+ writeBool(b.booleanValue());
+ }
+
+ public abstract void writeByte(byte b) throws TException;
+
+ public void writeByte(Byte b) throws TException {
+ writeByte(b.byteValue());
+ }
+
+ public abstract void writeI16(short i16) throws TException;
+
+ public void writeI16(Short i16) throws TException {
+ writeI16(i16.shortValue());
+ }
+
+ public abstract void writeI32(int i32) throws TException;
+
+ public void writeI32(Integer i32) throws TException {
+ writeI32(i32.intValue());
+ }
+
+ public abstract void writeI64(long i64) throws TException;
+
+ public void writeI64(Long i64) throws TException {
+ writeI64(i64.longValue());
+ }
+
+ public abstract void writeDouble(double dub) throws TException;
+
+ public void writeDouble(Double d) throws TException {
+ writeDouble(d.doubleValue());
+ }
+
+ public abstract void writeString(String str) throws TException;
+
+ public abstract void writeBinary(byte[] bin) throws TException;
+
+ /**
+ * Reading methods.
+ */
+
+ public abstract TMessage readMessageBegin() throws TException;
+
+ public abstract void readMessageEnd() throws TException;
+
+ public abstract TStruct readStructBegin() throws TException;
+
+ public abstract void readStructEnd() throws TException;
+
+ public abstract TField readFieldBegin() throws TException;
+
+ public abstract void readFieldEnd() throws TException;
+
+ public abstract TMap readMapBegin() throws TException;
+
+ public abstract void readMapEnd() throws TException;
+
+ public abstract TList readListBegin() throws TException;
+
+ public abstract void readListEnd() throws TException;
+
+ public abstract TSet readSetBegin() throws TException;
+
+ public abstract void readSetEnd() throws TException;
+
+ public abstract boolean readBool() throws TException;
+
+ public abstract byte readByte() throws TException;
+
+ public abstract short readI16() throws TException;
+
+ public abstract int readI32() throws TException;
+
+ public abstract long readI64() throws TException;
+
+ public abstract double readDouble() throws TException;
+
+ public abstract String readString() throws TException;
+
+ public abstract byte[] readBinary() throws TException;
+
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolException.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolException.java
new file mode 100644
index 000000000..248815bec
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolException.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.TException;
+
+/**
+ * Protocol exceptions.
+ *
+ */
+public class TProtocolException extends TException {
+
+
+ private static final long serialVersionUID = 1L;
+ public static final int UNKNOWN = 0;
+ public static final int INVALID_DATA = 1;
+ public static final int NEGATIVE_SIZE = 2;
+ public static final int SIZE_LIMIT = 3;
+ public static final int BAD_VERSION = 4;
+ public static final int NOT_IMPLEMENTED = 5;
+
+ protected int type_ = UNKNOWN;
+
+ public TProtocolException() {
+ super();
+ }
+
+ public TProtocolException(int type) {
+ super();
+ type_ = type;
+ }
+
+ public TProtocolException(int type, String message) {
+ super(message);
+ type_ = type;
+ }
+
+ public TProtocolException(String message) {
+ super(message);
+ }
+
+ public TProtocolException(int type, Throwable cause) {
+ super(cause);
+ type_ = type;
+ }
+
+ public TProtocolException(Throwable cause) {
+ super(cause);
+ }
+
+ public TProtocolException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public TProtocolException(int type, String message, Throwable cause) {
+ super(message, cause);
+ type_ = type;
+ }
+
+ public int getType() {
+ return type_;
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolFactory.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolFactory.java
new file mode 100644
index 000000000..afa502b70
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolFactory.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * Factory interface for constructing protocol instances.
+ *
+ */
+public interface TProtocolFactory {
+ public TProtocol getProtocol(TTransport trans);
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolUtil.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolUtil.java
new file mode 100644
index 000000000..c327448ef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolUtil.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.TException;
+
+/**
+ * Utility class with static methods for interacting with protocol data
+ * streams.
+ *
+ */
+public class TProtocolUtil {
+
+ /**
+ * The maximum recursive depth the skip() function will traverse before
+ * throwing a TException.
+ */
+ private static int maxSkipDepth = Integer.MAX_VALUE;
+
+ /**
+ * Specifies the maximum recursive depth that the skip function will
+ * traverse before throwing a TException. This is a global setting, so
+ * any call to skip in this JVM will enforce this value.
+ *
+ * @param depth the maximum recursive depth. A value of 2 would allow
+ * the skip function to skip a structure or collection with basic children,
+ * but it would not permit skipping a struct that had a field containing
+ * a child struct. A value of 1 would only allow skipping of simple
+ * types and empty structs/collections.
+ */
+ public static void setMaxSkipDepth(int depth) {
+ maxSkipDepth = depth;
+ }
+
+ /**
+ * Skips over the next data element from the provided input TProtocol object.
+ *
+ * @param prot the protocol object to read from
+ * @param type the next value will be intepreted as this TType value.
+ */
+ public static void skip(TProtocol prot, byte type)
+ throws TException {
+ skip(prot, type, maxSkipDepth);
+ }
+
+ /**
+ * Skips over the next data element from the provided input TProtocol object.
+ *
+ * @param prot the protocol object to read from
+ * @param type the next value will be intepreted as this TType value.
+ * @param maxDepth this function will only skip complex objects to this
+ * recursive depth, to prevent Java stack overflow.
+ */
+ public static void skip(TProtocol prot, byte type, int maxDepth)
+ throws TException {
+ if (maxDepth <= 0) {
+ throw new TException("Maximum skip depth exceeded");
+ }
+ switch (type) {
+ case TType.BOOL:
+ {
+ prot.readBool();
+ break;
+ }
+ case TType.BYTE:
+ {
+ prot.readByte();
+ break;
+ }
+ case TType.I16:
+ {
+ prot.readI16();
+ break;
+ }
+ case TType.I32:
+ {
+ prot.readI32();
+ break;
+ }
+ case TType.I64:
+ {
+ prot.readI64();
+ break;
+ }
+ case TType.DOUBLE:
+ {
+ prot.readDouble();
+ break;
+ }
+ case TType.STRING:
+ {
+ prot.readBinary();
+ break;
+ }
+ case TType.STRUCT:
+ {
+ prot.readStructBegin();
+ while (true) {
+ TField field = prot.readFieldBegin();
+ if (field.type == TType.STOP) {
+ break;
+ }
+ skip(prot, field.type, maxDepth - 1);
+ prot.readFieldEnd();
+ }
+ prot.readStructEnd();
+ break;
+ }
+ case TType.MAP:
+ {
+ TMap map = prot.readMapBegin();
+ for (int i = 0; i < map.size; i++) {
+ skip(prot, map.keyType, maxDepth - 1);
+ skip(prot, map.valueType, maxDepth - 1);
+ }
+ prot.readMapEnd();
+ break;
+ }
+ case TType.SET:
+ {
+ TSet set = prot.readSetBegin();
+ for (int i = 0; i < set.size; i++) {
+ skip(prot, set.elemType, maxDepth - 1);
+ }
+ prot.readSetEnd();
+ break;
+ }
+ case TType.LIST:
+ {
+ TList list = prot.readListBegin();
+ for (int i = 0; i < list.size; i++) {
+ skip(prot, list.elemType, maxDepth - 1);
+ }
+ prot.readListEnd();
+ break;
+ }
+ default:
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Unrecognized type " + type);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TSet.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TSet.java
new file mode 100644
index 000000000..f4e9b1d34
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TSet.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates set metadata.
+ *
+ */
+public class TSet {
+ public TSet() {}
+
+ public TSet(byte t, int s) {
+ elemType = t;
+ size = s;
+ }
+
+ public byte elemType = TType.STOP;
+ public int size = 0;
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TStruct.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TStruct.java
new file mode 100644
index 000000000..cecbbcfce
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TStruct.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates struct metadata.
+ *
+ */
+public class TStruct {
+ public TStruct() {}
+
+ public TStruct(String n) {
+ name = n;
+ }
+
+ public String name = "";
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TType.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TType.java
new file mode 100644
index 000000000..dbdc3caa8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TType.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Type constants in the Thrift protocol.
+ *
+ */
+public final class TType {
+ public static final byte STOP = 0;
+ public static final byte VOID = 1;
+ public static final byte BOOL = 2;
+ public static final byte BYTE = 3;
+ public static final byte DOUBLE = 4;
+ public static final byte I16 = 6;
+ public static final byte I32 = 8;
+ public static final byte I64 = 10;
+ public static final byte STRING = 11;
+ public static final byte STRUCT = 12;
+ public static final byte MAP = 13;
+ public static final byte SET = 14;
+ public static final byte LIST = 15;
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TFramedTransport.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TFramedTransport.java
new file mode 100644
index 000000000..c83748ad2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TFramedTransport.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.ByteArrayInputStream;
+
+import org.apache.thrift.TByteArrayOutputStream;
+
+/**
+ * Socket implementation of the TTransport interface. To be commented soon!
+ *
+ */
+public class TFramedTransport extends TTransport {
+
+ /**
+ * Underlying transport
+ */
+ private TTransport transport_ = null;
+
+ /**
+ * Buffer for output
+ */
+ private final TByteArrayOutputStream writeBuffer_ =
+ new TByteArrayOutputStream(1024);
+
+ /**
+ * Buffer for input
+ */
+ private ByteArrayInputStream readBuffer_ = null;
+
+ public static class Factory extends TTransportFactory {
+ public Factory() {
+ }
+
+ public TTransport getTransport(TTransport base) {
+ return new TFramedTransport(base);
+ }
+ }
+
+ /**
+ * Constructor wraps around another tranpsort
+ */
+ public TFramedTransport(TTransport transport) {
+ transport_ = transport;
+ }
+
+ public void open() throws TTransportException {
+ transport_.open();
+ }
+
+ public boolean isOpen() {
+ return transport_.isOpen();
+ }
+
+ public void close() {
+ transport_.close();
+ }
+
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ if (readBuffer_ != null) {
+ int got = readBuffer_.read(buf, off, len);
+ if (got > 0) {
+ return got;
+ }
+ }
+
+ // Read another frame of data
+ readFrame();
+
+ return readBuffer_.read(buf, off, len);
+ }
+
+ private void readFrame() throws TTransportException {
+ byte[] i32rd = new byte[4];
+ transport_.readAll(i32rd, 0, 4);
+ int size =
+ ((i32rd[0] & 0xff) << 24) |
+ ((i32rd[1] & 0xff) << 16) |
+ ((i32rd[2] & 0xff) << 8) |
+ ((i32rd[3] & 0xff));
+
+ byte[] buff = new byte[size];
+ transport_.readAll(buff, 0, size);
+ readBuffer_ = new ByteArrayInputStream(buff);
+ }
+
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ writeBuffer_.write(buf, off, len);
+ }
+
+ public void flush() throws TTransportException {
+ byte[] buf = writeBuffer_.get();
+ int len = writeBuffer_.len();
+ writeBuffer_.reset();
+
+ byte[] i32out = new byte[4];
+ i32out[0] = (byte)(0xff & (len >> 24));
+ i32out[1] = (byte)(0xff & (len >> 16));
+ i32out[2] = (byte)(0xff & (len >> 8));
+ i32out[3] = (byte)(0xff & (len));
+ transport_.write(i32out, 0, 4);
+ transport_.write(buf, 0, len);
+ transport_.flush();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/THttpClient.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/THttpClient.java
new file mode 100644
index 000000000..e6ffba4e5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/THttpClient.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.microedition.io.Connector;
+import javax.microedition.io.HttpConnection;
+
+/**
+ * HTTP implementation of the TTransport interface. Used for working with a
+ * Thrift web services implementation.
+ *
+ */
+public class THttpClient extends TTransport {
+
+ private String url_ = null;
+
+ private final ByteArrayOutputStream requestBuffer_ = new ByteArrayOutputStream();
+
+ private InputStream inputStream_ = null;
+
+ private HttpConnection connection = null;
+
+ private int connectTimeout_ = 0;
+
+ private int readTimeout_ = 0;
+
+ private Hashtable customHeaders_ = null;
+
+ public THttpClient(String url) throws TTransportException {
+ url_ = url;
+ }
+
+ public void setConnectTimeout(int timeout) {
+ connectTimeout_ = timeout;
+ }
+
+ public void setReadTimeout(int timeout) {
+ readTimeout_ = timeout;
+ }
+
+ public void setCustomHeaders(Hashtable headers) {
+ customHeaders_ = headers;
+ }
+
+ public void setCustomHeader(String key, String value) {
+ if (customHeaders_ == null) {
+ customHeaders_ = new Hashtable();
+ }
+ customHeaders_.put(key, value);
+ }
+
+ public void open() {}
+
+ public void close() {
+ if (null != inputStream_) {
+ try {
+ inputStream_.close();
+ } catch (IOException ioe) {
+ }
+ inputStream_ = null;
+ }
+
+ if (connection != null) {
+ try {
+ connection.close();
+ } catch (IOException ioe) {
+ }
+ connection = null;
+ }
+ }
+
+ public boolean isOpen() {
+ return true;
+ }
+
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ if (inputStream_ == null) {
+ throw new TTransportException("Response buffer is empty, no request.");
+ }
+ try {
+ int ret = inputStream_.read(buf, off, len);
+ if (ret == -1) {
+ throw new TTransportException("No more data available.");
+ }
+ return ret;
+ } catch (IOException iox) {
+ throw new TTransportException(iox);
+ }
+ }
+
+ public void write(byte[] buf, int off, int len) {
+ requestBuffer_.write(buf, off, len);
+ }
+
+ public void flush() throws TTransportException {
+ // Extract request and reset buffer
+ byte[] data = requestBuffer_.toByteArray();
+ requestBuffer_.reset();
+
+ try {
+ // Create connection object
+ connection = (HttpConnection)Connector.open(url_);
+
+ // Make the request
+ connection.setRequestMethod("POST");
+ connection.setRequestProperty("Content-Type", "application/x-thrift");
+ connection.setRequestProperty("Accept", "application/x-thrift");
+ connection.setRequestProperty("User-Agent", "JavaME/THttpClient");
+
+ connection.setRequestProperty("Connection", "Keep-Alive");
+ connection.setRequestProperty("Keep-Alive", "5000");
+ connection.setRequestProperty("Http-version", "HTTP/1.1");
+ connection.setRequestProperty("Cache-Control", "no-transform");
+
+ if (customHeaders_ != null) {
+ for (Enumeration e = customHeaders_.keys() ; e.hasMoreElements() ;) {
+ String key = (String)e.nextElement();
+ String value = (String)customHeaders_.get(key);
+ connection.setRequestProperty(key, value);
+ }
+ }
+
+ OutputStream os = connection.openOutputStream();
+ os.write(data);
+ os.close();
+
+ int responseCode = connection.getResponseCode();
+ if (responseCode != HttpConnection.HTTP_OK) {
+ throw new TTransportException("HTTP Response code: " + responseCode);
+ }
+
+ // Read the responses
+ inputStream_ = connection.openInputStream();
+ } catch (IOException iox) {
+ System.out.println(iox.toString());
+ throw new TTransportException(iox);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TIOStreamTransport.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TIOStreamTransport.java
new file mode 100644
index 000000000..ccf706461
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TIOStreamTransport.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * This is the most commonly used base transport. It takes an InputStream
+ * and an OutputStream and uses those to perform all transport operations.
+ * This allows for compatibility with all the nice constructs Java already
+ * has to provide a variety of types of streams.
+ *
+ */
+public class TIOStreamTransport extends TTransport {
+
+
+ /** Underlying inputStream */
+ protected InputStream inputStream_ = null;
+
+ /** Underlying outputStream */
+ protected OutputStream outputStream_ = null;
+
+ /**
+ * Subclasses can invoke the default constructor and then assign the input
+ * streams in the open method.
+ */
+ protected TIOStreamTransport() {}
+
+ /**
+ * Input stream constructor.
+ *
+ * @param is Input stream to read from
+ */
+ public TIOStreamTransport(InputStream is) {
+ inputStream_ = is;
+ }
+
+ /**
+ * Output stream constructor.
+ *
+ * @param os Output stream to read from
+ */
+ public TIOStreamTransport(OutputStream os) {
+ outputStream_ = os;
+ }
+
+ /**
+ * Two-way stream constructor.
+ *
+ * @param is Input stream to read from
+ * @param os Output stream to read from
+ */
+ public TIOStreamTransport(InputStream is, OutputStream os) {
+ inputStream_ = is;
+ outputStream_ = os;
+ }
+
+ /**
+ * The streams must already be open at construction time, so this should
+ * always return true.
+ *
+ * @return true
+ */
+ public boolean isOpen() {
+ return true;
+ }
+
+ /**
+ * The streams must already be open. This method does nothing.
+ */
+ public void open() throws TTransportException {}
+
+ /**
+ * Closes both the input and output streams.
+ */
+ public void close() {
+ if (inputStream_ != null) {
+ try {
+ inputStream_.close();
+ } catch (IOException iox) {
+ }
+ inputStream_ = null;
+ }
+ if (outputStream_ != null) {
+ try {
+ outputStream_.close();
+ } catch (IOException iox) {
+ }
+ outputStream_ = null;
+ }
+ }
+
+ /**
+ * Reads from the underlying input stream if not null.
+ */
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ if (inputStream_ == null) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "Cannot read from null inputStream");
+ }
+ try {
+ return inputStream_.read(buf, off, len);
+ } catch (IOException iox) {
+ throw new TTransportException(TTransportException.UNKNOWN, iox);
+ }
+ }
+
+ /**
+ * Writes to the underlying output stream if not null.
+ */
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ if (outputStream_ == null) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "Cannot write to null outputStream");
+ }
+ try {
+ outputStream_.write(buf, off, len);
+ } catch (IOException iox) {
+ throw new TTransportException(TTransportException.UNKNOWN, iox);
+ }
+ }
+
+ /**
+ * Flushes the underlying output stream if not null.
+ */
+ public void flush() throws TTransportException {
+ if (outputStream_ == null) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "Cannot flush null outputStream");
+ }
+ try {
+ outputStream_.flush();
+ } catch (IOException iox) {
+ throw new TTransportException(TTransportException.UNKNOWN, iox);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TMemoryBuffer.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TMemoryBuffer.java
new file mode 100644
index 000000000..4ae1ab242
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TMemoryBuffer.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.apache.thrift.TByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Memory buffer-based implementation of the TTransport interface.
+ */
+public class TMemoryBuffer extends TTransport {
+ /**
+ * Create a TMemoryBuffer with an initial buffer size of <i>size</i>. The
+ * internal buffer will grow as necessary to accommodate the size of the data
+ * being written to it.
+ */
+ public TMemoryBuffer(int size) {
+ arr_ = new TByteArrayOutputStream(size);
+ }
+
+ public boolean isOpen() {
+ return true;
+ }
+
+ public void open() {
+ /* Do nothing */
+ }
+
+ public void close() {
+ /* Do nothing */
+ }
+
+ public int read(byte[] buf, int off, int len) {
+ byte[] src = arr_.get();
+ int amtToRead = (len > arr_.len() - pos_ ? arr_.len() - pos_ : len);
+ if (amtToRead > 0) {
+ System.arraycopy(src, pos_, buf, off, amtToRead);
+ pos_ += amtToRead;
+ }
+ return amtToRead;
+ }
+
+ public void write(byte[] buf, int off, int len) {
+ arr_.write(buf, off, len);
+ }
+
+ /**
+ * Output the contents of the memory buffer as a String, using the supplied
+ * encoding
+ *
+ * @param enc the encoding to use
+ * @return the contents of the memory buffer as a String
+ */
+ public String toString(String enc) throws UnsupportedEncodingException {
+ return arr_.toString(enc);
+ }
+
+ public String inspect() {
+ String buf = "";
+ byte[] bytes = arr_.toByteArray();
+ for (int i = 0; i < bytes.length; i++) {
+ buf += (pos_ == i ? "==>" : "") + Integer.toHexString(bytes[i] & 0xff) + " ";
+ }
+ return buf;
+ }
+
+ // The contents of the buffer
+ private TByteArrayOutputStream arr_;
+
+ // Position to read next byte from
+ private int pos_;
+
+ public int length() {
+ return arr_.size();
+ }
+
+ public byte[] getArray() {
+ return arr_.get();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransport.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransport.java
new file mode 100644
index 000000000..a6c047bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransport.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+/**
+ * Generic class that encapsulates the I/O layer. This is basically a thin
+ * wrapper around the combined functionality of Java input/output streams.
+ *
+ */
+public abstract class TTransport {
+
+ /**
+ * Queries whether the transport is open.
+ *
+ * @return True if the transport is open.
+ */
+ public abstract boolean isOpen();
+
+ /**
+ * Is there more data to be read?
+ *
+ * @return True if the remote side is still alive and feeding us
+ */
+ public boolean peek() {
+ return isOpen();
+ }
+
+ /**
+ * Opens the transport for reading/writing.
+ *
+ * @throws TTransportException if the transport could not be opened
+ */
+ public abstract void open()
+ throws TTransportException;
+
+ /**
+ * Closes the transport.
+ */
+ public abstract void close();
+
+ /**
+ * Reads up to len bytes into buffer buf, starting att offset off.
+ *
+ * @param buf Array to read into
+ * @param off Index to start reading at
+ * @param len Maximum number of bytes to read
+ * @return The number of bytes actually read
+ * @throws TTransportException if there was an error reading data
+ */
+ public abstract int read(byte[] buf, int off, int len)
+ throws TTransportException;
+
+ /**
+ * Guarantees that all of len bytes are actually read off the transport.
+ *
+ * @param buf Array to read into
+ * @param off Index to start reading at
+ * @param len Maximum number of bytes to read
+ * @return The number of bytes actually read, which must be equal to len
+ * @throws TTransportException if there was an error reading data
+ */
+ public int readAll(byte[] buf, int off, int len)
+ throws TTransportException {
+ int got = 0;
+ int ret = 0;
+ while (got < len) {
+ ret = read(buf, off+got, len-got);
+ if (ret <= 0) {
+ throw new TTransportException("Cannot read. Remote side has closed. Tried to read " + len + " bytes, but only got " + got + " bytes.");
+ }
+ got += ret;
+ }
+ return got;
+ }
+
+ /**
+ * Writes the buffer to the output
+ *
+ * @param buf The output data buffer
+ * @throws TTransportException if an error occurs writing data
+ */
+ public void write(byte[] buf) throws TTransportException {
+ write(buf, 0, buf.length);
+ }
+
+ /**
+ * Writes up to len bytes from the buffer.
+ *
+ * @param buf The output data buffer
+ * @param off The offset to start writing from
+ * @param len The number of bytes to write
+ * @throws TTransportException if there was an error writing data
+ */
+ public abstract void write(byte[] buf, int off, int len)
+ throws TTransportException;
+
+ /**
+ * Flush any pending data out of a transport buffer.
+ *
+ * @throws TTransportException if there was an error writing out data.
+ */
+ public void flush()
+ throws TTransportException {}
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransportException.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransportException.java
new file mode 100644
index 000000000..d08f3b02b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransportException.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.apache.thrift.TException;
+
+/**
+ * Transport exceptions.
+ *
+ */
+public class TTransportException extends TException {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final int UNKNOWN = 0;
+ public static final int NOT_OPEN = 1;
+ public static final int ALREADY_OPEN = 2;
+ public static final int TIMED_OUT = 3;
+ public static final int END_OF_FILE = 4;
+
+ protected int type_ = UNKNOWN;
+
+ public TTransportException() {
+ super();
+ }
+
+ public TTransportException(int type) {
+ super();
+ type_ = type;
+ }
+
+ public TTransportException(int type, String message) {
+ super(message);
+ type_ = type;
+ }
+
+ public TTransportException(String message) {
+ super(message);
+ }
+
+ public TTransportException(int type, Throwable cause) {
+ super(cause);
+ type_ = type;
+ }
+
+ public TTransportException(Throwable cause) {
+ super(cause);
+ }
+
+ public TTransportException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public TTransportException(int type, String message, Throwable cause) {
+ super(message, cause);
+ type_ = type;
+ }
+
+ public int getType() {
+ return type_;
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransportFactory.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransportFactory.java
new file mode 100644
index 000000000..45b607f3e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransportFactory.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+/**
+ * Factory class used to create wrapped instance of Transports.
+ * This is used primarily in servers, which get Transports from
+ * a ServerTransport and then may want to mutate them (i.e. create
+ * a BufferedTransport from the underlying base transport)
+ *
+ */
+public class TTransportFactory {
+
+ /**
+ * Return a wrapped instance of the base Transport.
+ *
+ * @param in The base transport
+ * @returns Wrapped Transport
+ */
+ public TTransport getTransport(TTransport trans) {
+ return trans;
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/js/Gruntfile.js b/src/jaegertracing/thrift/lib/js/Gruntfile.js
new file mode 100644
index 000000000..87b826fb1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/Gruntfile.js
@@ -0,0 +1,327 @@
+//To build dist/thrift.js, dist/thrift.min.js and doc/*
+//run grunt at the command line in this directory.
+//Prerequisites:
+// Node Setup - nodejs.org
+// Grunt Setup - npm install //reads the ./package.json and installs project dependencies
+// Run grunt - npx grunt // uses project-local installed version of grunt (from package.json)
+
+module.exports = function(grunt) {
+ 'use strict';
+
+ grunt.initConfig({
+ pkg: grunt.file.readJSON('package.json'),
+ concat: {
+ options: {
+ separator: ';'
+ },
+ dist: {
+ src: ['src/**/*.js'],
+ dest: 'dist/<%= pkg.name %>.js'
+ }
+ },
+ jsdoc : {
+ dist : {
+ src: ['src/*.js', './README.md'],
+ options: {
+ destination: 'doc'
+ }
+ }
+ },
+ uglify: {
+ options: {
+ banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
+ },
+ dist: {
+ files: {
+ 'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
+ }
+ }
+ },
+ shell: {
+ InstallThriftJS: {
+ command: 'mkdir -p test/build/js/lib; cp src/thrift.js test/build/js/thrift.js'
+ },
+ InstallThriftNodeJSDep: {
+ command: 'cd ../..; npm install'
+ },
+ InstallTestLibs: {
+ command: 'cd test; ant download_jslibs'
+ },
+ ThriftGen: {
+ command: [
+ 'mkdir -p test/gen-js',
+ '../../compiler/cpp/thrift -gen js --out test/gen-js ../../test/ThriftTest.thrift',
+ '../../compiler/cpp/thrift -gen js --out test/gen-js ../../test/JsDeepConstructorTest.thrift',
+ 'mkdir -p test/gen-js-jquery',
+ '../../compiler/cpp/thrift -gen js:jquery --out test/gen-js-jquery ../../test/ThriftTest.thrift',
+ 'mkdir -p test/gen-nodejs',
+ '../../compiler/cpp/thrift -gen js:node --out test/gen-nodejs ../../test/ThriftTest.thrift',
+ 'mkdir -p test/gen-js-es6',
+ '../../compiler/cpp/thrift -gen js:es6 --out test/gen-js-es6 ../../test/ThriftTest.thrift',
+ 'mkdir -p test/gen-nodejs-es6',
+ '../../compiler/cpp/thrift -gen js:node,es6 --out ./test/gen-nodejs-es6 ../../test/ThriftTest.thrift',
+ ].join(' && ')
+ },
+ ThriftGenJQ: {
+ command: '../../compiler/cpp/thrift -gen js:jquery -gen js:node -o test ../../test/ThriftTest.thrift'
+ },
+ ThriftGenDeepConstructor: {
+ command: '../../compiler/cpp/thrift -gen js -o test ../../test/JsDeepConstructorTest.thrift'
+ },
+ ThriftBrowserifyNodeInt64: {
+ command: [
+ './node_modules/browserify/bin/cmd.js ./node_modules/node-int64/Int64.js -s Int64 -o test/build/js/lib/Int64.js',
+ './node_modules/browserify/bin/cmd.js ../nodejs/lib/thrift/int64_util.js -s Int64Util -o test/build/js/lib/Int64Util.js',
+ './node_modules/browserify/bin/cmd.js ./node_modules/json-int64/index.js -s JSONInt64 -o test/build/js/lib/JSONInt64.js'
+ ].join(' && ')
+ },
+ ThriftGenInt64: {
+ command: '../../compiler/cpp/thrift -gen js -o test ../../test/Int64Test.thrift'
+ },
+ ThriftGenDoubleConstants: {
+ command: '../../compiler/cpp/thrift -gen js -o test ../../test/DoubleConstantsTest.thrift'
+ },
+ ThriftTestServer: {
+ options: {
+ async: true,
+ execOptions: {
+ cwd: "./test",
+ env: {NODE_PATH: "../../nodejs/lib:../../../node_modules"}
+ }
+ },
+ command: "node server_http.js",
+ },
+ ThriftTestServerES6: {
+ options: {
+ async: true,
+ execOptions: {
+ cwd: "./test",
+ env: {NODE_PATH: "../../nodejs/lib:../../../node_modules"}
+ }
+ },
+ command: "node server_http.js --es6",
+ },
+ ThriftTestServer_TLS: {
+ options: {
+ async: true,
+ execOptions: {
+ cwd: "./test",
+ env: {NODE_PATH: "../../nodejs/lib:../../../node_modules"}
+ }
+ },
+ command: "node server_https.js",
+ },
+ ThriftTestServerES6_TLS: {
+ options: {
+ async: true,
+ execOptions: {
+ cwd: "./test",
+ env: {NODE_PATH: "../../nodejs/lib:../../../node_modules"}
+ }
+ },
+ command: "node server_https.js --es6",
+ },
+ },
+ qunit: {
+ ThriftJS: {
+ options: {
+ urls: [
+ 'http://localhost:8089/test-nojq.html'
+ ],
+ puppeteer: {
+ headless: true,
+ args: ['--no-sandbox'],
+ },
+ }
+ },
+ ThriftJSJQ: {
+ options: {
+ urls: [
+ 'http://localhost:8089/test.html'
+ ],
+ puppeteer: {
+ headless: true,
+ args: ['--no-sandbox'],
+ },
+ }
+ },
+ ThriftJS_DoubleRendering: {
+ options: {
+ urls: [
+ 'http://localhost:8089/test-double-rendering.html'
+ ],
+ puppeteer: {
+ headless: true,
+ args: ['--no-sandbox'],
+ ignoreHTTPSErrors: true,
+ },
+ }
+ },
+ ThriftJS_Int64: {
+ options: {
+ urls: [
+ 'http://localhost:8089/test-int64.html'
+ ],
+ puppeteer: {
+ headless: true,
+ args: ['--no-sandbox'],
+ ignoreHTTPSErrors: true,
+ },
+ }
+ },
+ ThriftWS: {
+ options: {
+ urls: [
+ 'http://localhost:8089/testws.html'
+ ],
+ puppeteer: {
+ headless: true,
+ args: ['--no-sandbox'],
+ },
+ }
+ },
+ ThriftJS_TLS: {
+ options: {
+ urls: [
+ 'https://localhost:8091/test-nojq.html'
+ ],
+ puppeteer: {
+ headless: true,
+ args: ['--no-sandbox'],
+ ignoreHTTPSErrors: true,
+ },
+ }
+ },
+ ThriftJSJQ_TLS: {
+ options: {
+ urls: [
+ 'https://localhost:8091/test.html'
+ ],
+ puppeteer: {
+ headless: true,
+ args: ['--no-sandbox'],
+ ignoreHTTPSErrors: true,
+ },
+ }
+ },
+ ThriftWS_TLS: {
+ options: {
+ urls: [
+ 'https://localhost:8091/testws.html'
+ ],
+ puppeteer: {
+ headless: true,
+ args: ['--no-sandbox'],
+ ignoreHTTPSErrors: true,
+ },
+ }
+ },
+ ThriftDeepConstructor: {
+ options: {
+ urls: [
+ 'http://localhost:8089/test-deep-constructor.html'
+ ],
+ puppeteer: {
+ headless: true,
+ args: ['--no-sandbox'],
+ },
+ }
+ },
+ ThriftWSES6: {
+ options: {
+ urls: [
+ 'http://localhost:8088/test-es6.html'
+ ],
+ puppeteer: {
+ headless: true,
+ args: ['--no-sandbox'],
+ },
+ }
+ }
+ },
+ jshint: {
+ // The main Thrift library file. not es6 yet :(
+ lib: {
+ src: ['src/**/*.js'],
+ },
+ // The test files use es6
+ test: {
+ src: ['Gruntfile.js', 'test/*.js'],
+ options: {
+ esversion: 6,
+ }
+ },
+ gen_js_code: {
+ src: ['test/gen-js/*.js', 'test/gen-js-jquery/*.js'],
+ },
+ gen_es6_code: {
+ src: ['test/gen-js-es6/*.js'],
+ options: {
+ esversion: 6,
+ }
+ },
+ gen_node_code: {
+ src: ['test/gen-nodejs/*.js'],
+ options: {
+ node: true,
+ }
+ },
+ gen_node_es6_code: {
+ src: ['test/gen-nodejs-es6/*.js'],
+ options: {
+ node: true,
+ esversion: 6,
+ }
+ }
+ },
+ });
+
+ grunt.loadNpmTasks('grunt-contrib-uglify');
+ grunt.loadNpmTasks('grunt-contrib-jshint');
+ grunt.loadNpmTasks('grunt-contrib-qunit');
+ grunt.loadNpmTasks('grunt-contrib-concat');
+ grunt.loadNpmTasks('grunt-jsdoc');
+ grunt.loadNpmTasks('grunt-shell-spawn');
+
+ grunt.registerTask('wait', 'Wait just one second for the server to start', function () {
+ var done = this.async();
+ setTimeout(function() {
+ done(true);
+ }, 1000);
+ });
+
+ grunt.registerTask('installAndGenerate', [
+ 'shell:InstallThriftJS',
+ 'shell:InstallThriftNodeJSDep',
+ 'shell:ThriftGen',
+ 'shell:ThriftGenDeepConstructor',
+ 'shell:ThriftGenDoubleConstants',
+ 'shell:InstallTestLibs',
+ 'shell:ThriftBrowserifyNodeInt64',
+ 'shell:ThriftGenInt64'
+ ]);
+
+ grunt.registerTask('test', [
+ 'installAndGenerate',
+ 'jshint',
+ 'shell:ThriftTestServer',
+ 'shell:ThriftTestServer_TLS',
+ 'shell:ThriftTestServerES6',
+ 'shell:ThriftTestServerES6_TLS',
+ 'wait',
+ 'qunit:ThriftDeepConstructor',
+ 'qunit:ThriftJS',
+ 'qunit:ThriftJS_TLS',
+ 'qunit:ThriftJS_DoubleRendering',
+ 'qunit:ThriftWS',
+ 'qunit:ThriftJSJQ',
+ 'qunit:ThriftJSJQ_TLS',
+ 'qunit:ThriftWSES6',
+ 'qunit:ThriftJS_Int64',
+ 'shell:ThriftTestServer:kill',
+ 'shell:ThriftTestServer_TLS:kill',
+ 'shell:ThriftTestServerES6:kill',
+ 'shell:ThriftTestServerES6_TLS:kill',
+ ]);
+ grunt.registerTask('default', ['test', 'concat', 'uglify', 'jsdoc']);
+};
diff --git a/src/jaegertracing/thrift/lib/js/Makefile.am b/src/jaegertracing/thrift/lib/js/Makefile.am
new file mode 100644
index 000000000..b53404264
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/Makefile.am
@@ -0,0 +1,62 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Make sure this doesn't fail if ant is not configured.
+# We call npm install twice to work around older npm issues
+# (note these issues may no longer be present, but it is ok)
+#
+
+if HAVE_NPM
+
+SUBDIRS = test
+
+prereq:
+ $(NPM) install || $(NPM) install
+ $(NPM) list
+
+check-local: prereq all
+ ./node_modules/.bin/grunt
+
+doc: prereq
+ ./node_modules/.bin/grunt jsdoc
+
+endif
+
+clean-local:
+ $(RM) -r dist
+ $(RM) -r doc
+ $(RM) -r node_modules
+ $(RM) -r test/build/
+ $(RM) -r test/gen-*/
+
+dist-hook:
+ $(RM) -r $(distdir)/dist/
+ $(RM) -r $(distdir)/doc/
+ $(RM) -r $(distdir)/node_modules/
+ $(RM) -r $(distdir)/test/build/
+ $(RM) -r $(distdir)/test/gen-*/
+
+EXTRA_DIST = \
+ coding_standards.md \
+ Gruntfile.js \
+ package.json \
+ package-lock.json \
+ README.md \
+ src \
+ test
diff --git a/src/jaegertracing/thrift/lib/js/README.md b/src/jaegertracing/thrift/lib/js/README.md
new file mode 100644
index 000000000..5c7a1c23b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/README.md
@@ -0,0 +1,145 @@
+Thrift Javascript Library
+=========================
+This browser based Apache Thrift implementation supports
+RPC clients using the JSON protocol over Http[s] with XHR
+and WebSocket.
+
+License
+-------
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+Grunt Build
+------------
+This is the base directory for the Apache Thrift JavaScript
+library. This directory contains a Gruntfile.js and a
+package.json. Many of the build and test tools used here
+require a recent version of Node.js to be installed. To
+install the support files for the Grunt build tool execute
+the command:
+
+ npm install
+
+This reads the package.json and pulls in the appropriate
+sources from the internet. To build the JavaScript branch
+of Apache Thrift execute the command:
+
+ npx grunt
+
+This runs the grunt build tool (from within `./node_modules/.bin/`),
+linting all of the source files, setting up and running the
+tests, concatenating and minifying the main libraries and
+generating the html documentation.
+
+Tree
+----
+The following directories are present (some only after the
+grunt build):
+ /src - The JavaScript Apache Thrift source
+ /doc - HTML documentation
+ /dist - Distribution files (thrift.js and thrift.min.js)
+ /test - Various tests, this is a good place to look for
+ example code
+ /node_modules - Build support files installed by npm
+
+
+Example JavaScript Client and Server
+------------------------------------
+The listing below demonstrates a simple browser based JavaScript
+Thrift client and Node.js JavaScript server for the hello_svc
+service.
+
+### hello.thrift - Service IDL
+### build with: $ thrift -gen js -gen js:node hello.thrift
+ service hello_svc {
+ string get_message(1: string name)
+ }
+
+### hello.html - Browser Client
+ <!DOCTYPE html>
+ <html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Hello Thrift</title>
+ </head>
+ <body>
+ Name: <input type="text" id="name_in">
+ <input type="button" id="get_msg" value="Get Message" >
+ <div id="output"></div>
+
+ <script src="thrift.js"></script>
+ <script src="gen-js/hello_svc.js"></script>
+ <script>
+ (function() {
+ var transport = new Thrift.TXHRTransport("/hello");
+ var protocol = new Thrift.TJSONProtocol(transport);
+ var client = new hello_svcClient(protocol);
+ var nameElement = document.getElementById("name_in");
+ var outputElement = document.getElementById("output");
+ document.getElementById("get_msg")
+ .addEventListener("click", function(){
+ client.get_message(nameElement.value, function(result) {
+ outputElement.innerHTML = result;
+ });
+ });
+ })();
+ </script>
+ </body>
+ </html>
+
+### hello.js - Node Server
+ var thrift = require('thrift');
+ var hello_svc = require('./gen-nodejs/hello_svc.js');
+
+ var hello_handler = {
+ get_message: function(name, result) {
+ var msg = "Hello " + name + "!";
+ result(null, msg);
+ }
+ }
+
+ var hello_svc_opt = {
+ transport: thrift.TBufferedTransport,
+ protocol: thrift.TJSONProtocol,
+ processor: hello_svc,
+ handler: hello_handler
+ };
+
+ var server_opt = {
+ staticFilePath: ".",
+ services: {
+ "/hello": hello_svc_opt
+ }
+ }
+
+ var server = Thrift.createWebServer(server_opt);
+ var port = 9099;
+ server.listen(port);
+ console.log("Http/Thrift Server running on port: " + port);
+
+
+TypeScript
+------------------------------------
+TypeScript definition files can also be generated by running:
+
+ thrift --gen js:ts file.thrift
+
+# Breaking Changes
+
+## 0.13.0
+
+1. 64-bit integer constants are now generatd using node-int64 e.g.: var x = new Int64("7fffffffffffffff");
diff --git a/src/jaegertracing/thrift/lib/js/coding_standards.md b/src/jaegertracing/thrift/lib/js/coding_standards.md
new file mode 100644
index 000000000..fa0390bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/coding_standards.md
@@ -0,0 +1 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
diff --git a/src/jaegertracing/thrift/lib/js/package-lock.json b/src/jaegertracing/thrift/lib/js/package-lock.json
new file mode 100644
index 000000000..00bf05c92
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/package-lock.json
@@ -0,0 +1,4344 @@
+{
+ "name": "thrift",
+ "version": "0.13.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "JSONStream": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+ "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+ "dev": true,
+ "requires": {
+ "jsonparse": "^1.2.0",
+ "through": ">=2.2.7 <3"
+ }
+ },
+ "abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+ },
+ "acorn": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.4.tgz",
+ "integrity": "sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg==",
+ "dev": true
+ },
+ "acorn-dynamic-import": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz",
+ "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==",
+ "dev": true
+ },
+ "acorn-node": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.6.2.tgz",
+ "integrity": "sha512-rIhNEZuNI8ibQcL7ANm/mGyPukIaZsRNX9psFNQURyJW0nu6k8wjSDld20z6v2mDBWqX13pIEnk9gGZJHIlEXg==",
+ "dev": true,
+ "requires": {
+ "acorn": "^6.0.2",
+ "acorn-dynamic-import": "^4.0.0",
+ "acorn-walk": "^6.1.0",
+ "xtend": "^4.0.1"
+ }
+ },
+ "acorn-walk": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz",
+ "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==",
+ "dev": true
+ },
+ "agent-base": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
+ "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
+ "dev": true,
+ "requires": {
+ "es6-promisify": "^5.0.0"
+ }
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ },
+ "dependencies": {
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ }
+ }
+ },
+ "arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+ "dev": true
+ },
+ "arr-flatten": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+ "dev": true
+ },
+ "arr-union": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+ "dev": true
+ },
+ "array-each": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz",
+ "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=",
+ "dev": true
+ },
+ "array-filter": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
+ "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=",
+ "dev": true
+ },
+ "array-find-index": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
+ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
+ "dev": true
+ },
+ "array-map": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
+ "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=",
+ "dev": true
+ },
+ "array-reduce": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
+ "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=",
+ "dev": true
+ },
+ "array-slice": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
+ "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==",
+ "dev": true
+ },
+ "array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+ "dev": true
+ },
+ "asn1.js": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
+ "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "assert": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
+ "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
+ "dev": true,
+ "requires": {
+ "util": "0.10.3"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+ "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
+ "dev": true
+ },
+ "util": {
+ "version": "0.10.3",
+ "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz",
+ "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.1"
+ }
+ }
+ }
+ },
+ "assign-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+ "dev": true
+ },
+ "async": {
+ "version": "1.5.2",
+ "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
+ "dev": true
+ },
+ "async-limiter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
+ "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
+ "dev": true
+ },
+ "atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "dev": true
+ },
+ "babylon": {
+ "version": "7.0.0-beta.19",
+ "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.19.tgz",
+ "integrity": "sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A=="
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "base": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "dev": true,
+ "requires": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "base64-js": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
+ "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==",
+ "dev": true
+ },
+ "bluebird": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz",
+ "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw=="
+ },
+ "bn.js": {
+ "version": "4.11.8",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
+ "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
+ "dev": true
+ },
+ "browser-pack": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz",
+ "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==",
+ "dev": true,
+ "requires": {
+ "JSONStream": "^1.0.3",
+ "combine-source-map": "~0.8.0",
+ "defined": "^1.0.0",
+ "safe-buffer": "^5.1.1",
+ "through2": "^2.0.0",
+ "umd": "^3.0.0"
+ }
+ },
+ "browser-resolve": {
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz",
+ "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==",
+ "dev": true,
+ "requires": {
+ "resolve": "1.1.7"
+ }
+ },
+ "browserify": {
+ "version": "16.2.3",
+ "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.2.3.tgz",
+ "integrity": "sha512-zQt/Gd1+W+IY+h/xX2NYMW4orQWhqSwyV+xsblycTtpOuB27h1fZhhNQuipJ4t79ohw4P4mMem0jp/ZkISQtjQ==",
+ "dev": true,
+ "requires": {
+ "JSONStream": "^1.0.3",
+ "assert": "^1.4.0",
+ "browser-pack": "^6.0.1",
+ "browser-resolve": "^1.11.0",
+ "browserify-zlib": "~0.2.0",
+ "buffer": "^5.0.2",
+ "cached-path-relative": "^1.0.0",
+ "concat-stream": "^1.6.0",
+ "console-browserify": "^1.1.0",
+ "constants-browserify": "~1.0.0",
+ "crypto-browserify": "^3.0.0",
+ "defined": "^1.0.0",
+ "deps-sort": "^2.0.0",
+ "domain-browser": "^1.2.0",
+ "duplexer2": "~0.1.2",
+ "events": "^2.0.0",
+ "glob": "^7.1.0",
+ "has": "^1.0.0",
+ "htmlescape": "^1.1.0",
+ "https-browserify": "^1.0.0",
+ "inherits": "~2.0.1",
+ "insert-module-globals": "^7.0.0",
+ "labeled-stream-splicer": "^2.0.0",
+ "mkdirp": "^0.5.0",
+ "module-deps": "^6.0.0",
+ "os-browserify": "~0.3.0",
+ "parents": "^1.0.1",
+ "path-browserify": "~0.0.0",
+ "process": "~0.11.0",
+ "punycode": "^1.3.2",
+ "querystring-es3": "~0.2.0",
+ "read-only-stream": "^2.0.0",
+ "readable-stream": "^2.0.2",
+ "resolve": "^1.1.4",
+ "shasum": "^1.0.0",
+ "shell-quote": "^1.6.1",
+ "stream-browserify": "^2.0.0",
+ "stream-http": "^2.0.0",
+ "string_decoder": "^1.1.1",
+ "subarg": "^1.0.0",
+ "syntax-error": "^1.1.1",
+ "through2": "^2.0.0",
+ "timers-browserify": "^1.0.1",
+ "tty-browserify": "0.0.1",
+ "url": "~0.11.0",
+ "util": "~0.10.1",
+ "vm-browserify": "^1.0.0",
+ "xtend": "^4.0.0"
+ },
+ "dependencies": {
+ "browserify-zlib": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+ "dev": true,
+ "requires": {
+ "pako": "~1.0.5"
+ }
+ },
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "pako": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.7.tgz",
+ "integrity": "sha512-3HNK5tW4x8o5mO8RuHZp3Ydw9icZXx0RANAOMzlMzx7LVXhMJ4mo3MOBpzyd7r/+RUu8BmndP47LXT+vzjtWcQ==",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ },
+ "dependencies": {
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "string_decoder": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz",
+ "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "browserify-aes": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+ "dev": true,
+ "requires": {
+ "buffer-xor": "^1.0.3",
+ "cipher-base": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.3",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "browserify-cipher": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
+ "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
+ "dev": true,
+ "requires": {
+ "browserify-aes": "^1.0.4",
+ "browserify-des": "^1.0.0",
+ "evp_bytestokey": "^1.0.0"
+ }
+ },
+ "browserify-des": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
+ "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.1",
+ "des.js": "^1.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "browserify-rsa": {
+ "version": "4.0.1",
+ "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+ "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "randombytes": "^2.0.1"
+ }
+ },
+ "browserify-sign": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
+ "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.1",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "create-hmac": "^1.1.2",
+ "elliptic": "^6.0.0",
+ "inherits": "^2.0.1",
+ "parse-asn1": "^5.0.0"
+ }
+ },
+ "buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz",
+ "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==",
+ "dev": true,
+ "requires": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4"
+ }
+ },
+ "buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+ "dev": true
+ },
+ "buffer-shims": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
+ "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=",
+ "dev": true
+ },
+ "buffer-xor": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
+ "dev": true
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+ "dev": true
+ },
+ "builtin-status-codes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
+ "dev": true
+ },
+ "cache-base": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "dev": true,
+ "requires": {
+ "collection-visit": "^1.0.0",
+ "component-emitter": "^1.2.1",
+ "get-value": "^2.0.6",
+ "has-value": "^1.0.0",
+ "isobject": "^3.0.1",
+ "set-value": "^2.0.0",
+ "to-object-path": "^0.3.0",
+ "union-value": "^1.0.0",
+ "unset-value": "^1.0.0"
+ }
+ },
+ "cached-path-relative": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz",
+ "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+ "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
+ "dev": true
+ },
+ "camelcase-keys": {
+ "version": "2.1.0",
+ "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
+ "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
+ "dev": true,
+ "requires": {
+ "camelcase": "^2.0.0",
+ "map-obj": "^1.0.0"
+ }
+ },
+ "catharsis": {
+ "version": "0.8.9",
+ "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz",
+ "integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=",
+ "requires": {
+ "underscore-contrib": "~0.3.0"
+ }
+ },
+ "chalk": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
+ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "cipher-base": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "class-utils": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "define-property": "^0.2.5",
+ "isobject": "^3.0.0",
+ "static-extend": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "cli": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz",
+ "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=",
+ "dev": true,
+ "requires": {
+ "exit": "0.1.2",
+ "glob": "^7.1.1"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
+ "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "coffeescript": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.10.0.tgz",
+ "integrity": "sha1-56qDAZF+9iGzXYo580jc3R234z4=",
+ "dev": true
+ },
+ "collection-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+ "dev": true,
+ "requires": {
+ "map-visit": "^1.0.0",
+ "object-visit": "^1.0.0"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
+ "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
+ "dev": true
+ },
+ "combine-source-map": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz",
+ "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=",
+ "dev": true,
+ "requires": {
+ "convert-source-map": "~1.1.0",
+ "inline-source-map": "~0.6.0",
+ "lodash.memoize": "~3.0.3",
+ "source-map": "~0.5.3"
+ }
+ },
+ "commander": {
+ "version": "2.20.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
+ "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
+ "dev": true
+ },
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "console-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
+ "dev": true,
+ "requires": {
+ "date-now": "^0.1.4"
+ }
+ },
+ "constants-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+ "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "1.1.3",
+ "resolved": "http://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz",
+ "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=",
+ "dev": true
+ },
+ "copy-descriptor": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true
+ },
+ "create-ecdh": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
+ "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "elliptic": "^6.0.0"
+ }
+ },
+ "create-hash": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.1",
+ "inherits": "^2.0.1",
+ "md5.js": "^1.3.4",
+ "ripemd160": "^2.0.1",
+ "sha.js": "^2.4.0"
+ }
+ },
+ "create-hmac": {
+ "version": "1.1.7",
+ "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.3",
+ "create-hash": "^1.1.0",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "crypto-browserify": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
+ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+ "dev": true,
+ "requires": {
+ "browserify-cipher": "^1.0.0",
+ "browserify-sign": "^4.0.0",
+ "create-ecdh": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "create-hmac": "^1.1.0",
+ "diffie-hellman": "^5.0.0",
+ "inherits": "^2.0.1",
+ "pbkdf2": "^3.0.3",
+ "public-encrypt": "^4.0.0",
+ "randombytes": "^2.0.0",
+ "randomfill": "^1.0.3"
+ }
+ },
+ "currently-unhandled": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
+ "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
+ "dev": true,
+ "requires": {
+ "array-find-index": "^1.0.1"
+ }
+ },
+ "date-now": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+ "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
+ "dev": true
+ },
+ "dateformat": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz",
+ "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=",
+ "dev": true,
+ "requires": {
+ "get-stdin": "^4.0.1",
+ "meow": "^3.3.0"
+ }
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true
+ },
+ "decode-uri-component": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+ "dev": true
+ },
+ "define-property": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.2",
+ "isobject": "^3.0.1"
+ },
+ "dependencies": {
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "defined": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
+ "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
+ "dev": true
+ },
+ "deps-sort": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz",
+ "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=",
+ "dev": true,
+ "requires": {
+ "JSONStream": "^1.0.3",
+ "shasum": "^1.0.0",
+ "subarg": "^1.0.0",
+ "through2": "^2.0.0"
+ }
+ },
+ "des.js": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
+ "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "detect-file": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
+ "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
+ "dev": true
+ },
+ "detective": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/detective/-/detective-5.1.0.tgz",
+ "integrity": "sha512-TFHMqfOvxlgrfVzTEkNBSh9SvSNX/HfF4OFI2QFGCyPm02EsyILqnUeb5P6q7JZ3SFNTBL5t2sePRgrN4epUWQ==",
+ "dev": true,
+ "requires": {
+ "acorn-node": "^1.3.0",
+ "defined": "^1.0.0",
+ "minimist": "^1.1.1"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true
+ }
+ }
+ },
+ "diffie-hellman": {
+ "version": "5.0.3",
+ "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+ "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "miller-rabin": "^4.0.0",
+ "randombytes": "^2.0.0"
+ }
+ },
+ "dom-serializer": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
+ "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^1.3.0",
+ "entities": "^1.1.1"
+ },
+ "dependencies": {
+ "entities": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
+ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
+ "dev": true
+ }
+ }
+ },
+ "domain-browser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
+ "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
+ "dev": true
+ },
+ "domelementtype": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
+ "dev": true
+ },
+ "domhandler": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz",
+ "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=",
+ "dev": true,
+ "requires": {
+ "domelementtype": "1"
+ }
+ },
+ "domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
+ "dev": true,
+ "requires": {
+ "dom-serializer": "0",
+ "domelementtype": "1"
+ }
+ },
+ "duplexer": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
+ "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
+ "dev": true
+ },
+ "duplexer2": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+ "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.0.2"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "elliptic": {
+ "version": "6.4.1",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
+ "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.4.0",
+ "brorand": "^1.0.1",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.0"
+ }
+ },
+ "entities": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
+ "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=",
+ "dev": true
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "es6-promise": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz",
+ "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==",
+ "dev": true
+ },
+ "es6-promisify": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+ "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
+ "dev": true,
+ "requires": {
+ "es6-promise": "^4.0.3"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
+ },
+ "esprima": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
+ "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
+ "dev": true
+ },
+ "eventemitter2": {
+ "version": "0.4.14",
+ "resolved": "http://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
+ "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=",
+ "dev": true
+ },
+ "events": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-2.1.0.tgz",
+ "integrity": "sha512-3Zmiobend8P9DjmKAty0Era4jV8oJ0yGYe2nJJAxgymF9+N8F2m0hhZiMoWtcfepExzNKZumFU3ksdQbInGWCg==",
+ "dev": true
+ },
+ "evp_bytestokey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+ "dev": true,
+ "requires": {
+ "md5.js": "^1.3.4",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+ "dev": true
+ },
+ "expand-brackets": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+ "dev": true,
+ "requires": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "expand-tilde": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
+ "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
+ "dev": true,
+ "requires": {
+ "homedir-polyfill": "^1.0.1"
+ }
+ },
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "extglob": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "dev": true,
+ "requires": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "extract-zip": {
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz",
+ "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=",
+ "dev": true,
+ "requires": {
+ "concat-stream": "1.6.2",
+ "debug": "2.6.9",
+ "mkdirp": "0.5.1",
+ "yauzl": "2.4.1"
+ }
+ },
+ "fd-slicer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
+ "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
+ "dev": true,
+ "requires": {
+ "pend": "~1.2.0"
+ }
+ },
+ "figures": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
+ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5",
+ "object-assign": "^4.1.0"
+ }
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "find-up": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+ "dev": true,
+ "requires": {
+ "path-exists": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "findup-sync": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz",
+ "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=",
+ "dev": true,
+ "requires": {
+ "glob": "~5.0.0"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "5.0.15",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+ "dev": true,
+ "requires": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "fined": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.1.tgz",
+ "integrity": "sha512-jQp949ZmEbiYHk3gkbdtpJ0G1+kgtLQBNdP5edFP7Fh+WAYceLQz6yO1SBj72Xkg8GVyTB3bBzAYrHJVh5Xd5g==",
+ "dev": true,
+ "requires": {
+ "expand-tilde": "^2.0.2",
+ "is-plain-object": "^2.0.3",
+ "object.defaults": "^1.1.0",
+ "object.pick": "^1.2.0",
+ "parse-filepath": "^1.0.1"
+ }
+ },
+ "flagged-respawn": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz",
+ "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==",
+ "dev": true
+ },
+ "for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+ "dev": true
+ },
+ "for-own": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
+ "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
+ "dev": true,
+ "requires": {
+ "for-in": "^1.0.1"
+ }
+ },
+ "fragment-cache": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+ "dev": true,
+ "requires": {
+ "map-cache": "^0.2.2"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "get-assigned-identifiers": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz",
+ "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==",
+ "dev": true
+ },
+ "get-stdin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
+ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
+ "dev": true
+ },
+ "get-value": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+ "dev": true
+ },
+ "getobject": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz",
+ "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
+ "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.2",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "global-modules": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
+ "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
+ "dev": true,
+ "requires": {
+ "global-prefix": "^1.0.1",
+ "is-windows": "^1.0.1",
+ "resolve-dir": "^1.0.0"
+ }
+ },
+ "global-prefix": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
+ "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
+ "dev": true,
+ "requires": {
+ "expand-tilde": "^2.0.2",
+ "homedir-polyfill": "^1.0.1",
+ "ini": "^1.3.4",
+ "is-windows": "^1.0.1",
+ "which": "^1.2.14"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
+ },
+ "grunt": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.3.tgz",
+ "integrity": "sha512-/JzmZNPfKorlCrrmxWqQO4JVodO+DVd5XX4DkocL/1WlLlKVLE9+SdEIempOAxDhWPysLle6afvn/hg7Ck2k9g==",
+ "dev": true,
+ "requires": {
+ "coffeescript": "~1.10.0",
+ "dateformat": "~1.0.12",
+ "eventemitter2": "~0.4.13",
+ "exit": "~0.1.1",
+ "findup-sync": "~0.3.0",
+ "glob": "~7.0.0",
+ "grunt-cli": "~1.2.0",
+ "grunt-known-options": "~1.1.0",
+ "grunt-legacy-log": "~2.0.0",
+ "grunt-legacy-util": "~1.1.1",
+ "iconv-lite": "~0.4.13",
+ "js-yaml": "~3.5.2",
+ "minimatch": "~3.0.2",
+ "mkdirp": "~0.5.1",
+ "nopt": "~3.0.6",
+ "path-is-absolute": "~1.0.0",
+ "rimraf": "~2.6.2"
+ },
+ "dependencies": {
+ "grunt-cli": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz",
+ "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=",
+ "dev": true,
+ "requires": {
+ "findup-sync": "~0.3.0",
+ "grunt-known-options": "~1.1.0",
+ "nopt": "~3.0.6",
+ "resolve": "~1.1.0"
+ }
+ },
+ "nopt": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+ "dev": true,
+ "requires": {
+ "abbrev": "1"
+ }
+ }
+ }
+ },
+ "grunt-cli": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.3.2.tgz",
+ "integrity": "sha512-8OHDiZZkcptxVXtMfDxJvmN7MVJNE8L/yIcPb4HB7TlyFD1kDvjHrb62uhySsU14wJx9ORMnTuhRMQ40lH/orQ==",
+ "dev": true,
+ "requires": {
+ "grunt-known-options": "~1.1.0",
+ "interpret": "~1.1.0",
+ "liftoff": "~2.5.0",
+ "nopt": "~4.0.1",
+ "v8flags": "~3.1.1"
+ }
+ },
+ "grunt-contrib-concat": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-concat/-/grunt-contrib-concat-1.0.1.tgz",
+ "integrity": "sha1-YVCYYwhOhx1+ht5IwBUlntl3Rb0=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.0.0",
+ "source-map": "^0.5.3"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-2.1.0.tgz",
+ "integrity": "sha512-65S2/C/6RfjY/umTxfwXXn+wVvaYmykHkHSsW6Q6rhkbv3oudTEgqnFFZvWzWCoHUb+3GMZLbP3oSrNyvshmIQ==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.2",
+ "hooker": "^0.2.3",
+ "jshint": "~2.10.2"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ }
+ }
+ },
+ "grunt-contrib-qunit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-qunit/-/grunt-contrib-qunit-3.1.0.tgz",
+ "integrity": "sha512-mdk8UltH6mxCD63E0hTXMAts42DOi4z4bBBrY7qnuHiShflMF7IueSMYe0zWaZ2dO8mgujh57Zfny2EbigJhRg==",
+ "dev": true,
+ "requires": {
+ "eventemitter2": "^5.0.1",
+ "p-each-series": "^1.0.0",
+ "puppeteer": "^1.11.0"
+ },
+ "dependencies": {
+ "eventemitter2": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz",
+ "integrity": "sha1-YZegldX7a1folC9v1+qtY6CclFI=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-contrib-uglify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-4.0.1.tgz",
+ "integrity": "sha512-dwf8/+4uW1+7pH72WButOEnzErPGmtUvc8p08B0eQS/6ON0WdeQu0+WFeafaPTbbY1GqtS25lsHWaDeiTQNWPg==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.1",
+ "maxmin": "^2.1.0",
+ "uglify-js": "^3.5.0",
+ "uri-path": "^1.0.0"
+ }
+ },
+ "grunt-jsdoc": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/grunt-jsdoc/-/grunt-jsdoc-2.3.0.tgz",
+ "integrity": "sha512-gC66TCRXeQMj3HIyqVSBJm8zdUz43e5vaG/PLO/627A1edbJnzxhJV7nF0KqLwMM0RDNu1istC6fvfnYqFKi3w==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^6.0.5",
+ "jsdoc": "~3.5.5",
+ "marked": "^0.5.0"
+ },
+ "dependencies": {
+ "marked": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-0.5.1.tgz",
+ "integrity": "sha512-iUkBZegCZou4AdwbKTwSW/lNDcz5OuRSl3qdcl31Ia0B2QPG0Jn+tKblh/9/eP9/6+4h27vpoh8wel/vQOV0vw==",
+ "dev": true
+ }
+ }
+ },
+ "grunt-known-options": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.1.tgz",
+ "integrity": "sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ==",
+ "dev": true
+ },
+ "grunt-legacy-log": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-2.0.0.tgz",
+ "integrity": "sha512-1m3+5QvDYfR1ltr8hjiaiNjddxGdQWcH0rw1iKKiQnF0+xtgTazirSTGu68RchPyh1OBng1bBUjLmX8q9NpoCw==",
+ "dev": true,
+ "requires": {
+ "colors": "~1.1.2",
+ "grunt-legacy-log-utils": "~2.0.0",
+ "hooker": "~0.2.3",
+ "lodash": "~4.17.5"
+ }
+ },
+ "grunt-legacy-log-utils": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.0.1.tgz",
+ "integrity": "sha512-o7uHyO/J+i2tXG8r2bZNlVk20vlIFJ9IEYyHMCQGfWYru8Jv3wTqKZzvV30YW9rWEjq0eP3cflQ1qWojIe9VFA==",
+ "dev": true,
+ "requires": {
+ "chalk": "~2.4.1",
+ "lodash": "~4.17.10"
+ }
+ },
+ "grunt-legacy-util": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.1.1.tgz",
+ "integrity": "sha512-9zyA29w/fBe6BIfjGENndwoe1Uy31BIXxTH3s8mga0Z5Bz2Sp4UCjkeyv2tI449ymkx3x26B+46FV4fXEddl5A==",
+ "dev": true,
+ "requires": {
+ "async": "~1.5.2",
+ "exit": "~0.1.1",
+ "getobject": "~0.1.0",
+ "hooker": "~0.2.3",
+ "lodash": "~4.17.10",
+ "underscore.string": "~3.3.4",
+ "which": "~1.3.0"
+ }
+ },
+ "grunt-shell-spawn": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/grunt-shell-spawn/-/grunt-shell-spawn-0.4.0.tgz",
+ "integrity": "sha512-lfYvEQjbO1Wv+1Fk3d3XlcEpuQjyXiErZMkiz/i/tDQeMHHGF1LziqA4ZcietBAo/bM2RHdEEUJfnNWt1VRMwQ==",
+ "dev": true,
+ "requires": {
+ "grunt": ">=0.4.x"
+ }
+ },
+ "gzip-size": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz",
+ "integrity": "sha1-VGGI6b3DN/Zzdy+BZgRks4nc5SA=",
+ "dev": true,
+ "requires": {
+ "duplexer": "^0.1.1"
+ }
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "has-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.6",
+ "has-values": "^1.0.0",
+ "isobject": "^3.0.0"
+ }
+ },
+ "has-values": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "kind-of": "^4.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "hash-base": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
+ "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "hash.js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+ "dev": true,
+ "requires": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "homedir-polyfill": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz",
+ "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=",
+ "dev": true,
+ "requires": {
+ "parse-passwd": "^1.0.0"
+ }
+ },
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz",
+ "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=",
+ "dev": true
+ },
+ "hosted-git-info": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
+ "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
+ "dev": true
+ },
+ "htmlescape": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz",
+ "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=",
+ "dev": true
+ },
+ "htmlparser2": {
+ "version": "3.8.3",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
+ "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=",
+ "dev": true,
+ "requires": {
+ "domelementtype": "1",
+ "domhandler": "2.3",
+ "domutils": "1.5",
+ "entities": "1.0",
+ "readable-stream": "1.1"
+ }
+ },
+ "https-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
+ "dev": true
+ },
+ "https-proxy-agent": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz",
+ "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==",
+ "dev": true,
+ "requires": {
+ "agent-base": "^4.1.0",
+ "debug": "^3.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ }
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ieee754": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz",
+ "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==",
+ "dev": true
+ },
+ "indent-string": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
+ "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
+ "dev": true,
+ "requires": {
+ "repeating": "^2.0.0"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ },
+ "ini": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+ "dev": true
+ },
+ "inline-source-map": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz",
+ "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=",
+ "dev": true,
+ "requires": {
+ "source-map": "~0.5.3"
+ }
+ },
+ "insert-module-globals": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.0.tgz",
+ "integrity": "sha512-VE6NlW+WGn2/AeOMd496AHFYmE7eLKkUY6Ty31k4og5vmA3Fjuwe9v6ifH6Xx/Hz27QvdoMoviw1/pqWRB09Sw==",
+ "dev": true,
+ "requires": {
+ "JSONStream": "^1.0.3",
+ "acorn-node": "^1.5.2",
+ "combine-source-map": "^0.8.0",
+ "concat-stream": "^1.6.1",
+ "is-buffer": "^1.1.0",
+ "path-is-absolute": "^1.0.1",
+ "process": "~0.11.0",
+ "through2": "^2.0.0",
+ "undeclared-identifiers": "^1.1.2",
+ "xtend": "^4.0.0"
+ }
+ },
+ "interpret": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
+ "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=",
+ "dev": true
+ },
+ "is-absolute": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
+ "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
+ "dev": true,
+ "requires": {
+ "is-relative": "^1.0.0",
+ "is-windows": "^1.0.1"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "is-builtin-module": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
+ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
+ "dev": true,
+ "requires": {
+ "builtin-modules": "^1.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-finite": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
+ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "is-relative": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
+ "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
+ "dev": true,
+ "requires": {
+ "is-unc-path": "^1.0.0"
+ }
+ },
+ "is-unc-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
+ "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
+ "dev": true,
+ "requires": {
+ "unc-path-regex": "^0.1.2"
+ }
+ },
+ "is-utf8": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
+ "dev": true
+ },
+ "is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.5.5",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.5.5.tgz",
+ "integrity": "sha1-A3fDgBfKvHMisNH7zSWkkWQfL74=",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.2",
+ "esprima": "^2.6.0"
+ }
+ },
+ "js2xmlparser": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-3.0.0.tgz",
+ "integrity": "sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=",
+ "requires": {
+ "xmlcreate": "^1.0.1"
+ }
+ },
+ "jsdoc": {
+ "version": "3.5.5",
+ "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.5.tgz",
+ "integrity": "sha512-6PxB65TAU4WO0Wzyr/4/YhlGovXl0EVYfpKbpSroSj0qBxT4/xod/l40Opkm38dRHRdQgdeY836M0uVnJQG7kg==",
+ "requires": {
+ "babylon": "7.0.0-beta.19",
+ "bluebird": "~3.5.0",
+ "catharsis": "~0.8.9",
+ "escape-string-regexp": "~1.0.5",
+ "js2xmlparser": "~3.0.0",
+ "klaw": "~2.0.0",
+ "marked": "~0.3.6",
+ "mkdirp": "~0.5.1",
+ "requizzle": "~0.2.1",
+ "strip-json-comments": "~2.0.1",
+ "taffydb": "2.6.2",
+ "underscore": "~1.8.3"
+ }
+ },
+ "jshint": {
+ "version": "2.10.2",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.10.2.tgz",
+ "integrity": "sha512-e7KZgCSXMJxznE/4WULzybCMNXNAd/bf5TSrvVEq78Q/K8ZwFpmBqQeDtNiHc3l49nV4E/+YeHU/JZjSUIrLAA==",
+ "dev": true,
+ "requires": {
+ "cli": "~1.0.0",
+ "console-browserify": "1.1.x",
+ "exit": "0.1.x",
+ "htmlparser2": "3.8.x",
+ "lodash": "~4.17.11",
+ "minimatch": "~3.0.2",
+ "shelljs": "0.3.x",
+ "strip-json-comments": "1.0.x"
+ },
+ "dependencies": {
+ "strip-json-comments": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
+ "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=",
+ "dev": true
+ }
+ }
+ },
+ "jslint": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/jslint/-/jslint-0.12.0.tgz",
+ "integrity": "sha512-RoCsyICcKA+6TFsbys9DpKTfPVaC71Mm5QSjvrWA0lDVN+LIvx6apa42FFisMqmCTvJ8DxkcoQGJ0j7m3kTVow==",
+ "dev": true,
+ "requires": {
+ "exit": "~0.1.2",
+ "glob": "~7.1.2",
+ "nopt": "~3.0.1",
+ "readable-stream": "~2.1.5"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "nopt": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+ "dev": true,
+ "requires": {
+ "abbrev": "1"
+ }
+ },
+ "process-nextick-args": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+ "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "2.1.5",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz",
+ "integrity": "sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA=",
+ "dev": true,
+ "requires": {
+ "buffer-shims": "^1.0.0",
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~1.0.6",
+ "string_decoder": "~0.10.x",
+ "util-deprecate": "~1.0.1"
+ }
+ }
+ }
+ },
+ "json-int64": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-int64/-/json-int64-1.0.0.tgz",
+ "integrity": "sha512-yrTg9swToElhEPETLMdZkEzDhbXLs+cxkw/b2rglMPOBlM1DE0utH1EReSMLcnpYJk5iUvD12r0fP2/xHitF5Q==",
+ "requires": {
+ "node-int64": "0.4.0"
+ }
+ },
+ "json-stable-stringify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz",
+ "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=",
+ "dev": true,
+ "requires": {
+ "jsonify": "~0.0.0"
+ }
+ },
+ "jsonify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
+ "dev": true
+ },
+ "jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+ "dev": true
+ },
+ "klaw": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/klaw/-/klaw-2.0.0.tgz",
+ "integrity": "sha1-WcEo4Nxc5BAgEVEZTuucv4WGUPY=",
+ "requires": {
+ "graceful-fs": "^4.1.9"
+ }
+ },
+ "labeled-stream-splicer": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.1.tgz",
+ "integrity": "sha512-MC94mHZRvJ3LfykJlTUipBqenZz1pacOZEMhhQ8dMGcDHs0SBE5GbsavUXV7YtP3icBW17W0Zy1I0lfASmo9Pg==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "isarray": "^2.0.4",
+ "stream-splicer": "^2.0.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.4.tgz",
+ "integrity": "sha512-GMxXOiUirWg1xTKRipM0Ek07rX+ubx4nNVElTJdNLYmNO/2YrDkgJGw9CljXn+r4EWiDQg/8lsRdHyg2PJuUaA==",
+ "dev": true
+ }
+ }
+ },
+ "liftoff": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz",
+ "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=",
+ "dev": true,
+ "requires": {
+ "extend": "^3.0.0",
+ "findup-sync": "^2.0.0",
+ "fined": "^1.0.1",
+ "flagged-respawn": "^1.0.0",
+ "is-plain-object": "^2.0.4",
+ "object.map": "^1.0.0",
+ "rechoir": "^0.6.2",
+ "resolve": "^1.1.7"
+ },
+ "dependencies": {
+ "findup-sync": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz",
+ "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=",
+ "dev": true,
+ "requires": {
+ "detect-file": "^1.0.0",
+ "is-glob": "^3.1.0",
+ "micromatch": "^3.0.4",
+ "resolve-dir": "^1.0.1"
+ }
+ }
+ }
+ },
+ "load-json-file": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "strip-bom": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.11",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+ "dev": true
+ },
+ "lodash.memoize": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz",
+ "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=",
+ "dev": true
+ },
+ "loud-rejection": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
+ "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
+ "dev": true,
+ "requires": {
+ "currently-unhandled": "^0.4.1",
+ "signal-exit": "^3.0.0"
+ }
+ },
+ "make-iterator": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
+ "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.2"
+ }
+ },
+ "map-cache": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+ "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+ "dev": true
+ },
+ "map-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+ "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
+ "dev": true
+ },
+ "map-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+ "dev": true,
+ "requires": {
+ "object-visit": "^1.0.0"
+ }
+ },
+ "marked": {
+ "version": "0.3.19",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz",
+ "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg=="
+ },
+ "maxmin": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-2.1.0.tgz",
+ "integrity": "sha1-TTsiCQPZXu5+t6x/qGTnLcCaMWY=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.0.0",
+ "figures": "^1.0.1",
+ "gzip-size": "^3.0.0",
+ "pretty-bytes": "^3.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "md5.js": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+ "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+ "dev": true,
+ "requires": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "meow": {
+ "version": "3.7.0",
+ "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+ "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
+ "dev": true,
+ "requires": {
+ "camelcase-keys": "^2.0.0",
+ "decamelize": "^1.1.2",
+ "loud-rejection": "^1.0.0",
+ "map-obj": "^1.0.1",
+ "minimist": "^1.1.3",
+ "normalize-package-data": "^2.3.4",
+ "object-assign": "^4.0.1",
+ "read-pkg-up": "^1.0.1",
+ "redent": "^1.0.0",
+ "trim-newlines": "^1.0.0"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true
+ }
+ }
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ }
+ },
+ "miller-rabin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.0.0",
+ "brorand": "^1.0.1"
+ }
+ },
+ "mime": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz",
+ "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==",
+ "dev": true
+ },
+ "minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "dev": true
+ },
+ "minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ },
+ "mixin-deep": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
+ "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
+ "dev": true,
+ "requires": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "module-deps": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.0.tgz",
+ "integrity": "sha512-hKPmO06so6bL/ZvqVNVqdTVO8UAYsi3tQWlCa+z9KuWhoN4KDQtb5hcqQQv58qYiDE21wIvnttZEPiDgEbpwbA==",
+ "dev": true,
+ "requires": {
+ "JSONStream": "^1.0.3",
+ "browser-resolve": "^1.7.0",
+ "cached-path-relative": "^1.0.0",
+ "concat-stream": "~1.6.0",
+ "defined": "^1.0.0",
+ "detective": "^5.0.2",
+ "duplexer2": "^0.1.2",
+ "inherits": "^2.0.1",
+ "parents": "^1.0.0",
+ "readable-stream": "^2.0.2",
+ "resolve": "^1.4.0",
+ "stream-combiner2": "^1.1.1",
+ "subarg": "^1.0.0",
+ "through2": "^2.0.0",
+ "xtend": "^4.0.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "resolve": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
+ "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.5"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "nanomatch": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "fragment-cache": "^0.2.1",
+ "is-windows": "^1.0.2",
+ "kind-of": "^6.0.2",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ }
+ },
+ "nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
+ },
+ "node-int64": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+ "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs="
+ },
+ "nopt": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
+ "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
+ "requires": {
+ "abbrev": "1",
+ "osenv": "^0.1.4"
+ }
+ },
+ "normalize-package-data": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "is-builtin-module": "^1.0.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true
+ },
+ "object-copy": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+ "dev": true,
+ "requires": {
+ "copy-descriptor": "^0.1.0",
+ "define-property": "^0.2.5",
+ "kind-of": "^3.0.3"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "object-visit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.0"
+ }
+ },
+ "object.defaults": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz",
+ "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=",
+ "dev": true,
+ "requires": {
+ "array-each": "^1.0.1",
+ "array-slice": "^1.0.0",
+ "for-own": "^1.0.0",
+ "isobject": "^3.0.0"
+ }
+ },
+ "object.map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
+ "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=",
+ "dev": true,
+ "requires": {
+ "for-own": "^1.0.0",
+ "make-iterator": "^1.0.0"
+ }
+ },
+ "object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "os-browserify": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
+ "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
+ "dev": true
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
+ },
+ "osenv": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+ "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "p-each-series": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz",
+ "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=",
+ "dev": true,
+ "requires": {
+ "p-reduce": "^1.0.0"
+ }
+ },
+ "p-reduce": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz",
+ "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=",
+ "dev": true
+ },
+ "parents": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz",
+ "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=",
+ "dev": true,
+ "requires": {
+ "path-platform": "~0.11.15"
+ }
+ },
+ "parse-asn1": {
+ "version": "5.1.1",
+ "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz",
+ "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==",
+ "dev": true,
+ "requires": {
+ "asn1.js": "^4.0.0",
+ "browserify-aes": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.0",
+ "pbkdf2": "^3.0.3"
+ }
+ },
+ "parse-filepath": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz",
+ "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=",
+ "dev": true,
+ "requires": {
+ "is-absolute": "^1.0.0",
+ "map-cache": "^0.2.0",
+ "path-root": "^0.1.1"
+ }
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.2.0"
+ }
+ },
+ "parse-passwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
+ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
+ "dev": true
+ },
+ "pascalcase": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+ "dev": true
+ },
+ "path-browserify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
+ "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+ "dev": true,
+ "requires": {
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "path-platform": {
+ "version": "0.11.15",
+ "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz",
+ "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=",
+ "dev": true
+ },
+ "path-root": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz",
+ "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=",
+ "dev": true,
+ "requires": {
+ "path-root-regex": "^0.1.0"
+ }
+ },
+ "path-root-regex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz",
+ "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=",
+ "dev": true
+ },
+ "path-type": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "pbkdf2": {
+ "version": "3.0.17",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
+ "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
+ "dev": true,
+ "requires": {
+ "create-hash": "^1.1.2",
+ "create-hmac": "^1.1.4",
+ "ripemd160": "^2.0.1",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
+ "dev": true
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "dev": true,
+ "requires": {
+ "pinkie": "^2.0.0"
+ }
+ },
+ "posix-character-classes": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+ "dev": true
+ },
+ "pretty-bytes": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-3.0.1.tgz",
+ "integrity": "sha1-J9AAjXeAY6C0gRuzXHnxvV1fvM8=",
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
+ "dev": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
+ "dev": true
+ },
+ "proxy-from-env": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz",
+ "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=",
+ "dev": true
+ },
+ "public-encrypt": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
+ "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "parse-asn1": "^5.0.0",
+ "randombytes": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+ "dev": true
+ },
+ "puppeteer": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.11.0.tgz",
+ "integrity": "sha512-iG4iMOHixc2EpzqRV+pv7o3GgmU2dNYEMkvKwSaQO/vMZURakwSOn/EYJ6OIRFYOque1qorzIBvrytPIQB3YzQ==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "extract-zip": "^1.6.6",
+ "https-proxy-agent": "^2.2.1",
+ "mime": "^2.0.3",
+ "progress": "^2.0.1",
+ "proxy-from-env": "^1.0.0",
+ "rimraf": "^2.6.1",
+ "ws": "^6.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ },
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true
+ }
+ }
+ },
+ "querystring": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+ "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
+ "dev": true
+ },
+ "querystring-es3": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
+ "dev": true
+ },
+ "randombytes": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz",
+ "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "randomfill": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
+ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.0.5",
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "read-only-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz",
+ "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.0.2"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "read-pkg": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^1.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^1.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+ "dev": true,
+ "requires": {
+ "find-up": "^1.0.0",
+ "read-pkg": "^1.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ }
+ }
+ },
+ "rechoir": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
+ "dev": true,
+ "requires": {
+ "resolve": "^1.1.6"
+ }
+ },
+ "redent": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
+ "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
+ "dev": true,
+ "requires": {
+ "indent-string": "^2.1.0",
+ "strip-indent": "^1.0.1"
+ }
+ },
+ "regex-not": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "repeat-element": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+ "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
+ "dev": true
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+ "dev": true
+ },
+ "repeating": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+ "dev": true,
+ "requires": {
+ "is-finite": "^1.0.0"
+ }
+ },
+ "requizzle": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz",
+ "integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=",
+ "requires": {
+ "underscore": "~1.6.0"
+ },
+ "dependencies": {
+ "underscore": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
+ "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
+ }
+ }
+ },
+ "resolve": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+ "dev": true
+ },
+ "resolve-dir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
+ "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
+ "dev": true,
+ "requires": {
+ "expand-tilde": "^2.0.0",
+ "global-modules": "^1.0.0"
+ }
+ },
+ "resolve-url": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+ "dev": true
+ },
+ "ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
+ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.0.5"
+ }
+ },
+ "ripemd160": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
+ "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
+ "dev": true,
+ "requires": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "safe-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+ "dev": true,
+ "requires": {
+ "ret": "~0.1.10"
+ }
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
+ "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
+ "dev": true
+ },
+ "set-value": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
+ "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "sha.js": {
+ "version": "2.4.11",
+ "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "shasum": {
+ "version": "1.0.2",
+ "resolved": "http://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz",
+ "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=",
+ "dev": true,
+ "requires": {
+ "json-stable-stringify": "~0.0.0",
+ "sha.js": "~2.4.4"
+ }
+ },
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+ "dev": true
+ },
+ "shell-quote": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz",
+ "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=",
+ "dev": true,
+ "requires": {
+ "array-filter": "~0.0.0",
+ "array-map": "~0.0.0",
+ "array-reduce": "~0.0.0",
+ "jsonify": "~0.0.0"
+ }
+ },
+ "shelljs": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz",
+ "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=",
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+ "dev": true
+ },
+ "simple-concat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
+ "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=",
+ "dev": true
+ },
+ "snapdragon": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "dev": true,
+ "requires": {
+ "base": "^0.11.1",
+ "debug": "^2.2.0",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "map-cache": "^0.2.2",
+ "source-map": "^0.5.6",
+ "source-map-resolve": "^0.5.0",
+ "use": "^3.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "snapdragon-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.0",
+ "snapdragon-util": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "snapdragon-util": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.2.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ },
+ "source-map-resolve": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
+ "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==",
+ "dev": true,
+ "requires": {
+ "atob": "^2.1.1",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "source-map-url": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
+ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
+ "dev": true
+ },
+ "spdx-correct": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz",
+ "integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
+ "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+ "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz",
+ "integrity": "sha512-TfOfPcYGBB5sDuPn3deByxPhmfegAhpDYKSOXZQN81Oyrrif8ZCodOLzK3AesELnCx03kikhyDwh0pfvvQvF8w==",
+ "dev": true
+ },
+ "split-string": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.0"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz",
+ "integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=",
+ "dev": true
+ },
+ "static-extend": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+ "dev": true,
+ "requires": {
+ "define-property": "^0.2.5",
+ "object-copy": "^0.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "stream-browserify": {
+ "version": "2.0.1",
+ "resolved": "http://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz",
+ "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=",
+ "dev": true,
+ "requires": {
+ "inherits": "~2.0.1",
+ "readable-stream": "^2.0.2"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "stream-combiner2": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz",
+ "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=",
+ "dev": true,
+ "requires": {
+ "duplexer2": "~0.1.0",
+ "readable-stream": "^2.0.2"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "stream-http": {
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz",
+ "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==",
+ "dev": true,
+ "requires": {
+ "builtin-status-codes": "^3.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.3.6",
+ "to-arraybuffer": "^1.0.0",
+ "xtend": "^4.0.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "stream-splicer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz",
+ "integrity": "sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.2"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-bom": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+ "dev": true,
+ "requires": {
+ "is-utf8": "^0.2.0"
+ }
+ },
+ "strip-indent": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
+ "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
+ "dev": true,
+ "requires": {
+ "get-stdin": "^4.0.1"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
+ },
+ "subarg": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz",
+ "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.1.0"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true
+ }
+ }
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "syntax-error": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz",
+ "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==",
+ "dev": true,
+ "requires": {
+ "acorn-node": "^1.2.0"
+ }
+ },
+ "taffydb": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz",
+ "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg="
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+ "dev": true
+ },
+ "through2": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dev": true,
+ "requires": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "timers-browserify": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz",
+ "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=",
+ "dev": true,
+ "requires": {
+ "process": "~0.11.0"
+ }
+ },
+ "to-arraybuffer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
+ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=",
+ "dev": true
+ },
+ "to-object-path": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "to-regex": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "regex-not": "^1.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ },
+ "trim-newlines": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
+ "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
+ "dev": true
+ },
+ "tty-browserify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz",
+ "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==",
+ "dev": true
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+ "dev": true
+ },
+ "uglify-js": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz",
+ "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==",
+ "dev": true,
+ "requires": {
+ "commander": "~2.20.0",
+ "source-map": "~0.6.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "umd": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz",
+ "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==",
+ "dev": true
+ },
+ "unc-path-regex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
+ "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=",
+ "dev": true
+ },
+ "undeclared-identifiers": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.2.tgz",
+ "integrity": "sha512-13EaeocO4edF/3JKime9rD7oB6QI8llAGhgn5fKOPyfkJbRb6NFv9pYV6dFEmpa4uRjKeBqLZP8GpuzqHlKDMQ==",
+ "dev": true,
+ "requires": {
+ "acorn-node": "^1.3.0",
+ "get-assigned-identifiers": "^1.2.0",
+ "simple-concat": "^1.0.0",
+ "xtend": "^4.0.1"
+ }
+ },
+ "underscore": {
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
+ "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI="
+ },
+ "underscore-contrib": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz",
+ "integrity": "sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=",
+ "requires": {
+ "underscore": "1.6.0"
+ },
+ "dependencies": {
+ "underscore": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
+ "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
+ }
+ }
+ },
+ "underscore.string": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz",
+ "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "^1.0.3",
+ "util-deprecate": "^1.0.2"
+ }
+ },
+ "union-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
+ "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^0.4.3"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "set-value": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz",
+ "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.1",
+ "to-object-path": "^0.3.0"
+ }
+ }
+ }
+ },
+ "unset-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+ "dev": true,
+ "requires": {
+ "has-value": "^0.3.1",
+ "isobject": "^3.0.0"
+ },
+ "dependencies": {
+ "has-value": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.3",
+ "has-values": "^0.1.4",
+ "isobject": "^2.0.0"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "dev": true,
+ "requires": {
+ "isarray": "1.0.0"
+ }
+ }
+ }
+ },
+ "has-values": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+ "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+ "dev": true
+ }
+ }
+ },
+ "uri-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/uri-path/-/uri-path-1.0.0.tgz",
+ "integrity": "sha1-l0fwGDWJM8Md4PzP2C0TjmcmLjI=",
+ "dev": true
+ },
+ "urix": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+ "dev": true
+ },
+ "url": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+ "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+ "dev": true,
+ "requires": {
+ "punycode": "1.3.2",
+ "querystring": "0.2.0"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+ "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
+ "dev": true
+ }
+ }
+ },
+ "use": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+ "dev": true
+ },
+ "util": {
+ "version": "0.10.4",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
+ "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "v8flags": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.2.tgz",
+ "integrity": "sha512-MtivA7GF24yMPte9Rp/BWGCYQNaUj86zeYxV/x2RRJMKagImbbv3u8iJC57lNhWLPcGLJmHcHmFWkNsplbbLWw==",
+ "dev": true,
+ "requires": {
+ "homedir-polyfill": "^1.0.1"
+ }
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "vm-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz",
+ "integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==",
+ "dev": true
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "ws": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.3.tgz",
+ "integrity": "sha512-tbSxiT+qJI223AP4iLfQbkbxkwdFcneYinM2+x46Gx2wgvbaOMO36czfdfVUBRTHvzAMRhDd98sA5d/BuWbQdg==",
+ "dev": true,
+ "requires": {
+ "async-limiter": "~1.0.0"
+ }
+ },
+ "xmlcreate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz",
+ "integrity": "sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8="
+ },
+ "xtend": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
+ "dev": true
+ },
+ "yauzl": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz",
+ "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=",
+ "dev": true,
+ "requires": {
+ "fd-slicer": "~1.0.1"
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/js/package.json b/src/jaegertracing/thrift/lib/js/package.json
new file mode 100644
index 000000000..309622dfd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "thrift",
+ "version": "0.13.0",
+ "description": "Thrift is a software framework for scalable cross-language services development.",
+ "author": {
+ "name": "Apache Thrift Developers",
+ "email": "dev@thrift.apache.org"
+ },
+ "bugs": "https://issues.apache.org/jira/projects/THRIFT/summary",
+ "homepage": "http://thrift.apache.org",
+ "repository": "https://github.com/apache/thrift",
+ "license": "Apache-2.0",
+ "devDependencies": {
+ "browserify": "~16.2",
+ "grunt": "~1.0",
+ "grunt-cli": "~1.3",
+ "grunt-contrib-concat": "~1.0",
+ "grunt-contrib-jshint": "~2.1",
+ "grunt-contrib-qunit": "~3.1",
+ "grunt-contrib-uglify": "~4.0",
+ "grunt-jsdoc": "~2.3",
+ "grunt-shell-spawn": "~0.4",
+ "jslint": "~0.12",
+ "node-int64": "~0.4.0"
+ },
+ "dependencies": {
+ "jsdoc": "~3.5",
+ "json-int64": "~1.0",
+ "nopt": "~4.0"
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/js/src/thrift.js b/src/jaegertracing/thrift/lib/js/src/thrift.js
new file mode 100644
index 000000000..21a3d65fc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/src/thrift.js
@@ -0,0 +1,1624 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*jshint evil:true*/
+
+/**
+ * The Thrift namespace houses the Apache Thrift JavaScript library
+ * elements providing JavaScript bindings for the Apache Thrift RPC
+ * system. End users will typically only directly make use of the
+ * Transport (TXHRTransport/TWebSocketTransport) and Protocol
+ * (TJSONPRotocol/TBinaryProtocol) constructors.
+ *
+ * Object methods beginning with a __ (e.g. __onOpen()) are internal
+ * and should not be called outside of the object's own methods.
+ *
+ * This library creates one global object: Thrift
+ * Code in this library must never create additional global identifiers,
+ * all features must be scoped within the Thrift namespace.
+ * @namespace
+ * @example
+ * var transport = new Thrift.Transport('http://localhost:8585');
+ * var protocol = new Thrift.Protocol(transport);
+ * var client = new MyThriftSvcClient(protocol);
+ * var result = client.MyMethod();
+ */
+var Thrift = {
+ /**
+ * Thrift JavaScript library version.
+ * @readonly
+ * @const {string} Version
+ * @memberof Thrift
+ */
+ Version: '0.13.0',
+
+ /**
+ * Thrift IDL type string to Id mapping.
+ * @readonly
+ * @property {number} STOP - End of a set of fields.
+ * @property {number} VOID - No value (only legal for return types).
+ * @property {number} BOOL - True/False integer.
+ * @property {number} BYTE - Signed 8 bit integer.
+ * @property {number} I08 - Signed 8 bit integer.
+ * @property {number} DOUBLE - 64 bit IEEE 854 floating point.
+ * @property {number} I16 - Signed 16 bit integer.
+ * @property {number} I32 - Signed 32 bit integer.
+ * @property {number} I64 - Signed 64 bit integer.
+ * @property {number} STRING - Array of bytes representing a string of characters.
+ * @property {number} UTF7 - Array of bytes representing a string of UTF7 encoded characters.
+ * @property {number} STRUCT - A multifield type.
+ * @property {number} MAP - A collection type (map/associative-array/dictionary).
+ * @property {number} SET - A collection type (unordered and without repeated values).
+ * @property {number} LIST - A collection type (unordered).
+ * @property {number} UTF8 - Array of bytes representing a string of UTF8 encoded characters.
+ * @property {number} UTF16 - Array of bytes representing a string of UTF16 encoded characters.
+ */
+ Type: {
+ STOP: 0,
+ VOID: 1,
+ BOOL: 2,
+ BYTE: 3,
+ I08: 3,
+ DOUBLE: 4,
+ I16: 6,
+ I32: 8,
+ I64: 10,
+ STRING: 11,
+ UTF7: 11,
+ STRUCT: 12,
+ MAP: 13,
+ SET: 14,
+ LIST: 15,
+ UTF8: 16,
+ UTF16: 17
+ },
+
+ /**
+ * Thrift RPC message type string to Id mapping.
+ * @readonly
+ * @property {number} CALL - RPC call sent from client to server.
+ * @property {number} REPLY - RPC call normal response from server to client.
+ * @property {number} EXCEPTION - RPC call exception response from server to client.
+ * @property {number} ONEWAY - Oneway RPC call from client to server with no response.
+ */
+ MessageType: {
+ CALL: 1,
+ REPLY: 2,
+ EXCEPTION: 3,
+ ONEWAY: 4
+ },
+
+ /**
+ * Utility function returning the count of an object's own properties.
+ * @param {object} obj - Object to test.
+ * @returns {number} number of object's own properties
+ */
+ objectLength: function(obj) {
+ var length = 0;
+ for (var k in obj) {
+ if (obj.hasOwnProperty(k)) {
+ length++;
+ }
+ }
+ return length;
+ },
+
+ /**
+ * Utility function to establish prototype inheritance.
+ * @see {@link http://javascript.crockford.com/prototypal.html|Prototypal Inheritance}
+ * @param {function} constructor - Contstructor function to set as derived.
+ * @param {function} superConstructor - Contstructor function to set as base.
+ * @param {string} [name] - Type name to set as name property in derived prototype.
+ */
+ inherits: function(constructor, superConstructor, name) {
+ function F() {}
+ F.prototype = superConstructor.prototype;
+ constructor.prototype = new F();
+ constructor.prototype.name = name || '';
+ }
+};
+
+/**
+ * Initializes a Thrift TException instance.
+ * @constructor
+ * @augments Error
+ * @param {string} message - The TException message (distinct from the Error message).
+ * @classdesc TException is the base class for all Thrift exceptions types.
+ */
+Thrift.TException = function(message) {
+ this.message = message;
+};
+Thrift.inherits(Thrift.TException, Error, 'TException');
+
+/**
+ * Returns the message set on the exception.
+ * @readonly
+ * @returns {string} exception message
+ */
+Thrift.TException.prototype.getMessage = function() {
+ return this.message;
+};
+
+/**
+ * Thrift Application Exception type string to Id mapping.
+ * @readonly
+ * @property {number} UNKNOWN - Unknown/undefined.
+ * @property {number} UNKNOWN_METHOD - Client attempted to call a method unknown to the server.
+ * @property {number} INVALID_MESSAGE_TYPE - Client passed an unknown/unsupported MessageType.
+ * @property {number} WRONG_METHOD_NAME - Unused.
+ * @property {number} BAD_SEQUENCE_ID - Unused in Thrift RPC, used to flag proprietary sequence number errors.
+ * @property {number} MISSING_RESULT - Raised by a server processor if a handler fails to supply the required return result.
+ * @property {number} INTERNAL_ERROR - Something bad happened.
+ * @property {number} PROTOCOL_ERROR - The protocol layer failed to serialize or deserialize data.
+ * @property {number} INVALID_TRANSFORM - Unused.
+ * @property {number} INVALID_PROTOCOL - The protocol (or version) is not supported.
+ * @property {number} UNSUPPORTED_CLIENT_TYPE - Unused.
+ */
+Thrift.TApplicationExceptionType = {
+ UNKNOWN: 0,
+ UNKNOWN_METHOD: 1,
+ INVALID_MESSAGE_TYPE: 2,
+ WRONG_METHOD_NAME: 3,
+ BAD_SEQUENCE_ID: 4,
+ MISSING_RESULT: 5,
+ INTERNAL_ERROR: 6,
+ PROTOCOL_ERROR: 7,
+ INVALID_TRANSFORM: 8,
+ INVALID_PROTOCOL: 9,
+ UNSUPPORTED_CLIENT_TYPE: 10
+};
+
+/**
+ * Initializes a Thrift TApplicationException instance.
+ * @constructor
+ * @augments Thrift.TException
+ * @param {string} message - The TApplicationException message (distinct from the Error message).
+ * @param {Thrift.TApplicationExceptionType} [code] - The TApplicationExceptionType code.
+ * @classdesc TApplicationException is the exception class used to propagate exceptions from an RPC server back to a calling client.
+*/
+Thrift.TApplicationException = function(message, code) {
+ this.message = message;
+ this.code = typeof code === 'number' ? code : 0;
+};
+Thrift.inherits(Thrift.TApplicationException, Thrift.TException, 'TApplicationException');
+
+/**
+ * Read a TApplicationException from the supplied protocol.
+ * @param {object} input - The input protocol to read from.
+ */
+Thrift.TApplicationException.prototype.read = function(input) {
+ while (1) {
+ var ret = input.readFieldBegin();
+
+ if (ret.ftype == Thrift.Type.STOP) {
+ break;
+ }
+
+ var fid = ret.fid;
+
+ switch (fid) {
+ case 1:
+ if (ret.ftype == Thrift.Type.STRING) {
+ ret = input.readString();
+ this.message = ret.value;
+ } else {
+ ret = input.skip(ret.ftype);
+ }
+ break;
+ case 2:
+ if (ret.ftype == Thrift.Type.I32) {
+ ret = input.readI32();
+ this.code = ret.value;
+ } else {
+ ret = input.skip(ret.ftype);
+ }
+ break;
+ default:
+ ret = input.skip(ret.ftype);
+ break;
+ }
+
+ input.readFieldEnd();
+ }
+
+ input.readStructEnd();
+};
+
+/**
+ * Wite a TApplicationException to the supplied protocol.
+ * @param {object} output - The output protocol to write to.
+ */
+Thrift.TApplicationException.prototype.write = function(output) {
+ output.writeStructBegin('TApplicationException');
+
+ if (this.message) {
+ output.writeFieldBegin('message', Thrift.Type.STRING, 1);
+ output.writeString(this.getMessage());
+ output.writeFieldEnd();
+ }
+
+ if (this.code) {
+ output.writeFieldBegin('type', Thrift.Type.I32, 2);
+ output.writeI32(this.code);
+ output.writeFieldEnd();
+ }
+
+ output.writeFieldStop();
+ output.writeStructEnd();
+};
+
+/**
+ * Returns the application exception code set on the exception.
+ * @readonly
+ * @returns {Thrift.TApplicationExceptionType} exception code
+ */
+Thrift.TApplicationException.prototype.getCode = function() {
+ return this.code;
+};
+
+Thrift.TProtocolExceptionType = {
+ UNKNOWN: 0,
+ INVALID_DATA: 1,
+ NEGATIVE_SIZE: 2,
+ SIZE_LIMIT: 3,
+ BAD_VERSION: 4,
+ NOT_IMPLEMENTED: 5,
+ DEPTH_LIMIT: 6
+};
+
+Thrift.TProtocolException = function TProtocolException(type, message) {
+ Error.call(this);
+ Error.captureStackTrace(this, this.constructor);
+ this.name = this.constructor.name;
+ this.type = type;
+ this.message = message;
+};
+Thrift.inherits(Thrift.TProtocolException, Thrift.TException, 'TProtocolException');
+
+/**
+ * Constructor Function for the XHR transport.
+ * If you do not specify a url then you must handle XHR operations on
+ * your own. This type can also be constructed using the Transport alias
+ * for backward compatibility.
+ * @constructor
+ * @param {string} [url] - The URL to connect to.
+ * @classdesc The Apache Thrift Transport layer performs byte level I/O
+ * between RPC clients and servers. The JavaScript TXHRTransport object
+ * uses Http[s]/XHR. Target servers must implement the http[s] transport
+ * (see: node.js example server_http.js).
+ * @example
+ * var transport = new Thrift.TXHRTransport("http://localhost:8585");
+ */
+Thrift.Transport = Thrift.TXHRTransport = function(url, options) {
+ this.url = url;
+ this.wpos = 0;
+ this.rpos = 0;
+ this.useCORS = (options && options.useCORS);
+ this.customHeaders = options ? (options.customHeaders ? options.customHeaders : {}): {};
+ this.send_buf = '';
+ this.recv_buf = '';
+};
+
+Thrift.TXHRTransport.prototype = {
+ /**
+ * Gets the browser specific XmlHttpRequest Object.
+ * @returns {object} the browser XHR interface object
+ */
+ getXmlHttpRequestObject: function() {
+ try { return new XMLHttpRequest(); } catch (e1) { }
+ try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch (e2) { }
+ try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch (e3) { }
+
+ throw "Your browser doesn't support XHR.";
+ },
+
+ /**
+ * Sends the current XRH request if the transport was created with a URL
+ * and the async parameter is false. If the transport was not created with
+ * a URL, or the async parameter is True and no callback is provided, or
+ * the URL is an empty string, the current send buffer is returned.
+ * @param {object} async - If true the current send buffer is returned.
+ * @param {object} callback - Optional async completion callback
+ * @returns {undefined|string} Nothing or the current send buffer.
+ * @throws {string} If XHR fails.
+ */
+ flush: function(async, callback) {
+ var self = this;
+ if ((async && !callback) || this.url === undefined || this.url === '') {
+ return this.send_buf;
+ }
+
+ var xreq = this.getXmlHttpRequestObject();
+
+ if (xreq.overrideMimeType) {
+ xreq.overrideMimeType('application/vnd.apache.thrift.json; charset=utf-8');
+ }
+
+ if (callback) {
+ //Ignore XHR callbacks until the data arrives, then call the
+ // client's callback
+ xreq.onreadystatechange =
+ (function() {
+ var clientCallback = callback;
+ return function() {
+ if (this.readyState == 4 && this.status == 200) {
+ self.setRecvBuffer(this.responseText);
+ clientCallback();
+ }
+ };
+ }());
+
+ // detect net::ERR_CONNECTION_REFUSED and call the callback.
+ xreq.onerror =
+ (function() {
+ var clientCallback = callback;
+ return function() {
+ clientCallback();
+ };
+ }());
+
+ }
+
+ xreq.open('POST', this.url, !!async);
+
+ // add custom headers
+ Object.keys(self.customHeaders).forEach(function(prop) {
+ xreq.setRequestHeader(prop, self.customHeaders[prop]);
+ });
+
+ if (xreq.setRequestHeader) {
+ xreq.setRequestHeader('Accept', 'application/vnd.apache.thrift.json; charset=utf-8');
+ xreq.setRequestHeader('Content-Type', 'application/vnd.apache.thrift.json; charset=utf-8');
+ }
+
+ xreq.send(this.send_buf);
+ if (async && callback) {
+ return;
+ }
+
+ if (xreq.readyState != 4) {
+ throw 'encountered an unknown ajax ready state: ' + xreq.readyState;
+ }
+
+ if (xreq.status != 200) {
+ throw 'encountered a unknown request status: ' + xreq.status;
+ }
+
+ this.recv_buf = xreq.responseText;
+ this.recv_buf_sz = this.recv_buf.length;
+ this.wpos = this.recv_buf.length;
+ this.rpos = 0;
+ },
+
+ /**
+ * Creates a jQuery XHR object to be used for a Thrift server call.
+ * @param {object} client - The Thrift Service client object generated by the IDL compiler.
+ * @param {object} postData - The message to send to the server.
+ * @param {function} args - The original call arguments with the success call back at the end.
+ * @param {function} recv_method - The Thrift Service Client receive method for the call.
+ * @returns {object} A new jQuery XHR object.
+ * @throws {string} If the jQuery version is prior to 1.5 or if jQuery is not found.
+ */
+ jqRequest: function(client, postData, args, recv_method) {
+ if (typeof jQuery === 'undefined' ||
+ typeof jQuery.Deferred === 'undefined') {
+ throw 'Thrift.js requires jQuery 1.5+ to use asynchronous requests';
+ }
+
+ var thriftTransport = this;
+
+ var jqXHR = jQuery.ajax({
+ url: this.url,
+ data: postData,
+ type: 'POST',
+ cache: false,
+ contentType: 'application/vnd.apache.thrift.json; charset=utf-8',
+ dataType: 'text thrift',
+ converters: {
+ 'text thrift' : function(responseData) {
+ thriftTransport.setRecvBuffer(responseData);
+ var value = recv_method.call(client);
+ return value;
+ }
+ },
+ context: client,
+ success: jQuery.makeArray(args).pop(),
+ beforeSend: function (xreq) {
+ Object.keys(thriftTransport.customHeaders).forEach(function (prop) {
+ xreq.setRequestHeader(prop, thriftTransport.customHeaders[prop]);
+ });
+ }
+ });
+
+ return jqXHR;
+ },
+
+ /**
+ * Sets the buffer to provide the protocol when deserializing.
+ * @param {string} buf - The buffer to supply the protocol.
+ */
+ setRecvBuffer: function(buf) {
+ this.recv_buf = buf;
+ this.recv_buf_sz = this.recv_buf.length;
+ this.wpos = this.recv_buf.length;
+ this.rpos = 0;
+ },
+
+ /**
+ * Returns true if the transport is open, XHR always returns true.
+ * @readonly
+ * @returns {boolean} Always True.
+ */
+ isOpen: function() {
+ return true;
+ },
+
+ /**
+ * Opens the transport connection, with XHR this is a nop.
+ */
+ open: function() {},
+
+ /**
+ * Closes the transport connection, with XHR this is a nop.
+ */
+ close: function() {},
+
+ /**
+ * Returns the specified number of characters from the response
+ * buffer.
+ * @param {number} len - The number of characters to return.
+ * @returns {string} Characters sent by the server.
+ */
+ read: function(len) {
+ var avail = this.wpos - this.rpos;
+
+ if (avail === 0) {
+ return '';
+ }
+
+ var give = len;
+
+ if (avail < len) {
+ give = avail;
+ }
+
+ var ret = this.read_buf.substr(this.rpos, give);
+ this.rpos += give;
+
+ //clear buf when complete?
+ return ret;
+ },
+
+ /**
+ * Returns the entire response buffer.
+ * @returns {string} Characters sent by the server.
+ */
+ readAll: function() {
+ return this.recv_buf;
+ },
+
+ /**
+ * Sets the send buffer to buf.
+ * @param {string} buf - The buffer to send.
+ */
+ write: function(buf) {
+ this.send_buf = buf;
+ },
+
+ /**
+ * Returns the send buffer.
+ * @readonly
+ * @returns {string} The send buffer.
+ */
+ getSendBuffer: function() {
+ return this.send_buf;
+ }
+
+};
+
+
+/**
+ * Constructor Function for the WebSocket transport.
+ * @constructor
+ * @param {string} [url] - The URL to connect to.
+ * @classdesc The Apache Thrift Transport layer performs byte level I/O
+ * between RPC clients and servers. The JavaScript TWebSocketTransport object
+ * uses the WebSocket protocol. Target servers must implement WebSocket.
+ * (see: node.js example server_http.js).
+ * @example
+ * var transport = new Thrift.TWebSocketTransport("http://localhost:8585");
+ */
+Thrift.TWebSocketTransport = function(url) {
+ this.__reset(url);
+};
+
+Thrift.TWebSocketTransport.prototype = {
+ __reset: function(url) {
+ this.url = url; //Where to connect
+ this.socket = null; //The web socket
+ this.callbacks = []; //Pending callbacks
+ this.send_pending = []; //Buffers/Callback pairs waiting to be sent
+ this.send_buf = ''; //Outbound data, immutable until sent
+ this.recv_buf = ''; //Inbound data
+ this.rb_wpos = 0; //Network write position in receive buffer
+ this.rb_rpos = 0; //Client read position in receive buffer
+ },
+
+ /**
+ * Sends the current WS request and registers callback. The async
+ * parameter is ignored (WS flush is always async) and the callback
+ * function parameter is required.
+ * @param {object} async - Ignored.
+ * @param {object} callback - The client completion callback.
+ * @returns {undefined|string} Nothing (undefined)
+ */
+ flush: function(async, callback) {
+ var self = this;
+ if (this.isOpen()) {
+ //Send data and register a callback to invoke the client callback
+ this.socket.send(this.send_buf);
+ this.callbacks.push((function() {
+ var clientCallback = callback;
+ return function(msg) {
+ self.setRecvBuffer(msg);
+ if (clientCallback) {
+ clientCallback();
+ }
+ };
+ }()));
+ } else {
+ //Queue the send to go out __onOpen
+ this.send_pending.push({
+ buf: this.send_buf,
+ cb: callback
+ });
+ }
+ },
+
+ __onOpen: function() {
+ var self = this;
+ if (this.send_pending.length > 0) {
+ //If the user made calls before the connection was fully
+ //open, send them now
+ this.send_pending.forEach(function(elem) {
+ self.socket.send(elem.buf);
+ self.callbacks.push((function() {
+ var clientCallback = elem.cb;
+ return function(msg) {
+ self.setRecvBuffer(msg);
+ clientCallback();
+ };
+ }()));
+ });
+ this.send_pending = [];
+ }
+ },
+
+ __onClose: function(evt) {
+ this.__reset(this.url);
+ },
+
+ __onMessage: function(evt) {
+ if (this.callbacks.length) {
+ this.callbacks.shift()(evt.data);
+ }
+ },
+
+ __onError: function(evt) {
+ console.log('Thrift WebSocket Error: ' + evt.toString());
+ this.socket.close();
+ },
+
+ /**
+ * Sets the buffer to use when receiving server responses.
+ * @param {string} buf - The buffer to receive server responses.
+ */
+ setRecvBuffer: function(buf) {
+ this.recv_buf = buf;
+ this.recv_buf_sz = this.recv_buf.length;
+ this.wpos = this.recv_buf.length;
+ this.rpos = 0;
+ },
+
+ /**
+ * Returns true if the transport is open
+ * @readonly
+ * @returns {boolean}
+ */
+ isOpen: function() {
+ return this.socket && this.socket.readyState == this.socket.OPEN;
+ },
+
+ /**
+ * Opens the transport connection
+ */
+ open: function() {
+ //If OPEN/CONNECTING/CLOSING ignore additional opens
+ if (this.socket && this.socket.readyState != this.socket.CLOSED) {
+ return;
+ }
+ //If there is no socket or the socket is closed:
+ this.socket = new WebSocket(this.url);
+ this.socket.onopen = this.__onOpen.bind(this);
+ this.socket.onmessage = this.__onMessage.bind(this);
+ this.socket.onerror = this.__onError.bind(this);
+ this.socket.onclose = this.__onClose.bind(this);
+ },
+
+ /**
+ * Closes the transport connection
+ */
+ close: function() {
+ this.socket.close();
+ },
+
+ /**
+ * Returns the specified number of characters from the response
+ * buffer.
+ * @param {number} len - The number of characters to return.
+ * @returns {string} Characters sent by the server.
+ */
+ read: function(len) {
+ var avail = this.wpos - this.rpos;
+
+ if (avail === 0) {
+ return '';
+ }
+
+ var give = len;
+
+ if (avail < len) {
+ give = avail;
+ }
+
+ var ret = this.read_buf.substr(this.rpos, give);
+ this.rpos += give;
+
+ //clear buf when complete?
+ return ret;
+ },
+
+ /**
+ * Returns the entire response buffer.
+ * @returns {string} Characters sent by the server.
+ */
+ readAll: function() {
+ return this.recv_buf;
+ },
+
+ /**
+ * Sets the send buffer to buf.
+ * @param {string} buf - The buffer to send.
+ */
+ write: function(buf) {
+ this.send_buf = buf;
+ },
+
+ /**
+ * Returns the send buffer.
+ * @readonly
+ * @returns {string} The send buffer.
+ */
+ getSendBuffer: function() {
+ return this.send_buf;
+ }
+
+};
+
+/**
+ * Initializes a Thrift JSON protocol instance.
+ * @constructor
+ * @param {Thrift.Transport} transport - The transport to serialize to/from.
+ * @classdesc Apache Thrift Protocols perform serialization which enables cross
+ * language RPC. The Protocol type is the JavaScript browser implementation
+ * of the Apache Thrift TJSONProtocol.
+ * @example
+ * var protocol = new Thrift.Protocol(transport);
+ */
+Thrift.TJSONProtocol = Thrift.Protocol = function(transport) {
+ this.tstack = [];
+ this.tpos = [];
+ this.transport = transport;
+};
+
+/**
+ * Thrift IDL type Id to string mapping.
+ * @readonly
+ * @see {@link Thrift.Type}
+ */
+Thrift.Protocol.Type = {};
+Thrift.Protocol.Type[Thrift.Type.BOOL] = '"tf"';
+Thrift.Protocol.Type[Thrift.Type.BYTE] = '"i8"';
+Thrift.Protocol.Type[Thrift.Type.I16] = '"i16"';
+Thrift.Protocol.Type[Thrift.Type.I32] = '"i32"';
+Thrift.Protocol.Type[Thrift.Type.I64] = '"i64"';
+Thrift.Protocol.Type[Thrift.Type.DOUBLE] = '"dbl"';
+Thrift.Protocol.Type[Thrift.Type.STRUCT] = '"rec"';
+Thrift.Protocol.Type[Thrift.Type.STRING] = '"str"';
+Thrift.Protocol.Type[Thrift.Type.MAP] = '"map"';
+Thrift.Protocol.Type[Thrift.Type.LIST] = '"lst"';
+Thrift.Protocol.Type[Thrift.Type.SET] = '"set"';
+
+/**
+ * Thrift IDL type string to Id mapping.
+ * @readonly
+ * @see {@link Thrift.Type}
+ */
+Thrift.Protocol.RType = {};
+Thrift.Protocol.RType.tf = Thrift.Type.BOOL;
+Thrift.Protocol.RType.i8 = Thrift.Type.BYTE;
+Thrift.Protocol.RType.i16 = Thrift.Type.I16;
+Thrift.Protocol.RType.i32 = Thrift.Type.I32;
+Thrift.Protocol.RType.i64 = Thrift.Type.I64;
+Thrift.Protocol.RType.dbl = Thrift.Type.DOUBLE;
+Thrift.Protocol.RType.rec = Thrift.Type.STRUCT;
+Thrift.Protocol.RType.str = Thrift.Type.STRING;
+Thrift.Protocol.RType.map = Thrift.Type.MAP;
+Thrift.Protocol.RType.lst = Thrift.Type.LIST;
+Thrift.Protocol.RType.set = Thrift.Type.SET;
+
+/**
+ * The TJSONProtocol version number.
+ * @readonly
+ * @const {number} Version
+ * @memberof Thrift.Protocol
+ */
+ Thrift.Protocol.Version = 1;
+
+Thrift.Protocol.prototype = {
+ /**
+ * Returns the underlying transport.
+ * @readonly
+ * @returns {Thrift.Transport} The underlying transport.
+ */
+ getTransport: function() {
+ return this.transport;
+ },
+
+ /**
+ * Serializes the beginning of a Thrift RPC message.
+ * @param {string} name - The service method to call.
+ * @param {Thrift.MessageType} messageType - The type of method call.
+ * @param {number} seqid - The sequence number of this call (always 0 in Apache Thrift).
+ */
+ writeMessageBegin: function(name, messageType, seqid) {
+ this.tstack = [];
+ this.tpos = [];
+
+ this.tstack.push([Thrift.Protocol.Version, '"' +
+ name + '"', messageType, seqid]);
+ },
+
+ /**
+ * Serializes the end of a Thrift RPC message.
+ */
+ writeMessageEnd: function() {
+ var obj = this.tstack.pop();
+
+ this.wobj = this.tstack.pop();
+ this.wobj.push(obj);
+
+ this.wbuf = '[' + this.wobj.join(',') + ']';
+
+ this.transport.write(this.wbuf);
+ },
+
+
+ /**
+ * Serializes the beginning of a struct.
+ * @param {string} name - The name of the struct.
+ */
+ writeStructBegin: function(name) {
+ this.tpos.push(this.tstack.length);
+ this.tstack.push({});
+ },
+
+ /**
+ * Serializes the end of a struct.
+ */
+ writeStructEnd: function() {
+
+ var p = this.tpos.pop();
+ var struct = this.tstack[p];
+ var str = '{';
+ var first = true;
+ for (var key in struct) {
+ if (first) {
+ first = false;
+ } else {
+ str += ',';
+ }
+
+ str += key + ':' + struct[key];
+ }
+
+ str += '}';
+ this.tstack[p] = str;
+ },
+
+ /**
+ * Serializes the beginning of a struct field.
+ * @param {string} name - The name of the field.
+ * @param {Thrift.Protocol.Type} fieldType - The data type of the field.
+ * @param {number} fieldId - The field's unique identifier.
+ */
+ writeFieldBegin: function(name, fieldType, fieldId) {
+ this.tpos.push(this.tstack.length);
+ this.tstack.push({ 'fieldId': '"' +
+ fieldId + '"', 'fieldType': Thrift.Protocol.Type[fieldType]
+ });
+
+ },
+
+ /**
+ * Serializes the end of a field.
+ */
+ writeFieldEnd: function() {
+ var value = this.tstack.pop();
+ var fieldInfo = this.tstack.pop();
+
+ this.tstack[this.tstack.length - 1][fieldInfo.fieldId] = '{' +
+ fieldInfo.fieldType + ':' + value + '}';
+ this.tpos.pop();
+ },
+
+ /**
+ * Serializes the end of the set of fields for a struct.
+ */
+ writeFieldStop: function() {
+ //na
+ },
+
+ /**
+ * Serializes the beginning of a map collection.
+ * @param {Thrift.Type} keyType - The data type of the key.
+ * @param {Thrift.Type} valType - The data type of the value.
+ * @param {number} [size] - The number of elements in the map (ignored).
+ */
+ writeMapBegin: function(keyType, valType, size) {
+ this.tpos.push(this.tstack.length);
+ this.tstack.push([Thrift.Protocol.Type[keyType],
+ Thrift.Protocol.Type[valType], 0]);
+ },
+
+ /**
+ * Serializes the end of a map.
+ */
+ writeMapEnd: function() {
+ var p = this.tpos.pop();
+
+ if (p == this.tstack.length) {
+ return;
+ }
+
+ if ((this.tstack.length - p - 1) % 2 !== 0) {
+ this.tstack.push('');
+ }
+
+ var size = (this.tstack.length - p - 1) / 2;
+
+ this.tstack[p][this.tstack[p].length - 1] = size;
+
+ var map = '}';
+ var first = true;
+ while (this.tstack.length > p + 1) {
+ var v = this.tstack.pop();
+ var k = this.tstack.pop();
+ if (first) {
+ first = false;
+ } else {
+ map = ',' + map;
+ }
+
+ if (! isNaN(k)) { k = '"' + k + '"'; } //json "keys" need to be strings
+ map = k + ':' + v + map;
+ }
+ map = '{' + map;
+
+ this.tstack[p].push(map);
+ this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
+ },
+
+ /**
+ * Serializes the beginning of a list collection.
+ * @param {Thrift.Type} elemType - The data type of the elements.
+ * @param {number} size - The number of elements in the list.
+ */
+ writeListBegin: function(elemType, size) {
+ this.tpos.push(this.tstack.length);
+ this.tstack.push([Thrift.Protocol.Type[elemType], size]);
+ },
+
+ /**
+ * Serializes the end of a list.
+ */
+ writeListEnd: function() {
+ var p = this.tpos.pop();
+
+ while (this.tstack.length > p + 1) {
+ var tmpVal = this.tstack[p + 1];
+ this.tstack.splice(p + 1, 1);
+ this.tstack[p].push(tmpVal);
+ }
+
+ this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
+ },
+
+ /**
+ * Serializes the beginning of a set collection.
+ * @param {Thrift.Type} elemType - The data type of the elements.
+ * @param {number} size - The number of elements in the list.
+ */
+ writeSetBegin: function(elemType, size) {
+ this.tpos.push(this.tstack.length);
+ this.tstack.push([Thrift.Protocol.Type[elemType], size]);
+ },
+
+ /**
+ * Serializes the end of a set.
+ */
+ writeSetEnd: function() {
+ var p = this.tpos.pop();
+
+ while (this.tstack.length > p + 1) {
+ var tmpVal = this.tstack[p + 1];
+ this.tstack.splice(p + 1, 1);
+ this.tstack[p].push(tmpVal);
+ }
+
+ this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
+ },
+
+ /** Serializes a boolean */
+ writeBool: function(value) {
+ this.tstack.push(value ? 1 : 0);
+ },
+
+ /** Serializes a number */
+ writeByte: function(i8) {
+ this.tstack.push(i8);
+ },
+
+ /** Serializes a number */
+ writeI16: function(i16) {
+ this.tstack.push(i16);
+ },
+
+ /** Serializes a number */
+ writeI32: function(i32) {
+ this.tstack.push(i32);
+ },
+
+ /** Serializes a number */
+ writeI64: function(i64) {
+ if (typeof i64 === 'number') {
+ this.tstack.push(i64);
+ } else {
+ this.tstack.push(Int64Util.toDecimalString(i64));
+ }
+ },
+
+ /** Serializes a number */
+ writeDouble: function(dbl) {
+ this.tstack.push(dbl);
+ },
+
+ /** Serializes a string */
+ writeString: function(str) {
+ // We do not encode uri components for wire transfer:
+ if (str === null) {
+ this.tstack.push(null);
+ } else {
+ // concat may be slower than building a byte buffer
+ var escapedString = '';
+ for (var i = 0; i < str.length; i++) {
+ var ch = str.charAt(i); // a single double quote: "
+ if (ch === '\"') {
+ escapedString += '\\\"'; // write out as: \"
+ } else if (ch === '\\') { // a single backslash
+ escapedString += '\\\\'; // write out as double backslash
+ } else if (ch === '\b') { // a single backspace: invisible
+ escapedString += '\\b'; // write out as: \b"
+ } else if (ch === '\f') { // a single formfeed: invisible
+ escapedString += '\\f'; // write out as: \f"
+ } else if (ch === '\n') { // a single newline: invisible
+ escapedString += '\\n'; // write out as: \n"
+ } else if (ch === '\r') { // a single return: invisible
+ escapedString += '\\r'; // write out as: \r"
+ } else if (ch === '\t') { // a single tab: invisible
+ escapedString += '\\t'; // write out as: \t"
+ } else {
+ escapedString += ch; // Else it need not be escaped
+ }
+ }
+ this.tstack.push('"' + escapedString + '"');
+ }
+ },
+
+ /** Serializes a string */
+ writeBinary: function(binary) {
+ var str = '';
+ if (typeof binary == 'string') {
+ str = binary;
+ } else if (binary instanceof Uint8Array) {
+ var arr = binary;
+ for (var i = 0; i < arr.length; ++i) {
+ str += String.fromCharCode(arr[i]);
+ }
+ } else {
+ throw new TypeError('writeBinary only accepts String or Uint8Array.');
+ }
+ this.tstack.push('"' + btoa(str) + '"');
+ },
+
+ /**
+ @class
+ @name AnonReadMessageBeginReturn
+ @property {string} fname - The name of the service method.
+ @property {Thrift.MessageType} mtype - The type of message call.
+ @property {number} rseqid - The sequence number of the message (0 in Thrift RPC).
+ */
+ /**
+ * Deserializes the beginning of a message.
+ * @returns {AnonReadMessageBeginReturn}
+ */
+ readMessageBegin: function() {
+ this.rstack = [];
+ this.rpos = [];
+
+ received = this.transport.readAll();
+
+ if (typeof JSONInt64 !== 'undefined' && typeof JSONInt64.parse === 'function') {
+ this.robj = JSONInt64.parse(received);
+ } else if (typeof JSON !== 'undefined' && typeof JSON.parse === 'function') {
+ this.robj = JSON.parse(received);
+ } else if (typeof jQuery !== 'undefined') {
+ this.robj = jQuery.parseJSON(received);
+ } else {
+ this.robj = eval(received);
+ }
+
+ var r = {};
+ var version = this.robj.shift();
+
+ if (version != Thrift.Protocol.Version) {
+ throw 'Wrong thrift protocol version: ' + version;
+ }
+
+ r.fname = this.robj.shift();
+ r.mtype = this.robj.shift();
+ r.rseqid = this.robj.shift();
+
+
+ //get to the main obj
+ this.rstack.push(this.robj.shift());
+
+ return r;
+ },
+
+ /** Deserializes the end of a message. */
+ readMessageEnd: function() {
+ },
+
+ /**
+ * Deserializes the beginning of a struct.
+ * @param {string} [name] - The name of the struct (ignored)
+ * @returns {object} - An object with an empty string fname property
+ */
+ readStructBegin: function(name) {
+ var r = {};
+ r.fname = '';
+
+ //incase this is an array of structs
+ if (this.rstack[this.rstack.length - 1] instanceof Array) {
+ this.rstack.push(this.rstack[this.rstack.length - 1].shift());
+ }
+
+ return r;
+ },
+
+ /** Deserializes the end of a struct. */
+ readStructEnd: function() {
+ if (this.rstack[this.rstack.length - 2] instanceof Array) {
+ this.rstack.pop();
+ }
+ },
+
+ /**
+ @class
+ @name AnonReadFieldBeginReturn
+ @property {string} fname - The name of the field (always '').
+ @property {Thrift.Type} ftype - The data type of the field.
+ @property {number} fid - The unique identifier of the field.
+ */
+ /**
+ * Deserializes the beginning of a field.
+ * @returns {AnonReadFieldBeginReturn}
+ */
+ readFieldBegin: function() {
+ var r = {};
+
+ var fid = -1;
+ var ftype = Thrift.Type.STOP;
+
+ //get a fieldId
+ for (var f in (this.rstack[this.rstack.length - 1])) {
+ if (f === null) {
+ continue;
+ }
+
+ fid = parseInt(f, 10);
+ this.rpos.push(this.rstack.length);
+
+ var field = this.rstack[this.rstack.length - 1][fid];
+
+ //remove so we don't see it again
+ delete this.rstack[this.rstack.length - 1][fid];
+
+ this.rstack.push(field);
+
+ break;
+ }
+
+ if (fid != -1) {
+
+ //should only be 1 of these but this is the only
+ //way to match a key
+ for (var i in (this.rstack[this.rstack.length - 1])) {
+ if (Thrift.Protocol.RType[i] === null) {
+ continue;
+ }
+
+ ftype = Thrift.Protocol.RType[i];
+ this.rstack[this.rstack.length - 1] =
+ this.rstack[this.rstack.length - 1][i];
+ }
+ }
+
+ r.fname = '';
+ r.ftype = ftype;
+ r.fid = fid;
+
+ return r;
+ },
+
+ /** Deserializes the end of a field. */
+ readFieldEnd: function() {
+ var pos = this.rpos.pop();
+
+ //get back to the right place in the stack
+ while (this.rstack.length > pos) {
+ this.rstack.pop();
+ }
+
+ },
+
+ /**
+ @class
+ @name AnonReadMapBeginReturn
+ @property {Thrift.Type} ktype - The data type of the key.
+ @property {Thrift.Type} vtype - The data type of the value.
+ @property {number} size - The number of elements in the map.
+ */
+ /**
+ * Deserializes the beginning of a map.
+ * @returns {AnonReadMapBeginReturn}
+ */
+ readMapBegin: function() {
+ var map = this.rstack.pop();
+ var first = map.shift();
+ if (first instanceof Array) {
+ this.rstack.push(map);
+ map = first;
+ first = map.shift();
+ }
+
+ var r = {};
+ r.ktype = Thrift.Protocol.RType[first];
+ r.vtype = Thrift.Protocol.RType[map.shift()];
+ r.size = map.shift();
+
+ this.rpos.push(this.rstack.length);
+ this.rstack.push(map.shift());
+
+ return r;
+ },
+
+ /** Deserializes the end of a map. */
+ readMapEnd: function() {
+ this.readFieldEnd();
+ },
+
+ /**
+ @class
+ @name AnonReadColBeginReturn
+ @property {Thrift.Type} etype - The data type of the element.
+ @property {number} size - The number of elements in the collection.
+ */
+ /**
+ * Deserializes the beginning of a list.
+ * @returns {AnonReadColBeginReturn}
+ */
+ readListBegin: function() {
+ var list = this.rstack[this.rstack.length - 1];
+
+ var r = {};
+ r.etype = Thrift.Protocol.RType[list.shift()];
+ r.size = list.shift();
+
+ this.rpos.push(this.rstack.length);
+ this.rstack.push(list.shift());
+
+ return r;
+ },
+
+ /** Deserializes the end of a list. */
+ readListEnd: function() {
+ var pos = this.rpos.pop() - 2;
+ var st = this.rstack;
+ st.pop();
+ if (st instanceof Array && st.length > pos && st[pos].length > 0) {
+ st.push(st[pos].shift());
+ }
+ },
+
+ /**
+ * Deserializes the beginning of a set.
+ * @returns {AnonReadColBeginReturn}
+ */
+ readSetBegin: function(elemType, size) {
+ return this.readListBegin(elemType, size);
+ },
+
+ /** Deserializes the end of a set. */
+ readSetEnd: function() {
+ return this.readListEnd();
+ },
+
+ /** Returns an object with a value property set to
+ * False unless the next number in the protocol buffer
+ * is 1, in which case the value property is True */
+ readBool: function() {
+ var r = this.readI32();
+
+ if (r !== null && r.value == '1') {
+ r.value = true;
+ } else {
+ r.value = false;
+ }
+
+ return r;
+ },
+
+ /** Returns the an object with a value property set to the
+ next value found in the protocol buffer */
+ readByte: function() {
+ return this.readI32();
+ },
+
+ /** Returns the an object with a value property set to the
+ next value found in the protocol buffer */
+ readI16: function() {
+ return this.readI32();
+ },
+
+ /** Returns the an object with a value property set to the
+ next value found in the protocol buffer */
+ readI32: function(f) {
+ if (f === undefined) {
+ f = this.rstack[this.rstack.length - 1];
+ }
+
+ var r = {};
+
+ if (f instanceof Array) {
+ if (f.length === 0) {
+ r.value = undefined;
+ } else {
+ if (!f.isReversed) {
+ f.reverse();
+ f.isReversed = true;
+ }
+ r.value = f.pop();
+ }
+ } else if (f instanceof Object) {
+ for (var i in f) {
+ if (i === null) {
+ continue;
+ }
+ this.rstack.push(f[i]);
+ delete f[i];
+
+ r.value = i;
+ break;
+ }
+ } else {
+ r.value = f;
+ this.rstack.pop();
+ }
+
+ return r;
+ },
+
+ /** Returns the an object with a value property set to the
+ next value found in the protocol buffer */
+ readI64: function(f) {
+ if (f === undefined) {
+ f = this.rstack[this.rstack.length - 1];
+ }
+
+ var r = {};
+
+ if (f instanceof Array) {
+ if (f.length === 0) {
+ r.value = undefined;
+ } else {
+ if (!f.isReversed) {
+ f.reverse();
+ f.isReversed = true;
+ }
+ r.value = f.pop();
+ }
+ } else if (f instanceof Object) {
+ var int64Object = true;
+ var objectKeys = Object.keys(f).sort();
+ var int64Keys = ['buffer', 'offset'];
+ if (objectKeys.length !== int64Keys.length) {
+ int64Object = false;
+ }
+ for (var it=0; int64Object && it < objectKeys.length; ++it) {
+ if (objectKeys[it] !== int64Keys[it]) {
+ int64Object = false;
+ }
+ }
+ if (int64Object) {
+ r.value = f;
+ } else {
+ for (var i in f) {
+ if (i === null) {
+ continue;
+ }
+ this.rstack.push(f[i]);
+ delete f[i];
+
+ r.value = i;
+ break;
+ }
+ }
+ } else {
+ r.value = f;
+ this.rstack.pop();
+ }
+ return r;
+ },
+
+ /** Returns the an object with a value property set to the
+ next value found in the protocol buffer */
+ readDouble: function() {
+ return this.readI32();
+ },
+
+ /** Returns the an object with a value property set to the
+ next value found in the protocol buffer */
+ readString: function() {
+ var r = this.readI32();
+ return r;
+ },
+
+ /** Returns the an object with a value property set to the
+ next value found in the protocol buffer */
+ readBinary: function() {
+ var r = this.readI32();
+ r.value = atob(r.value);
+ return r;
+ },
+
+ /**
+ * Method to arbitrarily skip over data */
+ skip: function(type) {
+ var ret, i;
+ switch (type) {
+ case Thrift.Type.BOOL:
+ return this.readBool();
+
+ case Thrift.Type.BYTE:
+ return this.readByte();
+
+ case Thrift.Type.I16:
+ return this.readI16();
+
+ case Thrift.Type.I32:
+ return this.readI32();
+
+ case Thrift.Type.I64:
+ return this.readI64();
+
+ case Thrift.Type.DOUBLE:
+ return this.readDouble();
+
+ case Thrift.Type.STRING:
+ return this.readString();
+
+ case Thrift.Type.STRUCT:
+ this.readStructBegin();
+ while (true) {
+ ret = this.readFieldBegin();
+ if (ret.ftype == Thrift.Type.STOP) {
+ break;
+ }
+ this.skip(ret.ftype);
+ this.readFieldEnd();
+ }
+ this.readStructEnd();
+ return null;
+
+ case Thrift.Type.MAP:
+ ret = this.readMapBegin();
+ for (i = 0; i < ret.size; i++) {
+ if (i > 0) {
+ if (this.rstack.length > this.rpos[this.rpos.length - 1] + 1) {
+ this.rstack.pop();
+ }
+ }
+ this.skip(ret.ktype);
+ this.skip(ret.vtype);
+ }
+ this.readMapEnd();
+ return null;
+
+ case Thrift.Type.SET:
+ ret = this.readSetBegin();
+ for (i = 0; i < ret.size; i++) {
+ this.skip(ret.etype);
+ }
+ this.readSetEnd();
+ return null;
+
+ case Thrift.Type.LIST:
+ ret = this.readListBegin();
+ for (i = 0; i < ret.size; i++) {
+ this.skip(ret.etype);
+ }
+ this.readListEnd();
+ return null;
+
+ default:
+ throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.INVALID_DATA);
+ }
+ }
+};
+
+
+/**
+ * Initializes a MutilplexProtocol Implementation as a Wrapper for Thrift.Protocol
+ * @constructor
+ */
+Thrift.MultiplexProtocol = function(srvName, trans, strictRead, strictWrite) {
+ Thrift.Protocol.call(this, trans, strictRead, strictWrite);
+ this.serviceName = srvName;
+};
+Thrift.inherits(Thrift.MultiplexProtocol, Thrift.Protocol, 'multiplexProtocol');
+
+/** Override writeMessageBegin method of prototype*/
+Thrift.MultiplexProtocol.prototype.writeMessageBegin = function(name, type, seqid) {
+
+ if (type === Thrift.MessageType.CALL || type === Thrift.MessageType.ONEWAY) {
+ Thrift.Protocol.prototype.writeMessageBegin.call(this, this.serviceName + ':' + name, type, seqid);
+ } else {
+ Thrift.Protocol.prototype.writeMessageBegin.call(this, name, type, seqid);
+ }
+};
+
+Thrift.Multiplexer = function() {
+ this.seqid = 0;
+};
+
+/** Instantiates a multiplexed client for a specific service
+ * @constructor
+ * @param {String} serviceName - The transport to serialize to/from.
+ * @param {Thrift.ServiceClient} SCl - The Service Client Class
+ * @param {Thrift.Transport} transport - Thrift.Transport instance which provides remote host:port
+ * @example
+ * var mp = new Thrift.Multiplexer();
+ * var transport = new Thrift.Transport("http://localhost:9090/foo.thrift");
+ * var protocol = new Thrift.Protocol(transport);
+ * var client = mp.createClient('AuthService', AuthServiceClient, transport);
+*/
+Thrift.Multiplexer.prototype.createClient = function(serviceName, SCl, transport) {
+ if (SCl.Client) {
+ SCl = SCl.Client;
+ }
+ var self = this;
+ SCl.prototype.new_seqid = function() {
+ self.seqid += 1;
+ return self.seqid;
+ };
+ var client = new SCl(new Thrift.MultiplexProtocol(serviceName, transport));
+
+ return client;
+};
+
+
+
+var copyList, copyMap;
+
+copyList = function(lst, types) {
+
+ if (!lst) {return lst; }
+
+ var type;
+
+ if (types.shift === undefined) {
+ type = types;
+ }
+ else {
+ type = types[0];
+ }
+ var Type = type;
+
+ var len = lst.length, result = [], i, val;
+ for (i = 0; i < len; i++) {
+ val = lst[i];
+ if (type === null) {
+ result.push(val);
+ }
+ else if (type === copyMap || type === copyList) {
+ result.push(type(val, types.slice(1)));
+ }
+ else {
+ result.push(new Type(val));
+ }
+ }
+ return result;
+};
+
+copyMap = function(obj, types) {
+
+ if (!obj) {return obj; }
+
+ var type;
+
+ if (types.shift === undefined) {
+ type = types;
+ }
+ else {
+ type = types[0];
+ }
+ var Type = type;
+
+ var result = {}, val;
+ for (var prop in obj) {
+ if (obj.hasOwnProperty(prop)) {
+ val = obj[prop];
+ if (type === null) {
+ result[prop] = val;
+ }
+ else if (type === copyMap || type === copyList) {
+ result[prop] = type(val, types.slice(1));
+ }
+ else {
+ result[prop] = new Type(val);
+ }
+ }
+ }
+ return result;
+};
+
+Thrift.copyMap = copyMap;
+Thrift.copyList = copyList;
diff --git a/src/jaegertracing/thrift/lib/js/test/Makefile.am b/src/jaegertracing/thrift/lib/js/test/Makefile.am
new file mode 100755
index 000000000..14927c40a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/Makefile.am
@@ -0,0 +1,29 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+export CLASSPATH
+
+# Make sure this doesn't fail if ant is not configured.
+clean-local:
+ ANT=$(ANT) ; if test -z "$$ANT" ; then ANT=: ; fi ; \
+ $$ANT $(ANT_FLAGS) clean
+
+check-local: all
+ $(ANT) $(ANT_FLAGS) test
+
diff --git a/src/jaegertracing/thrift/lib/js/test/README.md b/src/jaegertracing/thrift/lib/js/test/README.md
new file mode 100644
index 000000000..9ad140edb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/README.md
@@ -0,0 +1,68 @@
+Thrift Javascript Library
+=========================
+This browser based Apache Thrift implementation supports
+RPC clients using the JSON protocol over Http[s] with XHR
+and WebSocket.
+
+License
+-------
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+Test Servers
+------------
+drwxr-xr-x 2 randy randy 4096 Feb 8 15:44 sec
+-rw-r--r-- 1 randy randy 2183 Feb 9 04:01 server_http.js
+-rw-r--r-- 1 randy randy 2386 Feb 9 05:39 server_https.js
+
+server_http.js is a Node.js web server which support the
+standard Apache Thrift test suite (thrift/test/ThriftTest.thrift).
+The server supports Apache Thrift XHR and WebSocket clients.
+
+server_https.js is the same but uses SSL/TLS. The server key
+and cert are pulled from the thrift/test/keys folder.
+
+Both of these servers support WebSocket (the http: supports ws:,
+and the https: support wss:).
+
+To run the client test with the Java test server use:
+$ make check (requires the Apache Thrift Java branch
+and make check must have been run in thrift/lib/java
+previously).
+
+To run the client tests with the Node servers run the grunt
+ build in the parent js directory (see README there).
+
+Test Clients
+------------
+-rw-r--r-- 1 randy randy 13558 Feb 9 07:18 test-async.js
+-rw-r--r-- 1 randy randy 5724 Feb 9 03:45 test_handler.js
+-rwxr-xr-x 1 randy randy 2719 Feb 9 06:04 test.html
+-rw-r--r-- 1 randy randy 4611 Feb 9 06:05 test-jq.js
+-rwxr-xr-x 1 randy randy 12153 Feb 9 06:04 test.js
+-rw-r--r-- 1 randy randy 2593 Feb 9 06:16 test-nojq.html
+-rw-r--r-- 1 randy randy 1450 Feb 9 06:14 test-nojq.js
+-rw-r--r-- 1 randy randy 2847 Feb 9 06:31 testws.html
+
+There are three html test driver files, all of which are
+QUnit based. test.html tests the Apache Thrift jQuery
+generated code (thrift -gen js:jquery). The test-nojq.html
+runs almost identical tests against normal JavaScript builds
+(thrift -gen js). Both of the previous tests use the XHR
+transport. The testws.html runs similar tests using the
+WebSocket transport. The test*.js files are loaded by the
+html drivers and contain the actual Apache Thrift tests.
diff --git a/src/jaegertracing/thrift/lib/js/test/build.properties b/src/jaegertracing/thrift/lib/js/test/build.properties
new file mode 100644
index 000000000..84636683c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/build.properties
@@ -0,0 +1,5 @@
+# Maven Ant tasks Jar details
+mvn.ant.task.version=2.1.3
+mvn.repo=http://repo1.maven.org/maven2
+mvn.ant.task.url=${mvn.repo}/org/apache/maven/maven-ant-tasks/${mvn.ant.task.version}
+mvn.ant.task.jar=maven-ant-tasks-${mvn.ant.task.version}.jar
diff --git a/src/jaegertracing/thrift/lib/js/test/build.xml b/src/jaegertracing/thrift/lib/js/test/build.xml
new file mode 100755
index 000000000..d891b4394
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/build.xml
@@ -0,0 +1,248 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="Java Script Test" default="test" basedir="."
+ xmlns:artifact="antlib:org.apache.maven.artifact.ant"
+ xmlns:jsl="antlib:com.googlecode.jslint4java">
+
+ <description>Java Script Test based on Thrift Java Library</description>
+
+ <property name="src" location="src" />
+ <property name="genjava" location="gen-java" />
+ <property name="genjs" location="gen-js" />
+ <property name="build" location="build" />
+ <property name="jar.file" location="${build}/jstest.jar" />
+
+ <!-- the root directory, where you unpack thrift distibution (e.g.: thrift-0.x.x.tar.gz) -->
+ <property name="thrift.dir" location="../../../" />
+ <property name="thrift.java.dir" location="${thrift.dir}/lib/java" />
+ <property name="build.tools.dir" location="${thrift.java.dir}/build/tools/"/>
+ <property file="${basedir}/build.properties"/>
+
+ <!-- Include the base Java properties file -->
+ <property file="${thrift.java.dir}/gradle.properties" />
+
+ <property name="thrift.compiler" location="${thrift.dir}/compiler/cpp/thrift" />
+
+ <path id="libs.classpath">
+ <fileset dir="${thrift.java.dir}/build/libs">
+ <include name="libthrift*.jar" />
+ <exclude name="libthrift*javadoc.jar" />
+ <exclude name="libthrift*sources.jar" />
+ </fileset>
+ <fileset dir="${thrift.java.dir}/build/deps">
+ <include name="*.jar" />
+ </fileset>
+ <fileset dir="${build}/lib">
+ <include name="*.jar" />
+ </fileset>
+ </path>
+
+ <path id="test.classpath">
+ <path refid="libs.classpath" />
+ <pathelement location="${jar.file}" />
+ </path>
+
+ <target name="dependencies">
+ <fail>
+ <condition>
+ <not>
+ <resourcecount count="2">
+ <fileset id="fs" dir="${thrift.java.dir}/build/libs">
+ <include name="libthrift*.jar" />
+ <exclude name="libthrift*javadoc.jar" />
+ <exclude name="libthrift*sources.jar" />
+ </fileset>
+ </resourcecount>
+ </not>
+ </condition>
+ You need libthrift*.jar and libthrift*test.jar located at
+ ${thrift.java.dir}/build/libs
+ Did you compile Thrift Java library and its test suite by "make check"?
+ </fail>
+ <fail>
+ <condition>
+ <not>
+ <resourcecount count="1">
+ <fileset id="fs" dir="${thrift.dir}" includes="compiler/cpp/thrift"/>
+ </resourcecount>
+ </not>
+ </condition>
+ Thrift compiler is missing !
+ </fail>
+ </target>
+
+ <target name="init" depends="dependencies">
+ <tstamp />
+ <mkdir dir="${build.tools.dir}"/>
+ <mkdir dir="${build}"/>
+ <mkdir dir="${build}/js/lib"/>
+ <mkdir dir="${build}/lib"/>
+ <mkdir dir="${build}/log"/>
+ <mkdir dir="${build}/test"/>
+ <mkdir dir="${build}/test/log"/>
+ </target>
+
+ <target name="download_jslibs">
+ <get src="http://code.jquery.com/jquery-1.11.3.min.js" dest="${build}/js/lib/jquery.js" usetimestamp="true"/>
+ <get src="http://code.jquery.com/qunit/qunit-2.6.2.js" dest="${build}/js/lib/qunit.js" usetimestamp="true"/>
+ <get src="http://code.jquery.com/qunit/qunit-2.6.2.css" dest="${build}/js/lib/qunit.css" usetimestamp="true"/>
+ </target>
+
+ <target name="jslibs" depends="init, proxy, download_jslibs">
+ </target>
+
+ <target name="compile" description="compile the test suite" depends="init, generate, resolve">
+ <!-- //TODO enable <compilerarg value="-Xlint"/>-->
+ <javac compiler="modern" includeantruntime="false" srcdir="${genjava}" destdir="${build}/test" classpathref="libs.classpath"/>
+ <javac compiler="modern" includeantruntime="false" srcdir="${src}" destdir="${build}/test" classpathref="libs.classpath"/>
+ </target>
+
+ <target name="jstest" description="create the test suite jar file" depends="compile">
+ <jar jarfile="${jar.file}" basedir="${build}/test"/>
+ </target>
+
+ <target name="testserver" description="run the test server" depends="jstest, jslibs">
+ <java classname="test.Httpd" fork="true"
+ classpathref="test.classpath" failonerror="true">
+ <arg value="../" />
+ </java>
+ </target>
+
+ <target name="proxy" if="proxy.enabled">
+ <setproxy proxyhost="${proxy.host}" proxyport="${proxy.port}"
+ proxyuser="${proxy.user}" proxypassword="${proxy.pass}"/>
+ </target>
+
+ <target name="xvfb">
+ <echo>check if Xvfb is available:</echo>
+ <exec executable="Xvfb" failifexecutionfails="no" resultproperty="xvfb.present" failonerror="false" output="${build}/log/xvfb.log">
+ <arg line="--version"/>
+ </exec>
+ </target>
+
+ <target name="phantomjs" depends="xvfb" if="xvfb.present">
+ <echo>check if phantomjs is available:</echo>
+ <exec executable="phantomjs" failifexecutionfails="no" resultproperty="phantomjs.present" failonerror="false" output="${build}/log/phantomjs.log">
+ <arg line="--version"/>
+ </exec>
+ </target>
+
+ <target name="unittest" description="do unit tests with headless browser phantomjs" depends="init, phantomjs, jstest, jslibs" if="phantomjs.present">
+ <parallel>
+ <exec executable="Xvfb" spawn="true" failonerror="false">
+ <arg line=":99" />
+ </exec>
+ <java classname="test.Httpd" fork="true" timeout="10000"
+ classpathref="test.classpath" failonerror="false" output="${build}/log/unittest.log">
+ <arg value="../" />
+ </java>
+ <sequential>
+ <sleep seconds="2"/>
+ <echo>Running Unit Tests with headless browser!</echo>
+ <exec executable="phantomjs" failonerror="true">
+ <env key="DISPLAY" value=":99"/>
+ <arg line="phantomjs-qunit.js http://localhost:8088/test/test.html" />
+ </exec>
+ </sequential>
+ </parallel>
+ </target>
+
+ <target name="generate">
+ <exec executable="${thrift.compiler}" failonerror="true">
+ <arg line="--gen java ${thrift.dir}/test/ThriftTest.thrift" />
+ </exec>
+ <exec executable="${thrift.compiler}" failonerror="true">
+ <arg line="--gen js:jquery ${thrift.dir}/test/ThriftTest.thrift" />
+ </exec>
+ <exec executable="${thrift.compiler}" failonerror="true">
+ <arg line="--gen js:jquery ${thrift.dir}/test/DoubleConstantsTest.thrift" />
+ </exec>
+ </target>
+
+ <target name="test" description="run test suite (lint, unittest)" depends="lint, unittest"/>
+
+ <target name="lint" description="code quality checks (jslint and gjslint if available)" depends="generate, gjslint, jslint"/>
+
+ <target name="jslint" depends="resolve">
+ <taskdef uri="antlib:com.googlecode.jslint4java" resource="com/googlecode/jslint4java/antlib.xml" classpathref="libs.classpath" />
+ <!--
+ the following options would probably make sense in the future:
+ browser,undef,eqeqeq,plusplus,bitwise,regexp,strict,newcap,immed
+ -->
+ <jsl:jslint options="evil,forin,browser,bitwise,regexp,newcap,immed" encoding="UTF-8">
+ <formatter type="plain" />
+ <fileset dir="../src" includes="thrift.js" />
+
+ <!-- issues with unsafe character -->
+ <!-- fileset dir="." includes="*test*.js" /> -->
+ </jsl:jslint>
+ </target>
+
+ <target name="check-gjslint">
+ <echo>check if gjslint is available:</echo>
+ <exec executable="gjslint" failifexecutionfails="no" resultproperty="gjslint.present" failonerror="false">
+ <arg line="--helpshort"/>
+ </exec>
+ </target>
+
+ <target name="gjslint" depends="check-gjslint" if="gjslint.present">
+ <exec executable="gjslint" failifexecutionfails="no">
+ <arg line="--nojsdoc"/>
+ <arg line="${genjs}/*.js"/>
+ <arg line="../src/thrift.js"/>
+
+ <!-- issues with unsafe character, etc. -->
+ <!-- <arg line="*test*.js"/> -->
+ </exec>
+ </target>
+
+ <target name="clean">
+ <delete dir="${build}" />
+ <delete dir="${genjava}" />
+ <delete dir="${genjs}" />
+ </target>
+
+ <target name="mvn.ant.tasks.download" depends="init,mvn.ant.tasks.check" unless="mvn.ant.tasks.found">
+ <get src="${mvn.ant.task.url}/${mvn.ant.task.jar}" dest="${build.tools.dir}/${mvn.ant.task.jar}" usetimestamp="true"/>
+ </target>
+
+ <target name="mvn.ant.tasks.check">
+ <condition property="mvn.ant.tasks.found">
+ <typefound uri="antlib:org.apache.maven.artifact.ant" name="artifact"/>
+ </condition>
+ </target>
+
+ <target name="resolve" depends="mvn.ant.tasks.download" unless="mvn.finished">
+ <typedef uri="antlib:org.apache.maven.artifact.ant" classpath="${thrift.java.dir}/build/tools/${mvn.ant.task.jar}"/>
+
+ <artifact:dependencies filesetId="js.test.dependency.jars">
+ <dependency groupId="org.apache.httpcomponents" artifactId="httpclient" version="4.0.1"/>
+ <dependency groupId="com.googlecode.jslint4java" artifactId="jslint4java-ant" version="1.4.6"/>
+ <dependency groupId="eu.medsea.mimeutil" artifactId="mime-util" version="2.1.3"/>
+ </artifact:dependencies>
+
+ <!-- Copy the dependencies to the build/lib dir -->
+ <copy todir="${build}/lib">
+ <fileset refid="js.test.dependency.jars"/>
+ <mapper type="flatten"/>
+ </copy>
+
+ <property name="mvn.finished" value="true"/>
+ </target>
+</project>
diff --git a/src/jaegertracing/thrift/lib/js/test/deep-constructor.test.js b/src/jaegertracing/thrift/lib/js/test/deep-constructor.test.js
new file mode 100644
index 000000000..82d3a1e89
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/deep-constructor.test.js
@@ -0,0 +1,213 @@
+function serialize(data) {
+ const transport = new Thrift.Transport('/service');
+ const protocol = new Thrift.Protocol(transport);
+ protocol.writeMessageBegin('', 0, 0);
+ data.write(protocol);
+ protocol.writeMessageEnd();
+ return transport.send_buf;
+}
+
+function deserialize(serialized, type) {
+ const transport = new Thrift.Transport('/service');
+ transport.setRecvBuffer(serialized);
+ const protocol = new Thrift.Protocol(transport);
+ protocol.readMessageBegin();
+ const data = new type();
+ data.read(protocol);
+ protocol.readMessageEnd();
+ return data;
+}
+
+
+function createThriftObj() {
+
+ return new Complex({
+
+ struct_field: new Simple({value: 'a'}),
+
+ struct_list_field: [
+ new Simple({value: 'b'}),
+ new Simple({value: 'c'})
+ ],
+
+ struct_set_field: [
+ new Simple({value: 'd'}),
+ new Simple({value: 'e'})
+ ],
+
+ struct_map_field: {
+ A: new Simple({value: 'f'}),
+ B: new Simple({value: 'g'})
+ },
+
+ struct_nested_containers_field: [
+ [
+ {
+ C: [
+ new Simple({value: 'h'}),
+ new Simple({value: 'i'})
+ ]
+ }
+ ]
+ ],
+
+
+ struct_nested_containers_field2: {
+ D: [
+ {
+ DA: new Simple({value: 'j'})
+ },
+ {
+ DB: new Simple({value: 'k'})
+ }
+ ]
+ },
+
+ list_of_list_field: [
+ ['one', 'two'],
+ ['three', 'four'],
+ ['five', 'six']
+ ]
+ }
+ );
+}
+
+
+function createJsObj() {
+
+ return {
+
+ struct_field: {value: 'a'},
+
+ struct_list_field: [
+ {value: 'b'},
+ {value: 'c'}
+ ],
+
+ struct_set_field: [
+ {value: 'd'},
+ {value: 'e'}
+ ],
+
+ struct_map_field: {
+ A: {value: 'f'},
+ B: {value: 'g'}
+ },
+
+ struct_nested_containers_field: [
+ [
+ {
+ C: [
+ {value: 'h'},
+ {value: 'i'}
+ ]
+ }
+ ]
+ ],
+
+ struct_nested_containers_field2: {
+ D: [
+ {
+ DA: {value: 'j'}
+ },
+ {
+ DB: {value: 'k'}
+ }
+ ]
+ },
+
+ list_of_list_field: [
+ ['one', 'two'],
+ ['three', 'four'],
+ ['five', 'six']
+ ]
+ };
+}
+
+
+function assertValues(obj, assert) {
+ assert.equal(obj.struct_field.value, 'a');
+ assert.equal(obj.struct_list_field[0].value, 'b');
+ assert.equal(obj.struct_list_field[1].value, 'c');
+ assert.equal(obj.struct_set_field[0].value, 'd');
+ assert.equal(obj.struct_set_field[1].value, 'e');
+ assert.equal(obj.struct_map_field.A.value, 'f');
+ assert.equal(obj.struct_map_field.B.value, 'g');
+ assert.equal(obj.struct_nested_containers_field[0][0].C[0].value, 'h');
+ assert.equal(obj.struct_nested_containers_field[0][0].C[1].value, 'i');
+ assert.equal(obj.struct_nested_containers_field2.D[0].DA.value, 'j');
+ assert.equal(obj.struct_nested_containers_field2.D[1].DB.value, 'k');
+ assert.equal(obj.list_of_list_field[0][0], 'one');
+ assert.equal(obj.list_of_list_field[0][1], 'two');
+ assert.equal(obj.list_of_list_field[1][0], 'three');
+ assert.equal(obj.list_of_list_field[1][1], 'four');
+ assert.equal(obj.list_of_list_field[2][0], 'five');
+ assert.equal(obj.list_of_list_field[2][1], 'six');
+}
+
+const cases = {
+
+ 'Serialize/deserialize simple struct should return equal object': function(assert) {
+ const tObj = new Simple({value: 'a'});
+ const received = deserialize(serialize(tObj), Simple);
+ assert.ok(tObj !== received);
+ assert.deepEqual(received, tObj);
+ },
+
+
+ 'Serialize/deserialize should return equal object': function(assert) {
+ const tObj = createThriftObj();
+ const received = deserialize(serialize(tObj), Complex);
+ assert.ok(tObj !== received);
+ assert.deepEqual(received, tObj);
+ },
+
+ 'Nested structs and containers initialized from plain js objects should serialize same as if initialized from thrift objects': function(assert) {
+ const tObj1 = createThriftObj();
+ const tObj2 = new Complex(createJsObj());
+ assertValues(tObj2, assert);
+ assert.equal(serialize(tObj2), serialize(tObj1));
+ },
+
+ 'Modifications to args object should not affect constructed Thrift object': function(assert) {
+
+ const args = createJsObj();
+ assertValues(args, assert);
+
+ const tObj = new Complex(args);
+ assertValues(tObj, assert);
+
+ args.struct_field.value = 'ZZZ';
+ args.struct_list_field[0].value = 'ZZZ';
+ args.struct_list_field[1].value = 'ZZZ';
+ args.struct_set_field[0].value = 'ZZZ';
+ args.struct_set_field[1].value = 'ZZZ';
+ args.struct_map_field.A.value = 'ZZZ';
+ args.struct_map_field.B.value = 'ZZZ';
+ args.struct_nested_containers_field[0][0].C[0] = 'ZZZ';
+ args.struct_nested_containers_field[0][0].C[1] = 'ZZZ';
+ args.struct_nested_containers_field2.D[0].DA = 'ZZZ';
+ args.struct_nested_containers_field2.D[0].DB = 'ZZZ';
+
+ assertValues(tObj, assert);
+ },
+
+ 'nulls are ok': function(assert) {
+ const tObj = new Complex({
+ struct_field: null,
+ struct_list_field: null,
+ struct_set_field: null,
+ struct_map_field: null,
+ struct_nested_containers_field: null,
+ struct_nested_containers_field2: null
+ });
+ const received = deserialize(serialize(tObj), Complex);
+ assert.ok(tObj !== received);
+ assert.deepEqual(tObj, received);
+ }
+
+};
+
+Object.keys(cases).forEach(function(caseName) {
+ QUnit.test(caseName, cases[caseName]);
+});
diff --git a/src/jaegertracing/thrift/lib/js/test/jsTestDriver.conf b/src/jaegertracing/thrift/lib/js/test/jsTestDriver.conf
new file mode 100755
index 000000000..eb1588c82
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/jsTestDriver.conf
@@ -0,0 +1,18 @@
+server: http://localhost:9876
+
+load:
+# Qunit adapter
+ - build/js/lib/equiv.js
+ - build/js/lib/QUnitAdapter.js
+# dependencies
+ - build/js/lib/jquery.js
+ - build/js/thrift.js
+ - gen-js/DoubleConstantsTest_constants.js
+ - gen-js/ThriftTest_types.js
+ - gen-js/ThriftTest.js
+# the test suite
+ - test.js
+
+# redirect to the Java based Thrift testserver
+proxy:
+ - {matcher: "*", server: " http://localhost:8088"}
diff --git a/src/jaegertracing/thrift/lib/js/test/phantom-client.js b/src/jaegertracing/thrift/lib/js/test/phantom-client.js
new file mode 100644
index 000000000..d517e71c7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/phantom-client.js
@@ -0,0 +1,382 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ /* jshint -W100 */
+
+(function() {
+ 'use strict';
+
+ // Rudimentary test helper functions
+ // TODO: Return error code based on kind of errors rather than throw
+ var ok = function(t, msg) {
+ if (!t) {
+ console.log('*** FAILED ***');
+ throw new Error(msg);
+ }
+ };
+ var equal = function(a, b) {
+ if (a !== b) {
+ console.log('*** FAILED ***');
+ throw new Error();
+ }
+ };
+ var test = function(name, f) {
+ console.log('TEST : ' + name);
+ f();
+ console.log('OK\n');
+ };
+
+ var parseArgs = function(args) {
+ var skips = [
+ '--transport=http',
+ '--protocol=json'
+ ];
+ var opts = {
+ port: '9090'
+ // protocol: 'json',
+ };
+ var keys = {};
+ for (var key in opts) {
+ keys['--' + key + '='] = key;
+ }
+ for (var i in args) {
+ var arg = args[i];
+ if (skips.indexOf(arg) != -1) {
+ continue;
+ }
+ var hit = false;
+ for (var k in keys) {
+ if (arg.slice(0, k.length) === k) {
+ opts[keys[k]] = arg.slice(k.length);
+ hit = true;
+ break;
+ }
+ }
+ if (!hit) {
+ throw new Error('Unknown argument: ' + arg);
+ }
+ }
+ opts.port = parseInt(opts.port, 10);
+ if (!opts.port || opts.port < 1 || opts.port > 65535) {
+ throw new Error('Invalid port number');
+ }
+ return opts;
+ };
+
+ var execute = function() {
+ console.log('### Apache Thrift Javascript standalone test client');
+ console.log('------------------------------------------------------------');
+
+ phantom.page.injectJs('src/thrift.js');
+ phantom.page.injectJs('test/gen-js/ThriftTest_types.js');
+ phantom.page.injectJs('test/gen-js/ThriftTest.js');
+
+ var system = require('system');
+ var opts = parseArgs(system.args.slice(1));
+ var port = opts.port;
+ var transport = new Thrift.Transport('http://localhost:' + port + '/service');
+ var protocol = new Thrift.Protocol(transport);
+ var client = new ThriftTest.ThriftTestClient(protocol);
+
+
+ // TODO: Remove duplicate code with test.js.
+ // all Languages in UTF-8
+ var stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, Asturianu, Aymar aru, AzÉ™rbaycan, Башҡорт, Boarisch, ŽemaitÄ—Å¡ka, БеларуÑкаÑ, БеларуÑÐºÐ°Ñ (тарашкевіца), БългарÑки, Bamanankan, বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Ðохчийн, Cebuano, á£áŽ³áŽ©, ÄŒesky, СловѣÌньÑкъ / ⰔⰎⰑⰂⰡâ°â° â°”â°â°Ÿ, Чӑвашла, Cymraeg, Dansk, Zazaki, Þ‹Þ¨ÞˆÞ¬Þ€Þ¨Þ„Þ¦ÞÞ°, Ελληνικά, Emiliàn e rumagnòl, English, Esperanto, Español, Eesti, Euskara, Ùارسی, Suomi, Võro, Føroyskt, Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, Avañe'ẽ, ગà«àªœàª°àª¾àª¤à«€, Gaelg, עברית, हिनà¥à¤¦à¥€, Fiji Hindi, Hrvatski, Kreyòl ayisyen, Magyar, Õ€Õ¡ÕµÕ¥Ö€Õ¥Õ¶, Interlingua, Bahasa Indonesia, Ilokano, Ido, Ãslenska, Italiano, 日本語, Lojban, Basa Jawa, ქáƒáƒ áƒ—ული, Kongo, Kalaallisut, ಕನà³à²¨à²¡, 한국어, Къарачай-Малкъар, Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, LatvieÅ¡u, Basa Banyumasan, Malagasy, МакедонÑки, മലയാളം, मराठी, Bahasa Melayu, مازÙرونی, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪Norsk (nynorsk)‬, ‪Norsk (bokmÃ¥l)‬, Nouormand, Diné bizaad, Occitan, Иронау, Papiamentu, Deitsch, Norfuk / Pitkern, Polski, پنجابی, پښتو, Português, Runa Simi, Rumantsch, Romani, Română, РуÑÑкий, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple English, SlovenÄina, SlovenÅ¡Äina, СрпÑки / Srpski, Seeltersk, Svenska, Kiswahili, தமிழà¯, తెలà±à°—à±, Тоҷикӣ, ไทย, Türkmençe, Tagalog, Türkçe, Татарча/Tatarça, УкраїнÑька, اردو, Tiếng Việt, Volapük, Walon, Winaray, å´è¯­, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, Bân-lâm-gú, 粵語";
+
+ function checkRecursively(map1, map2) {
+ if (typeof map1 !== 'function' && typeof map2 !== 'function') {
+ if (!map1 || typeof map1 !== 'object') {
+ equal(map1, map2);
+ } else {
+ for (var key in map1) {
+ checkRecursively(map1[key], map2[key]);
+ }
+ }
+ }
+ }
+
+ test('Void', function() {
+ equal(client.testVoid(), undefined);
+ });
+ test('Binary (String)', function() {
+ var binary = '';
+ for (var v = 255; v >= 0; --v) {
+ binary += String.fromCharCode(v);
+ }
+ equal(client.testBinary(binary), binary);
+ });
+ test('Binary (Uint8Array)', function() {
+ var binary = '';
+ for (var v = 255; v >= 0; --v) {
+ binary += String.fromCharCode(v);
+ }
+ var arr = new Uint8Array(binary.length);
+ for (var i = 0; i < binary.length; ++i) {
+ arr[i] = binary[i].charCodeAt();
+ }
+ equal(client.testBinary(arr), binary);
+ });
+ test('String', function() {
+ equal(client.testString(''), '');
+ equal(client.testString(stringTest), stringTest);
+
+ var specialCharacters = 'quote: \" backslash:' +
+ ' forwardslash-escaped: \/ ' +
+ ' backspace: \b formfeed: \f newline: \n return: \r tab: ' +
+ ' now-all-of-them-together: "\\\/\b\n\r\t' +
+ ' now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><';
+ equal(client.testString(specialCharacters), specialCharacters);
+ });
+ test('Double', function() {
+ equal(client.testDouble(0), 0);
+ equal(client.testDouble(-1), -1);
+ equal(client.testDouble(3.14), 3.14);
+ equal(client.testDouble(Math.pow(2, 60)), Math.pow(2, 60));
+ });
+ test('Bool', function() {
+ equal(client.testBool(true), true);
+ equal(client.testBool(false), false);
+ });
+ test('I8', function() {
+ equal(client.testByte(0), 0);
+ equal(client.testByte(0x01), 0x01);
+ });
+ test('I32', function() {
+ equal(client.testI32(0), 0);
+ equal(client.testI32(Math.pow(2, 30)), Math.pow(2, 30));
+ equal(client.testI32(-Math.pow(2, 30)), -Math.pow(2, 30));
+ });
+ test('I64', function() {
+ equal(client.testI64(0), 0);
+ //This is usually 2^60 but JS cannot represent anything over 2^52 accurately
+ equal(client.testI64(Math.pow(2, 52)), Math.pow(2, 52));
+ equal(client.testI64(-Math.pow(2, 52)), -Math.pow(2, 52));
+ });
+
+ test('Struct', function() {
+ var structTestInput = new ThriftTest.Xtruct();
+ structTestInput.string_thing = 'worked';
+ structTestInput.byte_thing = 0x01;
+ structTestInput.i32_thing = Math.pow(2, 30);
+ //This is usually 2^60 but JS cannot represent anything over 2^52 accurately
+ structTestInput.i64_thing = Math.pow(2, 52);
+
+ var structTestOutput = client.testStruct(structTestInput);
+
+ equal(structTestOutput.string_thing, structTestInput.string_thing);
+ equal(structTestOutput.byte_thing, structTestInput.byte_thing);
+ equal(structTestOutput.i32_thing, structTestInput.i32_thing);
+ equal(structTestOutput.i64_thing, structTestInput.i64_thing);
+
+ equal(JSON.stringify(structTestOutput), JSON.stringify(structTestInput));
+ });
+
+ test('Nest', function() {
+ var xtrTestInput = new ThriftTest.Xtruct();
+ xtrTestInput.string_thing = 'worked';
+ xtrTestInput.byte_thing = 0x01;
+ xtrTestInput.i32_thing = Math.pow(2, 30);
+ //This is usually 2^60 but JS cannot represent anything over 2^52 accurately
+ xtrTestInput.i64_thing = Math.pow(2, 52);
+
+ var nestTestInput = new ThriftTest.Xtruct2();
+ nestTestInput.byte_thing = 0x02;
+ nestTestInput.struct_thing = xtrTestInput;
+ nestTestInput.i32_thing = Math.pow(2, 15);
+
+ var nestTestOutput = client.testNest(nestTestInput);
+
+ equal(nestTestOutput.byte_thing, nestTestInput.byte_thing);
+ equal(nestTestOutput.struct_thing.string_thing, nestTestInput.struct_thing.string_thing);
+ equal(nestTestOutput.struct_thing.byte_thing, nestTestInput.struct_thing.byte_thing);
+ equal(nestTestOutput.struct_thing.i32_thing, nestTestInput.struct_thing.i32_thing);
+ equal(nestTestOutput.struct_thing.i64_thing, nestTestInput.struct_thing.i64_thing);
+ equal(nestTestOutput.i32_thing, nestTestInput.i32_thing);
+
+ equal(JSON.stringify(nestTestOutput), JSON.stringify(nestTestInput));
+ });
+
+ test('Map', function() {
+ var mapTestInput = {7: 77, 8: 88, 9: 99};
+
+ var mapTestOutput = client.testMap(mapTestInput);
+
+ for (var key in mapTestOutput) {
+ equal(mapTestOutput[key], mapTestInput[key]);
+ }
+ });
+
+ test('StringMap', function() {
+ var mapTestInput = {
+ 'a': '123', 'a b': 'with spaces ', 'same': 'same', '0': 'numeric key',
+ 'longValue': stringTest, stringTest: 'long key'
+ };
+
+ var mapTestOutput = client.testStringMap(mapTestInput);
+
+ for (var key in mapTestOutput) {
+ equal(mapTestOutput[key], mapTestInput[key]);
+ }
+ });
+
+ test('Set', function() {
+ var setTestInput = [1, 2, 3];
+ ok(client.testSet(setTestInput), setTestInput);
+ });
+
+ test('List', function() {
+ var listTestInput = [1, 2, 3];
+ ok(client.testList(listTestInput), listTestInput);
+ });
+
+ test('Enum', function() {
+ equal(client.testEnum(ThriftTest.Numberz.ONE), ThriftTest.Numberz.ONE);
+ });
+
+ test('TypeDef', function() {
+ equal(client.testTypedef(69), 69);
+ });
+
+ test('Skip', function() {
+ var structTestInput = new ThriftTest.Xtruct();
+ var modifiedClient = new ThriftTest.ThriftTestClient(protocol);
+
+ modifiedClient.recv_testStruct = function() {
+ var input = modifiedClient.input;
+ var xtruct3 = new ThriftTest.Xtruct3();
+
+ input.readMessageBegin();
+ input.readStructBegin();
+
+ // read Xtruct data with Xtruct3
+ input.readFieldBegin();
+ xtruct3.read(input);
+ input.readFieldEnd();
+ // read Thrift.Type.STOP message
+ input.readFieldBegin();
+ input.readFieldEnd();
+
+ input.readStructEnd();
+ input.readMessageEnd();
+
+ return xtruct3;
+ };
+
+ structTestInput.string_thing = 'worked';
+ structTestInput.byte_thing = 0x01;
+ structTestInput.i32_thing = Math.pow(2, 30);
+ structTestInput.i64_thing = Math.pow(2, 52);
+
+ var structTestOutput = modifiedClient.testStruct(structTestInput);
+
+ equal(structTestOutput instanceof ThriftTest.Xtruct3, true);
+ equal(structTestOutput.string_thing, structTestInput.string_thing);
+ equal(structTestOutput.changed, null);
+ equal(structTestOutput.i32_thing, structTestInput.i32_thing);
+ equal(structTestOutput.i64_thing, structTestInput.i64_thing);
+ });
+
+ test('MapMap', function() {
+ var mapMapTestExpectedResult = {
+ '4': {'1': 1, '2': 2, '3': 3, '4': 4},
+ '-4': {'-4': -4, '-3': -3, '-2': -2, '-1': -1}
+ };
+
+ var mapMapTestOutput = client.testMapMap(1);
+
+
+ for (var key in mapMapTestOutput) {
+ for (var key2 in mapMapTestOutput[key]) {
+ equal(mapMapTestOutput[key][key2], mapMapTestExpectedResult[key][key2]);
+ }
+ }
+
+ checkRecursively(mapMapTestOutput, mapMapTestExpectedResult);
+ });
+
+ test('Xception', function() {
+ try {
+ client.testException('Xception');
+ ok(false);
+ } catch (e) {
+ equal(e.errorCode, 1001);
+ equal(e.message, 'Xception');
+ }
+ });
+
+ test('no Exception', function() {
+ try {
+ client.testException('no Exception');
+ } catch (e) {
+ ok(false);
+ }
+ });
+
+ test('TException', function() {
+ try {
+ client.testException('TException');
+ ok(false);
+ } catch (e) {
+ ok(ok);
+ }
+ });
+
+ var crazy = {
+ 'userMap': { '5': 5, '8': 8 },
+ 'xtructs': [{
+ 'string_thing': 'Goodbye4',
+ 'byte_thing': 4,
+ 'i32_thing': 4,
+ 'i64_thing': 4
+ },
+ {
+ 'string_thing': 'Hello2',
+ 'byte_thing': 2,
+ 'i32_thing': 2,
+ 'i64_thing': 2
+ }]
+ };
+ test('Insanity', function() {
+ var insanity = {
+ '1': {
+ '2': crazy,
+ '3': crazy
+ },
+ '2': { '6': { 'userMap': null, 'xtructs': null } }
+ };
+ var res = client.testInsanity(new ThriftTest.Insanity(crazy));
+ ok(res, JSON.stringify(res));
+ ok(insanity, JSON.stringify(insanity));
+
+ checkRecursively(res, insanity);
+ });
+
+ console.log('------------------------------------------------------------');
+ console.log('### All tests succeeded.');
+ return 0;
+ };
+
+ try {
+ var ret = execute();
+ phantom.exit(ret);
+ } catch (err) {
+ // Catch all and exit to avoid hang.
+ console.error(err);
+ phantom.exit(1);
+ }
+})();
diff --git a/src/jaegertracing/thrift/lib/js/test/phantomjs-qunit.js b/src/jaegertracing/thrift/lib/js/test/phantomjs-qunit.js
new file mode 100755
index 000000000..c1d7a5bb8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/phantomjs-qunit.js
@@ -0,0 +1,91 @@
+/*jshint evil:true*/
+
+/* This file is only used by the test suite.
+ *
+ * Origin: https://github.com/ariya/phantomjs/blob/master/examples/run-qunit.js
+ * License: https://github.com/ariya/phantomjs/blob/master/LICENSE.BSD
+ *
+ * Inclusion into Apache products is allowed according to http://www.apache.org/legal/3party.html
+ */
+
+var system = require('system');
+
+
+/**
+ * Wait until the test condition is true or a timeout occurs. Useful for waiting
+ * on a server response or for a ui change (fadeIn, etc.) to occur.
+ *
+ * @param testFx javascript condition that evaluates to a boolean,
+ * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
+ * as a callback function.
+ * @param onReady what to do when testFx condition is fulfilled,
+ * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
+ * as a callback function.
+ * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
+ */
+function waitFor(testFx, onReady, timeOutMillis) {
+ var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3001, //< Default Max Timout is 3s
+ start = new Date().getTime(),
+ condition = false,
+ interval = setInterval(function() {
+ if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) {
+ // If not time-out yet and condition not yet fulfilled
+ condition = (typeof(testFx) === 'string' ? eval(testFx) : testFx()); //< defensive code
+ } else {
+ if (!condition) {
+ // If condition still not fulfilled (timeout but condition is 'false')
+ console.log("'waitFor()' timeout");
+ phantom.exit(1);
+ } else {
+ // Condition fulfilled (timeout and/or condition is 'true')
+ console.log("'waitFor()' finished in " + (new Date().getTime() - start) + 'ms.');
+ if (typeof(onReady) === 'string') {
+ eval(onReady);
+ } else {
+ onReady(); //< Do what it's supposed to do once the condition is fulfilled
+ }
+ clearInterval(interval); //< Stop this interval
+ }
+ }
+ }, 100); //< repeat check every 250ms
+}
+
+
+if (system.args.length === 1 || system.args.length > 3) {
+ console.log('Usage: phantomjs phantomjs-qunit.js URL');
+ phantom.exit(1);
+}
+
+var page = new WebPage();
+
+// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
+page.onConsoleMessage = function(msg) {
+ console.log(msg);
+};
+
+page.open(system.args[1], function(status) {
+ if (status !== 'success') {
+ console.log('Unable to access network');
+ phantom.exit(1);
+ } else {
+ waitFor(function() {
+ return page.evaluate(function() {
+ var el = document.getElementById('qunit-testresult');
+ if (el && el.innerText.match('completed')) {
+ return true;
+ }
+ return false;
+ });
+ }, function() {
+ var failedNum = page.evaluate(function() {
+ var el = document.getElementById('qunit-testresult');
+ console.log(el.innerText);
+ try {
+ return el.getElementsByClassName('failed')[0].innerHTML;
+ } catch (e) { }
+ return 10000;
+ });
+ phantom.exit((parseInt(failedNum, 10) > 0) ? 1 : 0);
+ });
+ }
+});
diff --git a/src/jaegertracing/thrift/lib/js/test/server_http.js b/src/jaegertracing/thrift/lib/js/test/server_http.js
new file mode 100644
index 000000000..8380c3a77
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/server_http.js
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// This HTTP server is designed to serve the test.html browser
+// based JavaScript test page (which must be in the current directory).
+// This server also supplies the Thrift based test service, which depends
+// on the standard ThriftTest.thrift IDL service (which must be compiled
+// for Node and browser based JavaScript in ./gen-nodejs and ./gen-js
+// respectively).
+//
+// Using the command flag --es6, this server can be run using nodejs code built
+// for the es6 environment or for pre-es6 environment.
+//
+
+const thrift = require('../../nodejs/lib/thrift');
+const es6Mode = process.argv.includes('--es6');
+const genFolder = es6Mode ? 'gen-nodejs-es6' : 'gen-nodejs';
+const ThriftTestSvc = require(`./${genFolder}/ThriftTest.js`);
+const ThriftTestHandler = require('./test_handler').ThriftTestHandler;
+
+const ThriftTestSvcOpt = {
+ transport: thrift.TBufferedTransport,
+ protocol: thrift.TJSONProtocol,
+ processor: ThriftTestSvc,
+ handler: ThriftTestHandler
+};
+
+const ThriftWebServerOptions = {
+ files: __dirname,
+ services: {
+ '/service': ThriftTestSvcOpt
+ }
+};
+
+const server = thrift.createWebServer(ThriftWebServerOptions);
+const port = es6Mode ? 8088 : 8089;
+server.listen(port);
+console.log(`Serving files from: ${__dirname}`);
+console.log(`Http/Thrift Server (ES6 mode ${es6Mode}) running on port: ${port}`);
diff --git a/src/jaegertracing/thrift/lib/js/test/server_https.js b/src/jaegertracing/thrift/lib/js/test/server_https.js
new file mode 100644
index 000000000..1a171dde6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/server_https.js
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+//This HTTP server is designed to server the test.html browser
+// based JavaScript test page (which must be in the current directory).
+// This server also supplies the Thrift based test service, which depends
+// on the standard ThriftTest.thrift IDL service (which must be compiled
+// for Node and browser based JavaScript in ./gen-nodejs and ./gen-js
+// respectively). The current directory must also include the browser
+// support libraries for test.html (jquery.js, qunit.js and qunit.css
+// in ./build/js/lib).
+
+const fs = require('fs');
+const thrift = require('../../nodejs/lib/thrift');
+const es6Mode = process.argv.includes('--es6');
+const genFolder = es6Mode ? 'gen-nodejs-es6' : 'gen-nodejs';
+const ThriftTestSvc = require(`./${genFolder}/ThriftTest.js`);
+const ThriftTestHandler = require('./test_handler').ThriftTestHandler;
+
+//Setup the I/O stack options for the ThriftTest service
+const ThriftTestSvcOpt = {
+ transport: thrift.TBufferedTransport,
+ protocol: thrift.TJSONProtocol,
+ processor: ThriftTestSvc,
+ handler: ThriftTestHandler
+};
+
+const ThriftWebServerOptions = {
+ files: __dirname,
+ tls: {
+ key: fs.readFileSync('../../../test/keys/server.key'),
+ cert: fs.readFileSync('../../../test/keys/server.crt')
+ },
+ services: {
+ '/service': ThriftTestSvcOpt
+ }
+};
+
+const server = thrift.createWebServer(ThriftWebServerOptions);
+const port = es6Mode ? 8090 : 8091;
+server.listen(port);
+console.log(`Serving files from: ${__dirname}`);
+console.log(`Http/Thrift Server (ES6 mode ${es6Mode}) running on port: ${port}`);
diff --git a/src/jaegertracing/thrift/lib/js/test/src/test/Httpd.java b/src/jaegertracing/thrift/lib/js/test/src/test/Httpd.java
new file mode 100644
index 000000000..e4fc0ccd0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/src/test/Httpd.java
@@ -0,0 +1,323 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URLDecoder;
+import java.util.Locale;
+
+import org.apache.http.ConnectionClosedException;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpServerConnection;
+import org.apache.http.HttpStatus;
+import org.apache.http.MethodNotSupportedException;
+import org.apache.http.entity.ContentProducer;
+import org.apache.http.entity.EntityTemplate;
+import org.apache.http.entity.FileEntity;
+import org.apache.http.impl.DefaultHttpResponseFactory;
+import org.apache.http.impl.DefaultHttpServerConnection;
+import org.apache.http.impl.NoConnectionReuseStrategy;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.BasicHttpProcessor;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.apache.http.protocol.HttpRequestHandlerRegistry;
+import org.apache.http.protocol.HttpService;
+import org.apache.http.util.EntityUtils;
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.protocol.TJSONProtocol;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.transport.TMemoryBuffer;
+
+import thrift.test.ThriftTest;
+import org.apache.thrift.server.ServerTestBase.TestHandler;
+
+import eu.medsea.mimeutil.detector.ExtensionMimeDetector;
+import eu.medsea.mimeutil.MimeUtil2;
+import eu.medsea.mimeutil.MimeType;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * Basic, yet fully functional and spec compliant, HTTP/1.1 file server.
+ * <p>
+ * Please note the purpose of this application is demonstrate the usage of
+ * HttpCore APIs. It is NOT intended to demonstrate the most efficient way of
+ * building an HTTP file server.
+ *
+ *
+ */
+public class Httpd {
+
+ public static void main(String[] args) throws Exception {
+ if (args.length < 1) {
+ System.err.println("Please specify document root directory");
+ System.exit(1);
+ }
+ Thread t = new RequestListenerThread(8088, args[0]);
+ t.setDaemon(false);
+ t.start();
+ }
+
+ static class HttpFileHandler implements HttpRequestHandler {
+
+ private final String docRoot;
+
+ public HttpFileHandler(final String docRoot) {
+ super();
+ this.docRoot = docRoot;
+ }
+
+ public void handle(final HttpRequest request, final HttpResponse response, final HttpContext context) throws HttpException, IOException {
+
+ String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH);
+ if (!method.equals("GET") && !method.equals("HEAD") && !method.equals("POST")) {
+ throw new MethodNotSupportedException(method + " method not supported");
+ }
+ String target = request.getRequestLine().getUri();
+
+ if (request instanceof HttpEntityEnclosingRequest && target.equals("/service")) {
+ HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
+ byte[] entityContent = EntityUtils.toByteArray(entity);
+ System.out.println("Incoming content: " + new String(entityContent));
+
+ final String output = this.thriftRequest(entityContent);
+
+ System.out.println("Outgoing content: "+output);
+
+ EntityTemplate body = new EntityTemplate(new ContentProducer() {
+
+ public void writeTo(final OutputStream outstream) throws IOException {
+ OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
+ writer.write(output);
+ writer.flush();
+ }
+
+ });
+ body.setContentType("text/html; charset=UTF-8");
+ response.setEntity(body);
+ } else {
+ if(target.indexOf("?") != -1) {
+ target = target.substring(1, target.indexOf("?"));
+ }
+
+ final File file = new File(this.docRoot, URLDecoder.decode(target, "UTF-8"));
+
+ if (!file.exists()) {
+
+ response.setStatusCode(HttpStatus.SC_NOT_FOUND);
+ EntityTemplate body = new EntityTemplate(new ContentProducer() {
+
+ public void writeTo(final OutputStream outstream) throws IOException {
+ OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
+ writer.write("<html><body><h1>");
+ writer.write("File ");
+ writer.write(file.getPath());
+ writer.write(" not found");
+ writer.write("</h1></body></html>");
+ writer.flush();
+ }
+
+ });
+ body.setContentType("text/html; charset=UTF-8");
+ response.setEntity(body);
+ System.out.println("File " + file.getPath() + " not found");
+
+ } else if (!file.canRead() || file.isDirectory()) {
+
+ response.setStatusCode(HttpStatus.SC_FORBIDDEN);
+ EntityTemplate body = new EntityTemplate(new ContentProducer() {
+
+ public void writeTo(final OutputStream outstream) throws IOException {
+ OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
+ writer.write("<html><body><h1>");
+ writer.write("Access denied");
+ writer.write("</h1></body></html>");
+ writer.flush();
+ }
+
+ });
+ body.setContentType("text/html; charset=UTF-8");
+ response.setEntity(body);
+ System.out.println("Cannot read file " + file.getPath());
+
+ } else {
+
+ String mimeType = "application/octet-stream";
+ MimeUtil2 mimeUtil = new MimeUtil2();
+ synchronized (this) {
+ mimeUtil.registerMimeDetector(ExtensionMimeDetector.class.getName());
+ }
+ Collection<MimeType> collection = mimeUtil.getMimeTypes(file);
+ Iterator<MimeType> iterator = collection.iterator();
+ while(iterator.hasNext()) {
+ MimeType mt = iterator.next();
+ mimeType = mt.getMediaType() + "/" + mt.getSubType();
+ break;
+ }
+
+ response.setStatusCode(HttpStatus.SC_OK);
+ FileEntity body = new FileEntity(file, mimeType);
+ response.addHeader("Content-Type", mimeType);
+ response.setEntity(body);
+ System.out.println("Serving file " + file.getPath());
+
+ }
+ }
+ }
+
+ private String thriftRequest(byte[] input){
+ try{
+
+ //Input
+ TMemoryBuffer inbuffer = new TMemoryBuffer(input.length);
+ inbuffer.write(input);
+ TProtocol inprotocol = new TJSONProtocol(inbuffer);
+
+ //Output
+ TMemoryBuffer outbuffer = new TMemoryBuffer(100);
+ TProtocol outprotocol = new TJSONProtocol(outbuffer);
+
+ TProcessor processor = new ThriftTest.Processor(new TestHandler());
+ processor.process(inprotocol, outprotocol);
+
+ byte[] output = new byte[outbuffer.length()];
+ outbuffer.readAll(output, 0, output.length);
+
+ return new String(output,"UTF-8");
+ }catch(Throwable t){
+ return "Error:"+t.getMessage();
+ }
+
+
+ }
+
+ }
+
+ static class RequestListenerThread extends Thread {
+
+ private final ServerSocket serversocket;
+ private final HttpParams params;
+ private final HttpService httpService;
+
+ public RequestListenerThread(int port, final String docroot) throws IOException {
+ this.serversocket = new ServerSocket(port);
+ this.params = new BasicHttpParams();
+ this.params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 1000).setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
+ .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false).setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
+ .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1");
+
+ // Set up the HTTP protocol processor
+ HttpProcessor httpproc = new BasicHttpProcessor();
+
+ // Set up request handlers
+ HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry();
+ reqistry.register("*", new HttpFileHandler(docroot));
+
+ // Set up the HTTP service
+ this.httpService = new HttpService(httpproc, new NoConnectionReuseStrategy(), new DefaultHttpResponseFactory());
+ this.httpService.setParams(this.params);
+ this.httpService.setHandlerResolver(reqistry);
+ }
+
+ public void run() {
+ System.out.println("Listening on port " + this.serversocket.getLocalPort());
+ System.out.println("Point your browser to http://localhost:8088/test/test.html");
+
+ while (!Thread.interrupted()) {
+ try {
+ // Set up HTTP connection
+ Socket socket = this.serversocket.accept();
+ DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
+ System.out.println("Incoming connection from " + socket.getInetAddress());
+ conn.bind(socket, this.params);
+
+ // Start worker thread
+ Thread t = new WorkerThread(this.httpService, conn);
+ t.setDaemon(true);
+ t.start();
+ } catch (InterruptedIOException ex) {
+ break;
+ } catch (IOException e) {
+ System.err.println("I/O error initialising connection thread: " + e.getMessage());
+ break;
+ }
+ }
+ }
+ }
+
+ static class WorkerThread extends Thread {
+
+ private final HttpService httpservice;
+ private final HttpServerConnection conn;
+
+ public WorkerThread(final HttpService httpservice, final HttpServerConnection conn) {
+ super();
+ this.httpservice = httpservice;
+ this.conn = conn;
+ }
+
+ public void run() {
+ System.out.println("New connection thread");
+ HttpContext context = new BasicHttpContext(null);
+ try {
+ while (!Thread.interrupted() && this.conn.isOpen()) {
+ this.httpservice.handleRequest(this.conn, context);
+ }
+ } catch (ConnectionClosedException ex) {
+ System.err.println("Client closed connection");
+ } catch (IOException ex) {
+ System.err.println("I/O error: " + ex.getMessage());
+ } catch (HttpException ex) {
+ System.err.println("Unrecoverable HTTP protocol violation: " + ex.getMessage());
+ } finally {
+ try {
+ this.conn.shutdown();
+ } catch (IOException ignore) {
+ }
+ }
+ }
+
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/js/test/test-async.js b/src/jaegertracing/thrift/lib/js/test/test-async.js
new file mode 100644
index 000000000..8c6b13e00
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/test-async.js
@@ -0,0 +1,370 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ /* jshint -W100 */
+
+/*
+ * Fully Async JavaScript test suite for ThriftTest.thrift.
+ * These tests are designed to exercise the WebSocket transport
+ * (which is exclusively async).
+ *
+ * To compile client code for this test use:
+ * $ thrift -gen js ThriftTest.thrift
+ */
+
+
+
+// all Languages in UTF-8
+const stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, Asturianu, Aymar aru, AzÉ™rbaycan, Башҡорт, Boarisch, ŽemaitÄ—Å¡ka, БеларуÑкаÑ, БеларуÑÐºÐ°Ñ (тарашкевіца), БългарÑки, Bamanankan, বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Ðохчийн, Cebuano, á£áŽ³áŽ©, ÄŒesky, СловѣÌньÑкъ / ⰔⰎⰑⰂⰡâ°â° â°”â°â°Ÿ, Чӑвашла, Cymraeg, Dansk, Zazaki, Þ‹Þ¨ÞˆÞ¬Þ€Þ¨Þ„Þ¦ÞÞ°, Ελληνικά, Emiliàn e rumagnòl, English, Esperanto, Español, Eesti, Euskara, Ùارسی, Suomi, Võro, Føroyskt, Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, Avañe'ẽ, ગà«àªœàª°àª¾àª¤à«€, Gaelg, עברית, हिनà¥à¤¦à¥€, Fiji Hindi, Hrvatski, Kreyòl ayisyen, Magyar, Õ€Õ¡ÕµÕ¥Ö€Õ¥Õ¶, Interlingua, Bahasa Indonesia, Ilokano, Ido, Ãslenska, Italiano, 日本語, Lojban, Basa Jawa, ქáƒáƒ áƒ—ული, Kongo, Kalaallisut, ಕನà³à²¨à²¡, 한국어, Къарачай-Малкъар, Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, LatvieÅ¡u, Basa Banyumasan, Malagasy, МакедонÑки, മലയാളം, मराठी, Bahasa Melayu, مازÙرونی, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪Norsk (nynorsk)‬, ‪Norsk (bokmÃ¥l)‬, Nouormand, Diné bizaad, Occitan, Иронау, Papiamentu, Deitsch, Norfuk / Pitkern, Polski, پنجابی, پښتو, Português, Runa Simi, Rumantsch, Romani, Română, РуÑÑкий, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple English, SlovenÄina, SlovenÅ¡Äina, СрпÑки / Srpski, Seeltersk, Svenska, Kiswahili, தமிழà¯, తెలà±à°—à±, Тоҷикӣ, ไทย, Türkmençe, Tagalog, Türkçe, Татарча/Tatarça, УкраїнÑька, اردو, Tiếng Việt, Volapük, Walon, Winaray, å´è¯­, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, Bân-lâm-gú, 粵語";
+
+function checkRecursively(assert, map1, map2) {
+ if (typeof map1 !== 'function' && typeof map2 !== 'function') {
+ if (!map1 || typeof map1 !== 'object') {
+ assert.equal(map1, map2);
+ } else {
+ for (let key in map1) {
+ checkRecursively(assert, map1[key], map2[key]);
+ }
+ }
+ }
+}
+
+QUnit.module('Base Types');
+
+ QUnit.test('Void', function(assert) {
+ assert.expect(1);
+ const done = assert.async();
+ client.testVoid(function(result) {
+ assert.equal(result, undefined);
+ done();
+ });
+ });
+
+
+ QUnit.test('String', function(assert) {
+ assert.expect(3);
+ const done = assert.async(3);
+ client.testString('', function(result) {
+ assert.equal(result, '');
+ done();
+ });
+ client.testString(stringTest, function(result) {
+ assert.equal(result, stringTest);
+ done();
+ });
+
+ const specialCharacters = 'quote: \" backslash:' +
+ ' forwardslash-escaped: \/ ' +
+ ' backspace: \b formfeed: \f newline: \n return: \r tab: ' +
+ ' now-all-of-them-together: "\\\/\b\n\r\t' +
+ ' now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><';
+ client.testString(specialCharacters, function(result) {
+ assert.equal(result, specialCharacters);
+ done();
+ });
+ });
+ QUnit.test('Double', function(assert) {
+ assert.expect(4);
+ const done = assert.async(4);
+ client.testDouble(0, function(result) {
+ assert.equal(result, 0);
+ done();
+ });
+ client.testDouble(-1, function(result) {
+ assert.equal(result, -1);
+ done();
+ });
+ client.testDouble(3.14, function(result) {
+ assert.equal(result, 3.14);
+ done();
+ });
+ client.testDouble(Math.pow(2, 60), function(result) {
+ assert.equal(result, Math.pow(2, 60));
+ done();
+ });
+ });
+ // TODO: add testBinary()
+ QUnit.test('Byte', function(assert) {
+ assert.expect(2);
+ const done = assert.async(2);
+ client.testByte(0, function(result) {
+ assert.equal(result, 0);
+ done();
+ });
+ client.testByte(0x01, function(result) {
+ assert.equal(result, 0x01);
+ done();
+ });
+ });
+ QUnit.test('I32', function(assert) {
+ assert.expect(3);
+ const done = assert.async(3);
+ client.testI32(0, function(result) {
+ assert.equal(result, 0);
+ done();
+ });
+ client.testI32(Math.pow(2, 30), function(result) {
+ assert.equal(result, Math.pow(2, 30));
+ done();
+ });
+ client.testI32(-Math.pow(2, 30), function(result) {
+ assert.equal(result, -Math.pow(2, 30));
+ done();
+ });
+ });
+ QUnit.test('I64', function(assert) {
+ assert.expect(3);
+ const done = assert.async(3);
+ client.testI64(0, function(result) {
+ assert.equal(result, 0);
+ done();
+ });
+ //This is usually 2^60 but JS cannot represent anything over 2^52 accurately
+ client.testI64(Math.pow(2, 52), function(result) {
+ assert.equal(result, Math.pow(2, 52));
+ done();
+ });
+ client.testI64(-Math.pow(2, 52), function(result) {
+ assert.equal(result, -Math.pow(2, 52));
+ done();
+ });
+ });
+
+
+
+
+QUnit.module('Structured Types');
+
+ QUnit.test('Struct', function(assert) {
+ assert.expect(5);
+ const done = assert.async();
+ const structTestInput = new ThriftTest.Xtruct();
+ structTestInput.string_thing = 'worked';
+ structTestInput.byte_thing = 0x01;
+ structTestInput.i32_thing = Math.pow(2, 30);
+ //This is usually 2^60 but JS cannot represent anything over 2^52 accurately
+ structTestInput.i64_thing = Math.pow(2, 52);
+
+ client.testStruct(structTestInput, function(result) {
+ assert.equal(result.string_thing, structTestInput.string_thing);
+ assert.equal(result.byte_thing, structTestInput.byte_thing);
+ assert.equal(result.i32_thing, structTestInput.i32_thing);
+ assert.equal(result.i64_thing, structTestInput.i64_thing);
+ assert.equal(JSON.stringify(result), JSON.stringify(structTestInput));
+ done();
+ });
+ });
+
+ QUnit.test('Nest', function(assert) {
+ assert.expect(7);
+ const done = assert.async();
+ const xtrTestInput = new ThriftTest.Xtruct();
+ xtrTestInput.string_thing = 'worked';
+ xtrTestInput.byte_thing = 0x01;
+ xtrTestInput.i32_thing = Math.pow(2, 30);
+ //This is usually 2^60 but JS cannot represent anything over 2^52 accurately
+ xtrTestInput.i64_thing = Math.pow(2, 52);
+
+ const nestTestInput = new ThriftTest.Xtruct2();
+ nestTestInput.byte_thing = 0x02;
+ nestTestInput.struct_thing = xtrTestInput;
+ nestTestInput.i32_thing = Math.pow(2, 15);
+
+ client.testNest(nestTestInput, function(result) {
+ assert.equal(result.byte_thing, nestTestInput.byte_thing);
+ assert.equal(result.struct_thing.string_thing, nestTestInput.struct_thing.string_thing);
+ assert.equal(result.struct_thing.byte_thing, nestTestInput.struct_thing.byte_thing);
+ assert.equal(result.struct_thing.i32_thing, nestTestInput.struct_thing.i32_thing);
+ assert.equal(result.struct_thing.i64_thing, nestTestInput.struct_thing.i64_thing);
+ assert.equal(result.i32_thing, nestTestInput.i32_thing);
+ assert.equal(JSON.stringify(result), JSON.stringify(nestTestInput));
+ done();
+ });
+ });
+
+ QUnit.test('Map', function(assert) {
+ assert.expect(3);
+ const done = assert.async();
+ const mapTestInput = {7: 77, 8: 88, 9: 99};
+
+ client.testMap(mapTestInput, function(result) {
+ for (let key in result) {
+ assert.equal(result[key], mapTestInput[key]);
+ }
+ done();
+ });
+ });
+
+ QUnit.test('StringMap', function(assert) {
+ assert.expect(6);
+ const done = assert.async();
+ const mapTestInput = {
+ 'a': '123', 'a b': 'with spaces ', 'same': 'same', '0': 'numeric key',
+ 'longValue': stringTest, stringTest: 'long key'
+ };
+
+ client.testStringMap(mapTestInput, function(result) {
+ for (let key in result) {
+ assert.equal(result[key], mapTestInput[key]);
+ }
+ done();
+ });
+ });
+
+ QUnit.test('Set', function(assert) {
+ assert.expect(1);
+ const done = assert.async();
+ const setTestInput = [1, 2, 3];
+ client.testSet(setTestInput, function(result) {
+ assert.ok(result, setTestInput);
+ done();
+ });
+ });
+
+ QUnit.test('List', function(assert) {
+ assert.expect(1);
+ const done = assert.async();
+ const listTestInput = [1, 2, 3];
+ client.testList(listTestInput, function(result) {
+ assert.ok(result, listTestInput);
+ done();
+ });
+ });
+
+ QUnit.test('Enum', function(assert) {
+ assert.expect(1);
+ const done = assert.async();
+ client.testEnum(ThriftTest.Numberz.ONE, function(result) {
+ assert.equal(result, ThriftTest.Numberz.ONE);
+ done();
+ });
+ });
+
+ QUnit.test('TypeDef', function(assert) {
+ assert.expect(1);
+ const done = assert.async();
+ client.testTypedef(69, function(result) {
+ assert.equal(result, 69);
+ done();
+ });
+ });
+
+
+QUnit.module('deeper!');
+
+ QUnit.test('MapMap', function(assert) {
+ assert.expect(16);
+ const done = assert.async();
+ const mapMapTestExpectedResult = {
+ '4': {'1': 1, '2': 2, '3': 3, '4': 4},
+ '-4': {'-4': -4, '-3': -3, '-2': -2, '-1': -1}
+ };
+
+ client.testMapMap(1, function(result) {
+ for (let key in result) {
+ for (let key2 in result[key]) {
+ assert.equal(result[key][key2], mapMapTestExpectedResult[key][key2]);
+ }
+ }
+ checkRecursively(assert, result, mapMapTestExpectedResult);
+ done();
+ });
+ });
+
+
+QUnit.module('Exception');
+
+ QUnit.test('Xception', function(assert) {
+ assert.expect(2);
+ const done = assert.async();
+ client.testException('Xception', function(e) {
+ assert.equal(e.errorCode, 1001);
+ assert.equal(e.message, 'Xception');
+ done();
+ });
+ });
+
+ QUnit.test('no Exception', function(assert) {
+ assert.expect(1);
+ const done = assert.async();
+ client.testException('no Exception', function(e) {
+ assert.ok(!e);
+ done();
+ });
+ });
+
+QUnit.module('Insanity');
+
+ QUnit.test('testInsanity', function(assert) {
+ assert.expect(24);
+ const done = assert.async();
+ const insanity = {
+ '1': {
+ '2': {
+ 'userMap': { '5': 5, '8': 8 },
+ 'xtructs': [{
+ 'string_thing': 'Goodbye4',
+ 'byte_thing': 4,
+ 'i32_thing': 4,
+ 'i64_thing': 4
+ },
+ {
+ 'string_thing': 'Hello2',
+ 'byte_thing': 2,
+ 'i32_thing': 2,
+ 'i64_thing': 2
+ }
+ ]
+ },
+ '3': {
+ 'userMap': { '5': 5, '8': 8 },
+ 'xtructs': [{
+ 'string_thing': 'Goodbye4',
+ 'byte_thing': 4,
+ 'i32_thing': 4,
+ 'i64_thing': 4
+ },
+ {
+ 'string_thing': 'Hello2',
+ 'byte_thing': 2,
+ 'i32_thing': 2,
+ 'i64_thing': 2
+ }
+ ]
+ }
+ },
+ '2': { '6': { 'userMap': null, 'xtructs': null } }
+ };
+ client.testInsanity(new ThriftTest.Insanity(), function(res) {
+ assert.ok(res, JSON.stringify(res));
+ assert.ok(insanity, JSON.stringify(insanity));
+ checkRecursively(assert, res, insanity);
+ done();
+ });
+ });
+
+QUnit.module('Oneway');
+
+ QUnit.test('testOneway', function(assert) {
+ assert.expect(1);
+ const done = assert.async();
+ client.testOneway(1, function(result) {
+ assert.equal(result, undefined);
+ done();
+ });
+ });
diff --git a/src/jaegertracing/thrift/lib/js/test/test-deep-constructor.html b/src/jaegertracing/thrift/lib/js/test/test-deep-constructor.html
new file mode 100755
index 000000000..462481a2d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/test-deep-constructor.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>Thrift Javascript Bindings: Unit Test</title>
+
+ <script src="build/js/lib/JSONInt64.js" type="text/javascript" charset="utf-8"></script>
+ <script src="build/js/thrift.js" type="text/javascript" charset="utf-8"></script>
+ <script src="gen-js/JsDeepConstructorTest_types.js" type="text/javascript" charset="utf-8"></script>
+ <!-- jQuery -->
+ <script type="text/javascript" src="build/js/lib/jquery.js" charset="utf-8"></script>
+
+ <!-- QUnit Test framework-->
+ <script type="text/javascript" src="build/js/lib/qunit.js" charset="utf-8"></script>
+ <link rel="stylesheet" href="build/js/lib/qunit.css" type="text/css" media="screen" />
+
+ <!-- the Test Suite-->
+ <script type="text/javascript" src="deep-constructor.test.js" charset="utf-8"></script>
+</head>
+<body>
+ <h1 id="qunit-header">Thrift Javascript Bindings: Deep Constructor Test (<a href="https://github.com/apache/thrift/blob/master/test/JsDeepConstructorTest.thrift">JsDeepConstructorTest.thrift</a>)</h1>
+ <h2 id="qunit-banner"></h2>
+ <div id="qunit-testrunner-toolbar"></div>
+ <h2 id="qunit-userAgent"></h2>
+ <ol id="qunit-tests"><li><!-- get valid xhtml strict--></li></ol>
+ <p>
+ <a href="http://validator.w3.org/check/referer"><img
+ src="http://www.w3.org/Icons/valid-xhtml10"
+ alt="Valid XHTML 1.0!" height="31" width="88" /></a>
+ </p>
+</body>
+</html>
diff --git a/src/jaegertracing/thrift/lib/js/test/test-double-rendering.html b/src/jaegertracing/thrift/lib/js/test/test-double-rendering.html
new file mode 100644
index 000000000..7a430a507
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/test-double-rendering.html
@@ -0,0 +1,55 @@
++<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>Rendering Double Constants in JS: Unit Test</title>
+
+ <script src="build/js/thrift.js" type="text/javascript" charset="utf-8"></script>
+ <script src="gen-js/ThriftTest_types.js" type="text/javascript" charset="utf-8"></script>
+ <script src="gen-js/ThriftTest.js" type="text/javascript" charset="utf-8"></script>
+ <!-- double constants to check -->
+ <script src="gen-js/DoubleConstantsTest_types.js" type="text/javascript" charset="utf-8"></script>
+
+ <!-- jQuery -->
+ <script type="text/javascript" src="build/js/lib/jquery.js" charset="utf-8"></script>
+
+ <!-- QUnit Test framework-->
+ <script type="text/javascript" src="build/js/lib/qunit.js" charset="utf-8"></script>
+ <link rel="stylesheet" href="build/js/lib/qunit.css" type="text/css" media="screen" />
+
+ <!-- the Test Suite-->
+ <script type="text/javascript" src="test-double-rendering.js" charset="utf-8"></script>
+ </head>
+<body>
+ <h1 id="qunit-header">Rendering Double Constants in JS: Unit Test</h1>
+ <h2 id="qunit-banner"></h2>
+ <div id="qunit-testrunner-toolbar"></div>
+ <h2 id="qunit-userAgent"></h2>
+ <ol id="qunit-tests"><li><!-- get valid xhtml strict--></li></ol>
+ <!-- Uncomment this to check the validity. This significantly slows down the test.
+ <p>
+ <a href="http://validator.w3.org/check/referer"><img
+ src="http://www.w3.org/Icons/valid-xhtml10"
+ alt="Valid XHTML 1.0!" height="31" width="88" /></a>
+ </p>
+ -->
+</body>
+</html>
diff --git a/src/jaegertracing/thrift/lib/js/test/test-double-rendering.js b/src/jaegertracing/thrift/lib/js/test/test-double-rendering.js
new file mode 100644
index 000000000..1790c1b80
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/test-double-rendering.js
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ /* jshint -W100 */
+
+/*
+ * JavaScript test suite for double constants inside
+ * DoubleConstantsTest.thrift. These tests will run against Normal (-gen js)
+ * Apache Thrift interfaces.
+ *
+ * To compile client code for this test use:
+ * $ thrift -gen js DoubleConstantsTest.thrift
+ */
+
+// double assertion threshold
+const EPSILON = 0.0000001;
+
+// Work around for old API used by QUnitAdapter of jsTestDriver
+if (typeof QUnit.log == 'function') {
+ // When using real QUnit (fron PhantomJS) log failures to console
+ QUnit.log(function(details) {
+ if (!details.result) {
+ console.log('======== FAIL ========');
+ console.log('TestName: ' + details.name);
+ if (details.message) console.log(details.message);
+ console.log('Expected: ' + details.expected);
+ console.log('Actual : ' + details.actual);
+ console.log('======================');
+ }
+ });
+}
+
+QUnit.module('Double rendering');
+
+ QUnit.test('Double (rendering)', function(assert) {
+ console.log('Double rendering test -- starts');
+ const EXPECTED_DOUBLE_ASSIGNED_TO_INT_CONSTANT = 1;
+ const EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT = -100;
+ const EXPECTED_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT = 9223372036854775807;
+ const EXPECTED_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT = -9223372036854775807;
+ const EXPECTED_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS = 3.14159265359;
+ const EXPECTED_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE = 1000000.1;
+ const EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE = -1000000.1;
+ const EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE = 1.7e+308;
+ const EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE = 9223372036854775816.43;
+ const EXPECTED_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE = -1.7e+308;
+ const EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE = -9223372036854775816.43;
+ assert.ok(
+ Math.abs(EXPECTED_DOUBLE_ASSIGNED_TO_INT_CONSTANT - DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST) <= EPSILON);
+ assert.ok(
+ Math.abs(
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT -
+ DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST) <= EPSILON);
+ assert.ok(
+ Math.abs(
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT -
+ DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST) <= EPSILON);
+ assert.ok(
+ Math.abs(
+ EXPECTED_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT -
+ DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST) <= EPSILON);
+ assert.ok(
+ Math.abs(
+ EXPECTED_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS -
+ DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST) <= EPSILON);
+ assert.ok(
+ Math.abs(
+ EXPECTED_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE -
+ DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST) <= EPSILON);
+ assert.ok(
+ Math.abs(
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE -
+ DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST) <= EPSILON);
+ assert.ok(
+ Math.abs(
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE -
+ DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST) <= EPSILON);
+ assert.ok(
+ Math.abs(
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE -
+ DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST) <= EPSILON);
+ assert.ok(
+ Math.abs(
+ EXPECTED_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE -
+ DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST) <= EPSILON);
+ assert.ok(
+ Math.abs(
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE -
+ DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST) <= EPSILON);
+ assert.equal(typeof DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST, 'number');
+ assert.equal(typeof DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST, 'number');
+ assert.equal(typeof DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST, 'number');
+ assert.equal(typeof DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST, 'number');
+ assert.equal(typeof DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST, 'number');
+ assert.equal(typeof DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST, 'number');
+ assert.equal(typeof DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST, 'number');
+ assert.equal(typeof DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST, 'number');
+ assert.equal(typeof DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST, 'number');
+ assert.equal(typeof DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST, 'number');
+ assert.equal(typeof DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST, 'number');
+ const EXPECTED_DOUBLE_LIST =
+ [1,-100,100,9223372036854775807,-9223372036854775807,3.14159265359,1000000.1,-1000000.1,1.7e+308,-1.7e+308,
+ 9223372036854775816.43,-9223372036854775816.43];
+ assert.equal(DOUBLE_LIST_TEST.length, EXPECTED_DOUBLE_LIST.length);
+ for (let i = 0; i < EXPECTED_DOUBLE_LIST.length; ++i) {
+ assert.ok(Math.abs(EXPECTED_DOUBLE_LIST[i] - DOUBLE_LIST_TEST[i]) <= EPSILON);
+ }
+ console.log('Double rendering test -- ends');
+ });
+
diff --git a/src/jaegertracing/thrift/lib/js/test/test-es6.html b/src/jaegertracing/thrift/lib/js/test/test-es6.html
new file mode 100644
index 000000000..f0b8dd9b5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/test-es6.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>Thrift Javascript Bindings: Unit Test</title>
+
+ <script src="build/js/lib/Int64.js" type="text/javascript" charset="utf-8"></script>
+ <script src="build/js/lib/Int64Util.js" type="text/javascript" charset="utf-8"></script>
+ <script src="build/js/lib/JSONInt64.js" type="text/javascript" charset="utf-8"></script>
+ <script src="build/js/thrift.js" type="text/javascript" charset="utf-8"></script>
+ <script src="gen-js-es6/ThriftTest_types.js" type="text/javascript" charset="utf-8"></script>
+ <script src="gen-js-es6/ThriftTest.js" type="text/javascript" charset="utf-8"></script>
+
+ <!-- jQuery -->
+ <script type="text/javascript" src="build/js/lib/jquery.js" charset="utf-8"></script>
+
+ <!-- QUnit Test framework-->
+ <script type="text/javascript" src="build/js/lib/qunit.js" charset="utf-8"></script>
+ <link rel="stylesheet" href="build/js/lib/qunit.css" type="text/css" media="screen" />
+
+ <!-- the Test Suite-->
+ <script>
+ const loc = window.location;
+ const ws_uri = ((loc.protocol === "https:") ? "wss://" : "ws://") +
+ loc.hostname + ":" + loc.port + loc.pathname;
+ const transport = new Thrift.TWebSocketTransport(ws_uri);
+ const protocol = new Thrift.Protocol(transport);
+ const client = new ThriftTest.ThriftTestClient(protocol);
+ transport.open();
+ </script>
+ <script type="text/javascript" src="test-es6.js" charset="utf-8"></script>
+</head>
+<body>
+ <h1 id="qunit-header">Thrift Javascript Bindings: Unit Test (<a href="https://github.com/apache/thrift/blob/master/test/ThriftTest.thrift">ThriftTest.thrift</a>)</h1>
+ <h2 id="qunit-banner"></h2>
+ <div id="qunit-testrunner-toolbar"></div>
+ <h2 id="qunit-userAgent"></h2>
+ <ol id="qunit-tests"><li><!-- get valid xhtml strict--></li></ol>
+ <!-- Uncomment this to check the validity. This significantly slows down the test.
+ <p>
+ <a href="http://validator.w3.org/check/referer"><img
+ src="http://www.w3.org/Icons/valid-xhtml10"
+ alt="Valid XHTML 1.0!" height="31" width="88" /></a>
+ </p>
+ -->
+</body>
+</html>
diff --git a/src/jaegertracing/thrift/lib/js/test/test-es6.js b/src/jaegertracing/thrift/lib/js/test/test-es6.js
new file mode 100644
index 000000000..845171b86
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/test-es6.js
@@ -0,0 +1,374 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ /* jshint -W100 */
+
+/*
+ * Fully Async JavaScript test suite for ThriftTest.thrift.
+ * These tests are designed to exercise the WebSocket transport
+ * (which is exclusively async).
+ *
+ * To compile client code for this test use:
+ * $ thrift -gen js:es6 ThriftTest.thrift
+ */
+
+
+
+// all Languages in UTF-8
+
+const stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, Asturianu, Aymar aru, AzÉ™rbaycan, Башҡорт, Boarisch, ŽemaitÄ—Å¡ka, БеларуÑкаÑ, БеларуÑÐºÐ°Ñ (тарашкевіца), БългарÑки, Bamanankan, বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Ðохчийн, Cebuano, á£áŽ³áŽ©, ÄŒesky, СловѣÌньÑкъ / ⰔⰎⰑⰂⰡâ°â° â°”â°â°Ÿ, Чӑвашла, Cymraeg, Dansk, Zazaki, Þ‹Þ¨ÞˆÞ¬Þ€Þ¨Þ„Þ¦ÞÞ°, Ελληνικά, Emiliàn e rumagnòl, English, Esperanto, Español, Eesti, Euskara, Ùارسی, Suomi, Võro, Føroyskt, Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, Avañe'ẽ, ગà«àªœàª°àª¾àª¤à«€, Gaelg, עברית, हिनà¥à¤¦à¥€, Fiji Hindi, Hrvatski, Kreyòl ayisyen, Magyar, Õ€Õ¡ÕµÕ¥Ö€Õ¥Õ¶, Interlingua, Bahasa Indonesia, Ilokano, Ido, Ãslenska, Italiano, 日本語, Lojban, Basa Jawa, ქáƒáƒ áƒ—ული, Kongo, Kalaallisut, ಕನà³à²¨à²¡, 한국어, Къарачай-Малкъар, Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, LatvieÅ¡u, Basa Banyumasan, Malagasy, МакедонÑки, മലയാളം, मराठी, Bahasa Melayu, مازÙرونی, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪Norsk (nynorsk)‬, ‪Norsk (bokmÃ¥l)‬, Nouormand, Diné bizaad, Occitan, Иронау, Papiamentu, Deitsch, Norfuk / Pitkern, Polski, پنجابی, پښتو, Português, Runa Simi, Rumantsch, Romani, Română, РуÑÑкий, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple English, SlovenÄina, SlovenÅ¡Äina, СрпÑки / Srpski, Seeltersk, Svenska, Kiswahili, தமிழà¯, తెలà±à°—à±, Тоҷикӣ, ไทย, Türkmençe, Tagalog, Türkçe, Татарча/Tatarça, УкраїнÑька, اردو, Tiếng Việt, Volapük, Walon, Winaray, å´è¯­, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, Bân-lâm-gú, 粵語";
+
+function checkRecursively(assert, map1, map2) {
+ if (typeof map1 !== 'function' && typeof map2 !== 'function') {
+ if (!map1 || typeof map1 !== 'object') {
+ assert.equal(map1, map2);
+ } else {
+ for (var key in map1) {
+ checkRecursively(assert, map1[key], map2[key]);
+ }
+ }
+ }
+}
+
+QUnit.module('Base Types');
+
+ QUnit.test('Void', function( assert ) {
+ assert.expect(1);
+ const done = assert.async();
+ client.testVoid().then(function(result) {
+ assert.equal(result, undefined);
+ done();
+ });
+ });
+
+ QUnit.test('String', function( assert ) {
+ assert.expect(3);
+ const done = assert.async(3);
+ client.testString('').then(function(result) {
+ assert.equal(result, '');
+ done();
+ });
+ client.testString(stringTest).then(function(result) {
+ assert.equal(result, stringTest);
+ done();
+ });
+ var specialCharacters = 'quote: \" backslash:' +
+ ' forwardslash-escaped: \/ ' +
+ ' backspace: \b formfeed: \f newline: \n return: \r tab: ' +
+ ' now-all-of-them-together: "\\\/\b\n\r\t' +
+ ' now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><';
+ client.testString(specialCharacters).then(function(result) {
+ assert.equal(result, specialCharacters);
+ done();
+ });
+ });
+
+ QUnit.test('Double', function( assert ) {
+ assert.expect(4);
+ const done = assert.async(4);
+ client.testDouble(0).then(function(result) {
+ assert.equal(result, 0);
+ done();
+ });
+ client.testDouble(-1).then(function(result) {
+ assert.equal(result, -1);
+ done();
+ });
+ client.testDouble(3.14).then(function(result) {
+ assert.equal(result, 3.14);
+ done();
+ });
+ client.testDouble(Math.pow(2, 60)).then(function(result) {
+ assert.equal(result, Math.pow(2, 60));
+ done();
+ });
+ });
+ // TODO: add testBinary()
+ QUnit.test('Byte', function( assert ) {
+ assert.expect(2);
+ const done = assert.async(2);
+ client.testByte(0).then(function(result) {
+ assert.equal(result, 0);
+ done();
+ });
+ client.testByte(0x01).then(function(result) {
+ assert.equal(result, 0x01);
+ done();
+ });
+ });
+ QUnit.test('I32', function( assert ) {
+ assert.expect(3);
+ const done = assert.async(3);
+ client.testI32(0).then(function(result) {
+ assert.equal(result, 0);
+ done();
+ });
+ client.testI32(Math.pow(2, 30)).then(function(result) {
+ assert.equal(result, Math.pow(2, 30));
+ done();
+ });
+ client.testI32(-Math.pow(2, 30)).then(function(result) {
+ assert.equal(result, -Math.pow(2, 30));
+ done();
+ });
+ });
+ QUnit.test('I64', function( assert ) {
+ assert.expect(3);
+ const done = assert.async(3);
+ client.testI64(0).then(function(result) {
+ assert.equal(result, 0);
+ done();
+ });
+ //This is usually 2^60 but JS cannot represent anything over 2^52 accurately
+ client.testI64(Math.pow(2, 52)).then(function(result) {
+ assert.equal(result, Math.pow(2, 52));
+ done();
+ });
+ client.testI64(-Math.pow(2, 52)).then(function(result) {
+ assert.equal(result, -Math.pow(2, 52));
+ done();
+ });
+ });
+
+
+QUnit.module('Structured Types');
+
+ QUnit.test('Struct', function( assert ) {
+ assert.expect(5);
+ const done = assert.async();
+ var structTestInput = new ThriftTest.Xtruct();
+ structTestInput.string_thing = 'worked';
+ structTestInput.byte_thing = 0x01;
+ structTestInput.i32_thing = Math.pow(2, 30);
+ //This is usually 2^60 but JS cannot represent anything over 2^52 accurately
+ structTestInput.i64_thing = Math.pow(2, 52);
+
+ client.testStruct(structTestInput).then(function(result) {
+ assert.equal(result.string_thing, structTestInput.string_thing);
+ assert.equal(result.byte_thing, structTestInput.byte_thing);
+ assert.equal(result.i32_thing, structTestInput.i32_thing);
+ assert.equal(result.i64_thing, structTestInput.i64_thing);
+ assert.equal(JSON.stringify(result), JSON.stringify(structTestInput));
+ done();
+ });
+ });
+
+ QUnit.test('Nest', function( assert ) {
+ assert.expect(7);
+ const done = assert.async();
+ var xtrTestInput = new ThriftTest.Xtruct();
+ xtrTestInput.string_thing = 'worked';
+ xtrTestInput.byte_thing = 0x01;
+ xtrTestInput.i32_thing = Math.pow(2, 30);
+ //This is usually 2^60 but JS cannot represent anything over 2^52 accurately
+ xtrTestInput.i64_thing = Math.pow(2, 52);
+
+ var nestTestInput = new ThriftTest.Xtruct2();
+ nestTestInput.byte_thing = 0x02;
+ nestTestInput.struct_thing = xtrTestInput;
+ nestTestInput.i32_thing = Math.pow(2, 15);
+
+ client.testNest(nestTestInput).then(function(result) {
+ assert.equal(result.byte_thing, nestTestInput.byte_thing);
+ assert.equal(result.struct_thing.string_thing, nestTestInput.struct_thing.string_thing);
+ assert.equal(result.struct_thing.byte_thing, nestTestInput.struct_thing.byte_thing);
+ assert.equal(result.struct_thing.i32_thing, nestTestInput.struct_thing.i32_thing);
+ assert.equal(result.struct_thing.i64_thing, nestTestInput.struct_thing.i64_thing);
+ assert.equal(result.i32_thing, nestTestInput.i32_thing);
+ assert.equal(JSON.stringify(result), JSON.stringify(nestTestInput));
+ done();
+ });
+ });
+
+ QUnit.test('Map', function( assert ) {
+ assert.expect(3);
+ const done = assert.async();
+ var mapTestInput = {7: 77, 8: 88, 9: 99};
+
+ client.testMap(mapTestInput).then(function(result) {
+ for (var key in result) {
+ assert.equal(result[key], mapTestInput[key]);
+ }
+ done();
+ });
+ });
+
+ QUnit.test('StringMap', function( assert ) {
+ assert.expect(6);
+ const done = assert.async();
+ var mapTestInput = {
+ 'a': '123', 'a b': 'with spaces ', 'same': 'same', '0': 'numeric key',
+ 'longValue': stringTest, stringTest: 'long key'
+ };
+
+ client.testStringMap(mapTestInput).then(function(result) {
+ for (var key in result) {
+ assert.equal(result[key], mapTestInput[key]);
+ }
+ done();
+ });
+ });
+
+ QUnit.test('Set', function( assert ) {
+ assert.expect(1);
+ const done = assert.async();
+ var setTestInput = [1, 2, 3];
+ client.testSet(setTestInput).then(function(result) {
+ assert.ok(result, setTestInput);
+ done();
+ });
+ });
+
+ QUnit.test('List', function( assert ) {
+ assert.expect(1);
+ const done = assert.async();
+ var listTestInput = [1, 2, 3];
+ client.testList(listTestInput).then(function(result) {
+ assert.ok(result, listTestInput);
+ done();
+ });
+ });
+
+ QUnit.test('Enum', function( assert ) {
+ assert.expect(1);
+ const done = assert.async();
+ client.testEnum(ThriftTest.Numberz.ONE).then(function(result) {
+ assert.equal(result, ThriftTest.Numberz.ONE);
+ done();
+ });
+ });
+
+ QUnit.test('TypeDef', function( assert ) {
+ assert.expect(1);
+ const done = assert.async();
+ client.testTypedef(69).then(function(result) {
+ assert.equal(result, 69);
+ done();
+ });
+ });
+
+
+QUnit.module('deeper!');
+
+ QUnit.test('MapMap', function( assert ) {
+ assert.expect(16);
+ const done = assert.async();
+ var mapMapTestExpectedResult = {
+ '4': {'1': 1, '2': 2, '3': 3, '4': 4},
+ '-4': {'-4': -4, '-3': -3, '-2': -2, '-1': -1}
+ };
+
+ client.testMapMap(1).then(function(result) {
+ for (var key in result) {
+ for (var key2 in result[key]) {
+ assert.equal(result[key][key2], mapMapTestExpectedResult[key][key2]);
+ }
+ }
+ checkRecursively(assert, result, mapMapTestExpectedResult);
+ done();
+ });
+ });
+
+
+QUnit.module('Exception');
+
+ QUnit.test('Xception', function( assert ) {
+ assert.expect(2);
+ const done = assert.async();
+ client.testException('Xception').then(function(res) {
+ assert.ok(false);
+ }).catch(function(e) {
+
+ console.log(`Exception exception e`);
+ console.log(e);
+ console.log(JSON.stringify(e, null, 2));
+
+ assert.equal(e.errorCode, 1001);
+ assert.equal(e.message, 'Xception');
+ done();
+ });
+ });
+
+ QUnit.test('no Exception', function( assert ) {
+ assert.expect(1);
+ const done = assert.async();
+ client.testException('no Exception').then(function(e) {
+ assert.ok(!e);
+ done();
+ });
+ });
+
+QUnit.module('Insanity');
+
+ QUnit.test('testInsanity', function( assert ) {
+ assert.expect(24);
+ const done = assert.async();
+ var insanity = {
+ '1': {
+ '2': {
+ 'userMap': { '5': 5, '8': 8 },
+ 'xtructs': [{
+ 'string_thing': 'Goodbye4',
+ 'byte_thing': 4,
+ 'i32_thing': 4,
+ 'i64_thing': 4
+ },
+ {
+ 'string_thing': 'Hello2',
+ 'byte_thing': 2,
+ 'i32_thing': 2,
+ 'i64_thing': 2
+ }
+ ]
+ },
+ '3': {
+ 'userMap': { '5': 5, '8': 8 },
+ 'xtructs': [{
+ 'string_thing': 'Goodbye4',
+ 'byte_thing': 4,
+ 'i32_thing': 4,
+ 'i64_thing': 4
+ },
+ {
+ 'string_thing': 'Hello2',
+ 'byte_thing': 2,
+ 'i32_thing': 2,
+ 'i64_thing': 2
+ }
+ ]
+ }
+ },
+ '2': { '6': { 'userMap': null, 'xtructs': null } }
+ };
+ client.testInsanity(new ThriftTest.Insanity()).then(function(res) {
+ assert.ok(res, JSON.stringify(res));
+ assert.ok(insanity, JSON.stringify(insanity));
+ checkRecursively(assert, res, insanity);
+ done();
+ });
+ });
+
+QUnit.module('Oneway');
+ QUnit.test('testOneway', function( assert ) {
+ assert.expect(1);
+ const done = assert.async();
+ client.testOneway(1).then(function(result) {
+ assert.equal(result, undefined);
+ done();
+ });
+ });
diff --git a/src/jaegertracing/thrift/lib/js/test/test-int64.html b/src/jaegertracing/thrift/lib/js/test/test-int64.html
new file mode 100644
index 000000000..bcb05fb37
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/test-int64.html
@@ -0,0 +1,56 @@
++<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>Int64 Constants in JS: Unit Test</title>
+
+ <script src="build/js/thrift.js" type="text/javascript" charset="utf-8"></script>
+ <!-- browserified node-int64 -->
+ <script src="build/js/lib/Int64.js" type="text/javascript" charset="utf-8"></script>
+ <script src="build/js/lib/JSONInt64.js" type="text/javascript" charset="utf-8"></script>
+ <!-- int64 constants to check -->
+ <script src="gen-js/Int64Test_types.js" type="text/javascript" charset="utf-8"></script>
+
+ <!-- jQuery -->
+ <script type="text/javascript" src="build/js/lib/jquery.js" charset="utf-8"></script>
+
+ <!-- QUnit Test framework-->
+ <script type="text/javascript" src="build/js/lib/qunit.js" charset="utf-8"></script>
+ <link rel="stylesheet" href="build/js/lib/qunit.css" type="text/css" media="screen" />
+
+ <!-- the Test Suite-->
+ <script type="text/javascript" src="test-int64.js" charset="utf-8"></script>
+ </head>
+<body>
+ <h1 id="qunit-header">Int64 Constants in JS: Unit Test</h1>
+ <h2 id="qunit-banner"></h2>
+ <div id="qunit-testrunner-toolbar"></div>
+ <h2 id="qunit-userAgent"></h2>
+ <ol id="qunit-tests"><li><!-- get valid xhtml strict--></li></ol>
+ <!-- Uncomment this to check the validity. This significantly slows down the test.
+ <p>
+ <a href="http://validator.w3.org/check/referer"><img
+ src="http://www.w3.org/Icons/valid-xhtml10"
+ alt="Valid XHTML 1.0!" height="31" width="88" /></a>
+ </p>
+ -->
+</body>
+</html>
diff --git a/src/jaegertracing/thrift/lib/js/test/test-int64.js b/src/jaegertracing/thrift/lib/js/test/test-int64.js
new file mode 100644
index 000000000..cf7e8282b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/test-int64.js
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ /* jshint -W100 */
+
+// Work around for old API used by QUnitAdapter of jsTestDriver
+if (typeof QUnit.log == 'function') {
+ // When using real QUnit (fron PhantomJS) log failures to console
+ QUnit.log(function(details) {
+ if (!details.result) {
+ console.log('======== FAIL ========');
+ console.log('TestName: ' + details.name);
+ if (details.message) console.log(details.message);
+ console.log('Expected: ' + details.expected);
+ console.log('Actual : ' + details.actual);
+ console.log('======================');
+ }
+ });
+}
+
+QUnit.module('Int64');
+
+ QUnit.test('Int64', function(assert) {
+ console.log('Int64 test -- starts');
+ const EXPECTED_SMALL_INT64_AS_NUMBER = 42;
+ const EXPECTED_SMALL_INT64 = new Int64(42);
+ const EXPECTED_MAX_JS_SAFE_INT64 = new Int64(Number.MAX_SAFE_INTEGER);
+ const EXPECTED_MIN_JS_SAFE_INT64 = new Int64(Number.MIN_SAFE_INTEGER);
+ const EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64 = new Int64("0020000000000000"); // hex-encoded
+ const EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64 = new Int64("ffe0000000000000"); // hex-encoded 2's complement
+ const EXPECTED_MAX_SIGNED_INT64 = new Int64("7fffffffffffffff"); // hex-encoded
+ const EXPECTED_MIN_SIGNED_INT64 = new Int64("8000000000000000"); // hex-encoded 2's complement
+ const EXPECTED_INT64_LIST = [
+ EXPECTED_SMALL_INT64,
+ EXPECTED_MAX_JS_SAFE_INT64,
+ EXPECTED_MIN_JS_SAFE_INT64,
+ EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64,
+ EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64,
+ EXPECTED_MAX_SIGNED_INT64,
+ EXPECTED_MIN_SIGNED_INT64
+ ];
+
+ assert.ok(EXPECTED_SMALL_INT64.equals(Int64Test.SMALL_INT64));
+ assert.ok(EXPECTED_MAX_JS_SAFE_INT64.equals(Int64Test.MAX_JS_SAFE_INT64));
+ assert.ok(EXPECTED_MIN_JS_SAFE_INT64.equals(Int64Test.MIN_JS_SAFE_INT64));
+ assert.ok(
+ EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64.equals(
+ Int64Test.MAX_JS_SAFE_PLUS_ONE_INT64
+ )
+ );
+ assert.ok(
+ EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64.equals(
+ Int64Test.MIN_JS_SAFE_MINUS_ONE_INT64
+ )
+ );
+ assert.ok(EXPECTED_MAX_SIGNED_INT64.equals(Int64Test.MAX_SIGNED_INT64));
+ assert.ok(EXPECTED_MIN_SIGNED_INT64.equals(Int64Test.MIN_SIGNED_INT64));
+ assert.equal(
+ EXPECTED_SMALL_INT64_AS_NUMBER,
+ Int64Test.SMALL_INT64.toNumber()
+ );
+ assert.equal(
+ Number.MAX_SAFE_INTEGER,
+ Int64Test.MAX_JS_SAFE_INT64.toNumber()
+ );
+ assert.equal(
+ Number.MIN_SAFE_INTEGER,
+ Int64Test.MIN_JS_SAFE_INT64.toNumber()
+ );
+
+ for (let i = 0; i < EXPECTED_INT64_LIST.length; ++i) {
+ assert.ok(EXPECTED_INT64_LIST[i].equals(Int64Test.INT64_LIST[i]));
+ }
+
+ for (let i = 0; i < EXPECTED_INT64_LIST.length; ++i){
+ let int64Object = EXPECTED_INT64_LIST[i];
+ assert.ok(Int64Test.INT64_2_INT64_MAP[JSONInt64.toDecimalString(int64Object)].equals(int64Object));
+ }
+
+ console.log('Int64 test -- ends');
+ });
+
diff --git a/src/jaegertracing/thrift/lib/js/test/test-jq.js b/src/jaegertracing/thrift/lib/js/test/test-jq.js
new file mode 100644
index 000000000..f62bb957b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/test-jq.js
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ /* jshint -W100 */
+
+/*
+ * JavaScript test suite for ThriftTest.thrift. These tests
+ * will run only with jQuery (-gen js:jquery) Apache Thrift
+ * interfaces. To create client code:
+ * $ thrift -gen js:jquery ThriftTest.thrift
+ *
+ * See also:
+ * ++ test.js for generic tests
+ * ++ test-nojq.js for "-gen js" only tests
+ */
+
+
+//////////////////////////////////
+//jQuery asynchronous tests
+jQuery.ajaxSetup({ timeout: 0 });
+
+QUnit.module('jQ Async Manual');
+
+ QUnit.test('testI32', function(assert) {
+ assert.expect(2);
+ const done = assert.async(2);
+
+ const transport = new Thrift.Transport();
+ const protocol = new Thrift.Protocol(transport);
+ const client = new ThriftTest.ThriftTestClient(protocol);
+
+ const jqxhr = jQuery.ajax({
+ url: '/service',
+ data: client.send_testI32(Math.pow(-2, 31)),
+ type: 'POST',
+ cache: false,
+ dataType: 'text',
+ success: function(res) {
+ transport.setRecvBuffer(res);
+ assert.equal(client.recv_testI32(), Math.pow(-2, 31));
+ done();
+ },
+ error: function() { assert.ok(false); },
+ complete: function() {
+ assert.ok(true);
+ done();
+ }
+ });
+ });
+
+ QUnit.test('testI64', function(assert) {
+ assert.expect(2);
+ const done = assert.async(2);
+
+ const transport = new Thrift.Transport();
+ const protocol = new Thrift.Protocol(transport);
+ const client = new ThriftTest.ThriftTestClient(protocol);
+
+ jQuery.ajax({
+ url: '/service',
+ //This is usually 2^61 but JS cannot represent anything over 2^52 accurately
+ data: client.send_testI64(Math.pow(-2, 52)),
+ type: 'POST',
+ cache: false,
+ dataType: 'text',
+ success: function(res) {
+ transport.setRecvBuffer(res);
+ //This is usually 2^61 but JS cannot represent anything over 2^52 accurately
+ assert.equal(client.recv_testI64(), Math.pow(-2, 52));
+ done();
+ },
+ error: function() { assert.ok(false); },
+ complete: function() {
+ assert.ok(true);
+ done();
+ }
+ });
+ });
+
+
+QUnit.module('jQ Async');
+ QUnit.test('I32', function(assert) {
+ assert.expect(3);
+
+ const done = assert.async(3);
+ client.testI32(Math.pow(2, 30), function(result) {
+ assert.equal(result, Math.pow(2, 30));
+ done();
+ });
+
+ const jqxhr = client.testI32(Math.pow(-2, 31), function(result) {
+ assert.equal(result, Math.pow(-2, 31));
+ done();
+ });
+
+ jqxhr.success(function(result) {
+ assert.equal(result, Math.pow(-2, 31));
+ done();
+ });
+ });
+
+ QUnit.test('I64', function(assert) {
+ assert.expect(4);
+
+ const done = assert.async(4);
+ //This is usually 2^60 but JS cannot represent anything over 2^52 accurately
+ client.testI64(Math.pow(2, 52), function(result) {
+ assert.equal(result, Math.pow(2, 52));
+ done();
+ });
+
+ //This is usually 2^60 but JS cannot represent anything over 2^52 accurately
+ client.testI64(Math.pow(-2, 52), function(result) {
+ assert.equal(result, Math.pow(-2, 52));
+ done();
+ })
+ .error(function(xhr, status, e) { assert.ok(false, e.message); })
+ .success(function(result) {
+ //This is usually 2^60 but JS cannot represent anything over 2^52 accurately
+ assert.equal(result, Math.pow(-2, 52));
+ done();
+ })
+ .complete(function() {
+ assert.ok(true);
+ done();
+ });
+ });
+
+ QUnit.test('Xception', function(assert) {
+ assert.expect(2);
+
+ const done = assert.async(2);
+
+ const dfd = client.testException('Xception', function(result) {
+ assert.ok(false);
+ done();
+ })
+ .error(function(xhr, status, e) {
+ assert.equal(e.errorCode, 1001);
+ assert.equal(e.message, 'Xception');
+ done();
+ $(document).ajaxError( function() { done(); } );
+ });
+ });
diff --git a/src/jaegertracing/thrift/lib/js/test/test-nojq.html b/src/jaegertracing/thrift/lib/js/test/test-nojq.html
new file mode 100644
index 000000000..37b3eb8d0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/test-nojq.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>Thrift Javascript Bindings: Unit Test</title>
+
+ <script src="build/js/lib/Int64.js" type="text/javascript" charset="utf-8"></script>
+ <script src="build/js/lib/Int64Util.js" type="text/javascript" charset="utf-8"></script>
+ <script src="build/js/lib/JSONInt64.js" type="text/javascript" charset="utf-8"></script>
+ <script src="build/js/thrift.js" type="text/javascript" charset="utf-8"></script>
+ <script src="gen-js/ThriftTest_types.js" type="text/javascript" charset="utf-8"></script>
+ <script src="gen-js/ThriftTest.js" type="text/javascript" charset="utf-8"></script>
+
+ <!-- QUnit Test framework-->
+ <script type="text/javascript" src="build/js/lib/qunit.js" charset="utf-8"></script>
+ <link rel="stylesheet" href="build/js/lib/qunit.css" type="text/css" media="screen" />
+
+ <!-- the Test Suite-->
+ <script type="text/javascript" src="test.js" charset="utf-8"></script>
+ <script type="text/javascript" src="test-nojq.js" charset="utf-8"></script>
+</head>
+<body>
+ <h1 id="qunit-header">Thrift Javascript Bindings: Unit Test (<a href="https://github.com/apache/thrift/blob/master/test/ThriftTest.thrift">ThriftTest.thrift</a>)</h1>
+ <h2 id="qunit-banner"></h2>
+ <div id="qunit-testrunner-toolbar"></div>
+ <h2 id="qunit-userAgent"></h2>
+ <ol id="qunit-tests"><li><!-- get valid xhtml strict--></li></ol>
+ <!-- Uncomment this to check the validity. This significantly slows down the test.
+ <p>
+ <a href="http://validator.w3.org/check/referer"><img
+ src="http://www.w3.org/Icons/valid-xhtml10"
+ alt="Valid XHTML 1.0!" height="31" width="88" /></a>
+ </p>
+ -->
+</body>
+</html>
+
diff --git a/src/jaegertracing/thrift/lib/js/test/test-nojq.js b/src/jaegertracing/thrift/lib/js/test/test-nojq.js
new file mode 100644
index 000000000..2b801d2a6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/test-nojq.js
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ /* jshint -W100 */
+
+/*
+ * JavaScript test suite for ThriftTest.thrift. These tests
+ * will run only with normal "-gen js" Apache Thrift interfaces.
+ * To create client code:
+ * $ thrift -gen js ThriftTest.thrift
+ *
+ * See also:
+ * ++ test.js for generic tests
+ * ++ test-jq.js for "-gen js:jquery" only tests
+ */
+
+
+//////////////////////////////////
+//Async exception tests
+
+QUnit.module('NojQ Async');
+
+QUnit.test('Xception', function(assert) {
+ assert.expect(2);
+ const done = assert.async();
+
+ client.testException('Xception', function(result) {
+ assert.equal(result.errorCode, 1001);
+ assert.equal(result.message, 'Xception');
+ done();
+ });
+ });
+
diff --git a/src/jaegertracing/thrift/lib/js/test/test.html b/src/jaegertracing/thrift/lib/js/test/test.html
new file mode 100755
index 000000000..85c839567
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/test.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>Thrift Javascript Bindings: Unit Test</title>
+
+ <script src="build/js/lib/Int64.js" type="text/javascript" charset="utf-8"></script>
+ <script src="build/js/lib/Int64Util.js" type="text/javascript" charset="utf-8"></script>
+ <script src="build/js/lib/JSONInt64.js" type="text/javascript" charset="utf-8"></script>
+ <script src="build/js/thrift.js" type="text/javascript" charset="utf-8"></script>
+ <script src="gen-js-jquery/ThriftTest_types.js" type="text/javascript" charset="utf-8"></script>
+ <script src="gen-js-jquery/ThriftTest.js" type="text/javascript" charset="utf-8"></script>
+
+ <!-- jQuery -->
+ <script type="text/javascript" src="build/js/lib/jquery.js" charset="utf-8"></script>
+
+ <!-- QUnit Test framework-->
+ <script type="text/javascript" src="build/js/lib/qunit.js" charset="utf-8"></script>
+ <link rel="stylesheet" href="build/js/lib/qunit.css" type="text/css" media="screen" />
+
+ <!-- the Test Suite-->
+ <script type="text/javascript" src="test.js" charset="utf-8"></script>
+ <script type="text/javascript" src="test-jq.js" charset="utf-8"></script>
+</head>
+<body>
+ <h1 id="qunit-header">Thrift Javascript Bindings: Unit Test (<a href="https://github.com/apache/thrift/blob/master/test/ThriftTest.thrift">ThriftTest.thrift</a>)</h1>
+ <h2 id="qunit-banner"></h2>
+ <div id="qunit-testrunner-toolbar"></div>
+ <h2 id="qunit-userAgent"></h2>
+ <ol id="qunit-tests"><li><!-- get valid xhtml strict--></li></ol>
+ <!-- Uncomment this to check the validity. This significantly slows down the test.
+ <p>
+ <a href="http://validator.w3.org/check/referer"><img
+ src="http://www.w3.org/Icons/valid-xhtml10"
+ alt="Valid XHTML 1.0!" height="31" width="88" /></a>
+ </p>
+ -->
+</body>
+</html>
diff --git a/src/jaegertracing/thrift/lib/js/test/test.js b/src/jaegertracing/thrift/lib/js/test/test.js
new file mode 100755
index 000000000..35ed6ffd2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/test.js
@@ -0,0 +1,417 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ /* jshint -W100 */
+
+/*
+ * JavaScript test suite for ThriftTest.thrift. These tests
+ * will run against Normal (-gen js) and jQuery (-gen js:jquery)
+ * Apache Thrift interfaces.
+ *
+ * Synchronous blocking calls should be identical in both
+ * Normal and jQuery interfaces. All synchronous tests belong
+ * here.
+ *
+ * Asynchronous success callbacks passed as the last parameter
+ * of an RPC call should be identical in both Normal and jQuery
+ * interfaces. Async success tests belong here.
+ *
+ * Asynchronous exception processing is different in Normal
+ * and jQuery interfaces. Such tests belong in the test-nojq.js
+ * or test-jq.js files respectively. jQuery specific XHR object
+ * tests also belong in test-jq.js. Do not create any jQuery
+ * dependencies in this file or in test-nojq.js
+ *
+ * To compile client code for this test use:
+ * $ thrift -gen js ThriftTest.thrift
+ * -- or --
+ * $ thrift -gen js:jquery ThriftTest.thrift
+ *
+ * See also:
+ * ++ test-nojq.js for "-gen js" only tests
+ * ++ test-jq.js for "-gen js:jquery" only tests
+ */
+
+const transport = new Thrift.Transport('/service');
+const protocol = new Thrift.Protocol(transport);
+const client = new ThriftTest.ThriftTestClient(protocol);
+
+const int64_2_pow_60 = new Int64('1000000000000000');
+const int64_minus_2_pow_60 = new Int64('f000000000000000');
+
+// Work around for old API used by QUnitAdapter of jsTestDriver
+if (typeof QUnit.log == 'function') {
+ // When using real QUnit (fron PhantomJS) log failures to console
+ QUnit.log(function(details) {
+ if (!details.result) {
+ console.log('======== FAIL ========');
+ console.log('TestName: ' + details.name);
+ if (details.message) console.log(details.message);
+ console.log('Expected: ' + details.expected);
+ console.log('Actual : ' + details.actual);
+ console.log('======================');
+ }
+ });
+}
+
+// all Languages in UTF-8
+const stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, Asturianu, Aymar aru, AzÉ™rbaycan, Башҡорт, Boarisch, ŽemaitÄ—Å¡ka, БеларуÑкаÑ, БеларуÑÐºÐ°Ñ (тарашкевіца), БългарÑки, Bamanankan, বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Ðохчийн, Cebuano, á£áŽ³áŽ©, ÄŒesky, СловѣÌньÑкъ / ⰔⰎⰑⰂⰡâ°â° â°”â°â°Ÿ, Чӑвашла, Cymraeg, Dansk, Zazaki, Þ‹Þ¨ÞˆÞ¬Þ€Þ¨Þ„Þ¦ÞÞ°, Ελληνικά, Emiliàn e rumagnòl, English, Esperanto, Español, Eesti, Euskara, Ùارسی, Suomi, Võro, Føroyskt, Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, Avañe'ẽ, ગà«àªœàª°àª¾àª¤à«€, Gaelg, עברית, हिनà¥à¤¦à¥€, Fiji Hindi, Hrvatski, Kreyòl ayisyen, Magyar, Õ€Õ¡ÕµÕ¥Ö€Õ¥Õ¶, Interlingua, Bahasa Indonesia, Ilokano, Ido, Ãslenska, Italiano, 日本語, Lojban, Basa Jawa, ქáƒáƒ áƒ—ული, Kongo, Kalaallisut, ಕನà³à²¨à²¡, 한국어, Къарачай-Малкъар, Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, LatvieÅ¡u, Basa Banyumasan, Malagasy, МакедонÑки, മലയാളം, मराठी, Bahasa Melayu, مازÙرونی, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪Norsk (nynorsk)‬, ‪Norsk (bokmÃ¥l)‬, Nouormand, Diné bizaad, Occitan, Иронау, Papiamentu, Deitsch, Norfuk / Pitkern, Polski, پنجابی, پښتو, Português, Runa Simi, Rumantsch, Romani, Română, РуÑÑкий, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple English, SlovenÄina, SlovenÅ¡Äina, СрпÑки / Srpski, Seeltersk, Svenska, Kiswahili, தமிழà¯, తెలà±à°—à±, Тоҷикӣ, ไทย, Türkmençe, Tagalog, Türkçe, Татарча/Tatarça, УкраїнÑька, اردو, Tiếng Việt, Volapük, Walon, Winaray, å´è¯­, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, Bân-lâm-gú, 粵語";
+
+function checkRecursively(assert, map1, map2) {
+ if (typeof map1 !== 'function' && typeof map2 !== 'function') {
+ if (!map1 || typeof map1 !== 'object') {
+ assert.equal(map1, map2);
+ } else {
+ for (let key in map1) {
+ checkRecursively(assert, map1[key], map2[key]);
+ }
+ }
+ }
+}
+
+QUnit.module('Base Types');
+
+ QUnit.test('Void', function(assert) {
+ assert.equal(client.testVoid(), undefined);
+ });
+ QUnit.test('Binary (String)', function(assert) {
+ let binary = '';
+ for (let v = 255; v >= 0; --v) {
+ binary += String.fromCharCode(v);
+ }
+ assert.equal(client.testBinary(binary), binary);
+ });
+ QUnit.test('Binary (Uint8Array)', function(assert) {
+ let binary = '';
+ for (let v = 255; v >= 0; --v) {
+ binary += String.fromCharCode(v);
+ }
+ const arr = new Uint8Array(binary.length);
+ for (let i = 0; i < binary.length; ++i) {
+ arr[i] = binary[i].charCodeAt();
+ }
+ assert.equal(client.testBinary(arr), binary);
+ });
+ QUnit.test('String', function(assert) {
+ assert.equal(client.testString(''), '');
+ assert.equal(client.testString(stringTest), stringTest);
+
+ const specialCharacters = 'quote: \" backslash:' +
+ ' forwardslash-escaped: \/ ' +
+ ' backspace: \b formfeed: \f newline: \n return: \r tab: ' +
+ ' now-all-of-them-together: "\\\/\b\n\r\t' +
+ ' now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><';
+ assert.equal(client.testString(specialCharacters), specialCharacters);
+ });
+ QUnit.test('Double', function(assert) {
+ assert.equal(client.testDouble(0), 0);
+ assert.equal(client.testDouble(-1), -1);
+ assert.equal(client.testDouble(3.14), 3.14);
+ assert.equal(client.testDouble(Math.pow(2, 60)), Math.pow(2, 60));
+ });
+ QUnit.test('Byte', function(assert) {
+ assert.equal(client.testByte(0), 0);
+ assert.equal(client.testByte(0x01), 0x01);
+ });
+ QUnit.test('I32', function(assert) {
+ assert.equal(client.testI32(0), 0);
+ assert.equal(client.testI32(Math.pow(2, 30)), Math.pow(2, 30));
+ assert.equal(client.testI32(-Math.pow(2, 30)), -Math.pow(2, 30));
+ });
+ QUnit.test('I64', function(assert) {
+ assert.equal(client.testI64(0), 0);
+
+ let int64_2_pow_60_result = client.testI64(int64_2_pow_60);
+ assert.ok(int64_2_pow_60.equals(int64_2_pow_60_result));
+
+ let int64_minus_2_pow_60_result = client.testI64(int64_minus_2_pow_60);
+ assert.ok(int64_minus_2_pow_60.equals(int64_minus_2_pow_60_result));
+ });
+
+
+QUnit.module('Structured Types');
+
+ QUnit.test('Struct', function(assert) {
+ const structTestInput = new ThriftTest.Xtruct();
+ structTestInput.string_thing = 'worked';
+ structTestInput.byte_thing = 0x01;
+ structTestInput.i32_thing = Math.pow(2, 30);
+ structTestInput.i64_thing = int64_2_pow_60;
+
+ const structTestOutput = client.testStruct(structTestInput);
+
+ assert.equal(structTestOutput.string_thing, structTestInput.string_thing);
+ assert.equal(structTestOutput.byte_thing, structTestInput.byte_thing);
+ assert.equal(structTestOutput.i32_thing, structTestInput.i32_thing);
+ assert.ok(structTestOutput.i64_thing.equals(structTestInput.i64_thing));
+
+ assert.equal(JSON.stringify(structTestOutput), JSON.stringify(structTestInput));
+ });
+
+ QUnit.test('Nest', function(assert) {
+ const xtrTestInput = new ThriftTest.Xtruct();
+ xtrTestInput.string_thing = 'worked';
+ xtrTestInput.byte_thing = 0x01;
+ xtrTestInput.i32_thing = Math.pow(2, 30);
+ xtrTestInput.i64_thing = int64_2_pow_60;
+
+ const nestTestInput = new ThriftTest.Xtruct2();
+ nestTestInput.byte_thing = 0x02;
+ nestTestInput.struct_thing = xtrTestInput;
+ nestTestInput.i32_thing = Math.pow(2, 15);
+
+ const nestTestOutput = client.testNest(nestTestInput);
+
+ assert.equal(nestTestOutput.byte_thing, nestTestInput.byte_thing);
+ assert.equal(nestTestOutput.struct_thing.string_thing, nestTestInput.struct_thing.string_thing);
+ assert.equal(nestTestOutput.struct_thing.byte_thing, nestTestInput.struct_thing.byte_thing);
+ assert.equal(nestTestOutput.struct_thing.i32_thing, nestTestInput.struct_thing.i32_thing);
+ assert.ok(nestTestOutput.struct_thing.i64_thing.equals(nestTestInput.struct_thing.i64_thing));
+ assert.equal(nestTestOutput.i32_thing, nestTestInput.i32_thing);
+
+ assert.equal(JSON.stringify(nestTestOutput), JSON.stringify(nestTestInput));
+ });
+
+ QUnit.test('Map', function(assert) {
+ const mapTestInput = {7: 77, 8: 88, 9: 99};
+
+ const mapTestOutput = client.testMap(mapTestInput);
+
+ for (let key in mapTestOutput) {
+ assert.equal(mapTestOutput[key], mapTestInput[key]);
+ }
+ });
+
+ QUnit.test('StringMap', function(assert) {
+ const mapTestInput = {
+ 'a': '123', 'a b': 'with spaces ', 'same': 'same', '0': 'numeric key',
+ 'longValue': stringTest, stringTest: 'long key'
+ };
+
+ const mapTestOutput = client.testStringMap(mapTestInput);
+
+ for (let key in mapTestOutput) {
+ assert.equal(mapTestOutput[key], mapTestInput[key]);
+ }
+ });
+
+ QUnit.test('Set', function(assert) {
+ const setTestInput = [1, 2, 3];
+ assert.ok(client.testSet(setTestInput), setTestInput);
+ });
+
+ QUnit.test('List', function(assert) {
+ const listTestInput = [1, 2, 3];
+ assert.ok(client.testList(listTestInput), listTestInput);
+ });
+
+ QUnit.test('Enum', function(assert) {
+ assert.equal(client.testEnum(ThriftTest.Numberz.ONE), ThriftTest.Numberz.ONE);
+ });
+
+ QUnit.test('TypeDef', function(assert) {
+ assert.equal(client.testTypedef(69), 69);
+ });
+
+ QUnit.test('Skip', function(assert) {
+ const structTestInput = new ThriftTest.Xtruct();
+ const modifiedClient = new ThriftTest.ThriftTestClient(protocol);
+
+ modifiedClient.recv_testStruct = function() {
+ const input = modifiedClient.input;
+ const xtruct3 = new ThriftTest.Xtruct3();
+
+ input.readMessageBegin();
+ input.readStructBegin();
+
+ // read Xtruct data with Xtruct3
+ input.readFieldBegin();
+ xtruct3.read(input);
+ input.readFieldEnd();
+ // read Thrift.Type.STOP message
+ input.readFieldBegin();
+ input.readFieldEnd();
+
+ input.readStructEnd();
+ input.readMessageEnd();
+
+ return xtruct3;
+ };
+
+ structTestInput.string_thing = 'worked';
+ structTestInput.byte_thing = 0x01;
+ structTestInput.i32_thing = Math.pow(2, 30);
+ structTestInput.i64_thing = int64_2_pow_60;
+
+ const structTestOutput = modifiedClient.testStruct(structTestInput);
+
+ assert.equal(structTestOutput instanceof ThriftTest.Xtruct3, true);
+ assert.equal(structTestOutput.string_thing, structTestInput.string_thing);
+ assert.equal(structTestOutput.changed, null);
+ assert.equal(structTestOutput.i32_thing, structTestInput.i32_thing);
+ assert.ok(structTestOutput.i64_thing.equals(structTestInput.i64_thing));
+ });
+
+
+QUnit.module('deeper!');
+
+ QUnit.test('MapMap', function(assert) {
+ const mapMapTestExpectedResult = {
+ '4': {'1': 1, '2': 2, '3': 3, '4': 4},
+ '-4': {'-4': -4, '-3': -3, '-2': -2, '-1': -1}
+ };
+
+ const mapMapTestOutput = client.testMapMap(1);
+
+
+ for (let key in mapMapTestOutput) {
+ for (let key2 in mapMapTestOutput[key]) {
+ assert.equal(mapMapTestOutput[key][key2], mapMapTestExpectedResult[key][key2]);
+ }
+ }
+
+ checkRecursively(assert, mapMapTestOutput, mapMapTestExpectedResult);
+ });
+
+
+QUnit.module('Exception');
+
+ QUnit.test('Xception', function(assert) {
+ assert.expect(2);
+ const done = assert.async();
+ try {
+ client.testException('Xception');
+ assert.ok(false);
+ }catch (e) {
+ assert.equal(e.errorCode, 1001);
+ assert.equal(e.message, 'Xception');
+ done();
+ }
+ });
+
+ QUnit.test('no Exception', function(assert) {
+ assert.expect(1);
+ try {
+ client.testException('no Exception');
+ assert.ok(true);
+ }catch (e) {
+ assert.ok(false);
+ }
+ });
+
+ QUnit.test('TException', function(assert) {
+ //ThriftTest does not list TException as a legal exception so it will
+ // generate an exception on the server that does not propagate back to
+ // the client. This test has been modified to equate to "no exception"
+ assert.expect(1);
+ try {
+ client.testException('TException');
+ } catch (e) {
+ //assert.ok(false);
+ }
+ assert.ok(true);
+ });
+
+
+QUnit.module('Insanity');
+
+ const crazy = {
+ 'userMap': { '5': 5, '8': 8 },
+ 'xtructs': [{
+ 'string_thing': 'Goodbye4',
+ 'byte_thing': 4,
+ 'i32_thing': 4,
+ 'i64_thing': new Int64(4)
+ },
+ {
+ 'string_thing': 'Hello2',
+ 'byte_thing': 2,
+ 'i32_thing': 2,
+ 'i64_thing': new Int64(2)
+ }]
+ };
+ QUnit.test('testInsanity', function(assert) {
+ const insanity = {
+ '1': {
+ '2': crazy,
+ '3': crazy
+ },
+ '2': { '6': { 'userMap': null, 'xtructs': null } }
+ };
+ const res = client.testInsanity(new ThriftTest.Insanity(crazy));
+ assert.ok(res, JSON.stringify(res));
+ assert.ok(insanity, JSON.stringify(insanity));
+
+ checkRecursively(assert, res, insanity);
+ });
+
+
+//////////////////////////////////
+//Run same tests asynchronously
+
+QUnit.module('Async');
+
+ QUnit.test('Double', function(assert) {
+ assert.expect(1);
+
+ const done = assert.async();
+ client.testDouble(3.14159265, function(result) {
+ assert.equal(result, 3.14159265);
+ done();
+ });
+ });
+
+ QUnit.test('Byte', function(assert) {
+ assert.expect(1);
+
+ const done = assert.async();
+ client.testByte(0x01, function(result) {
+ assert.equal(result, 0x01);
+ done();
+ });
+ });
+
+ QUnit.test('I32', function(assert) {
+ assert.expect(2);
+
+ const done = assert.async(2);
+ client.testI32(Math.pow(2, 30), function(result) {
+ assert.equal(result, Math.pow(2, 30));
+ done();
+ });
+
+ client.testI32(Math.pow(-2, 31), function(result) {
+ assert.equal(result, Math.pow(-2, 31));
+ done();
+ });
+ });
+
+ QUnit.test('I64', function(assert) {
+ assert.expect(2);
+
+ const done = assert.async(2);
+ client.testI64(int64_2_pow_60, function(result) {
+ assert.ok(int64_2_pow_60.equals(result));
+ done();
+ });
+
+ client.testI64(int64_minus_2_pow_60, function(result) {
+ assert.ok(int64_minus_2_pow_60.equals(result));
+ done();
+ });
+ });
diff --git a/src/jaegertracing/thrift/lib/js/test/test_handler.js b/src/jaegertracing/thrift/lib/js/test/test_handler.js
new file mode 100644
index 000000000..8ba296ba1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/test_handler.js
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * 'License'); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+//This is the server side Node test handler for the standard
+// Apache Thrift test service.
+
+const es6Mode = process.argv.includes('--es6');
+const genFolder = es6Mode ? 'gen-nodejs-es6' : 'gen-nodejs';
+const ttypes = require(`./${genFolder}/ThriftTest_types`);
+const TException = require('../../nodejs/lib/thrift').TException;
+const Int64 = require('node-int64');
+
+exports.ThriftTestHandler = {
+ testVoid: function(result) {
+ console.log('testVoid()');
+ result(null);
+ },
+ testString: function(thing, result) {
+ console.log('testString(\'' + thing + '\')');
+ result(null, thing);
+ },
+ testByte: function(thing, result) {
+ console.log('testByte(' + thing + ')');
+ result(null, thing);
+ },
+ testI32: function(thing, result) {
+ console.log('testI32(' + thing + ')');
+ result(null, thing);
+ },
+ testI64: function(thing, result) {
+ console.log('testI64(' + thing + ')');
+ result(null, thing);
+ },
+ testDouble: function(thing, result) {
+ console.log('testDouble(' + thing + ')');
+ result(null, thing);
+ },
+ testBinary: function(thing, result) {
+ console.log('testBinary(\'' + thing + '\')');
+ result(null, thing);
+ },
+ testStruct: function(thing, result) {
+ console.log('testStruct(');
+ console.log(thing);
+ console.log(')');
+ result(null, thing);
+ },
+ testNest: function(nest, result) {
+ console.log('testNest(');
+ console.log(nest);
+ console.log(')');
+ result(null, nest);
+ },
+ testMap: function(thing, result) {
+ console.log('testMap(');
+ console.log(thing);
+ console.log(')');
+ result(null, thing);
+ },
+ testStringMap: function(thing, result) {
+ console.log('testStringMap(');
+ console.log(thing);
+ console.log(')');
+ result(null, thing);
+ },
+ testSet: function(thing, result) {
+ console.log('testSet(');
+ console.log(thing);
+ console.log(')');
+ result(null, thing);
+ },
+ testList: function(thing, result) {
+ console.log('testList(');
+ console.log(thing);
+ console.log(')');
+ result(null, thing);
+ },
+ testEnum: function(thing, result) {
+ console.log('testEnum(' + thing + ')');
+ result(null, thing);
+ },
+ testTypedef: function(thing, result) {
+ console.log('testTypedef(' + thing + ')');
+ result(null, thing);
+ },
+ testMapMap: function(hello, result) {
+ console.log('testMapMap(' + hello + ')');
+
+ const mapmap = [];
+ const pos = [];
+ const neg = [];
+ for (let i = 1; i < 5; i++) {
+ pos[i] = i;
+ neg[-i] = -i;
+ }
+ mapmap[4] = pos;
+ mapmap[-4] = neg;
+
+ result(null, mapmap);
+ },
+ testInsanity: function(argument, result) {
+ console.log('testInsanity(');
+ console.log(argument);
+ console.log(')');
+
+ const hello = new ttypes.Xtruct();
+ hello.string_thing = 'Hello2';
+ hello.byte_thing = 2;
+ hello.i32_thing = 2;
+ hello.i64_thing = new Int64(2);
+
+ const goodbye = new ttypes.Xtruct();
+ goodbye.string_thing = 'Goodbye4';
+ goodbye.byte_thing = 4;
+ goodbye.i32_thing = 4;
+ goodbye.i64_thing = new Int64(4);
+
+ const crazy = new ttypes.Insanity();
+ crazy.userMap = [];
+ crazy.userMap[ttypes.Numberz.EIGHT] = 8;
+ crazy.userMap[ttypes.Numberz.FIVE] = 5;
+ crazy.xtructs = [goodbye, hello];
+
+ const first_map = [];
+ const second_map = [];
+
+ first_map[ttypes.Numberz.TWO] = crazy;
+ first_map[ttypes.Numberz.THREE] = crazy;
+
+ const looney = new ttypes.Insanity();
+ second_map[ttypes.Numberz.SIX] = looney;
+
+ const insane = [];
+ insane[1] = first_map;
+ insane[2] = second_map;
+
+ console.log('insane result:');
+ console.log(insane);
+ result(null, insane);
+ },
+ testMulti: function(arg0, arg1, arg2, arg3, arg4, arg5, result) {
+ console.log('testMulti()');
+
+ const hello = new ttypes.Xtruct();
+ hello.string_thing = 'Hello2';
+ hello.byte_thing = arg0;
+ hello.i32_thing = arg1;
+ hello.i64_thing = arg2;
+ result(null, hello);
+ },
+ testException: function(arg, result) {
+ console.log('testException(' + arg + ')');
+ if (arg === 'Xception') {
+ const x = new ttypes.Xception();
+ x.errorCode = 1001;
+ x.message = arg;
+ result(x);
+ } else if (arg === 'TException') {
+ result(new TException(arg));
+ } else {
+ result(null);
+ }
+ },
+ testMultiException: function(arg0, arg1, result) {
+ console.log('testMultiException(' + arg0 + ', ' + arg1 + ')');
+ if (arg0 === ('Xception')) {
+ const x = new ttypes.Xception();
+ x.errorCode = 1001;
+ x.message = 'This is an Xception';
+ result(x);
+ } else if (arg0 === ('Xception2')) {
+ const x2 = new ttypes.Xception2();
+ x2.errorCode = 2002;
+ x2.struct_thing = new ttypes.Xtruct();
+ x2.struct_thing.string_thing = 'This is an Xception2';
+ result(x2);
+ }
+
+ const res = new ttypes.Xtruct();
+ res.string_thing = arg1;
+ result(null, res);
+ },
+ testOneway: function(sleepFor, result) {
+ console.log('testOneway(' + sleepFor + ') => JavaScript (like Rust) never sleeps!');
+ }
+}; //ThriftTestSvcHandler
diff --git a/src/jaegertracing/thrift/lib/js/test/testws.html b/src/jaegertracing/thrift/lib/js/test/testws.html
new file mode 100644
index 000000000..5ff9f2291
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/js/test/testws.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>Thrift Javascript Bindings: Unit Test</title>
+
+ <script src="build/js/lib/Int64.js" type="text/javascript" charset="utf-8"></script>
+ <script src="build/js/lib/Int64Util.js" type="text/javascript" charset="utf-8"></script>
+ <script src="build/js/lib/JSONInt64.js" type="text/javascript" charset="utf-8"></script>
+ <script src="build/js/thrift.js" type="text/javascript" charset="utf-8"></script>
+ <script src="gen-js/ThriftTest_types.js" type="text/javascript" charset="utf-8"></script>
+ <script src="gen-js/ThriftTest.js" type="text/javascript" charset="utf-8"></script>
+
+ <!-- jQuery -->
+ <script type="text/javascript" src="build/js/lib/jquery.js" charset="utf-8"></script>
+
+ <!-- QUnit Test framework-->
+ <script type="text/javascript" src="build/js/lib/qunit.js" charset="utf-8"></script>
+ <link rel="stylesheet" href="build/js/lib/qunit.css" type="text/css" media="screen" />
+
+ <!-- the Test Suite-->
+ <script>
+ const loc = window.location;
+ const ws_uri = ((loc.protocol === "https:") ? "wss://" : "ws://") +
+ loc.hostname + ":" + loc.port + loc.pathname;
+ const transport = new Thrift.TWebSocketTransport(ws_uri);
+ const protocol = new Thrift.Protocol(transport);
+ const client = new ThriftTest.ThriftTestClient(protocol);
+ transport.open();
+ </script>
+ <script type="text/javascript" src="test-async.js" charset="utf-8"></script>
+</head>
+<body>
+ <h1 id="qunit-header">Thrift Javascript Bindings: Unit Test (<a href="https://github.com/apache/thrift/blob/master/test/ThriftTest.thrift">ThriftTest.thrift</a>)</h1>
+ <h2 id="qunit-banner"></h2>
+ <div id="qunit-testrunner-toolbar"></div>
+ <h2 id="qunit-userAgent"></h2>
+ <ol id="qunit-tests"><li><!-- get valid xhtml strict--></li></ol>
+ <!-- Uncomment this to check the validity. This significantly slows down the test.
+ <p>
+ <a href="http://validator.w3.org/check/referer"><img
+ src="http://www.w3.org/Icons/valid-xhtml10"
+ alt="Valid XHTML 1.0!" height="31" width="88" /></a>
+ </p>
+ -->
+</body>
+</html>
diff --git a/src/jaegertracing/thrift/lib/json/Makefile.am b/src/jaegertracing/thrift/lib/json/Makefile.am
new file mode 100644
index 000000000..6c8c0ce81
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/json/Makefile.am
@@ -0,0 +1,33 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+if WITH_JAVA
+# Schema validation test depends on java
+SUBDIRS = test
+endif
+
+clean-local:
+ $(RM) -r test/build/
+
+dist-hook:
+ $(RM) -r $(distdir)/test/build/
+
+EXTRA_DIST = \
+ schema.json \
+ test
diff --git a/src/jaegertracing/thrift/lib/json/schema.json b/src/jaegertracing/thrift/lib/json/schema.json
new file mode 100644
index 000000000..d058a7c1a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/json/schema.json
@@ -0,0 +1,344 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+
+ "id": "http://thrift.apache.org/schema.json#",
+ "description": "Schema for Apache Thrift protocol descriptors",
+
+ "definitions": {
+ "type-id": {
+ "title": "Any type id (name)",
+ "enum": [
+ "void",
+ "string",
+ "bool",
+ "byte",
+ "i8",
+ "i16",
+ "i32",
+ "i64",
+ "double",
+ "list",
+ "set",
+ "map",
+ "union",
+ "struct",
+ "binary"
+ ]
+ },
+ "base-type": {
+ "title": "Base type schema",
+ "type": "object",
+ "properties": {
+ "typeId": {
+ "enum": ["void", "string", "bool", "byte", "i8", "i16", "i32", "i64", "double", "binary" ]
+ }
+ },
+ "required": [ "typeId" ]
+ },
+ "list-type": {
+ "title": "List and set schema",
+ "type": "object",
+ "properties": {
+ "typeId": {
+ "enum": [ "list", "set" ]
+ },
+ "elemTypeId": { "$ref": "#/definitions/type-id" },
+ "elemType": { "$ref": "#/definitions/type-desc" }
+ },
+ "required": [ "typeId", "elemTypeId" ]
+ },
+ "map-type": {
+ "title": "Map schema",
+ "type": "object",
+ "properties": {
+ "typeId": {
+ "enum": [ "map" ]
+ },
+ "keyTypeId": { "$ref": "#/definitions/type-id" },
+ "keyType": { "$ref": "#/definitions/type-desc" },
+ "valueTypeId": { "$ref": "#/definitions/type-id" },
+ "valueType": { "$ref": "#/definitions/type-desc" }
+ },
+ "required": [ "typeId", "keyTypeId", "valueTypeId" ]
+ },
+ "struct-type": {
+ "title": "Struct, union and exception schema",
+ "type": "object",
+ "properties": {
+ "typeId": {
+ "enum": [ "union", "struct", "exception" ]
+ }
+ },
+ "required": [ "typeId", "class" ]
+ },
+ "type-desc": {
+ "title": "Type descriptor schema",
+ "allOf": [
+ {
+ "type": "object",
+ "properties": {
+ "typeId": { "type": "string" },
+ "class": { "type": "string" }
+ }
+ },
+ {
+ "oneOf":
+ [
+ { "$ref": "#/definitions/base-type" },
+ { "$ref": "#/definitions/list-type" },
+ { "$ref": "#/definitions/map-type" },
+ { "$ref": "#/definitions/struct-type" }
+ ]
+ }
+ ]
+ },
+ "name-and-doc": {
+ "title": "Name and documentation sub-schema",
+ "type": "object",
+ "properties": {
+ "name": { "type": "string" },
+ "doc": { "type": "string" }
+ },
+ "required": [ "name" ]
+ },
+ "enum": {
+ "title": "Thrift 'enum' definition schema",
+ "type": "object",
+ "allOf": [
+ { "$ref": "#/definitions/name-and-doc" },
+ {
+ "required": [ "members" ],
+ "properties": {
+ "members": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": { "type": "string" },
+ "value": { "type": "integer" }
+ },
+ "required": [ "name", "value" ]
+ }
+ }
+ }
+ }
+ ]
+ },
+ "typedef": {
+ "title": "Thrift typedef definition schema",
+ "type": "object",
+ "allOf": [
+ { "$ref": "#/definitions/name-and-doc" },
+ {
+ "properties": {
+ "typeId": { "$ref": "#/definitions/type-id" },
+ "type": { "$ref": "#/definitions/type-desc" }
+ },
+ "required": [ "typeId" ]
+ }
+ ]
+ },
+ "constant": {
+ "title": "Thrift constant definition schema",
+ "type": "object",
+ "allOf": [
+ { "$ref": "#/definitions/name-and-doc" },
+ { "$ref": "#/definitions/type-desc" },
+ {
+ "properties": {
+ "value": {
+ "oneOf": [
+ { "type": "string" },
+ { "type": "number" },
+ { "type": "array" },
+ { "type": "object" }
+ ]
+ }
+ },
+ "required": [ "value" ]
+ }
+ ]
+ },
+ "field": {
+ "title": "Thrift struct field definition schema",
+ "type": "object",
+ "allOf": [
+ { "$ref": "#/definitions/name-and-doc" },
+ {
+ "properties": {
+ "key": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 65535
+ },
+ "required": {
+ "enum": [ "required", "optional", "req_out" ]
+ },
+ "typeId": { "$ref": "#/definitions/type-id" },
+ "type": { "$ref": "#/definitions/type-desc" },
+ "default": {
+ "oneOf": [
+ { "type": "string" },
+ { "type": "number" },
+ { "type": "array" },
+ { "type": "object" }
+ ]
+ }
+ },
+ "required": [ "key", "required" ]
+ }
+ ]
+ },
+ "struct": {
+ "title": "Thrift struct definition schema",
+ "type": "object",
+ "allOf": [
+ { "$ref": "#/definitions/name-and-doc" },
+ {
+ "properties": {
+ "isException": { "type": "boolean" },
+ "isUnion": { "type": "boolean" },
+ "fields": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/field"
+ }
+ }
+ },
+ "required": [ "isException", "isUnion", "fields" ]
+ }
+ ]
+ },
+ "union": {
+ "title": "Thrift union definition schema",
+ "$ref": "#/definitions/struct"
+ },
+ "exception": {
+ "title": "Thrift exception definition schema",
+ "type": "object",
+ "properties": {
+ "key": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 65535
+ },
+ "name": { "type": "string" },
+ "typeId": { "enum": [ "exception" ] },
+ "type": { "$ref": "#/definitions/struct-type" }
+ },
+ "required": [ "key", "name", "typeId" ]
+ },
+ "function": {
+ "title": "Thrift service function definition schema",
+ "type": "object",
+ "allOf": [
+ { "$ref": "#/definitions/name-and-doc" },
+ {
+ "properties": {
+ "oneway": {
+ "type": "boolean"
+ },
+ "returnType": {
+ "$ref": "#/definitions/type-desc"
+ },
+ "arguments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/field"
+ }
+ },
+ "exceptions": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/exception" }
+ }
+ },
+ "required": [ "oneway", "arguments", "exceptions" ]
+ }
+ ]
+ },
+ "service": {
+ "title": "Thrift service definition schema",
+ "type": "object",
+ "allOf": [
+ { "$ref": "#/definitions/name-and-doc" },
+ {
+ "properties": {
+ "functions": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/function"
+ }
+ }
+ },
+ "required": [ "functions" ]
+ }
+ ]
+ },
+ "annotations": {
+ "title": "Map of annotation names to values",
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ },
+
+ "type": "object",
+ "required": [
+ "name",
+ "enums",
+ "typedefs",
+ "structs",
+ "constants",
+ "services"
+ ],
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "includes": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "uniqueItems": true
+ },
+ "namespaces": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "enums": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/enum"
+ }
+ },
+ "typedefs": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/typedef"
+ }
+ },
+ "structs": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/struct"
+ }
+ },
+ "constants": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/constant"
+ }
+ },
+ "services": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/service"
+ }
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/src/jaegertracing/thrift/lib/json/test/Makefile.am b/src/jaegertracing/thrift/lib/json/test/Makefile.am
new file mode 100644
index 000000000..bb87a5203
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/json/test/Makefile.am
@@ -0,0 +1,26 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+check:
+ $(ANT) $(ANT_FLAGS) test
+
+# Make sure this doesn't fail if ant is not configured.
+clean-local:
+ ANT=$(ANT) ; if test -z "$$ANT" ; then ANT=: ; fi ; \
+ $$ANT $(ANT_FLAGS) clean
diff --git a/src/jaegertracing/thrift/lib/json/test/build.properties b/src/jaegertracing/thrift/lib/json/test/build.properties
new file mode 100644
index 000000000..075f64001
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/json/test/build.properties
@@ -0,0 +1,10 @@
+# Jar versions
+mvn.ant.task.version=2.1.3
+
+# Dependency versions
+json-schema-validator.version=2.2.6
+
+# Maven dependency download locations
+mvn.repo=http://repo1.maven.org/maven2
+mvn.ant.task.url=${mvn.repo}/org/apache/maven/maven-ant-tasks/${mvn.ant.task.version}
+mvn.ant.task.jar=maven-ant-tasks-${mvn.ant.task.version}.jar
diff --git a/src/jaegertracing/thrift/lib/json/test/build.xml b/src/jaegertracing/thrift/lib/json/test/build.xml
new file mode 100644
index 000000000..956a2382b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/json/test/build.xml
@@ -0,0 +1,144 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="JSON Schema Test" default="test" basedir="."
+ xmlns:artifact="antlib:org.apache.maven.artifact.ant">
+
+ <description>JSON Schema Validation Test</description>
+
+ <property name="build.dir" location="${basedir}/build" />
+ <property name="json.dir" location="${basedir}/.." />
+ <property name="gen.json.dir" location="${build.dir}/gen-json" />
+ <property name="json.schema" location="${json.dir}/schema.json" />
+ <property name="build.tools.dir" location="${build.dir}/tools"/>
+ <property name="build.lib.dir" location="${build.dir}/lib"/>
+
+ <!-- the root directory, where you unpack thrift distibution (e.g. thrift-0.x.x.tar.gz) -->
+ <property name="thrift.dir" location="../../../" />
+ <property name="thrift.test.dir" location="${thrift.dir}/test" />
+ <property name="thrift.compiler" location="${thrift.dir}/compiler/cpp/thrift" />
+
+ <!-- Get maven dependency versions from here -->
+ <property file="${basedir}/build.properties" />
+
+ <path id="test.classpath">
+ <fileset dir="${build.lib.dir}">
+ <include name="*.jar" />
+ </fileset>
+ </path>
+
+ <target name="compiler.check">
+ <fail>
+ <condition>
+ <not>
+ <resourcecount count="1">
+ <fileset id="fs" file="${thrift.compiler}"/>
+ </resourcecount>
+ </not>
+ </condition>
+ Thrift compiler is missing !
+ </fail>
+ </target>
+
+ <target name="init" depends="compiler.check, mkdirs, mvn.init">
+ <tstamp />
+ </target>
+
+ <target name="mkdirs">
+ <mkdir dir="${build.dir}"/>
+ <mkdir dir="${build.lib.dir}"/>
+ <mkdir dir="${build.tools.dir}"/>
+ <mkdir dir="${gen.json.dir}"/>
+ </target>
+
+ <target name="generate" depends="init">
+ <exec executable="${thrift.compiler}" failonerror="true">
+ <arg line="--gen json"/>
+ <arg line="-out ${gen.json.dir}"/>
+ <arg line="${thrift.test.dir}/ThriftTest.thrift"/>
+ </exec>
+ <exec executable="${thrift.compiler}" failonerror="true">
+ <arg line="--gen json:merge"/>
+ <arg line="-out ${gen.json.dir}"/>
+ <arg line="${thrift.test.dir}/Include.thrift"/>
+ </exec>
+ </target>
+
+ <target name="test" description="run schema validation"
+ depends="validate-schema, validate-generated-json"/>
+
+ <target name="validate-schema" depends="init">
+ <java classname="com.github.fge.jsonschema.main.cli.Main"
+ classpathref="test.classpath" failonerror="true">
+ <arg value="--syntax"/>
+ <arg value="${json.schema}"/>
+ </java>
+ </target>
+
+ <target name="validate-generated-json" depends="init, generate">
+ <validate-json file="${gen.json.dir}/ThriftTest.json"/>
+ <validate-json file="${gen.json.dir}/Include.json"/>
+ </target>
+
+ <target name="clean">
+ <delete dir="${build.dir}" />
+ <delete dir="${gen.json.dir}" />
+ </target>
+
+ <target name="mvn.ant.tasks.download" depends="mkdirs,mvn.ant.tasks.check" unless="mvn.ant.tasks.found">
+ <get src="${mvn.ant.task.url}/${mvn.ant.task.jar}" dest="${build.tools.dir}/${mvn.ant.task.jar}" usetimestamp="true"/>
+ </target>
+
+ <target name="mvn.ant.tasks.check">
+ <condition property="mvn.ant.tasks.found">
+ <typefound uri="antlib:org.apache.maven.artifact.ant" name="artifact"/>
+ </condition>
+ </target>
+
+ <target name="mvn.init" depends="mvn.ant.tasks.download" unless="mvn.finished">
+ <typedef uri="antlib:org.apache.maven.artifact.ant" classpath="${build.tools.dir}/${mvn.ant.task.jar}"/>
+
+ <artifact:dependencies filesetId="test.dependency.jars">
+ <dependency groupId="com.github.fge" artifactId="json-schema-validator" version="${json-schema-validator.version}"/>
+ </artifact:dependencies>
+
+ <!-- Copy the dependencies to the build/lib dir -->
+ <copy todir="${build.lib.dir}">
+ <fileset refid="test.dependency.jars"/>
+ <mapper type="flatten"/>
+ </copy>
+
+ <property name="mvn.finished" value="true"/>
+ </target>
+
+ <macrodef name="validate-json">
+ <attribute name="file" default=""/>
+ <sequential>
+ <java failonerror="true"
+ fork="true"
+ dir="${json.dir}"
+ classname="com.github.fge.jsonschema.main.cli.Main"
+ classpathref="test.classpath">
+ <arg line="--fakeroot http://thrift.apache.org/"/>
+ <arg value="${json.schema}"/>
+ <arg value="@{file}"/>
+ </java>
+ </sequential>
+ </macrodef>
+
+</project>
diff --git a/src/jaegertracing/thrift/lib/lua/Makefile.am b/src/jaegertracing/thrift/lib/lua/Makefile.am
new file mode 100644
index 000000000..3b272f56c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/Makefile.am
@@ -0,0 +1,73 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+AUTOMAKE_OPTIONS = subdir-objects nostdinc
+
+SUBDIRS = .
+
+lib_LTLIBRARIES = \
+ libluasocket.la \
+ liblualongnumber.la \
+ libluabpack.la \
+ libluabitwise.la
+
+libluasocket_la_SOURCES = \
+ src/luasocket.c \
+ src/usocket.c
+
+nobase_include_HEADERS = src/socket.h
+
+libluasocket_la_CPPFLAGS = $(AM_CPPFLAGS) $(LUA_INCLUDE) -DLUA_COMPAT_MODULE
+libluasocket_la_LDFLAGS = $(AM_LDFLAGS)
+libluasocket_la_LIBADD = $(LUA_LIB) -lm
+
+libluabpack_la_SOURCES = src/luabpack.c
+
+libluabpack_la_CPPFLAGS = $(AM_CPPFLAGS) $(LUA_INCLUDE) -DLUA_COMPAT_MODULE
+libluabpack_la_LDFLAGS = $(AM_LDFLAGS)
+libluabpack_la_LIBADD = liblualongnumber.la $(LUA_LIB) -lm
+
+libluabitwise_la_SOURCES = src/luabitwise.c
+
+libluabitwise_la_CPPFLAGS = $(AM_CPPFLAGS) $(LUA_INCLUDE) -DLUA_COMPAT_MODULE
+libluabitwise_la_LDFLAGS = $(AM_LDFLAGS)
+libluabitwise_la_LIBADD = $(LUA_LIB) -lm
+
+liblualongnumber_la_SOURCES = \
+ src/lualongnumber.c \
+ src/longnumberutils.c
+
+liblualongnumber_la_CPPFLAGS = $(AM_CPPFLAGS) $(LUA_INCLUDE) -DLUA_COMPAT_MODULE
+liblualongnumber_la_LDFLAGS = $(AM_LDFLAGS)
+liblualongnumber_la_LIBADD = $(LUA_LIB) -lm
+
+EXTRA_DIST = \
+ coding_standards.md \
+ TBinaryProtocol.lua \
+ TBufferedTransport.lua \
+ TCompactProtocol.lua \
+ TFramedTransport.lua \
+ Thrift.lua \
+ THttpTransport.lua \
+ TJsonProtocol.lua \
+ TMemoryBuffer.lua \
+ TProtocol.lua \
+ TServer.lua \
+ TSocket.lua \
+ TTransport.lua
diff --git a/src/jaegertracing/thrift/lib/lua/TBinaryProtocol.lua b/src/jaegertracing/thrift/lib/lua/TBinaryProtocol.lua
new file mode 100644
index 000000000..4b8e98a9d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/TBinaryProtocol.lua
@@ -0,0 +1,264 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+require 'TProtocol'
+require 'libluabpack'
+require 'libluabitwise'
+
+TBinaryProtocol = __TObject.new(TProtocolBase, {
+ __type = 'TBinaryProtocol',
+ VERSION_MASK = -65536, -- 0xffff0000
+ VERSION_1 = -2147418112, -- 0x80010000
+ TYPE_MASK = 0x000000ff,
+ strictRead = false,
+ strictWrite = true
+})
+
+function TBinaryProtocol:writeMessageBegin(name, ttype, seqid)
+ if self.strictWrite then
+ self:writeI32(libluabitwise.bor(TBinaryProtocol.VERSION_1, ttype))
+ self:writeString(name)
+ self:writeI32(seqid)
+ else
+ self:writeString(name)
+ self:writeByte(ttype)
+ self:writeI32(seqid)
+ end
+end
+
+function TBinaryProtocol:writeMessageEnd()
+end
+
+function TBinaryProtocol:writeStructBegin(name)
+end
+
+function TBinaryProtocol:writeStructEnd()
+end
+
+function TBinaryProtocol:writeFieldBegin(name, ttype, id)
+ self:writeByte(ttype)
+ self:writeI16(id)
+end
+
+function TBinaryProtocol:writeFieldEnd()
+end
+
+function TBinaryProtocol:writeFieldStop()
+ self:writeByte(TType.STOP);
+end
+
+function TBinaryProtocol:writeMapBegin(ktype, vtype, size)
+ self:writeByte(ktype)
+ self:writeByte(vtype)
+ self:writeI32(size)
+end
+
+function TBinaryProtocol:writeMapEnd()
+end
+
+function TBinaryProtocol:writeListBegin(etype, size)
+ self:writeByte(etype)
+ self:writeI32(size)
+end
+
+function TBinaryProtocol:writeListEnd()
+end
+
+function TBinaryProtocol:writeSetBegin(etype, size)
+ self:writeByte(etype)
+ self:writeI32(size)
+end
+
+function TBinaryProtocol:writeSetEnd()
+end
+
+function TBinaryProtocol:writeBool(bool)
+ if bool then
+ self:writeByte(1)
+ else
+ self:writeByte(0)
+ end
+end
+
+function TBinaryProtocol:writeByte(byte)
+ local buff = libluabpack.bpack('c', byte)
+ self.trans:write(buff)
+end
+
+function TBinaryProtocol:writeI16(i16)
+ local buff = libluabpack.bpack('s', i16)
+ self.trans:write(buff)
+end
+
+function TBinaryProtocol:writeI32(i32)
+ local buff = libluabpack.bpack('i', i32)
+ self.trans:write(buff)
+end
+
+function TBinaryProtocol:writeI64(i64)
+ local buff = libluabpack.bpack('l', i64)
+ self.trans:write(buff)
+end
+
+function TBinaryProtocol:writeDouble(dub)
+ local buff = libluabpack.bpack('d', dub)
+ self.trans:write(buff)
+end
+
+function TBinaryProtocol:writeString(str)
+ -- Should be utf-8
+ self:writeI32(string.len(str))
+ self.trans:write(str)
+end
+
+function TBinaryProtocol:readMessageBegin()
+ local sz, ttype, name, seqid = self:readI32()
+ if sz < 0 then
+ local version = libluabitwise.band(sz, TBinaryProtocol.VERSION_MASK)
+ if version ~= TBinaryProtocol.VERSION_1 then
+ terror(TProtocolException:new{
+ message = 'Bad version in readMessageBegin: ' .. sz
+ })
+ end
+ ttype = libluabitwise.band(sz, TBinaryProtocol.TYPE_MASK)
+ name = self:readString()
+ seqid = self:readI32()
+ else
+ if self.strictRead then
+ terror(TProtocolException:new{message = 'No protocol version header'})
+ end
+ name = self.trans:readAll(sz)
+ ttype = self:readByte()
+ seqid = self:readI32()
+ end
+ return name, ttype, seqid
+end
+
+function TBinaryProtocol:readMessageEnd()
+end
+
+function TBinaryProtocol:readStructBegin()
+ return nil
+end
+
+function TBinaryProtocol:readStructEnd()
+end
+
+function TBinaryProtocol:readFieldBegin()
+ local ttype = self:readByte()
+ if ttype == TType.STOP then
+ return nil, ttype, 0
+ end
+ local id = self:readI16()
+ return nil, ttype, id
+end
+
+function TBinaryProtocol:readFieldEnd()
+end
+
+function TBinaryProtocol:readMapBegin()
+ local ktype = self:readByte()
+ local vtype = self:readByte()
+ local size = self:readI32()
+ return ktype, vtype, size
+end
+
+function TBinaryProtocol:readMapEnd()
+end
+
+function TBinaryProtocol:readListBegin()
+ local etype = self:readByte()
+ local size = self:readI32()
+ return etype, size
+end
+
+function TBinaryProtocol:readListEnd()
+end
+
+function TBinaryProtocol:readSetBegin()
+ local etype = self:readByte()
+ local size = self:readI32()
+ return etype, size
+end
+
+function TBinaryProtocol:readSetEnd()
+end
+
+function TBinaryProtocol:readBool()
+ local byte = self:readByte()
+ if byte == 0 then
+ return false
+ end
+ return true
+end
+
+function TBinaryProtocol:readByte()
+ local buff = self.trans:readAll(1)
+ local val = libluabpack.bunpack('c', buff)
+ return val
+end
+
+function TBinaryProtocol:readI16()
+ local buff = self.trans:readAll(2)
+ local val = libluabpack.bunpack('s', buff)
+ return val
+end
+
+function TBinaryProtocol:readI32()
+ local buff = self.trans:readAll(4)
+ local val = libluabpack.bunpack('i', buff)
+ return val
+end
+
+function TBinaryProtocol:readI64()
+ local buff = self.trans:readAll(8)
+ local val = libluabpack.bunpack('l', buff)
+ return val
+end
+
+function TBinaryProtocol:readDouble()
+ local buff = self.trans:readAll(8)
+ local val = libluabpack.bunpack('d', buff)
+ return val
+end
+
+function TBinaryProtocol:readString()
+ local len = self:readI32()
+ local str = self.trans:readAll(len)
+ return str
+end
+
+TBinaryProtocolFactory = TProtocolFactory:new{
+ __type = 'TBinaryProtocolFactory',
+ strictRead = false
+}
+
+function TBinaryProtocolFactory:getProtocol(trans)
+ -- TODO Enforce that this must be a transport class (ie not a bool)
+ if not trans then
+ terror(TProtocolException:new{
+ message = 'Must supply a transport to ' .. ttype(self)
+ })
+ end
+ return TBinaryProtocol:new{
+ trans = trans,
+ strictRead = self.strictRead,
+ strictWrite = true
+ }
+end
diff --git a/src/jaegertracing/thrift/lib/lua/TBufferedTransport.lua b/src/jaegertracing/thrift/lib/lua/TBufferedTransport.lua
new file mode 100644
index 000000000..45ef4b1c7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/TBufferedTransport.lua
@@ -0,0 +1,91 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+require 'TTransport'
+
+TBufferedTransport = TTransportBase:new{
+ __type = 'TBufferedTransport',
+ rBufSize = 2048,
+ wBufSize = 2048,
+ wBuf = '',
+ rBuf = ''
+}
+
+function TBufferedTransport:new(obj)
+ if ttype(obj) ~= 'table' then
+ error(ttype(self) .. 'must be initialized with a table')
+ end
+
+ -- Ensure a transport is provided
+ if not obj.trans then
+ error('You must provide ' .. ttype(self) .. ' with a trans')
+ end
+
+ return TTransportBase.new(self, obj)
+end
+
+function TBufferedTransport:isOpen()
+ return self.trans:isOpen()
+end
+
+function TBufferedTransport:open()
+ return self.trans:open()
+end
+
+function TBufferedTransport:close()
+ return self.trans:close()
+end
+
+function TBufferedTransport:read(len)
+ return self.trans:read(len)
+end
+
+function TBufferedTransport:readAll(len)
+ return self.trans:readAll(len)
+end
+
+function TBufferedTransport:write(buf)
+ self.wBuf = self.wBuf .. buf
+ if string.len(self.wBuf) >= self.wBufSize then
+ self.trans:write(self.wBuf)
+ self.wBuf = ''
+ end
+end
+
+function TBufferedTransport:flush()
+ if string.len(self.wBuf) > 0 then
+ self.trans:write(self.wBuf)
+ self.wBuf = ''
+ end
+end
+
+TBufferedTransportFactory = TTransportFactoryBase:new{
+ __type = 'TBufferedTransportFactory'
+}
+
+function TBufferedTransportFactory:getTransport(trans)
+ if not trans then
+ terror(TTransportException:new{
+ message = 'Must supply a transport to ' .. ttype(self)
+ })
+ end
+ return TBufferedTransport:new{
+ trans = trans
+ }
+end
diff --git a/src/jaegertracing/thrift/lib/lua/TCompactProtocol.lua b/src/jaegertracing/thrift/lib/lua/TCompactProtocol.lua
new file mode 100644
index 000000000..877595a5d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/TCompactProtocol.lua
@@ -0,0 +1,457 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+require 'TProtocol'
+require 'libluabpack'
+require 'libluabitwise'
+require 'liblualongnumber'
+
+TCompactProtocol = __TObject.new(TProtocolBase, {
+ __type = 'TCompactProtocol',
+ COMPACT_PROTOCOL_ID = 0x82,
+ COMPACT_VERSION = 1,
+ COMPACT_VERSION_MASK = 0x1f,
+ COMPACT_TYPE_MASK = 0xE0,
+ COMPACT_TYPE_BITS = 0x07,
+ COMPACT_TYPE_SHIFT_AMOUNT = 5,
+
+ -- Used to keep track of the last field for the current and previous structs,
+ -- so we can do the delta stuff.
+ lastField = {},
+ lastFieldId = 0,
+ lastFieldIndex = 1,
+
+ -- If we encounter a boolean field begin, save the TField here so it can
+ -- have the value incorporated.
+ booleanFieldName = "",
+ booleanFieldId = 0,
+ booleanFieldPending = false,
+
+ -- If we read a field header, and it's a boolean field, save the boolean
+ -- value here so that readBool can use it.
+ boolValue = false,
+ boolValueIsNotNull = false,
+})
+
+TCompactType = {
+ COMPACT_BOOLEAN_TRUE = 0x01,
+ COMPACT_BOOLEAN_FALSE = 0x02,
+ COMPACT_BYTE = 0x03,
+ COMPACT_I16 = 0x04,
+ COMPACT_I32 = 0x05,
+ COMPACT_I64 = 0x06,
+ COMPACT_DOUBLE = 0x07,
+ COMPACT_BINARY = 0x08,
+ COMPACT_LIST = 0x09,
+ COMPACT_SET = 0x0A,
+ COMPACT_MAP = 0x0B,
+ COMPACT_STRUCT = 0x0C
+}
+
+TTypeToCompactType = {}
+TTypeToCompactType[TType.STOP] = TType.STOP
+TTypeToCompactType[TType.BOOL] = TCompactType.COMPACT_BOOLEAN_TRUE
+TTypeToCompactType[TType.BYTE] = TCompactType.COMPACT_BYTE
+TTypeToCompactType[TType.I16] = TCompactType.COMPACT_I16
+TTypeToCompactType[TType.I32] = TCompactType.COMPACT_I32
+TTypeToCompactType[TType.I64] = TCompactType.COMPACT_I64
+TTypeToCompactType[TType.DOUBLE] = TCompactType.COMPACT_DOUBLE
+TTypeToCompactType[TType.STRING] = TCompactType.COMPACT_BINARY
+TTypeToCompactType[TType.LIST] = TCompactType.COMPACT_LIST
+TTypeToCompactType[TType.SET] = TCompactType.COMPACT_SET
+TTypeToCompactType[TType.MAP] = TCompactType.COMPACT_MAP
+TTypeToCompactType[TType.STRUCT] = TCompactType.COMPACT_STRUCT
+
+CompactTypeToTType = {}
+CompactTypeToTType[TType.STOP] = TType.STOP
+CompactTypeToTType[TCompactType.COMPACT_BOOLEAN_TRUE] = TType.BOOL
+CompactTypeToTType[TCompactType.COMPACT_BOOLEAN_FALSE] = TType.BOOL
+CompactTypeToTType[TCompactType.COMPACT_BYTE] = TType.BYTE
+CompactTypeToTType[TCompactType.COMPACT_I16] = TType.I16
+CompactTypeToTType[TCompactType.COMPACT_I32] = TType.I32
+CompactTypeToTType[TCompactType.COMPACT_I64] = TType.I64
+CompactTypeToTType[TCompactType.COMPACT_DOUBLE] = TType.DOUBLE
+CompactTypeToTType[TCompactType.COMPACT_BINARY] = TType.STRING
+CompactTypeToTType[TCompactType.COMPACT_LIST] = TType.LIST
+CompactTypeToTType[TCompactType.COMPACT_SET] = TType.SET
+CompactTypeToTType[TCompactType.COMPACT_MAP] = TType.MAP
+CompactTypeToTType[TCompactType.COMPACT_STRUCT] = TType.STRUCT
+
+function TCompactProtocol:resetLastField()
+ self.lastField = {}
+ self.lastFieldId = 0
+ self.lastFieldIndex = 1
+end
+
+function TCompactProtocol:packCompactType(ktype, vtype)
+ return libluabitwise.bor(libluabitwise.shiftl(ktype, 4), vtype)
+end
+
+function TCompactProtocol:writeMessageBegin(name, ttype, seqid)
+ self:writeByte(TCompactProtocol.COMPACT_PROTOCOL_ID)
+ self:writeByte(libluabpack.packMesgType(TCompactProtocol.COMPACT_VERSION,
+ TCompactProtocol.COMPACT_VERSION_MASK,ttype,
+ TCompactProtocol.COMPACT_TYPE_SHIFT_AMOUNT,
+ TCompactProtocol.COMPACT_TYPE_MASK))
+ self:writeVarint32(seqid)
+ self:writeString(name)
+ self:resetLastField()
+end
+
+function TCompactProtocol:writeMessageEnd()
+end
+
+function TCompactProtocol:writeStructBegin(name)
+ self.lastFieldIndex = self.lastFieldIndex + 1
+ self.lastField[self.lastFieldIndex] = self.lastFieldId
+ self.lastFieldId = 0
+end
+
+function TCompactProtocol:writeStructEnd()
+ self.lastFieldIndex = self.lastFieldIndex - 1
+ self.lastFieldId = self.lastField[self.lastFieldIndex]
+end
+
+function TCompactProtocol:writeFieldBegin(name, ttype, id)
+ if ttype == TType.BOOL then
+ self.booleanFieldName = name
+ self.booleanFieldId = id
+ self.booleanFieldPending = true
+ else
+ self:writeFieldBeginInternal(name, ttype, id, -1)
+ end
+end
+
+function TCompactProtocol:writeFieldEnd()
+end
+
+function TCompactProtocol:writeFieldStop()
+ self:writeByte(TType.STOP);
+end
+
+function TCompactProtocol:writeMapBegin(ktype, vtype, size)
+ if size == 0 then
+ self:writeByte(0)
+ else
+ self:writeVarint32(size)
+ self:writeByte(self:packCompactType(TTypeToCompactType[ktype], TTypeToCompactType[vtype]))
+ end
+end
+
+function TCompactProtocol:writeMapEnd()
+end
+
+function TCompactProtocol:writeListBegin(etype, size)
+ self:writeCollectionBegin(etype, size)
+end
+
+function TCompactProtocol:writeListEnd()
+end
+
+function TCompactProtocol:writeSetBegin(etype, size)
+ self:writeCollectionBegin(etype, size)
+end
+
+function TCompactProtocol:writeSetEnd()
+end
+
+function TCompactProtocol:writeBool(bool)
+ local value = TCompactType.COMPACT_BOOLEAN_FALSE
+ if bool then
+ value = TCompactType.COMPACT_BOOLEAN_TRUE
+ end
+ print(value,self.booleanFieldPending,self.booleanFieldId)
+ if self.booleanFieldPending then
+ self:writeFieldBeginInternal(self.booleanFieldName, TType.BOOL, self.booleanFieldId, value)
+ self.booleanFieldPending = false
+ else
+ self:writeByte(value)
+ end
+end
+
+function TCompactProtocol:writeByte(byte)
+ local buff = libluabpack.bpack('c', byte)
+ self.trans:write(buff)
+end
+
+function TCompactProtocol:writeI16(i16)
+ self:writeVarint32(libluabpack.i32ToZigzag(i16))
+end
+
+function TCompactProtocol:writeI32(i32)
+ self:writeVarint32(libluabpack.i32ToZigzag(i32))
+end
+
+function TCompactProtocol:writeI64(i64)
+ self:writeVarint64(libluabpack.i64ToZigzag(i64))
+end
+
+function TCompactProtocol:writeDouble(dub)
+ local buff = libluabpack.bpack('d', dub)
+ self.trans:write(buff)
+end
+
+function TCompactProtocol:writeString(str)
+ -- Should be utf-8
+ self:writeBinary(str)
+end
+
+function TCompactProtocol:writeBinary(str)
+ -- Should be utf-8
+ self:writeVarint32(string.len(str))
+ self.trans:write(str)
+end
+
+function TCompactProtocol:writeFieldBeginInternal(name, ttype, id, typeOverride)
+ if typeOverride == -1 then
+ typeOverride = TTypeToCompactType[ttype]
+ end
+ local offset = id - self.lastFieldId
+ if id > self.lastFieldId and offset <= 15 then
+ self:writeByte(libluabitwise.bor(libluabitwise.shiftl(offset, 4), typeOverride))
+ else
+ self:writeByte(typeOverride)
+ self:writeI16(id)
+ end
+ self.lastFieldId = id
+end
+
+function TCompactProtocol:writeCollectionBegin(etype, size)
+ if size <= 14 then
+ self:writeByte(libluabitwise.bor(libluabitwise.shiftl(size, 4), TTypeToCompactType[etype]))
+ else
+ self:writeByte(libluabitwise.bor(0xf0, TTypeToCompactType[etype]))
+ self:writeVarint32(size)
+ end
+end
+
+function TCompactProtocol:writeVarint32(i32)
+ -- Should be utf-8
+ local str = libluabpack.toVarint32(i32)
+ self.trans:write(str)
+end
+
+function TCompactProtocol:writeVarint64(i64)
+ -- Should be utf-8
+ local str = libluabpack.toVarint64(i64)
+ self.trans:write(str)
+end
+
+function TCompactProtocol:readMessageBegin()
+ local protocolId = self:readSignByte()
+ if protocolId ~= self.COMPACT_PROTOCOL_ID then
+ terror(TProtocolException:new{
+ message = "Expected protocol id " .. self.COMPACT_PROTOCOL_ID .. " but got " .. protocolId})
+ end
+ local versionAndType = self:readSignByte()
+ local version = libluabitwise.band(versionAndType, self.COMPACT_VERSION_MASK)
+ local ttype = libluabitwise.band(libluabitwise.shiftr(versionAndType,
+ self.COMPACT_TYPE_SHIFT_AMOUNT), self.COMPACT_TYPE_BITS)
+ if version ~= self.COMPACT_VERSION then
+ terror(TProtocolException:new{
+ message = "Expected version " .. self.COMPACT_VERSION .. " but got " .. version})
+ end
+ local seqid = self:readVarint32()
+ local name = self:readString()
+ return name, ttype, seqid
+end
+
+function TCompactProtocol:readMessageEnd()
+end
+
+function TCompactProtocol:readStructBegin()
+ self.lastField[self.lastFieldIndex] = self.lastFieldId
+ self.lastFieldIndex = self.lastFieldIndex + 1
+ self.lastFieldId = 0
+ return nil
+end
+
+function TCompactProtocol:readStructEnd()
+ self.lastFieldIndex = self.lastFieldIndex - 1
+ self.lastFieldId = self.lastField[self.lastFieldIndex]
+end
+
+function TCompactProtocol:readFieldBegin()
+ local field_and_ttype = self:readSignByte()
+ local ttype = self:getTType(field_and_ttype)
+ if ttype == TType.STOP then
+ return nil, ttype, 0
+ end
+ -- mask off the 4 MSB of the type header. it could contain a field id delta.
+ local modifier = libluabitwise.shiftr(libluabitwise.band(field_and_ttype, 0xf0), 4)
+ local id = 0
+ if modifier == 0 then
+ id = self:readI16()
+ else
+ id = self.lastFieldId + modifier
+ end
+ if ttype == TType.BOOL then
+ boolValue = libluabitwise.band(field_and_ttype, 0x0f) == TCompactType.COMPACT_BOOLEAN_TRUE
+ boolValueIsNotNull = true
+ end
+ self.lastFieldId = id
+ return nil, ttype, id
+end
+
+function TCompactProtocol:readFieldEnd()
+end
+
+function TCompactProtocol:readMapBegin()
+ local size = self:readVarint32()
+ if size < 0 then
+ return nil,nil,nil
+ end
+ local kvtype = self:readSignByte()
+ local ktype = self:getTType(libluabitwise.shiftr(kvtype, 4))
+ local vtype = self:getTType(kvtype)
+ return ktype, vtype, size
+end
+
+function TCompactProtocol:readMapEnd()
+end
+
+function TCompactProtocol:readListBegin()
+ local size_and_type = self:readSignByte()
+ local size = libluabitwise.band(libluabitwise.shiftr(size_and_type, 4), 0x0f)
+ if size == 15 then
+ size = self:readVarint32()
+ end
+ if size < 0 then
+ return nil,nil
+ end
+ local etype = self:getTType(libluabitwise.band(size_and_type, 0x0f))
+ return etype, size
+end
+
+function TCompactProtocol:readListEnd()
+end
+
+function TCompactProtocol:readSetBegin()
+ return self:readListBegin()
+end
+
+function TCompactProtocol:readSetEnd()
+end
+
+function TCompactProtocol:readBool()
+ if boolValueIsNotNull then
+ boolValueIsNotNull = true
+ return boolValue
+ end
+ local val = self:readSignByte()
+ if val == TCompactType.COMPACT_BOOLEAN_TRUE then
+ return true
+ end
+ return false
+end
+
+function TCompactProtocol:readByte()
+ local buff = self.trans:readAll(1)
+ local val = libluabpack.bunpack('c', buff)
+ return val
+end
+
+function TCompactProtocol:readSignByte()
+ local buff = self.trans:readAll(1)
+ local val = libluabpack.bunpack('C', buff)
+ return val
+end
+
+function TCompactProtocol:readI16()
+ return self:readI32()
+end
+
+function TCompactProtocol:readI32()
+ local v = self:readVarint32()
+ local value = libluabpack.zigzagToI32(v)
+ return value
+end
+
+function TCompactProtocol:readI64()
+ local value = self:readVarint64()
+ return value
+end
+
+function TCompactProtocol:readDouble()
+ local buff = self.trans:readAll(8)
+ local val = libluabpack.bunpack('d', buff)
+ return val
+end
+
+function TCompactProtocol:readString()
+ return self:readBinary()
+end
+
+function TCompactProtocol:readBinary()
+ local size = self:readVarint32()
+ if size <= 0 then
+ return ""
+ end
+ return self.trans:readAll(size)
+end
+
+function TCompactProtocol:readVarint32()
+ local shiftl = 0
+ local result = 0
+ while true do
+ b = self:readByte()
+ result = libluabitwise.bor(result,
+ libluabitwise.shiftl(libluabitwise.band(b, 0x7f), shiftl))
+ if libluabitwise.band(b, 0x80) ~= 0x80 then
+ break
+ end
+ shiftl = shiftl + 7
+ end
+ return result
+end
+
+function TCompactProtocol:readVarint64()
+ local result = liblualongnumber.new
+ local data = result(0)
+ local shiftl = 0
+ while true do
+ b = self:readByte()
+ endFlag, data = libluabpack.fromVarint64(b, shiftl, data)
+ shiftl = shiftl + 7
+ if endFlag == 0 then
+ break
+ end
+ end
+ return data
+end
+
+function TCompactProtocol:getTType(ctype)
+ return CompactTypeToTType[libluabitwise.band(ctype, 0x0f)]
+end
+
+TCompactProtocolFactory = TProtocolFactory:new{
+ __type = 'TCompactProtocolFactory',
+}
+
+function TCompactProtocolFactory:getProtocol(trans)
+ -- TODO Enforce that this must be a transport class (ie not a bool)
+ if not trans then
+ terror(TProtocolException:new{
+ message = 'Must supply a transport to ' .. ttype(self)
+ })
+ end
+ return TCompactProtocol:new{
+ trans = trans
+ }
+end
diff --git a/src/jaegertracing/thrift/lib/lua/TFramedTransport.lua b/src/jaegertracing/thrift/lib/lua/TFramedTransport.lua
new file mode 100644
index 000000000..768e2d997
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/TFramedTransport.lua
@@ -0,0 +1,118 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+require 'TTransport'
+require 'libluabpack'
+
+TFramedTransport = TTransportBase:new{
+ __type = 'TFramedTransport',
+ doRead = true,
+ doWrite = true,
+ wBuf = '',
+ rBuf = ''
+}
+
+function TFramedTransport:new(obj)
+ if ttype(obj) ~= 'table' then
+ error(ttype(self) .. 'must be initialized with a table')
+ end
+
+ -- Ensure a transport is provided
+ if not obj.trans then
+ error('You must provide ' .. ttype(self) .. ' with a trans')
+ end
+
+ return TTransportBase.new(self, obj)
+end
+
+function TFramedTransport:isOpen()
+ return self.trans:isOpen()
+end
+
+function TFramedTransport:open()
+ return self.trans:open()
+end
+
+function TFramedTransport:close()
+ return self.trans:close()
+end
+
+function TFramedTransport:read(len)
+ if string.len(self.rBuf) == 0 then
+ self:__readFrame()
+ end
+
+ if self.doRead == false then
+ return self.trans:read(len)
+ end
+
+ if len > string.len(self.rBuf) then
+ local val = self.rBuf
+ self.rBuf = ''
+ return val
+ end
+
+ local val = string.sub(self.rBuf, 0, len)
+ self.rBuf = string.sub(self.rBuf, len+1)
+ return val
+end
+
+function TFramedTransport:__readFrame()
+ local buf = self.trans:readAll(4)
+ local frame_len = libluabpack.bunpack('i', buf)
+ self.rBuf = self.trans:readAll(frame_len)
+end
+
+
+function TFramedTransport:write(buf, len)
+ if self.doWrite == false then
+ return self.trans:write(buf, len)
+ end
+
+ if len and len < string.len(buf) then
+ buf = string.sub(buf, 0, len)
+ end
+ self.wBuf = self.wBuf .. buf
+end
+
+function TFramedTransport:flush()
+ if self.doWrite == false then
+ return self.trans:flush()
+ end
+
+ -- If the write fails we still want wBuf to be clear
+ local tmp = self.wBuf
+ self.wBuf = ''
+ local frame_len_buf = libluabpack.bpack("i", string.len(tmp))
+ tmp = frame_len_buf .. tmp
+ self.trans:write(tmp)
+ self.trans:flush()
+end
+
+TFramedTransportFactory = TTransportFactoryBase:new{
+ __type = 'TFramedTransportFactory'
+}
+function TFramedTransportFactory:getTransport(trans)
+ if not trans then
+ terror(TProtocolException:new{
+ message = 'Must supply a transport to ' .. ttype(self)
+ })
+ end
+ return TFramedTransport:new{trans = trans}
+end
diff --git a/src/jaegertracing/thrift/lib/lua/THttpTransport.lua b/src/jaegertracing/thrift/lib/lua/THttpTransport.lua
new file mode 100644
index 000000000..2951db79f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/THttpTransport.lua
@@ -0,0 +1,182 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+require 'TTransport'
+
+THttpTransport = TTransportBase:new{
+ __type = 'THttpTransport',
+ path = '/',
+ wBuf = '',
+ rBuf = '',
+ CRLF = '\r\n',
+ VERSION = version,
+ isServer = true
+}
+
+function THttpTransport:new(obj)
+ if ttype(obj) ~= 'table' then
+ error(ttype(self) .. 'must be initialized with a table')
+ end
+
+ -- Ensure a transport is provided
+ if not obj.trans then
+ error('You must provide ' .. ttype(self) .. ' with a trans')
+ end
+
+ return TTransportBase.new(self, obj)
+end
+
+function THttpTransport:isOpen()
+ return self.trans:isOpen()
+end
+
+function THttpTransport:open()
+ return self.trans:open()
+end
+
+function THttpTransport:close()
+ return self.trans:close()
+end
+
+function THttpTransport:readAll(len)
+ return self:read(len)
+end
+
+function THttpTransport:read(len)
+ if string.len(self.rBuf) == 0 then
+ self:_readMsg()
+ end
+ if len > string.len(self.rBuf) then
+ local val = self.rBuf
+ self.rBuf = ''
+ return val
+ end
+
+ local val = string.sub(self.rBuf, 0, len)
+ self.rBuf = string.sub(self.rBuf, len+1)
+ return val
+end
+
+function THttpTransport:_readMsg()
+ while true do
+ self.rBuf = self.rBuf .. self.trans:read(4)
+ if string.find(self.rBuf, self.CRLF .. self.CRLF) then
+ break
+ end
+ end
+ if not self.rBuf then
+ self.rBuf = ""
+ return
+ end
+ self:getLine()
+ local headers = self:_parseHeaders()
+ if not headers then
+ self.rBuf = ""
+ return
+ end
+
+ local length = tonumber(headers["Content-Length"])
+ if length then
+ length = length - string.len(self.rBuf)
+ self.rBuf = self.rBuf .. self.trans:readAll(length)
+ end
+ if self.rBuf == nil then
+ self.rBuf = ""
+ end
+end
+
+function THttpTransport:getLine()
+ local a,b = string.find(self.rBuf, self.CRLF)
+ local line = ""
+ if a and b then
+ line = string.sub(self.rBuf, 0, a-1)
+ self.rBuf = string.sub(self.rBuf, b+1)
+ end
+ return line
+end
+
+function THttpTransport:_parseHeaders()
+ local headers = {}
+
+ repeat
+ local line = self:getLine()
+ for key, val in string.gmatch(line, "([%w%-]+)%s*:%s*(.+)") do
+ if headers[key] then
+ local delimiter = ", "
+ if key == "Set-Cookie" then
+ delimiter = "; "
+ end
+ headers[key] = headers[key] .. delimiter .. tostring(val)
+ else
+ headers[key] = tostring(val)
+ end
+ end
+ until string.find(line, "^%s*$")
+
+ return headers
+end
+
+function THttpTransport:write(buf, len)
+ if len and len < string.len(buf) then
+ buf = string.sub(buf, 0, len)
+ end
+ self.wBuf = self.wBuf .. buf
+end
+
+function THttpTransport:writeHttpHeader(content_len)
+ if self.isServer then
+ local header = "HTTP/1.1 200 OK" .. self.CRLF
+ .. "Server: Thrift/" .. self.VERSION .. self.CRLF
+ .. "Access-Control-Allow-Origin: *" .. self.CRLF
+ .. "Content-Type: application/x-thrift" .. self.CRLF
+ .. "Content-Length: " .. content_len .. self.CRLF
+ .. "Connection: Keep-Alive" .. self.CRLF .. self.CRLF
+ self.trans:write(header)
+ else
+ local header = "POST " .. self.path .. " HTTP/1.1" .. self.CRLF
+ .. "Host: " .. self.trans.host .. self.CRLF
+ .. "Content-Type: application/x-thrift" .. self.CRLF
+ .. "Content-Length: " .. content_len .. self.CRLF
+ .. "Accept: application/x-thrift " .. self.CRLF
+ .. "User-Agent: Thrift/" .. self.VERSION .. " (Lua/THttpClient)"
+ .. self.CRLF .. self.CRLF
+ self.trans:write(header)
+ end
+end
+
+function THttpTransport:flush()
+ -- If the write fails we still want wBuf to be clear
+ local tmp = self.wBuf
+ self.wBuf = ''
+ self:writeHttpHeader(string.len(tmp))
+ self.trans:write(tmp)
+ self.trans:flush()
+end
+
+THttpTransportFactory = TTransportFactoryBase:new{
+ __type = 'THttpTransportFactory'
+}
+function THttpTransportFactory:getTransport(trans)
+ if not trans then
+ terror(TProtocolException:new{
+ message = 'Must supply a transport to ' .. ttype(self)
+ })
+ end
+ return THttpTransport:new{trans = trans}
+end
diff --git a/src/jaegertracing/thrift/lib/lua/TJsonProtocol.lua b/src/jaegertracing/thrift/lib/lua/TJsonProtocol.lua
new file mode 100644
index 000000000..db08eecf1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/TJsonProtocol.lua
@@ -0,0 +1,727 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"), you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+require 'TProtocol'
+require 'libluabpack'
+require 'libluabitwise'
+
+TJSONProtocol = __TObject.new(TProtocolBase, {
+ __type = 'TJSONProtocol',
+ THRIFT_JSON_PROTOCOL_VERSION = 1,
+ jsonContext = {},
+ jsonContextVal = {first = true, colon = true, ttype = 2, null = true},
+ jsonContextIndex = 1,
+ hasReadByte = ""
+})
+
+TTypeToString = {}
+TTypeToString[TType.BOOL] = "tf"
+TTypeToString[TType.BYTE] = "i8"
+TTypeToString[TType.I16] = "i16"
+TTypeToString[TType.I32] = "i32"
+TTypeToString[TType.I64] = "i64"
+TTypeToString[TType.DOUBLE] = "dbl"
+TTypeToString[TType.STRING] = "str"
+TTypeToString[TType.STRUCT] = "rec"
+TTypeToString[TType.LIST] = "lst"
+TTypeToString[TType.SET] = "set"
+TTypeToString[TType.MAP] = "map"
+
+StringToTType = {
+ tf = TType.BOOL,
+ i8 = TType.BYTE,
+ i16 = TType.I16,
+ i32 = TType.I32,
+ i64 = TType.I64,
+ dbl = TType.DOUBLE,
+ str = TType.STRING,
+ rec = TType.STRUCT,
+ map = TType.MAP,
+ set = TType.SET,
+ lst = TType.LIST
+}
+
+JSONNode = {
+ ObjectBegin = '{',
+ ObjectEnd = '}',
+ ArrayBegin = '[',
+ ArrayEnd = ']',
+ PairSeparator = ':',
+ ElemSeparator = ',',
+ Backslash = '\\',
+ StringDelimiter = '"',
+ ZeroChar = '0',
+ EscapeChar = 'u',
+ Nan = 'NaN',
+ Infinity = 'Infinity',
+ NegativeInfinity = '-Infinity',
+ EscapeChars = "\"\\bfnrt",
+ EscapePrefix = "\\u00"
+}
+
+EscapeCharVals = {
+ '"', '\\', '\b', '\f', '\n', '\r', '\t'
+}
+
+JSONCharTable = {
+ --0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 98,116,110, 0,102,114, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1,34, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+}
+
+-- character table string
+local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+
+-- encoding
+function base64_encode(data)
+ return ((data:gsub('.', function(x)
+ local r,b='',x:byte()
+ for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
+ return r;
+ end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
+ if (#x < 6) then return '' end
+ local c=0
+ for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
+ return b:sub(c+1,c+1)
+ end)..({ '', '==', '=' })[#data%3+1])
+end
+
+-- decoding
+function base64_decode(data)
+ data = string.gsub(data, '[^'..b..'=]', '')
+ return (data:gsub('.', function(x)
+ if (x == '=') then return '' end
+ local r,f='',(b:find(x)-1)
+ for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
+ return r;
+ end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
+ if (#x ~= 8) then return '' end
+ local c=0
+ for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
+ return string.char(c)
+ end))
+end
+
+function TJSONProtocol:resetContext()
+ self.jsonContext = {}
+ self.jsonContextVal = {first = true, colon = true, ttype = 2, null = true}
+ self.jsonContextIndex = 1
+end
+
+function TJSONProtocol:contextPush(context)
+ self.jsonContextIndex = self.jsonContextIndex + 1
+ self.jsonContext[self.jsonContextIndex] = self.jsonContextVal
+ self.jsonContextVal = context
+end
+
+function TJSONProtocol:contextPop()
+ self.jsonContextVal = self.jsonContext[self.jsonContextIndex]
+ self.jsonContextIndex = self.jsonContextIndex - 1
+end
+
+function TJSONProtocol:escapeNum()
+ if self.jsonContextVal.ttype == 1 then
+ return self.jsonContextVal.colon
+ else
+ return false
+ end
+end
+
+function TJSONProtocol:writeElemSeparator()
+ if self.jsonContextVal.null then
+ return
+ end
+ if self.jsonContextVal.first then
+ self.jsonContextVal.first = false
+ else
+ if self.jsonContextVal.ttype == 1 then
+ if self.jsonContextVal.colon then
+ self.trans:write(JSONNode.PairSeparator)
+ self.jsonContextVal.colon = false
+ else
+ self.trans:write(JSONNode.ElemSeparator)
+ self.jsonContextVal.colon = true
+ end
+ else
+ self.trans:write(JSONNode.ElemSeparator)
+ end
+ end
+end
+
+function TJSONProtocol:hexChar(val)
+ val = libluabitwise.band(val, 0x0f)
+ if val < 10 then
+ return val + 48
+ else
+ return val + 87
+ end
+end
+
+function TJSONProtocol:writeJSONEscapeChar(ch)
+ self.trans:write(JSONNode.EscapePrefix)
+ local outCh = hexChar(libluabitwise.shiftr(ch, 4))
+ local buff = libluabpack.bpack('c', outCh)
+ self.trans:write(buff)
+ outCh = hexChar(ch)
+ buff = libluabpack.bpack('c', outCh)
+ self.trans:write(buff)
+end
+
+function TJSONProtocol:writeJSONChar(byte)
+ ch = string.byte(byte)
+ if ch >= 0x30 then
+ if ch == JSONNode.Backslash then
+ self.trans:write(JSONNode.Backslash)
+ self.trans:write(JSONNode.Backslash)
+ else
+ self.trans:write(byte)
+ end
+ else
+ local outCh = JSONCharTable[ch+1]
+ if outCh == 1 then
+ self.trans:write(byte)
+ elseif outCh > 1 then
+ self.trans:write(JSONNode.Backslash)
+ local buff = libluabpack.bpack('c', outCh)
+ self.trans:write(buff)
+ else
+ self:writeJSONEscapeChar(ch)
+ end
+ end
+end
+
+function TJSONProtocol:writeJSONString(str)
+ self:writeElemSeparator()
+ self.trans:write(JSONNode.StringDelimiter)
+ -- TODO escape special characters
+ local length = string.len(str)
+ local ii = 1
+ while ii <= length do
+ self:writeJSONChar(string.sub(str, ii, ii))
+ ii = ii + 1
+ end
+ self.trans:write(JSONNode.StringDelimiter)
+end
+
+function TJSONProtocol:writeJSONBase64(str)
+ self:writeElemSeparator()
+ self.trans:write(JSONNode.StringDelimiter)
+ local length = string.len(str)
+ local offset = 1
+ while length >= 3 do
+ -- Encode 3 bytes at a time
+ local bytes = base64_encode(string.sub(str, offset, offset+3))
+ self.trans:write(bytes)
+ length = length - 3
+ offset = offset + 3
+ end
+ if length > 0 then
+ local bytes = base64_encode(string.sub(str, offset, offset+length))
+ self.trans:write(bytes)
+ end
+ self.trans:write(JSONNode.StringDelimiter)
+end
+
+function TJSONProtocol:writeJSONInteger(num)
+ self:writeElemSeparator()
+ if self:escapeNum() then
+ self.trans:write(JSONNode.StringDelimiter)
+ end
+ local numstr = "" .. num
+ numstr = string.sub(numstr, string.find(numstr, "^[+-]?%d+"))
+ self.trans:write(numstr)
+ if self:escapeNum() then
+ self.trans:write(JSONNode.StringDelimiter)
+ end
+end
+
+function TJSONProtocol:writeJSONDouble(dub)
+ self:writeElemSeparator()
+ local val = "" .. dub
+ local prefix = string.sub(val, 1, 1)
+ local special = false
+ if prefix == 'N' or prefix == 'n' then
+ val = JSONNode.Nan
+ special = true
+ elseif prefix == 'I' or prefix == 'i' then
+ val = JSONNode.Infinity
+ special = true
+ elseif prefix == '-' then
+ local secondByte = string.sub(val, 2, 2)
+ if secondByte == 'I' or secondByte == 'i' then
+ val = JSONNode.NegativeInfinity
+ special = true
+ end
+ end
+
+ if special or self:escapeNum() then
+ self.trans:write(JSONNode.StringDelimiter)
+ end
+ self.trans:write(val)
+ if special or self:escapeNum() then
+ self.trans:write(JSONNode.StringDelimiter)
+ end
+end
+
+function TJSONProtocol:writeJSONObjectBegin()
+ self:writeElemSeparator()
+ self.trans:write(JSONNode.ObjectBegin)
+ self:contextPush({first = true, colon = true, ttype = 1, null = false})
+end
+
+function TJSONProtocol:writeJSONObjectEnd()
+ self:contextPop()
+ self.trans:write(JSONNode.ObjectEnd)
+end
+
+function TJSONProtocol:writeJSONArrayBegin()
+ self:writeElemSeparator()
+ self.trans:write(JSONNode.ArrayBegin)
+ self:contextPush({first = true, colon = true, ttype = 2, null = false})
+end
+
+function TJSONProtocol:writeJSONArrayEnd()
+ self:contextPop()
+ self.trans:write(JSONNode.ArrayEnd)
+end
+
+function TJSONProtocol:writeMessageBegin(name, ttype, seqid)
+ self:resetContext()
+ self:writeJSONArrayBegin()
+ self:writeJSONInteger(TJSONProtocol.THRIFT_JSON_PROTOCOL_VERSION)
+ self:writeJSONString(name)
+ self:writeJSONInteger(ttype)
+ self:writeJSONInteger(seqid)
+end
+
+function TJSONProtocol:writeMessageEnd()
+ self:writeJSONArrayEnd()
+end
+
+function TJSONProtocol:writeStructBegin(name)
+ self:writeJSONObjectBegin()
+end
+
+function TJSONProtocol:writeStructEnd()
+ self:writeJSONObjectEnd()
+end
+
+function TJSONProtocol:writeFieldBegin(name, ttype, id)
+ self:writeJSONInteger(id)
+ self:writeJSONObjectBegin()
+ self:writeJSONString(TTypeToString[ttype])
+end
+
+function TJSONProtocol:writeFieldEnd()
+ self:writeJSONObjectEnd()
+end
+
+function TJSONProtocol:writeFieldStop()
+end
+
+function TJSONProtocol:writeMapBegin(ktype, vtype, size)
+ self:writeJSONArrayBegin()
+ self:writeJSONString(TTypeToString[ktype])
+ self:writeJSONString(TTypeToString[vtype])
+ self:writeJSONInteger(size)
+ return self:writeJSONObjectBegin()
+end
+
+function TJSONProtocol:writeMapEnd()
+ self:writeJSONObjectEnd()
+ self:writeJSONArrayEnd()
+end
+
+function TJSONProtocol:writeListBegin(etype, size)
+ self:writeJSONArrayBegin()
+ self:writeJSONString(TTypeToString[etype])
+ self:writeJSONInteger(size)
+end
+
+function TJSONProtocol:writeListEnd()
+ self:writeJSONArrayEnd()
+end
+
+function TJSONProtocol:writeSetBegin(etype, size)
+ self:writeJSONArrayBegin()
+ self:writeJSONString(TTypeToString[etype])
+ self:writeJSONInteger(size)
+end
+
+function TJSONProtocol:writeSetEnd()
+ self:writeJSONArrayEnd()
+end
+
+function TJSONProtocol:writeBool(bool)
+ if bool then
+ self:writeJSONInteger(1)
+ else
+ self:writeJSONInteger(0)
+ end
+end
+
+function TJSONProtocol:writeByte(byte)
+ local buff = libluabpack.bpack('c', byte)
+ local val = libluabpack.bunpack('c', buff)
+ self:writeJSONInteger(val)
+end
+
+function TJSONProtocol:writeI16(i16)
+ local buff = libluabpack.bpack('s', i16)
+ local val = libluabpack.bunpack('s', buff)
+ self:writeJSONInteger(val)
+end
+
+function TJSONProtocol:writeI32(i32)
+ local buff = libluabpack.bpack('i', i32)
+ local val = libluabpack.bunpack('i', buff)
+ self:writeJSONInteger(val)
+end
+
+function TJSONProtocol:writeI64(i64)
+ local buff = libluabpack.bpack('l', i64)
+ local val = libluabpack.bunpack('l', buff)
+ self:writeJSONInteger(tostring(val))
+end
+
+function TJSONProtocol:writeDouble(dub)
+ self:writeJSONDouble(string.format("%.16f", dub))
+end
+
+function TJSONProtocol:writeString(str)
+ self:writeJSONString(str)
+end
+
+function TJSONProtocol:writeBinary(str)
+ -- Should be utf-8
+ self:writeJSONBase64(str)
+end
+
+function TJSONProtocol:readJSONSyntaxChar(ch)
+ local ch2 = ""
+ if self.hasReadByte ~= "" then
+ ch2 = self.hasReadByte
+ self.hasReadByte = ""
+ else
+ ch2 = self.trans:readAll(1)
+ end
+ if ch2 ~= ch then
+ terror(TProtocolException:new{message = "Expected ".. ch .. ", got " .. ch2})
+ end
+end
+
+function TJSONProtocol:readElemSeparator()
+ if self.jsonContextVal.null then
+ return
+ end
+ if self.jsonContextVal.first then
+ self.jsonContextVal.first = false
+ else
+ if self.jsonContextVal.ttype == 1 then
+ if self.jsonContextVal.colon then
+ self:readJSONSyntaxChar(JSONNode.PairSeparator)
+ self.jsonContextVal.colon = false
+ else
+ self:readJSONSyntaxChar(JSONNode.ElemSeparator)
+ self.jsonContextVal.colon = true
+ end
+ else
+ self:readJSONSyntaxChar(JSONNode.ElemSeparator)
+ end
+ end
+end
+
+function TJSONProtocol:hexVal(ch)
+ local val = string.byte(ch)
+ if val >= 48 and val <= 57 then
+ return val - 48
+ elseif val >= 97 and val <= 102 then
+ return val - 87
+ else
+ terror(TProtocolException:new{message = "Expected hex val ([0-9a-f]); got " .. ch})
+ end
+end
+
+function TJSONProtocol:readJSONEscapeChar(ch)
+ self:readJSONSyntaxChar(JSONNode.ZeroChar)
+ self:readJSONSyntaxChar(JSONNode.ZeroChar)
+ local b1 = self.trans:readAll(1)
+ local b2 = self.trans:readAll(1)
+ return libluabitwise.shiftl(self:hexVal(b1), 4) + self:hexVal(b2)
+end
+
+
+function TJSONProtocol:readJSONString()
+ self:readElemSeparator()
+ self:readJSONSyntaxChar(JSONNode.StringDelimiter)
+ local result = ""
+ while true do
+ local ch = self.trans:readAll(1)
+ if ch == JSONNode.StringDelimiter then
+ break
+ end
+ if ch == JSONNode.Backslash then
+ ch = self.trans:readAll(1)
+ if ch == JSONNode.EscapeChar then
+ self:readJSONEscapeChar(ch)
+ else
+ local pos, _ = string.find(JSONNode.EscapeChars, ch)
+ if pos == nil then
+ terror(TProtocolException:new{message = "Expected control char, got " .. ch})
+ end
+ ch = EscapeCharVals[pos]
+ end
+ end
+ result = result .. ch
+ end
+ return result
+end
+
+function TJSONProtocol:readJSONBase64()
+ local result = self:readJSONString()
+ local length = string.len(result)
+ local str = ""
+ local offset = 1
+ while length >= 4 do
+ local bytes = string.sub(result, offset, offset+4)
+ str = str .. base64_decode(bytes)
+ offset = offset + 4
+ length = length - 4
+ end
+ if length >= 0 then
+ str = str .. base64_decode(string.sub(result, offset, offset + length))
+ end
+ return str
+end
+
+function TJSONProtocol:readJSONNumericChars()
+ local result = ""
+ while true do
+ local ch = self.trans:readAll(1)
+ if string.find(ch, '[-+0-9.Ee]') then
+ result = result .. ch
+ else
+ self.hasReadByte = ch
+ break
+ end
+ end
+ return result
+end
+
+function TJSONProtocol:readJSONLongInteger()
+ self:readElemSeparator()
+ if self:escapeNum() then
+ self:readJSONSyntaxChar(JSONNode.StringDelimiter)
+ end
+ local result = self:readJSONNumericChars()
+ if self:escapeNum() then
+ self:readJSONSyntaxChar(JSONNode.StringDelimiter)
+ end
+ return result
+end
+
+function TJSONProtocol:readJSONInteger()
+ return tonumber(self:readJSONLongInteger())
+end
+
+function TJSONProtocol:readJSONDouble()
+ self:readElemSeparator()
+ local delimiter = self.trans:readAll(1)
+ local num = 0.0
+ if delimiter == JSONNode.StringDelimiter then
+ local str = self:readJSONString()
+ if str == JSONNode.Nan then
+ num = 1.0
+ elseif str == JSONNode.Infinity then
+ num = math.maxinteger
+ elseif str == JSONNode.NegativeInfinity then
+ num = math.mininteger
+ else
+ num = tonumber(str)
+ end
+ else
+ if self:escapeNum() then
+ self:readJSONSyntaxChar(JSONNode.StringDelimiter)
+ end
+ local result = self:readJSONNumericChars()
+ num = tonumber(delimiter.. result)
+ end
+ return num
+end
+
+function TJSONProtocol:readJSONObjectBegin()
+ self:readElemSeparator()
+ self:readJSONSyntaxChar(JSONNode.ObjectBegin)
+ self:contextPush({first = true, colon = true, ttype = 1, null = false})
+end
+
+function TJSONProtocol:readJSONObjectEnd()
+ self:readJSONSyntaxChar(JSONNode.ObjectEnd)
+ self:contextPop()
+end
+
+function TJSONProtocol:readJSONArrayBegin()
+ self:readElemSeparator()
+ self:readJSONSyntaxChar(JSONNode.ArrayBegin)
+ self:contextPush({first = true, colon = true, ttype = 2, null = false})
+end
+
+function TJSONProtocol:readJSONArrayEnd()
+ self:readJSONSyntaxChar(JSONNode.ArrayEnd)
+ self:contextPop()
+end
+
+function TJSONProtocol:readMessageBegin()
+ self:resetContext()
+ self:readJSONArrayBegin()
+ local version = self:readJSONInteger()
+ if version ~= self.THRIFT_JSON_PROTOCOL_VERSION then
+ terror(TProtocolException:new{message = "Message contained bad version."})
+ end
+ local name = self:readJSONString()
+ local ttype = self:readJSONInteger()
+ local seqid = self:readJSONInteger()
+ return name, ttype, seqid
+end
+
+function TJSONProtocol:readMessageEnd()
+ self:readJSONArrayEnd()
+end
+
+function TJSONProtocol:readStructBegin()
+ self:readJSONObjectBegin()
+ return nil
+end
+
+function TJSONProtocol:readStructEnd()
+ self:readJSONObjectEnd()
+end
+
+function TJSONProtocol:readFieldBegin()
+ local ttype = TType.STOP
+ local id = 0
+ local ch = self.trans:readAll(1)
+ self.hasReadByte = ch
+ if ch ~= JSONNode.ObjectEnd then
+ id = self:readJSONInteger()
+ self:readJSONObjectBegin()
+ local typeName = self:readJSONString()
+ ttype = StringToTType[typeName]
+ end
+ return nil, ttype, id
+end
+
+function TJSONProtocol:readFieldEnd()
+ self:readJSONObjectEnd()
+end
+
+function TJSONProtocol:readMapBegin()
+ self:readJSONArrayBegin()
+ local typeName = self:readJSONString()
+ local ktype = StringToTType[typeName]
+ typeName = self:readJSONString()
+ local vtype = StringToTType[typeName]
+ local size = self:readJSONInteger()
+ self:readJSONObjectBegin()
+ return ktype, vtype, size
+end
+
+function TJSONProtocol:readMapEnd()
+ self:readJSONObjectEnd()
+ self:readJSONArrayEnd()
+end
+
+function TJSONProtocol:readListBegin()
+ self:readJSONArrayBegin()
+ local typeName = self:readJSONString()
+ local etype = StringToTType[typeName]
+ local size = self:readJSONInteger()
+ return etype, size
+end
+
+function TJSONProtocol:readListEnd()
+ return self:readJSONArrayEnd()
+end
+
+function TJSONProtocol:readSetBegin()
+ return self:readListBegin()
+end
+
+function TJSONProtocol:readSetEnd()
+ return self:readJSONArrayEnd()
+end
+
+function TJSONProtocol:readBool()
+ local result = self:readJSONInteger()
+ if result == 1 then
+ return true
+ else
+ return false
+ end
+end
+
+function TJSONProtocol:readByte()
+ local result = self:readJSONInteger()
+ if result >= 256 then
+ terror(TProtocolException:new{message = "UnExpected Byte " .. result})
+ end
+ return result
+end
+
+function TJSONProtocol:readI16()
+ return self:readJSONInteger()
+end
+
+function TJSONProtocol:readI32()
+ return self:readJSONInteger()
+end
+
+function TJSONProtocol:readI64()
+ local long = liblualongnumber.new
+ return long(self:readJSONLongInteger())
+end
+
+function TJSONProtocol:readDouble()
+ return self:readJSONDouble()
+end
+
+function TJSONProtocol:readString()
+ return self:readJSONString()
+end
+
+function TJSONProtocol:readBinary()
+ return self:readJSONBase64()
+end
+
+TJSONProtocolFactory = TProtocolFactory:new{
+ __type = 'TJSONProtocolFactory',
+}
+
+function TJSONProtocolFactory:getProtocol(trans)
+ -- TODO Enforce that this must be a transport class (ie not a bool)
+ if not trans then
+ terror(TProtocolException:new{
+ message = 'Must supply a transport to ' .. ttype(self)
+ })
+ end
+ return TJSONProtocol:new{
+ trans = trans
+ }
+end
diff --git a/src/jaegertracing/thrift/lib/lua/TMemoryBuffer.lua b/src/jaegertracing/thrift/lib/lua/TMemoryBuffer.lua
new file mode 100644
index 000000000..78b2f5cf0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/TMemoryBuffer.lua
@@ -0,0 +1,91 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+require 'TTransport'
+
+TMemoryBuffer = TTransportBase:new{
+ __type = 'TMemoryBuffer',
+ buffer = '',
+ bufferSize = 1024,
+ wPos = 0,
+ rPos = 0
+}
+function TMemoryBuffer:isOpen()
+ return 1
+end
+function TMemoryBuffer:open() end
+function TMemoryBuffer:close() end
+
+function TMemoryBuffer:peak()
+ return self.rPos < self.wPos
+end
+
+function TMemoryBuffer:getBuffer()
+ return self.buffer
+end
+
+function TMemoryBuffer:resetBuffer(buf)
+ if buf then
+ self.buffer = buf
+ self.bufferSize = string.len(buf)
+ else
+ self.buffer = ''
+ self.bufferSize = 1024
+ end
+ self.wPos = string.len(buf)
+ self.rPos = 0
+end
+
+function TMemoryBuffer:available()
+ return self.wPos - self.rPos
+end
+
+function TMemoryBuffer:read(len)
+ local avail = self:available()
+ if avail == 0 then
+ return ''
+ end
+
+ if avail < len then
+ len = avail
+ end
+
+ local val = string.sub(self.buffer, self.rPos + 1, self.rPos + len)
+ self.rPos = self.rPos + len
+ return val
+end
+
+function TMemoryBuffer:readAll(len)
+ local avail = self:available()
+
+ if avail < len then
+ local msg = string.format('Attempt to readAll(%d) found only %d available',
+ len, avail)
+ terror(TTransportException:new{message = msg})
+ end
+ -- read should block so we don't need a loop here
+ return self:read(len)
+end
+
+function TMemoryBuffer:write(buf)
+ self.buffer = self.buffer .. buf
+ self.wPos = self.wPos + string.len(buf)
+end
+
+function TMemoryBuffer:flush() end
diff --git a/src/jaegertracing/thrift/lib/lua/TProtocol.lua b/src/jaegertracing/thrift/lib/lua/TProtocol.lua
new file mode 100644
index 000000000..1306fb3d8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/TProtocol.lua
@@ -0,0 +1,164 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+require 'Thrift'
+
+TProtocolException = TException:new {
+ UNKNOWN = 0,
+ INVALID_DATA = 1,
+ NEGATIVE_SIZE = 2,
+ SIZE_LIMIT = 3,
+ BAD_VERSION = 4,
+ INVALID_PROTOCOL = 5,
+ DEPTH_LIMIT = 6,
+ errorCode = 0,
+ __type = 'TProtocolException'
+}
+function TProtocolException:__errorCodeToString()
+ if self.errorCode == self.INVALID_DATA then
+ return 'Invalid data'
+ elseif self.errorCode == self.NEGATIVE_SIZE then
+ return 'Negative size'
+ elseif self.errorCode == self.SIZE_LIMIT then
+ return 'Size limit'
+ elseif self.errorCode == self.BAD_VERSION then
+ return 'Bad version'
+ elseif self.errorCode == self.INVALID_PROTOCOL then
+ return 'Invalid protocol'
+ elseif self.errorCode == self.DEPTH_LIMIT then
+ return 'Exceeded size limit'
+ else
+ return 'Default (unknown)'
+ end
+end
+
+TProtocolBase = __TObject:new{
+ __type = 'TProtocolBase',
+ trans
+}
+
+function TProtocolBase:new(obj)
+ if ttype(obj) ~= 'table' then
+ error(ttype(self) .. 'must be initialized with a table')
+ end
+
+ -- Ensure a transport is provided
+ if not obj.trans then
+ error('You must provide ' .. ttype(self) .. ' with a trans')
+ end
+
+ return __TObject.new(self, obj)
+end
+
+function TProtocolBase:writeMessageBegin(name, ttype, seqid) end
+function TProtocolBase:writeMessageEnd() end
+function TProtocolBase:writeStructBegin(name) end
+function TProtocolBase:writeStructEnd() end
+function TProtocolBase:writeFieldBegin(name, ttype, id) end
+function TProtocolBase:writeFieldEnd() end
+function TProtocolBase:writeFieldStop() end
+function TProtocolBase:writeMapBegin(ktype, vtype, size) end
+function TProtocolBase:writeMapEnd() end
+function TProtocolBase:writeListBegin(ttype, size) end
+function TProtocolBase:writeListEnd() end
+function TProtocolBase:writeSetBegin(ttype, size) end
+function TProtocolBase:writeSetEnd() end
+function TProtocolBase:writeBool(bool) end
+function TProtocolBase:writeByte(byte) end
+function TProtocolBase:writeI16(i16) end
+function TProtocolBase:writeI32(i32) end
+function TProtocolBase:writeI64(i64) end
+function TProtocolBase:writeDouble(dub) end
+function TProtocolBase:writeString(str) end
+function TProtocolBase:readMessageBegin() end
+function TProtocolBase:readMessageEnd() end
+function TProtocolBase:readStructBegin() end
+function TProtocolBase:readStructEnd() end
+function TProtocolBase:readFieldBegin() end
+function TProtocolBase:readFieldEnd() end
+function TProtocolBase:readMapBegin() end
+function TProtocolBase:readMapEnd() end
+function TProtocolBase:readListBegin() end
+function TProtocolBase:readListEnd() end
+function TProtocolBase:readSetBegin() end
+function TProtocolBase:readSetEnd() end
+function TProtocolBase:readBool() end
+function TProtocolBase:readByte() end
+function TProtocolBase:readI16() end
+function TProtocolBase:readI32() end
+function TProtocolBase:readI64() end
+function TProtocolBase:readDouble() end
+function TProtocolBase:readString() end
+
+function TProtocolBase:skip(ttype)
+ if ttype == TType.BOOL then
+ self:readBool()
+ elseif ttype == TType.BYTE then
+ self:readByte()
+ elseif ttype == TType.I16 then
+ self:readI16()
+ elseif ttype == TType.I32 then
+ self:readI32()
+ elseif ttype == TType.I64 then
+ self:readI64()
+ elseif ttype == TType.DOUBLE then
+ self:readDouble()
+ elseif ttype == TType.STRING then
+ self:readString()
+ elseif ttype == TType.STRUCT then
+ local name = self:readStructBegin()
+ while true do
+ local name, ttype, id = self:readFieldBegin()
+ if ttype == TType.STOP then
+ break
+ end
+ self:skip(ttype)
+ self:readFieldEnd()
+ end
+ self:readStructEnd()
+ elseif ttype == TType.MAP then
+ local kttype, vttype, size = self:readMapBegin()
+ for i = 1, size, 1 do
+ self:skip(kttype)
+ self:skip(vttype)
+ end
+ self:readMapEnd()
+ elseif ttype == TType.SET then
+ local ettype, size = self:readSetBegin()
+ for i = 1, size, 1 do
+ self:skip(ettype)
+ end
+ self:readSetEnd()
+ elseif ttype == TType.LIST then
+ local ettype, size = self:readListBegin()
+ for i = 1, size, 1 do
+ self:skip(ettype)
+ end
+ self:readListEnd()
+ else
+ terror(TProtocolException:new{
+ message = 'Invalid data'
+ })
+ end
+end
+
+TProtocolFactory = __TObject:new{
+ __type = 'TProtocolFactory',
+}
+function TProtocolFactory:getProtocol(trans) end
diff --git a/src/jaegertracing/thrift/lib/lua/TServer.lua b/src/jaegertracing/thrift/lib/lua/TServer.lua
new file mode 100644
index 000000000..4e37d5871
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/TServer.lua
@@ -0,0 +1,140 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+require 'Thrift'
+require 'TFramedTransport'
+require 'TBinaryProtocol'
+
+-- TServer
+TServer = __TObject:new{
+ __type = 'TServer'
+}
+
+-- 2 possible constructors
+-- 1. {processor, serverTransport}
+-- 2. {processor, serverTransport, transportFactory, protocolFactory}
+function TServer:new(args)
+ if ttype(args) ~= 'table' then
+ error('TServer must be initialized with a table')
+ end
+ if args.processor == nil then
+ terror('You must provide ' .. ttype(self) .. ' with a processor')
+ end
+ if args.serverTransport == nil then
+ terror('You must provide ' .. ttype(self) .. ' with a serverTransport')
+ end
+
+ -- Create the object
+ local obj = __TObject.new(self, args)
+
+ if obj.transportFactory then
+ obj.inputTransportFactory = obj.transportFactory
+ obj.outputTransportFactory = obj.transportFactory
+ obj.transportFactory = nil
+ else
+ obj.inputTransportFactory = TFramedTransportFactory:new{}
+ obj.outputTransportFactory = obj.inputTransportFactory
+ end
+
+ if obj.protocolFactory then
+ obj.inputProtocolFactory = obj.protocolFactory
+ obj.outputProtocolFactory = obj.protocolFactory
+ obj.protocolFactory = nil
+ else
+ obj.inputProtocolFactory = TBinaryProtocolFactory:new{}
+ obj.outputProtocolFactory = obj.inputProtocolFactory
+ end
+
+ -- Set the __server variable in the handler so we can stop the server
+ obj.processor.handler.__server = self
+
+ return obj
+end
+
+function TServer:setServerEventHandler(handler)
+ self.serverEventHandler = handler
+end
+
+function TServer:_clientBegin(content, iprot, oprot)
+ if self.serverEventHandler and
+ type(self.serverEventHandler.clientBegin) == 'function' then
+ self.serverEventHandler:clientBegin(iprot, oprot)
+ end
+end
+
+function TServer:_preServe()
+ if self.serverEventHandler and
+ type(self.serverEventHandler.preServe) == 'function' then
+ self.serverEventHandler:preServe(self.serverTransport:getSocketInfo())
+ end
+end
+
+function TServer:_handleException(err)
+ if string.find(err, 'TTransportException') == nil then
+ print(err)
+ end
+end
+
+function TServer:serve() end
+function TServer:handle(client)
+ local itrans, otrans =
+ self.inputTransportFactory:getTransport(client),
+ self.outputTransportFactory:getTransport(client)
+ local iprot, oprot =
+ self.inputProtocolFactory:getProtocol(itrans),
+ self.outputProtocolFactory:getProtocol(otrans)
+
+ self:_clientBegin(iprot, oprot)
+ while true do
+ local ret, err = pcall(self.processor.process, self.processor, iprot, oprot)
+ if ret == false and err then
+ if not string.find(err, "TTransportException") then
+ self:_handleException(err)
+ end
+ break
+ end
+ end
+ itrans:close()
+ otrans:close()
+end
+
+function TServer:close()
+ self.serverTransport:close()
+end
+
+-- TSimpleServer
+-- Single threaded server that handles one transport (connection)
+TSimpleServer = __TObject:new(TServer, {
+ __type = 'TSimpleServer',
+ __stop = false
+})
+
+function TSimpleServer:serve()
+ self.serverTransport:listen()
+ self:_preServe()
+ while not self.__stop do
+ client = self.serverTransport:accept()
+ self:handle(client)
+ end
+ self:close()
+end
+
+function TSimpleServer:stop()
+ self.__stop = true
+end
diff --git a/src/jaegertracing/thrift/lib/lua/TSocket.lua b/src/jaegertracing/thrift/lib/lua/TSocket.lua
new file mode 100644
index 000000000..d71fc1f98
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/TSocket.lua
@@ -0,0 +1,132 @@
+---- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+require 'TTransport'
+require 'libluasocket'
+
+-- TSocketBase
+TSocketBase = TTransportBase:new{
+ __type = 'TSocketBase',
+ timeout = 1000,
+ host = 'localhost',
+ port = 9090,
+ handle
+}
+
+function TSocketBase:close()
+ if self.handle then
+ self.handle:destroy()
+ self.handle = nil
+ end
+end
+
+-- Returns a table with the fields host and port
+function TSocketBase:getSocketInfo()
+ if self.handle then
+ return self.handle:getsockinfo()
+ end
+ terror(TTransportException:new{errorCode = TTransportException.NOT_OPEN})
+end
+
+function TSocketBase:setTimeout(timeout)
+ if timeout and ttype(timeout) == 'number' then
+ if self.handle then
+ self.handle:settimeout(timeout)
+ end
+ self.timeout = timeout
+ end
+end
+
+-- TSocket
+TSocket = TSocketBase:new{
+ __type = 'TSocket',
+ host = 'localhost',
+ port = 9090
+}
+
+function TSocket:isOpen()
+ if self.handle then
+ return true
+ end
+ return false
+end
+
+function TSocket:open()
+ if self.handle then
+ self:close()
+ end
+
+ -- Create local handle
+ local sock, err = luasocket.create_and_connect(
+ self.host, self.port, self.timeout)
+ if err == nil then
+ self.handle = sock
+ end
+
+ if err then
+ terror(TTransportException:new{
+ message = 'Could not connect to ' .. self.host .. ':' .. self.port
+ .. ' (' .. err .. ')'
+ })
+ end
+end
+
+function TSocket:read(len)
+ local buf = self.handle:receive(self.handle, len)
+ if not buf or string.len(buf) ~= len then
+ terror(TTransportException:new{errorCode = TTransportException.UNKNOWN})
+ end
+ return buf
+end
+
+function TSocket:write(buf)
+ self.handle:send(self.handle, buf)
+end
+
+function TSocket:flush()
+end
+
+-- TServerSocket
+TServerSocket = TSocketBase:new{
+ __type = 'TServerSocket',
+ host = 'localhost',
+ port = 9090
+}
+
+function TServerSocket:listen()
+ if self.handle then
+ self:close()
+ end
+
+ local sock, err = luasocket.create(self.host, self.port)
+ if not err then
+ self.handle = sock
+ else
+ terror(err)
+ end
+ self.handle:settimeout(self.timeout)
+ self.handle:listen()
+end
+
+function TServerSocket:accept()
+ local client, err = self.handle:accept()
+ if err then
+ terror(err)
+ end
+ return TSocket:new({handle = client})
+end
diff --git a/src/jaegertracing/thrift/lib/lua/TTransport.lua b/src/jaegertracing/thrift/lib/lua/TTransport.lua
new file mode 100644
index 000000000..01c7e5979
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/TTransport.lua
@@ -0,0 +1,93 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+require 'Thrift'
+
+TTransportException = TException:new {
+ UNKNOWN = 0,
+ NOT_OPEN = 1,
+ ALREADY_OPEN = 2,
+ TIMED_OUT = 3,
+ END_OF_FILE = 4,
+ INVALID_FRAME_SIZE = 5,
+ INVALID_TRANSFORM = 6,
+ INVALID_CLIENT_TYPE = 7,
+ errorCode = 0,
+ __type = 'TTransportException'
+}
+
+function TTransportException:__errorCodeToString()
+ if self.errorCode == self.NOT_OPEN then
+ return 'Transport not open'
+ elseif self.errorCode == self.ALREADY_OPEN then
+ return 'Transport already open'
+ elseif self.errorCode == self.TIMED_OUT then
+ return 'Transport timed out'
+ elseif self.errorCode == self.END_OF_FILE then
+ return 'End of file'
+ elseif self.errorCode == self.INVALID_FRAME_SIZE then
+ return 'Invalid frame size'
+ elseif self.errorCode == self.INVALID_TRANSFORM then
+ return 'Invalid transform'
+ elseif self.errorCode == self.INVALID_CLIENT_TYPE then
+ return 'Invalid client type'
+ else
+ return 'Default (unknown)'
+ end
+end
+
+TTransportBase = __TObject:new{
+ __type = 'TTransportBase'
+}
+
+function TTransportBase:isOpen() end
+function TTransportBase:open() end
+function TTransportBase:close() end
+function TTransportBase:read(len) end
+function TTransportBase:readAll(len)
+ local buf, have, chunk = '', 0
+ while have < len do
+ chunk = self:read(len - have)
+ have = have + string.len(chunk)
+ buf = buf .. chunk
+
+ if string.len(chunk) == 0 then
+ terror(TTransportException:new{
+ errorCode = TTransportException.END_OF_FILE
+ })
+ end
+ end
+ return buf
+end
+function TTransportBase:write(buf) end
+function TTransportBase:flush() end
+
+TServerTransportBase = __TObject:new{
+ __type = 'TServerTransportBase'
+}
+function TServerTransportBase:listen() end
+function TServerTransportBase:accept() end
+function TServerTransportBase:close() end
+
+TTransportFactoryBase = __TObject:new{
+ __type = 'TTransportFactoryBase'
+}
+function TTransportFactoryBase:getTransport(trans)
+ return trans
+end
diff --git a/src/jaegertracing/thrift/lib/lua/Thrift.lua b/src/jaegertracing/thrift/lib/lua/Thrift.lua
new file mode 100644
index 000000000..a948b3dcb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/Thrift.lua
@@ -0,0 +1,281 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+---- namespace thrift
+--thrift = {}
+--setmetatable(thrift, {__index = _G}) --> perf hit for accessing global methods
+--setfenv(1, thrift)
+
+package.cpath = package.cpath .. ';bin/?.so' -- TODO FIX
+function ttype(obj)
+ if type(obj) == 'table' and
+ obj.__type and
+ type(obj.__type) == 'string' then
+ return obj.__type
+ end
+ return type(obj)
+end
+
+function terror(e)
+ if e and e.__tostring then
+ error(e:__tostring())
+ return
+ end
+ error(e)
+end
+
+function ttable_size(t)
+ local count = 0
+ for k, v in pairs(t) do
+ count = count + 1
+ end
+ return count
+end
+
+version = '0.13.0'
+
+TType = {
+ STOP = 0,
+ VOID = 1,
+ BOOL = 2,
+ BYTE = 3,
+ I08 = 3,
+ DOUBLE = 4,
+ I16 = 6,
+ I32 = 8,
+ I64 = 10,
+ STRING = 11,
+ UTF7 = 11,
+ STRUCT = 12,
+ MAP = 13,
+ SET = 14,
+ LIST = 15,
+ UTF8 = 16,
+ UTF16 = 17
+}
+
+TMessageType = {
+ CALL = 1,
+ REPLY = 2,
+ EXCEPTION = 3,
+ ONEWAY = 4
+}
+
+-- Recursive __index function to achieve inheritance
+function __tobj_index(self, key)
+ local v = rawget(self, key)
+ if v ~= nil then
+ return v
+ end
+
+ local p = rawget(self, '__parent')
+ if p then
+ return __tobj_index(p, key)
+ end
+
+ return nil
+end
+
+-- Basic Thrift-Lua Object
+__TObject = {
+ __type = '__TObject',
+ __mt = {
+ __index = __tobj_index
+ }
+}
+function __TObject:new(init_obj)
+ local obj = {}
+ if ttype(obj) == 'table' then
+ obj = init_obj
+ end
+
+ -- Use the __parent key and the __index function to achieve inheritance
+ obj.__parent = self
+ setmetatable(obj, __TObject.__mt)
+ return obj
+end
+
+-- Return a string representation of any lua variable
+function thrift_print_r(t)
+ local ret = ''
+ local ltype = type(t)
+ if (ltype == 'table') then
+ ret = ret .. '{ '
+ for key,value in pairs(t) do
+ ret = ret .. tostring(key) .. '=' .. thrift_print_r(value) .. ' '
+ end
+ ret = ret .. '}'
+ elseif ltype == 'string' then
+ ret = ret .. "'" .. tostring(t) .. "'"
+ else
+ ret = ret .. tostring(t)
+ end
+ return ret
+end
+
+-- Basic Exception
+TException = __TObject:new{
+ message,
+ errorCode,
+ __type = 'TException'
+}
+function TException:__tostring()
+ if self.message then
+ return string.format('%s: %s', self.__type, self.message)
+ else
+ local message
+ if self.errorCode and self.__errorCodeToString then
+ message = string.format('%d: %s', self.errorCode, self:__errorCodeToString())
+ else
+ message = thrift_print_r(self)
+ end
+ return string.format('%s:%s', self.__type, message)
+ end
+end
+
+TApplicationException = TException:new{
+ UNKNOWN = 0,
+ UNKNOWN_METHOD = 1,
+ INVALID_MESSAGE_TYPE = 2,
+ WRONG_METHOD_NAME = 3,
+ BAD_SEQUENCE_ID = 4,
+ MISSING_RESULT = 5,
+ INTERNAL_ERROR = 6,
+ PROTOCOL_ERROR = 7,
+ INVALID_TRANSFORM = 8,
+ INVALID_PROTOCOL = 9,
+ UNSUPPORTED_CLIENT_TYPE = 10,
+ errorCode = 0,
+ __type = 'TApplicationException'
+}
+
+function TApplicationException:__errorCodeToString()
+ if self.errorCode == self.UNKNOWN_METHOD then
+ return 'Unknown method'
+ elseif self.errorCode == self.INVALID_MESSAGE_TYPE then
+ return 'Invalid message type'
+ elseif self.errorCode == self.WRONG_METHOD_NAME then
+ return 'Wrong method name'
+ elseif self.errorCode == self.BAD_SEQUENCE_ID then
+ return 'Bad sequence ID'
+ elseif self.errorCode == self.MISSING_RESULT then
+ return 'Missing result'
+ elseif self.errorCode == self.INTERNAL_ERROR then
+ return 'Internal error'
+ elseif self.errorCode == self.PROTOCOL_ERROR then
+ return 'Protocol error'
+ elseif self.errorCode == self.INVALID_TRANSFORM then
+ return 'Invalid transform'
+ elseif self.errorCode == self.INVALID_PROTOCOL then
+ return 'Invalid protocol'
+ elseif self.errorCode == self.UNSUPPORTED_CLIENT_TYPE then
+ return 'Unsupported client type'
+ else
+ return 'Default (unknown)'
+ end
+end
+
+function TException:read(iprot)
+ iprot:readStructBegin()
+ while true do
+ local fname, ftype, fid = iprot:readFieldBegin()
+ if ftype == TType.STOP then
+ break
+ elseif fid == 1 then
+ if ftype == TType.STRING then
+ self.message = iprot:readString()
+ else
+ iprot:skip(ftype)
+ end
+ elseif fid == 2 then
+ if ftype == TType.I32 then
+ self.errorCode = iprot:readI32()
+ else
+ iprot:skip(ftype)
+ end
+ else
+ iprot:skip(ftype)
+ end
+ iprot:readFieldEnd()
+ end
+ iprot:readStructEnd()
+end
+
+function TException:write(oprot)
+ oprot:writeStructBegin('TApplicationException')
+ if self.message then
+ oprot:writeFieldBegin('message', TType.STRING, 1)
+ oprot:writeString(self.message)
+ oprot:writeFieldEnd()
+ end
+ if self.errorCode then
+ oprot:writeFieldBegin('type', TType.I32, 2)
+ oprot:writeI32(self.errorCode)
+ oprot:writeFieldEnd()
+ end
+ oprot:writeFieldStop()
+ oprot:writeStructEnd()
+end
+
+-- Basic Client (used in generated lua code)
+__TClient = __TObject:new{
+ __type = '__TClient',
+ _seqid = 0
+}
+function __TClient:new(obj)
+ if ttype(obj) ~= 'table' then
+ error('TClient must be initialized with a table')
+ end
+
+ -- Set iprot & oprot
+ if obj.protocol then
+ obj.iprot = obj.protocol
+ obj.oprot = obj.protocol
+ obj.protocol = nil
+ elseif not obj.iprot then
+ error('You must provide ' .. ttype(self) .. ' with an iprot')
+ end
+ if not obj.oprot then
+ obj.oprot = obj.iprot
+ end
+
+ return __TObject.new(self, obj)
+end
+
+function __TClient:close()
+ self.iprot.trans:close()
+ self.oprot.trans:close()
+end
+
+-- Basic Processor (used in generated lua code)
+__TProcessor = __TObject:new{
+ __type = '__TProcessor'
+}
+function __TProcessor:new(obj)
+ if ttype(obj) ~= 'table' then
+ error('TProcessor must be initialized with a table')
+ end
+
+ -- Ensure a handler is provided
+ if not obj.handler then
+ error('You must provide ' .. ttype(self) .. ' with a handler')
+ end
+
+ return __TObject.new(self, obj)
+end
diff --git a/src/jaegertracing/thrift/lib/lua/coding_standards.md b/src/jaegertracing/thrift/lib/lua/coding_standards.md
new file mode 100644
index 000000000..fa0390bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/coding_standards.md
@@ -0,0 +1 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
diff --git a/src/jaegertracing/thrift/lib/lua/src/longnumberutils.c b/src/jaegertracing/thrift/lib/lua/src/longnumberutils.c
new file mode 100644
index 000000000..fbc678900
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/src/longnumberutils.c
@@ -0,0 +1,47 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+const char * LONG_NUM_TYPE = "__thrift_longnumber";
+int64_t lualongnumber_checklong(lua_State *L, int index) {
+ switch (lua_type(L, index)) {
+ case LUA_TNUMBER:
+ return (int64_t)lua_tonumber(L, index);
+ case LUA_TSTRING:
+ return atoll(lua_tostring(L, index));
+ default:
+ return *((int64_t *)luaL_checkudata(L, index, LONG_NUM_TYPE));
+ }
+}
+
+// Creates a new longnumber and pushes it onto the statck
+int64_t * lualongnumber_pushlong(lua_State *L, int64_t *val) {
+ int64_t *data = (int64_t *)lua_newuserdata(L, sizeof(int64_t)); // longnum
+ luaL_getmetatable(L, LONG_NUM_TYPE); // longnum, mt
+ lua_setmetatable(L, -2); // longnum
+ if (val) {
+ *data = *val;
+ }
+ return data;
+}
+
diff --git a/src/jaegertracing/thrift/lib/lua/src/luabitwise.c b/src/jaegertracing/thrift/lib/lua/src/luabitwise.c
new file mode 100644
index 000000000..2e07e1724
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/src/luabitwise.c
@@ -0,0 +1,83 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+#include <lua.h>
+#include <lauxlib.h>
+
+static int l_not(lua_State *L) {
+ int a = luaL_checkinteger(L, 1);
+ a = ~a;
+ lua_pushnumber(L, a);
+ return 1;
+}
+
+static int l_xor(lua_State *L) {
+ int a = luaL_checkinteger(L, 1);
+ int b = luaL_checkinteger(L, 2);
+ a ^= b;
+ lua_pushnumber(L, a);
+ return 1;
+}
+
+static int l_and(lua_State *L) {
+ int a = luaL_checkinteger(L, 1);
+ int b = luaL_checkinteger(L, 2);
+ a &= b;
+ lua_pushnumber(L, a);
+ return 1;
+}
+
+static int l_or(lua_State *L) {
+ int a = luaL_checkinteger(L, 1);
+ int b = luaL_checkinteger(L, 2);
+ a |= b;
+ lua_pushnumber(L, a);
+ return 1;
+}
+
+static int l_shiftr(lua_State *L) {
+ int a = luaL_checkinteger(L, 1);
+ int b = luaL_checkinteger(L, 2);
+ a = a >> b;
+ lua_pushnumber(L, a);
+ return 1;
+}
+
+static int l_shiftl(lua_State *L) {
+ int a = luaL_checkinteger(L, 1);
+ int b = luaL_checkinteger(L, 2);
+ a = a << b;
+ lua_pushnumber(L, a);
+ return 1;
+}
+
+static const struct luaL_Reg funcs[] = {
+ {"band", l_and},
+ {"bor", l_or},
+ {"bxor", l_xor},
+ {"bnot", l_not},
+ {"shiftl", l_shiftl},
+ {"shiftr", l_shiftr},
+ {NULL, NULL}
+};
+
+int luaopen_libluabitwise(lua_State *L) {
+ luaL_register(L, "libluabitwise", funcs);
+ return 1;
+}
diff --git a/src/jaegertracing/thrift/lib/lua/src/luabpack.c b/src/jaegertracing/thrift/lib/lua/src/luabpack.c
new file mode 100644
index 000000000..077b6aa07
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/src/luabpack.c
@@ -0,0 +1,308 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <netinet/in.h>
+
+extern int64_t lualongnumber_checklong(lua_State *L, int index);
+extern int64_t lualongnumber_pushlong(lua_State *L, int64_t *val);
+
+// host order to network order (64-bit)
+static int64_t T_htonll(uint64_t data) {
+ uint32_t d1 = htonl((uint32_t)data);
+ uint32_t d2 = htonl((uint32_t)(data >> 32));
+ return ((uint64_t)d1 << 32) + (uint64_t)d2;
+}
+
+// network order to host order (64-bit)
+static int64_t T_ntohll(uint64_t data) {
+ uint32_t d1 = ntohl((uint32_t)data);
+ uint32_t d2 = ntohl((uint32_t)(data >> 32));
+ return ((uint64_t)d1 << 32) + (uint64_t)d2;
+}
+
+/**
+ * bpack(type, data)
+ * c - Signed Byte
+ * s - Signed Short
+ * i - Signed Int
+ * l - Signed Long
+ * d - Double
+ */
+static int l_bpack(lua_State *L) {
+ const char *code = luaL_checkstring(L, 1);
+ luaL_argcheck(L, code[1] == '\0', 0, "Format code must be one character.");
+ luaL_Buffer buf;
+ luaL_buffinit(L, &buf);
+
+ switch (code[0]) {
+ case 'c': {
+ int8_t data = luaL_checknumber(L, 2);
+ luaL_addlstring(&buf, (void*)&data, sizeof(data));
+ break;
+ }
+ case 's': {
+ int16_t data = luaL_checknumber(L, 2);
+ data = (int16_t)htons(data);
+ luaL_addlstring(&buf, (void*)&data, sizeof(data));
+ break;
+ }
+ case 'i': {
+ int32_t data = luaL_checkinteger(L, 2);
+ data = (int32_t)htonl(data);
+ luaL_addlstring(&buf, (void*)&data, sizeof(data));
+ break;
+ }
+ case 'l': {
+ int64_t data = lualongnumber_checklong(L, 2);
+ data = (int64_t)T_htonll(data);
+ luaL_addlstring(&buf, (void*)&data, sizeof(data));
+ break;
+ }
+ case 'd': {
+ double data = luaL_checknumber(L, 2);
+ luaL_addlstring(&buf, (void*)&data, sizeof(data));
+ break;
+ }
+ default:
+ luaL_argcheck(L, 0, 0, "Invalid format code.");
+ }
+
+ luaL_pushresult(&buf);
+ return 1;
+}
+
+/**
+ * bunpack(type, data)
+ * c - Signed Byte
+ * C - Unsigned Byte
+ * s - Signed Short
+ * i - Signed Int
+ * l - Signed Long
+ * d - Double
+ */
+static int l_bunpack(lua_State *L) {
+ const char *code = luaL_checkstring(L, 1);
+ luaL_argcheck(L, code[1] == '\0', 0, "Format code must be one character.");
+ const char *data = luaL_checkstring(L, 2);
+#if LUA_VERSION_NUM >= 502
+ size_t len = lua_rawlen(L, 2);
+#else
+ size_t len = lua_objlen(L, 2);
+#endif
+
+ switch (code[0]) {
+ case 'c': {
+ int8_t val;
+ luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size.");
+ memcpy(&val, data, sizeof(val));
+ lua_pushnumber(L, val);
+ break;
+ }
+ /**
+ * unpack unsigned Byte.
+ */
+ case 'C': {
+ uint8_t val;
+ luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size.");
+ memcpy(&val, data, sizeof(val));
+ lua_pushnumber(L, val);
+ break;
+ }
+ case 's': {
+ int16_t val;
+ luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size.");
+ memcpy(&val, data, sizeof(val));
+ val = (int16_t)ntohs(val);
+ lua_pushnumber(L, val);
+ break;
+ }
+ case 'i': {
+ int32_t val;
+ luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size.");
+ memcpy(&val, data, sizeof(val));
+ val = (int32_t)ntohl(val);
+ lua_pushnumber(L, val);
+ break;
+ }
+ case 'l': {
+ int64_t val;
+ luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size.");
+ memcpy(&val, data, sizeof(val));
+ val = (int64_t)T_ntohll(val);
+ lualongnumber_pushlong(L, &val);
+ break;
+ }
+ case 'd': {
+ double val;
+ luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size.");
+ memcpy(&val, data, sizeof(val));
+ lua_pushnumber(L, val);
+ break;
+ }
+ default:
+ luaL_argcheck(L, 0, 0, "Invalid format code.");
+ }
+ return 1;
+}
+
+/**
+ * Convert l into a zigzag long. This allows negative numbers to be
+ * represented compactly as a varint.
+ */
+static int l_i64ToZigzag(lua_State *L) {
+ int64_t n = lualongnumber_checklong(L, 1);
+ int64_t result = (n << 1) ^ (n >> 63);
+ lualongnumber_pushlong(L, &result);
+ return 1;
+}
+/**
+ * Convert n into a zigzag int. This allows negative numbers to be
+ * represented compactly as a varint.
+ */
+static int l_i32ToZigzag(lua_State *L) {
+ int32_t n = luaL_checkinteger(L, 1);
+ uint32_t result = (uint32_t)(n << 1) ^ (n >> 31);
+ lua_pushnumber(L, result);
+ return 1;
+}
+
+/**
+ * Convert from zigzag int to int.
+ */
+static int l_zigzagToI32(lua_State *L) {
+ uint32_t n = luaL_checkinteger(L, 1);
+ int32_t result = (int32_t)(n >> 1) ^ (uint32_t)(-(int32_t)(n & 1));
+ lua_pushnumber(L, result);
+ return 1;
+}
+
+/**
+ * Convert from zigzag long to long.
+ */
+static int l_zigzagToI64(lua_State *L) {
+ int64_t n = lualongnumber_checklong(L, 1);
+ int64_t result = (int64_t)(n >> 1) ^ (uint64_t)(-(int64_t)(n & 1));
+ lualongnumber_pushlong(L, &result);
+ return 1;
+}
+
+/**
+ * Convert an i32 to a varint. Results in 1-5 bytes on the buffer.
+ */
+static int l_toVarint32(lua_State *L) {
+ uint8_t buf[5];
+ uint32_t n = luaL_checkinteger(L, 1);
+ uint32_t wsize = 0;
+
+ while (1) {
+ if ((n & ~0x7F) == 0) {
+ buf[wsize++] = (int8_t)n;
+ break;
+ } else {
+ buf[wsize++] = (int8_t)((n & 0x7F) | 0x80);
+ n >>= 7;
+ }
+ }
+ lua_pushlstring(L, buf, wsize);
+ return 1;
+}
+
+/**
+ * Convert an i64 to a varint. Results in 1-10 bytes on the buffer.
+ */
+static int l_toVarint64(lua_State *L) {
+ uint8_t data[10];
+ uint64_t n = lualongnumber_checklong(L, 1);
+ uint32_t wsize = 0;
+ luaL_Buffer buf;
+ luaL_buffinit(L, &buf);
+
+ while (1) {
+ if ((n & ~0x7FL) == 0) {
+ data[wsize++] = (int8_t)n;
+ break;
+ } else {
+ data[wsize++] = (int8_t)((n & 0x7F) | 0x80);
+ n >>= 7;
+ }
+ }
+
+ luaL_addlstring(&buf, (void*)&data, wsize);
+ luaL_pushresult(&buf);
+ return 1;
+}
+
+/**
+ * Convert a varint to i64.
+ */
+static int l_fromVarint64(lua_State *L) {
+ int64_t result;
+ uint8_t byte = luaL_checknumber(L, 1);
+ int32_t shift = luaL_checknumber(L, 2);
+ uint64_t n = (uint64_t)lualongnumber_checklong(L, 3);
+ n |= (uint64_t)(byte & 0x7f) << shift;
+
+ if (!(byte & 0x80)) {
+ result = (int64_t)(n >> 1) ^ (uint64_t)(-(int64_t)(n & 1));
+ lua_pushnumber(L, 0);
+ } else {
+ result = n;
+ lua_pushnumber(L, 1);
+ }
+ lualongnumber_pushlong(L, &result);
+ return 2;
+}
+
+/**
+ * To pack message type of compact protocol.
+ */
+static int l_packMesgType(lua_State *L) {
+ int32_t version_n = luaL_checkinteger(L, 1);
+ int32_t version_mask = luaL_checkinteger(L, 2);
+ int32_t messagetype = luaL_checkinteger(L, 3);
+ int32_t type_shift_amount = luaL_checkinteger(L, 4);
+ int32_t type_mask = luaL_checkinteger(L, 5);
+ int32_t to_mesg_type = (version_n & version_mask) |
+ (((int32_t)messagetype << type_shift_amount) & type_mask);
+ lua_pushnumber(L, to_mesg_type);
+ return 1;
+}
+
+static const struct luaL_Reg lua_bpack[] = {
+ {"bpack", l_bpack},
+ {"bunpack", l_bunpack},
+ {"i32ToZigzag", l_i32ToZigzag},
+ {"i64ToZigzag", l_i64ToZigzag},
+ {"zigzagToI32", l_zigzagToI32},
+ {"zigzagToI64", l_zigzagToI64},
+ {"toVarint32", l_toVarint32},
+ {"toVarint64", l_toVarint64},
+ {"fromVarint64", l_fromVarint64},
+ {"packMesgType", l_packMesgType},
+ {NULL, NULL}
+};
+
+int luaopen_libluabpack(lua_State *L) {
+ luaL_register(L, "libluabpack", lua_bpack);
+ return 1;
+}
diff --git a/src/jaegertracing/thrift/lib/lua/src/lualongnumber.c b/src/jaegertracing/thrift/lib/lua/src/lualongnumber.c
new file mode 100644
index 000000000..9001e4a90
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/src/lualongnumber.c
@@ -0,0 +1,228 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <stdlib.h>
+#include <math.h>
+#include <inttypes.h>
+#include <string.h>
+
+extern const char * LONG_NUM_TYPE;
+extern int64_t lualongnumber_checklong(lua_State *L, int index);
+extern int64_t lualongnumber_pushlong(lua_State *L, int64_t *val);
+
+////////////////////////////////////////////////////////////////////////////////
+
+static void l_serialize(char *buf, int len, int64_t val) {
+ snprintf(buf, len, "%"PRId64, val);
+}
+
+static int64_t l_deserialize(const char *buf) {
+ int64_t data;
+ int rv;
+ // Support hex prefixed with '0x'
+ if (strstr(buf, "0x") == buf) {
+ rv = sscanf(buf, "%"PRIx64, &data);
+ } else {
+ rv = sscanf(buf, "%"PRId64, &data);
+ }
+ if (rv == 1) {
+ return data;
+ }
+ return 0; // Failed
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static int l_new(lua_State *L) {
+ int64_t val;
+ const char *str = NULL;
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ str = lua_tostring(L, 1);
+ val = l_deserialize(str);
+ } else if (lua_type(L, 1) == LUA_TNUMBER) {
+ val = (int64_t)lua_tonumber(L, 1);
+ str = (const char *)1;
+ }
+ lualongnumber_pushlong(L, (str ? &val : NULL));
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// a + b
+static int l_add(lua_State *L) {
+ int64_t a, b, c;
+ a = lualongnumber_checklong(L, 1);
+ b = lualongnumber_checklong(L, 2);
+ c = a + b;
+ lualongnumber_pushlong(L, &c);
+ return 1;
+}
+
+// a / b
+static int l_div(lua_State *L) {
+ int64_t a, b, c;
+ a = lualongnumber_checklong(L, 1);
+ b = lualongnumber_checklong(L, 2);
+ c = a / b;
+ lualongnumber_pushlong(L, &c);
+ return 1;
+}
+
+// a == b (both a and b are lualongnumber's)
+static int l_eq(lua_State *L) {
+ int64_t a, b;
+ a = lualongnumber_checklong(L, 1);
+ b = lualongnumber_checklong(L, 2);
+ lua_pushboolean(L, (a == b ? 1 : 0));
+ return 1;
+}
+
+// garbage collection
+static int l_gc(lua_State *L) {
+ lua_pushnil(L);
+ lua_setmetatable(L, 1);
+ return 0;
+}
+
+// a < b
+static int l_lt(lua_State *L) {
+ int64_t a, b;
+ a = lualongnumber_checklong(L, 1);
+ b = lualongnumber_checklong(L, 2);
+ lua_pushboolean(L, (a < b ? 1 : 0));
+ return 1;
+}
+
+// a <= b
+static int l_le(lua_State *L) {
+ int64_t a, b;
+ a = lualongnumber_checklong(L, 1);
+ b = lualongnumber_checklong(L, 2);
+ lua_pushboolean(L, (a <= b ? 1 : 0));
+ return 1;
+}
+
+// a % b
+static int l_mod(lua_State *L) {
+ int64_t a, b, c;
+ a = lualongnumber_checklong(L, 1);
+ b = lualongnumber_checklong(L, 2);
+ c = a % b;
+ lualongnumber_pushlong(L, &c);
+ return 1;
+}
+
+// a * b
+static int l_mul(lua_State *L) {
+ int64_t a, b, c;
+ a = lualongnumber_checklong(L, 1);
+ b = lualongnumber_checklong(L, 2);
+ c = a * b;
+ lualongnumber_pushlong(L, &c);
+ return 1;
+}
+
+// a ^ b
+static int l_pow(lua_State *L) {
+ long double a, b;
+ int64_t c;
+ a = (long double)lualongnumber_checklong(L, 1);
+ b = (long double)lualongnumber_checklong(L, 2);
+ c = (int64_t)pow(a, b);
+ lualongnumber_pushlong(L, &c);
+ return 1;
+}
+
+// a - b
+static int l_sub(lua_State *L) {
+ int64_t a, b, c;
+ a = lualongnumber_checklong(L, 1);
+ b = lualongnumber_checklong(L, 2);
+ c = a - b;
+ lualongnumber_pushlong(L, &c);
+ return 1;
+}
+
+// tostring()
+static int l_tostring(lua_State *L) {
+ int64_t a;
+ char str[256];
+ l_serialize(str, 256, lualongnumber_checklong(L, 1));
+ lua_pushstring(L, str);
+ return 1;
+}
+
+// -a
+static int l_unm(lua_State *L) {
+ int64_t a, c;
+ a = lualongnumber_checklong(L, 1);
+ c = -a;
+ lualongnumber_pushlong(L, &c);
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static const luaL_Reg methods[] = {
+ {"__add", l_add},
+ {"__div", l_div},
+ {"__eq", l_eq},
+ {"__gc", l_gc},
+ {"__lt", l_lt},
+ {"__le", l_le},
+ {"__mod", l_mod},
+ {"__mul", l_mul},
+ {"__pow", l_pow},
+ {"__sub", l_sub},
+ {"__tostring", l_tostring},
+ {"__unm", l_unm},
+ {NULL, NULL},
+};
+
+static const luaL_Reg funcs[] = {
+ {"new", l_new},
+ {NULL, NULL}
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+static void set_methods(lua_State *L,
+ const char *metatablename,
+ const struct luaL_Reg *methods) {
+ luaL_getmetatable(L, metatablename); // mt
+ // No need for a __index table since everything is __*
+ for (; methods->name; methods++) {
+ lua_pushstring(L, methods->name); // mt, "name"
+ lua_pushcfunction(L, methods->func); // mt, "name", func
+ lua_rawset(L, -3); // mt
+ }
+ lua_pop(L, 1);
+}
+
+LUALIB_API int luaopen_liblualongnumber(lua_State *L) {
+ luaL_newmetatable(L, LONG_NUM_TYPE);
+ lua_pop(L, 1);
+ set_methods(L, LONG_NUM_TYPE, methods);
+
+ luaL_register(L, "liblualongnumber", funcs);
+ return 1;
+}
diff --git a/src/jaegertracing/thrift/lib/lua/src/luasocket.c b/src/jaegertracing/thrift/lib/lua/src/luasocket.c
new file mode 100644
index 000000000..d48351077
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/src/luasocket.c
@@ -0,0 +1,380 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+#include <lua.h>
+#include <lauxlib.h>
+
+#include <unistd.h>
+#include "string.h"
+#include "socket.h"
+
+////////////////////////////////////////////////////////////////////////////////
+
+static const char *SOCKET_ANY = "__thrift_socket_any";
+static const char *SOCKET_CONN = "__thrift_socket_connected";
+
+static const char *SOCKET_GENERIC = "__thrift_socket_generic";
+static const char *SOCKET_CLIENT = "__thrift_socket_client";
+static const char *SOCKET_SERVER = "__thrift_socket_server";
+
+static const char *DEFAULT_HOST = "localhost";
+
+typedef struct __t_tcp {
+ t_socket sock;
+ int timeout; // Milliseconds
+} t_tcp;
+typedef t_tcp *p_tcp;
+
+////////////////////////////////////////////////////////////////////////////////
+// Util
+
+static void throw_argerror(lua_State *L, int index, const char *expected) {
+ char msg[256];
+ sprintf(msg, "%s expected, got %s", expected, luaL_typename(L, index));
+ luaL_argerror(L, index, msg);
+}
+
+static void *checkgroup(lua_State *L, int index, const char *groupname) {
+ if (!lua_getmetatable(L, index)) {
+ throw_argerror(L, index, groupname);
+ }
+
+ lua_pushstring(L, groupname);
+ lua_rawget(L, -2);
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 2);
+ throw_argerror(L, index, groupname);
+ } else {
+ lua_pop(L, 2);
+ return lua_touserdata(L, index);
+ }
+ return NULL; // Not reachable
+}
+
+static void *checktype(lua_State *L, int index, const char *typename) {
+ if (strcmp(typename, SOCKET_ANY) == 0 ||
+ strcmp(typename, SOCKET_CONN) == 0) {
+ return checkgroup(L, index, typename);
+ } else {
+ return luaL_checkudata(L, index, typename);
+ }
+}
+
+static void settype(lua_State *L, int index, const char *typename) {
+ luaL_getmetatable(L, typename);
+ lua_setmetatable(L, index);
+}
+
+#define LUA_SUCCESS_RETURN(L) \
+ lua_pushnumber(L, 1); \
+ return 1
+
+#define LUA_CHECK_RETURN(L, err) \
+ if (err) { \
+ lua_pushnil(L); \
+ lua_pushstring(L, err); \
+ return 2; \
+ } \
+ LUA_SUCCESS_RETURN(L)
+
+////////////////////////////////////////////////////////////////////////////////
+
+static int l_socket_create(lua_State *L);
+static int l_socket_destroy(lua_State *L);
+static int l_socket_settimeout(lua_State *L);
+static int l_socket_getsockinfo(lua_State *L);
+
+static int l_socket_accept(lua_State *L);
+static int l_socket_listen(lua_State *L);
+
+static int l_socket_create_and_connect(lua_State *L);
+static int l_socket_connect(lua_State *L);
+static int l_socket_send(lua_State *L);
+static int l_socket_receive(lua_State *L);
+
+////////////////////////////////////////////////////////////////////////////////
+
+static const struct luaL_Reg methods_generic[] = {
+ {"destroy", l_socket_destroy},
+ {"settimeout", l_socket_settimeout},
+ {"getsockinfo", l_socket_getsockinfo},
+ {"listen", l_socket_listen},
+ {"connect", l_socket_connect},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg methods_server[] = {
+ {"destroy", l_socket_destroy},
+ {"getsockinfo", l_socket_getsockinfo},
+ {"accept", l_socket_accept},
+ {"send", l_socket_send},
+ {"receive", l_socket_receive},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg methods_client[] = {
+ {"destroy", l_socket_destroy},
+ {"settimeout", l_socket_settimeout},
+ {"getsockinfo", l_socket_getsockinfo},
+ {"send", l_socket_send},
+ {"receive", l_socket_receive},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg funcs_luasocket[] = {
+ {"create", l_socket_create},
+ {"create_and_connect", l_socket_create_and_connect},
+ {NULL, NULL}
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Check/enforce inheritance
+static void add_to_group(lua_State *L,
+ const char *metatablename,
+ const char *groupname) {
+ luaL_getmetatable(L, metatablename); // mt
+ lua_pushstring(L, groupname); // mt, "name"
+ lua_pushboolean(L, 1); // mt, "name", true
+ lua_rawset(L, -3); // mt
+ lua_pop(L, 1);
+}
+
+static void set_methods(lua_State *L,
+ const char *metatablename,
+ const struct luaL_Reg *methods) {
+ luaL_getmetatable(L, metatablename); // mt
+ // Create the __index table
+ lua_pushstring(L, "__index"); // mt, "__index"
+ lua_newtable(L); // mt, "__index", t
+ for (; methods->name; methods++) {
+ lua_pushstring(L, methods->name); // mt, "__index", t, "name"
+ lua_pushcfunction(L, methods->func); // mt, "__index", t, "name", func
+ lua_rawset(L, -3); // mt, "__index", t
+ }
+ lua_rawset(L, -3); // mt
+ lua_pop(L, 1);
+}
+
+int luaopen_libluasocket(lua_State *L) {
+ luaL_newmetatable(L, SOCKET_GENERIC);
+ luaL_newmetatable(L, SOCKET_CLIENT);
+ luaL_newmetatable(L, SOCKET_SERVER);
+ lua_pop(L, 3);
+ add_to_group(L, SOCKET_GENERIC, SOCKET_ANY);
+ add_to_group(L, SOCKET_CLIENT, SOCKET_ANY);
+ add_to_group(L, SOCKET_SERVER, SOCKET_ANY);
+ add_to_group(L, SOCKET_CLIENT, SOCKET_CONN);
+ add_to_group(L, SOCKET_SERVER, SOCKET_CONN);
+ set_methods(L, SOCKET_GENERIC, methods_generic);
+ set_methods(L, SOCKET_CLIENT, methods_client);
+ set_methods(L, SOCKET_SERVER, methods_server);
+
+ luaL_register(L, "luasocket", funcs_luasocket);
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// General
+
+// sock,err create(bind_host, bind_port)
+// sock,err create(bind_host) -> any port
+// sock,err create() -> any port on localhost
+static int l_socket_create(lua_State *L) {
+ const char *err;
+ t_socket sock;
+ const char *addr = lua_tostring(L, 1);
+ if (!addr) {
+ addr = DEFAULT_HOST;
+ }
+ unsigned short port = lua_tonumber(L, 2);
+ err = tcp_create(&sock);
+ if (!err) {
+ err = tcp_bind(&sock, addr, port); // bind on create
+ if (err) {
+ tcp_destroy(&sock);
+ } else {
+ p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
+ settype(L, -2, SOCKET_GENERIC);
+ socket_setnonblocking(&sock);
+ tcp->sock = sock;
+ tcp->timeout = 0;
+ return 1; // Return userdata
+ }
+ }
+ LUA_CHECK_RETURN(L, err);
+}
+
+// destroy()
+static int l_socket_destroy(lua_State *L) {
+ p_tcp tcp = (p_tcp) checktype(L, 1, SOCKET_ANY);
+ const char *err = tcp_destroy(&tcp->sock);
+ LUA_CHECK_RETURN(L, err);
+}
+
+// send(socket, data)
+static int l_socket_send(lua_State *L) {
+ p_tcp self = (p_tcp) checktype(L, 1, SOCKET_CONN);
+ p_tcp tcp = (p_tcp) checktype(L, 2, SOCKET_CONN);
+ size_t len;
+ const char *data = luaL_checklstring(L, 3, &len);
+ const char *err =
+ tcp_send(&tcp->sock, data, len, tcp->timeout);
+ LUA_CHECK_RETURN(L, err);
+}
+
+#define LUA_READ_STEP 8192
+static int l_socket_receive(lua_State *L) {
+ p_tcp self = (p_tcp) checktype(L, 1, SOCKET_CONN);
+ p_tcp handle = (p_tcp) checktype(L, 2, SOCKET_CONN);
+ size_t len = luaL_checknumber(L, 3);
+ char buf[LUA_READ_STEP];
+ const char *err = NULL;
+ int received;
+ size_t got = 0, step = 0;
+ luaL_Buffer b;
+
+ luaL_buffinit(L, &b);
+ do {
+ step = (LUA_READ_STEP < len - got ? LUA_READ_STEP : len - got);
+ err = tcp_raw_receive(&handle->sock, buf, step, self->timeout, &received);
+ if (err == NULL) {
+ luaL_addlstring(&b, buf, received);
+ got += received;
+ }
+ } while (err == NULL && got < len);
+
+ if (err) {
+ lua_pushnil(L);
+ lua_pushstring(L, err);
+ return 2;
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+// settimeout(timeout)
+static int l_socket_settimeout(lua_State *L) {
+ p_tcp self = (p_tcp) checktype(L, 1, SOCKET_ANY);
+ int timeout = luaL_checknumber(L, 2);
+ self->timeout = timeout;
+ LUA_SUCCESS_RETURN(L);
+}
+
+// table getsockinfo()
+static int l_socket_getsockinfo(lua_State *L) {
+ char buf[256];
+ short port = 0;
+ p_tcp tcp = (p_tcp) checktype(L, 1, SOCKET_ANY);
+ if (socket_get_info(&tcp->sock, &port, buf, 256) == SUCCESS) {
+ lua_newtable(L); // t
+ lua_pushstring(L, "host"); // t, "host"
+ lua_pushstring(L, buf); // t, "host", buf
+ lua_rawset(L, -3); // t
+ lua_pushstring(L, "port"); // t, "port"
+ lua_pushnumber(L, port); // t, "port", port
+ lua_rawset(L, -3); // t
+ return 1;
+ }
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Server
+
+// accept()
+static int l_socket_accept(lua_State *L) {
+ const char *err;
+ p_tcp self = (p_tcp) checktype(L, 1, SOCKET_SERVER);
+ t_socket sock;
+ err = tcp_accept(&self->sock, &sock, self->timeout);
+ if (!err) { // Success
+ // Create a reference to the client
+ p_tcp client = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
+ settype(L, 2, SOCKET_CLIENT);
+ socket_setnonblocking(&sock);
+ client->sock = sock;
+ client->timeout = self->timeout;
+ return 1;
+ }
+ LUA_CHECK_RETURN(L, err);
+}
+
+static int l_socket_listen(lua_State *L) {
+ const char* err;
+ p_tcp tcp = (p_tcp) checktype(L, 1, SOCKET_GENERIC);
+ int backlog = 10;
+ err = tcp_listen(&tcp->sock, backlog);
+ if (!err) {
+ // Set the current as a server
+ settype(L, 1, SOCKET_SERVER); // Now a server
+ }
+ LUA_CHECK_RETURN(L, err);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Client
+
+// create_and_connect(host, port, timeout)
+extern double __gettime();
+static int l_socket_create_and_connect(lua_State *L) {
+ const char* err = NULL;
+ double end;
+ t_socket sock;
+ const char *host = luaL_checkstring(L, 1);
+ unsigned short port = luaL_checknumber(L, 2);
+ int timeout = luaL_checknumber(L, 3);
+
+ // Create and connect loop for timeout milliseconds
+ end = __gettime() + timeout/1000;
+ do {
+ // Create the socket
+ err = tcp_create(&sock);
+ if (!err) {
+ // Connect
+ err = tcp_connect(&sock, host, port, timeout);
+ if (err) {
+ tcp_destroy(&sock);
+ usleep(100000); // sleep for 100ms
+ } else {
+ p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
+ settype(L, -2, SOCKET_CLIENT);
+ socket_setnonblocking(&sock);
+ tcp->sock = sock;
+ tcp->timeout = timeout;
+ return 1; // Return userdata
+ }
+ }
+ } while (err && __gettime() < end);
+
+ LUA_CHECK_RETURN(L, err);
+}
+
+// connect(host, port)
+static int l_socket_connect(lua_State *L) {
+ const char *err;
+ p_tcp tcp = (p_tcp) checktype(L, 1, SOCKET_GENERIC);
+ const char *host = luaL_checkstring(L, 2);
+ unsigned short port = luaL_checknumber(L, 3);
+ err = tcp_connect(&tcp->sock, host, port, tcp->timeout);
+ if (!err) {
+ settype(L, 1, SOCKET_CLIENT); // Now a client
+ }
+ LUA_CHECK_RETURN(L, err);
+}
diff --git a/src/jaegertracing/thrift/lib/lua/src/socket.h b/src/jaegertracing/thrift/lib/lua/src/socket.h
new file mode 100644
index 000000000..afb827e47
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/src/socket.h
@@ -0,0 +1,78 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+#ifndef LUA_THRIFT_SOCKET_H
+#define LUA_THRIFT_SOCKET_H
+
+#include <sys/socket.h>
+
+#ifdef _WIN32
+// SOL
+#else
+typedef int t_socket;
+typedef t_socket* p_socket;
+#endif
+
+// Error Codes
+enum {
+ SUCCESS = 0,
+ TIMEOUT = -1,
+ CLOSED = -2,
+};
+typedef int T_ERRCODE;
+
+static const char * TIMEOUT_MSG = "Timeout";
+static const char * CLOSED_MSG = "Connection Closed";
+
+typedef struct sockaddr t_sa;
+typedef t_sa * p_sa;
+
+T_ERRCODE socket_create(p_socket sock, int domain, int type, int protocol);
+T_ERRCODE socket_destroy(p_socket sock);
+T_ERRCODE socket_bind(p_socket sock, p_sa addr, int addr_len);
+T_ERRCODE socket_get_info(p_socket sock, short *port, char *buf, size_t len);
+T_ERRCODE socket_send(p_socket sock, const char *data, size_t len, int timeout);
+T_ERRCODE socket_recv(p_socket sock, char *data, size_t len, int timeout,
+ int *received);
+
+T_ERRCODE socket_setblocking(p_socket sock);
+T_ERRCODE socket_setnonblocking(p_socket sock);
+
+T_ERRCODE socket_accept(p_socket sock, p_socket sibling,
+ p_sa addr, socklen_t *addr_len, int timeout);
+T_ERRCODE socket_listen(p_socket sock, int backlog);
+
+T_ERRCODE socket_connect(p_socket sock, p_sa addr, int addr_len, int timeout);
+
+const char * tcp_create(p_socket sock);
+const char * tcp_destroy(p_socket sock);
+const char * tcp_bind(p_socket sock, const char *host, unsigned short port);
+const char * tcp_send(p_socket sock, const char *data, size_t w_len,
+ int timeout);
+const char * tcp_receive(p_socket sock, char *data, size_t r_len, int timeout);
+const char * tcp_raw_receive(p_socket sock, char * data, size_t r_len,
+ int timeout, int *received);
+
+const char * tcp_listen(p_socket sock, int backlog);
+const char * tcp_accept(p_socket sock, p_socket client, int timeout);
+
+const char * tcp_connect(p_socket sock, const char *host, unsigned short port,
+ int timeout);
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/lua/src/usocket.c b/src/jaegertracing/thrift/lib/lua/src/usocket.c
new file mode 100644
index 000000000..1a1b549a0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/lua/src/usocket.c
@@ -0,0 +1,376 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <stdio.h> // TODO REMOVE
+
+#include "socket.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Private
+
+// Num seconds since Jan 1 1970 (UTC)
+#ifdef _WIN32
+// SOL
+#else
+ double __gettime() {
+ struct timeval v;
+ gettimeofday(&v, (struct timezone*) NULL);
+ return v.tv_sec + v.tv_usec/1.0e6;
+ }
+#endif
+
+#define WAIT_MODE_R 1
+#define WAIT_MODE_W 2
+#define WAIT_MODE_C (WAIT_MODE_R|WAIT_MODE_W)
+T_ERRCODE socket_wait(p_socket sock, int mode, int timeout) {
+ int ret = 0;
+ fd_set rfds, wfds;
+ struct timeval tv;
+ double end, t;
+ if (!timeout) {
+ return TIMEOUT;
+ }
+
+ end = __gettime() + timeout/1000;
+ do {
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+
+ // Specify what I/O operations we care about
+ if (mode & WAIT_MODE_R) {
+ FD_SET(*sock, &rfds);
+ }
+ if (mode & WAIT_MODE_W) {
+ FD_SET(*sock, &wfds);
+ }
+
+ // Check for timeout
+ t = end - __gettime();
+ if (t < 0.0) {
+ break;
+ }
+
+ // Wait
+ tv.tv_sec = (int)t;
+ tv.tv_usec = (int)((t - tv.tv_sec) * 1.0e6);
+ ret = select(*sock+1, &rfds, &wfds, NULL, &tv);
+ } while (ret == -1 && errno == EINTR);
+ if (ret == -1) {
+ return errno;
+ }
+
+ // Check for timeout
+ if (ret == 0) {
+ return TIMEOUT;
+ }
+
+ // Verify that we can actually read from the remote host
+ if (mode & WAIT_MODE_C && FD_ISSET(*sock, &rfds) &&
+ recv(*sock, (char*) &rfds, 0, 0) != 0) {
+ return errno;
+ }
+
+ return SUCCESS;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// General
+
+T_ERRCODE socket_create(p_socket sock, int domain, int type, int protocol) {
+ *sock = socket(domain, type, protocol);
+ if (*sock > 0) {
+ return SUCCESS;
+ } else {
+ return errno;
+ }
+}
+
+T_ERRCODE socket_destroy(p_socket sock) {
+ // TODO Figure out if I should be free-ing this
+ if (*sock > 0) {
+ (void)socket_setblocking(sock);
+ close(*sock);
+ *sock = -1;
+ }
+ return SUCCESS;
+}
+
+T_ERRCODE socket_bind(p_socket sock, p_sa addr, int addr_len) {
+ int ret = socket_setblocking(sock);
+ if (ret != SUCCESS) {
+ return ret;
+ }
+ if (bind(*sock, addr, addr_len)) {
+ ret = errno;
+ }
+ int ret2 = socket_setnonblocking(sock);
+ return ret == SUCCESS ? ret2 : ret;
+}
+
+T_ERRCODE socket_get_info(p_socket sock, short *port, char *buf, size_t len) {
+ struct sockaddr_storage sa;
+ memset(&sa, 0, sizeof(sa));
+ socklen_t addrlen = sizeof(sa);
+ int rc = getsockname(*sock, (struct sockaddr*)&sa, &addrlen);
+ if (!rc) {
+ if (sa.ss_family == AF_INET6) {
+ struct sockaddr_in6* sin = (struct sockaddr_in6*)(&sa);
+ if (!inet_ntop(AF_INET6, &sin->sin6_addr, buf, len)) {
+ return errno;
+ }
+ *port = ntohs(sin->sin6_port);
+ } else {
+ struct sockaddr_in* sin = (struct sockaddr_in*)(&sa);
+ if (!inet_ntop(AF_INET, &sin->sin_addr, buf, len)) {
+ return errno;
+ }
+ *port = ntohs(sin->sin_port);
+ }
+ return SUCCESS;
+ }
+ return errno;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Server
+
+T_ERRCODE socket_accept(p_socket sock, p_socket client,
+ p_sa addr, socklen_t *addrlen, int timeout) {
+ int err;
+ if (*sock < 0) {
+ return CLOSED;
+ }
+ do {
+ *client = accept(*sock, addr, addrlen);
+ if (*client > 0) {
+ return SUCCESS;
+ }
+ } while ((err = errno) == EINTR);
+
+ if (err == EAGAIN || err == ECONNABORTED) {
+ return socket_wait(sock, WAIT_MODE_R, timeout);
+ }
+
+ return err;
+}
+
+T_ERRCODE socket_listen(p_socket sock, int backlog) {
+ int ret = socket_setblocking(sock);
+ if (ret != SUCCESS) {
+ return ret;
+ }
+ if (listen(*sock, backlog)) {
+ ret = errno;
+ }
+ int ret2 = socket_setnonblocking(sock);
+ return ret == SUCCESS ? ret2 : ret;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Client
+
+T_ERRCODE socket_connect(p_socket sock, p_sa addr, int addr_len, int timeout) {
+ int err;
+ if (*sock < 0) {
+ return CLOSED;
+ }
+
+ do {
+ if (connect(*sock, addr, addr_len) == 0) {
+ return SUCCESS;
+ }
+ } while ((err = errno) == EINTR);
+ if (err != EINPROGRESS && err != EAGAIN) {
+ return err;
+ }
+ return socket_wait(sock, WAIT_MODE_C, timeout);
+}
+
+T_ERRCODE socket_send(
+ p_socket sock, const char *data, size_t len, int timeout) {
+ int err, put = 0;
+ if (*sock < 0) {
+ return CLOSED;
+ }
+ do {
+ put = send(*sock, data, len, 0);
+ if (put > 0) {
+ return SUCCESS;
+ }
+ } while ((err = errno) == EINTR);
+
+ if (err == EAGAIN) {
+ return socket_wait(sock, WAIT_MODE_W, timeout);
+ }
+
+ return err;
+}
+
+T_ERRCODE socket_recv(
+ p_socket sock, char *data, size_t len, int timeout, int *received) {
+ int err, got = 0;
+ if (*sock < 0) {
+ return CLOSED;
+ }
+ *received = 0;
+
+ do {
+ got = recv(*sock, data, len, 0);
+ if (got > 0) {
+ *received = got;
+ return SUCCESS;
+ }
+ err = errno;
+
+ // Connection has been closed by peer
+ if (got == 0) {
+ return CLOSED;
+ }
+ } while (err == EINTR);
+
+ if (err == EAGAIN) {
+ return socket_wait(sock, WAIT_MODE_R, timeout);
+ }
+
+ return err;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Util
+
+T_ERRCODE socket_setnonblocking(p_socket sock) {
+ int flags = fcntl(*sock, F_GETFL, 0);
+ flags |= O_NONBLOCK;
+ return fcntl(*sock, F_SETFL, flags) != -1 ? SUCCESS : errno;
+}
+
+T_ERRCODE socket_setblocking(p_socket sock) {
+ int flags = fcntl(*sock, F_GETFL, 0);
+ flags &= (~(O_NONBLOCK));
+ return fcntl(*sock, F_SETFL, flags) != -1 ? SUCCESS : errno;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TCP
+
+#define ERRORSTR_RETURN(err) \
+ if (err == SUCCESS) { \
+ return NULL; \
+ } else if (err == TIMEOUT) { \
+ return TIMEOUT_MSG; \
+ } else if (err == CLOSED) { \
+ return CLOSED_MSG; \
+ } \
+ return strerror(err)
+
+const char * tcp_create(p_socket sock) {
+ int err = socket_create(sock, AF_INET, SOCK_STREAM, 0);
+ ERRORSTR_RETURN(err);
+}
+
+const char * tcp_destroy(p_socket sock) {
+ int err = socket_destroy(sock);
+ ERRORSTR_RETURN(err);
+}
+
+const char * tcp_bind(p_socket sock, const char *host, unsigned short port) {
+ int err;
+ struct hostent *h;
+ struct sockaddr_in local;
+ memset(&local, 0, sizeof(local));
+ local.sin_family = AF_INET;
+ local.sin_addr.s_addr = htonl(INADDR_ANY);
+ local.sin_port = htons(port);
+ if (strcmp(host, "*") && !inet_aton(host, &local.sin_addr)) {
+ h = gethostbyname(host);
+ if (!h) {
+ return hstrerror(h_errno);
+ }
+ memcpy(&local.sin_addr,
+ (struct in_addr *)h->h_addr_list[0],
+ sizeof(struct in_addr));
+ }
+ err = socket_bind(sock, (p_sa) &local, sizeof(local));
+ ERRORSTR_RETURN(err);
+}
+
+const char * tcp_listen(p_socket sock, int backlog) {
+ int err = socket_listen(sock, backlog);
+ ERRORSTR_RETURN(err);
+}
+
+const char * tcp_accept(p_socket sock, p_socket client, int timeout) {
+ int err = socket_accept(sock, client, NULL, NULL, timeout);
+ ERRORSTR_RETURN(err);
+}
+
+const char * tcp_connect(p_socket sock,
+ const char *host,
+ unsigned short port,
+ int timeout) {
+ int err;
+ struct hostent *h;
+ struct sockaddr_in remote;
+ memset(&remote, 0, sizeof(remote));
+ remote.sin_family = AF_INET;
+ remote.sin_port = htons(port);
+ if (strcmp(host, "*") && !inet_aton(host, &remote.sin_addr)) {
+ h = gethostbyname(host);
+ if (!h) {
+ return hstrerror(h_errno);
+ }
+ memcpy(&remote.sin_addr,
+ (struct in_addr *)h->h_addr_list[0],
+ sizeof(struct in_addr));
+ }
+ err = socket_connect(sock, (p_sa) &remote, sizeof(remote), timeout);
+ ERRORSTR_RETURN(err);
+}
+
+#define WRITE_STEP 8192
+const char * tcp_send(
+ p_socket sock, const char * data, size_t w_len, int timeout) {
+ int err;
+ size_t put = 0, step;
+ if (!w_len) {
+ return NULL;
+ }
+
+ do {
+ step = (WRITE_STEP < w_len - put ? WRITE_STEP : w_len - put);
+ err = socket_send(sock, data + put, step, timeout);
+ put += step;
+ } while (err == SUCCESS && put < w_len);
+ ERRORSTR_RETURN(err);
+}
+
+const char * tcp_raw_receive(
+ p_socket sock, char * data, size_t r_len, int timeout, int *received) {
+ int err = socket_recv(sock, data, r_len, timeout, received);
+ ERRORSTR_RETURN(err);
+}
diff --git a/src/jaegertracing/thrift/lib/netcore/Makefile.am b/src/jaegertracing/thrift/lib/netcore/Makefile.am
new file mode 100644
index 000000000..4e2d6a597
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Makefile.am
@@ -0,0 +1,58 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+all-local:
+ $(DOTNETCORE) build
+
+check-local:
+ $(DOTNETCORE) test Tests/Thrift.Tests/Thrift.Tests.csproj
+ $(DOTNETCORE) test Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj
+
+clean-local:
+ $(RM) -r Thrift/bin
+ $(RM) -r Thrift/obj
+ $(RM) -r Tests/Thrift.Tests/bin
+ $(RM) -r Tests/Thrift.Tests/obj
+ $(RM) -r Tests/Thrift.IntegrationTests/bin
+ $(RM) -r Tests/Thrift.IntegrationTests/obj
+ $(RM) -r Tests/Thrift.PublicInterfaces.Compile.Tests/bin
+ $(RM) -r Tests/Thrift.PublicInterfaces.Compile.Tests/obj
+
+dist-hook:
+ $(RM) -r $(distdir)/Thrift/bin
+ $(RM) -r $(distdir)/Thrift/obj
+ $(RM) -r $(distdir)/Tests/Thrift.Tests/bin
+ $(RM) -r $(distdir)/Tests/Thrift.Tests/obj
+ $(RM) -r $(distdir)/Tests/Thrift.IntegrationTests/bin
+ $(RM) -r $(distdir)/Tests/Thrift.IntegrationTests/obj
+ $(RM) -r $(distdir)/Tests/Thrift.PublicInterfaces.Compile.Tests/bin
+ $(RM) -r $(distdir)/Tests/Thrift.PublicInterfaces.Compile.Tests/obj
+
+DISTCLEANFILES = \
+ Makefile.in
+
+EXTRA_DIST = \
+ README.md \
+ Tests \
+ Thrift \
+ Thrift.sln \
+ build.cmd \
+ build.sh \
+ runtests.cmd \
+ runtests.sh
diff --git a/src/jaegertracing/thrift/lib/netcore/README.md b/src/jaegertracing/thrift/lib/netcore/README.md
new file mode 100644
index 000000000..bcd25b927
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/README.md
@@ -0,0 +1,29 @@
+# Apache Thrift netcore
+
+Thrift client library ported to Microsoft .Net Core
+
+# Deprecation notice
+
+Per [THRIFT-4723](https://issues.apache.org/jira/browse/THRIFT-4723), both CSharp and Netcore targets are deprecated
+and will be removed with the next release. Migrate to the [NetStd language target](../netstd/README.md) instead.
+
+# Content
+- Tests/Thrift.PublicInterfaces.Compile.Tests - project for checking public interfaces during adding changes to Thrift library
+- Thrift - Thrift library
+
+# Reused components
+- .NET Standard 1.6 (SDK 2.0.0)
+
+# How to build on Windows
+- Get Thrift IDL compiler executable, add to some folder and add path to this folder into PATH variable
+- Open the Thrift.sln project with Visual Studio and build.
+or
+- Build with scripts
+
+# How to build on Unix
+- Ensure you have .NET Core 2.0.0 SDK installed or use the Ubuntu Xenial docker image
+- Follow common build practice for Thrift: bootstrap, configure, and make
+
+# Known issues
+- In trace logging mode you can see some not important internal exceptions
+
diff --git a/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.IntegrationTests/.gitignore b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.IntegrationTests/.gitignore
new file mode 100644
index 000000000..7254c313a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.IntegrationTests/.gitignore
@@ -0,0 +1,2 @@
+# ignore for autogenerated files
+/Apache
diff --git a/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.IntegrationTests/Protocols/ProtocolsOperationsTests.cs b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.IntegrationTests/Protocols/ProtocolsOperationsTests.cs
new file mode 100644
index 000000000..bc4afa156
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.IntegrationTests/Protocols/ProtocolsOperationsTests.cs
@@ -0,0 +1,502 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using KellermanSoftware.CompareNetObjects;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Thrift.Protocols;
+using Thrift.Protocols.Entities;
+using Thrift.Transports.Client;
+
+namespace Thrift.IntegrationTests.Protocols
+{
+ [TestClass]
+ public class ProtocolsOperationsTests
+ {
+ private readonly CompareLogic _compareLogic = new CompareLogic();
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol), TMessageType.Call)]
+ [DataRow(typeof(TBinaryProtocol), TMessageType.Exception)]
+ [DataRow(typeof(TBinaryProtocol), TMessageType.Oneway)]
+ [DataRow(typeof(TBinaryProtocol), TMessageType.Reply)]
+ [DataRow(typeof(TCompactProtocol), TMessageType.Call)]
+ [DataRow(typeof(TCompactProtocol), TMessageType.Exception)]
+ [DataRow(typeof(TCompactProtocol), TMessageType.Oneway)]
+ [DataRow(typeof(TCompactProtocol), TMessageType.Reply)]
+ [DataRow(typeof(TJsonProtocol), TMessageType.Call)]
+ [DataRow(typeof(TJsonProtocol), TMessageType.Exception)]
+ [DataRow(typeof(TJsonProtocol), TMessageType.Oneway)]
+ [DataRow(typeof(TJsonProtocol), TMessageType.Reply)]
+ public async Task WriteReadMessage_Test(Type protocolType, TMessageType messageType)
+ {
+ var expected = new TMessage(nameof(TMessage), messageType, 1);
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteMessageBeginAsync(expected);
+ await protocol.WriteMessageEndAsync();
+
+ stream.Seek(0, SeekOrigin.Begin);
+
+ var actualMessage = await protocol.ReadMessageBeginAsync();
+ await protocol.ReadMessageEndAsync();
+
+ var result = _compareLogic.Compare(expected, actualMessage);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ [ExpectedException(typeof(Exception))]
+ public async Task WriteReadStruct_Test(Type protocolType)
+ {
+ var expected = new TStruct(nameof(TStruct));
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteStructBeginAsync(expected);
+ await protocol.WriteStructEndAsync();
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadStructBeginAsync();
+ await protocol.ReadStructEndAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ [ExpectedException(typeof(Exception))]
+ public async Task WriteReadField_Test(Type protocolType)
+ {
+ var expected = new TField(nameof(TField), TType.String, 1);
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteFieldBeginAsync(expected);
+ await protocol.WriteFieldEndAsync();
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadFieldBeginAsync();
+ await protocol.ReadFieldEndAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadMap_Test(Type protocolType)
+ {
+ var expected = new TMap(TType.String, TType.String, 1);
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteMapBeginAsync(expected);
+ await protocol.WriteMapEndAsync();
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadMapBeginAsync();
+ await protocol.ReadMapEndAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadList_Test(Type protocolType)
+ {
+ var expected = new TList(TType.String, 1);
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteListBeginAsync(expected);
+ await protocol.WriteListEndAsync();
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadListBeginAsync();
+ await protocol.ReadListEndAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadSet_Test(Type protocolType)
+ {
+ var expected = new TSet(TType.String, 1);
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteSetBeginAsync(expected);
+ await protocol.WriteSetEndAsync();
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadSetBeginAsync();
+ await protocol.ReadSetEndAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadBool_Test(Type protocolType)
+ {
+ var expected = true;
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteBoolAsync(expected);
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadBoolAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadByte_Test(Type protocolType)
+ {
+ var expected = sbyte.MaxValue;
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteByteAsync(expected);
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadByteAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadI16_Test(Type protocolType)
+ {
+ var expected = short.MaxValue;
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteI16Async(expected);
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadI16Async();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadI32_Test(Type protocolType)
+ {
+ var expected = int.MaxValue;
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteI32Async(expected);
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadI32Async();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadI64_Test(Type protocolType)
+ {
+ var expected = long.MaxValue;
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteI64Async(expected);
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadI64Async();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadDouble_Test(Type protocolType)
+ {
+ var expected = double.MaxValue;
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteDoubleAsync(expected);
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadDoubleAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadString_Test(Type protocolType)
+ {
+ var expected = nameof(String);
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteStringAsync(expected);
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadStringAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadBinary_Test(Type protocolType)
+ {
+ var expected = Encoding.UTF8.GetBytes(nameof(String));
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteBinaryAsync(expected);
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadBinaryAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ private static Tuple<Stream, TProtocol> GetProtocolInstance(Type protocolType)
+ {
+ var memoryStream = new MemoryStream();
+ var streamClientTransport = new TStreamClientTransport(memoryStream, memoryStream);
+ var protocol = (TProtocol) Activator.CreateInstance(protocolType, streamClientTransport);
+ return new Tuple<Stream, TProtocol>(memoryStream, protocol);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj
new file mode 100644
index 000000000..f25dac536
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj
@@ -0,0 +1,29 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.0</TargetFramework>
+ <AssemblyName>Thrift.IntegrationTests</AssemblyName>
+ <PackageId>Thrift.IntegrationTests</PackageId>
+ <OutputType>Exe</OutputType>
+ <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
+ <GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
+ <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
+ <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
+ <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
+ <GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="CompareNETObjects" Version="4.3.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
+ <PackageReference Include="MSTest.TestAdapter" Version="1.2.0" />
+ <PackageReference Include="MSTest.TestFramework" Version="1.2.0" />
+ <PackageReference Include="System.ServiceModel.Primitives" Version="4.4.0" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\Thrift\Thrift.csproj" />
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
+ </ItemGroup>
+
+</Project> \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/.gitignore b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/.gitignore
new file mode 100644
index 000000000..ae929a32e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/.gitignore
@@ -0,0 +1,4 @@
+# ignore for autogenerated files
+/ThriftTest
+/Apache
+/Facebook
diff --git a/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/CassandraTest.thrift b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/CassandraTest.thrift
new file mode 100644
index 000000000..4b92720c2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/CassandraTest.thrift
@@ -0,0 +1,705 @@
+#!/usr/local/bin/thrift --java --php --py
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# *** PLEASE REMEMBER TO EDIT THE VERSION CONSTANT WHEN MAKING CHANGES ***
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+#
+# Interface definition for Cassandra Service
+#
+
+namespace netcore Apache.Cassandra.Test
+
+# Thrift.rb has a bug where top-level modules that include modules
+# with the same name are not properly referenced, so we can't do
+# Cassandra::Cassandra::Client.
+namespace rb CassandraThrift
+
+# The API version (NOT the product version), composed as a dot delimited
+# string with major, minor, and patch level components.
+#
+# - Major: Incremented for backward incompatible changes. An example would
+# be changes to the number or disposition of method arguments.
+# - Minor: Incremented for backward compatible changes. An example would
+# be the addition of a new (optional) method.
+# - Patch: Incremented for bug fixes. The patch level should be increased
+# for every edit that doesn't result in a change to major/minor.
+#
+# See the Semantic Versioning Specification (SemVer) http://semver.org.
+const string VERSION = "19.24.0"
+
+
+#
+# data structures
+#
+
+/** Basic unit of data within a ColumnFamily.
+ * @param name, the name by which this column is set and retrieved. Maximum 64KB long.
+ * @param value. The data associated with the name. Maximum 2GB long, but in practice you should limit it to small numbers of MB (since Thrift must read the full value into memory to operate on it).
+ * @param timestamp. The timestamp is used for conflict detection/resolution when two columns with same name need to be compared.
+ * @param ttl. An optional, positive delay (in seconds) after which the column will be automatically deleted.
+ */
+struct Column {
+ 1: required binary name,
+ 2: optional binary value,
+ 3: optional i64 timestamp,
+ 4: optional i32 ttl,
+}
+
+/** A named list of columns.
+ * @param name. see Column.name.
+ * @param columns. A collection of standard Columns. The columns within a super column are defined in an adhoc manner.
+ * Columns within a super column do not have to have matching structures (similarly named child columns).
+ */
+struct SuperColumn {
+ 1: required binary name,
+ 2: required list<Column> columns,
+}
+
+struct CounterColumn {
+ 1: required binary name,
+ 2: required i64 value
+}
+
+struct CounterSuperColumn {
+ 1: required binary name,
+ 2: required list<CounterColumn> columns
+}
+
+/**
+ Methods for fetching rows/records from Cassandra will return either a single instance of ColumnOrSuperColumn or a list
+ of ColumnOrSuperColumns (get_slice()). If you're looking up a SuperColumn (or list of SuperColumns) then the resulting
+ instances of ColumnOrSuperColumn will have the requested SuperColumn in the attribute super_column. For queries resulting
+ in Columns, those values will be in the attribute column. This change was made between 0.3 and 0.4 to standardize on
+ single query methods that may return either a SuperColumn or Column.
+
+ If the query was on a counter column family, you will either get a counter_column (instead of a column) or a
+ counter_super_column (instead of a super_column)
+
+ @param column. The Column returned by get() or get_slice().
+ @param super_column. The SuperColumn returned by get() or get_slice().
+ @param counter_column. The Counterolumn returned by get() or get_slice().
+ @param counter_super_column. The CounterSuperColumn returned by get() or get_slice().
+ */
+struct ColumnOrSuperColumn {
+ 1: optional Column column,
+ 2: optional SuperColumn super_column,
+ 3: optional CounterColumn counter_column,
+ 4: optional CounterSuperColumn counter_super_column
+}
+
+
+#
+# Exceptions
+# (note that internal server errors will raise a TApplicationException, courtesy of Thrift)
+#
+
+/** A specific column was requested that does not exist. */
+exception NotFoundException {
+}
+
+/** Invalid request could mean keyspace or column family does not exist, required parameters are missing, or a parameter is malformed.
+ why contains an associated error message.
+*/
+exception InvalidRequestException {
+ 1: required string why
+}
+
+/** Not all the replicas required could be created and/or read. */
+exception UnavailableException {
+}
+
+/** RPC timeout was exceeded. either a node failed mid-operation, or load was too high, or the requested op was too large. */
+exception TimedOutException {
+}
+
+/** invalid authentication request (invalid keyspace, user does not exist, or credentials invalid) */
+exception AuthenticationException {
+ 1: required string why
+}
+
+/** invalid authorization request (user does not have access to keyspace) */
+exception AuthorizationException {
+ 1: required string why
+}
+
+/** schemas are not in agreement across all nodes */
+exception SchemaDisagreementException {
+}
+
+
+#
+# service api
+#
+/**
+ * The ConsistencyLevel is an enum that controls both read and write
+ * behavior based on the ReplicationFactor of the keyspace. The
+ * different consistency levels have different meanings, depending on
+ * if you're doing a write or read operation.
+ *
+ * If W + R > ReplicationFactor, where W is the number of nodes to
+ * block for on write, and R the number to block for on reads, you
+ * will have strongly consistent behavior; that is, readers will
+ * always see the most recent write. Of these, the most interesting is
+ * to do QUORUM reads and writes, which gives you consistency while
+ * still allowing availability in the face of node failures up to half
+ * of <ReplicationFactor>. Of course if latency is more important than
+ * consistency then you can use lower values for either or both.
+ *
+ * Some ConsistencyLevels (ONE, TWO, THREE) refer to a specific number
+ * of replicas rather than a logical concept that adjusts
+ * automatically with the replication factor. Of these, only ONE is
+ * commonly used; TWO and (even more rarely) THREE are only useful
+ * when you care more about guaranteeing a certain level of
+ * durability, than consistency.
+ *
+ * Write consistency levels make the following guarantees before reporting success to the client:
+ * ANY Ensure that the write has been written once somewhere, including possibly being hinted in a non-target node.
+ * ONE Ensure that the write has been written to at least 1 node's commit log and memory table
+ * TWO Ensure that the write has been written to at least 2 node's commit log and memory table
+ * THREE Ensure that the write has been written to at least 3 node's commit log and memory table
+ * QUORUM Ensure that the write has been written to <ReplicationFactor> / 2 + 1 nodes
+ * LOCAL_QUORUM Ensure that the write has been written to <ReplicationFactor> / 2 + 1 nodes, within the local datacenter (requires NetworkTopologyStrategy)
+ * EACH_QUORUM Ensure that the write has been written to <ReplicationFactor> / 2 + 1 nodes in each datacenter (requires NetworkTopologyStrategy)
+ * ALL Ensure that the write is written to <code>&lt;ReplicationFactor&gt;</code> nodes before responding to the client.
+ *
+ * Read consistency levels make the following guarantees before returning successful results to the client:
+ * ANY Not supported. You probably want ONE instead.
+ * ONE Returns the record obtained from a single replica.
+ * TWO Returns the record with the most recent timestamp once two replicas have replied.
+ * THREE Returns the record with the most recent timestamp once three replicas have replied.
+ * QUORUM Returns the record with the most recent timestamp once a majority of replicas have replied.
+ * LOCAL_QUORUM Returns the record with the most recent timestamp once a majority of replicas within the local datacenter have replied.
+ * EACH_QUORUM Returns the record with the most recent timestamp once a majority of replicas within each datacenter have replied.
+ * ALL Returns the record with the most recent timestamp once all replicas have replied (implies no replica may be down)..
+*/
+enum ConsistencyLevel {
+ ONE = 1,
+ QUORUM = 2,
+ LOCAL_QUORUM = 3,
+ EACH_QUORUM = 4,
+ ALL = 5,
+ ANY = 6,
+ TWO = 7,
+ THREE = 8,
+}
+
+/**
+ ColumnParent is used when selecting groups of columns from the same ColumnFamily. In directory structure terms, imagine
+ ColumnParent as ColumnPath + '/../'.
+
+ See also <a href="cassandra.html#Struct_ColumnPath">ColumnPath</a>
+ */
+struct ColumnParent {
+ 3: required string column_family,
+ 4: optional binary super_column,
+}
+
+/** The ColumnPath is the path to a single column in Cassandra. It might make sense to think of ColumnPath and
+ * ColumnParent in terms of a directory structure.
+ *
+ * ColumnPath is used to looking up a single column.
+ *
+ * @param column_family. The name of the CF of the column being looked up.
+ * @param super_column. The super column name.
+ * @param column. The column name.
+ */
+struct ColumnPath {
+ 3: required string column_family,
+ 4: optional binary super_column,
+ 5: optional binary column,
+}
+
+/**
+ A slice range is a structure that stores basic range, ordering and limit information for a query that will return
+ multiple columns. It could be thought of as Cassandra's version of LIMIT and ORDER BY
+
+ @param start. The column name to start the slice with. This attribute is not required, though there is no default value,
+ and can be safely set to '', i.e., an empty byte array, to start with the first column name. Otherwise, it
+ must a valid value under the rules of the Comparator defined for the given ColumnFamily.
+ @param finish. The column name to stop the slice at. This attribute is not required, though there is no default value,
+ and can be safely set to an empty byte array to not stop until 'count' results are seen. Otherwise, it
+ must also be a valid value to the ColumnFamily Comparator.
+ @param reversed. Whether the results should be ordered in reversed order. Similar to ORDER BY blah DESC in SQL.
+ @param count. How many columns to return. Similar to LIMIT in SQL. May be arbitrarily large, but Thrift will
+ materialize the whole result into memory before returning it to the client, so be aware that you may
+ be better served by iterating through slices by passing the last value of one call in as the 'start'
+ of the next instead of increasing 'count' arbitrarily large.
+ */
+struct SliceRange {
+ 1: required binary start,
+ 2: required binary finish,
+ 3: required bool reversed=0,
+ 4: required i32 count=100,
+}
+
+/**
+ A SlicePredicate is similar to a mathematic predicate (see http://en.wikipedia.org/wiki/Predicate_(mathematical_logic)),
+ which is described as "a property that the elements of a set have in common."
+
+ SlicePredicate's in Cassandra are described with either a list of column_names or a SliceRange. If column_names is
+ specified, slice_range is ignored.
+
+ @param column_name. A list of column names to retrieve. This can be used similar to Memcached's "multi-get" feature
+ to fetch N known column names. For instance, if you know you wish to fetch columns 'Joe', 'Jack',
+ and 'Jim' you can pass those column names as a list to fetch all three at once.
+ @param slice_range. A SliceRange describing how to range, order, and/or limit the slice.
+ */
+struct SlicePredicate {
+ 1: optional list<binary> column_names,
+ 2: optional SliceRange slice_range,
+}
+
+enum IndexOperator {
+ EQ,
+ GTE,
+ GT,
+ LTE,
+ LT
+}
+
+struct IndexExpression {
+ 1: required binary column_name,
+ 2: required IndexOperator op,
+ 3: required binary value,
+}
+
+struct IndexClause {
+ 1: required list<IndexExpression> expressions
+ 2: required binary start_key,
+ 3: required i32 count=100,
+}
+
+/**
+The semantics of start keys and tokens are slightly different.
+Keys are start-inclusive; tokens are start-exclusive. Token
+ranges may also wrap -- that is, the end token may be less
+than the start one. Thus, a range from keyX to keyX is a
+one-element range, but a range from tokenY to tokenY is the
+full ring.
+*/
+struct KeyRange {
+ 1: optional binary start_key,
+ 2: optional binary end_key,
+ 3: optional string start_token,
+ 4: optional string end_token,
+ 5: required i32 count=100
+}
+
+/**
+ A KeySlice is key followed by the data it maps to. A collection of KeySlice is returned by the get_range_slice operation.
+
+ @param key. a row key
+ @param columns. List of data represented by the key. Typically, the list is pared down to only the columns specified by
+ a SlicePredicate.
+ */
+struct KeySlice {
+ 1: required binary key,
+ 2: required list<ColumnOrSuperColumn> columns,
+}
+
+struct KeyCount {
+ 1: required binary key,
+ 2: required i32 count
+}
+
+/**
+ * Note that the timestamp is only optional in case of counter deletion.
+ */
+struct Deletion {
+ 1: optional i64 timestamp,
+ 2: optional binary super_column,
+ 3: optional SlicePredicate predicate,
+}
+
+/**
+ A Mutation is either an insert (represented by filling column_or_supercolumn) or a deletion (represented by filling the deletion attribute).
+ @param column_or_supercolumn. An insert to a column or supercolumn (possibly counter column or supercolumn)
+ @param deletion. A deletion of a column or supercolumn
+*/
+struct Mutation {
+ 1: optional ColumnOrSuperColumn column_or_supercolumn,
+ 2: optional Deletion deletion,
+}
+
+struct EndpointDetails {
+ 1: string host,
+ 2: string datacenter,
+ 3: optional string rack
+}
+
+/**
+ A TokenRange describes part of the Cassandra ring, it is a mapping from a range to
+ endpoints responsible for that range.
+ @param start_token The first token in the range
+ @param end_token The last token in the range
+ @param endpoints The endpoints responsible for the range (listed by their configured listen_address)
+ @param rpc_endpoints The endpoints responsible for the range (listed by their configured rpc_address)
+*/
+struct TokenRange {
+ 1: required string start_token,
+ 2: required string end_token,
+ 3: required list<string> endpoints,
+ 4: optional list<string> rpc_endpoints
+ 5: optional list<EndpointDetails> endpoint_details,
+}
+
+/**
+ Authentication requests can contain any data, dependent on the IAuthenticator used
+*/
+struct AuthenticationRequest {
+ 1: required map<string, string> credentials
+}
+
+enum IndexType {
+ KEYS,
+ CUSTOM
+}
+
+/* describes a column in a column family. */
+struct ColumnDef {
+ 1: required binary name,
+ 2: required string validation_class,
+ 3: optional IndexType index_type,
+ 4: optional string index_name,
+ 5: optional map<string,string> index_options
+}
+
+
+/* describes a column family. */
+struct CfDef {
+ 1: required string keyspace,
+ 2: required string name,
+ 3: optional string column_type="Standard",
+ 5: optional string comparator_type="BytesType",
+ 6: optional string subcomparator_type,
+ 8: optional string comment,
+ 12: optional double read_repair_chance=1.0,
+ 13: optional list<ColumnDef> column_metadata,
+ 14: optional i32 gc_grace_seconds,
+ 15: optional string default_validation_class,
+ 16: optional i32 id,
+ 17: optional i32 min_compaction_threshold,
+ 18: optional i32 max_compaction_threshold,
+ 24: optional bool replicate_on_write,
+ 25: optional double merge_shards_chance,
+ 26: optional string key_validation_class,
+ 28: optional binary key_alias,
+ 29: optional string compaction_strategy,
+ 30: optional map<string,string> compaction_strategy_options,
+ 32: optional map<string,string> compression_options,
+ 33: optional double bloom_filter_fp_chance,
+}
+
+/* describes a keyspace. */
+struct KsDef {
+ 1: required string name,
+ 2: required string strategy_class,
+ 3: optional map<string,string> strategy_options,
+
+ /** @deprecated */
+ 4: optional i32 replication_factor,
+
+ 5: required list<CfDef> cf_defs,
+ 6: optional bool durable_writes=1,
+}
+
+/** CQL query compression */
+enum Compression {
+ GZIP = 1,
+ NONE = 2
+}
+
+enum CqlResultType {
+ ROWS = 1,
+ VOID = 2,
+ INT = 3
+}
+
+/** Row returned from a CQL query */
+struct CqlRow {
+ 1: required binary key,
+ 2: required list<Column> columns
+}
+
+struct CqlMetadata {
+ 1: required map<binary,string> name_types,
+ 2: required map<binary,string> value_types,
+ 3: required string default_name_type,
+ 4: required string default_value_type
+}
+
+struct CqlResult {
+ 1: required CqlResultType type,
+ 2: optional list<CqlRow> rows,
+ 3: optional i32 num,
+ 4: optional CqlMetadata schema
+}
+
+struct CqlPreparedResult {
+ 1: required i32 itemId,
+ 2: required i32 count
+}
+
+
+service Cassandra {
+ # auth methods
+ void login(1: required AuthenticationRequest auth_request) throws (1:AuthenticationException authnx, 2:AuthorizationException authzx),
+
+ # set keyspace
+ void set_keyspace(1: required string keyspace) throws (1:InvalidRequestException ire),
+
+ # retrieval methods
+
+ /**
+ Get the Column or SuperColumn at the given column_path. If no value is present, NotFoundException is thrown. (This is
+ the only method that can throw an exception under non-failure conditions.)
+ */
+ ColumnOrSuperColumn get(1:required binary key,
+ 2:required ColumnPath column_path,
+ 3:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:NotFoundException nfe, 3:UnavailableException ue, 4:TimedOutException te),
+
+ /**
+ Get the group of columns contained by column_parent (either a ColumnFamily name or a ColumnFamily/SuperColumn name
+ pair) specified by the given SlicePredicate. If no matching values are found, an empty list is returned.
+ */
+ list<ColumnOrSuperColumn> get_slice(1:required binary key,
+ 2:required ColumnParent column_parent,
+ 3:required SlicePredicate predicate,
+ 4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /**
+ returns the number of columns matching <code>predicate</code> for a particular <code>key</code>,
+ <code>ColumnFamily</code> and optionally <code>SuperColumn</code>.
+ */
+ i32 get_count(1:required binary key,
+ 2:required ColumnParent column_parent,
+ 3:required SlicePredicate predicate,
+ 4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /**
+ Performs a get_slice for column_parent and predicate for the given keys in parallel.
+ */
+ map<binary,list<ColumnOrSuperColumn>> multiget_slice(1:required list<binary> keys,
+ 2:required ColumnParent column_parent,
+ 3:required SlicePredicate predicate,
+ 4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /**
+ Perform a get_count in parallel on the given list<binary> keys. The return value maps keys to the count found.
+ */
+ map<binary, i32> multiget_count(1:required list<binary> keys,
+ 2:required ColumnParent column_parent,
+ 3:required SlicePredicate predicate,
+ 4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /**
+ returns a subset of columns for a contiguous range of keys.
+ */
+ list<KeySlice> get_range_slices(1:required ColumnParent column_parent,
+ 2:required SlicePredicate predicate,
+ 3:required KeyRange range,
+ 4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /** Returns the subset of columns specified in SlicePredicate for the rows matching the IndexClause */
+ list<KeySlice> get_indexed_slices(1:required ColumnParent column_parent,
+ 2:required IndexClause index_clause,
+ 3:required SlicePredicate column_predicate,
+ 4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ # modification methods
+
+ /**
+ * Insert a Column at the given column_parent.column_family and optional column_parent.super_column.
+ */
+ void insert(1:required binary key,
+ 2:required ColumnParent column_parent,
+ 3:required Column column,
+ 4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /**
+ * Increment or decrement a counter.
+ */
+ void add(1:required binary key,
+ 2:required ColumnParent column_parent,
+ 3:required CounterColumn column,
+ 4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /**
+ Remove data from the row specified by key at the granularity specified by column_path, and the given timestamp. Note
+ that all the values in column_path besides column_path.column_family are truly optional: you can remove the entire
+ row by just specifying the ColumnFamily, or you can remove a SuperColumn or a single Column by specifying those levels too.
+ */
+ void remove(1:required binary key,
+ 2:required ColumnPath column_path,
+ 3:required i64 timestamp,
+ 4:ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /**
+ * Remove a counter at the specified location.
+ * Note that counters have limited support for deletes: if you remove a counter, you must wait to issue any following update
+ * until the delete has reached all the nodes and all of them have been fully compacted.
+ */
+ void remove_counter(1:required binary key,
+ 2:required ColumnPath path,
+ 3:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+
+ /**
+ Mutate many columns or super columns for many row keys. See also: Mutation.
+
+ mutation_map maps key to column family to a list of Mutation objects to take place at that scope.
+ **/
+ void batch_mutate(1:required map<binary, map<string, list<Mutation>>> mutation_map,
+ 2:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /**
+ Truncate will mark and entire column family as deleted.
+ From the user's perspective a successful call to truncate will result complete data deletion from cfname.
+ Internally, however, disk space will not be immediatily released, as with all deletes in cassandra, this one
+ only marks the data as deleted.
+ The operation succeeds only if all hosts in the cluster at available and will throw an UnavailableException if
+ some hosts are down.
+ */
+ void truncate(1:required string cfname)
+ throws (1: InvalidRequestException ire, 2: UnavailableException ue, 3: TimedOutException te),
+
+
+
+ // Meta-APIs -- APIs to get information about the node or cluster,
+ // rather than user data. The nodeprobe program provides usage examples.
+
+ /**
+ * for each schema version present in the cluster, returns a list of nodes at that version.
+ * hosts that do not respond will be under the key DatabaseDescriptor.INITIAL_VERSION.
+ * the cluster is all on the same version if the size of the map is 1.
+ */
+ map<string, list<string>> describe_schema_versions()
+ throws (1: InvalidRequestException ire),
+
+ /** list the defined keyspaces in this cluster */
+ list<KsDef> describe_keyspaces()
+ throws (1:InvalidRequestException ire),
+
+ /** get the cluster name */
+ string describe_cluster_name(),
+
+ /** get the thrift api version */
+ string describe_version(),
+
+ /** get the token ring: a map of ranges to host addresses,
+ represented as a set of TokenRange instead of a map from range
+ to list of endpoints, because you can't use Thrift structs as
+ map keys:
+ https://issues.apache.org/jira/browse/THRIFT-162
+
+ for the same reason, we can't return a set here, even though
+ order is neither important nor predictable. */
+ list<TokenRange> describe_ring(1:required string keyspace)
+ throws (1:InvalidRequestException ire),
+
+ /** returns the partitioner used by this cluster */
+ string describe_partitioner(),
+
+ /** returns the snitch used by this cluster */
+ string describe_snitch(),
+
+ /** describe specified keyspace */
+ KsDef describe_keyspace(1:required string keyspace)
+ throws (1:NotFoundException nfe, 2:InvalidRequestException ire),
+
+ /** experimental API for hadoop/parallel query support.
+ may change violently and without warning.
+
+ returns list of token strings such that first subrange is (list[0], list[1]],
+ next is (list[1], list[2]], etc. */
+ list<string> describe_splits(1:required string cfName,
+ 2:required string start_token,
+ 3:required string end_token,
+ 4:required i32 keys_per_split)
+ throws (1:InvalidRequestException ire),
+
+ /** adds a column family. returns the new schema id. */
+ string system_add_column_family(1:required CfDef cf_def)
+ throws (1:InvalidRequestException ire, 2:SchemaDisagreementException sde),
+
+ /** drops a column family. returns the new schema id. */
+ string system_drop_column_family(1:required string column_family)
+ throws (1:InvalidRequestException ire, 2:SchemaDisagreementException sde),
+
+ /** adds a keyspace and any column families that are part of it. returns the new schema id. */
+ string system_add_keyspace(1:required KsDef ks_def)
+ throws (1:InvalidRequestException ire, 2:SchemaDisagreementException sde),
+
+ /** drops a keyspace and any column families that are part of it. returns the new schema id. */
+ string system_drop_keyspace(1:required string keyspace)
+ throws (1:InvalidRequestException ire, 2:SchemaDisagreementException sde),
+
+ /** updates properties of a keyspace. returns the new schema id. */
+ string system_update_keyspace(1:required KsDef ks_def)
+ throws (1:InvalidRequestException ire, 2:SchemaDisagreementException sde),
+
+ /** updates properties of a column family. returns the new schema id. */
+ string system_update_column_family(1:required CfDef cf_def)
+ throws (1:InvalidRequestException ire, 2:SchemaDisagreementException sde),
+
+ /**
+ * Executes a CQL (Cassandra Query Language) statement and returns a
+ * CqlResult containing the results.
+ */
+ CqlResult execute_cql_query(1:required binary query, 2:required Compression compression)
+ throws (1:InvalidRequestException ire,
+ 2:UnavailableException ue,
+ 3:TimedOutException te,
+ 4:SchemaDisagreementException sde)
+
+
+ /**
+ * Prepare a CQL (Cassandra Query Language) statement by compiling and returning
+ * - the type of CQL statement
+ * - an id token of the compiled CQL stored on the server side.
+ * - a count of the discovered bound markers in the statement
+ */
+ CqlPreparedResult prepare_cql_query(1:required binary query, 2:required Compression compression)
+ throws (1:InvalidRequestException ire)
+
+
+ /**
+ * Executes a prepared CQL (Cassandra Query Language) statement by passing an id token and a list of variables
+ * to bind and returns a CqlResult containing the results.
+ */
+ CqlResult execute_prepared_cql_query(1:required i32 itemId, 2:required list<string> values)
+ throws (1:InvalidRequestException ire,
+ 2:UnavailableException ue,
+ 3:TimedOutException te,
+ 4:SchemaDisagreementException sde)
+
+
+}
diff --git a/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/Properties/AssemblyInfo.cs b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..0bb460ff4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,40 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("The Apache Software Foundation")]
+[assembly: AssemblyProduct("Thrift")]
+[assembly: AssemblyCopyright("The Apache Software Foundation")]
+[assembly: AssemblyTrademark("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+
+[assembly: Guid("d0d3706b-fed5-4cf5-b984-04f448de9d7b")] \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj
new file mode 100644
index 000000000..c4a84a301
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj
@@ -0,0 +1,36 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.0</TargetFramework>
+ <AssemblyName>Thrift.PublicInterfaces.Compile.Tests</AssemblyName>
+ <PackageId>Thrift.PublicInterfaces.Compile.Tests</PackageId>
+ <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
+ <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
+ <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
+ <GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="../../Thrift/Thrift.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <PackageReference Include="System.ServiceModel.Primitives" Version="[4.1.0,)" />
+ </ItemGroup>
+
+ <Target Name="PreBuild" BeforeTargets="_GenerateRestoreProjectSpec;Restore;Compile">
+ <Exec Condition="'$(OS)' == 'Windows_NT'" Command="where thrift" ConsoleToMSBuild="true">
+ <Output TaskParameter="ConsoleOutput" PropertyName="PathToThrift" />
+ </Exec>
+ <Exec Condition="Exists('$(PathToThrift)')" Command="$(PathToThrift) -out $(ProjectDir) -gen netcore:wcf,union,serial,hashcode -r ./CassandraTest.thrift" />
+ <Exec Condition="Exists('thrift')" Command="thrift -out $(ProjectDir) -gen netcore:wcf,union,serial,hashcode -r ./CassandraTest.thrift" />
+ <Exec Condition="Exists('$(ProjectDir)/../../../compiler/cpp/thrift')" Command="$(ProjectDir)/../../../compiler/cpp/thrift -out $(ProjectDir) -gen netcore:wcf,union,serial,hashcode -r ./CassandraTest.thrift" />
+ <Exec Condition="Exists('$(PathToThrift)')" Command="$(PathToThrift) -out $(ProjectDir) -gen netcore:wcf,union,serial,hashcode -r ./../../../../test/ThriftTest.thrift" />
+ <Exec Condition="Exists('thrift')" Command="thrift -out $(ProjectDir) -gen netcore:wcf,union,serial,hashcode -r ./../../../../test/ThriftTest.thrift" />
+ <Exec Condition="Exists('$(ProjectDir)/../../../compiler/cpp/thrift')" Command="$(ProjectDir)/../../../compiler/cpp/thrift -out $(ProjectDir) -gen netcore:wcf,union,serial,hashcode -r ./../../../../test/ThriftTest.thrift" />
+ <Exec Condition="Exists('$(PathToThrift)')" Command="$(PathToThrift) -out $(ProjectDir) -gen netcore:wcf,union,serial,hashcode -r ./../../../../contrib/fb303/if/fb303.thrift" />
+ <Exec Condition="Exists('thrift')" Command="thrift -out $(ProjectDir) -gen netcore:wcf,union,serial,hashcode -r ./../../../../contrib/fb303/if/fb303.thrift" />
+ <Exec Condition="Exists('$(ProjectDir)/../../../compiler/cpp/thrift')" Command="$(ProjectDir)/../../../compiler/cpp/thrift -out $(ProjectDir) -gen netcore:wcf,union,serial,hashcode -r ./../../../../contrib/fb303/if/fb303.thrift" />
+ </Target>
+
+</Project>
diff --git a/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Collections/TCollectionsTests.cs b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Collections/TCollectionsTests.cs
new file mode 100644
index 000000000..1be99b48f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Collections/TCollectionsTests.cs
@@ -0,0 +1,83 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Thrift.Collections;
+
+namespace Thrift.Tests.Collections
+{
+ // ReSharper disable once InconsistentNaming
+ [TestClass]
+ public class TCollectionsTests
+ {
+ //TODO: Add tests for IEnumerable with objects and primitive values inside
+
+ [TestMethod]
+ public void TCollection_Equals_Primitive_Test()
+ {
+ var collection1 = new List<int> {1,2,3};
+ var collection2 = new List<int> {1,2,3};
+
+ var result = TCollections.Equals(collection1, collection2);
+
+ Assert.IsTrue(result);
+ }
+
+ [TestMethod]
+ public void TCollection_Equals_Primitive_Different_Test()
+ {
+ var collection1 = new List<int> { 1, 2, 3 };
+ var collection2 = new List<int> { 1, 2 };
+
+ var result = TCollections.Equals(collection1, collection2);
+
+ Assert.IsFalse(result);
+ }
+
+ [TestMethod]
+ public void TCollection_Equals_Objects_Test()
+ {
+ var collection1 = new List<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
+ var collection2 = new List<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
+
+ var result = TCollections.Equals(collection1, collection2);
+
+ // references to different collections
+ Assert.IsFalse(result);
+ }
+
+ [TestMethod]
+ public void TCollection_Equals_OneAndTheSameObject_Test()
+ {
+ var collection1 = new List<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
+ var collection2 = collection1;
+
+ var result = TCollections.Equals(collection1, collection2);
+
+ // references to one and the same collection
+ Assert.IsTrue(result);
+ }
+
+ private class ExampleClass
+ {
+ public int X { get; set; }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Collections/THashSetTests.cs b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Collections/THashSetTests.cs
new file mode 100644
index 000000000..8de573eee
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Collections/THashSetTests.cs
@@ -0,0 +1,71 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Thrift.Collections;
+
+namespace Thrift.Tests.Collections
+{
+ // ReSharper disable once InconsistentNaming
+ [TestClass]
+ public class THashSetTests
+ {
+ [TestMethod]
+ public void THashSet_Equals_Primitive_Test()
+ {
+ const int value = 1;
+
+ var hashSet = new THashSet<int> {value};
+
+ Assert.IsTrue(hashSet.Contains(value));
+
+ hashSet.Remove(value);
+
+ Assert.IsTrue(hashSet.Count == 0);
+
+ hashSet.Add(value);
+
+ Assert.IsTrue(hashSet.Contains(value));
+
+ hashSet.Clear();
+
+ Assert.IsTrue(hashSet.Count == 0);
+
+ var newArr = new int[1];
+ hashSet.Add(value);
+ hashSet.CopyTo(newArr, 0);
+
+ Assert.IsTrue(newArr.Contains(value));
+
+ var en = hashSet.GetEnumerator();
+ en.MoveNext();
+
+ Assert.IsTrue((int)en.Current == value);
+
+ using (var ien = ((IEnumerable<int>)hashSet).GetEnumerator())
+ {
+ ien.MoveNext();
+
+ Assert.IsTrue(ien.Current == value);
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Protocols/TJsonProtocolHelperTests.cs b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Protocols/TJsonProtocolHelperTests.cs
new file mode 100644
index 000000000..cdc8317e2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Protocols/TJsonProtocolHelperTests.cs
@@ -0,0 +1,172 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Thrift.Protocols;
+using Thrift.Protocols.Entities;
+using Thrift.Protocols.Utilities;
+
+namespace Thrift.Tests.Protocols
+{
+ [TestClass]
+ public class TJSONProtocolHelperTests
+ {
+ [TestMethod]
+ public void GetTypeNameForTypeId_Test()
+ {
+ // input/output
+ var sets = new List<Tuple<TType, byte[]>>
+ {
+ new Tuple<TType, byte[]>(TType.Bool, TJSONProtocolConstants.TypeNames.NameBool),
+ new Tuple<TType, byte[]>(TType.Byte, TJSONProtocolConstants.TypeNames.NameByte),
+ new Tuple<TType, byte[]>(TType.I16, TJSONProtocolConstants.TypeNames.NameI16),
+ new Tuple<TType, byte[]>(TType.I32, TJSONProtocolConstants.TypeNames.NameI32),
+ new Tuple<TType, byte[]>(TType.I64, TJSONProtocolConstants.TypeNames.NameI64),
+ new Tuple<TType, byte[]>(TType.Double, TJSONProtocolConstants.TypeNames.NameDouble),
+ new Tuple<TType, byte[]>(TType.String, TJSONProtocolConstants.TypeNames.NameString),
+ new Tuple<TType, byte[]>(TType.Struct, TJSONProtocolConstants.TypeNames.NameStruct),
+ new Tuple<TType, byte[]>(TType.Map, TJSONProtocolConstants.TypeNames.NameMap),
+ new Tuple<TType, byte[]>(TType.Set, TJSONProtocolConstants.TypeNames.NameSet),
+ new Tuple<TType, byte[]>(TType.List, TJSONProtocolConstants.TypeNames.NameList),
+ };
+
+ foreach (var t in sets)
+ {
+ Assert.IsTrue(TJSONProtocolHelper.GetTypeNameForTypeId(t.Item1) == t.Item2, $"Wrong mapping of TypeName {t.Item2} to TType: {t.Item1}");
+ }
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(TProtocolException))]
+ public void GetTypeNameForTypeId_TStop_Test()
+ {
+ TJSONProtocolHelper.GetTypeNameForTypeId(TType.Stop);
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(TProtocolException))]
+ public void GetTypeNameForTypeId_NonExistingTType_Test()
+ {
+ TJSONProtocolHelper.GetTypeNameForTypeId((TType)100);
+ }
+
+ [TestMethod]
+ public void GetTypeIdForTypeName_Test()
+ {
+ // input/output
+ var sets = new List<Tuple<TType, byte[]>>
+ {
+ new Tuple<TType, byte[]>(TType.Bool, TJSONProtocolConstants.TypeNames.NameBool),
+ new Tuple<TType, byte[]>(TType.Byte, TJSONProtocolConstants.TypeNames.NameByte),
+ new Tuple<TType, byte[]>(TType.I16, TJSONProtocolConstants.TypeNames.NameI16),
+ new Tuple<TType, byte[]>(TType.I32, TJSONProtocolConstants.TypeNames.NameI32),
+ new Tuple<TType, byte[]>(TType.I64, TJSONProtocolConstants.TypeNames.NameI64),
+ new Tuple<TType, byte[]>(TType.Double, TJSONProtocolConstants.TypeNames.NameDouble),
+ new Tuple<TType, byte[]>(TType.String, TJSONProtocolConstants.TypeNames.NameString),
+ new Tuple<TType, byte[]>(TType.Struct, TJSONProtocolConstants.TypeNames.NameStruct),
+ new Tuple<TType, byte[]>(TType.Map, TJSONProtocolConstants.TypeNames.NameMap),
+ new Tuple<TType, byte[]>(TType.Set, TJSONProtocolConstants.TypeNames.NameSet),
+ new Tuple<TType, byte[]>(TType.List, TJSONProtocolConstants.TypeNames.NameList),
+ };
+
+ foreach (var t in sets)
+ {
+ Assert.IsTrue(TJSONProtocolHelper.GetTypeIdForTypeName(t.Item2) == t.Item1, $"Wrong mapping of TypeName {t.Item2} to TType: {t.Item1}");
+ }
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(TProtocolException))]
+ public void GetTypeIdForTypeName_TStopTypeName_Test()
+ {
+ TJSONProtocolHelper.GetTypeIdForTypeName(new []{(byte)TType.Stop, (byte)TType.Stop});
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(TProtocolException))]
+ public void GetTypeIdForTypeName_NonExistingTypeName_Test()
+ {
+ TJSONProtocolHelper.GetTypeIdForTypeName(new byte[]{100});
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(TProtocolException))]
+ public void GetTypeIdForTypeName_EmptyName_Test()
+ {
+ TJSONProtocolHelper.GetTypeIdForTypeName(new byte[] {});
+ }
+
+ [TestMethod]
+ public void IsJsonNumeric_Test()
+ {
+ // input/output
+ var correctJsonNumeric = "+-.0123456789Ee";
+ var incorrectJsonNumeric = "AaBcDd/*\\";
+
+ var sets = correctJsonNumeric.Select(ch => new Tuple<byte, bool>((byte) ch, true)).ToList();
+ sets.AddRange(incorrectJsonNumeric.Select(ch => new Tuple<byte, bool>((byte) ch, false)));
+
+ foreach (var t in sets)
+ {
+ Assert.IsTrue(TJSONProtocolHelper.IsJsonNumeric(t.Item1) == t.Item2, $"Wrong mapping of Char {t.Item1} to bool: {t.Item2}");
+ }
+ }
+
+ [TestMethod]
+ public void ToHexVal_Test()
+ {
+ // input/output
+ var chars = "0123456789abcdef";
+ var expectedHexValues = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+
+ var sets = chars.Select((ch, i) => new Tuple<char, byte>(ch, expectedHexValues[i])).ToList();
+
+ foreach (var t in sets)
+ {
+ var actualResult = TJSONProtocolHelper.ToHexVal((byte)t.Item1);
+ Assert.IsTrue(actualResult == t.Item2, $"Wrong mapping of char byte {t.Item1} to it expected hex value: {t.Item2}. Actual hex value: {actualResult}");
+ }
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(TProtocolException))]
+ public void ToHexVal_WrongInputChar_Test()
+ {
+ TJSONProtocolHelper.ToHexVal((byte)'s');
+ }
+
+ [TestMethod]
+ public void ToHexChar_Test()
+ {
+ // input/output
+ var hexValues = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+ var expectedChars = "0123456789abcdef";
+
+
+ var sets = hexValues.Select((hv, i) => new Tuple<byte, char>(hv, expectedChars[i])).ToList();
+
+ foreach (var t in sets)
+ {
+ var actualResult = TJSONProtocolHelper.ToHexChar(t.Item1);
+ Assert.IsTrue(actualResult == t.Item2, $"Wrong mapping of hex value {t.Item1} to it expected char: {t.Item2}. Actual hex value: {actualResult}");
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Protocols/TJsonProtocolTests.cs b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Protocols/TJsonProtocolTests.cs
new file mode 100644
index 000000000..523736086
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Protocols/TJsonProtocolTests.cs
@@ -0,0 +1,67 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using NSubstitute;
+using Thrift.Protocols;
+using Thrift.Protocols.Entities;
+using Thrift.Transports;
+using Thrift.Transports.Client;
+
+namespace Thrift.Tests.Protocols
+{
+ // ReSharper disable once InconsistentNaming
+ [TestClass]
+ public class TJSONProtocolTests
+ {
+ [TestMethod]
+ public void TJSONProtocol_Can_Create_Instance_Test()
+ {
+ var httpClientTransport = Substitute.For<THttpClientTransport>(new Uri("http://localhost"), null);
+
+ var result = new TJSONProtocolWrapper(httpClientTransport);
+
+ Assert.IsNotNull(result);
+ Assert.IsNotNull(result.WrappedContext);
+ Assert.IsNotNull(result.WrappedReader);
+ Assert.IsNotNull(result.Transport);
+ Assert.IsTrue(result.WrappedRecursionDepth == 0);
+ Assert.IsTrue(result.WrappedRecursionLimit == TProtocol.DefaultRecursionDepth);
+
+ Assert.IsTrue(result.Transport.Equals(httpClientTransport));
+ Assert.IsTrue(result.WrappedContext.GetType().Name.Equals("JSONBaseContext", StringComparison.OrdinalIgnoreCase));
+ Assert.IsTrue(result.WrappedReader.GetType().Name.Equals("LookaheadReader", StringComparison.OrdinalIgnoreCase));
+ }
+
+ private class TJSONProtocolWrapper : TJsonProtocol
+ {
+ public TJSONProtocolWrapper(TClientTransport trans) : base(trans)
+ {
+ }
+
+ public object WrappedContext => Context;
+ public object WrappedReader => Reader;
+ public int WrappedRecursionDepth => RecursionDepth;
+ public int WrappedRecursionLimit => RecursionLimit;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Thrift.Tests.csproj b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Thrift.Tests.csproj
new file mode 100644
index 000000000..e46f16522
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Tests/Thrift.Tests/Thrift.Tests.csproj
@@ -0,0 +1,18 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.0</TargetFramework>
+ </PropertyGroup>
+ <ItemGroup>
+ <PackageReference Include="CompareNETObjects" Version="4.3.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
+ <PackageReference Include="MSTest.TestAdapter" Version="1.2.0" />
+ <PackageReference Include="MSTest.TestFramework" Version="1.2.0" />
+ <PackageReference Include="NSubstitute" Version="3.1.0" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\Thrift\Thrift.csproj" />
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift.sln b/src/jaegertracing/thrift/lib/netcore/Thrift.sln
new file mode 100644
index 000000000..fe30aa5c6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift.sln
@@ -0,0 +1,85 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26730.12
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{F51FC4DA-CAC0-48B1-A069-B1712BCAA5BE}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift", "Thrift\Thrift.csproj", "{D85F572F-7D80-40A4-9A9B-2731ED187C24}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.IntegrationTests", "Tests\Thrift.IntegrationTests\Thrift.IntegrationTests.csproj", "{9F9A11BF-3C95-4E80-AFBF-768541996844}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.Tests", "Tests\Thrift.Tests\Thrift.Tests.csproj", "{75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.PublicInterfaces.Compile.Tests", "Tests\Thrift.PublicInterfaces.Compile.Tests\Thrift.PublicInterfaces.Compile.Tests.csproj", "{A429F05B-F511-45EF-AE7B-04E1AE9C9367}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Debug|x64.Build.0 = Debug|Any CPU
+ {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Debug|x86.Build.0 = Debug|Any CPU
+ {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Release|x64.ActiveCfg = Release|Any CPU
+ {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Release|x64.Build.0 = Release|Any CPU
+ {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Release|x86.ActiveCfg = Release|Any CPU
+ {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Release|x86.Build.0 = Release|Any CPU
+ {9F9A11BF-3C95-4E80-AFBF-768541996844}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9F9A11BF-3C95-4E80-AFBF-768541996844}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9F9A11BF-3C95-4E80-AFBF-768541996844}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {9F9A11BF-3C95-4E80-AFBF-768541996844}.Debug|x64.Build.0 = Debug|Any CPU
+ {9F9A11BF-3C95-4E80-AFBF-768541996844}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {9F9A11BF-3C95-4E80-AFBF-768541996844}.Debug|x86.Build.0 = Debug|Any CPU
+ {9F9A11BF-3C95-4E80-AFBF-768541996844}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9F9A11BF-3C95-4E80-AFBF-768541996844}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9F9A11BF-3C95-4E80-AFBF-768541996844}.Release|x64.ActiveCfg = Release|Any CPU
+ {9F9A11BF-3C95-4E80-AFBF-768541996844}.Release|x64.Build.0 = Release|Any CPU
+ {9F9A11BF-3C95-4E80-AFBF-768541996844}.Release|x86.ActiveCfg = Release|Any CPU
+ {9F9A11BF-3C95-4E80-AFBF-768541996844}.Release|x86.Build.0 = Release|Any CPU
+ {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Debug|x64.Build.0 = Debug|Any CPU
+ {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Debug|x86.Build.0 = Debug|Any CPU
+ {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Release|x64.ActiveCfg = Release|Any CPU
+ {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Release|x64.Build.0 = Release|Any CPU
+ {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Release|x86.ActiveCfg = Release|Any CPU
+ {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Release|x86.Build.0 = Release|Any CPU
+ {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Debug|x64.Build.0 = Debug|Any CPU
+ {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Debug|x86.Build.0 = Debug|Any CPU
+ {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Release|x64.ActiveCfg = Release|Any CPU
+ {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Release|x64.Build.0 = Release|Any CPU
+ {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Release|x86.ActiveCfg = Release|Any CPU
+ {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {9F9A11BF-3C95-4E80-AFBF-768541996844} = {F51FC4DA-CAC0-48B1-A069-B1712BCAA5BE}
+ {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1} = {F51FC4DA-CAC0-48B1-A069-B1712BCAA5BE}
+ {A429F05B-F511-45EF-AE7B-04E1AE9C9367} = {F51FC4DA-CAC0-48B1-A069-B1712BCAA5BE}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {FD20BC4A-0109-41D8-8C0C-893E784D7EF9}
+ EndGlobalSection
+EndGlobal
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Collections/TCollections.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Collections/TCollections.cs
new file mode 100644
index 000000000..147bfc7d3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Collections/TCollections.cs
@@ -0,0 +1,101 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Collections;
+
+namespace Thrift.Collections
+{
+ // ReSharper disable once InconsistentNaming
+ public class TCollections
+ {
+ /// <summary>
+ /// This will return true if the two collections are value-wise the same.
+ /// If the collection contains a collection, the collections will be compared using this method.
+ /// </summary>
+ public static bool Equals(IEnumerable first, IEnumerable second)
+ {
+ if (first == null && second == null)
+ {
+ return true;
+ }
+
+ if (first == null || second == null)
+ {
+ return false;
+ }
+
+ var fiter = first.GetEnumerator();
+ var siter = second.GetEnumerator();
+
+ var fnext = fiter.MoveNext();
+ var snext = siter.MoveNext();
+
+ while (fnext && snext)
+ {
+ var fenum = fiter.Current as IEnumerable;
+ var senum = siter.Current as IEnumerable;
+
+ if (fenum != null && senum != null)
+ {
+ if (!Equals(fenum, senum))
+ {
+ return false;
+ }
+ }
+ else if (fenum == null ^ senum == null)
+ {
+ return false;
+ }
+ else if (!Equals(fiter.Current, siter.Current))
+ {
+ return false;
+ }
+
+ fnext = fiter.MoveNext();
+ snext = siter.MoveNext();
+ }
+
+ return fnext == snext;
+ }
+
+ /// <summary>
+ /// This returns a hashcode based on the value of the enumerable.
+ /// </summary>
+ public static int GetHashCode(IEnumerable enumerable)
+ {
+ if (enumerable == null)
+ {
+ return 0;
+ }
+
+ var hashcode = 0;
+
+ foreach (var obj in enumerable)
+ {
+ var enum2 = obj as IEnumerable;
+ var objHash = enum2 == null ? obj.GetHashCode() : GetHashCode(enum2);
+
+ unchecked
+ {
+ hashcode = (hashcode*397) ^ (objHash);
+ }
+ }
+
+ return hashcode;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Collections/THashSet.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Collections/THashSet.cs
new file mode 100644
index 000000000..011f0a0d6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Collections/THashSet.cs
@@ -0,0 +1,67 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Thrift.Collections
+{
+ // ReSharper disable once InconsistentNaming
+ public class THashSet<T> : ICollection<T>
+ {
+ private readonly HashSet<T> _set = new HashSet<T>();
+
+ public int Count => _set.Count;
+
+ public bool IsReadOnly => false;
+
+ public void Add(T item)
+ {
+ _set.Add(item);
+ }
+
+ public void Clear()
+ {
+ _set.Clear();
+ }
+
+ public bool Contains(T item)
+ {
+ return _set.Contains(item);
+ }
+
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ _set.CopyTo(array, arrayIndex);
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return _set.GetEnumerator();
+ }
+
+ IEnumerator<T> IEnumerable<T>.GetEnumerator()
+ {
+ return ((IEnumerable<T>) _set).GetEnumerator();
+ }
+
+ public bool Remove(T item)
+ {
+ return _set.Remove(item);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/ITAsyncProcessor.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/ITAsyncProcessor.cs
new file mode 100644
index 000000000..db8e40aef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/ITAsyncProcessor.cs
@@ -0,0 +1,29 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocols;
+
+namespace Thrift
+{
+ public interface ITAsyncProcessor
+ {
+ Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot);
+ Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken);
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/ITProcessorFactory.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/ITProcessorFactory.cs
new file mode 100644
index 000000000..5133e5c48
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/ITProcessorFactory.cs
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using Thrift.Server;
+using Thrift.Transports;
+
+namespace Thrift
+{
+ // ReSharper disable once InconsistentNaming
+ public interface ITProcessorFactory
+ {
+ ITAsyncProcessor GetAsyncProcessor(TClientTransport trans, TBaseServer baseServer = null);
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Properties/AssemblyInfo.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..0f433b600
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Properties/AssemblyInfo.cs
@@ -0,0 +1,56 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+
+[assembly: AssemblyTitle("Thrift")]
+[assembly: AssemblyDescription("C# .NET Core bindings for the Apache Thrift RPC system")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("The Apache Software Foundation")]
+[assembly: AssemblyProduct("Thrift")]
+[assembly: AssemblyCopyright("The Apache Software Foundation")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+//@TODO where to put License information?
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a exType in this assembly from
+// COM, set the ComVisible attribute to true on that exType.
+
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+
+[assembly: Guid("df3f8ef0-e0a3-4c86-a65b-8ec84e016b1d")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+
+[assembly: AssemblyVersion("0.13.0.0")]
+[assembly: AssemblyFileVersion("0.13.0.0")]
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TField.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TField.cs
new file mode 100644
index 000000000..d311535e7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TField.cs
@@ -0,0 +1,37 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocols.Entities
+{
+ // ReSharper disable once InconsistentNaming
+ public struct TField
+ {
+ public TField(string name, TType type, short id)
+ {
+ Name = name;
+ Type = type;
+ ID = id;
+ }
+
+ public string Name { get; set; }
+
+ public TType Type { get; set; }
+
+ // ReSharper disable once InconsistentNaming - do not rename - it used for generation
+ public short ID { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TList.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TList.cs
new file mode 100644
index 000000000..ce232207c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TList.cs
@@ -0,0 +1,33 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocols.Entities
+{
+ // ReSharper disable once InconsistentNaming
+ public struct TList
+ {
+ public TList(TType elementType, int count)
+ {
+ ElementType = elementType;
+ Count = count;
+ }
+
+ public TType ElementType { get; set; }
+
+ public int Count { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TMap.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TMap.cs
new file mode 100644
index 000000000..9195593db
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TMap.cs
@@ -0,0 +1,36 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocols.Entities
+{
+ // ReSharper disable once InconsistentNaming
+ public struct TMap
+ {
+ public TMap(TType keyType, TType valueType, int count)
+ {
+ KeyType = keyType;
+ ValueType = valueType;
+ Count = count;
+ }
+
+ public TType KeyType { get; set; }
+
+ public TType ValueType { get; set; }
+
+ public int Count { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TMessage.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TMessage.cs
new file mode 100644
index 000000000..17f49298f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TMessage.cs
@@ -0,0 +1,37 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocols.Entities
+{
+ // ReSharper disable once InconsistentNaming
+ public struct TMessage
+ {
+ public TMessage(string name, TMessageType type, int seqid)
+ {
+ Name = name;
+ Type = type;
+ SeqID = seqid;
+ }
+
+ public string Name { get; set; }
+
+ public TMessageType Type { get; set; }
+
+ // ReSharper disable once InconsistentNaming - do not rename - it used for generation
+ public int SeqID { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TMessageType.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TMessageType.cs
new file mode 100644
index 000000000..d7b9a2275
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TMessageType.cs
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocols.Entities
+{
+ // ReSharper disable once InconsistentNaming
+ public enum TMessageType
+ {
+ Call = 1,
+ Reply = 2,
+ Exception = 3,
+ Oneway = 4
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TSet.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TSet.cs
new file mode 100644
index 000000000..a583b54a6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TSet.cs
@@ -0,0 +1,38 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocols.Entities
+{
+ // ReSharper disable once InconsistentNaming
+ public struct TSet
+ {
+ public TSet(TType elementType, int count)
+ {
+ ElementType = elementType;
+ Count = count;
+ }
+
+ public TSet(TList list)
+ : this(list.ElementType, list.Count)
+ {
+ }
+
+ public TType ElementType { get; set; }
+
+ public int Count { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TStruct.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TStruct.cs
new file mode 100644
index 000000000..a28dcc3d0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TStruct.cs
@@ -0,0 +1,30 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocols.Entities
+{
+ // ReSharper disable once InconsistentNaming
+ public struct TStruct
+ {
+ public TStruct(string name)
+ {
+ Name = name;
+ }
+
+ public string Name { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TType.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TType.cs
new file mode 100644
index 000000000..ebe781c95
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Entities/TType.cs
@@ -0,0 +1,37 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocols.Entities
+{
+ // ReSharper disable once InconsistentNaming
+ public enum TType : byte
+ {
+ Stop = 0,
+ Void = 1,
+ Bool = 2,
+ Byte = 3,
+ Double = 4,
+ I16 = 6,
+ I32 = 8,
+ I64 = 10,
+ String = 11,
+ Struct = 12,
+ Map = 13,
+ Set = 14,
+ List = 15
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/ITProtocolFactory.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/ITProtocolFactory.cs
new file mode 100644
index 000000000..ecc5cc494
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/ITProtocolFactory.cs
@@ -0,0 +1,27 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using Thrift.Transports;
+
+namespace Thrift.Protocols
+{
+ // ReSharper disable once InconsistentNaming
+ public interface ITProtocolFactory
+ {
+ TProtocol GetProtocol(TClientTransport trans);
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TAbstractBase.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TAbstractBase.cs
new file mode 100644
index 000000000..4e18681bf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TAbstractBase.cs
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Protocols
+{
+ // ReSharper disable once InconsistentNaming
+ public interface TAbstractBase
+ {
+ Task WriteAsync(TProtocol tProtocol, CancellationToken cancellationToken);
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TBase.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TBase.cs
new file mode 100644
index 000000000..014e1aee8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TBase.cs
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Protocols
+{
+ // ReSharper disable once InconsistentNaming
+ public interface TBase : TAbstractBase
+ {
+ Task ReadAsync(TProtocol tProtocol, CancellationToken cancellationToken);
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TBinaryProtocol.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TBinaryProtocol.cs
new file mode 100644
index 000000000..deec85c42
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TBinaryProtocol.cs
@@ -0,0 +1,613 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocols.Entities;
+using Thrift.Transports;
+
+namespace Thrift.Protocols
+{
+ // ReSharper disable once InconsistentNaming
+ public class TBinaryProtocol : TProtocol
+ {
+ //TODO: Unit tests
+ //TODO: Localization
+ //TODO: pragma
+
+ protected const uint VersionMask = 0xffff0000;
+ protected const uint Version1 = 0x80010000;
+
+ protected bool StrictRead;
+ protected bool StrictWrite;
+
+ public TBinaryProtocol(TClientTransport trans)
+ : this(trans, false, true)
+ {
+ }
+
+ public TBinaryProtocol(TClientTransport trans, bool strictRead, bool strictWrite)
+ : base(trans)
+ {
+ StrictRead = strictRead;
+ StrictWrite = strictWrite;
+ }
+
+ public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ if (StrictWrite)
+ {
+ var version = Version1 | (uint) message.Type;
+ await WriteI32Async((int) version, cancellationToken);
+ await WriteStringAsync(message.Name, cancellationToken);
+ await WriteI32Async(message.SeqID, cancellationToken);
+ }
+ else
+ {
+ await WriteStringAsync(message.Name, cancellationToken);
+ await WriteByteAsync((sbyte) message.Type, cancellationToken);
+ await WriteI32Async(message.SeqID, cancellationToken);
+ }
+ }
+
+ public override async Task WriteMessageEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteStructEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await WriteByteAsync((sbyte) field.Type, cancellationToken);
+ await WriteI16Async(field.ID, cancellationToken);
+ }
+
+ public override async Task WriteFieldEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteFieldStopAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await WriteByteAsync((sbyte) TType.Stop, cancellationToken);
+ }
+
+ public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await WriteByteAsync((sbyte) map.KeyType, cancellationToken);
+ await WriteByteAsync((sbyte) map.ValueType, cancellationToken);
+ await WriteI32Async(map.Count, cancellationToken);
+ }
+
+ public override async Task WriteMapEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await WriteByteAsync((sbyte) list.ElementType, cancellationToken);
+ await WriteI32Async(list.Count, cancellationToken);
+ }
+
+ public override async Task WriteListEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await WriteByteAsync((sbyte) set.ElementType, cancellationToken);
+ await WriteI32Async(set.Count, cancellationToken);
+ }
+
+ public override async Task WriteSetEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await WriteByteAsync(b ? (sbyte) 1 : (sbyte) 0, cancellationToken);
+ }
+
+ protected internal static byte[] CreateWriteByte(sbyte b)
+ {
+ var bout = new byte[1];
+
+ bout[0] = (byte) b;
+
+ return bout;
+ }
+
+ public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ var bout = CreateWriteByte(b);
+ await Trans.WriteAsync(bout, 0, 1, cancellationToken);
+ }
+
+ protected internal static byte[] CreateWriteI16(short s)
+ {
+ var i16Out = new byte[2];
+
+ i16Out[0] = (byte) (0xff & (s >> 8));
+ i16Out[1] = (byte) (0xff & s);
+
+ return i16Out;
+ }
+
+ public override async Task WriteI16Async(short i16, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ var i16Out = CreateWriteI16(i16);
+ await Trans.WriteAsync(i16Out, 0, 2, cancellationToken);
+ }
+
+ protected internal static byte[] CreateWriteI32(int i32)
+ {
+ var i32Out = new byte[4];
+
+ i32Out[0] = (byte) (0xff & (i32 >> 24));
+ i32Out[1] = (byte) (0xff & (i32 >> 16));
+ i32Out[2] = (byte) (0xff & (i32 >> 8));
+ i32Out[3] = (byte) (0xff & i32);
+
+ return i32Out;
+ }
+
+ public override async Task WriteI32Async(int i32, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ var i32Out = CreateWriteI32(i32);
+ await Trans.WriteAsync(i32Out, 0, 4, cancellationToken);
+ }
+
+ protected internal static byte[] CreateWriteI64(long i64)
+ {
+ var i64Out = new byte[8];
+
+ i64Out[0] = (byte) (0xff & (i64 >> 56));
+ i64Out[1] = (byte) (0xff & (i64 >> 48));
+ i64Out[2] = (byte) (0xff & (i64 >> 40));
+ i64Out[3] = (byte) (0xff & (i64 >> 32));
+ i64Out[4] = (byte) (0xff & (i64 >> 24));
+ i64Out[5] = (byte) (0xff & (i64 >> 16));
+ i64Out[6] = (byte) (0xff & (i64 >> 8));
+ i64Out[7] = (byte) (0xff & i64);
+
+ return i64Out;
+ }
+
+ public override async Task WriteI64Async(long i64, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ var i64Out = CreateWriteI64(i64);
+ await Trans.WriteAsync(i64Out, 0, 8, cancellationToken);
+ }
+
+ public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await WriteI64Async(BitConverter.DoubleToInt64Bits(d), cancellationToken);
+ }
+
+ public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await WriteI32Async(bytes.Length, cancellationToken);
+ await Trans.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
+ }
+
+ public override async Task<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TMessage>(cancellationToken);
+ }
+
+ var message = new TMessage();
+ var size = await ReadI32Async(cancellationToken);
+ if (size < 0)
+ {
+ var version = (uint) size & VersionMask;
+ if (version != Version1)
+ {
+ throw new TProtocolException(TProtocolException.BAD_VERSION,
+ $"Bad version in ReadMessageBegin: {version}");
+ }
+ message.Type = (TMessageType) (size & 0x000000ff);
+ message.Name = await ReadStringAsync(cancellationToken);
+ message.SeqID = await ReadI32Async(cancellationToken);
+ }
+ else
+ {
+ if (StrictRead)
+ {
+ throw new TProtocolException(TProtocolException.BAD_VERSION,
+ "Missing version in ReadMessageBegin, old client?");
+ }
+ message.Name = await ReadStringBodyAsync(size, cancellationToken);
+ message.Type = (TMessageType) await ReadByteAsync(cancellationToken);
+ message.SeqID = await ReadI32Async(cancellationToken);
+ }
+ return message;
+ }
+
+ public override async Task ReadMessageEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+
+ //TODO: no read from internal transport?
+ return new TStruct();
+ }
+
+ public override async Task ReadStructEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TField>(cancellationToken);
+ }
+
+ var field = new TField
+ {
+ Type = (TType) await ReadByteAsync(cancellationToken)
+ };
+
+ if (field.Type != TType.Stop)
+ {
+ field.ID = await ReadI16Async(cancellationToken);
+ }
+
+ return field;
+ }
+
+ public override async Task ReadFieldEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TMap>(cancellationToken);
+ }
+
+ var map = new TMap
+ {
+ KeyType = (TType) await ReadByteAsync(cancellationToken),
+ ValueType = (TType) await ReadByteAsync(cancellationToken),
+ Count = await ReadI32Async(cancellationToken)
+ };
+
+ return map;
+ }
+
+ public override async Task ReadMapEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task<TList> ReadListBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TList>(cancellationToken);
+ }
+
+ var list = new TList
+ {
+ ElementType = (TType) await ReadByteAsync(cancellationToken),
+ Count = await ReadI32Async(cancellationToken)
+ };
+
+ return list;
+ }
+
+ public override async Task ReadListEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TSet>(cancellationToken);
+ }
+
+ var set = new TSet
+ {
+ ElementType = (TType) await ReadByteAsync(cancellationToken),
+ Count = await ReadI32Async(cancellationToken)
+ };
+
+ return set;
+ }
+
+ public override async Task ReadSetEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task<bool> ReadBoolAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<bool>(cancellationToken);
+ }
+
+ return await ReadByteAsync(cancellationToken) == 1;
+ }
+
+ public override async Task<sbyte> ReadByteAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<sbyte>(cancellationToken);
+ }
+
+ var bin = new byte[1];
+ await Trans.ReadAllAsync(bin, 0, 1, cancellationToken); //TODO: why readall ?
+ return (sbyte) bin[0];
+ }
+
+ public override async Task<short> ReadI16Async(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<short>(cancellationToken);
+ }
+
+ var i16In = new byte[2];
+ await Trans.ReadAllAsync(i16In, 0, 2, cancellationToken);
+ var result = (short) (((i16In[0] & 0xff) << 8) | i16In[1] & 0xff);
+ return result;
+ }
+
+ public override async Task<int> ReadI32Async(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<int>(cancellationToken);
+ }
+
+ var i32In = new byte[4];
+ await Trans.ReadAllAsync(i32In, 0, 4, cancellationToken);
+
+ var result =
+ ((i32In[0] & 0xff) << 24) |
+ ((i32In[1] & 0xff) << 16) |
+ ((i32In[2] & 0xff) << 8) |
+ i32In[3] & 0xff;
+
+ return result;
+ }
+
+#pragma warning disable 675
+
+ protected internal long CreateReadI64(byte[] buf)
+ {
+ var result =
+ ((long) (buf[0] & 0xff) << 56) |
+ ((long) (buf[1] & 0xff) << 48) |
+ ((long) (buf[2] & 0xff) << 40) |
+ ((long) (buf[3] & 0xff) << 32) |
+ ((long) (buf[4] & 0xff) << 24) |
+ ((long) (buf[5] & 0xff) << 16) |
+ ((long) (buf[6] & 0xff) << 8) |
+ buf[7] & 0xff;
+
+ return result;
+ }
+
+#pragma warning restore 675
+
+ public override async Task<long> ReadI64Async(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<long>(cancellationToken);
+ }
+
+ var i64In = new byte[8];
+ await Trans.ReadAllAsync(i64In, 0, 8, cancellationToken);
+ return CreateReadI64(i64In);
+ }
+
+ public override async Task<double> ReadDoubleAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<double>(cancellationToken);
+ }
+
+ var d = await ReadI64Async(cancellationToken);
+ return BitConverter.Int64BitsToDouble(d);
+ }
+
+ public override async Task<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<byte[]>(cancellationToken);
+ }
+
+ var size = await ReadI32Async(cancellationToken);
+ var buf = new byte[size];
+ await Trans.ReadAllAsync(buf, 0, size, cancellationToken);
+ return buf;
+ }
+
+ private async Task<string> ReadStringBodyAsync(int size, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled<string>(cancellationToken);
+ }
+
+ var buf = new byte[size];
+ await Trans.ReadAllAsync(buf, 0, size, cancellationToken);
+ return Encoding.UTF8.GetString(buf, 0, buf.Length);
+ }
+
+ public class Factory : ITProtocolFactory
+ {
+ protected bool StrictRead;
+ protected bool StrictWrite;
+
+ public Factory()
+ : this(false, true)
+ {
+ }
+
+ public Factory(bool strictRead, bool strictWrite)
+ {
+ StrictRead = strictRead;
+ StrictWrite = strictWrite;
+ }
+
+ public TProtocol GetProtocol(TClientTransport trans)
+ {
+ return new TBinaryProtocol(trans, StrictRead, StrictWrite);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TCompactProtocol.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TCompactProtocol.cs
new file mode 100644
index 000000000..cecdf03cc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TCompactProtocol.cs
@@ -0,0 +1,922 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocols.Entities;
+using Thrift.Transports;
+
+namespace Thrift.Protocols
+{
+ //TODO: implementation of TProtocol
+
+ // ReSharper disable once InconsistentNaming
+ public class TCompactProtocol : TProtocol
+ {
+ private const byte ProtocolId = 0x82;
+ private const byte Version = 1;
+ private const byte VersionMask = 0x1f; // 0001 1111
+ private const byte TypeMask = 0xE0; // 1110 0000
+ private const byte TypeBits = 0x07; // 0000 0111
+ private const int TypeShiftAmount = 5;
+ private static readonly TStruct AnonymousStruct = new TStruct(string.Empty);
+ private static readonly TField Tstop = new TField(string.Empty, TType.Stop, 0);
+
+ // ReSharper disable once InconsistentNaming
+ private static readonly byte[] TTypeToCompactType = new byte[16];
+
+ /// <summary>
+ /// Used to keep track of the last field for the current and previous structs, so we can do the delta stuff.
+ /// </summary>
+ private readonly Stack<short> _lastField = new Stack<short>(15);
+
+ /// <summary>
+ /// If we encounter a boolean field begin, save the TField here so it can have the value incorporated.
+ /// </summary>
+ private TField? _booleanField;
+
+ /// <summary>
+ /// If we Read a field header, and it's a boolean field, save the boolean value here so that ReadBool can use it.
+ /// </summary>
+ private bool? _boolValue;
+
+ private short _lastFieldId;
+
+ public TCompactProtocol(TClientTransport trans)
+ : base(trans)
+ {
+ TTypeToCompactType[(int) TType.Stop] = Types.Stop;
+ TTypeToCompactType[(int) TType.Bool] = Types.BooleanTrue;
+ TTypeToCompactType[(int) TType.Byte] = Types.Byte;
+ TTypeToCompactType[(int) TType.I16] = Types.I16;
+ TTypeToCompactType[(int) TType.I32] = Types.I32;
+ TTypeToCompactType[(int) TType.I64] = Types.I64;
+ TTypeToCompactType[(int) TType.Double] = Types.Double;
+ TTypeToCompactType[(int) TType.String] = Types.Binary;
+ TTypeToCompactType[(int) TType.List] = Types.List;
+ TTypeToCompactType[(int) TType.Set] = Types.Set;
+ TTypeToCompactType[(int) TType.Map] = Types.Map;
+ TTypeToCompactType[(int) TType.Struct] = Types.Struct;
+ }
+
+ public void Reset()
+ {
+ _lastField.Clear();
+ _lastFieldId = 0;
+ }
+
+ public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await Trans.WriteAsync(new[] {ProtocolId}, cancellationToken);
+ await
+ Trans.WriteAsync(
+ new[] {(byte) ((Version & VersionMask) | (((uint) message.Type << TypeShiftAmount) & TypeMask))},
+ cancellationToken);
+
+ var bufferTuple = CreateWriteVarInt32((uint) message.SeqID);
+ await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken);
+
+ await WriteStringAsync(message.Name, cancellationToken);
+ }
+
+ public override async Task WriteMessageEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ /// <summary>
+ /// Write a struct begin. This doesn't actually put anything on the wire. We
+ /// use it as an opportunity to put special placeholder markers on the field
+ /// stack so we can get the field id deltas correct.
+ /// </summary>
+ public override async Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+
+ _lastField.Push(_lastFieldId);
+ _lastFieldId = 0;
+ }
+
+ public override async Task WriteStructEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+
+ _lastFieldId = _lastField.Pop();
+ }
+
+ private async Task WriteFieldBeginInternalAsync(TField field, byte typeOverride,
+ CancellationToken cancellationToken)
+ {
+ // if there's a exType override, use that.
+ var typeToWrite = typeOverride == 0xFF ? GetCompactType(field.Type) : typeOverride;
+
+ // check if we can use delta encoding for the field id
+ if ((field.ID > _lastFieldId) && (field.ID - _lastFieldId <= 15))
+ {
+ var b = (byte) (((field.ID - _lastFieldId) << 4) | typeToWrite);
+ // Write them together
+ await Trans.WriteAsync(new[] {b}, cancellationToken);
+ }
+ else
+ {
+ // Write them separate
+ await Trans.WriteAsync(new[] {typeToWrite}, cancellationToken);
+ await WriteI16Async(field.ID, cancellationToken);
+ }
+
+ _lastFieldId = field.ID;
+ }
+
+ public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken)
+ {
+ if (field.Type == TType.Bool)
+ {
+ _booleanField = field;
+ }
+ else
+ {
+ await WriteFieldBeginInternalAsync(field, 0xFF, cancellationToken);
+ }
+ }
+
+ public override async Task WriteFieldEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteFieldStopAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await Trans.WriteAsync(new[] {Types.Stop}, cancellationToken);
+ }
+
+ protected async Task WriteCollectionBeginAsync(TType elemType, int size, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ /*
+ Abstract method for writing the start of lists and sets. List and sets on
+ the wire differ only by the exType indicator.
+ */
+
+ if (size <= 14)
+ {
+ await Trans.WriteAsync(new[] {(byte) ((size << 4) | GetCompactType(elemType))}, cancellationToken);
+ }
+ else
+ {
+ await Trans.WriteAsync(new[] {(byte) (0xf0 | GetCompactType(elemType))}, cancellationToken);
+
+ var bufferTuple = CreateWriteVarInt32((uint) size);
+ await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken);
+ }
+ }
+
+ public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken)
+ {
+ await WriteCollectionBeginAsync(list.ElementType, list.Count, cancellationToken);
+ }
+
+ public override async Task WriteListEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await WriteCollectionBeginAsync(set.ElementType, set.Count, cancellationToken);
+ }
+
+ public override async Task WriteSetEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ /*
+ Write a boolean value. Potentially, this could be a boolean field, in
+ which case the field header info isn't written yet. If so, decide what the
+ right exType header is for the value and then Write the field header.
+ Otherwise, Write a single byte.
+ */
+
+ if (_booleanField != null)
+ {
+ // we haven't written the field header yet
+ await
+ WriteFieldBeginInternalAsync(_booleanField.Value, b ? Types.BooleanTrue : Types.BooleanFalse,
+ cancellationToken);
+ _booleanField = null;
+ }
+ else
+ {
+ // we're not part of a field, so just Write the value.
+ await Trans.WriteAsync(new[] {b ? Types.BooleanTrue : Types.BooleanFalse}, cancellationToken);
+ }
+ }
+
+ public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await Trans.WriteAsync(new[] {(byte) b}, cancellationToken);
+ }
+
+ public override async Task WriteI16Async(short i16, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ var bufferTuple = CreateWriteVarInt32(IntToZigzag(i16));
+ await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken);
+ }
+
+ protected internal Tuple<byte[], int> CreateWriteVarInt32(uint n)
+ {
+ // Write an i32 as a varint.Results in 1 - 5 bytes on the wire.
+ var i32Buf = new byte[5];
+ var idx = 0;
+
+ while (true)
+ {
+ if ((n & ~0x7F) == 0)
+ {
+ i32Buf[idx++] = (byte) n;
+ break;
+ }
+
+ i32Buf[idx++] = (byte) ((n & 0x7F) | 0x80);
+ n >>= 7;
+ }
+
+ return new Tuple<byte[], int>(i32Buf, idx);
+ }
+
+ public override async Task WriteI32Async(int i32, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ var bufferTuple = CreateWriteVarInt32(IntToZigzag(i32));
+ await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken);
+ }
+
+ protected internal Tuple<byte[], int> CreateWriteVarInt64(ulong n)
+ {
+ // Write an i64 as a varint. Results in 1-10 bytes on the wire.
+ var buf = new byte[10];
+ var idx = 0;
+
+ while (true)
+ {
+ if ((n & ~(ulong) 0x7FL) == 0)
+ {
+ buf[idx++] = (byte) n;
+ break;
+ }
+ buf[idx++] = (byte) ((n & 0x7F) | 0x80);
+ n >>= 7;
+ }
+
+ return new Tuple<byte[], int>(buf, idx);
+ }
+
+ public override async Task WriteI64Async(long i64, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ var bufferTuple = CreateWriteVarInt64(LongToZigzag(i64));
+ await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken);
+ }
+
+ public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ var data = new byte[8];
+ FixedLongToBytes(BitConverter.DoubleToInt64Bits(d), data, 0);
+ await Trans.WriteAsync(data, cancellationToken);
+ }
+
+ public override async Task WriteStringAsync(string str, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ var bytes = Encoding.UTF8.GetBytes(str);
+
+ var bufferTuple = CreateWriteVarInt32((uint) bytes.Length);
+ await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken);
+ await Trans.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
+ }
+
+ public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ var bufferTuple = CreateWriteVarInt32((uint) bytes.Length);
+ await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken);
+ await Trans.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
+ }
+
+ public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ if (map.Count == 0)
+ {
+ await Trans.WriteAsync(new[] {(byte) 0}, cancellationToken);
+ }
+ else
+ {
+ var bufferTuple = CreateWriteVarInt32((uint) map.Count);
+ await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken);
+ await
+ Trans.WriteAsync(
+ new[] {(byte) ((GetCompactType(map.KeyType) << 4) | GetCompactType(map.ValueType))},
+ cancellationToken);
+ }
+ }
+
+ public override async Task WriteMapEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TMessage>(cancellationToken);
+ }
+
+ var protocolId = (byte) await ReadByteAsync(cancellationToken);
+ if (protocolId != ProtocolId)
+ {
+ throw new TProtocolException($"Expected protocol id {ProtocolId:X} but got {protocolId:X}");
+ }
+
+ var versionAndType = (byte) await ReadByteAsync(cancellationToken);
+ var version = (byte) (versionAndType & VersionMask);
+
+ if (version != Version)
+ {
+ throw new TProtocolException($"Expected version {Version} but got {version}");
+ }
+
+ var type = (byte) ((versionAndType >> TypeShiftAmount) & TypeBits);
+ var seqid = (int) await ReadVarInt32Async(cancellationToken);
+ var messageName = await ReadStringAsync(cancellationToken);
+
+ return new TMessage(messageName, (TMessageType) type, seqid);
+ }
+
+ public override async Task ReadMessageEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TStruct>(cancellationToken);
+ }
+
+ // some magic is here )
+
+ _lastField.Push(_lastFieldId);
+ _lastFieldId = 0;
+
+ return AnonymousStruct;
+ }
+
+ public override async Task ReadStructEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+
+ /*
+ Doesn't actually consume any wire data, just removes the last field for
+ this struct from the field stack.
+ */
+
+ // consume the last field we Read off the wire.
+ _lastFieldId = _lastField.Pop();
+ }
+
+ public override async Task<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
+ {
+ // Read a field header off the wire.
+ var type = (byte) await ReadByteAsync(cancellationToken);
+ // if it's a stop, then we can return immediately, as the struct is over.
+ if (type == Types.Stop)
+ {
+ return Tstop;
+ }
+
+ short fieldId;
+ // mask off the 4 MSB of the exType header. it could contain a field id delta.
+ var modifier = (short) ((type & 0xf0) >> 4);
+ if (modifier == 0)
+ {
+ fieldId = await ReadI16Async(cancellationToken);
+ }
+ else
+ {
+ fieldId = (short) (_lastFieldId + modifier);
+ }
+
+ var field = new TField(string.Empty, GetTType((byte) (type & 0x0f)), fieldId);
+ // if this happens to be a boolean field, the value is encoded in the exType
+ if (IsBoolType(type))
+ {
+ _boolValue = (byte) (type & 0x0f) == Types.BooleanTrue;
+ }
+
+ // push the new field onto the field stack so we can keep the deltas going.
+ _lastFieldId = field.ID;
+ return field;
+ }
+
+ public override async Task ReadFieldEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled<TMap>(cancellationToken);
+ }
+
+ /*
+ Read a map header off the wire. If the size is zero, skip Reading the key
+ and value exType. This means that 0-length maps will yield TMaps without the
+ "correct" types.
+ */
+
+ var size = (int) await ReadVarInt32Async(cancellationToken);
+ var keyAndValueType = size == 0 ? (byte) 0 : (byte) await ReadByteAsync(cancellationToken);
+ return new TMap(GetTType((byte) (keyAndValueType >> 4)), GetTType((byte) (keyAndValueType & 0xf)), size);
+ }
+
+ public override async Task ReadMapEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
+ {
+ /*
+ Read a set header off the wire. If the set size is 0-14, the size will
+ be packed into the element exType header. If it's a longer set, the 4 MSB
+ of the element exType header will be 0xF, and a varint will follow with the
+ true size.
+ */
+
+ return new TSet(await ReadListBeginAsync(cancellationToken));
+ }
+
+ public override async Task<bool> ReadBoolAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<bool>(cancellationToken);
+ }
+
+ /*
+ Read a boolean off the wire. If this is a boolean field, the value should
+ already have been Read during ReadFieldBegin, so we'll just consume the
+ pre-stored value. Otherwise, Read a byte.
+ */
+
+ if (_boolValue != null)
+ {
+ var result = _boolValue.Value;
+ _boolValue = null;
+ return result;
+ }
+
+ return await ReadByteAsync(cancellationToken) == Types.BooleanTrue;
+ }
+
+ public override async Task<sbyte> ReadByteAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<sbyte>(cancellationToken);
+ }
+
+ // Read a single byte off the wire. Nothing interesting here.
+ var buf = new byte[1];
+ await Trans.ReadAllAsync(buf, 0, 1, cancellationToken);
+ return (sbyte) buf[0];
+ }
+
+ public override async Task<short> ReadI16Async(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<short>(cancellationToken);
+ }
+
+ return (short) ZigzagToInt(await ReadVarInt32Async(cancellationToken));
+ }
+
+ public override async Task<int> ReadI32Async(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<int>(cancellationToken);
+ }
+
+ return ZigzagToInt(await ReadVarInt32Async(cancellationToken));
+ }
+
+ public override async Task<long> ReadI64Async(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<long>(cancellationToken);
+ }
+
+ return ZigzagToLong(await ReadVarInt64Async(cancellationToken));
+ }
+
+ public override async Task<double> ReadDoubleAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<double>(cancellationToken);
+ }
+
+ var longBits = new byte[8];
+ await Trans.ReadAllAsync(longBits, 0, 8, cancellationToken);
+
+ return BitConverter.Int64BitsToDouble(BytesToLong(longBits));
+ }
+
+ public override async Task<string> ReadStringAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled<string>(cancellationToken);
+ }
+
+ // Reads a byte[] (via ReadBinary), and then UTF-8 decodes it.
+ var length = (int) await ReadVarInt32Async(cancellationToken);
+
+ if (length == 0)
+ {
+ return string.Empty;
+ }
+
+ var buf = new byte[length];
+ await Trans.ReadAllAsync(buf, 0, length, cancellationToken);
+
+ return Encoding.UTF8.GetString(buf);
+ }
+
+ public override async Task<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<byte[]>(cancellationToken);
+ }
+
+ // Read a byte[] from the wire.
+ var length = (int) await ReadVarInt32Async(cancellationToken);
+ if (length == 0)
+ {
+ return new byte[0];
+ }
+
+ var buf = new byte[length];
+ await Trans.ReadAllAsync(buf, 0, length, cancellationToken);
+ return buf;
+ }
+
+ public override async Task<TList> ReadListBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled<TList>(cancellationToken);
+ }
+
+ /*
+ Read a list header off the wire. If the list size is 0-14, the size will
+ be packed into the element exType header. If it's a longer list, the 4 MSB
+ of the element exType header will be 0xF, and a varint will follow with the
+ true size.
+ */
+
+ var sizeAndType = (byte) await ReadByteAsync(cancellationToken);
+ var size = (sizeAndType >> 4) & 0x0f;
+ if (size == 15)
+ {
+ size = (int) await ReadVarInt32Async(cancellationToken);
+ }
+
+ var type = GetTType(sizeAndType);
+ return new TList(type, size);
+ }
+
+ public override async Task ReadListEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task ReadSetEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ private static byte GetCompactType(TType ttype)
+ {
+ // Given a TType value, find the appropriate TCompactProtocol.Types constant.
+ return TTypeToCompactType[(int) ttype];
+ }
+
+
+ private async Task<uint> ReadVarInt32Async(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<uint>(cancellationToken);
+ }
+
+ /*
+ Read an i32 from the wire as a varint. The MSB of each byte is set
+ if there is another byte to follow. This can Read up to 5 bytes.
+ */
+
+ uint result = 0;
+ var shift = 0;
+
+ while (true)
+ {
+ var b = (byte) await ReadByteAsync(cancellationToken);
+ result |= (uint) (b & 0x7f) << shift;
+ if ((b & 0x80) != 0x80)
+ {
+ break;
+ }
+ shift += 7;
+ }
+
+ return result;
+ }
+
+ private async Task<ulong> ReadVarInt64Async(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<uint>(cancellationToken);
+ }
+
+ /*
+ Read an i64 from the wire as a proper varint. The MSB of each byte is set
+ if there is another byte to follow. This can Read up to 10 bytes.
+ */
+
+ var shift = 0;
+ ulong result = 0;
+ while (true)
+ {
+ var b = (byte) await ReadByteAsync(cancellationToken);
+ result |= (ulong) (b & 0x7f) << shift;
+ if ((b & 0x80) != 0x80)
+ {
+ break;
+ }
+ shift += 7;
+ }
+
+ return result;
+ }
+
+ private static int ZigzagToInt(uint n)
+ {
+ return (int) (n >> 1) ^ -(int) (n & 1);
+ }
+
+ private static long ZigzagToLong(ulong n)
+ {
+ return (long) (n >> 1) ^ -(long) (n & 1);
+ }
+
+ private static long BytesToLong(byte[] bytes)
+ {
+ /*
+ Note that it's important that the mask bytes are long literals,
+ otherwise they'll default to ints, and when you shift an int left 56 bits,
+ you just get a messed up int.
+ */
+
+ return
+ ((bytes[7] & 0xffL) << 56) |
+ ((bytes[6] & 0xffL) << 48) |
+ ((bytes[5] & 0xffL) << 40) |
+ ((bytes[4] & 0xffL) << 32) |
+ ((bytes[3] & 0xffL) << 24) |
+ ((bytes[2] & 0xffL) << 16) |
+ ((bytes[1] & 0xffL) << 8) |
+ (bytes[0] & 0xffL);
+ }
+
+ private static bool IsBoolType(byte b)
+ {
+ var lowerNibble = b & 0x0f;
+ return (lowerNibble == Types.BooleanTrue) || (lowerNibble == Types.BooleanFalse);
+ }
+
+ private static TType GetTType(byte type)
+ {
+ // Given a TCompactProtocol.Types constant, convert it to its corresponding TType value.
+ switch ((byte) (type & 0x0f))
+ {
+ case Types.Stop:
+ return TType.Stop;
+ case Types.BooleanFalse:
+ case Types.BooleanTrue:
+ return TType.Bool;
+ case Types.Byte:
+ return TType.Byte;
+ case Types.I16:
+ return TType.I16;
+ case Types.I32:
+ return TType.I32;
+ case Types.I64:
+ return TType.I64;
+ case Types.Double:
+ return TType.Double;
+ case Types.Binary:
+ return TType.String;
+ case Types.List:
+ return TType.List;
+ case Types.Set:
+ return TType.Set;
+ case Types.Map:
+ return TType.Map;
+ case Types.Struct:
+ return TType.Struct;
+ default:
+ throw new TProtocolException($"Don't know what exType: {(byte) (type & 0x0f)}");
+ }
+ }
+
+ private static ulong LongToZigzag(long n)
+ {
+ // Convert l into a zigzag long. This allows negative numbers to be represented compactly as a varint
+ return (ulong) (n << 1) ^ (ulong) (n >> 63);
+ }
+
+ private static uint IntToZigzag(int n)
+ {
+ // Convert n into a zigzag int. This allows negative numbers to be represented compactly as a varint
+ return (uint) (n << 1) ^ (uint) (n >> 31);
+ }
+
+ private static void FixedLongToBytes(long n, byte[] buf, int off)
+ {
+ // Convert a long into little-endian bytes in buf starting at off and going until off+7.
+ buf[off + 0] = (byte) (n & 0xff);
+ buf[off + 1] = (byte) ((n >> 8) & 0xff);
+ buf[off + 2] = (byte) ((n >> 16) & 0xff);
+ buf[off + 3] = (byte) ((n >> 24) & 0xff);
+ buf[off + 4] = (byte) ((n >> 32) & 0xff);
+ buf[off + 5] = (byte) ((n >> 40) & 0xff);
+ buf[off + 6] = (byte) ((n >> 48) & 0xff);
+ buf[off + 7] = (byte) ((n >> 56) & 0xff);
+ }
+
+ public class Factory : ITProtocolFactory
+ {
+ public TProtocol GetProtocol(TClientTransport trans)
+ {
+ return new TCompactProtocol(trans);
+ }
+ }
+
+ /// <summary>
+ /// All of the on-wire exType codes.
+ /// </summary>
+ private static class Types
+ {
+ public const byte Stop = 0x00;
+ public const byte BooleanTrue = 0x01;
+ public const byte BooleanFalse = 0x02;
+ public const byte Byte = 0x03;
+ public const byte I16 = 0x04;
+ public const byte I32 = 0x05;
+ public const byte I64 = 0x06;
+ public const byte Double = 0x07;
+ public const byte Binary = 0x08;
+ public const byte List = 0x09;
+ public const byte Set = 0x0A;
+ public const byte Map = 0x0B;
+ public const byte Struct = 0x0C;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TJSONProtocol.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TJSONProtocol.cs
new file mode 100644
index 000000000..6d33f029e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TJSONProtocol.cs
@@ -0,0 +1,981 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocols.Entities;
+using Thrift.Protocols.Utilities;
+using Thrift.Transports;
+
+namespace Thrift.Protocols
+{
+ /// <summary>
+ /// JSON protocol implementation for thrift.
+ /// This is a full-featured protocol supporting Write and Read.
+ /// Please see the C++ class header for a detailed description of the
+ /// protocol's wire format.
+ /// Adapted from the Java version.
+ /// </summary>
+ // ReSharper disable once InconsistentNaming
+ public class TJsonProtocol : TProtocol
+ {
+ private const long Version = 1;
+
+ // Temporary buffer used by several methods
+ private readonly byte[] _tempBuffer = new byte[4];
+
+ // Current context that we are in
+ protected JSONBaseContext Context;
+
+ // Stack of nested contexts that we may be in
+ protected Stack<JSONBaseContext> ContextStack = new Stack<JSONBaseContext>();
+
+ // Reader that manages a 1-byte buffer
+ protected LookaheadReader Reader;
+
+ // Default encoding
+ protected Encoding Utf8Encoding = Encoding.UTF8;
+
+ /// <summary>
+ /// TJsonProtocol Constructor
+ /// </summary>
+ public TJsonProtocol(TClientTransport trans)
+ : base(trans)
+ {
+ Context = new JSONBaseContext(this);
+ Reader = new LookaheadReader(this);
+ }
+
+ /// <summary>
+ /// Push a new JSON context onto the stack.
+ /// </summary>
+ protected void PushContext(JSONBaseContext c)
+ {
+ ContextStack.Push(Context);
+ Context = c;
+ }
+
+ /// <summary>
+ /// Pop the last JSON context off the stack
+ /// </summary>
+ protected void PopContext()
+ {
+ Context = ContextStack.Pop();
+ }
+
+ /// <summary>
+ /// Read a byte that must match b[0]; otherwise an exception is thrown.
+ /// Marked protected to avoid synthetic accessor in JSONListContext.Read
+ /// and JSONPairContext.Read
+ /// </summary>
+ protected async Task ReadJsonSyntaxCharAsync(byte[] bytes, CancellationToken cancellationToken)
+ {
+ var ch = await Reader.ReadAsync(cancellationToken);
+ if (ch != bytes[0])
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, $"Unexpected character: {(char) ch}");
+ }
+ }
+
+ /// <summary>
+ /// Write the bytes in array buf as a JSON characters, escaping as needed
+ /// </summary>
+ private async Task WriteJsonStringAsync(byte[] bytes, CancellationToken cancellationToken)
+ {
+ await Context.WriteAsync(cancellationToken);
+ await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
+
+ var len = bytes.Length;
+ for (var i = 0; i < len; i++)
+ {
+ if ((bytes[i] & 0x00FF) >= 0x30)
+ {
+ if (bytes[i] == TJSONProtocolConstants.Backslash[0])
+ {
+ await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
+ await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
+ }
+ else
+ {
+ await Trans.WriteAsync(bytes.ToArray(), i, 1, cancellationToken);
+ }
+ }
+ else
+ {
+ _tempBuffer[0] = TJSONProtocolConstants.JsonCharTable[bytes[i]];
+ if (_tempBuffer[0] == 1)
+ {
+ await Trans.WriteAsync(bytes, i, 1, cancellationToken);
+ }
+ else if (_tempBuffer[0] > 1)
+ {
+ await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
+ await Trans.WriteAsync(_tempBuffer, 0, 1, cancellationToken);
+ }
+ else
+ {
+ await Trans.WriteAsync(TJSONProtocolConstants.EscSequences, cancellationToken);
+ _tempBuffer[0] = TJSONProtocolHelper.ToHexChar((byte) (bytes[i] >> 4));
+ _tempBuffer[1] = TJSONProtocolHelper.ToHexChar(bytes[i]);
+ await Trans.WriteAsync(_tempBuffer, 0, 2, cancellationToken);
+ }
+ }
+ }
+ await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+
+ /// <summary>
+ /// Write out number as a JSON value. If the context dictates so, it will be
+ /// wrapped in quotes to output as a JSON string.
+ /// </summary>
+ private async Task WriteJsonIntegerAsync(long num, CancellationToken cancellationToken)
+ {
+ await Context.WriteAsync(cancellationToken);
+ var str = num.ToString();
+
+ var escapeNum = Context.EscapeNumbers();
+ if (escapeNum)
+ {
+ await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+
+ var bytes = Utf8Encoding.GetBytes(str);
+ await Trans.WriteAsync(bytes, cancellationToken);
+
+ if (escapeNum)
+ {
+ await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+ }
+
+ /// <summary>
+ /// Write out a double as a JSON value. If it is NaN or infinity or if the
+ /// context dictates escaping, Write out as JSON string.
+ /// </summary>
+ private async Task WriteJsonDoubleAsync(double num, CancellationToken cancellationToken)
+ {
+ await Context.WriteAsync(cancellationToken);
+ var str = num.ToString("G17", CultureInfo.InvariantCulture);
+ var special = false;
+
+ switch (str[0])
+ {
+ case 'N': // NaN
+ case 'I': // Infinity
+ special = true;
+ break;
+ case '-':
+ if (str[1] == 'I')
+ {
+ // -Infinity
+ special = true;
+ }
+ break;
+ }
+
+ var escapeNum = special || Context.EscapeNumbers();
+
+ if (escapeNum)
+ {
+ await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+
+ await Trans.WriteAsync(Utf8Encoding.GetBytes(str), cancellationToken);
+
+ if (escapeNum)
+ {
+ await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+ }
+
+ /// <summary>
+ /// Write out contents of byte array b as a JSON string with base-64 encoded
+ /// data
+ /// </summary>
+ private async Task WriteJsonBase64Async(byte[] bytes, CancellationToken cancellationToken)
+ {
+ await Context.WriteAsync(cancellationToken);
+ await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
+
+ var len = bytes.Length;
+ var off = 0;
+
+ while (len >= 3)
+ {
+ // Encode 3 bytes at a time
+ TBase64Helper.Encode(bytes, off, 3, _tempBuffer, 0);
+ await Trans.WriteAsync(_tempBuffer, 0, 4, cancellationToken);
+ off += 3;
+ len -= 3;
+ }
+
+ if (len > 0)
+ {
+ // Encode remainder
+ TBase64Helper.Encode(bytes, off, len, _tempBuffer, 0);
+ await Trans.WriteAsync(_tempBuffer, 0, len + 1, cancellationToken);
+ }
+
+ await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+
+ private async Task WriteJsonObjectStartAsync(CancellationToken cancellationToken)
+ {
+ await Context.WriteAsync(cancellationToken);
+ await Trans.WriteAsync(TJSONProtocolConstants.LeftBrace, cancellationToken);
+ PushContext(new JSONPairContext(this));
+ }
+
+ private async Task WriteJsonObjectEndAsync(CancellationToken cancellationToken)
+ {
+ PopContext();
+ await Trans.WriteAsync(TJSONProtocolConstants.RightBrace, cancellationToken);
+ }
+
+ private async Task WriteJsonArrayStartAsync(CancellationToken cancellationToken)
+ {
+ await Context.WriteAsync(cancellationToken);
+ await Trans.WriteAsync(TJSONProtocolConstants.LeftBracket, cancellationToken);
+ PushContext(new JSONListContext(this));
+ }
+
+ private async Task WriteJsonArrayEndAsync(CancellationToken cancellationToken)
+ {
+ PopContext();
+ await Trans.WriteAsync(TJSONProtocolConstants.RightBracket, cancellationToken);
+ }
+
+ public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
+ {
+ await WriteJsonArrayStartAsync(cancellationToken);
+ await WriteJsonIntegerAsync(Version, cancellationToken);
+
+ var b = Utf8Encoding.GetBytes(message.Name);
+ await WriteJsonStringAsync(b, cancellationToken);
+
+ await WriteJsonIntegerAsync((long) message.Type, cancellationToken);
+ await WriteJsonIntegerAsync(message.SeqID, cancellationToken);
+ }
+
+ public override async Task WriteMessageEndAsync(CancellationToken cancellationToken)
+ {
+ await WriteJsonArrayEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken)
+ {
+ await WriteJsonObjectStartAsync(cancellationToken);
+ }
+
+ public override async Task WriteStructEndAsync(CancellationToken cancellationToken)
+ {
+ await WriteJsonObjectEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken)
+ {
+ await WriteJsonIntegerAsync(field.ID, cancellationToken);
+ await WriteJsonObjectStartAsync(cancellationToken);
+ await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(field.Type), cancellationToken);
+ }
+
+ public override async Task WriteFieldEndAsync(CancellationToken cancellationToken)
+ {
+ await WriteJsonObjectEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteFieldStopAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken)
+ {
+ await WriteJsonArrayStartAsync(cancellationToken);
+ await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(map.KeyType), cancellationToken);
+ await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(map.ValueType), cancellationToken);
+ await WriteJsonIntegerAsync(map.Count, cancellationToken);
+ await WriteJsonObjectStartAsync(cancellationToken);
+ }
+
+ public override async Task WriteMapEndAsync(CancellationToken cancellationToken)
+ {
+ await WriteJsonObjectEndAsync(cancellationToken);
+ await WriteJsonArrayEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken)
+ {
+ await WriteJsonArrayStartAsync(cancellationToken);
+ await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(list.ElementType), cancellationToken);
+ await WriteJsonIntegerAsync(list.Count, cancellationToken);
+ }
+
+ public override async Task WriteListEndAsync(CancellationToken cancellationToken)
+ {
+ await WriteJsonArrayEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken)
+ {
+ await WriteJsonArrayStartAsync(cancellationToken);
+ await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(set.ElementType), cancellationToken);
+ await WriteJsonIntegerAsync(set.Count, cancellationToken);
+ }
+
+ public override async Task WriteSetEndAsync(CancellationToken cancellationToken)
+ {
+ await WriteJsonArrayEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken)
+ {
+ await WriteJsonIntegerAsync(b ? 1 : 0, cancellationToken);
+ }
+
+ public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken)
+ {
+ await WriteJsonIntegerAsync(b, cancellationToken);
+ }
+
+ public override async Task WriteI16Async(short i16, CancellationToken cancellationToken)
+ {
+ await WriteJsonIntegerAsync(i16, cancellationToken);
+ }
+
+ public override async Task WriteI32Async(int i32, CancellationToken cancellationToken)
+ {
+ await WriteJsonIntegerAsync(i32, cancellationToken);
+ }
+
+ public override async Task WriteI64Async(long i64, CancellationToken cancellationToken)
+ {
+ await WriteJsonIntegerAsync(i64, cancellationToken);
+ }
+
+ public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken)
+ {
+ await WriteJsonDoubleAsync(d, cancellationToken);
+ }
+
+ public override async Task WriteStringAsync(string s, CancellationToken cancellationToken)
+ {
+ var b = Utf8Encoding.GetBytes(s);
+ await WriteJsonStringAsync(b, cancellationToken);
+ }
+
+ public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken)
+ {
+ await WriteJsonBase64Async(bytes, cancellationToken);
+ }
+
+ /// <summary>
+ /// Read in a JSON string, unescaping as appropriate.. Skip Reading from the
+ /// context if skipContext is true.
+ /// </summary>
+ private async Task<byte[]> ReadJsonStringAsync(bool skipContext, CancellationToken cancellationToken)
+ {
+ using (var buffer = new MemoryStream())
+ {
+ var codeunits = new List<char>();
+
+
+ if (!skipContext)
+ {
+ await Context.ReadAsync(cancellationToken);
+ }
+
+ await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
+
+ while (true)
+ {
+ var ch = await Reader.ReadAsync(cancellationToken);
+ if (ch == TJSONProtocolConstants.Quote[0])
+ {
+ break;
+ }
+
+ // escaped?
+ if (ch != TJSONProtocolConstants.EscSequences[0])
+ {
+ await buffer.WriteAsync(new[] {ch}, 0, 1, cancellationToken);
+ continue;
+ }
+
+ // distinguish between \uXXXX and \?
+ ch = await Reader.ReadAsync(cancellationToken);
+ if (ch != TJSONProtocolConstants.EscSequences[1]) // control chars like \n
+ {
+ var off = Array.IndexOf(TJSONProtocolConstants.EscapeChars, (char) ch);
+ if (off == -1)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected control char");
+ }
+ ch = TJSONProtocolConstants.EscapeCharValues[off];
+ await buffer.WriteAsync(new[] {ch}, 0, 1, cancellationToken);
+ continue;
+ }
+
+ // it's \uXXXX
+ await Trans.ReadAllAsync(_tempBuffer, 0, 4, cancellationToken);
+
+ var wch = (short) ((TJSONProtocolHelper.ToHexVal(_tempBuffer[0]) << 12) +
+ (TJSONProtocolHelper.ToHexVal(_tempBuffer[1]) << 8) +
+ (TJSONProtocolHelper.ToHexVal(_tempBuffer[2]) << 4) +
+ TJSONProtocolHelper.ToHexVal(_tempBuffer[3]));
+
+ if (char.IsHighSurrogate((char) wch))
+ {
+ if (codeunits.Count > 0)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected low surrogate char");
+ }
+ codeunits.Add((char) wch);
+ }
+ else if (char.IsLowSurrogate((char) wch))
+ {
+ if (codeunits.Count == 0)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected high surrogate char");
+ }
+
+ codeunits.Add((char) wch);
+ var tmp = Utf8Encoding.GetBytes(codeunits.ToArray());
+ await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken);
+ codeunits.Clear();
+ }
+ else
+ {
+ var tmp = Utf8Encoding.GetBytes(new[] {(char) wch});
+ await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken);
+ }
+ }
+
+ if (codeunits.Count > 0)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected low surrogate char");
+ }
+
+ return buffer.ToArray();
+ }
+ }
+
+ /// <summary>
+ /// Read in a sequence of characters that are all valid in JSON numbers. Does
+ /// not do a complete regex check to validate that this is actually a number.
+ /// </summary>
+ private async Task<string> ReadJsonNumericCharsAsync(CancellationToken cancellationToken)
+ {
+ var strbld = new StringBuilder();
+ while (true)
+ {
+ //TODO: workaround for primitive types with TJsonProtocol, think - how to rewrite into more easy form without exceptions
+ try
+ {
+ var ch = await Reader.PeekAsync(cancellationToken);
+ if (!TJSONProtocolHelper.IsJsonNumeric(ch))
+ {
+ break;
+ }
+ var c = (char)await Reader.ReadAsync(cancellationToken);
+ strbld.Append(c);
+ }
+ catch (TTransportException)
+ {
+ break;
+ }
+ }
+ return strbld.ToString();
+ }
+
+ /// <summary>
+ /// Read in a JSON number. If the context dictates, Read in enclosing quotes.
+ /// </summary>
+ private async Task<long> ReadJsonIntegerAsync(CancellationToken cancellationToken)
+ {
+ await Context.ReadAsync(cancellationToken);
+ if (Context.EscapeNumbers())
+ {
+ await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+
+ var str = await ReadJsonNumericCharsAsync(cancellationToken);
+ if (Context.EscapeNumbers())
+ {
+ await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+
+ try
+ {
+ return long.Parse(str);
+ }
+ catch (FormatException)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Bad data encounted in numeric data");
+ }
+ }
+
+ /// <summary>
+ /// Read in a JSON double value. Throw if the value is not wrapped in quotes
+ /// when expected or if wrapped in quotes when not expected.
+ /// </summary>
+ private async Task<double> ReadJsonDoubleAsync(CancellationToken cancellationToken)
+ {
+ await Context.ReadAsync(cancellationToken);
+ if (await Reader.PeekAsync(cancellationToken) == TJSONProtocolConstants.Quote[0])
+ {
+ var arr = await ReadJsonStringAsync(true, cancellationToken);
+ var dub = double.Parse(Utf8Encoding.GetString(arr, 0, arr.Length), CultureInfo.InvariantCulture);
+
+ if (!Context.EscapeNumbers() && !double.IsNaN(dub) && !double.IsInfinity(dub))
+ {
+ // Throw exception -- we should not be in a string in this case
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Numeric data unexpectedly quoted");
+ }
+
+ return dub;
+ }
+
+ if (Context.EscapeNumbers())
+ {
+ // This will throw - we should have had a quote if escapeNum == true
+ await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+
+ try
+ {
+ return double.Parse(await ReadJsonNumericCharsAsync(cancellationToken), CultureInfo.InvariantCulture);
+ }
+ catch (FormatException)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Bad data encounted in numeric data");
+ }
+ }
+
+ /// <summary>
+ /// Read in a JSON string containing base-64 encoded data and decode it.
+ /// </summary>
+ private async Task<byte[]> ReadJsonBase64Async(CancellationToken cancellationToken)
+ {
+ var b = await ReadJsonStringAsync(false, cancellationToken);
+ var len = b.Length;
+ var off = 0;
+ var size = 0;
+
+ // reduce len to ignore fill bytes
+ while ((len > 0) && (b[len - 1] == '='))
+ {
+ --len;
+ }
+
+ // read & decode full byte triplets = 4 source bytes
+ while (len > 4)
+ {
+ // Decode 4 bytes at a time
+ TBase64Helper.Decode(b, off, 4, b, size); // NB: decoded in place
+ off += 4;
+ len -= 4;
+ size += 3;
+ }
+
+ // Don't decode if we hit the end or got a single leftover byte (invalid
+ // base64 but legal for skip of regular string exType)
+ if (len > 1)
+ {
+ // Decode remainder
+ TBase64Helper.Decode(b, off, len, b, size); // NB: decoded in place
+ size += len - 1;
+ }
+
+ // Sadly we must copy the byte[] (any way around this?)
+ var result = new byte[size];
+ Array.Copy(b, 0, result, 0, size);
+ return result;
+ }
+
+ private async Task ReadJsonObjectStartAsync(CancellationToken cancellationToken)
+ {
+ await Context.ReadAsync(cancellationToken);
+ await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBrace, cancellationToken);
+ PushContext(new JSONPairContext(this));
+ }
+
+ private async Task ReadJsonObjectEndAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBrace, cancellationToken);
+ PopContext();
+ }
+
+ private async Task ReadJsonArrayStartAsync(CancellationToken cancellationToken)
+ {
+ await Context.ReadAsync(cancellationToken);
+ await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBracket, cancellationToken);
+ PushContext(new JSONListContext(this));
+ }
+
+ private async Task ReadJsonArrayEndAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBracket, cancellationToken);
+ PopContext();
+ }
+
+ public override async Task<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
+ {
+ var message = new TMessage();
+ await ReadJsonArrayStartAsync(cancellationToken);
+ if (await ReadJsonIntegerAsync(cancellationToken) != Version)
+ {
+ throw new TProtocolException(TProtocolException.BAD_VERSION, "Message contained bad version.");
+ }
+
+ var buf = await ReadJsonStringAsync(false, cancellationToken);
+ message.Name = Utf8Encoding.GetString(buf, 0, buf.Length);
+ message.Type = (TMessageType) await ReadJsonIntegerAsync(cancellationToken);
+ message.SeqID = (int) await ReadJsonIntegerAsync(cancellationToken);
+ return message;
+ }
+
+ public override async Task ReadMessageEndAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonArrayEndAsync(cancellationToken);
+ }
+
+ public override async Task<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonObjectStartAsync(cancellationToken);
+ return new TStruct();
+ }
+
+ public override async Task ReadStructEndAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonObjectEndAsync(cancellationToken);
+ }
+
+ public override async Task<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
+ {
+ var field = new TField();
+ var ch = await Reader.PeekAsync(cancellationToken);
+ if (ch == TJSONProtocolConstants.RightBrace[0])
+ {
+ field.Type = TType.Stop;
+ }
+ else
+ {
+ field.ID = (short) await ReadJsonIntegerAsync(cancellationToken);
+ await ReadJsonObjectStartAsync(cancellationToken);
+ field.Type = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
+ }
+ return field;
+ }
+
+ public override async Task ReadFieldEndAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonObjectEndAsync(cancellationToken);
+ }
+
+ public override async Task<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
+ {
+ var map = new TMap();
+ await ReadJsonArrayStartAsync(cancellationToken);
+ map.KeyType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
+ map.ValueType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
+ map.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
+ await ReadJsonObjectStartAsync(cancellationToken);
+ return map;
+ }
+
+ public override async Task ReadMapEndAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonObjectEndAsync(cancellationToken);
+ await ReadJsonArrayEndAsync(cancellationToken);
+ }
+
+ public override async Task<TList> ReadListBeginAsync(CancellationToken cancellationToken)
+ {
+ var list = new TList();
+ await ReadJsonArrayStartAsync(cancellationToken);
+ list.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
+ list.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
+ return list;
+ }
+
+ public override async Task ReadListEndAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonArrayEndAsync(cancellationToken);
+ }
+
+ public override async Task<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
+ {
+ var set = new TSet();
+ await ReadJsonArrayStartAsync(cancellationToken);
+ set.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
+ set.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
+ return set;
+ }
+
+ public override async Task ReadSetEndAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonArrayEndAsync(cancellationToken);
+ }
+
+ public override async Task<bool> ReadBoolAsync(CancellationToken cancellationToken)
+ {
+ return await ReadJsonIntegerAsync(cancellationToken) != 0;
+ }
+
+ public override async Task<sbyte> ReadByteAsync(CancellationToken cancellationToken)
+ {
+ return (sbyte) await ReadJsonIntegerAsync(cancellationToken);
+ }
+
+ public override async Task<short> ReadI16Async(CancellationToken cancellationToken)
+ {
+ return (short) await ReadJsonIntegerAsync(cancellationToken);
+ }
+
+ public override async Task<int> ReadI32Async(CancellationToken cancellationToken)
+ {
+ return (int) await ReadJsonIntegerAsync(cancellationToken);
+ }
+
+ public override async Task<long> ReadI64Async(CancellationToken cancellationToken)
+ {
+ return await ReadJsonIntegerAsync(cancellationToken);
+ }
+
+ public override async Task<double> ReadDoubleAsync(CancellationToken cancellationToken)
+ {
+ return await ReadJsonDoubleAsync(cancellationToken);
+ }
+
+ public override async Task<string> ReadStringAsync(CancellationToken cancellationToken)
+ {
+ var buf = await ReadJsonStringAsync(false, cancellationToken);
+ return Utf8Encoding.GetString(buf, 0, buf.Length);
+ }
+
+ public override async Task<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
+ {
+ return await ReadJsonBase64Async(cancellationToken);
+ }
+
+ /// <summary>
+ /// Factory for JSON protocol objects
+ /// </summary>
+ public class Factory : ITProtocolFactory
+ {
+ public TProtocol GetProtocol(TClientTransport trans)
+ {
+ return new TJsonProtocol(trans);
+ }
+ }
+
+ /// <summary>
+ /// Base class for tracking JSON contexts that may require
+ /// inserting/Reading additional JSON syntax characters
+ /// This base context does nothing.
+ /// </summary>
+ protected class JSONBaseContext
+ {
+ protected TJsonProtocol Proto;
+
+ public JSONBaseContext(TJsonProtocol proto)
+ {
+ Proto = proto;
+ }
+
+ public virtual async Task WriteAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public virtual async Task ReadAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public virtual bool EscapeNumbers()
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Context for JSON lists. Will insert/Read commas before each item except
+ /// for the first one
+ /// </summary>
+ protected class JSONListContext : JSONBaseContext
+ {
+ private bool _first = true;
+
+ public JSONListContext(TJsonProtocol protocol)
+ : base(protocol)
+ {
+ }
+
+ public override async Task WriteAsync(CancellationToken cancellationToken)
+ {
+ if (_first)
+ {
+ _first = false;
+ }
+ else
+ {
+ await Proto.Trans.WriteAsync(TJSONProtocolConstants.Comma, cancellationToken);
+ }
+ }
+
+ public override async Task ReadAsync(CancellationToken cancellationToken)
+ {
+ if (_first)
+ {
+ _first = false;
+ }
+ else
+ {
+ await Proto.ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Comma, cancellationToken);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Context for JSON records. Will insert/Read colons before the value portion
+ /// of each record pair, and commas before each key except the first. In
+ /// addition, will indicate that numbers in the key position need to be
+ /// escaped in quotes (since JSON keys must be strings).
+ /// </summary>
+ // ReSharper disable once InconsistentNaming
+ protected class JSONPairContext : JSONBaseContext
+ {
+ private bool _colon = true;
+
+ private bool _first = true;
+
+ public JSONPairContext(TJsonProtocol proto)
+ : base(proto)
+ {
+ }
+
+ public override async Task WriteAsync(CancellationToken cancellationToken)
+ {
+ if (_first)
+ {
+ _first = false;
+ _colon = true;
+ }
+ else
+ {
+ await Proto.Trans.WriteAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken);
+ _colon = !_colon;
+ }
+ }
+
+ public override async Task ReadAsync(CancellationToken cancellationToken)
+ {
+ if (_first)
+ {
+ _first = false;
+ _colon = true;
+ }
+ else
+ {
+ await Proto.ReadJsonSyntaxCharAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken);
+ _colon = !_colon;
+ }
+ }
+
+ public override bool EscapeNumbers()
+ {
+ return _colon;
+ }
+ }
+
+ /// <summary>
+ /// Holds up to one byte from the transport
+ /// </summary>
+ protected class LookaheadReader
+ {
+ private readonly byte[] _data = new byte[1];
+
+ private bool _hasData;
+ protected TJsonProtocol Proto;
+
+ public LookaheadReader(TJsonProtocol proto)
+ {
+ Proto = proto;
+ }
+
+ /// <summary>
+ /// Return and consume the next byte to be Read, either taking it from the
+ /// data buffer if present or getting it from the transport otherwise.
+ /// </summary>
+ public async Task<byte> ReadAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<byte>(cancellationToken);
+ }
+
+ if (_hasData)
+ {
+ _hasData = false;
+ }
+ else
+ {
+ // find more easy way to avoid exception on reading primitive types
+ await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken);
+ }
+ return _data[0];
+ }
+
+ /// <summary>
+ /// Return the next byte to be Read without consuming, filling the data
+ /// buffer if it has not been filled alReady.
+ /// </summary>
+ public async Task<byte> PeekAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<byte>(cancellationToken);
+ }
+
+ if (!_hasData)
+ {
+ // find more easy way to avoid exception on reading primitive types
+ await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken);
+ }
+ _hasData = true;
+ return _data[0];
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TMultiplexedProtocol.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TMultiplexedProtocol.cs
new file mode 100644
index 000000000..367e4e644
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TMultiplexedProtocol.cs
@@ -0,0 +1,91 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocols.Entities;
+
+namespace Thrift.Protocols
+{
+ /**
+ * TMultiplexedProtocol is a protocol-independent concrete decorator that allows a Thrift
+ * client to communicate with a multiplexing Thrift server, by prepending the service name
+ * to the function name during function calls.
+ *
+ * NOTE: THIS IS NOT TO BE USED BY SERVERS.
+ * On the server, use TMultiplexedProcessor to handle requests from a multiplexing client.
+ *
+ * This example uses a single socket transport to invoke two services:
+ *
+ * TSocketClientTransport transport = new TSocketClientTransport("localhost", 9090);
+ * transport.open();
+ *
+ * TBinaryProtocol protocol = new TBinaryProtocol(transport);
+ *
+ * TMultiplexedProtocol mp = new TMultiplexedProtocol(protocol, "Calculator");
+ * Calculator.Client service = new Calculator.Client(mp);
+ *
+ * TMultiplexedProtocol mp2 = new TMultiplexedProtocol(protocol, "WeatherReport");
+ * WeatherReport.Client service2 = new WeatherReport.Client(mp2);
+ *
+ * System.out.println(service.add(2,2));
+ * System.out.println(service2.getTemperature());
+ *
+ */
+
+ //TODO: implementation of TProtocol
+
+ // ReSharper disable once InconsistentNaming
+ public class TMultiplexedProtocol : TProtocolDecorator
+ {
+ /** Used to delimit the service name from the function name */
+ public const string Separator = ":";
+
+ private readonly string _serviceName;
+
+ /**
+ * Wrap the specified protocol, allowing it to be used to communicate with a
+ * multiplexing server. The <code>serviceName</code> is required as it is
+ * prepended to the message header so that the multiplexing server can broker
+ * the function call to the proper service.
+ *
+ * Args:
+ * protocol Your communication protocol of choice, e.g. TBinaryProtocol
+ * serviceName The service name of the service communicating via this protocol.
+ */
+
+ public TMultiplexedProtocol(TProtocol protocol, string serviceName)
+ : base(protocol)
+ {
+ _serviceName = serviceName;
+ }
+
+ public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
+ {
+ switch (message.Type)
+ {
+ case TMessageType.Call:
+ case TMessageType.Oneway:
+ await base.WriteMessageBeginAsync(new TMessage($"{_serviceName}{Separator}{message.Name}", message.Type, message.SeqID), cancellationToken);
+ break;
+ default:
+ await base.WriteMessageBeginAsync(message, cancellationToken);
+ break;
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TProtocol.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TProtocol.cs
new file mode 100644
index 000000000..91e009d63
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TProtocol.cs
@@ -0,0 +1,376 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocols.Entities;
+using Thrift.Transports;
+
+namespace Thrift.Protocols
+{
+ // ReSharper disable once InconsistentNaming
+ public abstract class TProtocol : IDisposable
+ {
+ public const int DefaultRecursionDepth = 64;
+ private bool _isDisposed;
+ protected int RecursionDepth;
+
+ protected TClientTransport Trans;
+
+ protected TProtocol(TClientTransport trans)
+ {
+ Trans = trans;
+ RecursionLimit = DefaultRecursionDepth;
+ RecursionDepth = 0;
+ }
+
+ public TClientTransport Transport => Trans;
+
+ protected int RecursionLimit { get; set; }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ public void IncrementRecursionDepth()
+ {
+ if (RecursionDepth < RecursionLimit)
+ {
+ ++RecursionDepth;
+ }
+ else
+ {
+ throw new TProtocolException(TProtocolException.DEPTH_LIMIT, "Depth limit exceeded");
+ }
+ }
+
+ public void DecrementRecursionDepth()
+ {
+ --RecursionDepth;
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ (Trans as IDisposable)?.Dispose();
+ }
+ }
+ _isDisposed = true;
+ }
+
+ public virtual async Task WriteMessageBeginAsync(TMessage message)
+ {
+ await WriteMessageBeginAsync(message, CancellationToken.None);
+ }
+
+ public abstract Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken);
+
+ public virtual async Task WriteMessageEndAsync()
+ {
+ await WriteMessageEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task WriteMessageEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task WriteStructBeginAsync(TStruct @struct)
+ {
+ await WriteStructBeginAsync(@struct, CancellationToken.None);
+ }
+
+ public abstract Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken);
+
+ public virtual async Task WriteStructEndAsync()
+ {
+ await WriteStructEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task WriteStructEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task WriteFieldBeginAsync(TField field)
+ {
+ await WriteFieldBeginAsync(field, CancellationToken.None);
+ }
+
+ public abstract Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken);
+
+ public virtual async Task WriteFieldEndAsync()
+ {
+ await WriteFieldEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task WriteFieldEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task WriteFieldStopAsync()
+ {
+ await WriteFieldStopAsync(CancellationToken.None);
+ }
+
+ public abstract Task WriteFieldStopAsync(CancellationToken cancellationToken);
+
+ public virtual async Task WriteMapBeginAsync(TMap map)
+ {
+ await WriteMapBeginAsync(map, CancellationToken.None);
+ }
+
+ public abstract Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken);
+
+ public virtual async Task WriteMapEndAsync()
+ {
+ await WriteMapEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task WriteMapEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task WriteListBeginAsync(TList list)
+ {
+ await WriteListBeginAsync(list, CancellationToken.None);
+ }
+
+ public abstract Task WriteListBeginAsync(TList list, CancellationToken cancellationToken);
+
+ public virtual async Task WriteListEndAsync()
+ {
+ await WriteListEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task WriteListEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task WriteSetBeginAsync(TSet set)
+ {
+ await WriteSetBeginAsync(set, CancellationToken.None);
+ }
+
+ public abstract Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken);
+
+ public virtual async Task WriteSetEndAsync()
+ {
+ await WriteSetEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task WriteSetEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task WriteBoolAsync(bool b)
+ {
+ await WriteBoolAsync(b, CancellationToken.None);
+ }
+
+ public abstract Task WriteBoolAsync(bool b, CancellationToken cancellationToken);
+
+ public virtual async Task WriteByteAsync(sbyte b)
+ {
+ await WriteByteAsync(b, CancellationToken.None);
+ }
+
+ public abstract Task WriteByteAsync(sbyte b, CancellationToken cancellationToken);
+
+ public virtual async Task WriteI16Async(short i16)
+ {
+ await WriteI16Async(i16, CancellationToken.None);
+ }
+
+ public abstract Task WriteI16Async(short i16, CancellationToken cancellationToken);
+
+ public virtual async Task WriteI32Async(int i32)
+ {
+ await WriteI32Async(i32, CancellationToken.None);
+ }
+
+ public abstract Task WriteI32Async(int i32, CancellationToken cancellationToken);
+
+ public virtual async Task WriteI64Async(long i64)
+ {
+ await WriteI64Async(i64, CancellationToken.None);
+ }
+
+ public abstract Task WriteI64Async(long i64, CancellationToken cancellationToken);
+
+ public virtual async Task WriteDoubleAsync(double d)
+ {
+ await WriteDoubleAsync(d, CancellationToken.None);
+ }
+
+ public abstract Task WriteDoubleAsync(double d, CancellationToken cancellationToken);
+
+ public virtual async Task WriteStringAsync(string s)
+ {
+ await WriteStringAsync(s, CancellationToken.None);
+ }
+
+ public virtual async Task WriteStringAsync(string s, CancellationToken cancellationToken)
+ {
+ var bytes = Encoding.UTF8.GetBytes(s);
+ await WriteBinaryAsync(bytes, cancellationToken);
+ }
+
+ public virtual async Task WriteBinaryAsync(byte[] bytes)
+ {
+ await WriteBinaryAsync(bytes, CancellationToken.None);
+ }
+
+ public abstract Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken);
+
+ public virtual async Task<TMessage> ReadMessageBeginAsync()
+ {
+ return await ReadMessageBeginAsync(CancellationToken.None);
+ }
+
+ public abstract Task<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken);
+
+ public virtual async Task ReadMessageEndAsync()
+ {
+ await ReadMessageEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task ReadMessageEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task<TStruct> ReadStructBeginAsync()
+ {
+ return await ReadStructBeginAsync(CancellationToken.None);
+ }
+
+ public abstract Task<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken);
+
+ public virtual async Task ReadStructEndAsync()
+ {
+ await ReadStructEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task ReadStructEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task<TField> ReadFieldBeginAsync()
+ {
+ return await ReadFieldBeginAsync(CancellationToken.None);
+ }
+
+ public abstract Task<TField> ReadFieldBeginAsync(CancellationToken cancellationToken);
+
+ public virtual async Task ReadFieldEndAsync()
+ {
+ await ReadFieldEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task ReadFieldEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task<TMap> ReadMapBeginAsync()
+ {
+ return await ReadMapBeginAsync(CancellationToken.None);
+ }
+
+ public abstract Task<TMap> ReadMapBeginAsync(CancellationToken cancellationToken);
+
+ public virtual async Task ReadMapEndAsync()
+ {
+ await ReadMapEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task ReadMapEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task<TList> ReadListBeginAsync()
+ {
+ return await ReadListBeginAsync(CancellationToken.None);
+ }
+
+ public abstract Task<TList> ReadListBeginAsync(CancellationToken cancellationToken);
+
+ public virtual async Task ReadListEndAsync()
+ {
+ await ReadListEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task ReadListEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task<TSet> ReadSetBeginAsync()
+ {
+ return await ReadSetBeginAsync(CancellationToken.None);
+ }
+
+ public abstract Task<TSet> ReadSetBeginAsync(CancellationToken cancellationToken);
+
+ public virtual async Task ReadSetEndAsync()
+ {
+ await ReadSetEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task ReadSetEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task<bool> ReadBoolAsync()
+ {
+ return await ReadBoolAsync(CancellationToken.None);
+ }
+
+ public abstract Task<bool> ReadBoolAsync(CancellationToken cancellationToken);
+
+ public virtual async Task<sbyte> ReadByteAsync()
+ {
+ return await ReadByteAsync(CancellationToken.None);
+ }
+
+ public abstract Task<sbyte> ReadByteAsync(CancellationToken cancellationToken);
+
+ public virtual async Task<short> ReadI16Async()
+ {
+ return await ReadI16Async(CancellationToken.None);
+ }
+
+ public abstract Task<short> ReadI16Async(CancellationToken cancellationToken);
+
+ public virtual async Task<int> ReadI32Async()
+ {
+ return await ReadI32Async(CancellationToken.None);
+ }
+
+ public abstract Task<int> ReadI32Async(CancellationToken cancellationToken);
+
+ public virtual async Task<long> ReadI64Async()
+ {
+ return await ReadI64Async(CancellationToken.None);
+ }
+
+ public abstract Task<long> ReadI64Async(CancellationToken cancellationToken);
+
+ public virtual async Task<double> ReadDoubleAsync()
+ {
+ return await ReadDoubleAsync(CancellationToken.None);
+ }
+
+ public abstract Task<double> ReadDoubleAsync(CancellationToken cancellationToken);
+
+ public virtual async Task<string> ReadStringAsync()
+ {
+ return await ReadStringAsync(CancellationToken.None);
+ }
+
+ public virtual async Task<string> ReadStringAsync(CancellationToken cancellationToken)
+ {
+ var buf = await ReadBinaryAsync(cancellationToken);
+ return Encoding.UTF8.GetString(buf, 0, buf.Length);
+ }
+
+ public virtual async Task<byte[]> ReadBinaryAsync()
+ {
+ return await ReadBinaryAsync(CancellationToken.None);
+ }
+
+ public abstract Task<byte[]> ReadBinaryAsync(CancellationToken cancellationToken);
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TProtocolDecorator.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TProtocolDecorator.cs
new file mode 100644
index 000000000..3222754a8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TProtocolDecorator.cs
@@ -0,0 +1,247 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocols.Entities;
+
+namespace Thrift.Protocols
+{
+ // ReSharper disable once InconsistentNaming
+ /// <summary>
+ /// TProtocolDecorator forwards all requests to an enclosed TProtocol instance,
+ /// providing a way to author concise concrete decorator subclasses.While it has
+ /// no abstract methods, it is marked abstract as a reminder that by itself,
+ /// it does not modify the behaviour of the enclosed TProtocol.
+ /// </summary>
+ public abstract class TProtocolDecorator : TProtocol
+ {
+ private readonly TProtocol _wrappedProtocol;
+
+ protected TProtocolDecorator(TProtocol protocol)
+ : base(protocol.Transport)
+ {
+ _wrappedProtocol = protocol ?? throw new ArgumentNullException(nameof(protocol));
+ }
+
+ public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteMessageBeginAsync(message, cancellationToken);
+ }
+
+ public override async Task WriteMessageEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteMessageEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteStructBeginAsync(@struct, cancellationToken);
+ }
+
+ public override async Task WriteStructEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteStructEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteFieldBeginAsync(field, cancellationToken);
+ }
+
+ public override async Task WriteFieldEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteFieldEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteFieldStopAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteFieldStopAsync(cancellationToken);
+ }
+
+ public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteMapBeginAsync(map, cancellationToken);
+ }
+
+ public override async Task WriteMapEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteMapEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteListBeginAsync(list, cancellationToken);
+ }
+
+ public override async Task WriteListEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteListEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteSetBeginAsync(set, cancellationToken);
+ }
+
+ public override async Task WriteSetEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteSetEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteBoolAsync(b, cancellationToken);
+ }
+
+ public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteByteAsync(b, cancellationToken);
+ }
+
+ public override async Task WriteI16Async(short i16, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteI16Async(i16, cancellationToken);
+ }
+
+ public override async Task WriteI32Async(int i32, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteI32Async(i32, cancellationToken);
+ }
+
+ public override async Task WriteI64Async(long i64, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteI64Async(i64, cancellationToken);
+ }
+
+ public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteDoubleAsync(d, cancellationToken);
+ }
+
+ public override async Task WriteStringAsync(string s, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteStringAsync(s, cancellationToken);
+ }
+
+ public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteBinaryAsync(bytes, cancellationToken);
+ }
+
+ public override async Task<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadMessageBeginAsync(cancellationToken);
+ }
+
+ public override async Task ReadMessageEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.ReadMessageEndAsync(cancellationToken);
+ }
+
+ public override async Task<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadStructBeginAsync(cancellationToken);
+ }
+
+ public override async Task ReadStructEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.ReadStructEndAsync(cancellationToken);
+ }
+
+ public override async Task<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadFieldBeginAsync(cancellationToken);
+ }
+
+ public override async Task ReadFieldEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.ReadFieldEndAsync(cancellationToken);
+ }
+
+ public override async Task<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadMapBeginAsync(cancellationToken);
+ }
+
+ public override async Task ReadMapEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.ReadMapEndAsync(cancellationToken);
+ }
+
+ public override async Task<TList> ReadListBeginAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadListBeginAsync(cancellationToken);
+ }
+
+ public override async Task ReadListEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.ReadListEndAsync(cancellationToken);
+ }
+
+ public override async Task<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadSetBeginAsync(cancellationToken);
+ }
+
+ public override async Task ReadSetEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.ReadSetEndAsync(cancellationToken);
+ }
+
+ public override async Task<bool> ReadBoolAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadBoolAsync(cancellationToken);
+ }
+
+ public override async Task<sbyte> ReadByteAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadByteAsync(cancellationToken);
+ }
+
+ public override async Task<short> ReadI16Async(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadI16Async(cancellationToken);
+ }
+
+ public override async Task<int> ReadI32Async(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadI32Async(cancellationToken);
+ }
+
+ public override async Task<long> ReadI64Async(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadI64Async(cancellationToken);
+ }
+
+ public override async Task<double> ReadDoubleAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadDoubleAsync(cancellationToken);
+ }
+
+ public override async Task<string> ReadStringAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadStringAsync(cancellationToken);
+ }
+
+ public override async Task<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadBinaryAsync(cancellationToken);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TProtocolException.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TProtocolException.cs
new file mode 100644
index 000000000..8c67c3bfd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/TProtocolException.cs
@@ -0,0 +1,59 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// ReSharper disable InconsistentNaming
+namespace Thrift.Protocols
+{
+ public class TProtocolException : TException
+ {
+ // do not rename public constants - they used in generated files
+ public const int UNKNOWN = 0;
+ public const int INVALID_DATA = 1;
+ public const int NEGATIVE_SIZE = 2;
+ public const int SIZE_LIMIT = 3;
+ public const int BAD_VERSION = 4;
+ public const int NOT_IMPLEMENTED = 5;
+ public const int DEPTH_LIMIT = 6;
+
+ protected int Type = UNKNOWN;
+
+ public TProtocolException()
+ {
+ }
+
+ public TProtocolException(int type)
+ {
+ Type = type;
+ }
+
+ public TProtocolException(int type, string message)
+ : base(message)
+ {
+ Type = type;
+ }
+
+ public TProtocolException(string message)
+ : base(message)
+ {
+ }
+
+ public int GetExceptionType()
+ {
+ return Type;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TBase64Helper.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TBase64Helper.cs
new file mode 100644
index 000000000..7eff5e181
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TBase64Helper.cs
@@ -0,0 +1,101 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+
+namespace Thrift.Protocols.Utilities
+{
+ // ReSharper disable once InconsistentNaming
+ internal static class TBase64Helper
+ {
+ //TODO: Constants
+ //TODO: Check for args
+ //TODO: Unitests
+
+ internal const string EncodeTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ private static readonly int[] DecodeTable =
+ {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ };
+
+ internal static void Encode(byte[] src, int srcOff, int len, byte[] dst, int dstOff)
+ {
+ if (src == null)
+ {
+ throw new ArgumentNullException(nameof(src));
+ }
+
+ dst[dstOff] = (byte) EncodeTable[(src[srcOff] >> 2) & 0x3F];
+
+ if (len == 3)
+ {
+ dst[dstOff + 1] = (byte) EncodeTable[((src[srcOff] << 4) & 0x30) | ((src[srcOff + 1] >> 4) & 0x0F)];
+ dst[dstOff + 2] = (byte) EncodeTable[((src[srcOff + 1] << 2) & 0x3C) | ((src[srcOff + 2] >> 6) & 0x03)];
+ dst[dstOff + 3] = (byte) EncodeTable[src[srcOff + 2] & 0x3F];
+ }
+ else if (len == 2)
+ {
+ dst[dstOff + 1] = (byte) EncodeTable[((src[srcOff] << 4) & 0x30) | ((src[srcOff + 1] >> 4) & 0x0F)];
+ dst[dstOff + 2] = (byte) EncodeTable[(src[srcOff + 1] << 2) & 0x3C];
+ }
+ else
+ {
+ // len == 1
+ dst[dstOff + 1] = (byte) EncodeTable[(src[srcOff] << 4) & 0x30];
+ }
+ }
+
+ internal static void Decode(byte[] src, int srcOff, int len, byte[] dst, int dstOff)
+ {
+ if (src == null)
+ {
+ throw new ArgumentNullException(nameof(src));
+ }
+
+ dst[dstOff] = (byte) ((DecodeTable[src[srcOff] & 0x0FF] << 2) | (DecodeTable[src[srcOff + 1] & 0x0FF] >> 4));
+
+ if (len > 2)
+ {
+ dst[dstOff + 1] =
+ (byte)
+ (((DecodeTable[src[srcOff + 1] & 0x0FF] << 4) & 0xF0) | (DecodeTable[src[srcOff + 2] & 0x0FF] >> 2));
+ if (len > 3)
+ {
+ dst[dstOff + 2] =
+ (byte)
+ (((DecodeTable[src[srcOff + 2] & 0x0FF] << 6) & 0xC0) | DecodeTable[src[srcOff + 3] & 0x0FF]);
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TBase64Utils.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TBase64Utils.cs
new file mode 100644
index 000000000..15fd45cbe
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TBase64Utils.cs
@@ -0,0 +1,101 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+
+namespace Thrift.Protocols.Utilities
+{
+ // ReSharper disable once InconsistentNaming
+ internal static class TBase64Utils
+ {
+ //TODO: Constants
+ //TODO: Check for args
+ //TODO: Unitests
+
+ internal const string EncodeTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ private static readonly int[] DecodeTable =
+ {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ };
+
+ internal static void Encode(byte[] src, int srcOff, int len, byte[] dst, int dstOff)
+ {
+ if (src == null)
+ {
+ throw new ArgumentNullException(nameof(src));
+ }
+
+ dst[dstOff] = (byte) EncodeTable[(src[srcOff] >> 2) & 0x3F];
+
+ if (len == 3)
+ {
+ dst[dstOff + 1] = (byte) EncodeTable[((src[srcOff] << 4) & 0x30) | ((src[srcOff + 1] >> 4) & 0x0F)];
+ dst[dstOff + 2] = (byte) EncodeTable[((src[srcOff + 1] << 2) & 0x3C) | ((src[srcOff + 2] >> 6) & 0x03)];
+ dst[dstOff + 3] = (byte) EncodeTable[src[srcOff + 2] & 0x3F];
+ }
+ else if (len == 2)
+ {
+ dst[dstOff + 1] = (byte) EncodeTable[((src[srcOff] << 4) & 0x30) | ((src[srcOff + 1] >> 4) & 0x0F)];
+ dst[dstOff + 2] = (byte) EncodeTable[(src[srcOff + 1] << 2) & 0x3C];
+ }
+ else
+ {
+ // len == 1
+ dst[dstOff + 1] = (byte) EncodeTable[(src[srcOff] << 4) & 0x30];
+ }
+ }
+
+ internal static void Decode(byte[] src, int srcOff, int len, byte[] dst, int dstOff)
+ {
+ if (src == null)
+ {
+ throw new ArgumentNullException(nameof(src));
+ }
+
+ dst[dstOff] = (byte) ((DecodeTable[src[srcOff] & 0x0FF] << 2) | (DecodeTable[src[srcOff + 1] & 0x0FF] >> 4));
+
+ if (len > 2)
+ {
+ dst[dstOff + 1] =
+ (byte)
+ (((DecodeTable[src[srcOff + 1] & 0x0FF] << 4) & 0xF0) | (DecodeTable[src[srcOff + 2] & 0x0FF] >> 2));
+ if (len > 3)
+ {
+ dst[dstOff + 2] =
+ (byte)
+ (((DecodeTable[src[srcOff + 2] & 0x0FF] << 6) & 0xC0) | DecodeTable[src[srcOff + 3] & 0x0FF]);
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TJsonProtocolConstants.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TJsonProtocolConstants.cs
new file mode 100644
index 000000000..93eff7855
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TJsonProtocolConstants.cs
@@ -0,0 +1,61 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocols.Utilities
+{
+ // ReSharper disable once InconsistentNaming
+ public static class TJSONProtocolConstants
+ {
+ //TODO Check for performance for reusing ImmutableArray from System.Collections.Immutable (https://blogs.msdn.microsoft.com/dotnet/2013/06/24/please-welcome-immutablearrayt/)
+ // can be possible to get better performance and also better GC
+
+ public static readonly byte[] Comma = {(byte) ','};
+ public static readonly byte[] Colon = {(byte) ':'};
+ public static readonly byte[] LeftBrace = {(byte) '{'};
+ public static readonly byte[] RightBrace = {(byte) '}'};
+ public static readonly byte[] LeftBracket = {(byte) '['};
+ public static readonly byte[] RightBracket = {(byte) ']'};
+ public static readonly byte[] Quote = {(byte) '"'};
+ public static readonly byte[] Backslash = {(byte) '\\'};
+
+ public static readonly byte[] JsonCharTable =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, (byte) 'b', (byte) 't', (byte) 'n', 0, (byte) 'f', (byte) 'r', 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, (byte) '"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ };
+
+ public static readonly char[] EscapeChars = "\"\\/bfnrt".ToCharArray();
+ public static readonly byte[] EscapeCharValues = {(byte) '"', (byte) '\\', (byte) '/', (byte) '\b', (byte) '\f', (byte) '\n', (byte) '\r', (byte) '\t'};
+ public static readonly byte[] EscSequences = {(byte) '\\', (byte) 'u', (byte) '0', (byte) '0'};
+
+ public static class TypeNames
+ {
+ public static readonly byte[] NameBool = { (byte)'t', (byte)'f' };
+ public static readonly byte[] NameByte = { (byte)'i', (byte)'8' };
+ public static readonly byte[] NameI16 = { (byte)'i', (byte)'1', (byte)'6' };
+ public static readonly byte[] NameI32 = { (byte)'i', (byte)'3', (byte)'2' };
+ public static readonly byte[] NameI64 = { (byte)'i', (byte)'6', (byte)'4' };
+ public static readonly byte[] NameDouble = { (byte)'d', (byte)'b', (byte)'l' };
+ public static readonly byte[] NameStruct = { (byte)'r', (byte)'e', (byte)'c' };
+ public static readonly byte[] NameString = { (byte)'s', (byte)'t', (byte)'r' };
+ public static readonly byte[] NameMap = { (byte)'m', (byte)'a', (byte)'p' };
+ public static readonly byte[] NameList = { (byte)'l', (byte)'s', (byte)'t' };
+ public static readonly byte[] NameSet = { (byte)'s', (byte)'e', (byte)'t' };
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TJsonProtocolHelper.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TJsonProtocolHelper.cs
new file mode 100644
index 000000000..adc26a9af
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TJsonProtocolHelper.cs
@@ -0,0 +1,176 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using Thrift.Protocols.Entities;
+
+namespace Thrift.Protocols.Utilities
+{
+ // ReSharper disable once InconsistentNaming
+ public static class TJSONProtocolHelper
+ {
+ public static byte[] GetTypeNameForTypeId(TType typeId)
+ {
+ switch (typeId)
+ {
+ case TType.Bool:
+ return TJSONProtocolConstants.TypeNames.NameBool;
+ case TType.Byte:
+ return TJSONProtocolConstants.TypeNames.NameByte;
+ case TType.I16:
+ return TJSONProtocolConstants.TypeNames.NameI16;
+ case TType.I32:
+ return TJSONProtocolConstants.TypeNames.NameI32;
+ case TType.I64:
+ return TJSONProtocolConstants.TypeNames.NameI64;
+ case TType.Double:
+ return TJSONProtocolConstants.TypeNames.NameDouble;
+ case TType.String:
+ return TJSONProtocolConstants.TypeNames.NameString;
+ case TType.Struct:
+ return TJSONProtocolConstants.TypeNames.NameStruct;
+ case TType.Map:
+ return TJSONProtocolConstants.TypeNames.NameMap;
+ case TType.Set:
+ return TJSONProtocolConstants.TypeNames.NameSet;
+ case TType.List:
+ return TJSONProtocolConstants.TypeNames.NameList;
+ default:
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "Unrecognized exType");
+ }
+ }
+
+ public static TType GetTypeIdForTypeName(byte[] name)
+ {
+ var result = TType.Stop;
+ if (name.Length > 1)
+ {
+ switch (name[0])
+ {
+ case (byte) 'd':
+ result = TType.Double;
+ break;
+ case (byte) 'i':
+ switch (name[1])
+ {
+ case (byte) '8':
+ result = TType.Byte;
+ break;
+ case (byte) '1':
+ result = TType.I16;
+ break;
+ case (byte) '3':
+ result = TType.I32;
+ break;
+ case (byte) '6':
+ result = TType.I64;
+ break;
+ }
+ break;
+ case (byte) 'l':
+ result = TType.List;
+ break;
+ case (byte) 'm':
+ result = TType.Map;
+ break;
+ case (byte) 'r':
+ result = TType.Struct;
+ break;
+ case (byte) 's':
+ if (name[1] == (byte) 't')
+ {
+ result = TType.String;
+ }
+ else if (name[1] == (byte) 'e')
+ {
+ result = TType.Set;
+ }
+ break;
+ case (byte) 't':
+ result = TType.Bool;
+ break;
+ }
+ }
+ if (result == TType.Stop)
+ {
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "Unrecognized exType");
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Return true if the given byte could be a valid part of a JSON number.
+ /// </summary>
+ public static bool IsJsonNumeric(byte b)
+ {
+ switch (b)
+ {
+ case (byte)'+':
+ case (byte)'-':
+ case (byte)'.':
+ case (byte)'0':
+ case (byte)'1':
+ case (byte)'2':
+ case (byte)'3':
+ case (byte)'4':
+ case (byte)'5':
+ case (byte)'6':
+ case (byte)'7':
+ case (byte)'8':
+ case (byte)'9':
+ case (byte)'E':
+ case (byte)'e':
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its
+ /// corresponding hex value
+ /// </summary>
+ public static byte ToHexVal(byte ch)
+ {
+ if (ch >= '0' && ch <= '9')
+ {
+ return (byte)((char)ch - '0');
+ }
+
+ if (ch >= 'a' && ch <= 'f')
+ {
+ ch += 10;
+ return (byte)((char)ch - 'a');
+ }
+
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected hex character");
+ }
+
+ /// <summary>
+ /// Convert a byte containing a hex value to its corresponding hex character
+ /// </summary>
+ public static byte ToHexChar(byte val)
+ {
+ val &= 0x0F;
+ if (val < 10)
+ {
+ return (byte)((char)val + '0');
+ }
+ val -= 10;
+ return (byte)((char)val + 'a');
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TProtocolUtil.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TProtocolUtil.cs
new file mode 100644
index 000000000..50b038566
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Protocols/Utilities/TProtocolUtil.cs
@@ -0,0 +1,110 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocols.Entities;
+
+namespace Thrift.Protocols.Utilities
+{
+ // ReSharper disable once InconsistentNaming
+ public static class TProtocolUtil
+ {
+ public static async Task SkipAsync(TProtocol protocol, TType type, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+
+ protocol.IncrementRecursionDepth();
+ try
+ {
+ switch (type)
+ {
+ case TType.Bool:
+ await protocol.ReadBoolAsync(cancellationToken);
+ break;
+ case TType.Byte:
+ await protocol.ReadByteAsync(cancellationToken);
+ break;
+ case TType.I16:
+ await protocol.ReadI16Async(cancellationToken);
+ break;
+ case TType.I32:
+ await protocol.ReadI32Async(cancellationToken);
+ break;
+ case TType.I64:
+ await protocol.ReadI64Async(cancellationToken);
+ break;
+ case TType.Double:
+ await protocol.ReadDoubleAsync(cancellationToken);
+ break;
+ case TType.String:
+ // Don't try to decode the string, just skip it.
+ await protocol.ReadBinaryAsync(cancellationToken);
+ break;
+ case TType.Struct:
+ await protocol.ReadStructBeginAsync(cancellationToken);
+ while (true)
+ {
+ var field = await protocol.ReadFieldBeginAsync(cancellationToken);
+ if (field.Type == TType.Stop)
+ {
+ break;
+ }
+ await SkipAsync(protocol, field.Type, cancellationToken);
+ await protocol.ReadFieldEndAsync(cancellationToken);
+ }
+ await protocol.ReadStructEndAsync(cancellationToken);
+ break;
+ case TType.Map:
+ var map = await protocol.ReadMapBeginAsync(cancellationToken);
+ for (var i = 0; i < map.Count; i++)
+ {
+ await SkipAsync(protocol, map.KeyType, cancellationToken);
+ await SkipAsync(protocol, map.ValueType, cancellationToken);
+ }
+ await protocol.ReadMapEndAsync(cancellationToken);
+ break;
+ case TType.Set:
+ var set = await protocol.ReadSetBeginAsync(cancellationToken);
+ for (var i = 0; i < set.Count; i++)
+ {
+ await SkipAsync(protocol, set.ElementType, cancellationToken);
+ }
+ await protocol.ReadSetEndAsync(cancellationToken);
+ break;
+ case TType.List:
+ var list = await protocol.ReadListBeginAsync(cancellationToken);
+ for (var i = 0; i < list.Count; i++)
+ {
+ await SkipAsync(protocol, list.ElementType, cancellationToken);
+ }
+ await protocol.ReadListEndAsync(cancellationToken);
+ break;
+ default:
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Unknown data type " + type.ToString("d"));
+ }
+ }
+ finally
+ {
+ protocol.DecrementRecursionDepth();
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Server/AsyncBaseServer.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Server/AsyncBaseServer.cs
new file mode 100644
index 000000000..325c39c71
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Server/AsyncBaseServer.cs
@@ -0,0 +1,183 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Thrift.Protocols;
+using Thrift.Transports;
+
+namespace Thrift.Server
+{
+ //TODO: unhandled exceptions, etc.
+
+ // ReSharper disable once InconsistentNaming
+ public class AsyncBaseServer : TBaseServer
+ {
+ private readonly int _clientWaitingDelay;
+ private volatile Task _serverTask;
+
+ public AsyncBaseServer(ITAsyncProcessor processor, TServerTransport serverTransport,
+ ITProtocolFactory inputProtocolFactory, ITProtocolFactory outputProtocolFactory,
+ ILoggerFactory loggerFactory, int clientWaitingDelay = 10)
+ : this(new SingletonTProcessorFactory(processor), serverTransport,
+ new TTransportFactory(), new TTransportFactory(),
+ inputProtocolFactory, outputProtocolFactory,
+ loggerFactory.CreateLogger(nameof(AsyncBaseServer)), clientWaitingDelay)
+ {
+ }
+
+ public AsyncBaseServer(ITProcessorFactory itProcessorFactory, TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory, TTransportFactory outputTransportFactory,
+ ITProtocolFactory inputProtocolFactory, ITProtocolFactory outputProtocolFactory,
+ ILogger logger, int clientWaitingDelay = 10)
+ : base(itProcessorFactory, serverTransport, inputTransportFactory, outputTransportFactory,
+ inputProtocolFactory, outputProtocolFactory, logger)
+ {
+ _clientWaitingDelay = clientWaitingDelay;
+ }
+
+ public override async Task ServeAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ // cancelation token
+ _serverTask = Task.Factory.StartNew(() => StartListening(cancellationToken), TaskCreationOptions.LongRunning);
+ await _serverTask;
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError(ex.ToString());
+ }
+ }
+
+ private async Task StartListening(CancellationToken cancellationToken)
+ {
+ ServerTransport.Listen();
+
+ Logger.LogTrace("Started listening at server");
+
+ if (ServerEventHandler != null)
+ {
+ await ServerEventHandler.PreServeAsync(cancellationToken);
+ }
+
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ if (ServerTransport.IsClientPending())
+ {
+ Logger.LogTrace("Waiting for client connection");
+
+ try
+ {
+ var client = await ServerTransport.AcceptAsync(cancellationToken);
+ await Task.Factory.StartNew(() => Execute(client, cancellationToken), cancellationToken);
+ }
+ catch (TTransportException ttx)
+ {
+ Logger.LogTrace($"Transport exception: {ttx}");
+
+ if (ttx.Type != TTransportException.ExceptionType.Interrupted)
+ {
+ Logger.LogError(ttx.ToString());
+ }
+ }
+ }
+ else
+ {
+ try
+ {
+ await Task.Delay(TimeSpan.FromMilliseconds(_clientWaitingDelay), cancellationToken);
+ }
+ catch(TaskCanceledException) { }
+ }
+ }
+
+ ServerTransport.Close();
+
+ Logger.LogTrace("Completed listening at server");
+ }
+
+ public override void Stop()
+ {
+ }
+
+ private async Task Execute(TClientTransport client, CancellationToken cancellationToken)
+ {
+ Logger.LogTrace("Started client request processing");
+
+ var processor = ItProcessorFactory.GetAsyncProcessor(client, this);
+
+ TClientTransport inputTransport = null;
+ TClientTransport outputTransport = null;
+ TProtocol inputProtocol = null;
+ TProtocol outputProtocol = null;
+ object connectionContext = null;
+
+ try
+ {
+ inputTransport = InputTransportFactory.GetTransport(client);
+ outputTransport = OutputTransportFactory.GetTransport(client);
+
+ inputProtocol = InputProtocolFactory.GetProtocol(inputTransport);
+ outputProtocol = OutputProtocolFactory.GetProtocol(outputTransport);
+
+ if (ServerEventHandler != null)
+ {
+ connectionContext = await ServerEventHandler.CreateContextAsync(inputProtocol, outputProtocol, cancellationToken);
+ }
+
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ if (!await inputTransport.PeekAsync(cancellationToken))
+ {
+ break;
+ }
+
+ if (ServerEventHandler != null)
+ {
+ await ServerEventHandler.ProcessContextAsync(connectionContext, inputTransport, cancellationToken);
+ }
+
+ if (!await processor.ProcessAsync(inputProtocol, outputProtocol, cancellationToken))
+ {
+ break;
+ }
+ }
+ }
+ catch (TTransportException ttx)
+ {
+ Logger.LogTrace($"Transport exception: {ttx}");
+ }
+ catch (Exception x)
+ {
+ Logger.LogError($"Error: {x}");
+ }
+
+ if (ServerEventHandler != null)
+ {
+ await ServerEventHandler.DeleteContextAsync(connectionContext, inputProtocol, outputProtocol, cancellationToken);
+ }
+
+ inputTransport?.Close();
+ outputTransport?.Close();
+
+ Logger.LogTrace("Completed client request processing");
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Server/TBaseServer.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Server/TBaseServer.cs
new file mode 100644
index 000000000..741dd5c95
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Server/TBaseServer.cs
@@ -0,0 +1,79 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Thrift.Protocols;
+using Thrift.Transports;
+
+namespace Thrift.Server
+{
+ // ReSharper disable once InconsistentNaming
+ public abstract class TBaseServer
+ {
+ protected readonly ILogger Logger;
+ protected ITProtocolFactory InputProtocolFactory;
+ protected TTransportFactory InputTransportFactory;
+ protected ITProcessorFactory ItProcessorFactory;
+ protected ITProtocolFactory OutputProtocolFactory;
+ protected TTransportFactory OutputTransportFactory;
+
+ protected TServerEventHandler ServerEventHandler;
+ protected TServerTransport ServerTransport;
+
+ protected TBaseServer(ITProcessorFactory itProcessorFactory, TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory, TTransportFactory outputTransportFactory,
+ ITProtocolFactory inputProtocolFactory, ITProtocolFactory outputProtocolFactory,
+ ILogger logger)
+ {
+ ItProcessorFactory = itProcessorFactory ?? throw new ArgumentNullException(nameof(itProcessorFactory));
+ ServerTransport = serverTransport;
+ InputTransportFactory = inputTransportFactory ?? throw new ArgumentNullException(nameof(inputTransportFactory));
+ OutputTransportFactory = outputTransportFactory ?? throw new ArgumentNullException(nameof(outputTransportFactory));
+ InputProtocolFactory = inputProtocolFactory ?? throw new ArgumentNullException(nameof(inputProtocolFactory));
+ OutputProtocolFactory = outputProtocolFactory ?? throw new ArgumentNullException(nameof(outputProtocolFactory));
+ Logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ }
+
+ public void SetEventHandler(TServerEventHandler seh)
+ {
+ ServerEventHandler = seh;
+ }
+
+ public TServerEventHandler GetEventHandler()
+ {
+ return ServerEventHandler;
+ }
+
+ public abstract void Stop();
+
+ public virtual void Start()
+ {
+ // do nothing
+ }
+
+ public virtual async Task ServeAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Server/TServerEventHandler.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Server/TServerEventHandler.cs
new file mode 100644
index 000000000..733bb4bef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Server/TServerEventHandler.cs
@@ -0,0 +1,54 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocols;
+using Thrift.Transports;
+
+namespace Thrift.Server
+{
+ //TODO: replacement by event?
+
+ /// <summary>
+ /// Interface implemented by server users to handle events from the server
+ /// </summary>
+ // ReSharper disable once InconsistentNaming
+ public interface TServerEventHandler
+ {
+ /// <summary>
+ /// Called before the server begins */
+ /// </summary>
+ Task PreServeAsync(CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Called when a new client has connected and is about to being processing */
+ /// </summary>
+ Task<object> CreateContextAsync(TProtocol input, TProtocol output, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Called when a client has finished request-handling to delete server context */
+ /// </summary>
+ Task DeleteContextAsync(object serverContext, TProtocol input, TProtocol output,
+ CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Called when a client is about to call the processor */
+ /// </summary>
+ Task ProcessContextAsync(object serverContext, TClientTransport transport, CancellationToken cancellationToken);
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/SingletonTProcessorFactory.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/SingletonTProcessorFactory.cs
new file mode 100644
index 000000000..c35123348
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/SingletonTProcessorFactory.cs
@@ -0,0 +1,38 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using Thrift.Server;
+using Thrift.Transports;
+
+namespace Thrift
+{
+ // ReSharper disable once InconsistentNaming
+ public class SingletonTProcessorFactory : ITProcessorFactory
+ {
+ private readonly ITAsyncProcessor _tAsyncProcessor;
+
+ public SingletonTProcessorFactory(ITAsyncProcessor tAsyncProcessor)
+ {
+ _tAsyncProcessor = tAsyncProcessor;
+ }
+
+ public ITAsyncProcessor GetAsyncProcessor(TClientTransport trans, TBaseServer baseServer = null)
+ {
+ return _tAsyncProcessor;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/TApplicationException.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/TApplicationException.cs
new file mode 100644
index 000000000..9ec145a85
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/TApplicationException.cs
@@ -0,0 +1,150 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocols;
+using Thrift.Protocols.Entities;
+using Thrift.Protocols.Utilities;
+
+namespace Thrift
+{
+ // ReSharper disable once InconsistentNaming
+ public class TApplicationException : TException
+ {
+ public enum ExceptionType
+ {
+ Unknown,
+ UnknownMethod,
+ InvalidMessageType,
+ WrongMethodName,
+ BadSequenceId,
+ MissingResult,
+ InternalError,
+ ProtocolError,
+ InvalidTransform,
+ InvalidProtocol,
+ UnsupportedClientType
+ }
+
+ private const int MessageTypeFieldId = 1;
+ private const int ExTypeFieldId = 2;
+
+ protected ExceptionType Type;
+
+ public TApplicationException()
+ {
+ }
+
+ public TApplicationException(ExceptionType type)
+ {
+ Type = type;
+ }
+
+ public TApplicationException(ExceptionType type, string message)
+ : base(message)
+ {
+ Type = type;
+ }
+
+ public static async Task<TApplicationException> ReadAsync(TProtocol inputProtocol, CancellationToken cancellationToken)
+ {
+ string message = null;
+ var type = ExceptionType.Unknown;
+
+ await inputProtocol.ReadStructBeginAsync(cancellationToken);
+ while (true)
+ {
+ var field = await inputProtocol.ReadFieldBeginAsync(cancellationToken);
+ if (field.Type == TType.Stop)
+ {
+ break;
+ }
+
+ switch (field.ID)
+ {
+ case MessageTypeFieldId:
+ if (field.Type == TType.String)
+ {
+ message = await inputProtocol.ReadStringAsync(cancellationToken);
+ }
+ else
+ {
+ await TProtocolUtil.SkipAsync(inputProtocol, field.Type, cancellationToken);
+ }
+ break;
+ case ExTypeFieldId:
+ if (field.Type == TType.I32)
+ {
+ type = (ExceptionType) await inputProtocol.ReadI32Async(cancellationToken);
+ }
+ else
+ {
+ await TProtocolUtil.SkipAsync(inputProtocol, field.Type, cancellationToken);
+ }
+ break;
+ default:
+ await TProtocolUtil.SkipAsync(inputProtocol, field.Type, cancellationToken);
+ break;
+ }
+
+ await inputProtocol.ReadFieldEndAsync(cancellationToken);
+ }
+
+ await inputProtocol.ReadStructEndAsync(cancellationToken);
+
+ return new TApplicationException(type, message);
+ }
+
+ public async Task WriteAsync(TProtocol outputProtocol, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+
+ const string messageTypeFieldName = "message";
+ const string exTypeFieldName = "exType";
+ const string structApplicationExceptionName = "TApplicationException";
+
+ var struc = new TStruct(structApplicationExceptionName);
+ var field = new TField();
+
+ await outputProtocol.WriteStructBeginAsync(struc, cancellationToken);
+
+ if (!string.IsNullOrEmpty(Message))
+ {
+ field.Name = messageTypeFieldName;
+ field.Type = TType.String;
+ field.ID = MessageTypeFieldId;
+ await outputProtocol.WriteFieldBeginAsync(field, cancellationToken);
+ await outputProtocol.WriteStringAsync(Message, cancellationToken);
+ await outputProtocol.WriteFieldEndAsync(cancellationToken);
+ }
+
+ field.Name = exTypeFieldName;
+ field.Type = TType.I32;
+ field.ID = ExTypeFieldId;
+
+ await outputProtocol.WriteFieldBeginAsync(field, cancellationToken);
+ await outputProtocol.WriteI32Async((int) Type, cancellationToken);
+ await outputProtocol.WriteFieldEndAsync(cancellationToken);
+ await outputProtocol.WriteFieldStopAsync(cancellationToken);
+ await outputProtocol.WriteStructEndAsync(cancellationToken);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/TBaseClient.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/TBaseClient.cs
new file mode 100644
index 000000000..e01925153
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/TBaseClient.cs
@@ -0,0 +1,91 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocols;
+
+namespace Thrift
+{
+ // ReSharper disable once InconsistentNaming
+ /// <summary>
+ /// TBaseClient.
+ /// Base client for generated clients.
+ /// Do not change this class without checking generated code (namings, etc.)
+ /// </summary>
+ public abstract class TBaseClient
+ {
+ private readonly TProtocol _inputProtocol;
+ private readonly TProtocol _outputProtocol;
+ private bool _isDisposed;
+ private int _seqId;
+ public readonly Guid ClientId = Guid.NewGuid();
+
+ protected TBaseClient(TProtocol inputProtocol, TProtocol outputProtocol)
+ {
+ _inputProtocol = inputProtocol ?? throw new ArgumentNullException(nameof(inputProtocol));
+ _outputProtocol = outputProtocol ?? throw new ArgumentNullException(nameof(outputProtocol));
+ }
+
+ public TProtocol InputProtocol => _inputProtocol;
+
+ public TProtocol OutputProtocol => _outputProtocol;
+
+ public int SeqId
+ {
+ get { return ++_seqId; }
+ }
+
+ public virtual async Task OpenTransportAsync()
+ {
+ await OpenTransportAsync(CancellationToken.None);
+ }
+
+ public virtual async Task OpenTransportAsync(CancellationToken cancellationToken)
+ {
+ if (!_inputProtocol.Transport.IsOpen)
+ {
+ await _inputProtocol.Transport.OpenAsync(cancellationToken);
+ }
+
+ if (!_inputProtocol.Transport.IsOpen)
+ {
+ await _outputProtocol.Transport.OpenAsync(cancellationToken);
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ _inputProtocol?.Dispose();
+ _outputProtocol?.Dispose();
+ }
+ }
+
+ _isDisposed = true;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/TException.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/TException.cs
new file mode 100644
index 000000000..6aa588d7f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/TException.cs
@@ -0,0 +1,34 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+
+namespace Thrift
+{
+ // ReSharper disable once InconsistentNaming
+ public class TException : Exception
+ {
+ public TException()
+ {
+ }
+
+ public TException(string message)
+ : base(message)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/TMultiplexedProcessor.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/TMultiplexedProcessor.cs
new file mode 100644
index 000000000..ad0e749e0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/TMultiplexedProcessor.cs
@@ -0,0 +1,143 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocols;
+using Thrift.Protocols.Entities;
+
+namespace Thrift
+{
+ // ReSharper disable once InconsistentNaming
+ public class TMultiplexedProcessor : ITAsyncProcessor
+ {
+ //TODO: Localization
+
+ private readonly Dictionary<string, ITAsyncProcessor> _serviceProcessorMap =
+ new Dictionary<string, ITAsyncProcessor>();
+
+ public async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot)
+ {
+ return await ProcessAsync(iprot, oprot, CancellationToken.None);
+ }
+
+ public async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<bool>(cancellationToken);
+ }
+
+ try
+ {
+ var message = await iprot.ReadMessageBeginAsync(cancellationToken);
+
+ if ((message.Type != TMessageType.Call) && (message.Type != TMessageType.Oneway))
+ {
+ await FailAsync(oprot, message, TApplicationException.ExceptionType.InvalidMessageType,
+ "Message exType CALL or ONEWAY expected", cancellationToken);
+ return false;
+ }
+
+ // Extract the service name
+ var index = message.Name.IndexOf(TMultiplexedProtocol.Separator, StringComparison.Ordinal);
+ if (index < 0)
+ {
+ await FailAsync(oprot, message, TApplicationException.ExceptionType.InvalidProtocol,
+ $"Service name not found in message name: {message.Name}. Did you forget to use a TMultiplexProtocol in your client?",
+ cancellationToken);
+ return false;
+ }
+
+ // Create a new TMessage, something that can be consumed by any TProtocol
+ var serviceName = message.Name.Substring(0, index);
+ ITAsyncProcessor actualProcessor;
+ if (!_serviceProcessorMap.TryGetValue(serviceName, out actualProcessor))
+ {
+ await FailAsync(oprot, message, TApplicationException.ExceptionType.InternalError,
+ $"Service name not found: {serviceName}. Did you forget to call RegisterProcessor()?",
+ cancellationToken);
+ return false;
+ }
+
+ // Create a new TMessage, removing the service name
+ var newMessage = new TMessage(
+ message.Name.Substring(serviceName.Length + TMultiplexedProtocol.Separator.Length),
+ message.Type,
+ message.SeqID);
+
+ // Dispatch processing to the stored processor
+ return
+ await
+ actualProcessor.ProcessAsync(new StoredMessageProtocol(iprot, newMessage), oprot,
+ cancellationToken);
+ }
+ catch (IOException)
+ {
+ return false; // similar to all other processors
+ }
+ }
+
+ public void RegisterProcessor(string serviceName, ITAsyncProcessor processor)
+ {
+ if (_serviceProcessorMap.ContainsKey(serviceName))
+ {
+ throw new InvalidOperationException(
+ $"Processor map already contains processor with name: '{serviceName}'");
+ }
+
+ _serviceProcessorMap.Add(serviceName, processor);
+ }
+
+ private async Task FailAsync(TProtocol oprot, TMessage message, TApplicationException.ExceptionType extype,
+ string etxt, CancellationToken cancellationToken)
+ {
+ var appex = new TApplicationException(extype, etxt);
+
+ var newMessage = new TMessage(message.Name, TMessageType.Exception, message.SeqID);
+
+ await oprot.WriteMessageBeginAsync(newMessage, cancellationToken);
+ await appex.WriteAsync(oprot, cancellationToken);
+ await oprot.WriteMessageEndAsync(cancellationToken);
+ await oprot.Transport.FlushAsync(cancellationToken);
+ }
+
+ private class StoredMessageProtocol : TProtocolDecorator
+ {
+ readonly TMessage _msgBegin;
+
+ public StoredMessageProtocol(TProtocol protocol, TMessage messageBegin)
+ : base(protocol)
+ {
+ _msgBegin = messageBegin;
+ }
+
+ public override async Task<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TMessage>(cancellationToken);
+ }
+
+ return _msgBegin;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Thrift.csproj b/src/jaegertracing/thrift/lib/netcore/Thrift/Thrift.csproj
new file mode 100644
index 000000000..6a2ccc30d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Thrift.csproj
@@ -0,0 +1,33 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <AssemblyName>Thrift</AssemblyName>
+ <PackageId>Thrift</PackageId>
+ <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+ <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
+ <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
+ <GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
+ <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
+ <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
+ <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
+ <GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
+ <GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
+ <GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
+ <SignAssembly>true</SignAssembly>
+ <AssemblyOriginatorKeyFile>thrift.snk</AssemblyOriginatorKeyFile>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.AspNetCore" Version="[2.0,)" />
+ <PackageReference Include="Microsoft.Extensions.Logging" Version="[2.0,)" />
+ <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="[2.0,)" />
+ <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="[2.0,)" />
+ <PackageReference Include="System.IO.Pipes" Version="[4.3,)" />
+ <PackageReference Include="System.Net.Http.WinHttpHandler" Version="4.4.0" />
+ <PackageReference Include="System.Net.NameResolution" Version="[4.3,)" />
+ <PackageReference Include="System.Net.Requests" Version="[4.3,)" />
+ <PackageReference Include="System.Net.Security" Version="[4.3,)" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TBufferedClientTransport.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TBufferedClientTransport.cs
new file mode 100644
index 000000000..761f1ac78
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TBufferedClientTransport.cs
@@ -0,0 +1,206 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transports.Client
+{
+ // ReSharper disable once InconsistentNaming
+ public class TBufferedClientTransport : TClientTransport
+ {
+ private readonly int _bufSize;
+ private readonly MemoryStream _inputBuffer = new MemoryStream(0);
+ private readonly MemoryStream _outputBuffer = new MemoryStream(0);
+ private readonly TClientTransport _transport;
+ private bool _isDisposed;
+
+ //TODO: should support only specified input transport?
+ public TBufferedClientTransport(TClientTransport transport, int bufSize = 1024)
+ {
+ if (bufSize <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(bufSize), "Buffer size must be a positive number.");
+ }
+
+ _transport = transport ?? throw new ArgumentNullException(nameof(transport));
+ _bufSize = bufSize;
+ }
+
+ public TClientTransport UnderlyingTransport
+ {
+ get
+ {
+ CheckNotDisposed();
+
+ return _transport;
+ }
+ }
+
+ public override bool IsOpen => !_isDisposed && _transport.IsOpen;
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ CheckNotDisposed();
+
+ await _transport.OpenAsync(cancellationToken);
+ }
+
+ public override void Close()
+ {
+ CheckNotDisposed();
+
+ _transport.Close();
+ }
+
+ public override async Task<int> ReadAsync(byte[] buffer, int offset, int length,
+ CancellationToken cancellationToken)
+ {
+ //TODO: investigate how it should work correctly
+ CheckNotDisposed();
+
+ ValidateBufferArgs(buffer, offset, length);
+
+ if (!IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ if (_inputBuffer.Capacity < _bufSize)
+ {
+ _inputBuffer.Capacity = _bufSize;
+ }
+
+ var got = await _inputBuffer.ReadAsync(buffer, offset, length, cancellationToken);
+ if (got > 0)
+ {
+ return got;
+ }
+
+ _inputBuffer.Seek(0, SeekOrigin.Begin);
+ _inputBuffer.SetLength(_inputBuffer.Capacity);
+
+ ArraySegment<byte> bufSegment;
+ _inputBuffer.TryGetBuffer(out bufSegment);
+
+ // investigate
+ var filled = await _transport.ReadAsync(bufSegment.Array, 0, (int) _inputBuffer.Length, cancellationToken);
+ _inputBuffer.SetLength(filled);
+
+ if (filled == 0)
+ {
+ return 0;
+ }
+
+ return await ReadAsync(buffer, offset, length, cancellationToken);
+ }
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ CheckNotDisposed();
+
+ ValidateBufferArgs(buffer, offset, length);
+
+ if (!IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ // Relative offset from "off" argument
+ var writtenCount = 0;
+ if (_outputBuffer.Length > 0)
+ {
+ var capa = (int) (_outputBuffer.Capacity - _outputBuffer.Length);
+ var writeSize = capa <= length ? capa : length;
+ await _outputBuffer.WriteAsync(buffer, offset, writeSize, cancellationToken);
+
+ writtenCount += writeSize;
+ if (writeSize == capa)
+ {
+ //ArraySegment<byte> bufSegment;
+ //_outputBuffer.TryGetBuffer(out bufSegment);
+ var data = _outputBuffer.ToArray();
+ //await _transport.WriteAsync(bufSegment.Array, cancellationToken);
+ await _transport.WriteAsync(data, cancellationToken);
+ _outputBuffer.SetLength(0);
+ }
+ }
+
+ while (length - writtenCount >= _bufSize)
+ {
+ await _transport.WriteAsync(buffer, offset + writtenCount, _bufSize, cancellationToken);
+ writtenCount += _bufSize;
+ }
+
+ var remain = length - writtenCount;
+ if (remain > 0)
+ {
+ if (_outputBuffer.Capacity < _bufSize)
+ {
+ _outputBuffer.Capacity = _bufSize;
+ }
+ await _outputBuffer.WriteAsync(buffer, offset + writtenCount, remain, cancellationToken);
+ }
+ }
+
+ public override async Task FlushAsync(CancellationToken cancellationToken)
+ {
+ CheckNotDisposed();
+
+ if (!IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ if (_outputBuffer.Length > 0)
+ {
+ //ArraySegment<byte> bufSegment;
+ var data = _outputBuffer.ToArray(); // TryGetBuffer(out bufSegment);
+
+ await _transport.WriteAsync(data /*bufSegment.Array*/, cancellationToken);
+ _outputBuffer.SetLength(0);
+ }
+
+ await _transport.FlushAsync(cancellationToken);
+ }
+
+ private void CheckNotDisposed()
+ {
+ if (_isDisposed)
+ {
+ throw new ObjectDisposedException(nameof(_transport));
+ }
+ }
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ _inputBuffer?.Dispose();
+ _outputBuffer?.Dispose();
+ _transport?.Dispose();
+ }
+ }
+ _isDisposed = true;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TFramedClientTransport.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TFramedClientTransport.cs
new file mode 100644
index 000000000..d11bb959a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TFramedClientTransport.cs
@@ -0,0 +1,201 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transports.Client
+{
+ //TODO: check for correct implementation
+
+ // ReSharper disable once InconsistentNaming
+ public class TFramedClientTransport : TClientTransport
+ {
+ private const int HeaderSize = 4;
+ private readonly byte[] _headerBuf = new byte[HeaderSize];
+ private readonly MemoryStream _readBuffer = new MemoryStream(1024);
+ private readonly TClientTransport _transport;
+ private readonly MemoryStream _writeBuffer = new MemoryStream(1024);
+
+ private bool _isDisposed;
+
+ public TFramedClientTransport(TClientTransport transport)
+ {
+ _transport = transport ?? throw new ArgumentNullException(nameof(transport));
+
+ InitWriteBuffer();
+ }
+
+ public override bool IsOpen => !_isDisposed && _transport.IsOpen;
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ CheckNotDisposed();
+
+ await _transport.OpenAsync(cancellationToken);
+ }
+
+ public override void Close()
+ {
+ CheckNotDisposed();
+
+ _transport.Close();
+ }
+
+ public override async Task<int> ReadAsync(byte[] buffer, int offset, int length,
+ CancellationToken cancellationToken)
+ {
+ CheckNotDisposed();
+
+ ValidateBufferArgs(buffer, offset, length);
+
+ if (!IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ var got = await _readBuffer.ReadAsync(buffer, offset, length, cancellationToken);
+ if (got > 0)
+ {
+ return got;
+ }
+
+ // Read another frame of data
+ await ReadFrameAsync(cancellationToken);
+
+ return await _readBuffer.ReadAsync(buffer, offset, length, cancellationToken);
+ }
+
+ private async Task ReadFrameAsync(CancellationToken cancellationToken)
+ {
+ await _transport.ReadAllAsync(_headerBuf, 0, HeaderSize, cancellationToken);
+
+ var size = DecodeFrameSize(_headerBuf);
+
+ _readBuffer.SetLength(size);
+ _readBuffer.Seek(0, SeekOrigin.Begin);
+
+ ArraySegment<byte> bufSegment;
+ _readBuffer.TryGetBuffer(out bufSegment);
+
+ var buff = bufSegment.Array;
+
+ await _transport.ReadAllAsync(buff, 0, size, cancellationToken);
+ }
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ CheckNotDisposed();
+
+ ValidateBufferArgs(buffer, offset, length);
+
+ if (!IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ if (_writeBuffer.Length + length > int.MaxValue)
+ {
+ await FlushAsync(cancellationToken);
+ }
+
+ await _writeBuffer.WriteAsync(buffer, offset, length, cancellationToken);
+ }
+
+ public override async Task FlushAsync(CancellationToken cancellationToken)
+ {
+ CheckNotDisposed();
+
+ if (!IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ //ArraySegment<byte> bufSegment;
+ //_writeBuffer.TryGetBuffer(out bufSegment);
+ //var buf = bufSegment.Array;
+ var buf = _writeBuffer.ToArray();
+
+ //var len = (int)_writeBuffer.Length;
+ var dataLen = (int) _writeBuffer.Length - HeaderSize;
+ if (dataLen < 0)
+ {
+ throw new InvalidOperationException(); // logic error actually
+ }
+
+ // Inject message header into the reserved buffer space
+ EncodeFrameSize(dataLen, buf);
+
+ // Send the entire message at once
+ await _transport.WriteAsync(buf, cancellationToken);
+
+ InitWriteBuffer();
+
+ await _transport.FlushAsync(cancellationToken);
+ }
+
+ private void InitWriteBuffer()
+ {
+ // Reserve space for message header to be put right before sending it out
+ _writeBuffer.SetLength(HeaderSize);
+ _writeBuffer.Seek(0, SeekOrigin.End);
+ }
+
+ private static void EncodeFrameSize(int frameSize, byte[] buf)
+ {
+ buf[0] = (byte) (0xff & (frameSize >> 24));
+ buf[1] = (byte) (0xff & (frameSize >> 16));
+ buf[2] = (byte) (0xff & (frameSize >> 8));
+ buf[3] = (byte) (0xff & (frameSize));
+ }
+
+ private static int DecodeFrameSize(byte[] buf)
+ {
+ return
+ ((buf[0] & 0xff) << 24) |
+ ((buf[1] & 0xff) << 16) |
+ ((buf[2] & 0xff) << 8) |
+ (buf[3] & 0xff);
+ }
+
+
+ private void CheckNotDisposed()
+ {
+ if (_isDisposed)
+ {
+ throw new ObjectDisposedException("TFramedClientTransport");
+ }
+ }
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ _readBuffer?.Dispose();
+ _writeBuffer?.Dispose();
+ _transport?.Dispose();
+ }
+ }
+ _isDisposed = true;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/THttpClientTransport.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/THttpClientTransport.cs
new file mode 100644
index 000000000..8bce9e4a0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/THttpClientTransport.cs
@@ -0,0 +1,227 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transports.Client
+{
+ // ReSharper disable once InconsistentNaming
+ public class THttpClientTransport : TClientTransport
+ {
+ private readonly X509Certificate[] _certificates;
+ private readonly Uri _uri;
+
+ // Timeouts in milliseconds
+ private int _connectTimeout = 30000;
+ private HttpClient _httpClient;
+ private Stream _inputStream;
+
+ private bool _isDisposed;
+ private MemoryStream _outputStream = new MemoryStream();
+
+ public THttpClientTransport(Uri u, IDictionary<string, string> customHeaders)
+ : this(u, Enumerable.Empty<X509Certificate>(), customHeaders)
+ {
+ }
+
+ public THttpClientTransport(Uri u, IEnumerable<X509Certificate> certificates,
+ IDictionary<string, string> customHeaders)
+ {
+ _uri = u;
+ _certificates = (certificates ?? Enumerable.Empty<X509Certificate>()).ToArray();
+ CustomHeaders = customHeaders;
+
+ // due to current bug with performance of Dispose in netcore https://github.com/dotnet/corefx/issues/8809
+ // this can be switched to default way (create client->use->dispose per flush) later
+ _httpClient = CreateClient();
+ }
+
+ public IDictionary<string, string> CustomHeaders { get; }
+
+ public int ConnectTimeout
+ {
+ set { _connectTimeout = value; }
+ get { return _connectTimeout; }
+ }
+
+ public override bool IsOpen => true;
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override void Close()
+ {
+ if (_inputStream != null)
+ {
+ _inputStream.Dispose();
+ _inputStream = null;
+ }
+
+ if (_outputStream != null)
+ {
+ _outputStream.Dispose();
+ _outputStream = null;
+ }
+
+ if (_httpClient != null)
+ {
+ _httpClient.Dispose();
+ _httpClient = null;
+ }
+ }
+
+ public override async Task<int> ReadAsync(byte[] buffer, int offset, int length,
+ CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<int>(cancellationToken);
+ }
+
+ if (_inputStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No request has been sent");
+ }
+
+ try
+ {
+ var ret = await _inputStream.ReadAsync(buffer, offset, length, cancellationToken);
+
+ if (ret == -1)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.EndOfFile, "No more data available");
+ }
+
+ return ret;
+ }
+ catch (IOException iox)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString());
+ }
+ }
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+
+ await _outputStream.WriteAsync(buffer, offset, length, cancellationToken);
+ }
+
+ private HttpClient CreateClient()
+ {
+ var handler = new HttpClientHandler();
+ handler.ClientCertificates.AddRange(_certificates);
+
+ var httpClient = new HttpClient(handler);
+
+ if (_connectTimeout > 0)
+ {
+ httpClient.Timeout = TimeSpan.FromMilliseconds(_connectTimeout);
+ }
+
+ httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-thrift"));
+ httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("THttpClientTransport", "0.13.0"));
+
+ if (CustomHeaders != null)
+ {
+ foreach (var item in CustomHeaders)
+ {
+ httpClient.DefaultRequestHeaders.Add(item.Key, item.Value);
+ }
+ }
+
+ return httpClient;
+ }
+
+ public override async Task FlushAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ try
+ {
+ if (_outputStream.CanSeek)
+ {
+ _outputStream.Seek(0, SeekOrigin.Begin);
+ }
+
+ using (var outStream = new StreamContent(_outputStream))
+ {
+ var msg = await _httpClient.PostAsync(_uri, outStream, cancellationToken);
+
+ msg.EnsureSuccessStatusCode();
+
+ if (_inputStream != null)
+ {
+ _inputStream.Dispose();
+ _inputStream = null;
+ }
+
+ _inputStream = await msg.Content.ReadAsStreamAsync();
+ if (_inputStream.CanSeek)
+ {
+ _inputStream.Seek(0, SeekOrigin.Begin);
+ }
+ }
+ }
+ catch (IOException iox)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString());
+ }
+ catch (HttpRequestException wx)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown,
+ "Couldn't connect to server: " + wx);
+ }
+ }
+ finally
+ {
+ _outputStream = new MemoryStream();
+ }
+ }
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ _inputStream?.Dispose();
+ _outputStream?.Dispose();
+ _httpClient?.Dispose();
+ }
+ }
+ _isDisposed = true;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TMemoryBufferClientTransport.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TMemoryBufferClientTransport.cs
new file mode 100644
index 000000000..46a55a64a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TMemoryBufferClientTransport.cs
@@ -0,0 +1,97 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transports.Client
+{
+ // ReSharper disable once InconsistentNaming
+ public class TMemoryBufferClientTransport : TClientTransport
+ {
+ private readonly MemoryStream _byteStream;
+ private bool _isDisposed;
+
+ public TMemoryBufferClientTransport()
+ {
+ _byteStream = new MemoryStream();
+ }
+
+ public TMemoryBufferClientTransport(byte[] buf)
+ {
+ _byteStream = new MemoryStream(buf);
+ }
+
+ public override bool IsOpen => true;
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override void Close()
+ {
+ /** do nothing **/
+ }
+
+ public override async Task<int> ReadAsync(byte[] buffer, int offset, int length,
+ CancellationToken cancellationToken)
+ {
+ return await _byteStream.ReadAsync(buffer, offset, length, cancellationToken);
+ }
+
+ public override async Task WriteAsync(byte[] buffer, CancellationToken cancellationToken)
+ {
+ await _byteStream.WriteAsync(buffer, 0, buffer.Length, cancellationToken);
+ }
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ await _byteStream.WriteAsync(buffer, offset, length, cancellationToken);
+ }
+
+ public override async Task FlushAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public byte[] GetBuffer()
+ {
+ return _byteStream.ToArray();
+ }
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ _byteStream?.Dispose();
+ }
+ }
+ _isDisposed = true;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TNamedPipeClientTransport.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TNamedPipeClientTransport.cs
new file mode 100644
index 000000000..f5e4baf4a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TNamedPipeClientTransport.cs
@@ -0,0 +1,95 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.IO.Pipes;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transports.Client
+{
+ // ReSharper disable once InconsistentNaming
+ public class TNamedPipeClientTransport : TClientTransport
+ {
+ private NamedPipeClientStream _client;
+
+ public TNamedPipeClientTransport(string pipe) : this(".", pipe)
+ {
+ }
+
+ public TNamedPipeClientTransport(string server, string pipe)
+ {
+ var serverName = string.IsNullOrWhiteSpace(server) ? server : ".";
+
+ _client = new NamedPipeClientStream(serverName, pipe, PipeDirection.InOut, PipeOptions.None);
+ }
+
+ public override bool IsOpen => _client != null && _client.IsConnected;
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ if (IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen);
+ }
+
+ await _client.ConnectAsync(cancellationToken);
+ }
+
+ public override void Close()
+ {
+ if (_client != null)
+ {
+ _client.Dispose();
+ _client = null;
+ }
+ }
+
+ public override async Task<int> ReadAsync(byte[] buffer, int offset, int length,
+ CancellationToken cancellationToken)
+ {
+ if (_client == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ return await _client.ReadAsync(buffer, offset, length, cancellationToken);
+ }
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ if (_client == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ await _client.WriteAsync(buffer, offset, length, cancellationToken);
+ }
+
+ public override async Task FlushAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ _client.Dispose();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TSocketClientTransport.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TSocketClientTransport.cs
new file mode 100644
index 000000000..e769d1421
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TSocketClientTransport.cs
@@ -0,0 +1,139 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transports.Client
+{
+ // ReSharper disable once InconsistentNaming
+ public class TSocketClientTransport : TStreamClientTransport
+ {
+ private bool _isDisposed;
+
+ public TSocketClientTransport(TcpClient client)
+ {
+ TcpClient = client ?? throw new ArgumentNullException(nameof(client));
+
+ if (IsOpen)
+ {
+ InputStream = client.GetStream();
+ OutputStream = client.GetStream();
+ }
+ }
+
+ public TSocketClientTransport(IPAddress host, int port)
+ : this(host, port, 0)
+ {
+ }
+
+ public TSocketClientTransport(IPAddress host, int port, int timeout)
+ {
+ Host = host;
+ Port = port;
+
+ TcpClient = new TcpClient();
+ TcpClient.ReceiveTimeout = TcpClient.SendTimeout = timeout;
+ TcpClient.Client.NoDelay = true;
+ }
+
+ public TcpClient TcpClient { get; private set; }
+ public IPAddress Host { get; }
+ public int Port { get; }
+
+ public int Timeout
+ {
+ set
+ {
+ if (TcpClient != null)
+ {
+ TcpClient.ReceiveTimeout = TcpClient.SendTimeout = value;
+ }
+ }
+ }
+
+ public override bool IsOpen
+ {
+ get
+ {
+ if (TcpClient == null)
+ {
+ return false;
+ }
+
+ return TcpClient.Connected;
+ }
+ }
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+
+ if (IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen, "Socket already connected");
+ }
+
+ if (Port <= 0)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open without port");
+ }
+
+ if (TcpClient == null)
+ {
+ throw new InvalidOperationException("Invalid or not initialized tcp client");
+ }
+
+ await TcpClient.ConnectAsync(Host, Port);
+
+ InputStream = TcpClient.GetStream();
+ OutputStream = TcpClient.GetStream();
+ }
+
+ public override void Close()
+ {
+ base.Close();
+
+ if (TcpClient != null)
+ {
+ TcpClient.Dispose();
+ TcpClient = null;
+ }
+ }
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ TcpClient?.Dispose();
+
+ base.Dispose(disposing);
+ }
+ }
+ _isDisposed = true;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TStreamClientTransport.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TStreamClientTransport.cs
new file mode 100644
index 000000000..be2011e83
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TStreamClientTransport.cs
@@ -0,0 +1,110 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transports.Client
+{
+ // ReSharper disable once InconsistentNaming
+ public class TStreamClientTransport : TClientTransport
+ {
+ private bool _isDisposed;
+
+ protected TStreamClientTransport()
+ {
+ }
+
+ public TStreamClientTransport(Stream inputStream, Stream outputStream)
+ {
+ InputStream = inputStream;
+ OutputStream = outputStream;
+ }
+
+ protected Stream OutputStream { get; set; }
+
+ protected Stream InputStream { get; set; }
+
+ public override bool IsOpen => true;
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override void Close()
+ {
+ if (InputStream != null)
+ {
+ InputStream.Dispose();
+ InputStream = null;
+ }
+
+ if (OutputStream != null)
+ {
+ OutputStream.Dispose();
+ OutputStream = null;
+ }
+ }
+
+ public override async Task<int> ReadAsync(byte[] buffer, int offset, int length,
+ CancellationToken cancellationToken)
+ {
+ if (InputStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen,
+ "Cannot read from null inputstream");
+ }
+
+ return await InputStream.ReadAsync(buffer, offset, length, cancellationToken);
+ }
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ if (OutputStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen,
+ "Cannot write to null outputstream");
+ }
+
+ await OutputStream.WriteAsync(buffer, offset, length, cancellationToken);
+ }
+
+ public override async Task FlushAsync(CancellationToken cancellationToken)
+ {
+ await OutputStream.FlushAsync(cancellationToken);
+ }
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ InputStream?.Dispose();
+ OutputStream?.Dispose();
+ }
+ }
+ _isDisposed = true;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TTlsSocketClientTransport.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TTlsSocketClientTransport.cs
new file mode 100644
index 000000000..c8be4ede1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Client/TTlsSocketClientTransport.cs
@@ -0,0 +1,237 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Net;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transports.Client
+{
+ //TODO: check for correct work
+
+ // ReSharper disable once InconsistentNaming
+ public class TTlsSocketClientTransport : TStreamClientTransport
+ {
+ private readonly X509Certificate2 _certificate;
+ private readonly RemoteCertificateValidationCallback _certValidator;
+ private readonly IPAddress _host;
+ private readonly bool _isServer;
+ private readonly LocalCertificateSelectionCallback _localCertificateSelectionCallback;
+ private readonly int _port;
+ private readonly SslProtocols _sslProtocols;
+ private TcpClient _client;
+ private SslStream _secureStream;
+ private int _timeout;
+
+ public TTlsSocketClientTransport(TcpClient client, X509Certificate2 certificate, bool isServer = false,
+ RemoteCertificateValidationCallback certValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls12)
+ {
+ _client = client;
+ _certificate = certificate;
+ _certValidator = certValidator;
+ _localCertificateSelectionCallback = localCertificateSelectionCallback;
+ _sslProtocols = sslProtocols;
+ _isServer = isServer;
+
+ if (isServer && certificate == null)
+ {
+ throw new ArgumentException("TTlsSocketClientTransport needs certificate to be used for server",
+ nameof(certificate));
+ }
+
+ if (IsOpen)
+ {
+ InputStream = client.GetStream();
+ OutputStream = client.GetStream();
+ }
+ }
+
+ public TTlsSocketClientTransport(IPAddress host, int port, string certificatePath,
+ RemoteCertificateValidationCallback certValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls12)
+ : this(host, port, 0,
+ new X509Certificate2(certificatePath),
+ certValidator,
+ localCertificateSelectionCallback,
+ sslProtocols)
+ {
+ }
+
+ public TTlsSocketClientTransport(IPAddress host, int port,
+ X509Certificate2 certificate = null,
+ RemoteCertificateValidationCallback certValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls12)
+ : this(host, port, 0,
+ certificate,
+ certValidator,
+ localCertificateSelectionCallback,
+ sslProtocols)
+ {
+ }
+
+ public TTlsSocketClientTransport(IPAddress host, int port, int timeout,
+ X509Certificate2 certificate,
+ RemoteCertificateValidationCallback certValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls12)
+ {
+ _host = host;
+ _port = port;
+ _timeout = timeout;
+ _certificate = certificate;
+ _certValidator = certValidator;
+ _localCertificateSelectionCallback = localCertificateSelectionCallback;
+ _sslProtocols = sslProtocols;
+
+ InitSocket();
+ }
+
+ public int Timeout
+ {
+ set { _client.ReceiveTimeout = _client.SendTimeout = _timeout = value; }
+ }
+
+ public TcpClient TcpClient => _client;
+
+ public IPAddress Host => _host;
+
+ public int Port => _port;
+
+ public override bool IsOpen
+ {
+ get
+ {
+ if (_client == null)
+ {
+ return false;
+ }
+
+ return _client.Connected;
+ }
+ }
+
+ private void InitSocket()
+ {
+ _client = new TcpClient();
+ _client.ReceiveTimeout = _client.SendTimeout = _timeout;
+ _client.Client.NoDelay = true;
+ }
+
+ private bool DefaultCertificateValidator(object sender, X509Certificate certificate, X509Chain chain,
+ SslPolicyErrors sslValidationErrors)
+ {
+ return sslValidationErrors == SslPolicyErrors.None;
+ }
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ if (IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen, "Socket already connected");
+ }
+
+ if (_host == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open null host");
+ }
+
+ if (_port <= 0)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open without port");
+ }
+
+ if (_client == null)
+ {
+ InitSocket();
+ }
+
+ if (_client != null)
+ {
+ await _client.ConnectAsync(_host, _port);
+ await SetupTlsAsync();
+ }
+ }
+
+ public async Task SetupTlsAsync()
+ {
+ var validator = _certValidator ?? DefaultCertificateValidator;
+
+ if (_localCertificateSelectionCallback != null)
+ {
+ _secureStream = new SslStream(_client.GetStream(), false, validator, _localCertificateSelectionCallback);
+ }
+ else
+ {
+ _secureStream = new SslStream(_client.GetStream(), false, validator);
+ }
+
+ try
+ {
+ if (_isServer)
+ {
+ // Server authentication
+ await
+ _secureStream.AuthenticateAsServerAsync(_certificate, _certValidator != null, _sslProtocols,
+ true);
+ }
+ else
+ {
+ // Client authentication
+ var certs = _certificate != null
+ ? new X509CertificateCollection {_certificate}
+ : new X509CertificateCollection();
+
+ var targetHost = _host.ToString();
+ await _secureStream.AuthenticateAsClientAsync(targetHost, certs, _sslProtocols, true);
+ }
+ }
+ catch (Exception)
+ {
+ Close();
+ throw;
+ }
+
+ InputStream = _secureStream;
+ OutputStream = _secureStream;
+ }
+
+ public override void Close()
+ {
+ base.Close();
+ if (_client != null)
+ {
+ _client.Dispose();
+ _client = null;
+ }
+
+ if (_secureStream != null)
+ {
+ _secureStream.Dispose();
+ _secureStream = null;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/THttpServerTransport.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/THttpServerTransport.cs
new file mode 100644
index 000000000..032063a37
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/THttpServerTransport.cs
@@ -0,0 +1,98 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Thrift.Protocols;
+using Thrift.Transports.Client;
+
+namespace Thrift.Transports.Server
+{
+ // ReSharper disable once InconsistentNaming
+ public class THttpServerTransport
+ {
+ protected const string ContentType = "application/x-thrift";
+ private readonly ILogger _logger;
+ private readonly RequestDelegate _next;
+ protected Encoding Encoding = Encoding.UTF8;
+
+ protected ITProtocolFactory InputProtocolFactory;
+ protected ITProtocolFactory OutputProtocolFactory;
+
+ protected ITAsyncProcessor Processor;
+
+ public THttpServerTransport(ITAsyncProcessor processor, RequestDelegate next, ILoggerFactory loggerFactory)
+ : this(processor, new TBinaryProtocol.Factory(), next, loggerFactory)
+ {
+ }
+
+ public THttpServerTransport(ITAsyncProcessor processor, ITProtocolFactory protocolFactory, RequestDelegate next,
+ ILoggerFactory loggerFactory)
+ : this(processor, protocolFactory, protocolFactory, next, loggerFactory)
+ {
+ }
+
+ public THttpServerTransport(ITAsyncProcessor processor, ITProtocolFactory inputProtocolFactory,
+ ITProtocolFactory outputProtocolFactory, RequestDelegate next, ILoggerFactory loggerFactory)
+ {
+ if (loggerFactory == null)
+ {
+ throw new ArgumentNullException(nameof(loggerFactory));
+ }
+
+ Processor = processor ?? throw new ArgumentNullException(nameof(processor));
+ InputProtocolFactory = inputProtocolFactory ?? throw new ArgumentNullException(nameof(inputProtocolFactory));
+ OutputProtocolFactory = outputProtocolFactory ?? throw new ArgumentNullException(nameof(outputProtocolFactory));
+
+ _next = next;
+ _logger = loggerFactory.CreateLogger<THttpServerTransport>();
+ }
+
+ public async Task Invoke(HttpContext context)
+ {
+ context.Response.ContentType = ContentType;
+ await ProcessRequestAsync(context, context.RequestAborted); //TODO: check for correct logic
+ }
+
+ public async Task ProcessRequestAsync(HttpContext context, CancellationToken cancellationToken)
+ {
+ var transport = new TStreamClientTransport(context.Request.Body, context.Response.Body);
+
+ try
+ {
+ var input = InputProtocolFactory.GetProtocol(transport);
+ var output = OutputProtocolFactory.GetProtocol(transport);
+
+ while (await Processor.ProcessAsync(input, output, cancellationToken))
+ {
+ }
+ }
+ catch (TTransportException)
+ {
+ // Client died, just move on
+ }
+ finally
+ {
+ transport.Close();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/TNamedPipeServerTransport.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/TNamedPipeServerTransport.cs
new file mode 100644
index 000000000..186786ed2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/TNamedPipeServerTransport.cs
@@ -0,0 +1,191 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.IO.Pipes;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transports.Server
+{
+ // ReSharper disable once InconsistentNaming
+ public class TNamedPipeServerTransport : TServerTransport
+ {
+ /// <summary>
+ /// This is the address of the Pipe on the localhost.
+ /// </summary>
+ private readonly string _pipeAddress;
+
+ private bool _asyncMode = true;
+ private volatile bool _isPending = true;
+
+ private NamedPipeServerStream _stream = null;
+
+ public TNamedPipeServerTransport(string pipeAddress)
+ {
+ _pipeAddress = pipeAddress;
+ }
+
+ public override void Listen()
+ {
+ // nothing to do here
+ }
+
+ public override void Close()
+ {
+ if (_stream != null)
+ {
+ try
+ {
+ //TODO: check for disconection
+ _stream.Disconnect();
+ _stream.Dispose();
+ }
+ finally
+ {
+ _stream = null;
+ _isPending = false;
+ }
+ }
+ }
+
+ public override bool IsClientPending()
+ {
+ return _isPending;
+ }
+
+ private void EnsurePipeInstance()
+ {
+ if (_stream == null)
+ {
+ var direction = PipeDirection.InOut;
+ var maxconn = 254;
+ var mode = PipeTransmissionMode.Byte;
+ var options = _asyncMode ? PipeOptions.Asynchronous : PipeOptions.None;
+ var inbuf = 4096;
+ var outbuf = 4096;
+ // TODO: security
+
+ try
+ {
+ _stream = new NamedPipeServerStream(_pipeAddress, direction, maxconn, mode, options, inbuf, outbuf);
+ }
+ catch (NotImplementedException) // Mono still does not support async, fallback to sync
+ {
+ if (_asyncMode)
+ {
+ options &= (~PipeOptions.Asynchronous);
+ _stream = new NamedPipeServerStream(_pipeAddress, direction, maxconn, mode, options, inbuf,
+ outbuf);
+ _asyncMode = false;
+ }
+ else
+ {
+ throw;
+ }
+ }
+ }
+ }
+
+ protected override async Task<TClientTransport> AcceptImplementationAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ EnsurePipeInstance();
+
+ await _stream.WaitForConnectionAsync(cancellationToken);
+
+ var trans = new ServerTransport(_stream);
+ _stream = null; // pass ownership to ServerTransport
+
+ //_isPending = false;
+
+ return trans;
+ }
+ catch (TTransportException)
+ {
+ Close();
+ throw;
+ }
+ catch (Exception e)
+ {
+ Close();
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, e.Message);
+ }
+ }
+
+ private class ServerTransport : TClientTransport
+ {
+ private readonly NamedPipeServerStream _stream;
+
+ public ServerTransport(NamedPipeServerStream stream)
+ {
+ _stream = stream;
+ }
+
+ public override bool IsOpen => _stream != null && _stream.IsConnected;
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override void Close()
+ {
+ _stream?.Dispose();
+ }
+
+ public override async Task<int> ReadAsync(byte[] buffer, int offset, int length,
+ CancellationToken cancellationToken)
+ {
+ if (_stream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ return await _stream.ReadAsync(buffer, offset, length, cancellationToken);
+ }
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int length,
+ CancellationToken cancellationToken)
+ {
+ if (_stream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ await _stream.WriteAsync(buffer, offset, length, cancellationToken);
+ }
+
+ public override async Task FlushAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ _stream?.Dispose();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/TServerFramedTransport.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/TServerFramedTransport.cs
new file mode 100644
index 000000000..0b86e9ebb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/TServerFramedTransport.cs
@@ -0,0 +1,150 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Transports.Client;
+
+namespace Thrift.Transports.Server
+{
+ // ReSharper disable once InconsistentNaming
+ public class TServerFramedTransport : TServerTransport
+ {
+ private readonly int _clientTimeout;
+ private readonly int _port;
+ private TcpListener _server;
+
+ public TServerFramedTransport(TcpListener listener)
+ : this(listener, 0)
+ {
+ }
+
+ public TServerFramedTransport(TcpListener listener, int clientTimeout)
+ {
+ _server = listener;
+ _clientTimeout = clientTimeout;
+ }
+
+ public TServerFramedTransport(int port)
+ : this(port, 0)
+ {
+ }
+
+ public TServerFramedTransport(int port, int clientTimeout)
+ {
+ _port = port;
+ _clientTimeout = clientTimeout;
+ try
+ {
+ // Make server socket
+ _server = new TcpListener(IPAddress.Any, _port);
+ _server.Server.NoDelay = true;
+ }
+ catch (Exception)
+ {
+ _server = null;
+ throw new TTransportException("Could not create ServerSocket on port " + port + ".");
+ }
+ }
+
+ public override void Listen()
+ {
+ // Make sure not to block on accept
+ if (_server != null)
+ {
+ try
+ {
+ _server.Start();
+ }
+ catch (SocketException sx)
+ {
+ throw new TTransportException("Could not accept on listening socket: " + sx.Message);
+ }
+ }
+ }
+
+ public override bool IsClientPending()
+ {
+ return _server.Pending();
+ }
+
+ protected override async Task<TClientTransport> AcceptImplementationAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TClientTransport>(cancellationToken);
+ }
+
+ if (_server == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No underlying server socket.");
+ }
+
+ try
+ {
+ TFramedClientTransport tSocketTransport = null;
+ var tcpClient = await _server.AcceptTcpClientAsync();
+
+ try
+ {
+ tSocketTransport = new TFramedClientTransport(new TSocketClientTransport(tcpClient)
+ {
+ Timeout = _clientTimeout
+ });
+
+ return tSocketTransport;
+ }
+ catch (Exception)
+ {
+ if (tSocketTransport != null)
+ {
+ tSocketTransport.Dispose();
+ }
+ else // Otherwise, clean it up ourselves.
+ {
+ ((IDisposable) tcpClient).Dispose();
+ }
+
+ throw;
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException(ex.ToString());
+ }
+ }
+
+ public override void Close()
+ {
+ if (_server != null)
+ {
+ try
+ {
+ _server.Stop();
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException("WARNING: Could not close server socket: " + ex);
+ }
+ _server = null;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/TServerSocketTransport.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/TServerSocketTransport.cs
new file mode 100644
index 000000000..3a9d8a17d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/TServerSocketTransport.cs
@@ -0,0 +1,174 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Transports.Client;
+
+namespace Thrift.Transports.Server
+{
+ // ReSharper disable once InconsistentNaming
+ public class TServerSocketTransport : TServerTransport
+ {
+ private readonly int _clientTimeout;
+ private readonly int _port;
+ private readonly bool _useBufferedSockets;
+ private readonly bool _useFramedTransport;
+ private TcpListener _server;
+
+ public TServerSocketTransport(TcpListener listener)
+ : this(listener, 0)
+ {
+ }
+
+ public TServerSocketTransport(TcpListener listener, int clientTimeout)
+ {
+ _server = listener;
+ _clientTimeout = clientTimeout;
+ }
+
+ public TServerSocketTransport(int port)
+ : this(port, 0)
+ {
+ }
+
+ public TServerSocketTransport(int port, int clientTimeout)
+ : this(port, clientTimeout, false)
+ {
+ }
+
+ public TServerSocketTransport(int port, int clientTimeout, bool useBufferedSockets):
+ this(port, clientTimeout, useBufferedSockets, false)
+ {
+ }
+
+ public TServerSocketTransport(int port, int clientTimeout, bool useBufferedSockets, bool useFramedTransport)
+ {
+ _port = port;
+ _clientTimeout = clientTimeout;
+ _useBufferedSockets = useBufferedSockets;
+ _useFramedTransport = useFramedTransport;
+ try
+ {
+ // Make server socket
+ _server = new TcpListener(IPAddress.Any, _port);
+ _server.Server.NoDelay = true;
+ }
+ catch (Exception)
+ {
+ _server = null;
+ throw new TTransportException("Could not create ServerSocket on port " + port + ".");
+ }
+ }
+
+ public override void Listen()
+ {
+ // Make sure not to block on accept
+ if (_server != null)
+ {
+ try
+ {
+ _server.Start();
+ }
+ catch (SocketException sx)
+ {
+ throw new TTransportException("Could not accept on listening socket: " + sx.Message);
+ }
+ }
+ }
+
+ public override bool IsClientPending()
+ {
+ return _server.Pending();
+ }
+
+ protected override async Task<TClientTransport> AcceptImplementationAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TClientTransport>(cancellationToken);
+ }
+
+ if (_server == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No underlying server socket.");
+ }
+
+ try
+ {
+ TClientTransport tSocketTransport = null;
+ var tcpClient = await _server.AcceptTcpClientAsync();
+
+ try
+ {
+ tSocketTransport = new TSocketClientTransport(tcpClient)
+ {
+ Timeout = _clientTimeout
+ };
+
+ if (_useBufferedSockets)
+ {
+ tSocketTransport = new TBufferedClientTransport(tSocketTransport);
+ }
+
+ if (_useFramedTransport)
+ {
+ tSocketTransport = new TFramedClientTransport(tSocketTransport);
+ }
+
+ return tSocketTransport;
+ }
+ catch (Exception)
+ {
+ if (tSocketTransport != null)
+ {
+ tSocketTransport.Dispose();
+ }
+ else // Otherwise, clean it up ourselves.
+ {
+ ((IDisposable) tcpClient).Dispose();
+ }
+
+ throw;
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException(ex.ToString());
+ }
+ }
+
+ public override void Close()
+ {
+ if (_server != null)
+ {
+ try
+ {
+ _server.Stop();
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException("WARNING: Could not close server socket: " + ex);
+ }
+ _server = null;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/TTlsServerSocketTransport.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/TTlsServerSocketTransport.cs
new file mode 100644
index 000000000..759feeddd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/Server/TTlsServerSocketTransport.cs
@@ -0,0 +1,177 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Net;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Transports.Client;
+
+namespace Thrift.Transports.Server
+{
+ // ReSharper disable once InconsistentNaming
+ public class TTlsServerSocketTransport : TServerTransport
+ {
+ private readonly RemoteCertificateValidationCallback _clientCertValidator;
+ private readonly int _clientTimeout = 0;
+ private readonly LocalCertificateSelectionCallback _localCertificateSelectionCallback;
+ private readonly int _port;
+ private readonly X509Certificate2 _serverCertificate;
+ private readonly SslProtocols _sslProtocols;
+ private readonly bool _useBufferedSockets;
+ private readonly bool _useFramedTransport;
+ private TcpListener _server;
+
+ public TTlsServerSocketTransport(int port, X509Certificate2 certificate)
+ : this(port, false, certificate)
+ {
+ }
+
+ public TTlsServerSocketTransport(
+ int port,
+ bool useBufferedSockets,
+ X509Certificate2 certificate,
+ RemoteCertificateValidationCallback clientCertValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls12)
+ : this(port, useBufferedSockets, false, certificate,
+ clientCertValidator, localCertificateSelectionCallback, sslProtocols)
+ {
+ }
+
+ public TTlsServerSocketTransport(
+ int port,
+ bool useBufferedSockets,
+ bool useFramedTransport,
+ X509Certificate2 certificate,
+ RemoteCertificateValidationCallback clientCertValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls12)
+ {
+ if (!certificate.HasPrivateKey)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown,
+ "Your server-certificate needs to have a private key");
+ }
+
+ _port = port;
+ _serverCertificate = certificate;
+ _useBufferedSockets = useBufferedSockets;
+ _useFramedTransport = useFramedTransport;
+ _clientCertValidator = clientCertValidator;
+ _localCertificateSelectionCallback = localCertificateSelectionCallback;
+ _sslProtocols = sslProtocols;
+
+ try
+ {
+ // Create server socket
+ _server = new TcpListener(IPAddress.Any, _port);
+ _server.Server.NoDelay = true;
+ }
+ catch (Exception)
+ {
+ _server = null;
+ throw new TTransportException($"Could not create ServerSocket on port {port}.");
+ }
+ }
+
+ public override void Listen()
+ {
+ // Make sure accept is not blocking
+ if (_server != null)
+ {
+ try
+ {
+ _server.Start();
+ }
+ catch (SocketException sx)
+ {
+ throw new TTransportException($"Could not accept on listening socket: {sx.Message}");
+ }
+ }
+ }
+
+ public override bool IsClientPending()
+ {
+ return _server.Pending();
+ }
+
+ protected override async Task<TClientTransport> AcceptImplementationAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TClientTransport>(cancellationToken);
+ }
+
+ if (_server == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No underlying server socket.");
+ }
+
+ try
+ {
+ var client = await _server.AcceptTcpClientAsync();
+ client.SendTimeout = client.ReceiveTimeout = _clientTimeout;
+
+ //wrap the client in an SSL Socket passing in the SSL cert
+ var tTlsSocket = new TTlsSocketClientTransport(client, _serverCertificate, true, _clientCertValidator,
+ _localCertificateSelectionCallback, _sslProtocols);
+
+ await tTlsSocket.SetupTlsAsync();
+
+ TClientTransport trans = tTlsSocket;
+
+ if (_useBufferedSockets)
+ {
+ trans = new TBufferedClientTransport(trans);
+ }
+
+ if (_useFramedTransport)
+ {
+ trans = new TFramedClientTransport(trans);
+ }
+
+ return trans;
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException(ex.ToString());
+ }
+ }
+
+ public override void Close()
+ {
+ if (_server != null)
+ {
+ try
+ {
+ _server.Stop();
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException($"WARNING: Could not close server socket: {ex}");
+ }
+
+ _server = null;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/TClientTransport.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/TClientTransport.cs
new file mode 100644
index 000000000..0dd96cb36
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/TClientTransport.cs
@@ -0,0 +1,179 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transports
+{
+ //TODO: think about client info
+ // ReSharper disable once InconsistentNaming
+ public abstract class TClientTransport : IDisposable
+ {
+ //TODO: think how to avoid peek byte
+ private readonly byte[] _peekBuffer = new byte[1];
+ private bool _hasPeekByte;
+ public abstract bool IsOpen { get; }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ public async Task<bool> PeekAsync(CancellationToken cancellationToken)
+ {
+ //If we already have a byte read but not consumed, do nothing.
+ if (_hasPeekByte)
+ {
+ return true;
+ }
+
+ //If transport closed we can't peek.
+ if (!IsOpen)
+ {
+ return false;
+ }
+
+ //Try to read one byte. If succeeds we will need to store it for the next read.
+ try
+ {
+ var bytes = await ReadAsync(_peekBuffer, 0, 1, cancellationToken);
+ if (bytes == 0)
+ {
+ return false;
+ }
+ }
+ catch (IOException)
+ {
+ return false;
+ }
+
+ _hasPeekByte = true;
+ return true;
+ }
+
+ public virtual async Task OpenAsync()
+ {
+ await OpenAsync(CancellationToken.None);
+ }
+
+ public abstract Task OpenAsync(CancellationToken cancellationToken);
+
+ public abstract void Close();
+
+ protected static void ValidateBufferArgs(byte[] buffer, int offset, int length)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer));
+ }
+
+ if (offset < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(offset), "Buffer offset is smaller than zero.");
+ }
+
+ if (length < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(length), "Buffer length is smaller than zero.");
+ }
+
+ if (offset + length > buffer.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(buffer), "Not enough data.");
+ }
+ }
+
+ public virtual async Task<int> ReadAsync(byte[] buffer, int offset, int length)
+ {
+ return await ReadAsync(buffer, offset, length, CancellationToken.None);
+ }
+
+ public abstract Task<int> ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken);
+
+ public virtual async Task<int> ReadAllAsync(byte[] buffer, int offset, int length)
+ {
+ return await ReadAllAsync(buffer, offset, length, CancellationToken.None);
+ }
+
+ public virtual async Task<int> ReadAllAsync(byte[] buffer, int offset, int length,
+ CancellationToken cancellationToken)
+ {
+ ValidateBufferArgs(buffer, offset, length);
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<int>(cancellationToken);
+ }
+
+ var retrieved = 0;
+
+ //If we previously peeked a byte, we need to use that first.
+ if (_hasPeekByte)
+ {
+ buffer[offset + retrieved++] = _peekBuffer[0];
+ _hasPeekByte = false;
+ }
+
+ while (retrieved < length)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<int>(cancellationToken);
+ }
+
+ var returnedCount = await ReadAsync(buffer, offset + retrieved, length - retrieved, cancellationToken);
+ if (returnedCount <= 0)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.EndOfFile,
+ "Cannot read, Remote side has closed");
+ }
+ retrieved += returnedCount;
+ }
+ return retrieved;
+ }
+
+ public virtual async Task WriteAsync(byte[] buffer)
+ {
+ await WriteAsync(buffer, CancellationToken.None);
+ }
+
+ public virtual async Task WriteAsync(byte[] buffer, CancellationToken cancellationToken)
+ {
+ await WriteAsync(buffer, 0, buffer.Length, CancellationToken.None);
+ }
+
+ public virtual async Task WriteAsync(byte[] buffer, int offset, int length)
+ {
+ await WriteAsync(buffer, offset, length, CancellationToken.None);
+ }
+
+ public abstract Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken);
+
+ public virtual async Task FlushAsync()
+ {
+ await FlushAsync(CancellationToken.None);
+ }
+
+ public abstract Task FlushAsync(CancellationToken cancellationToken);
+
+ protected abstract void Dispose(bool disposing);
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/TServerTransport.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/TServerTransport.cs
new file mode 100644
index 000000000..0d45a55f9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/TServerTransport.cs
@@ -0,0 +1,54 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transports
+{
+ // ReSharper disable once InconsistentNaming
+ public abstract class TServerTransport
+ {
+ public abstract void Listen();
+ public abstract void Close();
+ public abstract bool IsClientPending();
+
+ protected virtual async Task<TClientTransport> AcceptImplementationAsync()
+ {
+ return await AcceptImplementationAsync(CancellationToken.None);
+ }
+
+ protected abstract Task<TClientTransport> AcceptImplementationAsync(CancellationToken cancellationToken);
+
+ public async Task<TClientTransport> AcceptAsync()
+ {
+ return await AcceptAsync(CancellationToken.None);
+ }
+
+ public async Task<TClientTransport> AcceptAsync(CancellationToken cancellationToken)
+ {
+ var transport = await AcceptImplementationAsync(cancellationToken);
+
+ if (transport == null)
+ {
+ throw new TTransportException($"{nameof(AcceptImplementationAsync)} should not return null");
+ }
+
+ return transport;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/TTransportException.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/TTransportException.cs
new file mode 100644
index 000000000..b7c42e33a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/TTransportException.cs
@@ -0,0 +1,58 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Transports
+{
+ // ReSharper disable once InconsistentNaming
+ public class TTransportException : TException
+ {
+ public enum ExceptionType
+ {
+ Unknown,
+ NotOpen,
+ AlreadyOpen,
+ TimedOut,
+ EndOfFile,
+ Interrupted
+ }
+
+ protected ExceptionType ExType;
+
+ public TTransportException()
+ {
+ }
+
+ public TTransportException(ExceptionType exType)
+ : this()
+ {
+ ExType = exType;
+ }
+
+ public TTransportException(ExceptionType exType, string message)
+ : base(message)
+ {
+ ExType = exType;
+ }
+
+ public TTransportException(string message)
+ : base(message)
+ {
+ }
+
+ public ExceptionType Type => ExType;
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/TTransportFactory.cs b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/TTransportFactory.cs
new file mode 100644
index 000000000..26c3cc471
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/Transports/TTransportFactory.cs
@@ -0,0 +1,35 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Transports
+{
+ /// <summary>
+ /// From Mark Slee & Aditya Agarwal of Facebook:
+ /// Factory class used to create wrapped instance of Transports.
+ /// This is used primarily in servers, which get Transports from
+ /// a ServerTransport and then may want to mutate them (i.e. create
+ /// a BufferedTransport from the underlying base transport)
+ /// </summary>
+ // ReSharper disable once InconsistentNaming
+ public class TTransportFactory
+ {
+ public virtual TClientTransport GetTransport(TClientTransport trans)
+ {
+ return trans;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netcore/Thrift/thrift.snk b/src/jaegertracing/thrift/lib/netcore/Thrift/thrift.snk
new file mode 100644
index 000000000..97bc5812b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/Thrift/thrift.snk
Binary files differ
diff --git a/src/jaegertracing/thrift/lib/netcore/build.cmd b/src/jaegertracing/thrift/lib/netcore/build.cmd
new file mode 100644
index 000000000..863c4b45e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/build.cmd
@@ -0,0 +1,27 @@
+@echo off
+rem /*
+rem * Licensed to the Apache Software Foundation (ASF) under one
+rem * or more contributor license agreements. See the NOTICE file
+rem * distributed with this work for additional information
+rem * regarding copyright ownership. The ASF licenses this file
+rem * to you under the Apache License, Version 2.0 (the
+rem * "License"); you may not use this file except in compliance
+rem * with the License. You may obtain a copy of the License at
+rem *
+rem * http://www.apache.org/licenses/LICENSE-2.0
+rem *
+rem * Unless required by applicable law or agreed to in writing,
+rem * software distributed under the License is distributed on an
+rem * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem * KIND, either express or implied. See the License for the
+rem * specific language governing permissions and limitations
+rem * under the License.
+rem */
+
+setlocal
+
+thrift -version
+dotnet --info
+dotnet build
+
+:eof
diff --git a/src/jaegertracing/thrift/lib/netcore/build.sh b/src/jaegertracing/thrift/lib/netcore/build.sh
new file mode 100644
index 000000000..ae18bce9b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/build.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#exit if any command fails
+#set -e
+
+thrift --version
+dotnet --info
+dotnet build
+
+#revision=${TRAVIS_JOB_ID:=1}
+#revision=$(printf "%04d" $revision)
+
+#dotnet pack ./src/PROJECT_NAME -c Release -o ./artifacts --version-suffix=$revision
diff --git a/src/jaegertracing/thrift/lib/netcore/runtests.cmd b/src/jaegertracing/thrift/lib/netcore/runtests.cmd
new file mode 100644
index 000000000..5114bc594
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/runtests.cmd
@@ -0,0 +1,28 @@
+@echo off
+rem /*
+rem * Licensed to the Apache Software Foundation (ASF) under one
+rem * or more contributor license agreements. See the NOTICE file
+rem * distributed with this work for additional information
+rem * regarding copyright ownership. The ASF licenses this file
+rem * to you under the Apache License, Version 2.0 (the
+rem * "License"); you may not use this file except in compliance
+rem * with the License. You may obtain a copy of the License at
+rem *
+rem * http://www.apache.org/licenses/LICENSE-2.0
+rem *
+rem * Unless required by applicable law or agreed to in writing,
+rem * software distributed under the License is distributed on an
+rem * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem * KIND, either express or implied. See the License for the
+rem * specific language governing permissions and limitations
+rem * under the License.
+rem */
+setlocal
+
+thrift -version
+dotnet --info
+
+dotnet test Tests\Thrift.IntegrationTests\Thrift.IntegrationTests.csproj
+dotnet test Tests\Thrift.Tests\Thrift.Tests.csproj
+
+:eof
diff --git a/src/jaegertracing/thrift/lib/netcore/runtests.sh b/src/jaegertracing/thrift/lib/netcore/runtests.sh
new file mode 100644
index 000000000..a26cc36ac
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netcore/runtests.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+thrift -version
+dotnet --info
+
+dotnet test Tests\Thrift.IntegrationTests\Thrift.IntegrationTests.csproj
+dotnet test Tests\Thrift.Tests\Thrift.Tests.csproj \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Makefile.am b/src/jaegertracing/thrift/lib/netstd/Makefile.am
new file mode 100644
index 000000000..f6faacae5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Makefile.am
@@ -0,0 +1,58 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+SUBDIRS = .
+
+all-local:
+ $(DOTNETCORE) build
+
+check-local:
+ $(DOTNETCORE) test Tests/Thrift.Tests/Thrift.Tests.csproj
+ ${DOTNETCORE} test Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj
+
+clean-local:
+ $(RM) -r Thrift/bin
+ $(RM) -r Thrift/obj
+
+EXTRA_DIST = \
+ README.md \
+ Tests/Thrift.IntegrationTests/Protocols \
+ Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj \
+ Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj \
+ Tests/Thrift.PublicInterfaces.Compile.Tests/Properties/AssemblyInfo.cs \
+ Tests/Thrift.PublicInterfaces.Compile.Tests/CassandraTest.thrift \
+ Tests/Thrift.Tests/Thrift.Tests.csproj \
+ Tests/Thrift.Tests/Protocols \
+ Tests/Thrift.Tests/Collections \
+ Thrift/TApplicationException.cs \
+ Thrift/TBaseClient.cs \
+ Thrift/TException.cs \
+ Thrift/Thrift.csproj \
+ Thrift/Collections \
+ Thrift/Processor \
+ Thrift/Properties \
+ Thrift/Protocol \
+ Thrift/Server \
+ Thrift/Transport \
+ Thrift.sln \
+ build.cmd \
+ build.sh \
+ runtests.cmd \
+ runtests.sh
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/README.md b/src/jaegertracing/thrift/lib/netstd/README.md
new file mode 100644
index 000000000..88ba73aeb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/README.md
@@ -0,0 +1,54 @@
+# Apache Thrift netstd
+
+Thrift client library for Microsoft .NET Standard
+
+# Build the library
+
+## How to build on Windows
+- Get Thrift IDL compiler executable, add to some folder and add path to this folder into PATH variable
+- Open the Thrift.sln project with Visual Studio and build.
+or
+- Build with scripts
+
+## How to build on Unix/Linux
+- Ensure you have .NET SDK >= 2.0 installed, or use the [Ubuntu docker image](../../build/docker/README.md)
+- Follow common automake build practice: `./ bootstrap && ./ configure && make`
+
+## Known issues
+- In trace logging mode you can see some not important internal exceptions
+
+# Migration to netstd
+
+## ... from netcore
+
+If you are migrating your code from netcore library, you will have to:
+
+- Switch to `thrift -gen netstd`
+- the following compiler flags are no longer needed or supported: `hashcode` is now standard, while `nullable` is no longer supported.
+- the `Thrift.Transport` and `Thrift.Protocol` namespaces now use the singular form
+- add `using Thrift.Processor;` in the server code where appropriate
+- rename all `T*ClientTransport` to `T*Transport`
+- rename all `TBaseServer` occurrences in your code to `TServer`
+- the `SingletonTProcessorFactory` is now called `TSingletonProcessorFactory`
+- and the `AsyncBaseServer` is now the `TSimpleAsyncServer`
+
+You may wonder why we changed so many names. The naming scheme has been revised for two reasons: First, we want to get back the established, well-known naming consistency across the Thrift libraries which the netcore library did not fully respect. Second, by achieving that first objective, we get the additional benefit of making migration at least a bit easier for C# projects.
+
+## ... from csharp
+
+Because of the different environment requirements, migration from C# takes slightly more efforts. While the code changes related to Thrift itself are moderate, you may need to upgrade certain dependencies, components or even modules to more recent versions.
+
+1. Client and server applications must use at least framework 4.6.1, any version below will not work.
+1. Switch to `thrift -gen netstd`. The following compiler flags are no longer needed or supported: `hashcode` and `async` are now standard, while `nullable` is no longer supported.
+1. [Familiarize yourself with the `async/await` model](https://msdn.microsoft.com/en-us/magazine/jj991977.aspx), if you have not already done so. As netstd does not support `ISync` anymore, async is mandatory. The synchronous model is simply no longer available (that's also the reason why we don't need the `async` flag anymore).
+1. Consider proper use of `cancellationToken` parameters. They are optional but may be quite helpful.
+1. As you probably already guessed, there are a few names that have been changed:
+- add `using Thrift.Processor;` in the server code where appropriate
+- the `TServerSocket` is now called `TServerSocketTransport`
+- change `IProtocolFactory` into `ITProtocolFactory`
+- if you are looking for `TSimpleServer`, try `TSimpleAsyncServer` instead
+- similarly, the `TThreadPoolServer` is now a `TThreadPoolAsyncServer`
+- the server's `Serve()` method does now `ServeAsync()`
+- In case you are using Thrift server event handlers: the `SetEventHandler` method now starts with an uppercase letter
+- and you will also have to revise the method names of all `TServerEventHandler` descendants you have in your code
+
diff --git a/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.IntegrationTests/Protocols/ProtocolsOperationsTests.cs b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.IntegrationTests/Protocols/ProtocolsOperationsTests.cs
new file mode 100644
index 000000000..b1f841892
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.IntegrationTests/Protocols/ProtocolsOperationsTests.cs
@@ -0,0 +1,502 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using KellermanSoftware.CompareNetObjects;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Thrift.Protocol;
+using Thrift.Protocol.Entities;
+using Thrift.Transport.Client;
+
+namespace Thrift.IntegrationTests.Protocols
+{
+ [TestClass]
+ public class ProtocolsOperationsTests
+ {
+ private readonly CompareLogic _compareLogic = new CompareLogic();
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol), TMessageType.Call)]
+ [DataRow(typeof(TBinaryProtocol), TMessageType.Exception)]
+ [DataRow(typeof(TBinaryProtocol), TMessageType.Oneway)]
+ [DataRow(typeof(TBinaryProtocol), TMessageType.Reply)]
+ [DataRow(typeof(TCompactProtocol), TMessageType.Call)]
+ [DataRow(typeof(TCompactProtocol), TMessageType.Exception)]
+ [DataRow(typeof(TCompactProtocol), TMessageType.Oneway)]
+ [DataRow(typeof(TCompactProtocol), TMessageType.Reply)]
+ [DataRow(typeof(TJsonProtocol), TMessageType.Call)]
+ [DataRow(typeof(TJsonProtocol), TMessageType.Exception)]
+ [DataRow(typeof(TJsonProtocol), TMessageType.Oneway)]
+ [DataRow(typeof(TJsonProtocol), TMessageType.Reply)]
+ public async Task WriteReadMessage_Test(Type protocolType, TMessageType messageType)
+ {
+ var expected = new TMessage(nameof(TMessage), messageType, 1);
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteMessageBeginAsync(expected);
+ await protocol.WriteMessageEndAsync();
+
+ stream.Seek(0, SeekOrigin.Begin);
+
+ var actualMessage = await protocol.ReadMessageBeginAsync();
+ await protocol.ReadMessageEndAsync();
+
+ var result = _compareLogic.Compare(expected, actualMessage);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ [ExpectedException(typeof(Exception))]
+ public async Task WriteReadStruct_Test(Type protocolType)
+ {
+ var expected = new TStruct(nameof(TStruct));
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteStructBeginAsync(expected);
+ await protocol.WriteStructEndAsync();
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadStructBeginAsync();
+ await protocol.ReadStructEndAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ [ExpectedException(typeof(Exception))]
+ public async Task WriteReadField_Test(Type protocolType)
+ {
+ var expected = new TField(nameof(TField), TType.String, 1);
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteFieldBeginAsync(expected);
+ await protocol.WriteFieldEndAsync();
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadFieldBeginAsync();
+ await protocol.ReadFieldEndAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadMap_Test(Type protocolType)
+ {
+ var expected = new TMap(TType.String, TType.String, 1);
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteMapBeginAsync(expected);
+ await protocol.WriteMapEndAsync();
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadMapBeginAsync();
+ await protocol.ReadMapEndAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadList_Test(Type protocolType)
+ {
+ var expected = new TList(TType.String, 1);
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteListBeginAsync(expected);
+ await protocol.WriteListEndAsync();
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadListBeginAsync();
+ await protocol.ReadListEndAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadSet_Test(Type protocolType)
+ {
+ var expected = new TSet(TType.String, 1);
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteSetBeginAsync(expected);
+ await protocol.WriteSetEndAsync();
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadSetBeginAsync();
+ await protocol.ReadSetEndAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadBool_Test(Type protocolType)
+ {
+ var expected = true;
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteBoolAsync(expected);
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadBoolAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadByte_Test(Type protocolType)
+ {
+ var expected = sbyte.MaxValue;
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteByteAsync(expected);
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadByteAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadI16_Test(Type protocolType)
+ {
+ var expected = short.MaxValue;
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteI16Async(expected);
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadI16Async();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadI32_Test(Type protocolType)
+ {
+ var expected = int.MaxValue;
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteI32Async(expected);
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadI32Async();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadI64_Test(Type protocolType)
+ {
+ var expected = long.MaxValue;
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteI64Async(expected);
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadI64Async();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadDouble_Test(Type protocolType)
+ {
+ var expected = double.MaxValue;
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteDoubleAsync(expected);
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadDoubleAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadString_Test(Type protocolType)
+ {
+ var expected = nameof(String);
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteStringAsync(expected);
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadStringAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ [DataTestMethod]
+ [DataRow(typeof(TBinaryProtocol))]
+ [DataRow(typeof(TCompactProtocol))]
+ [DataRow(typeof(TJsonProtocol))]
+ public async Task WriteReadBinary_Test(Type protocolType)
+ {
+ var expected = Encoding.UTF8.GetBytes(nameof(String));
+
+ try
+ {
+ var tuple = GetProtocolInstance(protocolType);
+ using (var stream = tuple.Item1)
+ {
+ var protocol = tuple.Item2;
+
+ await protocol.WriteBinaryAsync(expected);
+
+ stream?.Seek(0, SeekOrigin.Begin);
+
+ var actual = await protocol.ReadBinaryAsync();
+
+ var result = _compareLogic.Compare(expected, actual);
+ Assert.IsTrue(result.AreEqual, result.DifferencesString);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"Exception during testing of protocol: {protocolType.FullName}", e);
+ }
+ }
+
+ private static Tuple<Stream, TProtocol> GetProtocolInstance(Type protocolType)
+ {
+ var memoryStream = new MemoryStream();
+ var streamClientTransport = new TStreamTransport(memoryStream, memoryStream);
+ var protocol = (TProtocol) Activator.CreateInstance(protocolType, streamClientTransport);
+ return new Tuple<Stream, TProtocol>(memoryStream, protocol);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj
new file mode 100644
index 000000000..381d8aa47
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj
@@ -0,0 +1,48 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <!--
+ Licensed to the Apache Software Foundation(ASF) under one
+ or more contributor license agreements.See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ -->
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.0</TargetFramework>
+ <AssemblyName>Thrift.IntegrationTests</AssemblyName>
+ <PackageId>Thrift.IntegrationTests</PackageId>
+ <OutputType>Exe</OutputType>
+ <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
+ <GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
+ <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
+ <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
+ <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
+ <GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="CompareNETObjects" Version="4.58.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
+ <PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
+ <PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
+ <PackageReference Include="System.ServiceModel.Primitives" Version="4.5.3" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\Thrift\Thrift.csproj" />
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
+ </ItemGroup>
+
+</Project> \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/CassandraTest.thrift b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/CassandraTest.thrift
new file mode 100644
index 000000000..26cb380c5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/CassandraTest.thrift
@@ -0,0 +1,705 @@
+#!/usr/local/bin/thrift --java --php --py
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# *** PLEASE REMEMBER TO EDIT THE VERSION CONSTANT WHEN MAKING CHANGES ***
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+#
+# Interface definition for Cassandra Service
+#
+
+namespace netstd Apache.Cassandra.Test
+
+# Thrift.rb has a bug where top-level modules that include modules
+# with the same name are not properly referenced, so we can't do
+# Cassandra::Cassandra::Client.
+namespace rb CassandraThrift
+
+# The API version (NOT the product version), composed as a dot delimited
+# string with major, minor, and patch level components.
+#
+# - Major: Incremented for backward incompatible changes. An example would
+# be changes to the number or disposition of method arguments.
+# - Minor: Incremented for backward compatible changes. An example would
+# be the addition of a new (optional) method.
+# - Patch: Incremented for bug fixes. The patch level should be increased
+# for every edit that doesn't result in a change to major/minor.
+#
+# See the Semantic Versioning Specification (SemVer) http://semver.org.
+const string VERSION = "19.24.0"
+
+
+#
+# data structures
+#
+
+/** Basic unit of data within a ColumnFamily.
+ * @param name, the name by which this column is set and retrieved. Maximum 64KB long.
+ * @param value. The data associated with the name. Maximum 2GB long, but in practice you should limit it to small numbers of MB (since Thrift must read the full value into memory to operate on it).
+ * @param timestamp. The timestamp is used for conflict detection/resolution when two columns with same name need to be compared.
+ * @param ttl. An optional, positive delay (in seconds) after which the column will be automatically deleted.
+ */
+struct Column {
+ 1: required binary name,
+ 2: optional binary value,
+ 3: optional i64 timestamp,
+ 4: optional i32 ttl,
+}
+
+/** A named list of columns.
+ * @param name. see Column.name.
+ * @param columns. A collection of standard Columns. The columns within a super column are defined in an adhoc manner.
+ * Columns within a super column do not have to have matching structures (similarly named child columns).
+ */
+struct SuperColumn {
+ 1: required binary name,
+ 2: required list<Column> columns,
+}
+
+struct CounterColumn {
+ 1: required binary name,
+ 2: required i64 value
+}
+
+struct CounterSuperColumn {
+ 1: required binary name,
+ 2: required list<CounterColumn> columns
+}
+
+/**
+ Methods for fetching rows/records from Cassandra will return either a single instance of ColumnOrSuperColumn or a list
+ of ColumnOrSuperColumns (get_slice()). If you're looking up a SuperColumn (or list of SuperColumns) then the resulting
+ instances of ColumnOrSuperColumn will have the requested SuperColumn in the attribute super_column. For queries resulting
+ in Columns, those values will be in the attribute column. This change was made between 0.3 and 0.4 to standardize on
+ single query methods that may return either a SuperColumn or Column.
+
+ If the query was on a counter column family, you will either get a counter_column (instead of a column) or a
+ counter_super_column (instead of a super_column)
+
+ @param column. The Column returned by get() or get_slice().
+ @param super_column. The SuperColumn returned by get() or get_slice().
+ @param counter_column. The Counterolumn returned by get() or get_slice().
+ @param counter_super_column. The CounterSuperColumn returned by get() or get_slice().
+ */
+struct ColumnOrSuperColumn {
+ 1: optional Column column,
+ 2: optional SuperColumn super_column,
+ 3: optional CounterColumn counter_column,
+ 4: optional CounterSuperColumn counter_super_column
+}
+
+
+#
+# Exceptions
+# (note that internal server errors will raise a TApplicationException, courtesy of Thrift)
+#
+
+/** A specific column was requested that does not exist. */
+exception NotFoundException {
+}
+
+/** Invalid request could mean keyspace or column family does not exist, required parameters are missing, or a parameter is malformed.
+ why contains an associated error message.
+*/
+exception InvalidRequestException {
+ 1: required string why
+}
+
+/** Not all the replicas required could be created and/or read. */
+exception UnavailableException {
+}
+
+/** RPC timeout was exceeded. either a node failed mid-operation, or load was too high, or the requested op was too large. */
+exception TimedOutException {
+}
+
+/** invalid authentication request (invalid keyspace, user does not exist, or credentials invalid) */
+exception AuthenticationException {
+ 1: required string why
+}
+
+/** invalid authorization request (user does not have access to keyspace) */
+exception AuthorizationException {
+ 1: required string why
+}
+
+/** schemas are not in agreement across all nodes */
+exception SchemaDisagreementException {
+}
+
+
+#
+# service api
+#
+/**
+ * The ConsistencyLevel is an enum that controls both read and write
+ * behavior based on the ReplicationFactor of the keyspace. The
+ * different consistency levels have different meanings, depending on
+ * if you're doing a write or read operation.
+ *
+ * If W + R > ReplicationFactor, where W is the number of nodes to
+ * block for on write, and R the number to block for on reads, you
+ * will have strongly consistent behavior; that is, readers will
+ * always see the most recent write. Of these, the most interesting is
+ * to do QUORUM reads and writes, which gives you consistency while
+ * still allowing availability in the face of node failures up to half
+ * of <ReplicationFactor>. Of course if latency is more important than
+ * consistency then you can use lower values for either or both.
+ *
+ * Some ConsistencyLevels (ONE, TWO, THREE) refer to a specific number
+ * of replicas rather than a logical concept that adjusts
+ * automatically with the replication factor. Of these, only ONE is
+ * commonly used; TWO and (even more rarely) THREE are only useful
+ * when you care more about guaranteeing a certain level of
+ * durability, than consistency.
+ *
+ * Write consistency levels make the following guarantees before reporting success to the client:
+ * ANY Ensure that the write has been written once somewhere, including possibly being hinted in a non-target node.
+ * ONE Ensure that the write has been written to at least 1 node's commit log and memory table
+ * TWO Ensure that the write has been written to at least 2 node's commit log and memory table
+ * THREE Ensure that the write has been written to at least 3 node's commit log and memory table
+ * QUORUM Ensure that the write has been written to <ReplicationFactor> / 2 + 1 nodes
+ * LOCAL_QUORUM Ensure that the write has been written to <ReplicationFactor> / 2 + 1 nodes, within the local datacenter (requires NetworkTopologyStrategy)
+ * EACH_QUORUM Ensure that the write has been written to <ReplicationFactor> / 2 + 1 nodes in each datacenter (requires NetworkTopologyStrategy)
+ * ALL Ensure that the write is written to <code>&lt;ReplicationFactor&gt;</code> nodes before responding to the client.
+ *
+ * Read consistency levels make the following guarantees before returning successful results to the client:
+ * ANY Not supported. You probably want ONE instead.
+ * ONE Returns the record obtained from a single replica.
+ * TWO Returns the record with the most recent timestamp once two replicas have replied.
+ * THREE Returns the record with the most recent timestamp once three replicas have replied.
+ * QUORUM Returns the record with the most recent timestamp once a majority of replicas have replied.
+ * LOCAL_QUORUM Returns the record with the most recent timestamp once a majority of replicas within the local datacenter have replied.
+ * EACH_QUORUM Returns the record with the most recent timestamp once a majority of replicas within each datacenter have replied.
+ * ALL Returns the record with the most recent timestamp once all replicas have replied (implies no replica may be down)..
+*/
+enum ConsistencyLevel {
+ ONE = 1,
+ QUORUM = 2,
+ LOCAL_QUORUM = 3,
+ EACH_QUORUM = 4,
+ ALL = 5,
+ ANY = 6,
+ TWO = 7,
+ THREE = 8,
+}
+
+/**
+ ColumnParent is used when selecting groups of columns from the same ColumnFamily. In directory structure terms, imagine
+ ColumnParent as ColumnPath + '/../'.
+
+ See also <a href="cassandra.html#Struct_ColumnPath">ColumnPath</a>
+ */
+struct ColumnParent {
+ 3: required string column_family,
+ 4: optional binary super_column,
+}
+
+/** The ColumnPath is the path to a single column in Cassandra. It might make sense to think of ColumnPath and
+ * ColumnParent in terms of a directory structure.
+ *
+ * ColumnPath is used to looking up a single column.
+ *
+ * @param column_family. The name of the CF of the column being looked up.
+ * @param super_column. The super column name.
+ * @param column. The column name.
+ */
+struct ColumnPath {
+ 3: required string column_family,
+ 4: optional binary super_column,
+ 5: optional binary column,
+}
+
+/**
+ A slice range is a structure that stores basic range, ordering and limit information for a query that will return
+ multiple columns. It could be thought of as Cassandra's version of LIMIT and ORDER BY
+
+ @param start. The column name to start the slice with. This attribute is not required, though there is no default value,
+ and can be safely set to '', i.e., an empty byte array, to start with the first column name. Otherwise, it
+ must a valid value under the rules of the Comparator defined for the given ColumnFamily.
+ @param finish. The column name to stop the slice at. This attribute is not required, though there is no default value,
+ and can be safely set to an empty byte array to not stop until 'count' results are seen. Otherwise, it
+ must also be a valid value to the ColumnFamily Comparator.
+ @param reversed. Whether the results should be ordered in reversed order. Similar to ORDER BY blah DESC in SQL.
+ @param count. How many columns to return. Similar to LIMIT in SQL. May be arbitrarily large, but Thrift will
+ materialize the whole result into memory before returning it to the client, so be aware that you may
+ be better served by iterating through slices by passing the last value of one call in as the 'start'
+ of the next instead of increasing 'count' arbitrarily large.
+ */
+struct SliceRange {
+ 1: required binary start,
+ 2: required binary finish,
+ 3: required bool reversed=0,
+ 4: required i32 count=100,
+}
+
+/**
+ A SlicePredicate is similar to a mathematic predicate (see http://en.wikipedia.org/wiki/Predicate_(mathematical_logic)),
+ which is described as "a property that the elements of a set have in common."
+
+ SlicePredicate's in Cassandra are described with either a list of column_names or a SliceRange. If column_names is
+ specified, slice_range is ignored.
+
+ @param column_name. A list of column names to retrieve. This can be used similar to Memcached's "multi-get" feature
+ to fetch N known column names. For instance, if you know you wish to fetch columns 'Joe', 'Jack',
+ and 'Jim' you can pass those column names as a list to fetch all three at once.
+ @param slice_range. A SliceRange describing how to range, order, and/or limit the slice.
+ */
+struct SlicePredicate {
+ 1: optional list<binary> column_names,
+ 2: optional SliceRange slice_range,
+}
+
+enum IndexOperator {
+ EQ,
+ GTE,
+ GT,
+ LTE,
+ LT
+}
+
+struct IndexExpression {
+ 1: required binary column_name,
+ 2: required IndexOperator op,
+ 3: required binary value,
+}
+
+struct IndexClause {
+ 1: required list<IndexExpression> expressions
+ 2: required binary start_key,
+ 3: required i32 count=100,
+}
+
+/**
+The semantics of start keys and tokens are slightly different.
+Keys are start-inclusive; tokens are start-exclusive. Token
+ranges may also wrap -- that is, the end token may be less
+than the start one. Thus, a range from keyX to keyX is a
+one-element range, but a range from tokenY to tokenY is the
+full ring.
+*/
+struct KeyRange {
+ 1: optional binary start_key,
+ 2: optional binary end_key,
+ 3: optional string start_token,
+ 4: optional string end_token,
+ 5: required i32 count=100
+}
+
+/**
+ A KeySlice is key followed by the data it maps to. A collection of KeySlice is returned by the get_range_slice operation.
+
+ @param key. a row key
+ @param columns. List of data represented by the key. Typically, the list is pared down to only the columns specified by
+ a SlicePredicate.
+ */
+struct KeySlice {
+ 1: required binary key,
+ 2: required list<ColumnOrSuperColumn> columns,
+}
+
+struct KeyCount {
+ 1: required binary key,
+ 2: required i32 count
+}
+
+/**
+ * Note that the timestamp is only optional in case of counter deletion.
+ */
+struct Deletion {
+ 1: optional i64 timestamp,
+ 2: optional binary super_column,
+ 3: optional SlicePredicate predicate,
+}
+
+/**
+ A Mutation is either an insert (represented by filling column_or_supercolumn) or a deletion (represented by filling the deletion attribute).
+ @param column_or_supercolumn. An insert to a column or supercolumn (possibly counter column or supercolumn)
+ @param deletion. A deletion of a column or supercolumn
+*/
+struct Mutation {
+ 1: optional ColumnOrSuperColumn column_or_supercolumn,
+ 2: optional Deletion deletion,
+}
+
+struct EndpointDetails {
+ 1: string host,
+ 2: string datacenter,
+ 3: optional string rack
+}
+
+/**
+ A TokenRange describes part of the Cassandra ring, it is a mapping from a range to
+ endpoints responsible for that range.
+ @param start_token The first token in the range
+ @param end_token The last token in the range
+ @param endpoints The endpoints responsible for the range (listed by their configured listen_address)
+ @param rpc_endpoints The endpoints responsible for the range (listed by their configured rpc_address)
+*/
+struct TokenRange {
+ 1: required string start_token,
+ 2: required string end_token,
+ 3: required list<string> endpoints,
+ 4: optional list<string> rpc_endpoints
+ 5: optional list<EndpointDetails> endpoint_details,
+}
+
+/**
+ Authentication requests can contain any data, dependent on the IAuthenticator used
+*/
+struct AuthenticationRequest {
+ 1: required map<string, string> credentials
+}
+
+enum IndexType {
+ KEYS,
+ CUSTOM
+}
+
+/* describes a column in a column family. */
+struct ColumnDef {
+ 1: required binary name,
+ 2: required string validation_class,
+ 3: optional IndexType index_type,
+ 4: optional string index_name,
+ 5: optional map<string,string> index_options
+}
+
+
+/* describes a column family. */
+struct CfDef {
+ 1: required string keyspace,
+ 2: required string name,
+ 3: optional string column_type="Standard",
+ 5: optional string comparator_type="BytesType",
+ 6: optional string subcomparator_type,
+ 8: optional string comment,
+ 12: optional double read_repair_chance=1.0,
+ 13: optional list<ColumnDef> column_metadata,
+ 14: optional i32 gc_grace_seconds,
+ 15: optional string default_validation_class,
+ 16: optional i32 id,
+ 17: optional i32 min_compaction_threshold,
+ 18: optional i32 max_compaction_threshold,
+ 24: optional bool replicate_on_write,
+ 25: optional double merge_shards_chance,
+ 26: optional string key_validation_class,
+ 28: optional binary key_alias,
+ 29: optional string compaction_strategy,
+ 30: optional map<string,string> compaction_strategy_options,
+ 32: optional map<string,string> compression_options,
+ 33: optional double bloom_filter_fp_chance,
+}
+
+/* describes a keyspace. */
+struct KsDef {
+ 1: required string name,
+ 2: required string strategy_class,
+ 3: optional map<string,string> strategy_options,
+
+ /** @deprecated */
+ 4: optional i32 replication_factor,
+
+ 5: required list<CfDef> cf_defs,
+ 6: optional bool durable_writes=1,
+}
+
+/** CQL query compression */
+enum Compression {
+ GZIP = 1,
+ NONE = 2
+}
+
+enum CqlResultType {
+ ROWS = 1,
+ VOID = 2,
+ INT = 3
+}
+
+/** Row returned from a CQL query */
+struct CqlRow {
+ 1: required binary key,
+ 2: required list<Column> columns
+}
+
+struct CqlMetadata {
+ 1: required map<binary,string> name_types,
+ 2: required map<binary,string> value_types,
+ 3: required string default_name_type,
+ 4: required string default_value_type
+}
+
+struct CqlResult {
+ 1: required CqlResultType type,
+ 2: optional list<CqlRow> rows,
+ 3: optional i32 num,
+ 4: optional CqlMetadata schema
+}
+
+struct CqlPreparedResult {
+ 1: required i32 itemId,
+ 2: required i32 count
+}
+
+
+service Cassandra {
+ # auth methods
+ void login(1: required AuthenticationRequest auth_request) throws (1:AuthenticationException authnx, 2:AuthorizationException authzx),
+
+ # set keyspace
+ void set_keyspace(1: required string keyspace) throws (1:InvalidRequestException ire),
+
+ # retrieval methods
+
+ /**
+ Get the Column or SuperColumn at the given column_path. If no value is present, NotFoundException is thrown. (This is
+ the only method that can throw an exception under non-failure conditions.)
+ */
+ ColumnOrSuperColumn get(1:required binary key,
+ 2:required ColumnPath column_path,
+ 3:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:NotFoundException nfe, 3:UnavailableException ue, 4:TimedOutException te),
+
+ /**
+ Get the group of columns contained by column_parent (either a ColumnFamily name or a ColumnFamily/SuperColumn name
+ pair) specified by the given SlicePredicate. If no matching values are found, an empty list is returned.
+ */
+ list<ColumnOrSuperColumn> get_slice(1:required binary key,
+ 2:required ColumnParent column_parent,
+ 3:required SlicePredicate predicate,
+ 4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /**
+ returns the number of columns matching <code>predicate</code> for a particular <code>key</code>,
+ <code>ColumnFamily</code> and optionally <code>SuperColumn</code>.
+ */
+ i32 get_count(1:required binary key,
+ 2:required ColumnParent column_parent,
+ 3:required SlicePredicate predicate,
+ 4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /**
+ Performs a get_slice for column_parent and predicate for the given keys in parallel.
+ */
+ map<binary,list<ColumnOrSuperColumn>> multiget_slice(1:required list<binary> keys,
+ 2:required ColumnParent column_parent,
+ 3:required SlicePredicate predicate,
+ 4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /**
+ Perform a get_count in parallel on the given list<binary> keys. The return value maps keys to the count found.
+ */
+ map<binary, i32> multiget_count(1:required list<binary> keys,
+ 2:required ColumnParent column_parent,
+ 3:required SlicePredicate predicate,
+ 4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /**
+ returns a subset of columns for a contiguous range of keys.
+ */
+ list<KeySlice> get_range_slices(1:required ColumnParent column_parent,
+ 2:required SlicePredicate predicate,
+ 3:required KeyRange range,
+ 4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /** Returns the subset of columns specified in SlicePredicate for the rows matching the IndexClause */
+ list<KeySlice> get_indexed_slices(1:required ColumnParent column_parent,
+ 2:required IndexClause index_clause,
+ 3:required SlicePredicate column_predicate,
+ 4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ # modification methods
+
+ /**
+ * Insert a Column at the given column_parent.column_family and optional column_parent.super_column.
+ */
+ void insert(1:required binary key,
+ 2:required ColumnParent column_parent,
+ 3:required Column column,
+ 4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /**
+ * Increment or decrement a counter.
+ */
+ void add(1:required binary key,
+ 2:required ColumnParent column_parent,
+ 3:required CounterColumn column,
+ 4:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /**
+ Remove data from the row specified by key at the granularity specified by column_path, and the given timestamp. Note
+ that all the values in column_path besides column_path.column_family are truly optional: you can remove the entire
+ row by just specifying the ColumnFamily, or you can remove a SuperColumn or a single Column by specifying those levels too.
+ */
+ void remove(1:required binary key,
+ 2:required ColumnPath column_path,
+ 3:required i64 timestamp,
+ 4:ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /**
+ * Remove a counter at the specified location.
+ * Note that counters have limited support for deletes: if you remove a counter, you must wait to issue any following update
+ * until the delete has reached all the nodes and all of them have been fully compacted.
+ */
+ void remove_counter(1:required binary key,
+ 2:required ColumnPath path,
+ 3:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+
+ /**
+ Mutate many columns or super columns for many row keys. See also: Mutation.
+
+ mutation_map maps key to column family to a list of Mutation objects to take place at that scope.
+ **/
+ void batch_mutate(1:required map<binary, map<string, list<Mutation>>> mutation_map,
+ 2:required ConsistencyLevel consistency_level=ConsistencyLevel.ONE)
+ throws (1:InvalidRequestException ire, 2:UnavailableException ue, 3:TimedOutException te),
+
+ /**
+ Truncate will mark and entire column family as deleted.
+ From the user's perspective a successful call to truncate will result complete data deletion from cfname.
+ Internally, however, disk space will not be immediatily released, as with all deletes in cassandra, this one
+ only marks the data as deleted.
+ The operation succeeds only if all hosts in the cluster at available and will throw an UnavailableException if
+ some hosts are down.
+ */
+ void truncate(1:required string cfname)
+ throws (1: InvalidRequestException ire, 2: UnavailableException ue, 3: TimedOutException te),
+
+
+
+ // Meta-APIs -- APIs to get information about the node or cluster,
+ // rather than user data. The nodeprobe program provides usage examples.
+
+ /**
+ * for each schema version present in the cluster, returns a list of nodes at that version.
+ * hosts that do not respond will be under the key DatabaseDescriptor.INITIAL_VERSION.
+ * the cluster is all on the same version if the size of the map is 1.
+ */
+ map<string, list<string>> describe_schema_versions()
+ throws (1: InvalidRequestException ire),
+
+ /** list the defined keyspaces in this cluster */
+ list<KsDef> describe_keyspaces()
+ throws (1:InvalidRequestException ire),
+
+ /** get the cluster name */
+ string describe_cluster_name(),
+
+ /** get the thrift api version */
+ string describe_version(),
+
+ /** get the token ring: a map of ranges to host addresses,
+ represented as a set of TokenRange instead of a map from range
+ to list of endpoints, because you can't use Thrift structs as
+ map keys:
+ https://issues.apache.org/jira/browse/THRIFT-162
+
+ for the same reason, we can't return a set here, even though
+ order is neither important nor predictable. */
+ list<TokenRange> describe_ring(1:required string keyspace)
+ throws (1:InvalidRequestException ire),
+
+ /** returns the partitioner used by this cluster */
+ string describe_partitioner(),
+
+ /** returns the snitch used by this cluster */
+ string describe_snitch(),
+
+ /** describe specified keyspace */
+ KsDef describe_keyspace(1:required string keyspace)
+ throws (1:NotFoundException nfe, 2:InvalidRequestException ire),
+
+ /** experimental API for hadoop/parallel query support.
+ may change violently and without warning.
+
+ returns list of token strings such that first subrange is (list[0], list[1]],
+ next is (list[1], list[2]], etc. */
+ list<string> describe_splits(1:required string cfName,
+ 2:required string start_token,
+ 3:required string end_token,
+ 4:required i32 keys_per_split)
+ throws (1:InvalidRequestException ire),
+
+ /** adds a column family. returns the new schema id. */
+ string system_add_column_family(1:required CfDef cf_def)
+ throws (1:InvalidRequestException ire, 2:SchemaDisagreementException sde),
+
+ /** drops a column family. returns the new schema id. */
+ string system_drop_column_family(1:required string column_family)
+ throws (1:InvalidRequestException ire, 2:SchemaDisagreementException sde),
+
+ /** adds a keyspace and any column families that are part of it. returns the new schema id. */
+ string system_add_keyspace(1:required KsDef ks_def)
+ throws (1:InvalidRequestException ire, 2:SchemaDisagreementException sde),
+
+ /** drops a keyspace and any column families that are part of it. returns the new schema id. */
+ string system_drop_keyspace(1:required string keyspace)
+ throws (1:InvalidRequestException ire, 2:SchemaDisagreementException sde),
+
+ /** updates properties of a keyspace. returns the new schema id. */
+ string system_update_keyspace(1:required KsDef ks_def)
+ throws (1:InvalidRequestException ire, 2:SchemaDisagreementException sde),
+
+ /** updates properties of a column family. returns the new schema id. */
+ string system_update_column_family(1:required CfDef cf_def)
+ throws (1:InvalidRequestException ire, 2:SchemaDisagreementException sde),
+
+ /**
+ * Executes a CQL (Cassandra Query Language) statement and returns a
+ * CqlResult containing the results.
+ */
+ CqlResult execute_cql_query(1:required binary query, 2:required Compression compression)
+ throws (1:InvalidRequestException ire,
+ 2:UnavailableException ue,
+ 3:TimedOutException te,
+ 4:SchemaDisagreementException sde)
+
+
+ /**
+ * Prepare a CQL (Cassandra Query Language) statement by compiling and returning
+ * - the type of CQL statement
+ * - an id token of the compiled CQL stored on the server side.
+ * - a count of the discovered bound markers in the statement
+ */
+ CqlPreparedResult prepare_cql_query(1:required binary query, 2:required Compression compression)
+ throws (1:InvalidRequestException ire)
+
+
+ /**
+ * Executes a prepared CQL (Cassandra Query Language) statement by passing an id token and a list of variables
+ * to bind and returns a CqlResult containing the results.
+ */
+ CqlResult execute_prepared_cql_query(1:required i32 itemId, 2:required list<string> values)
+ throws (1:InvalidRequestException ire,
+ 2:UnavailableException ue,
+ 3:TimedOutException te,
+ 4:SchemaDisagreementException sde)
+
+
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Properties/AssemblyInfo.cs b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..0bb460ff4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,40 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("The Apache Software Foundation")]
+[assembly: AssemblyProduct("Thrift")]
+[assembly: AssemblyCopyright("The Apache Software Foundation")]
+[assembly: AssemblyTrademark("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+
+[assembly: Guid("d0d3706b-fed5-4cf5-b984-04f448de9d7b")] \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj
new file mode 100644
index 000000000..58f61a2f1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj
@@ -0,0 +1,54 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <!--
+ Licensed to the Apache Software Foundation(ASF) under one
+ or more contributor license agreements.See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ -->
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.0</TargetFramework>
+ <AssemblyName>Thrift.PublicInterfaces.Compile.Tests</AssemblyName>
+ <PackageId>Thrift.PublicInterfaces.Compile.Tests</PackageId>
+ <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
+ <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
+ <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
+ <GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="../../Thrift/Thrift.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <PackageReference Include="System.ServiceModel.Primitives" Version="4.5.3" />
+ </ItemGroup>
+
+ <Target Name="PreBuild" BeforeTargets="_GenerateRestoreProjectSpec;Restore;Compile">
+ <Exec Condition="'$(OS)' == 'Windows_NT'" Command="where thrift" ConsoleToMSBuild="true">
+ <Output TaskParameter="ConsoleOutput" PropertyName="PathToThrift" />
+ </Exec>
+ <Exec Condition="Exists('$(PathToThrift)')" Command="$(PathToThrift) -gen netstd:wcf,union,serial -r ./CassandraTest.thrift" />
+ <Exec Condition="Exists('thrift')" Command="thrift -gen netstd:wcf,union,serial -r ./CassandraTest.thrift" />
+ <Exec Condition="Exists('$(ProjectDir)/../../../compiler/cpp/thrift')" Command="$(ProjectDir)/../../../compiler/cpp/thrift -gen netstd:wcf,union,serial -r ./CassandraTest.thrift" />
+ <Exec Condition="Exists('$(PathToThrift)')" Command="$(PathToThrift) -gen netstd:wcf,union,serial -r ./../../../../test/ThriftTest.thrift" />
+ <Exec Condition="Exists('thrift')" Command="thrift -gen netstd:wcf,union,serial -r ./../../../../test/ThriftTest.thrift" />
+ <Exec Condition="Exists('$(ProjectDir)/../../../compiler/cpp/thrift')" Command="$(ProjectDir)/../../../compiler/cpp/thrift -gen netstd:wcf,union,serial -r ./../../../../test/ThriftTest.thrift" />
+ <Exec Condition="Exists('$(PathToThrift)')" Command="$(PathToThrift) -gen netstd:wcf,union,serial -r ./../../../../contrib/fb303/if/fb303.thrift" />
+ <Exec Condition="Exists('thrift')" Command="thrift -gen netstd:wcf,union,serial -r ./../../../../contrib/fb303/if/fb303.thrift" />
+ <Exec Condition="Exists('$(ProjectDir)/../../../compiler/cpp/thrift')" Command="$(ProjectDir)/../../../compiler/cpp/thrift -gen netstd:wcf,union,serial -r ./../../../../contrib/fb303/if/fb303.thrift" />
+ </Target>
+
+</Project>
diff --git a/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Collections/TCollectionsTests.cs b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Collections/TCollectionsTests.cs
new file mode 100644
index 000000000..1be99b48f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Collections/TCollectionsTests.cs
@@ -0,0 +1,83 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Thrift.Collections;
+
+namespace Thrift.Tests.Collections
+{
+ // ReSharper disable once InconsistentNaming
+ [TestClass]
+ public class TCollectionsTests
+ {
+ //TODO: Add tests for IEnumerable with objects and primitive values inside
+
+ [TestMethod]
+ public void TCollection_Equals_Primitive_Test()
+ {
+ var collection1 = new List<int> {1,2,3};
+ var collection2 = new List<int> {1,2,3};
+
+ var result = TCollections.Equals(collection1, collection2);
+
+ Assert.IsTrue(result);
+ }
+
+ [TestMethod]
+ public void TCollection_Equals_Primitive_Different_Test()
+ {
+ var collection1 = new List<int> { 1, 2, 3 };
+ var collection2 = new List<int> { 1, 2 };
+
+ var result = TCollections.Equals(collection1, collection2);
+
+ Assert.IsFalse(result);
+ }
+
+ [TestMethod]
+ public void TCollection_Equals_Objects_Test()
+ {
+ var collection1 = new List<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
+ var collection2 = new List<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
+
+ var result = TCollections.Equals(collection1, collection2);
+
+ // references to different collections
+ Assert.IsFalse(result);
+ }
+
+ [TestMethod]
+ public void TCollection_Equals_OneAndTheSameObject_Test()
+ {
+ var collection1 = new List<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
+ var collection2 = collection1;
+
+ var result = TCollections.Equals(collection1, collection2);
+
+ // references to one and the same collection
+ Assert.IsTrue(result);
+ }
+
+ private class ExampleClass
+ {
+ public int X { get; set; }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Collections/THashSetTests.cs b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Collections/THashSetTests.cs
new file mode 100644
index 000000000..8de573eee
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Collections/THashSetTests.cs
@@ -0,0 +1,71 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Thrift.Collections;
+
+namespace Thrift.Tests.Collections
+{
+ // ReSharper disable once InconsistentNaming
+ [TestClass]
+ public class THashSetTests
+ {
+ [TestMethod]
+ public void THashSet_Equals_Primitive_Test()
+ {
+ const int value = 1;
+
+ var hashSet = new THashSet<int> {value};
+
+ Assert.IsTrue(hashSet.Contains(value));
+
+ hashSet.Remove(value);
+
+ Assert.IsTrue(hashSet.Count == 0);
+
+ hashSet.Add(value);
+
+ Assert.IsTrue(hashSet.Contains(value));
+
+ hashSet.Clear();
+
+ Assert.IsTrue(hashSet.Count == 0);
+
+ var newArr = new int[1];
+ hashSet.Add(value);
+ hashSet.CopyTo(newArr, 0);
+
+ Assert.IsTrue(newArr.Contains(value));
+
+ var en = hashSet.GetEnumerator();
+ en.MoveNext();
+
+ Assert.IsTrue((int)en.Current == value);
+
+ using (var ien = ((IEnumerable<int>)hashSet).GetEnumerator())
+ {
+ ien.MoveNext();
+
+ Assert.IsTrue(ien.Current == value);
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Protocols/TJsonProtocolHelperTests.cs b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Protocols/TJsonProtocolHelperTests.cs
new file mode 100644
index 000000000..6d391516e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Protocols/TJsonProtocolHelperTests.cs
@@ -0,0 +1,172 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Thrift.Protocol;
+using Thrift.Protocol.Entities;
+using Thrift.Protocol.Utilities;
+
+namespace Thrift.Tests.Protocols
+{
+ [TestClass]
+ public class TJSONProtocolHelperTests
+ {
+ [TestMethod]
+ public void GetTypeNameForTypeId_Test()
+ {
+ // input/output
+ var sets = new List<Tuple<TType, byte[]>>
+ {
+ new Tuple<TType, byte[]>(TType.Bool, TJSONProtocolConstants.TypeNames.NameBool),
+ new Tuple<TType, byte[]>(TType.Byte, TJSONProtocolConstants.TypeNames.NameByte),
+ new Tuple<TType, byte[]>(TType.I16, TJSONProtocolConstants.TypeNames.NameI16),
+ new Tuple<TType, byte[]>(TType.I32, TJSONProtocolConstants.TypeNames.NameI32),
+ new Tuple<TType, byte[]>(TType.I64, TJSONProtocolConstants.TypeNames.NameI64),
+ new Tuple<TType, byte[]>(TType.Double, TJSONProtocolConstants.TypeNames.NameDouble),
+ new Tuple<TType, byte[]>(TType.String, TJSONProtocolConstants.TypeNames.NameString),
+ new Tuple<TType, byte[]>(TType.Struct, TJSONProtocolConstants.TypeNames.NameStruct),
+ new Tuple<TType, byte[]>(TType.Map, TJSONProtocolConstants.TypeNames.NameMap),
+ new Tuple<TType, byte[]>(TType.Set, TJSONProtocolConstants.TypeNames.NameSet),
+ new Tuple<TType, byte[]>(TType.List, TJSONProtocolConstants.TypeNames.NameList),
+ };
+
+ foreach (var t in sets)
+ {
+ Assert.IsTrue(TJSONProtocolHelper.GetTypeNameForTypeId(t.Item1) == t.Item2, $"Wrong mapping of TypeName {t.Item2} to TType: {t.Item1}");
+ }
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(TProtocolException))]
+ public void GetTypeNameForTypeId_TStop_Test()
+ {
+ TJSONProtocolHelper.GetTypeNameForTypeId(TType.Stop);
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(TProtocolException))]
+ public void GetTypeNameForTypeId_NonExistingTType_Test()
+ {
+ TJSONProtocolHelper.GetTypeNameForTypeId((TType)100);
+ }
+
+ [TestMethod]
+ public void GetTypeIdForTypeName_Test()
+ {
+ // input/output
+ var sets = new List<Tuple<TType, byte[]>>
+ {
+ new Tuple<TType, byte[]>(TType.Bool, TJSONProtocolConstants.TypeNames.NameBool),
+ new Tuple<TType, byte[]>(TType.Byte, TJSONProtocolConstants.TypeNames.NameByte),
+ new Tuple<TType, byte[]>(TType.I16, TJSONProtocolConstants.TypeNames.NameI16),
+ new Tuple<TType, byte[]>(TType.I32, TJSONProtocolConstants.TypeNames.NameI32),
+ new Tuple<TType, byte[]>(TType.I64, TJSONProtocolConstants.TypeNames.NameI64),
+ new Tuple<TType, byte[]>(TType.Double, TJSONProtocolConstants.TypeNames.NameDouble),
+ new Tuple<TType, byte[]>(TType.String, TJSONProtocolConstants.TypeNames.NameString),
+ new Tuple<TType, byte[]>(TType.Struct, TJSONProtocolConstants.TypeNames.NameStruct),
+ new Tuple<TType, byte[]>(TType.Map, TJSONProtocolConstants.TypeNames.NameMap),
+ new Tuple<TType, byte[]>(TType.Set, TJSONProtocolConstants.TypeNames.NameSet),
+ new Tuple<TType, byte[]>(TType.List, TJSONProtocolConstants.TypeNames.NameList),
+ };
+
+ foreach (var t in sets)
+ {
+ Assert.IsTrue(TJSONProtocolHelper.GetTypeIdForTypeName(t.Item2) == t.Item1, $"Wrong mapping of TypeName {t.Item2} to TType: {t.Item1}");
+ }
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(TProtocolException))]
+ public void GetTypeIdForTypeName_TStopTypeName_Test()
+ {
+ TJSONProtocolHelper.GetTypeIdForTypeName(new []{(byte)TType.Stop, (byte)TType.Stop});
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(TProtocolException))]
+ public void GetTypeIdForTypeName_NonExistingTypeName_Test()
+ {
+ TJSONProtocolHelper.GetTypeIdForTypeName(new byte[]{100});
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(TProtocolException))]
+ public void GetTypeIdForTypeName_EmptyName_Test()
+ {
+ TJSONProtocolHelper.GetTypeIdForTypeName(new byte[] {});
+ }
+
+ [TestMethod]
+ public void IsJsonNumeric_Test()
+ {
+ // input/output
+ var correctJsonNumeric = "+-.0123456789Ee";
+ var incorrectJsonNumeric = "AaBcDd/*\\";
+
+ var sets = correctJsonNumeric.Select(ch => new Tuple<byte, bool>((byte) ch, true)).ToList();
+ sets.AddRange(incorrectJsonNumeric.Select(ch => new Tuple<byte, bool>((byte) ch, false)));
+
+ foreach (var t in sets)
+ {
+ Assert.IsTrue(TJSONProtocolHelper.IsJsonNumeric(t.Item1) == t.Item2, $"Wrong mapping of Char {t.Item1} to bool: {t.Item2}");
+ }
+ }
+
+ [TestMethod]
+ public void ToHexVal_Test()
+ {
+ // input/output
+ var chars = "0123456789abcdef";
+ var expectedHexValues = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+
+ var sets = chars.Select((ch, i) => new Tuple<char, byte>(ch, expectedHexValues[i])).ToList();
+
+ foreach (var t in sets)
+ {
+ var actualResult = TJSONProtocolHelper.ToHexVal((byte)t.Item1);
+ Assert.IsTrue(actualResult == t.Item2, $"Wrong mapping of char byte {t.Item1} to it expected hex value: {t.Item2}. Actual hex value: {actualResult}");
+ }
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(TProtocolException))]
+ public void ToHexVal_WrongInputChar_Test()
+ {
+ TJSONProtocolHelper.ToHexVal((byte)'s');
+ }
+
+ [TestMethod]
+ public void ToHexChar_Test()
+ {
+ // input/output
+ var hexValues = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+ var expectedChars = "0123456789abcdef";
+
+
+ var sets = hexValues.Select((hv, i) => new Tuple<byte, char>(hv, expectedChars[i])).ToList();
+
+ foreach (var t in sets)
+ {
+ var actualResult = TJSONProtocolHelper.ToHexChar(t.Item1);
+ Assert.IsTrue(actualResult == t.Item2, $"Wrong mapping of hex value {t.Item1} to it expected char: {t.Item2}. Actual hex value: {actualResult}");
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Protocols/TJsonProtocolTests.cs b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Protocols/TJsonProtocolTests.cs
new file mode 100644
index 000000000..970ce7ece
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Protocols/TJsonProtocolTests.cs
@@ -0,0 +1,67 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using NSubstitute;
+using Thrift.Protocol;
+using Thrift.Protocol.Entities;
+using Thrift.Transport;
+using Thrift.Transport.Client;
+
+namespace Thrift.Tests.Protocols
+{
+ // ReSharper disable once InconsistentNaming
+ [TestClass]
+ public class TJSONProtocolTests
+ {
+ [TestMethod]
+ public void TJSONProtocol_Can_Create_Instance_Test()
+ {
+ var httpClientTransport = Substitute.For<THttpTransport>(new Uri("http://localhost"), null, null);
+
+ var result = new TJSONProtocolWrapper(httpClientTransport);
+
+ Assert.IsNotNull(result);
+ Assert.IsNotNull(result.WrappedContext);
+ Assert.IsNotNull(result.WrappedReader);
+ Assert.IsNotNull(result.Transport);
+ Assert.IsTrue(result.WrappedRecursionDepth == 0);
+ Assert.IsTrue(result.WrappedRecursionLimit == TProtocol.DefaultRecursionDepth);
+
+ Assert.IsTrue(result.Transport.Equals(httpClientTransport));
+ Assert.IsTrue(result.WrappedContext.GetType().Name.Equals("JSONBaseContext", StringComparison.OrdinalIgnoreCase));
+ Assert.IsTrue(result.WrappedReader.GetType().Name.Equals("LookaheadReader", StringComparison.OrdinalIgnoreCase));
+ }
+
+ private class TJSONProtocolWrapper : TJsonProtocol
+ {
+ public TJSONProtocolWrapper(TTransport trans) : base(trans)
+ {
+ }
+
+ public object WrappedContext => Context;
+ public object WrappedReader => Reader;
+ public int WrappedRecursionDepth => RecursionDepth;
+ public int WrappedRecursionLimit => RecursionLimit;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Thrift.Tests.csproj b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Thrift.Tests.csproj
new file mode 100644
index 000000000..434424dba
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Tests/Thrift.Tests/Thrift.Tests.csproj
@@ -0,0 +1,36 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <!--
+ Licensed to the Apache Software Foundation(ASF) under one
+ or more contributor license agreements.See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ -->
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.0</TargetFramework>
+ </PropertyGroup>
+ <ItemGroup>
+ <PackageReference Include="CompareNETObjects" Version="4.58.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
+ <PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
+ <PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
+ <PackageReference Include="NSubstitute" Version="4.0.0" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\Thrift\Thrift.csproj" />
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift.sln b/src/jaegertracing/thrift/lib/netstd/Thrift.sln
new file mode 100644
index 000000000..2952eb0d8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift.sln
@@ -0,0 +1,85 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26730.12
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{ED5A45B0-07D1-4507-96B7-83FBD3D031CA}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift", "Thrift\Thrift.csproj", "{5B501D21-D428-408D-AB5C-32D6F5355294}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.IntegrationTests", "Tests\Thrift.IntegrationTests\Thrift.IntegrationTests.csproj", "{837F4084-AAD7-45F5-BC96-10E05A669DB4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.Tests", "Tests\Thrift.Tests\Thrift.Tests.csproj", "{0790D388-1A3C-4423-8CF2-C97074A8B68B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.PublicInterfaces.Compile.Tests", "Tests\Thrift.PublicInterfaces.Compile.Tests\Thrift.PublicInterfaces.Compile.Tests.csproj", "{A6AE021D-61CB-4D84-A103-0B663C62AE2C}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {5B501D21-D428-408D-AB5C-32D6F5355294}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5B501D21-D428-408D-AB5C-32D6F5355294}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5B501D21-D428-408D-AB5C-32D6F5355294}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5B501D21-D428-408D-AB5C-32D6F5355294}.Debug|x64.Build.0 = Debug|Any CPU
+ {5B501D21-D428-408D-AB5C-32D6F5355294}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5B501D21-D428-408D-AB5C-32D6F5355294}.Debug|x86.Build.0 = Debug|Any CPU
+ {5B501D21-D428-408D-AB5C-32D6F5355294}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5B501D21-D428-408D-AB5C-32D6F5355294}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5B501D21-D428-408D-AB5C-32D6F5355294}.Release|x64.ActiveCfg = Release|Any CPU
+ {5B501D21-D428-408D-AB5C-32D6F5355294}.Release|x64.Build.0 = Release|Any CPU
+ {5B501D21-D428-408D-AB5C-32D6F5355294}.Release|x86.ActiveCfg = Release|Any CPU
+ {5B501D21-D428-408D-AB5C-32D6F5355294}.Release|x86.Build.0 = Release|Any CPU
+ {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Debug|x64.Build.0 = Debug|Any CPU
+ {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Debug|x86.Build.0 = Debug|Any CPU
+ {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Release|x64.ActiveCfg = Release|Any CPU
+ {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Release|x64.Build.0 = Release|Any CPU
+ {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Release|x86.ActiveCfg = Release|Any CPU
+ {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Release|x86.Build.0 = Release|Any CPU
+ {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Debug|x64.Build.0 = Debug|Any CPU
+ {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Debug|x86.Build.0 = Debug|Any CPU
+ {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Release|x64.ActiveCfg = Release|Any CPU
+ {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Release|x64.Build.0 = Release|Any CPU
+ {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Release|x86.ActiveCfg = Release|Any CPU
+ {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Release|x86.Build.0 = Release|Any CPU
+ {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Debug|x64.Build.0 = Debug|Any CPU
+ {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Debug|x86.Build.0 = Debug|Any CPU
+ {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Release|x64.ActiveCfg = Release|Any CPU
+ {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Release|x64.Build.0 = Release|Any CPU
+ {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Release|x86.ActiveCfg = Release|Any CPU
+ {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {837F4084-AAD7-45F5-BC96-10E05A669DB4} = {ED5A45B0-07D1-4507-96B7-83FBD3D031CA}
+ {0790D388-1A3C-4423-8CF2-C97074A8B68B} = {ED5A45B0-07D1-4507-96B7-83FBD3D031CA}
+ {A6AE021D-61CB-4D84-A103-0B663C62AE2C} = {ED5A45B0-07D1-4507-96B7-83FBD3D031CA}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {FD20BC4A-0109-41D8-8C0C-893E784D7EF9}
+ EndGlobalSection
+EndGlobal
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Collections/TCollections.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Collections/TCollections.cs
new file mode 100644
index 000000000..147bfc7d3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Collections/TCollections.cs
@@ -0,0 +1,101 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Collections;
+
+namespace Thrift.Collections
+{
+ // ReSharper disable once InconsistentNaming
+ public class TCollections
+ {
+ /// <summary>
+ /// This will return true if the two collections are value-wise the same.
+ /// If the collection contains a collection, the collections will be compared using this method.
+ /// </summary>
+ public static bool Equals(IEnumerable first, IEnumerable second)
+ {
+ if (first == null && second == null)
+ {
+ return true;
+ }
+
+ if (first == null || second == null)
+ {
+ return false;
+ }
+
+ var fiter = first.GetEnumerator();
+ var siter = second.GetEnumerator();
+
+ var fnext = fiter.MoveNext();
+ var snext = siter.MoveNext();
+
+ while (fnext && snext)
+ {
+ var fenum = fiter.Current as IEnumerable;
+ var senum = siter.Current as IEnumerable;
+
+ if (fenum != null && senum != null)
+ {
+ if (!Equals(fenum, senum))
+ {
+ return false;
+ }
+ }
+ else if (fenum == null ^ senum == null)
+ {
+ return false;
+ }
+ else if (!Equals(fiter.Current, siter.Current))
+ {
+ return false;
+ }
+
+ fnext = fiter.MoveNext();
+ snext = siter.MoveNext();
+ }
+
+ return fnext == snext;
+ }
+
+ /// <summary>
+ /// This returns a hashcode based on the value of the enumerable.
+ /// </summary>
+ public static int GetHashCode(IEnumerable enumerable)
+ {
+ if (enumerable == null)
+ {
+ return 0;
+ }
+
+ var hashcode = 0;
+
+ foreach (var obj in enumerable)
+ {
+ var enum2 = obj as IEnumerable;
+ var objHash = enum2 == null ? obj.GetHashCode() : GetHashCode(enum2);
+
+ unchecked
+ {
+ hashcode = (hashcode*397) ^ (objHash);
+ }
+ }
+
+ return hashcode;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Collections/THashSet.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Collections/THashSet.cs
new file mode 100644
index 000000000..ffab57711
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Collections/THashSet.cs
@@ -0,0 +1,78 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Thrift.Collections
+{
+ // ReSharper disable once InconsistentNaming
+ public class THashSet<T> : ICollection<T>
+ {
+ private readonly HashSet<T> Items;
+
+ public THashSet()
+ {
+ Items = new HashSet<T>();
+ }
+
+ public THashSet(int capacity)
+ {
+ // TODO: uncomment capacity when NET Standard also implements it
+ Items = new HashSet<T>(/*capacity*/);
+ }
+
+ public int Count => Items.Count;
+
+ public bool IsReadOnly => false;
+
+ public void Add(T item)
+ {
+ Items.Add(item);
+ }
+
+ public void Clear()
+ {
+ Items.Clear();
+ }
+
+ public bool Contains(T item)
+ {
+ return Items.Contains(item);
+ }
+
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ Items.CopyTo(array, arrayIndex);
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return Items.GetEnumerator();
+ }
+
+ IEnumerator<T> IEnumerable<T>.GetEnumerator()
+ {
+ return ((IEnumerable<T>) Items).GetEnumerator();
+ }
+
+ public bool Remove(T item)
+ {
+ return Items.Remove(item);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Processor/ITAsyncProcessor.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Processor/ITAsyncProcessor.cs
new file mode 100644
index 000000000..f5b8d16e3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Processor/ITAsyncProcessor.cs
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocol;
+
+namespace Thrift.Processor
+{
+ public interface ITAsyncProcessor
+ {
+ Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken = default(CancellationToken));
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Processor/ITProcessorFactory.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Processor/ITProcessorFactory.cs
new file mode 100644
index 000000000..e0fe3d0a8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Processor/ITProcessorFactory.cs
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using Thrift.Server;
+using Thrift.Transport;
+
+namespace Thrift.Processor
+{
+ // ReSharper disable once InconsistentNaming
+ public interface ITProcessorFactory
+ {
+ ITAsyncProcessor GetAsyncProcessor(TTransport trans, TServer baseServer = null);
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Processor/TMultiplexedProcessor.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Processor/TMultiplexedProcessor.cs
new file mode 100644
index 000000000..e5aeaa6c7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Processor/TMultiplexedProcessor.cs
@@ -0,0 +1,143 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocol;
+using Thrift.Protocol.Entities;
+
+namespace Thrift.Processor
+{
+ // ReSharper disable once InconsistentNaming
+ public class TMultiplexedProcessor : ITAsyncProcessor
+ {
+ //TODO: Localization
+
+ private readonly Dictionary<string, ITAsyncProcessor> _serviceProcessorMap =
+ new Dictionary<string, ITAsyncProcessor>();
+
+ public async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot)
+ {
+ return await ProcessAsync(iprot, oprot, CancellationToken.None);
+ }
+
+ public async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<bool>(cancellationToken);
+ }
+
+ try
+ {
+ var message = await iprot.ReadMessageBeginAsync(cancellationToken);
+
+ if ((message.Type != TMessageType.Call) && (message.Type != TMessageType.Oneway))
+ {
+ await FailAsync(oprot, message, TApplicationException.ExceptionType.InvalidMessageType,
+ "Message exType CALL or ONEWAY expected", cancellationToken);
+ return false;
+ }
+
+ // Extract the service name
+ var index = message.Name.IndexOf(TMultiplexedProtocol.Separator, StringComparison.Ordinal);
+ if (index < 0)
+ {
+ await FailAsync(oprot, message, TApplicationException.ExceptionType.InvalidProtocol,
+ $"Service name not found in message name: {message.Name}. Did you forget to use a TMultiplexProtocol in your client?",
+ cancellationToken);
+ return false;
+ }
+
+ // Create a new TMessage, something that can be consumed by any TProtocol
+ var serviceName = message.Name.Substring(0, index);
+ ITAsyncProcessor actualProcessor;
+ if (!_serviceProcessorMap.TryGetValue(serviceName, out actualProcessor))
+ {
+ await FailAsync(oprot, message, TApplicationException.ExceptionType.InternalError,
+ $"Service name not found: {serviceName}. Did you forget to call RegisterProcessor()?",
+ cancellationToken);
+ return false;
+ }
+
+ // Create a new TMessage, removing the service name
+ var newMessage = new TMessage(
+ message.Name.Substring(serviceName.Length + TMultiplexedProtocol.Separator.Length),
+ message.Type,
+ message.SeqID);
+
+ // Dispatch processing to the stored processor
+ return
+ await
+ actualProcessor.ProcessAsync(new StoredMessageProtocol(iprot, newMessage), oprot,
+ cancellationToken);
+ }
+ catch (IOException)
+ {
+ return false; // similar to all other processors
+ }
+ }
+
+ public void RegisterProcessor(string serviceName, ITAsyncProcessor processor)
+ {
+ if (_serviceProcessorMap.ContainsKey(serviceName))
+ {
+ throw new InvalidOperationException(
+ $"Processor map already contains processor with name: '{serviceName}'");
+ }
+
+ _serviceProcessorMap.Add(serviceName, processor);
+ }
+
+ private async Task FailAsync(TProtocol oprot, TMessage message, TApplicationException.ExceptionType extype,
+ string etxt, CancellationToken cancellationToken)
+ {
+ var appex = new TApplicationException(extype, etxt);
+
+ var newMessage = new TMessage(message.Name, TMessageType.Exception, message.SeqID);
+
+ await oprot.WriteMessageBeginAsync(newMessage, cancellationToken);
+ await appex.WriteAsync(oprot, cancellationToken);
+ await oprot.WriteMessageEndAsync(cancellationToken);
+ await oprot.Transport.FlushAsync(cancellationToken);
+ }
+
+ private class StoredMessageProtocol : TProtocolDecorator
+ {
+ readonly TMessage _msgBegin;
+
+ public StoredMessageProtocol(TProtocol protocol, TMessage messageBegin)
+ : base(protocol)
+ {
+ _msgBegin = messageBegin;
+ }
+
+ public override async ValueTask<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TMessage>(cancellationToken);
+ }
+
+ return _msgBegin;
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Processor/TSingletonProcessorFactory.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Processor/TSingletonProcessorFactory.cs
new file mode 100644
index 000000000..97ecff65c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Processor/TSingletonProcessorFactory.cs
@@ -0,0 +1,38 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using Thrift.Server;
+using Thrift.Transport;
+
+namespace Thrift.Processor
+{
+ // ReSharper disable once InconsistentNaming
+ public class TSingletonProcessorFactory : ITProcessorFactory
+ {
+ private readonly ITAsyncProcessor _asyncProcessor;
+
+ public TSingletonProcessorFactory(ITAsyncProcessor asyncProcessor)
+ {
+ _asyncProcessor = asyncProcessor;
+ }
+
+ public ITAsyncProcessor GetAsyncProcessor(TTransport trans, TServer baseServer = null)
+ {
+ return _asyncProcessor;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Properties/AssemblyInfo.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..597290de5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Properties/AssemblyInfo.cs
@@ -0,0 +1,57 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+
+[assembly: AssemblyTitle("Thrift")]
+[assembly: AssemblyDescription("C# .NET Core bindings for the Apache Thrift RPC system")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("The Apache Software Foundation")]
+[assembly: AssemblyProduct("Thrift")]
+[assembly: AssemblyCopyright("The Apache Software Foundation")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+//@TODO where to put License information?
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a exType in this assembly from
+// COM, set the ComVisible attribute to true on that exType.
+
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+
+[assembly: Guid("df3f8ef0-e0a3-4c86-a65b-8ec84e016b1d")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+
+[assembly: AssemblyVersion("0.13.0.0")]
+[assembly: AssemblyFileVersion("0.13.0.0")]
+
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TField.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TField.cs
new file mode 100644
index 000000000..4e29bb5d4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TField.cs
@@ -0,0 +1,37 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocol.Entities
+{
+ // ReSharper disable once InconsistentNaming
+ public struct TField
+ {
+ public TField(string name, TType type, short id)
+ {
+ Name = name;
+ Type = type;
+ ID = id;
+ }
+
+ public string Name { get; set; }
+
+ public TType Type { get; set; }
+
+ // ReSharper disable once InconsistentNaming - do not rename - it used for generation
+ public short ID { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TList.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TList.cs
new file mode 100644
index 000000000..f59922564
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TList.cs
@@ -0,0 +1,33 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocol.Entities
+{
+ // ReSharper disable once InconsistentNaming
+ public struct TList
+ {
+ public TList(TType elementType, int count)
+ {
+ ElementType = elementType;
+ Count = count;
+ }
+
+ public TType ElementType { get; set; }
+
+ public int Count { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TMap.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TMap.cs
new file mode 100644
index 000000000..1efebe7a1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TMap.cs
@@ -0,0 +1,36 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocol.Entities
+{
+ // ReSharper disable once InconsistentNaming
+ public struct TMap
+ {
+ public TMap(TType keyType, TType valueType, int count)
+ {
+ KeyType = keyType;
+ ValueType = valueType;
+ Count = count;
+ }
+
+ public TType KeyType { get; set; }
+
+ public TType ValueType { get; set; }
+
+ public int Count { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TMessage.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TMessage.cs
new file mode 100644
index 000000000..08d741d65
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TMessage.cs
@@ -0,0 +1,37 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocol.Entities
+{
+ // ReSharper disable once InconsistentNaming
+ public struct TMessage
+ {
+ public TMessage(string name, TMessageType type, int seqid)
+ {
+ Name = name;
+ Type = type;
+ SeqID = seqid;
+ }
+
+ public string Name { get; set; }
+
+ public TMessageType Type { get; set; }
+
+ // ReSharper disable once InconsistentNaming - do not rename - it used for generation
+ public int SeqID { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TMessageType.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TMessageType.cs
new file mode 100644
index 000000000..24d663e2d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TMessageType.cs
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocol.Entities
+{
+ // ReSharper disable once InconsistentNaming
+ public enum TMessageType
+ {
+ Call = 1,
+ Reply = 2,
+ Exception = 3,
+ Oneway = 4
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TSet.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TSet.cs
new file mode 100644
index 000000000..692d5642c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TSet.cs
@@ -0,0 +1,38 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocol.Entities
+{
+ // ReSharper disable once InconsistentNaming
+ public struct TSet
+ {
+ public TSet(TType elementType, int count)
+ {
+ ElementType = elementType;
+ Count = count;
+ }
+
+ public TSet(TList list)
+ : this(list.ElementType, list.Count)
+ {
+ }
+
+ public TType ElementType { get; set; }
+
+ public int Count { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TStruct.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TStruct.cs
new file mode 100644
index 000000000..e04167e47
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TStruct.cs
@@ -0,0 +1,30 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocol.Entities
+{
+ // ReSharper disable once InconsistentNaming
+ public struct TStruct
+ {
+ public TStruct(string name)
+ {
+ Name = name;
+ }
+
+ public string Name { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TType.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TType.cs
new file mode 100644
index 000000000..4e922a7e7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Entities/TType.cs
@@ -0,0 +1,37 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocol.Entities
+{
+ // ReSharper disable once InconsistentNaming
+ public enum TType : byte
+ {
+ Stop = 0,
+ Void = 1,
+ Bool = 2,
+ Byte = 3,
+ Double = 4,
+ I16 = 6,
+ I32 = 8,
+ I64 = 10,
+ String = 11,
+ Struct = 12,
+ Map = 13,
+ Set = 14,
+ List = 15
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TBase.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TBase.cs
new file mode 100644
index 000000000..b5ef2aea9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TBase.cs
@@ -0,0 +1,33 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Protocol
+{
+ public interface TUnionBase
+ {
+ Task WriteAsync(TProtocol tProtocol, CancellationToken cancellationToken = default(CancellationToken));
+ }
+
+ // ReSharper disable once InconsistentNaming
+ public interface TBase : TUnionBase
+ {
+ Task ReadAsync(TProtocol tProtocol, CancellationToken cancellationToken = default(CancellationToken));
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TBinaryProtocol.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TBinaryProtocol.cs
new file mode 100644
index 000000000..3f30d4aa1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TBinaryProtocol.cs
@@ -0,0 +1,600 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocol.Entities;
+using Thrift.Transport;
+
+namespace Thrift.Protocol
+{
+ // ReSharper disable once InconsistentNaming
+ public class TBinaryProtocol : TProtocol
+ {
+ protected const uint VersionMask = 0xffff0000;
+ protected const uint Version1 = 0x80010000;
+
+ protected bool StrictRead;
+ protected bool StrictWrite;
+
+ // minimize memory allocations by means of an preallocated bytes buffer
+ // The value of 128 is arbitrarily chosen, the required minimum size must be sizeof(long)
+ private byte[] PreAllocatedBuffer = new byte[128];
+
+ private static readonly TStruct AnonymousStruct = new TStruct(string.Empty);
+ private static readonly TField StopField = new TField() { Type = TType.Stop };
+
+ public TBinaryProtocol(TTransport trans)
+ : this(trans, false, true)
+ {
+ }
+
+ public TBinaryProtocol(TTransport trans, bool strictRead, bool strictWrite)
+ : base(trans)
+ {
+ StrictRead = strictRead;
+ StrictWrite = strictWrite;
+ }
+
+ public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ if (StrictWrite)
+ {
+ var version = Version1 | (uint) message.Type;
+ await WriteI32Async((int) version, cancellationToken);
+ await WriteStringAsync(message.Name, cancellationToken);
+ await WriteI32Async(message.SeqID, cancellationToken);
+ }
+ else
+ {
+ await WriteStringAsync(message.Name, cancellationToken);
+ await WriteByteAsync((sbyte) message.Type, cancellationToken);
+ await WriteI32Async(message.SeqID, cancellationToken);
+ }
+ }
+
+ public override async Task WriteMessageEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteStructEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await WriteByteAsync((sbyte) field.Type, cancellationToken);
+ await WriteI16Async(field.ID, cancellationToken);
+ }
+
+ public override async Task WriteFieldEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteFieldStopAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await WriteByteAsync((sbyte) TType.Stop, cancellationToken);
+ }
+
+ public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ PreAllocatedBuffer[0] = (byte)map.KeyType;
+ PreAllocatedBuffer[1] = (byte)map.ValueType;
+ await Trans.WriteAsync(PreAllocatedBuffer, 0, 2, cancellationToken);
+
+ await WriteI32Async(map.Count, cancellationToken);
+ }
+
+ public override async Task WriteMapEndAsync(CancellationToken cancellationToken)
+
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await WriteByteAsync((sbyte) list.ElementType, cancellationToken);
+ await WriteI32Async(list.Count, cancellationToken);
+ }
+
+ public override async Task WriteListEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await WriteByteAsync((sbyte) set.ElementType, cancellationToken);
+ await WriteI32Async(set.Count, cancellationToken);
+ }
+
+ public override async Task WriteSetEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await WriteByteAsync(b ? (sbyte) 1 : (sbyte) 0, cancellationToken);
+ }
+
+ public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ PreAllocatedBuffer[0] = (byte)b;
+
+ await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
+ }
+ public override async Task WriteI16Async(short i16, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ PreAllocatedBuffer[0] = (byte)(0xff & (i16 >> 8));
+ PreAllocatedBuffer[1] = (byte)(0xff & i16);
+
+ await Trans.WriteAsync(PreAllocatedBuffer, 0, 2, cancellationToken);
+ }
+
+ public override async Task WriteI32Async(int i32, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ PreAllocatedBuffer[0] = (byte)(0xff & (i32 >> 24));
+ PreAllocatedBuffer[1] = (byte)(0xff & (i32 >> 16));
+ PreAllocatedBuffer[2] = (byte)(0xff & (i32 >> 8));
+ PreAllocatedBuffer[3] = (byte)(0xff & i32);
+
+ await Trans.WriteAsync(PreAllocatedBuffer, 0, 4, cancellationToken);
+ }
+
+
+ public override async Task WriteI64Async(long i64, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ PreAllocatedBuffer[0] = (byte)(0xff & (i64 >> 56));
+ PreAllocatedBuffer[1] = (byte)(0xff & (i64 >> 48));
+ PreAllocatedBuffer[2] = (byte)(0xff & (i64 >> 40));
+ PreAllocatedBuffer[3] = (byte)(0xff & (i64 >> 32));
+ PreAllocatedBuffer[4] = (byte)(0xff & (i64 >> 24));
+ PreAllocatedBuffer[5] = (byte)(0xff & (i64 >> 16));
+ PreAllocatedBuffer[6] = (byte)(0xff & (i64 >> 8));
+ PreAllocatedBuffer[7] = (byte)(0xff & i64);
+
+ await Trans.WriteAsync(PreAllocatedBuffer, 0, 8, cancellationToken);
+ }
+
+ public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await WriteI64Async(BitConverter.DoubleToInt64Bits(d), cancellationToken);
+ }
+
+
+ public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await WriteI32Async(bytes.Length, cancellationToken);
+ await Trans.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
+ }
+
+ public override async ValueTask<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TMessage>(cancellationToken);
+ }
+
+ var message = new TMessage();
+ var size = await ReadI32Async(cancellationToken);
+ if (size < 0)
+ {
+ var version = (uint) size & VersionMask;
+ if (version != Version1)
+ {
+ throw new TProtocolException(TProtocolException.BAD_VERSION,
+ $"Bad version in ReadMessageBegin: {version}");
+ }
+ message.Type = (TMessageType) (size & 0x000000ff);
+ message.Name = await ReadStringAsync(cancellationToken);
+ message.SeqID = await ReadI32Async(cancellationToken);
+ }
+ else
+ {
+ if (StrictRead)
+ {
+ throw new TProtocolException(TProtocolException.BAD_VERSION,
+ "Missing version in ReadMessageBegin, old client?");
+ }
+ message.Name = (size > 0) ? await ReadStringBodyAsync(size, cancellationToken) : string.Empty;
+ message.Type = (TMessageType) await ReadByteAsync(cancellationToken);
+ message.SeqID = await ReadI32Async(cancellationToken);
+ }
+ return message;
+ }
+
+ public override async Task ReadMessageEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async ValueTask<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+
+ return AnonymousStruct;
+ }
+
+ public override async Task ReadStructEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async ValueTask<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TField>(cancellationToken);
+ }
+
+
+ var type = (TType)await ReadByteAsync(cancellationToken);
+ if (type == TType.Stop)
+ {
+ return StopField;
+ }
+
+ return new TField {
+ Type = type,
+ ID = await ReadI16Async(cancellationToken)
+ };
+ }
+
+ public override async Task ReadFieldEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async ValueTask<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TMap>(cancellationToken);
+ }
+
+ var map = new TMap
+ {
+ KeyType = (TType) await ReadByteAsync(cancellationToken),
+ ValueType = (TType) await ReadByteAsync(cancellationToken),
+ Count = await ReadI32Async(cancellationToken)
+ };
+
+ return map;
+ }
+
+ public override async Task ReadMapEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async ValueTask<TList> ReadListBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TList>(cancellationToken);
+ }
+
+ var list = new TList
+ {
+ ElementType = (TType) await ReadByteAsync(cancellationToken),
+ Count = await ReadI32Async(cancellationToken)
+ };
+
+ return list;
+ }
+
+ public override async Task ReadListEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async ValueTask<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TSet>(cancellationToken);
+ }
+
+ var set = new TSet
+ {
+ ElementType = (TType) await ReadByteAsync(cancellationToken),
+ Count = await ReadI32Async(cancellationToken)
+ };
+
+ return set;
+ }
+
+ public override async Task ReadSetEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async ValueTask<bool> ReadBoolAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<bool>(cancellationToken);
+ }
+
+ return await ReadByteAsync(cancellationToken) == 1;
+ }
+
+ public override async ValueTask<sbyte> ReadByteAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<sbyte>(cancellationToken);
+ }
+
+ await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
+ return (sbyte)PreAllocatedBuffer[0];
+ }
+
+ public override async ValueTask<short> ReadI16Async(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<short>(cancellationToken);
+ }
+
+ await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 2, cancellationToken);
+ var result = (short) (((PreAllocatedBuffer[0] & 0xff) << 8) | PreAllocatedBuffer[1] & 0xff);
+ return result;
+ }
+
+ public override async ValueTask<int> ReadI32Async(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<int>(cancellationToken);
+ }
+
+ await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 4, cancellationToken);
+
+ var result =
+ ((PreAllocatedBuffer[0] & 0xff) << 24) |
+ ((PreAllocatedBuffer[1] & 0xff) << 16) |
+ ((PreAllocatedBuffer[2] & 0xff) << 8) |
+ PreAllocatedBuffer[3] & 0xff;
+
+ return result;
+ }
+
+#pragma warning disable 675
+
+ protected internal long ReadI64FromPreAllocatedBuffer()
+ {
+ var result =
+ ((long) (PreAllocatedBuffer[0] & 0xff) << 56) |
+ ((long) (PreAllocatedBuffer[1] & 0xff) << 48) |
+ ((long) (PreAllocatedBuffer[2] & 0xff) << 40) |
+ ((long) (PreAllocatedBuffer[3] & 0xff) << 32) |
+ ((long) (PreAllocatedBuffer[4] & 0xff) << 24) |
+ ((long) (PreAllocatedBuffer[5] & 0xff) << 16) |
+ ((long) (PreAllocatedBuffer[6] & 0xff) << 8) |
+ PreAllocatedBuffer[7] & 0xff;
+
+ return result;
+ }
+
+#pragma warning restore 675
+
+ public override async ValueTask<long> ReadI64Async(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<long>(cancellationToken);
+ }
+
+ await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 8, cancellationToken);
+ return ReadI64FromPreAllocatedBuffer();
+ }
+
+ public override async ValueTask<double> ReadDoubleAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<double>(cancellationToken);
+ }
+
+ var d = await ReadI64Async(cancellationToken);
+ return BitConverter.Int64BitsToDouble(d);
+ }
+
+ public override async ValueTask<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<byte[]>(cancellationToken);
+ }
+
+ var size = await ReadI32Async(cancellationToken);
+ var buf = new byte[size];
+ await Trans.ReadAllAsync(buf, 0, size, cancellationToken);
+ return buf;
+ }
+
+ public override async ValueTask<string> ReadStringAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<string>(cancellationToken);
+ }
+
+ var size = await ReadI32Async(cancellationToken);
+ return size > 0 ? await ReadStringBodyAsync(size, cancellationToken) : string.Empty;
+ }
+
+ private async ValueTask<string> ReadStringBodyAsync(int size, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled<string>(cancellationToken);
+ }
+
+ if (size <= PreAllocatedBuffer.Length)
+ {
+ await Trans.ReadAllAsync(PreAllocatedBuffer, 0, size, cancellationToken);
+ return Encoding.UTF8.GetString(PreAllocatedBuffer, 0, size);
+ }
+
+ var buf = new byte[size];
+ await Trans.ReadAllAsync(buf, 0, size, cancellationToken);
+ return Encoding.UTF8.GetString(buf, 0, buf.Length);
+ }
+
+ public class Factory : TProtocolFactory
+ {
+ protected bool StrictRead;
+ protected bool StrictWrite;
+
+ public Factory()
+ : this(false, true)
+ {
+ }
+
+ public Factory(bool strictRead, bool strictWrite)
+ {
+ StrictRead = strictRead;
+ StrictWrite = strictWrite;
+ }
+
+ public override TProtocol GetProtocol(TTransport trans)
+ {
+ return new TBinaryProtocol(trans, StrictRead, StrictWrite);
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TCompactProtocol.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TCompactProtocol.cs
new file mode 100644
index 000000000..c26633a14
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TCompactProtocol.cs
@@ -0,0 +1,919 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocol.Entities;
+using Thrift.Transport;
+
+namespace Thrift.Protocol
+{
+ //TODO: implementation of TProtocol
+
+ // ReSharper disable once InconsistentNaming
+ public class TCompactProtocol : TProtocol
+ {
+ private const byte ProtocolId = 0x82;
+ private const byte Version = 1;
+ private const byte VersionMask = 0x1f; // 0001 1111
+ private const byte TypeMask = 0xE0; // 1110 0000
+ private const byte TypeBits = 0x07; // 0000 0111
+ private const int TypeShiftAmount = 5;
+ private static readonly TStruct AnonymousStruct = new TStruct(string.Empty);
+ private static readonly TField StopField = new TField(string.Empty, TType.Stop, 0);
+
+ private const byte NoTypeOverride = 0xFF;
+
+ // ReSharper disable once InconsistentNaming
+ private static readonly byte[] TTypeToCompactType = new byte[16];
+ private static readonly TType[] CompactTypeToTType = new TType[13];
+
+ /// <summary>
+ /// Used to keep track of the last field for the current and previous structs, so we can do the delta stuff.
+ /// </summary>
+ private readonly Stack<short> _lastField = new Stack<short>(15);
+
+ /// <summary>
+ /// If we encounter a boolean field begin, save the TField here so it can have the value incorporated.
+ /// </summary>
+ private TField? _booleanField;
+
+ /// <summary>
+ /// If we Read a field header, and it's a boolean field, save the boolean value here so that ReadBool can use it.
+ /// </summary>
+ private bool? _boolValue;
+
+ private short _lastFieldId;
+
+ // minimize memory allocations by means of an preallocated bytes buffer
+ // The value of 128 is arbitrarily chosen, the required minimum size must be sizeof(long)
+ private byte[] PreAllocatedBuffer = new byte[128];
+
+ private struct VarInt
+ {
+ public byte[] bytes;
+ public int count;
+ }
+
+ // minimize memory allocations by means of an preallocated VarInt buffer
+ private VarInt PreAllocatedVarInt = new VarInt()
+ {
+ bytes = new byte[10], // see Int64ToVarInt()
+ count = 0
+ };
+
+
+
+
+ public TCompactProtocol(TTransport trans)
+ : base(trans)
+ {
+ TTypeToCompactType[(int) TType.Stop] = Types.Stop;
+ TTypeToCompactType[(int) TType.Bool] = Types.BooleanTrue;
+ TTypeToCompactType[(int) TType.Byte] = Types.Byte;
+ TTypeToCompactType[(int) TType.I16] = Types.I16;
+ TTypeToCompactType[(int) TType.I32] = Types.I32;
+ TTypeToCompactType[(int) TType.I64] = Types.I64;
+ TTypeToCompactType[(int) TType.Double] = Types.Double;
+ TTypeToCompactType[(int) TType.String] = Types.Binary;
+ TTypeToCompactType[(int) TType.List] = Types.List;
+ TTypeToCompactType[(int) TType.Set] = Types.Set;
+ TTypeToCompactType[(int) TType.Map] = Types.Map;
+ TTypeToCompactType[(int) TType.Struct] = Types.Struct;
+
+ CompactTypeToTType[Types.Stop] = TType.Stop;
+ CompactTypeToTType[Types.BooleanTrue] = TType.Bool;
+ CompactTypeToTType[Types.BooleanFalse] = TType.Bool;
+ CompactTypeToTType[Types.Byte] = TType.Byte;
+ CompactTypeToTType[Types.I16] = TType.I16;
+ CompactTypeToTType[Types.I32] = TType.I32;
+ CompactTypeToTType[Types.I64] = TType.I64;
+ CompactTypeToTType[Types.Double] = TType.Double;
+ CompactTypeToTType[Types.Binary] = TType.String;
+ CompactTypeToTType[Types.List] = TType.List;
+ CompactTypeToTType[Types.Set] = TType.Set;
+ CompactTypeToTType[Types.Map] = TType.Map;
+ CompactTypeToTType[Types.Struct] = TType.Struct;
+ }
+
+ public void Reset()
+ {
+ _lastField.Clear();
+ _lastFieldId = 0;
+ }
+
+ public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
+ {
+ PreAllocatedBuffer[0] = ProtocolId;
+ PreAllocatedBuffer[1] = (byte)((Version & VersionMask) | (((uint)message.Type << TypeShiftAmount) & TypeMask));
+ await Trans.WriteAsync(PreAllocatedBuffer, 0, 2, cancellationToken);
+
+ Int32ToVarInt((uint) message.SeqID, ref PreAllocatedVarInt);
+ await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
+
+ await WriteStringAsync(message.Name, cancellationToken);
+ }
+
+ public override async Task WriteMessageEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ /// <summary>
+ /// Write a struct begin. This doesn't actually put anything on the wire. We
+ /// use it as an opportunity to put special placeholder markers on the field
+ /// stack so we can get the field id deltas correct.
+ /// </summary>
+ public override async Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+
+ _lastField.Push(_lastFieldId);
+ _lastFieldId = 0;
+ }
+
+ public override async Task WriteStructEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+
+ _lastFieldId = _lastField.Pop();
+ }
+
+ private async Task WriteFieldBeginInternalAsync(TField field, byte fieldType, CancellationToken cancellationToken)
+ {
+ // if there's a exType override passed in, use that. Otherwise ask GetCompactType().
+ if (fieldType == NoTypeOverride)
+ fieldType = GetCompactType(field.Type);
+
+
+ // check if we can use delta encoding for the field id
+ if (field.ID > _lastFieldId)
+ {
+ var delta = field.ID - _lastFieldId;
+ if (delta <= 15)
+ {
+ // Write them together
+ PreAllocatedBuffer[0] = (byte)((delta << 4) | fieldType);
+ await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
+ _lastFieldId = field.ID;
+ return;
+ }
+ }
+
+ // Write them separate
+ PreAllocatedBuffer[0] = fieldType;
+ await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
+ await WriteI16Async(field.ID, cancellationToken);
+ _lastFieldId = field.ID;
+ }
+
+ public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken)
+ {
+ if (field.Type == TType.Bool)
+ {
+ _booleanField = field;
+ }
+ else
+ {
+ await WriteFieldBeginInternalAsync(field, NoTypeOverride, cancellationToken);
+ }
+ }
+
+ public override async Task WriteFieldEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteFieldStopAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ PreAllocatedBuffer[0] = Types.Stop;
+ await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
+ }
+
+ protected async Task WriteCollectionBeginAsync(TType elemType, int size, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ /*
+ Abstract method for writing the start of lists and sets. List and sets on
+ the wire differ only by the exType indicator.
+ */
+
+ if (size <= 14)
+ {
+ PreAllocatedBuffer[0] = (byte)((size << 4) | GetCompactType(elemType));
+ await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
+ }
+ else
+ {
+ PreAllocatedBuffer[0] = (byte)(0xf0 | GetCompactType(elemType));
+ await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
+
+ Int32ToVarInt((uint) size, ref PreAllocatedVarInt);
+ await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
+ }
+ }
+
+ public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken)
+ {
+ await WriteCollectionBeginAsync(list.ElementType, list.Count, cancellationToken);
+ }
+
+ public override async Task WriteListEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ await WriteCollectionBeginAsync(set.ElementType, set.Count, cancellationToken);
+ }
+
+ public override async Task WriteSetEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ /*
+ Write a boolean value. Potentially, this could be a boolean field, in
+ which case the field header info isn't written yet. If so, decide what the
+ right exType header is for the value and then Write the field header.
+ Otherwise, Write a single byte.
+ */
+
+ if (_booleanField != null)
+ {
+ // we haven't written the field header yet
+ var type = b ? Types.BooleanTrue : Types.BooleanFalse;
+ await WriteFieldBeginInternalAsync(_booleanField.Value, type, cancellationToken);
+ _booleanField = null;
+ }
+ else
+ {
+ // we're not part of a field, so just write the value.
+ PreAllocatedBuffer[0] = b ? Types.BooleanTrue : Types.BooleanFalse;
+ await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
+ }
+ }
+
+ public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ PreAllocatedBuffer[0] = (byte)b;
+ await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
+ }
+
+ public override async Task WriteI16Async(short i16, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ Int32ToVarInt(IntToZigzag(i16), ref PreAllocatedVarInt);
+ await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
+ }
+
+ private static void Int32ToVarInt(uint n, ref VarInt varint)
+ {
+ // Write an i32 as a varint. Results in 1 - 5 bytes on the wire.
+ varint.count = 0;
+ Debug.Assert(varint.bytes.Length >= 5);
+
+ while (true)
+ {
+ if ((n & ~0x7F) == 0)
+ {
+ varint.bytes[varint.count++] = (byte)n;
+ break;
+ }
+
+ varint.bytes[varint.count++] = (byte)((n & 0x7F) | 0x80);
+ n >>= 7;
+ }
+ }
+
+ public override async Task WriteI32Async(int i32, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ Int32ToVarInt(IntToZigzag(i32), ref PreAllocatedVarInt);
+ await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
+ }
+
+ static private void Int64ToVarInt(ulong n, ref VarInt varint)
+ {
+ // Write an i64 as a varint. Results in 1-10 bytes on the wire.
+ varint.count = 0;
+ Debug.Assert(varint.bytes.Length >= 10);
+
+ while (true)
+ {
+ if ((n & ~(ulong)0x7FL) == 0)
+ {
+ varint.bytes[varint.count++] = (byte)n;
+ break;
+ }
+ varint.bytes[varint.count++] = (byte)((n & 0x7F) | 0x80);
+ n >>= 7;
+ }
+ }
+
+ public override async Task WriteI64Async(long i64, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ Int64ToVarInt(LongToZigzag(i64), ref PreAllocatedVarInt);
+ await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
+ }
+
+ public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ FixedLongToBytes(BitConverter.DoubleToInt64Bits(d), PreAllocatedBuffer, 0);
+ await Trans.WriteAsync(PreAllocatedBuffer, 0, 8, cancellationToken);
+ }
+
+ public override async Task WriteStringAsync(string str, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ var bytes = Encoding.UTF8.GetBytes(str);
+
+ Int32ToVarInt((uint) bytes.Length, ref PreAllocatedVarInt);
+ await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
+ await Trans.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
+ }
+
+ public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ Int32ToVarInt((uint) bytes.Length, ref PreAllocatedVarInt);
+ await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
+ await Trans.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
+ }
+
+ public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ if (map.Count == 0)
+ {
+ PreAllocatedBuffer[0] = 0;
+ await Trans.WriteAsync( PreAllocatedBuffer, 0, 1, cancellationToken);
+ }
+ else
+ {
+ Int32ToVarInt((uint) map.Count, ref PreAllocatedVarInt);
+ await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
+
+ PreAllocatedBuffer[0] = (byte)((GetCompactType(map.KeyType) << 4) | GetCompactType(map.ValueType));
+ await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
+ }
+ }
+
+ public override async Task WriteMapEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async ValueTask<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TMessage>(cancellationToken);
+ }
+
+ var protocolId = (byte) await ReadByteAsync(cancellationToken);
+ if (protocolId != ProtocolId)
+ {
+ throw new TProtocolException($"Expected protocol id {ProtocolId:X} but got {protocolId:X}");
+ }
+
+ var versionAndType = (byte) await ReadByteAsync(cancellationToken);
+ var version = (byte) (versionAndType & VersionMask);
+
+ if (version != Version)
+ {
+ throw new TProtocolException($"Expected version {Version} but got {version}");
+ }
+
+ var type = (byte) ((versionAndType >> TypeShiftAmount) & TypeBits);
+ var seqid = (int) await ReadVarInt32Async(cancellationToken);
+ var messageName = await ReadStringAsync(cancellationToken);
+
+ return new TMessage(messageName, (TMessageType) type, seqid);
+ }
+
+ public override async Task ReadMessageEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async ValueTask<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TStruct>(cancellationToken);
+ }
+
+ // some magic is here )
+
+ _lastField.Push(_lastFieldId);
+ _lastFieldId = 0;
+
+ return AnonymousStruct;
+ }
+
+ public override async Task ReadStructEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+
+ /*
+ Doesn't actually consume any wire data, just removes the last field for
+ this struct from the field stack.
+ */
+
+ // consume the last field we Read off the wire.
+ _lastFieldId = _lastField.Pop();
+ }
+
+ public override async ValueTask<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
+ {
+ // Read a field header off the wire.
+ var type = (byte) await ReadByteAsync(cancellationToken);
+
+ // if it's a stop, then we can return immediately, as the struct is over.
+ if (type == Types.Stop)
+ {
+ return StopField;
+ }
+
+
+ // mask off the 4 MSB of the exType header. it could contain a field id delta.
+ var modifier = (short) ((type & 0xf0) >> 4);
+ var compactType = (byte)(type & 0x0f);
+
+ short fieldId;
+ if (modifier == 0)
+ {
+ fieldId = await ReadI16Async(cancellationToken);
+ }
+ else
+ {
+ fieldId = (short) (_lastFieldId + modifier);
+ }
+
+ var ttype = GetTType(compactType);
+ var field = new TField(string.Empty, ttype, fieldId);
+
+ // if this happens to be a boolean field, the value is encoded in the exType
+ if( ttype == TType.Bool)
+ {
+ _boolValue = (compactType == Types.BooleanTrue);
+ }
+
+ // push the new field onto the field stack so we can keep the deltas going.
+ _lastFieldId = field.ID;
+ return field;
+ }
+
+ public override async Task ReadFieldEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async ValueTask<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled<TMap>(cancellationToken);
+ }
+
+ /*
+ Read a map header off the wire. If the size is zero, skip Reading the key
+ and value exType. This means that 0-length maps will yield TMaps without the
+ "correct" types.
+ */
+
+ var size = (int) await ReadVarInt32Async(cancellationToken);
+ var keyAndValueType = size == 0 ? (byte) 0 : (byte) await ReadByteAsync(cancellationToken);
+ return new TMap(GetTType((byte) (keyAndValueType >> 4)), GetTType((byte) (keyAndValueType & 0xf)), size);
+ }
+
+ public override async Task ReadMapEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async ValueTask<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
+ {
+ /*
+ Read a set header off the wire. If the set size is 0-14, the size will
+ be packed into the element exType header. If it's a longer set, the 4 MSB
+ of the element exType header will be 0xF, and a varint will follow with the
+ true size.
+ */
+
+ return new TSet(await ReadListBeginAsync(cancellationToken));
+ }
+
+ public override ValueTask<bool> ReadBoolAsync(CancellationToken cancellationToken)
+ {
+ /*
+ Read a boolean off the wire. If this is a boolean field, the value should
+ already have been Read during ReadFieldBegin, so we'll just consume the
+ pre-stored value. Otherwise, Read a byte.
+ */
+
+ if (_boolValue != null)
+ {
+ var result = _boolValue.Value;
+ _boolValue = null;
+ return new ValueTask<bool>(result);
+ }
+
+ return InternalCall();
+
+ async ValueTask<bool> InternalCall()
+ {
+ var data = await ReadByteAsync(cancellationToken);
+ return (data == Types.BooleanTrue);
+ }
+ }
+
+
+ public override async ValueTask<sbyte> ReadByteAsync(CancellationToken cancellationToken)
+ {
+ // Read a single byte off the wire. Nothing interesting here.
+ await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
+ return (sbyte)PreAllocatedBuffer[0];
+ }
+
+ public override async ValueTask<short> ReadI16Async(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<short>(cancellationToken);
+ }
+
+ return (short) ZigzagToInt(await ReadVarInt32Async(cancellationToken));
+ }
+
+ public override async ValueTask<int> ReadI32Async(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<int>(cancellationToken);
+ }
+
+ return ZigzagToInt(await ReadVarInt32Async(cancellationToken));
+ }
+
+ public override async ValueTask<long> ReadI64Async(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<long>(cancellationToken);
+ }
+
+ return ZigzagToLong(await ReadVarInt64Async(cancellationToken));
+ }
+
+ public override async ValueTask<double> ReadDoubleAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<double>(cancellationToken);
+ }
+
+ await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 8, cancellationToken);
+
+ return BitConverter.Int64BitsToDouble(BytesToLong(PreAllocatedBuffer));
+ }
+
+ public override async ValueTask<string> ReadStringAsync(CancellationToken cancellationToken)
+ {
+ // read length
+ var length = (int) await ReadVarInt32Async(cancellationToken);
+ if (length == 0)
+ {
+ return string.Empty;
+ }
+
+ // read and decode data
+ if (length < PreAllocatedBuffer.Length)
+ {
+ await Trans.ReadAllAsync(PreAllocatedBuffer, 0, length, cancellationToken);
+ return Encoding.UTF8.GetString(PreAllocatedBuffer, 0, length);
+ }
+
+ var buf = new byte[length];
+ await Trans.ReadAllAsync(buf, 0, length, cancellationToken);
+ return Encoding.UTF8.GetString(buf, 0, length);
+ }
+
+ public override async ValueTask<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
+ {
+ // read length
+ var length = (int) await ReadVarInt32Async(cancellationToken);
+ if (length == 0)
+ {
+ return new byte[0];
+ }
+
+ // read data
+ var buf = new byte[length];
+ await Trans.ReadAllAsync(buf, 0, length, cancellationToken);
+ return buf;
+ }
+
+ public override async ValueTask<TList> ReadListBeginAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled<TList>(cancellationToken);
+ }
+
+ /*
+ Read a list header off the wire. If the list size is 0-14, the size will
+ be packed into the element exType header. If it's a longer list, the 4 MSB
+ of the element exType header will be 0xF, and a varint will follow with the
+ true size.
+ */
+
+ var sizeAndType = (byte) await ReadByteAsync(cancellationToken);
+ var size = (sizeAndType >> 4) & 0x0f;
+ if (size == 15)
+ {
+ size = (int) await ReadVarInt32Async(cancellationToken);
+ }
+
+ var type = GetTType(sizeAndType);
+ return new TList(type, size);
+ }
+
+ public override async Task ReadListEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task ReadSetEndAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ private static byte GetCompactType(TType ttype)
+ {
+ // Given a TType value, find the appropriate TCompactProtocol.Types constant.
+ return TTypeToCompactType[(int) ttype];
+ }
+
+
+ private async ValueTask<uint> ReadVarInt32Async(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<uint>(cancellationToken);
+ }
+
+ /*
+ Read an i32 from the wire as a varint. The MSB of each byte is set
+ if there is another byte to follow. This can Read up to 5 bytes.
+ */
+
+ uint result = 0;
+ var shift = 0;
+
+ while (true)
+ {
+ var b = (byte) await ReadByteAsync(cancellationToken);
+ result |= (uint) (b & 0x7f) << shift;
+ if ((b & 0x80) != 0x80)
+ {
+ break;
+ }
+ shift += 7;
+ }
+
+ return result;
+ }
+
+ private async ValueTask<ulong> ReadVarInt64Async(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<uint>(cancellationToken);
+ }
+
+ /*
+ Read an i64 from the wire as a proper varint. The MSB of each byte is set
+ if there is another byte to follow. This can Read up to 10 bytes.
+ */
+
+ var shift = 0;
+ ulong result = 0;
+ while (true)
+ {
+ var b = (byte) await ReadByteAsync(cancellationToken);
+ result |= (ulong) (b & 0x7f) << shift;
+ if ((b & 0x80) != 0x80)
+ {
+ break;
+ }
+ shift += 7;
+ }
+
+ return result;
+ }
+
+ private static int ZigzagToInt(uint n)
+ {
+ return (int) (n >> 1) ^ -(int) (n & 1);
+ }
+
+ private static long ZigzagToLong(ulong n)
+ {
+ return (long) (n >> 1) ^ -(long) (n & 1);
+ }
+
+ private static long BytesToLong(byte[] bytes)
+ {
+ /*
+ Note that it's important that the mask bytes are long literals,
+ otherwise they'll default to ints, and when you shift an int left 56 bits,
+ you just get a messed up int.
+ */
+
+ return
+ ((bytes[7] & 0xffL) << 56) |
+ ((bytes[6] & 0xffL) << 48) |
+ ((bytes[5] & 0xffL) << 40) |
+ ((bytes[4] & 0xffL) << 32) |
+ ((bytes[3] & 0xffL) << 24) |
+ ((bytes[2] & 0xffL) << 16) |
+ ((bytes[1] & 0xffL) << 8) |
+ (bytes[0] & 0xffL);
+ }
+
+ private static TType GetTType(byte type)
+ {
+ // Given a TCompactProtocol.Types constant, convert it to its corresponding TType value.
+ return CompactTypeToTType[type & 0x0f];
+ }
+
+ private static ulong LongToZigzag(long n)
+ {
+ // Convert l into a zigzag long. This allows negative numbers to be represented compactly as a varint
+ return (ulong) (n << 1) ^ (ulong) (n >> 63);
+ }
+
+ private static uint IntToZigzag(int n)
+ {
+ // Convert n into a zigzag int. This allows negative numbers to be represented compactly as a varint
+ return (uint) (n << 1) ^ (uint) (n >> 31);
+ }
+
+ private static void FixedLongToBytes(long n, byte[] buf, int off)
+ {
+ // Convert a long into little-endian bytes in buf starting at off and going until off+7.
+ buf[off + 0] = (byte) (n & 0xff);
+ buf[off + 1] = (byte) ((n >> 8) & 0xff);
+ buf[off + 2] = (byte) ((n >> 16) & 0xff);
+ buf[off + 3] = (byte) ((n >> 24) & 0xff);
+ buf[off + 4] = (byte) ((n >> 32) & 0xff);
+ buf[off + 5] = (byte) ((n >> 40) & 0xff);
+ buf[off + 6] = (byte) ((n >> 48) & 0xff);
+ buf[off + 7] = (byte) ((n >> 56) & 0xff);
+ }
+
+ public class Factory : TProtocolFactory
+ {
+ public override TProtocol GetProtocol(TTransport trans)
+ {
+ return new TCompactProtocol(trans);
+ }
+ }
+
+ /// <summary>
+ /// All of the on-wire exType codes.
+ /// </summary>
+ private static class Types
+ {
+ public const byte Stop = 0x00;
+ public const byte BooleanTrue = 0x01;
+ public const byte BooleanFalse = 0x02;
+ public const byte Byte = 0x03;
+ public const byte I16 = 0x04;
+ public const byte I32 = 0x05;
+ public const byte I64 = 0x06;
+ public const byte Double = 0x07;
+ public const byte Binary = 0x08;
+ public const byte List = 0x09;
+ public const byte Set = 0x0A;
+ public const byte Map = 0x0B;
+ public const byte Struct = 0x0C;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TJSONProtocol.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TJSONProtocol.cs
new file mode 100644
index 000000000..464bd62ff
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TJSONProtocol.cs
@@ -0,0 +1,981 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocol.Entities;
+using Thrift.Protocol.Utilities;
+using Thrift.Transport;
+
+namespace Thrift.Protocol
+{
+ /// <summary>
+ /// JSON protocol implementation for thrift.
+ /// This is a full-featured protocol supporting Write and Read.
+ /// Please see the C++ class header for a detailed description of the
+ /// protocol's wire format.
+ /// Adapted from the Java version.
+ /// </summary>
+ // ReSharper disable once InconsistentNaming
+ public class TJsonProtocol : TProtocol
+ {
+ private const long Version = 1;
+
+ // Temporary buffer used by several methods
+ private readonly byte[] _tempBuffer = new byte[4];
+
+ // Current context that we are in
+ protected JSONBaseContext Context;
+
+ // Stack of nested contexts that we may be in
+ protected Stack<JSONBaseContext> ContextStack = new Stack<JSONBaseContext>();
+
+ // Reader that manages a 1-byte buffer
+ protected LookaheadReader Reader;
+
+ // Default encoding
+ protected Encoding Utf8Encoding = Encoding.UTF8;
+
+ /// <summary>
+ /// TJsonProtocol Constructor
+ /// </summary>
+ public TJsonProtocol(TTransport trans)
+ : base(trans)
+ {
+ Context = new JSONBaseContext(this);
+ Reader = new LookaheadReader(this);
+ }
+
+ /// <summary>
+ /// Push a new JSON context onto the stack.
+ /// </summary>
+ protected void PushContext(JSONBaseContext c)
+ {
+ ContextStack.Push(Context);
+ Context = c;
+ }
+
+ /// <summary>
+ /// Pop the last JSON context off the stack
+ /// </summary>
+ protected void PopContext()
+ {
+ Context = ContextStack.Pop();
+ }
+
+ /// <summary>
+ /// Read a byte that must match b[0]; otherwise an exception is thrown.
+ /// Marked protected to avoid synthetic accessor in JSONListContext.Read
+ /// and JSONPairContext.Read
+ /// </summary>
+ protected async Task ReadJsonSyntaxCharAsync(byte[] bytes, CancellationToken cancellationToken)
+ {
+ var ch = await Reader.ReadAsync(cancellationToken);
+ if (ch != bytes[0])
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, $"Unexpected character: {(char) ch}");
+ }
+ }
+
+ /// <summary>
+ /// Write the bytes in array buf as a JSON characters, escaping as needed
+ /// </summary>
+ private async Task WriteJsonStringAsync(byte[] bytes, CancellationToken cancellationToken)
+ {
+ await Context.WriteConditionalDelimiterAsync(cancellationToken);
+ await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
+
+ var len = bytes.Length;
+ for (var i = 0; i < len; i++)
+ {
+ if ((bytes[i] & 0x00FF) >= 0x30)
+ {
+ if (bytes[i] == TJSONProtocolConstants.Backslash[0])
+ {
+ await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
+ await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
+ }
+ else
+ {
+ await Trans.WriteAsync(bytes.ToArray(), i, 1, cancellationToken);
+ }
+ }
+ else
+ {
+ _tempBuffer[0] = TJSONProtocolConstants.JsonCharTable[bytes[i]];
+ if (_tempBuffer[0] == 1)
+ {
+ await Trans.WriteAsync(bytes, i, 1, cancellationToken);
+ }
+ else if (_tempBuffer[0] > 1)
+ {
+ await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
+ await Trans.WriteAsync(_tempBuffer, 0, 1, cancellationToken);
+ }
+ else
+ {
+ await Trans.WriteAsync(TJSONProtocolConstants.EscSequences, cancellationToken);
+ _tempBuffer[0] = TJSONProtocolHelper.ToHexChar((byte) (bytes[i] >> 4));
+ _tempBuffer[1] = TJSONProtocolHelper.ToHexChar(bytes[i]);
+ await Trans.WriteAsync(_tempBuffer, 0, 2, cancellationToken);
+ }
+ }
+ }
+ await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+
+ /// <summary>
+ /// Write out number as a JSON value. If the context dictates so, it will be
+ /// wrapped in quotes to output as a JSON string.
+ /// </summary>
+ private async Task WriteJsonIntegerAsync(long num, CancellationToken cancellationToken)
+ {
+ await Context.WriteConditionalDelimiterAsync(cancellationToken);
+ var str = num.ToString();
+
+ var escapeNum = Context.EscapeNumbers();
+ if (escapeNum)
+ {
+ await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+
+ var bytes = Utf8Encoding.GetBytes(str);
+ await Trans.WriteAsync(bytes, cancellationToken);
+
+ if (escapeNum)
+ {
+ await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+ }
+
+ /// <summary>
+ /// Write out a double as a JSON value. If it is NaN or infinity or if the
+ /// context dictates escaping, Write out as JSON string.
+ /// </summary>
+ private async Task WriteJsonDoubleAsync(double num, CancellationToken cancellationToken)
+ {
+ await Context.WriteConditionalDelimiterAsync(cancellationToken);
+ var str = num.ToString("G17", CultureInfo.InvariantCulture);
+ var special = false;
+
+ switch (str[0])
+ {
+ case 'N': // NaN
+ case 'I': // Infinity
+ special = true;
+ break;
+ case '-':
+ if (str[1] == 'I')
+ {
+ // -Infinity
+ special = true;
+ }
+ break;
+ }
+
+ var escapeNum = special || Context.EscapeNumbers();
+
+ if (escapeNum)
+ {
+ await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+
+ await Trans.WriteAsync(Utf8Encoding.GetBytes(str), cancellationToken);
+
+ if (escapeNum)
+ {
+ await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+ }
+
+ /// <summary>
+ /// Write out contents of byte array b as a JSON string with base-64 encoded
+ /// data
+ /// </summary>
+ private async Task WriteJsonBase64Async(byte[] bytes, CancellationToken cancellationToken)
+ {
+ await Context.WriteConditionalDelimiterAsync(cancellationToken);
+ await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
+
+ var len = bytes.Length;
+ var off = 0;
+
+ while (len >= 3)
+ {
+ // Encode 3 bytes at a time
+ TBase64Utils.Encode(bytes, off, 3, _tempBuffer, 0);
+ await Trans.WriteAsync(_tempBuffer, 0, 4, cancellationToken);
+ off += 3;
+ len -= 3;
+ }
+
+ if (len > 0)
+ {
+ // Encode remainder
+ TBase64Utils.Encode(bytes, off, len, _tempBuffer, 0);
+ await Trans.WriteAsync(_tempBuffer, 0, len + 1, cancellationToken);
+ }
+
+ await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+
+ private async Task WriteJsonObjectStartAsync(CancellationToken cancellationToken)
+ {
+ await Context.WriteConditionalDelimiterAsync(cancellationToken);
+ await Trans.WriteAsync(TJSONProtocolConstants.LeftBrace, cancellationToken);
+ PushContext(new JSONPairContext(this));
+ }
+
+ private async Task WriteJsonObjectEndAsync(CancellationToken cancellationToken)
+ {
+ PopContext();
+ await Trans.WriteAsync(TJSONProtocolConstants.RightBrace, cancellationToken);
+ }
+
+ private async Task WriteJsonArrayStartAsync(CancellationToken cancellationToken)
+ {
+ await Context.WriteConditionalDelimiterAsync(cancellationToken);
+ await Trans.WriteAsync(TJSONProtocolConstants.LeftBracket, cancellationToken);
+ PushContext(new JSONListContext(this));
+ }
+
+ private async Task WriteJsonArrayEndAsync(CancellationToken cancellationToken)
+ {
+ PopContext();
+ await Trans.WriteAsync(TJSONProtocolConstants.RightBracket, cancellationToken);
+ }
+
+ public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
+ {
+ await WriteJsonArrayStartAsync(cancellationToken);
+ await WriteJsonIntegerAsync(Version, cancellationToken);
+
+ var b = Utf8Encoding.GetBytes(message.Name);
+ await WriteJsonStringAsync(b, cancellationToken);
+
+ await WriteJsonIntegerAsync((long) message.Type, cancellationToken);
+ await WriteJsonIntegerAsync(message.SeqID, cancellationToken);
+ }
+
+ public override async Task WriteMessageEndAsync(CancellationToken cancellationToken)
+ {
+ await WriteJsonArrayEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken)
+ {
+ await WriteJsonObjectStartAsync(cancellationToken);
+ }
+
+ public override async Task WriteStructEndAsync(CancellationToken cancellationToken)
+ {
+ await WriteJsonObjectEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken)
+ {
+ await WriteJsonIntegerAsync(field.ID, cancellationToken);
+ await WriteJsonObjectStartAsync(cancellationToken);
+ await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(field.Type), cancellationToken);
+ }
+
+ public override async Task WriteFieldEndAsync(CancellationToken cancellationToken)
+ {
+ await WriteJsonObjectEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteFieldStopAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken)
+ {
+ await WriteJsonArrayStartAsync(cancellationToken);
+ await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(map.KeyType), cancellationToken);
+ await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(map.ValueType), cancellationToken);
+ await WriteJsonIntegerAsync(map.Count, cancellationToken);
+ await WriteJsonObjectStartAsync(cancellationToken);
+ }
+
+ public override async Task WriteMapEndAsync(CancellationToken cancellationToken)
+ {
+ await WriteJsonObjectEndAsync(cancellationToken);
+ await WriteJsonArrayEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken)
+ {
+ await WriteJsonArrayStartAsync(cancellationToken);
+ await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(list.ElementType), cancellationToken);
+ await WriteJsonIntegerAsync(list.Count, cancellationToken);
+ }
+
+ public override async Task WriteListEndAsync(CancellationToken cancellationToken)
+ {
+ await WriteJsonArrayEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken)
+ {
+ await WriteJsonArrayStartAsync(cancellationToken);
+ await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(set.ElementType), cancellationToken);
+ await WriteJsonIntegerAsync(set.Count, cancellationToken);
+ }
+
+ public override async Task WriteSetEndAsync(CancellationToken cancellationToken)
+ {
+ await WriteJsonArrayEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken)
+ {
+ await WriteJsonIntegerAsync(b ? 1 : 0, cancellationToken);
+ }
+
+ public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken)
+ {
+ await WriteJsonIntegerAsync(b, cancellationToken);
+ }
+
+ public override async Task WriteI16Async(short i16, CancellationToken cancellationToken)
+ {
+ await WriteJsonIntegerAsync(i16, cancellationToken);
+ }
+
+ public override async Task WriteI32Async(int i32, CancellationToken cancellationToken)
+ {
+ await WriteJsonIntegerAsync(i32, cancellationToken);
+ }
+
+ public override async Task WriteI64Async(long i64, CancellationToken cancellationToken)
+ {
+ await WriteJsonIntegerAsync(i64, cancellationToken);
+ }
+
+ public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken)
+ {
+ await WriteJsonDoubleAsync(d, cancellationToken);
+ }
+
+ public override async Task WriteStringAsync(string s, CancellationToken cancellationToken)
+ {
+ var b = Utf8Encoding.GetBytes(s);
+ await WriteJsonStringAsync(b, cancellationToken);
+ }
+
+ public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken)
+ {
+ await WriteJsonBase64Async(bytes, cancellationToken);
+ }
+
+ /// <summary>
+ /// Read in a JSON string, unescaping as appropriate.. Skip Reading from the
+ /// context if skipContext is true.
+ /// </summary>
+ private async ValueTask<byte[]> ReadJsonStringAsync(bool skipContext, CancellationToken cancellationToken)
+ {
+ using (var buffer = new MemoryStream())
+ {
+ var codeunits = new List<char>();
+
+
+ if (!skipContext)
+ {
+ await Context.ReadConditionalDelimiterAsync(cancellationToken);
+ }
+
+ await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
+
+ while (true)
+ {
+ var ch = await Reader.ReadAsync(cancellationToken);
+ if (ch == TJSONProtocolConstants.Quote[0])
+ {
+ break;
+ }
+
+ // escaped?
+ if (ch != TJSONProtocolConstants.EscSequences[0])
+ {
+ await buffer.WriteAsync(new[] {ch}, 0, 1, cancellationToken);
+ continue;
+ }
+
+ // distinguish between \uXXXX and \?
+ ch = await Reader.ReadAsync(cancellationToken);
+ if (ch != TJSONProtocolConstants.EscSequences[1]) // control chars like \n
+ {
+ var off = Array.IndexOf(TJSONProtocolConstants.EscapeChars, (char) ch);
+ if (off == -1)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected control char");
+ }
+ ch = TJSONProtocolConstants.EscapeCharValues[off];
+ await buffer.WriteAsync(new[] {ch}, 0, 1, cancellationToken);
+ continue;
+ }
+
+ // it's \uXXXX
+ await Trans.ReadAllAsync(_tempBuffer, 0, 4, cancellationToken);
+
+ var wch = (short) ((TJSONProtocolHelper.ToHexVal(_tempBuffer[0]) << 12) +
+ (TJSONProtocolHelper.ToHexVal(_tempBuffer[1]) << 8) +
+ (TJSONProtocolHelper.ToHexVal(_tempBuffer[2]) << 4) +
+ TJSONProtocolHelper.ToHexVal(_tempBuffer[3]));
+
+ if (char.IsHighSurrogate((char) wch))
+ {
+ if (codeunits.Count > 0)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected low surrogate char");
+ }
+ codeunits.Add((char) wch);
+ }
+ else if (char.IsLowSurrogate((char) wch))
+ {
+ if (codeunits.Count == 0)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected high surrogate char");
+ }
+
+ codeunits.Add((char) wch);
+ var tmp = Utf8Encoding.GetBytes(codeunits.ToArray());
+ await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken);
+ codeunits.Clear();
+ }
+ else
+ {
+ var tmp = Utf8Encoding.GetBytes(new[] {(char) wch});
+ await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken);
+ }
+ }
+
+ if (codeunits.Count > 0)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected low surrogate char");
+ }
+
+ return buffer.ToArray();
+ }
+ }
+
+ /// <summary>
+ /// Read in a sequence of characters that are all valid in JSON numbers. Does
+ /// not do a complete regex check to validate that this is actually a number.
+ /// </summary>
+ private async ValueTask<string> ReadJsonNumericCharsAsync(CancellationToken cancellationToken)
+ {
+ var strbld = new StringBuilder();
+ while (true)
+ {
+ //TODO: workaround for primitive types with TJsonProtocol, think - how to rewrite into more easy form without exceptions
+ try
+ {
+ var ch = await Reader.PeekAsync(cancellationToken);
+ if (!TJSONProtocolHelper.IsJsonNumeric(ch))
+ {
+ break;
+ }
+ var c = (char)await Reader.ReadAsync(cancellationToken);
+ strbld.Append(c);
+ }
+ catch (TTransportException)
+ {
+ break;
+ }
+ }
+ return strbld.ToString();
+ }
+
+ /// <summary>
+ /// Read in a JSON number. If the context dictates, Read in enclosing quotes.
+ /// </summary>
+ private async ValueTask<long> ReadJsonIntegerAsync(CancellationToken cancellationToken)
+ {
+ await Context.ReadConditionalDelimiterAsync(cancellationToken);
+ if (Context.EscapeNumbers())
+ {
+ await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+
+ var str = await ReadJsonNumericCharsAsync(cancellationToken);
+ if (Context.EscapeNumbers())
+ {
+ await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+
+ try
+ {
+ return long.Parse(str);
+ }
+ catch (FormatException)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Bad data encounted in numeric data");
+ }
+ }
+
+ /// <summary>
+ /// Read in a JSON double value. Throw if the value is not wrapped in quotes
+ /// when expected or if wrapped in quotes when not expected.
+ /// </summary>
+ private async ValueTask<double> ReadJsonDoubleAsync(CancellationToken cancellationToken)
+ {
+ await Context.ReadConditionalDelimiterAsync(cancellationToken);
+ if (await Reader.PeekAsync(cancellationToken) == TJSONProtocolConstants.Quote[0])
+ {
+ var arr = await ReadJsonStringAsync(true, cancellationToken);
+ var dub = double.Parse(Utf8Encoding.GetString(arr, 0, arr.Length), CultureInfo.InvariantCulture);
+
+ if (!Context.EscapeNumbers() && !double.IsNaN(dub) && !double.IsInfinity(dub))
+ {
+ // Throw exception -- we should not be in a string in this case
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Numeric data unexpectedly quoted");
+ }
+
+ return dub;
+ }
+
+ if (Context.EscapeNumbers())
+ {
+ // This will throw - we should have had a quote if escapeNum == true
+ await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
+ }
+
+ try
+ {
+ return double.Parse(await ReadJsonNumericCharsAsync(cancellationToken), CultureInfo.InvariantCulture);
+ }
+ catch (FormatException)
+ {
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Bad data encounted in numeric data");
+ }
+ }
+
+ /// <summary>
+ /// Read in a JSON string containing base-64 encoded data and decode it.
+ /// </summary>
+ private async ValueTask<byte[]> ReadJsonBase64Async(CancellationToken cancellationToken)
+ {
+ var b = await ReadJsonStringAsync(false, cancellationToken);
+ var len = b.Length;
+ var off = 0;
+ var size = 0;
+
+ // reduce len to ignore fill bytes
+ while ((len > 0) && (b[len - 1] == '='))
+ {
+ --len;
+ }
+
+ // read & decode full byte triplets = 4 source bytes
+ while (len > 4)
+ {
+ // Decode 4 bytes at a time
+ TBase64Utils.Decode(b, off, 4, b, size); // NB: decoded in place
+ off += 4;
+ len -= 4;
+ size += 3;
+ }
+
+ // Don't decode if we hit the end or got a single leftover byte (invalid
+ // base64 but legal for skip of regular string exType)
+ if (len > 1)
+ {
+ // Decode remainder
+ TBase64Utils.Decode(b, off, len, b, size); // NB: decoded in place
+ size += len - 1;
+ }
+
+ // Sadly we must copy the byte[] (any way around this?)
+ var result = new byte[size];
+ Array.Copy(b, 0, result, 0, size);
+ return result;
+ }
+
+ private async Task ReadJsonObjectStartAsync(CancellationToken cancellationToken)
+ {
+ await Context.ReadConditionalDelimiterAsync(cancellationToken);
+ await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBrace, cancellationToken);
+ PushContext(new JSONPairContext(this));
+ }
+
+ private async Task ReadJsonObjectEndAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBrace, cancellationToken);
+ PopContext();
+ }
+
+ private async Task ReadJsonArrayStartAsync(CancellationToken cancellationToken)
+ {
+ await Context.ReadConditionalDelimiterAsync(cancellationToken);
+ await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBracket, cancellationToken);
+ PushContext(new JSONListContext(this));
+ }
+
+ private async Task ReadJsonArrayEndAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBracket, cancellationToken);
+ PopContext();
+ }
+
+ public override async ValueTask<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
+ {
+ var message = new TMessage();
+ await ReadJsonArrayStartAsync(cancellationToken);
+ if (await ReadJsonIntegerAsync(cancellationToken) != Version)
+ {
+ throw new TProtocolException(TProtocolException.BAD_VERSION, "Message contained bad version.");
+ }
+
+ var buf = await ReadJsonStringAsync(false, cancellationToken);
+ message.Name = Utf8Encoding.GetString(buf, 0, buf.Length);
+ message.Type = (TMessageType) await ReadJsonIntegerAsync(cancellationToken);
+ message.SeqID = (int) await ReadJsonIntegerAsync(cancellationToken);
+ return message;
+ }
+
+ public override async Task ReadMessageEndAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonArrayEndAsync(cancellationToken);
+ }
+
+ public override async ValueTask<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonObjectStartAsync(cancellationToken);
+ return new TStruct();
+ }
+
+ public override async Task ReadStructEndAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonObjectEndAsync(cancellationToken);
+ }
+
+ public override async ValueTask<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
+ {
+ var field = new TField();
+ var ch = await Reader.PeekAsync(cancellationToken);
+ if (ch == TJSONProtocolConstants.RightBrace[0])
+ {
+ field.Type = TType.Stop;
+ }
+ else
+ {
+ field.ID = (short) await ReadJsonIntegerAsync(cancellationToken);
+ await ReadJsonObjectStartAsync(cancellationToken);
+ field.Type = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
+ }
+ return field;
+ }
+
+ public override async Task ReadFieldEndAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonObjectEndAsync(cancellationToken);
+ }
+
+ public override async ValueTask<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
+ {
+ var map = new TMap();
+ await ReadJsonArrayStartAsync(cancellationToken);
+ map.KeyType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
+ map.ValueType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
+ map.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
+ await ReadJsonObjectStartAsync(cancellationToken);
+ return map;
+ }
+
+ public override async Task ReadMapEndAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonObjectEndAsync(cancellationToken);
+ await ReadJsonArrayEndAsync(cancellationToken);
+ }
+
+ public override async ValueTask<TList> ReadListBeginAsync(CancellationToken cancellationToken)
+ {
+ var list = new TList();
+ await ReadJsonArrayStartAsync(cancellationToken);
+ list.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
+ list.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
+ return list;
+ }
+
+ public override async Task ReadListEndAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonArrayEndAsync(cancellationToken);
+ }
+
+ public override async ValueTask<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
+ {
+ var set = new TSet();
+ await ReadJsonArrayStartAsync(cancellationToken);
+ set.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
+ set.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
+ return set;
+ }
+
+ public override async Task ReadSetEndAsync(CancellationToken cancellationToken)
+ {
+ await ReadJsonArrayEndAsync(cancellationToken);
+ }
+
+ public override async ValueTask<bool> ReadBoolAsync(CancellationToken cancellationToken)
+ {
+ return await ReadJsonIntegerAsync(cancellationToken) != 0;
+ }
+
+ public override async ValueTask<sbyte> ReadByteAsync(CancellationToken cancellationToken)
+ {
+ return (sbyte) await ReadJsonIntegerAsync(cancellationToken);
+ }
+
+ public override async ValueTask<short> ReadI16Async(CancellationToken cancellationToken)
+ {
+ return (short) await ReadJsonIntegerAsync(cancellationToken);
+ }
+
+ public override async ValueTask<int> ReadI32Async(CancellationToken cancellationToken)
+ {
+ return (int) await ReadJsonIntegerAsync(cancellationToken);
+ }
+
+ public override async ValueTask<long> ReadI64Async(CancellationToken cancellationToken)
+ {
+ return await ReadJsonIntegerAsync(cancellationToken);
+ }
+
+ public override async ValueTask<double> ReadDoubleAsync(CancellationToken cancellationToken)
+ {
+ return await ReadJsonDoubleAsync(cancellationToken);
+ }
+
+ public override async ValueTask<string> ReadStringAsync(CancellationToken cancellationToken)
+ {
+ var buf = await ReadJsonStringAsync(false, cancellationToken);
+ return Utf8Encoding.GetString(buf, 0, buf.Length);
+ }
+
+ public override async ValueTask<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
+ {
+ return await ReadJsonBase64Async(cancellationToken);
+ }
+
+ /// <summary>
+ /// Factory for JSON protocol objects
+ /// </summary>
+ public class Factory : TProtocolFactory
+ {
+ public override TProtocol GetProtocol(TTransport trans)
+ {
+ return new TJsonProtocol(trans);
+ }
+ }
+
+ /// <summary>
+ /// Base class for tracking JSON contexts that may require
+ /// inserting/Reading additional JSON syntax characters
+ /// This base context does nothing.
+ /// </summary>
+ protected class JSONBaseContext
+ {
+ protected TJsonProtocol Proto;
+
+ public JSONBaseContext(TJsonProtocol proto)
+ {
+ Proto = proto;
+ }
+
+ public virtual async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public virtual async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public virtual bool EscapeNumbers()
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Context for JSON lists. Will insert/Read commas before each item except
+ /// for the first one
+ /// </summary>
+ protected class JSONListContext : JSONBaseContext
+ {
+ private bool _first = true;
+
+ public JSONListContext(TJsonProtocol protocol)
+ : base(protocol)
+ {
+ }
+
+ public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
+ {
+ if (_first)
+ {
+ _first = false;
+ }
+ else
+ {
+ await Proto.Trans.WriteAsync(TJSONProtocolConstants.Comma, cancellationToken);
+ }
+ }
+
+ public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
+ {
+ if (_first)
+ {
+ _first = false;
+ }
+ else
+ {
+ await Proto.ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Comma, cancellationToken);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Context for JSON records. Will insert/Read colons before the value portion
+ /// of each record pair, and commas before each key except the first. In
+ /// addition, will indicate that numbers in the key position need to be
+ /// escaped in quotes (since JSON keys must be strings).
+ /// </summary>
+ // ReSharper disable once InconsistentNaming
+ protected class JSONPairContext : JSONBaseContext
+ {
+ private bool _colon = true;
+
+ private bool _first = true;
+
+ public JSONPairContext(TJsonProtocol proto)
+ : base(proto)
+ {
+ }
+
+ public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
+ {
+ if (_first)
+ {
+ _first = false;
+ _colon = true;
+ }
+ else
+ {
+ await Proto.Trans.WriteAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken);
+ _colon = !_colon;
+ }
+ }
+
+ public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
+ {
+ if (_first)
+ {
+ _first = false;
+ _colon = true;
+ }
+ else
+ {
+ await Proto.ReadJsonSyntaxCharAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken);
+ _colon = !_colon;
+ }
+ }
+
+ public override bool EscapeNumbers()
+ {
+ return _colon;
+ }
+ }
+
+ /// <summary>
+ /// Holds up to one byte from the transport
+ /// </summary>
+ protected class LookaheadReader
+ {
+ private readonly byte[] _data = new byte[1];
+
+ private bool _hasData;
+ protected TJsonProtocol Proto;
+
+ public LookaheadReader(TJsonProtocol proto)
+ {
+ Proto = proto;
+ }
+
+ /// <summary>
+ /// Return and consume the next byte to be Read, either taking it from the
+ /// data buffer if present or getting it from the transport otherwise.
+ /// </summary>
+ public async ValueTask<byte> ReadAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<byte>(cancellationToken);
+ }
+
+ if (_hasData)
+ {
+ _hasData = false;
+ }
+ else
+ {
+ // find more easy way to avoid exception on reading primitive types
+ await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken);
+ }
+ return _data[0];
+ }
+
+ /// <summary>
+ /// Return the next byte to be Read without consuming, filling the data
+ /// buffer if it has not been filled alReady.
+ /// </summary>
+ public async ValueTask<byte> PeekAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<byte>(cancellationToken);
+ }
+
+ if (!_hasData)
+ {
+ // find more easy way to avoid exception on reading primitive types
+ await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken);
+ _hasData = true;
+ }
+ return _data[0];
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TMultiplexedProtocol.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TMultiplexedProtocol.cs
new file mode 100644
index 000000000..fbc8c05cc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TMultiplexedProtocol.cs
@@ -0,0 +1,91 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocol.Entities;
+
+namespace Thrift.Protocol
+{
+ /**
+ * TMultiplexedProtocol is a protocol-independent concrete decorator that allows a Thrift
+ * client to communicate with a multiplexing Thrift server, by prepending the service name
+ * to the function name during function calls.
+ *
+ * NOTE: THIS IS NOT TO BE USED BY SERVERS.
+ * On the server, use TMultiplexedProcessor to handle requests from a multiplexing client.
+ *
+ * This example uses a single socket transport to invoke two services:
+ *
+ * TSocketTransport transport = new TSocketTransport("localhost", 9090);
+ * transport.open();
+ *
+ * TBinaryProtocol protocol = new TBinaryProtocol(transport);
+ *
+ * TMultiplexedProtocol mp = new TMultiplexedProtocol(protocol, "Calculator");
+ * Calculator.Client service = new Calculator.Client(mp);
+ *
+ * TMultiplexedProtocol mp2 = new TMultiplexedProtocol(protocol, "WeatherReport");
+ * WeatherReport.Client service2 = new WeatherReport.Client(mp2);
+ *
+ * System.out.println(service.add(2,2));
+ * System.out.println(service2.getTemperature());
+ *
+ */
+
+ //TODO: implementation of TProtocol
+
+ // ReSharper disable once InconsistentNaming
+ public class TMultiplexedProtocol : TProtocolDecorator
+ {
+ /** Used to delimit the service name from the function name */
+ public const string Separator = ":";
+
+ private readonly string _serviceName;
+
+ /**
+ * Wrap the specified protocol, allowing it to be used to communicate with a
+ * multiplexing server. The <code>serviceName</code> is required as it is
+ * prepended to the message header so that the multiplexing server can broker
+ * the function call to the proper service.
+ *
+ * Args:
+ * protocol Your communication protocol of choice, e.g. TBinaryProtocol
+ * serviceName The service name of the service communicating via this protocol.
+ */
+
+ public TMultiplexedProtocol(TProtocol protocol, string serviceName)
+ : base(protocol)
+ {
+ _serviceName = serviceName;
+ }
+
+ public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
+ {
+ switch (message.Type)
+ {
+ case TMessageType.Call:
+ case TMessageType.Oneway:
+ await base.WriteMessageBeginAsync(new TMessage($"{_serviceName}{Separator}{message.Name}", message.Type, message.SeqID), cancellationToken);
+ break;
+ default:
+ await base.WriteMessageBeginAsync(message, cancellationToken);
+ break;
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TProtocol.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TProtocol.cs
new file mode 100644
index 000000000..75edb11d1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TProtocol.cs
@@ -0,0 +1,376 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocol.Entities;
+using Thrift.Transport;
+
+namespace Thrift.Protocol
+{
+ // ReSharper disable once InconsistentNaming
+ public abstract class TProtocol : IDisposable
+ {
+ public const int DefaultRecursionDepth = 64;
+ private bool _isDisposed;
+ protected int RecursionDepth;
+
+ protected TTransport Trans;
+
+ protected TProtocol(TTransport trans)
+ {
+ Trans = trans;
+ RecursionLimit = DefaultRecursionDepth;
+ RecursionDepth = 0;
+ }
+
+ public TTransport Transport => Trans;
+
+ protected int RecursionLimit { get; set; }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ public void IncrementRecursionDepth()
+ {
+ if (RecursionDepth < RecursionLimit)
+ {
+ ++RecursionDepth;
+ }
+ else
+ {
+ throw new TProtocolException(TProtocolException.DEPTH_LIMIT, "Depth limit exceeded");
+ }
+ }
+
+ public void DecrementRecursionDepth()
+ {
+ --RecursionDepth;
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ (Trans as IDisposable)?.Dispose();
+ }
+ }
+ _isDisposed = true;
+ }
+
+ public virtual async Task WriteMessageBeginAsync(TMessage message)
+ {
+ await WriteMessageBeginAsync(message, CancellationToken.None);
+ }
+
+ public abstract Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken);
+
+ public virtual async Task WriteMessageEndAsync()
+ {
+ await WriteMessageEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task WriteMessageEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task WriteStructBeginAsync(TStruct @struct)
+ {
+ await WriteStructBeginAsync(@struct, CancellationToken.None);
+ }
+
+ public abstract Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken);
+
+ public virtual async Task WriteStructEndAsync()
+ {
+ await WriteStructEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task WriteStructEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task WriteFieldBeginAsync(TField field)
+ {
+ await WriteFieldBeginAsync(field, CancellationToken.None);
+ }
+
+ public abstract Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken);
+
+ public virtual async Task WriteFieldEndAsync()
+ {
+ await WriteFieldEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task WriteFieldEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task WriteFieldStopAsync()
+ {
+ await WriteFieldStopAsync(CancellationToken.None);
+ }
+
+ public abstract Task WriteFieldStopAsync(CancellationToken cancellationToken);
+
+ public virtual async Task WriteMapBeginAsync(TMap map)
+ {
+ await WriteMapBeginAsync(map, CancellationToken.None);
+ }
+
+ public abstract Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken);
+
+ public virtual async Task WriteMapEndAsync()
+ {
+ await WriteMapEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task WriteMapEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task WriteListBeginAsync(TList list)
+ {
+ await WriteListBeginAsync(list, CancellationToken.None);
+ }
+
+ public abstract Task WriteListBeginAsync(TList list, CancellationToken cancellationToken);
+
+ public virtual async Task WriteListEndAsync()
+ {
+ await WriteListEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task WriteListEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task WriteSetBeginAsync(TSet set)
+ {
+ await WriteSetBeginAsync(set, CancellationToken.None);
+ }
+
+ public abstract Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken);
+
+ public virtual async Task WriteSetEndAsync()
+ {
+ await WriteSetEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task WriteSetEndAsync(CancellationToken cancellationToken);
+
+ public virtual async Task WriteBoolAsync(bool b)
+ {
+ await WriteBoolAsync(b, CancellationToken.None);
+ }
+
+ public abstract Task WriteBoolAsync(bool b, CancellationToken cancellationToken);
+
+ public virtual async Task WriteByteAsync(sbyte b)
+ {
+ await WriteByteAsync(b, CancellationToken.None);
+ }
+
+ public abstract Task WriteByteAsync(sbyte b, CancellationToken cancellationToken);
+
+ public virtual async Task WriteI16Async(short i16)
+ {
+ await WriteI16Async(i16, CancellationToken.None);
+ }
+
+ public abstract Task WriteI16Async(short i16, CancellationToken cancellationToken);
+
+ public virtual async Task WriteI32Async(int i32)
+ {
+ await WriteI32Async(i32, CancellationToken.None);
+ }
+
+ public abstract Task WriteI32Async(int i32, CancellationToken cancellationToken);
+
+ public virtual async Task WriteI64Async(long i64)
+ {
+ await WriteI64Async(i64, CancellationToken.None);
+ }
+
+ public abstract Task WriteI64Async(long i64, CancellationToken cancellationToken);
+
+ public virtual async Task WriteDoubleAsync(double d)
+ {
+ await WriteDoubleAsync(d, CancellationToken.None);
+ }
+
+ public abstract Task WriteDoubleAsync(double d, CancellationToken cancellationToken);
+
+ public virtual async Task WriteStringAsync(string s)
+ {
+ await WriteStringAsync(s, CancellationToken.None);
+ }
+
+ public virtual async Task WriteStringAsync(string s, CancellationToken cancellationToken)
+ {
+ var bytes = Encoding.UTF8.GetBytes(s);
+ await WriteBinaryAsync(bytes, cancellationToken);
+ }
+
+ public virtual async Task WriteBinaryAsync(byte[] bytes)
+ {
+ await WriteBinaryAsync(bytes, CancellationToken.None);
+ }
+
+ public abstract Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken);
+
+ public virtual async ValueTask<TMessage> ReadMessageBeginAsync()
+ {
+ return await ReadMessageBeginAsync(CancellationToken.None);
+ }
+
+ public abstract ValueTask<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken);
+
+ public virtual async Task ReadMessageEndAsync()
+ {
+ await ReadMessageEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task ReadMessageEndAsync(CancellationToken cancellationToken);
+
+ public virtual async ValueTask<TStruct> ReadStructBeginAsync()
+ {
+ return await ReadStructBeginAsync(CancellationToken.None);
+ }
+
+ public abstract ValueTask<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken);
+
+ public virtual async Task ReadStructEndAsync()
+ {
+ await ReadStructEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task ReadStructEndAsync(CancellationToken cancellationToken);
+
+ public virtual async ValueTask<TField> ReadFieldBeginAsync()
+ {
+ return await ReadFieldBeginAsync(CancellationToken.None);
+ }
+
+ public abstract ValueTask<TField> ReadFieldBeginAsync(CancellationToken cancellationToken);
+
+ public virtual async Task ReadFieldEndAsync()
+ {
+ await ReadFieldEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task ReadFieldEndAsync(CancellationToken cancellationToken);
+
+ public virtual async ValueTask<TMap> ReadMapBeginAsync()
+ {
+ return await ReadMapBeginAsync(CancellationToken.None);
+ }
+
+ public abstract ValueTask<TMap> ReadMapBeginAsync(CancellationToken cancellationToken);
+
+ public virtual async Task ReadMapEndAsync()
+ {
+ await ReadMapEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task ReadMapEndAsync(CancellationToken cancellationToken);
+
+ public virtual async ValueTask<TList> ReadListBeginAsync()
+ {
+ return await ReadListBeginAsync(CancellationToken.None);
+ }
+
+ public abstract ValueTask<TList> ReadListBeginAsync(CancellationToken cancellationToken);
+
+ public virtual async Task ReadListEndAsync()
+ {
+ await ReadListEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task ReadListEndAsync(CancellationToken cancellationToken);
+
+ public virtual async ValueTask<TSet> ReadSetBeginAsync()
+ {
+ return await ReadSetBeginAsync(CancellationToken.None);
+ }
+
+ public abstract ValueTask<TSet> ReadSetBeginAsync(CancellationToken cancellationToken);
+
+ public virtual async Task ReadSetEndAsync()
+ {
+ await ReadSetEndAsync(CancellationToken.None);
+ }
+
+ public abstract Task ReadSetEndAsync(CancellationToken cancellationToken);
+
+ public virtual async ValueTask<bool> ReadBoolAsync()
+ {
+ return await ReadBoolAsync(CancellationToken.None);
+ }
+
+ public abstract ValueTask<bool> ReadBoolAsync(CancellationToken cancellationToken);
+
+ public virtual async ValueTask<sbyte> ReadByteAsync()
+ {
+ return await ReadByteAsync(CancellationToken.None);
+ }
+
+ public abstract ValueTask<sbyte> ReadByteAsync(CancellationToken cancellationToken);
+
+ public virtual async ValueTask<short> ReadI16Async()
+ {
+ return await ReadI16Async(CancellationToken.None);
+ }
+
+ public abstract ValueTask<short> ReadI16Async(CancellationToken cancellationToken);
+
+ public virtual async ValueTask<int> ReadI32Async()
+ {
+ return await ReadI32Async(CancellationToken.None);
+ }
+
+ public abstract ValueTask<int> ReadI32Async(CancellationToken cancellationToken);
+
+ public virtual async ValueTask<long> ReadI64Async()
+ {
+ return await ReadI64Async(CancellationToken.None);
+ }
+
+ public abstract ValueTask<long> ReadI64Async(CancellationToken cancellationToken);
+
+ public virtual async ValueTask<double> ReadDoubleAsync()
+ {
+ return await ReadDoubleAsync(CancellationToken.None);
+ }
+
+ public abstract ValueTask<double> ReadDoubleAsync(CancellationToken cancellationToken);
+
+ public virtual async ValueTask<string> ReadStringAsync()
+ {
+ return await ReadStringAsync(CancellationToken.None);
+ }
+
+ public virtual async ValueTask<string> ReadStringAsync(CancellationToken cancellationToken)
+ {
+ var buf = await ReadBinaryAsync(cancellationToken);
+ return Encoding.UTF8.GetString(buf, 0, buf.Length);
+ }
+
+ public virtual async ValueTask<byte[]> ReadBinaryAsync()
+ {
+ return await ReadBinaryAsync(CancellationToken.None);
+ }
+
+ public abstract ValueTask<byte[]> ReadBinaryAsync(CancellationToken cancellationToken);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TProtocolDecorator.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TProtocolDecorator.cs
new file mode 100644
index 000000000..845c82749
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TProtocolDecorator.cs
@@ -0,0 +1,247 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocol.Entities;
+
+namespace Thrift.Protocol
+{
+ // ReSharper disable once InconsistentNaming
+ /// <summary>
+ /// TProtocolDecorator forwards all requests to an enclosed TProtocol instance,
+ /// providing a way to author concise concrete decorator subclasses.While it has
+ /// no abstract methods, it is marked abstract as a reminder that by itself,
+ /// it does not modify the behaviour of the enclosed TProtocol.
+ /// </summary>
+ public abstract class TProtocolDecorator : TProtocol
+ {
+ private readonly TProtocol _wrappedProtocol;
+
+ protected TProtocolDecorator(TProtocol protocol)
+ : base(protocol.Transport)
+ {
+ _wrappedProtocol = protocol ?? throw new ArgumentNullException(nameof(protocol));
+ }
+
+ public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteMessageBeginAsync(message, cancellationToken);
+ }
+
+ public override async Task WriteMessageEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteMessageEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteStructBeginAsync(@struct, cancellationToken);
+ }
+
+ public override async Task WriteStructEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteStructEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteFieldBeginAsync(field, cancellationToken);
+ }
+
+ public override async Task WriteFieldEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteFieldEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteFieldStopAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteFieldStopAsync(cancellationToken);
+ }
+
+ public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteMapBeginAsync(map, cancellationToken);
+ }
+
+ public override async Task WriteMapEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteMapEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteListBeginAsync(list, cancellationToken);
+ }
+
+ public override async Task WriteListEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteListEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteSetBeginAsync(set, cancellationToken);
+ }
+
+ public override async Task WriteSetEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteSetEndAsync(cancellationToken);
+ }
+
+ public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteBoolAsync(b, cancellationToken);
+ }
+
+ public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteByteAsync(b, cancellationToken);
+ }
+
+ public override async Task WriteI16Async(short i16, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteI16Async(i16, cancellationToken);
+ }
+
+ public override async Task WriteI32Async(int i32, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteI32Async(i32, cancellationToken);
+ }
+
+ public override async Task WriteI64Async(long i64, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteI64Async(i64, cancellationToken);
+ }
+
+ public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteDoubleAsync(d, cancellationToken);
+ }
+
+ public override async Task WriteStringAsync(string s, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteStringAsync(s, cancellationToken);
+ }
+
+ public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.WriteBinaryAsync(bytes, cancellationToken);
+ }
+
+ public override async ValueTask<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadMessageBeginAsync(cancellationToken);
+ }
+
+ public override async Task ReadMessageEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.ReadMessageEndAsync(cancellationToken);
+ }
+
+ public override async ValueTask<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadStructBeginAsync(cancellationToken);
+ }
+
+ public override async Task ReadStructEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.ReadStructEndAsync(cancellationToken);
+ }
+
+ public override async ValueTask<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadFieldBeginAsync(cancellationToken);
+ }
+
+ public override async Task ReadFieldEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.ReadFieldEndAsync(cancellationToken);
+ }
+
+ public override async ValueTask<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadMapBeginAsync(cancellationToken);
+ }
+
+ public override async Task ReadMapEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.ReadMapEndAsync(cancellationToken);
+ }
+
+ public override async ValueTask<TList> ReadListBeginAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadListBeginAsync(cancellationToken);
+ }
+
+ public override async Task ReadListEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.ReadListEndAsync(cancellationToken);
+ }
+
+ public override async ValueTask<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadSetBeginAsync(cancellationToken);
+ }
+
+ public override async Task ReadSetEndAsync(CancellationToken cancellationToken)
+ {
+ await _wrappedProtocol.ReadSetEndAsync(cancellationToken);
+ }
+
+ public override async ValueTask<bool> ReadBoolAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadBoolAsync(cancellationToken);
+ }
+
+ public override async ValueTask<sbyte> ReadByteAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadByteAsync(cancellationToken);
+ }
+
+ public override async ValueTask<short> ReadI16Async(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadI16Async(cancellationToken);
+ }
+
+ public override async ValueTask<int> ReadI32Async(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadI32Async(cancellationToken);
+ }
+
+ public override async ValueTask<long> ReadI64Async(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadI64Async(cancellationToken);
+ }
+
+ public override async ValueTask<double> ReadDoubleAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadDoubleAsync(cancellationToken);
+ }
+
+ public override async ValueTask<string> ReadStringAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadStringAsync(cancellationToken);
+ }
+
+ public override async ValueTask<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
+ {
+ return await _wrappedProtocol.ReadBinaryAsync(cancellationToken);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TProtocolException.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TProtocolException.cs
new file mode 100644
index 000000000..328babd05
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TProtocolException.cs
@@ -0,0 +1,62 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// ReSharper disable InconsistentNaming
+using System;
+
+namespace Thrift.Protocol
+{
+ public class TProtocolException : TException
+ {
+ // do not rename public constants - they used in generated files
+ public const int UNKNOWN = 0;
+ public const int INVALID_DATA = 1;
+ public const int NEGATIVE_SIZE = 2;
+ public const int SIZE_LIMIT = 3;
+ public const int BAD_VERSION = 4;
+ public const int NOT_IMPLEMENTED = 5;
+ public const int DEPTH_LIMIT = 6;
+
+ protected int Type = UNKNOWN;
+
+ public TProtocolException()
+ {
+ }
+
+ public TProtocolException(int type, Exception inner = null)
+ : base(string.Empty, inner)
+ {
+ Type = type;
+ }
+
+ public TProtocolException(int type, string message, Exception inner = null)
+ : base(message, inner)
+ {
+ Type = type;
+ }
+
+ public TProtocolException(string message, Exception inner = null)
+ : base(message, inner)
+ {
+ }
+
+ public int GetExceptionType()
+ {
+ return Type;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TProtocolFactory.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TProtocolFactory.cs
new file mode 100644
index 000000000..31b05148b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TProtocolFactory.cs
@@ -0,0 +1,27 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using Thrift.Transport;
+
+namespace Thrift.Protocol
+{
+ // ReSharper disable once InconsistentNaming
+ public abstract class TProtocolFactory
+ {
+ public abstract TProtocol GetProtocol(TTransport trans);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Utilities/TBase64Utils.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Utilities/TBase64Utils.cs
new file mode 100644
index 000000000..90b8f8867
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Utilities/TBase64Utils.cs
@@ -0,0 +1,101 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+
+namespace Thrift.Protocol.Utilities
+{
+ // ReSharper disable once InconsistentNaming
+ internal static class TBase64Utils
+ {
+ //TODO: Constants
+ //TODO: Check for args
+ //TODO: Unitests
+
+ internal const string EncodeTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ private static readonly int[] DecodeTable =
+ {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ };
+
+ internal static void Encode(byte[] src, int srcOff, int len, byte[] dst, int dstOff)
+ {
+ if (src == null)
+ {
+ throw new ArgumentNullException(nameof(src));
+ }
+
+ dst[dstOff] = (byte) EncodeTable[(src[srcOff] >> 2) & 0x3F];
+
+ if (len == 3)
+ {
+ dst[dstOff + 1] = (byte) EncodeTable[((src[srcOff] << 4) & 0x30) | ((src[srcOff + 1] >> 4) & 0x0F)];
+ dst[dstOff + 2] = (byte) EncodeTable[((src[srcOff + 1] << 2) & 0x3C) | ((src[srcOff + 2] >> 6) & 0x03)];
+ dst[dstOff + 3] = (byte) EncodeTable[src[srcOff + 2] & 0x3F];
+ }
+ else if (len == 2)
+ {
+ dst[dstOff + 1] = (byte) EncodeTable[((src[srcOff] << 4) & 0x30) | ((src[srcOff + 1] >> 4) & 0x0F)];
+ dst[dstOff + 2] = (byte) EncodeTable[(src[srcOff + 1] << 2) & 0x3C];
+ }
+ else
+ {
+ // len == 1
+ dst[dstOff + 1] = (byte) EncodeTable[(src[srcOff] << 4) & 0x30];
+ }
+ }
+
+ internal static void Decode(byte[] src, int srcOff, int len, byte[] dst, int dstOff)
+ {
+ if (src == null)
+ {
+ throw new ArgumentNullException(nameof(src));
+ }
+
+ dst[dstOff] = (byte) ((DecodeTable[src[srcOff] & 0x0FF] << 2) | (DecodeTable[src[srcOff + 1] & 0x0FF] >> 4));
+
+ if (len > 2)
+ {
+ dst[dstOff + 1] =
+ (byte)
+ (((DecodeTable[src[srcOff + 1] & 0x0FF] << 4) & 0xF0) | (DecodeTable[src[srcOff + 2] & 0x0FF] >> 2));
+ if (len > 3)
+ {
+ dst[dstOff + 2] =
+ (byte)
+ (((DecodeTable[src[srcOff + 2] & 0x0FF] << 6) & 0xC0) | DecodeTable[src[srcOff + 3] & 0x0FF]);
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Utilities/TJsonProtocolConstants.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Utilities/TJsonProtocolConstants.cs
new file mode 100644
index 000000000..6cc1302e9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Utilities/TJsonProtocolConstants.cs
@@ -0,0 +1,61 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Protocol.Utilities
+{
+ // ReSharper disable once InconsistentNaming
+ public static class TJSONProtocolConstants
+ {
+ //TODO Check for performance for reusing ImmutableArray from System.Collections.Immutable (https://blogs.msdn.microsoft.com/dotnet/2013/06/24/please-welcome-immutablearrayt/)
+ // can be possible to get better performance and also better GC
+
+ public static readonly byte[] Comma = {(byte) ','};
+ public static readonly byte[] Colon = {(byte) ':'};
+ public static readonly byte[] LeftBrace = {(byte) '{'};
+ public static readonly byte[] RightBrace = {(byte) '}'};
+ public static readonly byte[] LeftBracket = {(byte) '['};
+ public static readonly byte[] RightBracket = {(byte) ']'};
+ public static readonly byte[] Quote = {(byte) '"'};
+ public static readonly byte[] Backslash = {(byte) '\\'};
+
+ public static readonly byte[] JsonCharTable =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, (byte) 'b', (byte) 't', (byte) 'n', 0, (byte) 'f', (byte) 'r', 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, (byte) '"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ };
+
+ public static readonly char[] EscapeChars = "\"\\/bfnrt".ToCharArray();
+ public static readonly byte[] EscapeCharValues = {(byte) '"', (byte) '\\', (byte) '/', (byte) '\b', (byte) '\f', (byte) '\n', (byte) '\r', (byte) '\t'};
+ public static readonly byte[] EscSequences = {(byte) '\\', (byte) 'u', (byte) '0', (byte) '0'};
+
+ public static class TypeNames
+ {
+ public static readonly byte[] NameBool = { (byte)'t', (byte)'f' };
+ public static readonly byte[] NameByte = { (byte)'i', (byte)'8' };
+ public static readonly byte[] NameI16 = { (byte)'i', (byte)'1', (byte)'6' };
+ public static readonly byte[] NameI32 = { (byte)'i', (byte)'3', (byte)'2' };
+ public static readonly byte[] NameI64 = { (byte)'i', (byte)'6', (byte)'4' };
+ public static readonly byte[] NameDouble = { (byte)'d', (byte)'b', (byte)'l' };
+ public static readonly byte[] NameStruct = { (byte)'r', (byte)'e', (byte)'c' };
+ public static readonly byte[] NameString = { (byte)'s', (byte)'t', (byte)'r' };
+ public static readonly byte[] NameMap = { (byte)'m', (byte)'a', (byte)'p' };
+ public static readonly byte[] NameList = { (byte)'l', (byte)'s', (byte)'t' };
+ public static readonly byte[] NameSet = { (byte)'s', (byte)'e', (byte)'t' };
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Utilities/TJsonProtocolHelper.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Utilities/TJsonProtocolHelper.cs
new file mode 100644
index 000000000..ff49ebe24
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Utilities/TJsonProtocolHelper.cs
@@ -0,0 +1,176 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using Thrift.Protocol.Entities;
+
+namespace Thrift.Protocol.Utilities
+{
+ // ReSharper disable once InconsistentNaming
+ public static class TJSONProtocolHelper
+ {
+ public static byte[] GetTypeNameForTypeId(TType typeId)
+ {
+ switch (typeId)
+ {
+ case TType.Bool:
+ return TJSONProtocolConstants.TypeNames.NameBool;
+ case TType.Byte:
+ return TJSONProtocolConstants.TypeNames.NameByte;
+ case TType.I16:
+ return TJSONProtocolConstants.TypeNames.NameI16;
+ case TType.I32:
+ return TJSONProtocolConstants.TypeNames.NameI32;
+ case TType.I64:
+ return TJSONProtocolConstants.TypeNames.NameI64;
+ case TType.Double:
+ return TJSONProtocolConstants.TypeNames.NameDouble;
+ case TType.String:
+ return TJSONProtocolConstants.TypeNames.NameString;
+ case TType.Struct:
+ return TJSONProtocolConstants.TypeNames.NameStruct;
+ case TType.Map:
+ return TJSONProtocolConstants.TypeNames.NameMap;
+ case TType.Set:
+ return TJSONProtocolConstants.TypeNames.NameSet;
+ case TType.List:
+ return TJSONProtocolConstants.TypeNames.NameList;
+ default:
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "Unrecognized exType");
+ }
+ }
+
+ public static TType GetTypeIdForTypeName(byte[] name)
+ {
+ var result = TType.Stop;
+ if (name.Length > 1)
+ {
+ switch (name[0])
+ {
+ case (byte) 'd':
+ result = TType.Double;
+ break;
+ case (byte) 'i':
+ switch (name[1])
+ {
+ case (byte) '8':
+ result = TType.Byte;
+ break;
+ case (byte) '1':
+ result = TType.I16;
+ break;
+ case (byte) '3':
+ result = TType.I32;
+ break;
+ case (byte) '6':
+ result = TType.I64;
+ break;
+ }
+ break;
+ case (byte) 'l':
+ result = TType.List;
+ break;
+ case (byte) 'm':
+ result = TType.Map;
+ break;
+ case (byte) 'r':
+ result = TType.Struct;
+ break;
+ case (byte) 's':
+ if (name[1] == (byte) 't')
+ {
+ result = TType.String;
+ }
+ else if (name[1] == (byte) 'e')
+ {
+ result = TType.Set;
+ }
+ break;
+ case (byte) 't':
+ result = TType.Bool;
+ break;
+ }
+ }
+ if (result == TType.Stop)
+ {
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "Unrecognized exType");
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Return true if the given byte could be a valid part of a JSON number.
+ /// </summary>
+ public static bool IsJsonNumeric(byte b)
+ {
+ switch (b)
+ {
+ case (byte)'+':
+ case (byte)'-':
+ case (byte)'.':
+ case (byte)'0':
+ case (byte)'1':
+ case (byte)'2':
+ case (byte)'3':
+ case (byte)'4':
+ case (byte)'5':
+ case (byte)'6':
+ case (byte)'7':
+ case (byte)'8':
+ case (byte)'9':
+ case (byte)'E':
+ case (byte)'e':
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its
+ /// corresponding hex value
+ /// </summary>
+ public static byte ToHexVal(byte ch)
+ {
+ if (ch >= '0' && ch <= '9')
+ {
+ return (byte)((char)ch - '0');
+ }
+
+ if (ch >= 'a' && ch <= 'f')
+ {
+ ch += 10;
+ return (byte)((char)ch - 'a');
+ }
+
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected hex character");
+ }
+
+ /// <summary>
+ /// Convert a byte containing a hex value to its corresponding hex character
+ /// </summary>
+ public static byte ToHexChar(byte val)
+ {
+ val &= 0x0F;
+ if (val < 10)
+ {
+ return (byte)((char)val + '0');
+ }
+ val -= 10;
+ return (byte)((char)val + 'a');
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Utilities/TProtocolUtil.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Utilities/TProtocolUtil.cs
new file mode 100644
index 000000000..18f92d816
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/Utilities/TProtocolUtil.cs
@@ -0,0 +1,110 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocol.Entities;
+
+namespace Thrift.Protocol.Utilities
+{
+ // ReSharper disable once InconsistentNaming
+ public static class TProtocolUtil
+ {
+ public static async Task SkipAsync(TProtocol protocol, TType type, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+
+ protocol.IncrementRecursionDepth();
+ try
+ {
+ switch (type)
+ {
+ case TType.Bool:
+ await protocol.ReadBoolAsync(cancellationToken);
+ break;
+ case TType.Byte:
+ await protocol.ReadByteAsync(cancellationToken);
+ break;
+ case TType.I16:
+ await protocol.ReadI16Async(cancellationToken);
+ break;
+ case TType.I32:
+ await protocol.ReadI32Async(cancellationToken);
+ break;
+ case TType.I64:
+ await protocol.ReadI64Async(cancellationToken);
+ break;
+ case TType.Double:
+ await protocol.ReadDoubleAsync(cancellationToken);
+ break;
+ case TType.String:
+ // Don't try to decode the string, just skip it.
+ await protocol.ReadBinaryAsync(cancellationToken);
+ break;
+ case TType.Struct:
+ await protocol.ReadStructBeginAsync(cancellationToken);
+ while (true)
+ {
+ var field = await protocol.ReadFieldBeginAsync(cancellationToken);
+ if (field.Type == TType.Stop)
+ {
+ break;
+ }
+ await SkipAsync(protocol, field.Type, cancellationToken);
+ await protocol.ReadFieldEndAsync(cancellationToken);
+ }
+ await protocol.ReadStructEndAsync(cancellationToken);
+ break;
+ case TType.Map:
+ var map = await protocol.ReadMapBeginAsync(cancellationToken);
+ for (var i = 0; i < map.Count; i++)
+ {
+ await SkipAsync(protocol, map.KeyType, cancellationToken);
+ await SkipAsync(protocol, map.ValueType, cancellationToken);
+ }
+ await protocol.ReadMapEndAsync(cancellationToken);
+ break;
+ case TType.Set:
+ var set = await protocol.ReadSetBeginAsync(cancellationToken);
+ for (var i = 0; i < set.Count; i++)
+ {
+ await SkipAsync(protocol, set.ElementType, cancellationToken);
+ }
+ await protocol.ReadSetEndAsync(cancellationToken);
+ break;
+ case TType.List:
+ var list = await protocol.ReadListBeginAsync(cancellationToken);
+ for (var i = 0; i < list.Count; i++)
+ {
+ await SkipAsync(protocol, list.ElementType, cancellationToken);
+ }
+ await protocol.ReadListEndAsync(cancellationToken);
+ break;
+ default:
+ throw new TProtocolException(TProtocolException.INVALID_DATA, "Unknown data type " + type.ToString("d"));
+ }
+ }
+ finally
+ {
+ protocol.DecrementRecursionDepth();
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Server/TServer.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Server/TServer.cs
new file mode 100644
index 000000000..f40f2b7e7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Server/TServer.cs
@@ -0,0 +1,87 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Thrift.Protocol;
+using Thrift.Transport;
+using Thrift.Processor;
+
+namespace Thrift.Server
+{
+ // ReSharper disable once InconsistentNaming
+ public abstract class TServer
+ {
+ protected readonly ILogger Logger;
+ protected TProtocolFactory InputProtocolFactory;
+ protected TTransportFactory InputTransportFactory;
+ protected ITProcessorFactory ProcessorFactory;
+ protected TProtocolFactory OutputProtocolFactory;
+ protected TTransportFactory OutputTransportFactory;
+
+ protected TServerEventHandler ServerEventHandler;
+ protected TServerTransport ServerTransport;
+
+ protected TServer(ITProcessorFactory processorFactory, TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory, TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory, TProtocolFactory outputProtocolFactory,
+ ILogger logger = null)
+ {
+ ProcessorFactory = processorFactory ?? throw new ArgumentNullException(nameof(processorFactory));
+ ServerTransport = serverTransport;
+ InputTransportFactory = inputTransportFactory ?? new TTransportFactory();
+ OutputTransportFactory = outputTransportFactory ?? new TTransportFactory();
+ InputProtocolFactory = inputProtocolFactory ?? throw new ArgumentNullException(nameof(inputProtocolFactory));
+ OutputProtocolFactory = outputProtocolFactory ?? throw new ArgumentNullException(nameof(outputProtocolFactory));
+ Logger = logger; // null is absolutely legal
+ }
+
+ public void SetEventHandler(TServerEventHandler seh)
+ {
+ ServerEventHandler = seh;
+ }
+
+ public TServerEventHandler GetEventHandler()
+ {
+ return ServerEventHandler;
+ }
+
+ // Log delegation? deprecated, use ILogger
+ protected void LogError( string msg)
+ {
+ if (Logger != null)
+ Logger.LogError(msg);
+ }
+
+ public abstract void Stop();
+
+ public virtual void Start()
+ {
+ // do nothing
+ }
+
+ public virtual async Task ServeAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Server/TServerEventHandler.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Server/TServerEventHandler.cs
new file mode 100644
index 000000000..69314efd6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Server/TServerEventHandler.cs
@@ -0,0 +1,54 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocol;
+using Thrift.Transport;
+
+namespace Thrift.Server
+{
+ //TODO: replacement by event?
+
+ /// <summary>
+ /// Interface implemented by server users to handle events from the server
+ /// </summary>
+ // ReSharper disable once InconsistentNaming
+ public interface TServerEventHandler
+ {
+ /// <summary>
+ /// Called before the server begins */
+ /// </summary>
+ Task PreServeAsync(CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Called when a new client has connected and is about to being processing */
+ /// </summary>
+ Task<object> CreateContextAsync(TProtocol input, TProtocol output, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Called when a client has finished request-handling to delete server context */
+ /// </summary>
+ Task DeleteContextAsync(object serverContext, TProtocol input, TProtocol output,
+ CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Called when a client is about to call the processor */
+ /// </summary>
+ Task ProcessContextAsync(object serverContext, TTransport transport, CancellationToken cancellationToken);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Server/TSimpleAsyncServer.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Server/TSimpleAsyncServer.cs
new file mode 100644
index 000000000..bdaa3489c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Server/TSimpleAsyncServer.cs
@@ -0,0 +1,230 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Thrift.Protocol;
+using Thrift.Processor;
+using Thrift.Transport;
+
+namespace Thrift.Server
+{
+ //TODO: unhandled exceptions, etc.
+
+ // ReSharper disable once InconsistentNaming
+ public class TSimpleAsyncServer : TServer
+ {
+ private readonly int _clientWaitingDelay;
+ private volatile Task _serverTask;
+
+ public TSimpleAsyncServer(ITProcessorFactory itProcessorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ ILogger logger,
+ int clientWaitingDelay = 10)
+ : base(itProcessorFactory,
+ serverTransport,
+ inputTransportFactory,
+ outputTransportFactory,
+ inputProtocolFactory,
+ outputProtocolFactory,
+ logger)
+ {
+ _clientWaitingDelay = clientWaitingDelay;
+ }
+
+ public TSimpleAsyncServer(ITProcessorFactory itProcessorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ ILoggerFactory loggerFactory,
+ int clientWaitingDelay = 10)
+ : this(itProcessorFactory,
+ serverTransport,
+ inputTransportFactory,
+ outputTransportFactory,
+ inputProtocolFactory,
+ outputProtocolFactory,
+ loggerFactory.CreateLogger<TSimpleAsyncServer>())
+ {
+ }
+
+ public TSimpleAsyncServer(ITAsyncProcessor processor,
+ TServerTransport serverTransport,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ ILoggerFactory loggerFactory,
+ int clientWaitingDelay = 10)
+ : this(new TSingletonProcessorFactory(processor),
+ serverTransport,
+ null, // defaults to TTransportFactory()
+ null, // defaults to TTransportFactory()
+ inputProtocolFactory,
+ outputProtocolFactory,
+ loggerFactory.CreateLogger(nameof(TSimpleAsyncServer)),
+ clientWaitingDelay)
+ {
+ }
+
+ public override async Task ServeAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ // cancelation token
+ _serverTask = Task.Factory.StartNew(() => StartListening(cancellationToken), TaskCreationOptions.LongRunning);
+ await _serverTask;
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError(ex.ToString());
+ }
+ }
+
+ private async Task StartListening(CancellationToken cancellationToken)
+ {
+ ServerTransport.Listen();
+
+ Logger.LogTrace("Started listening at server");
+
+ if (ServerEventHandler != null)
+ {
+ await ServerEventHandler.PreServeAsync(cancellationToken);
+ }
+
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ if (ServerTransport.IsClientPending())
+ {
+ Logger.LogTrace("Waiting for client connection");
+
+ try
+ {
+ var client = await ServerTransport.AcceptAsync(cancellationToken);
+ await Task.Factory.StartNew(() => Execute(client, cancellationToken), cancellationToken);
+ }
+ catch (TTransportException ttx)
+ {
+ Logger.LogTrace($"Transport exception: {ttx}");
+
+ if (ttx.Type != TTransportException.ExceptionType.Interrupted)
+ {
+ Logger.LogError(ttx.ToString());
+ }
+ }
+ }
+ else
+ {
+ try
+ {
+ await Task.Delay(TimeSpan.FromMilliseconds(_clientWaitingDelay), cancellationToken);
+ }
+ catch (TaskCanceledException) { }
+ }
+ }
+
+ ServerTransport.Close();
+
+ Logger.LogTrace("Completed listening at server");
+ }
+
+ public override void Stop()
+ {
+ }
+
+ private async Task Execute(TTransport client, CancellationToken cancellationToken)
+ {
+ Logger.LogTrace("Started client request processing");
+
+ var processor = ProcessorFactory.GetAsyncProcessor(client, this);
+
+ TTransport inputTransport = null;
+ TTransport outputTransport = null;
+ TProtocol inputProtocol = null;
+ TProtocol outputProtocol = null;
+ object connectionContext = null;
+
+ try
+ {
+ try
+ {
+ inputTransport = InputTransportFactory.GetTransport(client);
+ outputTransport = OutputTransportFactory.GetTransport(client);
+ inputProtocol = InputProtocolFactory.GetProtocol(inputTransport);
+ outputProtocol = OutputProtocolFactory.GetProtocol(outputTransport);
+
+ if (ServerEventHandler != null)
+ {
+ connectionContext = await ServerEventHandler.CreateContextAsync(inputProtocol, outputProtocol, cancellationToken);
+ }
+
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ if (!await inputTransport.PeekAsync(cancellationToken))
+ {
+ break;
+ }
+
+ if (ServerEventHandler != null)
+ {
+ await ServerEventHandler.ProcessContextAsync(connectionContext, inputTransport, cancellationToken);
+ }
+
+ if (!await processor.ProcessAsync(inputProtocol, outputProtocol, cancellationToken))
+ {
+ break;
+ }
+ }
+ }
+ catch (TTransportException ttx)
+ {
+ Logger.LogTrace($"Transport exception: {ttx}");
+ }
+ catch (Exception x)
+ {
+ Logger.LogError($"Error: {x}");
+ }
+
+ if (ServerEventHandler != null)
+ {
+ await ServerEventHandler.DeleteContextAsync(connectionContext, inputProtocol, outputProtocol, cancellationToken);
+ }
+
+ }
+ finally
+ {
+ //Close transports
+ inputTransport?.Close();
+ outputTransport?.Close();
+
+ // disposable stuff should be disposed
+ inputProtocol?.Dispose();
+ outputProtocol?.Dispose();
+ inputTransport?.Dispose();
+ outputTransport?.Dispose();
+ }
+
+ Logger.LogTrace("Completed client request processing");
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Server/TThreadPoolAsyncServer.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Server/TThreadPoolAsyncServer.cs
new file mode 100644
index 000000000..20e659d3a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Server/TThreadPoolAsyncServer.cs
@@ -0,0 +1,297 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Threading;
+using Thrift.Protocol;
+using Thrift.Transport;
+using Thrift.Processor;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+
+namespace Thrift.Server
+{
+ /// <summary>
+ /// Server that uses C# built-in ThreadPool to spawn threads when handling requests.
+ /// </summary>
+ public class TThreadPoolAsyncServer : TServer
+ {
+ private const int DEFAULT_MIN_THREADS = -1; // use .NET ThreadPool defaults
+ private const int DEFAULT_MAX_THREADS = -1; // use .NET ThreadPool defaults
+ private volatile bool stop = false;
+
+ private CancellationToken ServerCancellationToken;
+
+ public struct Configuration
+ {
+ public int MinWorkerThreads;
+ public int MaxWorkerThreads;
+ public int MinIOThreads;
+ public int MaxIOThreads;
+
+ public Configuration(int min = DEFAULT_MIN_THREADS, int max = DEFAULT_MAX_THREADS)
+ {
+ MinWorkerThreads = min;
+ MaxWorkerThreads = max;
+ MinIOThreads = min;
+ MaxIOThreads = max;
+ }
+
+ public Configuration(int minWork, int maxWork, int minIO, int maxIO)
+ {
+ MinWorkerThreads = minWork;
+ MaxWorkerThreads = maxWork;
+ MinIOThreads = minIO;
+ MaxIOThreads = maxIO;
+ }
+ }
+
+ public TThreadPoolAsyncServer(ITAsyncProcessor processor, TServerTransport serverTransport, ILogger logger = null)
+ : this(new TSingletonProcessorFactory(processor), serverTransport,
+ null, null, // defaults to TTransportFactory()
+ new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(),
+ new Configuration(), logger)
+ {
+ }
+
+ public TThreadPoolAsyncServer(ITAsyncProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory)
+ : this(new TSingletonProcessorFactory(processor), serverTransport,
+ transportFactory, transportFactory,
+ protocolFactory, protocolFactory,
+ new Configuration())
+ {
+ }
+
+ public TThreadPoolAsyncServer(ITProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory)
+ : this(processorFactory, serverTransport,
+ transportFactory, transportFactory,
+ protocolFactory, protocolFactory,
+ new Configuration())
+ {
+ }
+
+ public TThreadPoolAsyncServer(ITProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ int minThreadPoolThreads, int maxThreadPoolThreads, ILogger logger= null)
+ : this(processorFactory, serverTransport, inputTransportFactory, outputTransportFactory,
+ inputProtocolFactory, outputProtocolFactory,
+ new Configuration(minThreadPoolThreads, maxThreadPoolThreads),
+ logger)
+ {
+ }
+
+ public TThreadPoolAsyncServer(ITProcessorFactory processorFactory,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ Configuration threadConfig,
+ ILogger logger = null)
+ : base(processorFactory, serverTransport, inputTransportFactory, outputTransportFactory,
+ inputProtocolFactory, outputProtocolFactory, logger)
+ {
+ lock (typeof(TThreadPoolAsyncServer))
+ {
+ if ((threadConfig.MaxWorkerThreads > 0) || (threadConfig.MaxIOThreads > 0))
+ {
+ int work, comm;
+ ThreadPool.GetMaxThreads(out work, out comm);
+ if (threadConfig.MaxWorkerThreads > 0)
+ work = threadConfig.MaxWorkerThreads;
+ if (threadConfig.MaxIOThreads > 0)
+ comm = threadConfig.MaxIOThreads;
+ if (!ThreadPool.SetMaxThreads(work, comm))
+ throw new Exception("Error: could not SetMaxThreads in ThreadPool");
+ }
+
+ if ((threadConfig.MinWorkerThreads > 0) || (threadConfig.MinIOThreads > 0))
+ {
+ int work, comm;
+ ThreadPool.GetMinThreads(out work, out comm);
+ if (threadConfig.MinWorkerThreads > 0)
+ work = threadConfig.MinWorkerThreads;
+ if (threadConfig.MinIOThreads > 0)
+ comm = threadConfig.MinIOThreads;
+ if (!ThreadPool.SetMinThreads(work, comm))
+ throw new Exception("Error: could not SetMinThreads in ThreadPool");
+ }
+ }
+ }
+
+
+ /// <summary>
+ /// Use new ThreadPool thread for each new client connection.
+ /// </summary>
+ public override async Task ServeAsync(CancellationToken cancellationToken)
+ {
+ ServerCancellationToken = cancellationToken;
+ try
+ {
+ try
+ {
+ ServerTransport.Listen();
+ }
+ catch (TTransportException ttx)
+ {
+ LogError("Error, could not listen on ServerTransport: " + ttx);
+ return;
+ }
+
+ //Fire the preServe server event when server is up but before any client connections
+ if (ServerEventHandler != null)
+ await ServerEventHandler.PreServeAsync(cancellationToken);
+
+ while (!stop)
+ {
+ int failureCount = 0;
+ try
+ {
+ TTransport client = await ServerTransport.AcceptAsync(cancellationToken);
+ ThreadPool.QueueUserWorkItem(this.Execute, client);
+ }
+ catch (TTransportException ttx)
+ {
+ if (!stop || ttx.Type != TTransportException.ExceptionType.Interrupted)
+ {
+ ++failureCount;
+ LogError(ttx.ToString());
+ }
+
+ }
+ }
+
+ if (stop)
+ {
+ try
+ {
+ ServerTransport.Close();
+ }
+ catch (TTransportException ttx)
+ {
+ LogError("TServerTransport failed on close: " + ttx.Message);
+ }
+ stop = false;
+ }
+
+ }
+ finally
+ {
+ ServerCancellationToken = default(CancellationToken);
+ }
+ }
+
+ /// <summary>
+ /// Loops on processing a client forever
+ /// threadContext will be a TTransport instance
+ /// </summary>
+ /// <param name="threadContext"></param>
+ private void Execute(object threadContext)
+ {
+ var cancellationToken = ServerCancellationToken;
+
+ using (TTransport client = (TTransport)threadContext)
+ {
+ ITAsyncProcessor processor = ProcessorFactory.GetAsyncProcessor(client, this);
+ TTransport inputTransport = null;
+ TTransport outputTransport = null;
+ TProtocol inputProtocol = null;
+ TProtocol outputProtocol = null;
+ object connectionContext = null;
+ try
+ {
+ try
+ {
+ inputTransport = InputTransportFactory.GetTransport(client);
+ outputTransport = OutputTransportFactory.GetTransport(client);
+ inputProtocol = InputProtocolFactory.GetProtocol(inputTransport);
+ outputProtocol = OutputProtocolFactory.GetProtocol(outputTransport);
+
+ //Recover event handler (if any) and fire createContext server event when a client connects
+ if (ServerEventHandler != null)
+ connectionContext = ServerEventHandler.CreateContextAsync(inputProtocol, outputProtocol, cancellationToken).Result;
+
+ //Process client requests until client disconnects
+ while (!stop)
+ {
+ if (! inputTransport.PeekAsync(cancellationToken).Result)
+ break;
+
+ //Fire processContext server event
+ //N.B. This is the pattern implemented in C++ and the event fires provisionally.
+ //That is to say it may be many minutes between the event firing and the client request
+ //actually arriving or the client may hang up without ever makeing a request.
+ if (ServerEventHandler != null)
+ ServerEventHandler.ProcessContextAsync(connectionContext, inputTransport, cancellationToken).Wait();
+ //Process client request (blocks until transport is readable)
+ if (!processor.ProcessAsync(inputProtocol, outputProtocol, cancellationToken).Result)
+ break;
+ }
+ }
+ catch (TTransportException)
+ {
+ //Usually a client disconnect, expected
+ }
+ catch (Exception x)
+ {
+ //Unexpected
+ LogError("Error: " + x);
+ }
+
+ //Fire deleteContext server event after client disconnects
+ if (ServerEventHandler != null)
+ ServerEventHandler.DeleteContextAsync(connectionContext, inputProtocol, outputProtocol, cancellationToken).Wait();
+
+ }
+ finally
+ {
+ //Close transports
+ inputTransport?.Close();
+ outputTransport?.Close();
+
+ // disposable stuff should be disposed
+ inputProtocol?.Dispose();
+ outputProtocol?.Dispose();
+ inputTransport?.Dispose();
+ outputTransport?.Dispose();
+ }
+ }
+ }
+
+ public override void Stop()
+ {
+ stop = true;
+ ServerTransport?.Close();
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/TApplicationException.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/TApplicationException.cs
new file mode 100644
index 000000000..67ac2f8c7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/TApplicationException.cs
@@ -0,0 +1,150 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocol;
+using Thrift.Protocol.Entities;
+using Thrift.Protocol.Utilities;
+
+namespace Thrift
+{
+ // ReSharper disable once InconsistentNaming
+ public class TApplicationException : TException
+ {
+ public enum ExceptionType
+ {
+ Unknown,
+ UnknownMethod,
+ InvalidMessageType,
+ WrongMethodName,
+ BadSequenceId,
+ MissingResult,
+ InternalError,
+ ProtocolError,
+ InvalidTransform,
+ InvalidProtocol,
+ UnsupportedClientType
+ }
+
+ private const int MessageTypeFieldId = 1;
+ private const int ExTypeFieldId = 2;
+
+ public ExceptionType Type { get; private set; }
+
+ public TApplicationException()
+ {
+ }
+
+ public TApplicationException(ExceptionType type)
+ {
+ Type = type;
+ }
+
+ public TApplicationException(ExceptionType type, string message)
+ : base(message, null) // TApplicationException is serializable, but we never serialize InnerException
+ {
+ Type = type;
+ }
+
+ public static async ValueTask<TApplicationException> ReadAsync(TProtocol inputProtocol, CancellationToken cancellationToken)
+ {
+ string message = null;
+ var type = ExceptionType.Unknown;
+
+ await inputProtocol.ReadStructBeginAsync(cancellationToken);
+ while (true)
+ {
+ var field = await inputProtocol.ReadFieldBeginAsync(cancellationToken);
+ if (field.Type == TType.Stop)
+ {
+ break;
+ }
+
+ switch (field.ID)
+ {
+ case MessageTypeFieldId:
+ if (field.Type == TType.String)
+ {
+ message = await inputProtocol.ReadStringAsync(cancellationToken);
+ }
+ else
+ {
+ await TProtocolUtil.SkipAsync(inputProtocol, field.Type, cancellationToken);
+ }
+ break;
+ case ExTypeFieldId:
+ if (field.Type == TType.I32)
+ {
+ type = (ExceptionType) await inputProtocol.ReadI32Async(cancellationToken);
+ }
+ else
+ {
+ await TProtocolUtil.SkipAsync(inputProtocol, field.Type, cancellationToken);
+ }
+ break;
+ default:
+ await TProtocolUtil.SkipAsync(inputProtocol, field.Type, cancellationToken);
+ break;
+ }
+
+ await inputProtocol.ReadFieldEndAsync(cancellationToken);
+ }
+
+ await inputProtocol.ReadStructEndAsync(cancellationToken);
+
+ return new TApplicationException(type, message);
+ }
+
+ public async Task WriteAsync(TProtocol outputProtocol, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+
+ const string messageTypeFieldName = "message";
+ const string exTypeFieldName = "exType";
+ const string structApplicationExceptionName = "TApplicationException";
+
+ var struc = new TStruct(structApplicationExceptionName);
+ var field = new TField();
+
+ await outputProtocol.WriteStructBeginAsync(struc, cancellationToken);
+
+ if (!string.IsNullOrEmpty(Message))
+ {
+ field.Name = messageTypeFieldName;
+ field.Type = TType.String;
+ field.ID = MessageTypeFieldId;
+ await outputProtocol.WriteFieldBeginAsync(field, cancellationToken);
+ await outputProtocol.WriteStringAsync(Message, cancellationToken);
+ await outputProtocol.WriteFieldEndAsync(cancellationToken);
+ }
+
+ field.Name = exTypeFieldName;
+ field.Type = TType.I32;
+ field.ID = ExTypeFieldId;
+
+ await outputProtocol.WriteFieldBeginAsync(field, cancellationToken);
+ await outputProtocol.WriteI32Async((int) Type, cancellationToken);
+ await outputProtocol.WriteFieldEndAsync(cancellationToken);
+ await outputProtocol.WriteFieldStopAsync(cancellationToken);
+ await outputProtocol.WriteStructEndAsync(cancellationToken);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/TBaseClient.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/TBaseClient.cs
new file mode 100644
index 000000000..0edac0f08
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/TBaseClient.cs
@@ -0,0 +1,91 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Protocol;
+
+namespace Thrift
+{
+ // ReSharper disable once InconsistentNaming
+ /// <summary>
+ /// TBaseClient.
+ /// Base client for generated clients.
+ /// Do not change this class without checking generated code (namings, etc.)
+ /// </summary>
+ public abstract class TBaseClient
+ {
+ private readonly TProtocol _inputProtocol;
+ private readonly TProtocol _outputProtocol;
+ private bool _isDisposed;
+ private int _seqId;
+ public readonly Guid ClientId = Guid.NewGuid();
+
+ protected TBaseClient(TProtocol inputProtocol, TProtocol outputProtocol)
+ {
+ _inputProtocol = inputProtocol ?? throw new ArgumentNullException(nameof(inputProtocol));
+ _outputProtocol = outputProtocol ?? throw new ArgumentNullException(nameof(outputProtocol));
+ }
+
+ public TProtocol InputProtocol => _inputProtocol;
+
+ public TProtocol OutputProtocol => _outputProtocol;
+
+ public int SeqId
+ {
+ get { return ++_seqId; }
+ }
+
+ public virtual async Task OpenTransportAsync()
+ {
+ await OpenTransportAsync(CancellationToken.None);
+ }
+
+ public virtual async Task OpenTransportAsync(CancellationToken cancellationToken)
+ {
+ if (!_inputProtocol.Transport.IsOpen)
+ {
+ await _inputProtocol.Transport.OpenAsync(cancellationToken);
+ }
+
+ if (!_inputProtocol.Transport.IsOpen)
+ {
+ await _outputProtocol.Transport.OpenAsync(cancellationToken);
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ _inputProtocol?.Dispose();
+ _outputProtocol?.Dispose();
+ }
+ }
+
+ _isDisposed = true;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/TException.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/TException.cs
new file mode 100644
index 000000000..43e70549b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/TException.cs
@@ -0,0 +1,34 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+
+namespace Thrift
+{
+ // ReSharper disable once InconsistentNaming
+ public class TException : Exception
+ {
+ public TException()
+ {
+ }
+
+ public TException(string message, Exception inner)
+ : base(message, inner)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Thrift.csproj b/src/jaegertracing/thrift/lib/netstd/Thrift/Thrift.csproj
new file mode 100644
index 000000000..ceb4409c0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Thrift.csproj
@@ -0,0 +1,59 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <!--
+ Licensed to the Apache Software Foundation(ASF) under one
+ or more contributor license agreements.See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ -->
+
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <AssemblyName>Thrift</AssemblyName>
+ <PackageId>Thrift</PackageId>
+ <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+ <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
+ <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
+ <GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
+ <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
+ <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
+ <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
+ <GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
+ <GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
+ <GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <SignAssembly>true</SignAssembly>
+ <AssemblyOriginatorKeyFile>thrift.snk</AssemblyOriginatorKeyFile>
+ <DelaySign>false</DelaySign>
+ <Version>0.13.0.0</Version>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
+ <PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
+ <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.2.0" />
+ <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.2.0" />
+ <PackageReference Include="System.IO.Pipes" Version="[4.3,)" />
+ <PackageReference Include="System.IO.Pipes.AccessControl" Version="4.5.1" />
+ <PackageReference Include="System.Net.Http.WinHttpHandler" Version="4.5.2" />
+ <PackageReference Include="System.Net.NameResolution" Version="[4.3,)" />
+ <PackageReference Include="System.Net.Requests" Version="[4.3,)" />
+ <PackageReference Include="System.Net.Security" Version="4.3.2" />
+ <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.2" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/THttpTransport.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/THttpTransport.cs
new file mode 100644
index 000000000..c84df83ae
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/THttpTransport.cs
@@ -0,0 +1,222 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transport.Client
+{
+ // ReSharper disable once InconsistentNaming
+ public class THttpTransport : TTransport
+ {
+ private readonly X509Certificate[] _certificates;
+ private readonly Uri _uri;
+
+ private int _connectTimeout = 30000; // Timeouts in milliseconds
+ private HttpClient _httpClient;
+ private Stream _inputStream;
+ private MemoryStream _outputStream = new MemoryStream();
+ private bool _isDisposed;
+
+ public THttpTransport(Uri uri, IDictionary<string, string> customRequestHeaders = null, string userAgent = null)
+ : this(uri, Enumerable.Empty<X509Certificate>(), customRequestHeaders, userAgent)
+ {
+ }
+
+ public THttpTransport(Uri uri, IEnumerable<X509Certificate> certificates,
+ IDictionary<string, string> customRequestHeaders, string userAgent = null)
+ {
+ _uri = uri;
+ _certificates = (certificates ?? Enumerable.Empty<X509Certificate>()).ToArray();
+
+ if (!string.IsNullOrEmpty(userAgent))
+ UserAgent = userAgent;
+
+ // due to current bug with performance of Dispose in netcore https://github.com/dotnet/corefx/issues/8809
+ // this can be switched to default way (create client->use->dispose per flush) later
+ _httpClient = CreateClient(customRequestHeaders);
+ }
+
+ // According to RFC 2616 section 3.8, the "User-Agent" header may not carry a version number
+ public readonly string UserAgent = "Thrift netstd THttpClient";
+
+ public override bool IsOpen => true;
+
+ public HttpRequestHeaders RequestHeaders => _httpClient.DefaultRequestHeaders;
+
+ public MediaTypeHeaderValue ContentType { get; set; }
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override void Close()
+ {
+ if (_inputStream != null)
+ {
+ _inputStream.Dispose();
+ _inputStream = null;
+ }
+
+ if (_outputStream != null)
+ {
+ _outputStream.Dispose();
+ _outputStream = null;
+ }
+
+ if (_httpClient != null)
+ {
+ _httpClient.Dispose();
+ _httpClient = null;
+ }
+ }
+
+ public override async ValueTask<int> ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<int>(cancellationToken);
+ }
+
+ if (_inputStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No request has been sent");
+ }
+
+ try
+ {
+ var ret = await _inputStream.ReadAsync(buffer, offset, length, cancellationToken);
+
+ if (ret == -1)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.EndOfFile, "No more data available");
+ }
+
+ return ret;
+ }
+ catch (IOException iox)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString());
+ }
+ }
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+
+ await _outputStream.WriteAsync(buffer, offset, length, cancellationToken);
+ }
+
+ private HttpClient CreateClient(IDictionary<string, string> customRequestHeaders)
+ {
+ var handler = new HttpClientHandler();
+ handler.ClientCertificates.AddRange(_certificates);
+ handler.AutomaticDecompression = System.Net.DecompressionMethods.Deflate | System.Net.DecompressionMethods.GZip;
+
+ var httpClient = new HttpClient(handler);
+
+ if (_connectTimeout > 0)
+ {
+ httpClient.Timeout = TimeSpan.FromMilliseconds(_connectTimeout);
+ }
+
+ httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-thrift"));
+ httpClient.DefaultRequestHeaders.UserAgent.TryParseAdd(UserAgent);
+
+ httpClient.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate"));
+ httpClient.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
+
+ if (customRequestHeaders != null)
+ {
+ foreach (var item in customRequestHeaders)
+ {
+ httpClient.DefaultRequestHeaders.Add(item.Key, item.Value);
+ }
+ }
+
+ return httpClient;
+ }
+
+ public override async Task FlushAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ _outputStream.Seek(0, SeekOrigin.Begin);
+
+ using (var contentStream = new StreamContent(_outputStream))
+ {
+ contentStream.Headers.ContentType = ContentType ?? new MediaTypeHeaderValue(@"application/x-thrift");
+
+ var response = (await _httpClient.PostAsync(_uri, contentStream, cancellationToken)).EnsureSuccessStatusCode();
+
+ _inputStream?.Dispose();
+ _inputStream = await response.Content.ReadAsStreamAsync();
+ if (_inputStream.CanSeek)
+ {
+ _inputStream.Seek(0, SeekOrigin.Begin);
+ }
+ }
+ }
+ catch (IOException iox)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString());
+ }
+ catch (HttpRequestException wx)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown,
+ "Couldn't connect to server: " + wx);
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, ex.Message);
+ }
+ finally
+ {
+ _outputStream = new MemoryStream();
+ }
+ }
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ _inputStream?.Dispose();
+ _outputStream?.Dispose();
+ _httpClient?.Dispose();
+ }
+ }
+ _isDisposed = true;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TMemoryBufferTransport.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TMemoryBufferTransport.cs
new file mode 100644
index 000000000..25895c2b7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TMemoryBufferTransport.cs
@@ -0,0 +1,179 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transport.Client
+{
+ // ReSharper disable once InconsistentNaming
+ public class TMemoryBufferTransport : TTransport
+ {
+ private bool IsDisposed;
+ private byte[] Bytes;
+ private int _bytesUsed;
+
+ public TMemoryBufferTransport()
+ {
+ Bytes = new byte[2048]; // default size
+ }
+
+ public TMemoryBufferTransport(int initialCapacity)
+ {
+ Bytes = new byte[initialCapacity]; // default size
+ }
+
+ public TMemoryBufferTransport(byte[] buf)
+ {
+ Bytes = (byte[])buf.Clone();
+ _bytesUsed = Bytes.Length;
+ }
+
+ public int Position { get; set; }
+
+ public int Capacity
+ {
+ get
+ {
+ Debug.Assert(_bytesUsed <= Bytes.Length);
+ return Bytes.Length;
+ }
+ set
+ {
+ Array.Resize(ref Bytes, value);
+ _bytesUsed = value;
+ }
+ }
+
+ public int Length
+ {
+ get {
+ Debug.Assert(_bytesUsed <= Bytes.Length);
+ return _bytesUsed;
+ }
+ set {
+ if ((Bytes.Length < value) || (Bytes.Length > (10 * value)))
+ Array.Resize(ref Bytes, Math.Max(2048, (int)(value * 1.25)));
+ _bytesUsed = value;
+ }
+ }
+
+ public void SetLength(int value)
+ {
+ Length = value;
+ Position = Math.Min(Position, value);
+ }
+
+ public override bool IsOpen => true;
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override void Close()
+ {
+ /** do nothing **/
+ }
+
+ public void Seek(int delta, SeekOrigin origin)
+ {
+ int newPos;
+ switch (origin)
+ {
+ case SeekOrigin.Begin:
+ newPos = delta;
+ break;
+ case SeekOrigin.Current:
+ newPos = Position + delta;
+ break;
+ case SeekOrigin.End:
+ newPos = _bytesUsed + delta;
+ break;
+ default:
+ throw new ArgumentException(nameof(origin));
+ }
+
+ if ((0 > newPos) || (newPos > _bytesUsed))
+ throw new ArgumentException(nameof(origin));
+ Position = newPos;
+ }
+
+ public override ValueTask<int> ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ var count = Math.Min(Length - Position, length);
+ Buffer.BlockCopy(Bytes, Position, buffer, offset, count);
+ Position += count;
+ return new ValueTask<int>(count);
+ }
+
+ public override Task WriteAsync(byte[] buffer, CancellationToken cancellationToken)
+ {
+ return WriteAsync(buffer, 0, buffer.Length, cancellationToken);
+ }
+
+ public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ var free = Length - Position;
+ Length = Length + count - free;
+ Buffer.BlockCopy(buffer, offset, Bytes, Position, count);
+ Position += count;
+ return Task.CompletedTask;
+ }
+
+ public override async Task FlushAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public byte[] GetBuffer()
+ {
+ var retval = new byte[Length];
+ Buffer.BlockCopy(Bytes, 0, retval, 0, Length);
+ return retval;
+ }
+
+ internal bool TryGetBuffer(out ArraySegment<byte> bufSegment)
+ {
+ bufSegment = new ArraySegment<byte>(Bytes, 0, _bytesUsed);
+ return true;
+ }
+
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!IsDisposed)
+ {
+ if (disposing)
+ {
+ // nothing to do
+ }
+ }
+ IsDisposed = true;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TNamedPipeTransport.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TNamedPipeTransport.cs
new file mode 100644
index 000000000..7dfe0131e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TNamedPipeTransport.cs
@@ -0,0 +1,108 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.IO.Pipes;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transport.Client
+{
+ // ReSharper disable once InconsistentNaming
+ public class TNamedPipeTransport : TTransport
+ {
+ private NamedPipeClientStream PipeStream;
+ private int ConnectTimeout;
+
+ public TNamedPipeTransport(string pipe, int timeout = Timeout.Infinite)
+ : this(".", pipe, timeout)
+ {
+ }
+
+ public TNamedPipeTransport(string server, string pipe, int timeout = Timeout.Infinite)
+ {
+ var serverName = string.IsNullOrWhiteSpace(server) ? server : ".";
+ ConnectTimeout = (timeout > 0) ? timeout : Timeout.Infinite;
+
+ PipeStream = new NamedPipeClientStream(serverName, pipe, PipeDirection.InOut, PipeOptions.None);
+ }
+
+ public override bool IsOpen => PipeStream != null && PipeStream.IsConnected;
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ if (IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen);
+ }
+
+ await PipeStream.ConnectAsync( ConnectTimeout, cancellationToken);
+ }
+
+ public override void Close()
+ {
+ if (PipeStream != null)
+ {
+ PipeStream.Dispose();
+ PipeStream = null;
+ }
+ }
+
+ public override async ValueTask<int> ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ if (PipeStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ return await PipeStream.ReadAsync(buffer, offset, length, cancellationToken);
+ }
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ if (PipeStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ // if necessary, send the data in chunks
+ // there's a system limit around 0x10000 bytes that we hit otherwise
+ // MSDN: "Pipe write operations across a network are limited to 65,535 bytes per write. For more information regarding pipes, see the Remarks section."
+ var nBytes = Math.Min(15 * 4096, length); // 16 would exceed the limit
+ while (nBytes > 0)
+ {
+ await PipeStream.WriteAsync(buffer, offset, nBytes, cancellationToken);
+ offset += nBytes;
+ length -= nBytes;
+ nBytes = Math.Min(nBytes, length);
+ }
+ }
+
+ public override async Task FlushAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ PipeStream.Dispose();
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TSocketTransport.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TSocketTransport.cs
new file mode 100644
index 000000000..00da04581
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TSocketTransport.cs
@@ -0,0 +1,162 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transport.Client
+{
+ // ReSharper disable once InconsistentNaming
+ public class TSocketTransport : TStreamTransport
+ {
+ private bool _isDisposed;
+
+
+ public TSocketTransport(TcpClient client)
+ {
+ TcpClient = client ?? throw new ArgumentNullException(nameof(client));
+ SetInputOutputStream();
+ }
+
+ public TSocketTransport(IPAddress host, int port)
+ : this(host, port, 0)
+ {
+ }
+
+ public TSocketTransport(IPAddress host, int port, int timeout)
+ {
+ Host = host;
+ Port = port;
+
+ TcpClient = new TcpClient();
+ TcpClient.ReceiveTimeout = TcpClient.SendTimeout = timeout;
+ TcpClient.Client.NoDelay = true;
+ SetInputOutputStream();
+ }
+
+ public TSocketTransport(string host, int port, int timeout = 0)
+ {
+ try
+ {
+ var entry = Dns.GetHostEntry(host);
+ if (entry.AddressList.Length == 0)
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, "unable to resolve host name");
+
+ var addr = entry.AddressList[0];
+ Host = new IPAddress(addr.GetAddressBytes(), addr.ScopeId);
+ Port = port;
+
+ TcpClient = new TcpClient(host, port);
+ TcpClient.ReceiveTimeout = TcpClient.SendTimeout = timeout;
+ TcpClient.Client.NoDelay = true;
+ SetInputOutputStream();
+ }
+ catch (SocketException e)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, e.Message, e);
+ }
+ }
+
+ private void SetInputOutputStream()
+ {
+ if (IsOpen)
+ {
+ InputStream = TcpClient.GetStream();
+ OutputStream = TcpClient.GetStream();
+ }
+ }
+
+ public TcpClient TcpClient { get; private set; }
+ public IPAddress Host { get; }
+ public int Port { get; }
+
+ public int Timeout
+ {
+ set
+ {
+ if (TcpClient != null)
+ {
+ TcpClient.ReceiveTimeout = TcpClient.SendTimeout = value;
+ }
+ }
+ }
+
+ public override bool IsOpen
+ {
+ get
+ {
+ return (TcpClient != null) && TcpClient.Connected;
+ }
+ }
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+
+ if (IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen, "Socket already connected");
+ }
+
+ if (Port <= 0)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open without port");
+ }
+
+ if (TcpClient == null)
+ {
+ throw new InvalidOperationException("Invalid or not initialized tcp client");
+ }
+
+ await TcpClient.ConnectAsync(Host, Port);
+ SetInputOutputStream();
+ }
+
+ public override void Close()
+ {
+ base.Close();
+
+ if (TcpClient != null)
+ {
+ TcpClient.Dispose();
+ TcpClient = null;
+ }
+ }
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ TcpClient?.Dispose();
+
+ base.Dispose(disposing);
+ }
+ }
+ _isDisposed = true;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TStreamTransport.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TStreamTransport.cs
new file mode 100644
index 000000000..d8574d610
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TStreamTransport.cs
@@ -0,0 +1,109 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transport.Client
+{
+ // ReSharper disable once InconsistentNaming
+ public class TStreamTransport : TTransport
+ {
+ private bool _isDisposed;
+
+ protected TStreamTransport()
+ {
+ }
+
+ public TStreamTransport(Stream inputStream, Stream outputStream)
+ {
+ InputStream = inputStream;
+ OutputStream = outputStream;
+ }
+
+ protected Stream OutputStream { get; set; }
+
+ protected Stream InputStream { get; set; }
+
+ public override bool IsOpen => true;
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override void Close()
+ {
+ if (InputStream != null)
+ {
+ InputStream.Dispose();
+ InputStream = null;
+ }
+
+ if (OutputStream != null)
+ {
+ OutputStream.Dispose();
+ OutputStream = null;
+ }
+ }
+
+ public override async ValueTask<int> ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ if (InputStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen,
+ "Cannot read from null inputstream");
+ }
+
+ return await InputStream.ReadAsync(buffer, offset, length, cancellationToken);
+ }
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ if (OutputStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen,
+ "Cannot write to null outputstream");
+ }
+
+ await OutputStream.WriteAsync(buffer, offset, length, cancellationToken);
+ }
+
+ public override async Task FlushAsync(CancellationToken cancellationToken)
+ {
+ await OutputStream.FlushAsync(cancellationToken);
+ }
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ InputStream?.Dispose();
+ OutputStream?.Dispose();
+ }
+ }
+ _isDisposed = true;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TTlsSocketTransport.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TTlsSocketTransport.cs
new file mode 100644
index 000000000..9295bb01b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Client/TTlsSocketTransport.cs
@@ -0,0 +1,267 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Net;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transport.Client
+{
+ //TODO: check for correct work
+
+ // ReSharper disable once InconsistentNaming
+ public class TTlsSocketTransport : TStreamTransport
+ {
+ private readonly X509Certificate2 _certificate;
+ private readonly RemoteCertificateValidationCallback _certValidator;
+ private readonly IPAddress _host;
+ private readonly bool _isServer;
+ private readonly LocalCertificateSelectionCallback _localCertificateSelectionCallback;
+ private readonly int _port;
+ private readonly SslProtocols _sslProtocols;
+ private TcpClient _client;
+ private SslStream _secureStream;
+ private int _timeout;
+
+ public TTlsSocketTransport(TcpClient client, X509Certificate2 certificate, bool isServer = false,
+ RemoteCertificateValidationCallback certValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls12)
+ {
+ _client = client;
+ _certificate = certificate;
+ _certValidator = certValidator;
+ _localCertificateSelectionCallback = localCertificateSelectionCallback;
+ _sslProtocols = sslProtocols;
+ _isServer = isServer;
+
+ if (isServer && certificate == null)
+ {
+ throw new ArgumentException("TTlsSocketTransport needs certificate to be used for server",
+ nameof(certificate));
+ }
+
+ if (IsOpen)
+ {
+ InputStream = client.GetStream();
+ OutputStream = client.GetStream();
+ }
+ }
+
+ public TTlsSocketTransport(IPAddress host, int port, string certificatePath,
+ RemoteCertificateValidationCallback certValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls12)
+ : this(host, port, 0,
+ new X509Certificate2(certificatePath),
+ certValidator,
+ localCertificateSelectionCallback,
+ sslProtocols)
+ {
+ }
+
+ public TTlsSocketTransport(IPAddress host, int port,
+ X509Certificate2 certificate = null,
+ RemoteCertificateValidationCallback certValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls12)
+ : this(host, port, 0,
+ certificate,
+ certValidator,
+ localCertificateSelectionCallback,
+ sslProtocols)
+ {
+ }
+
+ public TTlsSocketTransport(IPAddress host, int port, int timeout,
+ X509Certificate2 certificate,
+ RemoteCertificateValidationCallback certValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls12)
+ {
+ _host = host;
+ _port = port;
+ _timeout = timeout;
+ _certificate = certificate;
+ _certValidator = certValidator;
+ _localCertificateSelectionCallback = localCertificateSelectionCallback;
+ _sslProtocols = sslProtocols;
+
+ InitSocket();
+ }
+
+ public TTlsSocketTransport(string host, int port, int timeout,
+ X509Certificate2 certificate,
+ RemoteCertificateValidationCallback certValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls12)
+ {
+ try
+ {
+ var entry = Dns.GetHostEntry(host);
+ if (entry.AddressList.Length == 0)
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, "unable to resolve host name");
+
+ var addr = entry.AddressList[0];
+
+ _host = new IPAddress(addr.GetAddressBytes(), addr.ScopeId);
+ _port = port;
+ _timeout = timeout;
+ _certificate = certificate;
+ _certValidator = certValidator;
+ _localCertificateSelectionCallback = localCertificateSelectionCallback;
+ _sslProtocols = sslProtocols;
+
+ InitSocket();
+ }
+ catch (SocketException e)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, e.Message, e);
+ }
+ }
+
+ public int Timeout
+ {
+ set { _client.ReceiveTimeout = _client.SendTimeout = _timeout = value; }
+ }
+
+ public TcpClient TcpClient => _client;
+
+ public IPAddress Host => _host;
+
+ public int Port => _port;
+
+ public override bool IsOpen
+ {
+ get
+ {
+ if (_client == null)
+ {
+ return false;
+ }
+
+ return _client.Connected;
+ }
+ }
+
+ private void InitSocket()
+ {
+ _client = new TcpClient();
+ _client.ReceiveTimeout = _client.SendTimeout = _timeout;
+ _client.Client.NoDelay = true;
+ }
+
+ private bool DefaultCertificateValidator(object sender, X509Certificate certificate, X509Chain chain,
+ SslPolicyErrors sslValidationErrors)
+ {
+ return sslValidationErrors == SslPolicyErrors.None;
+ }
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ if (IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen, "Socket already connected");
+ }
+
+ if (_host == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open null host");
+ }
+
+ if (_port <= 0)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open without port");
+ }
+
+ if (_client == null)
+ {
+ InitSocket();
+ }
+
+ if (_client != null)
+ {
+ await _client.ConnectAsync(_host, _port);
+ await SetupTlsAsync();
+ }
+ }
+
+ public async Task SetupTlsAsync()
+ {
+ var validator = _certValidator ?? DefaultCertificateValidator;
+
+ if (_localCertificateSelectionCallback != null)
+ {
+ _secureStream = new SslStream(_client.GetStream(), false, validator, _localCertificateSelectionCallback);
+ }
+ else
+ {
+ _secureStream = new SslStream(_client.GetStream(), false, validator);
+ }
+
+ try
+ {
+ if (_isServer)
+ {
+ // Server authentication
+ await
+ _secureStream.AuthenticateAsServerAsync(_certificate, _certValidator != null, _sslProtocols,
+ true);
+ }
+ else
+ {
+ // Client authentication
+ var certs = _certificate != null
+ ? new X509CertificateCollection {_certificate}
+ : new X509CertificateCollection();
+
+ var targetHost = _host.ToString();
+ await _secureStream.AuthenticateAsClientAsync(targetHost, certs, _sslProtocols, true);
+ }
+ }
+ catch (Exception)
+ {
+ Close();
+ throw;
+ }
+
+ InputStream = _secureStream;
+ OutputStream = _secureStream;
+ }
+
+ public override void Close()
+ {
+ base.Close();
+ if (_client != null)
+ {
+ _client.Dispose();
+ _client = null;
+ }
+
+ if (_secureStream != null)
+ {
+ _secureStream.Dispose();
+ _secureStream = null;
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/NullLogger.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/NullLogger.cs
new file mode 100644
index 000000000..1f1f542d5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/NullLogger.cs
@@ -0,0 +1,56 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using Microsoft.Extensions.Logging;
+using System;
+
+
+namespace Thrift.Transport.Server
+{
+ // sometimes we just don't want to log anything
+ internal class NullLogger<T> : IDisposable, ILogger, ILogger<T>
+ {
+ internal class NullScope : IDisposable
+ {
+ public void Dispose()
+ {
+ // nothing to do
+ }
+ }
+
+ public IDisposable BeginScope<TState>(TState state)
+ {
+ return new NullScope();
+ }
+
+ public void Dispose()
+ {
+ // nothing to do
+ }
+
+ public bool IsEnabled(LogLevel logLevel)
+ {
+ return false; // no
+ }
+
+ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
+ {
+ // do nothing
+ }
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/THttpServerTransport.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/THttpServerTransport.cs
new file mode 100644
index 000000000..056300cfe
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/THttpServerTransport.cs
@@ -0,0 +1,118 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Thrift.Processor;
+using Thrift.Protocol;
+using Thrift.Transport.Client;
+
+namespace Thrift.Transport.Server
+{
+ // ReSharper disable once InconsistentNaming
+ public class THttpServerTransport
+ {
+ protected const string ContentType = "application/x-thrift";
+ private readonly ILogger _logger;
+ private readonly RequestDelegate _next;
+ protected Encoding Encoding = Encoding.UTF8;
+
+ protected TProtocolFactory InputProtocolFactory;
+ protected TProtocolFactory OutputProtocolFactory;
+
+ protected TTransportFactory InputTransportFactory;
+ protected TTransportFactory OutputTransportFactory;
+
+ protected ITAsyncProcessor Processor;
+
+ public THttpServerTransport(ITAsyncProcessor processor, RequestDelegate next = null, ILoggerFactory loggerFactory = null)
+ : this(processor, new TBinaryProtocol.Factory(), null, next, loggerFactory)
+ {
+ }
+
+ public THttpServerTransport(
+ ITAsyncProcessor processor,
+ TProtocolFactory protocolFactory,
+ TTransportFactory transFactory = null,
+ RequestDelegate next = null,
+ ILoggerFactory loggerFactory = null)
+ : this(processor, protocolFactory, protocolFactory, transFactory, transFactory, next, loggerFactory)
+ {
+ }
+
+ public THttpServerTransport(
+ ITAsyncProcessor processor,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ TTransportFactory inputTransFactory = null,
+ TTransportFactory outputTransFactory = null,
+ RequestDelegate next = null,
+ ILoggerFactory loggerFactory = null)
+ {
+ // loggerFactory == null is not illegal anymore
+
+ Processor = processor ?? throw new ArgumentNullException(nameof(processor));
+ InputProtocolFactory = inputProtocolFactory ?? throw new ArgumentNullException(nameof(inputProtocolFactory));
+ OutputProtocolFactory = outputProtocolFactory ?? throw new ArgumentNullException(nameof(outputProtocolFactory));
+
+ InputTransportFactory = inputTransFactory;
+ OutputTransportFactory = outputTransFactory;
+
+ _next = next;
+ _logger = (loggerFactory != null) ? loggerFactory.CreateLogger<THttpServerTransport>() : new NullLogger<THttpServerTransport>();
+ }
+
+ public async Task Invoke(HttpContext context)
+ {
+ context.Response.ContentType = ContentType;
+ await ProcessRequestAsync(context, context.RequestAborted); //TODO: check for correct logic
+ }
+
+ public async Task ProcessRequestAsync(HttpContext context, CancellationToken cancellationToken)
+ {
+ var transport = new TStreamTransport(context.Request.Body, context.Response.Body);
+
+ try
+ {
+ var intrans = (InputTransportFactory != null) ? InputTransportFactory.GetTransport(transport) : transport;
+ var outtrans = (OutputTransportFactory != null) ? OutputTransportFactory.GetTransport(transport) : transport;
+
+ var input = InputProtocolFactory.GetProtocol(intrans);
+ var output = OutputProtocolFactory.GetProtocol(outtrans);
+
+ while (await Processor.ProcessAsync(input, output, cancellationToken))
+ {
+ if (!context.Response.HasStarted) // oneway method called
+ await context.Response.Body.FlushAsync(cancellationToken);
+ }
+ }
+ catch (TTransportException)
+ {
+ if (!context.Response.HasStarted) // if something goes bust, let the client know
+ context.Response.StatusCode = 500;
+ }
+ finally
+ {
+ transport.Close();
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/TNamedPipeServerTransport.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/TNamedPipeServerTransport.cs
new file mode 100644
index 000000000..77b825143
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/TNamedPipeServerTransport.cs
@@ -0,0 +1,308 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using Microsoft.Win32.SafeHandles;
+using System;
+using System.IO.Pipes;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using System.ComponentModel;
+using System.Security.AccessControl;
+using System.Security.Principal;
+
+namespace Thrift.Transport.Server
+{
+ // ReSharper disable once InconsistentNaming
+ public class TNamedPipeServerTransport : TServerTransport
+ {
+ /// <summary>
+ /// This is the address of the Pipe on the localhost.
+ /// </summary>
+ private readonly string _pipeAddress;
+ private bool _asyncMode = true;
+ private volatile bool _isPending = true;
+ private NamedPipeServerStream _stream = null;
+
+ public TNamedPipeServerTransport(string pipeAddress)
+ {
+ _pipeAddress = pipeAddress;
+ }
+
+ public override void Listen()
+ {
+ // nothing to do here
+ }
+
+ public override void Close()
+ {
+ if (_stream != null)
+ {
+ try
+ {
+ if (_stream.IsConnected)
+ _stream.Disconnect();
+ _stream.Dispose();
+ }
+ finally
+ {
+ _stream = null;
+ _isPending = false;
+ }
+ }
+ }
+
+ public override bool IsClientPending()
+ {
+ return _isPending;
+ }
+
+ private void EnsurePipeInstance()
+ {
+ if (_stream == null)
+ {
+ const PipeDirection direction = PipeDirection.InOut;
+ const int maxconn = NamedPipeServerStream.MaxAllowedServerInstances;
+ const PipeTransmissionMode mode = PipeTransmissionMode.Byte;
+ const int inbuf = 4096;
+ const int outbuf = 4096;
+ var options = _asyncMode ? PipeOptions.Asynchronous : PipeOptions.None;
+
+
+ // TODO: "CreatePipeNative" ist only a workaround, and there are have basically two possible outcomes:
+ // - once NamedPipeServerStream() gets a CTOR that supports pipesec, remove CreatePipeNative()
+ // - if 31190 gets resolved before, use _stream.SetAccessControl(pipesec) instead of CreatePipeNative()
+ // EITHER WAY,
+ // - if CreatePipeNative() finally gets removed, also remove "allow unsafe code" from the project settings
+
+ try
+ {
+ var handle = CreatePipeNative(_pipeAddress, inbuf, outbuf);
+ if( (handle != null) && (!handle.IsInvalid))
+ _stream = new NamedPipeServerStream(PipeDirection.InOut, _asyncMode, false, handle);
+ else
+ _stream = new NamedPipeServerStream(_pipeAddress, direction, maxconn, mode, options, inbuf, outbuf/*, pipesec*/);
+ }
+ catch (NotImplementedException) // Mono still does not support async, fallback to sync
+ {
+ if (_asyncMode)
+ {
+ options &= (~PipeOptions.Asynchronous);
+ _stream = new NamedPipeServerStream(_pipeAddress, direction, maxconn, mode, options, inbuf, outbuf);
+ _asyncMode = false;
+ }
+ else
+ {
+ throw;
+ }
+ }
+ }
+ }
+
+
+ #region CreatePipeNative workaround
+
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal class SECURITY_ATTRIBUTES
+ {
+ internal int nLength = 0;
+ internal IntPtr lpSecurityDescriptor = IntPtr.Zero;
+ internal int bInheritHandle = 0;
+ }
+
+
+ private const string Kernel32 = "kernel32.dll";
+
+ [DllImport(Kernel32, SetLastError = true)]
+ internal static extern IntPtr CreateNamedPipe(
+ string lpName, uint dwOpenMode, uint dwPipeMode,
+ uint nMaxInstances, uint nOutBufferSize, uint nInBufferSize, uint nDefaultTimeOut,
+ SECURITY_ATTRIBUTES pipeSecurityDescriptor
+ );
+
+
+
+ // Workaround: create the pipe via API call
+ // we have to do it this way, since NamedPipeServerStream() for netstd still lacks a few CTORs
+ // and _stream.SetAccessControl(pipesec); only keeps throwing ACCESS_DENIED errors at us
+ // References:
+ // - https://github.com/dotnet/corefx/issues/30170 (closed, continued in 31190)
+ // - https://github.com/dotnet/corefx/issues/31190 System.IO.Pipes.AccessControl package does not work
+ // - https://github.com/dotnet/corefx/issues/24040 NamedPipeServerStream: Provide support for WRITE_DAC
+ // - https://github.com/dotnet/corefx/issues/34400 Have a mechanism for lower privileged user to connect to a privileged user's pipe
+ private SafePipeHandle CreatePipeNative(string name, int inbuf, int outbuf)
+ {
+ if (Environment.OSVersion.Platform != PlatformID.Win32NT)
+ return null; // Windows only
+
+ var pinningHandle = new GCHandle();
+ try
+ {
+ // owner gets full access, everyone else read/write
+ var pipesec = new PipeSecurity();
+ using (var currentIdentity = WindowsIdentity.GetCurrent())
+ {
+ var sidOwner = currentIdentity.Owner;
+ var sidWorld = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
+
+ pipesec.SetOwner(sidOwner);
+ pipesec.AddAccessRule(new PipeAccessRule(sidOwner, PipeAccessRights.FullControl, AccessControlType.Allow));
+ pipesec.AddAccessRule(new PipeAccessRule(sidWorld, PipeAccessRights.ReadWrite, AccessControlType.Allow));
+ }
+
+ // create a security descriptor and assign it to the security attribs
+ var secAttrs = new SECURITY_ATTRIBUTES();
+ byte[] sdBytes = pipesec.GetSecurityDescriptorBinaryForm();
+ pinningHandle = GCHandle.Alloc(sdBytes, GCHandleType.Pinned);
+ unsafe {
+ fixed (byte* pSD = sdBytes) {
+ secAttrs.lpSecurityDescriptor = (IntPtr)pSD;
+ }
+ }
+
+ // a bunch of constants we will need shortly
+ const int PIPE_ACCESS_DUPLEX = 0x00000003;
+ const int FILE_FLAG_OVERLAPPED = 0x40000000;
+ const int WRITE_DAC = 0x00040000;
+ const int PIPE_TYPE_BYTE = 0x00000000;
+ const int PIPE_READMODE_BYTE = 0x00000000;
+ const int PIPE_UNLIMITED_INSTANCES = 255;
+
+ // create the pipe via API call
+ var rawHandle = CreateNamedPipe(
+ @"\\.\pipe\" + name,
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | WRITE_DAC,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
+ PIPE_UNLIMITED_INSTANCES, (uint)inbuf, (uint)outbuf,
+ 5 * 1000,
+ secAttrs
+ );
+
+ // make a SafePipeHandle() from it
+ var handle = new SafePipeHandle(rawHandle, true);
+ if (handle.IsInvalid)
+ throw new Win32Exception(Marshal.GetLastWin32Error());
+
+ // return it (to be packaged)
+ return handle;
+ }
+ finally
+ {
+ if (pinningHandle.IsAllocated)
+ pinningHandle.Free();
+ }
+ }
+
+ #endregion
+
+ protected override async ValueTask<TTransport> AcceptImplementationAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ EnsurePipeInstance();
+
+ await _stream.WaitForConnectionAsync(cancellationToken);
+
+ var trans = new ServerTransport(_stream);
+ _stream = null; // pass ownership to ServerTransport
+
+ //_isPending = false;
+
+ return trans;
+ }
+ catch (TTransportException)
+ {
+ Close();
+ throw;
+ }
+ catch (Exception e)
+ {
+ Close();
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, e.Message);
+ }
+ }
+
+ private class ServerTransport : TTransport
+ {
+ private readonly NamedPipeServerStream PipeStream;
+
+ public ServerTransport(NamedPipeServerStream stream)
+ {
+ PipeStream = stream;
+ }
+
+ public override bool IsOpen => PipeStream != null && PipeStream.IsConnected;
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ public override void Close()
+ {
+ PipeStream?.Dispose();
+ }
+
+ public override async ValueTask<int> ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ if (PipeStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ return await PipeStream.ReadAsync(buffer, offset, length, cancellationToken);
+ }
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ if (PipeStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ // if necessary, send the data in chunks
+ // there's a system limit around 0x10000 bytes that we hit otherwise
+ // MSDN: "Pipe write operations across a network are limited to 65,535 bytes per write. For more information regarding pipes, see the Remarks section."
+ var nBytes = Math.Min(15 * 4096, length); // 16 would exceed the limit
+ while (nBytes > 0)
+ {
+ await PipeStream.WriteAsync(buffer, offset, nBytes, cancellationToken);
+ offset += nBytes;
+ length -= nBytes;
+ nBytes = Math.Min(nBytes, length);
+ }
+ }
+
+ public override async Task FlushAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ await Task.FromCanceled(cancellationToken);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ PipeStream?.Dispose();
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/TServerSocketTransport.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/TServerSocketTransport.cs
new file mode 100644
index 000000000..86d82e3fc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/TServerSocketTransport.cs
@@ -0,0 +1,139 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Transport.Client;
+
+namespace Thrift.Transport.Server
+{
+
+ // ReSharper disable once InconsistentNaming
+ public class TServerSocketTransport : TServerTransport
+ {
+ private readonly int _clientTimeout;
+ private TcpListener _server;
+
+ public TServerSocketTransport(TcpListener listener, int clientTimeout = 0)
+ {
+ _server = listener;
+ _clientTimeout = clientTimeout;
+ }
+
+ public TServerSocketTransport(int port, int clientTimeout = 0)
+ : this(null, clientTimeout)
+ {
+ try
+ {
+ // Make server socket
+ _server = new TcpListener(IPAddress.Any, port);
+ _server.Server.NoDelay = true;
+ }
+ catch (Exception)
+ {
+ _server = null;
+ throw new TTransportException("Could not create ServerSocket on port " + port + ".");
+ }
+ }
+
+ public override void Listen()
+ {
+ // Make sure not to block on accept
+ if (_server != null)
+ {
+ try
+ {
+ _server.Start();
+ }
+ catch (SocketException sx)
+ {
+ throw new TTransportException("Could not accept on listening socket: " + sx.Message);
+ }
+ }
+ }
+
+ public override bool IsClientPending()
+ {
+ return _server.Pending();
+ }
+
+ protected override async ValueTask<TTransport> AcceptImplementationAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TTransport>(cancellationToken);
+ }
+
+ if (_server == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No underlying server socket.");
+ }
+
+ try
+ {
+ TTransport tSocketTransport = null;
+ var tcpClient = await _server.AcceptTcpClientAsync();
+
+ try
+ {
+ tSocketTransport = new TSocketTransport(tcpClient)
+ {
+ Timeout = _clientTimeout
+ };
+
+ return tSocketTransport;
+ }
+ catch (Exception)
+ {
+ if (tSocketTransport != null)
+ {
+ tSocketTransport.Dispose();
+ }
+ else // Otherwise, clean it up ourselves.
+ {
+ ((IDisposable) tcpClient).Dispose();
+ }
+
+ throw;
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException(ex.ToString());
+ }
+ }
+
+ public override void Close()
+ {
+ if (_server != null)
+ {
+ try
+ {
+ _server.Stop();
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException("WARNING: Could not close server socket: " + ex);
+ }
+ _server = null;
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/TTlsServerSocketTransport.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/TTlsServerSocketTransport.cs
new file mode 100644
index 000000000..128680599
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/Server/TTlsServerSocketTransport.cs
@@ -0,0 +1,150 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Net;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+using System.Threading.Tasks;
+using Thrift.Transport.Client;
+
+namespace Thrift.Transport.Server
+{
+ // ReSharper disable once InconsistentNaming
+ public class TTlsServerSocketTransport : TServerTransport
+ {
+ private readonly RemoteCertificateValidationCallback _clientCertValidator;
+ private readonly int _clientTimeout = 0;
+ private readonly LocalCertificateSelectionCallback _localCertificateSelectionCallback;
+ private readonly X509Certificate2 _serverCertificate;
+ private readonly SslProtocols _sslProtocols;
+ private TcpListener _server;
+
+ public TTlsServerSocketTransport(
+ TcpListener listener,
+ X509Certificate2 certificate,
+ RemoteCertificateValidationCallback clientCertValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls12)
+ {
+ if (!certificate.HasPrivateKey)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown,
+ "Your server-certificate needs to have a private key");
+ }
+
+ _serverCertificate = certificate;
+ _clientCertValidator = clientCertValidator;
+ _localCertificateSelectionCallback = localCertificateSelectionCallback;
+ _sslProtocols = sslProtocols;
+ _server = listener;
+ }
+
+ public TTlsServerSocketTransport(
+ int port,
+ X509Certificate2 certificate,
+ RemoteCertificateValidationCallback clientCertValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls12)
+ : this(null, certificate, clientCertValidator, localCertificateSelectionCallback)
+ {
+ try
+ {
+ // Create server socket
+ _server = new TcpListener(IPAddress.Any, port);
+ _server.Server.NoDelay = true;
+ }
+ catch (Exception)
+ {
+ _server = null;
+ throw new TTransportException($"Could not create ServerSocket on port {port}.");
+ }
+ }
+
+ public override void Listen()
+ {
+ // Make sure accept is not blocking
+ if (_server != null)
+ {
+ try
+ {
+ _server.Start();
+ }
+ catch (SocketException sx)
+ {
+ throw new TTransportException($"Could not accept on listening socket: {sx.Message}");
+ }
+ }
+ }
+
+ public override bool IsClientPending()
+ {
+ return _server.Pending();
+ }
+
+ protected override async ValueTask<TTransport> AcceptImplementationAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return await Task.FromCanceled<TTransport>(cancellationToken);
+ }
+
+ if (_server == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No underlying server socket.");
+ }
+
+ try
+ {
+ var client = await _server.AcceptTcpClientAsync();
+ client.SendTimeout = client.ReceiveTimeout = _clientTimeout;
+
+ //wrap the client in an SSL Socket passing in the SSL cert
+ var tTlsSocket = new TTlsSocketTransport(client, _serverCertificate, true, _clientCertValidator,
+ _localCertificateSelectionCallback, _sslProtocols);
+
+ await tTlsSocket.SetupTlsAsync();
+
+ return tTlsSocket;
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException(ex.ToString());
+ }
+ }
+
+ public override void Close()
+ {
+ if (_server != null)
+ {
+ try
+ {
+ _server.Stop();
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException($"WARNING: Could not close server socket: {ex}");
+ }
+
+ _server = null;
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TBufferedTransport.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TBufferedTransport.cs
new file mode 100644
index 000000000..e4fdd3a8d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TBufferedTransport.cs
@@ -0,0 +1,198 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transport
+{
+ // ReSharper disable once InconsistentNaming
+ public class TBufferedTransport : TTransport
+ {
+ private readonly int DesiredBufferSize;
+ private readonly Client.TMemoryBufferTransport ReadBuffer = new Client.TMemoryBufferTransport(1024);
+ private readonly Client.TMemoryBufferTransport WriteBuffer = new Client.TMemoryBufferTransport(1024);
+ private readonly TTransport InnerTransport;
+ private bool IsDisposed;
+
+ public class Factory : TTransportFactory
+ {
+ public override TTransport GetTransport(TTransport trans)
+ {
+ return new TBufferedTransport(trans);
+ }
+ }
+
+ //TODO: should support only specified input transport?
+ public TBufferedTransport(TTransport transport, int bufSize = 1024)
+ {
+ if (bufSize <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(bufSize), "Buffer size must be a positive number.");
+ }
+
+ InnerTransport = transport ?? throw new ArgumentNullException(nameof(transport));
+ DesiredBufferSize = bufSize;
+
+ if (DesiredBufferSize != ReadBuffer.Capacity)
+ ReadBuffer.Capacity = DesiredBufferSize;
+ if (DesiredBufferSize != WriteBuffer.Capacity)
+ WriteBuffer.Capacity = DesiredBufferSize;
+ }
+
+ public TTransport UnderlyingTransport
+ {
+ get
+ {
+ CheckNotDisposed();
+
+ return InnerTransport;
+ }
+ }
+
+ public override bool IsOpen => !IsDisposed && InnerTransport.IsOpen;
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ CheckNotDisposed();
+
+ await InnerTransport.OpenAsync(cancellationToken);
+ }
+
+ public override void Close()
+ {
+ CheckNotDisposed();
+
+ InnerTransport.Close();
+ }
+
+ public override async ValueTask<int> ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ CheckNotDisposed();
+ ValidateBufferArgs(buffer, offset, length);
+
+ if (!IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+
+ // do we have something buffered?
+ var count = ReadBuffer.Length - ReadBuffer.Position;
+ if (count > 0)
+ {
+ return await ReadBuffer.ReadAsync(buffer, offset, length, cancellationToken);
+ }
+
+ // does the request even fit into the buffer?
+ // Note we test for >= instead of > to avoid nonsense buffering
+ if (length >= ReadBuffer.Capacity)
+ {
+ return await InnerTransport.ReadAsync(buffer, offset, length, cancellationToken);
+ }
+
+ // buffer a new chunk of bytes from the underlying transport
+ ReadBuffer.Length = ReadBuffer.Capacity;
+ ArraySegment<byte> bufSegment;
+ ReadBuffer.TryGetBuffer(out bufSegment);
+ ReadBuffer.Length = await InnerTransport.ReadAsync(bufSegment.Array, 0, bufSegment.Count, cancellationToken);
+ ReadBuffer.Position = 0;
+
+ // deliver the bytes
+ return await ReadBuffer.ReadAsync(buffer, offset, length, cancellationToken);
+ }
+
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ CheckNotDisposed();
+ ValidateBufferArgs(buffer, offset, length);
+
+ if (!IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ // enough space left in buffer?
+ var free = WriteBuffer.Capacity - WriteBuffer.Length;
+ if (length > free)
+ {
+ ArraySegment<byte> bufSegment;
+ WriteBuffer.TryGetBuffer(out bufSegment);
+ await InnerTransport.WriteAsync(bufSegment.Array, 0, bufSegment.Count, cancellationToken);
+ WriteBuffer.SetLength(0);
+ }
+
+ // do the data even fit into the buffer?
+ // Note we test for < instead of <= to avoid nonsense buffering
+ if (length < WriteBuffer.Capacity)
+ {
+ await WriteBuffer.WriteAsync(buffer, offset, length, cancellationToken);
+ return;
+ }
+
+ // write thru
+ await InnerTransport.WriteAsync(buffer, offset, length, cancellationToken);
+ }
+
+ public override async Task FlushAsync(CancellationToken cancellationToken)
+ {
+ CheckNotDisposed();
+
+ if (!IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ if (WriteBuffer.Length > 0)
+ {
+ ArraySegment<byte> bufSegment;
+ WriteBuffer.TryGetBuffer(out bufSegment);
+ await InnerTransport.WriteAsync(bufSegment.Array, 0, bufSegment.Count, cancellationToken);
+ WriteBuffer.SetLength(0);
+ }
+
+ await InnerTransport.FlushAsync(cancellationToken);
+ }
+
+ private void CheckNotDisposed()
+ {
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(nameof(InnerTransport));
+ }
+ }
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!IsDisposed)
+ {
+ if (disposing)
+ {
+ ReadBuffer?.Dispose();
+ WriteBuffer?.Dispose();
+ InnerTransport?.Dispose();
+ }
+ }
+ IsDisposed = true;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TFramedTransport.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TFramedTransport.cs
new file mode 100644
index 000000000..de6df7238
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TFramedTransport.cs
@@ -0,0 +1,194 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transport
+{
+ // ReSharper disable once InconsistentNaming
+ public class TFramedTransport : TTransport
+ {
+ private const int HeaderSize = 4;
+ private readonly byte[] HeaderBuf = new byte[HeaderSize];
+ private readonly Client.TMemoryBufferTransport ReadBuffer = new Client.TMemoryBufferTransport();
+ private readonly Client.TMemoryBufferTransport WriteBuffer = new Client.TMemoryBufferTransport();
+ private readonly TTransport InnerTransport;
+
+ private bool IsDisposed;
+
+ public class Factory : TTransportFactory
+ {
+ public override TTransport GetTransport(TTransport trans)
+ {
+ return new TFramedTransport(trans);
+ }
+ }
+
+ public TFramedTransport(TTransport transport)
+ {
+ InnerTransport = transport ?? throw new ArgumentNullException(nameof(transport));
+
+ InitWriteBuffer();
+ }
+
+ public override bool IsOpen => !IsDisposed && InnerTransport.IsOpen;
+
+ public override async Task OpenAsync(CancellationToken cancellationToken)
+ {
+ CheckNotDisposed();
+
+ await InnerTransport.OpenAsync(cancellationToken);
+ }
+
+ public override void Close()
+ {
+ CheckNotDisposed();
+
+ InnerTransport.Close();
+ }
+
+ public override async ValueTask<int> ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ CheckNotDisposed();
+ ValidateBufferArgs(buffer, offset, length);
+
+ if (!IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ // Read another frame of data if we run out of bytes
+ if (ReadBuffer.Position >= ReadBuffer.Length)
+ {
+ await ReadFrameAsync(cancellationToken);
+ }
+
+ return await ReadBuffer.ReadAsync(buffer, offset, length, cancellationToken);
+ }
+
+ private async ValueTask ReadFrameAsync(CancellationToken cancellationToken)
+ {
+ await InnerTransport.ReadAllAsync(HeaderBuf, 0, HeaderSize, cancellationToken);
+ var size = DecodeFrameSize(HeaderBuf);
+
+ ReadBuffer.SetLength(size);
+ ReadBuffer.Seek(0, SeekOrigin.Begin);
+
+ ArraySegment<byte> bufSegment;
+ ReadBuffer.TryGetBuffer(out bufSegment);
+ await InnerTransport.ReadAllAsync(bufSegment.Array, 0, size, cancellationToken);
+ }
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ CheckNotDisposed();
+ ValidateBufferArgs(buffer, offset, length);
+
+ if (!IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ if (WriteBuffer.Length > (int.MaxValue - length))
+ {
+ await FlushAsync(cancellationToken);
+ }
+
+ await WriteBuffer.WriteAsync(buffer, offset, length, cancellationToken);
+ }
+
+ public override async Task FlushAsync(CancellationToken cancellationToken)
+ {
+ CheckNotDisposed();
+
+ if (!IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ ArraySegment<byte> bufSegment;
+ WriteBuffer.TryGetBuffer(out bufSegment);
+
+ int dataLen = bufSegment.Count - HeaderSize;
+ if (dataLen < 0)
+ {
+ throw new InvalidOperationException(); // logic error actually
+ }
+
+ // Inject message header into the reserved buffer space
+ EncodeFrameSize(dataLen, bufSegment.Array);
+
+ // Send the entire message at once
+ await InnerTransport.WriteAsync(bufSegment.Array, 0, bufSegment.Count, cancellationToken);
+
+ InitWriteBuffer();
+
+ await InnerTransport.FlushAsync(cancellationToken);
+ }
+
+ private void InitWriteBuffer()
+ {
+ // Reserve space for message header to be put right before sending it out
+ WriteBuffer.SetLength(HeaderSize);
+ WriteBuffer.Seek(0, SeekOrigin.End);
+ }
+
+ private static void EncodeFrameSize(int frameSize, byte[] buf)
+ {
+ buf[0] = (byte) (0xff & (frameSize >> 24));
+ buf[1] = (byte) (0xff & (frameSize >> 16));
+ buf[2] = (byte) (0xff & (frameSize >> 8));
+ buf[3] = (byte) (0xff & (frameSize));
+ }
+
+ private static int DecodeFrameSize(byte[] buf)
+ {
+ return
+ ((buf[0] & 0xff) << 24) |
+ ((buf[1] & 0xff) << 16) |
+ ((buf[2] & 0xff) << 8) |
+ (buf[3] & 0xff);
+ }
+
+
+ private void CheckNotDisposed()
+ {
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(this.GetType().Name);
+ }
+ }
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!IsDisposed)
+ {
+ if (disposing)
+ {
+ ReadBuffer?.Dispose();
+ WriteBuffer?.Dispose();
+ InnerTransport?.Dispose();
+ }
+ }
+ IsDisposed = true;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TServerTransport.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TServerTransport.cs
new file mode 100644
index 000000000..74c54cd0e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TServerTransport.cs
@@ -0,0 +1,54 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transport
+{
+ // ReSharper disable once InconsistentNaming
+ public abstract class TServerTransport
+ {
+ public abstract void Listen();
+ public abstract void Close();
+ public abstract bool IsClientPending();
+
+ protected virtual async ValueTask<TTransport> AcceptImplementationAsync()
+ {
+ return await AcceptImplementationAsync(CancellationToken.None);
+ }
+
+ protected abstract ValueTask<TTransport> AcceptImplementationAsync(CancellationToken cancellationToken);
+
+ public async ValueTask<TTransport> AcceptAsync()
+ {
+ return await AcceptAsync(CancellationToken.None);
+ }
+
+ public async ValueTask<TTransport> AcceptAsync(CancellationToken cancellationToken)
+ {
+ var transport = await AcceptImplementationAsync(cancellationToken);
+
+ if (transport == null)
+ {
+ throw new TTransportException($"{nameof(AcceptImplementationAsync)} should not return null");
+ }
+
+ return transport;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TTransport.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TTransport.cs
new file mode 100644
index 000000000..799801202
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TTransport.cs
@@ -0,0 +1,190 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Thrift.Transport
+{
+ //TODO: think about client info
+ // ReSharper disable once InconsistentNaming
+ public abstract class TTransport : IDisposable
+ {
+ //TODO: think how to avoid peek byte
+ private readonly byte[] _peekBuffer = new byte[1];
+ private bool _hasPeekByte;
+ public abstract bool IsOpen { get; }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ public async ValueTask<bool> PeekAsync(CancellationToken cancellationToken)
+ {
+ //If we already have a byte read but not consumed, do nothing.
+ if (_hasPeekByte)
+ {
+ return true;
+ }
+
+ //If transport closed we can't peek.
+ if (!IsOpen)
+ {
+ return false;
+ }
+
+ //Try to read one byte. If succeeds we will need to store it for the next read.
+ try
+ {
+ var bytes = await ReadAsync(_peekBuffer, 0, 1, cancellationToken);
+ if (bytes == 0)
+ {
+ return false;
+ }
+ }
+ catch (IOException)
+ {
+ return false;
+ }
+
+ _hasPeekByte = true;
+ return true;
+ }
+
+ public virtual async Task OpenAsync()
+ {
+ await OpenAsync(CancellationToken.None);
+ }
+
+ public abstract Task OpenAsync(CancellationToken cancellationToken);
+
+ public abstract void Close();
+
+ protected static void ValidateBufferArgs(byte[] buffer, int offset, int length)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer));
+ }
+
+#if DEBUG // let it fail with OutOfRange in RELEASE mode
+ if (offset < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(offset), "Buffer offset must be >= 0");
+ }
+
+ if (length < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(length), "Buffer length must be >= 0");
+ }
+
+ if (offset + length > buffer.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(buffer), "Not enough data");
+ }
+#endif
+ }
+
+ public virtual async ValueTask<int> ReadAsync(byte[] buffer, int offset, int length)
+ {
+ return await ReadAsync(buffer, offset, length, CancellationToken.None);
+ }
+
+ public abstract ValueTask<int> ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken);
+
+ public virtual async ValueTask<int> ReadAllAsync(byte[] buffer, int offset, int length)
+ {
+ return await ReadAllAsync(buffer, offset, length, CancellationToken.None);
+ }
+
+ public virtual async ValueTask<int> ReadAllAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ ValidateBufferArgs(buffer, offset, length);
+
+ if (cancellationToken.IsCancellationRequested)
+ return await Task.FromCanceled<int>(cancellationToken);
+
+ if (length <= 0)
+ return 0;
+
+ // If we previously peeked a byte, we need to use that first.
+ var totalBytes = 0;
+ if (_hasPeekByte)
+ {
+ buffer[offset++] = _peekBuffer[0];
+ _hasPeekByte = false;
+ if (1 == length)
+ {
+ return 1; // we're done
+ }
+ ++totalBytes;
+ }
+
+ var remaining = length - totalBytes;
+ Debug.Assert(remaining > 0); // any other possible cases should have been handled already
+ while (true)
+ {
+ var numBytes = await ReadAsync(buffer, offset, remaining, cancellationToken);
+ totalBytes += numBytes;
+ if (totalBytes >= length)
+ {
+ return totalBytes; // we're done
+ }
+
+ if (numBytes <= 0)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.EndOfFile,
+ "Cannot read, Remote side has closed");
+ }
+
+ remaining -= numBytes;
+ offset += numBytes;
+ }
+ }
+
+ public virtual async Task WriteAsync(byte[] buffer)
+ {
+ await WriteAsync(buffer, CancellationToken.None);
+ }
+
+ public virtual async Task WriteAsync(byte[] buffer, CancellationToken cancellationToken)
+ {
+ await WriteAsync(buffer, 0, buffer.Length, CancellationToken.None);
+ }
+
+ public virtual async Task WriteAsync(byte[] buffer, int offset, int length)
+ {
+ await WriteAsync(buffer, offset, length, CancellationToken.None);
+ }
+
+ public abstract Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken);
+
+ public virtual async Task FlushAsync()
+ {
+ await FlushAsync(CancellationToken.None);
+ }
+
+ public abstract Task FlushAsync(CancellationToken cancellationToken);
+
+ protected abstract void Dispose(bool disposing);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TTransportException.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TTransportException.cs
new file mode 100644
index 000000000..760a178e6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TTransportException.cs
@@ -0,0 +1,60 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+
+namespace Thrift.Transport
+{
+ // ReSharper disable once InconsistentNaming
+ public class TTransportException : TException
+ {
+ public enum ExceptionType
+ {
+ Unknown,
+ NotOpen,
+ AlreadyOpen,
+ TimedOut,
+ EndOfFile,
+ Interrupted
+ }
+
+ public ExceptionType ExType { get; private set; }
+
+ public TTransportException()
+ {
+ }
+
+ public TTransportException(ExceptionType exType, Exception inner = null)
+ : base(string.Empty, inner)
+ {
+ ExType = exType;
+ }
+
+ public TTransportException(ExceptionType exType, string message, Exception inner = null)
+ : base(message, inner)
+ {
+ ExType = exType;
+ }
+
+ public TTransportException(string message, Exception inner = null)
+ : base(message, inner)
+ {
+ }
+
+ public ExceptionType Type => ExType;
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TTransportFactory.cs b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TTransportFactory.cs
new file mode 100644
index 000000000..16e27ac82
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/Transport/TTransportFactory.cs
@@ -0,0 +1,35 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+namespace Thrift.Transport
+{
+ /// <summary>
+ /// From Mark Slee & Aditya Agarwal of Facebook:
+ /// Factory class used to create wrapped instance of Transports.
+ /// This is used primarily in servers, which get Transports from
+ /// a ServerTransport and then may want to mutate them (i.e. create
+ /// a BufferedTransport from the underlying base transport)
+ /// </summary>
+ // ReSharper disable once InconsistentNaming
+ public class TTransportFactory
+ {
+ public virtual TTransport GetTransport(TTransport trans)
+ {
+ return trans;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/netstd/Thrift/thrift.snk b/src/jaegertracing/thrift/lib/netstd/Thrift/thrift.snk
new file mode 100644
index 000000000..97bc5812b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/Thrift/thrift.snk
Binary files differ
diff --git a/src/jaegertracing/thrift/lib/netstd/build.cmd b/src/jaegertracing/thrift/lib/netstd/build.cmd
new file mode 100644
index 000000000..863c4b45e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/build.cmd
@@ -0,0 +1,27 @@
+@echo off
+rem /*
+rem * Licensed to the Apache Software Foundation (ASF) under one
+rem * or more contributor license agreements. See the NOTICE file
+rem * distributed with this work for additional information
+rem * regarding copyright ownership. The ASF licenses this file
+rem * to you under the Apache License, Version 2.0 (the
+rem * "License"); you may not use this file except in compliance
+rem * with the License. You may obtain a copy of the License at
+rem *
+rem * http://www.apache.org/licenses/LICENSE-2.0
+rem *
+rem * Unless required by applicable law or agreed to in writing,
+rem * software distributed under the License is distributed on an
+rem * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem * KIND, either express or implied. See the License for the
+rem * specific language governing permissions and limitations
+rem * under the License.
+rem */
+
+setlocal
+
+thrift -version
+dotnet --info
+dotnet build
+
+:eof
diff --git a/src/jaegertracing/thrift/lib/netstd/build.sh b/src/jaegertracing/thrift/lib/netstd/build.sh
new file mode 100644
index 000000000..ae18bce9b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/build.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#exit if any command fails
+#set -e
+
+thrift --version
+dotnet --info
+dotnet build
+
+#revision=${TRAVIS_JOB_ID:=1}
+#revision=$(printf "%04d" $revision)
+
+#dotnet pack ./src/PROJECT_NAME -c Release -o ./artifacts --version-suffix=$revision
diff --git a/src/jaegertracing/thrift/lib/netstd/runtests.cmd b/src/jaegertracing/thrift/lib/netstd/runtests.cmd
new file mode 100644
index 000000000..5114bc594
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/runtests.cmd
@@ -0,0 +1,28 @@
+@echo off
+rem /*
+rem * Licensed to the Apache Software Foundation (ASF) under one
+rem * or more contributor license agreements. See the NOTICE file
+rem * distributed with this work for additional information
+rem * regarding copyright ownership. The ASF licenses this file
+rem * to you under the Apache License, Version 2.0 (the
+rem * "License"); you may not use this file except in compliance
+rem * with the License. You may obtain a copy of the License at
+rem *
+rem * http://www.apache.org/licenses/LICENSE-2.0
+rem *
+rem * Unless required by applicable law or agreed to in writing,
+rem * software distributed under the License is distributed on an
+rem * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem * KIND, either express or implied. See the License for the
+rem * specific language governing permissions and limitations
+rem * under the License.
+rem */
+setlocal
+
+thrift -version
+dotnet --info
+
+dotnet test Tests\Thrift.IntegrationTests\Thrift.IntegrationTests.csproj
+dotnet test Tests\Thrift.Tests\Thrift.Tests.csproj
+
+:eof
diff --git a/src/jaegertracing/thrift/lib/netstd/runtests.sh b/src/jaegertracing/thrift/lib/netstd/runtests.sh
new file mode 100644
index 000000000..a26cc36ac
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/netstd/runtests.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+thrift -version
+dotnet --info
+
+dotnet test Tests\Thrift.IntegrationTests\Thrift.IntegrationTests.csproj
+dotnet test Tests\Thrift.Tests\Thrift.Tests.csproj \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/nodejs/Makefile.am b/src/jaegertracing/thrift/lib/nodejs/Makefile.am
new file mode 100755
index 000000000..71068b58f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/Makefile.am
@@ -0,0 +1,45 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# We call npm twice to work around npm issues
+
+stubs: $(top_srcdir)/test/ThriftTest.thrift
+ $(THRIFT) --gen js:node -o test/ $(top_srcdir)/test/ThriftTest.thrift
+
+deps: $(top_srcdir)/package.json
+ $(NPM) install $(top_srcdir)/ || $(NPM) install $(top_srcdir)/
+
+all-local: deps
+
+precross: deps stubs
+
+# TODO: Lint nodejs lib and gen-code as part of build
+check: deps
+ cd $(top_srcdir) && $(NPM) test && $(NPM) run lint-tests && cd lib/nodejs
+
+clean-local:
+ $(RM) -r test/gen-*
+ $(RM) -r $(top_srcdir)/node_modules
+ $(RM) -r test/episodic-code-generation-test/gen*
+ $(RM) -r test/episodic-code-generation-test/node_modules
+
+EXTRA_DIST = \
+ examples \
+ lib \
+ test \
+ coding_standards.md \
+ README.md
diff --git a/src/jaegertracing/thrift/lib/nodejs/README.md b/src/jaegertracing/thrift/lib/nodejs/README.md
new file mode 100644
index 000000000..ed306c15f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/README.md
@@ -0,0 +1,111 @@
+Thrift Node.js Library
+=========================
+
+License
+-------
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+## Compatibility
+
+node version 6 or later is required
+
+## Install
+
+ npm install thrift
+
+## Thrift Compiler
+
+You can compile IDL sources for Node.js with the following command:
+
+ thrift --gen js:node thrift_file
+
+## Cassandra Client Example:
+
+Here is a Cassandra example:
+
+```js
+var thrift = require('thrift'),
+ Cassandra = require('./gen-nodejs/Cassandra')
+ ttypes = require('./gen-nodejs/cassandra_types');
+
+var connection = thrift.createConnection("localhost", 9160),
+ client = thrift.createClient(Cassandra, connection);
+
+connection.on('error', function(err) {
+ console.error(err);
+});
+
+client.get_slice("Keyspace", "key", new ttypes.ColumnParent({column_family: "ExampleCF"}), new ttypes.SlicePredicate({slice_range: new ttypes.SliceRange({start: '', finish: ''})}), ttypes.ConsistencyLevel.ONE, function(err, data) {
+ if (err) {
+ // handle err
+ } else {
+ // data == [ttypes.ColumnOrSuperColumn, ...]
+ }
+ connection.end();
+});
+```
+
+<a name="int64"></a>
+## Int64
+
+Since JavaScript represents all numbers as doubles, int64 values cannot be accurately represented naturally. To solve this, int64 values in responses will be wrapped with Thrift.Int64 objects. The Int64 implementation used is [broofa/node-int64](https://github.com/broofa/node-int64).
+
+## Client and server examples
+
+Several example clients and servers are included in the thrift/lib/nodejs/examples folder and the cross language tutorial thrift/tutorial/nodejs folder.
+
+## Use on browsers
+
+You can use code generated with js:node on browsers with Webpack. Here is an example.
+
+thrift --gen js:node,ts,es6,with_ns
+
+```
+import * as thrift from 'thrift/browser';
+import { MyServiceClient } from '../gen-nodejs/MyService';
+
+let host = window.location.hostname;
+let port = 443;
+let opts = {
+ transport: thrift.TBufferedTransport,
+ protocol: thrift.TJSONProtocol,
+ headers: {
+ 'Content-Type': 'application/vnd.apache.thrift.json',
+ },
+ https: true,
+ path: '/url/path',
+ useCORS: true,
+};
+
+let connection = thrift.createXHRConnection(host, port, opts);
+let thriftClient = thrift.createXHRClient(MyServiceClient, connection);
+
+connection.on('error', (err) => {
+ console.error(err);
+});
+
+thriftClient.myService(param)
+ .then((result) => {
+ console.log(result);
+ })
+ .catch((err) => {
+ ....
+ });
+```
+
+Note that thrift/index.js must be renamed or skipped for browsers.
diff --git a/src/jaegertracing/thrift/lib/nodejs/coding_standards.md b/src/jaegertracing/thrift/lib/nodejs/coding_standards.md
new file mode 100644
index 000000000..fa0390bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/coding_standards.md
@@ -0,0 +1 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
diff --git a/src/jaegertracing/thrift/lib/nodejs/examples/Makefile b/src/jaegertracing/thrift/lib/nodejs/examples/Makefile
new file mode 100644
index 000000000..87157db63
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/examples/Makefile
@@ -0,0 +1,24 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+all:
+ ../../../compiler/cpp/thrift --gen js:node user.thrift
+
+server: all
+ NODE_PATH=../lib:../lib/thrift:$(NODE_PATH) node server.js
+
+client: all
+ NODE_PATH=../lib:../lib/thrift:$(NODE_PATH) node client.js
diff --git a/src/jaegertracing/thrift/lib/nodejs/examples/README.md b/src/jaegertracing/thrift/lib/nodejs/examples/README.md
new file mode 100644
index 000000000..7350c10c9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/examples/README.md
@@ -0,0 +1,40 @@
+# Thrift Node.js Examples
+
+## License
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+## Running the user example
+
+Generate the bindings:
+
+ ../../../compiler/cpp/thrift --gen js:node user.thrift
+ ../../../compiler/cpp/thrift --gen js:node --gen py hello.thrift
+
+To run the user example, first start up the server in one terminal:
+
+ NODE_PATH=../lib:../lib/thrift node server.js
+
+Now run the client:
+
+ NODE_PATH=../lib:../lib/thrift node client.js
+
+For an example using JavaScript in the browser to connect to
+a node.js server look at hello.html, hello.js and hello.thrift
+
+HTTP examples are provided also: httpClient.js and httpServer.js
+You can test HTTP cross platform with the httpServer.py Python server
diff --git a/src/jaegertracing/thrift/lib/nodejs/examples/client.js b/src/jaegertracing/thrift/lib/nodejs/examples/client.js
new file mode 100644
index 000000000..c83b34234
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/examples/client.js
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var thrift = require('thrift');
+
+var UserStorage = require('./gen-nodejs/UserStorage.js'),
+ ttypes = require('./gen-nodejs/user_types');
+
+var connection = thrift.createConnection('localhost', 9090),
+ client = thrift.createClient(UserStorage, connection);
+
+var user = new ttypes.UserProfile({uid: 1,
+ name: "Mark Slee",
+ blurb: "I'll find something to put here."});
+
+connection.on('error', function(err) {
+ console.error(err);
+});
+
+client.store(user, function(err, response) {
+ if (err) {
+ console.error(err);
+ } else {
+ console.log("client stored:", user.uid);
+ client.retrieve(user.uid, function(err, responseUser) {
+ if (err) {
+ console.error(err);
+ } else {
+ console.log("client retrieved:", responseUser.uid);
+ connection.end();
+ }
+ });
+ }
+});
diff --git a/src/jaegertracing/thrift/lib/nodejs/examples/client_multitransport.js b/src/jaegertracing/thrift/lib/nodejs/examples/client_multitransport.js
new file mode 100644
index 000000000..1e37de32f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/examples/client_multitransport.js
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var thrift = require('thrift'),
+ ttransport = require('thrift/transport');
+
+var UserStorage = require('./gen-nodejs/UserStorage'),
+ ttypes = require('./gen-nodejs/user_types');
+
+var f_conn = thrift.createConnection('localhost', 9090), // default: framed
+ f_client = thrift.createClient(UserStorage, f_conn);
+var b_conn = thrift.createConnection('localhost', 9091, {transport: ttransport.TBufferedTransport}),
+ b_client = thrift.createClient(UserStorage, b_conn);
+var user1 = new ttypes.UserProfile({uid: 1,
+ name: "Mark Slee",
+ blurb: "I'll find something to put here."});
+var user2 = new ttypes.UserProfile({uid: 2,
+ name: "Satoshi Tagomori",
+ blurb: "ok, let's test with buffered transport."});
+
+f_conn.on('error', function(err) {
+ console.error("framed:", err);
+});
+
+f_client.store(user1, function(err, response) {
+ if (err) { console.error(err); return; }
+
+ console.log("stored:", user1.uid, " as ", user1.name);
+ b_client.retrieve(user1.uid, function(err, responseUser) {
+ if (err) { console.error(err); return; }
+ console.log("retrieved:", responseUser.uid, " as ", responseUser.name);
+ });
+});
+
+b_client.store(user2, function(err, response) {
+ if (err) { console.error(err); return; }
+
+ console.log("stored:", user2.uid, " as ", user2.name);
+ f_client.retrieve(user2.uid, function(err, responseUser) {
+ if (err) { console.error(err); return; }
+ console.log("retrieved:", responseUser.uid, " as ", responseUser.name);
+ });
+});
diff --git a/src/jaegertracing/thrift/lib/nodejs/examples/hello.html b/src/jaegertracing/thrift/lib/nodejs/examples/hello.html
new file mode 100644
index 000000000..fe85a7e9d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/examples/hello.html
@@ -0,0 +1,65 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Apache Thrift JavaScript Browser Client Demo</title>
+ <script src="thrift.js" type="text/javascript"></script>
+ <script src="gen-js/HelloSvc.js" type="text/javascript"></script>
+ <script src="gen-js/TimesTwo.js" type="text/javascript"></script>
+</head>
+<body>
+ <h1>Apache Thrift JavaScript Browser Client Demo</h1>
+ <p>This html file demonstrates Apache Thrift JavaScrpt RPC between a browser client to a node.js server. Clicking the buttons below will call the RPC functions hosted by the Apache Thrift server at localhost:8585. The file hello.js contains the JavaScript node.js server required. Here are the steps to get the example running:</p>
+ <ol>
+ <li>Install Node.js <pre><a href="http://nodejs.org">nodejs.org</a></pre></li>
+ <li>Install Apache Thrift for node (note that the node package manager will create the node_modules folder in the current directory so make sure to run npm from the same directory as hello.js so that the server can find the Thrift libraries. This example requires Apache Thrift 0.9.2+) <pre>$ npm install thrift</pre></li>
+ <li>Compile the hello.idl for JavaScript and Node.js (you'll need to have the Apache Thrift compiler installed for this step. This also needs to be executed in the same directory as hello.js because hello.js and hello.html look for the gen-nodejs and gen-js directories here.)<pre>$ thrift -gen js -gen js:node hello.thrift</pre></li>
+ <li>Run the node server in the directory with the hello.html file<pre>$ node hello.js</pre></li>
+ <li>Copy the Apache Thrift JavaScript library, thrift.js, into the directory with this html file.<pre>$ cp ...../thrift.js . (you should be able to use Bower to install the browser based Apache Thrift library in the near future.)</pre>
+ <li>Reload this page in a browser through the node server using using the URL: <pre>http://localhost:8585/hello.html</pre>then click a button below to make an RPC call</li>
+ </ol>
+ <button id="btn">Get Message from Node Server</button>
+ <button id="btnDbl">Double 25</button>
+ <script type="text/javascript">
+ document.getElementById("btn").addEventListener("click", getMessage, false);
+
+ function getMessage() {
+ var transport = new Thrift.TXHRTransport("http://localhost:8585/hello");
+ var protocol = new Thrift.TJSONProtocol(transport);
+ var client = new HelloSvcClient(protocol);
+ var msg = client.hello_func();
+ document.getElementById("output").innerHTML = msg;
+ }
+
+ document.getElementById("btnDbl").addEventListener("click", dblMessage, false);
+
+ function dblMessage() {
+ var transport = new Thrift.TXHRTransport("http://localhost:8585/dbl");
+ var protocol = new Thrift.TJSONProtocol(transport);
+ var client = new TimesTwoClient(protocol);
+ var val = client.dbl(25);
+ document.getElementById("output2").innerHTML = val;
+ }
+ </script>
+ <h2>Server Response: <div id="output"></div></h2>
+ <h2>Server Dbl: <div id="output2"></div></h2>
+</body>
+</html>
+
diff --git a/src/jaegertracing/thrift/lib/nodejs/examples/hello.js b/src/jaegertracing/thrift/lib/nodejs/examples/hello.js
new file mode 100644
index 000000000..8b7c4e4ea
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/examples/hello.js
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var thrift = require('thrift');
+var HelloSvc = require('./gen-nodejs/HelloSvc.js');
+var TimesTwoSvc = require('./gen-nodejs/TimesTwo.js');
+
+var helloHandler = {
+ hello_func: function(result) {
+ this.call_counter = this.call_counter || 0;
+ console.log("Client call: " + (++this.call_counter));
+ result(null, "Hello Apache Thrift for JavaScript " + this.call_counter);
+ }
+}
+
+var timesTwoHandler = {
+ dbl: function(val, result) {
+ console.log("Client call: " + val);
+ result(null, val * 2);
+ }
+}
+
+var helloService = {
+ transport: thrift.TBufferedTransport,
+ protocol: thrift.TJSONProtocol,
+ processor: HelloSvc,
+ handler: helloHandler
+};
+
+var dblService = {
+ transport: thrift.TBufferedTransport,
+ protocol: thrift.TJSONProtocol,
+ processor: TimesTwoSvc,
+ handler: timesTwoHandler
+};
+
+var ServerOptions = {
+ files: ".",
+ services: {
+ "/hello": helloService,
+ "/dbl": dblService,
+ }
+}
+
+var server = thrift.createWebServer(ServerOptions);
+var port = 8585;
+server.listen(port);
+console.log("Http/Thrift Server running on port: " + port);
diff --git a/src/jaegertracing/thrift/lib/nodejs/examples/hello.thrift b/src/jaegertracing/thrift/lib/nodejs/examples/hello.thrift
new file mode 100644
index 000000000..deaf5a5f9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/examples/hello.thrift
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+service HelloSvc {
+ string hello_func(),
+}
+
+service TimesTwo {
+ i64 dbl(1: i64 val),
+}
+
+
diff --git a/src/jaegertracing/thrift/lib/nodejs/examples/httpClient.js b/src/jaegertracing/thrift/lib/nodejs/examples/httpClient.js
new file mode 100644
index 000000000..19cc0c362
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/examples/httpClient.js
@@ -0,0 +1,23 @@
+var thrift = require('thrift');
+var helloSvc = require('./gen-nodejs/HelloSvc.js');
+
+var options = {
+ transport: thrift.TBufferedTransport,
+ protocol: thrift.TJSONProtocol,
+ path: "/hello",
+ headers: {"Connection": "close"},
+ https: false
+};
+
+var connection = thrift.createHttpConnection("localhost", 9090, options);
+var client = thrift.createHttpClient(helloSvc, connection);
+
+connection.on("error", function(err) {
+ console.log("Error: " + err);
+});
+
+client.hello_func(function(error, result) {
+ console.log("Msg from server: " + result);
+});
+
+
diff --git a/src/jaegertracing/thrift/lib/nodejs/examples/httpServer.js b/src/jaegertracing/thrift/lib/nodejs/examples/httpServer.js
new file mode 100644
index 000000000..acae1369a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/examples/httpServer.js
@@ -0,0 +1,31 @@
+var thrift = require('thrift');
+var helloSvc = require('./gen-nodejs/HelloSvc');
+
+//ServiceHandler: Implement the hello service
+var helloHandler = {
+ hello_func: function (result) {
+ console.log("Received Hello call");
+ result(null, "Hello from Node.js");
+ }
+};
+
+//ServiceOptions: The I/O stack for the service
+var helloSvcOpt = {
+ handler: helloHandler,
+ processor: helloSvc,
+ protocol: thrift.TJSONProtocol,
+ transport: thrift.TBufferedTransport
+};
+
+//ServerOptions: Define server features
+var serverOpt = {
+ services: {
+ "/hello": helloSvcOpt
+ }
+}
+
+//Create and start the web server
+var port = 9090;
+thrift.createWebServer(serverOpt).listen(port);
+console.log("Http/Thrift Server running on port: " + port);
+
diff --git a/src/jaegertracing/thrift/lib/nodejs/examples/httpServer.py b/src/jaegertracing/thrift/lib/nodejs/examples/httpServer.py
new file mode 100644
index 000000000..76e9f4aa3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/examples/httpServer.py
@@ -0,0 +1,20 @@
+import sys
+sys.path.append('gen-py')
+
+from hello import HelloSvc
+from thrift.protocol import TJSONProtocol
+from thrift.server import THttpServer
+
+
+class HelloSvcHandler:
+ def hello_func(self):
+ print("Hello Called")
+ return "hello from Python"
+
+
+processor = HelloSvc.Processor(HelloSvcHandler())
+protoFactory = TJSONProtocol.TJSONProtocolFactory()
+port = 9090
+server = THttpServer.THttpServer(processor, ("localhost", port), protoFactory)
+print "Python server running on port " + str(port)
+server.serve()
diff --git a/src/jaegertracing/thrift/lib/nodejs/examples/parse.js b/src/jaegertracing/thrift/lib/nodejs/examples/parse.js
new file mode 100644
index 000000000..168a1aeea
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/examples/parse.js
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/**
+
+ This is a standalone deserialize/parse example if you just want to deserialize
+ thrift decoupled from cassandra server
+
+ 1. acquire thrift template specification files from who ever built it (eg: EXAMPLE.thrift)
+
+ 2. Install thrift on local machine
+
+ 3. generate thrift clients for nodejs using template specification files (#1)
+ thrift --gen js:node schema/EXAMPLE.thrift
+
+ This creates creates gen-node.js directory containing a new file, GENERATED.js
+
+ 4. Inside GENERATED.js is a class you will want to instanciate. Find this class name and plug
+ it into the example code below (ie, "YOUR_CLASS_NAME")
+ */
+
+function parseThrift(thriftEncodedData, callback) {
+ var thrift = require('thrift');
+ var transport = new thrift.TFramedTransport(thriftEncodedData);
+ var protocol = new thrift.TBinaryProtocol(transport);
+
+ var clientClass = require('../gen-nodejs/GENERATED').YOUR_CLASS_NAME;
+ var client = new clientClass();
+ client.read(protocol);
+ callback(null, client);
+}
diff --git a/src/jaegertracing/thrift/lib/nodejs/examples/server.js b/src/jaegertracing/thrift/lib/nodejs/examples/server.js
new file mode 100644
index 000000000..1c482fe71
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/examples/server.js
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var thrift = require('thrift');
+
+var UserStorage = require('./gen-nodejs/UserStorage.js'),
+ ttypes = require('./gen-nodejs/user_types');
+
+var users = {};
+
+var server = thrift.createServer(UserStorage, {
+ store: function(user, result) {
+ console.log("server stored:", user.uid);
+ users[user.uid] = user;
+ result(null);
+ },
+
+ retrieve: function(uid, result) {
+ console.log("server retrieved:", uid);
+ result(null, users[uid]);
+ },
+});
+
+server.listen(9090);
diff --git a/src/jaegertracing/thrift/lib/nodejs/examples/server_http.js b/src/jaegertracing/thrift/lib/nodejs/examples/server_http.js
new file mode 100644
index 000000000..ef2dc83a2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/examples/server_http.js
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var connect = require('connect');
+var thrift = require('thrift');
+
+var UserStorage = require('./gen-nodejs/UserStorage'),
+ ttypes = require('./gen-nodejs/user_types');
+
+var users = {};
+
+var store = function(user, result) {
+ console.log("stored:", user.uid);
+ users[user.uid] = user;
+ result(null);
+};
+var retrieve = function(uid, result) {
+ console.log("retrieved:", uid);
+ result(null, users[uid]);
+};
+
+var server_http = thrift.createHttpServer(UserStorage, {
+ store: store,
+ retrieve: retrieve
+});
+server_http.listen(9090);
+
+var server_connect = connect(thrift.httpMiddleware(UserStorage, {
+ store: store,
+ retrieve: retrieve
+}));
+server_http.listen(9091);
+
+var server_connect_json = connect(thrift.httpMiddleware(UserStorage, {
+ store: store,
+ retrieve: retrieve
+}, {protocol: thrift.TJSONProtocol}));
+server_connect_json.listen(9092);
diff --git a/src/jaegertracing/thrift/lib/nodejs/examples/server_multitransport.js b/src/jaegertracing/thrift/lib/nodejs/examples/server_multitransport.js
new file mode 100644
index 000000000..a348e6847
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/examples/server_multitransport.js
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var thrift = require('thrift'),
+ ttransport = require('thrift/transport');
+
+var UserStorage = require('./gen-nodejs/UserStorage'),
+ ttypes = require('./gen-nodejs/user_types');
+
+var users = {};
+
+var store = function(user, result) {
+ console.log("stored:", user.uid);
+ users[user.uid] = user;
+ result(null);
+};
+var retrieve = function(uid, result) {
+ console.log("retrieved:", uid);
+ result(null, users[uid]);
+};
+
+var server_framed = thrift.createServer(UserStorage, {
+ store: store,
+ retrieve: retrieve
+});
+server_framed.listen(9090);
+var server_buffered = thrift.createServer(UserStorage, {
+ store: store,
+ retrieve: retrieve
+}, {transport: ttransport.TBufferedTransport});
+server_buffered.listen(9091);
diff --git a/src/jaegertracing/thrift/lib/nodejs/examples/user.thrift b/src/jaegertracing/thrift/lib/nodejs/examples/user.thrift
new file mode 100644
index 000000000..d087fd442
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/examples/user.thrift
@@ -0,0 +1,27 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+struct UserProfile {
+ 1: i32 uid,
+ 2: string name,
+ 3: string blurb
+}
+
+service UserStorage {
+ void store(1: UserProfile user),
+ UserProfile retrieve(1: i32 uid)
+}
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/binary.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/binary.js
new file mode 100644
index 000000000..9813ffdb1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/binary.js
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var POW_8 = Math.pow(2, 8);
+var POW_16 = Math.pow(2, 16);
+var POW_24 = Math.pow(2, 24);
+var POW_32 = Math.pow(2, 32);
+var POW_40 = Math.pow(2, 40);
+var POW_48 = Math.pow(2, 48);
+var POW_52 = Math.pow(2, 52);
+var POW_1022 = Math.pow(2, 1022);
+
+exports.readByte = function(b){
+ return b > 127 ? b-256 : b;
+};
+
+exports.readI16 = function(buff, off) {
+ off = off || 0;
+ var v = buff[off + 1];
+ v += buff[off] << 8;
+ if (buff[off] & 128) {
+ v -= POW_16;
+ }
+ return v;
+};
+
+exports.readI32 = function(buff, off) {
+ off = off || 0;
+ var v = buff[off + 3];
+ v += buff[off + 2] << 8;
+ v += buff[off + 1] << 16;
+ v += buff[off] * POW_24;
+ if (buff[off] & 0x80) {
+ v -= POW_32;
+ }
+ return v;
+};
+
+exports.writeI16 = function(buff, v) {
+ buff[1] = v & 0xff;
+ v >>= 8;
+ buff[0] = v & 0xff;
+ return buff;
+};
+
+exports.writeI32 = function(buff, v) {
+ buff[3] = v & 0xff;
+ v >>= 8;
+ buff[2] = v & 0xff;
+ v >>= 8;
+ buff[1] = v & 0xff;
+ v >>= 8;
+ buff[0] = v & 0xff;
+ return buff;
+};
+
+exports.readDouble = function(buff, off) {
+ off = off || 0;
+ var signed = buff[off] & 0x80;
+ var e = (buff[off+1] & 0xF0) >> 4;
+ e += (buff[off] & 0x7F) << 4;
+
+ var m = buff[off+7];
+ m += buff[off+6] << 8;
+ m += buff[off+5] << 16;
+ m += buff[off+4] * POW_24;
+ m += buff[off+3] * POW_32;
+ m += buff[off+2] * POW_40;
+ m += (buff[off+1] & 0x0F) * POW_48;
+
+ switch (e) {
+ case 0:
+ e = -1022;
+ break;
+ case 2047:
+ return m ? NaN : (signed ? -Infinity : Infinity);
+ default:
+ m += POW_52;
+ e -= 1023;
+ }
+
+ if (signed) {
+ m *= -1;
+ }
+
+ return m * Math.pow(2, e - 52);
+};
+
+/*
+ * Based on code from the jspack module:
+ * http://code.google.com/p/jspack/
+ */
+exports.writeDouble = function(buff, v) {
+ var m, e, c;
+
+ buff[0] = (v < 0 ? 0x80 : 0x00);
+
+ v = Math.abs(v);
+ if (v !== v) {
+ // NaN, use QNaN IEEE format
+ m = 2251799813685248;
+ e = 2047;
+ } else if (v === Infinity) {
+ m = 0;
+ e = 2047;
+ } else {
+ e = Math.floor(Math.log(v) / Math.LN2);
+ c = Math.pow(2, -e);
+ if (v * c < 1) {
+ e--;
+ c *= 2;
+ }
+
+ if (e + 1023 >= 2047)
+ {
+ // Overflow
+ m = 0;
+ e = 2047;
+ }
+ else if (e + 1023 >= 1)
+ {
+ // Normalized - term order matters, as Math.pow(2, 52-e) and v*Math.pow(2, 52) can overflow
+ m = (v*c-1) * POW_52;
+ e += 1023;
+ }
+ else
+ {
+ // Denormalized - also catches the '0' case, somewhat by chance
+ m = (v * POW_1022) * POW_52;
+ e = 0;
+ }
+ }
+
+ buff[1] = (e << 4) & 0xf0;
+ buff[0] |= (e >> 4) & 0x7f;
+
+ buff[7] = m & 0xff;
+ m = Math.floor(m / POW_8);
+ buff[6] = m & 0xff;
+ m = Math.floor(m / POW_8);
+ buff[5] = m & 0xff;
+ m = Math.floor(m / POW_8);
+ buff[4] = m & 0xff;
+ m >>= 8;
+ buff[3] = m & 0xff;
+ m >>= 8;
+ buff[2] = m & 0xff;
+ m >>= 8;
+ buff[1] |= m & 0x0f;
+
+ return buff;
+};
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/binary_protocol.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/binary_protocol.js
new file mode 100644
index 000000000..af8836cf5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/binary_protocol.js
@@ -0,0 +1,367 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var log = require('./log');
+var binary = require('./binary');
+var Int64 = require('node-int64');
+var Thrift = require('./thrift');
+var Type = Thrift.Type;
+
+module.exports = TBinaryProtocol;
+
+// JavaScript supports only numeric doubles, therefore even hex values are always signed.
+// The largest integer value which can be represented in JavaScript is +/-2^53.
+// Bitwise operations convert numbers to 32 bit integers but perform sign extension
+// upon assigning values back to variables.
+var VERSION_MASK = -65536, // 0xffff0000
+ VERSION_1 = -2147418112, // 0x80010000
+ TYPE_MASK = 0x000000ff;
+
+TBinaryProtocol.VERSION_MASK = VERSION_MASK;
+TBinaryProtocol.VERSION_1 = VERSION_1;
+TBinaryProtocol.TYPE_MASK = TYPE_MASK
+
+function TBinaryProtocol(trans, strictRead, strictWrite) {
+ this.trans = trans;
+ this.strictRead = (strictRead !== undefined ? strictRead : false);
+ this.strictWrite = (strictWrite !== undefined ? strictWrite : true);
+ this._seqid = null;
+};
+
+TBinaryProtocol.prototype.flush = function() {
+ return this.trans.flush();
+};
+
+TBinaryProtocol.prototype.writeMessageBegin = function(name, type, seqid) {
+ if (this.strictWrite) {
+ this.writeI32(VERSION_1 | type);
+ this.writeString(name);
+ this.writeI32(seqid);
+ } else {
+ this.writeString(name);
+ this.writeByte(type);
+ this.writeI32(seqid);
+ }
+ // Record client seqid to find callback again
+ if (this._seqid !== null) {
+ log.warning('SeqId already set', { 'name': name });
+ } else {
+ this._seqid = seqid;
+ this.trans.setCurrSeqId(seqid);
+ }
+};
+
+TBinaryProtocol.prototype.writeMessageEnd = function() {
+ if (this._seqid !== null) {
+ this._seqid = null;
+ } else {
+ log.warning('No seqid to unset');
+ }
+};
+
+TBinaryProtocol.prototype.writeStructBegin = function(name) {
+};
+
+TBinaryProtocol.prototype.writeStructEnd = function() {
+};
+
+TBinaryProtocol.prototype.writeFieldBegin = function(name, type, id) {
+ this.writeByte(type);
+ this.writeI16(id);
+};
+
+TBinaryProtocol.prototype.writeFieldEnd = function() {
+};
+
+TBinaryProtocol.prototype.writeFieldStop = function() {
+ this.writeByte(Type.STOP);
+};
+
+TBinaryProtocol.prototype.writeMapBegin = function(ktype, vtype, size) {
+ this.writeByte(ktype);
+ this.writeByte(vtype);
+ this.writeI32(size);
+};
+
+TBinaryProtocol.prototype.writeMapEnd = function() {
+};
+
+TBinaryProtocol.prototype.writeListBegin = function(etype, size) {
+ this.writeByte(etype);
+ this.writeI32(size);
+};
+
+TBinaryProtocol.prototype.writeListEnd = function() {
+};
+
+TBinaryProtocol.prototype.writeSetBegin = function(etype, size) {
+ this.writeByte(etype);
+ this.writeI32(size);
+};
+
+TBinaryProtocol.prototype.writeSetEnd = function() {
+};
+
+TBinaryProtocol.prototype.writeBool = function(bool) {
+ if (bool) {
+ this.writeByte(1);
+ } else {
+ this.writeByte(0);
+ }
+};
+
+TBinaryProtocol.prototype.writeByte = function(b) {
+ this.trans.write(new Buffer([b]));
+};
+
+TBinaryProtocol.prototype.writeI16 = function(i16) {
+ this.trans.write(binary.writeI16(new Buffer(2), i16));
+};
+
+TBinaryProtocol.prototype.writeI32 = function(i32) {
+ this.trans.write(binary.writeI32(new Buffer(4), i32));
+};
+
+TBinaryProtocol.prototype.writeI64 = function(i64) {
+ if (i64.buffer) {
+ this.trans.write(i64.buffer);
+ } else {
+ this.trans.write(new Int64(i64).buffer);
+ }
+};
+
+TBinaryProtocol.prototype.writeDouble = function(dub) {
+ this.trans.write(binary.writeDouble(new Buffer(8), dub));
+};
+
+TBinaryProtocol.prototype.writeStringOrBinary = function(name, encoding, arg) {
+ if (typeof(arg) === 'string') {
+ this.writeI32(Buffer.byteLength(arg, encoding));
+ this.trans.write(new Buffer(arg, encoding));
+ } else if ((arg instanceof Buffer) ||
+ (Object.prototype.toString.call(arg) == '[object Uint8Array]')) {
+ // Buffers in Node.js under Browserify may extend UInt8Array instead of
+ // defining a new object. We detect them here so we can write them
+ // correctly
+ this.writeI32(arg.length);
+ this.trans.write(arg);
+ } else {
+ throw new Error(name + ' called without a string/Buffer argument: ' + arg);
+ }
+};
+
+TBinaryProtocol.prototype.writeString = function(arg) {
+ this.writeStringOrBinary('writeString', 'utf8', arg);
+};
+
+TBinaryProtocol.prototype.writeBinary = function(arg) {
+ this.writeStringOrBinary('writeBinary', 'binary', arg);
+};
+
+TBinaryProtocol.prototype.readMessageBegin = function() {
+ var sz = this.readI32();
+ var type, name, seqid;
+
+ if (sz < 0) {
+ var version = sz & VERSION_MASK;
+ if (version != VERSION_1) {
+ throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.BAD_VERSION, "Bad version in readMessageBegin: " + sz);
+ }
+ type = sz & TYPE_MASK;
+ name = this.readString();
+ seqid = this.readI32();
+ } else {
+ if (this.strictRead) {
+ throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.BAD_VERSION, "No protocol version header");
+ }
+ name = this.trans.read(sz);
+ type = this.readByte();
+ seqid = this.readI32();
+ }
+ return {fname: name, mtype: type, rseqid: seqid};
+};
+
+TBinaryProtocol.prototype.readMessageEnd = function() {
+};
+
+TBinaryProtocol.prototype.readStructBegin = function() {
+ return {fname: ''};
+};
+
+TBinaryProtocol.prototype.readStructEnd = function() {
+};
+
+TBinaryProtocol.prototype.readFieldBegin = function() {
+ var type = this.readByte();
+ if (type == Type.STOP) {
+ return {fname: null, ftype: type, fid: 0};
+ }
+ var id = this.readI16();
+ return {fname: null, ftype: type, fid: id};
+};
+
+TBinaryProtocol.prototype.readFieldEnd = function() {
+};
+
+TBinaryProtocol.prototype.readMapBegin = function() {
+ var ktype = this.readByte();
+ var vtype = this.readByte();
+ var size = this.readI32();
+ return {ktype: ktype, vtype: vtype, size: size};
+};
+
+TBinaryProtocol.prototype.readMapEnd = function() {
+};
+
+TBinaryProtocol.prototype.readListBegin = function() {
+ var etype = this.readByte();
+ var size = this.readI32();
+ return {etype: etype, size: size};
+};
+
+TBinaryProtocol.prototype.readListEnd = function() {
+};
+
+TBinaryProtocol.prototype.readSetBegin = function() {
+ var etype = this.readByte();
+ var size = this.readI32();
+ return {etype: etype, size: size};
+};
+
+TBinaryProtocol.prototype.readSetEnd = function() {
+};
+
+TBinaryProtocol.prototype.readBool = function() {
+ var b = this.readByte();
+ if (b === 0) {
+ return false;
+ }
+ return true;
+};
+
+TBinaryProtocol.prototype.readByte = function() {
+ return this.trans.readByte();
+};
+
+TBinaryProtocol.prototype.readI16 = function() {
+ return this.trans.readI16();
+};
+
+TBinaryProtocol.prototype.readI32 = function() {
+ return this.trans.readI32();
+};
+
+TBinaryProtocol.prototype.readI64 = function() {
+ var buff = this.trans.read(8);
+ return new Int64(buff);
+};
+
+TBinaryProtocol.prototype.readDouble = function() {
+ return this.trans.readDouble();
+};
+
+TBinaryProtocol.prototype.readBinary = function() {
+ var len = this.readI32();
+ if (len === 0) {
+ return new Buffer(0);
+ }
+
+ if (len < 0) {
+ throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative binary size");
+ }
+ return this.trans.read(len);
+};
+
+TBinaryProtocol.prototype.readString = function() {
+ var len = this.readI32();
+ if (len === 0) {
+ return "";
+ }
+
+ if (len < 0) {
+ throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative string size");
+ }
+ return this.trans.readString(len);
+};
+
+TBinaryProtocol.prototype.getTransport = function() {
+ return this.trans;
+};
+
+TBinaryProtocol.prototype.skip = function(type) {
+ switch (type) {
+ case Type.BOOL:
+ this.readBool();
+ break;
+ case Type.BYTE:
+ this.readByte();
+ break;
+ case Type.I16:
+ this.readI16();
+ break;
+ case Type.I32:
+ this.readI32();
+ break;
+ case Type.I64:
+ this.readI64();
+ break;
+ case Type.DOUBLE:
+ this.readDouble();
+ break;
+ case Type.STRING:
+ this.readString();
+ break;
+ case Type.STRUCT:
+ this.readStructBegin();
+ while (true) {
+ var r = this.readFieldBegin();
+ if (r.ftype === Type.STOP) {
+ break;
+ }
+ this.skip(r.ftype);
+ this.readFieldEnd();
+ }
+ this.readStructEnd();
+ break;
+ case Type.MAP:
+ var mapBegin = this.readMapBegin();
+ for (var i = 0; i < mapBegin.size; ++i) {
+ this.skip(mapBegin.ktype);
+ this.skip(mapBegin.vtype);
+ }
+ this.readMapEnd();
+ break;
+ case Type.SET:
+ var setBegin = this.readSetBegin();
+ for (var i2 = 0; i2 < setBegin.size; ++i2) {
+ this.skip(setBegin.etype);
+ }
+ this.readSetEnd();
+ break;
+ case Type.LIST:
+ var listBegin = this.readListBegin();
+ for (var i3 = 0; i3 < listBegin.size; ++i3) {
+ this.skip(listBegin.etype);
+ }
+ this.readListEnd();
+ break;
+ default:
+ throw new Error("Invalid type: " + type);
+ }
+};
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/browser.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/browser.js
new file mode 100644
index 000000000..67ce8535b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/browser.js
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+exports.Thrift = require('./thrift');
+
+var xhrConnection = require('./xhr_connection');
+exports.XHRConnection = xhrConnection.XHRConnection;
+exports.createXHRConnection = xhrConnection.createXHRConnection;
+exports.createXHRClient = xhrConnection.createXHRClient;
+
+exports.Multiplexer = require('./multiplexed_protocol').Multiplexer;
+
+exports.TWebSocketTransport = require('./ws_transport');
+exports.TBufferedTransport = require('./buffered_transport');
+exports.TFramedTransport = require('./framed_transport');
+
+exports.Protocol = exports.TJSONProtocol = require('./json_protocol');
+exports.TBinaryProtocol = require('./binary_protocol');
+exports.TCompactProtocol = require('./compact_protocol');
+
+exports.Int64 = require('node-int64');
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/buffered_transport.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/buffered_transport.js
new file mode 100644
index 000000000..113e21616
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/buffered_transport.js
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var binary = require('./binary');
+var InputBufferUnderrunError = require('./input_buffer_underrun_error');
+var THeaderTransport = require('./header_transport');
+
+module.exports = TBufferedTransport;
+
+function TBufferedTransport(buffer, callback) {
+ this.defaultReadBufferSize = 1024;
+ this.writeBufferSize = 512; // Soft Limit
+ this.inBuf = new Buffer(this.defaultReadBufferSize);
+ this.readCursor = 0;
+ this.writeCursor = 0; // for input buffer
+ this.outBuffers = [];
+ this.outCount = 0;
+ this.onFlush = callback;
+};
+
+TBufferedTransport.prototype = new THeaderTransport();
+
+TBufferedTransport.prototype.reset = function() {
+ this.inBuf = new Buffer(this.defaultReadBufferSize);
+ this.readCursor = 0;
+ this.writeCursor = 0;
+ this.outBuffers = [];
+ this.outCount = 0;
+}
+
+TBufferedTransport.receiver = function(callback, seqid) {
+ var reader = new TBufferedTransport();
+
+ return function(data) {
+ if (reader.writeCursor + data.length > reader.inBuf.length) {
+ var buf = new Buffer(reader.writeCursor + data.length);
+ reader.inBuf.copy(buf, 0, 0, reader.writeCursor);
+ reader.inBuf = buf;
+ }
+ data.copy(reader.inBuf, reader.writeCursor, 0);
+ reader.writeCursor += data.length;
+
+ callback(reader, seqid);
+ };
+};
+
+
+TBufferedTransport.prototype.commitPosition = function(){
+ var unreadSize = this.writeCursor - this.readCursor;
+ var bufSize = (unreadSize * 2 > this.defaultReadBufferSize) ?
+ unreadSize * 2 : this.defaultReadBufferSize;
+ var buf = new Buffer(bufSize);
+ if (unreadSize > 0) {
+ this.inBuf.copy(buf, 0, this.readCursor, this.writeCursor);
+ }
+ this.readCursor = 0;
+ this.writeCursor = unreadSize;
+ this.inBuf = buf;
+};
+
+TBufferedTransport.prototype.rollbackPosition = function(){
+ this.readCursor = 0;
+}
+
+ // TODO: Implement open/close support
+TBufferedTransport.prototype.isOpen = function() {
+ return true;
+};
+
+TBufferedTransport.prototype.open = function() {
+};
+
+TBufferedTransport.prototype.close = function() {
+};
+
+ // Set the seqid of the message in the client
+ // So that callbacks can be found
+TBufferedTransport.prototype.setCurrSeqId = function(seqid) {
+ this._seqid = seqid;
+};
+
+TBufferedTransport.prototype.ensureAvailable = function(len) {
+ if (this.readCursor + len > this.writeCursor) {
+ throw new InputBufferUnderrunError();
+ }
+};
+
+TBufferedTransport.prototype.read = function(len) {
+ this.ensureAvailable(len);
+ var buf = new Buffer(len);
+ this.inBuf.copy(buf, 0, this.readCursor, this.readCursor + len);
+ this.readCursor += len;
+ return buf;
+};
+
+TBufferedTransport.prototype.readByte = function() {
+ this.ensureAvailable(1);
+ return binary.readByte(this.inBuf[this.readCursor++]);
+};
+
+TBufferedTransport.prototype.readI16 = function() {
+ this.ensureAvailable(2);
+ var i16 = binary.readI16(this.inBuf, this.readCursor);
+ this.readCursor += 2;
+ return i16;
+};
+
+TBufferedTransport.prototype.readI32 = function() {
+ this.ensureAvailable(4);
+ var i32 = binary.readI32(this.inBuf, this.readCursor);
+ this.readCursor += 4;
+ return i32;
+};
+
+TBufferedTransport.prototype.readDouble = function() {
+ this.ensureAvailable(8);
+ var d = binary.readDouble(this.inBuf, this.readCursor);
+ this.readCursor += 8;
+ return d;
+};
+
+TBufferedTransport.prototype.readString = function(len) {
+ this.ensureAvailable(len);
+ var str = this.inBuf.toString('utf8', this.readCursor, this.readCursor + len);
+ this.readCursor += len;
+ return str;
+};
+
+TBufferedTransport.prototype.borrow = function() {
+ var obj = {buf: this.inBuf, readIndex: this.readCursor, writeIndex: this.writeCursor};
+ return obj;
+};
+
+TBufferedTransport.prototype.consume = function(bytesConsumed) {
+ this.readCursor += bytesConsumed;
+};
+
+TBufferedTransport.prototype.write = function(buf) {
+ if (typeof(buf) === "string") {
+ buf = new Buffer(buf, 'utf8');
+ }
+ this.outBuffers.push(buf);
+ this.outCount += buf.length;
+};
+
+TBufferedTransport.prototype.flush = function() {
+ // If the seqid of the callback is available pass it to the onFlush
+ // Then remove the current seqid
+ var seqid = this._seqid;
+ this._seqid = null;
+
+ if (this.outCount < 1) {
+ return;
+ }
+
+ var msg = new Buffer(this.outCount),
+ pos = 0;
+ this.outBuffers.forEach(function(buf) {
+ buf.copy(msg, pos, 0);
+ pos += buf.length;
+ });
+
+ if (this.onFlush) {
+ // Passing seqid through this call to get it to the connection
+ this.onFlush(msg, seqid);
+ }
+
+ this.outBuffers = [];
+ this.outCount = 0;
+}
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/compact_protocol.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/compact_protocol.js
new file mode 100644
index 000000000..302a88d4d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/compact_protocol.js
@@ -0,0 +1,915 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var log = require('./log');
+var Int64 = require('node-int64');
+var Thrift = require('./thrift');
+var Type = Thrift.Type;
+
+module.exports = TCompactProtocol;
+
+var POW_8 = Math.pow(2, 8);
+var POW_24 = Math.pow(2, 24);
+var POW_32 = Math.pow(2, 32);
+var POW_40 = Math.pow(2, 40);
+var POW_48 = Math.pow(2, 48);
+var POW_52 = Math.pow(2, 52);
+var POW_1022 = Math.pow(2, 1022);
+
+/**
+ * Constructor Function for the Compact Protocol.
+ * @constructor
+ * @param {object} [trans] - The underlying transport to read/write.
+ * @classdesc The Apache Thrift Protocol layer performs serialization
+ * of base types, the compact protocol serializes data in binary
+ * form with minimal space used for scalar values.
+ */
+function TCompactProtocol(trans) {
+ this.trans = trans;
+ this.lastField_ = [];
+ this.lastFieldId_ = 0;
+ this.string_limit_ = 0;
+ this.string_buf_ = null;
+ this.string_buf_size_ = 0;
+ this.container_limit_ = 0;
+ this.booleanField_ = {
+ name: null,
+ hasBoolValue: false
+ };
+ this.boolValue_ = {
+ hasBoolValue: false,
+ boolValue: false
+ };
+};
+
+
+//
+// Compact Protocol Constants
+//
+
+/**
+ * Compact Protocol ID number.
+ * @readonly
+ * @const {number} PROTOCOL_ID
+ */
+TCompactProtocol.PROTOCOL_ID = -126; //1000 0010
+
+/**
+ * Compact Protocol version number.
+ * @readonly
+ * @const {number} VERSION_N
+ */
+TCompactProtocol.VERSION_N = 1;
+
+/**
+ * Compact Protocol version mask for combining protocol version and message type in one byte.
+ * @readonly
+ * @const {number} VERSION_MASK
+ */
+TCompactProtocol.VERSION_MASK = 0x1f; //0001 1111
+
+/**
+ * Compact Protocol message type mask for combining protocol version and message type in one byte.
+ * @readonly
+ * @const {number} TYPE_MASK
+ */
+TCompactProtocol.TYPE_MASK = -32; //1110 0000
+
+/**
+ * Compact Protocol message type bits for ensuring message type bit size.
+ * @readonly
+ * @const {number} TYPE_BITS
+ */
+TCompactProtocol.TYPE_BITS = 7; //0000 0111
+
+/**
+ * Compact Protocol message type shift amount for combining protocol version and message type in one byte.
+ * @readonly
+ * @const {number} TYPE_SHIFT_AMOUNT
+ */
+TCompactProtocol.TYPE_SHIFT_AMOUNT = 5;
+
+/**
+ * Compact Protocol type IDs used to keep type data within one nibble.
+ * @readonly
+ * @property {number} CT_STOP - End of a set of fields.
+ * @property {number} CT_BOOLEAN_TRUE - Flag for Boolean field with true value (packed field and value).
+ * @property {number} CT_BOOLEAN_FALSE - Flag for Boolean field with false value (packed field and value).
+ * @property {number} CT_BYTE - Signed 8 bit integer.
+ * @property {number} CT_I16 - Signed 16 bit integer.
+ * @property {number} CT_I32 - Signed 32 bit integer.
+ * @property {number} CT_I64 - Signed 64 bit integer (2^53 max in JavaScript).
+ * @property {number} CT_DOUBLE - 64 bit IEEE 854 floating point.
+ * @property {number} CT_BINARY - Array of bytes (used for strings also).
+ * @property {number} CT_LIST - A collection type (unordered).
+ * @property {number} CT_SET - A collection type (unordered and without repeated values).
+ * @property {number} CT_MAP - A collection type (map/associative-array/dictionary).
+ * @property {number} CT_STRUCT - A multifield type.
+ */
+TCompactProtocol.Types = {
+ CT_STOP: 0x00,
+ CT_BOOLEAN_TRUE: 0x01,
+ CT_BOOLEAN_FALSE: 0x02,
+ CT_BYTE: 0x03,
+ CT_I16: 0x04,
+ CT_I32: 0x05,
+ CT_I64: 0x06,
+ CT_DOUBLE: 0x07,
+ CT_BINARY: 0x08,
+ CT_LIST: 0x09,
+ CT_SET: 0x0A,
+ CT_MAP: 0x0B,
+ CT_STRUCT: 0x0C
+};
+
+/**
+ * Array mapping Compact type IDs to standard Thrift type IDs.
+ * @readonly
+ */
+TCompactProtocol.TTypeToCType = [
+ TCompactProtocol.Types.CT_STOP, // T_STOP
+ 0, // unused
+ TCompactProtocol.Types.CT_BOOLEAN_TRUE, // T_BOOL
+ TCompactProtocol.Types.CT_BYTE, // T_BYTE
+ TCompactProtocol.Types.CT_DOUBLE, // T_DOUBLE
+ 0, // unused
+ TCompactProtocol.Types.CT_I16, // T_I16
+ 0, // unused
+ TCompactProtocol.Types.CT_I32, // T_I32
+ 0, // unused
+ TCompactProtocol.Types.CT_I64, // T_I64
+ TCompactProtocol.Types.CT_BINARY, // T_STRING
+ TCompactProtocol.Types.CT_STRUCT, // T_STRUCT
+ TCompactProtocol.Types.CT_MAP, // T_MAP
+ TCompactProtocol.Types.CT_SET, // T_SET
+ TCompactProtocol.Types.CT_LIST, // T_LIST
+];
+
+
+//
+// Compact Protocol Utilities
+//
+
+/**
+ * Returns the underlying transport layer.
+ * @return {object} The underlying transport layer.
+ */TCompactProtocol.prototype.getTransport = function() {
+ return this.trans;
+};
+
+/**
+ * Lookup a Compact Protocol Type value for a given Thrift Type value.
+ * N.B. Used only internally.
+ * @param {number} ttype - Thrift type value
+ * @returns {number} Compact protocol type value
+ */
+TCompactProtocol.prototype.getCompactType = function(ttype) {
+ return TCompactProtocol.TTypeToCType[ttype];
+};
+
+/**
+ * Lookup a Thrift Type value for a given Compact Protocol Type value.
+ * N.B. Used only internally.
+ * @param {number} type - Compact Protocol type value
+ * @returns {number} Thrift Type value
+ */
+TCompactProtocol.prototype.getTType = function(type) {
+ switch (type) {
+ case Type.STOP:
+ return Type.STOP;
+ case TCompactProtocol.Types.CT_BOOLEAN_FALSE:
+ case TCompactProtocol.Types.CT_BOOLEAN_TRUE:
+ return Type.BOOL;
+ case TCompactProtocol.Types.CT_BYTE:
+ return Type.BYTE;
+ case TCompactProtocol.Types.CT_I16:
+ return Type.I16;
+ case TCompactProtocol.Types.CT_I32:
+ return Type.I32;
+ case TCompactProtocol.Types.CT_I64:
+ return Type.I64;
+ case TCompactProtocol.Types.CT_DOUBLE:
+ return Type.DOUBLE;
+ case TCompactProtocol.Types.CT_BINARY:
+ return Type.STRING;
+ case TCompactProtocol.Types.CT_LIST:
+ return Type.LIST;
+ case TCompactProtocol.Types.CT_SET:
+ return Type.SET;
+ case TCompactProtocol.Types.CT_MAP:
+ return Type.MAP;
+ case TCompactProtocol.Types.CT_STRUCT:
+ return Type.STRUCT;
+ default:
+ throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.INVALID_DATA, "Unknown type: " + type);
+ }
+ return Type.STOP;
+};
+
+
+//
+// Compact Protocol write operations
+//
+
+/**
+ * Send any buffered bytes to the end point.
+ */
+TCompactProtocol.prototype.flush = function() {
+ return this.trans.flush();
+};
+
+/**
+ * Writes an RPC message header
+ * @param {string} name - The method name for the message.
+ * @param {number} type - The type of message (CALL, REPLY, EXCEPTION, ONEWAY).
+ * @param {number} seqid - The call sequence number (if any).
+ */
+TCompactProtocol.prototype.writeMessageBegin = function(name, type, seqid) {
+ this.writeByte(TCompactProtocol.PROTOCOL_ID);
+ this.writeByte((TCompactProtocol.VERSION_N & TCompactProtocol.VERSION_MASK) |
+ ((type << TCompactProtocol.TYPE_SHIFT_AMOUNT) & TCompactProtocol.TYPE_MASK));
+ this.writeVarint32(seqid);
+ this.writeString(name);
+
+ // Record client seqid to find callback again
+ if (this._seqid) {
+ log.warning('SeqId already set', { 'name': name });
+ } else {
+ this._seqid = seqid;
+ this.trans.setCurrSeqId(seqid);
+ }
+};
+
+TCompactProtocol.prototype.writeMessageEnd = function() {
+};
+
+TCompactProtocol.prototype.writeStructBegin = function(name) {
+ this.lastField_.push(this.lastFieldId_);
+ this.lastFieldId_ = 0;
+};
+
+TCompactProtocol.prototype.writeStructEnd = function() {
+ this.lastFieldId_ = this.lastField_.pop();
+};
+
+/**
+ * Writes a struct field header
+ * @param {string} name - The field name (not written with the compact protocol).
+ * @param {number} type - The field data type (a normal Thrift field Type).
+ * @param {number} id - The IDL field Id.
+ */
+TCompactProtocol.prototype.writeFieldBegin = function(name, type, id) {
+ if (type != Type.BOOL) {
+ return this.writeFieldBeginInternal(name, type, id, -1);
+ }
+
+ this.booleanField_.name = name;
+ this.booleanField_.fieldType = type;
+ this.booleanField_.fieldId = id;
+};
+
+TCompactProtocol.prototype.writeFieldEnd = function() {
+};
+
+TCompactProtocol.prototype.writeFieldStop = function() {
+ this.writeByte(TCompactProtocol.Types.CT_STOP);
+};
+
+/**
+ * Writes a map collection header
+ * @param {number} keyType - The Thrift type of the map keys.
+ * @param {number} valType - The Thrift type of the map values.
+ * @param {number} size - The number of k/v pairs in the map.
+ */
+TCompactProtocol.prototype.writeMapBegin = function(keyType, valType, size) {
+ if (size === 0) {
+ this.writeByte(0);
+ } else {
+ this.writeVarint32(size);
+ this.writeByte(this.getCompactType(keyType) << 4 | this.getCompactType(valType));
+ }
+};
+
+TCompactProtocol.prototype.writeMapEnd = function() {
+};
+
+/**
+ * Writes a list collection header
+ * @param {number} elemType - The Thrift type of the list elements.
+ * @param {number} size - The number of elements in the list.
+ */
+TCompactProtocol.prototype.writeListBegin = function(elemType, size) {
+ this.writeCollectionBegin(elemType, size);
+};
+
+TCompactProtocol.prototype.writeListEnd = function() {
+};
+
+/**
+ * Writes a set collection header
+ * @param {number} elemType - The Thrift type of the set elements.
+ * @param {number} size - The number of elements in the set.
+ */
+TCompactProtocol.prototype.writeSetBegin = function(elemType, size) {
+ this.writeCollectionBegin(elemType, size);
+};
+
+TCompactProtocol.prototype.writeSetEnd = function() {
+};
+
+TCompactProtocol.prototype.writeBool = function(value) {
+ if (this.booleanField_.name !== null) {
+ // we haven't written the field header yet
+ this.writeFieldBeginInternal(this.booleanField_.name,
+ this.booleanField_.fieldType,
+ this.booleanField_.fieldId,
+ (value ? TCompactProtocol.Types.CT_BOOLEAN_TRUE
+ : TCompactProtocol.Types.CT_BOOLEAN_FALSE));
+ this.booleanField_.name = null;
+ } else {
+ // we're not part of a field, so just write the value
+ this.writeByte((value ? TCompactProtocol.Types.CT_BOOLEAN_TRUE
+ : TCompactProtocol.Types.CT_BOOLEAN_FALSE));
+ }
+};
+
+TCompactProtocol.prototype.writeByte = function(b) {
+ this.trans.write(new Buffer([b]));
+};
+
+TCompactProtocol.prototype.writeI16 = function(i16) {
+ this.writeVarint32(this.i32ToZigzag(i16));
+};
+
+TCompactProtocol.prototype.writeI32 = function(i32) {
+ this.writeVarint32(this.i32ToZigzag(i32));
+};
+
+TCompactProtocol.prototype.writeI64 = function(i64) {
+ this.writeVarint64(this.i64ToZigzag(i64));
+};
+
+// Little-endian, unlike TBinaryProtocol
+TCompactProtocol.prototype.writeDouble = function(v) {
+ var buff = new Buffer(8);
+ var m, e, c;
+
+ buff[7] = (v < 0 ? 0x80 : 0x00);
+
+ v = Math.abs(v);
+ if (v !== v) {
+ // NaN, use QNaN IEEE format
+ m = 2251799813685248;
+ e = 2047;
+ } else if (v === Infinity) {
+ m = 0;
+ e = 2047;
+ } else {
+ e = Math.floor(Math.log(v) / Math.LN2);
+ c = Math.pow(2, -e);
+ if (v * c < 1) {
+ e--;
+ c *= 2;
+ }
+
+ if (e + 1023 >= 2047)
+ {
+ // Overflow
+ m = 0;
+ e = 2047;
+ }
+ else if (e + 1023 >= 1)
+ {
+ // Normalized - term order matters, as Math.pow(2, 52-e) and v*Math.pow(2, 52) can overflow
+ m = (v*c-1) * POW_52;
+ e += 1023;
+ }
+ else
+ {
+ // Denormalized - also catches the '0' case, somewhat by chance
+ m = (v * POW_1022) * POW_52;
+ e = 0;
+ }
+ }
+
+ buff[6] = (e << 4) & 0xf0;
+ buff[7] |= (e >> 4) & 0x7f;
+
+ buff[0] = m & 0xff;
+ m = Math.floor(m / POW_8);
+ buff[1] = m & 0xff;
+ m = Math.floor(m / POW_8);
+ buff[2] = m & 0xff;
+ m = Math.floor(m / POW_8);
+ buff[3] = m & 0xff;
+ m >>= 8;
+ buff[4] = m & 0xff;
+ m >>= 8;
+ buff[5] = m & 0xff;
+ m >>= 8;
+ buff[6] |= m & 0x0f;
+
+ this.trans.write(buff);
+};
+
+TCompactProtocol.prototype.writeStringOrBinary = function(name, encoding, arg) {
+ if (typeof arg === 'string') {
+ this.writeVarint32(Buffer.byteLength(arg, encoding)) ;
+ this.trans.write(new Buffer(arg, encoding));
+ } else if (arg instanceof Buffer ||
+ Object.prototype.toString.call(arg) == '[object Uint8Array]') {
+ // Buffers in Node.js under Browserify may extend UInt8Array instead of
+ // defining a new object. We detect them here so we can write them
+ // correctly
+ this.writeVarint32(arg.length);
+ this.trans.write(arg);
+ } else {
+ throw new Error(name + ' called without a string/Buffer argument: ' + arg);
+ }
+};
+
+TCompactProtocol.prototype.writeString = function(arg) {
+ this.writeStringOrBinary('writeString', 'utf8', arg);
+};
+
+TCompactProtocol.prototype.writeBinary = function(arg) {
+ this.writeStringOrBinary('writeBinary', 'binary', arg);
+};
+
+
+//
+// Compact Protocol internal write methods
+//
+
+TCompactProtocol.prototype.writeFieldBeginInternal = function(name,
+ fieldType,
+ fieldId,
+ typeOverride) {
+ //If there's a type override, use that.
+ var typeToWrite = (typeOverride == -1 ? this.getCompactType(fieldType) : typeOverride);
+ //Check if we can delta encode the field id
+ if (fieldId > this.lastFieldId_ && fieldId - this.lastFieldId_ <= 15) {
+ //Include the type delta with the field ID
+ this.writeByte((fieldId - this.lastFieldId_) << 4 | typeToWrite);
+ } else {
+ //Write separate type and ID values
+ this.writeByte(typeToWrite);
+ this.writeI16(fieldId);
+ }
+ this.lastFieldId_ = fieldId;
+};
+
+TCompactProtocol.prototype.writeCollectionBegin = function(elemType, size) {
+ if (size <= 14) {
+ //Combine size and type in one byte if possible
+ this.writeByte(size << 4 | this.getCompactType(elemType));
+ } else {
+ this.writeByte(0xf0 | this.getCompactType(elemType));
+ this.writeVarint32(size);
+ }
+};
+
+/**
+ * Write an i32 as a varint. Results in 1-5 bytes on the wire.
+ */
+TCompactProtocol.prototype.writeVarint32 = function(n) {
+ var buf = new Buffer(5);
+ var wsize = 0;
+ while (true) {
+ if ((n & ~0x7F) === 0) {
+ buf[wsize++] = n;
+ break;
+ } else {
+ buf[wsize++] = ((n & 0x7F) | 0x80);
+ n = n >>> 7;
+ }
+ }
+ var wbuf = new Buffer(wsize);
+ buf.copy(wbuf,0,0,wsize);
+ this.trans.write(wbuf);
+};
+
+/**
+ * Write an i64 as a varint. Results in 1-10 bytes on the wire.
+ * N.B. node-int64 is always big endian
+ */
+TCompactProtocol.prototype.writeVarint64 = function(n) {
+ if (typeof n === "number"){
+ n = new Int64(n);
+ }
+ if (! (n instanceof Int64)) {
+ throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.INVALID_DATA, "Expected Int64 or Number, found: " + n);
+ }
+
+ var buf = new Buffer(10);
+ var wsize = 0;
+ var hi = n.buffer.readUInt32BE(0, true);
+ var lo = n.buffer.readUInt32BE(4, true);
+ var mask = 0;
+ while (true) {
+ if (((lo & ~0x7F) === 0) && (hi === 0)) {
+ buf[wsize++] = lo;
+ break;
+ } else {
+ buf[wsize++] = ((lo & 0x7F) | 0x80);
+ mask = hi << 25;
+ lo = lo >>> 7;
+ hi = hi >>> 7;
+ lo = lo | mask;
+ }
+ }
+ var wbuf = new Buffer(wsize);
+ buf.copy(wbuf,0,0,wsize);
+ this.trans.write(wbuf);
+};
+
+/**
+ * Convert l into a zigzag long. This allows negative numbers to be
+ * represented compactly as a varint.
+ */
+TCompactProtocol.prototype.i64ToZigzag = function(l) {
+ if (typeof l === 'string') {
+ l = new Int64(parseInt(l, 10));
+ } else if (typeof l === 'number') {
+ l = new Int64(l);
+ }
+ if (! (l instanceof Int64)) {
+ throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.INVALID_DATA, "Expected Int64 or Number, found: " + l);
+ }
+ var hi = l.buffer.readUInt32BE(0, true);
+ var lo = l.buffer.readUInt32BE(4, true);
+ var sign = hi >>> 31;
+ hi = ((hi << 1) | (lo >>> 31)) ^ ((!!sign) ? 0xFFFFFFFF : 0);
+ lo = (lo << 1) ^ ((!!sign) ? 0xFFFFFFFF : 0);
+ return new Int64(hi, lo);
+};
+
+/**
+ * Convert n into a zigzag int. This allows negative numbers to be
+ * represented compactly as a varint.
+ */
+TCompactProtocol.prototype.i32ToZigzag = function(n) {
+ return (n << 1) ^ ((n & 0x80000000) ? 0xFFFFFFFF : 0);
+};
+
+
+//
+// Compact Protocol read operations
+//
+
+TCompactProtocol.prototype.readMessageBegin = function() {
+ //Read protocol ID
+ var protocolId = this.trans.readByte();
+ if (protocolId != TCompactProtocol.PROTOCOL_ID) {
+ throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.BAD_VERSION, "Bad protocol identifier " + protocolId);
+ }
+
+ //Read Version and Type
+ var versionAndType = this.trans.readByte();
+ var version = (versionAndType & TCompactProtocol.VERSION_MASK);
+ if (version != TCompactProtocol.VERSION_N) {
+ throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.BAD_VERSION, "Bad protocol version " + version);
+ }
+ var type = ((versionAndType >> TCompactProtocol.TYPE_SHIFT_AMOUNT) & TCompactProtocol.TYPE_BITS);
+
+ //Read SeqId
+ var seqid = this.readVarint32();
+
+ //Read name
+ var name = this.readString();
+
+ return {fname: name, mtype: type, rseqid: seqid};
+};
+
+TCompactProtocol.prototype.readMessageEnd = function() {
+};
+
+TCompactProtocol.prototype.readStructBegin = function() {
+ this.lastField_.push(this.lastFieldId_);
+ this.lastFieldId_ = 0;
+ return {fname: ''};
+};
+
+TCompactProtocol.prototype.readStructEnd = function() {
+ this.lastFieldId_ = this.lastField_.pop();
+};
+
+TCompactProtocol.prototype.readFieldBegin = function() {
+ var fieldId = 0;
+ var b = this.trans.readByte(b);
+ var type = (b & 0x0f);
+
+ if (type == TCompactProtocol.Types.CT_STOP) {
+ return {fname: null, ftype: Thrift.Type.STOP, fid: 0};
+ }
+
+ //Mask off the 4 MSB of the type header to check for field id delta.
+ var modifier = ((b & 0x000000f0) >>> 4);
+ if (modifier === 0) {
+ //If not a delta read the field id.
+ fieldId = this.readI16();
+ } else {
+ //Recover the field id from the delta
+ fieldId = (this.lastFieldId_ + modifier);
+ }
+ var fieldType = this.getTType(type);
+
+ //Boolean are encoded with the type
+ if (type == TCompactProtocol.Types.CT_BOOLEAN_TRUE ||
+ type == TCompactProtocol.Types.CT_BOOLEAN_FALSE) {
+ this.boolValue_.hasBoolValue = true;
+ this.boolValue_.boolValue =
+ (type == TCompactProtocol.Types.CT_BOOLEAN_TRUE ? true : false);
+ }
+
+ //Save the new field for the next delta computation.
+ this.lastFieldId_ = fieldId;
+ return {fname: null, ftype: fieldType, fid: fieldId};
+};
+
+TCompactProtocol.prototype.readFieldEnd = function() {
+};
+
+TCompactProtocol.prototype.readMapBegin = function() {
+ var msize = this.readVarint32();
+ if (msize < 0) {
+ throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative map size");
+ }
+
+ var kvType = 0;
+ if (msize !== 0) {
+ kvType = this.trans.readByte();
+ }
+
+ var keyType = this.getTType((kvType & 0xf0) >>> 4);
+ var valType = this.getTType(kvType & 0xf);
+ return {ktype: keyType, vtype: valType, size: msize};
+};
+
+TCompactProtocol.prototype.readMapEnd = function() {
+};
+
+TCompactProtocol.prototype.readListBegin = function() {
+ var size_and_type = this.trans.readByte();
+
+ var lsize = (size_and_type >>> 4) & 0x0000000f;
+ if (lsize == 15) {
+ lsize = this.readVarint32();
+ }
+
+ if (lsize < 0) {
+ throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative list size");
+ }
+
+ var elemType = this.getTType(size_and_type & 0x0000000f);
+
+ return {etype: elemType, size: lsize};
+};
+
+TCompactProtocol.prototype.readListEnd = function() {
+};
+
+TCompactProtocol.prototype.readSetBegin = function() {
+ return this.readListBegin();
+};
+
+TCompactProtocol.prototype.readSetEnd = function() {
+};
+
+TCompactProtocol.prototype.readBool = function() {
+ var value = false;
+ var rsize = 0;
+ if (this.boolValue_.hasBoolValue === true) {
+ value = this.boolValue_.boolValue;
+ this.boolValue_.hasBoolValue = false;
+ } else {
+ var res = this.trans.readByte();
+ rsize = res.rsize;
+ value = (res.value == TCompactProtocol.Types.CT_BOOLEAN_TRUE);
+ }
+ return value;
+};
+
+TCompactProtocol.prototype.readByte = function() {
+ return this.trans.readByte();
+};
+
+TCompactProtocol.prototype.readI16 = function() {
+ return this.readI32();
+};
+
+TCompactProtocol.prototype.readI32 = function() {
+ return this.zigzagToI32(this.readVarint32());
+};
+
+TCompactProtocol.prototype.readI64 = function() {
+ return this.zigzagToI64(this.readVarint64());
+};
+
+// Little-endian, unlike TBinaryProtocol
+TCompactProtocol.prototype.readDouble = function() {
+ var buff = this.trans.read(8);
+ var off = 0;
+
+ var signed = buff[off + 7] & 0x80;
+ var e = (buff[off+6] & 0xF0) >> 4;
+ e += (buff[off+7] & 0x7F) << 4;
+
+ var m = buff[off];
+ m += buff[off+1] << 8;
+ m += buff[off+2] << 16;
+ m += buff[off+3] * POW_24;
+ m += buff[off+4] * POW_32;
+ m += buff[off+5] * POW_40;
+ m += (buff[off+6] & 0x0F) * POW_48;
+
+ switch (e) {
+ case 0:
+ e = -1022;
+ break;
+ case 2047:
+ return m ? NaN : (signed ? -Infinity : Infinity);
+ default:
+ m += POW_52;
+ e -= 1023;
+ }
+
+ if (signed) {
+ m *= -1;
+ }
+
+ return m * Math.pow(2, e - 52);
+};
+
+TCompactProtocol.prototype.readBinary = function() {
+ var size = this.readVarint32();
+ if (size === 0) {
+ return new Buffer(0);
+ }
+
+ if (size < 0) {
+ throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative binary size");
+ }
+ return this.trans.read(size);
+};
+
+TCompactProtocol.prototype.readString = function() {
+ var size = this.readVarint32();
+ // Catch empty string case
+ if (size === 0) {
+ return "";
+ }
+
+ // Catch error cases
+ if (size < 0) {
+ throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative string size");
+ }
+ return this.trans.readString(size);
+};
+
+
+//
+// Compact Protocol internal read operations
+//
+
+/**
+ * Read an i32 from the wire as a varint. The MSB of each byte is set
+ * if there is another byte to follow. This can read up to 5 bytes.
+ */
+TCompactProtocol.prototype.readVarint32 = function() {
+ return this.readVarint64().toNumber();
+};
+
+/**
+ * Read an i64 from the wire as a proper varint. The MSB of each byte is set
+ * if there is another byte to follow. This can read up to 10 bytes.
+ */
+TCompactProtocol.prototype.readVarint64 = function() {
+ var rsize = 0;
+ var lo = 0;
+ var hi = 0;
+ var shift = 0;
+ while (true) {
+ var b = this.trans.readByte();
+ rsize ++;
+ if (shift <= 25) {
+ lo = lo | ((b & 0x7f) << shift);
+ } else if (25 < shift && shift < 32) {
+ lo = lo | ((b & 0x7f) << shift);
+ hi = hi | ((b & 0x7f) >>> (32-shift));
+ } else {
+ hi = hi | ((b & 0x7f) << (shift-32));
+ }
+ shift += 7;
+ if (!(b & 0x80)) {
+ break;
+ }
+ if (rsize >= 10) {
+ throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.INVALID_DATA, "Variable-length int over 10 bytes.");
+ }
+ }
+ return new Int64(hi, lo);
+};
+
+/**
+ * Convert from zigzag int to int.
+ */
+TCompactProtocol.prototype.zigzagToI32 = function(n) {
+ return (n >>> 1) ^ (-1 * (n & 1));
+};
+
+/**
+ * Convert from zigzag long to long.
+ */
+TCompactProtocol.prototype.zigzagToI64 = function(n) {
+ var hi = n.buffer.readUInt32BE(0, true);
+ var lo = n.buffer.readUInt32BE(4, true);
+
+ var neg = new Int64(hi & 0, lo & 1);
+ neg._2scomp();
+ var hi_neg = neg.buffer.readUInt32BE(0, true);
+ var lo_neg = neg.buffer.readUInt32BE(4, true);
+
+ var hi_lo = (hi << 31);
+ hi = (hi >>> 1) ^ (hi_neg);
+ lo = ((lo >>> 1) | hi_lo) ^ (lo_neg);
+ return new Int64(hi, lo);
+};
+
+TCompactProtocol.prototype.skip = function(type) {
+ switch (type) {
+ case Type.BOOL:
+ this.readBool();
+ break;
+ case Type.BYTE:
+ this.readByte();
+ break;
+ case Type.I16:
+ this.readI16();
+ break;
+ case Type.I32:
+ this.readI32();
+ break;
+ case Type.I64:
+ this.readI64();
+ break;
+ case Type.DOUBLE:
+ this.readDouble();
+ break;
+ case Type.STRING:
+ this.readString();
+ break;
+ case Type.STRUCT:
+ this.readStructBegin();
+ while (true) {
+ var r = this.readFieldBegin();
+ if (r.ftype === Type.STOP) {
+ break;
+ }
+ this.skip(r.ftype);
+ this.readFieldEnd();
+ }
+ this.readStructEnd();
+ break;
+ case Type.MAP:
+ var mapBegin = this.readMapBegin();
+ for (var i = 0; i < mapBegin.size; ++i) {
+ this.skip(mapBegin.ktype);
+ this.skip(mapBegin.vtype);
+ }
+ this.readMapEnd();
+ break;
+ case Type.SET:
+ var setBegin = this.readSetBegin();
+ for (var i2 = 0; i2 < setBegin.size; ++i2) {
+ this.skip(setBegin.etype);
+ }
+ this.readSetEnd();
+ break;
+ case Type.LIST:
+ var listBegin = this.readListBegin();
+ for (var i3 = 0; i3 < listBegin.size; ++i3) {
+ this.skip(listBegin.etype);
+ }
+ this.readListEnd();
+ break;
+ default:
+ throw new Error("Invalid type: " + type);
+ }
+};
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/connection.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/connection.js
new file mode 100644
index 000000000..25e34ed42
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/connection.js
@@ -0,0 +1,396 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var util = require('util');
+var EventEmitter = require('events').EventEmitter;
+var constants = require('constants');
+var net = require('net');
+var tls = require('tls');
+var thrift = require('./thrift');
+var log = require('./log');
+
+var TBufferedTransport = require('./buffered_transport');
+var TBinaryProtocol = require('./binary_protocol');
+var InputBufferUnderrunError = require('./input_buffer_underrun_error');
+
+var createClient = require('./create_client');
+
+var binary = require('./binary');
+
+var Connection = exports.Connection = function(stream, options) {
+ var self = this;
+ EventEmitter.call(this);
+
+ this.seqId2Service = {};
+ this.connection = stream;
+ this.ssl = (stream.encrypted);
+ this.options = options || {};
+ this.transport = this.options.transport || TBufferedTransport;
+ this.protocol = this.options.protocol || TBinaryProtocol;
+ this.offline_queue = [];
+ this.connected = false;
+ this.initialize_retry_vars();
+
+ this._debug = this.options.debug || false;
+ if (this.options.max_attempts &&
+ !isNaN(this.options.max_attempts) &&
+ this.options.max_attempts > 0) {
+ this.max_attempts = +this.options.max_attempts;
+ }
+ this.retry_max_delay = null;
+ if (this.options.retry_max_delay !== undefined &&
+ !isNaN(this.options.retry_max_delay) &&
+ this.options.retry_max_delay > 0) {
+ this.retry_max_delay = this.options.retry_max_delay;
+ }
+ this.connect_timeout = false;
+ if (this.options.connect_timeout &&
+ !isNaN(this.options.connect_timeout) &&
+ this.options.connect_timeout > 0) {
+ this.connect_timeout = +this.options.connect_timeout;
+ }
+
+ this.connection.addListener(this.ssl ? "secureConnect" : "connect", function() {
+ self.connected = true;
+
+ this.setTimeout(self.options.timeout || 0);
+ this.setNoDelay();
+ this.frameLeft = 0;
+ this.framePos = 0;
+ this.frame = null;
+ self.initialize_retry_vars();
+ self.flush_offline_queue();
+
+ self.emit("connect");
+ });
+
+ this.connection.addListener("error", function(err) {
+ // Only emit the error if no-one else is listening on the connection
+ // or if someone is listening on us, because Node turns unhandled
+ // 'error' events into exceptions.
+ if (self.connection.listeners('error').length === 1 ||
+ self.listeners('error').length > 0) {
+ self.emit("error", err);
+ }
+ });
+
+ // Add a close listener
+ this.connection.addListener("close", function() {
+ self.connection_gone(); // handle close event. try to reconnect
+ });
+
+ this.connection.addListener("timeout", function() {
+ self.emit("timeout");
+ });
+
+ this.connection.addListener("data", self.transport.receiver(function(transport_with_data) {
+ var message = new self.protocol(transport_with_data);
+ try {
+ while (true) {
+ var header = message.readMessageBegin();
+ var dummy_seqid = header.rseqid * -1;
+ var client = self.client;
+ //The Multiplexed Protocol stores a hash of seqid to service names
+ // in seqId2Service. If the SeqId is found in the hash we need to
+ // lookup the appropriate client for this call.
+ // The connection.client object is a single client object when not
+ // multiplexing, when using multiplexing it is a service name keyed
+ // hash of client objects.
+ //NOTE: The 2 way interdependencies between protocols, transports,
+ // connections and clients in the Node.js implementation are irregular
+ // and make the implementation difficult to extend and maintain. We
+ // should bring this stuff inline with typical thrift I/O stack
+ // operation soon.
+ // --ra
+ var service_name = self.seqId2Service[header.rseqid];
+ if (service_name) {
+ client = self.client[service_name];
+ }
+ /*jshint -W083 */
+ client._reqs[dummy_seqid] = function(err, success){
+ transport_with_data.commitPosition();
+
+ var callback = client._reqs[header.rseqid];
+ delete client._reqs[header.rseqid];
+ if (service_name) {
+ delete self.seqId2Service[header.rseqid];
+ }
+ if (callback) {
+ callback(err, success);
+ }
+ };
+ /*jshint +W083 */
+
+ if(client['recv_' + header.fname]) {
+ client['recv_' + header.fname](message, header.mtype, dummy_seqid);
+ } else {
+ delete client._reqs[dummy_seqid];
+ self.emit("error",
+ new thrift.TApplicationException(thrift.TApplicationExceptionType.WRONG_METHOD_NAME,
+ "Received a response to an unknown RPC function"));
+ }
+ }
+ }
+ catch (e) {
+ if (e instanceof InputBufferUnderrunError) {
+ transport_with_data.rollbackPosition();
+ }
+ else {
+ self.emit('error', e);
+ }
+ }
+ }));
+};
+util.inherits(Connection, EventEmitter);
+
+Connection.prototype.end = function() {
+ this.connection.end();
+};
+
+Connection.prototype.destroy = function() {
+ this.connection.destroy();
+};
+
+Connection.prototype.initialize_retry_vars = function () {
+ this.retry_timer = null;
+ this.retry_totaltime = 0;
+ this.retry_delay = 150;
+ this.retry_backoff = 1.7;
+ this.attempts = 0;
+};
+
+Connection.prototype.flush_offline_queue = function () {
+ var self = this;
+ var offline_queue = this.offline_queue;
+
+ // Reset offline queue
+ this.offline_queue = [];
+ // Attempt to write queued items
+ offline_queue.forEach(function(data) {
+ self.write(data);
+ });
+};
+
+Connection.prototype.write = function(data) {
+ if (!this.connected) {
+ this.offline_queue.push(data);
+ return;
+ }
+ this.connection.write(data);
+};
+
+Connection.prototype.connection_gone = function () {
+ var self = this;
+ this.connected = false;
+
+ // If a retry is already in progress, just let that happen
+ if (this.retry_timer) {
+ return;
+ }
+ // We cannot reconnect a secure socket.
+ if (!this.max_attempts || this.ssl) {
+ self.emit("close");
+ return;
+ }
+
+ if (this.retry_max_delay !== null && this.retry_delay >= this.retry_max_delay) {
+ this.retry_delay = this.retry_max_delay;
+ } else {
+ this.retry_delay = Math.floor(this.retry_delay * this.retry_backoff);
+ }
+
+ log.debug("Retry connection in " + this.retry_delay + " ms");
+
+ if (this.max_attempts && this.attempts >= this.max_attempts) {
+ this.retry_timer = null;
+ console.error("thrift: Couldn't get thrift connection after " + this.max_attempts + " attempts.");
+ self.emit("close");
+ return;
+ }
+
+ this.attempts += 1;
+ this.emit("reconnecting", {
+ delay: self.retry_delay,
+ attempt: self.attempts
+ });
+
+ this.retry_timer = setTimeout(function () {
+ if (self.connection.destroyed) {
+ self.retry_timer = null;
+ return;
+ }
+
+ log.debug("Retrying connection...");
+
+ self.retry_totaltime += self.retry_delay;
+
+ if (self.connect_timeout && self.retry_totaltime >= self.connect_timeout) {
+ self.retry_timer = null;
+ console.error("thrift: Couldn't get thrift connection after " + self.retry_totaltime + "ms.");
+ self.emit("close");
+ return;
+ }
+
+ if (self.path !== undefined) {
+ self.connection.connect(self.path);
+ } else {
+ self.connection.connect(self.port, self.host);
+ }
+ self.retry_timer = null;
+ }, this.retry_delay);
+};
+
+exports.createConnection = function(host, port, options) {
+ var stream = net.createConnection( {
+ port: port,
+ host: host,
+ timeout: options.connect_timeout || options.timeout || 0
+ });
+ var connection = new Connection(stream, options);
+ connection.host = host;
+ connection.port = port;
+
+ return connection;
+};
+
+exports.createUDSConnection = function(path, options) {
+ var stream = net.createConnection(path);
+ var connection = new Connection(stream, options);
+ connection.path = path;
+
+ return connection;
+};
+
+exports.createSSLConnection = function(host, port, options) {
+ if (!('secureProtocol' in options) && !('secureOptions' in options)) {
+ options.secureProtocol = "SSLv23_method";
+ options.secureOptions = constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3;
+ }
+
+ var stream = tls.connect(port, host, options);
+ var connection = new Connection(stream, options);
+ connection.host = host;
+ connection.port = port;
+
+ return connection;
+};
+
+
+exports.createClient = createClient;
+
+var child_process = require('child_process');
+var StdIOConnection = exports.StdIOConnection = function(command, options) {
+ var command_parts = command.split(' ');
+ command = command_parts[0];
+ var args = command_parts.splice(1,command_parts.length -1);
+ var child = this.child = child_process.spawn(command,args);
+
+ var self = this;
+ EventEmitter.call(this);
+
+ this.connection = child.stdin;
+ this.options = options || {};
+ this.transport = this.options.transport || TBufferedTransport;
+ this.protocol = this.options.protocol || TBinaryProtocol;
+ this.offline_queue = [];
+
+ if (log.getLogLevel() === 'debug') {
+ this.child.stderr.on('data', function (err) {
+ log.debug(err.toString(), 'CHILD ERROR');
+ });
+
+ this.child.on('exit', function (code,signal) {
+ log.debug(code + ':' + signal, 'CHILD EXITED');
+ });
+ }
+
+ this.frameLeft = 0;
+ this.framePos = 0;
+ this.frame = null;
+ this.connected = true;
+
+ self.flush_offline_queue();
+
+ this.connection.addListener("error", function(err) {
+ self.emit("error", err);
+ });
+
+ // Add a close listener
+ this.connection.addListener("close", function() {
+ self.emit("close");
+ });
+
+ child.stdout.addListener("data", self.transport.receiver(function(transport_with_data) {
+ var message = new self.protocol(transport_with_data);
+ try {
+ var header = message.readMessageBegin();
+ var dummy_seqid = header.rseqid * -1;
+ var client = self.client;
+ client._reqs[dummy_seqid] = function(err, success){
+ transport_with_data.commitPosition();
+
+ var callback = client._reqs[header.rseqid];
+ delete client._reqs[header.rseqid];
+ if (callback) {
+ callback(err, success);
+ }
+ };
+ client['recv_' + header.fname](message, header.mtype, dummy_seqid);
+ }
+ catch (e) {
+ if (e instanceof InputBufferUnderrunError) {
+ transport_with_data.rollbackPosition();
+ }
+ else {
+ throw e;
+ }
+ }
+ }));
+};
+
+util.inherits(StdIOConnection, EventEmitter);
+
+StdIOConnection.prototype.end = function() {
+ this.connection.end();
+};
+
+StdIOConnection.prototype.flush_offline_queue = function () {
+ var self = this;
+ var offline_queue = this.offline_queue;
+
+ // Reset offline queue
+ this.offline_queue = [];
+ // Attempt to write queued items
+ offline_queue.forEach(function(data) {
+ self.write(data);
+ });
+};
+
+StdIOConnection.prototype.write = function(data) {
+ if (!this.connected) {
+ this.offline_queue.push(data);
+ return;
+ }
+ this.connection.write(data);
+};
+
+exports.createStdIOConnection = function(command,options){
+ return new StdIOConnection(command,options);
+};
+
+exports.createStdIOClient = createClient;
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/create_client.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/create_client.js
new file mode 100644
index 000000000..d6b77a833
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/create_client.js
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+module.exports = createClient;
+
+/**
+ * Creates a new client object for the specified Thrift service.
+ * @param {object} ServiceClient - The module containing the generated service client
+ * @param {Connection} Connection - The connection to use.
+ * @returns {object} The client object.
+ */
+function createClient(ServiceClient, connection) {
+ // TODO validate required options and throw otherwise
+ if (ServiceClient.Client) {
+ ServiceClient = ServiceClient.Client;
+ }
+ // TODO detangle these initialization calls
+ // creating "client" requires
+ // - new service client instance
+ //
+ // New service client instance requires
+ // - new transport instance
+ // - protocol class reference
+ //
+ // New transport instance requires
+ // - Buffer to use (or none)
+ // - Callback to call on flush
+
+ // Wrap the write method
+ var writeCb = function(buf, seqid) {
+ connection.write(buf, seqid);
+ };
+ var transport = new connection.transport(undefined, writeCb);
+ var client = new ServiceClient(transport, connection.protocol);
+ transport.client = client;
+ connection.client = client;
+ return client;
+};
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/framed_transport.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/framed_transport.js
new file mode 100644
index 000000000..f7daa3f1c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/framed_transport.js
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var binary = require('./binary');
+var InputBufferUnderrunError = require('./input_buffer_underrun_error');
+var THeaderTransport = require('./header_transport');
+
+module.exports = TFramedTransport;
+
+function TFramedTransport(buffer, callback) {
+ this.inBuf = buffer || new Buffer(0);
+ this.outBuffers = [];
+ this.outCount = 0;
+ this.readPos = 0;
+ this.onFlush = callback;
+};
+
+TFramedTransport.prototype = new THeaderTransport();
+
+TFramedTransport.receiver = function(callback, seqid) {
+ var residual = null;
+
+ return function(data) {
+ // Prepend any residual data from our previous read
+ if (residual) {
+ data = Buffer.concat([residual, data]);
+ residual = null;
+ }
+
+ // framed transport
+ while (data.length) {
+ if (data.length < 4) {
+ // Not enough bytes to continue, save and resume on next packet
+ residual = data;
+ return;
+ }
+ var frameSize = binary.readI32(data, 0);
+ if (data.length < 4 + frameSize) {
+ // Not enough bytes to continue, save and resume on next packet
+ residual = data;
+ return;
+ }
+
+ var frame = data.slice(4, 4 + frameSize);
+ residual = data.slice(4 + frameSize);
+
+ callback(new TFramedTransport(frame), seqid);
+
+ data = residual;
+ residual = null;
+ }
+ };
+};
+
+TFramedTransport.prototype.commitPosition = function(){},
+TFramedTransport.prototype.rollbackPosition = function(){},
+
+ // TODO: Implement open/close support
+TFramedTransport.prototype.isOpen = function() {
+ return true;
+};
+TFramedTransport.prototype.open = function() {};
+TFramedTransport.prototype.close = function() {};
+
+ // Set the seqid of the message in the client
+ // So that callbacks can be found
+TFramedTransport.prototype.setCurrSeqId = function(seqid) {
+ this._seqid = seqid;
+};
+
+TFramedTransport.prototype.ensureAvailable = function(len) {
+ if (this.readPos + len > this.inBuf.length) {
+ throw new InputBufferUnderrunError();
+ }
+};
+
+TFramedTransport.prototype.read = function(len) { // this function will be used for each frames.
+ this.ensureAvailable(len);
+ var end = this.readPos + len;
+
+ if (this.inBuf.length < end) {
+ throw new Error('read(' + len + ') failed - not enough data');
+ }
+
+ var buf = this.inBuf.slice(this.readPos, end);
+ this.readPos = end;
+ return buf;
+};
+
+TFramedTransport.prototype.readByte = function() {
+ this.ensureAvailable(1);
+ return binary.readByte(this.inBuf[this.readPos++]);
+};
+
+TFramedTransport.prototype.readI16 = function() {
+ this.ensureAvailable(2);
+ var i16 = binary.readI16(this.inBuf, this.readPos);
+ this.readPos += 2;
+ return i16;
+};
+
+TFramedTransport.prototype.readI32 = function() {
+ this.ensureAvailable(4);
+ var i32 = binary.readI32(this.inBuf, this.readPos);
+ this.readPos += 4;
+ return i32;
+};
+
+TFramedTransport.prototype.readDouble = function() {
+ this.ensureAvailable(8);
+ var d = binary.readDouble(this.inBuf, this.readPos);
+ this.readPos += 8;
+ return d;
+};
+
+TFramedTransport.prototype.readString = function(len) {
+ this.ensureAvailable(len);
+ var str = this.inBuf.toString('utf8', this.readPos, this.readPos + len);
+ this.readPos += len;
+ return str;
+};
+
+TFramedTransport.prototype.borrow = function() {
+ return {
+ buf: this.inBuf,
+ readIndex: this.readPos,
+ writeIndex: this.inBuf.length
+ };
+};
+
+TFramedTransport.prototype.consume = function(bytesConsumed) {
+ this.readPos += bytesConsumed;
+};
+
+TFramedTransport.prototype.write = function(buf, encoding) {
+ if (typeof(buf) === "string") {
+ buf = new Buffer(buf, encoding || 'utf8');
+ }
+ this.outBuffers.push(buf);
+ this.outCount += buf.length;
+};
+
+TFramedTransport.prototype.flush = function() {
+ // If the seqid of the callback is available pass it to the onFlush
+ // Then remove the current seqid
+ var seqid = this._seqid;
+ this._seqid = null;
+
+ var out = new Buffer(this.outCount),
+ pos = 0;
+ this.outBuffers.forEach(function(buf) {
+ buf.copy(out, pos, 0);
+ pos += buf.length;
+ });
+
+ if (this.onFlush) {
+ // TODO: optimize this better, allocate one buffer instead of both:
+ var msg = new Buffer(out.length + 4);
+ binary.writeI32(msg, out.length);
+ out.copy(msg, 4, 0, out.length);
+ if (this.onFlush) {
+ // Passing seqid through this call to get it to the connection
+ this.onFlush(msg, seqid);
+ }
+ }
+
+ this.outBuffers = [];
+ this.outCount = 0;
+};
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/header_protocol.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/header_protocol.js
new file mode 100644
index 000000000..0c3b0db43
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/header_protocol.js
@@ -0,0 +1,256 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var util = require('util');
+var TBinaryProtocol = require('./binary_protocol');
+var TCompactProtocol = require('./compact_protocol');
+var THeaderTransport = require('./header_transport');
+
+var ProtocolMap = {};
+ProtocolMap[THeaderTransport.SubprotocolId.BINARY] = TBinaryProtocol;
+ProtocolMap[THeaderTransport.SubprotocolId.COMPACT] = TCompactProtocol;
+
+module.exports = THeaderProtocol;
+
+function THeaderProtocolError(message) {
+ Error.call(this);
+ Error.captureStackTrace(this, this.constructor);
+ this.name = this.constructor.name;
+ this.message = message;
+}
+
+util.inherits(THeaderProtocolError, Error);
+
+/**
+ * A framed protocol with headers.
+ *
+ * THeaderProtocol frames other Thrift protocols and adds support for
+ * optional out-of-band headers. The currently supported subprotocols are
+ * TBinaryProtocol and TCompactProtocol. It can currently only be used with
+ * transports that inherit THeaderTransport.
+ *
+ * THeaderProtocol does not currently support THTTPServer, TNonblockingServer,
+ * or TProcessPoolServer.
+ *
+ * See doc/specs/HeaderFormat.md for details of the wire format.
+ */
+function THeaderProtocol(trans) {
+ if (!(trans instanceof THeaderTransport)) {
+ throw new THeaderProtocolError(
+ 'Only transports that inherit THeaderTransport can be' +
+ ' used with THeaderProtocol'
+ );
+ }
+ this.trans = trans;
+ this.setProtocol();
+};
+
+THeaderProtocol.prototype.flush = function() {
+ // Headers must be written prior to flushing because because
+ // you need to calculate the length of the payload for the length
+ // field of the header
+ this.trans.writeHeaders();
+ return this.trans.flush();
+};
+
+THeaderProtocol.prototype.writeMessageBegin = function(name, type, seqid) {
+ return this.protocol.writeMessageBegin(name, type, seqid);
+};
+
+THeaderProtocol.prototype.writeMessageEnd = function() {
+ return this.protocol.writeMessageEnd();
+};
+
+THeaderProtocol.prototype.writeStructBegin = function(name) {
+ return this.protocol.writeStructBegin(name);
+};
+
+THeaderProtocol.prototype.writeStructEnd = function() {
+ return this.protocol.writeStructEnd();
+};
+
+THeaderProtocol.prototype.writeFieldBegin = function(name, type, id) {
+ return this.protocol.writeFieldBegin(name, type, id);
+}
+
+THeaderProtocol.prototype.writeFieldEnd = function() {
+ return this.protocol.writeFieldEnd();
+};
+
+THeaderProtocol.prototype.writeFieldStop = function() {
+ return this.protocol.writeFieldStop();
+};
+
+THeaderProtocol.prototype.writeMapBegin = function(ktype, vtype, size) {
+ return this.protocol.writeMapBegin(ktype, vtype, size);
+};
+
+THeaderProtocol.prototype.writeMapEnd = function() {
+ return this.protocol.writeMapEnd();
+};
+
+THeaderProtocol.prototype.writeListBegin = function(etype, size) {
+ return this.protocol.writeListBegin(etype, size);
+};
+
+THeaderProtocol.prototype.writeListEnd = function() {
+ return this.protocol.writeListEnd();
+};
+
+THeaderProtocol.prototype.writeSetBegin = function(etype, size) {
+ return this.protocol.writeSetBegin(etype, size);
+};
+
+THeaderProtocol.prototype.writeSetEnd = function() {
+ return this.protocol.writeSetEnd();
+};
+
+THeaderProtocol.prototype.writeBool = function(b) {
+ return this.protocol.writeBool(b);
+};
+
+THeaderProtocol.prototype.writeByte = function(b) {
+ return this.protocol.writeByte(b);
+};
+
+THeaderProtocol.prototype.writeI16 = function(i16) {
+ return this.protocol.writeI16(i16);
+};
+
+THeaderProtocol.prototype.writeI32 = function(i32) {
+ return this.protocol.writeI32(i32);
+};
+
+THeaderProtocol.prototype.writeI64 = function(i64) {
+ return this.protocol.writeI64(i64);
+};
+
+THeaderProtocol.prototype.writeDouble = function(dub) {
+ return this.protocol.writeDouble(dub);
+};
+
+THeaderProtocol.prototype.writeStringOrBinary = function(name, encoding, arg) {
+ return this.protocol.writeStringOrBinary(name, encoding, arg);
+};
+
+THeaderProtocol.prototype.writeString = function(arg) {
+ return this.protocol.writeString(arg);
+};
+
+THeaderProtocol.prototype.writeBinary = function(arg) {
+ return this.protocol.writeBinary(arg);
+};
+
+THeaderProtocol.prototype.readMessageBegin = function() {
+ this.trans.readHeaders();
+ this.setProtocol();
+ return this.protocol.readMessageBegin();
+};
+
+THeaderProtocol.prototype.readMessageEnd = function() {
+ return this.protocol.readMessageEnd();
+};
+
+THeaderProtocol.prototype.readStructBegin = function() {
+ return this.protocol.readStructBegin();
+};
+
+THeaderProtocol.prototype.readStructEnd = function() {
+ return this.protocol.readStructEnd();
+};
+
+THeaderProtocol.prototype.readFieldBegin = function() {
+ return this.protocol.readFieldBegin();
+};
+
+THeaderProtocol.prototype.readFieldEnd = function() {
+ return this.protocol.readFieldEnd();
+};
+
+THeaderProtocol.prototype.readMapBegin = function() {
+ return this.protocol.readMapBegin();
+};
+
+THeaderProtocol.prototype.readMapEnd = function() {
+ return this.protocol.readMapEnd();
+};
+
+THeaderProtocol.prototype.readListBegin = function() {
+ return this.protocol.readListBegin();
+};
+
+THeaderProtocol.prototype.readListEnd = function() {
+ return this.protocol.readListEnd();
+};
+
+THeaderProtocol.prototype.readSetBegin = function() {
+ return this.protocol.readSetBegin();
+};
+
+THeaderProtocol.prototype.readSetEnd = function() {
+ return this.protocol.readSetEnd();
+};
+
+THeaderProtocol.prototype.readBool = function() {
+ return this.protocol.readBool();
+};
+
+THeaderProtocol.prototype.readByte = function() {
+ return this.protocol.readByte();
+};
+
+THeaderProtocol.prototype.readI16 = function() {
+ return this.protocol.readI16();
+};
+
+THeaderProtocol.prototype.readI32 = function() {
+ return this.protocol.readI32();
+};
+
+THeaderProtocol.prototype.readI64 = function() {
+ return this.protocol.readI64();
+};
+
+THeaderProtocol.prototype.readDouble = function() {
+ return this.protocol.readDouble();
+};
+
+THeaderProtocol.prototype.readBinary = function() {
+ return this.protocol.readBinary();
+};
+
+THeaderProtocol.prototype.readString = function() {
+ return this.protocol.readString();
+};
+
+THeaderProtocol.prototype.getTransport = function() {
+ return this.trans;
+};
+
+THeaderProtocol.prototype.skip = function(type) {
+ return this.protocol.skip(type);
+};
+
+THeaderProtocol.prototype.setProtocol = function(subProtocolId) {
+ var subProtocolId = this.trans.getProtocolId();
+ if (!ProtocolMap[subProtocolId]) {
+ throw new THeaderProtocolError('Headers not supported for protocol ' + subProtocolId);
+ }
+
+ this.protocol = new ProtocolMap[subProtocolId](this.trans);
+};
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/header_transport.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/header_transport.js
new file mode 100644
index 000000000..c5f133e8d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/header_transport.js
@@ -0,0 +1,339 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var util = require('util');
+var TCompactProtocol = require('./compact_protocol');
+var TBinaryProtocol = require('./binary_protocol');
+var InputBufferUnderrunError = require('./input_buffer_underrun_error');
+
+function THeaderTransportError(message) {
+ Error.call(this);
+ Error.captureStackTrace(this, this.constructor);
+ this.name = this.constructor.name;
+ this.message = message;
+}
+
+util.inherits(THeaderTransportError, Error);
+
+module.exports = THeaderTransport;
+
+// from HeaderFormat.md
+var COMPACT_PROTOCOL_OFFSET = 0;
+var COMPACT_PROTOCOL_VERSION_OFFSET = 1;
+var FRAME_SIZE_OFFSET = 0;
+var HEADER_MAGIC_OFFSET = 32 / 8;
+var FLAGS_OFFSET = 48 / 8;
+var SEQID_OFFSET = 64 / 8;
+var HEADER_SIZE_OFFSET = 96 / 8;
+var HEADER_START_OFFSET = 112 / 8;
+
+var HEADER_MAGIC = 0x0FFF;
+
+var TINFO_HEADER_KEY_VALUE_TYPE = 0x01;
+var MAX_FRAME_SIZE = 0x3FFFFFFF;
+
+ // A helper class for reading/writing varints. Uses
+ // TCompactProtocol under the hood
+function VarintHelper(readBuffer) {
+ var TBufferedTransport = require('./buffered_transport');
+ this.outputBuffer = null;
+ var _this = this;
+ this.transport = new TBufferedTransport(null, function(output) {
+ _this.outputBuffer = output;
+ });
+
+ this.transport.inBuf = readBuffer || Buffer.alloc(0);
+ this.transport.writeCursor = this.transport.inBuf.length;
+ this.protocol = new TCompactProtocol(this.transport);
+};
+
+VarintHelper.prototype.readVarint32 = function() {
+ return this.protocol.readVarint32();
+};
+
+VarintHelper.prototype.writeVarint32 = function(i) {
+ this.protocol.writeVarint32(i);
+};
+
+VarintHelper.prototype.readString = function() {
+ return this.protocol.readString();
+};
+
+VarintHelper.prototype.writeString = function(str) {
+ this.protocol.writeString(str);
+}
+
+VarintHelper.prototype.getOutCount = function() {
+ return this.transport.outCount;
+};
+
+VarintHelper.prototype.write = function(str) {
+ this.transport.write(str);
+};
+
+VarintHelper.prototype.toBuffer = function() {
+ this.transport.flush();
+ return this.outputBuffer;
+};
+
+// from lib/cpp/src/thrift/protocol/TProtocolTypes.h
+THeaderTransport.SubprotocolId = {
+ BINARY: 0,
+ JSON: 1,
+ COMPACT: 2,
+};
+
+/**
+ An abstract transport used as a prototype for other transports
+ to enable reading/writing theaders. This should NOT be used as a standalone transport
+ The methods in this transport are called by THeaderProtocol, which will call readHeaders/writeHeaders
+ in the read/writeMessageBegin methods and parse/write headers to/from a request
+ prior to reading/writing.
+
+ The reason this is not a standalone transport type is because different transport types
+ have their own individual static receiver methods that are called prior to instantiation.
+ There doesn't seem to be a way for THeaderTransport to know which receiver method to use
+ without reworking the server API.
+
+ For reading headers from a request, the parsed headers can be retrieved via
+ getReadHeader. Similarly, you can set headers to be written on the client via
+ setWriteHeader.
+ */
+function THeaderTransport() {
+ this.maxFrameSize = MAX_FRAME_SIZE;
+ this.protocolId = THeaderTransport.SubprotocolId.BINARY;
+ this.rheaders = {};
+ this.wheaders = {};
+ this.inBuf = Buffer.alloc(0);
+ this.outCount = 0;
+ this.flags = null;
+ this.seqid = 0;
+ this.shouldWriteHeaders = true;
+};
+
+var validateHeaders = function(key, value) {
+ if (typeof key !== 'string' || typeof value !== 'string') {
+ throw new THeaderTransportError('Header key and values must be strings');
+ }
+};
+
+var validateProtocolId = function(protocolId) {
+ var protocols = Object.keys(THeaderTransport.SubprotocolId);
+ for (var i = 0; i < protocols.length; i++) {
+ if (protocolId === THeaderTransport.SubprotocolId[protocols[i]]) return true;
+ }
+
+ throw new Error(protocolId + ' is not a valid protocol id');
+};
+
+THeaderTransport.prototype.setSeqId = function(seqid) {
+ this.seqid = seqid;
+};
+
+THeaderTransport.prototype.getSeqId = function(seqid) {
+ return this.seqid;
+};
+
+THeaderTransport.prototype.setFlags = function(flags) {
+ this.flags = flags;
+};
+
+THeaderTransport.prototype.getReadHeaders = function() {
+ return this.rheaders;
+};
+
+THeaderTransport.prototype.setReadHeader = function(key, value) {
+ validateHeaders(key, value);
+ this.rheaders[key] = value;
+};
+
+THeaderTransport.prototype.clearReadHeaders = function() {
+ this.rheaders = {};
+};
+
+THeaderTransport.prototype.getWriteHeaders = function() {
+ return this.wheaders;
+};
+
+THeaderTransport.prototype.setWriteHeader = function(key, value) {
+ validateHeaders(key, value);
+ this.wheaders[key] = value;
+};
+
+THeaderTransport.prototype.clearWriteHeaders = function() {
+ this.wheaders = {};
+};
+
+THeaderTransport.prototype.setMaxFrameSize = function(frameSize) {
+ this.maxFrameSize = frameSize;
+};
+
+THeaderTransport.prototype.setProtocolId = function(protocolId) {
+ validateProtocolId(protocolId);
+ this.protocolId = protocolId;
+};
+
+THeaderTransport.prototype.getProtocolId = function() {
+ return this.protocolId;
+};
+
+var isUnframedBinary = function(readBuffer) {
+ var version = readBuffer.readInt32BE();
+ return (version & TBinaryProtocol.VERSION_MASK) === TBinaryProtocol.VERSION_1;
+}
+
+var isUnframedCompact = function(readBuffer) {
+ var protocolId = readBuffer.readInt8(COMPACT_PROTOCOL_OFFSET);
+ var version = readBuffer.readInt8(COMPACT_PROTOCOL_VERSION_OFFSET);
+ return protocolId === TCompactProtocol.PROTOCOL_ID &&
+ (version & TCompactProtocol.VERSION_MASK) === TCompactProtocol.VERSION_N;
+}
+
+THeaderTransport.prototype.readHeaders = function() {
+ var readBuffer = this.inBuf;
+
+ var isUnframed = false;
+ if (isUnframedBinary(readBuffer)) {
+ this.setProtocolId(THeaderTransport.SubprotocolId.BINARY);
+ isUnframed = true;
+ }
+
+ if (isUnframedCompact(readBuffer)) {
+ this.setProtocolId(THeaderTransport.SubprotocolId.COMPACT);
+ isUnframed = true;
+ }
+
+ if (isUnframed) {
+ this.shouldWriteHeaders = false;
+ return;
+ }
+
+ var frameSize = readBuffer.readInt32BE(FRAME_SIZE_OFFSET);
+ if (frameSize > this.maxFrameSize) {
+ throw new THeaderTransportError('Frame exceeds maximum frame size');
+ }
+
+ var headerMagic = readBuffer.readInt16BE(HEADER_MAGIC_OFFSET);
+ this.shouldWriteHeaders = headerMagic === HEADER_MAGIC;
+ if (!this.shouldWriteHeaders) {
+ return;
+ }
+
+ this.setFlags(readBuffer.readInt16BE(FLAGS_OFFSET));
+ this.setSeqId(readBuffer.readInt32BE(SEQID_OFFSET));
+ var headerSize = readBuffer.readInt16BE(HEADER_SIZE_OFFSET) * 4;
+ var endOfHeaders = HEADER_START_OFFSET + headerSize;
+ if (endOfHeaders > readBuffer.length) {
+ throw new THeaderTransportError('Header size is greater than frame size');
+ }
+
+ var headerBuffer = Buffer.alloc(headerSize);
+ readBuffer.copy(headerBuffer, 0, HEADER_START_OFFSET, endOfHeaders);
+
+ var varintHelper = new VarintHelper(headerBuffer);
+ this.setProtocolId(varintHelper.readVarint32());
+ var transformCount = varintHelper.readVarint32();
+ if (transformCount > 0) {
+ throw new THeaderTransportError('Transforms are not yet supported');
+ }
+
+ while (true) {
+ try {
+ var headerType = varintHelper.readVarint32();
+ if (headerType !== TINFO_HEADER_KEY_VALUE_TYPE) {
+ break;
+ }
+
+ var numberOfHeaders = varintHelper.readVarint32();
+ for (var i = 0; i < numberOfHeaders; i++) {
+ var key = varintHelper.readString();
+ var value = varintHelper.readString();
+ this.setReadHeader(key, value);
+ }
+ } catch (e) {
+ if (e instanceof InputBufferUnderrunError) {
+ break;
+ }
+ throw e;
+ }
+ }
+
+ // moves the read cursor past the headers
+ this.read(endOfHeaders);
+ return this.getReadHeaders();
+};
+
+THeaderTransport.prototype.writeHeaders = function() {
+ // only write headers on the server if the client contained headers
+ if (!this.shouldWriteHeaders) {
+ return;
+ }
+ var headers = this.getWriteHeaders();
+
+ var varintWriter = new VarintHelper();
+ varintWriter.writeVarint32(this.protocolId);
+ varintWriter.writeVarint32(0); // transforms not supported
+
+ // writing info header key values
+ var headerKeys = Object.keys(headers);
+ if (headerKeys.length > 0) {
+ varintWriter.writeVarint32(TINFO_HEADER_KEY_VALUE_TYPE);
+ varintWriter.writeVarint32(headerKeys.length);
+ for (var i = 0; i < headerKeys.length; i++) {
+ var key = headerKeys[i];
+ var value = headers[key];
+
+ varintWriter.writeString(key);
+ varintWriter.writeString(value);
+ }
+ }
+ var headerSizeWithoutPadding = varintWriter.getOutCount();
+ var paddingNeeded = (4 - (headerSizeWithoutPadding % 4)) % 4;
+
+ var headerSize = Buffer.alloc(2);
+ headerSize.writeInt16BE(Math.floor((headerSizeWithoutPadding + paddingNeeded) / 4));
+
+ var paddingBuffer = Buffer.alloc(paddingNeeded);
+ paddingBuffer.fill(0x00);
+ varintWriter.write(paddingBuffer);
+ var headerContentBuffer = varintWriter.toBuffer();
+ var frameSize = Buffer.alloc(4);
+ frameSize.writeInt32BE(10 + this.outCount + headerContentBuffer.length);
+ var headerMagic = Buffer.alloc(2);
+ headerMagic.writeInt16BE(HEADER_MAGIC);
+
+ // flags are not yet supported, so write a zero
+ var flags = Buffer.alloc(2);
+ flags.writeInt16BE(0);
+
+ var seqid = Buffer.alloc(4);
+ seqid.writeInt32BE(this.getSeqId());
+
+ var headerBuffer = Buffer.concat([
+ frameSize,
+ headerMagic,
+ flags,
+ seqid,
+ headerSize,
+ headerContentBuffer,
+ ]);
+
+ this.outBuffers.unshift(headerBuffer);
+ this.outCount += headerBuffer.length;
+};
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/http_connection.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/http_connection.js
new file mode 100644
index 000000000..3c2ab0f53
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/http_connection.js
@@ -0,0 +1,263 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var util = require('util');
+var http = require('http');
+var https = require('https');
+var EventEmitter = require('events').EventEmitter;
+var thrift = require('./thrift');
+
+var TBufferedTransport = require('./buffered_transport');
+var TBinaryProtocol = require('./binary_protocol');
+var InputBufferUnderrunError = require('./input_buffer_underrun_error');
+
+var createClient = require('./create_client');
+
+/**
+ * @class
+ * @name ConnectOptions
+ * @property {string} transport - The Thrift layered transport to use (TBufferedTransport, etc).
+ * @property {string} protocol - The Thrift serialization protocol to use (TBinaryProtocol, etc.).
+ * @property {string} path - The URL path to POST to (e.g. "/", "/mySvc", "/thrift/quoteSvc", etc.).
+ * @property {object} headers - A standard Node.js header hash, an object hash containing key/value
+ * pairs where the key is the header name string and the value is the header value string.
+ * @property {boolean} https - True causes the connection to use https, otherwise http is used.
+ * @property {object} nodeOptions - Options passed on to node.
+ * @example
+ * //Use a connection that requires ssl/tls, closes the connection after each request,
+ * // uses the buffered transport layer, uses the JSON protocol and directs RPC traffic
+ * // to https://thrift.example.com:9090/hello
+ * var thrift = require('thrift');
+ * var options = {
+ * transport: thrift.TBufferedTransport,
+ * protocol: thrift.TJSONProtocol,
+ * path: "/hello",
+ * headers: {"Connection": "close"},
+ * https: true
+ * };
+ * var con = thrift.createHttpConnection("thrift.example.com", 9090, options);
+ * var client = thrift.createHttpClient(myService, connection);
+ * client.myServiceFunction();
+ */
+
+/**
+ * Initializes a Thrift HttpConnection instance (use createHttpConnection() rather than
+ * instantiating directly).
+ * @constructor
+ * @param {ConnectOptions} options - The configuration options to use.
+ * @throws {error} Exceptions other than InputBufferUnderrunError are rethrown
+ * @event {error} The "error" event is fired when a Node.js error event occurs during
+ * request or response processing, in which case the node error is passed on. An "error"
+ * event may also be fired when the connection can not map a response back to the
+ * appropriate client (an internal error), generating a TApplicationException.
+ * @classdesc HttpConnection objects provide Thrift end point transport
+ * semantics implemented over the Node.js http.request() method.
+ * @see {@link createHttpConnection}
+ */
+var HttpConnection = exports.HttpConnection = function(options) {
+ //Initialize the emitter base object
+ EventEmitter.call(this);
+
+ //Set configuration
+ var self = this;
+ this.options = options || {};
+ this.host = this.options.host;
+ this.port = this.options.port;
+ this.socketPath = this.options.socketPath;
+ this.https = this.options.https || false;
+ this.transport = this.options.transport || TBufferedTransport;
+ this.protocol = this.options.protocol || TBinaryProtocol;
+
+ //Prepare Node.js options
+ this.nodeOptions = {
+ host: this.host,
+ port: this.port,
+ socketPath: this.socketPath,
+ path: this.options.path || '/',
+ method: 'POST',
+ headers: this.options.headers || {},
+ responseType: this.options.responseType || null
+ };
+ for (var attrname in this.options.nodeOptions) {
+ this.nodeOptions[attrname] = this.options.nodeOptions[attrname];
+ }
+ /*jshint -W069 */
+ if (! this.nodeOptions.headers['Connection']) {
+ this.nodeOptions.headers['Connection'] = 'keep-alive';
+ }
+ /*jshint +W069 */
+
+ //The sequence map is used to map seqIDs back to the
+ // calling client in multiplexed scenarios
+ this.seqId2Service = {};
+
+ function decodeCallback(transport_with_data) {
+ var proto = new self.protocol(transport_with_data);
+ try {
+ while (true) {
+ var header = proto.readMessageBegin();
+ var dummy_seqid = header.rseqid * -1;
+ var client = self.client;
+ //The Multiplexed Protocol stores a hash of seqid to service names
+ // in seqId2Service. If the SeqId is found in the hash we need to
+ // lookup the appropriate client for this call.
+ // The client var is a single client object when not multiplexing,
+ // when using multiplexing it is a service name keyed hash of client
+ // objects.
+ //NOTE: The 2 way interdependencies between protocols, transports,
+ // connections and clients in the Node.js implementation are irregular
+ // and make the implementation difficult to extend and maintain. We
+ // should bring this stuff inline with typical thrift I/O stack
+ // operation soon.
+ // --ra
+ var service_name = self.seqId2Service[header.rseqid];
+ if (service_name) {
+ client = self.client[service_name];
+ delete self.seqId2Service[header.rseqid];
+ }
+ /*jshint -W083 */
+ client._reqs[dummy_seqid] = function(err, success){
+ transport_with_data.commitPosition();
+ var clientCallback = client._reqs[header.rseqid];
+ delete client._reqs[header.rseqid];
+ if (clientCallback) {
+ process.nextTick(function() {
+ clientCallback(err, success);
+ });
+ }
+ };
+ /*jshint +W083 */
+ if(client['recv_' + header.fname]) {
+ client['recv_' + header.fname](proto, header.mtype, dummy_seqid);
+ } else {
+ delete client._reqs[dummy_seqid];
+ self.emit("error",
+ new thrift.TApplicationException(
+ thrift.TApplicationExceptionType.WRONG_METHOD_NAME,
+ "Received a response to an unknown RPC function"));
+ }
+ }
+ }
+ catch (e) {
+ if (e instanceof InputBufferUnderrunError) {
+ transport_with_data.rollbackPosition();
+ } else {
+ self.emit('error', e);
+ }
+ }
+ }
+
+
+ //Response handler
+ //////////////////////////////////////////////////
+ this.responseCallback = function(response) {
+ var data = [];
+ var dataLen = 0;
+
+ if (response.statusCode !== 200) {
+ this.emit("error", new THTTPException(response));
+ }
+
+ response.on('error', function (e) {
+ self.emit("error", e);
+ });
+
+ // When running directly under node, chunk will be a buffer,
+ // however, when running in a Browser (e.g. Browserify), chunk
+ // will be a string or an ArrayBuffer.
+ response.on('data', function (chunk) {
+ if ((typeof chunk == 'string') ||
+ (Object.prototype.toString.call(chunk) == '[object Uint8Array]')) {
+ // Wrap ArrayBuffer/string in a Buffer so data[i].copy will work
+ data.push(new Buffer(chunk));
+ } else {
+ data.push(chunk);
+ }
+ dataLen += chunk.length;
+ });
+
+ response.on('end', function(){
+ var buf = new Buffer(dataLen);
+ for (var i=0, len=data.length, pos=0; i<len; i++) {
+ data[i].copy(buf, pos);
+ pos += data[i].length;
+ }
+ //Get the receiver function for the transport and
+ // call it with the buffer
+ self.transport.receiver(decodeCallback)(buf);
+ });
+ };
+};
+util.inherits(HttpConnection, EventEmitter);
+
+/**
+ * Writes Thrift message data to the connection
+ * @param {Buffer} data - A Node.js Buffer containing the data to write
+ * @returns {void} No return value.
+ * @event {error} the "error" event is raised upon request failure passing the
+ * Node.js error object to the listener.
+ */
+HttpConnection.prototype.write = function(data) {
+ var self = this;
+ var opts = self.nodeOptions;
+ opts.headers["Content-length"] = data.length;
+ if (!opts.headers["Content-Type"])
+ opts.headers["Content-Type"] = "application/x-thrift";
+ var req = (self.https) ?
+ https.request(opts, self.responseCallback) :
+ http.request(opts, self.responseCallback);
+ req.on('error', function(err) {
+ self.emit("error", err);
+ });
+ req.write(data);
+ req.end();
+};
+
+/**
+ * Creates a new HttpConnection object, used by Thrift clients to connect
+ * to Thrift HTTP based servers.
+ * @param {string} host - The host name or IP to connect to.
+ * @param {number} port - The TCP port to connect to.
+ * @param {ConnectOptions} options - The configuration options to use.
+ * @returns {HttpConnection} The connection object.
+ * @see {@link ConnectOptions}
+ */
+exports.createHttpConnection = function(host, port, options) {
+ options.host = host;
+ options.port = port || 80;
+ return new HttpConnection(options);
+};
+
+exports.createHttpUDSConnection = function(path, options) {
+ options.socketPath = path;
+ return new HttpConnection(options);
+};
+
+exports.createHttpClient = createClient
+
+
+function THTTPException(response) {
+ thrift.TApplicationException.call(this);
+ Error.captureStackTrace(this, this.constructor);
+ this.name = this.constructor.name;
+ this.statusCode = response.statusCode;
+ this.response = response;
+ this.type = thrift.TApplicationExceptionType.PROTOCOL_ERROR;
+ this.message = "Received a response with a bad HTTP status code: " + response.statusCode;
+}
+util.inherits(THTTPException, thrift.TApplicationException);
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/index.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/index.js
new file mode 100644
index 000000000..0a2d02b71
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/index.js
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+exports.Thrift = require('./thrift');
+
+var log = require('./log');
+exports.setLogFunc = log.setLogFunc;
+exports.setLogLevel = log.setLogLevel;
+exports.getLogLevel = log.getLogLevel;
+
+var connection = require('./connection');
+exports.Connection = connection.Connection;
+exports.createClient = connection.createClient;
+exports.createConnection = connection.createConnection;
+exports.createUDSConnection = connection.createUDSConnection;
+exports.createSSLConnection = connection.createSSLConnection;
+exports.createStdIOClient = connection.createStdIOClient;
+exports.createStdIOConnection = connection.createStdIOConnection;
+
+var httpConnection = require('./http_connection');
+exports.HttpConnection = httpConnection.HttpConnection;
+exports.createHttpConnection = httpConnection.createHttpConnection;
+exports.createHttpUDSConnection = httpConnection.createHttpUDSConnection;
+exports.createHttpClient = httpConnection.createHttpClient;
+
+var wsConnection = require('./ws_connection');
+exports.WSConnection = wsConnection.WSConnection;
+exports.createWSConnection = wsConnection.createWSConnection;
+exports.createWSClient = wsConnection.createWSClient;
+
+var xhrConnection = require('./xhr_connection');
+exports.XHRConnection = xhrConnection.XHRConnection;
+exports.createXHRConnection = xhrConnection.createXHRConnection;
+exports.createXHRClient = xhrConnection.createXHRClient;
+
+var server = require('./server');
+exports.createServer = server.createServer;
+exports.createMultiplexServer = server.createMultiplexServer;
+
+var web_server = require('./web_server');
+exports.createWebServer = web_server.createWebServer;
+
+exports.Int64 = require('node-int64');
+exports.Q = require('q');
+
+var mprocessor = require('./multiplexed_processor');
+var mprotocol = require('./multiplexed_protocol');
+exports.Multiplexer = mprotocol.Multiplexer;
+exports.MultiplexedProcessor = mprocessor.MultiplexedProcessor;
+
+/*
+ * Export transport and protocol so they can be used outside of a
+ * cassandra/server context
+ */
+exports.TFramedTransport = require('./framed_transport');
+exports.TBufferedTransport = require('./buffered_transport');
+exports.TBinaryProtocol = require('./binary_protocol');
+exports.TJSONProtocol = require('./json_protocol');
+exports.TCompactProtocol = require('./compact_protocol');
+exports.THeaderProtocol = require('./header_protocol');
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/input_buffer_underrun_error.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/input_buffer_underrun_error.js
new file mode 100644
index 000000000..72555e516
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/input_buffer_underrun_error.js
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var util = require("util");
+
+module.exports = InputBufferUnderrunError;
+
+function InputBufferUnderrunError(message) {
+ Error.call(this);
+ Error.captureStackTrace(this, this.constructor);
+ this.name = this.constructor.name;
+ this.message = message;
+};
+
+util.inherits(InputBufferUnderrunError, Error);
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/int64_util.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/int64_util.js
new file mode 100644
index 000000000..e8d707de4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/int64_util.js
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var Int64 = require('node-int64');
+
+var Int64Util = module.exports = {};
+
+var POW2_24 = Math.pow(2, 24);
+var POW2_31 = Math.pow(2, 31);
+var POW2_32 = Math.pow(2, 32);
+var POW10_11 = Math.pow(10, 11);
+
+Int64Util.toDecimalString = function(i64) {
+ var b = i64.buffer;
+ var o = i64.offset;
+ if ((!b[o] && !(b[o + 1] & 0xe0)) ||
+ (!~b[o] && !~(b[o + 1] & 0xe0))) {
+ // The magnitude is small enough.
+ return i64.toString();
+ } else {
+ var negative = b[o] & 0x80;
+ if (negative) {
+ // 2's complement
+ var incremented = false;
+ var buffer = new Buffer(8);
+ for (var i = 7; i >= 0; --i) {
+ buffer[i] = (~b[o + i] + (incremented ? 0 : 1)) & 0xff;
+ incremented |= b[o + i];
+ }
+ b = buffer;
+ }
+ var high2 = b[o + 1] + (b[o] << 8);
+ // Lesser 11 digits with exceeding values but is under 53 bits capacity.
+ var low = b[o + 7] + (b[o + 6] << 8) + (b[o + 5] << 16)
+ + b[o + 4] * POW2_24 // Bit shift renders 32th bit as sign, so use multiplication
+ + (b[o + 3] + (b[o + 2] << 8)) * POW2_32 + high2 * 74976710656; // The literal is 2^48 % 10^11
+ // 12th digit and greater.
+ var high = Math.floor(low / POW10_11) + high2 * 2814; // The literal is 2^48 / 10^11
+ // Make it exactly 11 with leading zeros.
+ low = ('00000000000' + String(low % POW10_11)).slice(-11);
+ return (negative ? '-' : '') + String(high) + low;
+ }
+};
+
+Int64Util.fromDecimalString = function(text) {
+ var negative = text.charAt(0) === '-';
+ if (text.length < (negative ? 17 : 16)) {
+ // The magnitude is smaller than 2^53.
+ return new Int64(+text);
+ } else if (text.length > (negative ? 20 : 19)) {
+ throw new RangeError('Too many digits for Int64: ' + text);
+ } else {
+ // Most significant (up to 5) digits
+ var high5 = +text.slice(negative ? 1 : 0, -15);
+ var low = +text.slice(-15) + high5 * 2764472320; // The literal is 10^15 % 2^32
+ var high = Math.floor(low / POW2_32) + high5 * 232830; // The literal is 10^15 / 2^&32
+ low = low % POW2_32;
+ if (high >= POW2_31 &&
+ !(negative && high == POW2_31 && low == 0) // Allow minimum Int64
+ ) {
+ throw new RangeError('The magnitude is too large for Int64.');
+ }
+ if (negative) {
+ // 2's complement
+ high = ~high;
+ if (low === 0) {
+ high = (high + 1) & 0xffffffff;
+ } else {
+ low = ~low + 1;
+ }
+ high = 0x80000000 | high;
+ }
+ return new Int64(high, low);
+ }
+};
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/json_parse.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/json_parse.js
new file mode 100644
index 000000000..93b0bf2ab
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/json_parse.js
@@ -0,0 +1,299 @@
+/*
+ * Imported from Douglas Crockford's reference implementation with minimum modification
+ * to handle Int64.
+ *
+ * https://github.com/douglascrockford/JSON-js/blob/c98948ae1944a28e2e8ebc3717894e580aeaaa05/json_parse.js
+ *
+ * Original license header:
+ *
+ * json_parse.js
+ * 2015-05-02
+ * Public Domain.
+ * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+ */
+
+
+/*jslint for */
+
+/*property
+ at, b, call, charAt, f, fromCharCode, hasOwnProperty, message, n, name,
+ prototype, push, r, t, text
+*/
+
+var Int64 = require('node-int64');
+var Int64Util = require('./int64_util');
+
+var json_parse = module.exports = (function () {
+ "use strict";
+
+// This is a function that can parse a JSON text, producing a JavaScript
+// data structure. It is a simple, recursive descent parser. It does not use
+// eval or regular expressions, so it can be used as a model for implementing
+// a JSON parser in other languages.
+
+// We are defining the function inside of another function to avoid creating
+// global variables.
+
+ var at, // The index of the current character
+ ch, // The current character
+ escapee = {
+ '"': '"',
+ '\\': '\\',
+ '/': '/',
+ b: '\b',
+ f: '\f',
+ n: '\n',
+ r: '\r',
+ t: '\t'
+ },
+ text,
+
+ error = function (m) {
+
+// Call error when something is wrong.
+
+ throw new SyntaxError(m);
+ },
+
+ next = function (c) {
+
+// If a c parameter is provided, verify that it matches the current character.
+
+ if (c && c !== ch) {
+ error("Expected '" + c + "' instead of '" + ch + "'");
+ }
+
+// Get the next character. When there are no more characters,
+// return the empty string.
+
+ ch = text.charAt(at);
+ at += 1;
+ return ch;
+ },
+
+ number = function () {
+
+// Parse a number value.
+
+ var number,
+ string = '';
+
+ if (ch === '-') {
+ string = '-';
+ next('-');
+ }
+ while (ch >= '0' && ch <= '9') {
+ string += ch;
+ next();
+ }
+ if (ch === '.') {
+ string += '.';
+ while (next() && ch >= '0' && ch <= '9') {
+ string += ch;
+ }
+ }
+ if (ch === 'e' || ch === 'E') {
+ string += ch;
+ next();
+ if (ch === '-' || ch === '+') {
+ string += ch;
+ next();
+ }
+ while (ch >= '0' && ch <= '9') {
+ string += ch;
+ next();
+ }
+ }
+ number = +string;
+ if (!isFinite(number)) {
+ error("Bad number");
+ } else if (number >= Int64.MAX_INT || number <= Int64.MIN_INT) {
+ // Return raw string for further process in TJSONProtocol
+ return string;
+ } else {
+ return number;
+ }
+ },
+
+ string = function () {
+
+// Parse a string value.
+
+ var hex,
+ i,
+ string = '',
+ uffff;
+
+// When parsing for string values, we must look for " and \ characters.
+
+ if (ch === '"') {
+ while (next()) {
+ if (ch === '"') {
+ next();
+ return string;
+ }
+ if (ch === '\\') {
+ next();
+ if (ch === 'u') {
+ uffff = 0;
+ for (i = 0; i < 4; i += 1) {
+ hex = parseInt(next(), 16);
+ if (!isFinite(hex)) {
+ break;
+ }
+ uffff = uffff * 16 + hex;
+ }
+ string += String.fromCharCode(uffff);
+ } else if (typeof escapee[ch] === 'string') {
+ string += escapee[ch];
+ } else {
+ break;
+ }
+ } else {
+ string += ch;
+ }
+ }
+ }
+ error("Bad string");
+ },
+
+ white = function () {
+
+// Skip whitespace.
+
+ while (ch && ch <= ' ') {
+ next();
+ }
+ },
+
+ word = function () {
+
+// true, false, or null.
+
+ switch (ch) {
+ case 't':
+ next('t');
+ next('r');
+ next('u');
+ next('e');
+ return true;
+ case 'f':
+ next('f');
+ next('a');
+ next('l');
+ next('s');
+ next('e');
+ return false;
+ case 'n':
+ next('n');
+ next('u');
+ next('l');
+ next('l');
+ return null;
+ }
+ error("Unexpected '" + ch + "'");
+ },
+
+ value, // Place holder for the value function.
+
+ array = function () {
+
+// Parse an array value.
+
+ var array = [];
+
+ if (ch === '[') {
+ next('[');
+ white();
+ if (ch === ']') {
+ next(']');
+ return array; // empty array
+ }
+ while (ch) {
+ array.push(value());
+ white();
+ if (ch === ']') {
+ next(']');
+ return array;
+ }
+ next(',');
+ white();
+ }
+ }
+ error("Bad array");
+ },
+
+ object = function () {
+
+// Parse an object value.
+
+ var key,
+ object = {};
+
+ if (ch === '{') {
+ next('{');
+ white();
+ if (ch === '}') {
+ next('}');
+ return object; // empty object
+ }
+ while (ch) {
+ key = string();
+ white();
+ next(':');
+ if (Object.hasOwnProperty.call(object, key)) {
+ error('Duplicate key "' + key + '"');
+ }
+ object[key] = value();
+ white();
+ if (ch === '}') {
+ next('}');
+ return object;
+ }
+ next(',');
+ white();
+ }
+ }
+ error("Bad object");
+ };
+
+ value = function () {
+
+// Parse a JSON value. It could be an object, an array, a string, a number,
+// or a word.
+
+ white();
+ switch (ch) {
+ case '{':
+ return object();
+ case '[':
+ return array();
+ case '"':
+ return string();
+ case '-':
+ return number();
+ default:
+ return ch >= '0' && ch <= '9'
+ ? number()
+ : word();
+ }
+ };
+
+// Return the json_parse function. It will have access to all of the above
+// functions and variables.
+
+ return function (source) {
+ var result;
+
+ text = source;
+ at = 0;
+ ch = ' ';
+ result = value();
+ white();
+ if (ch) {
+ error("Syntax error");
+ }
+
+ return result;
+ };
+}());
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/json_protocol.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/json_protocol.js
new file mode 100644
index 000000000..7e2b7c908
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/json_protocol.js
@@ -0,0 +1,799 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var Int64 = require('node-int64');
+var Thrift = require('./thrift');
+var Type = Thrift.Type;
+var util = require("util");
+
+var Int64Util = require('./int64_util');
+var json_parse = require('./json_parse');
+
+var InputBufferUnderrunError = require('./input_buffer_underrun_error');
+
+module.exports = TJSONProtocol;
+
+/**
+ * Initializes a Thrift JSON protocol instance.
+ * @constructor
+ * @param {Thrift.Transport} trans - The transport to serialize to/from.
+ * @classdesc Apache Thrift Protocols perform serialization which enables cross
+ * language RPC. The Protocol type is the JavaScript browser implementation
+ * of the Apache Thrift TJSONProtocol.
+ * @example
+ * var protocol = new Thrift.Protocol(transport);
+ */
+function TJSONProtocol(trans) {
+ this.tstack = [];
+ this.tpos = [];
+ this.trans = trans;
+};
+
+/**
+ * Thrift IDL type Id to string mapping.
+ * @readonly
+ * @see {@link Thrift.Type}
+ */
+TJSONProtocol.Type = {};
+TJSONProtocol.Type[Type.BOOL] = '"tf"';
+TJSONProtocol.Type[Type.BYTE] = '"i8"';
+TJSONProtocol.Type[Type.I16] = '"i16"';
+TJSONProtocol.Type[Type.I32] = '"i32"';
+TJSONProtocol.Type[Type.I64] = '"i64"';
+TJSONProtocol.Type[Type.DOUBLE] = '"dbl"';
+TJSONProtocol.Type[Type.STRUCT] = '"rec"';
+TJSONProtocol.Type[Type.STRING] = '"str"';
+TJSONProtocol.Type[Type.MAP] = '"map"';
+TJSONProtocol.Type[Type.LIST] = '"lst"';
+TJSONProtocol.Type[Type.SET] = '"set"';
+
+/**
+ * Thrift IDL type string to Id mapping.
+ * @readonly
+ * @see {@link Thrift.Type}
+ */
+TJSONProtocol.RType = {};
+TJSONProtocol.RType.tf = Type.BOOL;
+TJSONProtocol.RType.i8 = Type.BYTE;
+TJSONProtocol.RType.i16 = Type.I16;
+TJSONProtocol.RType.i32 = Type.I32;
+TJSONProtocol.RType.i64 = Type.I64;
+TJSONProtocol.RType.dbl = Type.DOUBLE;
+TJSONProtocol.RType.rec = Type.STRUCT;
+TJSONProtocol.RType.str = Type.STRING;
+TJSONProtocol.RType.map = Type.MAP;
+TJSONProtocol.RType.lst = Type.LIST;
+TJSONProtocol.RType.set = Type.SET;
+
+/**
+ * The TJSONProtocol version number.
+ * @readonly
+ * @const {number} Version
+ * @memberof Thrift.Protocol
+ */
+TJSONProtocol.Version = 1;
+
+TJSONProtocol.prototype.flush = function() {
+ this.writeToTransportIfStackIsFlushable();
+ return this.trans.flush();
+};
+
+TJSONProtocol.prototype.writeToTransportIfStackIsFlushable = function() {
+ if (this.tstack.length === 1) {
+ this.trans.write(this.tstack.pop());
+ }
+};
+
+/**
+ * Serializes the beginning of a Thrift RPC message.
+ * @param {string} name - The service method to call.
+ * @param {Thrift.MessageType} messageType - The type of method call.
+ * @param {number} seqid - The sequence number of this call (always 0 in Apache Thrift).
+ */
+TJSONProtocol.prototype.writeMessageBegin = function(name, messageType, seqid) {
+ this.tstack.push([TJSONProtocol.Version, '"' + name + '"', messageType, seqid]);
+};
+
+/**
+ * Serializes the end of a Thrift RPC message.
+ */
+TJSONProtocol.prototype.writeMessageEnd = function() {
+ var obj = this.tstack.pop();
+
+ this.wobj = this.tstack.pop();
+ this.wobj.push(obj);
+
+ this.wbuf = '[' + this.wobj.join(',') + ']';
+
+ // we assume there is nothing more to come so we write
+ this.trans.write(this.wbuf);
+};
+
+/**
+ * Serializes the beginning of a struct.
+ * @param {string} name - The name of the struct.
+ */
+TJSONProtocol.prototype.writeStructBegin = function(name) {
+ this.tpos.push(this.tstack.length);
+ this.tstack.push({});
+};
+
+/**
+ * Serializes the end of a struct.
+ */
+TJSONProtocol.prototype.writeStructEnd = function() {
+ var p = this.tpos.pop();
+ var struct = this.tstack[p];
+ var str = '{';
+ var first = true;
+ for (var key in struct) {
+ if (first) {
+ first = false;
+ } else {
+ str += ',';
+ }
+
+ str += key + ':' + struct[key];
+ }
+
+ str += '}';
+ this.tstack[p] = str;
+
+ this.writeToTransportIfStackIsFlushable();
+};
+
+/**
+ * Serializes the beginning of a struct field.
+ * @param {string} name - The name of the field.
+ * @param {Thrift.Protocol.Type} fieldType - The data type of the field.
+ * @param {number} fieldId - The field's unique identifier.
+ */
+TJSONProtocol.prototype.writeFieldBegin = function(name, fieldType, fieldId) {
+ this.tpos.push(this.tstack.length);
+ this.tstack.push({ 'fieldId': '"' +
+ fieldId + '"', 'fieldType': TJSONProtocol.Type[fieldType]
+ });
+};
+
+/**
+ * Serializes the end of a field.
+ */
+TJSONProtocol.prototype.writeFieldEnd = function() {
+ var value = this.tstack.pop();
+ var fieldInfo = this.tstack.pop();
+
+ if (':' + value === ":[object Object]") {
+ this.tstack[this.tstack.length - 1][fieldInfo.fieldId] = '{' +
+ fieldInfo.fieldType + ':' + JSON.stringify(value) + '}';
+ } else {
+ this.tstack[this.tstack.length - 1][fieldInfo.fieldId] = '{' +
+ fieldInfo.fieldType + ':' + value + '}';
+ }
+ this.tpos.pop();
+
+ this.writeToTransportIfStackIsFlushable();
+};
+
+/**
+ * Serializes the end of the set of fields for a struct.
+ */
+TJSONProtocol.prototype.writeFieldStop = function() {
+};
+
+/**
+ * Serializes the beginning of a map collection.
+ * @param {Thrift.Type} keyType - The data type of the key.
+ * @param {Thrift.Type} valType - The data type of the value.
+ * @param {number} [size] - The number of elements in the map (ignored).
+ */
+TJSONProtocol.prototype.writeMapBegin = function(keyType, valType, size) {
+ //size is invalid, we'll set it on end.
+ this.tpos.push(this.tstack.length);
+ this.tstack.push([TJSONProtocol.Type[keyType], TJSONProtocol.Type[valType], 0]);
+};
+
+/**
+ * Serializes the end of a map.
+ */
+TJSONProtocol.prototype.writeMapEnd = function() {
+ var p = this.tpos.pop();
+
+ if (p == this.tstack.length) {
+ return;
+ }
+
+ if ((this.tstack.length - p - 1) % 2 !== 0) {
+ this.tstack.push('');
+ }
+
+ var size = (this.tstack.length - p - 1) / 2;
+
+ this.tstack[p][this.tstack[p].length - 1] = size;
+
+ var map = '}';
+ var first = true;
+ while (this.tstack.length > p + 1) {
+ var v = this.tstack.pop();
+ var k = this.tstack.pop();
+ if (first) {
+ first = false;
+ } else {
+ map = ',' + map;
+ }
+
+ if (! isNaN(k)) { k = '"' + k + '"'; } //json "keys" need to be strings
+ map = k + ':' + v + map;
+ }
+ map = '{' + map;
+
+ this.tstack[p].push(map);
+ this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
+
+ this.writeToTransportIfStackIsFlushable();
+};
+
+/**
+ * Serializes the beginning of a list collection.
+ * @param {Thrift.Type} elemType - The data type of the elements.
+ * @param {number} size - The number of elements in the list.
+ */
+TJSONProtocol.prototype.writeListBegin = function(elemType, size) {
+ this.tpos.push(this.tstack.length);
+ this.tstack.push([TJSONProtocol.Type[elemType], size]);
+};
+
+/**
+ * Serializes the end of a list.
+ */
+TJSONProtocol.prototype.writeListEnd = function() {
+ var p = this.tpos.pop();
+
+ while (this.tstack.length > p + 1) {
+ var tmpVal = this.tstack[p + 1];
+ this.tstack.splice(p + 1, 1);
+ this.tstack[p].push(tmpVal);
+ }
+
+ this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
+
+ this.writeToTransportIfStackIsFlushable();
+};
+
+/**
+ * Serializes the beginning of a set collection.
+ * @param {Thrift.Type} elemType - The data type of the elements.
+ * @param {number} size - The number of elements in the list.
+ */
+TJSONProtocol.prototype.writeSetBegin = function(elemType, size) {
+ this.tpos.push(this.tstack.length);
+ this.tstack.push([TJSONProtocol.Type[elemType], size]);
+};
+
+/**
+ * Serializes the end of a set.
+ */
+TJSONProtocol.prototype.writeSetEnd = function() {
+ var p = this.tpos.pop();
+
+ while (this.tstack.length > p + 1) {
+ var tmpVal = this.tstack[p + 1];
+ this.tstack.splice(p + 1, 1);
+ this.tstack[p].push(tmpVal);
+ }
+
+ this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
+
+ this.writeToTransportIfStackIsFlushable();
+};
+
+/** Serializes a boolean */
+TJSONProtocol.prototype.writeBool = function(bool) {
+ this.tstack.push(bool ? 1 : 0);
+};
+
+/** Serializes a number */
+TJSONProtocol.prototype.writeByte = function(byte) {
+ this.tstack.push(byte);
+};
+
+/** Serializes a number */
+TJSONProtocol.prototype.writeI16 = function(i16) {
+ this.tstack.push(i16);
+};
+
+/** Serializes a number */
+TJSONProtocol.prototype.writeI32 = function(i32) {
+ this.tstack.push(i32);
+};
+
+/** Serializes a number */
+TJSONProtocol.prototype.writeI64 = function(i64) {
+ if (i64 instanceof Int64) {
+ this.tstack.push(Int64Util.toDecimalString(i64));
+ } else {
+ this.tstack.push(i64);
+ }
+};
+
+/** Serializes a number */
+TJSONProtocol.prototype.writeDouble = function(dub) {
+ this.tstack.push(dub);
+};
+
+/** Serializes a string */
+TJSONProtocol.prototype.writeString = function(arg) {
+ // We do not encode uri components for wire transfer:
+ if (arg === null) {
+ this.tstack.push(null);
+ } else {
+ if (typeof arg === 'string') {
+ var str = arg;
+ } else if (arg instanceof Buffer) {
+ var str = arg.toString('utf8');
+ } else {
+ throw new Error('writeString called without a string/Buffer argument: ' + arg);
+ }
+
+ // concat may be slower than building a byte buffer
+ var escapedString = '';
+ for (var i = 0; i < str.length; i++) {
+ var ch = str.charAt(i); // a single double quote: "
+ if (ch === '\"') {
+ escapedString += '\\\"'; // write out as: \"
+ } else if (ch === '\\') { // a single backslash: \
+ escapedString += '\\\\'; // write out as: \\
+ /* Currently escaped forward slashes break TJSONProtocol.
+ * As it stands, we can simply pass forward slashes into
+ * our strings across the wire without being escaped.
+ * I think this is the protocol's bug, not thrift.js
+ * } else if(ch === '/') { // a single forward slash: /
+ * escapedString += '\\/'; // write out as \/
+ * }
+ */
+ } else if (ch === '\b') { // a single backspace: invisible
+ escapedString += '\\b'; // write out as: \b"
+ } else if (ch === '\f') { // a single formfeed: invisible
+ escapedString += '\\f'; // write out as: \f"
+ } else if (ch === '\n') { // a single newline: invisible
+ escapedString += '\\n'; // write out as: \n"
+ } else if (ch === '\r') { // a single return: invisible
+ escapedString += '\\r'; // write out as: \r"
+ } else if (ch === '\t') { // a single tab: invisible
+ escapedString += '\\t'; // write out as: \t"
+ } else {
+ escapedString += ch; // Else it need not be escaped
+ }
+ }
+ this.tstack.push('"' + escapedString + '"');
+ }
+};
+
+/** Serializes a string */
+TJSONProtocol.prototype.writeBinary = function(arg) {
+ if (typeof arg === 'string') {
+ var buf = new Buffer(arg, 'binary');
+ } else if (arg instanceof Buffer ||
+ Object.prototype.toString.call(arg) == '[object Uint8Array]') {
+ var buf = arg;
+ } else {
+ throw new Error('writeBinary called without a string/Buffer argument: ' + arg);
+ }
+ this.tstack.push('"' + buf.toString('base64') + '"');
+};
+
+/**
+ * @class
+ * @name AnonReadMessageBeginReturn
+ * @property {string} fname - The name of the service method.
+ * @property {Thrift.MessageType} mtype - The type of message call.
+ * @property {number} rseqid - The sequence number of the message (0 in Thrift RPC).
+ */
+/**
+ * Deserializes the beginning of a message.
+ * @returns {AnonReadMessageBeginReturn}
+ */
+TJSONProtocol.prototype.readMessageBegin = function() {
+ this.rstack = [];
+ this.rpos = [];
+
+ //Borrow the inbound transport buffer and ensure data is present/consistent
+ var transBuf = this.trans.borrow();
+ if (transBuf.readIndex >= transBuf.writeIndex) {
+ throw new InputBufferUnderrunError();
+ }
+ var cursor = transBuf.readIndex;
+
+ if (transBuf.buf[cursor] !== 0x5B) { //[
+ throw new Error("Malformed JSON input, no opening bracket");
+ }
+
+ //Parse a single message (there may be several in the buffer)
+ // TODO: Handle characters using multiple code units
+ cursor++;
+ var openBracketCount = 1;
+ var inString = false;
+ for (; cursor < transBuf.writeIndex; cursor++) {
+ var chr = transBuf.buf[cursor];
+ //we use hexa charcode here because data[i] returns an int and not a char
+ if (inString) {
+ if (chr === 0x22) { //"
+ inString = false;
+ } else if (chr === 0x5C) { //\
+ //escaped character, skip
+ cursor += 1;
+ }
+ } else {
+ if (chr === 0x5B) { //[
+ openBracketCount += 1;
+ } else if (chr === 0x5D) { //]
+ openBracketCount -= 1;
+ if (openBracketCount === 0) {
+ //end of json message detected
+ break;
+ }
+ } else if (chr === 0x22) { //"
+ inString = true;
+ }
+ }
+ }
+
+ if (openBracketCount !== 0) {
+ // Missing closing bracket. Can be buffer underrun.
+ throw new InputBufferUnderrunError();
+ }
+
+ //Reconstitute the JSON object and conume the necessary bytes
+ this.robj = json_parse(transBuf.buf.slice(transBuf.readIndex, cursor+1).toString());
+ this.trans.consume(cursor + 1 - transBuf.readIndex);
+
+ //Verify the protocol version
+ var version = this.robj.shift();
+ if (version != TJSONProtocol.Version) {
+ throw new Error('Wrong thrift protocol version: ' + version);
+ }
+
+ //Objectify the thrift message {name/type/sequence-number} for return
+ // and then save the JSON object in rstack
+ var r = {};
+ r.fname = this.robj.shift();
+ r.mtype = this.robj.shift();
+ r.rseqid = this.robj.shift();
+ this.rstack.push(this.robj.shift());
+ return r;
+};
+
+/** Deserializes the end of a message. */
+TJSONProtocol.prototype.readMessageEnd = function() {
+};
+
+/**
+ * Deserializes the beginning of a struct.
+ * @param {string} [name] - The name of the struct (ignored)
+ * @returns {object} - An object with an empty string fname property
+ */
+TJSONProtocol.prototype.readStructBegin = function() {
+ var r = {};
+ r.fname = '';
+
+ //incase this is an array of structs
+ if (this.rstack[this.rstack.length - 1] instanceof Array) {
+ this.rstack.push(this.rstack[this.rstack.length - 1].shift());
+ }
+
+ return r;
+};
+
+/** Deserializes the end of a struct. */
+TJSONProtocol.prototype.readStructEnd = function() {
+ this.rstack.pop();
+};
+
+/**
+ * @class
+ * @name AnonReadFieldBeginReturn
+ * @property {string} fname - The name of the field (always '').
+ * @property {Thrift.Type} ftype - The data type of the field.
+ * @property {number} fid - The unique identifier of the field.
+ */
+/**
+ * Deserializes the beginning of a field.
+ * @returns {AnonReadFieldBeginReturn}
+ */
+TJSONProtocol.prototype.readFieldBegin = function() {
+ var r = {};
+
+ var fid = -1;
+ var ftype = Type.STOP;
+
+ //get a fieldId
+ for (var f in (this.rstack[this.rstack.length - 1])) {
+ if (f === null) {
+ continue;
+ }
+
+ fid = parseInt(f, 10);
+ this.rpos.push(this.rstack.length);
+
+ var field = this.rstack[this.rstack.length - 1][fid];
+
+ //remove so we don't see it again
+ delete this.rstack[this.rstack.length - 1][fid];
+
+ this.rstack.push(field);
+
+ break;
+ }
+
+ if (fid != -1) {
+ //should only be 1 of these but this is the only
+ //way to match a key
+ for (var i in (this.rstack[this.rstack.length - 1])) {
+ if (TJSONProtocol.RType[i] === null) {
+ continue;
+ }
+
+ ftype = TJSONProtocol.RType[i];
+ this.rstack[this.rstack.length - 1] = this.rstack[this.rstack.length - 1][i];
+ }
+ }
+
+ r.fname = '';
+ r.ftype = ftype;
+ r.fid = fid;
+
+ return r;
+};
+
+/** Deserializes the end of a field. */
+TJSONProtocol.prototype.readFieldEnd = function() {
+ var pos = this.rpos.pop();
+
+ //get back to the right place in the stack
+ while (this.rstack.length > pos) {
+ this.rstack.pop();
+ }
+};
+
+/**
+ * @class
+ * @name AnonReadMapBeginReturn
+ * @property {Thrift.Type} ktype - The data type of the key.
+ * @property {Thrift.Type} vtype - The data type of the value.
+ * @property {number} size - The number of elements in the map.
+ */
+/**
+ * Deserializes the beginning of a map.
+ * @returns {AnonReadMapBeginReturn}
+ */
+TJSONProtocol.prototype.readMapBegin = function() {
+ var map = this.rstack.pop();
+ var first = map.shift();
+ if (first instanceof Array) {
+ this.rstack.push(map);
+ map = first;
+ first = map.shift();
+ }
+
+ var r = {};
+ r.ktype = TJSONProtocol.RType[first];
+ r.vtype = TJSONProtocol.RType[map.shift()];
+ r.size = map.shift();
+
+
+ this.rpos.push(this.rstack.length);
+ this.rstack.push(map.shift());
+
+ return r;
+};
+
+/** Deserializes the end of a map. */
+TJSONProtocol.prototype.readMapEnd = function() {
+ this.readFieldEnd();
+};
+
+/**
+ * @class
+ * @name AnonReadColBeginReturn
+ * @property {Thrift.Type} etype - The data type of the element.
+ * @property {number} size - The number of elements in the collection.
+ */
+/**
+ * Deserializes the beginning of a list.
+ * @returns {AnonReadColBeginReturn}
+ */
+TJSONProtocol.prototype.readListBegin = function() {
+ var list = this.rstack[this.rstack.length - 1];
+
+ var r = {};
+ r.etype = TJSONProtocol.RType[list.shift()];
+ r.size = list.shift();
+
+ this.rpos.push(this.rstack.length);
+ this.rstack.push(list.shift());
+
+ return r;
+};
+
+/** Deserializes the end of a list. */
+TJSONProtocol.prototype.readListEnd = function() {
+ var pos = this.rpos.pop() - 2;
+ var st = this.rstack;
+ st.pop();
+ if (st instanceof Array && st.length > pos && st[pos].length > 0) {
+ st.push(st[pos].shift());
+ }
+};
+
+/**
+ * Deserializes the beginning of a set.
+ * @returns {AnonReadColBeginReturn}
+ */
+TJSONProtocol.prototype.readSetBegin = function() {
+ return this.readListBegin();
+};
+
+/** Deserializes the end of a set. */
+TJSONProtocol.prototype.readSetEnd = function() {
+ return this.readListEnd();
+};
+
+TJSONProtocol.prototype.readBool = function() {
+ return this.readValue() == '1';
+};
+
+TJSONProtocol.prototype.readByte = function() {
+ return this.readI32();
+};
+
+TJSONProtocol.prototype.readI16 = function() {
+ return this.readI32();
+};
+
+TJSONProtocol.prototype.readI32 = function(f) {
+ return +this.readValue();
+}
+
+/** Returns the next value found in the protocol buffer */
+TJSONProtocol.prototype.readValue = function(f) {
+ if (f === undefined) {
+ f = this.rstack[this.rstack.length - 1];
+ }
+
+ var r = {};
+
+ if (f instanceof Array) {
+ if (f.length === 0) {
+ r.value = undefined;
+ } else {
+ r.value = f.shift();
+ }
+ } else if (!(f instanceof Int64) && f instanceof Object) {
+ for (var i in f) {
+ if (i === null) {
+ continue;
+ }
+ this.rstack.push(f[i]);
+ delete f[i];
+
+ r.value = i;
+ break;
+ }
+ } else {
+ r.value = f;
+ this.rstack.pop();
+ }
+
+ return r.value;
+};
+
+TJSONProtocol.prototype.readI64 = function() {
+ var n = this.readValue()
+ if (typeof n === 'string') {
+ // Assuming no one is sending in 1.11111e+33 format
+ return Int64Util.fromDecimalString(n);
+ } else {
+ return new Int64(n);
+ }
+};
+
+TJSONProtocol.prototype.readDouble = function() {
+ return this.readI32();
+};
+
+TJSONProtocol.prototype.readBinary = function() {
+ return new Buffer(this.readValue(), 'base64');
+};
+
+TJSONProtocol.prototype.readString = function() {
+ return this.readValue();
+};
+
+/**
+ * Returns the underlying transport.
+ * @readonly
+ * @returns {Thrift.Transport} The underlying transport.
+ */
+TJSONProtocol.prototype.getTransport = function() {
+ return this.trans;
+};
+
+/**
+ * Method to arbitrarily skip over data
+ */
+TJSONProtocol.prototype.skip = function(type) {
+ switch (type) {
+ case Type.BOOL:
+ this.readBool();
+ break;
+ case Type.BYTE:
+ this.readByte();
+ break;
+ case Type.I16:
+ this.readI16();
+ break;
+ case Type.I32:
+ this.readI32();
+ break;
+ case Type.I64:
+ this.readI64();
+ break;
+ case Type.DOUBLE:
+ this.readDouble();
+ break;
+ case Type.STRING:
+ this.readString();
+ break;
+ case Type.STRUCT:
+ this.readStructBegin();
+ while (true) {
+ var r = this.readFieldBegin();
+ if (r.ftype === Type.STOP) {
+ break;
+ }
+ this.skip(r.ftype);
+ this.readFieldEnd();
+ }
+ this.readStructEnd();
+ break;
+ case Type.MAP:
+ var mapBegin = this.readMapBegin();
+ for (var i = 0; i < mapBegin.size; ++i) {
+ this.skip(mapBegin.ktype);
+ this.skip(mapBegin.vtype);
+ }
+ this.readMapEnd();
+ break;
+ case Type.SET:
+ var setBegin = this.readSetBegin();
+ for (var i2 = 0; i2 < setBegin.size; ++i2) {
+ this.skip(setBegin.etype);
+ }
+ this.readSetEnd();
+ break;
+ case Type.LIST:
+ var listBegin = this.readListBegin();
+ for (var i3 = 0; i3 < listBegin.size; ++i3) {
+ this.skip(listBegin.etype);
+ }
+ this.readListEnd();
+ break;
+ default:
+ throw new Error("Invalid type: " + type);
+ }
+};
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/log.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/log.js
new file mode 100644
index 000000000..053e81361
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/log.js
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var util = require('util');
+
+var disabled = function () {};
+var logFunc = console.log;
+var logLevel = 'error'; // default level
+
+function factory(level) {
+ return function () {
+ // better use spread syntax, but due to compatibility,
+ // use legacy method here.
+ var args = ['thrift: [' + level + '] '].concat(Array.from(arguments));
+ return logFunc(util.format.apply(null, args));
+ };
+}
+
+var trace = disabled;
+var debug = disabled;
+var error = disabled;
+var warning = disabled;
+var info = disabled;
+
+exports.setLogFunc = function (func) {
+ logFunc = func;
+};
+
+var setLogLevel = exports.setLogLevel = function (level) {
+ trace = debug = error = warning = info = disabled;
+ logLevel = level;
+ switch (logLevel) {
+ case 'trace':
+ trace = factory('TRACE');
+ case 'debug':
+ debug = factory('DEBUG');
+ case 'error':
+ error = factory('ERROR');
+ case 'warning':
+ warning = factory('WARN');
+ case 'info':
+ info = factory('INFO');
+ }
+};
+
+// set default
+setLogLevel(logLevel);
+
+exports.getLogLevel = function () {
+ return logLevel;
+};
+
+exports.trace = function () {
+ return trace.apply(null, arguments);
+};
+
+exports.debug = function () {
+ return debug.apply(null, arguments);
+};
+
+exports.error = function () {
+ return error.apply(null, arguments);
+};
+
+exports.warning = function () {
+ return warning.apply(null, arguments);
+};
+
+exports.info = function () {
+ return info.apply(null, arguments);
+};
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/multiplexed_processor.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/multiplexed_processor.js
new file mode 100644
index 000000000..67b62f7a2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/multiplexed_processor.js
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var Thrift = require('./thrift');
+
+exports.MultiplexedProcessor = MultiplexedProcessor;
+
+function MultiplexedProcessor(stream, options) {
+ this.services = {};
+};
+
+MultiplexedProcessor.prototype.registerProcessor = function(name, handler) {
+ this.services[name] = handler;
+};
+
+MultiplexedProcessor.prototype.process = function(inp, out) {
+ var begin = inp.readMessageBegin();
+
+ if (begin.mtype != Thrift.MessageType.CALL && begin.mtype != Thrift.MessageType.ONEWAY) {
+ throw new Thrift.TException('TMultiplexedProcessor: Unexpected message type');
+ }
+
+ var p = begin.fname.split(':');
+ var sname = p[0];
+ var fname = p[1];
+
+ if (! (sname in this.services)) {
+ throw new Thrift.TException('TMultiplexedProcessor: Unknown service: ' + sname);
+ }
+
+ //construct a proxy object which stubs the readMessageBegin
+ //for the service
+ var inpProxy = {};
+
+ for (var attr in inp) {
+ inpProxy[attr] = inp[attr];
+ }
+
+ inpProxy.readMessageBegin = function() {
+ return {
+ fname: fname,
+ mtype: begin.mtype,
+ rseqid: begin.rseqid
+ };
+ };
+
+ this.services[sname].process(inpProxy, out);
+};
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/multiplexed_protocol.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/multiplexed_protocol.js
new file mode 100644
index 000000000..d078aa226
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/multiplexed_protocol.js
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var util = require('util');
+var Thrift = require('./thrift');
+
+exports.Multiplexer = Multiplexer;
+
+function Wrapper(serviceName, protocol, connection) {
+
+ function MultiplexProtocol(trans, strictRead, strictWrite) {
+ protocol.call(this, trans, strictRead, strictWrite);
+ };
+
+ util.inherits(MultiplexProtocol, protocol);
+
+ MultiplexProtocol.prototype.writeMessageBegin = function(name, type, seqid) {
+ if (type == Thrift.MessageType.CALL || type == Thrift.MessageType.ONEWAY) {
+ connection.seqId2Service[seqid] = serviceName;
+ MultiplexProtocol.super_.prototype.writeMessageBegin.call(this,
+ serviceName + ":" + name,
+ type,
+ seqid);
+ } else {
+ MultiplexProtocol.super_.prototype.writeMessageBegin.call(this, name, type, seqid);
+ }
+ };
+
+ return MultiplexProtocol;
+};
+
+function Multiplexer() {
+ this.seqid = 0;
+};
+
+Multiplexer.prototype.createClient = function(serviceName, ServiceClient, connection) {
+ if (ServiceClient.Client) {
+ ServiceClient = ServiceClient.Client;
+ }
+ var writeCb = function(buf, seqid) {
+ connection.write(buf,seqid);
+ };
+ var transport = new connection.transport(undefined, writeCb);
+ var protocolWrapper = new Wrapper(serviceName, connection.protocol, connection);
+ var client = new ServiceClient(transport, protocolWrapper);
+ var self = this;
+ client.new_seqid = function() {
+ self.seqid += 1;
+ return self.seqid;
+ };
+
+ if (typeof connection.client !== 'object') {
+ connection.client = {};
+ }
+ connection.client[serviceName] = client;
+
+ return client;
+};
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/protocol.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/protocol.js
new file mode 100644
index 000000000..a70ebe287
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/protocol.js
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+module.exports.TBinaryProtocol = require('./binary_protocol');
+module.exports.TCompactProtocol = require('./compact_protocol');
+module.exports.TJSONProtocol = require('./json_protocol');
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/server.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/server.js
new file mode 100644
index 000000000..16b74eaaf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/server.js
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var constants = require('constants');
+var net = require('net');
+var tls = require('tls');
+
+var TBufferedTransport = require('./buffered_transport');
+var TBinaryProtocol = require('./binary_protocol');
+var THeaderProtocol = require('./header_protocol');
+var InputBufferUnderrunError = require('./input_buffer_underrun_error');
+
+/**
+ * Create a Thrift server which can serve one or multiple services.
+ * @param {object} processor - A normal or multiplexedProcessor (must
+ * be preconstructed with the desired handler).
+ * @param {ServerOptions} options - Optional additional server configuration.
+ * @returns {object} - The Apache Thrift Multiplex Server.
+ */
+exports.createMultiplexServer = function(processor, options) {
+ var transport = (options && options.transport) ? options.transport : TBufferedTransport;
+ var protocol = (options && options.protocol) ? options.protocol : TBinaryProtocol;
+
+ function serverImpl(stream) {
+ var self = this;
+ stream.on('error', function(err) {
+ self.emit('error', err);
+ });
+ stream.on('data', transport.receiver(function(transportWithData) {
+ var input = new protocol(transportWithData);
+ var outputCb = function(buf) {
+ try {
+ stream.write(buf);
+ } catch (err) {
+ self.emit('error', err);
+ stream.end();
+ }
+ };
+
+ var output = new protocol(new transport(undefined, outputCb));
+ // Read and write need to be performed on the same transport
+ // for THeaderProtocol because we should only respond with
+ // headers if the request contains headers
+ if (protocol === THeaderProtocol) {
+ output = input;
+ output.trans.onFlush = outputCb;
+ }
+
+ try {
+ do {
+ processor.process(input, output);
+ transportWithData.commitPosition();
+ } while (true);
+ } catch (err) {
+ if (err instanceof InputBufferUnderrunError) {
+ //The last data in the buffer was not a complete message, wait for the rest
+ transportWithData.rollbackPosition();
+ }
+ else if (err.message === "Invalid type: undefined") {
+ //No more data in the buffer
+ //This trap is a bit hackish
+ //The next step to improve the node behavior here is to have
+ // the compiler generated process method throw a more explicit
+ // error when the network buffer is empty (regardles of the
+ // protocol/transport stack in use) and replace this heuristic.
+ // Also transports should probably not force upper layers to
+ // manage their buffer positions (i.e. rollbackPosition() and
+ // commitPosition() should be eliminated in lieu of a transport
+ // encapsulated buffer management strategy.)
+ transportWithData.rollbackPosition();
+ }
+ else {
+ //Unexpected error
+ self.emit('error', err);
+ stream.end();
+ }
+ }
+ }));
+
+ stream.on('end', function() {
+ stream.end();
+ });
+ }
+
+ if (options && options.tls) {
+ if (!('secureProtocol' in options.tls) && !('secureOptions' in options.tls)) {
+ options.tls.secureProtocol = "SSLv23_method";
+ options.tls.secureOptions = constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3;
+ }
+ return tls.createServer(options.tls, serverImpl);
+ } else {
+ return net.createServer(serverImpl);
+ }
+};
+
+/**
+ * Create a single service Apache Thrift server.
+ * @param {object} processor - A service class or processor function.
+ * @param {ServerOptions} options - Optional additional server configuration.
+ * @returns {object} - The Apache Thrift Multiplex Server.
+ */
+exports.createServer = function(processor, handler, options) {
+ if (processor.Processor) {
+ processor = processor.Processor;
+ }
+ return exports.createMultiplexServer(new processor(handler), options);
+};
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/thrift.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/thrift.js
new file mode 100644
index 000000000..f2b289672
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/thrift.js
@@ -0,0 +1,232 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var util = require('util');
+
+var Type = exports.Type = {
+ STOP: 0,
+ VOID: 1,
+ BOOL: 2,
+ BYTE: 3,
+ I08: 3,
+ DOUBLE: 4,
+ I16: 6,
+ I32: 8,
+ I64: 10,
+ STRING: 11,
+ UTF7: 11,
+ STRUCT: 12,
+ MAP: 13,
+ SET: 14,
+ LIST: 15,
+ UTF8: 16,
+ UTF16: 17
+};
+
+exports.MessageType = {
+ CALL: 1,
+ REPLY: 2,
+ EXCEPTION: 3,
+ ONEWAY: 4
+};
+
+exports.TException = TException;
+
+function TException(message) {
+ Error.call(this);
+ Error.captureStackTrace(this, this.constructor);
+ this.name = this.constructor.name;
+ this.message = message;
+};
+util.inherits(TException, Error);
+
+var TApplicationExceptionType = exports.TApplicationExceptionType = {
+ UNKNOWN: 0,
+ UNKNOWN_METHOD: 1,
+ INVALID_MESSAGE_TYPE: 2,
+ WRONG_METHOD_NAME: 3,
+ BAD_SEQUENCE_ID: 4,
+ MISSING_RESULT: 5,
+ INTERNAL_ERROR: 6,
+ PROTOCOL_ERROR: 7,
+ INVALID_TRANSFORM: 8,
+ INVALID_PROTOCOL: 9,
+ UNSUPPORTED_CLIENT_TYPE: 10
+};
+
+exports.TApplicationException = TApplicationException;
+
+function TApplicationException(type, message) {
+ TException.call(this);
+ Error.captureStackTrace(this, this.constructor);
+ this.type = type || TApplicationExceptionType.UNKNOWN;
+ this.name = this.constructor.name;
+ this.message = message;
+};
+util.inherits(TApplicationException, TException);
+
+TApplicationException.prototype.read = function(input) {
+ var ftype;
+ var ret = input.readStructBegin('TApplicationException');
+
+ while(1){
+ ret = input.readFieldBegin();
+ if(ret.ftype == Type.STOP)
+ break;
+
+ switch(ret.fid){
+ case 1:
+ if( ret.ftype == Type.STRING ){
+ ret = input.readString();
+ this.message = ret;
+ } else {
+ ret = input.skip(ret.ftype);
+ }
+ break;
+ case 2:
+ if( ret.ftype == Type.I32 ){
+ ret = input.readI32();
+ this.type = ret;
+ } else {
+ ret = input.skip(ret.ftype);
+ }
+ break;
+ default:
+ ret = input.skip(ret.ftype);
+ break;
+ }
+ input.readFieldEnd();
+ }
+ input.readStructEnd();
+};
+
+TApplicationException.prototype.write = function(output){
+ output.writeStructBegin('TApplicationException');
+
+ if (this.message) {
+ output.writeFieldBegin('message', Type.STRING, 1);
+ output.writeString(this.message);
+ output.writeFieldEnd();
+ }
+
+ if (this.code) {
+ output.writeFieldBegin('type', Type.I32, 2);
+ output.writeI32(this.code);
+ output.writeFieldEnd();
+ }
+
+ output.writeFieldStop();
+ output.writeStructEnd();
+};
+
+var TProtocolExceptionType = exports.TProtocolExceptionType = {
+ UNKNOWN: 0,
+ INVALID_DATA: 1,
+ NEGATIVE_SIZE: 2,
+ SIZE_LIMIT: 3,
+ BAD_VERSION: 4,
+ NOT_IMPLEMENTED: 5,
+ DEPTH_LIMIT: 6
+};
+
+
+exports.TProtocolException = TProtocolException;
+
+function TProtocolException(type, message) {
+ Error.call(this);
+ Error.captureStackTrace(this, this.constructor);
+ this.name = this.constructor.name;
+ this.type = type;
+ this.message = message;
+};
+util.inherits(TProtocolException, Error);
+
+exports.objectLength = function(obj) {
+ return Object.keys(obj).length;
+};
+
+exports.inherits = function(constructor, superConstructor) {
+ util.inherits(constructor, superConstructor);
+};
+
+var copyList, copyMap;
+
+copyList = function(lst, types) {
+
+ if (!lst) {return lst; }
+
+ var type;
+
+ if (types.shift === undefined) {
+ type = types;
+ }
+ else {
+ type = types[0];
+ }
+ var Type = type;
+
+ var len = lst.length, result = [], i, val;
+ for (i = 0; i < len; i++) {
+ val = lst[i];
+ if (type === null) {
+ result.push(val);
+ }
+ else if (type === copyMap || type === copyList) {
+ result.push(type(val, types.slice(1)));
+ }
+ else {
+ result.push(new Type(val));
+ }
+ }
+ return result;
+};
+
+copyMap = function(obj, types){
+
+ if (!obj) {return obj; }
+
+ var type;
+
+ if (types.shift === undefined) {
+ type = types;
+ }
+ else {
+ type = types[0];
+ }
+ var Type = type;
+
+ var result = {}, val;
+ for(var prop in obj) {
+ if(obj.hasOwnProperty(prop)) {
+ val = obj[prop];
+ if (type === null) {
+ result[prop] = val;
+ }
+ else if (type === copyMap || type === copyList) {
+ result[prop] = type(val, types.slice(1));
+ }
+ else {
+ result[prop] = new Type(val);
+ }
+ }
+ }
+ return result;
+};
+
+module.exports.copyMap = copyMap;
+module.exports.copyList = copyList;
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/transport.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/transport.js
new file mode 100644
index 000000000..59daa987d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/transport.js
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+module.exports.TBufferedTransport = require('./buffered_transport');
+module.exports.TFramedTransport = require('./framed_transport');
+module.exports.InputBufferUnderrunError = require('./input_buffer_underrun_error');
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/web_server.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/web_server.js
new file mode 100644
index 000000000..a33f47aed
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/web_server.js
@@ -0,0 +1,567 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var http = require('http');
+var https = require('https');
+var url = require("url");
+var path = require("path");
+var fs = require("fs");
+var crypto = require("crypto");
+var log = require('./log');
+
+var MultiplexedProcessor = require('./multiplexed_processor').MultiplexedProcessor;
+
+var TBufferedTransport = require('./buffered_transport');
+var TBinaryProtocol = require('./binary_protocol');
+var InputBufferUnderrunError = require('./input_buffer_underrun_error');
+
+// WSFrame constructor and prototype
+/////////////////////////////////////////////////////////////////////
+
+/** Apache Thrift RPC Web Socket Transport
+ * Frame layout conforming to RFC 6455 circa 12/2011
+ *
+ * Theoretical frame size limit is 4GB*4GB, however the Node Buffer
+ * limit is 1GB as of v0.10. The frame length encoding is also
+ * configured for a max of 4GB presently and needs to be adjusted
+ * if Node/Browsers become capabile of > 4GB frames.
+ *
+ * - FIN is 1 if the message is complete
+ * - RSV1/2/3 are always 0
+ * - Opcode is 1(TEXT) for TJSONProtocol and 2(BIN) for TBinaryProtocol
+ * - Mask Present bit is 1 sending to-server and 0 sending to-client
+ * - Payload Len:
+ * + If < 126: then represented directly
+ * + If >=126: but within range of an unsigned 16 bit integer
+ * then Payload Len is 126 and the two following bytes store
+ * the length
+ * + Else: Payload Len is 127 and the following 8 bytes store the
+ * length as an unsigned 64 bit integer
+ * - Masking key is a 32 bit key only present when sending to the server
+ * - Payload follows the masking key or length
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-------+-+-------------+-------------------------------+
+ * |F|R|R|R| opcode|M| Payload len | Extended payload length |
+ * |I|S|S|S| (4) |A| (7) | (16/64) |
+ * |N|V|V|V| |S| | (if payload len==126/127) |
+ * | |1|2|3| |K| | |
+ * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
+ * | Extended payload length continued, if payload len == 127 |
+ * + - - - - - - - - - - - - - - - +-------------------------------+
+ * | |Masking-key, if MASK set to 1 |
+ * +-------------------------------+-------------------------------+
+ * | Masking-key (continued) | Payload Data |
+ * +-------------------------------- - - - - - - - - - - - - - - - +
+ * : Payload Data continued ... :
+ * + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ * | Payload Data continued ... |
+ * +---------------------------------------------------------------+
+ */
+var wsFrame = {
+ /** Encodes a WebSocket frame
+ *
+ * @param {Buffer} data - The raw data to encode
+ * @param {Buffer} mask - The mask to apply when sending to server, null for no mask
+ * @param {Boolean} binEncoding - True for binary encoding, false for text encoding
+ * @returns {Buffer} - The WebSocket frame, ready to send
+ */
+ encode: function(data, mask, binEncoding) {
+ var frame = new Buffer(wsFrame.frameSizeFromData(data, mask));
+ //Byte 0 - FIN & OPCODE
+ frame[0] = wsFrame.fin.FIN +
+ (binEncoding ? wsFrame.frameOpCodes.BIN : wsFrame.frameOpCodes.TEXT);
+ //Byte 1 or 1-3 or 1-9 - MASK FLAG & SIZE
+ var payloadOffset = 2;
+ if (data.length < 0x7E) {
+ frame[1] = data.length + (mask ? wsFrame.mask.TO_SERVER : wsFrame.mask.TO_CLIENT);
+ } else if (data.length < 0xFFFF) {
+ frame[1] = 0x7E + (mask ? wsFrame.mask.TO_SERVER : wsFrame.mask.TO_CLIENT);
+ frame.writeUInt16BE(data.length, 2, true);
+ payloadOffset = 4;
+ } else {
+ frame[1] = 0x7F + (mask ? wsFrame.mask.TO_SERVER : wsFrame.mask.TO_CLIENT);
+ frame.writeUInt32BE(0, 2, true);
+ frame.writeUInt32BE(data.length, 6, true);
+ payloadOffset = 10;
+ }
+ //MASK
+ if (mask) {
+ mask.copy(frame, payloadOffset, 0, 4);
+ payloadOffset += 4;
+ }
+ //Payload
+ data.copy(frame, payloadOffset);
+ if (mask) {
+ wsFrame.applyMask(frame.slice(payloadOffset), frame.slice(payloadOffset-4,payloadOffset));
+ }
+ return frame;
+ },
+
+ /**
+ * @class
+ * @name WSDecodeResult
+ * @property {Buffer} data - The decoded data for the first ATRPC message
+ * @property {Buffer} mask - The frame mask
+ * @property {Boolean} binEncoding - True if binary (TBinaryProtocol),
+ * False if text (TJSONProtocol)
+ * @property {Buffer} nextFrame - Multiple ATRPC messages may be sent in a
+ * single WebSocket frame, this Buffer contains
+ * any bytes remaining to be decoded
+ * @property {Boolean} FIN - True is the message is complete
+ */
+
+ /** Decodes a WebSocket frame
+ *
+ * @param {Buffer} frame - The raw inbound frame, if this is a continuation
+ * frame it must have a mask property with the mask.
+ * @returns {WSDecodeResult} - The decoded payload
+ *
+ * @see {@link WSDecodeResult}
+ */
+ decode: function(frame) {
+ var result = {
+ data: null,
+ mask: null,
+ binEncoding: false,
+ nextFrame: null,
+ FIN: true
+ };
+
+ //Byte 0 - FIN & OPCODE
+ if (wsFrame.fin.FIN != (frame[0] & wsFrame.fin.FIN)) {
+ result.FIN = false;
+ }
+ result.binEncoding = (wsFrame.frameOpCodes.BIN == (frame[0] & wsFrame.frameOpCodes.BIN));
+ //Byte 1 or 1-3 or 1-9 - SIZE
+ var lenByte = (frame[1] & 0x0000007F);
+ var len = lenByte;
+ var dataOffset = 2;
+ if (lenByte == 0x7E) {
+ len = frame.readUInt16BE(2);
+ dataOffset = 4;
+ } else if (lenByte == 0x7F) {
+ len = frame.readUInt32BE(6);
+ dataOffset = 10;
+ }
+ //MASK
+ if (wsFrame.mask.TO_SERVER == (frame[1] & wsFrame.mask.TO_SERVER)) {
+ result.mask = new Buffer(4);
+ frame.copy(result.mask, 0, dataOffset, dataOffset + 4);
+ dataOffset += 4;
+ }
+ //Payload
+ result.data = new Buffer(len);
+ frame.copy(result.data, 0, dataOffset, dataOffset+len);
+ if (result.mask) {
+ wsFrame.applyMask(result.data, result.mask);
+ }
+ //Next Frame
+ if (frame.length > dataOffset+len) {
+ result.nextFrame = new Buffer(frame.length - (dataOffset+len));
+ frame.copy(result.nextFrame, 0, dataOffset+len, frame.length);
+ }
+ //Don't forward control frames
+ if (frame[0] & wsFrame.frameOpCodes.FINCTRL) {
+ result.data = null;
+ }
+
+ return result;
+ },
+
+ /** Masks/Unmasks data
+ *
+ * @param {Buffer} data - data to mask/unmask in place
+ * @param {Buffer} mask - the mask
+ */
+ applyMask: function(data, mask){
+ //TODO: look into xoring words at a time
+ var dataLen = data.length;
+ var maskLen = mask.length;
+ for (var i = 0; i < dataLen; i++) {
+ data[i] = data[i] ^ mask[i%maskLen];
+ }
+ },
+
+ /** Computes frame size on the wire from data to be sent
+ *
+ * @param {Buffer} data - data.length is the assumed payload size
+ * @param {Boolean} mask - true if a mask will be sent (TO_SERVER)
+ */
+ frameSizeFromData: function(data, mask) {
+ var headerSize = 10;
+ if (data.length < 0x7E) {
+ headerSize = 2;
+ } else if (data.length < 0xFFFF) {
+ headerSize = 4;
+ }
+ return headerSize + data.length + (mask ? 4 : 0);
+ },
+
+ frameOpCodes: {
+ CONT: 0x00,
+ TEXT: 0x01,
+ BIN: 0x02,
+ CTRL: 0x80
+ },
+
+ mask: {
+ TO_SERVER: 0x80,
+ TO_CLIENT: 0x00
+ },
+
+ fin: {
+ CONT: 0x00,
+ FIN: 0x80
+ }
+};
+
+
+// createWebServer constructor and options
+/////////////////////////////////////////////////////////////////////
+
+/**
+ * @class
+ * @name ServerOptions
+ * @property {array} cors - Array of CORS origin strings to permit requests from.
+ * @property {string} files - Path to serve static files from, if absent or ""
+ * static file service is disabled.
+ * @property {object} headers - An object hash mapping header strings to header value
+ * strings, these headers are transmitted in response to
+ * static file GET operations.
+ * @property {object} services - An object hash mapping service URI strings
+ * to ServiceOptions objects
+ * @property {object} tls - Node.js TLS options (see: nodejs.org/api/tls.html),
+ * if not present or null regular http is used,
+ * at least a key and a cert must be defined to use SSL/TLS
+ * @see {@link ServiceOptions}
+ */
+
+/**
+ * @class
+ * @name ServiceOptions
+ * @property {object} transport - The layered transport to use (defaults
+ * to TBufferedTransport).
+ * @property {object} protocol - The serialization Protocol to use (defaults to
+ * TBinaryProtocol).
+ * @property {object} processor - The Thrift Service class/processor generated
+ * by the IDL Compiler for the service (the "cls"
+ * key can also be used for this attribute).
+ * @property {object} handler - The handler methods for the Thrift Service.
+ */
+
+/**
+ * Create a Thrift server which can serve static files and/or one or
+ * more Thrift Services.
+ * @param {ServerOptions} options - The server configuration.
+ * @returns {object} - The Apache Thrift Web Server.
+ */
+exports.createWebServer = function(options) {
+ var baseDir = options.files;
+ var contentTypesByExtension = {
+ '.txt': 'text/plain',
+ '.html': 'text/html',
+ '.css': 'text/css',
+ '.xml': 'application/xml',
+ '.json': 'application/json',
+ '.js': 'application/javascript',
+ '.jpg': 'image/jpeg',
+ '.jpeg': 'image/jpeg',
+ '.gif': 'image/gif',
+ '.png': 'image/png',
+ '.svg': 'image/svg+xml'
+ };
+
+ //Setup all of the services
+ var services = options.services;
+ for (var uri in services) {
+ var svcObj = services[uri];
+
+ //Setup the processor
+ if (svcObj.processor instanceof MultiplexedProcessor) {
+ //Multiplex processors have pre embedded processor/handler pairs, save as is
+ svcObj.processor = svcObj.processor;
+ } else {
+ //For historical reasons Node.js supports processors passed in directly or via the
+ // IDL Compiler generated class housing the processor. Also, the options property
+ // for a Processor has been called both cls and processor at different times. We
+ // support any of the four possibilities here.
+ var processor = (svcObj.processor) ? (svcObj.processor.Processor || svcObj.processor) :
+ (svcObj.cls.Processor || svcObj.cls);
+ //Processors can be supplied as constructed objects with handlers already embedded,
+ // if a handler is provided we construct a new processor, if not we use the processor
+ // object directly
+ if (svcObj.handler) {
+ svcObj.processor = new processor(svcObj.handler);
+ } else {
+ svcObj.processor = processor;
+ }
+ }
+ svcObj.transport = svcObj.transport ? svcObj.transport : TBufferedTransport;
+ svcObj.protocol = svcObj.protocol ? svcObj.protocol : TBinaryProtocol;
+ }
+
+ //Verify CORS requirements
+ function VerifyCORSAndSetHeaders(request, response) {
+ if (request.headers.origin && options.cors) {
+ if (options.cors["*"] || options.cors[request.headers.origin]) {
+ //Allow, origin allowed
+ response.setHeader("access-control-allow-origin", request.headers.origin);
+ response.setHeader("access-control-allow-methods", "GET, POST, OPTIONS");
+ response.setHeader("access-control-allow-headers", "content-type, accept");
+ response.setHeader("access-control-max-age", "60");
+ return true;
+ } else {
+ //Disallow, origin denied
+ return false;
+ }
+ }
+ //Allow, CORS is not in use
+ return true;
+ }
+
+
+ //Handle OPTIONS method (CORS)
+ ///////////////////////////////////////////////////
+ function processOptions(request, response) {
+ if (VerifyCORSAndSetHeaders(request, response)) {
+ response.writeHead("204", "No Content", {"content-length": 0});
+ } else {
+ response.writeHead("403", "Origin " + request.headers.origin + " not allowed", {});
+ }
+ response.end();
+ }
+
+
+ //Handle POST methods (TXHRTransport)
+ ///////////////////////////////////////////////////
+ function processPost(request, response) {
+ //Lookup service
+ var uri = url.parse(request.url).pathname;
+ var svc = services[uri];
+ if (!svc) {
+ response.writeHead("403", "No Apache Thrift Service at " + uri, {});
+ response.end();
+ return;
+ }
+
+ //Verify CORS requirements
+ if (!VerifyCORSAndSetHeaders(request, response)) {
+ response.writeHead("403", "Origin " + request.headers.origin + " not allowed", {});
+ response.end();
+ return;
+ }
+
+ //Process XHR payload
+ request.on('data', svc.transport.receiver(function(transportWithData) {
+ var input = new svc.protocol(transportWithData);
+ var output = new svc.protocol(new svc.transport(undefined, function(buf) {
+ try {
+ response.writeHead(200);
+ response.end(buf);
+ } catch (err) {
+ response.writeHead(500);
+ response.end();
+ }
+ }));
+
+ try {
+ svc.processor.process(input, output);
+ transportWithData.commitPosition();
+ } catch (err) {
+ if (err instanceof InputBufferUnderrunError) {
+ transportWithData.rollbackPosition();
+ } else {
+ response.writeHead(500);
+ response.end();
+ }
+ }
+ }));
+ }
+
+
+ //Handle GET methods (Static Page Server)
+ ///////////////////////////////////////////////////
+ function processGet(request, response) {
+ //Undefined or empty base directory means do not serve static files
+ if (!baseDir || "" === baseDir) {
+ response.writeHead(404);
+ response.end();
+ return;
+ }
+
+ //Verify CORS requirements
+ if (!VerifyCORSAndSetHeaders(request, response)) {
+ response.writeHead("403", "Origin " + request.headers.origin + " not allowed", {});
+ response.end();
+ return;
+ }
+
+ //Locate the file requested and send it
+ var uri = url.parse(request.url).pathname;
+ var filename = path.resolve(path.join(baseDir, uri));
+
+ //Ensure the basedir path is not able to be escaped
+ if (filename.indexOf(baseDir) != 0) {
+ response.writeHead(400, "Invalid request path", {});
+ response.end();
+ return;
+ }
+
+ fs.exists(filename, function(exists) {
+ if(!exists) {
+ response.writeHead(404);
+ response.end();
+ return;
+ }
+
+ if (fs.statSync(filename).isDirectory()) {
+ filename += '/index.html';
+ }
+
+ fs.readFile(filename, "binary", function(err, file) {
+ if (err) {
+ response.writeHead(500);
+ response.end(err + "\n");
+ return;
+ }
+ var headers = {};
+ var contentType = contentTypesByExtension[path.extname(filename)];
+ if (contentType) {
+ headers["Content-Type"] = contentType;
+ }
+ for (var k in options.headers) {
+ headers[k] = options.headers[k];
+ }
+ response.writeHead(200, headers);
+ response.write(file, "binary");
+ response.end();
+ });
+ });
+ }
+
+
+ //Handle WebSocket calls (TWebSocketTransport)
+ ///////////////////////////////////////////////////
+ function processWS(data, socket, svc, binEncoding) {
+ svc.transport.receiver(function(transportWithData) {
+ var input = new svc.protocol(transportWithData);
+ var output = new svc.protocol(new svc.transport(undefined, function(buf) {
+ try {
+ var frame = wsFrame.encode(buf, null, binEncoding);
+ socket.write(frame);
+ } catch (err) {
+ //TODO: Add better error processing
+ }
+ }));
+
+ try {
+ svc.processor.process(input, output);
+ transportWithData.commitPosition();
+ }
+ catch (err) {
+ if (err instanceof InputBufferUnderrunError) {
+ transportWithData.rollbackPosition();
+ }
+ else {
+ //TODO: Add better error processing
+ }
+ }
+ })(data);
+ }
+
+ //Create the server (HTTP or HTTPS)
+ var server = null;
+ if (options.tls) {
+ server = https.createServer(options.tls);
+ } else {
+ server = http.createServer();
+ }
+
+ //Wire up listeners for upgrade(to WebSocket) & request methods for:
+ // - GET static files,
+ // - POST XHR Thrift services
+ // - OPTIONS CORS requests
+ server.on('request', function(request, response) {
+ if (request.method === 'POST') {
+ processPost(request, response);
+ } else if (request.method === 'GET') {
+ processGet(request, response);
+ } else if (request.method === 'OPTIONS') {
+ processOptions(request, response);
+ } else {
+ response.writeHead(500);
+ response.end();
+ }
+ }).on('upgrade', function(request, socket, head) {
+ //Lookup service
+ var svc;
+ try {
+ svc = services[Object.keys(services)[0]];
+ } catch(e) {
+ socket.write("HTTP/1.1 403 No Apache Thrift Service available\r\n\r\n");
+ return;
+ }
+ //Perform upgrade
+ var hash = crypto.createHash("sha1");
+ hash.update(request.headers['sec-websocket-key'] + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+ socket.write("HTTP/1.1 101 Switching Protocols\r\n" +
+ "Upgrade: websocket\r\n" +
+ "Connection: Upgrade\r\n" +
+ "Sec-WebSocket-Accept: " + hash.digest("base64") + "\r\n" +
+ "Sec-WebSocket-Origin: " + request.headers.origin + "\r\n" +
+ "Sec-WebSocket-Location: ws://" + request.headers.host + request.url + "\r\n" +
+ "\r\n");
+ //Handle WebSocket traffic
+ var data = null;
+ socket.on('data', function(frame) {
+ try {
+ while (frame) {
+ var result = wsFrame.decode(frame);
+ //Prepend any existing decoded data
+ if (data) {
+ if (result.data) {
+ var newData = new Buffer(data.length + result.data.length);
+ data.copy(newData);
+ result.data.copy(newData, data.length);
+ result.data = newData;
+ } else {
+ result.data = data;
+ }
+ data = null;
+ }
+ //If this completes a message process it
+ if (result.FIN) {
+ processWS(result.data, socket, svc, result.binEncoding);
+ } else {
+ data = result.data;
+ }
+ //Prepare next frame for decoding (if any)
+ frame = result.nextFrame;
+ }
+ } catch(e) {
+ log.error('TWebSocketTransport Exception: ' + e);
+ socket.destroy();
+ }
+ });
+ });
+
+ //Return the server
+ return server;
+};
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/ws_connection.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/ws_connection.js
new file mode 100644
index 000000000..052cbd4e9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/ws_connection.js
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var util = require('util');
+var WebSocket = require('ws');
+var EventEmitter = require("events").EventEmitter;
+var thrift = require('./thrift');
+var ttransport = require('./transport');
+var tprotocol = require('./protocol');
+
+var TBufferedTransport = require('./buffered_transport');
+var TJSONProtocol = require('./json_protocol');
+var InputBufferUnderrunError = require('./input_buffer_underrun_error');
+
+var createClient = require('./create_client');
+
+exports.WSConnection = WSConnection;
+
+/**
+ * @class
+ * @name WSConnectOptions
+ * @property {string} transport - The Thrift layered transport to use (TBufferedTransport, etc).
+ * @property {string} protocol - The Thrift serialization protocol to use (TJSONProtocol, etc.).
+ * @property {string} path - The URL path to connect to (e.g. "/", "/mySvc", "/thrift/quoteSvc", etc.).
+ * @property {object} headers - A standard Node.js header hash, an object hash containing key/value
+ * pairs where the key is the header name string and the value is the header value string.
+ * @property {boolean} secure - True causes the connection to use wss, otherwise ws is used.
+ * @property {object} wsOptions - Options passed on to WebSocket.
+ * @example
+ * //Use a secured websocket connection
+ * // uses the buffered transport layer, uses the JSON protocol and directs RPC traffic
+ * // to wss://thrift.example.com:9090/hello
+ * var thrift = require('thrift');
+ * var options = {
+ * transport: thrift.TBufferedTransport,
+ * protocol: thrift.TJSONProtocol,
+ * path: "/hello",
+ * secure: true
+ * };
+ * var con = thrift.createWSConnection("thrift.example.com", 9090, options);
+ * con.open()
+ * var client = thrift.createWSClient(myService, connection);
+ * client.myServiceFunction();
+ * con.close()
+ */
+
+/**
+ * Initializes a Thrift WSConnection instance (use createWSConnection() rather than
+ * instantiating directly).
+ * @constructor
+ * @param {string} host - The host name or IP to connect to.
+ * @param {number} port - The TCP port to connect to.
+ * @param {WSConnectOptions} options - The configuration options to use.
+ * @throws {error} Exceptions other than ttransport.InputBufferUnderrunError are rethrown
+ * @event {error} The "error" event is fired when a Node.js error event occurs during
+ * request or response processing, in which case the node error is passed on. An "error"
+ * event may also be fired when the connectison can not map a response back to the
+ * appropriate client (an internal error), generating a TApplicationException.
+ * @classdesc WSConnection objects provide Thrift end point transport
+ * semantics implemented using Websockets.
+ * @see {@link createWSConnection}
+ */
+function WSConnection(host, port, options) {
+ //Initialize the emitter base object
+ EventEmitter.call(this);
+
+ //Set configuration
+ var self = this;
+ this.options = options || {};
+ this.host = host;
+ this.port = port;
+ this.secure = this.options.secure || false;
+ this.transport = this.options.transport || TBufferedTransport;
+ this.protocol = this.options.protocol || TJSONProtocol;
+ this.path = this.options.path;
+ this.send_pending = [];
+
+ //The sequence map is used to map seqIDs back to the
+ // calling client in multiplexed scenarios
+ this.seqId2Service = {};
+
+ //Prepare WebSocket options
+ this.wsOptions = {
+ host: this.host,
+ port: this.port || 80,
+ path: this.options.path || '/',
+ headers: this.options.headers || {}
+ };
+ for (var attrname in this.options.wsOptions) {
+ this.wsOptions[attrname] = this.options.wsOptions[attrname];
+ }
+};
+util.inherits(WSConnection, EventEmitter);
+
+WSConnection.prototype.__reset = function() {
+ this.socket = null; //The web socket
+ this.send_pending = []; //Buffers/Callback pairs waiting to be sent
+};
+
+WSConnection.prototype.__onOpen = function() {
+ var self = this;
+ this.emit("open");
+ if (this.send_pending.length > 0) {
+ //If the user made calls before the connection was fully
+ //open, send them now
+ this.send_pending.forEach(function(data) {
+ self.socket.send(data);
+ });
+ this.send_pending = [];
+ }
+};
+
+WSConnection.prototype.__onClose = function(evt) {
+ this.emit("close");
+ this.__reset();
+};
+
+WSConnection.prototype.__decodeCallback = function(transport_with_data) {
+ var proto = new this.protocol(transport_with_data);
+ try {
+ while (true) {
+ var header = proto.readMessageBegin();
+ var dummy_seqid = header.rseqid * -1;
+ var client = this.client;
+ //The Multiplexed Protocol stores a hash of seqid to service names
+ // in seqId2Service. If the SeqId is found in the hash we need to
+ // lookup the appropriate client for this call.
+ // The client var is a single client object when not multiplexing,
+ // when using multiplexing it is a service name keyed hash of client
+ // objects.
+ //NOTE: The 2 way interdependencies between protocols, transports,
+ // connections and clients in the Node.js implementation are irregular
+ // and make the implementation difficult to extend and maintain. We
+ // should bring this stuff inline with typical thrift I/O stack
+ // operation soon.
+ // --ra
+ var service_name = this.seqId2Service[header.rseqid];
+ if (service_name) {
+ client = this.client[service_name];
+ delete this.seqId2Service[header.rseqid];
+ }
+ /*jshint -W083 */
+ client._reqs[dummy_seqid] = function(err, success) {
+ transport_with_data.commitPosition();
+ var clientCallback = client._reqs[header.rseqid];
+ delete client._reqs[header.rseqid];
+ if (clientCallback) {
+ clientCallback(err, success);
+ }
+ };
+ /*jshint +W083 */
+ if (client['recv_' + header.fname]) {
+ client['recv_' + header.fname](proto, header.mtype, dummy_seqid);
+ } else {
+ delete client._reqs[dummy_seqid];
+ this.emit("error",
+ new thrift.TApplicationException(
+ thrift.TApplicationExceptionType.WRONG_METHOD_NAME,
+ "Received a response to an unknown RPC function"));
+ }
+ }
+ } catch (e) {
+ if (e instanceof InputBufferUnderrunError) {
+ transport_with_data.rollbackPosition();
+ } else {
+ throw e;
+ }
+ }
+};
+
+WSConnection.prototype.__onData = function(data) {
+ if (Object.prototype.toString.call(data) == "[object ArrayBuffer]") {
+ data = new Uint8Array(data);
+ }
+ var buf = new Buffer(data);
+ this.transport.receiver(this.__decodeCallback.bind(this))(buf);
+
+};
+
+WSConnection.prototype.__onMessage = function(evt) {
+ this.__onData(evt.data);
+};
+
+WSConnection.prototype.__onError = function(evt) {
+ this.emit("error", evt);
+ this.socket.close();
+};
+
+/**
+ * Returns true if the transport is open
+ * @readonly
+ * @returns {boolean}
+ */
+WSConnection.prototype.isOpen = function() {
+ return this.socket && this.socket.readyState == this.socket.OPEN;
+};
+
+/**
+ * Opens the transport connection
+ */
+WSConnection.prototype.open = function() {
+ //If OPEN/CONNECTING/CLOSING ignore additional opens
+ if (this.socket && this.socket.readyState != this.socket.CLOSED) {
+ return;
+ }
+ //If there is no socket or the socket is closed:
+ this.socket = new WebSocket(this.uri(), "", this.wsOptions);
+ this.socket.binaryType = 'arraybuffer';
+ this.socket.onopen = this.__onOpen.bind(this);
+ this.socket.onmessage = this.__onMessage.bind(this);
+ this.socket.onerror = this.__onError.bind(this);
+ this.socket.onclose = this.__onClose.bind(this);
+};
+
+/**
+ * Closes the transport connection
+ */
+WSConnection.prototype.close = function() {
+ this.socket.close();
+};
+
+/**
+ * Return URI for the connection
+ * @returns {string} URI
+ */
+WSConnection.prototype.uri = function() {
+ var schema = this.secure ? 'wss' : 'ws';
+ var port = '';
+ var path = this.path || '/';
+ var host = this.host;
+
+ // avoid port if default for schema
+ if (this.port && (('wss' == schema && this.port != 443) ||
+ ('ws' == schema && this.port != 80))) {
+ port = ':' + this.port;
+ }
+
+ return schema + '://' + host + port + path;
+};
+
+/**
+ * Writes Thrift message data to the connection
+ * @param {Buffer} data - A Node.js Buffer containing the data to write
+ * @returns {void} No return value.
+ * @event {error} the "error" event is raised upon request failure passing the
+ * Node.js error object to the listener.
+ */
+WSConnection.prototype.write = function(data) {
+ if (this.isOpen()) {
+ //Send data and register a callback to invoke the client callback
+ this.socket.send(data);
+ } else {
+ //Queue the send to go out __onOpen
+ this.send_pending.push(data);
+ }
+};
+
+/**
+ * Creates a new WSConnection object, used by Thrift clients to connect
+ * to Thrift HTTP based servers.
+ * @param {string} host - The host name or IP to connect to.
+ * @param {number} port - The TCP port to connect to.
+ * @param {WSConnectOptions} options - The configuration options to use.
+ * @returns {WSConnection} The connection object.
+ * @see {@link WSConnectOptions}
+ */
+exports.createWSConnection = function(host, port, options) {
+ return new WSConnection(host, port, options);
+};
+
+exports.createWSClient = createClient;
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/ws_transport.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/ws_transport.js
new file mode 100644
index 000000000..3513b84be
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/ws_transport.js
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var log = require('./log');
+
+module.exports = TWebSocketTransport;
+
+/**
+ * Constructor Function for the WebSocket transport.
+ * @constructor
+ * @param {string} [url] - The URL to connect to.
+ * @classdesc The Apache Thrift Transport layer performs byte level I/O
+ * between RPC clients and servers. The JavaScript TWebSocketTransport object
+ * uses the WebSocket protocol. Target servers must implement WebSocket.
+ * (see: node.js example server_http.js).
+ * @example
+ * var transport = new Thrift.TWebSocketTransport("http://localhost:8585");
+ */
+function TWebSocketTransport(url) {
+ this.__reset(url);
+};
+
+
+TWebSocketTransport.prototype.__reset = function(url) {
+ this.url = url; //Where to connect
+ this.socket = null; //The web socket
+ this.callbacks = []; //Pending callbacks
+ this.send_pending = []; //Buffers/Callback pairs waiting to be sent
+ this.send_buf = ''; //Outbound data, immutable until sent
+ this.recv_buf = ''; //Inbound data
+ this.rb_wpos = 0; //Network write position in receive buffer
+ this.rb_rpos = 0; //Client read position in receive buffer
+};
+
+/**
+ * Sends the current WS request and registers callback. The async
+ * parameter is ignored (WS flush is always async) and the callback
+ * function parameter is required.
+ * @param {object} async - Ignored.
+ * @param {object} callback - The client completion callback.
+ * @returns {undefined|string} Nothing (undefined)
+ */
+TWebSocketTransport.prototype.flush = function(async, callback) {
+ var self = this;
+ if (this.isOpen()) {
+ //Send data and register a callback to invoke the client callback
+ this.socket.send(this.send_buf);
+ this.callbacks.push((function() {
+ var clientCallback = callback;
+ return function(msg) {
+ self.setRecvBuffer(msg);
+ clientCallback();
+ };
+ }()));
+ } else {
+ //Queue the send to go out __onOpen
+ this.send_pending.push({
+ buf: this.send_buf,
+ cb: callback
+ });
+ }
+};
+
+TWebSocketTransport.prototype.__onOpen = function() {
+ var self = this;
+ if (this.send_pending.length > 0) {
+ //If the user made calls before the connection was fully
+ //open, send them now
+ this.send_pending.forEach(function(elem) {
+ this.socket.send(elem.buf);
+ this.callbacks.push((function() {
+ var clientCallback = elem.cb;
+ return function(msg) {
+ self.setRecvBuffer(msg);
+ clientCallback();
+ };
+ }()));
+ });
+ this.send_pending = [];
+ }
+};
+
+TWebSocketTransport.prototype.__onClose = function(evt) {
+ this.__reset(this.url);
+};
+
+TWebSocketTransport.prototype.__onMessage = function(evt) {
+ if (this.callbacks.length) {
+ this.callbacks.shift()(evt.data);
+ }
+};
+
+TWebSocketTransport.prototype.__onError = function(evt) {
+ log.error('websocket: ' + evt.toString());
+ this.socket.close();
+};
+
+/**
+ * Sets the buffer to use when receiving server responses.
+ * @param {string} buf - The buffer to receive server responses.
+ */
+TWebSocketTransport.prototype.setRecvBuffer = function(buf) {
+ this.recv_buf = buf;
+ this.recv_buf_sz = this.recv_buf.length;
+ this.wpos = this.recv_buf.length;
+ this.rpos = 0;
+};
+
+/**
+ * Returns true if the transport is open
+ * @readonly
+ * @returns {boolean}
+ */
+TWebSocketTransport.prototype.isOpen = function() {
+ return this.socket && this.socket.readyState == this.socket.OPEN;
+};
+
+/**
+ * Opens the transport connection
+ */
+TWebSocketTransport.prototype.open = function() {
+ //If OPEN/CONNECTING/CLOSING ignore additional opens
+ if (this.socket && this.socket.readyState != this.socket.CLOSED) {
+ return;
+ }
+ //If there is no socket or the socket is closed:
+ this.socket = new WebSocket(this.url);
+ this.socket.onopen = this.__onOpen.bind(this);
+ this.socket.onmessage = this.__onMessage.bind(this);
+ this.socket.onerror = this.__onError.bind(this);
+ this.socket.onclose = this.__onClose.bind(this);
+};
+
+/**
+ * Closes the transport connection
+ */
+TWebSocketTransport.prototype.close = function() {
+ this.socket.close();
+};
+
+/**
+ * Returns the specified number of characters from the response
+ * buffer.
+ * @param {number} len - The number of characters to return.
+ * @returns {string} Characters sent by the server.
+ */
+TWebSocketTransport.prototype.read = function(len) {
+ var avail = this.wpos - this.rpos;
+
+ if (avail === 0) {
+ return '';
+ }
+
+ var give = len;
+
+ if (avail < len) {
+ give = avail;
+ }
+
+ var ret = this.read_buf.substr(this.rpos, give);
+ this.rpos += give;
+
+ //clear buf when complete?
+ return ret;
+};
+
+/**
+ * Returns the entire response buffer.
+ * @returns {string} Characters sent by the server.
+ */
+TWebSocketTransport.prototype.readAll = function() {
+ return this.recv_buf;
+};
+
+/**
+ * Sets the send buffer to buf.
+ * @param {string} buf - The buffer to send.
+ */
+TWebSocketTransport.prototype.write = function(buf) {
+ this.send_buf = buf;
+};
+
+/**
+ * Returns the send buffer.
+ * @readonly
+ * @returns {string} The send buffer.
+ */
+TWebSocketTransport.prototype.getSendBuffer = function() {
+ return this.send_buf;
+};
diff --git a/src/jaegertracing/thrift/lib/nodejs/lib/thrift/xhr_connection.js b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/xhr_connection.js
new file mode 100644
index 000000000..6459c900c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/lib/thrift/xhr_connection.js
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var util = require('util');
+var EventEmitter = require("events").EventEmitter;
+var thrift = require('./thrift');
+
+var TBufferedTransport = require('./buffered_transport');
+var TJSONProtocol = require('./json_protocol');
+var InputBufferUnderrunError = require('./input_buffer_underrun_error');
+
+var createClient = require('./create_client');
+
+exports.XHRConnection = XHRConnection;
+
+/**
+ * Constructor Function for the XHR Connection.
+ * If you do not specify a host and port then XHRConnection will default to the
+ * host and port of the page from which this javascript is served.
+ * @constructor
+ * @param {string} [url] - The URL to connect to.
+ * @classdesc TXHRConnection objects provide Thrift end point transport
+ * semantics implemented using XHR.
+ * @example
+ * var transport = new Thrift.TXHRConnection('localhost', 9099, {});
+ */
+function XHRConnection(host, port, options) {
+ this.options = options || {};
+ this.wpos = 0;
+ this.rpos = 0;
+ this.useCORS = (options && options.useCORS);
+ this.send_buf = '';
+ this.recv_buf = '';
+ this.transport = options.transport || TBufferedTransport;
+ this.protocol = options.protocol || TJSONProtocol;
+ this.headers = options.headers || {};
+
+ host = host || window.location.host;
+ port = port || window.location.port;
+ var prefix = options.https ? 'https://' : 'http://';
+ var path = options.path || '/';
+
+ if (port === '') {
+ port = undefined;
+ }
+
+ if (!port || port === 80 || port === '80') {
+ this.url = prefix + host + path;
+ } else {
+ this.url = prefix + host + ':' + port + path;
+ }
+
+ //The sequence map is used to map seqIDs back to the
+ // calling client in multiplexed scenarios
+ this.seqId2Service = {};
+};
+
+util.inherits(XHRConnection, EventEmitter);
+
+/**
+* Gets the browser specific XmlHttpRequest Object.
+* @returns {object} the browser XHR interface object
+*/
+XHRConnection.prototype.getXmlHttpRequestObject = function() {
+ try { return new XMLHttpRequest(); } catch (e1) { }
+ try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch (e2) { }
+ try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch (e3) { }
+
+ throw "Your browser doesn't support XHR.";
+};
+
+/**
+ * Sends the current XRH request if the transport was created with a URL
+ * and the async parameter is false. If the transport was not created with
+ * a URL, or the async parameter is True and no callback is provided, or
+ * the URL is an empty string, the current send buffer is returned.
+ * @param {object} async - If true the current send buffer is returned.
+ * @param {object} callback - Optional async completion callback
+ * @returns {undefined|string} Nothing or the current send buffer.
+ * @throws {string} If XHR fails.
+ */
+XHRConnection.prototype.flush = function() {
+ var self = this;
+ if (this.url === undefined || this.url === '') {
+ return this.send_buf;
+ }
+
+ var xreq = this.getXmlHttpRequestObject();
+
+ if (xreq.overrideMimeType) {
+ xreq.overrideMimeType('application/json');
+ }
+
+ xreq.onreadystatechange = function() {
+ if (this.readyState == 4 && this.status == 200) {
+ self.setRecvBuffer(this.responseText);
+ }
+ };
+
+ xreq.open('POST', this.url, true);
+
+ Object.keys(this.headers).forEach(function(headerKey) {
+ xreq.setRequestHeader(headerKey, self.headers[headerKey]);
+ });
+
+ xreq.send(this.send_buf);
+};
+
+/**
+ * Sets the buffer to provide the protocol when deserializing.
+ * @param {string} buf - The buffer to supply the protocol.
+ */
+XHRConnection.prototype.setRecvBuffer = function(buf) {
+ this.recv_buf = buf;
+ this.recv_buf_sz = this.recv_buf.length;
+ this.wpos = this.recv_buf.length;
+ this.rpos = 0;
+
+ if (Object.prototype.toString.call(buf) == "[object ArrayBuffer]") {
+ var data = new Uint8Array(buf);
+ }
+ var thing = new Buffer(data || buf);
+
+ this.transport.receiver(this.__decodeCallback.bind(this))(thing);
+
+};
+
+XHRConnection.prototype.__decodeCallback = function(transport_with_data) {
+ var proto = new this.protocol(transport_with_data);
+ try {
+ while (true) {
+ var header = proto.readMessageBegin();
+ var dummy_seqid = header.rseqid * -1;
+ var client = this.client;
+ //The Multiplexed Protocol stores a hash of seqid to service names
+ // in seqId2Service. If the SeqId is found in the hash we need to
+ // lookup the appropriate client for this call.
+ // The client var is a single client object when not multiplexing,
+ // when using multiplexing it is a service name keyed hash of client
+ // objects.
+ //NOTE: The 2 way interdependencies between protocols, transports,
+ // connections and clients in the Node.js implementation are irregular
+ // and make the implementation difficult to extend and maintain. We
+ // should bring this stuff inline with typical thrift I/O stack
+ // operation soon.
+ // --ra
+ var service_name = this.seqId2Service[header.rseqid];
+ if (service_name) {
+ client = this.client[service_name];
+ delete this.seqId2Service[header.rseqid];
+ }
+ /*jshint -W083 */
+ client._reqs[dummy_seqid] = function(err, success) {
+ transport_with_data.commitPosition();
+ var clientCallback = client._reqs[header.rseqid];
+ delete client._reqs[header.rseqid];
+ if (clientCallback) {
+ clientCallback(err, success);
+ }
+ };
+ /*jshint +W083 */
+ if (client['recv_' + header.fname]) {
+ client['recv_' + header.fname](proto, header.mtype, dummy_seqid);
+ } else {
+ delete client._reqs[dummy_seqid];
+ this.emit("error",
+ new thrift.TApplicationException(
+ thrift.TApplicationExceptionType.WRONG_METHOD_NAME,
+ "Received a response to an unknown RPC function"));
+ }
+ }
+ } catch (e) {
+ if (e instanceof InputBufferUnderrunError) {
+ transport_with_data.rollbackPosition();
+ } else {
+ throw e;
+ }
+ }
+};
+
+/**
+ * Returns true if the transport is open, XHR always returns true.
+ * @readonly
+ * @returns {boolean} Always True.
+ */
+XHRConnection.prototype.isOpen = function() {
+ return true;
+};
+
+/**
+ * Opens the transport connection, with XHR this is a nop.
+ */
+XHRConnection.prototype.open = function() {};
+
+/**
+ * Closes the transport connection, with XHR this is a nop.
+ */
+XHRConnection.prototype.close = function() {};
+
+/**
+ * Returns the specified number of characters from the response
+ * buffer.
+ * @param {number} len - The number of characters to return.
+ * @returns {string} Characters sent by the server.
+ */
+XHRConnection.prototype.read = function(len) {
+ var avail = this.wpos - this.rpos;
+
+ if (avail === 0) {
+ return '';
+ }
+
+ var give = len;
+
+ if (avail < len) {
+ give = avail;
+ }
+
+ var ret = this.read_buf.substr(this.rpos, give);
+ this.rpos += give;
+
+ //clear buf when complete?
+ return ret;
+};
+
+/**
+ * Returns the entire response buffer.
+ * @returns {string} Characters sent by the server.
+ */
+XHRConnection.prototype.readAll = function() {
+ return this.recv_buf;
+};
+
+/**
+ * Sets the send buffer to buf.
+ * @param {string} buf - The buffer to send.
+ */
+XHRConnection.prototype.write = function(buf) {
+ this.send_buf = buf;
+ this.flush();
+};
+
+/**
+ * Returns the send buffer.
+ * @readonly
+ * @returns {string} The send buffer.
+ */
+XHRConnection.prototype.getSendBuffer = function() {
+ return this.send_buf;
+};
+
+/**
+ * Creates a new TXHRTransport object, used by Thrift clients to connect
+ * to Thrift HTTP based servers.
+ * @param {string} host - The host name or IP to connect to.
+ * @param {number} port - The TCP port to connect to.
+ * @param {XHRConnectOptions} options - The configuration options to use.
+ * @returns {XHRConnection} The connection object.
+ * @see {@link XHRConnectOptions}
+ */
+exports.createXHRConnection = function(host, port, options) {
+ return new XHRConnection(host, port, options);
+};
+
+exports.createXHRClient = createClient;
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/binary.test.js b/src/jaegertracing/thrift/lib/nodejs/test/binary.test.js
new file mode 100644
index 000000000..187cd1874
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/binary.test.js
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const test = require("tape");
+const binary = require("thrift/binary");
+
+const cases = {
+ "Should read signed byte": function(assert) {
+ assert.equal(1, binary.readByte(0x01));
+ assert.equal(-1, binary.readByte(0xff));
+
+ assert.equal(127, binary.readByte(0x7f));
+ assert.equal(-128, binary.readByte(0x80));
+ assert.end();
+ },
+ "Should write byte": function(assert) {
+ //Protocol simply writes to the buffer. Nothing to test.. yet.
+ assert.ok(true);
+ assert.end();
+ },
+ "Should read I16": function(assert) {
+ assert.equal(0, binary.readI16([0x00, 0x00]));
+ assert.equal(1, binary.readI16([0x00, 0x01]));
+ assert.equal(-1, binary.readI16([0xff, 0xff]));
+
+ // Min I16
+ assert.equal(-32768, binary.readI16([0x80, 0x00]));
+ // Max I16
+ assert.equal(32767, binary.readI16([0x7f, 0xff]));
+ assert.end();
+ },
+
+ "Should write I16": function(assert) {
+ assert.deepEqual([0x00, 0x00], binary.writeI16([], 0));
+ assert.deepEqual([0x00, 0x01], binary.writeI16([], 1));
+ assert.deepEqual([0xff, 0xff], binary.writeI16([], -1));
+
+ // Min I16
+ assert.deepEqual([0x80, 0x00], binary.writeI16([], -32768));
+ // Max I16
+ assert.deepEqual([0x7f, 0xff], binary.writeI16([], 32767));
+ assert.end();
+ },
+
+ "Should read I32": function(assert) {
+ assert.equal(0, binary.readI32([0x00, 0x00, 0x00, 0x00]));
+ assert.equal(1, binary.readI32([0x00, 0x00, 0x00, 0x01]));
+ assert.equal(-1, binary.readI32([0xff, 0xff, 0xff, 0xff]));
+
+ // Min I32
+ assert.equal(-2147483648, binary.readI32([0x80, 0x00, 0x00, 0x00]));
+ // Max I32
+ assert.equal(2147483647, binary.readI32([0x7f, 0xff, 0xff, 0xff]));
+ assert.end();
+ },
+
+ "Should write I32": function(assert) {
+ assert.deepEqual([0x00, 0x00, 0x00, 0x00], binary.writeI32([], 0));
+ assert.deepEqual([0x00, 0x00, 0x00, 0x01], binary.writeI32([], 1));
+ assert.deepEqual([0xff, 0xff, 0xff, 0xff], binary.writeI32([], -1));
+
+ // Min I32
+ assert.deepEqual(
+ [0x80, 0x00, 0x00, 0x00],
+ binary.writeI32([], -2147483648)
+ );
+ // Max I32
+ assert.deepEqual([0x7f, 0xff, 0xff, 0xff], binary.writeI32([], 2147483647));
+ assert.end();
+ },
+
+ "Should read doubles": function(assert) {
+ assert.equal(
+ 0,
+ binary.readDouble([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
+ );
+ assert.equal(
+ 0,
+ binary.readDouble([0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
+ );
+ assert.equal(
+ 1,
+ binary.readDouble([0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
+ );
+ assert.equal(
+ 2,
+ binary.readDouble([0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
+ );
+ assert.equal(
+ -2,
+ binary.readDouble([0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
+ );
+
+ assert.equal(
+ Math.PI,
+ binary.readDouble([0x40, 0x9, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18])
+ );
+
+ assert.equal(
+ Infinity,
+ binary.readDouble([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
+ );
+ assert.equal(
+ -Infinity,
+ binary.readDouble([0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
+ );
+
+ assert.ok(
+ isNaN(binary.readDouble([0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
+ );
+
+ assert.equal(
+ 1 / 3,
+ binary.readDouble([0x3f, 0xd5, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55])
+ );
+
+ // Min subnormal positive double
+ assert.equal(
+ 4.9406564584124654e-324,
+ binary.readDouble([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])
+ );
+ // Min normal positive double
+ assert.equal(
+ 2.2250738585072014e-308,
+ binary.readDouble([0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
+ );
+ // Max positive double
+ assert.equal(
+ 1.7976931348623157e308,
+ binary.readDouble([0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])
+ );
+ assert.end();
+ },
+
+ "Should write doubles": function(assert) {
+ assert.deepEqual(
+ [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+ binary.writeDouble([], 0)
+ );
+ assert.deepEqual(
+ [0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+ binary.writeDouble([], 1)
+ );
+ assert.deepEqual(
+ [0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+ binary.writeDouble([], 2)
+ );
+ assert.deepEqual(
+ [0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+ binary.writeDouble([], -2)
+ );
+
+ assert.deepEqual(
+ [0x40, 0x9, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18],
+ binary.writeDouble([], Math.PI)
+ );
+
+ assert.deepEqual(
+ [0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+ binary.writeDouble([], Infinity)
+ );
+ assert.deepEqual(
+ [0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+ binary.writeDouble([], -Infinity)
+ );
+
+ assert.deepEqual(
+ [0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+ binary.writeDouble([], NaN)
+ );
+
+ assert.deepEqual(
+ [0x3f, 0xd5, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55],
+ binary.writeDouble([], 1 / 3)
+ );
+
+ // Min subnormal positive double
+ assert.deepEqual(
+ [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
+ binary.writeDouble([], 4.9406564584124654e-324)
+ );
+ // Min normal positive double
+ assert.deepEqual(
+ [0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+ binary.writeDouble([], 2.2250738585072014e-308)
+ );
+ // Max positive double
+ assert.deepEqual(
+ [0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
+ binary.writeDouble([], 1.7976931348623157e308)
+ );
+ assert.end();
+ }
+};
+
+Object.keys(cases).forEach(function(caseName) {
+ test(caseName, cases[caseName]);
+});
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/certificates.README b/src/jaegertracing/thrift/lib/nodejs/test/certificates.README
new file mode 100644
index 000000000..06c507e7d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/certificates.README
@@ -0,0 +1,7 @@
+server.crt AND server.key ARE PROVIDED FOR TEST PURPOSE AND SHOULD *NEVER* BE USED IN PRODUCTION
+
+
+Origin of the test key and cert is the folder test/keys of Apache Thrift source code distribution
+
+We need copies for npm deployment
+
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/client.js b/src/jaegertracing/thrift/lib/nodejs/test/client.js
new file mode 100644
index 000000000..49e3a5ec9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/client.js
@@ -0,0 +1,170 @@
+#!/usr/bin/env node
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const assert = require("assert");
+const thrift = require("thrift");
+const helpers = require("./helpers");
+
+const ThriftTest = require(`./${helpers.genPath}/ThriftTest`);
+const ThriftTestDriver = require("./test_driver").ThriftTestDriver;
+const ThriftTestDriverPromise = require("./test_driver")
+ .ThriftTestDriverPromise;
+const SecondService = require(`./${helpers.genPath}/SecondService`);
+
+const program = require("commander");
+
+program
+ .option(
+ "-p, --protocol <protocol>",
+ "Set thrift protocol (binary|compact|json) [protocol]"
+ )
+ .option(
+ "-t, --transport <transport>",
+ "Set thrift transport (buffered|framed|http) [transport]"
+ )
+ .option("--port <port>", "Set thrift server port number to connect", 9090)
+ .option("--host <host>", "Set thrift server host to connect", "localhost")
+ .option(
+ "--domain-socket <path>",
+ "Set thrift server unix domain socket to connect"
+ )
+ .option("--ssl", "use SSL transport")
+ .option("--callback", "test with callback style functions")
+ .option(
+ "-t, --type <type>",
+ "Select server type (http|multiplex|tcp|websocket)",
+ "tcp"
+ )
+ .option("--es6", "Use es6 code")
+ .option("--es5", "Use es5 code")
+ .parse(process.argv);
+
+const host = program.host;
+const port = program.port;
+const domainSocket = program.domainSocket;
+const ssl = program.ssl;
+let type = program.type;
+
+/* for compatibility with cross test invocation for http transport testing */
+if (program.transport === "http") {
+ program.transport = "buffered";
+ type = "http";
+}
+
+const options = {
+ transport: helpers.transports[program.transport],
+ protocol: helpers.protocols[program.protocol]
+};
+
+if (type === "http" || type === "websocket") {
+ options.path = "/test";
+}
+
+if (type === "http") {
+ options.headers = { Connection: "close" };
+}
+
+if (ssl) {
+ if (type === "tcp" || type === "multiplex") {
+ options.rejectUnauthorized = false;
+ } else if (type === "http") {
+ options.nodeOptions = { rejectUnauthorized: false };
+ options.https = true;
+ } else if (type === "websocket") {
+ options.wsOptions = { rejectUnauthorized: false };
+ options.secure = true;
+ }
+}
+
+let connection;
+let client;
+const testDriver = program.callback
+ ? ThriftTestDriver
+ : ThriftTestDriverPromise;
+if (helpers.ecmaMode === "es6" && program.callback) {
+ console.log("ES6 does not support callback style");
+ process.exit(0);
+}
+
+if (type === "tcp" || type === "multiplex") {
+ if (domainSocket) {
+ connection = thrift.createUDSConnection(domainSocket, options);
+ } else {
+ connection = ssl
+ ? thrift.createSSLConnection(host, port, options)
+ : thrift.createConnection(host, port, options);
+ }
+} else if (type === "http") {
+ if (domainSocket) {
+ connection = thrift.createHttpUDSConnection(domainSocket, options);
+ } else {
+ connection = thrift.createHttpConnection(host, port, options);
+ }
+} else if (type === "websocket") {
+ connection = thrift.createWSConnection(host, port, options);
+ connection.open();
+}
+
+connection.on("error", function(err) {
+ assert(false, err);
+});
+
+if (type === "tcp") {
+ client = thrift.createClient(ThriftTest, connection);
+ runTests();
+} else if (type === "multiplex") {
+ const mp = new thrift.Multiplexer();
+ client = mp.createClient("ThriftTest", ThriftTest, connection);
+ const secondclient = mp.createClient(
+ "SecondService",
+ SecondService,
+ connection
+ );
+
+ connection.on("connect", function() {
+ secondclient.secondtestString("Test", function(err, response) {
+ assert(!err);
+ assert.equal('testString("Test")', response);
+ });
+
+ runTests();
+ });
+} else if (type === "http") {
+ client = thrift.createHttpClient(ThriftTest, connection);
+ runTests();
+} else if (type === "websocket") {
+ client = thrift.createWSClient(ThriftTest, connection);
+ runTests();
+}
+
+function runTests() {
+ testDriver(client, function(status) {
+ console.log(status);
+ if (type !== "http" && type !== "websocket") {
+ connection.end();
+ }
+ if (type !== "multiplex") {
+ process.exit(0);
+ }
+ });
+}
+
+exports.expressoTest = function() {};
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/deep-constructor.test.js b/src/jaegertracing/thrift/lib/nodejs/test/deep-constructor.test.js
new file mode 100644
index 000000000..504dacf0b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/deep-constructor.test.js
@@ -0,0 +1,333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const ttypes = require("./gen-nodejs/JsDeepConstructorTest_types");
+const thrift = require("thrift");
+const test = require("tape");
+const bufferEquals = require("buffer-equals");
+
+function serializeBinary(data) {
+ let buff;
+ const transport = new thrift.TBufferedTransport(null, function(msg) {
+ buff = msg;
+ });
+ const prot = new thrift.TBinaryProtocol(transport);
+ data.write(prot);
+ prot.flush();
+ return buff;
+}
+
+function deserializeBinary(serialized, type) {
+ const t = new thrift.TFramedTransport(serialized);
+ const p = new thrift.TBinaryProtocol(t);
+ const data = new type();
+ data.read(p);
+ return data;
+}
+
+function serializeJSON(data) {
+ let buff;
+ const transport = new thrift.TBufferedTransport(null, function(msg) {
+ buff = msg;
+ });
+ const protocol = new thrift.TJSONProtocol(transport);
+ protocol.writeMessageBegin("", 0, 0);
+ data.write(protocol);
+ protocol.writeMessageEnd();
+ protocol.flush();
+ return buff;
+}
+
+function deserializeJSON(serialized, type) {
+ const transport = new thrift.TFramedTransport(serialized);
+ const protocol = new thrift.TJSONProtocol(transport);
+ protocol.readMessageBegin();
+ const data = new type();
+ data.read(protocol);
+ protocol.readMessageEnd();
+ return data;
+}
+
+function createThriftObj() {
+ return new ttypes.Complex({
+ struct_field: new ttypes.Simple({ value: "a" }),
+
+ struct_list_field: [
+ new ttypes.Simple({ value: "b" }),
+ new ttypes.Simple({ value: "c" })
+ ],
+
+ struct_set_field: [
+ new ttypes.Simple({ value: "d" }),
+ new ttypes.Simple({ value: "e" })
+ ],
+
+ struct_map_field: {
+ A: new ttypes.Simple({ value: "f" }),
+ B: new ttypes.Simple({ value: "g" })
+ },
+
+ struct_nested_containers_field: [
+ [
+ {
+ C: [
+ new ttypes.Simple({ value: "h" }),
+ new ttypes.Simple({ value: "i" })
+ ]
+ }
+ ]
+ ],
+
+ struct_nested_containers_field2: {
+ D: [
+ {
+ DA: new ttypes.Simple({ value: "j" })
+ },
+ {
+ DB: new ttypes.Simple({ value: "k" })
+ }
+ ]
+ },
+
+ list_of_list_field: [
+ ["l00", "l01", "l02"],
+ ["l10", "l11", "l12"],
+ ["l20", "l21", "l22"]
+ ],
+
+ list_of_list_of_list_field: [
+ [
+ ["m000", "m001", "m002"],
+ ["m010", "m011", "m012"],
+ ["m020", "m021", "m022"]
+ ],
+ [
+ ["m100", "m101", "m102"],
+ ["m110", "m111", "m112"],
+ ["m120", "m121", "m122"]
+ ],
+ [
+ ["m200", "m201", "m202"],
+ ["m210", "m211", "m212"],
+ ["m220", "m221", "m222"]
+ ]
+ ]
+ });
+}
+
+function createJsObj() {
+ return {
+ struct_field: { value: "a" },
+
+ struct_list_field: [{ value: "b" }, { value: "c" }],
+
+ struct_set_field: [{ value: "d" }, { value: "e" }],
+
+ struct_map_field: {
+ A: { value: "f" },
+ B: { value: "g" }
+ },
+
+ struct_nested_containers_field: [
+ [
+ {
+ C: [{ value: "h" }, { value: "i" }]
+ }
+ ]
+ ],
+
+ struct_nested_containers_field2: {
+ D: [
+ {
+ DA: { value: "j" }
+ },
+ {
+ DB: { value: "k" }
+ }
+ ]
+ },
+
+ list_of_list_field: [
+ ["l00", "l01", "l02"],
+ ["l10", "l11", "l12"],
+ ["l20", "l21", "l22"]
+ ],
+
+ list_of_list_of_list_field: [
+ [
+ ["m000", "m001", "m002"],
+ ["m010", "m011", "m012"],
+ ["m020", "m021", "m022"]
+ ],
+ [
+ ["m100", "m101", "m102"],
+ ["m110", "m111", "m112"],
+ ["m120", "m121", "m122"]
+ ],
+ [
+ ["m200", "m201", "m202"],
+ ["m210", "m211", "m212"],
+ ["m220", "m221", "m222"]
+ ]
+ ]
+ };
+}
+
+function assertValues(obj, assert) {
+ assert.equals(obj.struct_field.value, "a");
+ assert.equals(obj.struct_list_field[0].value, "b");
+ assert.equals(obj.struct_list_field[1].value, "c");
+ assert.equals(obj.struct_set_field[0].value, "d");
+ assert.equals(obj.struct_set_field[1].value, "e");
+ assert.equals(obj.struct_map_field.A.value, "f");
+ assert.equals(obj.struct_map_field.B.value, "g");
+ assert.equals(obj.struct_nested_containers_field[0][0].C[0].value, "h");
+ assert.equals(obj.struct_nested_containers_field[0][0].C[1].value, "i");
+ assert.equals(obj.struct_nested_containers_field2.D[0].DA.value, "j");
+ assert.equals(obj.struct_nested_containers_field2.D[1].DB.value, "k");
+ assert.equals(obj.list_of_list_field[0][0], "l00");
+ assert.equals(obj.list_of_list_field[0][1], "l01");
+ assert.equals(obj.list_of_list_field[0][2], "l02");
+ assert.equals(obj.list_of_list_field[1][0], "l10");
+ assert.equals(obj.list_of_list_field[1][1], "l11");
+ assert.equals(obj.list_of_list_field[1][2], "l12");
+ assert.equals(obj.list_of_list_field[2][0], "l20");
+ assert.equals(obj.list_of_list_field[2][1], "l21");
+ assert.equals(obj.list_of_list_field[2][2], "l22");
+
+ assert.equals(obj.list_of_list_of_list_field[0][0][0], "m000");
+ assert.equals(obj.list_of_list_of_list_field[0][0][1], "m001");
+ assert.equals(obj.list_of_list_of_list_field[0][0][2], "m002");
+ assert.equals(obj.list_of_list_of_list_field[0][1][0], "m010");
+ assert.equals(obj.list_of_list_of_list_field[0][1][1], "m011");
+ assert.equals(obj.list_of_list_of_list_field[0][1][2], "m012");
+ assert.equals(obj.list_of_list_of_list_field[0][2][0], "m020");
+ assert.equals(obj.list_of_list_of_list_field[0][2][1], "m021");
+ assert.equals(obj.list_of_list_of_list_field[0][2][2], "m022");
+
+ assert.equals(obj.list_of_list_of_list_field[1][0][0], "m100");
+ assert.equals(obj.list_of_list_of_list_field[1][0][1], "m101");
+ assert.equals(obj.list_of_list_of_list_field[1][0][2], "m102");
+ assert.equals(obj.list_of_list_of_list_field[1][1][0], "m110");
+ assert.equals(obj.list_of_list_of_list_field[1][1][1], "m111");
+ assert.equals(obj.list_of_list_of_list_field[1][1][2], "m112");
+ assert.equals(obj.list_of_list_of_list_field[1][2][0], "m120");
+ assert.equals(obj.list_of_list_of_list_field[1][2][1], "m121");
+ assert.equals(obj.list_of_list_of_list_field[1][2][2], "m122");
+
+ assert.equals(obj.list_of_list_of_list_field[2][0][0], "m200");
+ assert.equals(obj.list_of_list_of_list_field[2][0][1], "m201");
+ assert.equals(obj.list_of_list_of_list_field[2][0][2], "m202");
+ assert.equals(obj.list_of_list_of_list_field[2][1][0], "m210");
+ assert.equals(obj.list_of_list_of_list_field[2][1][1], "m211");
+ assert.equals(obj.list_of_list_of_list_field[2][1][2], "m212");
+ assert.equals(obj.list_of_list_of_list_field[2][2][0], "m220");
+ assert.equals(obj.list_of_list_of_list_field[2][2][1], "m221");
+ assert.equals(obj.list_of_list_of_list_field[2][2][2], "m222");
+}
+
+function createTestCases(serialize, deserialize) {
+ const cases = {
+ "Serialize/deserialize should return equal object": function(assert) {
+ const tObj = createThriftObj();
+ const received = deserialize(serialize(tObj), ttypes.Complex);
+ assert.ok(tObj !== received, "not the same object");
+ assert.deepEqual(tObj, received);
+ assert.end();
+ },
+
+ "Nested structs and containers initialized from plain js objects should serialize same as if initialized from thrift objects": function(
+ assert
+ ) {
+ const tObj1 = createThriftObj();
+ const tObj2 = new ttypes.Complex(createJsObj());
+ assertValues(tObj2, assert);
+ const s1 = serialize(tObj1);
+ const s2 = serialize(tObj2);
+ assert.ok(bufferEquals(s1, s2));
+ assert.end();
+ },
+
+ "Modifications to args object should not affect constructed Thrift object": function(
+ assert
+ ) {
+ const args = createJsObj();
+ assertValues(args, assert);
+
+ const tObj = new ttypes.Complex(args);
+ assertValues(tObj, assert);
+
+ args.struct_field.value = "ZZZ";
+ args.struct_list_field[0].value = "ZZZ";
+ args.struct_list_field[1].value = "ZZZ";
+ args.struct_set_field[0].value = "ZZZ";
+ args.struct_set_field[1].value = "ZZZ";
+ args.struct_map_field.A.value = "ZZZ";
+ args.struct_map_field.B.value = "ZZZ";
+ args.struct_nested_containers_field[0][0].C[0] = "ZZZ";
+ args.struct_nested_containers_field[0][0].C[1] = "ZZZ";
+ args.struct_nested_containers_field2.D[0].DA = "ZZZ";
+ args.struct_nested_containers_field2.D[0].DB = "ZZZ";
+
+ assertValues(tObj, assert);
+ assert.end();
+ },
+
+ "nulls are ok": function(assert) {
+ const tObj = new ttypes.Complex({
+ struct_field: null,
+ struct_list_field: null,
+ struct_set_field: null,
+ struct_map_field: null,
+ struct_nested_containers_field: null,
+ struct_nested_containers_field2: null
+ });
+ const received = deserialize(serialize(tObj), ttypes.Complex);
+ assert.strictEqual(tObj.struct_field, null);
+ assert.ok(tObj !== received);
+ assert.deepEqual(tObj, received);
+ assert.end();
+ },
+
+ "Can make list with objects": function(assert) {
+ const tObj = new ttypes.ComplexList({
+ struct_list_field: [new ttypes.Complex({})]
+ });
+ const innerObj = tObj.struct_list_field[0];
+ assert.ok(innerObj instanceof ttypes.Complex);
+ assert.strictEqual(innerObj.struct_field, null);
+ assert.strictEqual(innerObj.struct_list_field, null);
+ assert.strictEqual(innerObj.struct_set_field, null);
+ assert.strictEqual(innerObj.struct_map_field, null);
+ assert.strictEqual(innerObj.struct_nested_containers_field, null);
+ assert.strictEqual(innerObj.struct_nested_containers_field2, null);
+ assert.end();
+ }
+ };
+ return cases;
+}
+
+function run(name, cases) {
+ Object.keys(cases).forEach(function(caseName) {
+ test(name + ": " + caseName, cases[caseName]);
+ });
+}
+
+run("binary", createTestCases(serializeBinary, deserializeBinary));
+run("json", createTestCases(serializeJSON, deserializeJSON));
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/episodic-code-generation-test/client.js b/src/jaegertracing/thrift/lib/nodejs/test/episodic-code-generation-test/client.js
new file mode 100644
index 000000000..55dc70269
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/episodic-code-generation-test/client.js
@@ -0,0 +1,77 @@
+#!/usr/bin/env node
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const assert = require("assert");
+const test = require("tape");
+const thrift = require("thrift");
+const program = require("commander");
+
+program
+ .option("--host <host>", "Set the thrift server host to connect", "localhost")
+ .option("--port <port>", "Set the thrift server port number to connect", 9090)
+ .parse(process.argv);
+
+const Service = require("./gen-2/second-episode/gen-nodejs/Service");
+const Types = require("types-package/first-episode/Types_types");
+
+const host = program.host;
+const port = program.port;
+
+const options = {
+ transport: thrift.TBufferedTransport,
+ protocol: thrift.TJSONProtocol
+};
+
+const connection = thrift.createConnection(host, port, options);
+const testDriver = function(client, callback) {
+ test("NodeJS episodic compilation client-server test", function(assert) {
+ const type1Object = new Types.Type1();
+ type1Object.number = 42;
+ type1Object.message = "The answer";
+ client.testEpisode(type1Object, function(err, response) {
+ assert.error(err, "no callback error");
+ assert.equal(response.number, type1Object.number + 1);
+ assert.equal(
+ response.message,
+ type1Object.message + " [Hello from the server]"
+ );
+ assert.end();
+ callback("Server successfully tested");
+ });
+ });
+};
+
+connection.on("error", function(err) {
+ assert(false, err);
+});
+
+const client = thrift.createClient(Service, connection);
+
+runTests();
+
+function runTests() {
+ testDriver(client, function(status) {
+ console.log(status);
+ connection.destroy();
+ });
+}
+
+exports.expressoTest = function() {};
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/episodic-code-generation-test/episodic_compilation.package.json b/src/jaegertracing/thrift/lib/nodejs/test/episodic-code-generation-test/episodic_compilation.package.json
new file mode 100644
index 000000000..7a78b4beb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/episodic-code-generation-test/episodic_compilation.package.json
@@ -0,0 +1,3 @@
+{
+ "name": "types-package"
+}
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/episodic-code-generation-test/server.js b/src/jaegertracing/thrift/lib/nodejs/test/episodic-code-generation-test/server.js
new file mode 100644
index 000000000..2917b681c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/episodic-code-generation-test/server.js
@@ -0,0 +1,50 @@
+#!/usr/bin/env node
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * 'License'); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const thrift = require("../../lib/thrift");
+const program = require("commander");
+
+program
+ .option("--port <port>", "Set the thrift server port", 9090)
+ .parse(process.argv);
+
+const Service = require("./gen-2/second-episode/gen-nodejs/Service");
+const Types = require("types-package/first-episode/Types_types");
+
+const port = program.port;
+
+const options = {
+ transport: thrift.TBufferedTransport,
+ protocol: thrift.TJSONProtocol
+};
+
+const ServiceHandler = {
+ testEpisode: function(receivedType1Object) {
+ const type1Object = new Types.Type1();
+ type1Object.number = receivedType1Object.number + 1;
+ type1Object.message =
+ receivedType1Object.message + " [Hello from the server]";
+ return type1Object;
+ }
+};
+
+const server = thrift.createServer(Service, ServiceHandler, options);
+server.listen(port);
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/exceptions.js b/src/jaegertracing/thrift/lib/nodejs/test/exceptions.js
new file mode 100644
index 000000000..ab2798a26
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/exceptions.js
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+"use strict";
+const test = require("tape");
+const thrift = require("../lib/thrift/thrift.js");
+const InputBufferUnderrunError = require("../lib/thrift/input_buffer_underrun_error");
+
+test("TApplicationException", function t(assert) {
+ const e = new thrift.TApplicationException(1, "foo");
+ assert.ok(
+ e instanceof thrift.TApplicationException,
+ "is instanceof TApplicationException"
+ );
+ assert.ok(e instanceof thrift.TException, "is instanceof TException");
+ assert.ok(e instanceof Error, "is instanceof Error");
+ assert.equal(typeof e.stack, "string", "has stack trace");
+ assert.ok(
+ /^TApplicationException: foo/.test(e.stack),
+ "Stack trace has correct error name and message"
+ );
+ assert.ok(
+ e.stack.indexOf("test/exceptions.js:7:11") !== -1,
+ "stack trace starts on correct line and column"
+ );
+ assert.equal(
+ e.name,
+ "TApplicationException",
+ "has function name TApplicationException"
+ );
+ assert.equal(e.message, "foo", 'has error message "foo"');
+ assert.equal(e.type, 1, "has type 1");
+ assert.end();
+});
+
+test("unexpected TApplicationException ", function t(assert) {
+ const e = new thrift.TApplicationException(1, 100);
+ assert.ok(
+ e instanceof thrift.TApplicationException,
+ "is instanceof TApplicationException"
+ );
+ assert.ok(e instanceof thrift.TException, "is instanceof TException");
+ assert.ok(e instanceof Error, "is instanceof Error");
+ assert.equal(typeof e.stack, "string", "has stack trace");
+ assert.ok(
+ /^TApplicationException: 100/.test(e.stack),
+ "Stack trace has correct error name and message"
+ );
+ assert.ok(
+ e.stack.indexOf("test/exceptions.js:7:11") !== -1,
+ "stack trace starts on correct line and column"
+ );
+ assert.equal(
+ e.name,
+ "TApplicationException",
+ "has function name TApplicationException"
+ );
+ assert.equal(e.message, 100, "has error message 100");
+ assert.equal(e.type, 1, "has type 1");
+ assert.end();
+});
+
+test("TException", function t(assert) {
+ const e = new thrift.TException("foo");
+ assert.ok(e instanceof thrift.TException, "is instanceof TException");
+ assert.ok(e instanceof Error, "is instanceof Error");
+ assert.equal(typeof e.stack, "string", "has stack trace");
+ assert.ok(
+ /^TException: foo/.test(e.stack),
+ "Stack trace has correct error name and message"
+ );
+ assert.ok(
+ e.stack.indexOf("test/exceptions.js:21:11") !== -1,
+ "stack trace starts on correct line and column"
+ );
+ assert.equal(e.name, "TException", "has function name TException");
+ assert.equal(e.message, "foo", 'has error message "foo"');
+ assert.end();
+});
+
+test("TProtocolException", function t(assert) {
+ const e = new thrift.TProtocolException(1, "foo");
+ assert.ok(
+ e instanceof thrift.TProtocolException,
+ "is instanceof TProtocolException"
+ );
+ assert.ok(e instanceof Error, "is instanceof Error");
+ assert.equal(typeof e.stack, "string", "has stack trace");
+ assert.ok(
+ /^TProtocolException: foo/.test(e.stack),
+ "Stack trace has correct error name and message"
+ );
+ assert.ok(
+ e.stack.indexOf("test/exceptions.js:33:11") !== -1,
+ "stack trace starts on correct line and column"
+ );
+ assert.equal(
+ e.name,
+ "TProtocolException",
+ "has function name TProtocolException"
+ );
+ assert.equal(e.message, "foo", 'has error message "foo"');
+ assert.equal(e.type, 1, "has type 1");
+ assert.end();
+});
+
+test("InputBufferUnderrunError", function t(assert) {
+ const e = new InputBufferUnderrunError("foo");
+ assert.ok(
+ e instanceof InputBufferUnderrunError,
+ "is instanceof InputBufferUnderrunError"
+ );
+ assert.ok(e instanceof Error, "is instanceof Error");
+ assert.equal(typeof e.stack, "string", "has stack trace");
+ assert.ok(
+ /^InputBufferUnderrunError: foo/.test(e.stack),
+ "Stack trace has correct error name and message"
+ );
+ assert.ok(
+ e.stack.indexOf("test/exceptions.js:46:11") !== -1,
+ "stack trace starts on correct line and column"
+ );
+ assert.equal(
+ e.name,
+ "InputBufferUnderrunError",
+ "has function name InputBufferUnderrunError"
+ );
+ assert.equal(e.message, "foo", 'has error message "foo"');
+ assert.end();
+});
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/header.test.js b/src/jaegertracing/thrift/lib/nodejs/test/header.test.js
new file mode 100644
index 000000000..efd7f81d5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/header.test.js
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const TFramedTransport = require("../lib/thrift/framed_transport");
+const THeaderTransport = require("../lib/thrift/header_transport");
+const THeaderProtocol = require("../lib/thrift/header_protocol");
+const thrift = require("../lib/thrift");
+const fs = require("fs");
+const test = require("tape");
+const path = require("path");
+
+const headerPayload = fs.readFileSync(
+ path.join(__dirname, "test_header_payload")
+);
+
+const cases = {
+ "Should read headers from payload": function(assert) {
+ const transport = new TFramedTransport();
+ transport.inBuf = Buffer.from(headerPayload);
+
+ const headers = transport.readHeaders();
+ assert.equals(headers.Parent, "shoobar");
+ assert.equals(headers.Trace, "abcde");
+ assert.end();
+ },
+ "Should read headers when reading message begin": function(assert) {
+ const transport = new TFramedTransport();
+ transport.inBuf = Buffer.from(headerPayload);
+ const protocol = new THeaderProtocol(transport);
+ const result = protocol.readMessageBegin();
+
+ const headers = transport.getReadHeaders();
+ assert.equals(headers.Parent, "shoobar");
+ assert.equals(headers.Trace, "abcde");
+ assert.equals(result.fname, "add");
+ assert.equals(result.mtype, thrift.Thrift.MessageType.CALL);
+ assert.end();
+ },
+ "Should be able to write headers": function(assert) {
+ const writeTransport = new TFramedTransport();
+ writeTransport.setProtocolId(THeaderTransport.SubprotocolId.BINARY);
+ writeTransport.setWriteHeader("Hihihihi", "hohohoho");
+ writeTransport.setWriteHeader("boobooboo", "fooshoopoo");
+ writeTransport.setWriteHeader("a", "z");
+ writeTransport.writeHeaders();
+ const writeBuffer = writeTransport.outBuffers[0];
+
+ const readTransport = new TFramedTransport();
+ readTransport.inBuf = writeBuffer;
+ readTransport.readHeaders();
+
+ const headers = readTransport.getReadHeaders();
+ assert.equals(headers.Hihihihi, "hohohoho");
+ assert.equals(headers.boobooboo, "fooshoopoo");
+ assert.equals(headers.a, "z");
+ assert.end();
+ }
+};
+
+Object.keys(cases).forEach(function(caseName) {
+ test(caseName, cases[caseName]);
+});
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/helpers.js b/src/jaegertracing/thrift/lib/nodejs/test/helpers.js
new file mode 100644
index 000000000..f3c27b3d1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/helpers.js
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+"use strict";
+const thrift = require("../lib/thrift");
+
+module.exports.transports = {
+ buffered: thrift.TBufferedTransport,
+ framed: thrift.TFramedTransport
+};
+
+module.exports.protocols = {
+ json: thrift.TJSONProtocol,
+ binary: thrift.TBinaryProtocol,
+ compact: thrift.TCompactProtocol,
+ header: thrift.THeaderProtocol
+};
+
+module.exports.ecmaMode = process.argv.includes("--es6") ? "es6" : "es5";
+module.exports.genPath = process.argv.includes("--es6")
+ ? "gen-nodejs-es6"
+ : "gen-nodejs";
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/int64.test.js b/src/jaegertracing/thrift/lib/nodejs/test/int64.test.js
new file mode 100644
index 000000000..27ad28c00
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/int64.test.js
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const Int64 = require("node-int64");
+const JSONInt64 = require("json-int64");
+const i64types = require("./gen-nodejs-es6/Int64Test_types.js");
+const test = require("tape");
+
+const cases = {
+ "should correctly generate Int64 constants": function(assert) {
+ const EXPECTED_SMALL_INT64_AS_NUMBER = 42;
+ const EXPECTED_SMALL_INT64 = new Int64(42);
+ const EXPECTED_MAX_JS_SAFE_INT64 = new Int64(Number.MAX_SAFE_INTEGER);
+ const EXPECTED_MIN_JS_SAFE_INT64 = new Int64(Number.MIN_SAFE_INTEGER);
+ const EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64 = new Int64("0020000000000000"); // hex-encoded
+ const EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64 = new Int64("ffe0000000000000"); // hex-encoded 2's complement
+ const EXPECTED_MAX_SIGNED_INT64 = new Int64("7fffffffffffffff"); // hex-encoded
+ const EXPECTED_MIN_SIGNED_INT64 = new Int64("8000000000000000"); // hex-encoded 2's complement
+ const EXPECTED_INT64_LIST = [
+ EXPECTED_SMALL_INT64,
+ EXPECTED_MAX_JS_SAFE_INT64,
+ EXPECTED_MIN_JS_SAFE_INT64,
+ EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64,
+ EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64,
+ EXPECTED_MAX_SIGNED_INT64,
+ EXPECTED_MIN_SIGNED_INT64
+ ];
+
+ assert.ok(EXPECTED_SMALL_INT64.equals(i64types.SMALL_INT64));
+ assert.ok(EXPECTED_MAX_JS_SAFE_INT64.equals(i64types.MAX_JS_SAFE_INT64));
+ assert.ok(EXPECTED_MIN_JS_SAFE_INT64.equals(i64types.MIN_JS_SAFE_INT64));
+ assert.ok(
+ EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64.equals(
+ i64types.MAX_JS_SAFE_PLUS_ONE_INT64
+ )
+ );
+ assert.ok(
+ EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64.equals(
+ i64types.MIN_JS_SAFE_MINUS_ONE_INT64
+ )
+ );
+ assert.ok(EXPECTED_MAX_SIGNED_INT64.equals(i64types.MAX_SIGNED_INT64));
+ assert.ok(EXPECTED_MIN_SIGNED_INT64.equals(i64types.MIN_SIGNED_INT64));
+ assert.equal(
+ EXPECTED_SMALL_INT64_AS_NUMBER,
+ i64types.SMALL_INT64.toNumber()
+ );
+ assert.equal(
+ Number.MAX_SAFE_INTEGER,
+ i64types.MAX_JS_SAFE_INT64.toNumber()
+ );
+ assert.equal(
+ Number.MIN_SAFE_INTEGER,
+ i64types.MIN_JS_SAFE_INT64.toNumber()
+ );
+
+ for (let i = 0; i < EXPECTED_INT64_LIST.length; ++i) {
+ assert.ok(EXPECTED_INT64_LIST[i].equals(i64types.INT64_LIST[i]));
+ }
+
+ for (let i = 0; i < EXPECTED_INT64_LIST.length; ++i) {
+ const int64Object = EXPECTED_INT64_LIST[i];
+ assert.ok(
+ i64types.INT64_2_INT64_MAP[
+ JSONInt64.toDecimalString(int64Object)
+ ].equals(int64Object)
+ );
+ }
+
+ assert.end();
+ }
+};
+
+Object.keys(cases).forEach(function(caseName) {
+ test(caseName, cases[caseName]);
+});
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/server.crt b/src/jaegertracing/thrift/lib/nodejs/test/server.crt
new file mode 100644
index 000000000..8a5ef3c3a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/server.crt
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIENzCCAx+gAwIBAgIJAOYfYfw7NCOcMA0GCSqGSIb3DQEBBQUAMIGxMQswCQYD
+VQQGEwJVUzERMA8GA1UECAwITWFyeWxhbmQxFDASBgNVBAcMC0ZvcmVzdCBIaWxs
+MScwJQYDVQQKDB5UaGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24xFjAUBgNV
+BAsMDUFwYWNoZSBUaHJpZnQxEjAQBgNVBAMMCWxvY2FsaG9zdDEkMCIGCSqGSIb3
+DQEJARYVZGV2QHRocmlmdC5hcGFjaGUub3JnMB4XDTE0MDQwNzE4NTgwMFoXDTIy
+MDYyNDE4NTgwMFowgbExCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEU
+MBIGA1UEBwwLRm9yZXN0IEhpbGwxJzAlBgNVBAoMHlRoZSBBcGFjaGUgU29mdHdh
+cmUgRm91bmRhdGlvbjEWMBQGA1UECwwNQXBhY2hlIFRocmlmdDESMBAGA1UEAwwJ
+bG9jYWxob3N0MSQwIgYJKoZIhvcNAQkBFhVkZXZAdGhyaWZ0LmFwYWNoZS5vcmcw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqE9TE9wEXp5LRtLQVDSGQ
+GV78+7ZtP/I/ZaJ6Q6ZGlfxDFvZjFF73seNhAvlKlYm/jflIHYLnNOCySN8I2Xw6
+L9MbC+jvwkEKfQo4eDoxZnOZjNF5J1/lZtBeOowMkhhzBMH1Rds351/HjKNg6ZKg
+2Cldd0j7HbDtEixOLgLbPRpBcaYrLrNMasf3Hal+x8/b8ue28x93HSQBGmZmMIUw
+AinEu/fNP4lLGl/0kZb76TnyRpYSPYojtS6CnkH+QLYnsRREXJYwD1Xku62LipkX
+wCkRTnZ5nUsDMX6FPKgjQFQCWDXG/N096+PRUQAChhrXsJ+gF3NqWtDmtrhVQF4n
+AgMBAAGjUDBOMB0GA1UdDgQWBBQo8v0wzQPx3EEexJPGlxPK1PpgKjAfBgNVHSME
+GDAWgBQo8v0wzQPx3EEexJPGlxPK1PpgKjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3
+DQEBBQUAA4IBAQBGFRiJslcX0aJkwZpzTwSUdgcfKbpvNEbCNtVohfQVTI4a/oN5
+U+yqDZJg3vOaOuiAZqyHcIlZ8qyesCgRN314Tl4/JQ++CW8mKj1meTgo5YFxcZYm
+T9vsI3C+Nzn84DINgI9mx6yktIt3QOKZRDpzyPkUzxsyJ8J427DaimDrjTR+fTwD
+1Dh09xeeMnSa5zeV1HEDyJTqCXutLetwQ/IyfmMBhIx+nvB5f67pz/m+Dv6V0r3I
+p4HCcdnDUDGJbfqtoqsAATQQWO+WWuswB6mOhDbvPTxhRpZq6AkgWqv4S+u3M2GO
+r5p9FrBgavAw5bKO54C0oQKpN/5fta5l6Ws0
+-----END CERTIFICATE-----
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/server.js b/src/jaegertracing/thrift/lib/nodejs/test/server.js
new file mode 100644
index 000000000..7402094bc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/server.js
@@ -0,0 +1,137 @@
+#!/usr/bin/env node
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * 'License'); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const fs = require("fs");
+const path = require("path");
+const thrift = require("../lib/thrift");
+const program = require("commander");
+const helpers = require("./helpers");
+
+program
+ .option(
+ "-p, --protocol <protocol>",
+ "Set thrift protocol (binary|compact|json)",
+ "binary"
+ )
+ .option(
+ "-t, --transport <transport>",
+ "Set thrift transport (buffered|framed|http)",
+ "buffered"
+ )
+ .option("--ssl", "use ssl transport")
+ .option("--port <port>", "Set thrift server port", 9090)
+ .option("--domain-socket <path>", "Set thift server unix domain socket")
+ .option(
+ "-t, --type <type>",
+ "Select server type (http|multiplex|tcp|websocket)",
+ "tcp"
+ )
+ .option("--callback", "test with callback style functions")
+ .option("--es6", "Use es6 code")
+ .option("--es5", "Use es5 code")
+ .parse(process.argv);
+
+const ThriftTest = require(`./${helpers.genPath}/ThriftTest`);
+const SecondService = require(`./${helpers.genPath}/SecondService`);
+const { ThriftTestHandler } = require("./test_handler");
+
+const port = program.port;
+const domainSocket = program.domainSocket;
+const ssl = program.ssl;
+
+let type = program.type;
+if (program.transport === "http") {
+ program.transport = "buffered";
+ type = "http";
+}
+
+let options = {
+ transport: helpers.transports[program.transport],
+ protocol: helpers.protocols[program.protocol]
+};
+
+if (type === "http" || type === "websocket") {
+ options.handler = ThriftTestHandler;
+ options.processor = ThriftTest;
+
+ options = {
+ services: { "/test": options },
+ cors: {
+ "*": true
+ }
+ };
+}
+
+let processor;
+if (type === "multiplex") {
+ const SecondServiceHandler = {
+ secondtestString: function(thing, result) {
+ console.log('testString("' + thing + '")');
+ result(null, 'testString("' + thing + '")');
+ }
+ };
+
+ processor = new thrift.MultiplexedProcessor();
+
+ processor.registerProcessor(
+ "ThriftTest",
+ new ThriftTest.Processor(ThriftTestHandler)
+ );
+
+ processor.registerProcessor(
+ "SecondService",
+ new SecondService.Processor(SecondServiceHandler)
+ );
+}
+
+if (ssl) {
+ if (
+ type === "tcp" ||
+ type === "multiplex" ||
+ type === "http" ||
+ type === "websocket"
+ ) {
+ options.tls = {
+ key: fs.readFileSync(path.resolve(__dirname, "server.key")),
+ cert: fs.readFileSync(path.resolve(__dirname, "server.crt"))
+ };
+ }
+}
+
+let server;
+if (type === "tcp") {
+ server = thrift.createServer(ThriftTest, ThriftTestHandler, options);
+} else if (type === "multiplex") {
+ server = thrift.createMultiplexServer(processor, options);
+} else if (type === "http" || type === "websocket") {
+ server = thrift.createWebServer(options);
+}
+
+if (domainSocket) {
+ server.listen(domainSocket);
+} else if (
+ type === "tcp" ||
+ type === "multiplex" ||
+ type === "http" ||
+ type === "websocket"
+) {
+ server.listen(port);
+}
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/server.key b/src/jaegertracing/thrift/lib/nodejs/test/server.key
new file mode 100644
index 000000000..263cfce59
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/server.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCqE9TE9wEXp5LR
+tLQVDSGQGV78+7ZtP/I/ZaJ6Q6ZGlfxDFvZjFF73seNhAvlKlYm/jflIHYLnNOCy
+SN8I2Xw6L9MbC+jvwkEKfQo4eDoxZnOZjNF5J1/lZtBeOowMkhhzBMH1Rds351/H
+jKNg6ZKg2Cldd0j7HbDtEixOLgLbPRpBcaYrLrNMasf3Hal+x8/b8ue28x93HSQB
+GmZmMIUwAinEu/fNP4lLGl/0kZb76TnyRpYSPYojtS6CnkH+QLYnsRREXJYwD1Xk
+u62LipkXwCkRTnZ5nUsDMX6FPKgjQFQCWDXG/N096+PRUQAChhrXsJ+gF3NqWtDm
+trhVQF4nAgMBAAECggEAW/y52YYW6ypROGbZ94DQpFV0kLO7qT8q0Ksxw5sPNaIt
+fEPRIymDa8ikyHWJS5Oxmw84wo5jnJV26jaLmwe2Lupq7Xf1lqej8f5LJtuv7cQR
+xfzp1vM65KJFFJHp6WqjGqJ6HSSZOpVDsnQYcXQjQCdpyAmaSWd3p+FqYSZ1mQmD
+bFNI7jqpczWSZhTdotQ7p7Hn9TVCehflP3yGIB3bQ+wCcCB85dOBz201L+YgaIck
+Sz43A4NvWaQIRLRDw7s9GW4jY5T0Jv282WIeAlVpVxLIwu48r4R4yGTIx9Ydowvq
+57+Y5iPPjAXxu0V9t00oS3bYxDaKh2DUfc/5zowq8QKBgQDYNVPXmaG0aIH4vjQ9
+7fRdw/UDkYcQbn6CnglQOu77/S8ogQzpKCVJgJgkZNqOVtQMEPzekGEcLTbje1gU
+8Bky2k+PL9UwbFy0emnOVh4rqrNXHsRvJcehNT/PRb5hjF3MUMFV/0iD4b+naFaE
+jrSWiZ2ZXj2qfwAK52GFbtOuBQKBgQDJYQuGiY0r22E4waJmCSKczoBT3cwlVzWj
+V2ljgA9RHLNTVkvNNYQLGu2qngFrtwpeaSnsMDerVG4wKAQWyCnYzxVrlnC4uDrJ
+HXuFEltBWi9Ffbgfsnd3749AT0oBP1NT2tMleguyf5DFgjCR3VRJLdrVaaZ8row/
+LqKcFMqnOwKBgB+OIO99l7E584Y3VG6ZdSneOLtNmRXX2pT7tcZE465ZdHGH7Dd3
+SYHhx9K/+Xn+yDH+pLli/xlarAEldmSP6k2WuTfftlC78AfTOfAId5zN7CDR9791
+Fx67I9X/itq33tS8EIuZl57P6uXm/4GXRloWOa8xpvRkVsBApuYPl8t1AoGATQDS
+y2sllDObBXzlgGbV2WgNIgSZ311toTv3jJiXQsjauW8yJRHln+l4H9mzaWDgkiFc
+ang1kUoDqF5k0eFQPxtQcYdhKwEnWWfwp33RbzfxA32DPnubuzzbZhfrkHaKgnIW
+cyor9uFYlm2l7ODZLfJez2RKyTplXnOSsmQw6akCgYAz3dj9Hskyj+HVJ+ht1OcE
+c7ai/ESkSA7Vajp0tjJp0EKjW/zq8DvUSXOtcdnJgkKycFluLwbmnaN4txBds1C1
+Qr8Rt2sUCCBNZe1L6DHe3XBdbkJe9sgZVNTjtUSQrzy8UhvsCqG4YWeCu07Szcbc
+rdPUV9/uQkdx8VrShxlD8A==
+-----END PRIVATE KEY-----
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/test-cases.js b/src/jaegertracing/thrift/lib/nodejs/test/test-cases.js
new file mode 100644
index 000000000..02c566fbf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/test-cases.js
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+"use strict";
+
+const helpers = require("./helpers");
+const ttypes = require(`./${helpers.genPath}/ThriftTest_types`);
+const Int64 = require("node-int64");
+
+//all Languages in UTF-8
+/*jshint -W100 */
+const stringTest = (module.exports.stringTest =
+ "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, " +
+ "Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, " +
+ "БеларуÑкаÑ, БеларуÑÐºÐ°Ñ (тарашкевіца), БългарÑки, Bamanankan, " +
+ "বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Ðохчийн, " +
+ "Cebuano, á£áŽ³áŽ©, ÄŒesky, СловѣÌньÑкъ / ⰔⰎⰑⰂⰡâ°â° â°”â°â°Ÿ, Чӑвашла, Cymraeg, " +
+ "Dansk, Zazaki, Þ‹Þ¨ÞˆÞ¬Þ€Þ¨Þ„Þ¦ÞÞ°, Ελληνικά, Emiliàn e rumagnòl, English, " +
+ "Esperanto, Español, Eesti, Euskara, Ùارسی, Suomi, Võro, Føroyskt, " +
+ "Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, " +
+ "Avañe'ẽ, ગà«àªœàª°àª¾àª¤à«€, Gaelg, עברית, हिनà¥à¤¦à¥€, Fiji Hindi, Hrvatski, " +
+ "Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia, " +
+ "Ilokano, Ido, Ãslenska, Italiano, 日本語, Lojban, Basa Jawa, " +
+ "ქáƒáƒ áƒ—ული, Kongo, Kalaallisut, ಕನà³à²¨à²¡, 한국어, Къарачай-Малкъар, " +
+ "Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, " +
+ "Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa " +
+ "Banyumasan, Malagasy, МакедонÑки, മലയാളം, मराठी, مازÙرونی, Bahasa " +
+ "Melayu, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪" +
+ "Norsk (nynorsk)‬, ‪Norsk (bokmål)‬, Nouormand, Diné bizaad, " +
+ "Occitan, Иронау, Papiamentu, Deitsch, Polski, پنجابی, پښتو, " +
+ "Norfuk / Pitkern, Português, Runa Simi, Rumantsch, Romani, Română, " +
+ "РуÑÑкий, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple " +
+ "English, SlovenÄina, SlovenÅ¡Äina, СрпÑки / Srpski, Seeltersk, " +
+ "Svenska, Kiswahili, தமிழà¯, తెలà±à°—à±, Тоҷикӣ, ไทย, Türkmençe, Tagalog, " +
+ "Türkçe, Татарча/Tatarça, УкраїнÑька, اردو, Tiếng Việt, Volapük, " +
+ "Walon, Winaray, å´è¯­, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, " +
+ "Bân-lâm-gú, 粵語");
+/*jshint +W100 */
+
+const specialCharacters = (module.exports.specialCharacters =
+ 'quote: " backslash:' +
+ " forwardslash-escaped: / " +
+ " backspace: \b formfeed: \f newline: \n return: \r tab: " +
+ ' now-all-of-them-together: "\\/\b\n\r\t' +
+ " now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><" +
+ ' char-to-test-json-parsing: ]] "]] \\" }}}{ [[[ ');
+
+const mapTestInput = (module.exports.mapTestInput = {
+ a: "123",
+ "a b": "with spaces ",
+ same: "same",
+ "0": "numeric key",
+ longValue: stringTest,
+ stringTest: "long key"
+});
+
+const simple = [
+ ["testVoid", undefined],
+ ["testString", "Test"],
+ ["testString", ""],
+ ["testString", stringTest],
+ ["testString", specialCharacters],
+ ["testBool", true],
+ ["testBool", false],
+ ["testByte", 1],
+ ["testByte", 0],
+ ["testByte", -1],
+ ["testByte", -127],
+ ["testI32", -1],
+ ["testDouble", -5.2098523],
+ ["testDouble", 7.012052175215044],
+ ["testEnum", ttypes.Numberz.ONE],
+ ["testI64", 5],
+ ["testI64", -5],
+ ["testI64", 734359738368],
+ ["testI64", -734359738368],
+ ["testI64", new Int64(new Buffer([0, 0x20, 0, 0, 0, 0, 0, 1]))], // 2^53+1
+ [
+ "testI64",
+ new Int64(new Buffer([0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]))
+ ], // -2^53-1
+ ["testTypedef", 69]
+];
+
+const mapout = {};
+for (let i = 0; i < 5; ++i) {
+ mapout[i] = i - 10;
+}
+
+const deep = [
+ [
+ "testList",
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
+ ]
+];
+
+const deepUnordered = [
+ ["testMap", mapout],
+ ["testSet", [1, 2, 3]],
+ ["testStringMap", mapTestInput]
+];
+
+const out = new ttypes.Xtruct({
+ string_thing: "Zero",
+ byte_thing: 1,
+ i32_thing: -3,
+ i64_thing: 1000000
+});
+
+const out2 = new ttypes.Xtruct2();
+out2.byte_thing = 1;
+out2.struct_thing = out;
+out2.i32_thing = 5;
+
+const crazy = new ttypes.Insanity({
+ userMap: { "5": 5, "8": 8 },
+ xtructs: [
+ new ttypes.Xtruct({
+ string_thing: "Goodbye4",
+ byte_thing: 4,
+ i32_thing: 4,
+ i64_thing: 4
+ }),
+ new ttypes.Xtruct({
+ string_thing: "Hello2",
+ byte_thing: 2,
+ i32_thing: 2,
+ i64_thing: 2
+ })
+ ]
+});
+
+const crazy2 = new ttypes.Insanity({
+ userMap: { "5": 5, "8": 8 },
+ xtructs: [
+ {
+ string_thing: "Goodbye4",
+ byte_thing: 4,
+ i32_thing: 4,
+ i64_thing: 4
+ },
+ {
+ string_thing: "Hello2",
+ byte_thing: 2,
+ i32_thing: 2,
+ i64_thing: 2
+ }
+ ]
+});
+
+const insanity = {
+ "1": { "2": crazy, "3": crazy },
+ "2": { "6": { userMap: {}, xtructs: [] } }
+};
+
+module.exports.simple = simple;
+module.exports.deep = deep;
+module.exports.deepUnordered = deepUnordered;
+
+module.exports.out = out;
+module.exports.out2 = out2;
+module.exports.crazy = crazy;
+module.exports.crazy2 = crazy2;
+module.exports.insanity = insanity;
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/testAll.sh b/src/jaegertracing/thrift/lib/nodejs/test/testAll.sh
new file mode 100755
index 000000000..3ae88b369
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/testAll.sh
@@ -0,0 +1,151 @@
+#! /bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+if [ -n "${1}" ]; then
+ COVER=${1};
+fi
+
+DIR="$( cd "$( dirname "$0" )" && pwd )"
+
+EPISODIC_DIR=${DIR}/episodic-code-generation-test
+
+THRIFT_FILES_DIR=${DIR}/../../../test
+
+THRIFT_COMPILER=${DIR}/../../../compiler/cpp/thrift
+
+ISTANBUL="$DIR/../../../node_modules/istanbul/lib/cli.js"
+
+REPORT_PREFIX="${DIR}/../coverage/report"
+
+COUNT=0
+
+export NODE_PATH="${DIR}:${DIR}/../lib:${NODE_PATH}"
+
+testServer()
+{
+ echo " [ECMA $1] Testing $2 Client/Server with protocol $3 and transport $4 $5";
+ RET=0
+ if [ -n "${COVER}" ]; then
+ ${ISTANBUL} cover ${DIR}/server.js --dir ${REPORT_PREFIX}${COUNT} --handle-sigint -- --type $2 -p $3 -t $4 $5 &
+ COUNT=$((COUNT+1))
+ else
+ node ${DIR}/server.js --${1} --type $2 -p $3 -t $4 $5 &
+ fi
+ SERVERPID=$!
+ sleep 0.1
+ if [ -n "${COVER}" ]; then
+ ${ISTANBUL} cover ${DIR}/client.js --dir ${REPORT_PREFIX}${COUNT} -- --${1} --type $2 -p $3 -t $4 $5 || RET=1
+ COUNT=$((COUNT+1))
+ else
+ node ${DIR}/client.js --${1} --type $2 -p $3 -t $4 $5 || RET=1
+ fi
+ kill -2 $SERVERPID || RET=1
+ wait $SERVERPID
+ return $RET
+}
+
+testEpisodicCompilation()
+{
+ RET=0
+ if [ -n "${COVER}" ]; then
+ ${ISTANBUL} cover ${EPISODIC_DIR}/server.js --dir ${REPORT_PREFIX}${COUNT} --handle-sigint &
+ COUNT=$((COUNT+1))
+ else
+ node ${EPISODIC_DIR}/server.js &
+ fi
+ SERVERPID=$!
+ sleep 0.1
+ if [ -n "${COVER}" ]; then
+ ${ISTANBUL} cover ${EPISODIC_DIR}/client.js --dir ${REPORT_PREFIX}${COUNT} || RET=1
+ COUNT=$((COUNT+1))
+ else
+ node ${EPISODIC_DIR}/client.js || RET=1
+ fi
+ kill -2 $SERVERPID || RET=1
+ wait $SERVERPID
+ return $RET
+}
+
+
+TESTOK=0
+
+# generating Thrift code
+
+${THRIFT_COMPILER} -o ${DIR} --gen js:node ${THRIFT_FILES_DIR}/ThriftTest.thrift
+${THRIFT_COMPILER} -o ${DIR} --gen js:node ${THRIFT_FILES_DIR}/JsDeepConstructorTest.thrift
+${THRIFT_COMPILER} -o ${DIR} --gen js:node ${THRIFT_FILES_DIR}/Int64Test.thrift
+mkdir ${DIR}/gen-nodejs-es6
+${THRIFT_COMPILER} -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${THRIFT_FILES_DIR}/ThriftTest.thrift
+${THRIFT_COMPILER} -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${THRIFT_FILES_DIR}/JsDeepConstructorTest.thrift
+${THRIFT_COMPILER} -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${THRIFT_FILES_DIR}/Int64Test.thrift
+
+# generate episodic compilation test code
+TYPES_PACKAGE=${EPISODIC_DIR}/node_modules/types-package
+
+# generate the first episode
+mkdir --parents ${EPISODIC_DIR}/gen-1/first-episode
+${THRIFT_COMPILER} -o ${EPISODIC_DIR}/gen-1/first-episode --gen js:node,thrift_package_output_directory=first-episode ${THRIFT_FILES_DIR}/Types.thrift
+
+# create a "package" from the first episode and "install" it, the episode file must be at the module root
+mkdir --parents ${TYPES_PACKAGE}/first-episode
+cp --force ${EPISODIC_DIR}/episodic_compilation.package.json ${TYPES_PACKAGE}/package.json
+cp --force ${EPISODIC_DIR}/gen-1/first-episode/gen-nodejs/Types_types.js ${TYPES_PACKAGE}/first-episode/
+cp --force ${EPISODIC_DIR}/gen-1/first-episode/gen-nodejs/thrift.js.episode ${TYPES_PACKAGE}
+
+# generate the second episode
+mkdir --parents ${EPISODIC_DIR}/gen-2/second-episode
+${THRIFT_COMPILER} -o ${EPISODIC_DIR}/gen-2/second-episode --gen js:node,imports=${TYPES_PACKAGE} ${THRIFT_FILES_DIR}/Service.thrift
+if [ -f ${EPISODIC_DIR}/gen-2/second-episode/Types_types.js ]; then
+ TESTOK=1
+fi
+
+# unit tests
+
+node ${DIR}/binary.test.js || TESTOK=1
+node ${DIR}/int64.test.js || TESTOK=1
+node ${DIR}/deep-constructor.test.js || TESTOK=1
+
+# integration tests
+
+for type in tcp multiplex websocket http
+do
+ for protocol in compact binary json
+ do
+ for transport in buffered framed
+ do
+ for ecma_version in es5 es6
+ do
+ testServer $ecma_version $type $protocol $transport || TESTOK=1
+ testServer $ecma_version $type $protocol $transport --ssl || TESTOK=1
+ testServer $ecma_version $type $protocol $transport --callback || TESTOK=1
+ done
+ done
+ done
+done
+
+# episodic compilation test
+testEpisodicCompilation
+
+if [ -n "${COVER}" ]; then
+ ${ISTANBUL} report --dir "${DIR}/../coverage" --include "${DIR}/../coverage/report*/coverage.json" lcov cobertura html
+ rm -r ${DIR}/../coverage/report*/*
+ rmdir ${DIR}/../coverage/report*
+fi
+
+exit $TESTOK
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/test_driver.js b/src/jaegertracing/thrift/lib/nodejs/test/test_driver.js
new file mode 100644
index 000000000..7c9a91914
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/test_driver.js
@@ -0,0 +1,361 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * 'License'); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// This is the Node.js test driver for the standard Apache Thrift
+// test service. The driver invokes every function defined in the
+// Thrift Test service with a representative range of parameters.
+//
+// The ThriftTestDriver function requires a client object
+// connected to a server hosting the Thrift Test service and
+// supports an optional callback function which is called with
+// a status message when the test is complete.
+
+const test = require("tape");
+
+const helpers = require("./helpers");
+const ttypes = require(`./${helpers.genPath}/ThriftTest_types`);
+const TException = require("thrift").Thrift.TException;
+const Int64 = require("node-int64");
+const testCases = require("./test-cases");
+
+exports.ThriftTestDriver = function(client, callback) {
+ test(
+ "NodeJS Style Callback Client Tests",
+ { skip: helpers.ecmaMode === "es6" },
+ function(assert) {
+ const checkRecursively = makeRecursiveCheck(assert);
+
+ function makeAsserter(assertionFn) {
+ return function(c) {
+ const fnName = c[0];
+ const expected = c[1];
+ client[fnName](expected, function(err, actual) {
+ assert.error(err, fnName + ": no callback error");
+ assertionFn(actual, expected, fnName);
+ });
+ };
+ }
+
+ testCases.simple.forEach(
+ makeAsserter(function(a, e, m) {
+ if (a instanceof Int64) {
+ const e64 = e instanceof Int64 ? e : new Int64(e);
+ assert.deepEqual(a.buffer, e64.buffer, m);
+ } else {
+ assert.equal(a, e, m);
+ }
+ })
+ );
+ testCases.deep.forEach(makeAsserter(assert.deepEqual));
+ testCases.deepUnordered.forEach(
+ makeAsserter(makeUnorderedDeepEqual(assert))
+ );
+
+ const arr = [];
+ for (let i = 0; i < 256; ++i) {
+ arr[i] = 255 - i;
+ }
+ let buf = new Buffer(arr);
+ client.testBinary(buf, function(err, response) {
+ assert.error(err, "testBinary: no callback error");
+ assert.equal(response.length, 256, "testBinary");
+ assert.deepEqual(response, buf, "testBinary(Buffer)");
+ });
+ buf = new Buffer(arr);
+ client.testBinary(buf.toString("binary"), function(err, response) {
+ assert.error(err, "testBinary: no callback error");
+ assert.equal(response.length, 256, "testBinary");
+ assert.deepEqual(response, buf, "testBinary(string)");
+ });
+
+ client.testMapMap(42, function(err, response) {
+ const expected = {
+ "4": { "1": 1, "2": 2, "3": 3, "4": 4 },
+ "-4": { "-4": -4, "-3": -3, "-2": -2, "-1": -1 }
+ };
+ assert.error(err, "testMapMap: no callback error");
+ assert.deepEqual(expected, response, "testMapMap");
+ });
+
+ client.testStruct(testCases.out, function(err, response) {
+ assert.error(err, "testStruct: no callback error");
+ checkRecursively(testCases.out, response, "testStruct");
+ });
+
+ client.testNest(testCases.out2, function(err, response) {
+ assert.error(err, "testNest: no callback error");
+ checkRecursively(testCases.out2, response, "testNest");
+ });
+
+ client.testInsanity(testCases.crazy, function(err, response) {
+ assert.error(err, "testInsanity: no callback error");
+ checkRecursively(testCases.insanity, response, "testInsanity");
+ });
+
+ client.testInsanity(testCases.crazy2, function(err, response) {
+ assert.error(err, "testInsanity2: no callback error");
+ checkRecursively(testCases.insanity, response, "testInsanity2");
+ });
+
+ client.testException("TException", function(err, response) {
+ assert.ok(
+ err instanceof TException,
+ "testException: correct error type"
+ );
+ assert.ok(!response, "testException: no response");
+ });
+
+ client.testException("Xception", function(err, response) {
+ assert.ok(
+ err instanceof ttypes.Xception,
+ "testException: correct error type"
+ );
+ assert.ok(!response, "testException: no response");
+ assert.equal(err.errorCode, 1001, "testException: correct error code");
+ assert.equal(
+ "Xception",
+ err.message,
+ "testException: correct error message"
+ );
+ });
+
+ client.testException("no Exception", function(err, response) {
+ assert.error(err, "testException: no callback error");
+ assert.ok(!response, "testException: no response");
+ });
+
+ client.testOneway(0, function(err, response) {
+ assert.error(err, "testOneway: no callback error");
+ assert.strictEqual(response, undefined, "testOneway: void response");
+ });
+
+ checkOffByOne(function(done) {
+ client.testI32(-1, function(err, response) {
+ assert.error(err, "checkOffByOne: no callback error");
+ assert.equal(-1, response);
+ assert.end();
+ done();
+ });
+ }, callback);
+ }
+ );
+
+ // ES6 does not support callback style
+ if (helpers.ecmaMode === "es6") {
+ checkOffByOne(done => done(), callback);
+ }
+};
+
+exports.ThriftTestDriverPromise = function(client, callback) {
+ test("Promise Client Tests", function(assert) {
+ const checkRecursively = makeRecursiveCheck(assert);
+
+ function makeAsserter(assertionFn) {
+ return function(c) {
+ const fnName = c[0];
+ const expected = c[1];
+ client[fnName](expected)
+ .then(function(actual) {
+ assertionFn(actual, expected, fnName);
+ })
+ .catch(() => assert.fail("fnName"));
+ };
+ }
+
+ testCases.simple.forEach(
+ makeAsserter(function(a, e, m) {
+ if (a instanceof Int64) {
+ const e64 = e instanceof Int64 ? e : new Int64(e);
+ assert.deepEqual(a.buffer, e64.buffer, m);
+ } else {
+ assert.equal(a, e, m);
+ }
+ })
+ );
+ testCases.deep.forEach(makeAsserter(assert.deepEqual));
+ testCases.deepUnordered.forEach(
+ makeAsserter(makeUnorderedDeepEqual(assert))
+ );
+
+ client
+ .testStruct(testCases.out)
+ .then(function(response) {
+ checkRecursively(testCases.out, response, "testStruct");
+ })
+ .catch(() => assert.fail("testStruct"));
+
+ client
+ .testNest(testCases.out2)
+ .then(function(response) {
+ checkRecursively(testCases.out2, response, "testNest");
+ })
+ .catch(() => assert.fail("testNest"));
+
+ client
+ .testInsanity(testCases.crazy)
+ .then(function(response) {
+ checkRecursively(testCases.insanity, response, "testInsanity");
+ })
+ .catch(() => assert.fail("testInsanity"));
+
+ client
+ .testInsanity(testCases.crazy2)
+ .then(function(response) {
+ checkRecursively(testCases.insanity, response, "testInsanity2");
+ })
+ .catch(() => assert.fail("testInsanity2"));
+
+ client
+ .testException("TException")
+ .then(function() {
+ assert.fail("testException: TException");
+ })
+ .catch(function(err) {
+ assert.ok(err instanceof TException);
+ });
+
+ client
+ .testException("Xception")
+ .then(function() {
+ assert.fail("testException: Xception");
+ })
+ .catch(function(err) {
+ assert.ok(err instanceof ttypes.Xception);
+ assert.equal(err.errorCode, 1001);
+ assert.equal("Xception", err.message);
+ });
+
+ client
+ .testException("no Exception")
+ .then(function(response) {
+ assert.equal(undefined, response); //void
+ })
+ .catch(() => assert.fail("testException"));
+
+ client
+ .testOneway(0)
+ .then(function(response) {
+ assert.strictEqual(response, undefined, "testOneway: void response");
+ })
+ .catch(() => assert.fail("testOneway: should not reject"));
+
+ checkOffByOne(function(done) {
+ client
+ .testI32(-1)
+ .then(function(response) {
+ assert.equal(-1, response);
+ assert.end();
+ done();
+ })
+ .catch(() => assert.fail("checkOffByOne"));
+ }, callback);
+ });
+};
+
+// Helper Functions
+// =========================================================
+
+function makeRecursiveCheck(assert) {
+ return function(map1, map2, msg) {
+ const equal = checkRecursively(map1, map2);
+
+ assert.ok(equal, msg);
+
+ // deepEqual doesn't work with fields using node-int64
+ function checkRecursively(map1, map2) {
+ if (typeof map1 !== "function" && typeof map2 !== "function") {
+ if (!map1 || typeof map1 !== "object") {
+ //Handle int64 types (which use node-int64 in Node.js JavaScript)
+ if (
+ typeof map1 === "number" &&
+ typeof map2 === "object" &&
+ map2.buffer &&
+ map2.buffer instanceof Buffer &&
+ map2.buffer.length === 8
+ ) {
+ const n = new Int64(map2.buffer);
+ return map1 === n.toNumber();
+ } else {
+ return map1 == map2;
+ }
+ } else {
+ return Object.keys(map1).every(function(key) {
+ return checkRecursively(map1[key], map2[key]);
+ });
+ }
+ }
+ }
+ };
+}
+
+function checkOffByOne(done, callback) {
+ const retry_limit = 30;
+ const retry_interval = 100;
+ let test_complete = false;
+ let retrys = 0;
+
+ /**
+ * redo a simple test after the oneway to make sure we aren't "off by one" --
+ * if the server treated oneway void like normal void, this next test will
+ * fail since it will get the void confirmation rather than the correct
+ * result. In this circumstance, the client will throw the exception:
+ *
+ * Because this is the last test against the server, when it completes
+ * the entire suite is complete by definition (the tests run serially).
+ */
+ done(function() {
+ test_complete = true;
+ });
+
+ //We wait up to retry_limit * retry_interval for the test suite to complete
+ function TestForCompletion() {
+ if (test_complete && callback) {
+ callback("Server successfully tested!");
+ } else {
+ if (++retrys < retry_limit) {
+ setTimeout(TestForCompletion, retry_interval);
+ } else if (callback) {
+ callback(
+ "Server test failed to complete after " +
+ (retry_limit * retry_interval) / 1000 +
+ " seconds"
+ );
+ }
+ }
+ }
+
+ setTimeout(TestForCompletion, retry_interval);
+}
+
+function makeUnorderedDeepEqual(assert) {
+ return function(actual, expected, name) {
+ assert.equal(actual.length, expected.length, name);
+ for (const k in actual) {
+ let found = false;
+ for (const k2 in expected) {
+ if (actual[k] === expected[k2]) {
+ found = true;
+ }
+ }
+ if (!found) {
+ assert.fail("Unexpected value " + actual[k] + " with key " + k);
+ }
+ }
+ };
+}
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/test_handler.js b/src/jaegertracing/thrift/lib/nodejs/test/test_handler.js
new file mode 100644
index 000000000..317a7c810
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/test_handler.js
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * 'License'); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+//This is the server side Node test handler for the standard
+// Apache Thrift test service.
+const helpers = require("./helpers");
+const ttypes = require(`./${helpers.genPath}/ThriftTest_types`);
+const TException = require("thrift").Thrift.TException;
+
+function makeSyncHandler() {
+ return function(thing) {
+ return thing;
+ };
+}
+
+const syncHandlers = {
+ testVoid: testVoid,
+ testMapMap: testMapMap,
+ testInsanity: testInsanity,
+ testMulti: testMulti,
+ testException: testException,
+ testMultiException: testMultiException,
+ testOneway: testOneway
+};
+
+function makeAsyncHandler(label) {
+ return function(thing, result) {
+ thing = syncHandlers[label](thing);
+ result(null, thing);
+ };
+}
+
+const asyncHandlers = {
+ testVoid: testVoidAsync,
+ testMulti: testMultiAsync,
+ testException: testExceptionAsync,
+ testMultiException: testMultiExceptionAsync,
+ testOneway: testOnewayAsync
+};
+
+const identityHandlers = [
+ "testString",
+ "testBool",
+ "testByte",
+ "testI32",
+ "testI64",
+ "testDouble",
+ "testBinary",
+ "testStruct",
+ "testNest",
+ "testMap",
+ "testStringMap",
+ "testSet",
+ "testList",
+ "testEnum",
+ "testTypedef"
+];
+
+function testVoid() {
+ //console.log('testVoid()');
+}
+
+function testVoidAsync(result) {
+ result(testVoid());
+}
+
+function testMapMap() {
+ const mapmap = [];
+ const pos = [];
+ const neg = [];
+ for (let i = 1; i < 5; i++) {
+ pos[i] = i;
+ neg[-i] = -i;
+ }
+ mapmap[4] = pos;
+ mapmap[-4] = neg;
+
+ return mapmap;
+}
+
+function testInsanity(argument) {
+ //console.log('testInsanity(');
+ //console.log(argument);
+ //console.log(')');
+
+ const first_map = [];
+ const second_map = [];
+
+ first_map[ttypes.Numberz.TWO] = argument;
+ first_map[ttypes.Numberz.THREE] = argument;
+
+ const looney = new ttypes.Insanity();
+ second_map[ttypes.Numberz.SIX] = looney;
+
+ const insane = [];
+ insane[1] = first_map;
+ insane[2] = second_map;
+
+ //console.log('insane result:');
+ //console.log(insane);
+ return insane;
+}
+
+function testMulti(arg0, arg1, arg2) {
+ //console.log('testMulti()');
+
+ const hello = new ttypes.Xtruct();
+ hello.string_thing = "Hello2";
+ hello.byte_thing = arg0;
+ hello.i32_thing = arg1;
+ hello.i64_thing = arg2;
+ return hello;
+}
+
+function testMultiAsync(arg0, arg1, arg2, arg3, arg4, arg5, result) {
+ const hello = testMulti(arg0, arg1, arg2, arg3, arg4, arg5);
+ result(null, hello);
+}
+
+function testException(arg) {
+ //console.log('testException('+arg+')');
+ if (arg === "Xception") {
+ const x = new ttypes.Xception();
+ x.errorCode = 1001;
+ x.message = arg;
+ throw x;
+ } else if (arg === "TException") {
+ throw new TException(arg);
+ } else {
+ return;
+ }
+}
+
+function testExceptionAsync(arg, result) {
+ //console.log('testException('+arg+')');
+ if (arg === "Xception") {
+ const x = new ttypes.Xception();
+ x.errorCode = 1001;
+ x.message = arg;
+ result(x);
+ } else if (arg === "TException") {
+ result(new TException(arg));
+ } else {
+ result(null);
+ }
+}
+
+function testMultiException(arg0, arg1) {
+ //console.log('testMultiException(' + arg0 + ', ' + arg1 + ')');
+ if (arg0 === "Xception") {
+ const x = new ttypes.Xception();
+ x.errorCode = 1001;
+ x.message = "This is an Xception";
+ throw x;
+ } else if (arg0 === "Xception2") {
+ const x2 = new ttypes.Xception2();
+ x2.errorCode = 2002;
+ x2.struct_thing = new ttypes.Xtruct();
+ x2.struct_thing.string_thing = "This is an Xception2";
+ throw x2;
+ }
+
+ const res = new ttypes.Xtruct();
+ res.string_thing = arg1;
+ return res;
+}
+
+function testMultiExceptionAsync(arg0, arg1, result) {
+ //console.log('testMultiException(' + arg0 + ', ' + arg1 + ')');
+ if (arg0 === "Xception") {
+ const x = new ttypes.Xception();
+ x.errorCode = 1001;
+ x.message = "This is an Xception";
+ result(x);
+ } else if (arg0 === "Xception2") {
+ const x2 = new ttypes.Xception2();
+ x2.errorCode = 2002;
+ x2.struct_thing = new ttypes.Xtruct();
+ x2.struct_thing.string_thing = "This is an Xception2";
+ result(x2);
+ } else {
+ const res = new ttypes.Xtruct();
+ res.string_thing = arg1;
+ result(null, res);
+ }
+}
+
+//console.log('testOneway(' + sleepFor + ') => JavaScript (like Rust) never sleeps!');
+function testOneway() {}
+
+function testOnewayAsync(sleepFor) {
+ testOneway(sleepFor);
+}
+
+identityHandlers.forEach(function(label) {
+ syncHandlers[label] = makeSyncHandler(label);
+ asyncHandlers[label] = makeAsyncHandler(label);
+});
+
+["testMapMap", "testInsanity"].forEach(function(label) {
+ asyncHandlers[label] = makeAsyncHandler(label);
+});
+
+exports.ThriftTestHandler = asyncHandlers;
diff --git a/src/jaegertracing/thrift/lib/nodejs/test/test_header_payload b/src/jaegertracing/thrift/lib/nodejs/test/test_header_payload
new file mode 100644
index 000000000..22d5ef7a2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodejs/test/test_header_payload
Binary files differ
diff --git a/src/jaegertracing/thrift/lib/nodets/.gitignore b/src/jaegertracing/thrift/lib/nodets/.gitignore
new file mode 100644
index 000000000..c7aba8924
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodets/.gitignore
@@ -0,0 +1 @@
+test-compiled/
diff --git a/src/jaegertracing/thrift/lib/nodets/Makefile.am b/src/jaegertracing/thrift/lib/nodets/Makefile.am
new file mode 100755
index 000000000..939dff2a5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodets/Makefile.am
@@ -0,0 +1,46 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# We call npm twice to work around npm issues
+
+stubs: $(top_srcdir)/test/ThriftTest.thrift
+ mkdir -p test-compiled
+ $(THRIFT) --gen js:node,ts -o test/ $(top_srcdir)/test/ThriftTest.thrift && $(THRIFT) --gen js:node,ts -o test-compiled $(top_srcdir)/test/ThriftTest.thrift
+ $(THRIFT) --gen js:node,ts -o test/ $(top_srcdir)/test/Int64Test.thrift && $(THRIFT) --gen js:node,ts -o test-compiled $(top_srcdir)/test/Int64Test.thrift
+
+ts-compile: stubs
+ mkdir -p test-compiled
+ ../../node_modules/typescript/bin/tsc --outDir test-compiled/ --project test/tsconfig.json
+
+deps: $(top_srcdir)/package.json
+ $(NPM) install $(top_srcdir)/ || $(NPM) install $(top_srcdir)/
+
+all-local: deps ts-compile
+
+precross: deps stubs ts-compile
+
+check: deps ts-compile
+ cd $(top_srcdir) && $(NPM) run test-ts && cd lib/nodets
+
+clean-local:
+ $(RM) -r test/gen-nodejs
+ $(RM) -r $(top_srcdir)/node_modules
+ $(RM) -r test-compiled
+
+EXTRA_DIST = \
+ test \
+ coding_standards.md
diff --git a/src/jaegertracing/thrift/lib/nodets/coding_standards.md b/src/jaegertracing/thrift/lib/nodets/coding_standards.md
new file mode 100644
index 000000000..fa0390bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodets/coding_standards.md
@@ -0,0 +1 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
diff --git a/src/jaegertracing/thrift/lib/nodets/test/client.ts b/src/jaegertracing/thrift/lib/nodets/test/client.ts
new file mode 100644
index 000000000..4fa3c2868
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodets/test/client.ts
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import assert = require("assert");
+import thrift = require("thrift");
+import Thrift = thrift.Thrift;
+import ThriftTest = require("./gen-nodejs/ThriftTest");
+import test_driver = require("./test_driver");
+import ThriftTestDriver = test_driver.ThriftTestDriver;
+import ThriftTestDriverPromise = test_driver.ThriftTestDriverPromise;
+
+// var program = require("commander");
+import * as program from "commander";
+
+program
+ .option("--port <port>", "Set thrift server port number to connect", 9090)
+ .option("--promise", "test with promise style functions")
+ .option("--protocol", "Set thrift protocol (binary) [protocol]")
+ .parse(process.argv);
+
+var port: number = program.port;
+var promise = program.promise;
+
+var options = {
+ transport: Thrift.TBufferedTransport,
+ protocol: Thrift.TBinaryProtocol
+};
+
+var testDriver = promise ? ThriftTestDriverPromise : ThriftTestDriver;
+
+var connection = thrift.createConnection("localhost", port, options);
+
+connection.on("error", function(err: string) {
+ assert(false, err);
+});
+
+var client = thrift.createClient(ThriftTest.Client, connection);
+runTests();
+
+function runTests() {
+ testDriver(client, function (status: string) {
+ console.log(status);
+ process.exit(0);
+ });
+}
+
+exports.expressoTest = function() {};
diff --git a/src/jaegertracing/thrift/lib/nodets/test/int64.test.ts b/src/jaegertracing/thrift/lib/nodets/test/int64.test.ts
new file mode 100644
index 000000000..d209234b5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodets/test/int64.test.ts
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import Int64 = require("node-int64");
+import JSONInt64 = require('json-int64');
+import i64types = require("./gen-nodejs/Int64Test_types");
+import test = require("tape");
+
+const cases = {
+ "should correctly generate Int64 constants": function(assert) {
+ const EXPECTED_SMALL_INT64_AS_NUMBER: number = 42;
+ const EXPECTED_SMALL_INT64: Int64 = new Int64(42);
+ const EXPECTED_MAX_JS_SAFE_INT64: Int64 = new Int64(Number.MAX_SAFE_INTEGER);
+ const EXPECTED_MIN_JS_SAFE_INT64: Int64 = new Int64(Number.MIN_SAFE_INTEGER);
+ const EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64: Int64 = new Int64("0020000000000000"); // hex-encoded
+ const EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64: Int64 = new Int64("ffe0000000000000"); // hex-encoded 2's complement
+ const EXPECTED_MAX_SIGNED_INT64: Int64 = new Int64("7fffffffffffffff"); // hex-encoded
+ const EXPECTED_MIN_SIGNED_INT64: Int64 = new Int64("8000000000000000"); // hex-encoded 2's complement
+ const EXPECTED_INT64_LIST: Int64[] = [
+ EXPECTED_SMALL_INT64,
+ EXPECTED_MAX_JS_SAFE_INT64,
+ EXPECTED_MIN_JS_SAFE_INT64,
+ EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64,
+ EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64,
+ EXPECTED_MAX_SIGNED_INT64,
+ EXPECTED_MIN_SIGNED_INT64
+ ];
+
+ assert.ok(EXPECTED_SMALL_INT64.equals(i64types.SMALL_INT64));
+ assert.ok(EXPECTED_MAX_JS_SAFE_INT64.equals(i64types.MAX_JS_SAFE_INT64));
+ assert.ok(EXPECTED_MIN_JS_SAFE_INT64.equals(i64types.MIN_JS_SAFE_INT64));
+ assert.ok(
+ EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64.equals(
+ i64types.MAX_JS_SAFE_PLUS_ONE_INT64
+ )
+ );
+ assert.ok(
+ EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64.equals(
+ i64types.MIN_JS_SAFE_MINUS_ONE_INT64
+ )
+ );
+ assert.ok(EXPECTED_MAX_SIGNED_INT64.equals(i64types.MAX_SIGNED_INT64));
+ assert.ok(EXPECTED_MIN_SIGNED_INT64.equals(i64types.MIN_SIGNED_INT64));
+ assert.equal(
+ EXPECTED_SMALL_INT64_AS_NUMBER,
+ i64types.SMALL_INT64.toNumber()
+ );
+ assert.equal(
+ Number.MAX_SAFE_INTEGER,
+ i64types.MAX_JS_SAFE_INT64.toNumber()
+ );
+ assert.equal(
+ Number.MIN_SAFE_INTEGER,
+ i64types.MIN_JS_SAFE_INT64.toNumber()
+ );
+
+ for (let i = 0; i < EXPECTED_INT64_LIST.length; ++i) {
+ assert.ok(EXPECTED_INT64_LIST[i].equals(i64types.INT64_LIST[i]));
+ }
+
+ for (let i = 0; i < EXPECTED_INT64_LIST.length; ++i){
+ let int64Object = EXPECTED_INT64_LIST[i];
+ assert.ok(i64types.INT64_2_INT64_MAP[JSONInt64.toDecimalString(int64Object)].equals(int64Object));
+ }
+
+ assert.end();
+ }
+};
+
+Object.keys(cases).forEach(function(caseName) {
+ test(caseName, cases[caseName]);
+});
diff --git a/src/jaegertracing/thrift/lib/nodets/test/runClient.sh b/src/jaegertracing/thrift/lib/nodets/test/runClient.sh
new file mode 100755
index 000000000..8d5e9a33f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodets/test/runClient.sh
@@ -0,0 +1,18 @@
+#! /bin/sh
+
+DIR="$( cd "$( dirname "$0" )" && pwd )"
+
+mkdir -p $DIR/../test-compiled
+
+COMPILEDDIR="$(cd $DIR && cd ../test-compiled && pwd)"
+export NODE_PATH="${DIR}:${DIR}/../../nodejs/lib:${NODE_PATH}"
+
+compile()
+{
+ #generating thrift code
+ ${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node,ts ${DIR}/../../../test/ThriftTest.thrift
+ ${DIR}/../../../compiler/cpp/thrift -o ${COMPILEDDIR} --gen js:node,ts ${DIR}/../../../test/ThriftTest.thrift
+}
+compile
+
+node ${COMPILEDDIR}/client.js $*
diff --git a/src/jaegertracing/thrift/lib/nodets/test/runServer.sh b/src/jaegertracing/thrift/lib/nodets/test/runServer.sh
new file mode 100755
index 000000000..4eee92717
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodets/test/runServer.sh
@@ -0,0 +1,20 @@
+#! /bin/sh
+
+DIR="$( cd "$( dirname "$0" )" && pwd )"
+
+mkdir -p $DIR/../test-compiled
+
+COMPILEDDIR="$(cd $DIR && cd ../test-compiled && pwd)"
+export NODE_PATH="${DIR}:${DIR}/../../nodejs/lib:${NODE_PATH}"
+
+compile()
+{
+ #generating thrift code
+ ${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node,ts ${DIR}/../../../test/ThriftTest.thrift
+ ${DIR}/../../../compiler/cpp/thrift -o ${COMPILEDDIR} --gen js:node,ts ${DIR}/../../../test/ThriftTest.thrift
+}
+compile
+
+node ${COMPILEDDIR}/server.js $*
+
+
diff --git a/src/jaegertracing/thrift/lib/nodets/test/server.ts b/src/jaegertracing/thrift/lib/nodets/test/server.ts
new file mode 100644
index 000000000..2da53aee2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodets/test/server.ts
@@ -0,0 +1,26 @@
+import thrift = require("thrift");
+var program = require('commander');
+import ThriftTest = require('./gen-nodejs/ThriftTest');
+import test_handler = require('./test_handler');
+
+
+program
+ .option('--port <port>', 'Set thrift server port', 9090)
+ .option('--promise', 'test with promise style functions')
+ .option('--protocol', '"Set thrift protocol (binary) [protocol]"')
+ .parse(process.argv);
+
+var port: number = program.port;
+
+var options: thrift.ServerOptions = {
+ transport: thrift.TBufferedTransport,
+ protocol: thrift.TBinaryProtocol
+};
+
+var server: thrift.Server;
+if (program.promise) {
+ server = thrift.createServer(ThriftTest.Processor, new test_handler.AsyncThriftTestHandler(), options);
+} else {
+ server = thrift.createServer(ThriftTest.Processor, new test_handler.SyncThriftTestHandler(), options);
+}
+server.listen(port);
diff --git a/src/jaegertracing/thrift/lib/nodets/test/test-cases.ts b/src/jaegertracing/thrift/lib/nodets/test/test-cases.ts
new file mode 100644
index 000000000..44f254e92
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodets/test/test-cases.ts
@@ -0,0 +1,114 @@
+'use strict';
+
+import ttypes = require('./gen-nodejs/ThriftTest_types');
+import Int64 = require('node-int64');
+
+//all Languages in UTF-8
+/*jshint -W100 */
+export var stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, " +
+ "Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, " +
+ "БеларуÑкаÑ, БеларуÑÐºÐ°Ñ (тарашкевіца), БългарÑки, Bamanankan, " +
+ "বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Ðохчийн, " +
+ "Cebuano, á£áŽ³áŽ©, ÄŒesky, СловѣÌньÑкъ / ⰔⰎⰑⰂⰡâ°â° â°”â°â°Ÿ, Чӑвашла, Cymraeg, " +
+ "Dansk, Zazaki, Þ‹Þ¨ÞˆÞ¬Þ€Þ¨Þ„Þ¦ÞÞ°, Ελληνικά, Emiliàn e rumagnòl, English, " +
+ "Esperanto, Español, Eesti, Euskara, Ùارسی, Suomi, Võro, Føroyskt, " +
+ "Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, " +
+ "Avañe'ẽ, ગà«àªœàª°àª¾àª¤à«€, Gaelg, עברית, हिनà¥à¤¦à¥€, Fiji Hindi, Hrvatski, " +
+ "Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia, " +
+ "Ilokano, Ido, Ãslenska, Italiano, 日本語, Lojban, Basa Jawa, " +
+ "ქáƒáƒ áƒ—ული, Kongo, Kalaallisut, ಕನà³à²¨à²¡, 한국어, Къарачай-Малкъар, " +
+ "Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, " +
+ "Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa " +
+ "Banyumasan, Malagasy, МакедонÑки, മലയാളം, मराठी, مازÙرونی, Bahasa " +
+ "Melayu, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪" +
+ "Norsk (nynorsk)‬, ‪Norsk (bokmål)‬, Nouormand, Diné bizaad, " +
+ "Occitan, Иронау, Papiamentu, Deitsch, Polski, پنجابی, پښتو, " +
+ "Norfuk / Pitkern, Português, Runa Simi, Rumantsch, Romani, Română, " +
+ "РуÑÑкий, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple " +
+ "English, SlovenÄina, SlovenÅ¡Äina, СрпÑки / Srpski, Seeltersk, " +
+ "Svenska, Kiswahili, தமிழà¯, తెలà±à°—à±, Тоҷикӣ, ไทย, Türkmençe, Tagalog, " +
+ "Türkçe, Татарча/Tatarça, УкраїнÑька, اردو, Tiếng Việt, Volapük, " +
+ "Walon, Winaray, å´è¯­, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, " +
+ "Bân-lâm-gú, 粵語";
+/*jshint +W100 */
+
+export var specialCharacters = 'quote: \" backslash:' +
+ ' forwardslash-escaped: \/ ' +
+ ' backspace: \b formfeed: \f newline: \n return: \r tab: ' +
+ ' now-all-of-them-together: "\\\/\b\n\r\t' +
+ ' now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><' +
+ ' char-to-test-json-parsing: ]] \"]] \\" }}}{ [[[ ';
+
+export var mapTestInput = {
+ "a":"123", "a b":"with spaces ", "same":"same", "0":"numeric key",
+ "longValue":stringTest, stringTest:"long key"
+};
+
+export var simple = [
+ ['testVoid', undefined],
+ ['testString', 'Test'],
+ ['testString', ''],
+ ['testString', stringTest],
+ ['testString', specialCharacters],
+ ['testByte', 1],
+ ['testByte', 0],
+ ['testByte', -1],
+ ['testByte', -127],
+ ['testI32', -1],
+ ['testDouble', -5.2098523],
+ ['testDouble', 7.012052175215044],
+ ['testEnum', ttypes.Numberz.ONE]
+];
+
+export var simpleLoose = [
+ ['testI64', 5],
+ ['testI64', -5],
+ ['testI64', 734359738368],
+ ['testI64', -34359738368],
+ ['testI64', -734359738368],
+ ['testTypedef', 69]
+]
+
+var mapout: {[key: number]: number; } = {};
+for (var i = 0; i < 5; ++i) {
+ mapout[i] = i-10;
+}
+
+export var deep = [
+ ['testMap', mapout],
+ ['testSet', [1,2,3]],
+ ['testList', [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]],
+ ['testStringMap', mapTestInput]
+];
+
+export var out = new ttypes.Xtruct({
+ string_thing: 'Zero',
+ byte_thing: 1,
+ i32_thing: -3,
+ i64_thing: new Int64(1000000)
+});
+
+export var out2 = new ttypes.Xtruct2();
+out2.byte_thing = 1;
+out2.struct_thing = out;
+out2.i32_thing = 5;
+
+export var crazy = new ttypes.Insanity({
+ "userMap":{ "5":new Int64(5), "8":new Int64(8) },
+ "xtructs":[new ttypes.Xtruct({
+ "string_thing":"Goodbye4",
+ "byte_thing":4,
+ "i32_thing":4,
+ "i64_thing":new Int64(4)
+ }), new ttypes.Xtruct({
+ "string_thing":"Hello2",
+ "byte_thing":2,
+ "i32_thing":2,
+ "i64_thing":new Int64(2)
+ })]
+});
+
+export var insanity: any = {
+ "1":{ "2": crazy, "3": crazy },
+ "2":{ "6":{ "userMap":{}, "xtructs":[] } }
+};
diff --git a/src/jaegertracing/thrift/lib/nodets/test/testAll.sh b/src/jaegertracing/thrift/lib/nodets/test/testAll.sh
new file mode 100755
index 000000000..3be12c362
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodets/test/testAll.sh
@@ -0,0 +1,42 @@
+#! /bin/sh
+
+DIR="$( cd "$( dirname "$0" )" && pwd )"
+
+mkdir -p $DIR/../test-compiled
+
+COMPILEDDIR="$(cd $DIR && cd ../test-compiled && pwd)"
+export NODE_PATH="${DIR}:${DIR}/../../nodejs/lib:${NODE_PATH}"
+
+compile()
+{
+ #generating thrift code
+ ${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node,ts ${DIR}/../../../test/ThriftTest.thrift
+ ${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node,ts ${DIR}/../../../test/Int64Test.thrift
+ ${DIR}/../../../compiler/cpp/thrift -o ${COMPILEDDIR} --gen js:node,ts ${DIR}/../../../test/ThriftTest.thrift
+ ${DIR}/../../../compiler/cpp/thrift -o ${COMPILEDDIR} --gen js:node,ts ${DIR}/../../../test/Int64Test.thrift
+
+ tsc --outDir $COMPILEDDIR --project $DIR/tsconfig.json
+}
+compile
+
+testServer()
+{
+ echo "start server $1"
+ RET=0
+ node ${COMPILEDDIR}/server.js $1 &
+ SERVERPID=$!
+ sleep 1
+ echo "start client $1"
+ node ${COMPILEDDIR}/client.js $1 || RET=1
+ kill -2 $SERVERPID || RET=1
+ return $RET
+}
+
+node ${COMPILEDDIR}/int64.test.js || TESTOK=1
+
+#integration tests
+
+testServer || TESTOK=1
+testServer --promise || TESTOK=1
+
+exit $TESTOK
diff --git a/src/jaegertracing/thrift/lib/nodets/test/test_driver.ts b/src/jaegertracing/thrift/lib/nodets/test/test_driver.ts
new file mode 100644
index 000000000..2c4152616
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodets/test/test_driver.ts
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * 'License'); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+ // This is the Node.js test driver for the standard Apache Thrift
+ // test service. The driver invokes every function defined in the
+ // Thrift Test service with a representative range of parameters.
+ //
+ // The ThriftTestDriver function requires a client object
+ // connected to a server hosting the Thrift Test service and
+ // supports an optional callback function which is called with
+ // a status message when the test is complete.
+
+import test = require("tape");
+import ttypes = require("./gen-nodejs/ThriftTest_types");
+import ThriftTest = require("./gen-nodejs/ThriftTest");
+import thrift = require("thrift");
+import Q = thrift.Q;
+import TException = thrift.Thrift.TException;
+var Int64 = require("node-int64");
+import testCases = require("./test-cases");
+
+export function ThriftTestDriver(client: ThriftTest.Client, callback: (status: string) => void) {
+
+ test("NodeJS Style Callback Client Tests", function(assert) {
+
+ var checkRecursively = makeRecursiveCheck(assert);
+
+ function makeAsserter(assertionFn: (a: any, b: any, msg?: string) => void) {
+ return function(c: (string | any)[]) {
+ var fnName = c[0];
+ var expected = c[1];
+ (<any>client)[fnName](expected, function(err: any, actual: any) {
+ assert.error(err, fnName + ": no callback error");
+ assertionFn(actual, expected, fnName);
+ })
+ };
+ }
+
+ testCases.simple.forEach(makeAsserter(assert.equal));
+ testCases.simpleLoose.forEach(makeAsserter(function(a, e, m){
+ assert.ok(a == e, m);
+ }));
+ testCases.deep.forEach(makeAsserter(assert.deepEqual));
+
+ client.testMapMap(42, function(err, response) {
+ var expected: typeof response = {
+ "4": {"1":1, "2":2, "3":3, "4":4},
+ "-4": {"-4":-4, "-3":-3, "-2":-2, "-1":-1}
+ };
+ assert.error(err, 'testMapMap: no callback error');
+ assert.deepEqual(expected, response, "testMapMap");
+ });
+
+ client.testStruct(testCases.out, function(err, response) {
+ assert.error(err, "testStruct: no callback error");
+ checkRecursively(testCases.out, response, "testStruct");
+ });
+
+ client.testNest(testCases.out2, function(err, response) {
+ assert.error(err, "testNest: no callback error");
+ checkRecursively(testCases.out2, response, "testNest");
+ });
+
+ client.testInsanity(testCases.crazy, function(err, response) {
+ assert.error(err, "testInsanity: no callback error");
+ checkRecursively(testCases.insanity, response, "testInsanity");
+ });
+
+ client.testException("TException", function(err, response) {
+ assert.ok(err instanceof TException, 'testException: correct error type');
+ assert.ok(!Boolean(response), 'testException: no response');
+ });
+
+ client.testException("Xception", function(err, response) {
+ assert.ok(err instanceof ttypes.Xception, 'testException: correct error type');
+ assert.ok(!Boolean(response), 'testException: no response');
+ assert.equal(err.errorCode, 1001, 'testException: correct error code');
+ assert.equal('Xception', err.message, 'testException: correct error message');
+ });
+
+ client.testException("no Exception", function(err, response) {
+ assert.error(err, 'testException: no callback error');
+ assert.ok(!Boolean(response), 'testException: no response');
+ });
+
+ client.testOneway(0, function(err, response) {
+ assert.error(err, 'testOneway: no callback error');
+ assert.strictEqual(response, undefined, 'testOneway: void response');
+ });
+
+ checkOffByOne(function(done) {
+ client.testI32(-1, function(err, response) {
+ assert.error(err, "checkOffByOne: no callback error");
+ assert.equal(-1, response);
+ assert.end();
+ done();
+ });
+ }, callback);
+
+ });
+};
+
+export function ThriftTestDriverPromise(client: ThriftTest.Client, callback: (status: string) => void) {
+
+ test("Q Promise Client Tests", function(assert) {
+
+ var checkRecursively = makeRecursiveCheck(assert);
+
+ function fail(msg: string) {
+ return function(error, response) {
+ if (error !== null) {
+ assert.fail(msg);
+ }
+ }
+ }
+
+ function makeAsserter(assertionFn: (a: any, b: any, msg?: string) => void) {
+ return function(c: (string | any)[]) {
+ var fnName = c[0];
+ var expected = c[1];
+ (<any>client)[fnName](expected)
+ .then(function(actual: any) {
+ assertionFn(actual, expected, fnName);
+ })
+ .fail(fail("fnName"));
+ };
+ }
+
+ testCases.simple.forEach(makeAsserter(assert.equal));
+ testCases.simpleLoose.forEach(makeAsserter(function(a, e, m){
+ assert.ok(a == e, m);
+ }));
+ testCases.deep.forEach(makeAsserter(assert.deepEqual));
+
+ Q.resolve(client.testStruct(testCases.out))
+ .then(function(response) {
+ checkRecursively(testCases.out, response, "testStruct");
+ })
+ .fail(fail("testStruct"));
+
+ Q.resolve(client.testNest(testCases.out2))
+ .then(function(response) {
+ checkRecursively(testCases.out2, response, "testNest");
+ })
+ .fail(fail("testNest"));
+
+ Q.resolve(client.testInsanity(testCases.crazy))
+ .then(function(response) {
+ checkRecursively(testCases.insanity, response, "testInsanity");
+ })
+ .fail(fail("testInsanity"));
+
+ Q.resolve(client.testException("TException"))
+ .then(function(response) {
+ fail("testException: TException");
+ })
+ .fail(function(err) {
+ assert.ok(err instanceof TException);
+ });
+
+ Q.resolve(client.testException("Xception"))
+ .then(function(response) {
+ fail("testException: Xception");
+ })
+ .fail(function(err) {
+ assert.ok(err instanceof ttypes.Xception);
+ assert.equal(err.errorCode, 1001);
+ assert.equal("Xception", err.message);
+ });
+
+ Q.resolve(client.testException("no Exception"))
+ .then(function(response) {
+ assert.equal(undefined, response); //void
+ })
+ .fail(fail("testException"));
+
+ client.testOneway(0, fail("testOneway: should not answer"));
+
+ checkOffByOne(function(done) {
+ Q.resolve(client.testI32(-1))
+ .then(function(response) {
+ assert.equal(-1, response);
+ assert.end();
+ done();
+ })
+ .fail(fail("checkOffByOne"));
+ }, callback);
+ });
+};
+
+
+// Helper Functions
+// =========================================================
+
+function makeRecursiveCheck(assert: test.Test) {
+
+ return function (map1: any, map2: any, msg: string) {
+ var equal = true;
+
+ var equal = checkRecursively(map1, map2);
+
+ assert.ok(equal, msg);
+
+ // deepEqual doesn't work with fields using node-int64
+ function checkRecursively(map1: any, map2: any) : boolean {
+ if (!(typeof map1 !== "function" && typeof map2 !== "function")) {
+ return false;
+ }
+ if (!map1 || typeof map1 !== "object") {
+ //Handle int64 types (which use node-int64 in Node.js JavaScript)
+ if ((typeof map1 === "number") && (typeof map2 === "object") &&
+ (map2.buffer) && (map2.buffer instanceof Buffer) && (map2.buffer.length === 8)) {
+ var n = new Int64(map2.buffer);
+ return map1 === n.toNumber();
+ } else {
+ return map1 == map2;
+ }
+ } else {
+ return Object.keys(map1).every(function(key) {
+ return checkRecursively(map1[key], map2[key]);
+ });
+ }
+ }
+ }
+}
+
+function checkOffByOne(done: (callback: () => void) => void, callback: (message: string) => void) {
+
+ var retry_limit = 30;
+ var retry_interval = 100;
+ var test_complete = false;
+ var retrys = 0;
+
+ /**
+ * redo a simple test after the oneway to make sure we aren't "off by one" --
+ * if the server treated oneway void like normal void, this next test will
+ * fail since it will get the void confirmation rather than the correct
+ * result. In this circumstance, the client will throw the exception:
+ *
+ * Because this is the last test against the server, when it completes
+ * the entire suite is complete by definition (the tests run serially).
+ */
+ done(function() {
+ test_complete = true;
+ });
+
+ //We wait up to retry_limit * retry_interval for the test suite to complete
+ function TestForCompletion() {
+ if(test_complete && callback) {
+ callback("Server successfully tested!");
+ } else {
+ if (++retrys < retry_limit) {
+ setTimeout(TestForCompletion, retry_interval);
+ } else if (callback) {
+ callback("Server test failed to complete after " +
+ (retry_limit * retry_interval / 1000) + " seconds");
+ }
+ }
+ }
+
+ setTimeout(TestForCompletion, retry_interval);
+}
diff --git a/src/jaegertracing/thrift/lib/nodets/test/test_handler.ts b/src/jaegertracing/thrift/lib/nodets/test/test_handler.ts
new file mode 100644
index 000000000..996c32a5a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodets/test/test_handler.ts
@@ -0,0 +1,300 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * 'License'); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+//This is the server side Node test handler for the standard
+// Apache Thrift test service.
+
+import ttypes = require("./gen-nodejs/ThriftTest_types");
+import thrift = require("thrift");
+import Thrift = thrift.Thrift;
+import Q = require("q");
+import Int64 = require("node-int64");
+
+
+export class SyncThriftTestHandler {
+ testVoid(): Q.IPromise<void> {
+ //console.log('testVoid()');
+ return Q.resolve<void>(undefined);
+ }
+ testMapMap(hello: number) {
+ //console.log('testMapMap(' + hello + ')');
+
+ var mapmap: {[key: number]: {[key: number]: number; }} = [];
+ var pos: {[key: number]: number; } = [];
+ var neg: {[key: number]: number; } = [];
+ for (var i = 1; i < 5; i++) {
+ pos[i] = i;
+ neg[-i] = -i;
+ }
+ mapmap[4] = pos;
+ mapmap[-4] = neg;
+
+ return Q.resolve(mapmap);
+ }
+ testInsanity(argument: ttypes.Insanity): Q.IPromise<{ [k: number]: any; }> {
+ const first_map: { [k: number]: any; } = [];
+ const second_map: { [k: number]: any; } = [];
+
+ first_map[ttypes.Numberz.TWO] = argument;
+ first_map[ttypes.Numberz.THREE] = argument;
+
+ const looney = new ttypes.Insanity();
+ second_map[ttypes.Numberz.SIX] = looney;
+
+ const insane: { [k: number]: any; } = [];
+ insane[1] = first_map;
+ insane[2] = second_map;
+
+ return Q.resolve(insane);
+ }
+ testMulti(arg0: any, arg1: number, arg2: Int64, arg3: { [k: number]: string; }, arg4: ttypes.Numberz, arg5: number) {
+ var hello = new ttypes.Xtruct();
+ hello.string_thing = 'Hello2';
+ hello.byte_thing = arg0;
+ hello.i32_thing = arg1;
+ hello.i64_thing = arg2;
+ return Q.resolve(hello);
+ }
+ testException(arg: string): Q.IPromise<void> {
+ if (arg === 'Xception') {
+ var x = new ttypes.Xception();
+ x.errorCode = 1001;
+ x.message = arg;
+ throw x;
+ } else if (arg === 'TException') {
+ throw new Thrift.TException(arg);
+ } else {
+ return Q.resolve();
+ }
+ }
+ testMultiException(arg0: string, arg1: string) {
+ if (arg0 === ('Xception')) {
+ var x = new ttypes.Xception();
+ x.errorCode = 1001;
+ x.message = 'This is an Xception';
+ throw x;
+ } else if (arg0 === ('Xception2')) {
+ var x2 = new ttypes.Xception2();
+ x2.errorCode = 2002;
+ x2.struct_thing = new ttypes.Xtruct();
+ x2.struct_thing.string_thing = 'This is an Xception2';
+ throw x2;
+ }
+
+ var res = new ttypes.Xtruct();
+ res.string_thing = arg1;
+ return Q.resolve(res);
+ }
+ testOneway(sleepFor: number) {
+ }
+
+ testString(thing: string) {
+ return Q.resolve(thing);
+ }
+ testBool(thing: boolean) {
+ return Q.resolve(thing);
+ }
+ testByte(thing: number) {
+ return Q.resolve(thing);
+ }
+ testI32(thing: number) {
+ return Q.resolve(thing);
+ }
+ testI64(thing: number) {
+ return Q.resolve(thing);
+ }
+ testDouble(thing: number) {
+ return Q.resolve(thing);
+ }
+ testBinary(thing: Buffer) {
+ return Q.resolve(thing);
+ }
+ testStruct(thing: ttypes.Xtruct) {
+ return Q.resolve(thing);
+ }
+ testNest(thing: ttypes.Xtruct2) {
+ return Q.resolve(thing);
+ }
+ testMap(thing: { [k: number]: number; }) {
+ return Q.resolve(thing);
+ }
+ testStringMap(thing: { [k: string]: string; }) {
+ return Q.resolve(thing);
+ }
+ testSet(thing: number[]) {
+ return Q.resolve(thing);
+ }
+ testList(thing: number[]) {
+ return Q.resolve(thing);
+ }
+ testEnum(thing: ttypes.Numberz) {
+ return Q.resolve(thing);
+ }
+ testTypedef(thing: number) {
+ return Q.resolve(thing);
+ }
+}
+
+export class AsyncThriftTestHandler {
+ private syncHandler: SyncThriftTestHandler;
+ constructor() {
+ this.syncHandler = new SyncThriftTestHandler();
+ }
+
+ testVoid(callback: (result: void) => void): Q.IPromise<void> {
+ callback(undefined);
+ return Q.resolve();
+ }
+ testMapMap(hello: number,
+ callback: (err: any, result: { [k: number]: { [k: number]: number; }; }) => void):
+ Q.IPromise<{ [k: number]: { [k: number]: number; }; }> {
+
+ var mapmap: {[key: number]: {[key: number]: number; }} = [];
+ var pos: {[key: number]: number; } = [];
+ var neg: {[key: number]: number; } = [];
+ for (var i = 1; i < 5; i++) {
+ pos[i] = i;
+ neg[-i] = -i;
+ }
+ mapmap[4] = pos;
+ mapmap[-4] = neg;
+
+ callback(null, mapmap);
+ return Q.resolve();
+ }
+ testInsanity(argument: ttypes.Insanity, callback?: (err: any, result: { [k: number]: any; }) => void): Q.IPromise<{ [k: number]: any; }> {
+ const first_map: { [k: number]: any; } = [];
+ const second_map: { [k: number]: any; } = [];
+
+ first_map[ttypes.Numberz.TWO] = argument;
+ first_map[ttypes.Numberz.THREE] = argument;
+
+ const looney = new ttypes.Insanity();
+ second_map[ttypes.Numberz.SIX] = looney;
+
+ const insane: { [k: number]: any; } = [];
+ insane[1] = first_map;
+ insane[2] = second_map;
+
+ if (callback !== undefined){
+ callback(null, insane);
+ }
+ return Q.resolve();
+ }
+ testMulti(arg0: any, arg1: number, arg2: Int64, arg3: { [k: number]: string; }, arg4: ttypes.Numberz, arg5: number, result: Function): Q.IPromise<ttypes.Xtruct> {
+ var hello = this.syncHandler.testMulti(arg0, arg1, arg2, arg3, arg4, arg5);
+ hello.then(hello => result(null, hello));
+ return Q.resolve();
+ }
+ testException(arg: string, result: (err: any) => void): Q.IPromise<void> {
+ if (arg === 'Xception') {
+ var x = new ttypes.Xception();
+ x.errorCode = 1001;
+ x.message = arg;
+ result(x);
+ } else if (arg === 'TException') {
+ result(new Thrift.TException(arg));
+ } else {
+ result(null);
+ }
+ return Q.resolve();
+ }
+ testMultiException(arg0: string, arg1: string, result: (err: any, res?: ttypes.Xtruct) => void): Q.IPromise<ttypes.Xtruct> {
+ if (arg0 === ('Xception')) {
+ var x = new ttypes.Xception();
+ x.errorCode = 1001;
+ x.message = 'This is an Xception';
+ result(x);
+ } else if (arg0 === ('Xception2')) {
+ var x2 = new ttypes.Xception2();
+ x2.errorCode = 2002;
+ x2.struct_thing = new ttypes.Xtruct();
+ x2.struct_thing.string_thing = 'This is an Xception2';
+ result(x2);
+ } else {
+ var res = new ttypes.Xtruct();
+ res.string_thing = arg1;
+ result(null, res);
+ }
+ return Q.resolve();
+ }
+ testOneway(sleepFor: number, result: Function) {
+ this.syncHandler.testOneway(sleepFor);
+ }
+ testString(thing: string, callback: (err: any, result: string) => void): Q.IPromise<string> {
+ callback(null, thing);
+ return Q.resolve();
+ }
+ testByte(thing: number, callback: (err: any, result: number) => void): Q.IPromise<number> {
+ callback(null, thing);
+ return Q.resolve();
+ }
+ testBool(thing: boolean, callback: (err: any, result: boolean) => void ): Q.IPromise<boolean> {
+ callback(null, thing);
+ return Q.resolve();
+ }
+ testI32(thing: number, callback: (err: any, result: number) => void): Q.IPromise<number> {
+ callback(null, thing);
+ return Q.resolve();
+ }
+ testI64(thing: number, callback: (err: any, result: number) => void): Q.IPromise<number> {
+ callback(null, thing);
+ return Q.resolve();
+ }
+ testDouble(thing: number, callback: (err: any, result: number) => void): Q.IPromise<number> {
+ callback(null, thing);
+ return Q.resolve();
+ }
+ testBinary(thing: Buffer, callback: (err: any, result: Buffer) => void): Q.IPromise<Buffer> {
+ callback(null, thing);
+ return Q.resolve();
+ }
+ testStruct(thing: ttypes.Xtruct, callback: (err: any, result: ttypes.Xtruct) => void): Q.IPromise<ttypes.Xtruct> {
+ callback(null, thing);
+ return Q.resolve();
+ }
+ testNest(thing: ttypes.Xtruct2, callback: (err: any, result: ttypes.Xtruct2) => void): Q.IPromise<ttypes.Xtruct2> {
+ callback(null, thing);
+ return Q.resolve();
+ }
+ testMap(thing: { [k: number]: number; }, callback: (err: any, result: { [k: number]: number; }) => void): Q.IPromise<{ [k: number]: number; }> {
+ callback(null, thing);
+ return Q.resolve();
+ }
+ testStringMap(thing: { [k: string]: string; }, callback: (err: any, result: { [k: string]: string; }) => void): Q.IPromise<{ [k: string]: string; }> {
+ callback(null, thing);
+ return Q.resolve();
+ }
+ testSet(thing: number[], callback: (err: any, result: number[]) => void): Q.IPromise<number[]> {
+ callback(null, thing);
+ return Q.resolve();
+ }
+ testList(thing: number[], callback: (err: any, result: number[]) => void): Q.IPromise<number[]> {
+ callback(null, thing);
+ return Q.resolve();
+ }
+ testEnum(thing: ttypes.Numberz, callback: (err: any, result: ttypes.Numberz) => void): Q.IPromise<ttypes.Numberz> {
+ callback(null, thing);
+ return Q.resolve();
+ }
+ testTypedef(thing: number, callback: (err: any, result: number) => void): Q.IPromise<number> {
+ callback(null, thing);
+ return Q.resolve();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/nodets/test/tsconfig.json b/src/jaegertracing/thrift/lib/nodets/test/tsconfig.json
new file mode 100644
index 000000000..029d06d96
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/nodets/test/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "allowJs": false,
+ "alwaysStrict": true,
+ "baseUrl": ".",
+ "declaration": true,
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "noImplicitThis": true,
+ "noUnusedLocals": true,
+ "preserveConstEnums": true,
+ "removeComments": true,
+ "strictFunctionTypes": true,
+ "strictNullChecks": true,
+ "target": "es6",
+ "paths": {
+ "thrift": ["../../nodejs/lib/thrift"]
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/ocaml/.gitignore b/src/jaegertracing/thrift/lib/ocaml/.gitignore
new file mode 100644
index 000000000..0d9a6af46
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/.gitignore
@@ -0,0 +1,11 @@
+_build/
+_tags
+configure
+setup.data
+setup.ml
+myocamlbuild.ml
+*/META
+*/*.mllib
+*/*.mldylib
+Makefile
+OCamlMakefile
diff --git a/src/jaegertracing/thrift/lib/ocaml/DEVELOPMENT b/src/jaegertracing/thrift/lib/ocaml/DEVELOPMENT
new file mode 100644
index 000000000..3d5a03c24
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/DEVELOPMENT
@@ -0,0 +1,76 @@
+Thrift OCaml Development
+========================
+
+Prerequisites
+-------------
+
+In order to build this library, you must have the following installed:
+
+ * The OCaml compiler, preferably >4.00
+ * The Oasis build tool
+
+In addition you may want to install OPAM, which will allow you to setup an
+OCaml development environment that's isolated from your system installation,
+much like virutalenv for Python or the myriad systems available for Ruby. If
+you have OPAM installed, then installing Oasis is as simple as running:
+
+ $ opam install oasis
+
+Building
+--------
+
+Once all the prerequisites have been installed, run the following commands:
+
+ $ oasis setup
+ $ ./configure
+ $ make
+
+The `oasis setup` command will generate the configure script and Makefile,
+along with other files that opam will use to create an installable library.
+The cofigure script will ensure that all build dependencies are installed, and
+make will actually build the library.
+
+To remove files that the compiler geneates, run:
+
+ $ make clean
+
+To remove those files _as well as_ files that the setup and configure process
+generates, run:
+
+ $ rm `cat .gitignore`
+
+Installing
+----------
+
+If you're using opam, simply run the following command:
+
+ $ make install
+
+While development, you may want to install your latest build on the system to
+test against other libraries or programs. To do this, use:
+
+ $ make reinstall
+
+Distribution
+------------
+
+The de facto preferred method for distributing OCaml libraries is through the
+OPAM package repository. To publish the latest package, issue a pull request
+against the following github repository:
+
+ https://github.com/ocaml/opam-repository
+
+The pull requestion should add the following directory structure and files:
+
+ package
+ |__thrift
+ |__thrift.<VERSION>
+ |__ descr
+ |__ opam
+ |__ url
+
+Templates for the following files can be found in the opam/ subdirectory of
+this library's root, with XXX(...) indicating fields that need to be filled
+out. You can find further documentation here:
+
+ http://opam.ocaml.org/doc/Packaging.html
diff --git a/src/jaegertracing/thrift/lib/ocaml/README.md b/src/jaegertracing/thrift/lib/ocaml/README.md
new file mode 100644
index 000000000..5a47a4247
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/README.md
@@ -0,0 +1,119 @@
+Thrift OCaml Software Library
+
+License
+=======
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+
+Library
+=======
+
+The library abstract classes, exceptions, and general use functions
+are mostly jammed in Thrift.ml (an exception being
+TServer).
+
+Generally, classes are used, however they are often put in their own
+module along with other relevant types and functions. The classes
+often called t, exceptions are called E.
+
+Implementations live in their own files. There is TBinaryProtocol,
+TSocket, TThreadedServer, TSimpleServer, and TServerSocket.
+
+A note on making the library: Running make should create native, debug
+code libraries, and a toplevel.
+
+
+Struct format
+-------------
+Structs are turned into classes. The fields are all option types and
+are initially None. Write is a method, but reading is done by a
+separate function (since there is no such thing as a static
+class). The class type is t and is in a module with the name of the
+struct.
+
+
+enum format
+-----------
+Enums are put in their own module along with
+functions to_i and of_i which convert the ocaml types into ints. For
+example:
+
+enum Numberz
+{
+ ONE = 1,
+ TWO,
+ THREE,
+ FIVE = 5,
+ SIX,
+ EIGHT = 8
+}
+
+==>
+
+module Numberz =
+struct
+type t =
+| ONE
+| TWO
+| THREE
+| FIVE
+| SIX
+| EIGHT
+
+let of_i = ...
+let to_i = ...
+end
+
+typedef format
+--------------
+Typedef turns into the type declaration:
+typedef i64 UserId
+
+==>
+
+type userid Int64.t
+
+exception format
+----------------
+The same as structs except that the module also has an exception type
+E of t that is raised/caught.
+
+For example, with an exception Xception,
+raise (Xception.E (new Xception.t))
+and
+try
+ ...
+with Xception.E e -> ...
+
+list format
+-----------
+Lists are turned into OCaml native lists.
+
+Map/Set formats
+---------------
+These are both turned into Hashtbl.t's. Set values are bool.
+
+Services
+--------
+The client is a class "client" parametrized on input and output
+protocols. The processor is a class parametrized on a handler. A
+handler is a class inheriting the iface abstract class. Unlike other
+implementations, client does not implement iface since iface functions
+must take option arguments so as to deal with the case where a client
+does not send all the arguments.
diff --git a/src/jaegertracing/thrift/lib/ocaml/TODO b/src/jaegertracing/thrift/lib/ocaml/TODO
new file mode 100644
index 000000000..4d1dc771b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/TODO
@@ -0,0 +1,5 @@
+Write interfaces
+Clean up the code generator
+Avoid capture properly instead of relying on the user not to use _
+
+
diff --git a/src/jaegertracing/thrift/lib/ocaml/_oasis b/src/jaegertracing/thrift/lib/ocaml/_oasis
new file mode 100644
index 000000000..83566aaa9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/_oasis
@@ -0,0 +1,19 @@
+Name: libthrift-ocaml
+Version: 0.13.0
+OASISFormat: 0.3
+Synopsis: OCaml bindings for the Apache Thrift RPC system
+Authors: Apache Thrift Developers <dev@thrift.apache.org>
+License: Apache-2.0
+Homepage: http://thrift.apache.org
+BuildTools: ocamlbuild
+Plugins: META (0.3),
+ DevFiles (0.3)
+
+Library "libthrift-ocaml"
+ Path: src
+ FindlibName: thrift
+ buildTools: ocamlbuild
+ BuildDepends: threads
+ Modules: Thrift,TBinaryProtocol,TSocket,TFramedTransport,TChannelTransport,TServer,TSimpleServer,TServerSocket,TThreadedServer
+ XMETARequires: threads
+
diff --git a/src/jaegertracing/thrift/lib/ocaml/coding_standards.md b/src/jaegertracing/thrift/lib/ocaml/coding_standards.md
new file mode 100644
index 000000000..fa0390bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/coding_standards.md
@@ -0,0 +1 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
diff --git a/src/jaegertracing/thrift/lib/ocaml/descr b/src/jaegertracing/thrift/lib/ocaml/descr
new file mode 100644
index 000000000..a41749d5e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/descr
@@ -0,0 +1 @@
+OCaml bindings for the Apache Thrift RPC system
diff --git a/src/jaegertracing/thrift/lib/ocaml/opam b/src/jaegertracing/thrift/lib/ocaml/opam
new file mode 100644
index 000000000..9dbc3d911
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/opam
@@ -0,0 +1,8 @@
+opam-version: "1"
+maintainer: "XXX(FILL ME IN WITH EMAIL)"
+build: [
+ [make]
+ [make "install"]
+]
+remove: [["ocamlfind" "remove" "thrift"]]
+depends: ["ocamlfind"]
diff --git a/src/jaegertracing/thrift/lib/ocaml/src/Makefile b/src/jaegertracing/thrift/lib/ocaml/src/Makefile
new file mode 100644
index 000000000..a97ade5ef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/src/Makefile
@@ -0,0 +1,26 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+SOURCES = Thrift.ml TBinaryProtocol.ml TSocket.ml TFramedTransport.ml TChannelTransport.ml TServer.ml TSimpleServer.ml TServerSocket.ml TThreadedServer.ml
+RESULT = thrift
+LIBS = unix threads
+THREADS = yes
+all: native-code-library debug-code-library top
+OCAMLMAKEFILE = ../OCamlMakefile
+include $(OCAMLMAKEFILE)
diff --git a/src/jaegertracing/thrift/lib/ocaml/src/TBinaryProtocol.ml b/src/jaegertracing/thrift/lib/ocaml/src/TBinaryProtocol.ml
new file mode 100644
index 000000000..6d7500e9c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/src/TBinaryProtocol.ml
@@ -0,0 +1,171 @@
+(*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*)
+
+open Thrift
+
+module P = Protocol
+
+let get_byte i b = 255 land (i lsr (8*b))
+let get_byte32 i b = 255 land (Int32.to_int (Int32.shift_right i (8*b)))
+let get_byte64 i b = 255 land (Int64.to_int (Int64.shift_right i (8*b)))
+
+
+let tv = P.t_type_to_i
+let vt = P.t_type_of_i
+
+
+let comp_int b n =
+ let s = ref 0l in
+ let sb = 32 - 8*n in
+ for i=0 to (n-1) do
+ s:= Int32.logor !s (Int32.shift_left (Int32.of_int (int_of_char b.[i])) (8*(n-1-i)))
+ done;
+ Int32.shift_right (Int32.shift_left !s sb) sb
+
+let comp_int64 b n =
+ let s = ref 0L in
+ for i=0 to (n-1) do
+ s:=Int64.logor !s (Int64.shift_left (Int64.of_int (int_of_char b.[i])) (8*(n-1-i)))
+ done;
+ !s
+
+let version_mask = 0xffff0000l
+let version_1 = 0x80010000l
+
+class t trans =
+object (self)
+ inherit P.t trans
+ val ibyte = String.create 8
+ method writeBool b =
+ ibyte.[0] <- char_of_int (if b then 1 else 0);
+ trans#write ibyte 0 1
+ method writeByte i =
+ ibyte.[0] <- char_of_int (get_byte i 0);
+ trans#write ibyte 0 1
+ method writeI16 i =
+ let gb = get_byte i in
+ ibyte.[1] <- char_of_int (gb 0);
+ ibyte.[0] <- char_of_int (gb 1);
+ trans#write ibyte 0 2
+ method writeI32 i =
+ let gb = get_byte32 i in
+ for i=0 to 3 do
+ ibyte.[3-i] <- char_of_int (gb i)
+ done;
+ trans#write ibyte 0 4
+ method writeI64 i=
+ let gb = get_byte64 i in
+ for i=0 to 7 do
+ ibyte.[7-i] <- char_of_int (gb i)
+ done;
+ trans#write ibyte 0 8
+ method writeDouble d =
+ self#writeI64 (Int64.bits_of_float d)
+ method writeString s=
+ let n = String.length s in
+ self#writeI32 (Int32.of_int n);
+ trans#write s 0 n
+ method writeBinary a = self#writeString a
+ method writeMessageBegin (n,t,s) =
+ self#writeI32 (Int32.logor version_1 (Int32.of_int (P.message_type_to_i t)));
+ self#writeString n;
+ self#writeI32 (Int32.of_int s)
+ method writeMessageEnd = ()
+ method writeStructBegin s = ()
+ method writeStructEnd = ()
+ method writeFieldBegin (n,t,i) =
+ self#writeByte (tv t);
+ self#writeI16 i
+ method writeFieldEnd = ()
+ method writeFieldStop =
+ self#writeByte (tv (P.T_STOP))
+ method writeMapBegin (k,v,s) =
+ self#writeByte (tv k);
+ self#writeByte (tv v);
+ self#writeI32 (Int32.of_int s)
+ method writeMapEnd = ()
+ method writeListBegin (t,s) =
+ self#writeByte (tv t);
+ self#writeI32 (Int32.of_int s)
+ method writeListEnd = ()
+ method writeSetBegin (t,s) =
+ self#writeByte (tv t);
+ self#writeI32 (Int32.of_int s)
+ method writeSetEnd = ()
+ method readByte =
+ ignore (trans#readAll ibyte 0 1);
+ Int32.to_int (comp_int ibyte 1)
+ method readI16 =
+ ignore (trans#readAll ibyte 0 2);
+ Int32.to_int (comp_int ibyte 2)
+ method readI32 =
+ ignore (trans#readAll ibyte 0 4);
+ comp_int ibyte 4
+ method readI64 =
+ ignore (trans#readAll ibyte 0 8);
+ comp_int64 ibyte 8
+ method readDouble =
+ Int64.float_of_bits (self#readI64)
+ method readBool =
+ self#readByte = 1
+ method readString =
+ let sz = Int32.to_int (self#readI32) in
+ let buf = String.create sz in
+ ignore (trans#readAll buf 0 sz);
+ buf
+ method readBinary = self#readString
+ method readMessageBegin =
+ let ver = self#readI32 in
+ if Int32.compare (Int32.logand ver version_mask) version_1 != 0 then
+ raise (P.E (P.BAD_VERSION, "Missing version identifier"))
+ else
+ let s = self#readString in
+ let mt = P.message_type_of_i (Int32.to_int (Int32.logand ver 0xFFl)) in
+ (s,mt, Int32.to_int self#readI32)
+ method readMessageEnd = ()
+ method readStructBegin =
+ ""
+ method readStructEnd = ()
+ method readFieldBegin =
+ let t = (vt (self#readByte))
+ in
+ if t != P.T_STOP then
+ ("",t,self#readI16)
+ else ("",t,0);
+ method readFieldEnd = ()
+ method readMapBegin =
+ let kt = vt (self#readByte) in
+ let vt = vt (self#readByte) in
+ (kt,vt, Int32.to_int self#readI32)
+ method readMapEnd = ()
+ method readListBegin =
+ let t = vt (self#readByte) in
+ (t, Int32.to_int self#readI32)
+ method readListEnd = ()
+ method readSetBegin =
+ let t = vt (self#readByte) in
+ (t, Int32.to_int self#readI32);
+ method readSetEnd = ()
+end
+
+class factory =
+object
+ inherit P.factory
+ method getProtocol tr = new t tr
+end
diff --git a/src/jaegertracing/thrift/lib/ocaml/src/TChannelTransport.ml b/src/jaegertracing/thrift/lib/ocaml/src/TChannelTransport.ml
new file mode 100644
index 000000000..0f7d616f5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/src/TChannelTransport.ml
@@ -0,0 +1,39 @@
+(*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*)
+
+open Thrift
+module T = Transport
+
+class t (i,o) =
+object (self)
+ val mutable opened = true
+ inherit Transport.t
+ method isOpen = opened
+ method opn = ()
+ method close = close_in i; opened <- false
+ method read buf off len =
+ if opened then
+ try
+ really_input i buf off len; len
+ with _ -> raise (T.E (T.UNKNOWN, ("TChannelTransport: Could not read "^(string_of_int len))))
+ else
+ raise (T.E (T.NOT_OPEN, "TChannelTransport: Channel was closed"))
+ method write buf off len = output o buf off len
+ method flush = flush o
+end
diff --git a/src/jaegertracing/thrift/lib/ocaml/src/TFramedTransport.ml b/src/jaegertracing/thrift/lib/ocaml/src/TFramedTransport.ml
new file mode 100644
index 000000000..1be51e763
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/src/TFramedTransport.ml
@@ -0,0 +1,93 @@
+open Thrift
+
+module T = Transport
+
+let c_0xff_32 = Int32.of_string "0xff"
+
+(* Copied from OCamlnet rtypes.ml *)
+let encode_frame_size x =
+ let s = String.create 4 in
+ let n3 = Int32.to_int (Int32.shift_right_logical x 24) land 0xff in
+ let n2 = Int32.to_int (Int32.shift_right_logical x 16) land 0xff in
+ let n1 = Int32.to_int (Int32.shift_right_logical x 8) land 0xff in
+ let n0 = Int32.to_int (Int32.logand x c_0xff_32) in
+ String.unsafe_set s 0 (Char.unsafe_chr n3);
+ String.unsafe_set s 1 (Char.unsafe_chr n2);
+ String.unsafe_set s 2 (Char.unsafe_chr n1);
+ String.unsafe_set s 3 (Char.unsafe_chr n0);
+ s
+
+let decode_frame_size s =
+ let n3 = Int32.of_int (Char.code s.[0]) in
+ let n2 = Int32.of_int (Char.code s.[1]) in
+ let n1 = Int32.of_int (Char.code s.[2]) in
+ let n0 = Int32.of_int (Char.code s.[3]) in
+ Int32.logor
+ (Int32.shift_left n3 24)
+ (Int32.logor
+ (Int32.shift_left n2 16)
+ (Int32.logor
+ (Int32.shift_left n1 8)
+ n0))
+
+class t ?(max_length=Sys.max_string_length) (transport: T.t) =
+object (self)
+ inherit T.t
+
+ method isOpen = transport#isOpen
+ method opn = transport#opn
+ method close = transport#close
+
+ val mutable read_buf = None
+ val mutable read_buf_offset = 0
+ val mutable write_buf = ""
+
+ method private read_frame =
+ let len_buf = String.create 4 in
+ assert (transport#readAll len_buf 0 4 = 4);
+
+ let size = Int32.to_int (decode_frame_size len_buf) in
+
+ (if size < 0
+ then failwith (Printf.sprintf "Read a negative frame size (%i)!" size));
+
+ (if size > max_length
+ then failwith (Printf.sprintf "Frame size (%i) larger than max length (%i)!" size max_length));
+
+ let buf = String.create size in
+ assert (transport#readAll buf 0 size = size);
+ read_buf <- Some buf;
+ read_buf_offset <- 0
+
+ method private read_from_frame frame buf off len =
+ let to_copy = min len ((String.length frame) - read_buf_offset) in
+ String.blit frame read_buf_offset buf off to_copy;
+ read_buf_offset <- read_buf_offset + to_copy;
+ to_copy
+
+ method read buf off len =
+ match read_buf with
+ | Some frame ->
+ let i = self#read_from_frame frame buf off len in
+ if i > 0
+ then i
+ else begin
+ self#read_frame;
+ self#read_from_frame frame buf off len
+ end
+ | None ->
+ self#read_frame;
+ self#read buf off len
+
+ method write buf off len =
+ write_buf <- write_buf ^ (String.sub buf off len)
+
+ method flush =
+ let encoded_size = encode_frame_size (Int32.of_int (String.length write_buf)) in
+ transport#write encoded_size 0 (String.length encoded_size);
+ transport#write write_buf 0 (String.length write_buf);
+ transport#flush;
+ write_buf <- ""
+end
+
+
diff --git a/src/jaegertracing/thrift/lib/ocaml/src/TServer.ml b/src/jaegertracing/thrift/lib/ocaml/src/TServer.ml
new file mode 100644
index 000000000..fc51efa8f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/src/TServer.ml
@@ -0,0 +1,42 @@
+(*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*)
+
+open Thrift
+
+class virtual t
+ (pf : Processor.t)
+ (st : Transport.server_t)
+ (tf : Transport.factory)
+ (ipf : Protocol.factory)
+ (opf : Protocol.factory)=
+object
+ method virtual serve : unit
+end;;
+
+
+
+let run_basic_server proc port =
+ Unix.establish_server (fun inp -> fun out ->
+ let trans = new TChannelTransport.t (inp,out) in
+ let proto = new TBinaryProtocol.t (trans :> Transport.t) in
+ try
+ while proc#process proto proto do () done; ()
+ with e -> ()) (Unix.ADDR_INET (Unix.inet_addr_of_string "127.0.0.1",port))
+
+
diff --git a/src/jaegertracing/thrift/lib/ocaml/src/TServerSocket.ml b/src/jaegertracing/thrift/lib/ocaml/src/TServerSocket.ml
new file mode 100644
index 000000000..405ef82c1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/src/TServerSocket.ml
@@ -0,0 +1,41 @@
+(*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*)
+
+open Thrift
+
+class t port =
+object
+ inherit Transport.server_t
+ val mutable sock = None
+ method listen =
+ let s = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
+ sock <- Some s;
+ Unix.bind s (Unix.ADDR_INET (Unix.inet_addr_any, port));
+ Unix.listen s 256
+ method close =
+ match sock with
+ Some s -> Unix.shutdown s Unix.SHUTDOWN_ALL; Unix.close s;
+ sock <- None
+ | _ -> ()
+ method acceptImpl =
+ match sock with
+ Some s -> let (fd,_) = Unix.accept s in
+ new TChannelTransport.t (Unix.in_channel_of_descr fd,Unix.out_channel_of_descr fd)
+ | _ -> raise (Transport.E (Transport.NOT_OPEN,"TServerSocket: Not listening but tried to accept"))
+end
diff --git a/src/jaegertracing/thrift/lib/ocaml/src/TSimpleServer.ml b/src/jaegertracing/thrift/lib/ocaml/src/TSimpleServer.ml
new file mode 100644
index 000000000..2927c08fd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/src/TSimpleServer.ml
@@ -0,0 +1,40 @@
+(*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*)
+
+open Thrift
+module S = TServer
+
+class t pf st tf ipf opf =
+object
+ inherit S.t pf st tf ipf opf
+ method serve =
+ try
+ st#listen;
+ while true do
+ let c = st#accept in
+ let trans = tf#getTransport c in
+ let inp = ipf#getProtocol trans in
+ let op = opf#getProtocol trans in
+ try
+ while (pf#process inp op) do () done;
+ trans#close
+ with e -> trans#close; raise e
+ done
+ with _ -> ()
+end
diff --git a/src/jaegertracing/thrift/lib/ocaml/src/TSocket.ml b/src/jaegertracing/thrift/lib/ocaml/src/TSocket.ml
new file mode 100644
index 000000000..109e11c56
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/src/TSocket.ml
@@ -0,0 +1,59 @@
+(*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*)
+
+open Thrift
+
+module T = Transport
+
+class t host port=
+object (self)
+ inherit T.t
+ val mutable chans = None
+ method isOpen = chans != None
+ method opn =
+ try
+ let addr = (let {Unix.h_addr_list=x} = Unix.gethostbyname host in x.(0)) in
+ chans <- Some(Unix.open_connection (Unix.ADDR_INET (addr,port)))
+ with
+ Unix.Unix_error (e,fn,_) -> raise (T.E (T.NOT_OPEN, ("TSocket: Could not connect to "^host^":"^(string_of_int port)^" because: "^fn^":"^(Unix.error_message e))))
+ | _ -> raise (T.E (T.NOT_OPEN, ("TSocket: Could not connect to "^host^":"^(string_of_int port))))
+
+ method close =
+ match chans with
+ None -> ()
+ | Some(inc,out) -> (Unix.shutdown_connection inc;
+ close_in inc;
+ chans <- None)
+ method read buf off len = match chans with
+ None -> raise (T.E (T.NOT_OPEN, "TSocket: Socket not open"))
+ | Some(i,o) ->
+ try
+ really_input i buf off len; len
+ with
+ Unix.Unix_error (e,fn,_) -> raise (T.E (T.UNKNOWN, ("TSocket: Could not read "^(string_of_int len)^" from "^host^":"^(string_of_int port)^" because: "^fn^":"^(Unix.error_message e))))
+ | _ -> raise (T.E (T.UNKNOWN, ("TSocket: Could not read "^(string_of_int len)^" from "^host^":"^(string_of_int port))))
+ method write buf off len = match chans with
+ None -> raise (T.E (T.NOT_OPEN, "TSocket: Socket not open"))
+ | Some(i,o) -> output o buf off len
+ method flush = match chans with
+ None -> raise (T.E (T.NOT_OPEN, "TSocket: Socket not open"))
+ | Some(i,o) -> flush o
+end
+
+
diff --git a/src/jaegertracing/thrift/lib/ocaml/src/TThreadedServer.ml b/src/jaegertracing/thrift/lib/ocaml/src/TThreadedServer.ml
new file mode 100644
index 000000000..4462dbd73
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/src/TThreadedServer.ml
@@ -0,0 +1,45 @@
+(*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*)
+
+open Thrift
+
+class t
+ (pf : Processor.t)
+ (st : Transport.server_t)
+ (tf : Transport.factory)
+ (ipf : Protocol.factory)
+ (opf : Protocol.factory)=
+object
+ inherit TServer.t pf st tf ipf opf
+ method serve =
+ st#listen;
+ while true do
+ let tr = tf#getTransport (st#accept) in
+ ignore (Thread.create
+ (fun _ ->
+ let ip = ipf#getProtocol tr in
+ let op = opf#getProtocol tr in
+ try
+ while pf#process ip op do
+ ()
+ done
+ with _ -> ()) ())
+ done
+end
+
diff --git a/src/jaegertracing/thrift/lib/ocaml/src/Thrift.ml b/src/jaegertracing/thrift/lib/ocaml/src/Thrift.ml
new file mode 100644
index 000000000..063459ba0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/src/Thrift.ml
@@ -0,0 +1,382 @@
+(*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*)
+
+exception Break;;
+exception Thrift_error;;
+exception Field_empty of string;;
+
+class t_exn =
+object
+ val mutable message = ""
+ method get_message = message
+ method set_message s = message <- s
+end;;
+
+module Transport =
+struct
+ type exn_type =
+ | UNKNOWN
+ | NOT_OPEN
+ | ALREADY_OPEN
+ | TIMED_OUT
+ | END_OF_FILE;;
+
+ exception E of exn_type * string
+
+ class virtual t =
+ object (self)
+ method virtual isOpen : bool
+ method virtual opn : unit
+ method virtual close : unit
+ method virtual read : string -> int -> int -> int
+ method readAll buf off len =
+ let got = ref 0 in
+ let ret = ref 0 in
+ while !got < len do
+ ret := self#read buf (off+(!got)) (len - (!got));
+ if !ret <= 0 then
+ raise (E (UNKNOWN, "Cannot read. Remote side has closed."));
+ got := !got + !ret
+ done;
+ !got
+ method virtual write : string -> int -> int -> unit
+ method virtual flush : unit
+ end
+
+ class factory =
+ object
+ method getTransport (t : t) = t
+ end
+
+ class virtual server_t =
+ object (self)
+ method virtual listen : unit
+ method accept = self#acceptImpl
+ method virtual close : unit
+ method virtual acceptImpl : t
+ end
+
+end;;
+
+
+
+module Protocol =
+struct
+ type t_type =
+ | T_STOP
+ | T_VOID
+ | T_BOOL
+ | T_BYTE
+ | T_I08
+ | T_I16
+ | T_I32
+ | T_U64
+ | T_I64
+ | T_DOUBLE
+ | T_STRING
+ | T_UTF7
+ | T_STRUCT
+ | T_MAP
+ | T_SET
+ | T_LIST
+ | T_UTF8
+ | T_UTF16
+
+ let t_type_to_i = function
+ T_STOP -> 0
+ | T_VOID -> 1
+ | T_BOOL -> 2
+ | T_BYTE -> 3
+ | T_I08 -> 3
+ | T_I16 -> 6
+ | T_I32 -> 8
+ | T_U64 -> 9
+ | T_I64 -> 10
+ | T_DOUBLE -> 4
+ | T_STRING -> 11
+ | T_UTF7 -> 11
+ | T_STRUCT -> 12
+ | T_MAP -> 13
+ | T_SET -> 14
+ | T_LIST -> 15
+ | T_UTF8 -> 16
+ | T_UTF16 -> 17
+
+ let t_type_of_i = function
+ 0 -> T_STOP
+ | 1 -> T_VOID
+ | 2 -> T_BOOL
+ | 3 -> T_BYTE
+ | 6-> T_I16
+ | 8 -> T_I32
+ | 9 -> T_U64
+ | 10 -> T_I64
+ | 4 -> T_DOUBLE
+ | 11 -> T_STRING
+ | 12 -> T_STRUCT
+ | 13 -> T_MAP
+ | 14 -> T_SET
+ | 15 -> T_LIST
+ | 16 -> T_UTF8
+ | 17 -> T_UTF16
+ | _ -> raise Thrift_error
+
+ type message_type =
+ | CALL
+ | REPLY
+ | EXCEPTION
+ | ONEWAY
+
+ let message_type_to_i = function
+ | CALL -> 1
+ | REPLY -> 2
+ | EXCEPTION -> 3
+ | ONEWAY -> 4
+
+ let message_type_of_i = function
+ | 1 -> CALL
+ | 2 -> REPLY
+ | 3 -> EXCEPTION
+ | 4 -> ONEWAY
+ | _ -> raise Thrift_error
+
+ class virtual t (trans: Transport.t) =
+ object (self)
+ val mutable trans_ = trans
+ method getTransport = trans_
+ (* writing methods *)
+ method virtual writeMessageBegin : string * message_type * int -> unit
+ method virtual writeMessageEnd : unit
+ method virtual writeStructBegin : string -> unit
+ method virtual writeStructEnd : unit
+ method virtual writeFieldBegin : string * t_type * int -> unit
+ method virtual writeFieldEnd : unit
+ method virtual writeFieldStop : unit
+ method virtual writeMapBegin : t_type * t_type * int -> unit
+ method virtual writeMapEnd : unit
+ method virtual writeListBegin : t_type * int -> unit
+ method virtual writeListEnd : unit
+ method virtual writeSetBegin : t_type * int -> unit
+ method virtual writeSetEnd : unit
+ method virtual writeBool : bool -> unit
+ method virtual writeByte : int -> unit
+ method virtual writeI16 : int -> unit
+ method virtual writeI32 : Int32.t -> unit
+ method virtual writeI64 : Int64.t -> unit
+ method virtual writeDouble : float -> unit
+ method virtual writeString : string -> unit
+ method virtual writeBinary : string -> unit
+ (* reading methods *)
+ method virtual readMessageBegin : string * message_type * int
+ method virtual readMessageEnd : unit
+ method virtual readStructBegin : string
+ method virtual readStructEnd : unit
+ method virtual readFieldBegin : string * t_type * int
+ method virtual readFieldEnd : unit
+ method virtual readMapBegin : t_type * t_type * int
+ method virtual readMapEnd : unit
+ method virtual readListBegin : t_type * int
+ method virtual readListEnd : unit
+ method virtual readSetBegin : t_type * int
+ method virtual readSetEnd : unit
+ method virtual readBool : bool
+ method virtual readByte : int
+ method virtual readI16 : int
+ method virtual readI32: Int32.t
+ method virtual readI64 : Int64.t
+ method virtual readDouble : float
+ method virtual readString : string
+ method virtual readBinary : string
+ (* skippage *)
+ method skip typ =
+ match typ with
+ | T_BOOL -> ignore self#readBool
+ | T_BYTE
+ | T_I08 -> ignore self#readByte
+ | T_I16 -> ignore self#readI16
+ | T_I32 -> ignore self#readI32
+ | T_U64
+ | T_I64 -> ignore self#readI64
+ | T_DOUBLE -> ignore self#readDouble
+ | T_STRING -> ignore self#readString
+ | T_UTF7 -> ()
+ | T_STRUCT -> ignore ((ignore self#readStructBegin);
+ (try
+ while true do
+ let (_,t,_) = self#readFieldBegin in
+ if t = T_STOP then
+ raise Break
+ else
+ (self#skip t;
+ self#readFieldEnd)
+ done
+ with Break -> ());
+ self#readStructEnd)
+ | T_MAP -> ignore (let (k,v,s) = self#readMapBegin in
+ for i=0 to s do
+ self#skip k;
+ self#skip v;
+ done;
+ self#readMapEnd)
+ | T_SET -> ignore (let (t,s) = self#readSetBegin in
+ for i=0 to s do
+ self#skip t
+ done;
+ self#readSetEnd)
+ | T_LIST -> ignore (let (t,s) = self#readListBegin in
+ for i=0 to s do
+ self#skip t
+ done;
+ self#readListEnd)
+ | T_UTF8 -> ()
+ | T_UTF16 -> ()
+ | _ -> raise (Protocol.E (Protocol.INVALID_DATA, "Invalid data"))
+ end
+
+ class virtual factory =
+ object
+ method virtual getProtocol : Transport.t -> t
+ end
+
+ type exn_type =
+ | UNKNOWN
+ | INVALID_DATA
+ | NEGATIVE_SIZE
+ | SIZE_LIMIT
+ | BAD_VERSION
+ | NOT_IMPLEMENTED
+ | DEPTH_LIMIT
+
+ exception E of exn_type * string;;
+
+end;;
+
+
+module Processor =
+struct
+ class virtual t =
+ object
+ method virtual process : Protocol.t -> Protocol.t -> bool
+ end;;
+
+ class factory (processor : t) =
+ object
+ val processor_ = processor
+ method getProcessor (trans : Transport.t) = processor_
+ end;;
+end
+
+
+(* Ugly *)
+module Application_Exn =
+struct
+ type typ=
+ | UNKNOWN
+ | UNKNOWN_METHOD
+ | INVALID_MESSAGE_TYPE
+ | WRONG_METHOD_NAME
+ | BAD_SEQUENCE_ID
+ | MISSING_RESULT
+ | INTERNAL_ERROR
+ | PROTOCOL_ERROR
+ | INVALID_TRANSFORM
+ | INVALID_PROTOCOL
+ | UNSUPPORTED_CLIENT_TYPE
+
+ let typ_of_i = function
+ 0l -> UNKNOWN
+ | 1l -> UNKNOWN_METHOD
+ | 2l -> INVALID_MESSAGE_TYPE
+ | 3l -> WRONG_METHOD_NAME
+ | 4l -> BAD_SEQUENCE_ID
+ | 5l -> MISSING_RESULT
+ | 6l -> INTERNAL_ERROR
+ | 7l -> PROTOCOL_ERROR
+ | 8l -> INVALID_TRANSFORM
+ | 9l -> INVALID_PROTOCOL
+ | 10l -> UNSUPPORTED_CLIENT_TYPE
+ | _ -> raise Thrift_error;;
+ let typ_to_i = function
+ | UNKNOWN -> 0l
+ | UNKNOWN_METHOD -> 1l
+ | INVALID_MESSAGE_TYPE -> 2l
+ | WRONG_METHOD_NAME -> 3l
+ | BAD_SEQUENCE_ID -> 4l
+ | MISSING_RESULT -> 5l
+ | INTERNAL_ERROR -> 6l
+ | PROTOCOL_ERROR -> 7l
+ | INVALID_TRANSFORM -> 8l
+ | INVALID_PROTOCOL -> 9l
+ | UNSUPPORTED_CLIENT_TYPE -> 10l
+
+ class t =
+ object (self)
+ inherit t_exn
+ val mutable typ = UNKNOWN
+ method get_type = typ
+ method set_type t = typ <- t
+ method write (oprot : Protocol.t) =
+ oprot#writeStructBegin "TApplicationExeception";
+ if self#get_message != "" then
+ (oprot#writeFieldBegin ("message",Protocol.T_STRING, 1);
+ oprot#writeString self#get_message;
+ oprot#writeFieldEnd)
+ else ();
+ oprot#writeFieldBegin ("type",Protocol.T_I32,2);
+ oprot#writeI32 (typ_to_i typ);
+ oprot#writeFieldEnd;
+ oprot#writeFieldStop;
+ oprot#writeStructEnd
+ end;;
+
+ let create typ msg =
+ let e = new t in
+ e#set_type typ;
+ e#set_message msg;
+ e
+
+ let read (iprot : Protocol.t) =
+ let msg = ref "" in
+ let typ = ref 0l in
+ ignore iprot#readStructBegin;
+ (try
+ while true do
+ let (name,ft,id) =iprot#readFieldBegin in
+ if ft = Protocol.T_STOP
+ then raise Break
+ else ();
+ (match id with
+ | 1 -> (if ft = Protocol.T_STRING
+ then msg := (iprot#readString)
+ else iprot#skip ft)
+ | 2 -> (if ft = Protocol.T_I32
+ then typ := iprot#readI32
+ else iprot#skip ft)
+ | _ -> iprot#skip ft);
+ iprot#readFieldEnd
+ done
+ with Break -> ());
+ iprot#readStructEnd;
+ let e = new t in
+ e#set_type (typ_of_i !typ);
+ e#set_message !msg;
+ e;;
+
+ exception E of t
+end;;
diff --git a/src/jaegertracing/thrift/lib/ocaml/url b/src/jaegertracing/thrift/lib/ocaml/url
new file mode 100644
index 000000000..fe4d604e8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ocaml/url
@@ -0,0 +1,2 @@
+archive: "XXX(FILL ME IN WITH URL)"
+checksum: "XXX(FILL ME IN WITH MD5)"
diff --git a/src/jaegertracing/thrift/lib/perl/MANIFEST.SKIP b/src/jaegertracing/thrift/lib/perl/MANIFEST.SKIP
new file mode 100644
index 000000000..9b044509e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/MANIFEST.SKIP
@@ -0,0 +1,14 @@
+blib/.*$
+build-cpan-dist.sh
+FixupDist.pl
+MANIFEST.bak
+MANIFEST.SKIP
+MYMETA.json
+Makefile
+Makefile.am
+Makefile.in
+pm_to_blib
+t/Makefile
+t/Makefile.am
+t/Makefile.in
+tools/FixupDist.pl
diff --git a/src/jaegertracing/thrift/lib/perl/Makefile.PL b/src/jaegertracing/thrift/lib/perl/Makefile.PL
new file mode 100644
index 000000000..5e60ab40a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/Makefile.PL
@@ -0,0 +1,49 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use ExtUtils::MakeMaker;
+
+WriteMakefile( ABSTRACT => 'Apache Thrift is a software framework for scalable cross-language services development.',
+ AUTHOR => 'Apache Thrift <dev@thrift.apache.org>',
+ LICENSE => 'apache_2_0',
+ MIN_PERL_VERSION => '5.010000',
+ NAME => 'Thrift',
+ NEEDS_LINKING => 0,
+ PREREQ_PM => {
+ 'Bit::Vector' => 0,
+ 'Class::Accessor' => 0
+ },
+# SIGN => 1,
+ VERSION_FROM => 'lib/Thrift.pm' );
+
+# THRIFT-4691
+package MY; # so that "SUPER" works right
+sub test {
+ # Adds gen-perl and gen-perl2 to the test execution as include paths
+ # Could not find anything in MakeMaker that would do this...
+ my @result;
+ for (@result = shift->SUPER::test(@_)) {
+ s/\$\(TEST_FILES\)/-Igen-perl -Igen-perl2 \$(TEST_FILES)/ig;
+ }
+ @result;
+}
diff --git a/src/jaegertracing/thrift/lib/perl/Makefile.am b/src/jaegertracing/thrift/lib/perl/Makefile.am
new file mode 100644
index 000000000..abae1e77f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/Makefile.am
@@ -0,0 +1,108 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+SUBDIRS = t
+
+Makefile-perl.mk : Makefile.PL
+ $(PERL) Makefile.PL MAKEFILE=Makefile-perl.mk INSTALLDIRS=$(INSTALLDIRS) INSTALL_BASE=$(PERL_PREFIX)
+
+all-local: Makefile-perl.mk
+ $(MAKE) -f $<
+ find blib -name 'Makefile*' -exec rm -f {} \;
+
+install-exec-local: Makefile-perl.mk
+ $(MAKE) -f $< install DESTDIR=$(DESTDIR)/
+
+clean-local:
+ if test -f Makefile-perl.mk ; then \
+ $(MAKE) -f Makefile-perl.mk clean ; \
+ fi
+ $(RM) Makefile-perl.mk.old
+ $(RM) -r gen-perl gen-perl2
+
+EXTRA_DIST = \
+ coding_standards.md \
+ build-cpan-dist.sh \
+ Makefile.PL \
+ test.pl \
+ lib/Thrift.pm \
+ lib/Thrift.pm \
+ lib/Thrift/BinaryProtocol.pm \
+ lib/Thrift/BufferedTransport.pm \
+ lib/Thrift/Exception.pm \
+ lib/Thrift/FramedTransport.pm \
+ lib/Thrift/HttpClient.pm \
+ lib/Thrift/MemoryBuffer.pm \
+ lib/Thrift/MessageType.pm \
+ lib/Thrift/MultiplexedProcessor.pm \
+ lib/Thrift/MultiplexedProtocol.pm \
+ lib/Thrift/Protocol.pm \
+ lib/Thrift/ProtocolDecorator.pm \
+ lib/Thrift/Server.pm \
+ lib/Thrift/ServerSocket.pm \
+ lib/Thrift/Socket.pm \
+ lib/Thrift/SSLSocket.pm \
+ lib/Thrift/SSLServerSocket.pm \
+ lib/Thrift/UnixServerSocket.pm \
+ lib/Thrift/UnixSocket.pm \
+ lib/Thrift/Type.pm \
+ lib/Thrift/Transport.pm \
+ README.md
+
+THRIFT = @top_builddir@/compiler/cpp/thrift
+THRIFT_IF = @top_srcdir@/test/ThriftTest.thrift
+NAME_BENCHMARKSERVICE = @top_srcdir@/lib/rb/benchmark/Benchmark.thrift
+NAME_AGGR = @top_srcdir@/contrib/async-test/aggr.thrift
+
+THRIFTTEST_GEN = \
+ gen-perl/ThriftTest/Constants.pm \
+ gen-perl/ThriftTest/SecondService.pm \
+ gen-perl/ThriftTest/ThriftTest.pm \
+ gen-perl/ThriftTest/Types.pm
+
+BENCHMARK_GEN = \
+ gen-perl/BenchmarkService.pm \
+ gen-perl/Constants.pm \
+ gen-perl/Types.pm
+
+AGGR_GEN = \
+ gen-perl2/Aggr.pm \
+ gen-perl2/Constants.pm \
+ gen-perl2/Types.pm
+
+PERL_GEN = \
+ $(THRIFTTEST_GEN) \
+ $(BENCHMARK_GEN) \
+ $(AGGR_GEN)
+
+BUILT_SOURCES = $(PERL_GEN)
+
+check-local: $(PERL_GEN)
+ $(PERL) -Iblib/lib -I@abs_srcdir@ -I@builddir@/gen-perl2 -I@builddir@/gen-perl \
+ @abs_srcdir@/test.pl @abs_srcdir@/t/*.t
+
+$(THRIFTTEST_GEN): $(THRIFT_IF) $(THRIFT)
+ $(THRIFT) --gen perl $<
+
+$(BENCHMARK_GEN): $(NAME_BENCHMARKSERVICE) $(THRIFT)
+ $(THRIFT) --gen perl $<
+
+$(AGGR_GEN): $(NAME_AGGR) $(THRIFT)
+ $(MKDIR_P) gen-perl2
+ $(THRIFT) -out gen-perl2 --gen perl $<
diff --git a/src/jaegertracing/thrift/lib/perl/README.md b/src/jaegertracing/thrift/lib/perl/README.md
new file mode 100644
index 000000000..bd1e5b2e4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/README.md
@@ -0,0 +1,124 @@
+Thrift Perl Software Library
+
+# Summary
+
+Apache Thrift is a software framework for scalable cross-language services development.
+It combines a software stack with a code generation engine to build services that work
+efficiently and seamlessly between many programming languages. A language-neutral IDL
+is used to generate functioning client libraries and server-side handling frameworks.
+
+# License
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+# For More Information
+
+See the [Apache Thrift Web Site](http://thrift.apache.org/) for more information.
+
+# Using Thrift with Perl
+
+Thrift requires Perl >= 5.10.0
+
+Unexpected exceptions in a service handler are converted to
+TApplicationException with type INTERNAL ERROR and the string
+of the exception is delivered as the message.
+
+On the client side, exceptions are thrown with die, so be sure
+to wrap eval{} statments around any code that contains exceptions.
+
+Please see tutoral and test dirs for examples.
+
+The Perl ForkingServer ignores SIGCHLD allowing the forks to be
+reaped by the operating system naturally when they exit. This means
+one cannot use a custom SIGCHLD handler in the consuming perl
+implementation that calls serve(). It is acceptable to use
+a custom SIGCHLD handler within a thrift handler implementation
+as the ForkingServer resets the forked child process to use
+default signal handling.
+
+# Dependencies
+
+The following modules are not provided by Perl 5.10.0 but are required
+to use Thrift.
+
+## Runtime
+
+ * Bit::Vector
+ * Class::Accessor
+
+### HttpClient Transport
+
+These are only required if using Thrift::HttpClient:
+
+ * HTTP::Request
+ * IO::String
+ * LWP::UserAgent
+
+### SSL/TLS
+
+These are only required if using Thrift::SSLSocket or Thrift::SSLServerSocket:
+
+ * IO::Socket::SSL
+
+# Breaking Changes
+
+## 0.10.0
+
+The socket classes were refactored in 0.10.0 so that there is one package per
+file. This means `use Socket;` no longer defines SSLSocket. You can use this
+technique to make your application run against 0.10.0 as well as earlier versions:
+
+`eval { require Thrift::SSLSocket; } or do { require Thrift::Socket; }`
+
+## 0.11.0
+
+ * Namespaces of packages that were not scoped within Thrift have been fixed.
+ ** TApplicationException is now Thrift::TApplicationException
+ ** TException is now Thrift::TException
+ ** TMessageType is now Thrift::TMessageType
+ ** TProtocolException is now Thrift::TProtocolException
+ ** TProtocolFactory is now Thrift::TProtocolFactory
+ ** TTransportException is now Thrift::TTransportException
+ ** TType is now Thrift::TType
+
+If you need a single version of your code to work with both older and newer thrift
+namespace changes, you can make the new, correct namespaces behave like the old ones
+in your files with this technique to create an alias, which will allow you code to
+run against either version of the perl runtime for thrift:
+
+`BEGIN {*TType:: = *Thrift::TType::}`
+
+ * Packages found in Thrift.pm were moved into the Thrift/ directory in separate files:
+ ** Thrift::TApplicationException is now in Thrift/Exception.pm
+ ** Thrift::TException is now in Thrift/Exception.pm
+ ** Thrift::TMessageType is now in Thrift/MessageType.pm
+ ** Thrift::TType is now in Thrift/Type.pm
+
+If you need to modify your code to work against both older or newer thrift versions,
+you can deal with these changes in a backwards compatible way in your projects using eval:
+
+`eval { require Thrift::Exception; require Thrift::MessageType; require Thrift::Type; }
+ or do { require Thrift; }`
+
+# Deprecations
+
+## 0.11.0
+
+Thrift::HttpClient setRecvTimeout() and setSendTimeout() are deprecated.
+Use setTimeout instead.
+
diff --git a/src/jaegertracing/thrift/lib/perl/build-cpan-dist.sh b/src/jaegertracing/thrift/lib/perl/build-cpan-dist.sh
new file mode 100755
index 000000000..c92fd76b5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/build-cpan-dist.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+#
+# This script is intended to be used after tagging the repository and updating
+# the version files for a release. It will create a CPAN archive. Run this
+# from inside a docker image like ubuntu-xenial.
+#
+
+set -e
+
+rm -f MANIFEST
+rm -rf Thrift-*
+
+# setup cpan without a prompt
+echo | cpan
+cpan install HTTP::Date Log::Log4perl
+cpan install CPAN
+cpan install CPAN::Meta ExtUtils::MakeMaker JSON::PP
+# cpan install Module::Signature
+
+perl Makefile.PL
+rm MYMETA.yml
+make manifest
+make dist
+
+#
+# We unpack the archive so we can add version metadata for CPAN
+# so that it properly indexes Thrift and remove unnecessary files.
+#
+
+echo '-----------------------------------------------------------'
+set -x
+
+DISTFILE=$(ls Thrift*.gz)
+NEWFILE=${DISTFILE/t-v/t-}
+if [[ "$DISTFILE" != "$NEWFILE" ]]; then
+ mv $DISTFILE $NEWFILE
+ DISTFILE="$NEWFILE"
+fi
+tar xzf $DISTFILE
+rm $DISTFILE
+DISTDIR=$(ls -d Thrift*)
+# cpan doesn't like "Thrift-v0.nn.0 as a directory name
+# needs to be Thrift-0.nn.0
+NEWDIR=${DISTDIR/t-v/t-}
+if [[ "$DISTDIR" != "$NEWDIR" ]]; then
+ mv $DISTDIR $NEWDIR
+ DISTDIR="$NEWDIR"
+fi
+cd $DISTDIR
+cp -p ../Makefile.PL .
+cp -pr ../gen-perl .
+cp -pr ../gen-perl2 .
+perl ../tools/FixupDist.pl
+cd ..
+tar cvzf --hard-dereference $DISTFILE $DISTDIR
+rm -r $DISTDIR
diff --git a/src/jaegertracing/thrift/lib/perl/coding_standards.md b/src/jaegertracing/thrift/lib/perl/coding_standards.md
new file mode 100644
index 000000000..e4e825555
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/coding_standards.md
@@ -0,0 +1,2 @@
+Please follow [General Coding Standards](/doc/coding_standards.md).
+Additional perl coding standards can be found in [perlstyle](http://perldoc.perl.org/perlstyle.html).
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift.pm
new file mode 100644
index 000000000..01985ea87
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift.pm
@@ -0,0 +1,36 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+#
+# Versioning
+#
+# Every perl module for Thrift will have the same version
+# declaration. For a production build, change it below to
+# something like "v0.11.0" and all of the packages in all
+# of the files will pick it up from here.
+#
+
+package Thrift;
+use version 0.77; our $VERSION = version->declare("v0.13.0");
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/BinaryProtocol.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/BinaryProtocol.pm
new file mode 100644
index 000000000..d62509a56
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/BinaryProtocol.pm
@@ -0,0 +1,518 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Bit::Vector;
+use Encode;
+use Thrift;
+use Thrift::Exception;
+use Thrift::MessageType;
+use Thrift::Protocol;
+use Thrift::Type;
+use utf8;
+
+#
+# Binary implementation of the Thrift protocol.
+#
+package Thrift::BinaryProtocol;
+use base('Thrift::Protocol');
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+use constant VERSION_MASK => 0xffff0000;
+use constant VERSION_1 => 0x80010000;
+use constant IS_BIG_ENDIAN => unpack('h*', pack('s', 1)) =~ m/01/;
+
+sub new
+{
+ my $classname = shift;
+ my $trans = shift;
+ my $self = $classname->SUPER::new($trans);
+
+ return bless($self,$classname);
+}
+
+sub writeMessageBegin
+{
+ my $self = shift;
+ my ($name, $type, $seqid) = @_;
+
+ return
+ $self->writeI32(VERSION_1 | $type) +
+ $self->writeString($name) +
+ $self->writeI32($seqid);
+}
+
+sub writeMessageEnd
+{
+ my $self = shift;
+ return 0;
+}
+
+sub writeStructBegin
+{
+ my $self = shift;
+ my $name = shift;
+ return 0;
+}
+
+sub writeStructEnd
+{
+ my $self = shift;
+ return 0;
+}
+
+sub writeFieldBegin
+{
+ my $self = shift;
+ my ($fieldName, $fieldType, $fieldId) = @_;
+
+ return
+ $self->writeByte($fieldType) +
+ $self->writeI16($fieldId);
+}
+
+sub writeFieldEnd
+{
+ my $self = shift;
+ return 0;
+}
+
+sub writeFieldStop
+{
+ my $self = shift;
+ return $self->writeByte(Thrift::TType::STOP);
+}
+
+sub writeMapBegin
+{
+ my $self = shift;
+ my ($keyType, $valType, $size) = @_;
+
+ return
+ $self->writeByte($keyType) +
+ $self->writeByte($valType) +
+ $self->writeI32($size);
+}
+
+sub writeMapEnd
+{
+ my $self = shift;
+ return 0;
+}
+
+sub writeListBegin
+{
+ my $self = shift;
+ my ($elemType, $size) = @_;
+
+ return
+ $self->writeByte($elemType) +
+ $self->writeI32($size);
+}
+
+sub writeListEnd
+{
+ my $self = shift;
+ return 0;
+}
+
+sub writeSetBegin
+{
+ my $self = shift;
+ my ($elemType, $size) = @_;
+
+ return
+ $self->writeByte($elemType) +
+ $self->writeI32($size);
+}
+
+sub writeSetEnd
+{
+ my $self = shift;
+ return 0;
+}
+
+sub writeBool
+{
+ my $self = shift;
+ my $value = shift;
+
+ my $data = pack('c', $value ? 1 : 0);
+ $self->{trans}->write($data, 1);
+ return 1;
+}
+
+sub writeByte
+{
+ my $self = shift;
+ my $value= shift;
+
+ my $data = pack('c', $value);
+ $self->{trans}->write($data, 1);
+ return 1;
+}
+
+sub writeI16
+{
+ my $self = shift;
+ my $value= shift;
+
+ my $data = pack('n', $value);
+ $self->{trans}->write($data, 2);
+ return 2;
+}
+
+sub writeI32
+{
+ my $self = shift;
+ my $value= shift;
+
+ my $data = pack('N', $value);
+ $self->{trans}->write($data, 4);
+ return 4;
+}
+
+sub writeI64
+{
+ my $self = shift;
+ my $value= shift;
+ my $data;
+
+ my $vec;
+ #stop annoying error
+ $vec = Bit::Vector->new_Dec(64, $value);
+ $data = pack 'NN', $vec->Chunk_Read(32, 32), $vec->Chunk_Read(32, 0);
+
+ $self->{trans}->write($data, 8);
+
+ return 8;
+}
+
+
+sub writeDouble
+{
+ my $self = shift;
+ my $value= shift;
+
+ my $data = pack('d', $value);
+ if (IS_BIG_ENDIAN) {
+ $self->{trans}->write($data, 8);
+ }
+ else {
+ $self->{trans}->write(scalar reverse($data), 8);
+ }
+ return 8;
+}
+
+sub writeString{
+ my $self = shift;
+ my $value= shift;
+
+ if( utf8::is_utf8($value) ){
+ $value = Encode::encode_utf8($value);
+ }
+
+ my $len = length($value);
+
+ my $result = $self->writeI32($len);
+
+ if ($len) {
+ $self->{trans}->write($value,$len);
+ }
+ return $result + $len;
+ }
+
+
+#
+#All references
+#
+sub readMessageBegin
+{
+ my $self = shift;
+ my ($name, $type, $seqid) = @_;
+
+ my $version = 0;
+ my $result = $self->readI32(\$version);
+ if (($version & VERSION_MASK) > 0) {
+ if (($version & VERSION_MASK) != VERSION_1) {
+ die Thrift::TProtocolException->new('Missing version identifier',
+ Thrift::TProtocolException::BAD_VERSION);
+ }
+ $$type = $version & 0x000000ff;
+ return
+ $result +
+ $self->readString($name) +
+ $self->readI32($seqid);
+ }
+ else { # old client support code
+ return
+ $result +
+ $self->readStringBody($name, $version) + # version here holds the size of the string
+ $self->readByte($type) +
+ $self->readI32($seqid);
+ }
+}
+
+sub readMessageEnd
+{
+ my $self = shift;
+ return 0;
+}
+
+sub readStructBegin
+{
+ my $self = shift;
+ my $name = shift;
+
+ $$name = '';
+
+ return 0;
+}
+
+sub readStructEnd
+{
+ my $self = shift;
+ return 0;
+}
+
+sub readFieldBegin
+{
+ my $self = shift;
+ my ($name, $fieldType, $fieldId) = @_;
+
+ my $result = $self->readByte($fieldType);
+
+ if ($$fieldType == Thrift::TType::STOP) {
+ $$fieldId = 0;
+ return $result;
+ }
+
+ $result += $self->readI16($fieldId);
+
+ return $result;
+}
+
+sub readFieldEnd() {
+ my $self = shift;
+ return 0;
+}
+
+sub readMapBegin
+{
+ my $self = shift;
+ my ($keyType, $valType, $size) = @_;
+
+ return
+ $self->readByte($keyType) +
+ $self->readByte($valType) +
+ $self->readI32($size);
+}
+
+sub readMapEnd()
+{
+ my $self = shift;
+ return 0;
+}
+
+sub readListBegin
+{
+ my $self = shift;
+ my ($elemType, $size) = @_;
+
+ return
+ $self->readByte($elemType) +
+ $self->readI32($size);
+}
+
+sub readListEnd
+{
+ my $self = shift;
+ return 0;
+}
+
+sub readSetBegin
+{
+ my $self = shift;
+ my ($elemType, $size) = @_;
+
+ return
+ $self->readByte($elemType) +
+ $self->readI32($size);
+}
+
+sub readSetEnd
+{
+ my $self = shift;
+ return 0;
+}
+
+sub readBool
+{
+ my $self = shift;
+ my $value = shift;
+
+ my $data = $self->{trans}->readAll(1);
+ my @arr = unpack('c', $data);
+ $$value = $arr[0] == 1;
+ return 1;
+}
+
+sub readByte
+{
+ my $self = shift;
+ my $value = shift;
+
+ my $data = $self->{trans}->readAll(1);
+ my @arr = unpack('c', $data);
+ $$value = $arr[0];
+ return 1;
+}
+
+sub readI16
+{
+ my $self = shift;
+ my $value = shift;
+
+ my $data = $self->{trans}->readAll(2);
+
+ my @arr = unpack('n', $data);
+
+ $$value = $arr[0];
+
+ if ($$value > 0x7fff) {
+ $$value = 0 - (($$value - 1) ^ 0xffff);
+ }
+
+ return 2;
+}
+
+sub readI32
+{
+ my $self = shift;
+ my $value= shift;
+
+ my $data = $self->{trans}->readAll(4);
+ my @arr = unpack('N', $data);
+
+ $$value = $arr[0];
+ if ($$value > 0x7fffffff) {
+ $$value = 0 - (($$value - 1) ^ 0xffffffff);
+ }
+ return 4;
+}
+
+sub readI64
+{
+ my $self = shift;
+ my $value = shift;
+
+ my $data = $self->{trans}->readAll(8);
+
+ my ($hi,$lo)=unpack('NN',$data);
+
+ my $vec = Bit::Vector->new(64);
+
+ $vec->Chunk_Store(32,32,$hi);
+ $vec->Chunk_Store(32,0,$lo);
+
+ $$value = $vec->to_Dec();
+
+ return 8;
+}
+
+sub readDouble
+{
+ my $self = shift;
+ my $value = shift;
+
+ my $data;
+ if (IS_BIG_ENDIAN) {
+ $data = $self->{trans}->readAll(8);
+ }
+ else {
+ $data = scalar reverse($self->{trans}->readAll(8));
+ }
+
+ my @arr = unpack('d', $data);
+
+ $$value = $arr[0];
+
+ return 8;
+}
+
+sub readString
+{
+ my $self = shift;
+ my $value = shift;
+
+ my $len;
+ my $result = $self->readI32(\$len);
+
+ if ($len) {
+ $$value = $self->{trans}->readAll($len);
+ }
+ else {
+ $$value = '';
+ }
+
+ return $result + $len;
+}
+
+sub readStringBody
+{
+ my $self = shift;
+ my $value = shift;
+ my $len = shift;
+
+ if ($len) {
+ $$value = $self->{trans}->readAll($len);
+ }
+ else {
+ $$value = '';
+ }
+
+ return $len;
+}
+
+#
+# Binary Protocol Factory
+#
+package Thrift::BinaryProtocolFactory;
+use base('Thrift::TProtocolFactory');
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+sub new
+{
+ my $classname = shift;
+ my $self = $classname->SUPER::new();
+
+ return bless($self,$classname);
+}
+
+sub getProtocol{
+ my $self = shift;
+ my $trans = shift;
+
+ return Thrift::BinaryProtocol->new($trans);
+}
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/BufferedTransport.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/BufferedTransport.pm
new file mode 100644
index 000000000..6b5bf7a4c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/BufferedTransport.pm
@@ -0,0 +1,139 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Thrift;
+use Thrift::Exception;
+use Thrift::Transport;
+
+package Thrift::BufferedTransport;
+use base('Thrift::Transport');
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+sub new
+{
+ my $classname = shift;
+ my $transport = shift;
+ my $rBufSize = shift || 512;
+ my $wBufSize = shift || 512;
+
+ my $self = {
+ transport => $transport,
+ rBufSize => $rBufSize,
+ wBufSize => $wBufSize,
+ wBuf => '',
+ rBuf => '',
+ };
+
+ return bless($self,$classname);
+}
+
+sub isOpen
+{
+ my $self = shift;
+
+ return $self->{transport}->isOpen();
+}
+
+sub open
+{
+ my $self = shift;
+ $self->{transport}->open();
+}
+
+sub close()
+{
+ my $self = shift;
+ $self->{transport}->close();
+}
+
+sub readAll
+{
+ my $self = shift;
+ my $len = shift;
+
+ return $self->{transport}->readAll($len);
+}
+
+sub read
+{
+ my $self = shift;
+ my $len = shift;
+ my $ret;
+
+ # Methinks Perl is already buffering these for us
+ return $self->{transport}->read($len);
+}
+
+sub write
+{
+ my $self = shift;
+ my $buf = shift;
+
+ $self->{wBuf} .= $buf;
+ if (length($self->{wBuf}) >= $self->{wBufSize}) {
+ $self->{transport}->write($self->{wBuf});
+ $self->{wBuf} = '';
+ }
+}
+
+sub flush
+{
+ my $self = shift;
+
+ if (length($self->{wBuf}) > 0) {
+ $self->{transport}->write($self->{wBuf});
+ $self->{wBuf} = '';
+ }
+ $self->{transport}->flush();
+}
+
+
+#
+# BufferedTransport factory creates buffered transport objects from transports
+#
+package Thrift::BufferedTransportFactory;
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+sub new {
+ my $classname = shift;
+ my $self = {};
+
+ return bless($self,$classname);
+}
+
+#
+# Build a buffered transport from the base transport
+#
+# @return Thrift::BufferedTransport transport
+#
+sub getTransport
+{
+ my $self = shift;
+ my $trans = shift;
+
+ my $buffered = Thrift::BufferedTransport->new($trans);
+ return $buffered;
+}
+
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/Exception.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/Exception.pm
new file mode 100644
index 000000000..e4040689c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/Exception.pm
@@ -0,0 +1,161 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Thrift;
+use Thrift::Type;
+
+package Thrift::TException;
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+use overload '""' => sub {
+ return
+ sprintf '%s error: %s (code %s)',
+ ref( $_[0] ),
+ ( $_[0]->{message} || 'empty message' ),
+ ( defined $_[0]->{code} ? $_[0]->{code} : 'undefined' );
+ };
+
+sub new {
+ my $classname = shift;
+ my $self = {message => shift, code => shift || 0};
+
+ return bless($self,$classname);
+}
+
+package Thrift::TApplicationException;
+use parent -norequire, 'Thrift::TException';
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+use constant UNKNOWN => 0;
+use constant UNKNOWN_METHOD => 1;
+use constant INVALID_MESSAGE_TYPE => 2;
+use constant WRONG_METHOD_NAME => 3;
+use constant BAD_SEQUENCE_ID => 4;
+use constant MISSING_RESULT => 5;
+use constant INTERNAL_ERROR => 6;
+use constant PROTOCOL_ERROR => 7;
+use constant INVALID_TRANSFORM => 8;
+use constant INVALID_PROTOCOL => 9;
+use constant UNSUPPORTED_CLIENT_TYPE => 10;
+
+sub new {
+ my $classname = shift;
+
+ my $self = $classname->SUPER::new(@_);
+
+ return bless($self,$classname);
+}
+
+sub read {
+ my $self = shift;
+ my $input = shift;
+
+ my $xfer = 0;
+ my $fname = undef;
+ my $ftype = 0;
+ my $fid = 0;
+
+ $xfer += $input->readStructBegin(\$fname);
+
+ while (1)
+ {
+ $xfer += $input->readFieldBegin(\$fname, \$ftype, \$fid);
+ if ($ftype == Thrift::TType::STOP) {
+ last; next;
+ }
+
+ SWITCH: for($fid)
+ {
+ /1/ && do{
+
+ if ($ftype == Thrift::TType::STRING) {
+ $xfer += $input->readString(\$self->{message});
+ }
+ else {
+ $xfer += $input->skip($ftype);
+ }
+
+ last;
+ };
+
+ /2/ && do{
+ if ($ftype == Thrift::TType::I32) {
+ $xfer += $input->readI32(\$self->{code});
+ }
+ else {
+ $xfer += $input->skip($ftype);
+ }
+ last;
+ };
+
+ $xfer += $input->skip($ftype);
+ }
+
+ $xfer += $input->readFieldEnd();
+ }
+ $xfer += $input->readStructEnd();
+
+ return $xfer;
+}
+
+sub write {
+ my $self = shift;
+ my $output = shift;
+
+ my $xfer = 0;
+
+ $xfer += $output->writeStructBegin('TApplicationException');
+
+ if ($self->getMessage()) {
+ $xfer += $output->writeFieldBegin('message', Thrift::TType::STRING, 1);
+ $xfer += $output->writeString($self->getMessage());
+ $xfer += $output->writeFieldEnd();
+ }
+
+ if ($self->getCode()) {
+ $xfer += $output->writeFieldBegin('type', Thrift::TType::I32, 2);
+ $xfer += $output->writeI32($self->getCode());
+ $xfer += $output->writeFieldEnd();
+ }
+
+ $xfer += $output->writeFieldStop();
+ $xfer += $output->writeStructEnd();
+
+ return $xfer;
+}
+
+sub getMessage
+{
+ my $self = shift;
+
+ return $self->{message};
+}
+
+sub getCode
+{
+ my $self = shift;
+
+ return $self->{code};
+}
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/FramedTransport.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/FramedTransport.pm
new file mode 100644
index 000000000..ba89ba3e0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/FramedTransport.pm
@@ -0,0 +1,194 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Thrift;
+use Thrift::Transport;
+
+#
+# Framed transport. Writes and reads data in chunks that are stamped with
+# their length.
+#
+# @package thrift.transport
+#
+package Thrift::FramedTransport;
+use base('Thrift::Transport');
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+sub new
+{
+ my $classname = shift;
+ my $transport = shift;
+ my $read = shift || 1;
+ my $write = shift || 1;
+
+ my $self = {
+ transport => $transport,
+ read => $read,
+ write => $write,
+ wBuf => '',
+ rBuf => '',
+ };
+
+ return bless($self,$classname);
+}
+
+sub isOpen
+{
+ my $self = shift;
+ return $self->{transport}->isOpen();
+}
+
+sub open
+{
+ my $self = shift;
+
+ $self->{transport}->open();
+}
+
+sub close
+{
+ my $self = shift;
+
+ if (defined $self->{transport}) {
+ $self->{transport}->close();
+ }
+}
+
+#
+# Reads from the buffer. When more data is required reads another entire
+# chunk and serves future reads out of that.
+#
+# @param int $len How much data
+#
+sub read
+{
+
+ my $self = shift;
+ my $len = shift;
+
+ if (!$self->{read}) {
+ return $self->{transport}->read($len);
+ }
+
+ if (length($self->{rBuf}) == 0) {
+ $self->_readFrame();
+ }
+
+
+ # Just return full buff
+ if ($len > length($self->{rBuf})) {
+ my $out = $self->{rBuf};
+ $self->{rBuf} = '';
+ return $out;
+ }
+
+ # Return substr
+ my $out = substr($self->{rBuf}, 0, $len);
+ $self->{rBuf} = substr($self->{rBuf}, $len);
+ return $out;
+}
+
+#
+# Reads a chunk of data into the internal read buffer.
+# (private)
+sub _readFrame
+{
+ my $self = shift;
+ my $buf = $self->{transport}->readAll(4);
+ my @val = unpack('N', $buf);
+ my $sz = $val[0];
+
+ $self->{rBuf} = $self->{transport}->readAll($sz);
+}
+
+#
+# Writes some data to the pending output buffer.
+#
+# @param string $buf The data
+# @param int $len Limit of bytes to write
+#
+sub write
+{
+ my $self = shift;
+ my $buf = shift;
+ my $len = shift;
+
+ unless($self->{write}) {
+ return $self->{transport}->write($buf, $len);
+ }
+
+ if ( defined $len && $len < length($buf)) {
+ $buf = substr($buf, 0, $len);
+ }
+
+ $self->{wBuf} .= $buf;
+ }
+
+#
+# Writes the output buffer to the stream in the format of a 4-byte length
+# followed by the actual data.
+#
+sub flush
+{
+ my $self = shift;
+
+ unless ($self->{write}) {
+ return $self->{transport}->flush();
+ }
+
+ my $out = pack('N', length($self->{wBuf}));
+ $out .= $self->{wBuf};
+ $self->{transport}->write($out);
+ $self->{transport}->flush();
+ $self->{wBuf} = '';
+
+}
+
+#
+# FramedTransport factory creates framed transport objects from transports
+#
+package Thrift::FramedTransportFactory;
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+sub new {
+ my $classname = shift;
+ my $self = {};
+
+ return bless($self, $classname);
+}
+
+#
+# Build a framed transport from the base transport
+#
+# @return Thrift::FramedTransport transport
+#
+sub getTransport
+{
+ my $self = shift;
+ my $trans = shift;
+
+ my $buffered = Thrift::FramedTransport->new($trans);
+ return $buffered;
+}
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/HttpClient.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/HttpClient.pm
new file mode 100644
index 000000000..40ec9ce20
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/HttpClient.pm
@@ -0,0 +1,204 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use HTTP::Request;
+use IO::String;
+use LWP::UserAgent;
+use Thrift;
+use Thrift::Exception;
+use Thrift::Transport;
+
+package Thrift::HttpClient;
+use base('Thrift::Transport');
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+sub new
+{
+ my $classname = shift;
+ my $url = shift || 'http://localhost:9090';
+
+ my $out = IO::String->new;
+ binmode($out);
+
+ my $self = {
+ url => $url,
+ out => $out,
+ timeout => 100,
+ handle => undef,
+ headers => {},
+ };
+
+ return bless($self,$classname);
+}
+
+sub setTimeout
+{
+ my $self = shift;
+ my $timeout = shift;
+
+ $self->{timeout} = $timeout;
+}
+
+sub setRecvTimeout
+{
+ warn 'setRecvTimeout is deprecated - use setTimeout instead';
+ # note: recvTimeout was never used so we do not need to do anything here
+}
+
+sub setSendTimeout
+{
+ my $self = shift;
+ my $timeout = shift;
+
+ warn 'setSendTimeout is deprecated - use setTimeout instead';
+
+ $self->setTimeout($timeout);
+}
+
+sub setHeader
+{
+ my $self = shift;
+ my ($name, $value) = @_;
+
+ $self->{headers}->{$name} = $value;
+}
+
+#
+# Tests whether this is open
+#
+# @return bool true if the socket is open
+#
+sub isOpen
+{
+ return 1;
+}
+
+sub open {}
+
+#
+# Cleans up the buffer.
+#
+sub close
+{
+ my $self = shift;
+ if (defined($self->{io})) {
+ close($self->{io});
+ $self->{io} = undef;
+ }
+}
+
+#
+# Guarantees that the full amount of data is read.
+#
+# @return string The data, of exact length
+# @throws TTransportException if cannot read data
+#
+sub readAll
+{
+ my $self = shift;
+ my $len = shift;
+
+ my $buf = $self->read($len);
+
+ if (!defined($buf)) {
+ die Thrift::TTransportException->new("TSocket: Could not read $len bytes from input buffer",
+ Thrift::TTransportException::END_OF_FILE);
+ }
+ return $buf;
+}
+
+#
+# Read and return string
+#
+sub read
+{
+ my $self = shift;
+ my $len = shift;
+
+ my $buf;
+
+ my $in = $self->{in};
+
+ if (!defined($in)) {
+ die Thrift::TTransportException->new('Response buffer is empty, no request.',
+ Thrift::TTransportException::END_OF_FILE);
+ }
+ eval {
+ my $ret = sysread($in, $buf, $len);
+ if (! defined($ret)) {
+ die Thrift::TTransportException->new('No more data available.',
+ Thrift::TTransportException::TIMED_OUT);
+ }
+ };
+ if($@){
+ die Thrift::TTransportException->new("$@", Thrift::TTransportException::UNKNOWN);
+ }
+
+ return $buf;
+}
+
+#
+# Write string
+#
+sub write
+{
+ my $self = shift;
+ my $buf = shift;
+ $self->{out}->print($buf);
+}
+
+#
+# Flush output (do the actual HTTP/HTTPS request)
+#
+sub flush
+{
+ my $self = shift;
+
+ my $ua = LWP::UserAgent->new(
+ 'timeout' => ($self->{timeout} / 1000),
+ 'agent' => 'Perl/THttpClient'
+ );
+ $ua->default_header('Accept' => 'application/x-thrift');
+ $ua->default_header('Content-Type' => 'application/x-thrift');
+ $ua->cookie_jar({}); # hash to remember cookies between redirects
+
+ my $out = $self->{out};
+ $out->setpos(0); # rewind
+ my $buf = join('', <$out>);
+
+ my $request = HTTP::Request->new(POST => $self->{url}, ($self->{headers} || undef), $buf);
+ my $response = $ua->request($request);
+ my $content_ref = $response->content_ref;
+
+ my $in = IO::String->new($content_ref);
+ binmode($in);
+ $self->{in} = $in;
+ $in->setpos(0); # rewind
+
+ # reset write buffer
+ $out = IO::String->new;
+ binmode($out);
+ $self->{out} = $out;
+}
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/MemoryBuffer.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/MemoryBuffer.pm
new file mode 100644
index 000000000..be97ce4f8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/MemoryBuffer.pm
@@ -0,0 +1,148 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Thrift;
+use Thrift::Transport;
+
+package Thrift::MemoryBuffer;
+use base('Thrift::Transport');
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+sub new
+{
+ my $classname = shift;
+
+ my $bufferSize= shift || 1024;
+
+ my $self = {
+ buffer => '',
+ bufferSize => $bufferSize,
+ wPos => 0,
+ rPos => 0,
+ };
+
+ return bless($self,$classname);
+}
+
+sub isOpen
+{
+ return 1;
+}
+
+sub open
+{
+
+}
+
+sub close
+{
+
+}
+
+sub peek
+{
+ my $self = shift;
+ return($self->{rPos} < $self->{wPos});
+}
+
+
+sub getBuffer
+{
+ my $self = shift;
+ return $self->{buffer};
+}
+
+sub resetBuffer
+{
+ my $self = shift;
+
+ my $new_buffer = shift || '';
+
+ $self->{buffer} = $new_buffer;
+ $self->{bufferSize} = length($new_buffer);
+ $self->{wPos} = length($new_buffer);
+ $self->{rPos} = 0;
+}
+
+sub available
+{
+ my $self = shift;
+ return ($self->{wPos} - $self->{rPos});
+}
+
+sub read
+{
+ my $self = shift;
+ my $len = shift;
+ my $ret;
+
+ my $avail = ($self->{wPos} - $self->{rPos});
+ return '' if $avail == 0;
+
+ #how much to give
+ my $give = $len;
+ $give = $avail if $avail < $len;
+
+ $ret = substr($self->{buffer},$self->{rPos},$give);
+
+ $self->{rPos} += $give;
+
+ return $ret;
+}
+
+sub readAll
+{
+ my $self = shift;
+ my $len = shift;
+
+ my $avail = ($self->{wPos} - $self->{rPos});
+ if ($avail < $len) {
+ die TTransportException->new("Attempt to readAll($len) found only $avail available",
+ Thrift::TTransportException::END_OF_FILE);
+ }
+
+ my $data = '';
+ my $got = 0;
+
+ while (($got = length($data)) < $len) {
+ $data .= $self->read($len - $got);
+ }
+
+ return $data;
+}
+
+sub write
+{
+ my $self = shift;
+ my $buf = shift;
+
+ $self->{buffer} .= $buf;
+ $self->{wPos} += length($buf);
+}
+
+sub flush
+{
+
+}
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/MessageType.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/MessageType.pm
new file mode 100644
index 000000000..d25c2f771
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/MessageType.pm
@@ -0,0 +1,37 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Thrift;
+
+#
+# Message types for RPC
+#
+package Thrift::TMessageType;
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+use constant CALL => 1;
+use constant REPLY => 2;
+use constant EXCEPTION => 3;
+use constant ONEWAY => 4;
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/MultiplexedProcessor.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/MultiplexedProcessor.pm
new file mode 100644
index 000000000..ae925d7cd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/MultiplexedProcessor.pm
@@ -0,0 +1,133 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Thrift;
+use Thrift::MessageType;
+use Thrift::MultiplexedProtocol;
+use Thrift::Protocol;
+use Thrift::ProtocolDecorator;
+
+package Thrift::StoredMessageProtocol;
+use base qw(Thrift::ProtocolDecorator);
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+sub new {
+ my $classname = shift;
+ my $protocol = shift;
+ my $fname = shift;
+ my $mtype = shift;
+ my $rseqid = shift;
+ my $self = $classname->SUPER::new($protocol);
+
+ $self->{fname} = $fname;
+ $self->{mtype} = $mtype;
+ $self->{rseqid} = $rseqid;
+
+ return bless($self,$classname);
+}
+
+sub readMessageBegin
+{
+ my $self = shift;
+ my $name = shift;
+ my $type = shift;
+ my $seqid = shift;
+
+ $$name = $self->{fname};
+ $$type = $self->{mtype};
+ $$seqid = $self->{rseqid};
+}
+
+package Thrift::MultiplexedProcessor;
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+sub new {
+ my $classname = shift;
+ my $self = {};
+
+ $self->{serviceProcessorMap} = {};
+ $self->{defaultProcessor} = undef;
+
+ return bless($self,$classname);
+}
+
+sub defaultProcessor {
+ my $self = shift;
+ my $processor = shift;
+
+ $self->{defaultProcessor} = $processor;
+}
+
+sub registerProcessor {
+ my $self = shift;
+ my $serviceName = shift;
+ my $processor = shift;
+
+ $self->{serviceProcessorMap}->{$serviceName} = $processor;
+}
+
+sub process {
+ my $self = shift;
+ my $input = shift;
+ my $output = shift;
+
+ #
+ # Use the actual underlying protocol (e.g. BinaryProtocol) to read the
+ # message header. This pulls the message "off the wire", which we'll
+ # deal with at the end of this method.
+ #
+
+ my ($fname, $mtype, $rseqid);
+ $input->readMessageBegin(\$fname, \$mtype, \$rseqid);
+
+ if ($mtype ne Thrift::TMessageType::CALL && $mtype ne Thrift::TMessageType::ONEWAY) {
+ die Thrift::TException->new('This should not have happened!?');
+ }
+
+ # Extract the service name and the new Message name.
+ if (index($fname, Thrift::MultiplexedProtocol::SEPARATOR) == -1) {
+ if (defined $self->{defaultProcessor}) {
+ return $self->{defaultProcessor}->process(
+ Thrift::StoredMessageProtocol->new($input, $fname, $mtype, $rseqid), $output
+ );
+ } else {
+ die Thrift::TException->new("Service name not found in message name: {$fname} and no default processor defined. Did you " .
+ 'forget to use a MultiplexProtocol in your client?');
+ }
+ }
+
+ (my $serviceName, my $messageName) = split(':', $fname, 2);
+
+ if (!exists($self->{serviceProcessorMap}->{$serviceName})) {
+ die Thrift::TException->new("Service name not found: {$serviceName}. Did you forget " .
+ 'to call registerProcessor()?');
+ }
+
+ # Dispatch processing to the stored processor
+ my $processor = $self->{serviceProcessorMap}->{$serviceName};
+ return $processor->process(
+ Thrift::StoredMessageProtocol->new($input, $messageName, $mtype, $rseqid), $output
+ );
+}
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/MultiplexedProtocol.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/MultiplexedProtocol.pm
new file mode 100644
index 000000000..5b5b60bc7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/MultiplexedProtocol.pm
@@ -0,0 +1,68 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Thrift;
+use Thrift::MessageType;
+use Thrift::Protocol;
+use Thrift::ProtocolDecorator;
+
+package Thrift::MultiplexedProtocol;
+use base qw(Thrift::ProtocolDecorator);
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+use constant SEPARATOR => ':';
+
+sub new {
+ my $classname = shift;
+ my $protocol = shift;
+ my $serviceName = shift;
+ my $self = $classname->SUPER::new($protocol);
+
+ $self->{serviceName} = $serviceName;
+
+ return bless($self,$classname);
+}
+
+#
+# Writes the message header.
+# Prepends the service name to the function name, separated by MultiplexedProtocol::SEPARATOR.
+#
+# @param string $name Function name.
+# @param int $type Message type.
+# @param int $seqid The sequence id of this message.
+#
+sub writeMessageBegin
+{
+ my $self = shift;
+ my ($name, $type, $seqid) = @_;
+
+ if ($type == Thrift::TMessageType::CALL || $type == Thrift::TMessageType::ONEWAY) {
+ my $nameWithService = $self->{serviceName}.SEPARATOR.$name;
+ $self->SUPER::writeMessageBegin($nameWithService, $type, $seqid);
+ }
+ else {
+ $self->SUPER::writeMessageBegin($name, $type, $seqid);
+ }
+}
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/Protocol.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/Protocol.pm
new file mode 100644
index 000000000..26ef46a00
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/Protocol.pm
@@ -0,0 +1,549 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Thrift;
+use Thrift::Exception;
+use Thrift::Type;
+
+#
+# Protocol exceptions
+#
+package Thrift::TProtocolException;
+use base('Thrift::TException');
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+use constant UNKNOWN => 0;
+use constant INVALID_DATA => 1;
+use constant NEGATIVE_SIZE => 2;
+use constant SIZE_LIMIT => 3;
+use constant BAD_VERSION => 4;
+use constant NOT_IMPLEMENTED => 5;
+use constant DEPTH_LIMIT => 6;
+
+sub new {
+ my $classname = shift;
+
+ my $self = $classname->SUPER::new();
+
+ return bless($self,$classname);
+}
+
+#
+# Protocol base class module.
+#
+package Thrift::Protocol;
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+sub new {
+ my $classname = shift;
+ my $self = {};
+
+ my $trans = shift;
+ $self->{trans}= $trans;
+
+ return bless($self,$classname);
+}
+
+sub getTransport
+{
+ my $self = shift;
+
+ return $self->{trans};
+}
+
+#
+# Writes the message header
+#
+# @param string $name Function name
+# @param int $type message type TMessageType::CALL or TMessageType::REPLY
+# @param int $seqid The sequence id of this message
+#
+sub writeMessageBegin
+{
+ my ($name, $type, $seqid);
+ die 'abstract';
+}
+
+#
+# Close the message
+#
+sub writeMessageEnd {
+ die 'abstract';
+}
+
+#
+# Writes a struct header.
+#
+# @param string $name Struct name
+# @throws TProtocolException on write error
+# @return int How many bytes written
+#
+sub writeStructBegin {
+ my ($name);
+
+ die 'abstract';
+}
+
+#
+# Close a struct.
+#
+# @throws TProtocolException on write error
+# @return int How many bytes written
+#
+sub writeStructEnd {
+ die 'abstract';
+}
+
+#
+# Starts a field.
+#
+# @param string $name Field name
+# @param int $type Field type
+# @param int $fid Field id
+# @throws TProtocolException on write error
+# @return int How many bytes written
+#
+sub writeFieldBegin {
+ my ($fieldName, $fieldType, $fieldId);
+
+ die 'abstract';
+}
+
+sub writeFieldEnd {
+ die 'abstract';
+}
+
+sub writeFieldStop {
+ die 'abstract';
+}
+
+sub writeMapBegin {
+ my ($keyType, $valType, $size);
+
+ die 'abstract';
+}
+
+sub writeMapEnd {
+ die 'abstract';
+}
+
+sub writeListBegin {
+ my ($elemType, $size);
+ die 'abstract';
+}
+
+sub writeListEnd {
+ die 'abstract';
+}
+
+sub writeSetBegin {
+ my ($elemType, $size);
+ die 'abstract';
+}
+
+sub writeSetEnd {
+ die 'abstract';
+}
+
+sub writeBool {
+ my ($bool);
+ die 'abstract';
+}
+
+sub writeByte {
+ my ($byte);
+ die 'abstract';
+}
+
+sub writeI16 {
+ my ($i16);
+ die 'abstract';
+}
+
+sub writeI32 {
+ my ($i32);
+ die 'abstract';
+}
+
+sub writeI64 {
+ my ($i64);
+ die 'abstract';
+}
+
+sub writeDouble {
+ my ($dub);
+ die 'abstract';
+}
+
+sub writeString
+{
+ my ($str);
+ die 'abstract';
+}
+
+#
+# Reads the message header
+#
+# @param string $name Function name
+# @param int $type message type TMessageType::CALL or TMessageType::REPLY
+# @parem int $seqid The sequence id of this message
+#
+sub readMessageBegin
+{
+ my ($name, $type, $seqid);
+ die 'abstract';
+}
+
+#
+# Read the close of message
+#
+sub readMessageEnd
+{
+ die 'abstract';
+}
+
+sub readStructBegin
+{
+ my($name);
+
+ die 'abstract';
+}
+
+sub readStructEnd
+{
+ die 'abstract';
+}
+
+sub readFieldBegin
+{
+ my ($name, $fieldType, $fieldId);
+ die 'abstract';
+}
+
+sub readFieldEnd
+{
+ die 'abstract';
+}
+
+sub readMapBegin
+{
+ my ($keyType, $valType, $size);
+ die 'abstract';
+}
+
+sub readMapEnd
+{
+ die 'abstract';
+}
+
+sub readListBegin
+{
+ my ($elemType, $size);
+ die 'abstract';
+}
+
+sub readListEnd
+{
+ die 'abstract';
+}
+
+sub readSetBegin
+{
+ my ($elemType, $size);
+ die 'abstract';
+}
+
+sub readSetEnd
+{
+ die 'abstract';
+}
+
+sub readBool
+{
+ my ($bool);
+ die 'abstract';
+}
+
+sub readByte
+{
+ my ($byte);
+ die 'abstract';
+}
+
+sub readI16
+{
+ my ($i16);
+ die 'abstract';
+}
+
+sub readI32
+{
+ my ($i32);
+ die 'abstract';
+}
+
+sub readI64
+{
+ my ($i64);
+ die 'abstract';
+}
+
+sub readDouble
+{
+ my ($dub);
+ die 'abstract';
+}
+
+sub readString
+{
+ my ($str);
+ die 'abstract';
+}
+
+#
+# The skip function is a utility to parse over unrecognized data without
+# causing corruption.
+#
+# @param TType $type What type is it
+#
+sub skip
+{
+ my $self = shift;
+ my $type = shift;
+
+ my $ref;
+ my $result;
+ my $i;
+
+ if($type == Thrift::TType::BOOL)
+ {
+ return $self->readBool(\$ref);
+ }
+ elsif($type == Thrift::TType::BYTE){
+ return $self->readByte(\$ref);
+ }
+ elsif($type == Thrift::TType::I16){
+ return $self->readI16(\$ref);
+ }
+ elsif($type == Thrift::TType::I32){
+ return $self->readI32(\$ref);
+ }
+ elsif($type == Thrift::TType::I64){
+ return $self->readI64(\$ref);
+ }
+ elsif($type == Thrift::TType::DOUBLE){
+ return $self->readDouble(\$ref);
+ }
+ elsif($type == Thrift::TType::STRING)
+ {
+ return $self->readString(\$ref);
+ }
+ elsif($type == Thrift::TType::STRUCT)
+ {
+ $result = $self->readStructBegin(\$ref);
+ while (1) {
+ my ($ftype,$fid);
+ $result += $self->readFieldBegin(\$ref, \$ftype, \$fid);
+ if ($ftype == Thrift::TType::STOP) {
+ last;
+ }
+ $result += $self->skip($ftype);
+ $result += $self->readFieldEnd();
+ }
+ $result += $self->readStructEnd();
+ return $result;
+ }
+ elsif($type == Thrift::TType::MAP)
+ {
+ my($keyType,$valType,$size);
+ $result = $self->readMapBegin(\$keyType, \$valType, \$size);
+ for ($i = 0; $i < $size; $i++) {
+ $result += $self->skip($keyType);
+ $result += $self->skip($valType);
+ }
+ $result += $self->readMapEnd();
+ return $result;
+ }
+ elsif($type == Thrift::TType::SET)
+ {
+ my ($elemType,$size);
+ $result = $self->readSetBegin(\$elemType, \$size);
+ for ($i = 0; $i < $size; $i++) {
+ $result += $self->skip($elemType);
+ }
+ $result += $self->readSetEnd();
+ return $result;
+ }
+ elsif($type == Thrift::TType::LIST)
+ {
+ my ($elemType,$size);
+ $result = $self->readListBegin(\$elemType, \$size);
+ for ($i = 0; $i < $size; $i++) {
+ $result += $self->skip($elemType);
+ }
+ $result += $self->readListEnd();
+ return $result;
+ }
+
+ die Thrift::TProtocolException->new("Type $type not recognized --- corrupt data?",
+ Thrift::TProtocolException::INVALID_DATA);
+
+ }
+
+#
+# Utility for skipping binary data
+#
+# @param TTransport $itrans TTransport object
+# @param int $type Field type
+#
+sub skipBinary
+{
+ my $self = shift;
+ my $itrans = shift;
+ my $type = shift;
+
+ if($type == Thrift::TType::BOOL)
+ {
+ return $itrans->readAll(1);
+ }
+ elsif($type == Thrift::TType::BYTE)
+ {
+ return $itrans->readAll(1);
+ }
+ elsif($type == Thrift::TType::I16)
+ {
+ return $itrans->readAll(2);
+ }
+ elsif($type == Thrift::TType::I32)
+ {
+ return $itrans->readAll(4);
+ }
+ elsif($type == Thrift::TType::I64)
+ {
+ return $itrans->readAll(8);
+ }
+ elsif($type == Thrift::TType::DOUBLE)
+ {
+ return $itrans->readAll(8);
+ }
+ elsif( $type == Thrift::TType::STRING )
+ {
+ my @len = unpack('N', $itrans->readAll(4));
+ my $len = $len[0];
+ if ($len > 0x7fffffff) {
+ $len = 0 - (($len - 1) ^ 0xffffffff);
+ }
+ return 4 + $itrans->readAll($len);
+ }
+ elsif( $type == Thrift::TType::STRUCT )
+ {
+ my $result = 0;
+ while (1) {
+ my $ftype = 0;
+ my $fid = 0;
+ my $data = $itrans->readAll(1);
+ my @arr = unpack('c', $data);
+ $ftype = $arr[0];
+ if ($ftype == Thrift::TType::STOP) {
+ last;
+ }
+ # I16 field id
+ $result += $itrans->readAll(2);
+ $result += $self->skipBinary($itrans, $ftype);
+ }
+ return $result;
+ }
+ elsif($type == Thrift::TType::MAP)
+ {
+ # Ktype
+ my $data = $itrans->readAll(1);
+ my @arr = unpack('c', $data);
+ my $ktype = $arr[0];
+ # Vtype
+ $data = $itrans->readAll(1);
+ @arr = unpack('c', $data);
+ my $vtype = $arr[0];
+ # Size
+ $data = $itrans->readAll(4);
+ @arr = unpack('N', $data);
+ my $size = $arr[0];
+ if ($size > 0x7fffffff) {
+ $size = 0 - (($size - 1) ^ 0xffffffff);
+ }
+ my $result = 6;
+ for (my $i = 0; $i < $size; $i++) {
+ $result += $self->skipBinary($itrans, $ktype);
+ $result += $self->skipBinary($itrans, $vtype);
+ }
+ return $result;
+ }
+ elsif($type == Thrift::TType::SET || $type == Thrift::TType::LIST)
+ {
+ # Vtype
+ my $data = $itrans->readAll(1);
+ my @arr = unpack('c', $data);
+ my $vtype = $arr[0];
+ # Size
+ $data = $itrans->readAll(4);
+ @arr = unpack('N', $data);
+ my $size = $arr[0];
+ if ($size > 0x7fffffff) {
+ $size = 0 - (($size - 1) ^ 0xffffffff);
+ }
+ my $result = 5;
+ for (my $i = 0; $i < $size; $i++) {
+ $result += $self->skipBinary($itrans, $vtype);
+ }
+ return $result;
+ }
+
+ die Thrift::TProtocolException->new("Type $type not recognized --- corrupt data?",
+ Thrift::TProtocolException::INVALID_DATA);
+}
+
+#
+# Protocol factory creates protocol objects from transports
+#
+package Thrift::TProtocolFactory;
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+sub new {
+ my $classname = shift;
+ my $self = {};
+
+ return bless($self,$classname);
+}
+
+#
+# Build a protocol from the base transport
+#
+# @return TProtcol protocol
+#
+sub getProtocol
+{
+ my ($trans);
+ die 'interface';
+}
+
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/ProtocolDecorator.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/ProtocolDecorator.pm
new file mode 100644
index 000000000..cc5c9dae0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/ProtocolDecorator.pm
@@ -0,0 +1,363 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Thrift;
+use Thrift::Protocol;
+
+package Thrift::ProtocolDecorator;
+use base qw(Thrift::Protocol);
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+sub new {
+ my $classname = shift;
+ my $protocol = shift;
+ my $self = $classname->SUPER::new($protocol->getTransport());
+
+ $self->{concreteProtocol} = $protocol;
+
+ return bless($self,$classname);
+}
+
+#
+# Writes the message header
+#
+# @param string $name Function name
+# @param int $type message type TMessageType::CALL or TMessageType::REPLY
+# @param int $seqid The sequence id of this message
+#
+sub writeMessageBegin {
+ my $self = shift;
+ my ($name, $type, $seqid) = @_;
+
+ return $self->{concreteProtocol}->writeMessageBegin($name, $type, $seqid);
+}
+
+#
+# Close the message
+#
+sub writeMessageEnd {
+ my $self = shift;
+
+ return $self->{concreteProtocol}->writeMessageEnd();
+}
+
+#
+# Writes a struct header.
+#
+# @param string $name Struct name
+# @throws TException on write error
+# @return int How many bytes written
+#
+sub writeStructBegin {
+ my $self = shift;
+ my ($name) = @_;
+
+ return $self->{concreteProtocol}->writeStructBegin($name);
+}
+
+#
+# Close a struct.
+#
+# @throws TException on write error
+# @return int How many bytes written
+#
+sub writeStructEnd {
+ my $self = shift;
+
+ return $self->{concreteProtocol}->writeStructEnd();
+}
+
+#
+# Starts a field.
+#
+# @param string $name Field name
+# @param int $type Field type
+# @param int $fid Field id
+# @throws TException on write error
+# @return int How many bytes written
+#
+sub writeFieldBegin {
+ my $self = shift;
+ my ($fieldName, $fieldType, $fieldId) = @_;
+
+ return $self->{concreteProtocol}->writeFieldBegin($fieldName, $fieldType, $fieldId);
+}
+
+sub writeFieldEnd {
+ my $self = shift;
+
+ return $self->{concreteProtocol}->writeFieldEnd();
+}
+
+sub writeFieldStop {
+ my $self = shift;
+
+ return $self->{concreteProtocol}->writeFieldStop();
+}
+
+sub writeMapBegin {
+ my $self = shift;
+ my ($keyType, $valType, $size) = @_;
+
+ return $self->{concreteProtocol}->writeMapBegin($keyType, $valType, $size);
+}
+
+sub writeMapEnd {
+ my $self = shift;
+
+ return $self->{concreteProtocol}->writeMapEnd();
+}
+
+sub writeListBegin {
+ my $self = shift;
+ my ($elemType, $size) = @_;
+
+ return $self->{concreteProtocol}->writeListBegin($elemType, $size);
+}
+
+sub writeListEnd {
+ my $self = shift;
+
+ return $self->{concreteProtocol}->writeListEnd();
+}
+
+sub writeSetBegin {
+ my $self = shift;
+ my ($elemType, $size) = @_;
+
+ return $self->{concreteProtocol}->writeSetBegin($elemType, $size);
+}
+
+sub writeSetEnd {
+ my $self = shift;
+
+ return $self->{concreteProtocol}->writeListEnd();
+}
+
+sub writeBool {
+ my $self = shift;
+ my $bool = shift;
+
+ return $self->{concreteProtocol}->writeBool($bool);
+}
+
+sub writeByte {
+ my $self = shift;
+ my $byte = shift;
+
+ return $self->{concreteProtocol}->writeByte($byte);
+}
+
+sub writeI16 {
+ my $self = shift;
+ my $i16 = shift;
+
+ return $self->{concreteProtocol}->writeI16($i16);
+}
+
+sub writeI32 {
+ my $self = shift;
+ my ($i32) = @_;
+
+ return $self->{concreteProtocol}->writeI32($i32);
+
+}
+
+sub writeI64 {
+ my $self = shift;
+ my $i64 = shift;
+
+ return $self->{concreteProtocol}->writeI64($i64);
+}
+
+sub writeDouble {
+ my $self = shift;
+ my $dub = shift;
+
+ return $self->{concreteProtocol}->writeDouble($dub);
+}
+
+sub writeString {
+ my $self = shift;
+ my $str = shift;
+
+ return $self->{concreteProtocol}->writeString($str);
+}
+
+#
+# Reads the message header
+#
+# @param string $name Function name
+# @param int $type message type TMessageType::CALL or TMessageType::REPLY
+# @parem int $seqid The sequence id of this message
+#
+sub readMessageBegin
+{
+ my $self = shift;
+ my ($name, $type, $seqid) = @_;
+
+ return $self->{concreteProtocol}->readMessageBegin($name, $type, $seqid);
+}
+
+#
+# Read the close of message
+#
+sub readMessageEnd
+{
+ my $self = shift;
+
+ return $self->{concreteProtocol}->readMessageEnd();
+}
+
+sub readStructBegin
+{
+ my $self = shift;
+ my $name = shift;
+
+ return $self->{concreteProtocol}->readStructBegin($name);
+}
+
+sub readStructEnd
+{
+ my $self = shift;
+
+ return $self->{concreteProtocol}->readStructEnd();
+}
+
+sub readFieldBegin
+{
+ my $self = shift;
+ my ($name, $fieldType, $fieldId) = @_;
+
+ return $self->{concreteProtocol}->readFieldBegin($name, $fieldType, $fieldId);
+}
+
+sub readFieldEnd
+{
+ my $self = shift;
+
+ return $self->{concreteProtocol}->readFieldEnd();
+}
+
+sub readMapBegin
+{
+ my $self = shift;
+ my ($keyType, $valType, $size) = @_;
+
+ return $self->{concreteProtocol}->readMapBegin($keyType, $valType, $size);
+}
+
+sub readMapEnd
+{
+ my $self = shift;
+
+ return $self->{concreteProtocol}->readMapEnd();
+}
+
+sub readListBegin
+{
+ my $self = shift;
+ my ($elemType, $size) = @_;
+
+ return $self->{concreteProtocol}->readListBegin($elemType, $size);
+}
+
+sub readListEnd
+{
+ my $self = shift;
+
+ return $self->{concreteProtocol}->readListEnd();
+}
+
+sub readSetBegin
+{
+ my $self = shift;
+ my ($elemType, $size) = @_;
+
+ return $self->{concreteProtocol}->readSetBegin($elemType, $size);
+}
+
+sub readSetEnd
+{
+ my $self = shift;
+
+ return $self->{concreteProtocol}->readSetEnd();
+}
+
+sub readBool
+{
+ my $self = shift;
+ my $bool = shift;
+
+ return $self->{concreteProtocol}->readBool($bool);
+}
+
+sub readByte
+{
+ my $self = shift;
+ my $byte = shift;
+
+ return $self->{concreteProtocol}->readByte($byte);
+}
+
+sub readI16
+{
+ my $self = shift;
+ my $i16 = shift;
+
+ return $self->{concreteProtocol}->readI16($i16);
+}
+
+sub readI32
+{
+ my $self = shift;
+ my $i32 = shift;
+
+ return $self->{concreteProtocol}->readI32($i32);
+}
+
+sub readI64
+{
+ my $self = shift;
+ my $i64 = shift;
+
+ return $self->{concreteProtocol}->readI64($i64);
+}
+
+sub readDouble
+{
+ my $self = shift;
+ my $dub = shift;
+
+ return $self->{concreteProtocol}->readDouble($dub);
+}
+
+sub readString
+{
+ my $self = shift;
+ my $str = shift;
+
+ return $self->{concreteProtocol}->readString($str);
+}
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/SSLServerSocket.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/SSLServerSocket.pm
new file mode 100644
index 000000000..7b0643102
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/SSLServerSocket.pm
@@ -0,0 +1,76 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Thrift;
+use Thrift::SSLSocket;
+use Thrift::ServerSocket;
+
+use IO::Socket::SSL;
+
+package Thrift::SSLServerSocket;
+use base qw( Thrift::ServerSocket );
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+#
+# Constructor.
+# Takes a hash:
+# See Thrift::Socket for base class parameters.
+# @param[in] ca certificate authority filename - not required
+# @param[in] cert certificate filename; may contain key in which case key is not required
+# @param[in] key private key filename for the certificate if it is not inside the cert file
+#
+sub new
+{
+ my $classname = shift;
+ my $self = $classname->SUPER::new(@_);
+ return bless($self, $classname);
+}
+
+sub __client
+{
+ return Thrift::SSLSocket->new();
+}
+
+sub __listen
+{
+ my $self = shift;
+ my $opts = {Listen => $self->{queue},
+ LocalAddr => $self->{host},
+ LocalPort => $self->{port},
+ Proto => 'tcp',
+ ReuseAddr => 1};
+
+ my $verify = IO::Socket::SSL::SSL_VERIFY_PEER | IO::Socket::SSL::SSL_VERIFY_FAIL_IF_NO_PEER_CERT | IO::Socket::SSL::SSL_VERIFY_CLIENT_ONCE;
+
+ $opts->{SSL_ca_file} = $self->{ca} if defined $self->{ca};
+ $opts->{SSL_cert_file} = $self->{cert} if defined $self->{cert};
+ $opts->{SSL_cipher_list} = $self->{ciphers} if defined $self->{ciphers};
+ $opts->{SSL_key_file} = $self->{key} if defined $self->{key};
+ $opts->{SSL_use_cert} = (defined $self->{cert}) ? 1 : 0;
+ $opts->{SSL_verify_mode} = (defined $self->{ca}) ? $verify : IO::Socket::SSL::SSL_VERIFY_NONE;
+ $opts->{SSL_version} = (defined $self->{version}) ? $self->{version} : 'SSLv23:!SSLv3:!SSLv2';
+
+ return IO::Socket::SSL->new(%$opts);
+}
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/SSLSocket.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/SSLSocket.pm
new file mode 100644
index 000000000..e34924df4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/SSLSocket.pm
@@ -0,0 +1,126 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Thrift;
+use Thrift::Socket;
+
+use IO::Socket::SSL;
+
+package Thrift::SSLSocket;
+use base qw( Thrift::Socket );
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+#
+# Construction and usage
+#
+# my $opts = {}
+# my $socket = Thrift::SSLSocket->new(\%opts);
+#
+# options:
+#
+# Any option from Socket.pm is valid, and then:
+#
+# ca => certificate authority file (PEM file) to authenticate the
+# server against; if not specified then the server is not
+# authenticated
+# cert => certificate to use as the client; if not specified then
+# the client does not present one but still connects using
+# secure protocol
+# ciphers => allowed cipher list
+# (see http://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS)
+# key => certificate key for "cert" option
+# version => acceptable SSL/TLS versions - if not specified then the
+# default is to use SSLv23 handshake but only negotiate
+# at TLSv1.0 or later
+#
+
+sub new
+{
+ my $classname = shift;
+ my $self = $classname->SUPER::new(@_);
+
+ return bless($self, $classname);
+}
+
+sub __open
+{
+ my $self = shift;
+ my $opts = {PeerAddr => $self->{host},
+ PeerPort => $self->{port},
+ Proto => 'tcp',
+ Timeout => $self->{sendTimeout} / 1000};
+
+ my $verify = IO::Socket::SSL::SSL_VERIFY_PEER | IO::Socket::SSL::SSL_VERIFY_FAIL_IF_NO_PEER_CERT | IO::Socket::SSL::SSL_VERIFY_CLIENT_ONCE;
+
+ $opts->{SSL_ca_file} = $self->{ca} if defined $self->{ca};
+ $opts->{SSL_cert_file} = $self->{cert} if defined $self->{cert};
+ $opts->{SSL_cipher_list} = $self->{ciphers} if defined $self->{ciphers};
+ $opts->{SSL_key_file} = $self->{key} if defined $self->{key};
+ $opts->{SSL_use_cert} = (defined $self->{cert}) ? 1 : 0;
+ $opts->{SSL_verify_mode} = (defined $self->{ca}) ? $verify : IO::Socket::SSL::SSL_VERIFY_NONE;
+ $opts->{SSL_version} = (defined $self->{version}) ? $self->{version} : 'SSLv23:!SSLv3:!SSLv2';
+
+ return IO::Socket::SSL->new(%$opts);
+}
+
+sub __close
+{
+ my $self = shift;
+ my $sock = ($self->{handle}->handles())[0];
+ if ($sock) {
+ $sock->close(SSL_no_shutdown => 1);
+ }
+}
+
+sub __recv
+{
+ my $self = shift;
+ my $sock = shift;
+ my $len = shift;
+ my $buf = undef;
+ if ($sock) {
+ sysread($sock, $buf, $len);
+ }
+ return $buf;
+}
+
+sub __send
+{
+ my $self = shift;
+ my $sock = shift;
+ my $buf = shift;
+ return syswrite($sock, $buf);
+}
+
+sub __wait
+{
+ my $self = shift;
+ my $sock = ($self->{handle}->handles())[0];
+ if ($sock and $sock->pending() eq 0) {
+ return $self->SUPER::__wait();
+ }
+ return $sock;
+}
+
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/Server.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/Server.pm
new file mode 100644
index 000000000..28822e874
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/Server.pm
@@ -0,0 +1,311 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Thrift;
+use Thrift::BinaryProtocol;
+use Thrift::BufferedTransport;
+use Thrift::Exception;
+
+#
+# Server base class module
+#
+package Thrift::Server;
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+#
+# 3 possible constructors:
+# 1. (processor, serverTransport)
+# Uses a BufferedTransportFactory and a BinaryProtocolFactory.
+# 2. (processor, serverTransport, transportFactory, protocolFactory)
+# Uses the same factory for input and output of each type.
+# 3. (processor, serverTransport,
+# inputTransportFactory, outputTransportFactory,
+# inputProtocolFactory, outputProtocolFactory)
+#
+sub new
+{
+ my $classname = shift;
+ my @args = @_;
+
+ my $self;
+
+ if (scalar @args == 2)
+ {
+ $self = _init($args[0], $args[1],
+ Thrift::BufferedTransportFactory->new(),
+ Thrift::BufferedTransportFactory->new(),
+ Thrift::BinaryProtocolFactory->new(),
+ Thrift::BinaryProtocolFactory->new());
+ }
+ elsif (scalar @args == 4)
+ {
+ $self = _init($args[0], $args[1], $args[2], $args[2], $args[3], $args[3]);
+ }
+ elsif (scalar @args == 6)
+ {
+ $self = _init($args[0], $args[1], $args[2], $args[3], $args[4], $args[5]);
+ }
+ else
+ {
+ die Thrift::TException->new('Thrift::Server expects exactly 2, 4, or 6 args');
+ }
+
+ return bless($self,$classname);
+}
+
+sub _init
+{
+ my $processor = shift;
+ my $serverTransport = shift;
+ my $inputTransportFactory = shift;
+ my $outputTransportFactory = shift;
+ my $inputProtocolFactory = shift;
+ my $outputProtocolFactory = shift;
+
+ my $self = {
+ processor => $processor,
+ serverTransport => $serverTransport,
+ inputTransportFactory => $inputTransportFactory,
+ outputTransportFactory => $outputTransportFactory,
+ inputProtocolFactory => $inputProtocolFactory,
+ outputProtocolFactory => $outputProtocolFactory,
+ };
+}
+
+sub serve
+{
+ die 'abstract';
+}
+
+sub _clientBegin
+{
+ my $self = shift;
+ my $iprot = shift;
+ my $oprot = shift;
+
+ if (exists $self->{serverEventHandler} and
+ defined $self->{serverEventHandler})
+ {
+ $self->{serverEventHandler}->clientBegin($iprot, $oprot);
+ }
+}
+
+sub _handleException
+{
+ my $self = shift;
+ my $e = shift;
+
+ if ($e->isa('Thrift::TException') and exists $e->{message}) {
+ my $message = $e->{message};
+ my $code = $e->{code};
+ my $out = $code . ':' . $message;
+
+ $message =~ m/TTransportException/ and die $out;
+ if ($message =~ m/Socket/) {
+ # suppress Socket messages
+ }
+ else {
+ warn $out;
+ }
+ }
+ else {
+ warn $e;
+ }
+}
+
+#
+# SimpleServer from the Server base class that handles one connection at a time
+#
+package Thrift::SimpleServer;
+use parent -norequire, 'Thrift::Server';
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+sub new
+{
+ my $classname = shift;
+
+ my $self = $classname->SUPER::new(@_);
+
+ return bless($self,$classname);
+}
+
+sub serve
+{
+ my $self = shift;
+ my $stop = 0;
+
+ $self->{serverTransport}->listen();
+ while (!$stop) {
+ my $client = $self->{serverTransport}->accept();
+ if (defined $client) {
+ my $itrans = $self->{inputTransportFactory}->getTransport($client);
+ my $otrans = $self->{outputTransportFactory}->getTransport($client);
+ my $iprot = $self->{inputProtocolFactory}->getProtocol($itrans);
+ my $oprot = $self->{outputProtocolFactory}->getProtocol($otrans);
+ eval {
+ $self->_clientBegin($iprot, $oprot);
+ while (1)
+ {
+ $self->{processor}->process($iprot, $oprot);
+ }
+ };
+ if($@) {
+ $self->_handleException($@);
+ }
+ $itrans->close();
+ $otrans->close();
+ } else {
+ $stop = 1;
+ }
+ }
+}
+
+
+#
+# ForkingServer that forks a new process for each request
+#
+package Thrift::ForkingServer;
+use parent -norequire, 'Thrift::Server';
+use POSIX ':sys_wait_h';
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+sub new
+{
+ my $classname = shift;
+ my @args = @_;
+
+ my $self = $classname->SUPER::new(@args);
+ return bless($self,$classname);
+}
+
+
+sub serve
+{
+ my $self = shift;
+
+ # THRIFT-3848: without ignoring SIGCHLD, perl ForkingServer goes into a tight loop
+ $SIG{CHLD} = 'IGNORE';
+
+ $self->{serverTransport}->listen();
+ while (1)
+ {
+ my $client = $self->{serverTransport}->accept();
+ $self->_client($client);
+ }
+}
+
+sub _client
+{
+ my $self = shift;
+ my $client = shift;
+
+ eval {
+ my $itrans = $self->{inputTransportFactory}->getTransport($client);
+ my $otrans = $self->{outputTransportFactory}->getTransport($client);
+
+ my $iprot = $self->{inputProtocolFactory}->getProtocol($itrans);
+ my $oprot = $self->{outputProtocolFactory}->getProtocol($otrans);
+
+ $self->_clientBegin($iprot, $oprot);
+
+ my $pid = fork();
+
+ if ($pid)
+ {
+ $self->_parent($pid, $itrans, $otrans);
+ }
+ else {
+ $self->_child($itrans, $otrans, $iprot, $oprot);
+ }
+ };
+ if($@) {
+ $self->_handleException($@);
+ }
+}
+
+sub _parent
+{
+ my $self = shift;
+ my $pid = shift;
+ my $itrans = shift;
+ my $otrans = shift;
+
+ # Parent must close socket or the connection may not get closed promptly
+ $self->tryClose($itrans);
+ $self->tryClose($otrans);
+}
+
+sub _child
+{
+ my $self = shift;
+ my $itrans = shift;
+ my $otrans = shift;
+ my $iprot = shift;
+ my $oprot = shift;
+
+ my $ecode = 0;
+ eval {
+ # THRIFT-4065 ensure child process has normal signal handling in case thrift handler uses it
+ $SIG{CHLD} = 'DEFAULT';
+ while (1)
+ {
+ $self->{processor}->process($iprot, $oprot);
+ }
+ };
+ if($@) {
+ $ecode = 1;
+ $self->_handleException($@);
+ }
+
+ $self->tryClose($itrans);
+ $self->tryClose($otrans);
+
+ exit($ecode);
+}
+
+sub tryClose
+{
+ my $self = shift;
+ my $file = shift;
+
+ eval {
+ if (defined $file)
+ {
+ $file->close();
+ }
+ };
+ if($@) {
+ if ($@->isa('Thrift::TException') and exists $@->{message}) {
+ my $message = $@->{message};
+ my $code = $@->{code};
+ my $out = $code . ':' . $message;
+
+ warn $out;
+ }
+ else {
+ warn $@;
+ }
+ }
+}
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/ServerSocket.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/ServerSocket.pm
new file mode 100644
index 000000000..39726438b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/ServerSocket.pm
@@ -0,0 +1,125 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use IO::Socket::INET;
+use IO::Select;
+use Thrift;
+use Thrift::Transport;
+use Thrift::Socket;
+
+package Thrift::ServerSocket;
+use base qw( Thrift::ServerTransport );
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+#
+# Constructor.
+# Legacy construction takes one argument, port number.
+# New construction takes a hash:
+# @param[in] host host interface to listen on (undef = all interfaces)
+# @param[in] port port number to listen on (required)
+# @param[in] queue the listen queue size (default if not specified is 128)
+# @example my $serversock = Thrift::ServerSocket->new(host => undef, port => port)
+#
+sub new
+{
+ my $classname = shift;
+ my $args = shift;
+ my $self;
+
+ # Support both old-style "port number" construction and newer...
+ if (ref($args) eq 'HASH') {
+ $self = $args;
+ }
+ else {
+ $self = { port => $args };
+ }
+
+ if (not defined $self->{queue}) {
+ $self->{queue} = 128;
+ }
+
+ return bless($self, $classname);
+}
+
+sub listen
+{
+ my $self = shift;
+
+ my $sock = $self->__listen() || do {
+ my $error = ref($self) . ': Could not bind to ' . '*:' . $self->{port} . ' (' . $! . ')';
+
+ if ($self->{debug}) {
+ $self->{debugHandler}->($error);
+ }
+
+ die Thrift::TTransportException->new($error, Thrift::TTransportException::NOT_OPEN);
+ };
+
+ $self->{handle} = $sock;
+}
+
+sub accept
+{
+ my $self = shift;
+
+ if ( exists $self->{handle} and defined $self->{handle} ) {
+ my $client = $self->{handle}->accept();
+ my $result = $self->__client();
+ $result->{handle} = IO::Select->new($client);
+ return $result;
+ }
+
+ return undef;
+}
+
+sub close
+{
+ my $self = shift;
+
+ if ( exists $self->{handle} and defined $self->{handle} )
+ {
+ $self->{handle}->close();
+ }
+}
+
+###
+### Overridable methods
+###
+
+sub __client
+{
+ return Thrift::Socket->new();
+}
+
+sub __listen
+{
+ my $self = shift;
+ return IO::Socket::INET->new(LocalAddr => $self->{host},
+ LocalPort => $self->{port},
+ Proto => 'tcp',
+ Listen => $self->{queue},
+ ReuseAddr => 1);
+}
+
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/Socket.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/Socket.pm
new file mode 100644
index 000000000..ba0db5eb4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/Socket.pm
@@ -0,0 +1,327 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Thrift;
+use Thrift::Exception;
+use Thrift::Transport;
+
+use IO::Socket::INET;
+use IO::Select;
+
+package Thrift::Socket;
+use base qw( Thrift::Transport );
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+#
+# Construction and usage
+#
+# my $opts = {}
+# my $socket = Thrift::Socket->new(\%opts);
+#
+# options:
+#
+# host => host to connect to
+# port => port to connect to
+# sendTimeout => timeout used for send and for connect
+# recvTimeout => timeout used for recv
+#
+
+sub new
+{
+ my $classname = shift;
+ my $opts = shift;
+
+ # default settings:
+ my $self = {
+ host => 'localhost',
+ port => 9090,
+ recvTimeout => 10000,
+ sendTimeout => 10000,
+
+ handle => undef
+ };
+
+ if (defined $opts and ref $opts eq ref {}) {
+
+ # argument is a hash of options so override the defaults
+ $self->{$_} = $opts->{$_} for keys %$opts;
+
+ } else {
+
+ # older style constructor takes 3 arguments, none of which are required
+ $self->{host} = $opts || 'localhost';
+ $self->{port} = shift || 9090;
+
+ }
+
+ return bless($self,$classname);
+}
+
+
+sub setSendTimeout
+{
+ my $self = shift;
+ my $timeout = shift;
+
+ $self->{sendTimeout} = $timeout;
+}
+
+sub setRecvTimeout
+{
+ my $self = shift;
+ my $timeout = shift;
+
+ $self->{recvTimeout} = $timeout;
+}
+
+
+#
+# Tests whether this is open
+#
+# @return bool true if the socket is open
+#
+sub isOpen
+{
+ my $self = shift;
+
+ if( defined $self->{handle} ){
+ return ($self->{handle}->handles())[0]->connected;
+ }
+
+ return 0;
+}
+
+#
+# Connects the socket.
+#
+sub open
+{
+ my $self = shift;
+
+ my $sock = $self->__open() || do {
+ my $error = ref($self).': Could not connect to '.$self->{host}.':'.$self->{port}.' ('.$!.')';
+ die Thrift::TTransportException->new($error, Thrift::TTransportException::NOT_OPEN);
+ };
+
+ $self->{handle} = IO::Select->new( $sock );
+}
+
+#
+# Closes the socket.
+#
+sub close
+{
+ my $self = shift;
+ if( defined $self->{handle} ) {
+ $self->__close();
+ }
+}
+
+#
+# Uses stream get contents to do the reading
+#
+# @param int $len How many bytes
+# @return string Binary data
+#
+sub readAll
+{
+ my $self = shift;
+ my $len = shift;
+
+
+ return unless defined $self->{handle};
+
+ my $pre = "";
+ while (1) {
+
+ my $sock = $self->__wait();
+ my $buf = $self->__recv($sock, $len);
+
+ if (!defined $buf || $buf eq '') {
+
+ die Thrift::TTransportException->new(ref($self).': Could not read '.$len.' bytes from '.
+ $self->{host}.':'.$self->{port}, Thrift::TTransportException::END_OF_FILE);
+
+ }
+ elsif ((my $sz = length($buf)) < $len) {
+
+ $pre .= $buf;
+ $len -= $sz;
+
+ }
+ else {
+ return $pre.$buf;
+ }
+ }
+}
+
+#
+# Read from the socket
+#
+# @param int $len How many bytes
+# @return string Binary data
+#
+sub read
+{
+ my $self = shift;
+ my $len = shift;
+
+ return unless defined $self->{handle};
+
+ my $sock = $self->__wait();
+ my $buf = $self->__recv($sock, $len);
+
+ if (!defined $buf || $buf eq '') {
+
+ die Thrift::TTransportException->new(ref($self).': Could not read '.$len.' bytes from '.
+ $self->{host}.':'.$self->{port}, Thrift::TTransportException::END_OF_FILE);
+
+ }
+
+ return $buf;
+}
+
+
+#
+# Write to the socket.
+#
+# @param string $buf The data to write
+#
+sub write
+{
+ my $self = shift;
+ my $buf = shift;
+
+ return unless defined $self->{handle};
+
+ while (length($buf) > 0) {
+ #check for timeout
+ my @sockets = $self->{handle}->can_write( $self->{sendTimeout} / 1000 );
+
+ if(@sockets == 0){
+ die Thrift::TTransportException->new(ref($self).': timed out writing to bytes from '.
+ $self->{host}.':'.$self->{port}, Thrift::TTransportException::TIMED_OUT);
+ }
+
+ my $sent = $self->__send($sockets[0], $buf);
+
+ if (!defined $sent || $sent == 0 ) {
+
+ die Thrift::TTransportException->new(ref($self).': Could not write '.length($buf).' bytes '.
+ $self->{host}.':'.$self->{host}, Thrift::TTransportException::END_OF_FILE);
+
+ }
+
+ $buf = substr($buf, $sent);
+ }
+}
+
+#
+# Flush output to the socket.
+#
+sub flush
+{
+ my $self = shift;
+
+ return unless defined $self->{handle};
+
+ my $ret = ($self->{handle}->handles())[0]->flush;
+}
+
+###
+### Overridable methods
+###
+
+#
+# Open a connection to a server.
+#
+sub __open
+{
+ my $self = shift;
+ return IO::Socket::INET->new(PeerAddr => $self->{host},
+ PeerPort => $self->{port},
+ Proto => 'tcp',
+ Timeout => $self->{sendTimeout} / 1000);
+}
+
+#
+# Close the connection
+#
+sub __close
+{
+ my $self = shift;
+ CORE::close(($self->{handle}->handles())[0]);
+}
+
+#
+# Read data
+#
+# @param[in] $sock the socket
+# @param[in] $len the length to read
+# @returns the data buffer that was read
+#
+sub __recv
+{
+ my $self = shift;
+ my $sock = shift;
+ my $len = shift;
+ my $buf = undef;
+ $sock->recv($buf, $len);
+ return $buf;
+}
+
+#
+# Send data
+#
+# @param[in] $sock the socket
+# @param[in] $buf the data buffer
+# @returns the number of bytes written
+#
+sub __send
+{
+ my $self = shift;
+ my $sock = shift;
+ my $buf = shift;
+ return $sock->send($buf);
+}
+
+#
+# Wait for data to be readable
+#
+# @returns a socket that can be read
+#
+sub __wait
+{
+ my $self = shift;
+ my @sockets = $self->{handle}->can_read( $self->{recvTimeout} / 1000 );
+
+ if (@sockets == 0) {
+ die Thrift::TTransportException->new(ref($self).': timed out reading from '.
+ $self->{host}.':'.$self->{port}, Thrift::TTransportException::TIMED_OUT);
+ }
+
+ return $sockets[0];
+}
+
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/Transport.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/Transport.pm
new file mode 100644
index 000000000..41b7e150f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/Transport.pm
@@ -0,0 +1,180 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Thrift;
+use Thrift::Exception;
+
+#
+# Transport exceptions
+#
+package Thrift::TTransportException;
+use base('Thrift::TException');
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+use constant UNKNOWN => 0;
+use constant NOT_OPEN => 1;
+use constant ALREADY_OPEN => 2;
+use constant TIMED_OUT => 3;
+use constant END_OF_FILE => 4;
+
+sub new {
+ my $classname = shift;
+ my $self = $classname->SUPER::new(@_);
+
+ return bless($self,$classname);
+}
+
+package Thrift::Transport;
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+#
+# Whether this transport is open.
+#
+# @return boolean true if open
+#
+sub isOpen
+{
+ die 'abstract';
+}
+
+#
+# Open the transport for reading/writing
+#
+# @throws TTransportException if cannot open
+#
+sub open
+{
+ die 'abstract';
+}
+
+#
+# Close the transport.
+#
+sub close
+{
+ die 'abstract';
+}
+
+#
+# Read some data into the array.
+#
+# @param int $len How much to read
+# @return string The data that has been read
+# @throws TTransportException if cannot read any more data
+#
+sub read
+{
+ die 'abstract';
+}
+
+#
+# Guarantees that the full amount of data is read.
+#
+# @return string The data, of exact length
+# @throws TTransportException if cannot read data
+#
+sub readAll
+{
+ my $self = shift;
+ my $len = shift;
+
+ my $data = '';
+ my $got = 0;
+
+ while (($got = length($data)) < $len) {
+ $data .= $self->read($len - $got);
+ }
+
+ return $data;
+}
+
+#
+# Writes the given data out.
+#
+# @param string $buf The data to write
+# @throws TTransportException if writing fails
+#
+sub write
+{
+ die 'abstract';
+}
+
+#
+# Flushes any pending data out of a buffer
+#
+# @throws TTransportException if a writing error occurs
+#
+sub flush {}
+
+
+#
+# TransportFactory creates transport objects from transports
+#
+package Thrift::TransportFactory;
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+sub new {
+ my $classname = shift;
+ my $self = {};
+
+ return bless($self,$classname);
+}
+
+#
+# Build a transport from the base transport
+#
+# @return Thrift::Transport transport
+#
+sub getTransport
+{
+ my $self = shift;
+ my $trans = shift;
+
+ return $trans;
+}
+
+
+#
+# ServerTransport base class module
+#
+package Thrift::ServerTransport;
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+sub listen
+{
+ die 'abstract';
+}
+
+sub accept
+{
+ die 'abstract';
+}
+
+sub close
+{
+ die 'abstract';
+}
+
+
+1;
+
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/Type.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/Type.pm
new file mode 100644
index 000000000..ad8da3b6c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/Type.pm
@@ -0,0 +1,50 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Thrift;
+
+#
+# Data types that can be sent via Thrift
+#
+package Thrift::TType;
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+use constant STOP => 0;
+use constant VOID => 1;
+use constant BOOL => 2;
+use constant BYTE => 3;
+use constant I08 => 3;
+use constant DOUBLE => 4;
+use constant I16 => 6;
+use constant I32 => 8;
+use constant I64 => 10;
+use constant STRING => 11;
+use constant UTF7 => 11;
+use constant STRUCT => 12;
+use constant MAP => 13;
+use constant SET => 14;
+use constant LIST => 15;
+use constant UTF8 => 16;
+use constant UTF16 => 17;
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/UnixServerSocket.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/UnixServerSocket.pm
new file mode 100644
index 000000000..875e8049a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/UnixServerSocket.pm
@@ -0,0 +1,84 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Thrift;
+use Thrift::ServerSocket;
+use Thrift::UnixSocket;
+
+use IO::Socket::UNIX;
+
+package Thrift::UnixServerSocket;
+use base qw( Thrift::ServerSocket );
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+#
+# Constructor.
+# If a single argument is given that is not a hash, that is the unix domain socket path.
+# If a single argument is given that is a hash:
+# @param[in] path unix domain socket file name
+# @param[in] queue the listen queue size (default is not specified is supplied by ServerSocket)
+# @example my $serversock = Thrift::UnixServerSocket->new($path);
+# @example my $serversock = Thrift::UnixServerSocket->new(path => "somepath", queue => 64);
+#
+sub new
+{
+ my $classname = shift;
+ my $args = shift;
+ my $self;
+
+ if (ref($args) eq 'HASH') {
+ $self = $classname->SUPER::new($args);
+ } else {
+ $self = $classname->SUPER::new();
+ $self->{path} = $args;
+ }
+
+ return bless($self, $classname);
+}
+
+sub __client
+{
+ return Thrift::UnixSocket->new();
+}
+
+sub __listen
+{
+ my $self = shift;
+
+ my $sock = IO::Socket::UNIX->new(
+ Type => IO::Socket::SOCK_STREAM,
+ Local => $self->{path},
+ Listen => $self->{queue})
+ || do {
+ my $error = 'UnixServerSocket: Could not bind to ' .
+ $self->{path} . ' (' . $! . ')';
+ if ($self->{debug}) {
+ $self->{debugHandler}->($error);
+ }
+ die Thrift::TTransportException->new($error, Thrift::TTransportException::NOT_OPEN);
+ };
+
+ return $sock;
+}
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/lib/Thrift/UnixSocket.pm b/src/jaegertracing/thrift/lib/perl/lib/Thrift/UnixSocket.pm
new file mode 100644
index 000000000..ba386d1ce
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/lib/Thrift/UnixSocket.pm
@@ -0,0 +1,67 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+
+use Thrift;
+use Thrift::Socket;
+
+use IO::Socket::UNIX;
+
+package Thrift::UnixSocket;
+use base qw( Thrift::Socket );
+use version 0.77; our $VERSION = version->declare("$Thrift::VERSION");
+
+#
+# Constructor.
+# Takes a unix domain socket filename.
+# See Thrift::Socket for base class parameters.
+# @param[in] path path to unix socket file
+# @example my $sock = Thrift::UnixSocket->new($path);
+#
+sub new
+{
+ my $classname = shift;
+ my $self = $classname->SUPER::new();
+ $self->{path} = shift;
+ return bless($self, $classname);
+}
+
+sub __open
+{
+ my $self = shift;
+
+ my $sock = IO::Socket::UNIX->new(
+ Type => IO::Socket::SOCK_STREAM,
+ Peer => $self->{path})
+ || do {
+ my $error = 'UnixSocket: Could not connect to ' .
+ $self->{path} . ' (' . $! . ')';
+ if ($self->{debug}) {
+ $self->{debugHandler}->($error);
+ }
+ die Thrift::TTransportException->new($error, Thrift::TTransportException::NOT_OPEN);
+ };
+
+ return $sock;
+}
+
+1;
diff --git a/src/jaegertracing/thrift/lib/perl/t/Makefile.am b/src/jaegertracing/thrift/lib/perl/t/Makefile.am
new file mode 100644
index 000000000..de0397186
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/t/Makefile.am
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+EXTRA_DIST = memory_buffer.t processor.t multiplex.t
diff --git a/src/jaegertracing/thrift/lib/perl/t/memory_buffer.t b/src/jaegertracing/thrift/lib/perl/t/memory_buffer.t
new file mode 100644
index 000000000..8fa9fd72e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/t/memory_buffer.t
@@ -0,0 +1,53 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use Test::More tests => 6;
+
+use strict;
+use warnings;
+
+use Data::Dumper;
+
+use Thrift::BinaryProtocol;
+use Thrift::MemoryBuffer;
+
+use ThriftTest::Types;
+
+
+my $transport = Thrift::MemoryBuffer->new();
+my $protocol = Thrift::BinaryProtocol->new($transport);
+
+my $a = ThriftTest::Xtruct->new();
+$a->i32_thing(10);
+$a->i64_thing(30);
+$a->string_thing('Hello, world!');
+$a->write($protocol);
+
+my $b = ThriftTest::Xtruct->new();
+$b->read($protocol);
+is($b->i32_thing, $a->i32_thing);
+is($b->i64_thing, $a->i64_thing);
+is($b->string_thing, $a->string_thing);
+
+$b->write($protocol);
+my $c = ThriftTest::Xtruct->new();
+$c->read($protocol);
+is($c->i32_thing, $a->i32_thing);
+is($c->i64_thing, $a->i64_thing);
+is($c->string_thing, $a->string_thing);
diff --git a/src/jaegertracing/thrift/lib/perl/t/multiplex.t b/src/jaegertracing/thrift/lib/perl/t/multiplex.t
new file mode 100644
index 000000000..90a9b4d02
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/t/multiplex.t
@@ -0,0 +1,201 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use Test::More tests => 6;
+
+use strict;
+use warnings;
+
+use Thrift::BinaryProtocol;
+use Thrift::FramedTransport;
+use Thrift::MemoryBuffer;
+use Thrift::MessageType;
+use Thrift::MultiplexedProcessor;
+use Thrift::Server;
+use Thrift::Socket;
+
+use BenchmarkService;
+use Aggr;
+
+use constant NAME_BENCHMARKSERVICE => 'BenchmarkService';
+use constant NAME_AGGR => 'Aggr';
+
+my $buffer = Thrift::MemoryBuffer->new(1024);
+my $aggr_protocol = Thrift::MultiplexedProtocol->new(Thrift::BinaryProtocol->new($buffer), NAME_AGGR);
+my $aggr_client = AggrClient->new($aggr_protocol);
+my $benchmark_protocol = Thrift::MultiplexedProtocol->new(Thrift::BinaryProtocol->new($buffer), NAME_BENCHMARKSERVICE);
+my $benchmark_client = BenchmarkServiceClient->new($benchmark_protocol);
+
+$buffer->open();
+
+for(my $i = 1; $i <= 5; $i++) {
+ $aggr_client->send_addValue($i);
+ $aggr_client->{seqid}++;
+}
+
+$aggr_client->send_getValues();
+
+for(my $i = 1; $i <= 5; $i++) {
+ $benchmark_client->send_fibonacci($i);
+ $benchmark_client->{seqid}++;
+}
+$benchmark_client->{seqid}--;
+
+my $client_command_binary = $buffer->getBuffer;
+$buffer->resetBuffer;
+
+
+# Process by server
+my $server_output_binary;
+{
+ my $benchmark_handler = My::BenchmarkService->new();
+ my $benchmark_processor = BenchmarkServiceProcessor->new($benchmark_handler);
+ my $aggr_handler = My::Aggr->new();
+ my $aggr_processor = AggrProcessor->new($aggr_handler);
+
+ my $protocol_factory = Thrift::BinaryProtocolFactory->new();
+
+ my $input_buffer = Thrift::MemoryBuffer->new();
+ $input_buffer->write($client_command_binary);
+
+ my $input_protocol = $protocol_factory->getProtocol($input_buffer);
+
+ my $output_buffer = Thrift::MemoryBuffer->new();
+ my $output_protocol = $protocol_factory->getProtocol($output_buffer);
+
+ my $processor = Thrift::MultiplexedProcessor->new();
+
+ $processor->registerProcessor(NAME_BENCHMARKSERVICE, $benchmark_processor);
+ $processor->registerProcessor(NAME_AGGR, $aggr_processor);
+ my $result;
+ for(my $i = 1; $i <= 11; $i++) {
+ $result = $processor->process($input_protocol, $output_protocol);
+ print "process resulted in $result\n";
+ }
+
+ $server_output_binary = $output_buffer->getBuffer();
+}
+
+$buffer->write($server_output_binary);
+
+
+
+for(my $i = 1; $i <= 5; $i++) {
+ my ($function_name, $message_type, $sequence_id);
+
+ $aggr_protocol->readMessageBegin(\$function_name, \$message_type, \$sequence_id);
+
+ if ($message_type == Thrift::TMessageType::EXCEPTION) {
+ die;
+ }
+
+ my $aggr_result = Aggr_addValue_result->new();
+ $aggr_result->read($aggr_protocol);
+ $aggr_protocol->readMessageEnd();
+}
+
+my ($function_name, $message_type, $sequence_id);
+
+$aggr_protocol->readMessageBegin(\$function_name, \$message_type, \$sequence_id);
+
+if ($message_type == Thrift::TMessageType::EXCEPTION) {
+ die;
+}
+
+my $aggr_result = Aggr_getValues_result->new();
+$aggr_result->read($aggr_protocol);
+$aggr_protocol->readMessageEnd();
+
+is_deeply($aggr_result->success(), [1,2,3,4,5]);
+
+
+foreach my $val((1,2,3,5,8)) {
+ my ($function_name, $message_type, $sequence_id);
+
+ $benchmark_protocol->readMessageBegin(\$function_name, \$message_type, \$sequence_id);
+
+ if ($message_type == Thrift::TMessageType::EXCEPTION) {
+ die;
+ }
+ my $benchmark_result = BenchmarkService_fibonacci_result->new();
+ $benchmark_result->read($benchmark_protocol);
+ $benchmark_protocol->readMessageEnd();
+
+ is($benchmark_result->success(), $val);
+}
+
+
+package My::Aggr;
+use base qw(AggrIf);
+
+use strict;
+use warnings;
+
+sub new {
+ my $classname = shift;
+ my $self = {};
+
+ $self->{values} = ();
+
+ return bless($self,$classname);
+}
+
+sub addValue{
+ my $self = shift;
+ my $value = shift;
+
+ push (@{$self->{values}}, $value);
+}
+
+sub getValues{
+ my $self = shift;
+
+ return $self->{values};
+}
+
+
+
+package My::BenchmarkService;
+use base qw(BenchmarkServiceIf);
+
+use strict;
+use warnings;
+
+sub new {
+ my $class = shift;
+ return bless {}, $class;
+}
+
+sub fibonacci {
+ my ($self, $n) = @_;
+
+ my $prev = 0;
+ my $next;
+ my $result = 1;
+
+ while ($n > 0) {
+ $next = $result + $prev;
+ $prev = $result;
+ $result = $next;
+ --$n;
+ }
+
+ return $result;
+}
+
diff --git a/src/jaegertracing/thrift/lib/perl/t/processor.t b/src/jaegertracing/thrift/lib/perl/t/processor.t
new file mode 100644
index 000000000..f8330354f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/t/processor.t
@@ -0,0 +1,104 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use Test::More tests => 2;
+
+use strict;
+use warnings;
+
+use Thrift::BinaryProtocol;
+use Thrift::MemoryBuffer;
+use Thrift::MessageType;
+
+use ThriftTest::ThriftTest;
+use ThriftTest::Types;
+
+use Data::Dumper;
+
+my $buffer = Thrift::MemoryBuffer->new(1024);
+my $protocol = Thrift::BinaryProtocol->new($buffer);
+my $client = ThriftTest::ThriftTestClient->new($protocol);
+
+$buffer->open();
+$client->send_testString("foo");
+$client->{seqid}++;
+$client->send_testString("bar");
+
+my $client_command_binary = $buffer->getBuffer;
+$buffer->resetBuffer;
+
+# Process by server
+
+my $server_output_binary;
+{
+ my $protocol_factory = Thrift::BinaryProtocolFactory->new();
+
+ my $input_buffer = Thrift::MemoryBuffer->new();
+ $input_buffer->write($client_command_binary);
+ my $input_protocol = $protocol_factory->getProtocol($input_buffer);
+
+ my $output_buffer = Thrift::MemoryBuffer->new();
+ my $output_protocol = $protocol_factory->getProtocol($output_buffer);
+
+ my $processor = ThriftTest::ThriftTestProcessor->new( My::ThriftTest->new() );
+ my $result = $processor->process($input_protocol, $output_protocol);
+ print "process resulted in $result\n";
+ $result = $processor->process($input_protocol, $output_protocol);
+ print "process resulted in $result\n";
+ $server_output_binary = $output_buffer->getBuffer();
+}
+
+$buffer->write($server_output_binary);
+
+foreach my $val (("got foo","got bar")){
+ my ($function_name, $message_type, $sequence_id);
+
+ $protocol->readMessageBegin(\$function_name, \$message_type, \$sequence_id);
+ print " $function_name, $message_type, $sequence_id\n";
+
+ if ($message_type == Thrift::TMessageType::EXCEPTION) {
+ die;
+ }
+
+ my $result = ThriftTest::ThriftTest_testString_result->new();
+ $result->read($protocol);
+ $protocol->readMessageEnd();
+
+ is($result->success(),$val);
+}
+
+
+package My::ThriftTest;
+
+use strict;
+use warnings;
+use Data::Dumper;
+
+sub new {
+ my $class = shift;
+ return bless {}, $class;
+}
+
+sub testString {
+ my ($self, $string) = @_;
+
+ print __PACKAGE__ . "->testString()\n";
+
+ return "got ".$string;
+}
diff --git a/src/jaegertracing/thrift/lib/perl/test.pl b/src/jaegertracing/thrift/lib/perl/test.pl
new file mode 100644
index 000000000..7e068402f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/test.pl
@@ -0,0 +1,25 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+use strict;
+use warnings;
+
+use Test::Harness;
+
+runtests(@ARGV);
diff --git a/src/jaegertracing/thrift/lib/perl/tools/FixupDist.pl b/src/jaegertracing/thrift/lib/perl/tools/FixupDist.pl
new file mode 100644
index 000000000..24a2b200a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/perl/tools/FixupDist.pl
@@ -0,0 +1,35 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#
+# This will fix up the distribution so that CPAN properly
+# indexes Thrift.
+#
+
+use 5.10.0;
+use strict;
+use warnings;
+use utf8;
+
+use Data::Dumper;
+use CPAN::Meta;
+
+my $meta = CPAN::Meta->load_file('META.json');
+$meta->{'provides'} = { 'Thrift' => { 'file' => 'lib/Thrift.pm', 'version' => $meta->version() } };
+$meta->save('META.json');
diff --git a/src/jaegertracing/thrift/lib/php/Makefile.am b/src/jaegertracing/thrift/lib/php/Makefile.am
new file mode 100755
index 000000000..ce353f0e6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/Makefile.am
@@ -0,0 +1,152 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+if WITH_TESTS
+SUBDIRS = test
+endif
+
+if WITH_PHP_EXTENSION
+%.so:
+ cd src/ext/thrift_protocol/ && $(MAKE)
+
+phpconfdir=$(PHP_CONFIG_PREFIX)
+phpconf_DATA=thrift_protocol.ini
+
+phpmoduledir = `php-config --extension-dir`
+phpmodule_SCRIPTS = src/ext/thrift_protocol/modules/thrift_protocol.so
+
+distclean-local:
+ if [ -f src/ext/thrift_protocol/Makefile ]; then cd src/ext/thrift_protocol/ && $(MAKE) distclean; fi
+ cd $(phpmodule_SCRIPTS) && $(PHPIZE) --clean
+
+endif
+
+phpdir = $(PHP_PREFIX)/
+php_DATA = \
+ lib/TMultiplexedProcessor.php
+
+phpbasedir = $(phpdir)/Base
+phpbase_DATA = \
+ lib/Base/TBase.php
+
+phpclassloaderdir = $(phpdir)/ClassLoader
+phpclassloader_DATA = \
+ lib/ClassLoader/ThriftClassLoader.php
+
+phpexceptiondir = $(phpdir)/Exception
+phpexception_DATA = \
+ lib/Exception/TApplicationException.php \
+ lib/Exception/TException.php \
+ lib/Exception/TProtocolException.php \
+ lib/Exception/TTransportException.php
+
+phpfactorydir = $(phpdir)/Factory
+phpfactory_DATA = \
+ lib/Factory/TBinaryProtocolFactory.php \
+ lib/Factory/TCompactProtocolFactory.php \
+ lib/Factory/TJSONProtocolFactory.php \
+ lib/Factory/TProtocolFactory.php \
+ lib/Factory/TStringFuncFactory.php \
+ lib/Factory/TTransportFactory.php
+
+phpprotocoldir = $(phpdir)/Protocol
+phpprotocol_DATA = \
+ lib/Protocol/TBinaryProtocolAccelerated.php \
+ lib/Protocol/TBinaryProtocol.php \
+ lib/Protocol/TCompactProtocol.php \
+ lib/Protocol/TJSONProtocol.php \
+ lib/Protocol/TMultiplexedProtocol.php \
+ lib/Protocol/TProtocol.php \
+ lib/Protocol/TProtocolDecorator.php \
+ lib/Protocol/TSimpleJSONProtocol.php
+
+phpprotocoljsondir = $(phpprotocoldir)/JSON
+phpprotocoljson_DATA = \
+ lib/Protocol/JSON/BaseContext.php \
+ lib/Protocol/JSON/ListContext.php \
+ lib/Protocol/JSON/LookaheadReader.php \
+ lib/Protocol/JSON/PairContext.php
+
+phpprotocolsimplejsondir = $(phpprotocoldir)/SimpleJSON
+phpprotocolsimplejson_DATA = \
+ lib/Protocol/SimpleJSON/CollectionMapKeyException.php \
+ lib/Protocol/SimpleJSON/Context.php \
+ lib/Protocol/SimpleJSON/ListContext.php \
+ lib/Protocol/SimpleJSON/MapContext.php \
+ lib/Protocol/SimpleJSON/StructContext.php
+
+phpserializerdir = $(phpdir)/Serializer
+phpserializer_DATA = \
+ lib/Serializer/TBinarySerializer.php
+
+phpserverdir = $(phpdir)/Server
+phpserver_DATA = \
+ lib/Server/TServerSocket.php \
+ lib/Server/TForkingServer.php \
+ lib/Server/TServer.php \
+ lib/Server/TServerTransport.php \
+ lib/Server/TSimpleServer.php
+
+phpstringfuncdir = $(phpdir)/StringFunc
+phpstringfunc_DATA = \
+ lib/StringFunc/Mbstring.php \
+ lib/StringFunc/Core.php \
+ lib/StringFunc/TStringFunc.php
+
+phptransportdir = $(phpdir)/Transport
+phptransport_DATA = \
+ lib/Transport/TBufferedTransport.php \
+ lib/Transport/TCurlClient.php \
+ lib/Transport/TFramedTransport.php \
+ lib/Transport/THttpClient.php \
+ lib/Transport/TMemoryBuffer.php \
+ lib/Transport/TNullTransport.php \
+ lib/Transport/TPhpStream.php \
+ lib/Transport/TSocket.php \
+ lib/Transport/TSocketPool.php \
+ lib/Transport/TTransport.php
+
+phptypedir = $(phpdir)/Type
+phptype_DATA = \
+ lib/Type/TMessageType.php \
+ lib/Type/TType.php \
+ lib/Type/TConstant.php
+
+clean-local:
+ if [ -f src/ext/thrift_protocol/Makefile ]; then cd src/ext/thrift_protocol/ && $(MAKE) clean; fi
+
+
+EXTRA_DIST = \
+ lib \
+ src/autoload.php \
+ src/ext/thrift_protocol/config.m4 \
+ src/ext/thrift_protocol/config.w32 \
+ src/ext/thrift_protocol/php_thrift_protocol.cpp \
+ src/ext/thrift_protocol/php_thrift_protocol.h \
+ src/Thrift.php \
+ src/TStringUtils.php \
+ coding_standards.md \
+ thrift_protocol.ini \
+ README.apache.md \
+ README.md
+
+MAINTAINERCLEANFILES = \
+ Makefile.in
+
diff --git a/src/jaegertracing/thrift/lib/php/README.apache.md b/src/jaegertracing/thrift/lib/php/README.apache.md
new file mode 100644
index 000000000..5e9258975
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/README.apache.md
@@ -0,0 +1,74 @@
+Thrift PHP/Apache Integration
+
+License
+=======
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+Building PHP Thrift Services with Apache
+========================================
+
+Thrift can be embedded in the Apache webserver with PHP installed. Sample
+code is provided below. Note that to make requests to this type of server
+you must use a THttpClient transport.
+
+Sample Code
+===========
+
+<?php
+
+namespace MyNamespace;
+
+/**
+ * Include path
+ */
+$THRIFT_ROOT = '/your/thrift/root/lib';
+
+/**
+ * Init Autloader
+ */
+require_once $THRIFT_ROOT . '/Thrift/ClassLoader/ThriftClassLoader.php';
+
+$loader = new ThriftClassLoader();
+$loader->registerNamespace('Thrift', $THRIFT_ROOT);
+$loader->registerDefinition('Thrift', $THRIFT_ROOT . '/packages');
+$loader->register();
+
+use Thrift\Transport\TPhpStream;
+use Thrift\Protocol\TBinaryProtocol;
+
+/**
+ * Example of how to build a Thrift server in Apache/PHP
+ */
+
+class ServiceHandler implements ServiceIf {
+ // Implement your interface and methods here
+}
+
+header('Content-Type: application/x-thrift');
+
+$handler = new ServiceHandler();
+$processor = new ServiceProcessor($handler);
+
+// Use the TPhpStream transport to read/write directly from HTTP
+$transport = new TPhpStream(TPhpStream::MODE_R | TPhpStream::MODE_W);
+$protocol = new TBinaryProtocol($transport);
+
+$transport->open();
+$processor->process($protocol, $protocol);
+$transport->close();
diff --git a/src/jaegertracing/thrift/lib/php/README.md b/src/jaegertracing/thrift/lib/php/README.md
new file mode 100644
index 000000000..7170104df
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/README.md
@@ -0,0 +1,60 @@
+Thrift PHP Software Library
+
+# License
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+# Using Thrift with PHP
+
+Thrift requires PHP 5. Thrift makes as few assumptions about your PHP
+environment as possible while trying to make some more advanced PHP
+features (i.e. APC cacheing using asbolute path URLs) as simple as possible.
+
+To use Thrift in your PHP codebase, take the following steps:
+
+1. Copy all of thrift/lib/php/lib into your PHP codebase
+2. Configure Symfony Autoloader (or whatever you usually use)
+
+After that, you have to manually include the Thrift package
+created by the compiler:
+
+```
+require_once 'packages/Service/Service.php';
+require_once 'packages/Service/Types.php';
+```
+
+# Dependencies
+
+PHP_INT_SIZE
+
+ This built-in signals whether your architecture is 32 or 64 bit and is
+ used by the TBinaryProtocol to properly use pack() and unpack() to
+ serialize data.
+
+apc_fetch(), apc_store()
+
+ APC cache is used by the TSocketPool class. If you do not have APC installed,
+ Thrift will fill in null stub function definitions.
+
+# Breaking Changes
+
+## 0.12.0
+
+1. [PSR-4](https://www.php-fig.org/psr/psr-4/) loader is now the default. If you want to use class maps instead, use `-gen php:classmap`.
+
+2. If using PSR-4, use `$thriftClassLoader->registerNamespace('namespace', '<path>')` instead of `$thriftClassLoader->registerDefinition('namespace', '<path>')`.
diff --git a/src/jaegertracing/thrift/lib/php/coding_standards.md b/src/jaegertracing/thrift/lib/php/coding_standards.md
new file mode 100644
index 000000000..e217539cd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/coding_standards.md
@@ -0,0 +1,5 @@
+## PHP Coding Standards
+
+Please follow:
+ * [Thrift General Coding Standards](/doc/coding_standards.md)
+ * [PSR-2](http://www.php-fig.org/psr/psr-2/)
diff --git a/src/jaegertracing/thrift/lib/php/lib/Base/TBase.php b/src/jaegertracing/thrift/lib/php/lib/Base/TBase.php
new file mode 100644
index 000000000..c61b631af
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Base/TBase.php
@@ -0,0 +1,382 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift
+ */
+
+namespace Thrift\Base;
+
+use Thrift\Type\TType;
+
+/**
+ * Base class from which other Thrift structs extend. This is so that we can
+ * cut back on the size of the generated code which is turning out to have a
+ * nontrivial cost just to load thanks to the wondrously abysmal implementation
+ * of PHP. Note that code is intentionally duplicated in here to avoid making
+ * function calls for every field or member of a container..
+ */
+abstract class TBase
+{
+ public static $tmethod = array(
+ TType::BOOL => 'Bool',
+ TType::BYTE => 'Byte',
+ TType::I16 => 'I16',
+ TType::I32 => 'I32',
+ TType::I64 => 'I64',
+ TType::DOUBLE => 'Double',
+ TType::STRING => 'String'
+ );
+
+ abstract public function read($input);
+
+ abstract public function write($output);
+
+ public function __construct($spec = null, $vals = null)
+ {
+ if (is_array($spec) && is_array($vals)) {
+ foreach ($spec as $fid => $fspec) {
+ $var = $fspec['var'];
+ if (isset($vals[$var])) {
+ $this->$var = $vals[$var];
+ }
+ }
+ }
+ }
+
+ public function __wakeup()
+ {
+ $this->__construct(get_object_vars($this));
+ }
+
+ private function _readMap(&$var, $spec, $input)
+ {
+ $xfer = 0;
+ $ktype = $spec['ktype'];
+ $vtype = $spec['vtype'];
+ $kread = $vread = null;
+ if (isset(TBase::$tmethod[$ktype])) {
+ $kread = 'read' . TBase::$tmethod[$ktype];
+ } else {
+ $kspec = $spec['key'];
+ }
+ if (isset(TBase::$tmethod[$vtype])) {
+ $vread = 'read' . TBase::$tmethod[$vtype];
+ } else {
+ $vspec = $spec['val'];
+ }
+ $var = array();
+ $_ktype = $_vtype = $size = 0;
+ $xfer += $input->readMapBegin($_ktype, $_vtype, $size);
+ for ($i = 0; $i < $size; ++$i) {
+ $key = $val = null;
+ if ($kread !== null) {
+ $xfer += $input->$kread($key);
+ } else {
+ switch ($ktype) {
+ case TType::STRUCT:
+ $class = $kspec['class'];
+ $key = new $class();
+ $xfer += $key->read($input);
+ break;
+ case TType::MAP:
+ $xfer += $this->_readMap($key, $kspec, $input);
+ break;
+ case TType::LST:
+ $xfer += $this->_readList($key, $kspec, $input, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_readList($key, $kspec, $input, true);
+ break;
+ }
+ }
+ if ($vread !== null) {
+ $xfer += $input->$vread($val);
+ } else {
+ switch ($vtype) {
+ case TType::STRUCT:
+ $class = $vspec['class'];
+ $val = new $class();
+ $xfer += $val->read($input);
+ break;
+ case TType::MAP:
+ $xfer += $this->_readMap($val, $vspec, $input);
+ break;
+ case TType::LST:
+ $xfer += $this->_readList($val, $vspec, $input, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_readList($val, $vspec, $input, true);
+ break;
+ }
+ }
+ $var[$key] = $val;
+ }
+ $xfer += $input->readMapEnd();
+
+ return $xfer;
+ }
+
+ private function _readList(&$var, $spec, $input, $set = false)
+ {
+ $xfer = 0;
+ $etype = $spec['etype'];
+ $eread = $vread = null;
+ if (isset(TBase::$tmethod[$etype])) {
+ $eread = 'read' . TBase::$tmethod[$etype];
+ } else {
+ $espec = $spec['elem'];
+ }
+ $var = array();
+ $_etype = $size = 0;
+ if ($set) {
+ $xfer += $input->readSetBegin($_etype, $size);
+ } else {
+ $xfer += $input->readListBegin($_etype, $size);
+ }
+ for ($i = 0; $i < $size; ++$i) {
+ $elem = null;
+ if ($eread !== null) {
+ $xfer += $input->$eread($elem);
+ } else {
+ $espec = $spec['elem'];
+ switch ($etype) {
+ case TType::STRUCT:
+ $class = $espec['class'];
+ $elem = new $class();
+ $xfer += $elem->read($input);
+ break;
+ case TType::MAP:
+ $xfer += $this->_readMap($elem, $espec, $input);
+ break;
+ case TType::LST:
+ $xfer += $this->_readList($elem, $espec, $input, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_readList($elem, $espec, $input, true);
+ break;
+ }
+ }
+ if ($set) {
+ $var[$elem] = true;
+ } else {
+ $var [] = $elem;
+ }
+ }
+ if ($set) {
+ $xfer += $input->readSetEnd();
+ } else {
+ $xfer += $input->readListEnd();
+ }
+
+ return $xfer;
+ }
+
+ protected function _read($class, $spec, $input)
+ {
+ $xfer = 0;
+ $fname = null;
+ $ftype = 0;
+ $fid = 0;
+ $xfer += $input->readStructBegin($fname);
+ while (true) {
+ $xfer += $input->readFieldBegin($fname, $ftype, $fid);
+ if ($ftype == TType::STOP) {
+ break;
+ }
+ if (isset($spec[$fid])) {
+ $fspec = $spec[$fid];
+ $var = $fspec['var'];
+ if ($ftype == $fspec['type']) {
+ $xfer = 0;
+ if (isset(TBase::$tmethod[$ftype])) {
+ $func = 'read' . TBase::$tmethod[$ftype];
+ $xfer += $input->$func($this->$var);
+ } else {
+ switch ($ftype) {
+ case TType::STRUCT:
+ $class = $fspec['class'];
+ $this->$var = new $class();
+ $xfer += $this->$var->read($input);
+ break;
+ case TType::MAP:
+ $xfer += $this->_readMap($this->$var, $fspec, $input);
+ break;
+ case TType::LST:
+ $xfer += $this->_readList($this->$var, $fspec, $input, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_readList($this->$var, $fspec, $input, true);
+ break;
+ }
+ }
+ } else {
+ $xfer += $input->skip($ftype);
+ }
+ } else {
+ $xfer += $input->skip($ftype);
+ }
+ $xfer += $input->readFieldEnd();
+ }
+ $xfer += $input->readStructEnd();
+
+ return $xfer;
+ }
+
+ private function _writeMap($var, $spec, $output)
+ {
+ $xfer = 0;
+ $ktype = $spec['ktype'];
+ $vtype = $spec['vtype'];
+ $kwrite = $vwrite = null;
+ if (isset(TBase::$tmethod[$ktype])) {
+ $kwrite = 'write' . TBase::$tmethod[$ktype];
+ } else {
+ $kspec = $spec['key'];
+ }
+ if (isset(TBase::$tmethod[$vtype])) {
+ $vwrite = 'write' . TBase::$tmethod[$vtype];
+ } else {
+ $vspec = $spec['val'];
+ }
+ $xfer += $output->writeMapBegin($ktype, $vtype, count($var));
+ foreach ($var as $key => $val) {
+ if (isset($kwrite)) {
+ $xfer += $output->$kwrite($key);
+ } else {
+ switch ($ktype) {
+ case TType::STRUCT:
+ $xfer += $key->write($output);
+ break;
+ case TType::MAP:
+ $xfer += $this->_writeMap($key, $kspec, $output);
+ break;
+ case TType::LST:
+ $xfer += $this->_writeList($key, $kspec, $output, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_writeList($key, $kspec, $output, true);
+ break;
+ }
+ }
+ if (isset($vwrite)) {
+ $xfer += $output->$vwrite($val);
+ } else {
+ switch ($vtype) {
+ case TType::STRUCT:
+ $xfer += $val->write($output);
+ break;
+ case TType::MAP:
+ $xfer += $this->_writeMap($val, $vspec, $output);
+ break;
+ case TType::LST:
+ $xfer += $this->_writeList($val, $vspec, $output, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_writeList($val, $vspec, $output, true);
+ break;
+ }
+ }
+ }
+ $xfer += $output->writeMapEnd();
+
+ return $xfer;
+ }
+
+ private function _writeList($var, $spec, $output, $set = false)
+ {
+ $xfer = 0;
+ $etype = $spec['etype'];
+ $ewrite = null;
+ if (isset(TBase::$tmethod[$etype])) {
+ $ewrite = 'write' . TBase::$tmethod[$etype];
+ } else {
+ $espec = $spec['elem'];
+ }
+ if ($set) {
+ $xfer += $output->writeSetBegin($etype, count($var));
+ } else {
+ $xfer += $output->writeListBegin($etype, count($var));
+ }
+ foreach ($var as $key => $val) {
+ $elem = $set ? $key : $val;
+ if (isset($ewrite)) {
+ $xfer += $output->$ewrite($elem);
+ } else {
+ switch ($etype) {
+ case TType::STRUCT:
+ $xfer += $elem->write($output);
+ break;
+ case TType::MAP:
+ $xfer += $this->_writeMap($elem, $espec, $output);
+ break;
+ case TType::LST:
+ $xfer += $this->_writeList($elem, $espec, $output, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_writeList($elem, $espec, $output, true);
+ break;
+ }
+ }
+ }
+ if ($set) {
+ $xfer += $output->writeSetEnd();
+ } else {
+ $xfer += $output->writeListEnd();
+ }
+
+ return $xfer;
+ }
+
+ protected function _write($class, $spec, $output)
+ {
+ $xfer = 0;
+ $xfer += $output->writeStructBegin($class);
+ foreach ($spec as $fid => $fspec) {
+ $var = $fspec['var'];
+ if ($this->$var !== null) {
+ $ftype = $fspec['type'];
+ $xfer += $output->writeFieldBegin($var, $ftype, $fid);
+ if (isset(TBase::$tmethod[$ftype])) {
+ $func = 'write' . TBase::$tmethod[$ftype];
+ $xfer += $output->$func($this->$var);
+ } else {
+ switch ($ftype) {
+ case TType::STRUCT:
+ $xfer += $this->$var->write($output);
+ break;
+ case TType::MAP:
+ $xfer += $this->_writeMap($this->$var, $fspec, $output);
+ break;
+ case TType::LST:
+ $xfer += $this->_writeList($this->$var, $fspec, $output, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_writeList($this->$var, $fspec, $output, true);
+ break;
+ }
+ }
+ $xfer += $output->writeFieldEnd();
+ }
+ }
+ $xfer += $output->writeFieldStop();
+ $xfer += $output->writeStructEnd();
+
+ return $xfer;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/ClassLoader/ThriftClassLoader.php b/src/jaegertracing/thrift/lib/php/lib/ClassLoader/ThriftClassLoader.php
new file mode 100644
index 000000000..4361bd84e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/ClassLoader/ThriftClassLoader.php
@@ -0,0 +1,206 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * ClassLoader to load Thrift library and definitions
+ * Inspired from UniversalClassLoader from Symfony 2
+ *
+ * @package thrift.classloader
+ */
+
+namespace Thrift\ClassLoader;
+
+class ThriftClassLoader
+{
+ /**
+ * Namespaces path
+ * @var array
+ */
+ protected $namespaces = array();
+
+ /**
+ * Thrift definition paths
+ * @var type
+ */
+ protected $definitions = array();
+
+ /**
+ * Do we use APC cache ?
+ * @var boolean
+ */
+ protected $apc = false;
+
+ /**
+ * APC Cache prefix
+ * @var string
+ */
+ protected $apc_prefix;
+
+ /**
+ * Set autoloader to use APC cache
+ * @param boolean $apc
+ * @param string $apc_prefix
+ */
+ public function __construct($apc = false, $apc_prefix = null)
+ {
+ $this->apc = $apc;
+ $this->apc_prefix = $apc_prefix;
+ }
+
+ /**
+ * Registers a namespace.
+ *
+ * @param string $namespace The namespace
+ * @param array|string $paths The location(s) of the namespace
+ */
+ public function registerNamespace($namespace, $paths)
+ {
+ $this->namespaces[$namespace] = (array)$paths;
+ }
+
+ /**
+ * Registers a Thrift definition namespace.
+ *
+ * @param string $namespace The definition namespace
+ * @param array|string $paths The location(s) of the definition namespace
+ */
+ public function registerDefinition($namespace, $paths)
+ {
+ $this->definitions[$namespace] = (array)$paths;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param Boolean $prepend Whether to prepend the autoloader or not
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+ }
+
+ /**
+ * Loads the given class, definition or interface.
+ *
+ * @param string $class The name of the class
+ */
+ public function loadClass($class)
+ {
+ if ((true === $this->apc && ($file = $this->findFileInApc($class))) or
+ ($file = $this->findFile($class))
+ ) {
+ require_once $file;
+ }
+ }
+
+ /**
+ * Loads the given class or interface in APC.
+ * @param string $class The name of the class
+ * @return string
+ */
+ protected function findFileInApc($class)
+ {
+ if (false === $file = apc_fetch($this->apc_prefix . $class)) {
+ apc_store($this->apc_prefix . $class, $file = $this->findFile($class));
+ }
+
+ return $file;
+ }
+
+ /**
+ * Find class in namespaces or definitions directories
+ * @param string $class
+ * @return string
+ */
+ public function findFile($class)
+ {
+ // Remove first backslash
+ if ('\\' == $class[0]) {
+ $class = substr($class, 1);
+ }
+
+ if (false !== $pos = strrpos($class, '\\')) {
+ // Namespaced class name
+ $namespace = substr($class, 0, $pos);
+
+ // Iterate in normal namespaces
+ foreach ($this->namespaces as $ns => $dirs) {
+ //Don't interfere with other autoloaders
+ if (0 !== strpos($namespace, $ns)) {
+ continue;
+ }
+
+ foreach ($dirs as $dir) {
+ $className = substr($class, $pos + 1);
+
+ $file = $dir . DIRECTORY_SEPARATOR .
+ str_replace('\\', DIRECTORY_SEPARATOR, $namespace) .
+ DIRECTORY_SEPARATOR .
+ $className . '.php';
+
+ if (file_exists($file)) {
+ return $file;
+ }
+ }
+ }
+
+ // Iterate in Thrift namespaces
+
+ // Remove first part of namespace
+ $m = explode('\\', $class);
+
+ // Ignore wrong call
+ if (count($m) <= 1) {
+ return;
+ }
+
+ $class = array_pop($m);
+ $namespace = implode('\\', $m);
+
+ foreach ($this->definitions as $ns => $dirs) {
+ //Don't interfere with other autoloaders
+ if (0 !== strpos($namespace, $ns)) {
+ continue;
+ }
+
+ foreach ($dirs as $dir) {
+ /**
+ * Available in service: Interface, Client, Processor, Rest
+ * And every service methods (_.+)
+ */
+ if (0 === preg_match('#(.+)(if|client|processor|rest)$#i', $class, $n) and
+ 0 === preg_match('#(.+)_[a-z0-9]+_(args|result)$#i', $class, $n)
+ ) {
+ $className = 'Types';
+ } else {
+ $className = $n[1];
+ }
+
+ $file = $dir . DIRECTORY_SEPARATOR .
+ str_replace('\\', DIRECTORY_SEPARATOR, $namespace) .
+ DIRECTORY_SEPARATOR .
+ $className . '.php';
+
+ if (file_exists($file)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Exception/TApplicationException.php b/src/jaegertracing/thrift/lib/php/lib/Exception/TApplicationException.php
new file mode 100644
index 000000000..ebb6a6a89
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Exception/TApplicationException.php
@@ -0,0 +1,76 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift
+ */
+
+namespace Thrift\Exception;
+
+use Thrift\Type\TType;
+
+class TApplicationException extends TException
+{
+ public static $_TSPEC =
+ array(1 => array('var' => 'message',
+ 'type' => TType::STRING),
+ 2 => array('var' => 'code',
+ 'type' => TType::I32));
+
+ const UNKNOWN = 0;
+ const UNKNOWN_METHOD = 1;
+ const INVALID_MESSAGE_TYPE = 2;
+ const WRONG_METHOD_NAME = 3;
+ const BAD_SEQUENCE_ID = 4;
+ const MISSING_RESULT = 5;
+ const INTERNAL_ERROR = 6;
+ const PROTOCOL_ERROR = 7;
+ const INVALID_TRANSFORM = 8;
+ const INVALID_PROTOCOL = 9;
+ const UNSUPPORTED_CLIENT_TYPE = 10;
+
+ public function __construct($message = null, $code = 0)
+ {
+ parent::__construct($message, $code);
+ }
+
+ public function read($output)
+ {
+ return $this->_read('TApplicationException', self::$_TSPEC, $output);
+ }
+
+ public function write($output)
+ {
+ $xfer = 0;
+ $xfer += $output->writeStructBegin('TApplicationException');
+ if ($message = $this->getMessage()) {
+ $xfer += $output->writeFieldBegin('message', TType::STRING, 1);
+ $xfer += $output->writeString($message);
+ $xfer += $output->writeFieldEnd();
+ }
+ if ($code = $this->getCode()) {
+ $xfer += $output->writeFieldBegin('type', TType::I32, 2);
+ $xfer += $output->writeI32($code);
+ $xfer += $output->writeFieldEnd();
+ }
+ $xfer += $output->writeFieldStop();
+ $xfer += $output->writeStructEnd();
+
+ return $xfer;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Exception/TException.php b/src/jaegertracing/thrift/lib/php/lib/Exception/TException.php
new file mode 100644
index 000000000..7dbf83293
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Exception/TException.php
@@ -0,0 +1,384 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift
+ */
+
+namespace Thrift\Exception;
+
+use Thrift\Type\TType;
+use Thrift\Base\TBase;
+
+/**
+ * NOTE(mcslee): This currently contains a ton of duplicated code from TBase
+ * because we need to save CPU cycles and this is not yet in an extension.
+ * Ideally we'd multiply-inherit TException from both Exception and Base, but
+ * that's not possible in PHP and there are no modules either, so for now we
+ * apologetically take a trip to HackTown.
+ *
+ * Can be called with standard Exception constructor (message, code) or with
+ * Thrift Base object constructor (spec, vals).
+ *
+ * @param mixed $p1 Message (string) or type-spec (array)
+ * @param mixed $p2 Code (integer) or values (array)
+ */
+class TException extends \Exception
+{
+ public function __construct($p1 = null, $p2 = 0)
+ {
+ if (is_array($p1) && is_array($p2)) {
+ $spec = $p1;
+ $vals = $p2;
+ foreach ($spec as $fid => $fspec) {
+ $var = $fspec['var'];
+ if (isset($vals[$var])) {
+ $this->$var = $vals[$var];
+ }
+ }
+ } else {
+ parent::__construct($p1, $p2);
+ }
+ }
+
+ public static $tmethod = array(
+ TType::BOOL => 'Bool',
+ TType::BYTE => 'Byte',
+ TType::I16 => 'I16',
+ TType::I32 => 'I32',
+ TType::I64 => 'I64',
+ TType::DOUBLE => 'Double',
+ TType::STRING => 'String'
+ );
+
+ private function _readMap(&$var, $spec, $input)
+ {
+ $xfer = 0;
+ $ktype = $spec['ktype'];
+ $vtype = $spec['vtype'];
+ $kread = $vread = null;
+ if (isset(TBase::$tmethod[$ktype])) {
+ $kread = 'read' . TBase::$tmethod[$ktype];
+ } else {
+ $kspec = $spec['key'];
+ }
+ if (isset(TBase::$tmethod[$vtype])) {
+ $vread = 'read' . TBase::$tmethod[$vtype];
+ } else {
+ $vspec = $spec['val'];
+ }
+ $var = array();
+ $_ktype = $_vtype = $size = 0;
+ $xfer += $input->readMapBegin($_ktype, $_vtype, $size);
+ for ($i = 0; $i < $size; ++$i) {
+ $key = $val = null;
+ if ($kread !== null) {
+ $xfer += $input->$kread($key);
+ } else {
+ switch ($ktype) {
+ case TType::STRUCT:
+ $class = $kspec['class'];
+ $key = new $class();
+ $xfer += $key->read($input);
+ break;
+ case TType::MAP:
+ $xfer += $this->_readMap($key, $kspec, $input);
+ break;
+ case TType::LST:
+ $xfer += $this->_readList($key, $kspec, $input, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_readList($key, $kspec, $input, true);
+ break;
+ }
+ }
+ if ($vread !== null) {
+ $xfer += $input->$vread($val);
+ } else {
+ switch ($vtype) {
+ case TType::STRUCT:
+ $class = $vspec['class'];
+ $val = new $class();
+ $xfer += $val->read($input);
+ break;
+ case TType::MAP:
+ $xfer += $this->_readMap($val, $vspec, $input);
+ break;
+ case TType::LST:
+ $xfer += $this->_readList($val, $vspec, $input, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_readList($val, $vspec, $input, true);
+ break;
+ }
+ }
+ $var[$key] = $val;
+ }
+ $xfer += $input->readMapEnd();
+
+ return $xfer;
+ }
+
+ private function _readList(&$var, $spec, $input, $set = false)
+ {
+ $xfer = 0;
+ $etype = $spec['etype'];
+ $eread = $vread = null;
+ if (isset(TBase::$tmethod[$etype])) {
+ $eread = 'read' . TBase::$tmethod[$etype];
+ } else {
+ $espec = $spec['elem'];
+ }
+ $var = array();
+ $_etype = $size = 0;
+ if ($set) {
+ $xfer += $input->readSetBegin($_etype, $size);
+ } else {
+ $xfer += $input->readListBegin($_etype, $size);
+ }
+ for ($i = 0; $i < $size; ++$i) {
+ $elem = null;
+ if ($eread !== null) {
+ $xfer += $input->$eread($elem);
+ } else {
+ $espec = $spec['elem'];
+ switch ($etype) {
+ case TType::STRUCT:
+ $class = $espec['class'];
+ $elem = new $class();
+ $xfer += $elem->read($input);
+ break;
+ case TType::MAP:
+ $xfer += $this->_readMap($elem, $espec, $input);
+ break;
+ case TType::LST:
+ $xfer += $this->_readList($elem, $espec, $input, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_readList($elem, $espec, $input, true);
+ break;
+ }
+ }
+ if ($set) {
+ $var[$elem] = true;
+ } else {
+ $var [] = $elem;
+ }
+ }
+ if ($set) {
+ $xfer += $input->readSetEnd();
+ } else {
+ $xfer += $input->readListEnd();
+ }
+
+ return $xfer;
+ }
+
+ protected function _read($class, $spec, $input)
+ {
+ $xfer = 0;
+ $fname = null;
+ $ftype = 0;
+ $fid = 0;
+ $xfer += $input->readStructBegin($fname);
+ while (true) {
+ $xfer += $input->readFieldBegin($fname, $ftype, $fid);
+ if ($ftype == TType::STOP) {
+ break;
+ }
+ if (isset($spec[$fid])) {
+ $fspec = $spec[$fid];
+ $var = $fspec['var'];
+ if ($ftype == $fspec['type']) {
+ $xfer = 0;
+ if (isset(TBase::$tmethod[$ftype])) {
+ $func = 'read' . TBase::$tmethod[$ftype];
+ $xfer += $input->$func($this->$var);
+ } else {
+ switch ($ftype) {
+ case TType::STRUCT:
+ $class = $fspec['class'];
+ $this->$var = new $class();
+ $xfer += $this->$var->read($input);
+ break;
+ case TType::MAP:
+ $xfer += $this->_readMap($this->$var, $fspec, $input);
+ break;
+ case TType::LST:
+ $xfer += $this->_readList($this->$var, $fspec, $input, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_readList($this->$var, $fspec, $input, true);
+ break;
+ }
+ }
+ } else {
+ $xfer += $input->skip($ftype);
+ }
+ } else {
+ $xfer += $input->skip($ftype);
+ }
+ $xfer += $input->readFieldEnd();
+ }
+ $xfer += $input->readStructEnd();
+
+ return $xfer;
+ }
+
+ private function _writeMap($var, $spec, $output)
+ {
+ $xfer = 0;
+ $ktype = $spec['ktype'];
+ $vtype = $spec['vtype'];
+ $kwrite = $vwrite = null;
+ if (isset(TBase::$tmethod[$ktype])) {
+ $kwrite = 'write' . TBase::$tmethod[$ktype];
+ } else {
+ $kspec = $spec['key'];
+ }
+ if (isset(TBase::$tmethod[$vtype])) {
+ $vwrite = 'write' . TBase::$tmethod[$vtype];
+ } else {
+ $vspec = $spec['val'];
+ }
+ $xfer += $output->writeMapBegin($ktype, $vtype, count($var));
+ foreach ($var as $key => $val) {
+ if (isset($kwrite)) {
+ $xfer += $output->$kwrite($key);
+ } else {
+ switch ($ktype) {
+ case TType::STRUCT:
+ $xfer += $key->write($output);
+ break;
+ case TType::MAP:
+ $xfer += $this->_writeMap($key, $kspec, $output);
+ break;
+ case TType::LST:
+ $xfer += $this->_writeList($key, $kspec, $output, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_writeList($key, $kspec, $output, true);
+ break;
+ }
+ }
+ if (isset($vwrite)) {
+ $xfer += $output->$vwrite($val);
+ } else {
+ switch ($vtype) {
+ case TType::STRUCT:
+ $xfer += $val->write($output);
+ break;
+ case TType::MAP:
+ $xfer += $this->_writeMap($val, $vspec, $output);
+ break;
+ case TType::LST:
+ $xfer += $this->_writeList($val, $vspec, $output, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_writeList($val, $vspec, $output, true);
+ break;
+ }
+ }
+ }
+ $xfer += $output->writeMapEnd();
+
+ return $xfer;
+ }
+
+ private function _writeList($var, $spec, $output, $set = false)
+ {
+ $xfer = 0;
+ $etype = $spec['etype'];
+ $ewrite = null;
+ if (isset(TBase::$tmethod[$etype])) {
+ $ewrite = 'write' . TBase::$tmethod[$etype];
+ } else {
+ $espec = $spec['elem'];
+ }
+ if ($set) {
+ $xfer += $output->writeSetBegin($etype, count($var));
+ } else {
+ $xfer += $output->writeListBegin($etype, count($var));
+ }
+ foreach ($var as $key => $val) {
+ $elem = $set ? $key : $val;
+ if (isset($ewrite)) {
+ $xfer += $output->$ewrite($elem);
+ } else {
+ switch ($etype) {
+ case TType::STRUCT:
+ $xfer += $elem->write($output);
+ break;
+ case TType::MAP:
+ $xfer += $this->_writeMap($elem, $espec, $output);
+ break;
+ case TType::LST:
+ $xfer += $this->_writeList($elem, $espec, $output, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_writeList($elem, $espec, $output, true);
+ break;
+ }
+ }
+ }
+ if ($set) {
+ $xfer += $output->writeSetEnd();
+ } else {
+ $xfer += $output->writeListEnd();
+ }
+
+ return $xfer;
+ }
+
+ protected function _write($class, $spec, $output)
+ {
+ $xfer = 0;
+ $xfer += $output->writeStructBegin($class);
+ foreach ($spec as $fid => $fspec) {
+ $var = $fspec['var'];
+ if ($this->$var !== null) {
+ $ftype = $fspec['type'];
+ $xfer += $output->writeFieldBegin($var, $ftype, $fid);
+ if (isset(TBase::$tmethod[$ftype])) {
+ $func = 'write' . TBase::$tmethod[$ftype];
+ $xfer += $output->$func($this->$var);
+ } else {
+ switch ($ftype) {
+ case TType::STRUCT:
+ $xfer += $this->$var->write($output);
+ break;
+ case TType::MAP:
+ $xfer += $this->_writeMap($this->$var, $fspec, $output);
+ break;
+ case TType::LST:
+ $xfer += $this->_writeList($this->$var, $fspec, $output, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_writeList($this->$var, $fspec, $output, true);
+ break;
+ }
+ }
+ $xfer += $output->writeFieldEnd();
+ }
+ }
+ $xfer += $output->writeFieldStop();
+ $xfer += $output->writeStructEnd();
+
+ return $xfer;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Exception/TProtocolException.php b/src/jaegertracing/thrift/lib/php/lib/Exception/TProtocolException.php
new file mode 100644
index 000000000..3a55d45ff
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Exception/TProtocolException.php
@@ -0,0 +1,50 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ * @author: rmarin (marin.radu@facebook.com)
+ */
+
+namespace Thrift\Exception;
+
+/**
+ * Protocol module. Contains all the types and definitions needed to implement
+ * a protocol encoder/decoder.
+ *
+ * @package thrift.protocol
+ */
+
+/**
+ * Protocol exceptions
+ */
+class TProtocolException extends TException
+{
+ const UNKNOWN = 0;
+ const INVALID_DATA = 1;
+ const NEGATIVE_SIZE = 2;
+ const SIZE_LIMIT = 3;
+ const BAD_VERSION = 4;
+ const NOT_IMPLEMENTED = 5;
+ const DEPTH_LIMIT = 6;
+
+ public function __construct($message = null, $code = 0)
+ {
+ parent::__construct($message, $code);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Exception/TTransportException.php b/src/jaegertracing/thrift/lib/php/lib/Exception/TTransportException.php
new file mode 100644
index 000000000..7d8d56743
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Exception/TTransportException.php
@@ -0,0 +1,40 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Exception;
+
+/**
+ * Transport exceptions
+ */
+class TTransportException extends TException
+{
+ const UNKNOWN = 0;
+ const NOT_OPEN = 1;
+ const ALREADY_OPEN = 2;
+ const TIMED_OUT = 3;
+ const END_OF_FILE = 4;
+
+ public function __construct($message = null, $code = 0)
+ {
+ parent::__construct($message, $code);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Factory/TBinaryProtocolFactory.php b/src/jaegertracing/thrift/lib/php/lib/Factory/TBinaryProtocolFactory.php
new file mode 100644
index 000000000..2519183df
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Factory/TBinaryProtocolFactory.php
@@ -0,0 +1,45 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Factory;
+
+use Thrift\Protocol\TBinaryProtocol;
+
+/**
+ * Binary Protocol Factory
+ */
+class TBinaryProtocolFactory implements TProtocolFactory
+{
+ private $strictRead_ = false;
+ private $strictWrite_ = false;
+
+ public function __construct($strictRead = false, $strictWrite = false)
+ {
+ $this->strictRead_ = $strictRead;
+ $this->strictWrite_ = $strictWrite;
+ }
+
+ public function getProtocol($trans)
+ {
+ return new TBinaryProtocol($trans, $this->strictRead_, $this->strictWrite_);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Factory/TCompactProtocolFactory.php b/src/jaegertracing/thrift/lib/php/lib/Factory/TCompactProtocolFactory.php
new file mode 100644
index 000000000..11fb8ff33
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Factory/TCompactProtocolFactory.php
@@ -0,0 +1,40 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Factory;
+
+use Thrift\Protocol\TCompactProtocol;
+
+/**
+ * Compact Protocol Factory
+ */
+class TCompactProtocolFactory implements TProtocolFactory
+{
+ public function __construct()
+ {
+ }
+
+ public function getProtocol($trans)
+ {
+ return new TCompactProtocol($trans);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Factory/TJSONProtocolFactory.php b/src/jaegertracing/thrift/lib/php/lib/Factory/TJSONProtocolFactory.php
new file mode 100644
index 000000000..fbfb1d731
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Factory/TJSONProtocolFactory.php
@@ -0,0 +1,40 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Factory;
+
+use Thrift\Protocol\TJSONProtocol;
+
+/**
+ * JSON Protocol Factory
+ */
+class TJSONProtocolFactory implements TProtocolFactory
+{
+ public function __construct()
+ {
+ }
+
+ public function getProtocol($trans)
+ {
+ return new TJSONProtocol($trans);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Factory/TProtocolFactory.php b/src/jaegertracing/thrift/lib/php/lib/Factory/TProtocolFactory.php
new file mode 100644
index 000000000..d3066c8ec
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Factory/TProtocolFactory.php
@@ -0,0 +1,36 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Factory;
+
+/**
+ * Protocol factory creates protocol objects from transports
+ */
+interface TProtocolFactory
+{
+ /**
+ * Build a protocol from the base transport
+ *
+ * @return Thrift\Protocol\TProtocol protocol
+ */
+ public function getProtocol($trans);
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Factory/TStringFuncFactory.php b/src/jaegertracing/thrift/lib/php/lib/Factory/TStringFuncFactory.php
new file mode 100644
index 000000000..30de4d780
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Factory/TStringFuncFactory.php
@@ -0,0 +1,66 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+namespace Thrift\Factory;
+
+use Thrift\StringFunc\Core;
+use Thrift\StringFunc\Mbstring;
+use Thrift\StringFunc\TStringFunc;
+
+class TStringFuncFactory
+{
+ private static $_instance;
+
+ /**
+ * Get the Singleton instance of TStringFunc implementation that is
+ * compatible with the current system's mbstring.func_overload settings.
+ *
+ * @return TStringFunc
+ */
+ public static function create()
+ {
+ if (!self::$_instance) {
+ self::_setInstance();
+ }
+
+ return self::$_instance;
+ }
+
+ private static function _setInstance()
+ {
+ /**
+ * Cannot use str* functions for byte counting because multibyte
+ * characters will be read a single bytes.
+ *
+ * See: http://php.net/manual/en/mbstring.overload.php
+ */
+ if (ini_get('mbstring.func_overload') & 2) {
+ self::$_instance = new Mbstring();
+ } else {
+ /**
+ * mbstring is not installed or does not have function overloading
+ * of the str* functions enabled so use PHP core str* functions for
+ * byte counting.
+ */
+ self::$_instance = new Core();
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Factory/TTransportFactory.php b/src/jaegertracing/thrift/lib/php/lib/Factory/TTransportFactory.php
new file mode 100644
index 000000000..43f2eecde
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Factory/TTransportFactory.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Thrift\Factory;
+
+use Thrift\Transport\TTransport;
+
+class TTransportFactory
+{
+ /**
+ * @static
+ * @param TTransport $transport
+ * @return TTransport
+ */
+ public static function getTransport(TTransport $transport)
+ {
+ return $transport;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Protocol/JSON/BaseContext.php b/src/jaegertracing/thrift/lib/php/lib/Protocol/JSON/BaseContext.php
new file mode 100644
index 000000000..31bcb48e4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Protocol/JSON/BaseContext.php
@@ -0,0 +1,39 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Protocol\JSON;
+
+class BaseContext
+{
+ public function escapeNum()
+ {
+ return false;
+ }
+
+ public function write()
+ {
+ }
+
+ public function read()
+ {
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Protocol/JSON/ListContext.php b/src/jaegertracing/thrift/lib/php/lib/Protocol/JSON/ListContext.php
new file mode 100644
index 000000000..eef659442
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Protocol/JSON/ListContext.php
@@ -0,0 +1,54 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Protocol\JSON;
+
+use Thrift\Protocol\TJSONProtocol;
+
+class ListContext extends BaseContext
+{
+ private $first_ = true;
+ private $p_;
+
+ public function __construct($p)
+ {
+ $this->p_ = $p;
+ }
+
+ public function write()
+ {
+ if ($this->first_) {
+ $this->first_ = false;
+ } else {
+ $this->p_->getTransport()->write(TJSONProtocol::COMMA);
+ }
+ }
+
+ public function read()
+ {
+ if ($this->first_) {
+ $this->first_ = false;
+ } else {
+ $this->p_->readJSONSyntaxChar(TJSONProtocol::COMMA);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Protocol/JSON/LookaheadReader.php b/src/jaegertracing/thrift/lib/php/lib/Protocol/JSON/LookaheadReader.php
new file mode 100644
index 000000000..0b18c40d0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Protocol/JSON/LookaheadReader.php
@@ -0,0 +1,57 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Protocol\JSON;
+
+class LookaheadReader
+{
+ private $hasData_ = false;
+ private $data_ = array();
+ private $p_;
+
+ public function __construct($p)
+ {
+ $this->p_ = $p;
+ }
+
+ public function read()
+ {
+ if ($this->hasData_) {
+ $this->hasData_ = false;
+ } else {
+ $this->data_ = $this->p_->getTransport()->readAll(1);
+ }
+
+ return substr($this->data_, 0, 1);
+ }
+
+ public function peek()
+ {
+ if (!$this->hasData_) {
+ $this->data_ = $this->p_->getTransport()->readAll(1);
+ }
+
+ $this->hasData_ = true;
+
+ return substr($this->data_, 0, 1);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Protocol/JSON/PairContext.php b/src/jaegertracing/thrift/lib/php/lib/Protocol/JSON/PairContext.php
new file mode 100644
index 000000000..7b353c4ad
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Protocol/JSON/PairContext.php
@@ -0,0 +1,64 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Protocol\JSON;
+
+use Thrift\Protocol\TJSONProtocol;
+
+class PairContext extends BaseContext
+{
+ private $first_ = true;
+ private $colon_ = true;
+ private $p_ = null;
+
+ public function __construct($p)
+ {
+ $this->p_ = $p;
+ }
+
+ public function write()
+ {
+ if ($this->first_) {
+ $this->first_ = false;
+ $this->colon_ = true;
+ } else {
+ $this->p_->getTransport()->write($this->colon_ ? TJSONProtocol::COLON : TJSONProtocol::COMMA);
+ $this->colon_ = !$this->colon_;
+ }
+ }
+
+ public function read()
+ {
+ if ($this->first_) {
+ $this->first_ = false;
+ $this->colon_ = true;
+ } else {
+ $this->p_->readJSONSyntaxChar($this->colon_ ? TJSONProtocol::COLON : TJSONProtocol::COMMA);
+ $this->colon_ = !$this->colon_;
+ }
+ }
+
+ public function escapeNum()
+ {
+ return $this->colon_;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/CollectionMapKeyException.php b/src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/CollectionMapKeyException.php
new file mode 100644
index 000000000..522b85a5b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/CollectionMapKeyException.php
@@ -0,0 +1,33 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Protocol\SimpleJSON;
+
+use Thrift\Exception\TException;
+
+class CollectionMapKeyException extends TException
+{
+ public function __construct($message)
+ {
+ parent::__construct($message);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/Context.php b/src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/Context.php
new file mode 100644
index 000000000..dbd16faa2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/Context.php
@@ -0,0 +1,35 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Protocol\SimpleJSON;
+
+class Context
+{
+ public function write()
+ {
+ }
+
+ public function isMapKey()
+ {
+ return false;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/ListContext.php b/src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/ListContext.php
new file mode 100644
index 000000000..6f346d8f8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/ListContext.php
@@ -0,0 +1,45 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Protocol\SimpleJSON;
+
+use Thrift\Protocol\TSimpleJSONProtocol;
+
+class ListContext extends Context
+{
+ protected $first_ = true;
+ private $p_;
+
+ public function __construct($p)
+ {
+ $this->p_ = $p;
+ }
+
+ public function write()
+ {
+ if ($this->first_) {
+ $this->first_ = false;
+ } else {
+ $this->p_->getTransport()->write(TSimpleJSONProtocol::COMMA);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/MapContext.php b/src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/MapContext.php
new file mode 100644
index 000000000..61c060d09
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/MapContext.php
@@ -0,0 +1,47 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Protocol\SimpleJSON;
+
+class MapContext extends StructContext
+{
+ protected $isKey = true;
+ private $p_;
+
+ public function __construct($p)
+ {
+ parent::__construct($p);
+ }
+
+ public function write()
+ {
+ parent::write();
+ $this->isKey = !$this->isKey;
+ }
+
+ public function isMapKey()
+ {
+ // we want to coerce map keys to json strings regardless
+ // of their type
+ return $this->isKey;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/StructContext.php b/src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/StructContext.php
new file mode 100644
index 000000000..38a62d1a2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Protocol/SimpleJSON/StructContext.php
@@ -0,0 +1,52 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Protocol\SimpleJSON;
+
+use Thrift\Protocol\TSimpleJSONProtocol;
+
+class StructContext extends Context
+{
+ protected $first_ = true;
+ protected $colon_ = true;
+ private $p_;
+
+ public function __construct($p)
+ {
+ $this->p_ = $p;
+ }
+
+ public function write()
+ {
+ if ($this->first_) {
+ $this->first_ = false;
+ $this->colon_ = true;
+ } else {
+ $this->p_->getTransport()->write(
+ $this->colon_ ?
+ TSimpleJSONProtocol::COLON :
+ TSimpleJSONProtocol::COMMA
+ );
+ $this->colon_ = !$this->colon_;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Protocol/TBinaryProtocol.php b/src/jaegertracing/thrift/lib/php/lib/Protocol/TBinaryProtocol.php
new file mode 100644
index 000000000..cda5c0d4c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Protocol/TBinaryProtocol.php
@@ -0,0 +1,453 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Protocol;
+
+use Thrift\Type\TType;
+use Thrift\Exception\TProtocolException;
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * Binary implementation of the Thrift protocol.
+ *
+ */
+class TBinaryProtocol extends TProtocol
+{
+ const VERSION_MASK = 0xffff0000;
+ const VERSION_1 = 0x80010000;
+
+ protected $strictRead_ = false;
+ protected $strictWrite_ = true;
+
+ public function __construct($trans, $strictRead = false, $strictWrite = true)
+ {
+ parent::__construct($trans);
+ $this->strictRead_ = $strictRead;
+ $this->strictWrite_ = $strictWrite;
+ }
+
+ public function writeMessageBegin($name, $type, $seqid)
+ {
+ if ($this->strictWrite_) {
+ $version = self::VERSION_1 | $type;
+
+ return
+ $this->writeI32($version) +
+ $this->writeString($name) +
+ $this->writeI32($seqid);
+ } else {
+ return
+ $this->writeString($name) +
+ $this->writeByte($type) +
+ $this->writeI32($seqid);
+ }
+ }
+
+ public function writeMessageEnd()
+ {
+ return 0;
+ }
+
+ public function writeStructBegin($name)
+ {
+ return 0;
+ }
+
+ public function writeStructEnd()
+ {
+ return 0;
+ }
+
+ public function writeFieldBegin($fieldName, $fieldType, $fieldId)
+ {
+ return
+ $this->writeByte($fieldType) +
+ $this->writeI16($fieldId);
+ }
+
+ public function writeFieldEnd()
+ {
+ return 0;
+ }
+
+ public function writeFieldStop()
+ {
+ return
+ $this->writeByte(TType::STOP);
+ }
+
+ public function writeMapBegin($keyType, $valType, $size)
+ {
+ return
+ $this->writeByte($keyType) +
+ $this->writeByte($valType) +
+ $this->writeI32($size);
+ }
+
+ public function writeMapEnd()
+ {
+ return 0;
+ }
+
+ public function writeListBegin($elemType, $size)
+ {
+ return
+ $this->writeByte($elemType) +
+ $this->writeI32($size);
+ }
+
+ public function writeListEnd()
+ {
+ return 0;
+ }
+
+ public function writeSetBegin($elemType, $size)
+ {
+ return
+ $this->writeByte($elemType) +
+ $this->writeI32($size);
+ }
+
+ public function writeSetEnd()
+ {
+ return 0;
+ }
+
+ public function writeBool($value)
+ {
+ $data = pack('c', $value ? 1 : 0);
+ $this->trans_->write($data, 1);
+
+ return 1;
+ }
+
+ public function writeByte($value)
+ {
+ $data = pack('c', $value);
+ $this->trans_->write($data, 1);
+
+ return 1;
+ }
+
+ public function writeI16($value)
+ {
+ $data = pack('n', $value);
+ $this->trans_->write($data, 2);
+
+ return 2;
+ }
+
+ public function writeI32($value)
+ {
+ $data = pack('N', $value);
+ $this->trans_->write($data, 4);
+
+ return 4;
+ }
+
+ public function writeI64($value)
+ {
+ // If we are on a 32bit architecture we have to explicitly deal with
+ // 64-bit twos-complement arithmetic since PHP wants to treat all ints
+ // as signed and any int over 2^31 - 1 as a float
+ if (PHP_INT_SIZE == 4) {
+ $neg = $value < 0;
+
+ if ($neg) {
+ $value *= -1;
+ }
+
+ $hi = (int)($value / 4294967296);
+ $lo = (int)$value;
+
+ if ($neg) {
+ $hi = ~$hi;
+ $lo = ~$lo;
+ if (($lo & (int)0xffffffff) == (int)0xffffffff) {
+ $lo = 0;
+ $hi++;
+ } else {
+ $lo++;
+ }
+ }
+ $data = pack('N2', $hi, $lo);
+ } else {
+ $hi = $value >> 32;
+ $lo = $value & 0xFFFFFFFF;
+ $data = pack('N2', $hi, $lo);
+ }
+
+ $this->trans_->write($data, 8);
+
+ return 8;
+ }
+
+ public function writeDouble($value)
+ {
+ $data = pack('d', $value);
+ $this->trans_->write(strrev($data), 8);
+
+ return 8;
+ }
+
+ public function writeString($value)
+ {
+ $len = TStringFuncFactory::create()->strlen($value);
+ $result = $this->writeI32($len);
+ if ($len) {
+ $this->trans_->write($value, $len);
+ }
+
+ return $result + $len;
+ }
+
+ public function readMessageBegin(&$name, &$type, &$seqid)
+ {
+ $result = $this->readI32($sz);
+ if ($sz < 0) {
+ $version = (int)($sz & self::VERSION_MASK);
+ if ($version != (int)self::VERSION_1) {
+ throw new TProtocolException('Bad version identifier: ' . $sz, TProtocolException::BAD_VERSION);
+ }
+ $type = $sz & 0x000000ff;
+ $result +=
+ $this->readString($name) +
+ $this->readI32($seqid);
+ } else {
+ if ($this->strictRead_) {
+ throw new TProtocolException(
+ 'No version identifier, old protocol client?',
+ TProtocolException::BAD_VERSION
+ );
+ } else {
+ // Handle pre-versioned input
+ $name = $this->trans_->readAll($sz);
+ $result +=
+ $sz +
+ $this->readByte($type) +
+ $this->readI32($seqid);
+ }
+ }
+
+ return $result;
+ }
+
+ public function readMessageEnd()
+ {
+ return 0;
+ }
+
+ public function readStructBegin(&$name)
+ {
+ $name = '';
+
+ return 0;
+ }
+
+ public function readStructEnd()
+ {
+ return 0;
+ }
+
+ public function readFieldBegin(&$name, &$fieldType, &$fieldId)
+ {
+ $result = $this->readByte($fieldType);
+ if ($fieldType == TType::STOP) {
+ $fieldId = 0;
+
+ return $result;
+ }
+ $result += $this->readI16($fieldId);
+
+ return $result;
+ }
+
+ public function readFieldEnd()
+ {
+ return 0;
+ }
+
+ public function readMapBegin(&$keyType, &$valType, &$size)
+ {
+ return
+ $this->readByte($keyType) +
+ $this->readByte($valType) +
+ $this->readI32($size);
+ }
+
+ public function readMapEnd()
+ {
+ return 0;
+ }
+
+ public function readListBegin(&$elemType, &$size)
+ {
+ return
+ $this->readByte($elemType) +
+ $this->readI32($size);
+ }
+
+ public function readListEnd()
+ {
+ return 0;
+ }
+
+ public function readSetBegin(&$elemType, &$size)
+ {
+ return
+ $this->readByte($elemType) +
+ $this->readI32($size);
+ }
+
+ public function readSetEnd()
+ {
+ return 0;
+ }
+
+ public function readBool(&$value)
+ {
+ $data = $this->trans_->readAll(1);
+ $arr = unpack('c', $data);
+ $value = $arr[1] == 1;
+
+ return 1;
+ }
+
+ public function readByte(&$value)
+ {
+ $data = $this->trans_->readAll(1);
+ $arr = unpack('c', $data);
+ $value = $arr[1];
+
+ return 1;
+ }
+
+ public function readI16(&$value)
+ {
+ $data = $this->trans_->readAll(2);
+ $arr = unpack('n', $data);
+ $value = $arr[1];
+ if ($value > 0x7fff) {
+ $value = 0 - (($value - 1) ^ 0xffff);
+ }
+
+ return 2;
+ }
+
+ public function readI32(&$value)
+ {
+ $data = $this->trans_->readAll(4);
+ $arr = unpack('N', $data);
+ $value = $arr[1];
+ if ($value > 0x7fffffff) {
+ $value = 0 - (($value - 1) ^ 0xffffffff);
+ }
+
+ return 4;
+ }
+
+ public function readI64(&$value)
+ {
+ $data = $this->trans_->readAll(8);
+
+ $arr = unpack('N2', $data);
+
+ // If we are on a 32bit architecture we have to explicitly deal with
+ // 64-bit twos-complement arithmetic since PHP wants to treat all ints
+ // as signed and any int over 2^31 - 1 as a float
+ if (PHP_INT_SIZE == 4) {
+ $hi = $arr[1];
+ $lo = $arr[2];
+ $isNeg = $hi < 0;
+
+ // Check for a negative
+ if ($isNeg) {
+ $hi = ~$hi & (int)0xffffffff;
+ $lo = ~$lo & (int)0xffffffff;
+
+ if ($lo == (int)0xffffffff) {
+ $hi++;
+ $lo = 0;
+ } else {
+ $lo++;
+ }
+ }
+
+ // Force 32bit words in excess of 2G to pe positive - we deal wigh sign
+ // explicitly below
+
+ if ($hi & (int)0x80000000) {
+ $hi &= (int)0x7fffffff;
+ $hi += 0x80000000;
+ }
+
+ if ($lo & (int)0x80000000) {
+ $lo &= (int)0x7fffffff;
+ $lo += 0x80000000;
+ }
+
+ $value = $hi * 4294967296 + $lo;
+
+ if ($isNeg) {
+ $value = 0 - $value;
+ }
+ } else {
+ // Upcast negatives in LSB bit
+ if ($arr[2] & 0x80000000) {
+ $arr[2] = $arr[2] & 0xffffffff;
+ }
+
+ // Check for a negative
+ if ($arr[1] & 0x80000000) {
+ $arr[1] = $arr[1] & 0xffffffff;
+ $arr[1] = $arr[1] ^ 0xffffffff;
+ $arr[2] = $arr[2] ^ 0xffffffff;
+ $value = 0 - $arr[1] * 4294967296 - $arr[2] - 1;
+ } else {
+ $value = $arr[1] * 4294967296 + $arr[2];
+ }
+ }
+
+ return 8;
+ }
+
+ public function readDouble(&$value)
+ {
+ $data = strrev($this->trans_->readAll(8));
+ $arr = unpack('d', $data);
+ $value = $arr[1];
+
+ return 8;
+ }
+
+ public function readString(&$value)
+ {
+ $result = $this->readI32($len);
+ if ($len) {
+ $value = $this->trans_->readAll($len);
+ } else {
+ $value = '';
+ }
+
+ return $result + $len;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Protocol/TBinaryProtocolAccelerated.php b/src/jaegertracing/thrift/lib/php/lib/Protocol/TBinaryProtocolAccelerated.php
new file mode 100644
index 000000000..ff799a6ab
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Protocol/TBinaryProtocolAccelerated.php
@@ -0,0 +1,67 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Protocol;
+
+use Thrift\Transport\TBufferedTransport;
+
+/**
+ * Accelerated binary protocol: used in conjunction with the thrift_protocol
+ * extension for faster deserialization
+ */
+class TBinaryProtocolAccelerated extends TBinaryProtocol
+{
+ public function __construct($trans, $strictRead = false, $strictWrite = true)
+ {
+ // If the transport doesn't implement putBack, wrap it in a
+ // TBufferedTransport (which does)
+
+ // NOTE (t.heintz): This is very evil to do, because the TBufferedTransport may swallow bytes, which
+ // are then never written to the underlying transport. This happens precisely when a number of bytes
+ // less than the max buffer size (512 by default) is written to the transport and then flush() is NOT
+ // called. In that case the data stays in the writeBuffer of the transport, from where it can never be
+ // accessed again (for example through read()).
+ //
+ // Since the caller of this method does not know about the wrapping transport, this creates bugs which
+ // are very difficult to find. Hence the wrapping of a transport in a buffer should be left to the
+ // calling code. An interface could used to mandate the presence of the putBack() method in the transport.
+ //
+ // I am leaving this code in nonetheless, because there may be applications depending on this behavior.
+ //
+ // @see THRIFT-1579
+
+ if (!method_exists($trans, 'putBack')) {
+ $trans = new TBufferedTransport($trans);
+ }
+ parent::__construct($trans, $strictRead, $strictWrite);
+ }
+
+ public function isStrictRead()
+ {
+ return $this->strictRead_;
+ }
+
+ public function isStrictWrite()
+ {
+ return $this->strictWrite_;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Protocol/TCompactProtocol.php b/src/jaegertracing/thrift/lib/php/lib/Protocol/TCompactProtocol.php
new file mode 100644
index 000000000..1af2a274a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Protocol/TCompactProtocol.php
@@ -0,0 +1,739 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Protocol;
+
+use Thrift\Type\TType;
+use Thrift\Exception\TProtocolException;
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * Compact implementation of the Thrift protocol.
+ *
+ */
+class TCompactProtocol extends TProtocol
+{
+ const COMPACT_STOP = 0x00;
+ const COMPACT_TRUE = 0x01;
+ const COMPACT_FALSE = 0x02;
+ const COMPACT_BYTE = 0x03;
+ const COMPACT_I16 = 0x04;
+ const COMPACT_I32 = 0x05;
+ const COMPACT_I64 = 0x06;
+ const COMPACT_DOUBLE = 0x07;
+ const COMPACT_BINARY = 0x08;
+ const COMPACT_LIST = 0x09;
+ const COMPACT_SET = 0x0A;
+ const COMPACT_MAP = 0x0B;
+ const COMPACT_STRUCT = 0x0C;
+
+ const STATE_CLEAR = 0;
+ const STATE_FIELD_WRITE = 1;
+ const STATE_VALUE_WRITE = 2;
+ const STATE_CONTAINER_WRITE = 3;
+ const STATE_BOOL_WRITE = 4;
+ const STATE_FIELD_READ = 5;
+ const STATE_CONTAINER_READ = 6;
+ const STATE_VALUE_READ = 7;
+ const STATE_BOOL_READ = 8;
+
+ const VERSION_MASK = 0x1f;
+ const VERSION = 1;
+ const PROTOCOL_ID = 0x82;
+ const TYPE_MASK = 0xe0;
+ const TYPE_BITS = 0x07;
+ const TYPE_SHIFT_AMOUNT = 5;
+
+ protected static $ctypes = array(
+ TType::STOP => TCompactProtocol::COMPACT_STOP,
+ TType::BOOL => TCompactProtocol::COMPACT_TRUE, // used for collection
+ TType::BYTE => TCompactProtocol::COMPACT_BYTE,
+ TType::I16 => TCompactProtocol::COMPACT_I16,
+ TType::I32 => TCompactProtocol::COMPACT_I32,
+ TType::I64 => TCompactProtocol::COMPACT_I64,
+ TType::DOUBLE => TCompactProtocol::COMPACT_DOUBLE,
+ TType::STRING => TCompactProtocol::COMPACT_BINARY,
+ TType::STRUCT => TCompactProtocol::COMPACT_STRUCT,
+ TType::LST => TCompactProtocol::COMPACT_LIST,
+ TType::SET => TCompactProtocol::COMPACT_SET,
+ TType::MAP => TCompactProtocol::COMPACT_MAP,
+ );
+
+ protected static $ttypes = array(
+ TCompactProtocol::COMPACT_STOP => TType::STOP,
+ TCompactProtocol::COMPACT_TRUE => TType::BOOL, // used for collection
+ TCompactProtocol::COMPACT_FALSE => TType::BOOL,
+ TCompactProtocol::COMPACT_BYTE => TType::BYTE,
+ TCompactProtocol::COMPACT_I16 => TType::I16,
+ TCompactProtocol::COMPACT_I32 => TType::I32,
+ TCompactProtocol::COMPACT_I64 => TType::I64,
+ TCompactProtocol::COMPACT_DOUBLE => TType::DOUBLE,
+ TCompactProtocol::COMPACT_BINARY => TType::STRING,
+ TCompactProtocol::COMPACT_STRUCT => TType::STRUCT,
+ TCompactProtocol::COMPACT_LIST => TType::LST,
+ TCompactProtocol::COMPACT_SET => TType::SET,
+ TCompactProtocol::COMPACT_MAP => TType::MAP,
+ );
+
+ protected $state = TCompactProtocol::STATE_CLEAR;
+ protected $lastFid = 0;
+ protected $boolFid = null;
+ protected $boolValue = null;
+ protected $structs = array();
+ protected $containers = array();
+
+ // Some varint / zigzag helper methods
+ public function toZigZag($n, $bits)
+ {
+ return ($n << 1) ^ ($n >> ($bits - 1));
+ }
+
+ public function fromZigZag($n)
+ {
+ return ($n >> 1) ^ -($n & 1);
+ }
+
+ public function getVarint($data)
+ {
+ $out = "";
+ while (true) {
+ if (($data & ~0x7f) === 0) {
+ $out .= chr($data);
+ break;
+ } else {
+ $out .= chr(($data & 0xff) | 0x80);
+ $data = $data >> 7;
+ }
+ }
+
+ return $out;
+ }
+
+ public function writeVarint($data)
+ {
+ $out = $this->getVarint($data);
+ $result = TStringFuncFactory::create()->strlen($out);
+ $this->trans_->write($out, $result);
+
+ return $result;
+ }
+
+ public function readVarint(&$result)
+ {
+ $idx = 0;
+ $shift = 0;
+ $result = 0;
+ while (true) {
+ $x = $this->trans_->readAll(1);
+ $arr = unpack('C', $x);
+ $byte = $arr[1];
+ $idx += 1;
+ $result |= ($byte & 0x7f) << $shift;
+ if (($byte >> 7) === 0) {
+ return $idx;
+ }
+ $shift += 7;
+ }
+
+ return $idx;
+ }
+
+ public function __construct($trans)
+ {
+ parent::__construct($trans);
+ }
+
+ public function writeMessageBegin($name, $type, $seqid)
+ {
+ $written =
+ $this->writeUByte(TCompactProtocol::PROTOCOL_ID) +
+ $this->writeUByte(TCompactProtocol::VERSION |
+ ($type << TCompactProtocol::TYPE_SHIFT_AMOUNT)) +
+ $this->writeVarint($seqid) +
+ $this->writeString($name);
+ $this->state = TCompactProtocol::STATE_VALUE_WRITE;
+
+ return $written;
+ }
+
+ public function writeMessageEnd()
+ {
+ $this->state = TCompactProtocol::STATE_CLEAR;
+
+ return 0;
+ }
+
+ public function writeStructBegin($name)
+ {
+ $this->structs[] = array($this->state, $this->lastFid);
+ $this->state = TCompactProtocol::STATE_FIELD_WRITE;
+ $this->lastFid = 0;
+
+ return 0;
+ }
+
+ public function writeStructEnd()
+ {
+ $old_values = array_pop($this->structs);
+ $this->state = $old_values[0];
+ $this->lastFid = $old_values[1];
+
+ return 0;
+ }
+
+ public function writeFieldStop()
+ {
+ return $this->writeByte(0);
+ }
+
+ public function writeFieldHeader($type, $fid)
+ {
+ $written = 0;
+ $delta = $fid - $this->lastFid;
+ if (0 < $delta && $delta <= 15) {
+ $written = $this->writeUByte(($delta << 4) | $type);
+ } else {
+ $written = $this->writeByte($type) +
+ $this->writeI16($fid);
+ }
+ $this->lastFid = $fid;
+
+ return $written;
+ }
+
+ public function writeFieldBegin($field_name, $field_type, $field_id)
+ {
+ if ($field_type == TTYPE::BOOL) {
+ $this->state = TCompactProtocol::STATE_BOOL_WRITE;
+ $this->boolFid = $field_id;
+
+ return 0;
+ } else {
+ $this->state = TCompactProtocol::STATE_VALUE_WRITE;
+
+ return $this->writeFieldHeader(self::$ctypes[$field_type], $field_id);
+ }
+ }
+
+ public function writeFieldEnd()
+ {
+ $this->state = TCompactProtocol::STATE_FIELD_WRITE;
+
+ return 0;
+ }
+
+ public function writeCollectionBegin($etype, $size)
+ {
+ $written = 0;
+ if ($size <= 14) {
+ $written = $this->writeUByte($size << 4 |
+ self::$ctypes[$etype]);
+ } else {
+ $written = $this->writeUByte(0xf0 |
+ self::$ctypes[$etype]) +
+ $this->writeVarint($size);
+ }
+ $this->containers[] = $this->state;
+ $this->state = TCompactProtocol::STATE_CONTAINER_WRITE;
+
+ return $written;
+ }
+
+ public function writeMapBegin($key_type, $val_type, $size)
+ {
+ $written = 0;
+ if ($size == 0) {
+ $written = $this->writeByte(0);
+ } else {
+ $written = $this->writeVarint($size) +
+ $this->writeUByte(self::$ctypes[$key_type] << 4 |
+ self::$ctypes[$val_type]);
+ }
+ $this->containers[] = $this->state;
+
+ return $written;
+ }
+
+ public function writeCollectionEnd()
+ {
+ $this->state = array_pop($this->containers);
+
+ return 0;
+ }
+
+ public function writeMapEnd()
+ {
+ return $this->writeCollectionEnd();
+ }
+
+ public function writeListBegin($elem_type, $size)
+ {
+ return $this->writeCollectionBegin($elem_type, $size);
+ }
+
+ public function writeListEnd()
+ {
+ return $this->writeCollectionEnd();
+ }
+
+ public function writeSetBegin($elem_type, $size)
+ {
+ return $this->writeCollectionBegin($elem_type, $size);
+ }
+
+ public function writeSetEnd()
+ {
+ return $this->writeCollectionEnd();
+ }
+
+ public function writeBool($value)
+ {
+ if ($this->state == TCompactProtocol::STATE_BOOL_WRITE) {
+ $ctype = TCompactProtocol::COMPACT_FALSE;
+ if ($value) {
+ $ctype = TCompactProtocol::COMPACT_TRUE;
+ }
+
+ return $this->writeFieldHeader($ctype, $this->boolFid);
+ } elseif ($this->state == TCompactProtocol::STATE_CONTAINER_WRITE) {
+ return $this->writeByte($value ? 1 : 0);
+ } else {
+ throw new TProtocolException('Invalid state in compact protocol');
+ }
+ }
+
+ public function writeByte($value)
+ {
+ $data = pack('c', $value);
+ $this->trans_->write($data, 1);
+
+ return 1;
+ }
+
+ public function writeUByte($byte)
+ {
+ $this->trans_->write(pack('C', $byte), 1);
+
+ return 1;
+ }
+
+ public function writeI16($value)
+ {
+ $thing = $this->toZigZag($value, 16);
+
+ return $this->writeVarint($thing);
+ }
+
+ public function writeI32($value)
+ {
+ $thing = $this->toZigZag($value, 32);
+
+ return $this->writeVarint($thing);
+ }
+
+ public function writeDouble($value)
+ {
+ $data = pack('d', $value);
+ $this->trans_->write($data, 8);
+
+ return 8;
+ }
+
+ public function writeString($value)
+ {
+ $len = TStringFuncFactory::create()->strlen($value);
+ $result = $this->writeVarint($len);
+ if ($len) {
+ $this->trans_->write($value, $len);
+ }
+
+ return $result + $len;
+ }
+
+ public function readFieldBegin(&$name, &$field_type, &$field_id)
+ {
+ $result = $this->readUByte($compact_type_and_delta);
+
+ $compact_type = $compact_type_and_delta & 0x0f;
+
+ if ($compact_type == TType::STOP) {
+ $field_type = $compact_type;
+ $field_id = 0;
+
+ return $result;
+ }
+ $delta = $compact_type_and_delta >> 4;
+ if ($delta == 0) {
+ $result += $this->readI16($field_id);
+ } else {
+ $field_id = $this->lastFid + $delta;
+ }
+ $this->lastFid = $field_id;
+ $field_type = $this->getTType($compact_type);
+
+ if ($compact_type == TCompactProtocol::COMPACT_TRUE) {
+ $this->state = TCompactProtocol::STATE_BOOL_READ;
+ $this->boolValue = true;
+ } elseif ($compact_type == TCompactProtocol::COMPACT_FALSE) {
+ $this->state = TCompactProtocol::STATE_BOOL_READ;
+ $this->boolValue = false;
+ } else {
+ $this->state = TCompactProtocol::STATE_VALUE_READ;
+ }
+
+ return $result;
+ }
+
+ public function readFieldEnd()
+ {
+ $this->state = TCompactProtocol::STATE_FIELD_READ;
+
+ return 0;
+ }
+
+ public function readUByte(&$value)
+ {
+ $data = $this->trans_->readAll(1);
+ $arr = unpack('C', $data);
+ $value = $arr[1];
+
+ return 1;
+ }
+
+ public function readByte(&$value)
+ {
+ $data = $this->trans_->readAll(1);
+ $arr = unpack('c', $data);
+ $value = $arr[1];
+
+ return 1;
+ }
+
+ public function readZigZag(&$value)
+ {
+ $result = $this->readVarint($value);
+ $value = $this->fromZigZag($value);
+
+ return $result;
+ }
+
+ public function readMessageBegin(&$name, &$type, &$seqid)
+ {
+ $protoId = 0;
+ $result = $this->readUByte($protoId);
+ if ($protoId != TCompactProtocol::PROTOCOL_ID) {
+ throw new TProtocolException('Bad protocol id in TCompact message');
+ }
+ $verType = 0;
+ $result += $this->readUByte($verType);
+ $type = ($verType >> TCompactProtocol::TYPE_SHIFT_AMOUNT) & TCompactProtocol::TYPE_BITS;
+ $version = $verType & TCompactProtocol::VERSION_MASK;
+ if ($version != TCompactProtocol::VERSION) {
+ throw new TProtocolException('Bad version in TCompact message');
+ }
+ $result += $this->readVarint($seqid);
+ $result += $this->readString($name);
+
+ return $result;
+ }
+
+ public function readMessageEnd()
+ {
+ return 0;
+ }
+
+ public function readStructBegin(&$name)
+ {
+ $name = ''; // unused
+ $this->structs[] = array($this->state, $this->lastFid);
+ $this->state = TCompactProtocol::STATE_FIELD_READ;
+ $this->lastFid = 0;
+
+ return 0;
+ }
+
+ public function readStructEnd()
+ {
+ $last = array_pop($this->structs);
+ $this->state = $last[0];
+ $this->lastFid = $last[1];
+
+ return 0;
+ }
+
+ public function readCollectionBegin(&$type, &$size)
+ {
+ $sizeType = 0;
+ $result = $this->readUByte($sizeType);
+ $size = $sizeType >> 4;
+ $type = $this->getTType($sizeType);
+ if ($size == 15) {
+ $result += $this->readVarint($size);
+ }
+ $this->containers[] = $this->state;
+ $this->state = TCompactProtocol::STATE_CONTAINER_READ;
+
+ return $result;
+ }
+
+ public function readMapBegin(&$key_type, &$val_type, &$size)
+ {
+ $result = $this->readVarint($size);
+ $types = 0;
+ if ($size > 0) {
+ $result += $this->readUByte($types);
+ }
+ $val_type = $this->getTType($types);
+ $key_type = $this->getTType($types >> 4);
+ $this->containers[] = $this->state;
+ $this->state = TCompactProtocol::STATE_CONTAINER_READ;
+
+ return $result;
+ }
+
+ public function readCollectionEnd()
+ {
+ $this->state = array_pop($this->containers);
+
+ return 0;
+ }
+
+ public function readMapEnd()
+ {
+ return $this->readCollectionEnd();
+ }
+
+ public function readListBegin(&$elem_type, &$size)
+ {
+ return $this->readCollectionBegin($elem_type, $size);
+ }
+
+ public function readListEnd()
+ {
+ return $this->readCollectionEnd();
+ }
+
+ public function readSetBegin(&$elem_type, &$size)
+ {
+ return $this->readCollectionBegin($elem_type, $size);
+ }
+
+ public function readSetEnd()
+ {
+ return $this->readCollectionEnd();
+ }
+
+ public function readBool(&$value)
+ {
+ if ($this->state == TCompactProtocol::STATE_BOOL_READ) {
+ $value = $this->boolValue;
+
+ return 0;
+ } elseif ($this->state == TCompactProtocol::STATE_CONTAINER_READ) {
+ return $this->readByte($value);
+ } else {
+ throw new TProtocolException('Invalid state in compact protocol');
+ }
+ }
+
+ public function readI16(&$value)
+ {
+ return $this->readZigZag($value);
+ }
+
+ public function readI32(&$value)
+ {
+ return $this->readZigZag($value);
+ }
+
+ public function readDouble(&$value)
+ {
+ $data = $this->trans_->readAll(8);
+ $arr = unpack('d', $data);
+ $value = $arr[1];
+
+ return 8;
+ }
+
+ public function readString(&$value)
+ {
+ $result = $this->readVarint($len);
+ if ($len) {
+ $value = $this->trans_->readAll($len);
+ } else {
+ $value = '';
+ }
+
+ return $result + $len;
+ }
+
+ public function getTType($byte)
+ {
+ return self::$ttypes[$byte & 0x0f];
+ }
+
+ // If we are on a 32bit architecture we have to explicitly deal with
+ // 64-bit twos-complement arithmetic since PHP wants to treat all ints
+ // as signed and any int over 2^31 - 1 as a float
+
+ // Read and write I64 as two 32 bit numbers $hi and $lo
+
+ public function readI64(&$value)
+ {
+ // Read varint from wire
+ $hi = 0;
+ $lo = 0;
+
+ $idx = 0;
+ $shift = 0;
+
+ while (true) {
+ $x = $this->trans_->readAll(1);
+ $arr = unpack('C', $x);
+ $byte = $arr[1];
+ $idx += 1;
+ // Shift hi and lo together.
+ if ($shift < 28) {
+ $lo |= (($byte & 0x7f) << $shift);
+ } elseif ($shift == 28) {
+ $lo |= (($byte & 0x0f) << 28);
+ $hi |= (($byte & 0x70) >> 4);
+ } else {
+ $hi |= (($byte & 0x7f) << ($shift - 32));
+ }
+ if (($byte >> 7) === 0) {
+ break;
+ }
+ $shift += 7;
+ }
+
+ // Now, unzig it.
+ $xorer = 0;
+ if ($lo & 1) {
+ $xorer = 0xffffffff;
+ }
+ $lo = ($lo >> 1) & 0x7fffffff;
+ $lo = $lo | (($hi & 1) << 31);
+ $hi = ($hi >> 1) ^ $xorer;
+ $lo = $lo ^ $xorer;
+
+ // Now put $hi and $lo back together
+ $isNeg = $hi < 0 || $hi & 0x80000000;
+
+ // Check for a negative
+ if ($isNeg) {
+ $hi = ~$hi & (int)0xffffffff;
+ $lo = ~$lo & (int)0xffffffff;
+
+ if ($lo == (int)0xffffffff) {
+ $hi++;
+ $lo = 0;
+ } else {
+ $lo++;
+ }
+ }
+
+ // Force 32bit words in excess of 2G to be positive - we deal with sign
+ // explicitly below
+
+ if ($hi & (int)0x80000000) {
+ $hi &= (int)0x7fffffff;
+ $hi += 0x80000000;
+ }
+
+ if ($lo & (int)0x80000000) {
+ $lo &= (int)0x7fffffff;
+ $lo += 0x80000000;
+ }
+
+ // Create as negative value first, since we can store -2^63 but not 2^63
+ $value = -$hi * 4294967296 - $lo;
+
+ if (!$isNeg) {
+ $value = -$value;
+ }
+
+ return $idx;
+ }
+
+ public function writeI64($value)
+ {
+ // If we are in an I32 range, use the easy method below.
+ if (($value > 4294967296) || ($value < -4294967296)) {
+ // Convert $value to $hi and $lo
+ $neg = $value < 0;
+
+ if ($neg) {
+ $value *= -1;
+ }
+
+ $hi = (int)$value >> 32;
+ $lo = (int)$value & 0xffffffff;
+
+ if ($neg) {
+ $hi = ~$hi;
+ $lo = ~$lo;
+ if (($lo & (int)0xffffffff) == (int)0xffffffff) {
+ $lo = 0;
+ $hi++;
+ } else {
+ $lo++;
+ }
+ }
+
+ // Now do the zigging and zagging.
+ $xorer = 0;
+ if ($neg) {
+ $xorer = 0xffffffff;
+ }
+ $lowbit = ($lo >> 31) & 1;
+ $hi = ($hi << 1) | $lowbit;
+ $lo = ($lo << 1);
+ $lo = ($lo ^ $xorer) & 0xffffffff;
+ $hi = ($hi ^ $xorer) & 0xffffffff;
+
+ // now write out the varint, ensuring we shift both hi and lo
+ $out = "";
+ while (true) {
+ if (($lo & ~0x7f) === 0 &&
+ $hi === 0) {
+ $out .= chr($lo);
+ break;
+ } else {
+ $out .= chr(($lo & 0xff) | 0x80);
+ $lo = $lo >> 7;
+ $lo = $lo | ($hi << 25);
+ $hi = $hi >> 7;
+ // Right shift carries sign, but we don't want it to.
+ $hi = $hi & (127 << 25);
+ }
+ }
+
+ $ret = TStringFuncFactory::create()->strlen($out);
+ $this->trans_->write($out, $ret);
+
+ return $ret;
+ } else {
+ return $this->writeVarint($this->toZigZag($value, 64));
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Protocol/TJSONProtocol.php b/src/jaegertracing/thrift/lib/php/lib/Protocol/TJSONProtocol.php
new file mode 100644
index 000000000..914488421
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Protocol/TJSONProtocol.php
@@ -0,0 +1,815 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Protocol;
+
+use Thrift\Type\TType;
+use Thrift\Exception\TProtocolException;
+use Thrift\Protocol\JSON\BaseContext;
+use Thrift\Protocol\JSON\LookaheadReader;
+use Thrift\Protocol\JSON\PairContext;
+use Thrift\Protocol\JSON\ListContext;
+
+/**
+ * JSON implementation of thrift protocol, ported from Java.
+ */
+class TJSONProtocol extends TProtocol
+{
+ const COMMA = ',';
+ const COLON = ':';
+ const LBRACE = '{';
+ const RBRACE = '}';
+ const LBRACKET = '[';
+ const RBRACKET = ']';
+ const QUOTE = '"';
+ const BACKSLASH = '\\';
+ const ZERO = '0';
+ const ESCSEQ = '\\';
+ const DOUBLEESC = '__DOUBLE_ESCAPE_SEQUENCE__';
+
+ const VERSION = 1;
+
+ public static $JSON_CHAR_TABLE = array(
+ /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
+ 1, 1, '"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
+ );
+
+ public static $ESCAPE_CHARS = array('"', '\\', '/', "b", "f", "n", "r", "t");
+
+ public static $ESCAPE_CHAR_VALS = array(
+ '"', '\\', '/', "\x08", "\f", "\n", "\r", "\t",
+ );
+
+ const NAME_BOOL = "tf";
+ const NAME_BYTE = "i8";
+ const NAME_I16 = "i16";
+ const NAME_I32 = "i32";
+ const NAME_I64 = "i64";
+ const NAME_DOUBLE = "dbl";
+ const NAME_STRUCT = "rec";
+ const NAME_STRING = "str";
+ const NAME_MAP = "map";
+ const NAME_LIST = "lst";
+ const NAME_SET = "set";
+
+ private function getTypeNameForTypeID($typeID)
+ {
+ switch ($typeID) {
+ case TType::BOOL:
+ return self::NAME_BOOL;
+ case TType::BYTE:
+ return self::NAME_BYTE;
+ case TType::I16:
+ return self::NAME_I16;
+ case TType::I32:
+ return self::NAME_I32;
+ case TType::I64:
+ return self::NAME_I64;
+ case TType::DOUBLE:
+ return self::NAME_DOUBLE;
+ case TType::STRING:
+ return self::NAME_STRING;
+ case TType::STRUCT:
+ return self::NAME_STRUCT;
+ case TType::MAP:
+ return self::NAME_MAP;
+ case TType::SET:
+ return self::NAME_SET;
+ case TType::LST:
+ return self::NAME_LIST;
+ default:
+ throw new TProtocolException("Unrecognized type", TProtocolException::UNKNOWN);
+ }
+ }
+
+ private function getTypeIDForTypeName($name)
+ {
+ $result = TType::STOP;
+
+ if (strlen($name) > 1) {
+ switch (substr($name, 0, 1)) {
+ case 'd':
+ $result = TType::DOUBLE;
+ break;
+ case 'i':
+ switch (substr($name, 1, 1)) {
+ case '8':
+ $result = TType::BYTE;
+ break;
+ case '1':
+ $result = TType::I16;
+ break;
+ case '3':
+ $result = TType::I32;
+ break;
+ case '6':
+ $result = TType::I64;
+ break;
+ }
+ break;
+ case 'l':
+ $result = TType::LST;
+ break;
+ case 'm':
+ $result = TType::MAP;
+ break;
+ case 'r':
+ $result = TType::STRUCT;
+ break;
+ case 's':
+ if (substr($name, 1, 1) == 't') {
+ $result = TType::STRING;
+ } elseif (substr($name, 1, 1) == 'e') {
+ $result = TType::SET;
+ }
+ break;
+ case 't':
+ $result = TType::BOOL;
+ break;
+ }
+ }
+ if ($result == TType::STOP) {
+ throw new TProtocolException("Unrecognized type", TProtocolException::INVALID_DATA);
+ }
+
+ return $result;
+ }
+
+ public $contextStack_ = array();
+ public $context_;
+ public $reader_;
+
+ private function pushContext($c)
+ {
+ array_push($this->contextStack_, $this->context_);
+ $this->context_ = $c;
+ }
+
+ private function popContext()
+ {
+ $this->context_ = array_pop($this->contextStack_);
+ }
+
+ public function __construct($trans)
+ {
+ parent::__construct($trans);
+ $this->context_ = new BaseContext();
+ $this->reader_ = new LookaheadReader($this);
+ }
+
+ public function reset()
+ {
+ $this->contextStack_ = array();
+ $this->context_ = new BaseContext();
+ $this->reader_ = new LookaheadReader($this);
+ }
+
+ private $tmpbuf_ = array(4);
+
+ public function readJSONSyntaxChar($b)
+ {
+ $ch = $this->reader_->read();
+
+ if (substr($ch, 0, 1) != $b) {
+ throw new TProtocolException("Unexpected character: " . $ch, TProtocolException::INVALID_DATA);
+ }
+ }
+
+ private function hexVal($s)
+ {
+ for ($i = 0; $i < strlen($s); $i++) {
+ $ch = substr($s, $i, 1);
+
+ if (!($ch >= "a" && $ch <= "f") && !($ch >= "0" && $ch <= "9")) {
+ throw new TProtocolException("Expected hex character " . $ch, TProtocolException::INVALID_DATA);
+ }
+ }
+
+ return hexdec($s);
+ }
+
+ private function hexChar($val)
+ {
+ return dechex($val);
+ }
+
+ private function hasJSONUnescapedUnicode()
+ {
+ if (PHP_MAJOR_VERSION > 5 || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private function unescapedUnicode($str)
+ {
+ if ($this->hasJSONUnescapedUnicode()) {
+ return json_encode($str, JSON_UNESCAPED_UNICODE);
+ }
+
+ $json = json_encode($str);
+
+ /*
+ * Unescaped character outside the Basic Multilingual Plane
+ * High surrogate: 0xD800 - 0xDBFF
+ * Low surrogate: 0xDC00 - 0xDFFF
+ */
+ $json = preg_replace_callback(
+ '/\\\\u(d[89ab][0-9a-f]{2})\\\\u(d[cdef][0-9a-f]{2})/i',
+ function ($matches) {
+ return mb_convert_encoding(pack('H*', $matches[1] . $matches[2]), 'UTF-8', 'UTF-16BE');
+ },
+ $json
+ );
+
+ /*
+ * Unescaped characters within the Basic Multilingual Plane
+ */
+ $json = preg_replace_callback(
+ '/\\\\u([0-9a-f]{4})/i',
+ function ($matches) {
+ return mb_convert_encoding(pack('H*', $matches[1]), 'UTF-8', 'UTF-16BE');
+ },
+ $json
+ );
+
+ return $json;
+ }
+
+ private function writeJSONString($b)
+ {
+ $this->context_->write();
+
+ if (is_numeric($b) && $this->context_->escapeNum()) {
+ $this->trans_->write(self::QUOTE);
+ }
+
+ $this->trans_->write($this->unescapedUnicode($b));
+
+ if (is_numeric($b) && $this->context_->escapeNum()) {
+ $this->trans_->write(self::QUOTE);
+ }
+ }
+
+ private function writeJSONInteger($num)
+ {
+ $this->context_->write();
+
+ if ($this->context_->escapeNum()) {
+ $this->trans_->write(self::QUOTE);
+ }
+
+ $this->trans_->write($num);
+
+ if ($this->context_->escapeNum()) {
+ $this->trans_->write(self::QUOTE);
+ }
+ }
+
+ private function writeJSONDouble($num)
+ {
+ $this->context_->write();
+
+ if ($this->context_->escapeNum()) {
+ $this->trans_->write(self::QUOTE);
+ }
+
+ $this->trans_->write(json_encode($num));
+
+ if ($this->context_->escapeNum()) {
+ $this->trans_->write(self::QUOTE);
+ }
+ }
+
+ private function writeJSONBase64($data)
+ {
+ $this->context_->write();
+ $this->trans_->write(self::QUOTE);
+ $this->trans_->write(json_encode(base64_encode($data)));
+ $this->trans_->write(self::QUOTE);
+ }
+
+ private function writeJSONObjectStart()
+ {
+ $this->context_->write();
+ $this->trans_->write(self::LBRACE);
+ $this->pushContext(new PairContext($this));
+ }
+
+ private function writeJSONObjectEnd()
+ {
+ $this->popContext();
+ $this->trans_->write(self::RBRACE);
+ }
+
+ private function writeJSONArrayStart()
+ {
+ $this->context_->write();
+ $this->trans_->write(self::LBRACKET);
+ $this->pushContext(new ListContext($this));
+ }
+
+ private function writeJSONArrayEnd()
+ {
+ $this->popContext();
+ $this->trans_->write(self::RBRACKET);
+ }
+
+ private function readJSONString($skipContext)
+ {
+ if (!$skipContext) {
+ $this->context_->read();
+ }
+
+ $jsonString = '';
+ $lastChar = null;
+ while (true) {
+ $ch = $this->reader_->read();
+ $jsonString .= $ch;
+ if ($ch == self::QUOTE &&
+ $lastChar !== null &&
+ $lastChar !== self::ESCSEQ) {
+ break;
+ }
+ if ($ch == self::ESCSEQ && $lastChar == self::ESCSEQ) {
+ $lastChar = self::DOUBLEESC;
+ } else {
+ $lastChar = $ch;
+ }
+ }
+
+ return json_decode($jsonString);
+ }
+
+ private function isJSONNumeric($b)
+ {
+ switch ($b) {
+ case '+':
+ case '-':
+ case '.':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'E':
+ case 'e':
+ return true;
+ }
+
+ return false;
+ }
+
+ private function readJSONNumericChars()
+ {
+ $strbld = array();
+
+ while (true) {
+ $ch = $this->reader_->peek();
+
+ if (!$this->isJSONNumeric($ch)) {
+ break;
+ }
+
+ $strbld[] = $this->reader_->read();
+ }
+
+ return implode("", $strbld);
+ }
+
+ private function readJSONInteger()
+ {
+ $this->context_->read();
+
+ if ($this->context_->escapeNum()) {
+ $this->readJSONSyntaxChar(self::QUOTE);
+ }
+
+ $str = $this->readJSONNumericChars();
+
+ if ($this->context_->escapeNum()) {
+ $this->readJSONSyntaxChar(self::QUOTE);
+ }
+
+ if (!is_numeric($str)) {
+ throw new TProtocolException("Invalid data in numeric: " . $str, TProtocolException::INVALID_DATA);
+ }
+
+ return intval($str);
+ }
+
+ /**
+ * Identical to readJSONInteger but without the final cast.
+ * Needed for proper handling of i64 on 32 bit machines. Why a
+ * separate function? So we don't have to force the rest of the
+ * use cases through the extra conditional.
+ */
+ private function readJSONIntegerAsString()
+ {
+ $this->context_->read();
+
+ if ($this->context_->escapeNum()) {
+ $this->readJSONSyntaxChar(self::QUOTE);
+ }
+
+ $str = $this->readJSONNumericChars();
+
+ if ($this->context_->escapeNum()) {
+ $this->readJSONSyntaxChar(self::QUOTE);
+ }
+
+ if (!is_numeric($str)) {
+ throw new TProtocolException("Invalid data in numeric: " . $str, TProtocolException::INVALID_DATA);
+ }
+
+ return $str;
+ }
+
+ private function readJSONDouble()
+ {
+ $this->context_->read();
+
+ if (substr($this->reader_->peek(), 0, 1) == self::QUOTE) {
+ $arr = $this->readJSONString(true);
+
+ if ($arr == "NaN") {
+ return NAN;
+ } elseif ($arr == "Infinity") {
+ return INF;
+ } elseif (!$this->context_->escapeNum()) {
+ throw new TProtocolException(
+ "Numeric data unexpectedly quoted " . $arr,
+ TProtocolException::INVALID_DATA
+ );
+ }
+
+ return floatval($arr);
+ } else {
+ if ($this->context_->escapeNum()) {
+ $this->readJSONSyntaxChar(self::QUOTE);
+ }
+
+ return floatval($this->readJSONNumericChars());
+ }
+ }
+
+ private function readJSONBase64()
+ {
+ $arr = $this->readJSONString(false);
+ $data = base64_decode($arr, true);
+
+ if ($data === false) {
+ throw new TProtocolException("Invalid base64 data " . $arr, TProtocolException::INVALID_DATA);
+ }
+
+ return $data;
+ }
+
+ private function readJSONObjectStart()
+ {
+ $this->context_->read();
+ $this->readJSONSyntaxChar(self::LBRACE);
+ $this->pushContext(new PairContext($this));
+ }
+
+ private function readJSONObjectEnd()
+ {
+ $this->readJSONSyntaxChar(self::RBRACE);
+ $this->popContext();
+ }
+
+ private function readJSONArrayStart()
+ {
+ $this->context_->read();
+ $this->readJSONSyntaxChar(self::LBRACKET);
+ $this->pushContext(new ListContext($this));
+ }
+
+ private function readJSONArrayEnd()
+ {
+ $this->readJSONSyntaxChar(self::RBRACKET);
+ $this->popContext();
+ }
+
+ /**
+ * Writes the message header
+ *
+ * @param string $name Function name
+ * @param int $type message type TMessageType::CALL or TMessageType::REPLY
+ * @param int $seqid The sequence id of this message
+ */
+ public function writeMessageBegin($name, $type, $seqid)
+ {
+ $this->writeJSONArrayStart();
+ $this->writeJSONInteger(self::VERSION);
+ $this->writeJSONString($name);
+ $this->writeJSONInteger($type);
+ $this->writeJSONInteger($seqid);
+ }
+
+ /**
+ * Close the message
+ */
+ public function writeMessageEnd()
+ {
+ $this->writeJSONArrayEnd();
+ }
+
+ /**
+ * Writes a struct header.
+ *
+ * @param string $name Struct name
+ * @throws TException on write error
+ * @return int How many bytes written
+ */
+ public function writeStructBegin($name)
+ {
+ $this->writeJSONObjectStart();
+ }
+
+ /**
+ * Close a struct.
+ *
+ * @throws TException on write error
+ * @return int How many bytes written
+ */
+ public function writeStructEnd()
+ {
+ $this->writeJSONObjectEnd();
+ }
+
+ public function writeFieldBegin($fieldName, $fieldType, $fieldId)
+ {
+ $this->writeJSONInteger($fieldId);
+ $this->writeJSONObjectStart();
+ $this->writeJSONString($this->getTypeNameForTypeID($fieldType));
+ }
+
+ public function writeFieldEnd()
+ {
+ $this->writeJsonObjectEnd();
+ }
+
+ public function writeFieldStop()
+ {
+ }
+
+ public function writeMapBegin($keyType, $valType, $size)
+ {
+ $this->writeJSONArrayStart();
+ $this->writeJSONString($this->getTypeNameForTypeID($keyType));
+ $this->writeJSONString($this->getTypeNameForTypeID($valType));
+ $this->writeJSONInteger($size);
+ $this->writeJSONObjectStart();
+ }
+
+ public function writeMapEnd()
+ {
+ $this->writeJSONObjectEnd();
+ $this->writeJSONArrayEnd();
+ }
+
+ public function writeListBegin($elemType, $size)
+ {
+ $this->writeJSONArrayStart();
+ $this->writeJSONString($this->getTypeNameForTypeID($elemType));
+ $this->writeJSONInteger($size);
+ }
+
+ public function writeListEnd()
+ {
+ $this->writeJSONArrayEnd();
+ }
+
+ public function writeSetBegin($elemType, $size)
+ {
+ $this->writeJSONArrayStart();
+ $this->writeJSONString($this->getTypeNameForTypeID($elemType));
+ $this->writeJSONInteger($size);
+ }
+
+ public function writeSetEnd()
+ {
+ $this->writeJSONArrayEnd();
+ }
+
+ public function writeBool($bool)
+ {
+ $this->writeJSONInteger($bool ? 1 : 0);
+ }
+
+ public function writeByte($byte)
+ {
+ $this->writeJSONInteger($byte);
+ }
+
+ public function writeI16($i16)
+ {
+ $this->writeJSONInteger($i16);
+ }
+
+ public function writeI32($i32)
+ {
+ $this->writeJSONInteger($i32);
+ }
+
+ public function writeI64($i64)
+ {
+ $this->writeJSONInteger($i64);
+ }
+
+ public function writeDouble($dub)
+ {
+ $this->writeJSONDouble($dub);
+ }
+
+ public function writeString($str)
+ {
+ $this->writeJSONString($str);
+ }
+
+ /**
+ * Reads the message header
+ *
+ * @param string $name Function name
+ * @param int $type message type TMessageType::CALL or TMessageType::REPLY
+ * @parem int $seqid The sequence id of this message
+ */
+ public function readMessageBegin(&$name, &$type, &$seqid)
+ {
+ $this->readJSONArrayStart();
+
+ if ($this->readJSONInteger() != self::VERSION) {
+ throw new TProtocolException("Message contained bad version", TProtocolException::BAD_VERSION);
+ }
+
+ $name = $this->readJSONString(false);
+ $type = $this->readJSONInteger();
+ $seqid = $this->readJSONInteger();
+
+ return true;
+ }
+
+ /**
+ * Read the close of message
+ */
+ public function readMessageEnd()
+ {
+ $this->readJSONArrayEnd();
+ }
+
+ public function readStructBegin(&$name)
+ {
+ $this->readJSONObjectStart();
+
+ return 0;
+ }
+
+ public function readStructEnd()
+ {
+ $this->readJSONObjectEnd();
+ }
+
+ public function readFieldBegin(&$name, &$fieldType, &$fieldId)
+ {
+ $ch = $this->reader_->peek();
+ $name = "";
+
+ if (substr($ch, 0, 1) == self::RBRACE) {
+ $fieldType = TType::STOP;
+ } else {
+ $fieldId = $this->readJSONInteger();
+ $this->readJSONObjectStart();
+ $fieldType = $this->getTypeIDForTypeName($this->readJSONString(false));
+ }
+ }
+
+ public function readFieldEnd()
+ {
+ $this->readJSONObjectEnd();
+ }
+
+ public function readMapBegin(&$keyType, &$valType, &$size)
+ {
+ $this->readJSONArrayStart();
+ $keyType = $this->getTypeIDForTypeName($this->readJSONString(false));
+ $valType = $this->getTypeIDForTypeName($this->readJSONString(false));
+ $size = $this->readJSONInteger();
+ $this->readJSONObjectStart();
+ }
+
+ public function readMapEnd()
+ {
+ $this->readJSONObjectEnd();
+ $this->readJSONArrayEnd();
+ }
+
+ public function readListBegin(&$elemType, &$size)
+ {
+ $this->readJSONArrayStart();
+ $elemType = $this->getTypeIDForTypeName($this->readJSONString(false));
+ $size = $this->readJSONInteger();
+
+ return true;
+ }
+
+ public function readListEnd()
+ {
+ $this->readJSONArrayEnd();
+ }
+
+ public function readSetBegin(&$elemType, &$size)
+ {
+ $this->readJSONArrayStart();
+ $elemType = $this->getTypeIDForTypeName($this->readJSONString(false));
+ $size = $this->readJSONInteger();
+
+ return true;
+ }
+
+ public function readSetEnd()
+ {
+ $this->readJSONArrayEnd();
+ }
+
+ public function readBool(&$bool)
+ {
+ $bool = $this->readJSONInteger() == 0 ? false : true;
+
+ return true;
+ }
+
+ public function readByte(&$byte)
+ {
+ $byte = $this->readJSONInteger();
+
+ return true;
+ }
+
+ public function readI16(&$i16)
+ {
+ $i16 = $this->readJSONInteger();
+
+ return true;
+ }
+
+ public function readI32(&$i32)
+ {
+ $i32 = $this->readJSONInteger();
+
+ return true;
+ }
+
+ public function readI64(&$i64)
+ {
+ if (PHP_INT_SIZE === 4) {
+ $i64 = $this->readJSONIntegerAsString();
+ } else {
+ $i64 = $this->readJSONInteger();
+ }
+
+ return true;
+ }
+
+ public function readDouble(&$dub)
+ {
+ $dub = $this->readJSONDouble();
+
+ return true;
+ }
+
+ public function readString(&$str)
+ {
+ $str = $this->readJSONString(false);
+
+ return true;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Protocol/TMultiplexedProtocol.php b/src/jaegertracing/thrift/lib/php/lib/Protocol/TMultiplexedProtocol.php
new file mode 100644
index 000000000..d579c099d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Protocol/TMultiplexedProtocol.php
@@ -0,0 +1,85 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Protocol;
+
+use Thrift\Type\TMessageType;
+
+/**
+ * <code>TMultiplexedProtocol</code> is a protocol-independent concrete decorator
+ * that allows a Thrift client to communicate with a multiplexing Thrift server,
+ * by prepending the service name to the function name during function calls.
+ *
+ * @package Thrift\Protocol
+ */
+class TMultiplexedProtocol extends TProtocolDecorator
+{
+ /**
+ * Separator between service name and function name.
+ * Should be the same as used at multiplexed Thrift server.
+ *
+ * @var string
+ */
+ const SEPARATOR = ":";
+
+ /**
+ * The name of service.
+ *
+ * @var string
+ */
+ private $serviceName_;
+
+ /**
+ * Constructor of <code>TMultiplexedProtocol</code> class.
+ *
+ * Wrap the specified protocol, allowing it to be used to communicate with a
+ * multiplexing server. The <code>$serviceName</code> is required as it is
+ * prepended to the message header so that the multiplexing server can broker
+ * the function call to the proper service.
+ *
+ * @param TProtocol $protocol
+ * @param string $serviceName The name of service.
+ */
+ public function __construct(TProtocol $protocol, $serviceName)
+ {
+ parent::__construct($protocol);
+ $this->serviceName_ = $serviceName;
+ }
+
+ /**
+ * Writes the message header.
+ * Prepends the service name to the function name, separated by <code>TMultiplexedProtocol::SEPARATOR</code>.
+ *
+ * @param string $name Function name.
+ * @param int $type Message type.
+ * @param int $seqid The sequence id of this message.
+ */
+ public function writeMessageBegin($name, $type, $seqid)
+ {
+ if ($type == TMessageType::CALL || $type == TMessageType::ONEWAY) {
+ $nameWithService = $this->serviceName_ . self::SEPARATOR . $name;
+ parent::writeMessageBegin($nameWithService, $type, $seqid);
+ } else {
+ parent::writeMessageBegin($name, $type, $seqid);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Protocol/TProtocol.php b/src/jaegertracing/thrift/lib/php/lib/Protocol/TProtocol.php
new file mode 100644
index 000000000..f7b581f7b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Protocol/TProtocol.php
@@ -0,0 +1,352 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Protocol;
+
+use Thrift\Exception\TException;
+use Thrift\Transport\TTransport;
+use Thrift\Type\TType;
+use Thrift\Exception\TProtocolException;
+
+/**
+ * Protocol base class module.
+ */
+abstract class TProtocol
+{
+ /**
+ * Underlying transport
+ *
+ * @var TTransport
+ */
+ protected $trans_;
+
+ /**
+ * @param TTransport $trans
+ */
+ protected function __construct($trans)
+ {
+ $this->trans_ = $trans;
+ }
+
+ /**
+ * Accessor for transport
+ *
+ * @return TTransport
+ */
+ public function getTransport()
+ {
+ return $this->trans_;
+ }
+
+ /**
+ * Writes the message header
+ *
+ * @param string $name Function name
+ * @param int $type message type TMessageType::CALL or TMessageType::REPLY
+ * @param int $seqid The sequence id of this message
+ */
+ abstract public function writeMessageBegin($name, $type, $seqid);
+
+ /**
+ * Close the message
+ */
+ abstract public function writeMessageEnd();
+
+ /**
+ * Writes a struct header.
+ *
+ * @param string $name Struct name
+ * @throws TException on write error
+ * @return int How many bytes written
+ */
+ abstract public function writeStructBegin($name);
+
+ /**
+ * Close a struct.
+ *
+ * @throws TException on write error
+ * @return int How many bytes written
+ */
+ abstract public function writeStructEnd();
+
+ /*
+ * Starts a field.
+ *
+ * @param string $name Field name
+ * @param int $type Field type
+ * @param int $fid Field id
+ * @throws TException on write error
+ * @return int How many bytes written
+ */
+ abstract public function writeFieldBegin($fieldName, $fieldType, $fieldId);
+
+ abstract public function writeFieldEnd();
+
+ abstract public function writeFieldStop();
+
+ abstract public function writeMapBegin($keyType, $valType, $size);
+
+ abstract public function writeMapEnd();
+
+ abstract public function writeListBegin($elemType, $size);
+
+ abstract public function writeListEnd();
+
+ abstract public function writeSetBegin($elemType, $size);
+
+ abstract public function writeSetEnd();
+
+ abstract public function writeBool($bool);
+
+ abstract public function writeByte($byte);
+
+ abstract public function writeI16($i16);
+
+ abstract public function writeI32($i32);
+
+ abstract public function writeI64($i64);
+
+ abstract public function writeDouble($dub);
+
+ abstract public function writeString($str);
+
+ /**
+ * Reads the message header
+ *
+ * @param string $name Function name
+ * @param int $type message type TMessageType::CALL or TMessageType::REPLY
+ * @parem int $seqid The sequence id of this message
+ */
+ abstract public function readMessageBegin(&$name, &$type, &$seqid);
+
+ /**
+ * Read the close of message
+ */
+ abstract public function readMessageEnd();
+
+ abstract public function readStructBegin(&$name);
+
+ abstract public function readStructEnd();
+
+ abstract public function readFieldBegin(&$name, &$fieldType, &$fieldId);
+
+ abstract public function readFieldEnd();
+
+ abstract public function readMapBegin(&$keyType, &$valType, &$size);
+
+ abstract public function readMapEnd();
+
+ abstract public function readListBegin(&$elemType, &$size);
+
+ abstract public function readListEnd();
+
+ abstract public function readSetBegin(&$elemType, &$size);
+
+ abstract public function readSetEnd();
+
+ abstract public function readBool(&$bool);
+
+ abstract public function readByte(&$byte);
+
+ abstract public function readI16(&$i16);
+
+ abstract public function readI32(&$i32);
+
+ abstract public function readI64(&$i64);
+
+ abstract public function readDouble(&$dub);
+
+ abstract public function readString(&$str);
+
+ /**
+ * The skip function is a utility to parse over unrecognized date without
+ * causing corruption.
+ *
+ * @param TType $type What type is it
+ */
+ public function skip($type)
+ {
+ switch ($type) {
+ case TType::BOOL:
+ return $this->readBool($bool);
+ case TType::BYTE:
+ return $this->readByte($byte);
+ case TType::I16:
+ return $this->readI16($i16);
+ case TType::I32:
+ return $this->readI32($i32);
+ case TType::I64:
+ return $this->readI64($i64);
+ case TType::DOUBLE:
+ return $this->readDouble($dub);
+ case TType::STRING:
+ return $this->readString($str);
+ case TType::STRUCT:
+ $result = $this->readStructBegin($name);
+ while (true) {
+ $result += $this->readFieldBegin($name, $ftype, $fid);
+ if ($ftype == TType::STOP) {
+ break;
+ }
+ $result += $this->skip($ftype);
+ $result += $this->readFieldEnd();
+ }
+ $result += $this->readStructEnd();
+
+ return $result;
+
+ case TType::MAP:
+ $result = $this->readMapBegin($keyType, $valType, $size);
+ for ($i = 0; $i < $size; $i++) {
+ $result += $this->skip($keyType);
+ $result += $this->skip($valType);
+ }
+ $result += $this->readMapEnd();
+
+ return $result;
+
+ case TType::SET:
+ $result = $this->readSetBegin($elemType, $size);
+ for ($i = 0; $i < $size; $i++) {
+ $result += $this->skip($elemType);
+ }
+ $result += $this->readSetEnd();
+
+ return $result;
+
+ case TType::LST:
+ $result = $this->readListBegin($elemType, $size);
+ for ($i = 0; $i < $size; $i++) {
+ $result += $this->skip($elemType);
+ }
+ $result += $this->readListEnd();
+
+ return $result;
+
+ default:
+ throw new TProtocolException(
+ 'Unknown field type: ' . $type,
+ TProtocolException::INVALID_DATA
+ );
+ }
+ }
+
+ /**
+ * Utility for skipping binary data
+ *
+ * @param TTransport $itrans TTransport object
+ * @param int $type Field type
+ */
+ public static function skipBinary($itrans, $type)
+ {
+ switch ($type) {
+ case TType::BOOL:
+ return $itrans->readAll(1);
+ case TType::BYTE:
+ return $itrans->readAll(1);
+ case TType::I16:
+ return $itrans->readAll(2);
+ case TType::I32:
+ return $itrans->readAll(4);
+ case TType::I64:
+ return $itrans->readAll(8);
+ case TType::DOUBLE:
+ return $itrans->readAll(8);
+ case TType::STRING:
+ $len = unpack('N', $itrans->readAll(4));
+ $len = $len[1];
+ if ($len > 0x7fffffff) {
+ $len = 0 - (($len - 1) ^ 0xffffffff);
+ }
+
+ return 4 + $itrans->readAll($len);
+
+ case TType::STRUCT:
+ $result = 0;
+ while (true) {
+ $ftype = 0;
+ $fid = 0;
+ $data = $itrans->readAll(1);
+ $arr = unpack('c', $data);
+ $ftype = $arr[1];
+ if ($ftype == TType::STOP) {
+ break;
+ }
+ // I16 field id
+ $result += $itrans->readAll(2);
+ $result += self::skipBinary($itrans, $ftype);
+ }
+
+ return $result;
+
+ case TType::MAP:
+ // Ktype
+ $data = $itrans->readAll(1);
+ $arr = unpack('c', $data);
+ $ktype = $arr[1];
+ // Vtype
+ $data = $itrans->readAll(1);
+ $arr = unpack('c', $data);
+ $vtype = $arr[1];
+ // Size
+ $data = $itrans->readAll(4);
+ $arr = unpack('N', $data);
+ $size = $arr[1];
+ if ($size > 0x7fffffff) {
+ $size = 0 - (($size - 1) ^ 0xffffffff);
+ }
+ $result = 6;
+ for ($i = 0; $i < $size; $i++) {
+ $result += self::skipBinary($itrans, $ktype);
+ $result += self::skipBinary($itrans, $vtype);
+ }
+
+ return $result;
+
+ case TType::SET:
+ case TType::LST:
+ // Vtype
+ $data = $itrans->readAll(1);
+ $arr = unpack('c', $data);
+ $vtype = $arr[1];
+ // Size
+ $data = $itrans->readAll(4);
+ $arr = unpack('N', $data);
+ $size = $arr[1];
+ if ($size > 0x7fffffff) {
+ $size = 0 - (($size - 1) ^ 0xffffffff);
+ }
+ $result = 5;
+ for ($i = 0; $i < $size; $i++) {
+ $result += self::skipBinary($itrans, $vtype);
+ }
+
+ return $result;
+
+ default:
+ throw new TProtocolException(
+ 'Unknown field type: ' . $type,
+ TProtocolException::INVALID_DATA
+ );
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Protocol/TProtocolDecorator.php b/src/jaegertracing/thrift/lib/php/lib/Protocol/TProtocolDecorator.php
new file mode 100644
index 000000000..a85e0b8e5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Protocol/TProtocolDecorator.php
@@ -0,0 +1,285 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Protocol;
+
+use Thrift\Exception\TException;
+
+/**
+ * <code>TProtocolDecorator</code> forwards all requests to an enclosed
+ * <code>TProtocol</code> instance, providing a way to author concise
+ * concrete decorator subclasses. While it has no abstract methods, it
+ * is marked abstract as a reminder that by itself, it does not modify
+ * the behaviour of the enclosed <code>TProtocol</code>.
+ *
+ * @package Thrift\Protocol
+ */
+abstract class TProtocolDecorator extends TProtocol
+{
+ /**
+ * Instance of protocol, to which all operations will be forwarded.
+ *
+ * @var TProtocol
+ */
+ private $concreteProtocol_;
+
+ /**
+ * Constructor of <code>TProtocolDecorator</code> class.
+ * Encloses the specified protocol.
+ *
+ * @param TProtocol $protocol All operations will be forward to this instance. Must be non-null.
+ */
+ protected function __construct(TProtocol $protocol)
+ {
+ parent::__construct($protocol->getTransport());
+ $this->concreteProtocol_ = $protocol;
+ }
+
+ /**
+ * Writes the message header.
+ *
+ * @param string $name Function name
+ * @param int $type message type TMessageType::CALL or TMessageType::REPLY
+ * @param int $seqid The sequence id of this message
+ */
+ public function writeMessageBegin($name, $type, $seqid)
+ {
+ return $this->concreteProtocol_->writeMessageBegin($name, $type, $seqid);
+ }
+
+ /**
+ * Closes the message.
+ */
+ public function writeMessageEnd()
+ {
+ return $this->concreteProtocol_->writeMessageEnd();
+ }
+
+ /**
+ * Writes a struct header.
+ *
+ * @param string $name Struct name
+ *
+ * @throws TException on write error
+ * @return int How many bytes written
+ */
+ public function writeStructBegin($name)
+ {
+ return $this->concreteProtocol_->writeStructBegin($name);
+ }
+
+ /**
+ * Close a struct.
+ *
+ * @throws TException on write error
+ * @return int How many bytes written
+ */
+ public function writeStructEnd()
+ {
+ return $this->concreteProtocol_->writeStructEnd();
+ }
+
+ public function writeFieldBegin($fieldName, $fieldType, $fieldId)
+ {
+ return $this->concreteProtocol_->writeFieldBegin($fieldName, $fieldType, $fieldId);
+ }
+
+ public function writeFieldEnd()
+ {
+ return $this->concreteProtocol_->writeFieldEnd();
+ }
+
+ public function writeFieldStop()
+ {
+ return $this->concreteProtocol_->writeFieldStop();
+ }
+
+ public function writeMapBegin($keyType, $valType, $size)
+ {
+ return $this->concreteProtocol_->writeMapBegin($keyType, $valType, $size);
+ }
+
+ public function writeMapEnd()
+ {
+ return $this->concreteProtocol_->writeMapEnd();
+ }
+
+ public function writeListBegin($elemType, $size)
+ {
+ return $this->concreteProtocol_->writeListBegin($elemType, $size);
+ }
+
+ public function writeListEnd()
+ {
+ return $this->concreteProtocol_->writeListEnd();
+ }
+
+ public function writeSetBegin($elemType, $size)
+ {
+ return $this->concreteProtocol_->writeSetBegin($elemType, $size);
+ }
+
+ public function writeSetEnd()
+ {
+ return $this->concreteProtocol_->writeSetEnd();
+ }
+
+ public function writeBool($bool)
+ {
+ return $this->concreteProtocol_->writeBool($bool);
+ }
+
+ public function writeByte($byte)
+ {
+ return $this->concreteProtocol_->writeByte($byte);
+ }
+
+ public function writeI16($i16)
+ {
+ return $this->concreteProtocol_->writeI16($i16);
+ }
+
+ public function writeI32($i32)
+ {
+ return $this->concreteProtocol_->writeI32($i32);
+ }
+
+ public function writeI64($i64)
+ {
+ return $this->concreteProtocol_->writeI64($i64);
+ }
+
+ public function writeDouble($dub)
+ {
+ return $this->concreteProtocol_->writeDouble($dub);
+ }
+
+ public function writeString($str)
+ {
+ return $this->concreteProtocol_->writeString($str);
+ }
+
+ /**
+ * Reads the message header
+ *
+ * @param string $name Function name
+ * @param int $type message type TMessageType::CALL or TMessageType::REPLY
+ * @param int $seqid The sequence id of this message
+ */
+ public function readMessageBegin(&$name, &$type, &$seqid)
+ {
+ return $this->concreteProtocol_->readMessageBegin($name, $type, $seqid);
+ }
+
+ /**
+ * Read the close of message
+ */
+ public function readMessageEnd()
+ {
+ return $this->concreteProtocol_->readMessageEnd();
+ }
+
+ public function readStructBegin(&$name)
+ {
+ return $this->concreteProtocol_->readStructBegin($name);
+ }
+
+ public function readStructEnd()
+ {
+ return $this->concreteProtocol_->readStructEnd();
+ }
+
+ public function readFieldBegin(&$name, &$fieldType, &$fieldId)
+ {
+ return $this->concreteProtocol_->readFieldBegin($name, $fieldType, $fieldId);
+ }
+
+ public function readFieldEnd()
+ {
+ return $this->concreteProtocol_->readFieldEnd();
+ }
+
+ public function readMapBegin(&$keyType, &$valType, &$size)
+ {
+ $this->concreteProtocol_->readMapBegin($keyType, $valType, $size);
+ }
+
+ public function readMapEnd()
+ {
+ return $this->concreteProtocol_->readMapEnd();
+ }
+
+ public function readListBegin(&$elemType, &$size)
+ {
+ $this->concreteProtocol_->readListBegin($elemType, $size);
+ }
+
+ public function readListEnd()
+ {
+ return $this->concreteProtocol_->readListEnd();
+ }
+
+ public function readSetBegin(&$elemType, &$size)
+ {
+ return $this->concreteProtocol_->readSetBegin($elemType, $size);
+ }
+
+ public function readSetEnd()
+ {
+ return $this->concreteProtocol_->readSetEnd();
+ }
+
+ public function readBool(&$bool)
+ {
+ return $this->concreteProtocol_->readBool($bool);
+ }
+
+ public function readByte(&$byte)
+ {
+ return $this->concreteProtocol_->readByte($byte);
+ }
+
+ public function readI16(&$i16)
+ {
+ return $this->concreteProtocol_->readI16($i16);
+ }
+
+ public function readI32(&$i32)
+ {
+ return $this->concreteProtocol_->readI32($i32);
+ }
+
+ public function readI64(&$i64)
+ {
+ return $this->concreteProtocol_->readI64($i64);
+ }
+
+ public function readDouble(&$dub)
+ {
+ return $this->concreteProtocol_->readDouble($dub);
+ }
+
+ public function readString(&$str)
+ {
+ return $this->concreteProtocol_->readString($str);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Protocol/TSimpleJSONProtocol.php b/src/jaegertracing/thrift/lib/php/lib/Protocol/TSimpleJSONProtocol.php
new file mode 100644
index 000000000..1cf1f6407
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Protocol/TSimpleJSONProtocol.php
@@ -0,0 +1,374 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ */
+
+namespace Thrift\Protocol;
+
+use Thrift\Exception\TException;
+use Thrift\Exception\TProtocolException;
+use Thrift\Protocol\SimpleJSON\Context;
+use Thrift\Protocol\SimpleJSON\ListContext;
+use Thrift\Protocol\SimpleJSON\StructContext;
+use Thrift\Protocol\SimpleJSON\MapContext;
+use Thrift\Protocol\SimpleJSON\CollectionMapKeyException;
+
+/**
+ * SimpleJSON implementation of thrift protocol, ported from Java.
+ */
+class TSimpleJSONProtocol extends TProtocol
+{
+ const COMMA = ',';
+ const COLON = ':';
+ const LBRACE = '{';
+ const RBRACE = '}';
+ const LBRACKET = '[';
+ const RBRACKET = ']';
+ const QUOTE = '"';
+
+ const NAME_MAP = "map";
+ const NAME_LIST = "lst";
+ const NAME_SET = "set";
+
+ protected $writeContext_ = null;
+ protected $writeContextStack_ = [];
+
+ /**
+ * Push a new write context onto the stack.
+ */
+ protected function pushWriteContext(Context $c)
+ {
+ $this->writeContextStack_[] = $this->writeContext_;
+ $this->writeContext_ = $c;
+ }
+
+ /**
+ * Pop the last write context off the stack
+ */
+ protected function popWriteContext()
+ {
+ $this->writeContext_ = array_pop($this->writeContextStack_);
+ }
+
+ /**
+ * Used to make sure that we are not encountering a map whose keys are containers
+ */
+ protected function assertContextIsNotMapKey($invalidKeyType)
+ {
+ if ($this->writeContext_->isMapKey()) {
+ throw new CollectionMapKeyException(
+ "Cannot serialize a map with keys that are of type " .
+ $invalidKeyType
+ );
+ }
+ }
+
+ private function writeJSONString($b)
+ {
+ $this->writeContext_->write();
+
+ $this->trans_->write(json_encode((string)$b));
+ }
+
+ private function writeJSONInteger($num)
+ {
+ $isMapKey = $this->writeContext_->isMapKey();
+
+ $this->writeContext_->write();
+
+ if ($isMapKey) {
+ $this->trans_->write(self::QUOTE);
+ }
+
+ $this->trans_->write((int)$num);
+
+ if ($isMapKey) {
+ $this->trans_->write(self::QUOTE);
+ }
+ }
+
+ private function writeJSONDouble($num)
+ {
+ $isMapKey = $this->writeContext_->isMapKey();
+
+ $this->writeContext_->write();
+
+ if ($isMapKey) {
+ $this->trans_->write(self::QUOTE);
+ }
+
+ $this->trans_->write(json_encode((float)$num));
+
+ if ($isMapKey) {
+ $this->trans_->write(self::QUOTE);
+ }
+ }
+
+ /**
+ * Constructor
+ */
+ public function __construct($trans)
+ {
+ parent::__construct($trans);
+ $this->writeContext_ = new Context();
+ }
+
+ /**
+ * Writes the message header
+ *
+ * @param string $name Function name
+ * @param int $type message type TMessageType::CALL or TMessageType::REPLY
+ * @param int $seqid The sequence id of this message
+ */
+ public function writeMessageBegin($name, $type, $seqid)
+ {
+ $this->trans_->write(self::LBRACKET);
+ $this->pushWriteContext(new ListContext($this));
+ $this->writeJSONString($name);
+ $this->writeJSONInteger($type);
+ $this->writeJSONInteger($seqid);
+ }
+
+ /**
+ * Close the message
+ */
+ public function writeMessageEnd()
+ {
+ $this->popWriteContext();
+ $this->trans_->write(self::RBRACKET);
+ }
+
+ /**
+ * Writes a struct header.
+ *
+ * @param string $name Struct name
+ */
+ public function writeStructBegin($name)
+ {
+ $this->writeContext_->write();
+ $this->trans_->write(self::LBRACE);
+ $this->pushWriteContext(new StructContext($this));
+ }
+
+ /**
+ * Close a struct.
+ */
+ public function writeStructEnd()
+ {
+ $this->popWriteContext();
+ $this->trans_->write(self::RBRACE);
+ }
+
+ public function writeFieldBegin($fieldName, $fieldType, $fieldId)
+ {
+ $this->writeJSONString($fieldName);
+ }
+
+ public function writeFieldEnd()
+ {
+ }
+
+ public function writeFieldStop()
+ {
+ }
+
+ public function writeMapBegin($keyType, $valType, $size)
+ {
+ $this->assertContextIsNotMapKey(self::NAME_MAP);
+ $this->writeContext_->write();
+ $this->trans_->write(self::LBRACE);
+ $this->pushWriteContext(new MapContext($this));
+ }
+
+ public function writeMapEnd()
+ {
+ $this->popWriteContext();
+ $this->trans_->write(self::RBRACE);
+ }
+
+ public function writeListBegin($elemType, $size)
+ {
+ $this->assertContextIsNotMapKey(self::NAME_LIST);
+ $this->writeContext_->write();
+ $this->trans_->write(self::LBRACKET);
+ $this->pushWriteContext(new ListContext($this));
+ // No metadata!
+ }
+
+ public function writeListEnd()
+ {
+ $this->popWriteContext();
+ $this->trans_->write(self::RBRACKET);
+ }
+
+ public function writeSetBegin($elemType, $size)
+ {
+ $this->assertContextIsNotMapKey(self::NAME_SET);
+ $this->writeContext_->write();
+ $this->trans_->write(self::LBRACKET);
+ $this->pushWriteContext(new ListContext($this));
+ // No metadata!
+ }
+
+ public function writeSetEnd()
+ {
+ $this->popWriteContext();
+ $this->trans_->write(self::RBRACKET);
+ }
+
+ public function writeBool($bool)
+ {
+ $this->writeJSONInteger($bool ? 1 : 0);
+ }
+
+ public function writeByte($byte)
+ {
+ $this->writeJSONInteger($byte);
+ }
+
+ public function writeI16($i16)
+ {
+ $this->writeJSONInteger($i16);
+ }
+
+ public function writeI32($i32)
+ {
+ $this->writeJSONInteger($i32);
+ }
+
+ public function writeI64($i64)
+ {
+ $this->writeJSONInteger($i64);
+ }
+
+ public function writeDouble($dub)
+ {
+ $this->writeJSONDouble($dub);
+ }
+
+ public function writeString($str)
+ {
+ $this->writeJSONString($str);
+ }
+
+ /**
+ * Reading methods.
+ *
+ * simplejson is not meant to be read back into thrift
+ * - see http://wiki.apache.org/thrift/ThriftUsageJava
+ * - use JSON instead
+ */
+
+ public function readMessageBegin(&$name, &$type, &$seqid)
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readMessageEnd()
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readStructBegin(&$name)
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readStructEnd()
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readFieldBegin(&$name, &$fieldType, &$fieldId)
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readFieldEnd()
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readMapBegin(&$keyType, &$valType, &$size)
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readMapEnd()
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readListBegin(&$elemType, &$size)
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readListEnd()
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readSetBegin(&$elemType, &$size)
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readSetEnd()
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readBool(&$bool)
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readByte(&$byte)
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readI16(&$i16)
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readI32(&$i32)
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readI64(&$i64)
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readDouble(&$dub)
+ {
+ throw new TException("Not implemented");
+ }
+
+ public function readString(&$str)
+ {
+ throw new TException("Not implemented");
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Serializer/TBinarySerializer.php b/src/jaegertracing/thrift/lib/php/lib/Serializer/TBinarySerializer.php
new file mode 100644
index 000000000..9d2b14730
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Serializer/TBinarySerializer.php
@@ -0,0 +1,87 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.protocol
+ * @author: rmarin (marin.radu@facebook.com)
+ */
+
+namespace Thrift\Serializer;
+
+use Thrift\Transport\TMemoryBuffer;
+use Thrift\Protocol\TBinaryProtocolAccelerated;
+use Thrift\Type\TMessageType;
+
+/**
+ * Utility class for serializing and deserializing
+ * a thrift object using TBinaryProtocolAccelerated.
+ */
+class TBinarySerializer
+{
+ // NOTE(rmarin): Because thrift_protocol_write_binary
+ // adds a begin message prefix, you cannot specify
+ // a transport in which to serialize an object. It has to
+ // be a string. Otherwise we will break the compatibility with
+ // normal deserialization.
+ public static function serialize($object)
+ {
+ $transport = new TMemoryBuffer();
+ $protocol = new TBinaryProtocolAccelerated($transport);
+ if (function_exists('thrift_protocol_write_binary')) {
+ thrift_protocol_write_binary(
+ $protocol,
+ $object->getName(),
+ TMessageType::REPLY,
+ $object,
+ 0,
+ $protocol->isStrictWrite()
+ );
+
+ $protocol->readMessageBegin($unused_name, $unused_type, $unused_seqid);
+ } else {
+ $object->write($protocol);
+ }
+ $protocol->getTransport()->flush();
+
+ return $transport->getBuffer();
+ }
+
+ public static function deserialize($string_object, $class_name, $buffer_size = 8192)
+ {
+ $transport = new TMemoryBuffer();
+ $protocol = new TBinaryProtocolAccelerated($transport);
+ if (function_exists('thrift_protocol_read_binary')) {
+ // NOTE (t.heintz) TBinaryProtocolAccelerated internally wraps our TMemoryBuffer in a
+ // TBufferedTransport, so we have to retrieve it again or risk losing data when writing
+ // less than 512 bytes to the transport (see the comment there as well).
+ // @see THRIFT-1579
+ $protocol->writeMessageBegin('', TMessageType::REPLY, 0);
+ $protocolTransport = $protocol->getTransport();
+ $protocolTransport->write($string_object);
+ $protocolTransport->flush();
+
+ return thrift_protocol_read_binary($protocol, $class_name, $protocol->isStrictRead(), $buffer_size);
+ } else {
+ $transport->write($string_object);
+ $object = new $class_name();
+ $object->read($protocol);
+
+ return $object;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Server/TForkingServer.php b/src/jaegertracing/thrift/lib/php/lib/Server/TForkingServer.php
new file mode 100644
index 000000000..0bb6e9192
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Server/TForkingServer.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace Thrift\Server;
+
+use Thrift\Transport\TTransport;
+use Thrift\Exception\TException;
+use Thrift\Exception\TTransportException;
+
+/**
+ * A forking implementation of a Thrift server.
+ *
+ * @package thrift.server
+ */
+class TForkingServer extends TServer
+{
+ /**
+ * Flag for the main serving loop
+ *
+ * @var bool
+ */
+ private $stop_ = false;
+
+ /**
+ * List of children.
+ *
+ * @var array
+ */
+ protected $children_ = array();
+
+ /**
+ * Listens for new client using the supplied
+ * transport. We fork when a new connection
+ * arrives.
+ *
+ * @return void
+ */
+ public function serve()
+ {
+ $this->transport_->listen();
+
+ while (!$this->stop_) {
+ try {
+ $transport = $this->transport_->accept();
+
+ if ($transport != null) {
+ $pid = pcntl_fork();
+
+ if ($pid > 0) {
+ $this->handleParent($transport, $pid);
+ } elseif ($pid === 0) {
+ $this->handleChild($transport);
+ } else {
+ throw new TException('Failed to fork');
+ }
+ }
+ } catch (TTransportException $e) {
+ }
+
+ $this->collectChildren();
+ }
+ }
+
+ /**
+ * Code run by the parent
+ *
+ * @param TTransport $transport
+ * @param int $pid
+ * @return void
+ */
+ private function handleParent(TTransport $transport, $pid)
+ {
+ $this->children_[$pid] = $transport;
+ }
+
+ /**
+ * Code run by the child.
+ *
+ * @param TTransport $transport
+ * @return void
+ */
+ private function handleChild(TTransport $transport)
+ {
+ try {
+ $inputTransport = $this->inputTransportFactory_->getTransport($transport);
+ $outputTransport = $this->outputTransportFactory_->getTransport($transport);
+ $inputProtocol = $this->inputProtocolFactory_->getProtocol($inputTransport);
+ $outputProtocol = $this->outputProtocolFactory_->getProtocol($outputTransport);
+ while ($this->processor_->process($inputProtocol, $outputProtocol)) {
+ }
+ @$transport->close();
+ } catch (TTransportException $e) {
+ }
+
+ exit(0);
+ }
+
+ /**
+ * Collects any children we may have
+ *
+ * @return void
+ */
+ private function collectChildren()
+ {
+ foreach ($this->children_ as $pid => $transport) {
+ if (pcntl_waitpid($pid, $status, WNOHANG) > 0) {
+ unset($this->children_[$pid]);
+ if ($transport) {
+ @$transport->close();
+ }
+ }
+ }
+ }
+
+ /**
+ * Stops the server running. Kills the transport
+ * and then stops the main serving loop
+ *
+ * @return void
+ */
+ public function stop()
+ {
+ $this->transport_->close();
+ $this->stop_ = true;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Server/TSSLServerSocket.php b/src/jaegertracing/thrift/lib/php/lib/Server/TSSLServerSocket.php
new file mode 100644
index 000000000..ac589b76b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Server/TSSLServerSocket.php
@@ -0,0 +1,97 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+namespace Thrift\Server;
+
+use Thrift\Transport\TSSLSocket;
+
+/**
+ * Socket implementation of a server agent.
+ *
+ * @package thrift.transport
+ */
+class TSSLServerSocket extends TServerSocket
+{
+ /**
+ * Remote port
+ *
+ * @var resource
+ */
+ protected $context_ = null;
+
+ /**
+ * ServerSocket constructor
+ *
+ * @param string $host Host to listen on
+ * @param int $port Port to listen on
+ * @param resource $context Stream context
+ * @return void
+ */
+ public function __construct($host = 'localhost', $port = 9090, $context = null)
+ {
+ $ssl_host = $this->getSSLHost($host);
+ parent::__construct($ssl_host, $port);
+ $this->context_ = $context;
+ }
+
+ public function getSSLHost($host)
+ {
+ $transport_protocol_loc = strpos($host, "://");
+ if ($transport_protocol_loc === false) {
+ $host = 'ssl://' . $host;
+ }
+ return $host;
+ }
+
+ /**
+ * Opens a new socket server handle
+ *
+ * @return void
+ */
+ public function listen()
+ {
+ $this->listener_ = @stream_socket_server(
+ $this->host_ . ':' . $this->port_,
+ $errno,
+ $errstr,
+ STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,
+ $this->context_
+ );
+ }
+
+ /**
+ * Implementation of accept. If not client is accepted in the given time
+ *
+ * @return TSocket
+ */
+ protected function acceptImpl()
+ {
+ $handle = @stream_socket_accept($this->listener_, $this->acceptTimeout_ / 1000.0);
+ if (!$handle) {
+ return null;
+ }
+
+ $socket = new TSSLSocket();
+ $socket->setHandle($handle);
+
+ return $socket;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Server/TServer.php b/src/jaegertracing/thrift/lib/php/lib/Server/TServer.php
new file mode 100644
index 000000000..268c37820
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Server/TServer.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Thrift\Server;
+
+use Thrift\Factory\TTransportFactory;
+use Thrift\Factory\TProtocolFactory;
+
+/**
+ * Generic class for a Thrift server.
+ *
+ * @package thrift.server
+ */
+abstract class TServer
+{
+ /**
+ * Processor to handle new clients
+ *
+ * @var TProcessor
+ */
+ protected $processor_;
+
+ /**
+ * Server transport to be used for listening
+ * and accepting new clients
+ *
+ * @var TServerTransport
+ */
+ protected $transport_;
+
+ /**
+ * Input transport factory
+ *
+ * @var TTransportFactory
+ */
+ protected $inputTransportFactory_;
+
+ /**
+ * Output transport factory
+ *
+ * @var TTransportFactory
+ */
+ protected $outputTransportFactory_;
+
+ /**
+ * Input protocol factory
+ *
+ * @var TProtocolFactory
+ */
+ protected $inputProtocolFactory_;
+
+ /**
+ * Output protocol factory
+ *
+ * @var TProtocolFactory
+ */
+ protected $outputProtocolFactory_;
+
+ /**
+ * Sets up all the factories, etc
+ *
+ * @param object $processor
+ * @param TServerTransport $transport
+ * @param TTransportFactory $inputTransportFactory
+ * @param TTransportFactory $outputTransportFactory
+ * @param TProtocolFactory $inputProtocolFactory
+ * @param TProtocolFactory $outputProtocolFactory
+ * @return void
+ */
+ public function __construct(
+ $processor,
+ TServerTransport $transport,
+ TTransportFactory $inputTransportFactory,
+ TTransportFactory $outputTransportFactory,
+ TProtocolFactory $inputProtocolFactory,
+ TProtocolFactory $outputProtocolFactory
+ ) {
+ $this->processor_ = $processor;
+ $this->transport_ = $transport;
+ $this->inputTransportFactory_ = $inputTransportFactory;
+ $this->outputTransportFactory_ = $outputTransportFactory;
+ $this->inputProtocolFactory_ = $inputProtocolFactory;
+ $this->outputProtocolFactory_ = $outputProtocolFactory;
+ }
+
+ /**
+ * Serves the server. This should never return
+ * unless a problem permits it to do so or it
+ * is interrupted intentionally
+ *
+ * @abstract
+ * @return void
+ */
+ abstract public function serve();
+
+ /**
+ * Stops the server serving
+ *
+ * @abstract
+ * @return void
+ */
+ abstract public function stop();
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Server/TServerSocket.php b/src/jaegertracing/thrift/lib/php/lib/Server/TServerSocket.php
new file mode 100644
index 000000000..8f38fb23f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Server/TServerSocket.php
@@ -0,0 +1,124 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Server;
+
+use Thrift\Transport\TSocket;
+
+/**
+ * Socket implementation of a server agent.
+ *
+ * @package thrift.transport
+ */
+class TServerSocket extends TServerTransport
+{
+ /**
+ * Handle for the listener socket
+ *
+ * @var resource
+ */
+ protected $listener_;
+
+ /**
+ * Port for the listener to listen on
+ *
+ * @var int
+ */
+ protected $port_;
+
+ /**
+ * Timeout when listening for a new client
+ *
+ * @var int
+ */
+ protected $acceptTimeout_ = 30000;
+
+ /**
+ * Host to listen on
+ *
+ * @var string
+ */
+ protected $host_;
+
+ /**
+ * ServerSocket constructor
+ *
+ * @param string $host Host to listen on
+ * @param int $port Port to listen on
+ * @return void
+ */
+ public function __construct($host = 'localhost', $port = 9090)
+ {
+ $this->host_ = $host;
+ $this->port_ = $port;
+ }
+
+ /**
+ * Sets the accept timeout
+ *
+ * @param int $acceptTimeout
+ * @return void
+ */
+ public function setAcceptTimeout($acceptTimeout)
+ {
+ $this->acceptTimeout_ = $acceptTimeout;
+ }
+
+ /**
+ * Opens a new socket server handle
+ *
+ * @return void
+ */
+ public function listen()
+ {
+ $this->listener_ = stream_socket_server('tcp://' . $this->host_ . ':' . $this->port_);
+ }
+
+ /**
+ * Closes the socket server handle
+ *
+ * @return void
+ */
+ public function close()
+ {
+ @fclose($this->listener_);
+ $this->listener_ = null;
+ }
+
+ /**
+ * Implementation of accept. If not client is accepted in the given time
+ *
+ * @return TSocket
+ */
+ protected function acceptImpl()
+ {
+ $handle = @stream_socket_accept($this->listener_, $this->acceptTimeout_ / 1000.0);
+ if (!$handle) {
+ return null;
+ }
+
+ $socket = new TSocket();
+ $socket->setHandle($handle);
+
+ return $socket;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Server/TServerTransport.php b/src/jaegertracing/thrift/lib/php/lib/Server/TServerTransport.php
new file mode 100644
index 000000000..15a27afa8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Server/TServerTransport.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Thrift\Server;
+
+use Thrift\Exception\TTransportException;
+
+/**
+ * Generic class for Server agent.
+ *
+ * @package thrift.transport
+ */
+abstract class TServerTransport
+{
+ /**
+ * List for new clients
+ *
+ * @abstract
+ * @return void
+ */
+ abstract public function listen();
+
+ /**
+ * Close the server
+ *
+ * @abstract
+ * @return void
+ */
+ abstract public function close();
+
+ /**
+ * Subclasses should use this to implement
+ * accept.
+ *
+ * @abstract
+ * @return TTransport
+ */
+ abstract protected function acceptImpl();
+
+ /**
+ * Uses the accept implemtation. If null is returned, an
+ * exception is thrown.
+ *
+ * @throws TTransportException
+ * @return TTransport
+ */
+ public function accept()
+ {
+ $transport = $this->acceptImpl();
+
+ if ($transport == null) {
+ throw new TTransportException("accept() may not return NULL");
+ }
+
+ return $transport;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Server/TSimpleServer.php b/src/jaegertracing/thrift/lib/php/lib/Server/TSimpleServer.php
new file mode 100644
index 000000000..4c1dda5a5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Server/TSimpleServer.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Thrift\Server;
+
+use Thrift\Exception\TTransportException;
+
+/**
+ * Simple implemtation of a Thrift server.
+ *
+ * @package thrift.server
+ */
+class TSimpleServer extends TServer
+{
+ /**
+ * Flag for the main serving loop
+ *
+ * @var bool
+ */
+ private $stop_ = false;
+
+ /**
+ * Listens for new client using the supplied
+ * transport. It handles TTransportExceptions
+ * to avoid timeouts etc killing it
+ *
+ * @return void
+ */
+ public function serve()
+ {
+ $this->transport_->listen();
+
+ while (!$this->stop_) {
+ try {
+ $transport = $this->transport_->accept();
+
+ if ($transport != null) {
+ $inputTransport = $this->inputTransportFactory_->getTransport($transport);
+ $outputTransport = $this->outputTransportFactory_->getTransport($transport);
+ $inputProtocol = $this->inputProtocolFactory_->getProtocol($inputTransport);
+ $outputProtocol = $this->outputProtocolFactory_->getProtocol($outputTransport);
+ while ($this->processor_->process($inputProtocol, $outputProtocol)) {
+ }
+ }
+ } catch (TTransportException $e) {
+ }
+ }
+ }
+
+ /**
+ * Stops the server running. Kills the transport
+ * and then stops the main serving loop
+ *
+ * @return void
+ */
+ public function stop()
+ {
+ $this->transport_->close();
+ $this->stop_ = true;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/StoredMessageProtocol.php b/src/jaegertracing/thrift/lib/php/lib/StoredMessageProtocol.php
new file mode 100644
index 000000000..c4aaaa9ec
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/StoredMessageProtocol.php
@@ -0,0 +1,53 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.processor
+ */
+
+namespace Thrift;
+
+use Thrift\Protocol\TProtocol;
+use Thrift\Protocol\TProtocolDecorator;
+
+/**
+ * Our goal was to work with any protocol. In order to do that, we needed
+ * to allow them to call readMessageBegin() and get the Message in exactly
+ * the standard format, without the service name prepended to the Message name.
+ */
+class StoredMessageProtocol extends TProtocolDecorator
+{
+ private $fname_;
+ private $mtype_;
+ private $rseqid_;
+
+ public function __construct(TProtocol $protocol, $fname, $mtype, $rseqid)
+ {
+ parent::__construct($protocol);
+ $this->fname_ = $fname;
+ $this->mtype_ = $mtype;
+ $this->rseqid_ = $rseqid;
+ }
+
+ public function readMessageBegin(&$name, &$type, &$seqid)
+ {
+ $name = $this->fname_;
+ $type = $this->mtype_;
+ $seqid = $this->rseqid_;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/StringFunc/Core.php b/src/jaegertracing/thrift/lib/php/lib/StringFunc/Core.php
new file mode 100644
index 000000000..39a75b3a2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/StringFunc/Core.php
@@ -0,0 +1,40 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+namespace Thrift\StringFunc;
+
+class Core implements TStringFunc
+{
+ public function substr($str, $start, $length = null)
+ {
+ // specifying a null $length would return an empty string
+ if ($length === null) {
+ return substr($str, $start);
+ }
+
+ return substr($str, $start, $length);
+ }
+
+ public function strlen($str)
+ {
+ return strlen($str);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/StringFunc/Mbstring.php b/src/jaegertracing/thrift/lib/php/lib/StringFunc/Mbstring.php
new file mode 100644
index 000000000..968ff18f1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/StringFunc/Mbstring.php
@@ -0,0 +1,46 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+namespace Thrift\StringFunc;
+
+class Mbstring implements TStringFunc
+{
+ public function substr($str, $start, $length = null)
+ {
+ /**
+ * We need to set the charset parameter, which is the second
+ * optional parameter and the first optional parameter can't
+ * be null or false as a "magic" value because that would
+ * cause an empty string to be returned, so we need to
+ * actually calculate the proper length value.
+ */
+ if ($length === null) {
+ $length = $this->strlen($str) - $start;
+ }
+
+ return mb_substr($str, $start, $length, '8bit');
+ }
+
+ public function strlen($str)
+ {
+ return mb_strlen($str, '8bit');
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/StringFunc/TStringFunc.php b/src/jaegertracing/thrift/lib/php/lib/StringFunc/TStringFunc.php
new file mode 100644
index 000000000..dea497f2e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/StringFunc/TStringFunc.php
@@ -0,0 +1,28 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+namespace Thrift\StringFunc;
+
+interface TStringFunc
+{
+ public function substr($str, $start, $length = null);
+ public function strlen($str);
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/TMultiplexedProcessor.php b/src/jaegertracing/thrift/lib/php/lib/TMultiplexedProcessor.php
new file mode 100644
index 000000000..a64a9687c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/TMultiplexedProcessor.php
@@ -0,0 +1,118 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.processor
+ */
+
+namespace Thrift;
+
+use Thrift\Exception\TException;
+use Thrift\Protocol\TProtocol;
+use Thrift\Protocol\TMultiplexedProtocol;
+use Thrift\Type\TMessageType;
+
+/**
+ * <code>TMultiplexedProcessor</code> is a Processor allowing
+ * a single <code>TServer</code> to provide multiple services.
+ *
+ * <p>To do so, you instantiate the processor and then register additional
+ * processors with it, as shown in the following example:</p>
+ *
+ * <blockquote><code>
+ * $processor = new TMultiplexedProcessor();
+ *
+ * processor->registerProcessor(
+ * "Calculator",
+ * new \tutorial\CalculatorProcessor(new CalculatorHandler()));
+ *
+ * processor->registerProcessor(
+ * "WeatherReport",
+ * new \tutorial\WeatherReportProcessor(new WeatherReportHandler()));
+ *
+ * $processor->process($protocol, $protocol);
+ * </code></blockquote>
+ */
+
+class TMultiplexedProcessor
+{
+ private $serviceProcessorMap_;
+
+ /**
+ * 'Register' a service with this <code>TMultiplexedProcessor</code>. This
+ * allows us to broker requests to individual services by using the service
+ * name to select them at request time.
+ *
+ * @param serviceName Name of a service, has to be identical to the name
+ * declared in the Thrift IDL, e.g. "WeatherReport".
+ * @param processor Implementation of a service, usually referred to
+ * as "handlers", e.g. WeatherReportHandler implementing WeatherReport.Iface.
+ */
+ public function registerProcessor($serviceName, $processor)
+ {
+ $this->serviceProcessorMap_[$serviceName] = $processor;
+ }
+
+ /**
+ * This implementation of <code>process</code> performs the following steps:
+ *
+ * <ol>
+ * <li>Read the beginning of the message.</li>
+ * <li>Extract the service name from the message.</li>
+ * <li>Using the service name to locate the appropriate processor.</li>
+ * <li>Dispatch to the processor, with a decorated instance of TProtocol
+ * that allows readMessageBegin() to return the original Message.</li>
+ * </ol>
+ *
+ * @throws TException If the message type is not CALL or ONEWAY, if
+ * the service name was not found in the message, or if the service
+ * name was not found in the service map.
+ */
+ public function process(TProtocol $input, TProtocol $output)
+ {
+ /*
+ Use the actual underlying protocol (e.g. TBinaryProtocol) to read the
+ message header. This pulls the message "off the wire", which we'll
+ deal with at the end of this method.
+ */
+ $input->readMessageBegin($fname, $mtype, $rseqid);
+
+ if ($mtype !== TMessageType::CALL && $mtype != TMessageType::ONEWAY) {
+ throw new TException("This should not have happened!?");
+ }
+
+ // Extract the service name and the new Message name.
+ if (strpos($fname, TMultiplexedProtocol::SEPARATOR) === false) {
+ throw new TException("Service name not found in message name: {$fname}. Did you " .
+ "forget to use a TMultiplexProtocol in your client?");
+ }
+ list($serviceName, $messageName) = explode(':', $fname, 2);
+ if (!array_key_exists($serviceName, $this->serviceProcessorMap_)) {
+ throw new TException("Service name not found: {$serviceName}. Did you forget " .
+ "to call registerProcessor()?");
+ }
+
+ // Dispatch processing to the stored processor
+ $processor = $this->serviceProcessorMap_[$serviceName];
+
+ return $processor->process(
+ new StoredMessageProtocol($input, $messageName, $mtype, $rseqid),
+ $output
+ );
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Transport/TBufferedTransport.php b/src/jaegertracing/thrift/lib/php/lib/Transport/TBufferedTransport.php
new file mode 100644
index 000000000..253c5acfb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Transport/TBufferedTransport.php
@@ -0,0 +1,206 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TTransportException;
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * Buffered transport. Stores data to an internal buffer that it doesn't
+ * actually write out until flush is called. For reading, we do a greedy
+ * read and then serve data out of the internal buffer.
+ *
+ * @package thrift.transport
+ */
+class TBufferedTransport extends TTransport
+{
+ /**
+ * The underlying transport
+ *
+ * @var TTransport
+ */
+ protected $transport_;
+
+ /**
+ * The receive buffer size
+ *
+ * @var int
+ */
+ protected $rBufSize_ = 512;
+
+ /**
+ * The write buffer size
+ *
+ * @var int
+ */
+ protected $wBufSize_ = 512;
+
+ /**
+ * The write buffer.
+ *
+ * @var string
+ */
+ protected $wBuf_ = '';
+
+ /**
+ * The read buffer.
+ *
+ * @var string
+ */
+ protected $rBuf_ = '';
+
+ /**
+ * Constructor. Creates a buffered transport around an underlying transport
+ */
+ public function __construct($transport, $rBufSize = 512, $wBufSize = 512)
+ {
+ $this->transport_ = $transport;
+ $this->rBufSize_ = $rBufSize;
+ $this->wBufSize_ = $wBufSize;
+ }
+
+ public function isOpen()
+ {
+ return $this->transport_->isOpen();
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * @throws TTransportException
+ */
+ public function open()
+ {
+ $this->transport_->open();
+ }
+
+ public function close()
+ {
+ $this->transport_->close();
+ }
+
+ public function putBack($data)
+ {
+ if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) {
+ $this->rBuf_ = $data;
+ } else {
+ $this->rBuf_ = ($data . $this->rBuf_);
+ }
+ }
+
+ /**
+ * The reason that we customize readAll here is that the majority of PHP
+ * streams are already internally buffered by PHP. The socket stream, for
+ * example, buffers internally and blocks if you call read with $len greater
+ * than the amount of data available, unlike recv() in C.
+ *
+ * Therefore, use the readAll method of the wrapped transport inside
+ * the buffered readAll.
+ *
+ * @throws TTransportException
+ */
+ public function readAll($len)
+ {
+ $have = TStringFuncFactory::create()->strlen($this->rBuf_);
+ if ($have == 0) {
+ $data = $this->transport_->readAll($len);
+ } elseif ($have < $len) {
+ $data = $this->rBuf_;
+ $this->rBuf_ = '';
+ $data .= $this->transport_->readAll($len - $have);
+ } elseif ($have == $len) {
+ $data = $this->rBuf_;
+ $this->rBuf_ = '';
+ } elseif ($have > $len) {
+ $data = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len);
+ $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len);
+ }
+
+ return $data;
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * @param int $len
+ * @return string
+ * @throws TTransportException
+ */
+ public function read($len)
+ {
+ if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) {
+ $this->rBuf_ = $this->transport_->read($this->rBufSize_);
+ }
+
+ if (TStringFuncFactory::create()->strlen($this->rBuf_) <= $len) {
+ $ret = $this->rBuf_;
+ $this->rBuf_ = '';
+
+ return $ret;
+ }
+
+ $ret = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len);
+ $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len);
+
+ return $ret;
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * @param string $buf
+ * @throws TTransportException
+ */
+ public function write($buf)
+ {
+ $this->wBuf_ .= $buf;
+ if (TStringFuncFactory::create()->strlen($this->wBuf_) >= $this->wBufSize_) {
+ $out = $this->wBuf_;
+
+ // Note that we clear the internal wBuf_ prior to the underlying write
+ // to ensure we're in a sane state (i.e. internal buffer cleaned)
+ // if the underlying write throws up an exception
+ $this->wBuf_ = '';
+ $this->transport_->write($out);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * @throws TTransportException
+ */
+ public function flush()
+ {
+ if (TStringFuncFactory::create()->strlen($this->wBuf_) > 0) {
+ $out = $this->wBuf_;
+
+ // Note that we clear the internal wBuf_ prior to the underlying write
+ // to ensure we're in a sane state (i.e. internal buffer cleaned)
+ // if the underlying write throws up an exception
+ $this->wBuf_ = '';
+ $this->transport_->write($out);
+ }
+ $this->transport_->flush();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Transport/TCurlClient.php b/src/jaegertracing/thrift/lib/php/lib/Transport/TCurlClient.php
new file mode 100644
index 000000000..2060d34e2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Transport/TCurlClient.php
@@ -0,0 +1,281 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TTransportException;
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * HTTP client for Thrift
+ *
+ * @package thrift.transport
+ */
+class TCurlClient extends TTransport
+{
+ private static $curlHandle;
+
+ /**
+ * The host to connect to
+ *
+ * @var string
+ */
+ protected $host_;
+
+ /**
+ * The port to connect on
+ *
+ * @var int
+ */
+ protected $port_;
+
+ /**
+ * The URI to request
+ *
+ * @var string
+ */
+ protected $uri_;
+
+ /**
+ * The scheme to use for the request, i.e. http, https
+ *
+ * @var string
+ */
+ protected $scheme_;
+
+ /**
+ * Buffer for the HTTP request data
+ *
+ * @var string
+ */
+ protected $request_;
+
+ /**
+ * Buffer for the HTTP response data.
+ *
+ * @var binary string
+ */
+ protected $response_;
+
+ /**
+ * Read timeout
+ *
+ * @var float
+ */
+ protected $timeout_;
+
+ /**
+ * http headers
+ *
+ * @var array
+ */
+ protected $headers_;
+
+ /**
+ * Make a new HTTP client.
+ *
+ * @param string $host
+ * @param int $port
+ * @param string $uri
+ */
+ public function __construct($host, $port = 80, $uri = '', $scheme = 'http')
+ {
+ if ((TStringFuncFactory::create()->strlen($uri) > 0) && ($uri{0} != '/')) {
+ $uri = '/' . $uri;
+ }
+ $this->scheme_ = $scheme;
+ $this->host_ = $host;
+ $this->port_ = $port;
+ $this->uri_ = $uri;
+ $this->request_ = '';
+ $this->response_ = null;
+ $this->timeout_ = null;
+ $this->headers_ = array();
+ }
+
+ /**
+ * Set read timeout
+ *
+ * @param float $timeout
+ */
+ public function setTimeoutSecs($timeout)
+ {
+ $this->timeout_ = $timeout;
+ }
+
+ /**
+ * Whether this transport is open.
+ *
+ * @return boolean true if open
+ */
+ public function isOpen()
+ {
+ return true;
+ }
+
+ /**
+ * Open the transport for reading/writing
+ *
+ * @throws TTransportException if cannot open
+ */
+ public function open()
+ {
+ }
+
+ /**
+ * Close the transport.
+ */
+ public function close()
+ {
+ $this->request_ = '';
+ $this->response_ = null;
+ }
+
+ /**
+ * Read some data into the array.
+ *
+ * @param int $len How much to read
+ * @return string The data that has been read
+ * @throws TTransportException if cannot read any more data
+ */
+ public function read($len)
+ {
+ if ($len >= strlen($this->response_)) {
+ return $this->response_;
+ } else {
+ $ret = substr($this->response_, 0, $len);
+ $this->response_ = substr($this->response_, $len);
+
+ return $ret;
+ }
+ }
+
+ /**
+ * Guarantees that the full amount of data is read. Since TCurlClient gets entire payload at
+ * once, parent readAll cannot be used.
+ *
+ * @return string The data, of exact length
+ * @throws TTransportException if cannot read data
+ */
+ public function readAll($len)
+ {
+ $data = $this->read($len);
+
+ if (TStringFuncFactory::create()->strlen($data) !== $len) {
+ throw new TTransportException('TCurlClient could not read '.$len.' bytes');
+ }
+
+ return $data;
+ }
+
+ /**
+ * Writes some data into the pending buffer
+ *
+ * @param string $buf The data to write
+ * @throws TTransportException if writing fails
+ */
+ public function write($buf)
+ {
+ $this->request_ .= $buf;
+ }
+
+ /**
+ * Opens and sends the actual request over the HTTP connection
+ *
+ * @throws TTransportException if a writing error occurs
+ */
+ public function flush()
+ {
+ if (!self::$curlHandle) {
+ register_shutdown_function(array('Thrift\\Transport\\TCurlClient', 'closeCurlHandle'));
+ self::$curlHandle = curl_init();
+ curl_setopt(self::$curlHandle, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt(self::$curlHandle, CURLOPT_BINARYTRANSFER, true);
+ curl_setopt(self::$curlHandle, CURLOPT_USERAGENT, 'PHP/TCurlClient');
+ curl_setopt(self::$curlHandle, CURLOPT_CUSTOMREQUEST, 'POST');
+ curl_setopt(self::$curlHandle, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt(self::$curlHandle, CURLOPT_MAXREDIRS, 1);
+ }
+ // God, PHP really has some esoteric ways of doing simple things.
+ $host = $this->host_ . ($this->port_ != 80 ? ':' . $this->port_ : '');
+ $fullUrl = $this->scheme_ . "://" . $host . $this->uri_;
+
+ $headers = array();
+ $defaultHeaders = array('Accept' => 'application/x-thrift',
+ 'Content-Type' => 'application/x-thrift',
+ 'Content-Length' => TStringFuncFactory::create()->strlen($this->request_));
+ foreach (array_merge($defaultHeaders, $this->headers_) as $key => $value) {
+ $headers[] = "$key: $value";
+ }
+
+ curl_setopt(self::$curlHandle, CURLOPT_HTTPHEADER, $headers);
+
+ if ($this->timeout_ > 0) {
+ if ($this->timeout_ < 1.0) {
+ // Timestamps smaller than 1 second are ignored when CURLOPT_TIMEOUT is used
+ curl_setopt(self::$curlHandle, CURLOPT_TIMEOUT_MS, 1000 * $this->timeout_);
+ } else {
+ curl_setopt(self::$curlHandle, CURLOPT_TIMEOUT, $this->timeout_);
+ }
+ }
+ curl_setopt(self::$curlHandle, CURLOPT_POSTFIELDS, $this->request_);
+ $this->request_ = '';
+
+ curl_setopt(self::$curlHandle, CURLOPT_URL, $fullUrl);
+ $this->response_ = curl_exec(self::$curlHandle);
+ $responseError = curl_error(self::$curlHandle);
+
+ $code = curl_getinfo(self::$curlHandle, CURLINFO_HTTP_CODE);
+
+ // Handle non 200 status code / connect failure
+ if ($this->response_ === false || $code !== 200) {
+ curl_close(self::$curlHandle);
+ self::$curlHandle = null;
+ $this->response_ = null;
+ $error = 'TCurlClient: Could not connect to ' . $fullUrl;
+ if ($responseError) {
+ $error .= ', ' . $responseError;
+ }
+ if ($code) {
+ $error .= ', HTTP status code: ' . $code;
+ }
+ throw new TTransportException($error, TTransportException::UNKNOWN);
+ }
+ }
+
+ public static function closeCurlHandle()
+ {
+ try {
+ if (self::$curlHandle) {
+ curl_close(self::$curlHandle);
+ self::$curlHandle = null;
+ }
+ } catch (\Exception $x) {
+ error_log('There was an error closing the curl handle: ' . $x->getMessage());
+ }
+ }
+
+ public function addHeaders($headers)
+ {
+ $this->headers_ = array_merge($this->headers_, $headers);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Transport/TFramedTransport.php b/src/jaegertracing/thrift/lib/php/lib/Transport/TFramedTransport.php
new file mode 100644
index 000000000..39d186987
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Transport/TFramedTransport.php
@@ -0,0 +1,192 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * Framed transport. Writes and reads data in chunks that are stamped with
+ * their length.
+ *
+ * @package thrift.transport
+ */
+class TFramedTransport extends TTransport
+{
+ /**
+ * Underlying transport object.
+ *
+ * @var TTransport
+ */
+ private $transport_;
+
+ /**
+ * Buffer for read data.
+ *
+ * @var string
+ */
+ private $rBuf_;
+
+ /**
+ * Buffer for queued output data
+ *
+ * @var string
+ */
+ private $wBuf_;
+
+ /**
+ * Whether to frame reads
+ *
+ * @var bool
+ */
+ private $read_;
+
+ /**
+ * Whether to frame writes
+ *
+ * @var bool
+ */
+ private $write_;
+
+ /**
+ * Constructor.
+ *
+ * @param TTransport $transport Underlying transport
+ */
+ public function __construct($transport = null, $read = true, $write = true)
+ {
+ $this->transport_ = $transport;
+ $this->read_ = $read;
+ $this->write_ = $write;
+ }
+
+ public function isOpen()
+ {
+ return $this->transport_->isOpen();
+ }
+
+ public function open()
+ {
+ $this->transport_->open();
+ }
+
+ public function close()
+ {
+ $this->transport_->close();
+ }
+
+ /**
+ * Reads from the buffer. When more data is required reads another entire
+ * chunk and serves future reads out of that.
+ *
+ * @param int $len How much data
+ */
+ public function read($len)
+ {
+ if (!$this->read_) {
+ return $this->transport_->read($len);
+ }
+
+ if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) {
+ $this->readFrame();
+ }
+
+ // Just return full buff
+ if ($len >= TStringFuncFactory::create()->strlen($this->rBuf_)) {
+ $out = $this->rBuf_;
+ $this->rBuf_ = null;
+
+ return $out;
+ }
+
+ // Return TStringFuncFactory::create()->substr
+ $out = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len);
+ $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len);
+
+ return $out;
+ }
+
+ /**
+ * Put previously read data back into the buffer
+ *
+ * @param string $data data to return
+ */
+ public function putBack($data)
+ {
+ if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) {
+ $this->rBuf_ = $data;
+ } else {
+ $this->rBuf_ = ($data . $this->rBuf_);
+ }
+ }
+
+ /**
+ * Reads a chunk of data into the internal read buffer.
+ */
+ private function readFrame()
+ {
+ $buf = $this->transport_->readAll(4);
+ $val = unpack('N', $buf);
+ $sz = $val[1];
+
+ $this->rBuf_ = $this->transport_->readAll($sz);
+ }
+
+ /**
+ * Writes some data to the pending output buffer.
+ *
+ * @param string $buf The data
+ * @param int $len Limit of bytes to write
+ */
+ public function write($buf, $len = null)
+ {
+ if (!$this->write_) {
+ return $this->transport_->write($buf, $len);
+ }
+
+ if ($len !== null && $len < TStringFuncFactory::create()->strlen($buf)) {
+ $buf = TStringFuncFactory::create()->substr($buf, 0, $len);
+ }
+ $this->wBuf_ .= $buf;
+ }
+
+ /**
+ * Writes the output buffer to the stream in the format of a 4-byte length
+ * followed by the actual data.
+ */
+ public function flush()
+ {
+ if (!$this->write_ || TStringFuncFactory::create()->strlen($this->wBuf_) == 0) {
+ return $this->transport_->flush();
+ }
+
+ $out = pack('N', TStringFuncFactory::create()->strlen($this->wBuf_));
+ $out .= $this->wBuf_;
+
+ // Note that we clear the internal wBuf_ prior to the underlying write
+ // to ensure we're in a sane state (i.e. internal buffer cleaned)
+ // if the underlying write throws up an exception
+ $this->wBuf_ = '';
+ $this->transport_->write($out);
+ $this->transport_->flush();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Transport/THttpClient.php b/src/jaegertracing/thrift/lib/php/lib/Transport/THttpClient.php
new file mode 100644
index 000000000..0158809d2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Transport/THttpClient.php
@@ -0,0 +1,258 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TTransportException;
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * HTTP client for Thrift
+ *
+ * @package thrift.transport
+ */
+class THttpClient extends TTransport
+{
+ /**
+ * The host to connect to
+ *
+ * @var string
+ */
+ protected $host_;
+
+ /**
+ * The port to connect on
+ *
+ * @var int
+ */
+ protected $port_;
+
+ /**
+ * The URI to request
+ *
+ * @var string
+ */
+ protected $uri_;
+
+ /**
+ * The scheme to use for the request, i.e. http, https
+ *
+ * @var string
+ */
+ protected $scheme_;
+
+ /**
+ * Buffer for the HTTP request data
+ *
+ * @var string
+ */
+ protected $buf_;
+
+ /**
+ * Input socket stream.
+ *
+ * @var resource
+ */
+ protected $handle_;
+
+ /**
+ * Read timeout
+ *
+ * @var float
+ */
+ protected $timeout_;
+
+ /**
+ * http headers
+ *
+ * @var array
+ */
+ protected $headers_;
+
+ /**
+ * Context additional options
+ *
+ * @var array
+ */
+ protected $context_;
+
+ /**
+ * Make a new HTTP client.
+ *
+ * @param string $host
+ * @param int $port
+ * @param string $uri
+ * @param string $scheme
+ * @param array $context
+ */
+ public function __construct($host, $port = 80, $uri = '', $scheme = 'http', array $context = array())
+ {
+ if ((TStringFuncFactory::create()->strlen($uri) > 0) && ($uri{0} != '/')) {
+ $uri = '/' . $uri;
+ }
+ $this->scheme_ = $scheme;
+ $this->host_ = $host;
+ $this->port_ = $port;
+ $this->uri_ = $uri;
+ $this->buf_ = '';
+ $this->handle_ = null;
+ $this->timeout_ = null;
+ $this->headers_ = array();
+ $this->context_ = $context;
+ }
+
+ /**
+ * Set read timeout
+ *
+ * @param float $timeout
+ */
+ public function setTimeoutSecs($timeout)
+ {
+ $this->timeout_ = $timeout;
+ }
+
+ /**
+ * Whether this transport is open.
+ *
+ * @return boolean true if open
+ */
+ public function isOpen()
+ {
+ return true;
+ }
+
+ /**
+ * Open the transport for reading/writing
+ *
+ * @throws TTransportException if cannot open
+ */
+ public function open()
+ {
+ }
+
+ /**
+ * Close the transport.
+ */
+ public function close()
+ {
+ if ($this->handle_) {
+ @fclose($this->handle_);
+ $this->handle_ = null;
+ }
+ }
+
+ /**
+ * Read some data into the array.
+ *
+ * @param int $len How much to read
+ * @return string The data that has been read
+ * @throws TTransportException if cannot read any more data
+ */
+ public function read($len)
+ {
+ $data = @fread($this->handle_, $len);
+ if ($data === false || $data === '') {
+ $md = stream_get_meta_data($this->handle_);
+ if ($md['timed_out']) {
+ throw new TTransportException(
+ 'THttpClient: timed out reading ' . $len . ' bytes from ' .
+ $this->host_ . ':' . $this->port_ . $this->uri_,
+ TTransportException::TIMED_OUT
+ );
+ } else {
+ throw new TTransportException(
+ 'THttpClient: Could not read ' . $len . ' bytes from ' .
+ $this->host_ . ':' . $this->port_ . $this->uri_,
+ TTransportException::UNKNOWN
+ );
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Writes some data into the pending buffer
+ *
+ * @param string $buf The data to write
+ * @throws TTransportException if writing fails
+ */
+ public function write($buf)
+ {
+ $this->buf_ .= $buf;
+ }
+
+ /**
+ * Opens and sends the actual request over the HTTP connection
+ *
+ * @throws TTransportException if a writing error occurs
+ */
+ public function flush()
+ {
+ // God, PHP really has some esoteric ways of doing simple things.
+ $host = $this->host_ . ($this->port_ != 80 ? ':' . $this->port_ : '');
+
+ $headers = array();
+ $defaultHeaders = array('Host' => $host,
+ 'Accept' => 'application/x-thrift',
+ 'User-Agent' => 'PHP/THttpClient',
+ 'Content-Type' => 'application/x-thrift',
+ 'Content-Length' => TStringFuncFactory::create()->strlen($this->buf_));
+ foreach (array_merge($defaultHeaders, $this->headers_) as $key => $value) {
+ $headers[] = "$key: $value";
+ }
+
+ $options = $this->context_;
+
+ $baseHttpOptions = isset($options["http"]) ? $options["http"] : array();
+
+ $httpOptions = $baseHttpOptions + array('method' => 'POST',
+ 'header' => implode("\r\n", $headers),
+ 'max_redirects' => 1,
+ 'content' => $this->buf_);
+ if ($this->timeout_ > 0) {
+ $httpOptions['timeout'] = $this->timeout_;
+ }
+ $this->buf_ = '';
+
+ $options["http"] = $httpOptions;
+ $contextid = stream_context_create($options);
+ $this->handle_ = @fopen(
+ $this->scheme_ . '://' . $host . $this->uri_,
+ 'r',
+ false,
+ $contextid
+ );
+
+ // Connect failed?
+ if ($this->handle_ === false) {
+ $this->handle_ = null;
+ $error = 'THttpClient: Could not connect to ' . $host . $this->uri_;
+ throw new TTransportException($error, TTransportException::NOT_OPEN);
+ }
+ }
+
+ public function addHeaders($headers)
+ {
+ $this->headers_ = array_merge($this->headers_, $headers);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Transport/TMemoryBuffer.php b/src/jaegertracing/thrift/lib/php/lib/Transport/TMemoryBuffer.php
new file mode 100644
index 000000000..fee03a2a4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Transport/TMemoryBuffer.php
@@ -0,0 +1,106 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TTransportException;
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * A memory buffer is a tranpsort that simply reads from and writes to an
+ * in-memory string buffer. Anytime you call write on it, the data is simply
+ * placed into a buffer, and anytime you call read, data is read from that
+ * buffer.
+ *
+ * @package thrift.transport
+ */
+class TMemoryBuffer extends TTransport
+{
+ /**
+ * Constructor. Optionally pass an initial value
+ * for the buffer.
+ */
+ public function __construct($buf = '')
+ {
+ $this->buf_ = $buf;
+ }
+
+ protected $buf_ = '';
+
+ public function isOpen()
+ {
+ return true;
+ }
+
+ public function open()
+ {
+ }
+
+ public function close()
+ {
+ }
+
+ public function write($buf)
+ {
+ $this->buf_ .= $buf;
+ }
+
+ public function read($len)
+ {
+ $bufLength = TStringFuncFactory::create()->strlen($this->buf_);
+
+ if ($bufLength === 0) {
+ throw new TTransportException(
+ 'TMemoryBuffer: Could not read ' .
+ $len . ' bytes from buffer.',
+ TTransportException::UNKNOWN
+ );
+ }
+
+ if ($bufLength <= $len) {
+ $ret = $this->buf_;
+ $this->buf_ = '';
+
+ return $ret;
+ }
+
+ $ret = TStringFuncFactory::create()->substr($this->buf_, 0, $len);
+ $this->buf_ = TStringFuncFactory::create()->substr($this->buf_, $len);
+
+ return $ret;
+ }
+
+ public function getBuffer()
+ {
+ return $this->buf_;
+ }
+
+ public function available()
+ {
+ return TStringFuncFactory::create()->strlen($this->buf_);
+ }
+
+ public function putBack($data)
+ {
+ $this->buf_ = $data . $this->buf_;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Transport/TNullTransport.php b/src/jaegertracing/thrift/lib/php/lib/Transport/TNullTransport.php
new file mode 100644
index 000000000..7e086b67c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Transport/TNullTransport.php
@@ -0,0 +1,56 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TTransportException;
+
+/**
+ * Transport that only accepts writes and ignores them.
+ * This is useful for measuring the serialized size of structures.
+ *
+ * @package thrift.transport
+ */
+class TNullTransport extends TTransport
+{
+ public function isOpen()
+ {
+ return true;
+ }
+
+ public function open()
+ {
+ }
+
+ public function close()
+ {
+ }
+
+ public function read($len)
+ {
+ throw new TTransportException("Can't read from TNullTransport.");
+ }
+
+ public function write($buf)
+ {
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Transport/TPhpStream.php b/src/jaegertracing/thrift/lib/php/lib/Transport/TPhpStream.php
new file mode 100644
index 000000000..42823ff33
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Transport/TPhpStream.php
@@ -0,0 +1,124 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TException;
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * Php stream transport. Reads to and writes from the php standard streams
+ * php://input and php://output
+ *
+ * @package thrift.transport
+ */
+class TPhpStream extends TTransport
+{
+ const MODE_R = 1;
+ const MODE_W = 2;
+
+ private $inStream_ = null;
+
+ private $outStream_ = null;
+
+ private $read_ = false;
+
+ private $write_ = false;
+
+ public function __construct($mode)
+ {
+ $this->read_ = $mode & self::MODE_R;
+ $this->write_ = $mode & self::MODE_W;
+ }
+
+ public function open()
+ {
+ if ($this->read_) {
+ $this->inStream_ = @fopen(self::inStreamName(), 'r');
+ if (!is_resource($this->inStream_)) {
+ throw new TException('TPhpStream: Could not open php://input');
+ }
+ }
+ if ($this->write_) {
+ $this->outStream_ = @fopen('php://output', 'w');
+ if (!is_resource($this->outStream_)) {
+ throw new TException('TPhpStream: Could not open php://output');
+ }
+ }
+ }
+
+ public function close()
+ {
+ if ($this->read_) {
+ @fclose($this->inStream_);
+ $this->inStream_ = null;
+ }
+ if ($this->write_) {
+ @fclose($this->outStream_);
+ $this->outStream_ = null;
+ }
+ }
+
+ public function isOpen()
+ {
+ return
+ (!$this->read_ || is_resource($this->inStream_)) &&
+ (!$this->write_ || is_resource($this->outStream_));
+ }
+
+ public function read($len)
+ {
+ $data = @fread($this->inStream_, $len);
+ if ($data === false || $data === '') {
+ throw new TException('TPhpStream: Could not read ' . $len . ' bytes');
+ }
+
+ return $data;
+ }
+
+ public function write($buf)
+ {
+ while (TStringFuncFactory::create()->strlen($buf) > 0) {
+ $got = @fwrite($this->outStream_, $buf);
+ if ($got === 0 || $got === false) {
+ throw new TException(
+ 'TPhpStream: Could not write ' . TStringFuncFactory::create()->strlen($buf) . ' bytes'
+ );
+ }
+ $buf = TStringFuncFactory::create()->substr($buf, $got);
+ }
+ }
+
+ public function flush()
+ {
+ @fflush($this->outStream_);
+ }
+
+ private static function inStreamName()
+ {
+ if (php_sapi_name() == 'cli') {
+ return 'php://stdin';
+ }
+
+ return 'php://input';
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Transport/TSSLSocket.php b/src/jaegertracing/thrift/lib/php/lib/Transport/TSSLSocket.php
new file mode 100644
index 000000000..b4a0adb54
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Transport/TSSLSocket.php
@@ -0,0 +1,117 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TException;
+use Thrift\Exception\TTransportException;
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * Sockets implementation of the TTransport interface.
+ *
+ * @package thrift.transport
+ */
+class TSSLSocket extends TSocket
+{
+ /**
+ * Remote port
+ *
+ * @var resource
+ */
+ protected $context_ = null;
+
+ /**
+ * Socket constructor
+ *
+ * @param string $host Remote hostname
+ * @param int $port Remote port
+ * @param resource $context Stream context
+ * @param bool $persist Whether to use a persistent socket
+ * @param string $debugHandler Function to call for error logging
+ */
+ public function __construct(
+ $host = 'localhost',
+ $port = 9090,
+ $context = null,
+ $debugHandler = null
+ ) {
+ $this->host_ = $this->getSSLHost($host);
+ $this->port_ = $port;
+ $this->context_ = $context;
+ $this->debugHandler_ = $debugHandler ? $debugHandler : 'error_log';
+ }
+
+ /**
+ * Creates a host name with SSL transport protocol
+ * if no transport protocol already specified in
+ * the host name.
+ *
+ * @param string $host Host to listen on
+ * @return string $host Host name with transport protocol
+ */
+ private function getSSLHost($host)
+ {
+ $transport_protocol_loc = strpos($host, "://");
+ if ($transport_protocol_loc === false) {
+ $host = 'ssl://' . $host;
+ }
+ return $host;
+ }
+
+ /**
+ * Connects the socket.
+ */
+ public function open()
+ {
+ if ($this->isOpen()) {
+ throw new TTransportException('Socket already connected', TTransportException::ALREADY_OPEN);
+ }
+
+ if (empty($this->host_)) {
+ throw new TTransportException('Cannot open null host', TTransportException::NOT_OPEN);
+ }
+
+ if ($this->port_ <= 0) {
+ throw new TTransportException('Cannot open without port', TTransportException::NOT_OPEN);
+ }
+
+ $this->handle_ = @stream_socket_client(
+ $this->host_ . ':' . $this->port_,
+ $errno,
+ $errstr,
+ $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000),
+ STREAM_CLIENT_CONNECT,
+ $this->context_
+ );
+
+ // Connect failed?
+ if ($this->handle_ === false) {
+ $error = 'TSocket: Could not connect to ' .
+ $this->host_ . ':' . $this->port_ . ' (' . $errstr . ' [' . $errno . '])';
+ if ($this->debug_) {
+ call_user_func($this->debugHandler_, $error);
+ }
+ throw new TException($error);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Transport/TSocket.php b/src/jaegertracing/thrift/lib/php/lib/Transport/TSocket.php
new file mode 100644
index 000000000..5147efa63
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Transport/TSocket.php
@@ -0,0 +1,366 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TException;
+use Thrift\Exception\TTransportException;
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * Sockets implementation of the TTransport interface.
+ *
+ * @package thrift.transport
+ */
+class TSocket extends TTransport
+{
+ /**
+ * Handle to PHP socket
+ *
+ * @var resource
+ */
+ protected $handle_ = null;
+
+ /**
+ * Remote hostname
+ *
+ * @var string
+ */
+ protected $host_ = 'localhost';
+
+ /**
+ * Remote port
+ *
+ * @var int
+ */
+ protected $port_ = '9090';
+
+ /**
+ * Send timeout in seconds.
+ *
+ * Combined with sendTimeoutUsec this is used for send timeouts.
+ *
+ * @var int
+ */
+ protected $sendTimeoutSec_ = 0;
+
+ /**
+ * Send timeout in microseconds.
+ *
+ * Combined with sendTimeoutSec this is used for send timeouts.
+ *
+ * @var int
+ */
+ protected $sendTimeoutUsec_ = 100000;
+
+ /**
+ * Recv timeout in seconds
+ *
+ * Combined with recvTimeoutUsec this is used for recv timeouts.
+ *
+ * @var int
+ */
+ protected $recvTimeoutSec_ = 0;
+
+ /**
+ * Recv timeout in microseconds
+ *
+ * Combined with recvTimeoutSec this is used for recv timeouts.
+ *
+ * @var int
+ */
+ protected $recvTimeoutUsec_ = 750000;
+
+ /**
+ * Persistent socket or plain?
+ *
+ * @var bool
+ */
+ protected $persist_ = false;
+
+ /**
+ * Debugging on?
+ *
+ * @var bool
+ */
+ protected $debug_ = false;
+
+ /**
+ * Debug handler
+ *
+ * @var mixed
+ */
+ protected $debugHandler_ = null;
+
+ /**
+ * Socket constructor
+ *
+ * @param string $host Remote hostname
+ * @param int $port Remote port
+ * @param bool $persist Whether to use a persistent socket
+ * @param string $debugHandler Function to call for error logging
+ */
+ public function __construct(
+ $host = 'localhost',
+ $port = 9090,
+ $persist = false,
+ $debugHandler = null
+ ) {
+ $this->host_ = $host;
+ $this->port_ = $port;
+ $this->persist_ = $persist;
+ $this->debugHandler_ = $debugHandler ? $debugHandler : 'error_log';
+ }
+
+ /**
+ * @param resource $handle
+ * @return void
+ */
+ public function setHandle($handle)
+ {
+ $this->handle_ = $handle;
+ stream_set_blocking($this->handle_, false);
+ }
+
+ /**
+ * Sets the send timeout.
+ *
+ * @param int $timeout Timeout in milliseconds.
+ */
+ public function setSendTimeout($timeout)
+ {
+ $this->sendTimeoutSec_ = floor($timeout / 1000);
+ $this->sendTimeoutUsec_ =
+ ($timeout - ($this->sendTimeoutSec_ * 1000)) * 1000;
+ }
+
+ /**
+ * Sets the receive timeout.
+ *
+ * @param int $timeout Timeout in milliseconds.
+ */
+ public function setRecvTimeout($timeout)
+ {
+ $this->recvTimeoutSec_ = floor($timeout / 1000);
+ $this->recvTimeoutUsec_ =
+ ($timeout - ($this->recvTimeoutSec_ * 1000)) * 1000;
+ }
+
+ /**
+ * Sets debugging output on or off
+ *
+ * @param bool $debug
+ */
+ public function setDebug($debug)
+ {
+ $this->debug_ = $debug;
+ }
+
+ /**
+ * Get the host that this socket is connected to
+ *
+ * @return string host
+ */
+ public function getHost()
+ {
+ return $this->host_;
+ }
+
+ /**
+ * Get the remote port that this socket is connected to
+ *
+ * @return int port
+ */
+ public function getPort()
+ {
+ return $this->port_;
+ }
+
+ /**
+ * Tests whether this is open
+ *
+ * @return bool true if the socket is open
+ */
+ public function isOpen()
+ {
+ return is_resource($this->handle_);
+ }
+
+ /**
+ * Connects the socket.
+ */
+ public function open()
+ {
+ if ($this->isOpen()) {
+ throw new TTransportException('Socket already connected', TTransportException::ALREADY_OPEN);
+ }
+
+ if (empty($this->host_)) {
+ throw new TTransportException('Cannot open null host', TTransportException::NOT_OPEN);
+ }
+
+ if ($this->port_ <= 0) {
+ throw new TTransportException('Cannot open without port', TTransportException::NOT_OPEN);
+ }
+
+ if ($this->persist_) {
+ $this->handle_ = @pfsockopen(
+ $this->host_,
+ $this->port_,
+ $errno,
+ $errstr,
+ $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000)
+ );
+ } else {
+ $this->handle_ = @fsockopen(
+ $this->host_,
+ $this->port_,
+ $errno,
+ $errstr,
+ $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000)
+ );
+ }
+
+ // Connect failed?
+ if ($this->handle_ === false) {
+ $error = 'TSocket: Could not connect to ' .
+ $this->host_ . ':' . $this->port_ . ' (' . $errstr . ' [' . $errno . '])';
+ if ($this->debug_) {
+ call_user_func($this->debugHandler_, $error);
+ }
+ throw new TException($error);
+ }
+
+ if (function_exists('socket_import_stream') && function_exists('socket_set_option')) {
+ $socket = socket_import_stream($this->handle_);
+ socket_set_option($socket, SOL_TCP, TCP_NODELAY, 1);
+ }
+ }
+
+ /**
+ * Closes the socket.
+ */
+ public function close()
+ {
+ @fclose($this->handle_);
+ $this->handle_ = null;
+ }
+
+ /**
+ * Read from the socket at most $len bytes.
+ *
+ * This method will not wait for all the requested data, it will return as
+ * soon as any data is received.
+ *
+ * @param int $len Maximum number of bytes to read.
+ * @return string Binary data
+ */
+ public function read($len)
+ {
+ $null = null;
+ $read = array($this->handle_);
+ $readable = @stream_select(
+ $read,
+ $null,
+ $null,
+ $this->recvTimeoutSec_,
+ $this->recvTimeoutUsec_
+ );
+
+ if ($readable > 0) {
+ $data = fread($this->handle_, $len);
+ if ($data === false) {
+ throw new TTransportException('TSocket: Could not read ' . $len . ' bytes from ' .
+ $this->host_ . ':' . $this->port_);
+ } elseif ($data == '' && feof($this->handle_)) {
+ throw new TTransportException('TSocket read 0 bytes');
+ }
+
+ return $data;
+ } elseif ($readable === 0) {
+ throw new TTransportException('TSocket: timed out reading ' . $len . ' bytes from ' .
+ $this->host_ . ':' . $this->port_);
+ } else {
+ throw new TTransportException('TSocket: Could not read ' . $len . ' bytes from ' .
+ $this->host_ . ':' . $this->port_);
+ }
+ }
+
+ /**
+ * Write to the socket.
+ *
+ * @param string $buf The data to write
+ */
+ public function write($buf)
+ {
+ $null = null;
+ $write = array($this->handle_);
+
+ // keep writing until all the data has been written
+ while (TStringFuncFactory::create()->strlen($buf) > 0) {
+ // wait for stream to become available for writing
+ $writable = @stream_select(
+ $null,
+ $write,
+ $null,
+ $this->sendTimeoutSec_,
+ $this->sendTimeoutUsec_
+ );
+ if ($writable > 0) {
+ // write buffer to stream
+ $written = fwrite($this->handle_, $buf);
+ if ($written === -1 || $written === false) {
+ throw new TTransportException(
+ 'TSocket: Could not write ' . TStringFuncFactory::create()->strlen($buf) . ' bytes ' .
+ $this->host_ . ':' . $this->port_
+ );
+ }
+ // determine how much of the buffer is left to write
+ $buf = TStringFuncFactory::create()->substr($buf, $written);
+ } elseif ($writable === 0) {
+ throw new TTransportException(
+ 'TSocket: timed out writing ' . TStringFuncFactory::create()->strlen($buf) . ' bytes from ' .
+ $this->host_ . ':' . $this->port_
+ );
+ } else {
+ throw new TTransportException(
+ 'TSocket: Could not write ' . TStringFuncFactory::create()->strlen($buf) . ' bytes ' .
+ $this->host_ . ':' . $this->port_
+ );
+ }
+ }
+ }
+
+ /**
+ * Flush output to the socket.
+ *
+ * Since read(), readAll() and write() operate on the sockets directly,
+ * this is a no-op
+ *
+ * If you wish to have flushable buffering behaviour, wrap this TSocket
+ * in a TBufferedTransport.
+ */
+ public function flush()
+ {
+ // no-op
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Transport/TSocketPool.php b/src/jaegertracing/thrift/lib/php/lib/Transport/TSocketPool.php
new file mode 100644
index 000000000..cb9e8ddfa
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Transport/TSocketPool.php
@@ -0,0 +1,310 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TException;
+
+/**
+ * This library makes use of APC cache to make hosts as down in a web
+ * environment. If you are running from the CLI or on a system without APC
+ * installed, then these null functions will step in and act like cache
+ * misses.
+ */
+if (!function_exists('apc_fetch')) {
+ function apc_fetch($key)
+ {
+ return false;
+ }
+
+ function apc_store($key, $var, $ttl = 0)
+ {
+ return false;
+ }
+}
+
+/**
+ * Sockets implementation of the TTransport interface that allows connection
+ * to a pool of servers.
+ *
+ * @package thrift.transport
+ */
+class TSocketPool extends TSocket
+{
+ /**
+ * Remote servers. Array of associative arrays with 'host' and 'port' keys
+ */
+ private $servers_ = array();
+
+ /**
+ * How many times to retry each host in connect
+ *
+ * @var int
+ */
+ private $numRetries_ = 1;
+
+ /**
+ * Retry interval in seconds, how long to not try a host if it has been
+ * marked as down.
+ *
+ * @var int
+ */
+ private $retryInterval_ = 60;
+
+ /**
+ * Max consecutive failures before marking a host down.
+ *
+ * @var int
+ */
+ private $maxConsecutiveFailures_ = 1;
+
+ /**
+ * Try hosts in order? or Randomized?
+ *
+ * @var bool
+ */
+ private $randomize_ = true;
+
+ /**
+ * Always try last host, even if marked down?
+ *
+ * @var bool
+ */
+ private $alwaysTryLast_ = true;
+
+ /**
+ * Socket pool constructor
+ *
+ * @param array $hosts List of remote hostnames
+ * @param mixed $ports Array of remote ports, or a single common port
+ * @param bool $persist Whether to use a persistent socket
+ * @param mixed $debugHandler Function for error logging
+ */
+ public function __construct(
+ $hosts = array('localhost'),
+ $ports = array(9090),
+ $persist = false,
+ $debugHandler = null
+ ) {
+ parent::__construct(null, 0, $persist, $debugHandler);
+
+ if (!is_array($ports)) {
+ $port = $ports;
+ $ports = array();
+ foreach ($hosts as $key => $val) {
+ $ports[$key] = $port;
+ }
+ }
+
+ foreach ($hosts as $key => $host) {
+ $this->servers_ [] = array('host' => $host,
+ 'port' => $ports[$key]);
+ }
+ }
+
+ /**
+ * Add a server to the pool
+ *
+ * This function does not prevent you from adding a duplicate server entry.
+ *
+ * @param string $host hostname or IP
+ * @param int $port port
+ */
+ public function addServer($host, $port)
+ {
+ $this->servers_[] = array('host' => $host, 'port' => $port);
+ }
+
+ /**
+ * Sets how many time to keep retrying a host in the connect function.
+ *
+ * @param int $numRetries
+ */
+ public function setNumRetries($numRetries)
+ {
+ $this->numRetries_ = $numRetries;
+ }
+
+ /**
+ * Sets how long to wait until retrying a host if it was marked down
+ *
+ * @param int $numRetries
+ */
+ public function setRetryInterval($retryInterval)
+ {
+ $this->retryInterval_ = $retryInterval;
+ }
+
+ /**
+ * Sets how many time to keep retrying a host before marking it as down.
+ *
+ * @param int $numRetries
+ */
+ public function setMaxConsecutiveFailures($maxConsecutiveFailures)
+ {
+ $this->maxConsecutiveFailures_ = $maxConsecutiveFailures;
+ }
+
+ /**
+ * Turns randomization in connect order on or off.
+ *
+ * @param bool $randomize
+ */
+ public function setRandomize($randomize)
+ {
+ $this->randomize_ = $randomize;
+ }
+
+ /**
+ * Whether to always try the last server.
+ *
+ * @param bool $alwaysTryLast
+ */
+ public function setAlwaysTryLast($alwaysTryLast)
+ {
+ $this->alwaysTryLast_ = $alwaysTryLast;
+ }
+
+ /**
+ * Connects the socket by iterating through all the servers in the pool
+ * and trying to find one that works.
+ */
+ public function open()
+ {
+ // Check if we want order randomization
+ if ($this->randomize_) {
+ shuffle($this->servers_);
+ }
+
+ // Count servers to identify the "last" one
+ $numServers = count($this->servers_);
+
+ for ($i = 0; $i < $numServers; ++$i) {
+ // This extracts the $host and $port variables
+ extract($this->servers_[$i]);
+
+ // Check APC cache for a record of this server being down
+ $failtimeKey = 'thrift_failtime:' . $host . ':' . $port . '~';
+
+ // Cache miss? Assume it's OK
+ $lastFailtime = apc_fetch($failtimeKey);
+ if ($lastFailtime === false) {
+ $lastFailtime = 0;
+ }
+
+ $retryIntervalPassed = false;
+
+ // Cache hit...make sure enough the retry interval has elapsed
+ if ($lastFailtime > 0) {
+ $elapsed = time() - $lastFailtime;
+ if ($elapsed > $this->retryInterval_) {
+ $retryIntervalPassed = true;
+ if ($this->debug_) {
+ call_user_func(
+ $this->debugHandler_,
+ 'TSocketPool: retryInterval ' .
+ '(' . $this->retryInterval_ . ') ' .
+ 'has passed for host ' . $host . ':' . $port
+ );
+ }
+ }
+ }
+
+ // Only connect if not in the middle of a fail interval, OR if this
+ // is the LAST server we are trying, just hammer away on it
+ $isLastServer = false;
+ if ($this->alwaysTryLast_) {
+ $isLastServer = ($i == ($numServers - 1));
+ }
+
+ if (($lastFailtime === 0) ||
+ ($isLastServer) ||
+ ($lastFailtime > 0 && $retryIntervalPassed)) {
+ // Set underlying TSocket params to this one
+ $this->host_ = $host;
+ $this->port_ = $port;
+
+ // Try up to numRetries_ connections per server
+ for ($attempt = 0; $attempt < $this->numRetries_; $attempt++) {
+ try {
+ // Use the underlying TSocket open function
+ parent::open();
+
+ // Only clear the failure counts if required to do so
+ if ($lastFailtime > 0) {
+ apc_store($failtimeKey, 0);
+ }
+
+ // Successful connection, return now
+ return;
+ } catch (TException $tx) {
+ // Connection failed
+ }
+ }
+
+ // Mark failure of this host in the cache
+ $consecfailsKey = 'thrift_consecfails:' . $host . ':' . $port . '~';
+
+ // Ignore cache misses
+ $consecfails = apc_fetch($consecfailsKey);
+ if ($consecfails === false) {
+ $consecfails = 0;
+ }
+
+ // Increment by one
+ $consecfails++;
+
+ // Log and cache this failure
+ if ($consecfails >= $this->maxConsecutiveFailures_) {
+ if ($this->debug_) {
+ call_user_func(
+ $this->debugHandler_,
+ 'TSocketPool: marking ' . $host . ':' . $port .
+ ' as down for ' . $this->retryInterval_ . ' secs ' .
+ 'after ' . $consecfails . ' failed attempts.'
+ );
+ }
+ // Store the failure time
+ apc_store($failtimeKey, time());
+
+ // Clear the count of consecutive failures
+ apc_store($consecfailsKey, 0);
+ } else {
+ apc_store($consecfailsKey, $consecfails);
+ }
+ }
+ }
+
+ // Oh no; we failed them all. The system is totally ill!
+ $error = 'TSocketPool: All hosts in pool are down. ';
+ $hosts = array();
+ foreach ($this->servers_ as $server) {
+ $hosts [] = $server['host'] . ':' . $server['port'];
+ }
+ $hostlist = implode(',', $hosts);
+ $error .= '(' . $hostlist . ')';
+ if ($this->debug_) {
+ call_user_func($this->debugHandler_, $error);
+ }
+ throw new TException($error);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Transport/TTransport.php b/src/jaegertracing/thrift/lib/php/lib/Transport/TTransport.php
new file mode 100644
index 000000000..35921c666
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Transport/TTransport.php
@@ -0,0 +1,98 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.transport
+ */
+
+namespace Thrift\Transport;
+
+use Thrift\Exception\TTransportException;
+use Thrift\Factory\TStringFuncFactory;
+
+/**
+ * Base interface for a transport agent.
+ *
+ * @package thrift.transport
+ */
+abstract class TTransport
+{
+ /**
+ * Whether this transport is open.
+ *
+ * @return boolean true if open
+ */
+ abstract public function isOpen();
+
+ /**
+ * Open the transport for reading/writing
+ *
+ * @throws TTransportException if cannot open
+ */
+ abstract public function open();
+
+ /**
+ * Close the transport.
+ */
+ abstract public function close();
+
+ /**
+ * Read some data into the array.
+ *
+ * @param int $len How much to read
+ * @return string The data that has been read
+ * @throws TTransportException if cannot read any more data
+ */
+ abstract public function read($len);
+
+ /**
+ * Guarantees that the full amount of data is read.
+ *
+ * @return string The data, of exact length
+ * @throws TTransportException if cannot read data
+ */
+ public function readAll($len)
+ {
+ // return $this->read($len);
+
+ $data = '';
+ $got = 0;
+ while (($got = TStringFuncFactory::create()->strlen($data)) < $len) {
+ $data .= $this->read($len - $got);
+ }
+
+ return $data;
+ }
+
+ /**
+ * Writes the given data out.
+ *
+ * @param string $buf The data to write
+ * @throws TTransportException if writing fails
+ */
+ abstract public function write($buf);
+
+ /**
+ * Flushes any pending data out of a buffer
+ *
+ * @throws TTransportException if a writing error occurs
+ */
+ public function flush()
+ {
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Type/TConstant.php b/src/jaegertracing/thrift/lib/php/lib/Type/TConstant.php
new file mode 100644
index 000000000..215da4a3d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Type/TConstant.php
@@ -0,0 +1,52 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift
+ */
+
+namespace Thrift\Type;
+
+/**
+ * Base class for constant Management
+ */
+abstract class TConstant
+{
+ /**
+ * Don't instanciate this class
+ */
+ protected function __construct()
+ {
+ }
+
+ /**
+ * Get a constant value
+ * @param string $constant
+ * @return mixed
+ */
+ public static function get($constant)
+ {
+ if (is_null(static::$$constant)) {
+ static::$$constant = call_user_func(
+ sprintf('static::init_%s', $constant)
+ );
+ }
+
+ return static::$$constant;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Type/TMessageType.php b/src/jaegertracing/thrift/lib/php/lib/Type/TMessageType.php
new file mode 100644
index 000000000..dc9ae6284
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Type/TMessageType.php
@@ -0,0 +1,34 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift
+ */
+
+namespace Thrift\Type;
+
+/**
+ * Message types for RPC
+ */
+class TMessageType
+{
+ const CALL = 1;
+ const REPLY = 2;
+ const EXCEPTION = 3;
+ const ONEWAY = 4;
+}
diff --git a/src/jaegertracing/thrift/lib/php/lib/Type/TType.php b/src/jaegertracing/thrift/lib/php/lib/Type/TType.php
new file mode 100644
index 000000000..3fdb15f53
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/lib/Type/TType.php
@@ -0,0 +1,47 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift
+ */
+
+namespace Thrift\Type;
+
+/**
+ * Data types that can be sent via Thrift
+ */
+class TType
+{
+ const STOP = 0;
+ const VOID = 1;
+ const BOOL = 2;
+ const BYTE = 3;
+ const I08 = 3;
+ const DOUBLE = 4;
+ const I16 = 6;
+ const I32 = 8;
+ const I64 = 10;
+ const STRING = 11;
+ const UTF7 = 11;
+ const STRUCT = 12;
+ const MAP = 13;
+ const SET = 14;
+ const LST = 15; // N.B. cannot use LIST keyword in PHP!
+ const UTF8 = 16;
+ const UTF16 = 17;
+}
diff --git a/src/jaegertracing/thrift/lib/php/src/TStringUtils.php b/src/jaegertracing/thrift/lib/php/src/TStringUtils.php
new file mode 100644
index 000000000..894baf8d4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/src/TStringUtils.php
@@ -0,0 +1,90 @@
+<?php
+
+interface TStringFunc
+{
+ public function substr($str, $start, $length = null);
+ public function strlen($str);
+}
+
+class TStringFunc_Core
+implements TStringFunc {
+ public function substr($str, $start, $length = null)
+ {
+ // specifying a null $length would return an empty string
+ if ($length === null) {
+ return substr($str, $start);
+ }
+
+ return substr($str, $start, $length);
+ }
+
+ public function strlen($str)
+ {
+ return strlen($str);
+ }
+}
+
+class TStringFunc_Mbstring
+implements TStringFunc {
+ public function substr($str, $start, $length = null)
+ {
+ /**
+ * We need to set the charset parameter, which is the second
+ * optional parameter and the first optional parameter can't
+ * be null or false as a "magic" value because that would
+ * cause an empty string to be returned, so we need to
+ * actually calculate the proper length value.
+ */
+ if ($length === null) {
+ $length = $this->strlen($str) - $start;
+ }
+
+ return mb_substr($str, $start, $length, '8bit');
+ }
+
+ public function strlen($str)
+ {
+ return mb_strlen($str, '8bit');
+ }
+}
+
+class TStringFuncFactory
+{
+ private static $_instance;
+
+ /**
+ * Get the Singleton instance of TStringFunc implementation that is
+ * compatible with the current system's mbstring.func_overload settings.
+ *
+ * @return TStringFunc
+ */
+ public static function create()
+ {
+ if (!self::$_instance) {
+ self::_setInstance();
+ }
+
+ return self::$_instance;
+ }
+
+ private static function _setInstance()
+ {
+ /**
+ * Cannot use str* functions for byte counting because multibyte
+ * characters will be read a single bytes.
+ *
+ * See: http://us.php.net/manual/en/mbstring.overload.php
+ */
+ if (ini_get('mbstring.func_overload') & 2) {
+ self::$_instance = new TStringFunc_Mbstring();
+ }
+ /**
+ * mbstring is not installed or does not have function overloading
+ * of the str* functions enabled so use PHP core str* functions for
+ * byte counting.
+ */
+ else {
+ self::$_instance = new TStringFunc_Core();
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/src/Thrift.php b/src/jaegertracing/thrift/lib/php/src/Thrift.php
new file mode 100644
index 000000000..4fe439271
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/src/Thrift.php
@@ -0,0 +1,821 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift
+ */
+
+/**
+ * Data types that can be sent via Thrift
+ */
+class TType
+{
+ const STOP = 0;
+ const VOID = 1;
+ const BOOL = 2;
+ const BYTE = 3;
+ const I08 = 3;
+ const DOUBLE = 4;
+ const I16 = 6;
+ const I32 = 8;
+ const I64 = 10;
+ const STRING = 11;
+ const UTF7 = 11;
+ const STRUCT = 12;
+ const MAP = 13;
+ const SET = 14;
+ const LST = 15; // N.B. cannot use LIST keyword in PHP!
+ const UTF8 = 16;
+ const UTF16 = 17;
+}
+
+/**
+ * Message types for RPC
+ */
+class TMessageType
+{
+ const CALL = 1;
+ const REPLY = 2;
+ const EXCEPTION = 3;
+ const ONEWAY = 4;
+}
+
+/**
+ * NOTE(mcslee): This currently contains a ton of duplicated code from TBase
+ * because we need to save CPU cycles and this is not yet in an extension.
+ * Ideally we'd multiply-inherit TException from both Exception and Base, but
+ * that's not possible in PHP and there are no modules either, so for now we
+ * apologetically take a trip to HackTown.
+ *
+ * Can be called with standard Exception constructor (message, code) or with
+ * Thrift Base object constructor (spec, vals).
+ *
+ * @param mixed $p1 Message (string) or type-spec (array)
+ * @param mixed $p2 Code (integer) or values (array)
+ */
+class TException extends Exception
+{
+ public function __construct($p1=null, $p2=0)
+ {
+ if (is_array($p1) && is_array($p2)) {
+ $spec = $p1;
+ $vals = $p2;
+ foreach ($spec as $fid => $fspec) {
+ $var = $fspec['var'];
+ if (isset($vals[$var])) {
+ $this->$var = $vals[$var];
+ }
+ }
+ } else {
+ parent::__construct($p1, $p2);
+ }
+ }
+
+ static $tmethod = array(TType::BOOL => 'Bool',
+ TType::BYTE => 'Byte',
+ TType::I16 => 'I16',
+ TType::I32 => 'I32',
+ TType::I64 => 'I64',
+ TType::DOUBLE => 'Double',
+ TType::STRING => 'String');
+
+ private function _readMap(&$var, $spec, $input)
+ {
+ $xfer = 0;
+ $ktype = $spec['ktype'];
+ $vtype = $spec['vtype'];
+ $kread = $vread = null;
+ if (isset(TBase::$tmethod[$ktype])) {
+ $kread = 'read'.TBase::$tmethod[$ktype];
+ } else {
+ $kspec = $spec['key'];
+ }
+ if (isset(TBase::$tmethod[$vtype])) {
+ $vread = 'read'.TBase::$tmethod[$vtype];
+ } else {
+ $vspec = $spec['val'];
+ }
+ $var = array();
+ $_ktype = $_vtype = $size = 0;
+ $xfer += $input->readMapBegin($_ktype, $_vtype, $size);
+ for ($i = 0; $i < $size; ++$i) {
+ $key = $val = null;
+ if ($kread !== null) {
+ $xfer += $input->$kread($key);
+ } else {
+ switch ($ktype) {
+ case TType::STRUCT:
+ $class = $kspec['class'];
+ $key = new $class();
+ $xfer += $key->read($input);
+ break;
+ case TType::MAP:
+ $xfer += $this->_readMap($key, $kspec, $input);
+ break;
+ case TType::LST:
+ $xfer += $this->_readList($key, $kspec, $input, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_readList($key, $kspec, $input, true);
+ break;
+ }
+ }
+ if ($vread !== null) {
+ $xfer += $input->$vread($val);
+ } else {
+ switch ($vtype) {
+ case TType::STRUCT:
+ $class = $vspec['class'];
+ $val = new $class();
+ $xfer += $val->read($input);
+ break;
+ case TType::MAP:
+ $xfer += $this->_readMap($val, $vspec, $input);
+ break;
+ case TType::LST:
+ $xfer += $this->_readList($val, $vspec, $input, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_readList($val, $vspec, $input, true);
+ break;
+ }
+ }
+ $var[$key] = $val;
+ }
+ $xfer += $input->readMapEnd();
+
+ return $xfer;
+ }
+
+ private function _readList(&$var, $spec, $input, $set=false)
+ {
+ $xfer = 0;
+ $etype = $spec['etype'];
+ $eread = $vread = null;
+ if (isset(TBase::$tmethod[$etype])) {
+ $eread = 'read'.TBase::$tmethod[$etype];
+ } else {
+ $espec = $spec['elem'];
+ }
+ $var = array();
+ $_etype = $size = 0;
+ if ($set) {
+ $xfer += $input->readSetBegin($_etype, $size);
+ } else {
+ $xfer += $input->readListBegin($_etype, $size);
+ }
+ for ($i = 0; $i < $size; ++$i) {
+ $elem = null;
+ if ($eread !== null) {
+ $xfer += $input->$eread($elem);
+ } else {
+ $espec = $spec['elem'];
+ switch ($etype) {
+ case TType::STRUCT:
+ $class = $espec['class'];
+ $elem = new $class();
+ $xfer += $elem->read($input);
+ break;
+ case TType::MAP:
+ $xfer += $this->_readMap($elem, $espec, $input);
+ break;
+ case TType::LST:
+ $xfer += $this->_readList($elem, $espec, $input, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_readList($elem, $espec, $input, true);
+ break;
+ }
+ }
+ if ($set) {
+ $var[$elem] = true;
+ } else {
+ $var []= $elem;
+ }
+ }
+ if ($set) {
+ $xfer += $input->readSetEnd();
+ } else {
+ $xfer += $input->readListEnd();
+ }
+
+ return $xfer;
+ }
+
+ protected function _read($class, $spec, $input)
+ {
+ $xfer = 0;
+ $fname = null;
+ $ftype = 0;
+ $fid = 0;
+ $xfer += $input->readStructBegin($fname);
+ while (true) {
+ $xfer += $input->readFieldBegin($fname, $ftype, $fid);
+ if ($ftype == TType::STOP) {
+ break;
+ }
+ if (isset($spec[$fid])) {
+ $fspec = $spec[$fid];
+ $var = $fspec['var'];
+ if ($ftype == $fspec['type']) {
+ $xfer = 0;
+ if (isset(TBase::$tmethod[$ftype])) {
+ $func = 'read'.TBase::$tmethod[$ftype];
+ $xfer += $input->$func($this->$var);
+ } else {
+ switch ($ftype) {
+ case TType::STRUCT:
+ $class = $fspec['class'];
+ $this->$var = new $class();
+ $xfer += $this->$var->read($input);
+ break;
+ case TType::MAP:
+ $xfer += $this->_readMap($this->$var, $fspec, $input);
+ break;
+ case TType::LST:
+ $xfer += $this->_readList($this->$var, $fspec, $input, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_readList($this->$var, $fspec, $input, true);
+ break;
+ }
+ }
+ } else {
+ $xfer += $input->skip($ftype);
+ }
+ } else {
+ $xfer += $input->skip($ftype);
+ }
+ $xfer += $input->readFieldEnd();
+ }
+ $xfer += $input->readStructEnd();
+
+ return $xfer;
+ }
+
+ private function _writeMap($var, $spec, $output)
+ {
+ $xfer = 0;
+ $ktype = $spec['ktype'];
+ $vtype = $spec['vtype'];
+ $kwrite = $vwrite = null;
+ if (isset(TBase::$tmethod[$ktype])) {
+ $kwrite = 'write'.TBase::$tmethod[$ktype];
+ } else {
+ $kspec = $spec['key'];
+ }
+ if (isset(TBase::$tmethod[$vtype])) {
+ $vwrite = 'write'.TBase::$tmethod[$vtype];
+ } else {
+ $vspec = $spec['val'];
+ }
+ $xfer += $output->writeMapBegin($ktype, $vtype, count($var));
+ foreach ($var as $key => $val) {
+ if (isset($kwrite)) {
+ $xfer += $output->$kwrite($key);
+ } else {
+ switch ($ktype) {
+ case TType::STRUCT:
+ $xfer += $key->write($output);
+ break;
+ case TType::MAP:
+ $xfer += $this->_writeMap($key, $kspec, $output);
+ break;
+ case TType::LST:
+ $xfer += $this->_writeList($key, $kspec, $output, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_writeList($key, $kspec, $output, true);
+ break;
+ }
+ }
+ if (isset($vwrite)) {
+ $xfer += $output->$vwrite($val);
+ } else {
+ switch ($vtype) {
+ case TType::STRUCT:
+ $xfer += $val->write($output);
+ break;
+ case TType::MAP:
+ $xfer += $this->_writeMap($val, $vspec, $output);
+ break;
+ case TType::LST:
+ $xfer += $this->_writeList($val, $vspec, $output, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_writeList($val, $vspec, $output, true);
+ break;
+ }
+ }
+ }
+ $xfer += $output->writeMapEnd();
+
+ return $xfer;
+ }
+
+ private function _writeList($var, $spec, $output, $set=false)
+ {
+ $xfer = 0;
+ $etype = $spec['etype'];
+ $ewrite = null;
+ if (isset(TBase::$tmethod[$etype])) {
+ $ewrite = 'write'.TBase::$tmethod[$etype];
+ } else {
+ $espec = $spec['elem'];
+ }
+ if ($set) {
+ $xfer += $output->writeSetBegin($etype, count($var));
+ } else {
+ $xfer += $output->writeListBegin($etype, count($var));
+ }
+ foreach ($var as $key => $val) {
+ $elem = $set ? $key : $val;
+ if (isset($ewrite)) {
+ $xfer += $output->$ewrite($elem);
+ } else {
+ switch ($etype) {
+ case TType::STRUCT:
+ $xfer += $elem->write($output);
+ break;
+ case TType::MAP:
+ $xfer += $this->_writeMap($elem, $espec, $output);
+ break;
+ case TType::LST:
+ $xfer += $this->_writeList($elem, $espec, $output, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_writeList($elem, $espec, $output, true);
+ break;
+ }
+ }
+ }
+ if ($set) {
+ $xfer += $output->writeSetEnd();
+ } else {
+ $xfer += $output->writeListEnd();
+ }
+
+ return $xfer;
+ }
+
+ protected function _write($class, $spec, $output)
+ {
+ $xfer = 0;
+ $xfer += $output->writeStructBegin($class);
+ foreach ($spec as $fid => $fspec) {
+ $var = $fspec['var'];
+ if ($this->$var !== null) {
+ $ftype = $fspec['type'];
+ $xfer += $output->writeFieldBegin($var, $ftype, $fid);
+ if (isset(TBase::$tmethod[$ftype])) {
+ $func = 'write'.TBase::$tmethod[$ftype];
+ $xfer += $output->$func($this->$var);
+ } else {
+ switch ($ftype) {
+ case TType::STRUCT:
+ $xfer += $this->$var->write($output);
+ break;
+ case TType::MAP:
+ $xfer += $this->_writeMap($this->$var, $fspec, $output);
+ break;
+ case TType::LST:
+ $xfer += $this->_writeList($this->$var, $fspec, $output, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_writeList($this->$var, $fspec, $output, true);
+ break;
+ }
+ }
+ $xfer += $output->writeFieldEnd();
+ }
+ }
+ $xfer += $output->writeFieldStop();
+ $xfer += $output->writeStructEnd();
+
+ return $xfer;
+ }
+
+}
+
+/**
+ * Base class from which other Thrift structs extend. This is so that we can
+ * cut back on the size of the generated code which is turning out to have a
+ * nontrivial cost just to load thanks to the wondrously abysmal implementation
+ * of PHP. Note that code is intentionally duplicated in here to avoid making
+ * function calls for every field or member of a container..
+ */
+abstract class TBase
+{
+ static $tmethod = array(TType::BOOL => 'Bool',
+ TType::BYTE => 'Byte',
+ TType::I16 => 'I16',
+ TType::I32 => 'I32',
+ TType::I64 => 'I64',
+ TType::DOUBLE => 'Double',
+ TType::STRING => 'String');
+
+ abstract public function read($input);
+
+ abstract public function write($output);
+
+ public function __construct($spec=null, $vals=null)
+ {
+ if (is_array($spec) && is_array($vals)) {
+ foreach ($spec as $fid => $fspec) {
+ $var = $fspec['var'];
+ if (isset($vals[$var])) {
+ $this->$var = $vals[$var];
+ }
+ }
+ }
+ }
+
+ private function _readMap(&$var, $spec, $input)
+ {
+ $xfer = 0;
+ $ktype = $spec['ktype'];
+ $vtype = $spec['vtype'];
+ $kread = $vread = null;
+ if (isset(TBase::$tmethod[$ktype])) {
+ $kread = 'read'.TBase::$tmethod[$ktype];
+ } else {
+ $kspec = $spec['key'];
+ }
+ if (isset(TBase::$tmethod[$vtype])) {
+ $vread = 'read'.TBase::$tmethod[$vtype];
+ } else {
+ $vspec = $spec['val'];
+ }
+ $var = array();
+ $_ktype = $_vtype = $size = 0;
+ $xfer += $input->readMapBegin($_ktype, $_vtype, $size);
+ for ($i = 0; $i < $size; ++$i) {
+ $key = $val = null;
+ if ($kread !== null) {
+ $xfer += $input->$kread($key);
+ } else {
+ switch ($ktype) {
+ case TType::STRUCT:
+ $class = $kspec['class'];
+ $key = new $class();
+ $xfer += $key->read($input);
+ break;
+ case TType::MAP:
+ $xfer += $this->_readMap($key, $kspec, $input);
+ break;
+ case TType::LST:
+ $xfer += $this->_readList($key, $kspec, $input, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_readList($key, $kspec, $input, true);
+ break;
+ }
+ }
+ if ($vread !== null) {
+ $xfer += $input->$vread($val);
+ } else {
+ switch ($vtype) {
+ case TType::STRUCT:
+ $class = $vspec['class'];
+ $val = new $class();
+ $xfer += $val->read($input);
+ break;
+ case TType::MAP:
+ $xfer += $this->_readMap($val, $vspec, $input);
+ break;
+ case TType::LST:
+ $xfer += $this->_readList($val, $vspec, $input, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_readList($val, $vspec, $input, true);
+ break;
+ }
+ }
+ $var[$key] = $val;
+ }
+ $xfer += $input->readMapEnd();
+
+ return $xfer;
+ }
+
+ private function _readList(&$var, $spec, $input, $set=false)
+ {
+ $xfer = 0;
+ $etype = $spec['etype'];
+ $eread = $vread = null;
+ if (isset(TBase::$tmethod[$etype])) {
+ $eread = 'read'.TBase::$tmethod[$etype];
+ } else {
+ $espec = $spec['elem'];
+ }
+ $var = array();
+ $_etype = $size = 0;
+ if ($set) {
+ $xfer += $input->readSetBegin($_etype, $size);
+ } else {
+ $xfer += $input->readListBegin($_etype, $size);
+ }
+ for ($i = 0; $i < $size; ++$i) {
+ $elem = null;
+ if ($eread !== null) {
+ $xfer += $input->$eread($elem);
+ } else {
+ $espec = $spec['elem'];
+ switch ($etype) {
+ case TType::STRUCT:
+ $class = $espec['class'];
+ $elem = new $class();
+ $xfer += $elem->read($input);
+ break;
+ case TType::MAP:
+ $xfer += $this->_readMap($elem, $espec, $input);
+ break;
+ case TType::LST:
+ $xfer += $this->_readList($elem, $espec, $input, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_readList($elem, $espec, $input, true);
+ break;
+ }
+ }
+ if ($set) {
+ $var[$elem] = true;
+ } else {
+ $var []= $elem;
+ }
+ }
+ if ($set) {
+ $xfer += $input->readSetEnd();
+ } else {
+ $xfer += $input->readListEnd();
+ }
+
+ return $xfer;
+ }
+
+ protected function _read($class, $spec, $input)
+ {
+ $xfer = 0;
+ $fname = null;
+ $ftype = 0;
+ $fid = 0;
+ $xfer += $input->readStructBegin($fname);
+ while (true) {
+ $xfer += $input->readFieldBegin($fname, $ftype, $fid);
+ if ($ftype == TType::STOP) {
+ break;
+ }
+ if (isset($spec[$fid])) {
+ $fspec = $spec[$fid];
+ $var = $fspec['var'];
+ if ($ftype == $fspec['type']) {
+ $xfer = 0;
+ if (isset(TBase::$tmethod[$ftype])) {
+ $func = 'read'.TBase::$tmethod[$ftype];
+ $xfer += $input->$func($this->$var);
+ } else {
+ switch ($ftype) {
+ case TType::STRUCT:
+ $class = $fspec['class'];
+ $this->$var = new $class();
+ $xfer += $this->$var->read($input);
+ break;
+ case TType::MAP:
+ $xfer += $this->_readMap($this->$var, $fspec, $input);
+ break;
+ case TType::LST:
+ $xfer += $this->_readList($this->$var, $fspec, $input, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_readList($this->$var, $fspec, $input, true);
+ break;
+ }
+ }
+ } else {
+ $xfer += $input->skip($ftype);
+ }
+ } else {
+ $xfer += $input->skip($ftype);
+ }
+ $xfer += $input->readFieldEnd();
+ }
+ $xfer += $input->readStructEnd();
+
+ return $xfer;
+ }
+
+ private function _writeMap($var, $spec, $output)
+ {
+ $xfer = 0;
+ $ktype = $spec['ktype'];
+ $vtype = $spec['vtype'];
+ $kwrite = $vwrite = null;
+ if (isset(TBase::$tmethod[$ktype])) {
+ $kwrite = 'write'.TBase::$tmethod[$ktype];
+ } else {
+ $kspec = $spec['key'];
+ }
+ if (isset(TBase::$tmethod[$vtype])) {
+ $vwrite = 'write'.TBase::$tmethod[$vtype];
+ } else {
+ $vspec = $spec['val'];
+ }
+ $xfer += $output->writeMapBegin($ktype, $vtype, count($var));
+ foreach ($var as $key => $val) {
+ if (isset($kwrite)) {
+ $xfer += $output->$kwrite($key);
+ } else {
+ switch ($ktype) {
+ case TType::STRUCT:
+ $xfer += $key->write($output);
+ break;
+ case TType::MAP:
+ $xfer += $this->_writeMap($key, $kspec, $output);
+ break;
+ case TType::LST:
+ $xfer += $this->_writeList($key, $kspec, $output, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_writeList($key, $kspec, $output, true);
+ break;
+ }
+ }
+ if (isset($vwrite)) {
+ $xfer += $output->$vwrite($val);
+ } else {
+ switch ($vtype) {
+ case TType::STRUCT:
+ $xfer += $val->write($output);
+ break;
+ case TType::MAP:
+ $xfer += $this->_writeMap($val, $vspec, $output);
+ break;
+ case TType::LST:
+ $xfer += $this->_writeList($val, $vspec, $output, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_writeList($val, $vspec, $output, true);
+ break;
+ }
+ }
+ }
+ $xfer += $output->writeMapEnd();
+
+ return $xfer;
+ }
+
+ private function _writeList($var, $spec, $output, $set=false)
+ {
+ $xfer = 0;
+ $etype = $spec['etype'];
+ $ewrite = null;
+ if (isset(TBase::$tmethod[$etype])) {
+ $ewrite = 'write'.TBase::$tmethod[$etype];
+ } else {
+ $espec = $spec['elem'];
+ }
+ if ($set) {
+ $xfer += $output->writeSetBegin($etype, count($var));
+ } else {
+ $xfer += $output->writeListBegin($etype, count($var));
+ }
+ foreach ($var as $key => $val) {
+ $elem = $set ? $key : $val;
+ if (isset($ewrite)) {
+ $xfer += $output->$ewrite($elem);
+ } else {
+ switch ($etype) {
+ case TType::STRUCT:
+ $xfer += $elem->write($output);
+ break;
+ case TType::MAP:
+ $xfer += $this->_writeMap($elem, $espec, $output);
+ break;
+ case TType::LST:
+ $xfer += $this->_writeList($elem, $espec, $output, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_writeList($elem, $espec, $output, true);
+ break;
+ }
+ }
+ }
+ if ($set) {
+ $xfer += $output->writeSetEnd();
+ } else {
+ $xfer += $output->writeListEnd();
+ }
+
+ return $xfer;
+ }
+
+ protected function _write($class, $spec, $output)
+ {
+ $xfer = 0;
+ $xfer += $output->writeStructBegin($class);
+ foreach ($spec as $fid => $fspec) {
+ $var = $fspec['var'];
+ if ($this->$var !== null) {
+ $ftype = $fspec['type'];
+ $xfer += $output->writeFieldBegin($var, $ftype, $fid);
+ if (isset(TBase::$tmethod[$ftype])) {
+ $func = 'write'.TBase::$tmethod[$ftype];
+ $xfer += $output->$func($this->$var);
+ } else {
+ switch ($ftype) {
+ case TType::STRUCT:
+ $xfer += $this->$var->write($output);
+ break;
+ case TType::MAP:
+ $xfer += $this->_writeMap($this->$var, $fspec, $output);
+ break;
+ case TType::LST:
+ $xfer += $this->_writeList($this->$var, $fspec, $output, false);
+ break;
+ case TType::SET:
+ $xfer += $this->_writeList($this->$var, $fspec, $output, true);
+ break;
+ }
+ }
+ $xfer += $output->writeFieldEnd();
+ }
+ }
+ $xfer += $output->writeFieldStop();
+ $xfer += $output->writeStructEnd();
+
+ return $xfer;
+ }
+}
+
+class TApplicationException extends TException
+{
+ static $_TSPEC =
+ array(1 => array('var' => 'message',
+ 'type' => TType::STRING),
+ 2 => array('var' => 'code',
+ 'type' => TType::I32));
+
+ const UNKNOWN = 0;
+ const UNKNOWN_METHOD = 1;
+ const INVALID_MESSAGE_TYPE = 2;
+ const WRONG_METHOD_NAME = 3;
+ const BAD_SEQUENCE_ID = 4;
+ const MISSING_RESULT = 5;
+ const INTERNAL_ERROR = 6;
+ const PROTOCOL_ERROR = 7;
+
+ public function __construct($message=null, $code=0)
+ {
+ parent::__construct($message, $code);
+ }
+
+ public function read($output)
+ {
+ return $this->_read('TApplicationException', self::$_TSPEC, $output);
+ }
+
+ public function write($output)
+ {
+ $xfer = 0;
+ $xfer += $output->writeStructBegin('TApplicationException');
+ if ($message = $this->getMessage()) {
+ $xfer += $output->writeFieldBegin('message', TType::STRING, 1);
+ $xfer += $output->writeString($message);
+ $xfer += $output->writeFieldEnd();
+ }
+ if ($code = $this->getCode()) {
+ $xfer += $output->writeFieldBegin('type', TType::I32, 2);
+ $xfer += $output->writeI32($code);
+ $xfer += $output->writeFieldEnd();
+ }
+ $xfer += $output->writeFieldStop();
+ $xfer += $output->writeStructEnd();
+
+ return $xfer;
+ }
+}
+
+/**
+ * Set global THRIFT ROOT automatically via inclusion here
+ */
+if (!isset($GLOBALS['THRIFT_ROOT'])) {
+ $GLOBALS['THRIFT_ROOT'] = dirname(__FILE__);
+}
+include_once $GLOBALS['THRIFT_ROOT'].'/protocol/TProtocol.php';
+include_once $GLOBALS['THRIFT_ROOT'].'/transport/TTransport.php';
+include_once $GLOBALS['THRIFT_ROOT'].'/TStringUtils.php';
diff --git a/src/jaegertracing/thrift/lib/php/src/autoload.php b/src/jaegertracing/thrift/lib/php/src/autoload.php
new file mode 100644
index 000000000..85bd797a3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/src/autoload.php
@@ -0,0 +1,51 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift
+ */
+
+/**
+ * Include this file if you wish to use autoload with your PHP generated Thrift
+ * code. The generated code will *not* include any defined Thrift classes by
+ * default, except for the service interfaces. The generated code will populate
+ * values into $GLOBALS['THRIFT_AUTOLOAD'] which can be used by the autoload
+ * method below. If you have your own autoload system already in place, rename your
+ * __autoload function to something else and then do:
+ * $GLOBALS['AUTOLOAD_HOOKS'][] = 'my_autoload_func';
+ *
+ * Generate this code using the --gen php:autoload Thrift generator flag.
+ */
+
+$GLOBALS['THRIFT_AUTOLOAD'] = array();
+$GLOBALS['AUTOLOAD_HOOKS'] = array();
+
+if (!function_exists('__autoload')) {
+ function __autoload($class)
+ {
+ global $THRIFT_AUTOLOAD;
+ $classl = strtolower($class);
+ if (isset($THRIFT_AUTOLOAD[$classl])) {
+ include_once $GLOBALS['THRIFT_ROOT'].'/packages/'.$THRIFT_AUTOLOAD[$classl];
+ } elseif (!empty($GLOBALS['AUTOLOAD_HOOKS'])) {
+ foreach ($GLOBALS['AUTOLOAD_HOOKS'] as $hook) {
+ $hook($class);
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/src/ext/thrift_protocol/config.m4 b/src/jaegertracing/thrift/lib/php/src/ext/thrift_protocol/config.m4
new file mode 100644
index 000000000..e2138c8c2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/src/ext/thrift_protocol/config.m4
@@ -0,0 +1,34 @@
+dnl Copyright (C) 2009 Facebook
+dnl Copying and distribution of this file, with or without modification,
+dnl are permitted in any medium without royalty provided the copyright
+dnl notice and this notice are preserved.
+dnl
+dnl Licensed to the Apache Software Foundation (ASF) under one
+dnl or more contributor license agreements. See the NOTICE file
+dnl distributed with this work for additional information
+dnl regarding copyright ownership. The ASF licenses this file
+dnl to you under the Apache License, Version 2.0 (the
+dnl "License"); you may not use this file except in compliance
+dnl with the License. You may obtain a copy of the License at
+dnl
+dnl http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing,
+dnl software distributed under the License is distributed on an
+dnl "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+dnl KIND, either express or implied. See the License for the
+dnl specific language governing permissions and limitations
+dnl under the License.
+
+PHP_ARG_ENABLE(thrift_protocol, whether to enable the thrift_protocol extension,
+[ --enable-thrift_protocol Enable the thrift_protocol extension])
+
+if test "$PHP_THRIFT_PROTOCOL" != "no"; then
+ PHP_REQUIRE_CXX()
+ PHP_ADD_LIBRARY_WITH_PATH(stdc++, "", THRIFT_PROTOCOL_SHARED_LIBADD)
+ PHP_SUBST(THRIFT_PROTOCOL_SHARED_LIBADD)
+ CXXFLAGS="$CXXFLAGS -std=c++11"
+
+ PHP_NEW_EXTENSION(thrift_protocol, php_thrift_protocol.cpp, $ext_shared)
+fi
+
diff --git a/src/jaegertracing/thrift/lib/php/src/ext/thrift_protocol/config.w32 b/src/jaegertracing/thrift/lib/php/src/ext/thrift_protocol/config.w32
new file mode 100644
index 000000000..e0f273f95
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/src/ext/thrift_protocol/config.w32
@@ -0,0 +1,8 @@
+// $Id: config.w32 250404 2008-01-11 13:37:24Z rrichards $
+// vim:ft=javascript
+
+ARG_WITH("thrift_protocol", "whether to enable the thrift_protocol extension", "yes");
+
+if (PHP_THRIFT_PROTOCOL == "yes"){
+ EXTENSION("thrift_protocol", "php_thrift_protocol.cpp")
+}
diff --git a/src/jaegertracing/thrift/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp b/src/jaegertracing/thrift/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp
new file mode 100644
index 000000000..e152d08a3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp
@@ -0,0 +1,1172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "zend_interfaces.h"
+#include "zend_exceptions.h"
+#include "php_thrift_protocol.h"
+
+#if PHP_VERSION_ID >= 70000
+
+#include <sys/types.h>
+#include <arpa/inet.h>
+
+#include <cstdint>
+#include <stdexcept>
+#include <algorithm>
+
+#ifndef bswap_64
+#define bswap_64(x) (((uint64_t)(x) << 56) | \
+ (((uint64_t)(x) << 40) & 0xff000000000000ULL) | \
+ (((uint64_t)(x) << 24) & 0xff0000000000ULL) | \
+ (((uint64_t)(x) << 8) & 0xff00000000ULL) | \
+ (((uint64_t)(x) >> 8) & 0xff000000ULL) | \
+ (((uint64_t)(x) >> 24) & 0xff0000ULL) | \
+ (((uint64_t)(x) >> 40) & 0xff00ULL) | \
+ ((uint64_t)(x) >> 56))
+#endif
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htonll(x) bswap_64(x)
+#define ntohll(x) bswap_64(x)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define htonll(x) x
+#define ntohll(x) x
+#else
+#error Unknown __BYTE_ORDER
+#endif
+
+enum TType {
+ T_STOP = 0,
+ T_VOID = 1,
+ T_BOOL = 2,
+ T_BYTE = 3,
+ T_I08 = 3,
+ T_I16 = 6,
+ T_I32 = 8,
+ T_U64 = 9,
+ T_I64 = 10,
+ T_DOUBLE = 4,
+ T_STRING = 11,
+ T_UTF7 = 11,
+ T_STRUCT = 12,
+ T_MAP = 13,
+ T_SET = 14,
+ T_LIST = 15,
+ T_UTF8 = 16,
+ T_UTF16 = 17
+};
+
+const int32_t VERSION_MASK = 0xffff0000;
+const int32_t VERSION_1 = 0x80010000;
+const int8_t T_CALL = 1;
+const int8_t T_REPLY = 2;
+const int8_t T_EXCEPTION = 3;
+// tprotocolexception
+const int INVALID_DATA = 1;
+const int BAD_VERSION = 4;
+
+static zend_function_entry thrift_protocol_functions[] = {
+ PHP_FE(thrift_protocol_write_binary, nullptr)
+ PHP_FE(thrift_protocol_read_binary, nullptr)
+ PHP_FE(thrift_protocol_read_binary_after_message_begin, nullptr)
+ {nullptr, nullptr, nullptr}
+};
+
+zend_module_entry thrift_protocol_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "thrift_protocol",
+ thrift_protocol_functions,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ "1.0",
+ STANDARD_MODULE_PROPERTIES
+};
+
+#ifdef COMPILE_DL_THRIFT_PROTOCOL
+ZEND_GET_MODULE(thrift_protocol)
+#endif
+
+class PHPExceptionWrapper : public std::exception {
+public:
+ PHPExceptionWrapper(zval* _ex) throw() {
+ ZVAL_COPY(&ex, _ex);
+ snprintf(_what, 40, "PHP exception zval=%p", _ex);
+ }
+
+ PHPExceptionWrapper(zend_object* _exobj) throw() {
+ ZVAL_OBJ(&ex, _exobj);
+ snprintf(_what, 40, "PHP exception zval=%p", _exobj);
+ }
+ ~PHPExceptionWrapper() throw() {
+ zval_dtor(&ex);
+ }
+
+ const char* what() const throw() {
+ return _what;
+ }
+ operator zval*() const throw() {
+ return const_cast<zval*>(&ex);
+ } // Zend API doesn't do 'const'...
+protected:
+ zval ex;
+ char _what[40];
+} ;
+
+class PHPTransport {
+protected:
+ PHPTransport(zval* _p, size_t _buffer_size) {
+ assert(Z_TYPE_P(_p) == IS_OBJECT);
+
+ ZVAL_UNDEF(&t);
+
+ buffer = reinterpret_cast<char*>(emalloc(_buffer_size));
+ buffer_ptr = buffer;
+ buffer_used = 0;
+ buffer_size = _buffer_size;
+
+ // Get the transport for the passed protocol
+ zval gettransport;
+ ZVAL_STRING(&gettransport, "getTransport");
+ call_user_function(nullptr, _p, &gettransport, &t, 0, nullptr);
+
+ zval_dtor(&gettransport);
+
+ if (EG(exception)) {
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
+ throw PHPExceptionWrapper(ex);
+ }
+
+ assert(Z_TYPE(t) == IS_OBJECT);
+ }
+
+ ~PHPTransport() {
+ efree(buffer);
+ zval_dtor(&t);
+ }
+
+ char* buffer;
+ char* buffer_ptr;
+ size_t buffer_used;
+ size_t buffer_size;
+
+ zval t;
+};
+
+
+class PHPOutputTransport : public PHPTransport {
+public:
+ PHPOutputTransport(zval* _p, size_t _buffer_size = 8192) : PHPTransport(_p, _buffer_size) { }
+ ~PHPOutputTransport() { }
+
+ void write(const char* data, size_t len) {
+ if ((len + buffer_used) > buffer_size) {
+ internalFlush();
+ }
+ if (len > buffer_size) {
+ directWrite(data, len);
+ } else {
+ memcpy(buffer_ptr, data, len);
+ buffer_used += len;
+ buffer_ptr += len;
+ }
+ }
+
+ void writeI64(int64_t i) {
+ i = htonll(i);
+ write((const char*)&i, 8);
+ }
+
+ void writeU32(uint32_t i) {
+ i = htonl(i);
+ write((const char*)&i, 4);
+ }
+
+ void writeI32(int32_t i) {
+ i = htonl(i);
+ write((const char*)&i, 4);
+ }
+
+ void writeI16(int16_t i) {
+ i = htons(i);
+ write((const char*)&i, 2);
+ }
+
+ void writeI8(int8_t i) {
+ write((const char*)&i, 1);
+ }
+
+ void writeString(const char* str, size_t len) {
+ writeU32(len);
+ write(str, len);
+ }
+
+ void flush() {
+ internalFlush();
+ directFlush();
+ }
+
+protected:
+ void internalFlush() {
+ if (buffer_used) {
+ directWrite(buffer, buffer_used);
+ buffer_ptr = buffer;
+ buffer_used = 0;
+ }
+ }
+ void directFlush() {
+ zval ret, flushfn;
+ ZVAL_NULL(&ret);
+ ZVAL_STRING(&flushfn, "flush");
+
+ call_user_function(EG(function_table), &(this->t), &flushfn, &ret, 0, nullptr);
+ zval_dtor(&flushfn);
+ zval_dtor(&ret);
+ if (EG(exception)) {
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
+ throw PHPExceptionWrapper(ex);
+ }
+ }
+ void directWrite(const char* data, size_t len) {
+ zval args[1], ret, writefn;
+
+ ZVAL_STRING(&writefn, "write");
+ ZVAL_STRINGL(&args[0], data, len);
+
+ ZVAL_NULL(&ret);
+ call_user_function(EG(function_table), &(this->t), &writefn, &ret, 1, args);
+
+ zval_dtor(&writefn);
+ zval_dtor(&ret);
+ zval_dtor(&args[0]);
+
+ if (EG(exception)) {
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
+ throw PHPExceptionWrapper(ex);
+ }
+ }
+};
+
+class PHPInputTransport : public PHPTransport {
+public:
+ PHPInputTransport(zval* _p, size_t _buffer_size = 8192) : PHPTransport(_p, _buffer_size) {
+ }
+
+ ~PHPInputTransport() {
+ put_back();
+ }
+
+ void put_back() {
+ if (buffer_used) {
+ zval args[1], ret, putbackfn;
+ ZVAL_STRINGL(&args[0], buffer_ptr, buffer_used);
+ ZVAL_STRING(&putbackfn, "putBack");
+ ZVAL_NULL(&ret);
+
+ call_user_function(EG(function_table), &(this->t), &putbackfn, &ret, 1, args);
+
+ zval_dtor(&putbackfn);
+ zval_dtor(&ret);
+ zval_dtor(&args[0]);
+ if (EG(exception)) {
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
+ throw PHPExceptionWrapper(ex);
+ }
+ }
+ buffer_used = 0;
+ buffer_ptr = buffer;
+ }
+
+ void skip(size_t len) {
+ while (len) {
+ size_t chunk_size = (std::min)(len, buffer_used);
+ if (chunk_size) {
+ buffer_ptr = reinterpret_cast<char*>(buffer_ptr) + chunk_size;
+ buffer_used -= chunk_size;
+ len -= chunk_size;
+ }
+ if (! len) break;
+ refill();
+ }
+ }
+
+ void readBytes(void* buf, size_t len) {
+ while (len) {
+ size_t chunk_size = (std::min)(len, buffer_used);
+ if (chunk_size) {
+ memcpy(buf, buffer_ptr, chunk_size);
+ buffer_ptr = reinterpret_cast<char*>(buffer_ptr) + chunk_size;
+ buffer_used -= chunk_size;
+ buf = reinterpret_cast<char*>(buf) + chunk_size;
+ len -= chunk_size;
+ }
+ if (! len) break;
+ refill();
+ }
+ }
+
+ int8_t readI8() {
+ int8_t c;
+ readBytes(&c, 1);
+ return c;
+ }
+
+ int16_t readI16() {
+ int16_t c;
+ readBytes(&c, 2);
+ return (int16_t)ntohs(c);
+ }
+
+ uint32_t readU32() {
+ uint32_t c;
+ readBytes(&c, 4);
+ return (uint32_t)ntohl(c);
+ }
+
+ int32_t readI32() {
+ int32_t c;
+ readBytes(&c, 4);
+ return (int32_t)ntohl(c);
+ }
+
+protected:
+ void refill() {
+ assert(buffer_used == 0);
+ zval retval;
+ zval args[1];
+ zval funcname;
+
+ ZVAL_NULL(&retval);
+ ZVAL_LONG(&args[0], buffer_size);
+
+ ZVAL_STRING(&funcname, "read");
+
+ call_user_function(EG(function_table), &(this->t), &funcname, &retval, 1, args);
+ zval_dtor(&args[0]);
+ zval_dtor(&funcname);
+
+ if (EG(exception)) {
+ zval_dtor(&retval);
+
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
+ throw PHPExceptionWrapper(ex);
+ }
+
+ buffer_used = Z_STRLEN(retval);
+ memcpy(buffer, Z_STRVAL(retval), buffer_used);
+
+ zval_dtor(&retval);
+
+ buffer_ptr = buffer;
+ }
+
+};
+
+static
+void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec);
+static
+void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec);
+static
+void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval* value, HashTable* fieldspec);
+static inline
+bool ttype_is_scalar(int8_t t);
+
+// Create a PHP object given a typename and call the ctor, optionally passing up to 2 arguments
+static
+void createObject(const char* obj_typename, zval* return_value, int nargs = 0, zval* arg1 = nullptr, zval* arg2 = nullptr) {
+ /* is there a better way to do that on the stack ? */
+ zend_string *obj_name = zend_string_init(obj_typename, strlen(obj_typename), 0);
+ zend_class_entry* ce = zend_fetch_class(obj_name, ZEND_FETCH_CLASS_DEFAULT);
+ zend_string_release(obj_name);
+
+ if (! ce) {
+ php_error_docref(nullptr, E_ERROR, "Class %s does not exist", obj_typename);
+ RETURN_NULL();
+ }
+
+ object_and_properties_init(return_value, ce, nullptr);
+ zend_function* constructor = zend_std_get_constructor(Z_OBJ_P(return_value));
+ zval ctor_rv;
+ zend_call_method(return_value, ce, &constructor, NULL, 0, &ctor_rv, nargs, arg1, arg2);
+ zval_dtor(&ctor_rv);
+ if (EG(exception)) {
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
+ throw PHPExceptionWrapper(ex);
+ }
+}
+
+static
+void throw_tprotocolexception(const char* what, long errorcode) {
+ zval zwhat, zerrorcode;
+
+ ZVAL_STRING(&zwhat, what);
+ ZVAL_LONG(&zerrorcode, errorcode);
+
+ zval ex;
+ createObject("\\Thrift\\Exception\\TProtocolException", &ex, 2, &zwhat, &zerrorcode);
+
+ zval_dtor(&zwhat);
+ zval_dtor(&zerrorcode);
+
+ throw PHPExceptionWrapper(&ex);
+}
+
+// Sets EG(exception), call this and then RETURN_NULL();
+static
+void throw_zend_exception_from_std_exception(const std::exception& ex) {
+ zend_throw_exception(zend_exception_get_default(), const_cast<char*>(ex.what()), 0);
+}
+
+static
+void skip_element(long thrift_typeID, PHPInputTransport& transport) {
+ switch (thrift_typeID) {
+ case T_STOP:
+ case T_VOID:
+ return;
+ case T_STRUCT:
+ while (true) {
+ int8_t ttype = transport.readI8(); // get field type
+ if (ttype == T_STOP) break;
+ transport.skip(2); // skip field number, I16
+ skip_element(ttype, transport); // skip field payload
+ }
+ return;
+ case T_BOOL:
+ case T_BYTE:
+ transport.skip(1);
+ return;
+ case T_I16:
+ transport.skip(2);
+ return;
+ case T_I32:
+ transport.skip(4);
+ return;
+ case T_U64:
+ case T_I64:
+ case T_DOUBLE:
+ transport.skip(8);
+ return;
+ //case T_UTF7: // aliases T_STRING
+ case T_UTF8:
+ case T_UTF16:
+ case T_STRING: {
+ uint32_t len = transport.readU32();
+ transport.skip(len);
+ } return;
+ case T_MAP: {
+ int8_t keytype = transport.readI8();
+ int8_t valtype = transport.readI8();
+ uint32_t size = transport.readU32();
+ for (uint32_t i = 0; i < size; ++i) {
+ skip_element(keytype, transport);
+ skip_element(valtype, transport);
+ }
+ } return;
+ case T_LIST:
+ case T_SET: {
+ int8_t valtype = transport.readI8();
+ uint32_t size = transport.readU32();
+ for (uint32_t i = 0; i < size; ++i) {
+ skip_element(valtype, transport);
+ }
+ } return;
+ };
+
+ char errbuf[128];
+ sprintf(errbuf, "Unknown thrift typeID %ld", thrift_typeID);
+ throw_tprotocolexception(errbuf, INVALID_DATA);
+}
+
+static inline
+bool zval_is_bool(zval* v) {
+ return Z_TYPE_P(v) == IS_TRUE || Z_TYPE_P(v) == IS_FALSE;
+}
+
+static
+void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval* return_value, HashTable* fieldspec) {
+ ZVAL_NULL(return_value);
+
+ switch (thrift_typeID) {
+ case T_STOP:
+ case T_VOID:
+ RETURN_NULL();
+ return;
+ case T_STRUCT: {
+ zval* val_ptr = zend_hash_str_find(fieldspec, "class", sizeof("class")-1);
+ if (val_ptr == nullptr) {
+ throw_tprotocolexception("no class type in spec", INVALID_DATA);
+ skip_element(T_STRUCT, transport);
+ RETURN_NULL();
+ }
+
+ char* structType = Z_STRVAL_P(val_ptr);
+ // Create an object in PHP userland based on our spec
+ createObject(structType, return_value);
+ if (Z_TYPE_P(return_value) == IS_NULL) {
+ // unable to create class entry
+ skip_element(T_STRUCT, transport);
+ RETURN_NULL();
+ }
+
+ zval* spec = zend_read_static_property(Z_OBJCE_P(return_value), "_TSPEC", sizeof("_TSPEC")-1, false);
+ ZVAL_DEREF(spec);
+ if (EG(exception)) {
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
+ throw PHPExceptionWrapper(ex);
+ }
+ if (Z_TYPE_P(spec) != IS_ARRAY) {
+ char errbuf[128];
+ snprintf(errbuf, 128, "spec for %s is wrong type: %d\n", structType, Z_TYPE_P(spec));
+ throw_tprotocolexception(errbuf, INVALID_DATA);
+ RETURN_NULL();
+ }
+ binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec));
+ return;
+ } break;
+ case T_BOOL: {
+ uint8_t c;
+ transport.readBytes(&c, 1);
+ RETURN_BOOL(c != 0);
+ }
+ //case T_I08: // same numeric value as T_BYTE
+ case T_BYTE: {
+ uint8_t c;
+ transport.readBytes(&c, 1);
+ RETURN_LONG((int8_t)c);
+ }
+ case T_I16: {
+ uint16_t c;
+ transport.readBytes(&c, 2);
+ RETURN_LONG((int16_t)ntohs(c));
+ }
+ case T_I32: {
+ uint32_t c;
+ transport.readBytes(&c, 4);
+ RETURN_LONG((int32_t)ntohl(c));
+ }
+ case T_U64:
+ case T_I64: {
+ uint64_t c;
+ transport.readBytes(&c, 8);
+ RETURN_LONG((int64_t)ntohll(c));
+ }
+ case T_DOUBLE: {
+ union {
+ uint64_t c;
+ double d;
+ } a;
+ transport.readBytes(&(a.c), 8);
+ a.c = ntohll(a.c);
+ RETURN_DOUBLE(a.d);
+ }
+ //case T_UTF7: // aliases T_STRING
+ case T_UTF8:
+ case T_UTF16:
+ case T_STRING: {
+ uint32_t size = transport.readU32();
+ if (size) {
+ char strbuf[size+1];
+ transport.readBytes(strbuf, size);
+ strbuf[size] = '\0';
+ ZVAL_STRINGL(return_value, strbuf, size);
+ } else {
+ ZVAL_EMPTY_STRING(return_value);
+ }
+ return;
+ }
+ case T_MAP: { // array of key -> value
+ uint8_t types[2];
+ transport.readBytes(types, 2);
+ uint32_t size = transport.readU32();
+ array_init(return_value);
+
+ zval *val_ptr;
+ val_ptr = zend_hash_str_find(fieldspec, "key", sizeof("key")-1);
+ HashTable* keyspec = Z_ARRVAL_P(val_ptr);
+ val_ptr = zend_hash_str_find(fieldspec, "val", sizeof("val")-1);
+ HashTable* valspec = Z_ARRVAL_P(val_ptr);
+
+ for (uint32_t s = 0; s < size; ++s) {
+ zval key, value;
+
+ binary_deserialize(types[0], transport, &key, keyspec);
+ binary_deserialize(types[1], transport, &value, valspec);
+ if (Z_TYPE(key) == IS_LONG) {
+ zend_hash_index_update(Z_ARR_P(return_value), Z_LVAL(key), &value);
+ } else {
+ if (Z_TYPE(key) != IS_STRING) convert_to_string(&key);
+ zend_symtable_update(Z_ARR_P(return_value), Z_STR(key), &value);
+ }
+ zval_dtor(&key);
+ }
+ return; // return_value already populated
+ }
+ case T_LIST: { // array with autogenerated numeric keys
+ int8_t type = transport.readI8();
+ uint32_t size = transport.readU32();
+ zval *val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1);
+ HashTable* elemspec = Z_ARRVAL_P(val_ptr);
+
+ array_init(return_value);
+ for (uint32_t s = 0; s < size; ++s) {
+ zval value;
+ binary_deserialize(type, transport, &value, elemspec);
+ zend_hash_next_index_insert(Z_ARR_P(return_value), &value);
+ }
+ return;
+ }
+ case T_SET: { // array of key -> TRUE
+ uint8_t type;
+ uint32_t size;
+ transport.readBytes(&type, 1);
+ transport.readBytes(&size, 4);
+ size = ntohl(size);
+ zval *val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1);
+ HashTable* elemspec = Z_ARRVAL_P(val_ptr);
+
+ array_init(return_value);
+
+ for (uint32_t s = 0; s < size; ++s) {
+ zval key, value;
+ ZVAL_TRUE(&value);
+
+ binary_deserialize(type, transport, &key, elemspec);
+
+ if (Z_TYPE(key) == IS_LONG) {
+ zend_hash_index_update(Z_ARR_P(return_value), Z_LVAL(key), &value);
+ } else {
+ if (Z_TYPE(key) != IS_STRING) convert_to_string(&key);
+ zend_symtable_update(Z_ARR_P(return_value), Z_STR(key), &value);
+ }
+ zval_dtor(&key);
+ }
+ return;
+ }
+ };
+
+ char errbuf[128];
+ sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID);
+ throw_tprotocolexception(errbuf, INVALID_DATA);
+}
+
+static
+void binary_serialize_hashtable_key(int8_t keytype, PHPOutputTransport& transport, HashTable* ht, HashPosition& ht_pos, HashTable* spec) {
+ bool keytype_is_numeric = (!((keytype == T_STRING) || (keytype == T_UTF8) || (keytype == T_UTF16)));
+
+ zend_string* key;
+ uint key_len;
+ long index = 0;
+
+ zval z;
+
+ int res = zend_hash_get_current_key_ex(ht, &key, (zend_ulong*)&index, &ht_pos);
+ if (res == HASH_KEY_IS_STRING) {
+ ZVAL_STR_COPY(&z, key);
+ } else {
+ ZVAL_LONG(&z, index);
+ }
+ binary_serialize(keytype, transport, &z, spec);
+ zval_dtor(&z);
+}
+
+static
+void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval* value, HashTable* fieldspec) {
+ if (value) {
+ ZVAL_DEREF(value);
+ }
+ // At this point the typeID (and field num, if applicable) should've already been written to the output so all we need to do is write the payload.
+ switch (thrift_typeID) {
+ case T_STOP:
+ case T_VOID:
+ return;
+ case T_STRUCT: {
+ if (Z_TYPE_P(value) != IS_OBJECT) {
+ throw_tprotocolexception("Attempt to send non-object type as a T_STRUCT", INVALID_DATA);
+ }
+ zval* spec = zend_read_static_property(Z_OBJCE_P(value), "_TSPEC", sizeof("_TSPEC")-1, true);
+ if (spec && Z_TYPE_P(spec) == IS_REFERENCE) {
+ ZVAL_DEREF(spec);
+ }
+ if (!spec || Z_TYPE_P(spec) != IS_ARRAY) {
+ throw_tprotocolexception("Attempt to send non-Thrift object as a T_STRUCT", INVALID_DATA);
+ }
+ binary_serialize_spec(value, transport, Z_ARRVAL_P(spec));
+ } return;
+ case T_BOOL:
+ if (!zval_is_bool(value)) convert_to_boolean(value);
+ transport.writeI8(Z_TYPE_INFO_P(value) == IS_TRUE ? 1 : 0);
+ return;
+ case T_BYTE:
+ if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
+ transport.writeI8(Z_LVAL_P(value));
+ return;
+ case T_I16:
+ if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
+ transport.writeI16(Z_LVAL_P(value));
+ return;
+ case T_I32:
+ if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
+ transport.writeI32(Z_LVAL_P(value));
+ return;
+ case T_I64:
+ case T_U64: {
+ int64_t l_data;
+#if defined(_LP64) || defined(_WIN64)
+ if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
+ l_data = Z_LVAL_P(value);
+#else
+ if (Z_TYPE_P(value) != IS_DOUBLE) convert_to_double(value);
+ l_data = (int64_t)Z_DVAL_P(value);
+#endif
+ transport.writeI64(l_data);
+ } return;
+ case T_DOUBLE: {
+ union {
+ int64_t c;
+ double d;
+ } a;
+ if (Z_TYPE_P(value) != IS_DOUBLE) convert_to_double(value);
+ a.d = Z_DVAL_P(value);
+ transport.writeI64(a.c);
+ } return;
+ case T_UTF8:
+ case T_UTF16:
+ case T_STRING:
+ if (Z_TYPE_P(value) != IS_STRING) convert_to_string(value);
+ transport.writeString(Z_STRVAL_P(value), Z_STRLEN_P(value));
+ return;
+ case T_MAP: {
+ if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value);
+ if (Z_TYPE_P(value) != IS_ARRAY) {
+ throw_tprotocolexception("Attempt to send an incompatible type as an array (T_MAP)", INVALID_DATA);
+ }
+ HashTable* ht = Z_ARRVAL_P(value);
+ zval* val_ptr;
+
+ val_ptr = zend_hash_str_find(fieldspec, "ktype", sizeof("ktype")-1);
+ if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+ uint8_t keytype = Z_LVAL_P(val_ptr);
+ transport.writeI8(keytype);
+ val_ptr = zend_hash_str_find(fieldspec, "vtype", sizeof("vtype")-1);
+ if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+ uint8_t valtype = Z_LVAL_P(val_ptr);
+ transport.writeI8(valtype);
+
+ val_ptr = zend_hash_str_find(fieldspec, "val", sizeof("val")-1);
+ HashTable* valspec = Z_ARRVAL_P(val_ptr);
+ HashTable* keyspec = Z_ARRVAL_P(zend_hash_str_find(fieldspec, "key", sizeof("key")-1));
+
+ transport.writeI32(zend_hash_num_elements(ht));
+ HashPosition key_ptr;
+ for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
+ (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
+ zend_hash_move_forward_ex(ht, &key_ptr)) {
+ binary_serialize_hashtable_key(keytype, transport, ht, key_ptr, keyspec);
+ binary_serialize(valtype, transport, val_ptr, valspec);
+ }
+ } return;
+ case T_LIST: {
+ if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value);
+ if (Z_TYPE_P(value) != IS_ARRAY) {
+ throw_tprotocolexception("Attempt to send an incompatible type as an array (T_LIST)", INVALID_DATA);
+ }
+ HashTable* ht = Z_ARRVAL_P(value);
+ zval* val_ptr;
+
+ val_ptr = zend_hash_str_find(fieldspec, "etype", sizeof("etype")-1);
+ if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+ uint8_t valtype = Z_LVAL_P(val_ptr);
+ transport.writeI8(valtype);
+
+ val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1);
+ HashTable* valspec = Z_ARRVAL_P(val_ptr);
+
+ transport.writeI32(zend_hash_num_elements(ht));
+ HashPosition key_ptr;
+ for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
+ (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
+ zend_hash_move_forward_ex(ht, &key_ptr)) {
+ binary_serialize(valtype, transport, val_ptr, valspec);
+ }
+ } return;
+ case T_SET: {
+ if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value);
+ if (Z_TYPE_P(value) != IS_ARRAY) {
+ throw_tprotocolexception("Attempt to send an incompatible type as an array (T_SET)", INVALID_DATA);
+ }
+ HashTable* ht = Z_ARRVAL_P(value);
+ zval* val_ptr;
+
+ val_ptr = zend_hash_str_find(fieldspec, "etype", sizeof("etype")-1);
+ HashTable* spec = Z_ARRVAL_P(zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1));
+ if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+ uint8_t keytype = Z_LVAL_P(val_ptr);
+ transport.writeI8(keytype);
+
+ transport.writeI32(zend_hash_num_elements(ht));
+ HashPosition key_ptr;
+ if(ttype_is_scalar(keytype)){
+ for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
+ (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
+ zend_hash_move_forward_ex(ht, &key_ptr)) {
+ binary_serialize_hashtable_key(keytype, transport, ht, key_ptr, spec);
+ }
+ } else {
+ for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
+ (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
+ zend_hash_move_forward_ex(ht, &key_ptr)) {
+ binary_serialize(keytype, transport, val_ptr, spec);
+ }
+ }
+ } return;
+ };
+
+ char errbuf[128];
+ snprintf(errbuf, 128, "Unknown thrift typeID %d", thrift_typeID);
+ throw_tprotocolexception(errbuf, INVALID_DATA);
+}
+
+static
+void protocol_writeMessageBegin(zval* transport, zend_string* method_name, int32_t msgtype, int32_t seqID) {
+ zval args[3];
+ zval ret;
+ zval writeMessagefn;
+
+ ZVAL_STR_COPY(&args[0], method_name);
+ ZVAL_LONG(&args[1], msgtype);
+ ZVAL_LONG(&args[2], seqID);
+ ZVAL_NULL(&ret);
+ ZVAL_STRING(&writeMessagefn, "writeMessageBegin");
+
+ call_user_function(EG(function_table), transport, &writeMessagefn, &ret, 3, args);
+
+ zval_dtor(&writeMessagefn);
+ zval_dtor(&args[2]); zval_dtor(&args[1]); zval_dtor(&args[0]);
+ zval_dtor(&ret);
+ if (EG(exception)) {
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
+ throw PHPExceptionWrapper(ex);
+ }
+}
+
+static inline
+bool ttype_is_int(int8_t t) {
+ return ((t == T_BYTE) || ((t >= T_I16) && (t <= T_I64)));
+}
+static inline
+bool ttype_is_scalar(int8_t t) {
+ return !((t == T_STRUCT) || ( t== T_MAP) || (t == T_SET) || (t == T_LIST));
+}
+
+static inline
+bool ttypes_are_compatible(int8_t t1, int8_t t2) {
+ // Integer types of different widths are considered compatible;
+ // otherwise the typeID must match.
+ return ((t1 == t2) || (ttype_is_int(t1) && ttype_is_int(t2)));
+}
+
+//is used to validate objects before serialization and after deserialization. For now, only required fields are validated.
+static
+void validate_thrift_object(zval* object) {
+ zend_class_entry* object_class_entry = Z_OBJCE_P(object);
+ zval* is_validate = zend_read_static_property(object_class_entry, "isValidate", sizeof("isValidate")-1, true);
+ if (is_validate) {
+ ZVAL_DEREF(is_validate);
+ }
+ zval* spec = zend_read_static_property(object_class_entry, "_TSPEC", sizeof("_TSPEC")-1, true);
+ if (spec) {
+ ZVAL_DEREF(spec);
+ }
+ HashPosition key_ptr;
+ zval* val_ptr;
+
+ if (is_validate && Z_TYPE_INFO_P(is_validate) == IS_TRUE) {
+ for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(spec), &key_ptr);
+ (val_ptr = zend_hash_get_current_data_ex(Z_ARRVAL_P(spec), &key_ptr)) != nullptr;
+ zend_hash_move_forward_ex(Z_ARRVAL_P(spec), &key_ptr)) {
+
+ zend_ulong fieldno;
+ if (zend_hash_get_current_key_ex(Z_ARRVAL_P(spec), nullptr, &fieldno, &key_ptr) != HASH_KEY_IS_LONG) {
+ throw_tprotocolexception("Bad keytype in TSPEC (expected 'long')", INVALID_DATA);
+ return;
+ }
+ HashTable* fieldspec = Z_ARRVAL_P(val_ptr);
+
+ // field name
+ zval* zvarname = zend_hash_str_find(fieldspec, "var", sizeof("var")-1);
+ char* varname = Z_STRVAL_P(zvarname);
+
+ zval* is_required = zend_hash_str_find(fieldspec, "isRequired", sizeof("isRequired")-1);
+ zval rv;
+ zval* prop = zend_read_property(object_class_entry, object, varname, strlen(varname), false, &rv);
+
+ if (Z_TYPE_INFO_P(is_required) == IS_TRUE && Z_TYPE_P(prop) == IS_NULL) {
+ char errbuf[128];
+ snprintf(errbuf, 128, "Required field %s.%s is unset!", ZSTR_VAL(object_class_entry->name), varname);
+ throw_tprotocolexception(errbuf, INVALID_DATA);
+ }
+ }
+ }
+}
+
+static
+void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec) {
+ // SET and LIST have 'elem' => array('type', [optional] 'class')
+ // MAP has 'val' => array('type', [optiona] 'class')
+ zend_class_entry* ce = Z_OBJCE_P(zthis);
+ while (true) {
+ int8_t ttype = transport.readI8();
+ if (ttype == T_STOP) {
+ validate_thrift_object(zthis);
+ return;
+ }
+
+ int16_t fieldno = transport.readI16();
+ zval* val_ptr = zend_hash_index_find(spec, fieldno);
+ if (val_ptr != nullptr) {
+ HashTable* fieldspec = Z_ARRVAL_P(val_ptr);
+ // pull the field name
+ val_ptr = zend_hash_str_find(fieldspec, "var", sizeof("var")-1);
+ char* varname = Z_STRVAL_P(val_ptr);
+
+ // and the type
+ val_ptr = zend_hash_str_find(fieldspec, "type", sizeof("type")-1);
+ if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+ int8_t expected_ttype = Z_LVAL_P(val_ptr);
+
+ if (ttypes_are_compatible(ttype, expected_ttype)) {
+ zval rv;
+ ZVAL_UNDEF(&rv);
+
+ binary_deserialize(ttype, transport, &rv, fieldspec);
+ zend_update_property(ce, zthis, varname, strlen(varname), &rv);
+
+ zval_ptr_dtor(&rv);
+ } else {
+ skip_element(ttype, transport);
+ }
+ } else {
+ skip_element(ttype, transport);
+ }
+ }
+}
+
+static
+void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec) {
+
+ validate_thrift_object(zthis);
+
+ HashPosition key_ptr;
+ zval* val_ptr;
+
+ for (zend_hash_internal_pointer_reset_ex(spec, &key_ptr);
+ (val_ptr = zend_hash_get_current_data_ex(spec, &key_ptr)) != nullptr;
+ zend_hash_move_forward_ex(spec, &key_ptr)) {
+
+ zend_ulong fieldno;
+ if (zend_hash_get_current_key_ex(spec, nullptr, &fieldno, &key_ptr) != HASH_KEY_IS_LONG) {
+ throw_tprotocolexception("Bad keytype in TSPEC (expected 'long')", INVALID_DATA);
+ return;
+ }
+ HashTable* fieldspec = Z_ARRVAL_P(val_ptr);
+
+ // field name
+ val_ptr = zend_hash_str_find(fieldspec, "var", sizeof("var")-1);
+ char* varname = Z_STRVAL_P(val_ptr);
+
+ // thrift type
+ val_ptr = zend_hash_str_find(fieldspec, "type", sizeof("type")-1);
+ if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
+ int8_t ttype = Z_LVAL_P(val_ptr);
+
+ zval rv;
+ zval* prop = zend_read_property(Z_OBJCE_P(zthis), zthis, varname, strlen(varname), false, &rv);
+
+ if (Z_TYPE_P(prop) == IS_REFERENCE){
+ ZVAL_DEREF(prop);
+ }
+ if (Z_TYPE_P(prop) != IS_NULL) {
+ transport.writeI8(ttype);
+ transport.writeI16(fieldno);
+ binary_serialize(ttype, transport, prop, fieldspec);
+ }
+ }
+ transport.writeI8(T_STOP); // struct end
+}
+
+// 6 params: $transport $method_name $ttype $request_struct $seqID $strict_write
+PHP_FUNCTION(thrift_protocol_write_binary) {
+ zval *protocol;
+ zval *request_struct;
+ zend_string *method_name;
+ long msgtype, seqID;
+ zend_bool strict_write;
+
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "oSlolb",
+ &protocol, &method_name, &msgtype,
+ &request_struct, &seqID, &strict_write) == FAILURE) {
+ return;
+ }
+
+ try {
+ zval* spec = zend_read_static_property(Z_OBJCE_P(request_struct), "_TSPEC", sizeof("_TSPEC")-1, true);
+ if (spec) {
+ ZVAL_DEREF(spec);
+ }
+
+ if (!spec || Z_TYPE_P(spec) != IS_ARRAY) {
+ throw_tprotocolexception("Attempt serialize from non-Thrift object", INVALID_DATA);
+ }
+
+ PHPOutputTransport transport(protocol);
+ protocol_writeMessageBegin(protocol, method_name, (int32_t) msgtype, (int32_t) seqID);
+ binary_serialize_spec(request_struct, transport, Z_ARRVAL_P(spec));
+ transport.flush();
+
+ } catch (const PHPExceptionWrapper& ex) {
+ // ex will be destructed, so copy to a zval that zend_throw_exception_object can take ownership of
+ zval myex;
+ ZVAL_COPY(&myex, ex);
+ zend_throw_exception_object(&myex);
+ RETURN_NULL();
+ } catch (const std::exception& ex) {
+ throw_zend_exception_from_std_exception(ex);
+ RETURN_NULL();
+ }
+}
+
+
+// 4 params: $transport $response_Typename $strict_read $buffer_size
+PHP_FUNCTION(thrift_protocol_read_binary) {
+ zval *protocol;
+ zend_string *obj_typename;
+ zend_bool strict_read;
+ size_t buffer_size = 8192;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "oSb|l", &protocol, &obj_typename, &strict_read, &buffer_size) == FAILURE) {
+ return;
+ }
+
+ try {
+ PHPInputTransport transport(protocol, buffer_size);
+ int8_t messageType = 0;
+ int32_t sz = transport.readI32();
+
+ if (sz < 0) {
+ // Check for correct version number
+ int32_t version = sz & VERSION_MASK;
+ if (version != VERSION_1) {
+ throw_tprotocolexception("Bad version identifier", BAD_VERSION);
+ }
+ messageType = (sz & 0x000000ff);
+ int32_t namelen = transport.readI32();
+ // skip the name string and the sequence ID, we don't care about those
+ transport.skip(namelen + 4);
+ } else {
+ if (strict_read) {
+ throw_tprotocolexception("No version identifier... old protocol client in strict mode?", BAD_VERSION);
+ } else {
+ // Handle pre-versioned input
+ transport.skip(sz); // skip string body
+ messageType = transport.readI8();
+ transport.skip(4); // skip sequence number
+ }
+ }
+
+ if (messageType == T_EXCEPTION) {
+ zval ex;
+ createObject("\\Thrift\\Exception\\TApplicationException", &ex);
+ zval* spec = zend_read_static_property(Z_OBJCE(ex), "_TSPEC", sizeof("_TPSEC")-1, false);
+ ZVAL_DEREF(spec);
+ if (EG(exception)) {
+ zend_object *ex = EG(exception);
+ EG(exception) = nullptr;
+ throw PHPExceptionWrapper(ex);
+ }
+ binary_deserialize_spec(&ex, transport, Z_ARRVAL_P(spec));
+ throw PHPExceptionWrapper(&ex);
+ }
+
+ createObject(ZSTR_VAL(obj_typename), return_value);
+ zval* spec = zend_read_static_property(Z_OBJCE_P(return_value), "_TSPEC", sizeof("_TSPEC")-1, true);
+ if (spec) {
+ ZVAL_DEREF(spec);
+ }
+ if (!spec || Z_TYPE_P(spec) != IS_ARRAY) {
+ throw_tprotocolexception("Attempt deserialize to non-Thrift object", INVALID_DATA);
+ }
+ binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec));
+ } catch (const PHPExceptionWrapper& ex) {
+ // ex will be destructed, so copy to a zval that zend_throw_exception_object can ownership of
+ zval myex;
+ ZVAL_COPY(&myex, ex);
+ zval_dtor(return_value);
+ zend_throw_exception_object(&myex);
+ RETURN_NULL();
+ } catch (const std::exception& ex) {
+ throw_zend_exception_from_std_exception(ex);
+ RETURN_NULL();
+ }
+}
+
+// 4 params: $transport $response_Typename $strict_read $buffer_size
+PHP_FUNCTION(thrift_protocol_read_binary_after_message_begin) {
+ zval *protocol;
+ zend_string *obj_typename;
+ zend_bool strict_read;
+ size_t buffer_size = 8192;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "oSb|l", &protocol, &obj_typename, &strict_read, &buffer_size) == FAILURE) {
+ return;
+ }
+
+ try {
+ PHPInputTransport transport(protocol, buffer_size);
+
+ createObject(ZSTR_VAL(obj_typename), return_value);
+ zval* spec = zend_read_static_property(Z_OBJCE_P(return_value), "_TSPEC", sizeof("_TSPEC")-1, false);
+ ZVAL_DEREF(spec);
+ binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec));
+ } catch (const PHPExceptionWrapper& ex) {
+ // ex will be destructed, so copy to a zval that zend_throw_exception_object can take ownership of
+ zval myex;
+ ZVAL_COPY(&myex, ex);
+ zend_throw_exception_object(&myex);
+ RETURN_NULL();
+ } catch (const std::exception& ex) {
+ throw_zend_exception_from_std_exception(ex);
+ RETURN_NULL();
+ }
+}
+
+#endif /* PHP_VERSION_ID >= 70000 */
diff --git a/src/jaegertracing/thrift/lib/php/src/ext/thrift_protocol/php_thrift_protocol.h b/src/jaegertracing/thrift/lib/php/src/ext/thrift_protocol/php_thrift_protocol.h
new file mode 100644
index 000000000..04209973c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/src/ext/thrift_protocol/php_thrift_protocol.h
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+PHP_FUNCTION(thrift_protocol_write_binary);
+PHP_FUNCTION(thrift_protocol_read_binary);
+PHP_FUNCTION(thrift_protocol_read_binary_after_message_begin);
+
+extern zend_module_entry thrift_protocol_module_entry;
+#define phpext_thrift_protocol_ptr &thrift_protocol_module_entry
+
diff --git a/src/jaegertracing/thrift/lib/php/test/Fixtures.php b/src/jaegertracing/thrift/lib/php/test/Fixtures.php
new file mode 100644
index 000000000..fd57d831c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/test/Fixtures.php
@@ -0,0 +1,194 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.test
+ */
+
+namespace Test\Thrift;
+
+use ThriftTest\Xtruct;
+use ThriftTest\Xtruct2;
+use ThriftTest\Numberz;
+use ThriftTest\Insanity;
+
+class Fixtures
+{
+ public static $bufsize = 8192; //big enough to read biggest serialized Fixture arg.
+ public static $testArgs = array();
+
+ public static function populateTestArgs()
+ {
+ self::$testArgs['testString1'] = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, Asturianu, Aymar aru, AzÉ™rbaycan, Башҡорт, Boarisch, ŽemaitÄ—Å¡ka, БеларуÑкаÑ, БеларуÑÐºÐ°Ñ (тарашкевіца), БългарÑки, Bamanankan, বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Ðохчийн, Cebuano, á£áŽ³áŽ©, ÄŒesky, СловѣÌньÑкъ / ⰔⰎⰑⰂⰡâ°â° â°”â°â°Ÿ, Чӑвашла, Cymraeg, Dansk, Zazaki, Þ‹Þ¨ÞˆÞ¬Þ€Þ¨Þ„Þ¦ÞÞ°, Ελληνικά, Emiliàn e rumagnòl, English, Esperanto, Español, Eesti, Euskara, Ùارسی, Suomi, Võro, Føroyskt, Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, Avañe'ẽ, ગà«àªœàª°àª¾àª¤à«€, Gaelg, עברית, हिनà¥à¤¦à¥€, Fiji Hindi, Hrvatski, Kreyòl ayisyen, Magyar, Õ€Õ¡ÕµÕ¥Ö€Õ¥Õ¶, Interlingua, Bahasa Indonesia, Ilokano, Ido, Ãslenska, Italiano, 日本語, Lojban, Basa Jawa, ქáƒáƒ áƒ—ული, Kongo, Kalaallisut, ಕನà³à²¨à²¡, 한국어, Къарачай-Малкъар, Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, LatvieÅ¡u, Basa Banyumasan, Malagasy, МакедонÑки, മലയാളം, मराठी, Bahasa Melayu, مازÙرونی, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪Norsk (nynorsk)‬, ‪Norsk (bokmÃ¥l)‬, Nouormand, Diné bizaad, Occitan, Иронау, Papiamentu, Deitsch, Norfuk / Pitkern, Polski, پنجابی, پښتو, Português, Runa Simi, Rumantsch, Romani, Română, РуÑÑкий, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple English, SlovenÄina, SlovenÅ¡Äina, СрпÑки / Srpski, Seeltersk, Svenska, Kiswahili, தமிழà¯, తెలà±à°—à±, Тоҷикӣ, ไทย, Türkmençe, Tagalog, Türkçe, Татарча/Tatarça, УкраїнÑька, اردو, Tiếng Việt, Volapük, Walon, Winaray, å´è¯­, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, Bân-lâm-gú, 粵語";
+
+ self::$testArgs['testString2'] =
+ "quote: \\\" backslash:" .
+ " forwardslash-escaped: \\/ " .
+ " backspace: \b formfeed: \f newline: \n return: \r tab: " .
+ " now-all-of-them-together: \"\\\/\b\n\r\t" .
+ " now-a-bunch-of-junk: !@#\$%&()(&%$#{}{}<><><";
+
+ self::$testArgs['testString3'] =
+ "string that ends in double-backslash \\\\";
+
+ self::$testArgs['testUnicodeStringWithNonBMP'] =
+ "สวัสดี/ð’¯";
+
+ self::$testArgs['testDouble'] = 3.1415926535898;
+
+ // TODO: add testBinary() call
+
+ self::$testArgs['testByte'] = 0x01;
+
+ self::$testArgs['testI32'] = pow(2, 30);
+
+ if (PHP_INT_SIZE == 8) {
+ self::$testArgs['testI64'] = pow(2, 60);
+ } else {
+ self::$testArgs['testI64'] = "1152921504606847000";
+ }
+
+ self::$testArgs['testStruct'] =
+ new Xtruct(
+ array(
+ 'string_thing' => 'worked',
+ 'byte_thing' => 0x01,
+ 'i32_thing' => pow(2, 30),
+ 'i64_thing' => self::$testArgs['testI64']
+ )
+ );
+
+ self::$testArgs['testNestNested'] =
+ new Xtruct(
+ array(
+ 'string_thing' => 'worked',
+ 'byte_thing' => 0x01,
+ 'i32_thing' => pow(2, 30),
+ 'i64_thing' => self::$testArgs['testI64']
+ )
+ );
+
+ self::$testArgs['testNest'] =
+ new Xtruct2(
+ array(
+ 'byte_thing' => 0x01,
+ 'struct_thing' => self::$testArgs['testNestNested'],
+ 'i32_thing' => pow(2, 15)
+ )
+ );
+
+ self::$testArgs['testMap'] =
+ array(
+ 7 => 77,
+ 8 => 88,
+ 9 => 99
+ );
+
+ self::$testArgs['testStringMap'] =
+ array(
+ "a" => "123",
+ "a b" => "with spaces ",
+ "same" => "same",
+ "0" => "numeric key",
+ "longValue" => self::$testArgs['testString1'],
+ self::$testArgs['testString1'] => "long key"
+ );
+
+ self::$testArgs['testSet'] = array(1 => true, 5 => true, 6 => true);
+
+ self::$testArgs['testList'] = array(1, 2, 3);
+
+ self::$testArgs['testEnum'] = Numberz::ONE;
+
+ self::$testArgs['testTypedef'] = 69;
+
+ self::$testArgs['testMapMapExpectedResult'] =
+ array(
+ 4 => array(
+ 1 => 1,
+ 2 => 2,
+ 3 => 3,
+ 4 => 4,
+ ),
+ -4 => array(
+ -4 => -4,
+ -3 => -3,
+ -2 => -2,
+ -1 => -1
+ )
+ );
+
+ // testInsanity ... takes a few steps to set up!
+
+ $xtruct1 =
+ new Xtruct(
+ array(
+ 'string_thing' => 'Goodbye4',
+ 'byte_thing' => 4,
+ 'i32_thing' => 4,
+ 'i64_thing' => 4
+ )
+ );
+
+ $xtruct2 =
+ new Xtruct(
+ array(
+ 'string_thing' => 'Hello2',
+ 'byte_thing' => 2,
+ 'i32_thing' => 2,
+ 'i64_thing' => 2
+ )
+ );
+
+ $userMap =
+ array(
+ Numberz::FIVE => 5,
+ Numberz::EIGHT => 8
+ );
+
+ $insanity2 =
+ new Insanity(
+ array(
+ 'userMap' => $userMap,
+ 'xtructs' => array($xtruct1, $xtruct2)
+ )
+ );
+
+ $insanity3 = $insanity2;
+
+ $insanity6 =
+ new Insanity(
+ array(
+ 'userMap' => null,
+ 'xtructs' => null
+ )
+ );
+
+ self::$testArgs['testInsanityExpectedResult'] =
+ array(
+ "1" => array(
+ Numberz::TWO => $insanity2,
+ Numberz::THREE => $insanity3
+ ),
+ "2" => array(
+ Numberz::SIX => $insanity6
+ )
+ );
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/test/JsonSerialize/JsonSerializeTest.php b/src/jaegertracing/thrift/lib/php/test/JsonSerialize/JsonSerializeTest.php
new file mode 100644
index 000000000..c6686525f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/test/JsonSerialize/JsonSerializeTest.php
@@ -0,0 +1,116 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Test\Thrift\JsonSerialize;
+
+use PHPUnit\Framework\TestCase;
+use stdClass;
+
+require __DIR__ . '/../../../../vendor/autoload.php';
+
+/**
+ * @runTestsInSeparateProcesses
+ */
+class JsonSerializeTest extends TestCase
+{
+ protected function setUp()
+ {
+ if (version_compare(phpversion(), '5.4', '<')) {
+ $this->markTestSkipped('Requires PHP 5.4 or newer!');
+ }
+ /** @var \Composer\Autoload\ClassLoader $loader */
+ $loader = require __DIR__ . '/../../../../vendor/autoload.php';
+ $loader->addPsr4('', __DIR__ . '/../packages/phpjs');
+ }
+
+ public function testEmptyStruct()
+ {
+ $empty = new \ThriftTest\EmptyStruct(array('non_existing_key' => 'bar'));
+ $this->assertEquals(new stdClass(), json_decode(json_encode($empty)));
+ }
+
+ public function testStringsAndInts()
+ {
+ $input = array(
+ 'string_thing' => 'foo',
+ 'i64_thing' => 1234567890,
+ );
+ $xtruct = new \ThriftTest\Xtruct($input);
+
+ // Xtruct's 'i32_thing' and 'byte_thing' fields should not be present here!
+ $expected = new stdClass();
+ $expected->string_thing = $input['string_thing'];
+ $expected->i64_thing = $input['i64_thing'];
+ $this->assertEquals($expected, json_decode(json_encode($xtruct)));
+ }
+
+ public function testNestedStructs()
+ {
+ $xtruct2 = new \ThriftTest\Xtruct2(array(
+ 'byte_thing' => 42,
+ 'struct_thing' => new \ThriftTest\Xtruct(array(
+ 'i32_thing' => 123456,
+ )),
+ ));
+
+ $expected = new stdClass();
+ $expected->byte_thing = $xtruct2->byte_thing;
+ $expected->struct_thing = new stdClass();
+ $expected->struct_thing->i32_thing = $xtruct2->struct_thing->i32_thing;
+ $this->assertEquals($expected, json_decode(json_encode($xtruct2)));
+ }
+
+ public function testInsanity()
+ {
+ $xinput = array('string_thing' => 'foo');
+ $xtruct = new \ThriftTest\Xtruct($xinput);
+ $insanity = new \ThriftTest\Insanity(array(
+ 'xtructs' => array($xtruct, $xtruct, $xtruct)
+ ));
+ $expected = new stdClass();
+ $expected->xtructs = array((object)$xinput, (object)$xinput, (object)$xinput);
+ $this->assertEquals($expected, json_decode(json_encode($insanity)));
+ }
+
+ public function testNestedLists()
+ {
+ $bonk = new \ThriftTest\Bonk(array('message' => 'foo'));
+ $nested = new \ThriftTest\NestedListsBonk(array('bonk' => array(array(array($bonk)))));
+ $expected = new stdClass();
+ $expected->bonk = array(array(array((object)array('message' => 'foo'))));
+ $this->assertEquals($expected, json_decode(json_encode($nested)));
+ }
+
+ public function testMaps()
+ {
+ $intmap = new \ThriftTest\ThriftTest_testMap_args(['thing' => [0 => 'zero']]);
+ $emptymap = new \ThriftTest\ThriftTest_testMap_args([]);
+ $this->assertEquals('{"thing":{"0":"zero"}}', json_encode($intmap));
+ $this->assertEquals('{}', json_encode($emptymap));
+ }
+
+ public function testScalarTypes()
+ {
+ $b = new \ThriftTest\Bools(['im_true' => '1', 'im_false' => '0']);
+ $this->assertEquals('{"im_true":true,"im_false":false}', json_encode($b));
+ $s = new \ThriftTest\StructA(['s' => 42]);
+ $this->assertEquals('{"s":"42"}', json_encode($s));
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/test/Makefile.am b/src/jaegertracing/thrift/lib/php/test/Makefile.am
new file mode 100755
index 000000000..6f4e50a50
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/test/Makefile.am
@@ -0,0 +1,55 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+PHPUNIT=php $(top_srcdir)/vendor/bin/phpunit
+
+stubs: ../../../test/ThriftTest.thrift TestValidators.thrift
+ mkdir -p ./packages/php
+ $(THRIFT) --gen php -r --out ./packages/php ../../../test/ThriftTest.thrift
+ mkdir -p ./packages/phpv
+ mkdir -p ./packages/phpvo
+ mkdir -p ./packages/phpjs
+ $(THRIFT) --gen php:validate -r --out ./packages/phpv TestValidators.thrift
+ $(THRIFT) --gen php:validate,oop -r --out ./packages/phpvo TestValidators.thrift
+ $(THRIFT) --gen php:json -r --out ./packages/phpjs TestValidators.thrift
+
+deps: $(top_srcdir)/composer.json
+ composer install --working-dir=$(top_srcdir)
+
+all-local: deps
+
+check-json-serializer: deps stubs
+ $(PHPUNIT) --log-junit=TEST-log-json-serializer.xml JsonSerialize/
+
+check-validator: deps stubs
+ $(PHPUNIT) --log-junit=TEST-log-validator.xml Validator/
+
+check-protocol: deps stubs
+ $(PHPUNIT) --log-junit=TEST-log-protocol.xml Protocol/
+
+check: deps stubs \
+ check-protocol \
+ check-validator \
+ check-json-serializer
+
+distclean-local:
+
+clean-local:
+ $(RM) -r ./packages
+ $(RM) TEST-*.xml
diff --git a/src/jaegertracing/thrift/lib/php/test/Protocol/BinarySerializerTest.php b/src/jaegertracing/thrift/lib/php/test/Protocol/BinarySerializerTest.php
new file mode 100644
index 000000000..71b0bb506
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/test/Protocol/BinarySerializerTest.php
@@ -0,0 +1,60 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.test
+ */
+
+namespace Test\Thrift\Protocol;
+
+use PHPUnit\Framework\TestCase;
+use Thrift\Serializer\TBinarySerializer;
+
+require __DIR__ . '/../../../../vendor/autoload.php';
+
+/***
+ * This test suite depends on running the compiler against the
+ * standard ThriftTest.thrift file:
+ *
+ * lib/php/test$ ../../../compiler/cpp/thrift --gen php -r \
+ * --out ./packages ../../../test/ThriftTest.thrift
+ *
+ * @runTestsInSeparateProcesses
+ */
+class BinarySerializerTest extends TestCase
+{
+ public function setUp()
+ {
+ /** @var \Composer\Autoload\ClassLoader $loader */
+ $loader = require __DIR__ . '/../../../../vendor/autoload.php';
+ $loader->addPsr4('', __DIR__ . '/../packages/php');
+ }
+
+ /**
+ * We try to serialize and deserialize a random object to make sure no exceptions are thrown.
+ * @see THRIFT-1579
+ */
+ public function testBinarySerializer()
+ {
+ $struct = new \ThriftTest\Xtruct(array('string_thing' => 'abc'));
+ $serialized = TBinarySerializer::serialize($struct, 'ThriftTest\\Xtruct');
+ $deserialized = TBinarySerializer::deserialize($serialized, 'ThriftTest\\Xtruct');
+ $this->assertEquals($struct, $deserialized);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/test/Protocol/TJSONProtocolFixtures.php b/src/jaegertracing/thrift/lib/php/test/Protocol/TJSONProtocolFixtures.php
new file mode 100644
index 000000000..dd9039fca
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/test/Protocol/TJSONProtocolFixtures.php
@@ -0,0 +1,74 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.test
+ */
+
+namespace Test\Thrift\Protocol;
+
+class TJSONProtocolFixtures
+{
+ public static $testArgsJSON = array();
+
+ public static function populateTestArgsJSON()
+ {
+ self::$testArgsJSON['testVoid'] = '{}';
+
+ self::$testArgsJSON['testString1'] = '{"1":{"str":"Afrikaans, Alemannisch, Aragonés, العربية, مصرى, Asturianu, Aymar aru, AzÉ™rbaycan, Башҡорт, Boarisch, ŽemaitÄ—Å¡ka, БеларуÑкаÑ, БеларуÑÐºÐ°Ñ (тарашкевіца), БългарÑки, Bamanankan, বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Ðохчийн, Cebuano, á£áŽ³áŽ©, ÄŒesky, СловѣÌньÑкъ \/ ⰔⰎⰑⰂⰡâ°â° â°”â°â°Ÿ, Чӑвашла, Cymraeg, Dansk, Zazaki, Þ‹Þ¨ÞˆÞ¬Þ€Þ¨Þ„Þ¦ÞÞ°, Ελληνικά, Emiliàn e rumagnòl, English, Esperanto, Español, Eesti, Euskara, Ùارسی, Suomi, Võro, Føroyskt, Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, Avañe\'ẽ, ગà«àªœàª°àª¾àª¤à«€, Gaelg, עברית, हिनà¥à¤¦à¥€, Fiji Hindi, Hrvatski, Kreyòl ayisyen, Magyar, Õ€Õ¡ÕµÕ¥Ö€Õ¥Õ¶, Interlingua, Bahasa Indonesia, Ilokano, Ido, Ãslenska, Italiano, 日本語, Lojban, Basa Jawa, ქáƒáƒ áƒ—ული, Kongo, Kalaallisut, ಕನà³à²¨à²¡, 한국어, Къарачай-Малкъар, Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, LatvieÅ¡u, Basa Banyumasan, Malagasy, МакедонÑки, മലയാളം, मराठी, Bahasa Melayu, مازÙرونی, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪Norsk (nynorsk)‬, ‪Norsk (bokmÃ¥l)‬, Nouormand, Diné bizaad, Occitan, Иронау, Papiamentu, Deitsch, Norfuk \/ Pitkern, Polski, پنجابی, پښتو, Português, Runa Simi, Rumantsch, Romani, Română, РуÑÑкий, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple English, SlovenÄina, SlovenÅ¡Äina, СрпÑки \/ Srpski, Seeltersk, Svenska, Kiswahili, தமிழà¯, తెలà±à°—à±, Тоҷикӣ, ไทย, Türkmençe, Tagalog, Türkçe, Татарча\/Tatarça, УкраїнÑька, اردو, Tiếng Việt, Volapük, Walon, Winaray, å´è¯­, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, Bân-lâm-gú, 粵語"}}';
+
+ self::$testArgsJSON['testString2'] = '{"1":{"str":"quote: \\\\\" backslash: forwardslash-escaped: \\\\\/ backspace: \\\\b formfeed: \f newline: \n return: \r tab: now-all-of-them-together: \"\\\\\\\\\/\\\\b\n\r\t now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><"}}';
+
+ self::$testArgsJSON['testString3'] = '{"1":{"str":"string that ends in double-backslash \\\\\\\\"}}';
+
+ self::$testArgsJSON['testUnicodeStringWithNonBMP'] = '{"1":{"str":"สวัสดี\/ð’¯"}}';
+
+ self::$testArgsJSON['testDouble'] = '{"1":{"dbl":3.1415926535898}}';
+
+ self::$testArgsJSON['testByte'] = '{"1":{"i8":1}}';
+
+ self::$testArgsJSON['testI32'] = '{"1":{"i32":1073741824}}';
+
+ if (PHP_INT_SIZE == 8) {
+ self::$testArgsJSON['testI64'] = '{"1":{"i64":' . pow(2, 60) . '}}';
+ self::$testArgsJSON['testStruct'] = '{"1":{"rec":{"1":{"str":"worked"},"4":{"i8":1},"9":{"i32":1073741824},"11":{"i64":' . pow(2, 60) . '}}}}';
+ self::$testArgsJSON['testNest'] = '{"1":{"rec":{"1":{"i8":1},"2":{"rec":{"1":{"str":"worked"},"4":{"i8":1},"9":{"i32":1073741824},"11":{"i64":' . pow(2, 60) . '}}},"3":{"i32":32768}}}}';
+ } else {
+ self::$testArgsJSON['testI64'] = '{"1":{"i64":1152921504606847000}}';
+ self::$testArgsJSON['testStruct'] = '{"1":{"rec":{"1":{"str":"worked"},"4":{"i8":1},"9":{"i32":1073741824},"11":{"i64":1152921504606847000}}}}';
+ self::$testArgsJSON['testNest'] = '{"1":{"rec":{"1":{"i8":1},"2":{"rec":{"1":{"str":"worked"},"4":{"i8":1},"9":{"i32":1073741824},"11":{"i64":1152921504606847000}}},"3":{"i32":32768}}}}';
+ }
+
+ self::$testArgsJSON['testMap'] = '{"1":{"map":["i32","i32",3,{"7":77,"8":88,"9":99}]}}';
+
+ self::$testArgsJSON['testStringMap'] = '{"1":{"map":["str","str",6,{"a":"123","a b":"with spaces ","same":"same","0":"numeric key","longValue":"Afrikaans, Alemannisch, Aragon\u00e9s, \u0627\u0644\u0639\u0631\u0628\u064a\u0629, \u0645\u0635\u0631\u0649, Asturianu, Aymar aru, Az\u0259rbaycan, \u0411\u0430\u0448\u04a1\u043e\u0440\u0442, Boarisch, \u017demait\u0117\u0161ka, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430), \u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438, Bamanankan, \u09ac\u09be\u0982\u09b2\u09be, Brezhoneg, Bosanski, Catal\u00e0, M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304, \u041d\u043e\u0445\u0447\u0438\u0439\u043d, Cebuano, \u13e3\u13b3\u13a9, \u010cesky, \u0421\u043b\u043e\u0432\u0463\u0301\u043d\u044c\u0441\u043a\u044a \/ \u2c14\u2c0e\u2c11\u2c02\u2c21\u2c10\u2c20\u2c14\u2c0d\u2c1f, \u0427\u04d1\u0432\u0430\u0448\u043b\u0430, Cymraeg, Dansk, Zazaki, \u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0, \u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac, Emili\u00e0n e rumagn\u00f2l, English, Esperanto, Espa\u00f1ol, Eesti, Euskara, \u0641\u0627\u0631\u0633\u06cc, Suomi, V\u00f5ro, F\u00f8royskt, Fran\u00e7ais, Arpetan, Furlan, Frysk, Gaeilge, \u8d1b\u8a9e, G\u00e0idhlig, Galego, Ava\u00f1e\'\u1ebd, \u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0, Gaelg, \u05e2\u05d1\u05e8\u05d9\u05ea, \u0939\u093f\u0928\u094d\u0926\u0940, Fiji Hindi, Hrvatski, Krey\u00f2l ayisyen, Magyar, \u0540\u0561\u0575\u0565\u0580\u0565\u0576, Interlingua, Bahasa Indonesia, Ilokano, Ido, \u00cdslenska, Italiano, \u65e5\u672c\u8a9e, Lojban, Basa Jawa, \u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8, Kongo, Kalaallisut, \u0c95\u0ca8\u0ccd\u0ca8\u0ca1, \ud55c\uad6d\uc5b4, \u041a\u044a\u0430\u0440\u0430\u0447\u0430\u0439-\u041c\u0430\u043b\u043a\u044a\u0430\u0440, Ripoarisch, Kurd\u00ee, \u041a\u043e\u043c\u0438, Kernewek, \u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430, Latina, Ladino, L\u00ebtzebuergesch, Limburgs, Ling\u00e1la, \u0ea5\u0eb2\u0ea7, Lietuvi\u0173, Latvie\u0161u, Basa Banyumasan, Malagasy, \u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438, \u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02, \u092e\u0930\u093e\u0920\u0940, Bahasa Melayu, \u0645\u0627\u0632\u0650\u0631\u0648\u0646\u06cc, Nnapulitano, Nedersaksisch, \u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e, Nederlands, \u202aNorsk (nynorsk)\u202c, \u202aNorsk (bokm\u00e5l)\u202c, Nouormand, Din\u00e9 bizaad, Occitan, \u0418\u0440\u043e\u043d\u0430\u0443, Papiamentu, Deitsch, Norfuk \/ Pitkern, Polski, \u067e\u0646\u062c\u0627\u0628\u06cc, \u067e\u069a\u062a\u0648, Portugu\u00eas, Runa Simi, Rumantsch, Romani, Rom\u00e2n\u0103, \u0420\u0443\u0441\u0441\u043a\u0438\u0439, \u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430, Sardu, Sicilianu, Scots, S\u00e1megiella, Simple English, Sloven\u010dina, Sloven\u0161\u010dina, \u0421\u0440\u043f\u0441\u043a\u0438 \/ Srpski, Seeltersk, Svenska, Kiswahili, \u0ba4\u0bae\u0bbf\u0bb4\u0bcd, \u0c24\u0c46\u0c32\u0c41\u0c17\u0c41, \u0422\u043e\u04b7\u0438\u043a\u04e3, \u0e44\u0e17\u0e22, T\u00fcrkmen\u00e7e, Tagalog, T\u00fcrk\u00e7e, \u0422\u0430\u0442\u0430\u0440\u0447\u0430\/Tatar\u00e7a, \u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430, \u0627\u0631\u062f\u0648, Ti\u1ebfng Vi\u1ec7t, Volap\u00fck, Walon, Winaray, \u5434\u8bed, isiXhosa, \u05d9\u05d9\u05b4\u05d3\u05d9\u05e9, Yor\u00f9b\u00e1, Ze\u00eauws, \u4e2d\u6587, B\u00e2n-l\u00e2m-g\u00fa, \u7cb5\u8a9e","Afrikaans, Alemannisch, Aragon\u00e9s, \u0627\u0644\u0639\u0631\u0628\u064a\u0629, \u0645\u0635\u0631\u0649, Asturianu, Aymar aru, Az\u0259rbaycan, \u0411\u0430\u0448\u04a1\u043e\u0440\u0442, Boarisch, \u017demait\u0117\u0161ka, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430), \u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438, Bamanankan, \u09ac\u09be\u0982\u09b2\u09be, Brezhoneg, Bosanski, Catal\u00e0, M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304, \u041d\u043e\u0445\u0447\u0438\u0439\u043d, Cebuano, \u13e3\u13b3\u13a9, \u010cesky, \u0421\u043b\u043e\u0432\u0463\u0301\u043d\u044c\u0441\u043a\u044a \/ \u2c14\u2c0e\u2c11\u2c02\u2c21\u2c10\u2c20\u2c14\u2c0d\u2c1f, \u0427\u04d1\u0432\u0430\u0448\u043b\u0430, Cymraeg, Dansk, Zazaki, \u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0, \u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac, Emili\u00e0n e rumagn\u00f2l, English, Esperanto, Espa\u00f1ol, Eesti, Euskara, \u0641\u0627\u0631\u0633\u06cc, Suomi, V\u00f5ro, F\u00f8royskt, Fran\u00e7ais, Arpetan, Furlan, Frysk, Gaeilge, \u8d1b\u8a9e, G\u00e0idhlig, Galego, Ava\u00f1e\'\u1ebd, \u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0, Gaelg, \u05e2\u05d1\u05e8\u05d9\u05ea, \u0939\u093f\u0928\u094d\u0926\u0940, Fiji Hindi, Hrvatski, Krey\u00f2l ayisyen, Magyar, \u0540\u0561\u0575\u0565\u0580\u0565\u0576, Interlingua, Bahasa Indonesia, Ilokano, Ido, \u00cdslenska, Italiano, \u65e5\u672c\u8a9e, Lojban, Basa Jawa, \u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8, Kongo, Kalaallisut, \u0c95\u0ca8\u0ccd\u0ca8\u0ca1, \ud55c\uad6d\uc5b4, \u041a\u044a\u0430\u0440\u0430\u0447\u0430\u0439-\u041c\u0430\u043b\u043a\u044a\u0430\u0440, Ripoarisch, Kurd\u00ee, \u041a\u043e\u043c\u0438, Kernewek, \u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430, Latina, Ladino, L\u00ebtzebuergesch, Limburgs, Ling\u00e1la, \u0ea5\u0eb2\u0ea7, Lietuvi\u0173, Latvie\u0161u, Basa Banyumasan, Malagasy, \u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438, \u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02, \u092e\u0930\u093e\u0920\u0940, Bahasa Melayu, \u0645\u0627\u0632\u0650\u0631\u0648\u0646\u06cc, Nnapulitano, Nedersaksisch, \u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e, Nederlands, \u202aNorsk (nynorsk)\u202c, \u202aNorsk (bokm\u00e5l)\u202c, Nouormand, Din\u00e9 bizaad, Occitan, \u0418\u0440\u043e\u043d\u0430\u0443, Papiamentu, Deitsch, Norfuk \/ Pitkern, Polski, \u067e\u0646\u062c\u0627\u0628\u06cc, \u067e\u069a\u062a\u0648, Portugu\u00eas, Runa Simi, Rumantsch, Romani, Rom\u00e2n\u0103, \u0420\u0443\u0441\u0441\u043a\u0438\u0439, \u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430, Sardu, Sicilianu, Scots, S\u00e1megiella, Simple English, Sloven\u010dina, Sloven\u0161\u010dina, \u0421\u0440\u043f\u0441\u043a\u0438 \/ Srpski, Seeltersk, Svenska, Kiswahili, \u0ba4\u0bae\u0bbf\u0bb4\u0bcd, \u0c24\u0c46\u0c32\u0c41\u0c17\u0c41, \u0422\u043e\u04b7\u0438\u043a\u04e3, \u0e44\u0e17\u0e22, T\u00fcrkmen\u00e7e, Tagalog, T\u00fcrk\u00e7e, \u0422\u0430\u0442\u0430\u0440\u0447\u0430\/Tatar\u00e7a, \u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430, \u0627\u0631\u062f\u0648, Ti\u1ebfng Vi\u1ec7t, Volap\u00fck, Walon, Winaray, \u5434\u8bed, isiXhosa, \u05d9\u05d9\u05b4\u05d3\u05d9\u05e9, Yor\u00f9b\u00e1, Ze\u00eauws, \u4e2d\u6587, B\u00e2n-l\u00e2m-g\u00fa, \u7cb5\u8a9e":"long key"}]}}';
+
+ self::$testArgsJSON['testSet'] = '{"1":{"set":["i32",3,1,5,6]}}';
+
+ self::$testArgsJSON['testList'] = '{"1":{"lst":["i32",3,1,2,3]}}';
+
+ self::$testArgsJSON['testEnum'] = '{"1":{"i32":1}}';
+
+ self::$testArgsJSON['testTypedef'] = '{"1":{"i64":69}}';
+
+ self::$testArgsJSON['testMapMap'] = '{"0":{"map":["i32","map",2,{"4":["i32","i32",4,{"1":1,"2":2,"3":3,"4":4}],"-4":["i32","i32",4,{"-4":-4,"-3":-3,"-2":-2,"-1":-1}]}]}}';
+
+ self::$testArgsJSON['testInsanity'] = '{"0":{"map":["i64","map",2,{"1":["i32","rec",2,{"2":{"1":{"map":["i32","i64",2,{"5":5,"8":8}]},"2":{"lst":["rec",2,{"1":{"str":"Goodbye4"},"4":{"i8":4},"9":{"i32":4},"11":{"i64":4}},{"1":{"str":"Hello2"},"4":{"i8":2},"9":{"i32":2},"11":{"i64":2}}]}},"3":{"1":{"map":["i32","i64",2,{"5":5,"8":8}]},"2":{"lst":["rec",2,{"1":{"str":"Goodbye4"},"4":{"i8":4},"9":{"i32":4},"11":{"i64":4}},{"1":{"str":"Hello2"},"4":{"i8":2},"9":{"i32":2},"11":{"i64":2}}]}}}],"2":["i32","rec",1,{"6":{}}]}]}}';
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/test/Protocol/TJSONProtocolTest.php b/src/jaegertracing/thrift/lib/php/test/Protocol/TJSONProtocolTest.php
new file mode 100644
index 000000000..bf0ecce42
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/test/Protocol/TJSONProtocolTest.php
@@ -0,0 +1,518 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.test
+ */
+
+namespace Test\Thrift\Protocol;
+
+use PHPUnit\Framework\TestCase;
+use Test\Thrift\Fixtures;
+use Thrift\Protocol\TJSONProtocol;
+use Thrift\Transport\TMemoryBuffer;
+
+require __DIR__ . '/../../../../vendor/autoload.php';
+
+/***
+ * This test suite depends on running the compiler against the
+ * standard ThriftTest.thrift file:
+ *
+ * lib/php/test$ ../../../compiler/cpp/thrift --gen php -r \
+ * --out ./packages ../../../test/ThriftTest.thrift
+ *
+ * @runTestsInSeparateProcesses
+ */
+class TJSONProtocolTest extends TestCase
+{
+ private $transport;
+ private $protocol;
+
+ public static function setUpBeforeClass()
+ {
+ /** @var \Composer\Autoload\ClassLoader $loader */
+ $loader = require __DIR__ . '/../../../../vendor/autoload.php';
+ $loader->addPsr4('', __DIR__ . '/../packages/php');
+
+ Fixtures::populateTestArgs();
+ TJSONProtocolFixtures::populateTestArgsJSON();
+ }
+
+ public function setUp()
+ {
+ $this->transport = new TMemoryBuffer();
+ $this->protocol = new TJSONProtocol($this->transport);
+ $this->transport->open();
+ }
+
+ /**
+ * WRITE TESTS
+ */
+ public function testVoidWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testVoid_args();
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TJSONProtocolFixtures::$testArgsJSON['testVoid'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testString1Write()
+ {
+ $args = new \ThriftTest\ThriftTest_testString_args();
+ $args->thing = Fixtures::$testArgs['testString1'];
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TJSONProtocolFixtures::$testArgsJSON['testString1'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testString2Write()
+ {
+ $args = new \ThriftTest\ThriftTest_testString_args();
+ $args->thing = Fixtures::$testArgs['testString2'];
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TJSONProtocolFixtures::$testArgsJSON['testString2'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testDoubleWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testDouble_args();
+ $args->thing = Fixtures::$testArgs['testDouble'];
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TJSONProtocolFixtures::$testArgsJSON['testDouble'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testByteWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testByte_args();
+ $args->thing = Fixtures::$testArgs['testByte'];
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TJSONProtocolFixtures::$testArgsJSON['testByte'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testI32Write()
+ {
+ $args = new \ThriftTest\ThriftTest_testI32_args();
+ $args->thing = Fixtures::$testArgs['testI32'];
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TJSONProtocolFixtures::$testArgsJSON['testI32'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testI64Write()
+ {
+ $args = new \ThriftTest\ThriftTest_testI64_args();
+ $args->thing = Fixtures::$testArgs['testI64'];
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TJSONProtocolFixtures::$testArgsJSON['testI64'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testStructWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testStruct_args();
+ $args->thing = Fixtures::$testArgs['testStruct'];
+
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TJSONProtocolFixtures::$testArgsJSON['testStruct'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testNestWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testNest_args();
+ $args->thing = Fixtures::$testArgs['testNest'];
+
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TJSONProtocolFixtures::$testArgsJSON['testNest'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testMapWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testMap_args();
+ $args->thing = Fixtures::$testArgs['testMap'];
+
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TJSONProtocolFixtures::$testArgsJSON['testMap'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testStringMapWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testStringMap_args();
+ $args->thing = Fixtures::$testArgs['testStringMap'];
+
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TJSONProtocolFixtures::$testArgsJSON['testStringMap'];
+
+ /*
+ * The $actual returns unescaped string.
+ * It is required to to decode then encode it again
+ * to get the expected escaped unicode.
+ */
+ $this->assertEquals($expected, json_encode(json_decode($actual)));
+ }
+
+ public function testSetWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testSet_args();
+ $args->thing = Fixtures::$testArgs['testSet'];
+
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TJSONProtocolFixtures::$testArgsJSON['testSet'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testListWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testList_args();
+ $args->thing = Fixtures::$testArgs['testList'];
+
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TJSONProtocolFixtures::$testArgsJSON['testList'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testEnumWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testEnum_args();
+ $args->thing = Fixtures::$testArgs['testEnum'];
+
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TJSONProtocolFixtures::$testArgsJSON['testEnum'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testTypedefWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testTypedef_args();
+ $args->thing = Fixtures::$testArgs['testTypedef'];
+
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TJSONProtocolFixtures::$testArgsJSON['testTypedef'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ /**
+ * READ TESTS
+ */
+ public function testVoidRead()
+ {
+ $this->transport->write(
+ TJSONProtocolFixtures::$testArgsJSON['testVoid']
+ );
+ $args = new \ThriftTest\ThriftTest_testVoid_args();
+ $args->read($this->protocol);
+ }
+
+ public function testString1Read()
+ {
+ $this->transport->write(
+ TJSONProtocolFixtures::$testArgsJSON['testString1']
+ );
+ $args = new \ThriftTest\ThriftTest_testString_args();
+ $args->read($this->protocol);
+
+ $actual = $args->thing;
+ $expected = Fixtures::$testArgs['testString1'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testString2Read()
+ {
+ $this->transport->write(
+ TJSONProtocolFixtures::$testArgsJSON['testString2']
+ );
+ $args = new \ThriftTest\ThriftTest_testString_args();
+ $args->read($this->protocol);
+
+ $actual = $args->thing;
+ $expected = Fixtures::$testArgs['testString2'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testString3Write()
+ {
+ $args = new \ThriftTest\ThriftTest_testString_args();
+ $args->thing = Fixtures::$testArgs['testString3'];
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TJSONProtocolFixtures::$testArgsJSON['testString3'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testString4Write()
+ {
+ $args = new \ThriftTest\ThriftTest_testString_args();
+ $args->thing = Fixtures::$testArgs['testUnicodeStringWithNonBMP'];
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TJSONProtocolFixtures::$testArgsJSON['testUnicodeStringWithNonBMP'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testDoubleRead()
+ {
+ $this->transport->write(
+ TJSONProtocolFixtures::$testArgsJSON['testDouble']
+ );
+ $args = new \ThriftTest\ThriftTest_testDouble_args();
+ $args->read($this->protocol);
+
+ $actual = $args->thing;
+ $expected = Fixtures::$testArgs['testDouble'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testByteRead()
+ {
+ $this->transport->write(
+ TJSONProtocolFixtures::$testArgsJSON['testByte']
+ );
+ $args = new \ThriftTest\ThriftTest_testByte_args();
+ $args->read($this->protocol);
+
+ $actual = $args->thing;
+ $expected = Fixtures::$testArgs['testByte'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testI32Read()
+ {
+ $this->transport->write(
+ TJSONProtocolFixtures::$testArgsJSON['testI32']
+ );
+ $args = new \ThriftTest\ThriftTest_testI32_args();
+ $args->read($this->protocol);
+
+ $actual = $args->thing;
+ $expected = Fixtures::$testArgs['testI32'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testI64Read()
+ {
+ $this->transport->write(
+ TJSONProtocolFixtures::$testArgsJSON['testI64']
+ );
+ $args = new \ThriftTest\ThriftTest_testI64_args();
+ $args->read($this->protocol);
+
+ $actual = $args->thing;
+ $expected = Fixtures::$testArgs['testI64'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testStructRead()
+ {
+ $this->transport->write(
+ TJSONProtocolFixtures::$testArgsJSON['testStruct']
+ );
+ $args = new \ThriftTest\ThriftTest_testStruct_args();
+ $args->read($this->protocol);
+
+ $actual = $args->thing;
+ $expected = Fixtures::$testArgs['testStruct'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testNestRead()
+ {
+ $this->transport->write(
+ TJSONProtocolFixtures::$testArgsJSON['testNest']
+ );
+ $args = new \ThriftTest\ThriftTest_testNest_args();
+ $args->read($this->protocol);
+
+ $actual = $args->thing;
+ $expected = Fixtures::$testArgs['testNest'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testMapRead()
+ {
+ $this->transport->write(
+ TJSONProtocolFixtures::$testArgsJSON['testMap']
+ );
+ $args = new \ThriftTest\ThriftTest_testMap_args();
+ $args->read($this->protocol);
+
+ $actual = $args->thing;
+ $expected = Fixtures::$testArgs['testMap'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testStringMapRead()
+ {
+ $this->transport->write(
+ TJSONProtocolFixtures::$testArgsJSON['testStringMap']
+ );
+ $args = new \ThriftTest\ThriftTest_testStringMap_args();
+ $args->read($this->protocol);
+
+ $actual = $args->thing;
+ $expected = Fixtures::$testArgs['testStringMap'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testSetRead()
+ {
+ $this->transport->write(
+ TJSONProtocolFixtures::$testArgsJSON['testSet']
+ );
+ $args = new \ThriftTest\ThriftTest_testSet_args();
+ $args->read($this->protocol);
+
+ $actual = $args->thing;
+ $expected = Fixtures::$testArgs['testSet'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testListRead()
+ {
+ $this->transport->write(
+ TJSONProtocolFixtures::$testArgsJSON['testList']
+ );
+ $args = new \ThriftTest\ThriftTest_testList_args();
+ $args->read($this->protocol);
+
+ $actual = $args->thing;
+ $expected = Fixtures::$testArgs['testList'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testEnumRead()
+ {
+ $this->transport->write(
+ TJSONProtocolFixtures::$testArgsJSON['testEnum']
+ );
+ $args = new \ThriftTest\ThriftTest_testEnum_args();
+ $args->read($this->protocol);
+
+ $actual = $args->thing;
+ $expected = Fixtures::$testArgs['testEnum'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testTypedefRead()
+ {
+ $this->transport->write(
+ TJSONProtocolFixtures::$testArgsJSON['testTypedef']
+ );
+ $args = new \ThriftTest\ThriftTest_testTypedef_args();
+ $args->read($this->protocol);
+
+ $actual = $args->thing;
+ $expected = Fixtures::$testArgs['testTypedef'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testMapMapRead()
+ {
+ $this->transport->write(
+ TJSONProtocolFixtures::$testArgsJSON['testMapMap']
+ );
+ $result = new \ThriftTest\ThriftTest_testMapMap_result();
+ $result->read($this->protocol);
+
+ $actual = $result->success;
+ $expected = Fixtures::$testArgs['testMapMapExpectedResult'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testInsanityRead()
+ {
+ $this->transport->write(
+ TJSONProtocolFixtures::$testArgsJSON['testInsanity']
+ );
+ $result = new \ThriftTest\ThriftTest_testInsanity_result();
+ $result->read($this->protocol);
+
+ $actual = $result->success;
+ $expected = Fixtures::$testArgs['testInsanityExpectedResult'];
+
+ $this->assertEquals($expected, $actual);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/test/Protocol/TSimpleJSONProtocolFixtures.php b/src/jaegertracing/thrift/lib/php/test/Protocol/TSimpleJSONProtocolFixtures.php
new file mode 100644
index 000000000..547fd8662
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/test/Protocol/TSimpleJSONProtocolFixtures.php
@@ -0,0 +1,67 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.test
+ */
+
+namespace test\Thrift\Protocol;
+
+class TSimpleJSONProtocolFixtures
+{
+ public static $testArgsJSON = array();
+
+ public static function populateTestArgsSimpleJSON()
+ {
+ self::$testArgsJSON['testVoid'] = '{}';
+
+ self::$testArgsJSON['testString1'] = '{"thing":"Afrikaans, Alemannisch, Aragon\u00e9s, \u0627\u0644\u0639\u0631\u0628\u064a\u0629, \u0645\u0635\u0631\u0649, Asturianu, Aymar aru, Az\u0259rbaycan, \u0411\u0430\u0448\u04a1\u043e\u0440\u0442, Boarisch, \u017demait\u0117\u0161ka, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430), \u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438, Bamanankan, \u09ac\u09be\u0982\u09b2\u09be, Brezhoneg, Bosanski, Catal\u00e0, M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304, \u041d\u043e\u0445\u0447\u0438\u0439\u043d, Cebuano, \u13e3\u13b3\u13a9, \u010cesky, \u0421\u043b\u043e\u0432\u0463\u0301\u043d\u044c\u0441\u043a\u044a \/ \u2c14\u2c0e\u2c11\u2c02\u2c21\u2c10\u2c20\u2c14\u2c0d\u2c1f, \u0427\u04d1\u0432\u0430\u0448\u043b\u0430, Cymraeg, Dansk, Zazaki, \u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0, \u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac, Emili\u00e0n e rumagn\u00f2l, English, Esperanto, Espa\u00f1ol, Eesti, Euskara, \u0641\u0627\u0631\u0633\u06cc, Suomi, V\u00f5ro, F\u00f8royskt, Fran\u00e7ais, Arpetan, Furlan, Frysk, Gaeilge, \u8d1b\u8a9e, G\u00e0idhlig, Galego, Ava\u00f1e\'\u1ebd, \u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0, Gaelg, \u05e2\u05d1\u05e8\u05d9\u05ea, \u0939\u093f\u0928\u094d\u0926\u0940, Fiji Hindi, Hrvatski, Krey\u00f2l ayisyen, Magyar, \u0540\u0561\u0575\u0565\u0580\u0565\u0576, Interlingua, Bahasa Indonesia, Ilokano, Ido, \u00cdslenska, Italiano, \u65e5\u672c\u8a9e, Lojban, Basa Jawa, \u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8, Kongo, Kalaallisut, \u0c95\u0ca8\u0ccd\u0ca8\u0ca1, \ud55c\uad6d\uc5b4, \u041a\u044a\u0430\u0440\u0430\u0447\u0430\u0439-\u041c\u0430\u043b\u043a\u044a\u0430\u0440, Ripoarisch, Kurd\u00ee, \u041a\u043e\u043c\u0438, Kernewek, \u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430, Latina, Ladino, L\u00ebtzebuergesch, Limburgs, Ling\u00e1la, \u0ea5\u0eb2\u0ea7, Lietuvi\u0173, Latvie\u0161u, Basa Banyumasan, Malagasy, \u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438, \u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02, \u092e\u0930\u093e\u0920\u0940, Bahasa Melayu, \u0645\u0627\u0632\u0650\u0631\u0648\u0646\u06cc, Nnapulitano, Nedersaksisch, \u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e, Nederlands, \u202aNorsk (nynorsk)\u202c, \u202aNorsk (bokm\u00e5l)\u202c, Nouormand, Din\u00e9 bizaad, Occitan, \u0418\u0440\u043e\u043d\u0430\u0443, Papiamentu, Deitsch, Norfuk \/ Pitkern, Polski, \u067e\u0646\u062c\u0627\u0628\u06cc, \u067e\u069a\u062a\u0648, Portugu\u00eas, Runa Simi, Rumantsch, Romani, Rom\u00e2n\u0103, \u0420\u0443\u0441\u0441\u043a\u0438\u0439, \u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430, Sardu, Sicilianu, Scots, S\u00e1megiella, Simple English, Sloven\u010dina, Sloven\u0161\u010dina, \u0421\u0440\u043f\u0441\u043a\u0438 \/ Srpski, Seeltersk, Svenska, Kiswahili, \u0ba4\u0bae\u0bbf\u0bb4\u0bcd, \u0c24\u0c46\u0c32\u0c41\u0c17\u0c41, \u0422\u043e\u04b7\u0438\u043a\u04e3, \u0e44\u0e17\u0e22, T\u00fcrkmen\u00e7e, Tagalog, T\u00fcrk\u00e7e, \u0422\u0430\u0442\u0430\u0440\u0447\u0430\/Tatar\u00e7a, \u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430, \u0627\u0631\u062f\u0648, Ti\u1ebfng Vi\u1ec7t, Volap\u00fck, Walon, Winaray, \u5434\u8bed, isiXhosa, \u05d9\u05d9\u05b4\u05d3\u05d9\u05e9, Yor\u00f9b\u00e1, Ze\u00eauws, \u4e2d\u6587, B\u00e2n-l\u00e2m-g\u00fa, \u7cb5\u8a9e"}';
+
+ self::$testArgsJSON['testString2'] = '{"thing":"quote: \\\\\" backslash: forwardslash-escaped: \\\\\/ backspace: \\\\b formfeed: \f newline: \n return: \r tab: now-all-of-them-together: \"\\\\\\\\\/\\\\b\n\r\t now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><"}';
+
+ self::$testArgsJSON['testDouble'] = '{"thing":3.1415926535898}';
+
+ self::$testArgsJSON['testByte'] = '{"thing":1}';
+
+ self::$testArgsJSON['testI32'] = '{"thing":1073741824}';
+
+ if (PHP_INT_SIZE == 8) {
+ self::$testArgsJSON['testI64'] = '{"thing":' . pow(2, 60) . '}';
+ self::$testArgsJSON['testStruct'] = '{"thing":{"string_thing":"worked","byte_thing":1,"i32_thing":1073741824,"i64_thing":' . pow(2, 60) . '}}';
+ self::$testArgsJSON['testNest'] = '{"thing":{"byte_thing":1,"struct_thing":{"string_thing":"worked","byte_thing":1,"i32_thing":1073741824,"i64_thing":' . pow(2, 60) . '},"i32_thing":32768}}';
+ } else {
+ self::$testArgsJSON['testI64'] = '{"thing":1152921504606847000}';
+
+ self::$testArgsJSON['testStruct'] = '{"thing":{"string_thing":"worked","byte_thing":1,"i32_thing":1073741824,"i64_thing":1152921504606847000}}';
+ self::$testArgsJSON['testNest'] = '{"thing":{"byte_thing":1,"struct_thing":{"string_thing":"worked","byte_thing":1,"i32_thing":1073741824,"i64_thing":1152921504606847000},"i32_thing":32768}}';
+ }
+
+ self::$testArgsJSON['testMap'] = '{"thing":{"7":77,"8":88,"9":99}}';
+
+ self::$testArgsJSON['testStringMap'] = '{"thing":{"a":"123","a b":"with spaces ","same":"same","0":"numeric key","longValue":"Afrikaans, Alemannisch, Aragon\u00e9s, \u0627\u0644\u0639\u0631\u0628\u064a\u0629, \u0645\u0635\u0631\u0649, Asturianu, Aymar aru, Az\u0259rbaycan, \u0411\u0430\u0448\u04a1\u043e\u0440\u0442, Boarisch, \u017demait\u0117\u0161ka, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430), \u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438, Bamanankan, \u09ac\u09be\u0982\u09b2\u09be, Brezhoneg, Bosanski, Catal\u00e0, M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304, \u041d\u043e\u0445\u0447\u0438\u0439\u043d, Cebuano, \u13e3\u13b3\u13a9, \u010cesky, \u0421\u043b\u043e\u0432\u0463\u0301\u043d\u044c\u0441\u043a\u044a \/ \u2c14\u2c0e\u2c11\u2c02\u2c21\u2c10\u2c20\u2c14\u2c0d\u2c1f, \u0427\u04d1\u0432\u0430\u0448\u043b\u0430, Cymraeg, Dansk, Zazaki, \u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0, \u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac, Emili\u00e0n e rumagn\u00f2l, English, Esperanto, Espa\u00f1ol, Eesti, Euskara, \u0641\u0627\u0631\u0633\u06cc, Suomi, V\u00f5ro, F\u00f8royskt, Fran\u00e7ais, Arpetan, Furlan, Frysk, Gaeilge, \u8d1b\u8a9e, G\u00e0idhlig, Galego, Ava\u00f1e\'\u1ebd, \u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0, Gaelg, \u05e2\u05d1\u05e8\u05d9\u05ea, \u0939\u093f\u0928\u094d\u0926\u0940, Fiji Hindi, Hrvatski, Krey\u00f2l ayisyen, Magyar, \u0540\u0561\u0575\u0565\u0580\u0565\u0576, Interlingua, Bahasa Indonesia, Ilokano, Ido, \u00cdslenska, Italiano, \u65e5\u672c\u8a9e, Lojban, Basa Jawa, \u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8, Kongo, Kalaallisut, \u0c95\u0ca8\u0ccd\u0ca8\u0ca1, \ud55c\uad6d\uc5b4, \u041a\u044a\u0430\u0440\u0430\u0447\u0430\u0439-\u041c\u0430\u043b\u043a\u044a\u0430\u0440, Ripoarisch, Kurd\u00ee, \u041a\u043e\u043c\u0438, Kernewek, \u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430, Latina, Ladino, L\u00ebtzebuergesch, Limburgs, Ling\u00e1la, \u0ea5\u0eb2\u0ea7, Lietuvi\u0173, Latvie\u0161u, Basa Banyumasan, Malagasy, \u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438, \u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02, \u092e\u0930\u093e\u0920\u0940, Bahasa Melayu, \u0645\u0627\u0632\u0650\u0631\u0648\u0646\u06cc, Nnapulitano, Nedersaksisch, \u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e, Nederlands, \u202aNorsk (nynorsk)\u202c, \u202aNorsk (bokm\u00e5l)\u202c, Nouormand, Din\u00e9 bizaad, Occitan, \u0418\u0440\u043e\u043d\u0430\u0443, Papiamentu, Deitsch, Norfuk \/ Pitkern, Polski, \u067e\u0646\u062c\u0627\u0628\u06cc, \u067e\u069a\u062a\u0648, Portugu\u00eas, Runa Simi, Rumantsch, Romani, Rom\u00e2n\u0103, \u0420\u0443\u0441\u0441\u043a\u0438\u0439, \u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430, Sardu, Sicilianu, Scots, S\u00e1megiella, Simple English, Sloven\u010dina, Sloven\u0161\u010dina, \u0421\u0440\u043f\u0441\u043a\u0438 \/ Srpski, Seeltersk, Svenska, Kiswahili, \u0ba4\u0bae\u0bbf\u0bb4\u0bcd, \u0c24\u0c46\u0c32\u0c41\u0c17\u0c41, \u0422\u043e\u04b7\u0438\u043a\u04e3, \u0e44\u0e17\u0e22, T\u00fcrkmen\u00e7e, Tagalog, T\u00fcrk\u00e7e, \u0422\u0430\u0442\u0430\u0440\u0447\u0430\/Tatar\u00e7a, \u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430, \u0627\u0631\u062f\u0648, Ti\u1ebfng Vi\u1ec7t, Volap\u00fck, Walon, Winaray, \u5434\u8bed, isiXhosa, \u05d9\u05d9\u05b4\u05d3\u05d9\u05e9, Yor\u00f9b\u00e1, Ze\u00eauws, \u4e2d\u6587, B\u00e2n-l\u00e2m-g\u00fa, \u7cb5\u8a9e","Afrikaans, Alemannisch, Aragon\u00e9s, \u0627\u0644\u0639\u0631\u0628\u064a\u0629, \u0645\u0635\u0631\u0649, Asturianu, Aymar aru, Az\u0259rbaycan, \u0411\u0430\u0448\u04a1\u043e\u0440\u0442, Boarisch, \u017demait\u0117\u0161ka, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430), \u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438, Bamanankan, \u09ac\u09be\u0982\u09b2\u09be, Brezhoneg, Bosanski, Catal\u00e0, M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304, \u041d\u043e\u0445\u0447\u0438\u0439\u043d, Cebuano, \u13e3\u13b3\u13a9, \u010cesky, \u0421\u043b\u043e\u0432\u0463\u0301\u043d\u044c\u0441\u043a\u044a \/ \u2c14\u2c0e\u2c11\u2c02\u2c21\u2c10\u2c20\u2c14\u2c0d\u2c1f, \u0427\u04d1\u0432\u0430\u0448\u043b\u0430, Cymraeg, Dansk, Zazaki, \u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0, \u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac, Emili\u00e0n e rumagn\u00f2l, English, Esperanto, Espa\u00f1ol, Eesti, Euskara, \u0641\u0627\u0631\u0633\u06cc, Suomi, V\u00f5ro, F\u00f8royskt, Fran\u00e7ais, Arpetan, Furlan, Frysk, Gaeilge, \u8d1b\u8a9e, G\u00e0idhlig, Galego, Ava\u00f1e\'\u1ebd, \u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0, Gaelg, \u05e2\u05d1\u05e8\u05d9\u05ea, \u0939\u093f\u0928\u094d\u0926\u0940, Fiji Hindi, Hrvatski, Krey\u00f2l ayisyen, Magyar, \u0540\u0561\u0575\u0565\u0580\u0565\u0576, Interlingua, Bahasa Indonesia, Ilokano, Ido, \u00cdslenska, Italiano, \u65e5\u672c\u8a9e, Lojban, Basa Jawa, \u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8, Kongo, Kalaallisut, \u0c95\u0ca8\u0ccd\u0ca8\u0ca1, \ud55c\uad6d\uc5b4, \u041a\u044a\u0430\u0440\u0430\u0447\u0430\u0439-\u041c\u0430\u043b\u043a\u044a\u0430\u0440, Ripoarisch, Kurd\u00ee, \u041a\u043e\u043c\u0438, Kernewek, \u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430, Latina, Ladino, L\u00ebtzebuergesch, Limburgs, Ling\u00e1la, \u0ea5\u0eb2\u0ea7, Lietuvi\u0173, Latvie\u0161u, Basa Banyumasan, Malagasy, \u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438, \u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02, \u092e\u0930\u093e\u0920\u0940, Bahasa Melayu, \u0645\u0627\u0632\u0650\u0631\u0648\u0646\u06cc, Nnapulitano, Nedersaksisch, \u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e, Nederlands, \u202aNorsk (nynorsk)\u202c, \u202aNorsk (bokm\u00e5l)\u202c, Nouormand, Din\u00e9 bizaad, Occitan, \u0418\u0440\u043e\u043d\u0430\u0443, Papiamentu, Deitsch, Norfuk \/ Pitkern, Polski, \u067e\u0646\u062c\u0627\u0628\u06cc, \u067e\u069a\u062a\u0648, Portugu\u00eas, Runa Simi, Rumantsch, Romani, Rom\u00e2n\u0103, \u0420\u0443\u0441\u0441\u043a\u0438\u0439, \u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430, Sardu, Sicilianu, Scots, S\u00e1megiella, Simple English, Sloven\u010dina, Sloven\u0161\u010dina, \u0421\u0440\u043f\u0441\u043a\u0438 \/ Srpski, Seeltersk, Svenska, Kiswahili, \u0ba4\u0bae\u0bbf\u0bb4\u0bcd, \u0c24\u0c46\u0c32\u0c41\u0c17\u0c41, \u0422\u043e\u04b7\u0438\u043a\u04e3, \u0e44\u0e17\u0e22, T\u00fcrkmen\u00e7e, Tagalog, T\u00fcrk\u00e7e, \u0422\u0430\u0442\u0430\u0440\u0447\u0430\/Tatar\u00e7a, \u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430, \u0627\u0631\u062f\u0648, Ti\u1ebfng Vi\u1ec7t, Volap\u00fck, Walon, Winaray, \u5434\u8bed, isiXhosa, \u05d9\u05d9\u05b4\u05d3\u05d9\u05e9, Yor\u00f9b\u00e1, Ze\u00eauws, \u4e2d\u6587, B\u00e2n-l\u00e2m-g\u00fa, \u7cb5\u8a9e":"long key"}}';
+
+ self::$testArgsJSON['testSet'] = '{"thing":[1,5,6]}';
+
+ self::$testArgsJSON['testList'] = '{"thing":[1,2,3]}';
+
+ self::$testArgsJSON['testEnum'] = '{"thing":1}';
+
+ self::$testArgsJSON['testTypedef'] = '{"thing":69}';
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/test/Protocol/TSimpleJSONProtocolTest.php b/src/jaegertracing/thrift/lib/php/test/Protocol/TSimpleJSONProtocolTest.php
new file mode 100644
index 000000000..e4a13736e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/test/Protocol/TSimpleJSONProtocolTest.php
@@ -0,0 +1,254 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift.test
+ */
+
+namespace Test\Thrift\Protocol;
+
+use PHPUnit\Framework\TestCase;
+use Test\Thrift\Fixtures;
+use Thrift\Protocol\TSimpleJSONProtocol;
+use Thrift\Transport\TMemoryBuffer;
+
+require __DIR__ . '/../../../../vendor/autoload.php';
+
+/***
+ * This test suite depends on running the compiler against the
+ * standard ThriftTest.thrift file:
+ *
+ * lib/php/test$ ../../../compiler/cpp/thrift --gen php -r \
+ * --out ./packages ../../../test/ThriftTest.thrift
+ *
+ * @runTestsInSeparateProcesses
+ */
+class TSimpleJSONProtocolTest extends TestCase
+{
+ private $transport;
+ private $protocol;
+
+ public static function setUpBeforeClass()
+ {
+
+ /** @var \Composer\Autoload\ClassLoader $loader */
+ $loader = require __DIR__ . '/../../../../vendor/autoload.php';
+ $loader->addPsr4('', __DIR__ . '/../packages/php');
+
+ Fixtures::populateTestArgs();
+ TSimpleJSONProtocolFixtures::populateTestArgsSimpleJSON();
+ }
+
+ public function setUp()
+ {
+ $this->transport = new TMemoryBuffer();
+ $this->protocol = new TSimpleJSONProtocol($this->transport);
+ $this->transport->open();
+ }
+
+ /**
+ * WRITE TESTS
+ */
+ public function testVoidWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testVoid_args();
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testVoid'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testString1Write()
+ {
+ $args = new \ThriftTest\ThriftTest_testString_args();
+ $args->thing = Fixtures::$testArgs['testString1'];
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testString1'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testString2Write()
+ {
+ $args = new \ThriftTest\ThriftTest_testString_args();
+ $args->thing = Fixtures::$testArgs['testString2'];
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testString2'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testDoubleWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testDouble_args();
+ $args->thing = Fixtures::$testArgs['testDouble'];
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testDouble'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testByteWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testByte_args();
+ $args->thing = Fixtures::$testArgs['testByte'];
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testByte'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testI32Write()
+ {
+ $args = new \ThriftTest\ThriftTest_testI32_args();
+ $args->thing = Fixtures::$testArgs['testI32'];
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testI32'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testI64Write()
+ {
+ $args = new \ThriftTest\ThriftTest_testI64_args();
+ $args->thing = Fixtures::$testArgs['testI64'];
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testI64'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testStructWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testStruct_args();
+ $args->thing = Fixtures::$testArgs['testStruct'];
+
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testStruct'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testNestWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testNest_args();
+ $args->thing = Fixtures::$testArgs['testNest'];
+
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testNest'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testMapWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testMap_args();
+ $args->thing = Fixtures::$testArgs['testMap'];
+
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testMap'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testStringMapWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testStringMap_args();
+ $args->thing = Fixtures::$testArgs['testStringMap'];
+
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testStringMap'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testSetWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testSet_args();
+ $args->thing = Fixtures::$testArgs['testSet'];
+
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testSet'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testListWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testList_args();
+ $args->thing = Fixtures::$testArgs['testList'];
+
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testList'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testEnumWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testEnum_args();
+ $args->thing = Fixtures::$testArgs['testEnum'];
+
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testEnum'];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testTypedefWrite()
+ {
+ $args = new \ThriftTest\ThriftTest_testTypedef_args();
+ $args->thing = Fixtures::$testArgs['testTypedef'];
+
+ $args->write($this->protocol);
+
+ $actual = $this->transport->read(Fixtures::$bufsize);
+ $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testTypedef'];
+
+ $this->assertEquals($expected, $actual);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/test/TestValidators.thrift b/src/jaegertracing/thrift/lib/php/test/TestValidators.thrift
new file mode 100644
index 000000000..9c38d92af
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/test/TestValidators.thrift
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace php TestValidators
+
+include "../../../test/ThriftTest.thrift"
+
+union UnionOfStrings {
+ 1: string aa;
+ 2: string bb;
+}
+
+service TestService {
+ void test() throws(1: ThriftTest.Xception xception);
+}
diff --git a/src/jaegertracing/thrift/lib/php/test/Validator/BaseValidatorTest.php b/src/jaegertracing/thrift/lib/php/test/Validator/BaseValidatorTest.php
new file mode 100644
index 000000000..60290830e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/test/Validator/BaseValidatorTest.php
@@ -0,0 +1,154 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Test\Thrift;
+
+use PHPUnit\Framework\TestCase;
+use Thrift\Exception\TProtocolException;
+use Thrift\Protocol\TBinaryProtocol;
+use Thrift\Transport\TMemoryBuffer;
+
+abstract class BaseValidatorTest extends TestCase
+{
+ public function testEmptyStructValidator()
+ {
+ $this->assertNoReadValidator('ThriftTest\EmptyStruct');
+ $this->assertNoWriteValidator('ThriftTest\EmptyStruct');
+ }
+
+ public function testBonkValidator()
+ {
+ $this->assertNoReadValidator('ThriftTest\Bonk');
+ $this->assertHasWriteValidator('ThriftTest\Bonk');
+ }
+
+ public function testStructAValidator()
+ {
+ $this->assertHasReadValidator('ThriftTest\StructA');
+ $this->assertHasWriteValidator('ThriftTest\StructA');
+ }
+
+ public function testUnionOfStringsValidator()
+ {
+ $this->assertNoWriteValidator('TestValidators\UnionOfStrings');
+ }
+
+ public function testServiceResultValidator()
+ {
+ $this->assertNoReadValidator('TestValidators\TestService_test_result');
+ $this->assertNoWriteValidator('TestValidators\TestService_test_result');
+ }
+
+ public function testReadEmpty()
+ {
+ $bonk = new \ThriftTest\Bonk();
+ $transport = new TMemoryBuffer("\000");
+ $protocol = new TBinaryProtocol($transport);
+ $bonk->read($protocol);
+ }
+
+ public function testWriteEmpty()
+ {
+ $bonk = new \ThriftTest\Bonk();
+ $transport = new TMemoryBuffer();
+ $protocol = new TBinaryProtocol($transport);
+ try {
+ $bonk->write($protocol);
+ $this->fail('Bonk was able to write an empty object');
+ } catch (TProtocolException $e) {
+ }
+ }
+
+ public function testWriteWithMissingRequired()
+ {
+ // Check that we are not able to write StructA with a missing required field
+ $structa = new \ThriftTest\StructA();
+ $transport = new TMemoryBuffer();
+ $protocol = new TBinaryProtocol($transport);
+
+ try {
+ $structa->write($protocol);
+ $this->fail('StructA was able to write an empty object');
+ } catch (TProtocolException $e) {
+ }
+ }
+
+ public function testReadStructA()
+ {
+ $transport = new TMemoryBuffer(base64_decode('CwABAAAAA2FiYwA='));
+ $protocol = new TBinaryProtocol($transport);
+ $structa = new \ThriftTest\StructA();
+ $structa->read($protocol);
+ $this->assertEquals("abc", $structa->s);
+ }
+
+ public function testWriteStructA()
+ {
+ $transport = new TMemoryBuffer();
+ $protocol = new TBinaryProtocol($transport);
+ $structa = new \ThriftTest\StructA();
+ $structa->s = "abc";
+ $structa->write($protocol);
+ $writeResult = base64_encode($transport->getBuffer());
+ $this->assertEquals('CwABAAAAA2FiYwA=', $writeResult);
+ }
+
+ protected static function assertHasReadValidator($class)
+ {
+ if (!static::hasReadValidator($class)) {
+ static::fail($class . ' class should have a read validator');
+ }
+ }
+
+ protected static function assertNoReadValidator($class)
+ {
+ if (static::hasReadValidator($class)) {
+ static::fail($class . ' class should not have a write validator');
+ }
+ }
+
+ protected static function assertHasWriteValidator($class)
+ {
+ if (!static::hasWriteValidator($class)) {
+ static::fail($class . ' class should have a write validator');
+ }
+ }
+
+ protected static function assertNoWriteValidator($class)
+ {
+ if (static::hasWriteValidator($class)) {
+ static::fail($class . ' class should not have a write validator');
+ }
+ }
+
+ private static function hasReadValidator($class)
+ {
+ $rc = new \ReflectionClass($class);
+
+ return $rc->hasMethod('_validateForRead');
+ }
+
+ private static function hasWriteValidator($class)
+ {
+ $rc = new \ReflectionClass($class);
+
+ return $rc->hasMethod('_validateForWrite');
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/test/Validator/ValidatorTest.php b/src/jaegertracing/thrift/lib/php/test/Validator/ValidatorTest.php
new file mode 100644
index 000000000..fa6c7a9f7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/test/Validator/ValidatorTest.php
@@ -0,0 +1,41 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Test\Thrift;
+
+require __DIR__ . '/../../../../vendor/autoload.php';
+
+use Thrift\ClassLoader\ThriftClassLoader;
+
+/**
+ * Class TestValidators
+ * @package Test\Thrift
+ *
+ * @runTestsInSeparateProcesses
+ */
+class ValidatorTest extends BaseValidatorTest
+{
+ public function setUp()
+ {
+ /** @var \Composer\Autoload\ClassLoader $loader */
+ $loader = require __DIR__ . '/../../../../vendor/autoload.php';
+ $loader->addPsr4('', __DIR__ . '/../packages/phpv');
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/test/Validator/ValidatorTestOop.php b/src/jaegertracing/thrift/lib/php/test/Validator/ValidatorTestOop.php
new file mode 100644
index 000000000..93bca4d0c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/test/Validator/ValidatorTestOop.php
@@ -0,0 +1,41 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Test\Thrift;
+
+require_once __DIR__ . '/../../../../vendor/autoload.php';
+
+use Thrift\ClassLoader\ThriftClassLoader;
+
+/**
+ * Class TestValidatorsOop
+ * @package Test\Thrift
+ *
+ * @runTestsInSeparateProcesses
+ */
+class ValidatorTestOop extends BaseValidatorTest
+{
+ public function setUp()
+ {
+ /** @var \Composer\Autoload\ClassLoader $loader */
+ $loader = require __DIR__ . '/../../../../vendor/autoload.php';
+ $loader->addPsr4('', __DIR__ . '/../packages/phpvo');
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/php/thrift_protocol.ini b/src/jaegertracing/thrift/lib/php/thrift_protocol.ini
new file mode 100644
index 000000000..a260d83b0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/php/thrift_protocol.ini
@@ -0,0 +1 @@
+extension=thrift_protocol.so
diff --git a/src/jaegertracing/thrift/lib/py/CMakeLists.txt b/src/jaegertracing/thrift/lib/py/CMakeLists.txt
new file mode 100644
index 000000000..7bb91fe67
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/CMakeLists.txt
@@ -0,0 +1,31 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+include_directories(${PYTHON_INCLUDE_DIRS})
+
+add_custom_target(python_build ALL
+ COMMAND ${PYTHON_EXECUTABLE} setup.py build
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMENT "Building Python library"
+)
+
+if(BUILD_TESTING)
+ add_test(PythonTestSSLSocket ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/test_sslsocket.py)
+ add_test(PythonThriftJson ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/thrift_json.py)
+endif()
diff --git a/src/jaegertracing/thrift/lib/py/MANIFEST.in b/src/jaegertracing/thrift/lib/py/MANIFEST.in
new file mode 100644
index 000000000..af54e29dc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/MANIFEST.in
@@ -0,0 +1 @@
+include src/ext/*
diff --git a/src/jaegertracing/thrift/lib/py/Makefile.am b/src/jaegertracing/thrift/lib/py/Makefile.am
new file mode 100644
index 000000000..46e44054b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/Makefile.am
@@ -0,0 +1,65 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+AUTOMAKE_OPTIONS = serial-tests
+DESTDIR ?= /
+
+if WITH_PY3
+py3-build:
+ $(PYTHON3) setup.py build
+py3-test: py3-build
+ $(PYTHON3) test/thrift_json.py
+ $(PYTHON3) test/test_sslsocket.py
+else
+py3-build:
+py3-test:
+endif
+
+all-local: py3-build
+ $(PYTHON) setup.py build
+
+# We're ignoring prefix here because site-packages seems to be
+# the equivalent of /usr/local/lib in Python land.
+# Old version (can't put inline because it's not portable).
+#$(PYTHON) setup.py install --prefix=$(prefix) --root=$(DESTDIR) $(PYTHON_SETUPUTIL_ARGS)
+install-exec-hook:
+ $(PYTHON) setup.py install --root=$(DESTDIR) --prefix=$(PY_PREFIX) $(PYTHON_SETUPUTIL_ARGS)
+
+check-local: all py3-test
+ $(PYTHON) test/thrift_json.py
+ $(PYTHON) test/test_sslsocket.py
+
+clean-local:
+ $(RM) -r build
+ find . -type f \( -iname "*.pyc" \) | xargs rm -f
+ find . -type d \( -iname "__pycache__" -or -iname "_trial_temp" \) | xargs rm -rf
+
+dist-hook:
+ find $(distdir) -type f \( -iname "*.pyc" \) | xargs rm -f
+ find $(distdir) -type d \( -iname "__pycache__" -or -iname "_trial_temp" \) | xargs rm -rf
+
+EXTRA_DIST = \
+ CMakeLists.txt \
+ MANIFEST.in \
+ coding_standards.md \
+ compat \
+ setup.py \
+ setup.cfg \
+ src \
+ test \
+ README.md
diff --git a/src/jaegertracing/thrift/lib/py/README.md b/src/jaegertracing/thrift/lib/py/README.md
new file mode 100644
index 000000000..29b8c73c4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/README.md
@@ -0,0 +1,35 @@
+Thrift Python Software Library
+
+License
+=======
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+Using Thrift with Python
+========================
+
+Thrift is provided as a set of Python packages. The top level package is
+thrift, and there are subpackages for the protocol, transport, and server
+code. Each package contains modules using standard Thrift naming conventions
+(i.e. TProtocol, TTransport) and implementations in corresponding modules
+(i.e. TSocket). There is also a subpackage reflection, which contains
+the generated code for the reflection structures.
+
+The Python libraries can be installed manually using the provided setup.py
+file, or automatically using the install hook provided via autoconf/automake.
+To use the latter, become superuser and do make install.
diff --git a/src/jaegertracing/thrift/lib/py/coding_standards.md b/src/jaegertracing/thrift/lib/py/coding_standards.md
new file mode 100644
index 000000000..4c560b524
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/coding_standards.md
@@ -0,0 +1,7 @@
+## Python Coding Standards
+
+Please follow:
+ * [Thrift General Coding Standards](/doc/coding_standards.md)
+ * Code Style for Python Code [PEP8](http://legacy.python.org/dev/peps/pep-0008/)
+
+When in doubt - check with <http://www.pylint.org/> or online with <http://pep8online.com>.
diff --git a/src/jaegertracing/thrift/lib/py/compat/win32/stdint.h b/src/jaegertracing/thrift/lib/py/compat/win32/stdint.h
new file mode 100644
index 000000000..d02608a59
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/compat/win32/stdint.h
@@ -0,0 +1,247 @@
+// ISO C9x compliant stdint.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
+//
+// Copyright (c) 2006-2008 Alexander Chemeris
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// 3. The name of the author may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _MSC_VER // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif // _MSC_VER ]
+
+#ifndef _MSC_STDINT_H_ // [
+#define _MSC_STDINT_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include <limits.h>
+
+// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
+// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
+// or compiler give many errors like this:
+// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+#ifdef __cplusplus
+extern "C" {
+#endif
+# include <wchar.h>
+#ifdef __cplusplus
+}
+#endif
+
+// Define _W64 macros to mark types changing their size, like intptr_t.
+#ifndef _W64
+# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
+# define _W64 __w64
+# else
+# define _W64
+# endif
+#endif
+
+
+// 7.18.1 Integer types
+
+// 7.18.1.1 Exact-width integer types
+
+// Visual Studio 6 and Embedded Visual C++ 4 doesn't
+// realize that, e.g. char has the same size as __int8
+// so we give up on __intX for them.
+#if (_MSC_VER < 1300)
+ typedef signed char int8_t;
+ typedef signed short int16_t;
+ typedef signed int int32_t;
+ typedef unsigned char uint8_t;
+ typedef unsigned short uint16_t;
+ typedef unsigned int uint32_t;
+#else
+ typedef signed __int8 int8_t;
+ typedef signed __int16 int16_t;
+ typedef signed __int32 int32_t;
+ typedef unsigned __int8 uint8_t;
+ typedef unsigned __int16 uint16_t;
+ typedef unsigned __int32 uint32_t;
+#endif
+typedef signed __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+
+
+// 7.18.1.2 Minimum-width integer types
+typedef int8_t int_least8_t;
+typedef int16_t int_least16_t;
+typedef int32_t int_least32_t;
+typedef int64_t int_least64_t;
+typedef uint8_t uint_least8_t;
+typedef uint16_t uint_least16_t;
+typedef uint32_t uint_least32_t;
+typedef uint64_t uint_least64_t;
+
+// 7.18.1.3 Fastest minimum-width integer types
+typedef int8_t int_fast8_t;
+typedef int16_t int_fast16_t;
+typedef int32_t int_fast32_t;
+typedef int64_t int_fast64_t;
+typedef uint8_t uint_fast8_t;
+typedef uint16_t uint_fast16_t;
+typedef uint32_t uint_fast32_t;
+typedef uint64_t uint_fast64_t;
+
+// 7.18.1.4 Integer types capable of holding object pointers
+#ifdef _WIN64 // [
+ typedef signed __int64 intptr_t;
+ typedef unsigned __int64 uintptr_t;
+#else // _WIN64 ][
+ typedef _W64 signed int intptr_t;
+ typedef _W64 unsigned int uintptr_t;
+#endif // _WIN64 ]
+
+// 7.18.1.5 Greatest-width integer types
+typedef int64_t intmax_t;
+typedef uint64_t uintmax_t;
+
+
+// 7.18.2 Limits of specified-width integer types
+
+#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
+
+// 7.18.2.1 Limits of exact-width integer types
+#define INT8_MIN ((int8_t)_I8_MIN)
+#define INT8_MAX _I8_MAX
+#define INT16_MIN ((int16_t)_I16_MIN)
+#define INT16_MAX _I16_MAX
+#define INT32_MIN ((int32_t)_I32_MIN)
+#define INT32_MAX _I32_MAX
+#define INT64_MIN ((int64_t)_I64_MIN)
+#define INT64_MAX _I64_MAX
+#define UINT8_MAX _UI8_MAX
+#define UINT16_MAX _UI16_MAX
+#define UINT32_MAX _UI32_MAX
+#define UINT64_MAX _UI64_MAX
+
+// 7.18.2.2 Limits of minimum-width integer types
+#define INT_LEAST8_MIN INT8_MIN
+#define INT_LEAST8_MAX INT8_MAX
+#define INT_LEAST16_MIN INT16_MIN
+#define INT_LEAST16_MAX INT16_MAX
+#define INT_LEAST32_MIN INT32_MIN
+#define INT_LEAST32_MAX INT32_MAX
+#define INT_LEAST64_MIN INT64_MIN
+#define INT_LEAST64_MAX INT64_MAX
+#define UINT_LEAST8_MAX UINT8_MAX
+#define UINT_LEAST16_MAX UINT16_MAX
+#define UINT_LEAST32_MAX UINT32_MAX
+#define UINT_LEAST64_MAX UINT64_MAX
+
+// 7.18.2.3 Limits of fastest minimum-width integer types
+#define INT_FAST8_MIN INT8_MIN
+#define INT_FAST8_MAX INT8_MAX
+#define INT_FAST16_MIN INT16_MIN
+#define INT_FAST16_MAX INT16_MAX
+#define INT_FAST32_MIN INT32_MIN
+#define INT_FAST32_MAX INT32_MAX
+#define INT_FAST64_MIN INT64_MIN
+#define INT_FAST64_MAX INT64_MAX
+#define UINT_FAST8_MAX UINT8_MAX
+#define UINT_FAST16_MAX UINT16_MAX
+#define UINT_FAST32_MAX UINT32_MAX
+#define UINT_FAST64_MAX UINT64_MAX
+
+// 7.18.2.4 Limits of integer types capable of holding object pointers
+#ifdef _WIN64 // [
+# define INTPTR_MIN INT64_MIN
+# define INTPTR_MAX INT64_MAX
+# define UINTPTR_MAX UINT64_MAX
+#else // _WIN64 ][
+# define INTPTR_MIN INT32_MIN
+# define INTPTR_MAX INT32_MAX
+# define UINTPTR_MAX UINT32_MAX
+#endif // _WIN64 ]
+
+// 7.18.2.5 Limits of greatest-width integer types
+#define INTMAX_MIN INT64_MIN
+#define INTMAX_MAX INT64_MAX
+#define UINTMAX_MAX UINT64_MAX
+
+// 7.18.3 Limits of other integer types
+
+#ifdef _WIN64 // [
+# define PTRDIFF_MIN _I64_MIN
+# define PTRDIFF_MAX _I64_MAX
+#else // _WIN64 ][
+# define PTRDIFF_MIN _I32_MIN
+# define PTRDIFF_MAX _I32_MAX
+#endif // _WIN64 ]
+
+#define SIG_ATOMIC_MIN INT_MIN
+#define SIG_ATOMIC_MAX INT_MAX
+
+#ifndef SIZE_MAX // [
+# ifdef _WIN64 // [
+# define SIZE_MAX _UI64_MAX
+# else // _WIN64 ][
+# define SIZE_MAX _UI32_MAX
+# endif // _WIN64 ]
+#endif // SIZE_MAX ]
+
+// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+#ifndef WCHAR_MIN // [
+# define WCHAR_MIN 0
+#endif // WCHAR_MIN ]
+#ifndef WCHAR_MAX // [
+# define WCHAR_MAX _UI16_MAX
+#endif // WCHAR_MAX ]
+
+#define WINT_MIN 0
+#define WINT_MAX _UI16_MAX
+
+#endif // __STDC_LIMIT_MACROS ]
+
+
+// 7.18.4 Limits of other integer types
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val) val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val) val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+#define INTMAX_C INT64_C
+#define UINTMAX_C UINT64_C
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+
+#endif // _MSC_STDINT_H_ ]
diff --git a/src/jaegertracing/thrift/lib/py/setup.cfg b/src/jaegertracing/thrift/lib/py/setup.cfg
new file mode 100644
index 000000000..c9ed0aec5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/setup.cfg
@@ -0,0 +1,6 @@
+[install]
+optimize = 1
+[metadata]
+description-file = README.md
+[flake8]
+max-line-length = 100
diff --git a/src/jaegertracing/thrift/lib/py/setup.py b/src/jaegertracing/thrift/lib/py/setup.py
new file mode 100644
index 000000000..2ba269159
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/setup.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import sys
+try:
+ from setuptools import setup, Extension
+except Exception:
+ from distutils.core import setup, Extension
+
+from distutils.command.build_ext import build_ext
+from distutils.errors import CCompilerError, DistutilsExecError, DistutilsPlatformError
+
+# Fix to build sdist under vagrant
+import os
+if 'vagrant' in str(os.environ):
+ try:
+ del os.link
+ except AttributeError:
+ pass
+
+include_dirs = ['src']
+if sys.platform == 'win32':
+ include_dirs.append('compat/win32')
+ ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError, IOError)
+else:
+ ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError)
+
+
+class BuildFailed(Exception):
+ pass
+
+
+class ve_build_ext(build_ext):
+ def run(self):
+ try:
+ build_ext.run(self)
+ except DistutilsPlatformError:
+ raise BuildFailed()
+
+ def build_extension(self, ext):
+ try:
+ build_ext.build_extension(self, ext)
+ except ext_errors:
+ raise BuildFailed()
+
+
+def run_setup(with_binary):
+ if with_binary:
+ extensions = dict(
+ ext_modules=[
+ Extension('thrift.protocol.fastbinary',
+ sources=[
+ 'src/ext/module.cpp',
+ 'src/ext/types.cpp',
+ 'src/ext/binary.cpp',
+ 'src/ext/compact.cpp',
+ ],
+ include_dirs=include_dirs,
+ )
+ ],
+ cmdclass=dict(build_ext=ve_build_ext)
+ )
+ else:
+ extensions = dict()
+
+ ssl_deps = []
+ if sys.version_info[0] == 2:
+ ssl_deps.append('ipaddress')
+ if sys.hexversion < 0x03050000:
+ ssl_deps.append('backports.ssl_match_hostname>=3.5')
+ tornado_deps = ['tornado>=4.0']
+ twisted_deps = ['twisted']
+
+ setup(name='thrift',
+ version='0.13.0',
+ description='Python bindings for the Apache Thrift RPC system',
+ author='Apache Thrift Developers',
+ author_email='dev@thrift.apache.org',
+ url='http://thrift.apache.org',
+ license='Apache License 2.0',
+ install_requires=['six>=1.7.2'],
+ extras_require={
+ 'ssl': ssl_deps,
+ 'tornado': tornado_deps,
+ 'twisted': twisted_deps,
+ 'all': ssl_deps + tornado_deps + twisted_deps,
+ },
+ packages=[
+ 'thrift',
+ 'thrift.protocol',
+ 'thrift.transport',
+ 'thrift.server',
+ ],
+ package_dir={'thrift': 'src'},
+ classifiers=[
+ 'Development Status :: 5 - Production/Stable',
+ 'Environment :: Console',
+ 'Intended Audience :: Developers',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 3',
+ 'Topic :: Software Development :: Libraries',
+ 'Topic :: System :: Networking'
+ ],
+ zip_safe=False,
+ **extensions
+ )
+
+
+try:
+ with_binary = True
+ run_setup(with_binary)
+except BuildFailed:
+ print()
+ print('*' * 80)
+ print("An error occurred while trying to compile with the C extension enabled")
+ print("Attempting to build without the extension now")
+ print('*' * 80)
+ print()
+
+ run_setup(False)
diff --git a/src/jaegertracing/thrift/lib/py/src/TMultiplexedProcessor.py b/src/jaegertracing/thrift/lib/py/src/TMultiplexedProcessor.py
new file mode 100644
index 000000000..ff88430bd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/TMultiplexedProcessor.py
@@ -0,0 +1,82 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from thrift.Thrift import TProcessor, TMessageType
+from thrift.protocol import TProtocolDecorator, TMultiplexedProtocol
+from thrift.protocol.TProtocol import TProtocolException
+
+
+class TMultiplexedProcessor(TProcessor):
+ def __init__(self):
+ self.defaultProcessor = None
+ self.services = {}
+
+ def registerDefault(self, processor):
+ """
+ If a non-multiplexed processor connects to the server and wants to
+ communicate, use the given processor to handle it. This mechanism
+ allows servers to upgrade from non-multiplexed to multiplexed in a
+ backwards-compatible way and still handle old clients.
+ """
+ self.defaultProcessor = processor
+
+ def registerProcessor(self, serviceName, processor):
+ self.services[serviceName] = processor
+
+ def on_message_begin(self, func):
+ for key in self.services.keys():
+ self.services[key].on_message_begin(func)
+
+ def process(self, iprot, oprot):
+ (name, type, seqid) = iprot.readMessageBegin()
+ if type != TMessageType.CALL and type != TMessageType.ONEWAY:
+ raise TProtocolException(
+ TProtocolException.NOT_IMPLEMENTED,
+ "TMultiplexedProtocol only supports CALL & ONEWAY")
+
+ index = name.find(TMultiplexedProtocol.SEPARATOR)
+ if index < 0:
+ if self.defaultProcessor:
+ return self.defaultProcessor.process(
+ StoredMessageProtocol(iprot, (name, type, seqid)), oprot)
+ else:
+ raise TProtocolException(
+ TProtocolException.NOT_IMPLEMENTED,
+ "Service name not found in message name: " + name + ". " +
+ "Did you forget to use TMultiplexedProtocol in your client?")
+
+ serviceName = name[0:index]
+ call = name[index + len(TMultiplexedProtocol.SEPARATOR):]
+ if serviceName not in self.services:
+ raise TProtocolException(
+ TProtocolException.NOT_IMPLEMENTED,
+ "Service name not found: " + serviceName + ". " +
+ "Did you forget to call registerProcessor()?")
+
+ standardMessage = (call, type, seqid)
+ return self.services[serviceName].process(
+ StoredMessageProtocol(iprot, standardMessage), oprot)
+
+
+class StoredMessageProtocol(TProtocolDecorator.TProtocolDecorator):
+ def __init__(self, protocol, messageBegin):
+ self.messageBegin = messageBegin
+
+ def readMessageBegin(self):
+ return self.messageBegin
diff --git a/src/jaegertracing/thrift/lib/py/src/TRecursive.py b/src/jaegertracing/thrift/lib/py/src/TRecursive.py
new file mode 100644
index 000000000..abf202cb1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/TRecursive.py
@@ -0,0 +1,83 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+from __future__ import unicode_literals
+
+from thrift.Thrift import TType
+
+TYPE_IDX = 1
+SPEC_ARGS_IDX = 3
+SPEC_ARGS_CLASS_REF_IDX = 0
+SPEC_ARGS_THRIFT_SPEC_IDX = 1
+
+
+def fix_spec(all_structs):
+ """Wire up recursive references for all TStruct definitions inside of each thrift_spec."""
+ for struc in all_structs:
+ spec = struc.thrift_spec
+ for thrift_spec in spec:
+ if thrift_spec is None:
+ continue
+ elif thrift_spec[TYPE_IDX] == TType.STRUCT:
+ other = thrift_spec[SPEC_ARGS_IDX][SPEC_ARGS_CLASS_REF_IDX].thrift_spec
+ thrift_spec[SPEC_ARGS_IDX][SPEC_ARGS_THRIFT_SPEC_IDX] = other
+ elif thrift_spec[TYPE_IDX] in (TType.LIST, TType.SET):
+ _fix_list_or_set(thrift_spec[SPEC_ARGS_IDX])
+ elif thrift_spec[TYPE_IDX] == TType.MAP:
+ _fix_map(thrift_spec[SPEC_ARGS_IDX])
+
+
+def _fix_list_or_set(element_type):
+ # For a list or set, the thrift_spec entry looks like,
+ # (1, TType.LIST, 'lister', (TType.STRUCT, [RecList, None], False), None, ), # 1
+ # so ``element_type`` will be,
+ # (TType.STRUCT, [RecList, None], False)
+ if element_type[0] == TType.STRUCT:
+ element_type[1][1] = element_type[1][0].thrift_spec
+ elif element_type[0] in (TType.LIST, TType.SET):
+ _fix_list_or_set(element_type[1])
+ elif element_type[0] == TType.MAP:
+ _fix_map(element_type[1])
+
+
+def _fix_map(element_type):
+ # For a map of key -> value type, ``element_type`` will be,
+ # (TType.I16, None, TType.STRUCT, [RecMapBasic, None], False), None, )
+ # which is just a normal struct definition.
+ #
+ # For a map of key -> list / set, ``element_type`` will be,
+ # (TType.I16, None, TType.LIST, (TType.STRUCT, [RecMapList, None], False), False)
+ # and we need to process the 3rd element as a list.
+ #
+ # For a map of key -> map, ``element_type`` will be,
+ # (TType.I16, None, TType.MAP, (TType.I16, None, TType.STRUCT,
+ # [RecMapMap, None], False), False)
+ # and need to process 3rd element as a map.
+
+ # Is the map key a struct?
+ if element_type[0] == TType.STRUCT:
+ element_type[1][1] = element_type[1][0].thrift_spec
+ elif element_type[0] in (TType.LIST, TType.SET):
+ _fix_list_or_set(element_type[1])
+ elif element_type[0] == TType.MAP:
+ _fix_map(element_type[1])
+
+ # Is the map value a struct?
+ if element_type[2] == TType.STRUCT:
+ element_type[3][1] = element_type[3][0].thrift_spec
+ elif element_type[2] in (TType.LIST, TType.SET):
+ _fix_list_or_set(element_type[3])
+ elif element_type[2] == TType.MAP:
+ _fix_map(element_type[3])
diff --git a/src/jaegertracing/thrift/lib/py/src/TSCons.py b/src/jaegertracing/thrift/lib/py/src/TSCons.py
new file mode 100644
index 000000000..bc67d7069
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/TSCons.py
@@ -0,0 +1,36 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from os import path
+from SCons.Builder import Builder
+from six.moves import map
+
+
+def scons_env(env, add=''):
+ opath = path.dirname(path.abspath('$TARGET'))
+ lstr = 'thrift --gen cpp -o ' + opath + ' ' + add + ' $SOURCE'
+ cppbuild = Builder(action=lstr)
+ env.Append(BUILDERS={'ThriftCpp': cppbuild})
+
+
+def gen_cpp(env, dir, file):
+ scons_env(env)
+ suffixes = ['_types.h', '_types.cpp']
+ targets = map(lambda s: 'gen-cpp/' + file + s, suffixes)
+ return env.ThriftCpp(targets, dir + file + '.thrift')
diff --git a/src/jaegertracing/thrift/lib/py/src/TSerialization.py b/src/jaegertracing/thrift/lib/py/src/TSerialization.py
new file mode 100644
index 000000000..fbbe76807
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/TSerialization.py
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from .protocol import TBinaryProtocol
+from .transport import TTransport
+
+
+def serialize(thrift_object,
+ protocol_factory=TBinaryProtocol.TBinaryProtocolFactory()):
+ transport = TTransport.TMemoryBuffer()
+ protocol = protocol_factory.getProtocol(transport)
+ thrift_object.write(protocol)
+ return transport.getvalue()
+
+
+def deserialize(base,
+ buf,
+ protocol_factory=TBinaryProtocol.TBinaryProtocolFactory()):
+ transport = TTransport.TMemoryBuffer(buf)
+ protocol = protocol_factory.getProtocol(transport)
+ base.read(protocol)
+ return base
diff --git a/src/jaegertracing/thrift/lib/py/src/TTornado.py b/src/jaegertracing/thrift/lib/py/src/TTornado.py
new file mode 100644
index 000000000..5eff11d2d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/TTornado.py
@@ -0,0 +1,188 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from __future__ import absolute_import
+import logging
+import socket
+import struct
+
+from .transport.TTransport import TTransportException, TTransportBase, TMemoryBuffer
+
+from io import BytesIO
+from collections import deque
+from contextlib import contextmanager
+from tornado import gen, iostream, ioloop, tcpserver, concurrent
+
+__all__ = ['TTornadoServer', 'TTornadoStreamTransport']
+
+logger = logging.getLogger(__name__)
+
+
+class _Lock(object):
+ def __init__(self):
+ self._waiters = deque()
+
+ def acquired(self):
+ return len(self._waiters) > 0
+
+ @gen.coroutine
+ def acquire(self):
+ blocker = self._waiters[-1] if self.acquired() else None
+ future = concurrent.Future()
+ self._waiters.append(future)
+ if blocker:
+ yield blocker
+
+ raise gen.Return(self._lock_context())
+
+ def release(self):
+ assert self.acquired(), 'Lock not aquired'
+ future = self._waiters.popleft()
+ future.set_result(None)
+
+ @contextmanager
+ def _lock_context(self):
+ try:
+ yield
+ finally:
+ self.release()
+
+
+class TTornadoStreamTransport(TTransportBase):
+ """a framed, buffered transport over a Tornado stream"""
+ def __init__(self, host, port, stream=None, io_loop=None):
+ self.host = host
+ self.port = port
+ self.io_loop = io_loop or ioloop.IOLoop.current()
+ self.__wbuf = BytesIO()
+ self._read_lock = _Lock()
+
+ # servers provide a ready-to-go stream
+ self.stream = stream
+
+ def with_timeout(self, timeout, future):
+ return gen.with_timeout(timeout, future, self.io_loop)
+
+ @gen.coroutine
+ def open(self, timeout=None):
+ logger.debug('socket connecting')
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
+ self.stream = iostream.IOStream(sock)
+
+ try:
+ connect = self.stream.connect((self.host, self.port))
+ if timeout is not None:
+ yield self.with_timeout(timeout, connect)
+ else:
+ yield connect
+ except (socket.error, IOError, ioloop.TimeoutError) as e:
+ message = 'could not connect to {}:{} ({})'.format(self.host, self.port, e)
+ raise TTransportException(
+ type=TTransportException.NOT_OPEN,
+ message=message)
+
+ raise gen.Return(self)
+
+ def set_close_callback(self, callback):
+ """
+ Should be called only after open() returns
+ """
+ self.stream.set_close_callback(callback)
+
+ def close(self):
+ # don't raise if we intend to close
+ self.stream.set_close_callback(None)
+ self.stream.close()
+
+ def read(self, _):
+ # The generated code for Tornado shouldn't do individual reads -- only
+ # frames at a time
+ assert False, "you're doing it wrong"
+
+ @contextmanager
+ def io_exception_context(self):
+ try:
+ yield
+ except (socket.error, IOError) as e:
+ raise TTransportException(
+ type=TTransportException.END_OF_FILE,
+ message=str(e))
+ except iostream.StreamBufferFullError as e:
+ raise TTransportException(
+ type=TTransportException.UNKNOWN,
+ message=str(e))
+
+ @gen.coroutine
+ def readFrame(self):
+ # IOStream processes reads one at a time
+ with (yield self._read_lock.acquire()):
+ with self.io_exception_context():
+ frame_header = yield self.stream.read_bytes(4)
+ if len(frame_header) == 0:
+ raise iostream.StreamClosedError('Read zero bytes from stream')
+ frame_length, = struct.unpack('!i', frame_header)
+ frame = yield self.stream.read_bytes(frame_length)
+ raise gen.Return(frame)
+
+ def write(self, buf):
+ self.__wbuf.write(buf)
+
+ def flush(self):
+ frame = self.__wbuf.getvalue()
+ # reset wbuf before write/flush to preserve state on underlying failure
+ frame_length = struct.pack('!i', len(frame))
+ self.__wbuf = BytesIO()
+ with self.io_exception_context():
+ return self.stream.write(frame_length + frame)
+
+
+class TTornadoServer(tcpserver.TCPServer):
+ def __init__(self, processor, iprot_factory, oprot_factory=None,
+ *args, **kwargs):
+ super(TTornadoServer, self).__init__(*args, **kwargs)
+
+ self._processor = processor
+ self._iprot_factory = iprot_factory
+ self._oprot_factory = (oprot_factory if oprot_factory is not None
+ else iprot_factory)
+
+ @gen.coroutine
+ def handle_stream(self, stream, address):
+ host, port = address[:2]
+ trans = TTornadoStreamTransport(host=host, port=port, stream=stream,
+ io_loop=self.io_loop)
+ oprot = self._oprot_factory.getProtocol(trans)
+
+ try:
+ while not trans.stream.closed():
+ try:
+ frame = yield trans.readFrame()
+ except TTransportException as e:
+ if e.type == TTransportException.END_OF_FILE:
+ break
+ else:
+ raise
+ tr = TMemoryBuffer(frame)
+ iprot = self._iprot_factory.getProtocol(tr)
+ yield self._processor.process(iprot, oprot)
+ except Exception:
+ logger.exception('thrift exception in handle_stream')
+ trans.close()
+
+ logger.info('client disconnected %s:%d', host, port)
diff --git a/src/jaegertracing/thrift/lib/py/src/Thrift.py b/src/jaegertracing/thrift/lib/py/src/Thrift.py
new file mode 100644
index 000000000..c390cbb54
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/Thrift.py
@@ -0,0 +1,204 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import sys
+
+
+class TType(object):
+ STOP = 0
+ VOID = 1
+ BOOL = 2
+ BYTE = 3
+ I08 = 3
+ DOUBLE = 4
+ I16 = 6
+ I32 = 8
+ I64 = 10
+ STRING = 11
+ UTF7 = 11
+ STRUCT = 12
+ MAP = 13
+ SET = 14
+ LIST = 15
+ UTF8 = 16
+ UTF16 = 17
+
+ _VALUES_TO_NAMES = (
+ 'STOP',
+ 'VOID',
+ 'BOOL',
+ 'BYTE',
+ 'DOUBLE',
+ None,
+ 'I16',
+ None,
+ 'I32',
+ None,
+ 'I64',
+ 'STRING',
+ 'STRUCT',
+ 'MAP',
+ 'SET',
+ 'LIST',
+ 'UTF8',
+ 'UTF16',
+ )
+
+
+class TMessageType(object):
+ CALL = 1
+ REPLY = 2
+ EXCEPTION = 3
+ ONEWAY = 4
+
+
+class TProcessor(object):
+ """Base class for processor, which works on two streams."""
+
+ def process(self, iprot, oprot):
+ """
+ Process a request. The normal behvaior is to have the
+ processor invoke the correct handler and then it is the
+ server's responsibility to write the response to oprot.
+ """
+ pass
+
+ def on_message_begin(self, func):
+ """
+ Install a callback that receives (name, type, seqid)
+ after the message header is read.
+ """
+ pass
+
+
+class TException(Exception):
+ """Base class for all thrift exceptions."""
+
+ # BaseException.message is deprecated in Python v[2.6,3.0)
+ if (2, 6, 0) <= sys.version_info < (3, 0):
+ def _get_message(self):
+ return self._message
+
+ def _set_message(self, message):
+ self._message = message
+ message = property(_get_message, _set_message)
+
+ def __init__(self, message=None):
+ Exception.__init__(self, message)
+ self.message = message
+
+
+class TApplicationException(TException):
+ """Application level thrift exceptions."""
+
+ UNKNOWN = 0
+ UNKNOWN_METHOD = 1
+ INVALID_MESSAGE_TYPE = 2
+ WRONG_METHOD_NAME = 3
+ BAD_SEQUENCE_ID = 4
+ MISSING_RESULT = 5
+ INTERNAL_ERROR = 6
+ PROTOCOL_ERROR = 7
+ INVALID_TRANSFORM = 8
+ INVALID_PROTOCOL = 9
+ UNSUPPORTED_CLIENT_TYPE = 10
+
+ def __init__(self, type=UNKNOWN, message=None):
+ TException.__init__(self, message)
+ self.type = type
+
+ def __str__(self):
+ if self.message:
+ return self.message
+ elif self.type == self.UNKNOWN_METHOD:
+ return 'Unknown method'
+ elif self.type == self.INVALID_MESSAGE_TYPE:
+ return 'Invalid message type'
+ elif self.type == self.WRONG_METHOD_NAME:
+ return 'Wrong method name'
+ elif self.type == self.BAD_SEQUENCE_ID:
+ return 'Bad sequence ID'
+ elif self.type == self.MISSING_RESULT:
+ return 'Missing result'
+ elif self.type == self.INTERNAL_ERROR:
+ return 'Internal error'
+ elif self.type == self.PROTOCOL_ERROR:
+ return 'Protocol error'
+ elif self.type == self.INVALID_TRANSFORM:
+ return 'Invalid transform'
+ elif self.type == self.INVALID_PROTOCOL:
+ return 'Invalid protocol'
+ elif self.type == self.UNSUPPORTED_CLIENT_TYPE:
+ return 'Unsupported client type'
+ else:
+ return 'Default (unknown) TApplicationException'
+
+ def read(self, iprot):
+ iprot.readStructBegin()
+ while True:
+ (fname, ftype, fid) = iprot.readFieldBegin()
+ if ftype == TType.STOP:
+ break
+ if fid == 1:
+ if ftype == TType.STRING:
+ self.message = iprot.readString()
+ else:
+ iprot.skip(ftype)
+ elif fid == 2:
+ if ftype == TType.I32:
+ self.type = iprot.readI32()
+ else:
+ iprot.skip(ftype)
+ else:
+ iprot.skip(ftype)
+ iprot.readFieldEnd()
+ iprot.readStructEnd()
+
+ def write(self, oprot):
+ oprot.writeStructBegin('TApplicationException')
+ if self.message is not None:
+ oprot.writeFieldBegin('message', TType.STRING, 1)
+ oprot.writeString(self.message)
+ oprot.writeFieldEnd()
+ if self.type is not None:
+ oprot.writeFieldBegin('type', TType.I32, 2)
+ oprot.writeI32(self.type)
+ oprot.writeFieldEnd()
+ oprot.writeFieldStop()
+ oprot.writeStructEnd()
+
+
+class TFrozenDict(dict):
+ """A dictionary that is "frozen" like a frozenset"""
+
+ def __init__(self, *args, **kwargs):
+ super(TFrozenDict, self).__init__(*args, **kwargs)
+ # Sort the items so they will be in a consistent order.
+ # XOR in the hash of the class so we don't collide with
+ # the hash of a list of tuples.
+ self.__hashval = hash(TFrozenDict) ^ hash(tuple(sorted(self.items())))
+
+ def __setitem__(self, *args):
+ raise TypeError("Can't modify frozen TFreezableDict")
+
+ def __delitem__(self, *args):
+ raise TypeError("Can't modify frozen TFreezableDict")
+
+ def __hash__(self):
+ return self.__hashval
diff --git a/src/jaegertracing/thrift/lib/py/src/__init__.py b/src/jaegertracing/thrift/lib/py/src/__init__.py
new file mode 100644
index 000000000..48d659c40
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/__init__.py
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+__all__ = ['Thrift', 'TSCons']
diff --git a/src/jaegertracing/thrift/lib/py/src/compat.py b/src/jaegertracing/thrift/lib/py/src/compat.py
new file mode 100644
index 000000000..0e8271dc1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/compat.py
@@ -0,0 +1,46 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import sys
+
+if sys.version_info[0] == 2:
+
+ from cStringIO import StringIO as BufferIO
+
+ def binary_to_str(bin_val):
+ return bin_val
+
+ def str_to_binary(str_val):
+ return str_val
+
+ def byte_index(bytes_val, i):
+ return ord(bytes_val[i])
+
+else:
+
+ from io import BytesIO as BufferIO # noqa
+
+ def binary_to_str(bin_val):
+ return bin_val.decode('utf8')
+
+ def str_to_binary(str_val):
+ return bytes(str_val, 'utf8')
+
+ def byte_index(bytes_val, i):
+ return bytes_val[i]
diff --git a/src/jaegertracing/thrift/lib/py/src/ext/binary.cpp b/src/jaegertracing/thrift/lib/py/src/ext/binary.cpp
new file mode 100644
index 000000000..85d8d922e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/ext/binary.cpp
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "ext/binary.h"
+namespace apache {
+namespace thrift {
+namespace py {
+
+bool BinaryProtocol::readFieldBegin(TType& type, int16_t& tag) {
+ uint8_t b = 0;
+ if (!readByte(b)) {
+ return false;
+ }
+ type = static_cast<TType>(b);
+ if (type == T_STOP) {
+ return true;
+ }
+ return readI16(tag);
+}
+}
+}
+}
diff --git a/src/jaegertracing/thrift/lib/py/src/ext/binary.h b/src/jaegertracing/thrift/lib/py/src/ext/binary.h
new file mode 100644
index 000000000..960b0d003
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/ext/binary.h
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef THRIFT_PY_BINARY_H
+#define THRIFT_PY_BINARY_H
+
+#include <Python.h>
+#include "ext/protocol.h"
+#include "ext/endian.h"
+#include <stdint.h>
+
+namespace apache {
+namespace thrift {
+namespace py {
+
+class BinaryProtocol : public ProtocolBase<BinaryProtocol> {
+public:
+ virtual ~BinaryProtocol() {}
+
+ void writeI8(int8_t val) { writeBuffer(reinterpret_cast<char*>(&val), sizeof(int8_t)); }
+
+ void writeI16(int16_t val) {
+ int16_t net = static_cast<int16_t>(htons(val));
+ writeBuffer(reinterpret_cast<char*>(&net), sizeof(int16_t));
+ }
+
+ void writeI32(int32_t val) {
+ int32_t net = static_cast<int32_t>(htonl(val));
+ writeBuffer(reinterpret_cast<char*>(&net), sizeof(int32_t));
+ }
+
+ void writeI64(int64_t val) {
+ int64_t net = static_cast<int64_t>(htonll(val));
+ writeBuffer(reinterpret_cast<char*>(&net), sizeof(int64_t));
+ }
+
+ void writeDouble(double dub) {
+ // Unfortunately, bitwise_cast doesn't work in C. Bad C!
+ union {
+ double f;
+ int64_t t;
+ } transfer;
+ transfer.f = dub;
+ writeI64(transfer.t);
+ }
+
+ void writeBool(int v) { writeByte(static_cast<uint8_t>(v)); }
+
+ void writeString(PyObject* value, int32_t len) {
+ writeI32(len);
+ writeBuffer(PyBytes_AS_STRING(value), len);
+ }
+
+ bool writeListBegin(PyObject* value, const SetListTypeArgs& parsedargs, int32_t len) {
+ writeByte(parsedargs.element_type);
+ writeI32(len);
+ return true;
+ }
+
+ bool writeMapBegin(PyObject* value, const MapTypeArgs& parsedargs, int32_t len) {
+ writeByte(parsedargs.ktag);
+ writeByte(parsedargs.vtag);
+ writeI32(len);
+ return true;
+ }
+
+ bool writeStructBegin() { return true; }
+ bool writeStructEnd() { return true; }
+ bool writeField(PyObject* value, const StructItemSpec& parsedspec) {
+ writeByte(static_cast<uint8_t>(parsedspec.type));
+ writeI16(parsedspec.tag);
+ return encodeValue(value, parsedspec.type, parsedspec.typeargs);
+ }
+
+ void writeFieldStop() { writeByte(static_cast<uint8_t>(T_STOP)); }
+
+ bool readBool(bool& val) {
+ char* buf;
+ if (!readBytes(&buf, 1)) {
+ return false;
+ }
+ val = buf[0] == 1;
+ return true;
+ }
+
+ bool readI8(int8_t& val) {
+ char* buf;
+ if (!readBytes(&buf, 1)) {
+ return false;
+ }
+ val = buf[0];
+ return true;
+ }
+
+ bool readI16(int16_t& val) {
+ char* buf;
+ if (!readBytes(&buf, sizeof(int16_t))) {
+ return false;
+ }
+ memcpy(&val, buf, sizeof(int16_t));
+ val = ntohs(val);
+ return true;
+ }
+
+ bool readI32(int32_t& val) {
+ char* buf;
+ if (!readBytes(&buf, sizeof(int32_t))) {
+ return false;
+ }
+ memcpy(&val, buf, sizeof(int32_t));
+ val = ntohl(val);
+ return true;
+ }
+
+ bool readI64(int64_t& val) {
+ char* buf;
+ if (!readBytes(&buf, sizeof(int64_t))) {
+ return false;
+ }
+ memcpy(&val, buf, sizeof(int64_t));
+ val = ntohll(val);
+ return true;
+ }
+
+ bool readDouble(double& val) {
+ union {
+ int64_t f;
+ double t;
+ } transfer;
+
+ if (!readI64(transfer.f)) {
+ return false;
+ }
+ val = transfer.t;
+ return true;
+ }
+
+ int32_t readString(char** buf) {
+ int32_t len = 0;
+ if (!readI32(len) || !checkLengthLimit(len, stringLimit()) || !readBytes(buf, len)) {
+ return -1;
+ }
+ return len;
+ }
+
+ int32_t readListBegin(TType& etype) {
+ int32_t len;
+ uint8_t b = 0;
+ if (!readByte(b) || !readI32(len) || !checkLengthLimit(len, containerLimit())) {
+ return -1;
+ }
+ etype = static_cast<TType>(b);
+ return len;
+ }
+
+ int32_t readMapBegin(TType& ktype, TType& vtype) {
+ int32_t len;
+ uint8_t k, v;
+ if (!readByte(k) || !readByte(v) || !readI32(len) || !checkLengthLimit(len, containerLimit())) {
+ return -1;
+ }
+ ktype = static_cast<TType>(k);
+ vtype = static_cast<TType>(v);
+ return len;
+ }
+
+ bool readStructBegin() { return true; }
+ bool readStructEnd() { return true; }
+
+ bool readFieldBegin(TType& type, int16_t& tag);
+
+#define SKIPBYTES(n) \
+ do { \
+ if (!readBytes(&dummy_buf_, (n))) { \
+ return false; \
+ } \
+ return true; \
+ } while (0)
+
+ bool skipBool() { SKIPBYTES(1); }
+ bool skipByte() { SKIPBYTES(1); }
+ bool skipI16() { SKIPBYTES(2); }
+ bool skipI32() { SKIPBYTES(4); }
+ bool skipI64() { SKIPBYTES(8); }
+ bool skipDouble() { SKIPBYTES(8); }
+ bool skipString() {
+ int32_t len;
+ if (!readI32(len)) {
+ return false;
+ }
+ SKIPBYTES(len);
+ }
+#undef SKIPBYTES
+
+private:
+ char* dummy_buf_;
+};
+}
+}
+}
+#endif // THRIFT_PY_BINARY_H
diff --git a/src/jaegertracing/thrift/lib/py/src/ext/compact.cpp b/src/jaegertracing/thrift/lib/py/src/ext/compact.cpp
new file mode 100644
index 000000000..15a99a077
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/ext/compact.cpp
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "ext/compact.h"
+
+namespace apache {
+namespace thrift {
+namespace py {
+
+const uint8_t CompactProtocol::TTypeToCType[] = {
+ CT_STOP, // T_STOP
+ 0, // unused
+ CT_BOOLEAN_TRUE, // T_BOOL
+ CT_BYTE, // T_BYTE
+ CT_DOUBLE, // T_DOUBLE
+ 0, // unused
+ CT_I16, // T_I16
+ 0, // unused
+ CT_I32, // T_I32
+ 0, // unused
+ CT_I64, // T_I64
+ CT_BINARY, // T_STRING
+ CT_STRUCT, // T_STRUCT
+ CT_MAP, // T_MAP
+ CT_SET, // T_SET
+ CT_LIST, // T_LIST
+};
+
+bool CompactProtocol::readFieldBegin(TType& type, int16_t& tag) {
+ uint8_t b;
+ if (!readByte(b)) {
+ return false;
+ }
+ uint8_t ctype = b & 0xf;
+ type = getTType(ctype);
+ if (type == -1) {
+ return false;
+ } else if (type == T_STOP) {
+ tag = 0;
+ return true;
+ }
+ uint8_t diff = (b & 0xf0) >> 4;
+ if (diff) {
+ tag = readTags_.top() + diff;
+ } else if (!readI16(tag)) {
+ readTags_.top() = -1;
+ return false;
+ }
+ if (ctype == CT_BOOLEAN_FALSE || ctype == CT_BOOLEAN_TRUE) {
+ readBool_.exists = true;
+ readBool_.value = ctype == CT_BOOLEAN_TRUE;
+ }
+ readTags_.top() = tag;
+ return true;
+}
+
+TType CompactProtocol::getTType(uint8_t type) {
+ switch (type) {
+ case T_STOP:
+ return T_STOP;
+ case CT_BOOLEAN_FALSE:
+ case CT_BOOLEAN_TRUE:
+ return T_BOOL;
+ case CT_BYTE:
+ return T_BYTE;
+ case CT_I16:
+ return T_I16;
+ case CT_I32:
+ return T_I32;
+ case CT_I64:
+ return T_I64;
+ case CT_DOUBLE:
+ return T_DOUBLE;
+ case CT_BINARY:
+ return T_STRING;
+ case CT_LIST:
+ return T_LIST;
+ case CT_SET:
+ return T_SET;
+ case CT_MAP:
+ return T_MAP;
+ case CT_STRUCT:
+ return T_STRUCT;
+ default:
+ PyErr_Format(PyExc_TypeError, "don't know what type: %d", type);
+ return static_cast<TType>(-1);
+ }
+}
+}
+}
+}
diff --git a/src/jaegertracing/thrift/lib/py/src/ext/compact.h b/src/jaegertracing/thrift/lib/py/src/ext/compact.h
new file mode 100644
index 000000000..a78d7a703
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/ext/compact.h
@@ -0,0 +1,368 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef THRIFT_PY_COMPACT_H
+#define THRIFT_PY_COMPACT_H
+
+#include <Python.h>
+#include "ext/protocol.h"
+#include "ext/endian.h"
+#include <stdint.h>
+#include <stack>
+
+namespace apache {
+namespace thrift {
+namespace py {
+
+class CompactProtocol : public ProtocolBase<CompactProtocol> {
+public:
+ CompactProtocol() { readBool_.exists = false; }
+
+ virtual ~CompactProtocol() {}
+
+ void writeI8(int8_t val) { writeBuffer(reinterpret_cast<char*>(&val), 1); }
+
+ void writeI16(int16_t val) { writeVarint(toZigZag(val)); }
+
+ int writeI32(int32_t val) { return writeVarint(toZigZag(val)); }
+
+ void writeI64(int64_t val) { writeVarint64(toZigZag64(val)); }
+
+ void writeDouble(double dub) {
+ union {
+ double f;
+ int64_t t;
+ } transfer;
+ transfer.f = htolell(dub);
+ writeBuffer(reinterpret_cast<char*>(&transfer.t), sizeof(int64_t));
+ }
+
+ void writeBool(int v) { writeByte(static_cast<uint8_t>(v ? CT_BOOLEAN_TRUE : CT_BOOLEAN_FALSE)); }
+
+ void writeString(PyObject* value, int32_t len) {
+ writeVarint(len);
+ writeBuffer(PyBytes_AS_STRING(value), len);
+ }
+
+ bool writeListBegin(PyObject* value, const SetListTypeArgs& args, int32_t len) {
+ int ctype = toCompactType(args.element_type);
+ if (len <= 14) {
+ writeByte(static_cast<uint8_t>(len << 4 | ctype));
+ } else {
+ writeByte(0xf0 | ctype);
+ writeVarint(len);
+ }
+ return true;
+ }
+
+ bool writeMapBegin(PyObject* value, const MapTypeArgs& args, int32_t len) {
+ if (len == 0) {
+ writeByte(0);
+ return true;
+ }
+ int ctype = toCompactType(args.ktag) << 4 | toCompactType(args.vtag);
+ writeVarint(len);
+ writeByte(ctype);
+ return true;
+ }
+
+ bool writeStructBegin() {
+ writeTags_.push(0);
+ return true;
+ }
+ bool writeStructEnd() {
+ writeTags_.pop();
+ return true;
+ }
+
+ bool writeField(PyObject* value, const StructItemSpec& spec) {
+ if (spec.type == T_BOOL) {
+ doWriteFieldBegin(spec, PyObject_IsTrue(value) ? CT_BOOLEAN_TRUE : CT_BOOLEAN_FALSE);
+ return true;
+ } else {
+ doWriteFieldBegin(spec, toCompactType(spec.type));
+ return encodeValue(value, spec.type, spec.typeargs);
+ }
+ }
+
+ void writeFieldStop() { writeByte(0); }
+
+ bool readBool(bool& val) {
+ if (readBool_.exists) {
+ readBool_.exists = false;
+ val = readBool_.value;
+ return true;
+ }
+ char* buf;
+ if (!readBytes(&buf, 1)) {
+ return false;
+ }
+ val = buf[0] == CT_BOOLEAN_TRUE;
+ return true;
+ }
+ bool readI8(int8_t& val) {
+ char* buf;
+ if (!readBytes(&buf, 1)) {
+ return false;
+ }
+ val = buf[0];
+ return true;
+ }
+
+ bool readI16(int16_t& val) {
+ uint16_t uval;
+ if (readVarint<uint16_t, 3>(uval)) {
+ val = fromZigZag<int16_t, uint16_t>(uval);
+ return true;
+ }
+ return false;
+ }
+
+ bool readI32(int32_t& val) {
+ uint32_t uval;
+ if (readVarint<uint32_t, 5>(uval)) {
+ val = fromZigZag<int32_t, uint32_t>(uval);
+ return true;
+ }
+ return false;
+ }
+
+ bool readI64(int64_t& val) {
+ uint64_t uval;
+ if (readVarint<uint64_t, 10>(uval)) {
+ val = fromZigZag<int64_t, uint64_t>(uval);
+ return true;
+ }
+ return false;
+ }
+
+ bool readDouble(double& val) {
+ union {
+ int64_t f;
+ double t;
+ } transfer;
+
+ char* buf;
+ if (!readBytes(&buf, 8)) {
+ return false;
+ }
+ memcpy(&transfer.f, buf, sizeof(int64_t));
+ transfer.f = letohll(transfer.f);
+ val = transfer.t;
+ return true;
+ }
+
+ int32_t readString(char** buf) {
+ uint32_t len;
+ if (!readVarint<uint32_t, 5>(len) || !checkLengthLimit(len, stringLimit())) {
+ return -1;
+ }
+ if (len == 0) {
+ return 0;
+ }
+ if (!readBytes(buf, len)) {
+ return -1;
+ }
+ return len;
+ }
+
+ int32_t readListBegin(TType& etype) {
+ uint8_t b;
+ if (!readByte(b)) {
+ return -1;
+ }
+ etype = getTType(b & 0xf);
+ if (etype == -1) {
+ return -1;
+ }
+ uint32_t len = (b >> 4) & 0xf;
+ if (len == 15 && !readVarint<uint32_t, 5>(len)) {
+ return -1;
+ }
+ if (!checkLengthLimit(len, containerLimit())) {
+ return -1;
+ }
+ return len;
+ }
+
+ int32_t readMapBegin(TType& ktype, TType& vtype) {
+ uint32_t len;
+ if (!readVarint<uint32_t, 5>(len) || !checkLengthLimit(len, containerLimit())) {
+ return -1;
+ }
+ if (len != 0) {
+ uint8_t kvType;
+ if (!readByte(kvType)) {
+ return -1;
+ }
+ ktype = getTType(kvType >> 4);
+ vtype = getTType(kvType & 0xf);
+ if (ktype == -1 || vtype == -1) {
+ return -1;
+ }
+ }
+ return len;
+ }
+
+ bool readStructBegin() {
+ readTags_.push(0);
+ return true;
+ }
+ bool readStructEnd() {
+ readTags_.pop();
+ return true;
+ }
+ bool readFieldBegin(TType& type, int16_t& tag);
+
+ bool skipBool() {
+ bool val;
+ return readBool(val);
+ }
+#define SKIPBYTES(n) \
+ do { \
+ if (!readBytes(&dummy_buf_, (n))) { \
+ return false; \
+ } \
+ return true; \
+ } while (0)
+ bool skipByte() { SKIPBYTES(1); }
+ bool skipDouble() { SKIPBYTES(8); }
+ bool skipI16() {
+ int16_t val;
+ return readI16(val);
+ }
+ bool skipI32() {
+ int32_t val;
+ return readI32(val);
+ }
+ bool skipI64() {
+ int64_t val;
+ return readI64(val);
+ }
+ bool skipString() {
+ uint32_t len;
+ if (!readVarint<uint32_t, 5>(len)) {
+ return false;
+ }
+ SKIPBYTES(len);
+ }
+#undef SKIPBYTES
+
+private:
+ enum Types {
+ CT_STOP = 0x00,
+ CT_BOOLEAN_TRUE = 0x01,
+ CT_BOOLEAN_FALSE = 0x02,
+ CT_BYTE = 0x03,
+ CT_I16 = 0x04,
+ CT_I32 = 0x05,
+ CT_I64 = 0x06,
+ CT_DOUBLE = 0x07,
+ CT_BINARY = 0x08,
+ CT_LIST = 0x09,
+ CT_SET = 0x0A,
+ CT_MAP = 0x0B,
+ CT_STRUCT = 0x0C
+ };
+
+ static const uint8_t TTypeToCType[];
+
+ TType getTType(uint8_t type);
+
+ int toCompactType(TType type) {
+ int i = static_cast<int>(type);
+ return i < 16 ? TTypeToCType[i] : -1;
+ }
+
+ uint32_t toZigZag(int32_t val) { return (val >> 31) ^ (val << 1); }
+
+ uint64_t toZigZag64(int64_t val) { return (val >> 63) ^ (val << 1); }
+
+ int writeVarint(uint32_t val) {
+ int cnt = 1;
+ while (val & ~0x7fU) {
+ writeByte(static_cast<char>((val & 0x7fU) | 0x80U));
+ val >>= 7;
+ ++cnt;
+ }
+ writeByte(static_cast<char>(val));
+ return cnt;
+ }
+
+ int writeVarint64(uint64_t val) {
+ int cnt = 1;
+ while (val & ~0x7fULL) {
+ writeByte(static_cast<char>((val & 0x7fULL) | 0x80ULL));
+ val >>= 7;
+ ++cnt;
+ }
+ writeByte(static_cast<char>(val));
+ return cnt;
+ }
+
+ template <typename T, int Max>
+ bool readVarint(T& result) {
+ uint8_t b;
+ T val = 0;
+ int shift = 0;
+ for (int i = 0; i < Max; ++i) {
+ if (!readByte(b)) {
+ return false;
+ }
+ if (b & 0x80) {
+ val |= static_cast<T>(b & 0x7f) << shift;
+ } else {
+ val |= static_cast<T>(b) << shift;
+ result = val;
+ return true;
+ }
+ shift += 7;
+ }
+ PyErr_Format(PyExc_OverflowError, "varint exceeded %d bytes", Max);
+ return false;
+ }
+
+ template <typename S, typename U>
+ S fromZigZag(U val) {
+ return (val >> 1) ^ static_cast<U>(-static_cast<S>(val & 1));
+ }
+
+ void doWriteFieldBegin(const StructItemSpec& spec, int ctype) {
+ int diff = spec.tag - writeTags_.top();
+ if (diff > 0 && diff <= 15) {
+ writeByte(static_cast<uint8_t>(diff << 4 | ctype));
+ } else {
+ writeByte(static_cast<uint8_t>(ctype));
+ writeI16(spec.tag);
+ }
+ writeTags_.top() = spec.tag;
+ }
+
+ std::stack<int> writeTags_;
+ std::stack<int> readTags_;
+ struct {
+ bool exists;
+ bool value;
+ } readBool_;
+ char* dummy_buf_;
+};
+}
+}
+}
+#endif // THRIFT_PY_COMPACT_H
diff --git a/src/jaegertracing/thrift/lib/py/src/ext/endian.h b/src/jaegertracing/thrift/lib/py/src/ext/endian.h
new file mode 100644
index 000000000..1660cbd98
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/ext/endian.h
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef THRIFT_PY_ENDIAN_H
+#define THRIFT_PY_ENDIAN_H
+
+#include <Python.h>
+
+#ifndef _WIN32
+#include <netinet/in.h>
+#else
+#include <WinSock2.h>
+#pragma comment(lib, "ws2_32.lib")
+#define BIG_ENDIAN (4321)
+#define LITTLE_ENDIAN (1234)
+#define BYTE_ORDER LITTLE_ENDIAN
+#define inline __inline
+#endif
+
+/* Fix endianness issues on Solaris */
+#if defined(__SVR4) && defined(__sun)
+#if defined(__i386) && !defined(__i386__)
+#define __i386__
+#endif
+
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN (4321)
+#endif
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN (1234)
+#endif
+
+/* I386 is LE, even on Solaris */
+#if !defined(BYTE_ORDER) && defined(__i386__)
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+#endif
+
+#ifndef __BYTE_ORDER
+#if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
+#define __BYTE_ORDER BYTE_ORDER
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#define __BIG_ENDIAN BIG_ENDIAN
+#else
+#error "Cannot determine endianness"
+#endif
+#endif
+
+// Same comment as the enum. Sorry.
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define ntohll(n) (n)
+#define htonll(n) (n)
+#if defined(__GNUC__) && defined(__GLIBC__)
+#include <byteswap.h>
+#define letohll(n) bswap_64(n)
+#define htolell(n) bswap_64(n)
+#else /* GNUC & GLIBC */
+#define letohll(n) ((((unsigned long long)ntohl(n)) << 32) + ntohl(n >> 32))
+#define htolell(n) ((((unsigned long long)htonl(n)) << 32) + htonl(n >> 32))
+#endif
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+#if defined(__GNUC__) && defined(__GLIBC__)
+#include <byteswap.h>
+#define ntohll(n) bswap_64(n)
+#define htonll(n) bswap_64(n)
+#elif defined(_MSC_VER)
+#include <stdlib.h>
+#define ntohll(n) _byteswap_uint64(n)
+#define htonll(n) _byteswap_uint64(n)
+#else /* GNUC & GLIBC */
+#define ntohll(n) ((((unsigned long long)ntohl(n)) << 32) + ntohl(n >> 32))
+#define htonll(n) ((((unsigned long long)htonl(n)) << 32) + htonl(n >> 32))
+#endif /* GNUC & GLIBC */
+#define letohll(n) (n)
+#define htolell(n) (n)
+#else /* __BYTE_ORDER */
+#error "Can't define htonll or ntohll!"
+#endif
+
+#endif // THRIFT_PY_ENDIAN_H
diff --git a/src/jaegertracing/thrift/lib/py/src/ext/module.cpp b/src/jaegertracing/thrift/lib/py/src/ext/module.cpp
new file mode 100644
index 000000000..7158b8fdf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/ext/module.cpp
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <Python.h>
+#include "types.h"
+#include "binary.h"
+#include "compact.h"
+#include <limits>
+#include <stdint.h>
+
+// TODO(dreiss): defval appears to be unused. Look into removing it.
+// TODO(dreiss): Make parse_spec_args recursive, and cache the output
+// permanently in the object. (Malloc and orphan.)
+// TODO(dreiss): Why do we need cStringIO for reading, why not just char*?
+// Can cStringIO let us work with a BufferedTransport?
+// TODO(dreiss): Don't ignore the rv from cwrite (maybe).
+
+// Doing a benchmark shows that interning actually makes a difference, amazingly.
+
+/** Pointer to interned string to speed up attribute lookup. */
+PyObject* INTERN_STRING(TFrozenDict);
+PyObject* INTERN_STRING(cstringio_buf);
+PyObject* INTERN_STRING(cstringio_refill);
+static PyObject* INTERN_STRING(string_length_limit);
+static PyObject* INTERN_STRING(container_length_limit);
+static PyObject* INTERN_STRING(trans);
+
+namespace apache {
+namespace thrift {
+namespace py {
+
+template <typename T>
+static PyObject* encode_impl(PyObject* args) {
+ if (!args)
+ return NULL;
+
+ PyObject* enc_obj = NULL;
+ PyObject* type_args = NULL;
+ if (!PyArg_ParseTuple(args, "OO", &enc_obj, &type_args)) {
+ return NULL;
+ }
+ if (!enc_obj || !type_args) {
+ return NULL;
+ }
+
+ T protocol;
+ if (!protocol.prepareEncodeBuffer() || !protocol.encodeValue(enc_obj, T_STRUCT, type_args)) {
+ return NULL;
+ }
+
+ return protocol.getEncodedValue();
+}
+
+static inline long as_long_then_delete(PyObject* value, long default_value) {
+ ScopedPyObject scope(value);
+ long v = PyInt_AsLong(value);
+ if (INT_CONV_ERROR_OCCURRED(v)) {
+ PyErr_Clear();
+ return default_value;
+ }
+ return v;
+}
+
+template <typename T>
+static PyObject* decode_impl(PyObject* args) {
+ PyObject* output_obj = NULL;
+ PyObject* oprot = NULL;
+ PyObject* typeargs = NULL;
+ if (!PyArg_ParseTuple(args, "OOO", &output_obj, &oprot, &typeargs)) {
+ return NULL;
+ }
+
+ T protocol;
+ int32_t default_limit = (std::numeric_limits<int32_t>::max)();
+ protocol.setStringLengthLimit(
+ as_long_then_delete(PyObject_GetAttr(oprot, INTERN_STRING(string_length_limit)),
+ default_limit));
+ protocol.setContainerLengthLimit(
+ as_long_then_delete(PyObject_GetAttr(oprot, INTERN_STRING(container_length_limit)),
+ default_limit));
+ ScopedPyObject transport(PyObject_GetAttr(oprot, INTERN_STRING(trans)));
+ if (!transport) {
+ return NULL;
+ }
+
+ StructTypeArgs parsedargs;
+ if (!parse_struct_args(&parsedargs, typeargs)) {
+ return NULL;
+ }
+
+ if (!protocol.prepareDecodeBufferFromTransport(transport.get())) {
+ return NULL;
+ }
+
+ return protocol.readStruct(output_obj, parsedargs.klass, parsedargs.spec);
+}
+}
+}
+}
+
+using namespace apache::thrift::py;
+
+/* -- PYTHON MODULE SETUP STUFF --- */
+
+extern "C" {
+
+static PyObject* encode_binary(PyObject*, PyObject* args) {
+ return encode_impl<BinaryProtocol>(args);
+}
+
+static PyObject* decode_binary(PyObject*, PyObject* args) {
+ return decode_impl<BinaryProtocol>(args);
+}
+
+static PyObject* encode_compact(PyObject*, PyObject* args) {
+ return encode_impl<CompactProtocol>(args);
+}
+
+static PyObject* decode_compact(PyObject*, PyObject* args) {
+ return decode_impl<CompactProtocol>(args);
+}
+
+static PyMethodDef ThriftFastBinaryMethods[] = {
+ {"encode_binary", encode_binary, METH_VARARGS, ""},
+ {"decode_binary", decode_binary, METH_VARARGS, ""},
+ {"encode_compact", encode_compact, METH_VARARGS, ""},
+ {"decode_compact", decode_compact, METH_VARARGS, ""},
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+#if PY_MAJOR_VERSION >= 3
+
+static struct PyModuleDef ThriftFastBinaryDef = {PyModuleDef_HEAD_INIT,
+ "thrift.protocol.fastbinary",
+ NULL,
+ 0,
+ ThriftFastBinaryMethods,
+ NULL,
+ NULL,
+ NULL,
+ NULL};
+
+#define INITERROR return NULL;
+
+PyObject* PyInit_fastbinary() {
+
+#else
+
+#define INITERROR return;
+
+void initfastbinary() {
+
+ PycString_IMPORT;
+ if (PycStringIO == NULL)
+ INITERROR
+
+#endif
+
+#define INIT_INTERN_STRING(value) \
+ do { \
+ INTERN_STRING(value) = PyString_InternFromString(#value); \
+ if (!INTERN_STRING(value)) \
+ INITERROR \
+ } while (0)
+
+ INIT_INTERN_STRING(TFrozenDict);
+ INIT_INTERN_STRING(cstringio_buf);
+ INIT_INTERN_STRING(cstringio_refill);
+ INIT_INTERN_STRING(string_length_limit);
+ INIT_INTERN_STRING(container_length_limit);
+ INIT_INTERN_STRING(trans);
+#undef INIT_INTERN_STRING
+
+ PyObject* module =
+#if PY_MAJOR_VERSION >= 3
+ PyModule_Create(&ThriftFastBinaryDef);
+#else
+ Py_InitModule("thrift.protocol.fastbinary", ThriftFastBinaryMethods);
+#endif
+ if (module == NULL)
+ INITERROR;
+
+#if PY_MAJOR_VERSION >= 3
+ return module;
+#endif
+}
+}
diff --git a/src/jaegertracing/thrift/lib/py/src/ext/protocol.h b/src/jaegertracing/thrift/lib/py/src/ext/protocol.h
new file mode 100644
index 000000000..521b7ee92
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/ext/protocol.h
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef THRIFT_PY_PROTOCOL_H
+#define THRIFT_PY_PROTOCOL_H
+
+#include "ext/types.h"
+#include <limits>
+#include <stdint.h>
+
+namespace apache {
+namespace thrift {
+namespace py {
+
+template <typename Impl>
+class ProtocolBase {
+
+public:
+ ProtocolBase()
+ : stringLimit_((std::numeric_limits<int32_t>::max)()),
+ containerLimit_((std::numeric_limits<int32_t>::max)()),
+ output_(NULL) {}
+ inline virtual ~ProtocolBase();
+
+ bool prepareDecodeBufferFromTransport(PyObject* trans);
+
+ PyObject* readStruct(PyObject* output, PyObject* klass, PyObject* spec_seq);
+
+ bool prepareEncodeBuffer();
+
+ bool encodeValue(PyObject* value, TType type, PyObject* typeargs);
+
+ PyObject* getEncodedValue();
+
+ long stringLimit() const { return stringLimit_; }
+ void setStringLengthLimit(long limit) { stringLimit_ = limit; }
+
+ long containerLimit() const { return containerLimit_; }
+ void setContainerLengthLimit(long limit) { containerLimit_ = limit; }
+
+protected:
+ bool readBytes(char** output, int len);
+
+ bool readByte(uint8_t& val) {
+ char* buf;
+ if (!readBytes(&buf, 1)) {
+ return false;
+ }
+ val = static_cast<uint8_t>(buf[0]);
+ return true;
+ }
+
+ bool writeBuffer(char* data, size_t len);
+
+ void writeByte(uint8_t val) { writeBuffer(reinterpret_cast<char*>(&val), 1); }
+
+ PyObject* decodeValue(TType type, PyObject* typeargs);
+
+ bool skip(TType type);
+
+ inline bool checkType(TType got, TType expected);
+ inline bool checkLengthLimit(int32_t len, long limit);
+
+ inline bool isUtf8(PyObject* typeargs);
+
+private:
+ Impl* impl() { return static_cast<Impl*>(this); }
+
+ long stringLimit_;
+ long containerLimit_;
+ EncodeBuffer* output_;
+ DecodeBuffer input_;
+};
+}
+}
+}
+
+#include "ext/protocol.tcc"
+
+#endif // THRIFT_PY_PROTOCOL_H
diff --git a/src/jaegertracing/thrift/lib/py/src/ext/protocol.tcc b/src/jaegertracing/thrift/lib/py/src/ext/protocol.tcc
new file mode 100644
index 000000000..e15df7ea0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/ext/protocol.tcc
@@ -0,0 +1,913 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef THRIFT_PY_PROTOCOL_TCC
+#define THRIFT_PY_PROTOCOL_TCC
+
+#include <iterator>
+
+#define CHECK_RANGE(v, min, max) (((v) <= (max)) && ((v) >= (min)))
+#define INIT_OUTBUF_SIZE 128
+
+#if PY_MAJOR_VERSION < 3
+#include <cStringIO.h>
+#else
+#include <algorithm>
+#endif
+
+namespace apache {
+namespace thrift {
+namespace py {
+
+#if PY_MAJOR_VERSION < 3
+
+namespace detail {
+
+inline bool input_check(PyObject* input) {
+ return PycStringIO_InputCheck(input);
+}
+
+inline EncodeBuffer* new_encode_buffer(size_t size) {
+ if (!PycStringIO) {
+ PycString_IMPORT;
+ }
+ if (!PycStringIO) {
+ return NULL;
+ }
+ return PycStringIO->NewOutput(size);
+}
+
+inline int read_buffer(PyObject* buf, char** output, int len) {
+ if (!PycStringIO) {
+ PycString_IMPORT;
+ }
+ if (!PycStringIO) {
+ PyErr_SetString(PyExc_ImportError, "failed to import native cStringIO");
+ return -1;
+ }
+ return PycStringIO->cread(buf, output, len);
+}
+}
+
+template <typename Impl>
+inline ProtocolBase<Impl>::~ProtocolBase() {
+ if (output_) {
+ Py_CLEAR(output_);
+ }
+}
+
+template <typename Impl>
+inline bool ProtocolBase<Impl>::isUtf8(PyObject* typeargs) {
+ return PyString_Check(typeargs) && !strncmp(PyString_AS_STRING(typeargs), "UTF8", 4);
+}
+
+template <typename Impl>
+PyObject* ProtocolBase<Impl>::getEncodedValue() {
+ if (!PycStringIO) {
+ PycString_IMPORT;
+ }
+ if (!PycStringIO) {
+ return NULL;
+ }
+ return PycStringIO->cgetvalue(output_);
+}
+
+template <typename Impl>
+inline bool ProtocolBase<Impl>::writeBuffer(char* data, size_t size) {
+ if (!PycStringIO) {
+ PycString_IMPORT;
+ }
+ if (!PycStringIO) {
+ PyErr_SetString(PyExc_ImportError, "failed to import native cStringIO");
+ return false;
+ }
+ int len = PycStringIO->cwrite(output_, data, size);
+ if (len < 0) {
+ PyErr_SetString(PyExc_IOError, "failed to write to cStringIO object");
+ return false;
+ }
+ if (static_cast<size_t>(len) != size) {
+ PyErr_Format(PyExc_EOFError, "write length mismatch: expected %lu got %d", size, len);
+ return false;
+ }
+ return true;
+}
+
+#else
+
+namespace detail {
+
+inline bool input_check(PyObject* input) {
+ // TODO: Check for BytesIO type
+ return true;
+}
+
+inline EncodeBuffer* new_encode_buffer(size_t size) {
+ EncodeBuffer* buffer = new EncodeBuffer;
+ buffer->buf.reserve(size);
+ buffer->pos = 0;
+ return buffer;
+}
+
+struct bytesio {
+ PyObject_HEAD
+#if PY_MINOR_VERSION < 5
+ char* buf;
+#else
+ PyObject* buf;
+#endif
+ Py_ssize_t pos;
+ Py_ssize_t string_size;
+};
+
+inline int read_buffer(PyObject* buf, char** output, int len) {
+ bytesio* buf2 = reinterpret_cast<bytesio*>(buf);
+#if PY_MINOR_VERSION < 5
+ *output = buf2->buf + buf2->pos;
+#else
+ *output = PyBytes_AS_STRING(buf2->buf) + buf2->pos;
+#endif
+ Py_ssize_t pos0 = buf2->pos;
+ buf2->pos = (std::min)(buf2->pos + static_cast<Py_ssize_t>(len), buf2->string_size);
+ return static_cast<int>(buf2->pos - pos0);
+}
+}
+
+template <typename Impl>
+inline ProtocolBase<Impl>::~ProtocolBase() {
+ if (output_) {
+ delete output_;
+ }
+}
+
+template <typename Impl>
+inline bool ProtocolBase<Impl>::isUtf8(PyObject* typeargs) {
+ // while condition for py2 is "arg == 'UTF8'", it should be "arg != 'BINARY'" for py3.
+ // HACK: check the length and don't bother reading the value
+ return !PyUnicode_Check(typeargs) || PyUnicode_GET_LENGTH(typeargs) != 6;
+}
+
+template <typename Impl>
+PyObject* ProtocolBase<Impl>::getEncodedValue() {
+ return PyBytes_FromStringAndSize(output_->buf.data(), output_->buf.size());
+}
+
+template <typename Impl>
+inline bool ProtocolBase<Impl>::writeBuffer(char* data, size_t size) {
+ size_t need = size + output_->pos;
+ if (output_->buf.capacity() < need) {
+ try {
+ output_->buf.reserve(need);
+ } catch (std::bad_alloc& ex) {
+ PyErr_SetString(PyExc_MemoryError, "Failed to allocate write buffer");
+ return false;
+ }
+ }
+ std::copy(data, data + size, std::back_inserter(output_->buf));
+ return true;
+}
+
+#endif
+
+namespace detail {
+
+#define DECLARE_OP_SCOPE(name, op) \
+ template <typename Impl> \
+ struct name##Scope { \
+ Impl* impl; \
+ bool valid; \
+ name##Scope(Impl* thiz) : impl(thiz), valid(impl->op##Begin()) {} \
+ ~name##Scope() { \
+ if (valid) \
+ impl->op##End(); \
+ } \
+ operator bool() { return valid; } \
+ }; \
+ template <typename Impl, template <typename> class T> \
+ name##Scope<Impl> op##Scope(T<Impl>* thiz) { \
+ return name##Scope<Impl>(static_cast<Impl*>(thiz)); \
+ }
+DECLARE_OP_SCOPE(WriteStruct, writeStruct)
+DECLARE_OP_SCOPE(ReadStruct, readStruct)
+#undef DECLARE_OP_SCOPE
+
+inline bool check_ssize_t_32(Py_ssize_t len) {
+ // error from getting the int
+ if (INT_CONV_ERROR_OCCURRED(len)) {
+ return false;
+ }
+ if (!CHECK_RANGE(len, 0, (std::numeric_limits<int32_t>::max)())) {
+ PyErr_SetString(PyExc_OverflowError, "size out of range: exceeded INT32_MAX");
+ return false;
+ }
+ return true;
+}
+}
+
+template <typename T>
+bool parse_pyint(PyObject* o, T* ret, int32_t min, int32_t max) {
+ long val = PyInt_AsLong(o);
+
+ if (INT_CONV_ERROR_OCCURRED(val)) {
+ return false;
+ }
+ if (!CHECK_RANGE(val, min, max)) {
+ PyErr_SetString(PyExc_OverflowError, "int out of range");
+ return false;
+ }
+
+ *ret = static_cast<T>(val);
+ return true;
+}
+
+template <typename Impl>
+inline bool ProtocolBase<Impl>::checkType(TType got, TType expected) {
+ if (expected != got) {
+ PyErr_SetString(PyExc_TypeError, "got wrong ttype while reading field");
+ return false;
+ }
+ return true;
+}
+
+template <typename Impl>
+bool ProtocolBase<Impl>::checkLengthLimit(int32_t len, long limit) {
+ if (len < 0) {
+ PyErr_Format(PyExc_OverflowError, "negative length: %ld", limit);
+ return false;
+ }
+ if (len > limit) {
+ PyErr_Format(PyExc_OverflowError, "size exceeded specified limit: %ld", limit);
+ return false;
+ }
+ return true;
+}
+
+template <typename Impl>
+bool ProtocolBase<Impl>::readBytes(char** output, int len) {
+ if (len < 0) {
+ PyErr_Format(PyExc_ValueError, "attempted to read negative length: %d", len);
+ return false;
+ }
+ // TODO(dreiss): Don't fear the malloc. Think about taking a copy of
+ // the partial read instead of forcing the transport
+ // to prepend it to its buffer.
+
+ int rlen = detail::read_buffer(input_.stringiobuf.get(), output, len);
+
+ if (rlen == len) {
+ return true;
+ } else if (rlen == -1) {
+ return false;
+ } else {
+ // using building functions as this is a rare codepath
+ ScopedPyObject newiobuf(PyObject_CallFunction(input_.refill_callable.get(), refill_signature,
+ *output, rlen, len, NULL));
+ if (!newiobuf) {
+ return false;
+ }
+
+ // must do this *AFTER* the call so that we don't deref the io buffer
+ input_.stringiobuf.reset(newiobuf.release());
+
+ rlen = detail::read_buffer(input_.stringiobuf.get(), output, len);
+
+ if (rlen == len) {
+ return true;
+ } else if (rlen == -1) {
+ return false;
+ } else {
+ // TODO(dreiss): This could be a valid code path for big binary blobs.
+ PyErr_SetString(PyExc_TypeError, "refill claimed to have refilled the buffer, but didn't!!");
+ return false;
+ }
+ }
+}
+
+template <typename Impl>
+bool ProtocolBase<Impl>::prepareDecodeBufferFromTransport(PyObject* trans) {
+ if (input_.stringiobuf) {
+ PyErr_SetString(PyExc_ValueError, "decode buffer is already initialized");
+ return false;
+ }
+
+ ScopedPyObject stringiobuf(PyObject_GetAttr(trans, INTERN_STRING(cstringio_buf)));
+ if (!stringiobuf) {
+ return false;
+ }
+ if (!detail::input_check(stringiobuf.get())) {
+ PyErr_SetString(PyExc_TypeError, "expecting stringio input_");
+ return false;
+ }
+
+ ScopedPyObject refill_callable(PyObject_GetAttr(trans, INTERN_STRING(cstringio_refill)));
+ if (!refill_callable) {
+ return false;
+ }
+ if (!PyCallable_Check(refill_callable.get())) {
+ PyErr_SetString(PyExc_TypeError, "expecting callable");
+ return false;
+ }
+
+ input_.stringiobuf.swap(stringiobuf);
+ input_.refill_callable.swap(refill_callable);
+ return true;
+}
+
+template <typename Impl>
+bool ProtocolBase<Impl>::prepareEncodeBuffer() {
+ output_ = detail::new_encode_buffer(INIT_OUTBUF_SIZE);
+ return output_ != NULL;
+}
+
+template <typename Impl>
+bool ProtocolBase<Impl>::encodeValue(PyObject* value, TType type, PyObject* typeargs) {
+ /*
+ * Refcounting Strategy:
+ *
+ * We assume that elements of the thrift_spec tuple are not going to be
+ * mutated, so we don't ref count those at all. Other than that, we try to
+ * keep a reference to all the user-created objects while we work with them.
+ * encodeValue assumes that a reference is already held. The *caller* is
+ * responsible for handling references
+ */
+
+ switch (type) {
+
+ case T_BOOL: {
+ int v = PyObject_IsTrue(value);
+ if (v == -1) {
+ return false;
+ }
+ impl()->writeBool(v);
+ return true;
+ }
+ case T_I08: {
+ int8_t val;
+
+ if (!parse_pyint(value, &val, (std::numeric_limits<int8_t>::min)(),
+ (std::numeric_limits<int8_t>::max)())) {
+ return false;
+ }
+
+ impl()->writeI8(val);
+ return true;
+ }
+ case T_I16: {
+ int16_t val;
+
+ if (!parse_pyint(value, &val, (std::numeric_limits<int16_t>::min)(),
+ (std::numeric_limits<int16_t>::max)())) {
+ return false;
+ }
+
+ impl()->writeI16(val);
+ return true;
+ }
+ case T_I32: {
+ int32_t val;
+
+ if (!parse_pyint(value, &val, (std::numeric_limits<int32_t>::min)(),
+ (std::numeric_limits<int32_t>::max)())) {
+ return false;
+ }
+
+ impl()->writeI32(val);
+ return true;
+ }
+ case T_I64: {
+ int64_t nval = PyLong_AsLongLong(value);
+
+ if (INT_CONV_ERROR_OCCURRED(nval)) {
+ return false;
+ }
+
+ if (!CHECK_RANGE(nval, (std::numeric_limits<int64_t>::min)(),
+ (std::numeric_limits<int64_t>::max)())) {
+ PyErr_SetString(PyExc_OverflowError, "int out of range");
+ return false;
+ }
+
+ impl()->writeI64(nval);
+ return true;
+ }
+
+ case T_DOUBLE: {
+ double nval = PyFloat_AsDouble(value);
+ if (nval == -1.0 && PyErr_Occurred()) {
+ return false;
+ }
+
+ impl()->writeDouble(nval);
+ return true;
+ }
+
+ case T_STRING: {
+ ScopedPyObject nval;
+
+ if (PyUnicode_Check(value)) {
+ nval.reset(PyUnicode_AsUTF8String(value));
+ if (!nval) {
+ return false;
+ }
+ } else {
+ Py_INCREF(value);
+ nval.reset(value);
+ }
+
+ Py_ssize_t len = PyBytes_Size(nval.get());
+ if (!detail::check_ssize_t_32(len)) {
+ return false;
+ }
+
+ impl()->writeString(nval.get(), static_cast<int32_t>(len));
+ return true;
+ }
+
+ case T_LIST:
+ case T_SET: {
+ SetListTypeArgs parsedargs;
+ if (!parse_set_list_args(&parsedargs, typeargs)) {
+ return false;
+ }
+
+ Py_ssize_t len = PyObject_Length(value);
+ if (!detail::check_ssize_t_32(len)) {
+ return false;
+ }
+
+ if (!impl()->writeListBegin(value, parsedargs, static_cast<int32_t>(len)) || PyErr_Occurred()) {
+ return false;
+ }
+ ScopedPyObject iterator(PyObject_GetIter(value));
+ if (!iterator) {
+ return false;
+ }
+
+ while (PyObject* rawItem = PyIter_Next(iterator.get())) {
+ ScopedPyObject item(rawItem);
+ if (!encodeValue(item.get(), parsedargs.element_type, parsedargs.typeargs)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ case T_MAP: {
+ Py_ssize_t len = PyDict_Size(value);
+ if (!detail::check_ssize_t_32(len)) {
+ return false;
+ }
+
+ MapTypeArgs parsedargs;
+ if (!parse_map_args(&parsedargs, typeargs)) {
+ return false;
+ }
+
+ if (!impl()->writeMapBegin(value, parsedargs, static_cast<int32_t>(len)) || PyErr_Occurred()) {
+ return false;
+ }
+ Py_ssize_t pos = 0;
+ PyObject* k = NULL;
+ PyObject* v = NULL;
+ // TODO(bmaurer): should support any mapping, not just dicts
+ while (PyDict_Next(value, &pos, &k, &v)) {
+ if (!encodeValue(k, parsedargs.ktag, parsedargs.ktypeargs)
+ || !encodeValue(v, parsedargs.vtag, parsedargs.vtypeargs)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ case T_STRUCT: {
+ StructTypeArgs parsedargs;
+ if (!parse_struct_args(&parsedargs, typeargs)) {
+ return false;
+ }
+
+ Py_ssize_t nspec = PyTuple_Size(parsedargs.spec);
+ if (nspec == -1) {
+ PyErr_SetString(PyExc_TypeError, "spec is not a tuple");
+ return false;
+ }
+
+ detail::WriteStructScope<Impl> scope = detail::writeStructScope(this);
+ if (!scope) {
+ return false;
+ }
+ for (Py_ssize_t i = 0; i < nspec; i++) {
+ PyObject* spec_tuple = PyTuple_GET_ITEM(parsedargs.spec, i);
+ if (spec_tuple == Py_None) {
+ continue;
+ }
+
+ StructItemSpec parsedspec;
+ if (!parse_struct_item_spec(&parsedspec, spec_tuple)) {
+ return false;
+ }
+
+ ScopedPyObject instval(PyObject_GetAttr(value, parsedspec.attrname));
+
+ if (!instval) {
+ return false;
+ }
+
+ if (instval.get() == Py_None) {
+ continue;
+ }
+
+ bool res = impl()->writeField(instval.get(), parsedspec);
+ if (!res) {
+ return false;
+ }
+ }
+ impl()->writeFieldStop();
+ return true;
+ }
+
+ case T_STOP:
+ case T_VOID:
+ case T_UTF16:
+ case T_UTF8:
+ case T_U64:
+ default:
+ PyErr_Format(PyExc_TypeError, "Unexpected TType for encodeValue: %d", type);
+ return false;
+ }
+
+ return true;
+}
+
+template <typename Impl>
+bool ProtocolBase<Impl>::skip(TType type) {
+ switch (type) {
+ case T_BOOL:
+ return impl()->skipBool();
+ case T_I08:
+ return impl()->skipByte();
+ case T_I16:
+ return impl()->skipI16();
+ case T_I32:
+ return impl()->skipI32();
+ case T_I64:
+ return impl()->skipI64();
+ case T_DOUBLE:
+ return impl()->skipDouble();
+
+ case T_STRING: {
+ return impl()->skipString();
+ }
+
+ case T_LIST:
+ case T_SET: {
+ TType etype = T_STOP;
+ int32_t len = impl()->readListBegin(etype);
+ if (len < 0) {
+ return false;
+ }
+ for (int32_t i = 0; i < len; i++) {
+ if (!skip(etype)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ case T_MAP: {
+ TType ktype = T_STOP;
+ TType vtype = T_STOP;
+ int32_t len = impl()->readMapBegin(ktype, vtype);
+ if (len < 0) {
+ return false;
+ }
+ for (int32_t i = 0; i < len; i++) {
+ if (!skip(ktype) || !skip(vtype)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ case T_STRUCT: {
+ detail::ReadStructScope<Impl> scope = detail::readStructScope(this);
+ if (!scope) {
+ return false;
+ }
+ while (true) {
+ TType type = T_STOP;
+ int16_t tag;
+ if (!impl()->readFieldBegin(type, tag)) {
+ return false;
+ }
+ if (type == T_STOP) {
+ return true;
+ }
+ if (!skip(type)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ case T_STOP:
+ case T_VOID:
+ case T_UTF16:
+ case T_UTF8:
+ case T_U64:
+ default:
+ PyErr_Format(PyExc_TypeError, "Unexpected TType for skip: %d", type);
+ return false;
+ }
+
+ return true;
+}
+
+// Returns a new reference.
+template <typename Impl>
+PyObject* ProtocolBase<Impl>::decodeValue(TType type, PyObject* typeargs) {
+ switch (type) {
+
+ case T_BOOL: {
+ bool v = 0;
+ if (!impl()->readBool(v)) {
+ return NULL;
+ }
+ if (v) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+ }
+ case T_I08: {
+ int8_t v = 0;
+ if (!impl()->readI8(v)) {
+ return NULL;
+ }
+ return PyInt_FromLong(v);
+ }
+ case T_I16: {
+ int16_t v = 0;
+ if (!impl()->readI16(v)) {
+ return NULL;
+ }
+ return PyInt_FromLong(v);
+ }
+ case T_I32: {
+ int32_t v = 0;
+ if (!impl()->readI32(v)) {
+ return NULL;
+ }
+ return PyInt_FromLong(v);
+ }
+
+ case T_I64: {
+ int64_t v = 0;
+ if (!impl()->readI64(v)) {
+ return NULL;
+ }
+ // TODO(dreiss): Find out if we can take this fastpath always when
+ // sizeof(long) == sizeof(long long).
+ if (CHECK_RANGE(v, LONG_MIN, LONG_MAX)) {
+ return PyInt_FromLong((long)v);
+ }
+ return PyLong_FromLongLong(v);
+ }
+
+ case T_DOUBLE: {
+ double v = 0.0;
+ if (!impl()->readDouble(v)) {
+ return NULL;
+ }
+ return PyFloat_FromDouble(v);
+ }
+
+ case T_STRING: {
+ char* buf = NULL;
+ int len = impl()->readString(&buf);
+ if (len < 0) {
+ return NULL;
+ }
+ if (isUtf8(typeargs)) {
+ return PyUnicode_DecodeUTF8(buf, len, 0);
+ } else {
+ return PyBytes_FromStringAndSize(buf, len);
+ }
+ }
+
+ case T_LIST:
+ case T_SET: {
+ SetListTypeArgs parsedargs;
+ if (!parse_set_list_args(&parsedargs, typeargs)) {
+ return NULL;
+ }
+
+ TType etype = T_STOP;
+ int32_t len = impl()->readListBegin(etype);
+ if (len < 0) {
+ return NULL;
+ }
+ if (len > 0 && !checkType(etype, parsedargs.element_type)) {
+ return NULL;
+ }
+
+ bool use_tuple = type == T_LIST && parsedargs.immutable;
+ ScopedPyObject ret(use_tuple ? PyTuple_New(len) : PyList_New(len));
+ if (!ret) {
+ return NULL;
+ }
+
+ for (int i = 0; i < len; i++) {
+ PyObject* item = decodeValue(etype, parsedargs.typeargs);
+ if (!item) {
+ return NULL;
+ }
+ if (use_tuple) {
+ PyTuple_SET_ITEM(ret.get(), i, item);
+ } else {
+ PyList_SET_ITEM(ret.get(), i, item);
+ }
+ }
+
+ // TODO(dreiss): Consider biting the bullet and making two separate cases
+ // for list and set, avoiding this post facto conversion.
+ if (type == T_SET) {
+ PyObject* setret;
+ setret = parsedargs.immutable ? PyFrozenSet_New(ret.get()) : PySet_New(ret.get());
+ return setret;
+ }
+ return ret.release();
+ }
+
+ case T_MAP: {
+ MapTypeArgs parsedargs;
+ if (!parse_map_args(&parsedargs, typeargs)) {
+ return NULL;
+ }
+
+ TType ktype = T_STOP;
+ TType vtype = T_STOP;
+ uint32_t len = impl()->readMapBegin(ktype, vtype);
+ if (len > 0 && (!checkType(ktype, parsedargs.ktag) || !checkType(vtype, parsedargs.vtag))) {
+ return NULL;
+ }
+
+ ScopedPyObject ret(PyDict_New());
+ if (!ret) {
+ return NULL;
+ }
+
+ for (uint32_t i = 0; i < len; i++) {
+ ScopedPyObject k(decodeValue(ktype, parsedargs.ktypeargs));
+ if (!k) {
+ return NULL;
+ }
+ ScopedPyObject v(decodeValue(vtype, parsedargs.vtypeargs));
+ if (!v) {
+ return NULL;
+ }
+ if (PyDict_SetItem(ret.get(), k.get(), v.get()) == -1) {
+ return NULL;
+ }
+ }
+
+ if (parsedargs.immutable) {
+ if (!ThriftModule) {
+ ThriftModule = PyImport_ImportModule("thrift.Thrift");
+ }
+ if (!ThriftModule) {
+ return NULL;
+ }
+
+ ScopedPyObject cls(PyObject_GetAttr(ThriftModule, INTERN_STRING(TFrozenDict)));
+ if (!cls) {
+ return NULL;
+ }
+
+ ScopedPyObject arg(PyTuple_New(1));
+ PyTuple_SET_ITEM(arg.get(), 0, ret.release());
+ ret.reset(PyObject_CallObject(cls.get(), arg.get()));
+ }
+
+ return ret.release();
+ }
+
+ case T_STRUCT: {
+ StructTypeArgs parsedargs;
+ if (!parse_struct_args(&parsedargs, typeargs)) {
+ return NULL;
+ }
+ return readStruct(Py_None, parsedargs.klass, parsedargs.spec);
+ }
+
+ case T_STOP:
+ case T_VOID:
+ case T_UTF16:
+ case T_UTF8:
+ case T_U64:
+ default:
+ PyErr_Format(PyExc_TypeError, "Unexpected TType for decodeValue: %d", type);
+ return NULL;
+ }
+}
+
+template <typename Impl>
+PyObject* ProtocolBase<Impl>::readStruct(PyObject* output, PyObject* klass, PyObject* spec_seq) {
+ int spec_seq_len = PyTuple_Size(spec_seq);
+ bool immutable = output == Py_None;
+ ScopedPyObject kwargs;
+ if (spec_seq_len == -1) {
+ return NULL;
+ }
+
+ if (immutable) {
+ kwargs.reset(PyDict_New());
+ if (!kwargs) {
+ PyErr_SetString(PyExc_TypeError, "failed to prepare kwargument storage");
+ return NULL;
+ }
+ }
+
+ detail::ReadStructScope<Impl> scope = detail::readStructScope(this);
+ if (!scope) {
+ return NULL;
+ }
+ while (true) {
+ TType type = T_STOP;
+ int16_t tag;
+ if (!impl()->readFieldBegin(type, tag)) {
+ return NULL;
+ }
+ if (type == T_STOP) {
+ break;
+ }
+ if (tag < 0 || tag >= spec_seq_len) {
+ if (!skip(type)) {
+ PyErr_SetString(PyExc_TypeError, "Error while skipping unknown field");
+ return NULL;
+ }
+ continue;
+ }
+
+ PyObject* item_spec = PyTuple_GET_ITEM(spec_seq, tag);
+ if (item_spec == Py_None) {
+ if (!skip(type)) {
+ PyErr_SetString(PyExc_TypeError, "Error while skipping unknown field");
+ return NULL;
+ }
+ continue;
+ }
+ StructItemSpec parsedspec;
+ if (!parse_struct_item_spec(&parsedspec, item_spec)) {
+ return NULL;
+ }
+ if (parsedspec.type != type) {
+ if (!skip(type)) {
+ PyErr_Format(PyExc_TypeError, "struct field had wrong type: expected %d but got %d",
+ parsedspec.type, type);
+ return NULL;
+ }
+ continue;
+ }
+
+ ScopedPyObject fieldval(decodeValue(parsedspec.type, parsedspec.typeargs));
+ if (!fieldval) {
+ return NULL;
+ }
+
+ if ((immutable && PyDict_SetItem(kwargs.get(), parsedspec.attrname, fieldval.get()) == -1)
+ || (!immutable && PyObject_SetAttr(output, parsedspec.attrname, fieldval.get()) == -1)) {
+ return NULL;
+ }
+ }
+ if (immutable) {
+ ScopedPyObject args(PyTuple_New(0));
+ if (!args) {
+ PyErr_SetString(PyExc_TypeError, "failed to prepare argument storage");
+ return NULL;
+ }
+ return PyObject_Call(klass, args.get(), kwargs.get());
+ }
+ Py_INCREF(output);
+ return output;
+}
+}
+}
+}
+#endif // THRIFT_PY_PROTOCOL_H
diff --git a/src/jaegertracing/thrift/lib/py/src/ext/types.cpp b/src/jaegertracing/thrift/lib/py/src/ext/types.cpp
new file mode 100644
index 000000000..68443fbe8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/ext/types.cpp
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "ext/types.h"
+#include "ext/protocol.h"
+
+namespace apache {
+namespace thrift {
+namespace py {
+
+PyObject* ThriftModule = NULL;
+
+#if PY_MAJOR_VERSION < 3
+char refill_signature[] = {'s', '#', 'i'};
+#else
+const char* refill_signature = "y#i";
+#endif
+
+bool parse_struct_item_spec(StructItemSpec* dest, PyObject* spec_tuple) {
+ // i'd like to use ParseArgs here, but it seems to be a bottleneck.
+ if (PyTuple_Size(spec_tuple) != 5) {
+ PyErr_Format(PyExc_TypeError, "expecting 5 arguments for spec tuple but got %d",
+ static_cast<int>(PyTuple_Size(spec_tuple)));
+ return false;
+ }
+
+ dest->tag = static_cast<TType>(PyInt_AsLong(PyTuple_GET_ITEM(spec_tuple, 0)));
+ if (INT_CONV_ERROR_OCCURRED(dest->tag)) {
+ return false;
+ }
+
+ dest->type = static_cast<TType>(PyInt_AsLong(PyTuple_GET_ITEM(spec_tuple, 1)));
+ if (INT_CONV_ERROR_OCCURRED(dest->type)) {
+ return false;
+ }
+
+ dest->attrname = PyTuple_GET_ITEM(spec_tuple, 2);
+ dest->typeargs = PyTuple_GET_ITEM(spec_tuple, 3);
+ dest->defval = PyTuple_GET_ITEM(spec_tuple, 4);
+ return true;
+}
+
+bool parse_set_list_args(SetListTypeArgs* dest, PyObject* typeargs) {
+ if (PyTuple_Size(typeargs) != 3) {
+ PyErr_SetString(PyExc_TypeError, "expecting tuple of size 3 for list/set type args");
+ return false;
+ }
+
+ dest->element_type = static_cast<TType>(PyInt_AsLong(PyTuple_GET_ITEM(typeargs, 0)));
+ if (INT_CONV_ERROR_OCCURRED(dest->element_type)) {
+ return false;
+ }
+
+ dest->typeargs = PyTuple_GET_ITEM(typeargs, 1);
+
+ dest->immutable = Py_True == PyTuple_GET_ITEM(typeargs, 2);
+
+ return true;
+}
+
+bool parse_map_args(MapTypeArgs* dest, PyObject* typeargs) {
+ if (PyTuple_Size(typeargs) != 5) {
+ PyErr_SetString(PyExc_TypeError, "expecting 5 arguments for typeargs to map");
+ return false;
+ }
+
+ dest->ktag = static_cast<TType>(PyInt_AsLong(PyTuple_GET_ITEM(typeargs, 0)));
+ if (INT_CONV_ERROR_OCCURRED(dest->ktag)) {
+ return false;
+ }
+
+ dest->vtag = static_cast<TType>(PyInt_AsLong(PyTuple_GET_ITEM(typeargs, 2)));
+ if (INT_CONV_ERROR_OCCURRED(dest->vtag)) {
+ return false;
+ }
+
+ dest->ktypeargs = PyTuple_GET_ITEM(typeargs, 1);
+ dest->vtypeargs = PyTuple_GET_ITEM(typeargs, 3);
+ dest->immutable = Py_True == PyTuple_GET_ITEM(typeargs, 4);
+
+ return true;
+}
+
+bool parse_struct_args(StructTypeArgs* dest, PyObject* typeargs) {
+ if (PyList_Size(typeargs) != 2) {
+ PyErr_SetString(PyExc_TypeError, "expecting list of size 2 for struct args");
+ return false;
+ }
+
+ dest->klass = PyList_GET_ITEM(typeargs, 0);
+ dest->spec = PyList_GET_ITEM(typeargs, 1);
+
+ return true;
+}
+}
+}
+}
diff --git a/src/jaegertracing/thrift/lib/py/src/ext/types.h b/src/jaegertracing/thrift/lib/py/src/ext/types.h
new file mode 100644
index 000000000..5cd8dda9e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/ext/types.h
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef THRIFT_PY_TYPES_H
+#define THRIFT_PY_TYPES_H
+
+#include <Python.h>
+
+#ifdef _MSC_VER
+#define __STDC_FORMAT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+#include <stdint.h>
+
+#if PY_MAJOR_VERSION >= 3
+
+#include <vector>
+
+// TODO: better macros
+#define PyInt_AsLong(v) PyLong_AsLong(v)
+#define PyInt_FromLong(v) PyLong_FromLong(v)
+
+#define PyString_InternFromString(v) PyUnicode_InternFromString(v)
+
+#endif
+
+#define INTERN_STRING(value) _intern_##value
+
+#define INT_CONV_ERROR_OCCURRED(v) (((v) == -1) && PyErr_Occurred())
+
+extern "C" {
+extern PyObject* INTERN_STRING(TFrozenDict);
+extern PyObject* INTERN_STRING(cstringio_buf);
+extern PyObject* INTERN_STRING(cstringio_refill);
+}
+
+namespace apache {
+namespace thrift {
+namespace py {
+
+extern PyObject* ThriftModule;
+
+// Stolen out of TProtocol.h.
+// It would be a huge pain to have both get this from one place.
+enum TType {
+ T_INVALID = -1,
+ T_STOP = 0,
+ T_VOID = 1,
+ T_BOOL = 2,
+ T_BYTE = 3,
+ T_I08 = 3,
+ T_I16 = 6,
+ T_I32 = 8,
+ T_U64 = 9,
+ T_I64 = 10,
+ T_DOUBLE = 4,
+ T_STRING = 11,
+ T_UTF7 = 11,
+ T_STRUCT = 12,
+ T_MAP = 13,
+ T_SET = 14,
+ T_LIST = 15,
+ T_UTF8 = 16,
+ T_UTF16 = 17
+};
+
+// replace with unique_ptr when we're OK with C++11
+class ScopedPyObject {
+public:
+ ScopedPyObject() : obj_(NULL) {}
+ explicit ScopedPyObject(PyObject* py_object) : obj_(py_object) {}
+ ~ScopedPyObject() {
+ if (obj_)
+ Py_DECREF(obj_);
+ }
+ PyObject* get() throw() { return obj_; }
+ operator bool() { return obj_; }
+ void reset(PyObject* py_object) throw() {
+ if (obj_)
+ Py_DECREF(obj_);
+ obj_ = py_object;
+ }
+ PyObject* release() throw() {
+ PyObject* tmp = obj_;
+ obj_ = NULL;
+ return tmp;
+ }
+ void swap(ScopedPyObject& other) throw() {
+ ScopedPyObject tmp(other.release());
+ other.reset(release());
+ reset(tmp.release());
+ }
+
+private:
+ ScopedPyObject(const ScopedPyObject&) {}
+ ScopedPyObject& operator=(const ScopedPyObject&) { return *this; }
+
+ PyObject* obj_;
+};
+
+/**
+ * A cache of the two key attributes of a CReadableTransport,
+ * so we don't have to keep calling PyObject_GetAttr.
+ */
+struct DecodeBuffer {
+ ScopedPyObject stringiobuf;
+ ScopedPyObject refill_callable;
+};
+
+#if PY_MAJOR_VERSION < 3
+extern char refill_signature[3];
+typedef PyObject EncodeBuffer;
+#else
+extern const char* refill_signature;
+struct EncodeBuffer {
+ std::vector<char> buf;
+ size_t pos;
+};
+#endif
+
+/**
+ * A cache of the spec_args for a set or list,
+ * so we don't have to keep calling PyTuple_GET_ITEM.
+ */
+struct SetListTypeArgs {
+ TType element_type;
+ PyObject* typeargs;
+ bool immutable;
+};
+
+/**
+ * A cache of the spec_args for a map,
+ * so we don't have to keep calling PyTuple_GET_ITEM.
+ */
+struct MapTypeArgs {
+ TType ktag;
+ TType vtag;
+ PyObject* ktypeargs;
+ PyObject* vtypeargs;
+ bool immutable;
+};
+
+/**
+ * A cache of the spec_args for a struct,
+ * so we don't have to keep calling PyTuple_GET_ITEM.
+ */
+struct StructTypeArgs {
+ PyObject* klass;
+ PyObject* spec;
+ bool immutable;
+};
+
+/**
+ * A cache of the item spec from a struct specification,
+ * so we don't have to keep calling PyTuple_GET_ITEM.
+ */
+struct StructItemSpec {
+ int tag;
+ TType type;
+ PyObject* attrname;
+ PyObject* typeargs;
+ PyObject* defval;
+};
+
+bool parse_set_list_args(SetListTypeArgs* dest, PyObject* typeargs);
+
+bool parse_map_args(MapTypeArgs* dest, PyObject* typeargs);
+
+bool parse_struct_args(StructTypeArgs* dest, PyObject* typeargs);
+
+bool parse_struct_item_spec(StructItemSpec* dest, PyObject* spec_tuple);
+}
+}
+}
+
+#endif // THRIFT_PY_TYPES_H
diff --git a/src/jaegertracing/thrift/lib/py/src/protocol/TBase.py b/src/jaegertracing/thrift/lib/py/src/protocol/TBase.py
new file mode 100644
index 000000000..9ae1b1182
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/protocol/TBase.py
@@ -0,0 +1,82 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from thrift.transport import TTransport
+
+
+class TBase(object):
+ __slots__ = ()
+
+ def __repr__(self):
+ L = ['%s=%r' % (key, getattr(self, key)) for key in self.__slots__]
+ return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+ def __eq__(self, other):
+ if not isinstance(other, self.__class__):
+ return False
+ for attr in self.__slots__:
+ my_val = getattr(self, attr)
+ other_val = getattr(other, attr)
+ if my_val != other_val:
+ return False
+ return True
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ def read(self, iprot):
+ if (iprot._fast_decode is not None and
+ isinstance(iprot.trans, TTransport.CReadableTransport) and
+ self.thrift_spec is not None):
+ iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+ else:
+ iprot.readStruct(self, self.thrift_spec)
+
+ def write(self, oprot):
+ if (oprot._fast_encode is not None and self.thrift_spec is not None):
+ oprot.trans.write(
+ oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+ else:
+ oprot.writeStruct(self, self.thrift_spec)
+
+
+class TExceptionBase(TBase, Exception):
+ pass
+
+
+class TFrozenBase(TBase):
+ def __setitem__(self, *args):
+ raise TypeError("Can't modify frozen struct")
+
+ def __delitem__(self, *args):
+ raise TypeError("Can't modify frozen struct")
+
+ def __hash__(self, *args):
+ return hash(self.__class__) ^ hash(self.__slots__)
+
+ @classmethod
+ def read(cls, iprot):
+ if (iprot._fast_decode is not None and
+ isinstance(iprot.trans, TTransport.CReadableTransport) and
+ cls.thrift_spec is not None):
+ self = cls()
+ return iprot._fast_decode(None, iprot,
+ [self.__class__, self.thrift_spec])
+ else:
+ return iprot.readStruct(cls, cls.thrift_spec, True)
diff --git a/src/jaegertracing/thrift/lib/py/src/protocol/TBinaryProtocol.py b/src/jaegertracing/thrift/lib/py/src/protocol/TBinaryProtocol.py
new file mode 100644
index 000000000..6b2facc4f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/protocol/TBinaryProtocol.py
@@ -0,0 +1,301 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from .TProtocol import TType, TProtocolBase, TProtocolException, TProtocolFactory
+from struct import pack, unpack
+
+
+class TBinaryProtocol(TProtocolBase):
+ """Binary implementation of the Thrift protocol driver."""
+
+ # NastyHaxx. Python 2.4+ on 32-bit machines forces hex constants to be
+ # positive, converting this into a long. If we hardcode the int value
+ # instead it'll stay in 32 bit-land.
+
+ # VERSION_MASK = 0xffff0000
+ VERSION_MASK = -65536
+
+ # VERSION_1 = 0x80010000
+ VERSION_1 = -2147418112
+
+ TYPE_MASK = 0x000000ff
+
+ def __init__(self, trans, strictRead=False, strictWrite=True, **kwargs):
+ TProtocolBase.__init__(self, trans)
+ self.strictRead = strictRead
+ self.strictWrite = strictWrite
+ self.string_length_limit = kwargs.get('string_length_limit', None)
+ self.container_length_limit = kwargs.get('container_length_limit', None)
+
+ def _check_string_length(self, length):
+ self._check_length(self.string_length_limit, length)
+
+ def _check_container_length(self, length):
+ self._check_length(self.container_length_limit, length)
+
+ def writeMessageBegin(self, name, type, seqid):
+ if self.strictWrite:
+ self.writeI32(TBinaryProtocol.VERSION_1 | type)
+ self.writeString(name)
+ self.writeI32(seqid)
+ else:
+ self.writeString(name)
+ self.writeByte(type)
+ self.writeI32(seqid)
+
+ def writeMessageEnd(self):
+ pass
+
+ def writeStructBegin(self, name):
+ pass
+
+ def writeStructEnd(self):
+ pass
+
+ def writeFieldBegin(self, name, type, id):
+ self.writeByte(type)
+ self.writeI16(id)
+
+ def writeFieldEnd(self):
+ pass
+
+ def writeFieldStop(self):
+ self.writeByte(TType.STOP)
+
+ def writeMapBegin(self, ktype, vtype, size):
+ self.writeByte(ktype)
+ self.writeByte(vtype)
+ self.writeI32(size)
+
+ def writeMapEnd(self):
+ pass
+
+ def writeListBegin(self, etype, size):
+ self.writeByte(etype)
+ self.writeI32(size)
+
+ def writeListEnd(self):
+ pass
+
+ def writeSetBegin(self, etype, size):
+ self.writeByte(etype)
+ self.writeI32(size)
+
+ def writeSetEnd(self):
+ pass
+
+ def writeBool(self, bool):
+ if bool:
+ self.writeByte(1)
+ else:
+ self.writeByte(0)
+
+ def writeByte(self, byte):
+ buff = pack("!b", byte)
+ self.trans.write(buff)
+
+ def writeI16(self, i16):
+ buff = pack("!h", i16)
+ self.trans.write(buff)
+
+ def writeI32(self, i32):
+ buff = pack("!i", i32)
+ self.trans.write(buff)
+
+ def writeI64(self, i64):
+ buff = pack("!q", i64)
+ self.trans.write(buff)
+
+ def writeDouble(self, dub):
+ buff = pack("!d", dub)
+ self.trans.write(buff)
+
+ def writeBinary(self, str):
+ self.writeI32(len(str))
+ self.trans.write(str)
+
+ def readMessageBegin(self):
+ sz = self.readI32()
+ if sz < 0:
+ version = sz & TBinaryProtocol.VERSION_MASK
+ if version != TBinaryProtocol.VERSION_1:
+ raise TProtocolException(
+ type=TProtocolException.BAD_VERSION,
+ message='Bad version in readMessageBegin: %d' % (sz))
+ type = sz & TBinaryProtocol.TYPE_MASK
+ name = self.readString()
+ seqid = self.readI32()
+ else:
+ if self.strictRead:
+ raise TProtocolException(type=TProtocolException.BAD_VERSION,
+ message='No protocol version header')
+ name = self.trans.readAll(sz)
+ type = self.readByte()
+ seqid = self.readI32()
+ return (name, type, seqid)
+
+ def readMessageEnd(self):
+ pass
+
+ def readStructBegin(self):
+ pass
+
+ def readStructEnd(self):
+ pass
+
+ def readFieldBegin(self):
+ type = self.readByte()
+ if type == TType.STOP:
+ return (None, type, 0)
+ id = self.readI16()
+ return (None, type, id)
+
+ def readFieldEnd(self):
+ pass
+
+ def readMapBegin(self):
+ ktype = self.readByte()
+ vtype = self.readByte()
+ size = self.readI32()
+ self._check_container_length(size)
+ return (ktype, vtype, size)
+
+ def readMapEnd(self):
+ pass
+
+ def readListBegin(self):
+ etype = self.readByte()
+ size = self.readI32()
+ self._check_container_length(size)
+ return (etype, size)
+
+ def readListEnd(self):
+ pass
+
+ def readSetBegin(self):
+ etype = self.readByte()
+ size = self.readI32()
+ self._check_container_length(size)
+ return (etype, size)
+
+ def readSetEnd(self):
+ pass
+
+ def readBool(self):
+ byte = self.readByte()
+ if byte == 0:
+ return False
+ return True
+
+ def readByte(self):
+ buff = self.trans.readAll(1)
+ val, = unpack('!b', buff)
+ return val
+
+ def readI16(self):
+ buff = self.trans.readAll(2)
+ val, = unpack('!h', buff)
+ return val
+
+ def readI32(self):
+ buff = self.trans.readAll(4)
+ val, = unpack('!i', buff)
+ return val
+
+ def readI64(self):
+ buff = self.trans.readAll(8)
+ val, = unpack('!q', buff)
+ return val
+
+ def readDouble(self):
+ buff = self.trans.readAll(8)
+ val, = unpack('!d', buff)
+ return val
+
+ def readBinary(self):
+ size = self.readI32()
+ self._check_string_length(size)
+ s = self.trans.readAll(size)
+ return s
+
+
+class TBinaryProtocolFactory(TProtocolFactory):
+ def __init__(self, strictRead=False, strictWrite=True, **kwargs):
+ self.strictRead = strictRead
+ self.strictWrite = strictWrite
+ self.string_length_limit = kwargs.get('string_length_limit', None)
+ self.container_length_limit = kwargs.get('container_length_limit', None)
+
+ def getProtocol(self, trans):
+ prot = TBinaryProtocol(trans, self.strictRead, self.strictWrite,
+ string_length_limit=self.string_length_limit,
+ container_length_limit=self.container_length_limit)
+ return prot
+
+
+class TBinaryProtocolAccelerated(TBinaryProtocol):
+ """C-Accelerated version of TBinaryProtocol.
+
+ This class does not override any of TBinaryProtocol's methods,
+ but the generated code recognizes it directly and will call into
+ our C module to do the encoding, bypassing this object entirely.
+ We inherit from TBinaryProtocol so that the normal TBinaryProtocol
+ encoding can happen if the fastbinary module doesn't work for some
+ reason. (TODO(dreiss): Make this happen sanely in more cases.)
+ To disable this behavior, pass fallback=False constructor argument.
+
+ In order to take advantage of the C module, just use
+ TBinaryProtocolAccelerated instead of TBinaryProtocol.
+
+ NOTE: This code was contributed by an external developer.
+ The internal Thrift team has reviewed and tested it,
+ but we cannot guarantee that it is production-ready.
+ Please feel free to report bugs and/or success stories
+ to the public mailing list.
+ """
+ pass
+
+ def __init__(self, *args, **kwargs):
+ fallback = kwargs.pop('fallback', True)
+ super(TBinaryProtocolAccelerated, self).__init__(*args, **kwargs)
+ try:
+ from thrift.protocol import fastbinary
+ except ImportError:
+ if not fallback:
+ raise
+ else:
+ self._fast_decode = fastbinary.decode_binary
+ self._fast_encode = fastbinary.encode_binary
+
+
+class TBinaryProtocolAcceleratedFactory(TProtocolFactory):
+ def __init__(self,
+ string_length_limit=None,
+ container_length_limit=None,
+ fallback=True):
+ self.string_length_limit = string_length_limit
+ self.container_length_limit = container_length_limit
+ self._fallback = fallback
+
+ def getProtocol(self, trans):
+ return TBinaryProtocolAccelerated(
+ trans,
+ string_length_limit=self.string_length_limit,
+ container_length_limit=self.container_length_limit,
+ fallback=self._fallback)
diff --git a/src/jaegertracing/thrift/lib/py/src/protocol/TCompactProtocol.py b/src/jaegertracing/thrift/lib/py/src/protocol/TCompactProtocol.py
new file mode 100644
index 000000000..700e792f7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/protocol/TCompactProtocol.py
@@ -0,0 +1,487 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from .TProtocol import TType, TProtocolBase, TProtocolException, TProtocolFactory, checkIntegerLimits
+from struct import pack, unpack
+
+from ..compat import binary_to_str, str_to_binary
+
+__all__ = ['TCompactProtocol', 'TCompactProtocolFactory']
+
+CLEAR = 0
+FIELD_WRITE = 1
+VALUE_WRITE = 2
+CONTAINER_WRITE = 3
+BOOL_WRITE = 4
+FIELD_READ = 5
+CONTAINER_READ = 6
+VALUE_READ = 7
+BOOL_READ = 8
+
+
+def make_helper(v_from, container):
+ def helper(func):
+ def nested(self, *args, **kwargs):
+ assert self.state in (v_from, container), (self.state, v_from, container)
+ return func(self, *args, **kwargs)
+ return nested
+ return helper
+
+
+writer = make_helper(VALUE_WRITE, CONTAINER_WRITE)
+reader = make_helper(VALUE_READ, CONTAINER_READ)
+
+
+def makeZigZag(n, bits):
+ checkIntegerLimits(n, bits)
+ return (n << 1) ^ (n >> (bits - 1))
+
+
+def fromZigZag(n):
+ return (n >> 1) ^ -(n & 1)
+
+
+def writeVarint(trans, n):
+ assert n >= 0, "Input to TCompactProtocol writeVarint cannot be negative!"
+ out = bytearray()
+ while True:
+ if n & ~0x7f == 0:
+ out.append(n)
+ break
+ else:
+ out.append((n & 0xff) | 0x80)
+ n = n >> 7
+ trans.write(bytes(out))
+
+
+def readVarint(trans):
+ result = 0
+ shift = 0
+ while True:
+ x = trans.readAll(1)
+ byte = ord(x)
+ result |= (byte & 0x7f) << shift
+ if byte >> 7 == 0:
+ return result
+ shift += 7
+
+
+class CompactType(object):
+ STOP = 0x00
+ TRUE = 0x01
+ FALSE = 0x02
+ BYTE = 0x03
+ I16 = 0x04
+ I32 = 0x05
+ I64 = 0x06
+ DOUBLE = 0x07
+ BINARY = 0x08
+ LIST = 0x09
+ SET = 0x0A
+ MAP = 0x0B
+ STRUCT = 0x0C
+
+
+CTYPES = {
+ TType.STOP: CompactType.STOP,
+ TType.BOOL: CompactType.TRUE, # used for collection
+ TType.BYTE: CompactType.BYTE,
+ TType.I16: CompactType.I16,
+ TType.I32: CompactType.I32,
+ TType.I64: CompactType.I64,
+ TType.DOUBLE: CompactType.DOUBLE,
+ TType.STRING: CompactType.BINARY,
+ TType.STRUCT: CompactType.STRUCT,
+ TType.LIST: CompactType.LIST,
+ TType.SET: CompactType.SET,
+ TType.MAP: CompactType.MAP,
+}
+
+TTYPES = {}
+for k, v in CTYPES.items():
+ TTYPES[v] = k
+TTYPES[CompactType.FALSE] = TType.BOOL
+del k
+del v
+
+
+class TCompactProtocol(TProtocolBase):
+ """Compact implementation of the Thrift protocol driver."""
+
+ PROTOCOL_ID = 0x82
+ VERSION = 1
+ VERSION_MASK = 0x1f
+ TYPE_MASK = 0xe0
+ TYPE_BITS = 0x07
+ TYPE_SHIFT_AMOUNT = 5
+
+ def __init__(self, trans,
+ string_length_limit=None,
+ container_length_limit=None):
+ TProtocolBase.__init__(self, trans)
+ self.state = CLEAR
+ self.__last_fid = 0
+ self.__bool_fid = None
+ self.__bool_value = None
+ self.__structs = []
+ self.__containers = []
+ self.string_length_limit = string_length_limit
+ self.container_length_limit = container_length_limit
+
+ def _check_string_length(self, length):
+ self._check_length(self.string_length_limit, length)
+
+ def _check_container_length(self, length):
+ self._check_length(self.container_length_limit, length)
+
+ def __writeVarint(self, n):
+ writeVarint(self.trans, n)
+
+ def writeMessageBegin(self, name, type, seqid):
+ assert self.state == CLEAR
+ self.__writeUByte(self.PROTOCOL_ID)
+ self.__writeUByte(self.VERSION | (type << self.TYPE_SHIFT_AMOUNT))
+ # The sequence id is a signed 32-bit integer but the compact protocol
+ # writes this out as a "var int" which is always positive, and attempting
+ # to write a negative number results in an infinite loop, so we may
+ # need to do some conversion here...
+ tseqid = seqid
+ if tseqid < 0:
+ tseqid = 2147483648 + (2147483648 + tseqid)
+ self.__writeVarint(tseqid)
+ self.__writeBinary(str_to_binary(name))
+ self.state = VALUE_WRITE
+
+ def writeMessageEnd(self):
+ assert self.state == VALUE_WRITE
+ self.state = CLEAR
+
+ def writeStructBegin(self, name):
+ assert self.state in (CLEAR, CONTAINER_WRITE, VALUE_WRITE), self.state
+ self.__structs.append((self.state, self.__last_fid))
+ self.state = FIELD_WRITE
+ self.__last_fid = 0
+
+ def writeStructEnd(self):
+ assert self.state == FIELD_WRITE
+ self.state, self.__last_fid = self.__structs.pop()
+
+ def writeFieldStop(self):
+ self.__writeByte(0)
+
+ def __writeFieldHeader(self, type, fid):
+ delta = fid - self.__last_fid
+ if 0 < delta <= 15:
+ self.__writeUByte(delta << 4 | type)
+ else:
+ self.__writeByte(type)
+ self.__writeI16(fid)
+ self.__last_fid = fid
+
+ def writeFieldBegin(self, name, type, fid):
+ assert self.state == FIELD_WRITE, self.state
+ if type == TType.BOOL:
+ self.state = BOOL_WRITE
+ self.__bool_fid = fid
+ else:
+ self.state = VALUE_WRITE
+ self.__writeFieldHeader(CTYPES[type], fid)
+
+ def writeFieldEnd(self):
+ assert self.state in (VALUE_WRITE, BOOL_WRITE), self.state
+ self.state = FIELD_WRITE
+
+ def __writeUByte(self, byte):
+ self.trans.write(pack('!B', byte))
+
+ def __writeByte(self, byte):
+ self.trans.write(pack('!b', byte))
+
+ def __writeI16(self, i16):
+ self.__writeVarint(makeZigZag(i16, 16))
+
+ def __writeSize(self, i32):
+ self.__writeVarint(i32)
+
+ def writeCollectionBegin(self, etype, size):
+ assert self.state in (VALUE_WRITE, CONTAINER_WRITE), self.state
+ if size <= 14:
+ self.__writeUByte(size << 4 | CTYPES[etype])
+ else:
+ self.__writeUByte(0xf0 | CTYPES[etype])
+ self.__writeSize(size)
+ self.__containers.append(self.state)
+ self.state = CONTAINER_WRITE
+ writeSetBegin = writeCollectionBegin
+ writeListBegin = writeCollectionBegin
+
+ def writeMapBegin(self, ktype, vtype, size):
+ assert self.state in (VALUE_WRITE, CONTAINER_WRITE), self.state
+ if size == 0:
+ self.__writeByte(0)
+ else:
+ self.__writeSize(size)
+ self.__writeUByte(CTYPES[ktype] << 4 | CTYPES[vtype])
+ self.__containers.append(self.state)
+ self.state = CONTAINER_WRITE
+
+ def writeCollectionEnd(self):
+ assert self.state == CONTAINER_WRITE, self.state
+ self.state = self.__containers.pop()
+ writeMapEnd = writeCollectionEnd
+ writeSetEnd = writeCollectionEnd
+ writeListEnd = writeCollectionEnd
+
+ def writeBool(self, bool):
+ if self.state == BOOL_WRITE:
+ if bool:
+ ctype = CompactType.TRUE
+ else:
+ ctype = CompactType.FALSE
+ self.__writeFieldHeader(ctype, self.__bool_fid)
+ elif self.state == CONTAINER_WRITE:
+ if bool:
+ self.__writeByte(CompactType.TRUE)
+ else:
+ self.__writeByte(CompactType.FALSE)
+ else:
+ raise AssertionError("Invalid state in compact protocol")
+
+ writeByte = writer(__writeByte)
+ writeI16 = writer(__writeI16)
+
+ @writer
+ def writeI32(self, i32):
+ self.__writeVarint(makeZigZag(i32, 32))
+
+ @writer
+ def writeI64(self, i64):
+ self.__writeVarint(makeZigZag(i64, 64))
+
+ @writer
+ def writeDouble(self, dub):
+ self.trans.write(pack('<d', dub))
+
+ def __writeBinary(self, s):
+ self.__writeSize(len(s))
+ self.trans.write(s)
+ writeBinary = writer(__writeBinary)
+
+ def readFieldBegin(self):
+ assert self.state == FIELD_READ, self.state
+ type = self.__readUByte()
+ if type & 0x0f == TType.STOP:
+ return (None, 0, 0)
+ delta = type >> 4
+ if delta == 0:
+ fid = self.__readI16()
+ else:
+ fid = self.__last_fid + delta
+ self.__last_fid = fid
+ type = type & 0x0f
+ if type == CompactType.TRUE:
+ self.state = BOOL_READ
+ self.__bool_value = True
+ elif type == CompactType.FALSE:
+ self.state = BOOL_READ
+ self.__bool_value = False
+ else:
+ self.state = VALUE_READ
+ return (None, self.__getTType(type), fid)
+
+ def readFieldEnd(self):
+ assert self.state in (VALUE_READ, BOOL_READ), self.state
+ self.state = FIELD_READ
+
+ def __readUByte(self):
+ result, = unpack('!B', self.trans.readAll(1))
+ return result
+
+ def __readByte(self):
+ result, = unpack('!b', self.trans.readAll(1))
+ return result
+
+ def __readVarint(self):
+ return readVarint(self.trans)
+
+ def __readZigZag(self):
+ return fromZigZag(self.__readVarint())
+
+ def __readSize(self):
+ result = self.__readVarint()
+ if result < 0:
+ raise TProtocolException("Length < 0")
+ return result
+
+ def readMessageBegin(self):
+ assert self.state == CLEAR
+ proto_id = self.__readUByte()
+ if proto_id != self.PROTOCOL_ID:
+ raise TProtocolException(TProtocolException.BAD_VERSION,
+ 'Bad protocol id in the message: %d' % proto_id)
+ ver_type = self.__readUByte()
+ type = (ver_type >> self.TYPE_SHIFT_AMOUNT) & self.TYPE_BITS
+ version = ver_type & self.VERSION_MASK
+ if version != self.VERSION:
+ raise TProtocolException(TProtocolException.BAD_VERSION,
+ 'Bad version: %d (expect %d)' % (version, self.VERSION))
+ seqid = self.__readVarint()
+ # the sequence is a compact "var int" which is treaded as unsigned,
+ # however the sequence is actually signed...
+ if seqid > 2147483647:
+ seqid = -2147483648 - (2147483648 - seqid)
+ name = binary_to_str(self.__readBinary())
+ return (name, type, seqid)
+
+ def readMessageEnd(self):
+ assert self.state == CLEAR
+ assert len(self.__structs) == 0
+
+ def readStructBegin(self):
+ assert self.state in (CLEAR, CONTAINER_READ, VALUE_READ), self.state
+ self.__structs.append((self.state, self.__last_fid))
+ self.state = FIELD_READ
+ self.__last_fid = 0
+
+ def readStructEnd(self):
+ assert self.state == FIELD_READ
+ self.state, self.__last_fid = self.__structs.pop()
+
+ def readCollectionBegin(self):
+ assert self.state in (VALUE_READ, CONTAINER_READ), self.state
+ size_type = self.__readUByte()
+ size = size_type >> 4
+ type = self.__getTType(size_type)
+ if size == 15:
+ size = self.__readSize()
+ self._check_container_length(size)
+ self.__containers.append(self.state)
+ self.state = CONTAINER_READ
+ return type, size
+ readSetBegin = readCollectionBegin
+ readListBegin = readCollectionBegin
+
+ def readMapBegin(self):
+ assert self.state in (VALUE_READ, CONTAINER_READ), self.state
+ size = self.__readSize()
+ self._check_container_length(size)
+ types = 0
+ if size > 0:
+ types = self.__readUByte()
+ vtype = self.__getTType(types)
+ ktype = self.__getTType(types >> 4)
+ self.__containers.append(self.state)
+ self.state = CONTAINER_READ
+ return (ktype, vtype, size)
+
+ def readCollectionEnd(self):
+ assert self.state == CONTAINER_READ, self.state
+ self.state = self.__containers.pop()
+ readSetEnd = readCollectionEnd
+ readListEnd = readCollectionEnd
+ readMapEnd = readCollectionEnd
+
+ def readBool(self):
+ if self.state == BOOL_READ:
+ return self.__bool_value == CompactType.TRUE
+ elif self.state == CONTAINER_READ:
+ return self.__readByte() == CompactType.TRUE
+ else:
+ raise AssertionError("Invalid state in compact protocol: %d" %
+ self.state)
+
+ readByte = reader(__readByte)
+ __readI16 = __readZigZag
+ readI16 = reader(__readZigZag)
+ readI32 = reader(__readZigZag)
+ readI64 = reader(__readZigZag)
+
+ @reader
+ def readDouble(self):
+ buff = self.trans.readAll(8)
+ val, = unpack('<d', buff)
+ return val
+
+ def __readBinary(self):
+ size = self.__readSize()
+ self._check_string_length(size)
+ return self.trans.readAll(size)
+ readBinary = reader(__readBinary)
+
+ def __getTType(self, byte):
+ return TTYPES[byte & 0x0f]
+
+
+class TCompactProtocolFactory(TProtocolFactory):
+ def __init__(self,
+ string_length_limit=None,
+ container_length_limit=None):
+ self.string_length_limit = string_length_limit
+ self.container_length_limit = container_length_limit
+
+ def getProtocol(self, trans):
+ return TCompactProtocol(trans,
+ self.string_length_limit,
+ self.container_length_limit)
+
+
+class TCompactProtocolAccelerated(TCompactProtocol):
+ """C-Accelerated version of TCompactProtocol.
+
+ This class does not override any of TCompactProtocol's methods,
+ but the generated code recognizes it directly and will call into
+ our C module to do the encoding, bypassing this object entirely.
+ We inherit from TCompactProtocol so that the normal TCompactProtocol
+ encoding can happen if the fastbinary module doesn't work for some
+ reason.
+ To disable this behavior, pass fallback=False constructor argument.
+
+ In order to take advantage of the C module, just use
+ TCompactProtocolAccelerated instead of TCompactProtocol.
+ """
+ pass
+
+ def __init__(self, *args, **kwargs):
+ fallback = kwargs.pop('fallback', True)
+ super(TCompactProtocolAccelerated, self).__init__(*args, **kwargs)
+ try:
+ from thrift.protocol import fastbinary
+ except ImportError:
+ if not fallback:
+ raise
+ else:
+ self._fast_decode = fastbinary.decode_compact
+ self._fast_encode = fastbinary.encode_compact
+
+
+class TCompactProtocolAcceleratedFactory(TProtocolFactory):
+ def __init__(self,
+ string_length_limit=None,
+ container_length_limit=None,
+ fallback=True):
+ self.string_length_limit = string_length_limit
+ self.container_length_limit = container_length_limit
+ self._fallback = fallback
+
+ def getProtocol(self, trans):
+ return TCompactProtocolAccelerated(
+ trans,
+ string_length_limit=self.string_length_limit,
+ container_length_limit=self.container_length_limit,
+ fallback=self._fallback)
diff --git a/src/jaegertracing/thrift/lib/py/src/protocol/THeaderProtocol.py b/src/jaegertracing/thrift/lib/py/src/protocol/THeaderProtocol.py
new file mode 100644
index 000000000..13982e898
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/protocol/THeaderProtocol.py
@@ -0,0 +1,225 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from thrift.protocol.TBinaryProtocol import TBinaryProtocolAccelerated
+from thrift.protocol.TCompactProtocol import TCompactProtocolAccelerated
+from thrift.protocol.TProtocol import TProtocolBase, TProtocolException, TProtocolFactory
+from thrift.Thrift import TApplicationException, TMessageType
+from thrift.transport.THeaderTransport import THeaderTransport, THeaderSubprotocolID, THeaderClientType
+
+
+PROTOCOLS_BY_ID = {
+ THeaderSubprotocolID.BINARY: TBinaryProtocolAccelerated,
+ THeaderSubprotocolID.COMPACT: TCompactProtocolAccelerated,
+}
+
+
+class THeaderProtocol(TProtocolBase):
+ """A framed protocol with headers and payload transforms.
+
+ THeaderProtocol frames other Thrift protocols and adds support for optional
+ out-of-band headers. The currently supported subprotocols are
+ TBinaryProtocol and TCompactProtocol.
+
+ It's also possible to apply transforms to the encoded message payload. The
+ only transform currently supported is to gzip.
+
+ When used in a server, THeaderProtocol can accept messages from
+ non-THeaderProtocol clients if allowed (see `allowed_client_types`). This
+ includes framed and unframed transports and both TBinaryProtocol and
+ TCompactProtocol. The server will respond in the appropriate dialect for
+ the connected client. HTTP clients are not currently supported.
+
+ THeaderProtocol does not currently support THTTPServer, TNonblockingServer,
+ or TProcessPoolServer.
+
+ See doc/specs/HeaderFormat.md for details of the wire format.
+
+ """
+
+ def __init__(self, transport, allowed_client_types):
+ # much of the actual work for THeaderProtocol happens down in
+ # THeaderTransport since we need to do low-level shenanigans to detect
+ # if the client is sending us headers or one of the headerless formats
+ # we support. this wraps the real transport with the one that does all
+ # the magic.
+ if not isinstance(transport, THeaderTransport):
+ transport = THeaderTransport(transport, allowed_client_types)
+ super(THeaderProtocol, self).__init__(transport)
+ self._set_protocol()
+
+ def get_headers(self):
+ return self.trans.get_headers()
+
+ def set_header(self, key, value):
+ self.trans.set_header(key, value)
+
+ def clear_headers(self):
+ self.trans.clear_headers()
+
+ def add_transform(self, transform_id):
+ self.trans.add_transform(transform_id)
+
+ def writeMessageBegin(self, name, ttype, seqid):
+ self.trans.sequence_id = seqid
+ return self._protocol.writeMessageBegin(name, ttype, seqid)
+
+ def writeMessageEnd(self):
+ return self._protocol.writeMessageEnd()
+
+ def writeStructBegin(self, name):
+ return self._protocol.writeStructBegin(name)
+
+ def writeStructEnd(self):
+ return self._protocol.writeStructEnd()
+
+ def writeFieldBegin(self, name, ttype, fid):
+ return self._protocol.writeFieldBegin(name, ttype, fid)
+
+ def writeFieldEnd(self):
+ return self._protocol.writeFieldEnd()
+
+ def writeFieldStop(self):
+ return self._protocol.writeFieldStop()
+
+ def writeMapBegin(self, ktype, vtype, size):
+ return self._protocol.writeMapBegin(ktype, vtype, size)
+
+ def writeMapEnd(self):
+ return self._protocol.writeMapEnd()
+
+ def writeListBegin(self, etype, size):
+ return self._protocol.writeListBegin(etype, size)
+
+ def writeListEnd(self):
+ return self._protocol.writeListEnd()
+
+ def writeSetBegin(self, etype, size):
+ return self._protocol.writeSetBegin(etype, size)
+
+ def writeSetEnd(self):
+ return self._protocol.writeSetEnd()
+
+ def writeBool(self, bool_val):
+ return self._protocol.writeBool(bool_val)
+
+ def writeByte(self, byte):
+ return self._protocol.writeByte(byte)
+
+ def writeI16(self, i16):
+ return self._protocol.writeI16(i16)
+
+ def writeI32(self, i32):
+ return self._protocol.writeI32(i32)
+
+ def writeI64(self, i64):
+ return self._protocol.writeI64(i64)
+
+ def writeDouble(self, dub):
+ return self._protocol.writeDouble(dub)
+
+ def writeBinary(self, str_val):
+ return self._protocol.writeBinary(str_val)
+
+ def _set_protocol(self):
+ try:
+ protocol_cls = PROTOCOLS_BY_ID[self.trans.protocol_id]
+ except KeyError:
+ raise TApplicationException(
+ TProtocolException.INVALID_PROTOCOL,
+ "Unknown protocol requested.",
+ )
+
+ self._protocol = protocol_cls(self.trans)
+ self._fast_encode = self._protocol._fast_encode
+ self._fast_decode = self._protocol._fast_decode
+
+ def readMessageBegin(self):
+ try:
+ self.trans.readFrame(0)
+ self._set_protocol()
+ except TApplicationException as exc:
+ self._protocol.writeMessageBegin(b"", TMessageType.EXCEPTION, 0)
+ exc.write(self._protocol)
+ self._protocol.writeMessageEnd()
+ self.trans.flush()
+
+ return self._protocol.readMessageBegin()
+
+ def readMessageEnd(self):
+ return self._protocol.readMessageEnd()
+
+ def readStructBegin(self):
+ return self._protocol.readStructBegin()
+
+ def readStructEnd(self):
+ return self._protocol.readStructEnd()
+
+ def readFieldBegin(self):
+ return self._protocol.readFieldBegin()
+
+ def readFieldEnd(self):
+ return self._protocol.readFieldEnd()
+
+ def readMapBegin(self):
+ return self._protocol.readMapBegin()
+
+ def readMapEnd(self):
+ return self._protocol.readMapEnd()
+
+ def readListBegin(self):
+ return self._protocol.readListBegin()
+
+ def readListEnd(self):
+ return self._protocol.readListEnd()
+
+ def readSetBegin(self):
+ return self._protocol.readSetBegin()
+
+ def readSetEnd(self):
+ return self._protocol.readSetEnd()
+
+ def readBool(self):
+ return self._protocol.readBool()
+
+ def readByte(self):
+ return self._protocol.readByte()
+
+ def readI16(self):
+ return self._protocol.readI16()
+
+ def readI32(self):
+ return self._protocol.readI32()
+
+ def readI64(self):
+ return self._protocol.readI64()
+
+ def readDouble(self):
+ return self._protocol.readDouble()
+
+ def readBinary(self):
+ return self._protocol.readBinary()
+
+
+class THeaderProtocolFactory(TProtocolFactory):
+ def __init__(self, allowed_client_types=(THeaderClientType.HEADERS,)):
+ self.allowed_client_types = allowed_client_types
+
+ def getProtocol(self, trans):
+ return THeaderProtocol(trans, self.allowed_client_types)
diff --git a/src/jaegertracing/thrift/lib/py/src/protocol/TJSONProtocol.py b/src/jaegertracing/thrift/lib/py/src/protocol/TJSONProtocol.py
new file mode 100644
index 000000000..17417027a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/protocol/TJSONProtocol.py
@@ -0,0 +1,677 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from .TProtocol import (TType, TProtocolBase, TProtocolException,
+ TProtocolFactory, checkIntegerLimits)
+import base64
+import math
+import sys
+
+from ..compat import str_to_binary
+
+
+__all__ = ['TJSONProtocol',
+ 'TJSONProtocolFactory',
+ 'TSimpleJSONProtocol',
+ 'TSimpleJSONProtocolFactory']
+
+VERSION = 1
+
+COMMA = b','
+COLON = b':'
+LBRACE = b'{'
+RBRACE = b'}'
+LBRACKET = b'['
+RBRACKET = b']'
+QUOTE = b'"'
+BACKSLASH = b'\\'
+ZERO = b'0'
+
+ESCSEQ0 = ord('\\')
+ESCSEQ1 = ord('u')
+ESCAPE_CHAR_VALS = {
+ '"': '\\"',
+ '\\': '\\\\',
+ '\b': '\\b',
+ '\f': '\\f',
+ '\n': '\\n',
+ '\r': '\\r',
+ '\t': '\\t',
+ # '/': '\\/',
+}
+ESCAPE_CHARS = {
+ b'"': '"',
+ b'\\': '\\',
+ b'b': '\b',
+ b'f': '\f',
+ b'n': '\n',
+ b'r': '\r',
+ b't': '\t',
+ b'/': '/',
+}
+NUMERIC_CHAR = b'+-.0123456789Ee'
+
+CTYPES = {
+ TType.BOOL: 'tf',
+ TType.BYTE: 'i8',
+ TType.I16: 'i16',
+ TType.I32: 'i32',
+ TType.I64: 'i64',
+ TType.DOUBLE: 'dbl',
+ TType.STRING: 'str',
+ TType.STRUCT: 'rec',
+ TType.LIST: 'lst',
+ TType.SET: 'set',
+ TType.MAP: 'map',
+}
+
+JTYPES = {}
+for key in CTYPES.keys():
+ JTYPES[CTYPES[key]] = key
+
+
+class JSONBaseContext(object):
+
+ def __init__(self, protocol):
+ self.protocol = protocol
+ self.first = True
+
+ def doIO(self, function):
+ pass
+
+ def write(self):
+ pass
+
+ def read(self):
+ pass
+
+ def escapeNum(self):
+ return False
+
+ def __str__(self):
+ return self.__class__.__name__
+
+
+class JSONListContext(JSONBaseContext):
+
+ def doIO(self, function):
+ if self.first is True:
+ self.first = False
+ else:
+ function(COMMA)
+
+ def write(self):
+ self.doIO(self.protocol.trans.write)
+
+ def read(self):
+ self.doIO(self.protocol.readJSONSyntaxChar)
+
+
+class JSONPairContext(JSONBaseContext):
+
+ def __init__(self, protocol):
+ super(JSONPairContext, self).__init__(protocol)
+ self.colon = True
+
+ def doIO(self, function):
+ if self.first:
+ self.first = False
+ self.colon = True
+ else:
+ function(COLON if self.colon else COMMA)
+ self.colon = not self.colon
+
+ def write(self):
+ self.doIO(self.protocol.trans.write)
+
+ def read(self):
+ self.doIO(self.protocol.readJSONSyntaxChar)
+
+ def escapeNum(self):
+ return self.colon
+
+ def __str__(self):
+ return '%s, colon=%s' % (self.__class__.__name__, self.colon)
+
+
+class LookaheadReader():
+ hasData = False
+ data = ''
+
+ def __init__(self, protocol):
+ self.protocol = protocol
+
+ def read(self):
+ if self.hasData is True:
+ self.hasData = False
+ else:
+ self.data = self.protocol.trans.read(1)
+ return self.data
+
+ def peek(self):
+ if self.hasData is False:
+ self.data = self.protocol.trans.read(1)
+ self.hasData = True
+ return self.data
+
+
+class TJSONProtocolBase(TProtocolBase):
+
+ def __init__(self, trans):
+ TProtocolBase.__init__(self, trans)
+ self.resetWriteContext()
+ self.resetReadContext()
+
+ # We don't have length limit implementation for JSON protocols
+ @property
+ def string_length_limit(senf):
+ return None
+
+ @property
+ def container_length_limit(senf):
+ return None
+
+ def resetWriteContext(self):
+ self.context = JSONBaseContext(self)
+ self.contextStack = [self.context]
+
+ def resetReadContext(self):
+ self.resetWriteContext()
+ self.reader = LookaheadReader(self)
+
+ def pushContext(self, ctx):
+ self.contextStack.append(ctx)
+ self.context = ctx
+
+ def popContext(self):
+ self.contextStack.pop()
+ if self.contextStack:
+ self.context = self.contextStack[-1]
+ else:
+ self.context = JSONBaseContext(self)
+
+ def writeJSONString(self, string):
+ self.context.write()
+ json_str = ['"']
+ for s in string:
+ escaped = ESCAPE_CHAR_VALS.get(s, s)
+ json_str.append(escaped)
+ json_str.append('"')
+ self.trans.write(str_to_binary(''.join(json_str)))
+
+ def writeJSONNumber(self, number, formatter='{0}'):
+ self.context.write()
+ jsNumber = str(formatter.format(number)).encode('ascii')
+ if self.context.escapeNum():
+ self.trans.write(QUOTE)
+ self.trans.write(jsNumber)
+ self.trans.write(QUOTE)
+ else:
+ self.trans.write(jsNumber)
+
+ def writeJSONBase64(self, binary):
+ self.context.write()
+ self.trans.write(QUOTE)
+ self.trans.write(base64.b64encode(binary))
+ self.trans.write(QUOTE)
+
+ def writeJSONObjectStart(self):
+ self.context.write()
+ self.trans.write(LBRACE)
+ self.pushContext(JSONPairContext(self))
+
+ def writeJSONObjectEnd(self):
+ self.popContext()
+ self.trans.write(RBRACE)
+
+ def writeJSONArrayStart(self):
+ self.context.write()
+ self.trans.write(LBRACKET)
+ self.pushContext(JSONListContext(self))
+
+ def writeJSONArrayEnd(self):
+ self.popContext()
+ self.trans.write(RBRACKET)
+
+ def readJSONSyntaxChar(self, character):
+ current = self.reader.read()
+ if character != current:
+ raise TProtocolException(TProtocolException.INVALID_DATA,
+ "Unexpected character: %s" % current)
+
+ def _isHighSurrogate(self, codeunit):
+ return codeunit >= 0xd800 and codeunit <= 0xdbff
+
+ def _isLowSurrogate(self, codeunit):
+ return codeunit >= 0xdc00 and codeunit <= 0xdfff
+
+ def _toChar(self, high, low=None):
+ if not low:
+ if sys.version_info[0] == 2:
+ return ("\\u%04x" % high).decode('unicode-escape') \
+ .encode('utf-8')
+ else:
+ return chr(high)
+ else:
+ codepoint = (1 << 16) + ((high & 0x3ff) << 10)
+ codepoint += low & 0x3ff
+ if sys.version_info[0] == 2:
+ s = "\\U%08x" % codepoint
+ return s.decode('unicode-escape').encode('utf-8')
+ else:
+ return chr(codepoint)
+
+ def readJSONString(self, skipContext):
+ highSurrogate = None
+ string = []
+ if skipContext is False:
+ self.context.read()
+ self.readJSONSyntaxChar(QUOTE)
+ while True:
+ character = self.reader.read()
+ if character == QUOTE:
+ break
+ if ord(character) == ESCSEQ0:
+ character = self.reader.read()
+ if ord(character) == ESCSEQ1:
+ character = self.trans.read(4).decode('ascii')
+ codeunit = int(character, 16)
+ if self._isHighSurrogate(codeunit):
+ if highSurrogate:
+ raise TProtocolException(
+ TProtocolException.INVALID_DATA,
+ "Expected low surrogate char")
+ highSurrogate = codeunit
+ continue
+ elif self._isLowSurrogate(codeunit):
+ if not highSurrogate:
+ raise TProtocolException(
+ TProtocolException.INVALID_DATA,
+ "Expected high surrogate char")
+ character = self._toChar(highSurrogate, codeunit)
+ highSurrogate = None
+ else:
+ character = self._toChar(codeunit)
+ else:
+ if character not in ESCAPE_CHARS:
+ raise TProtocolException(
+ TProtocolException.INVALID_DATA,
+ "Expected control char")
+ character = ESCAPE_CHARS[character]
+ elif character in ESCAPE_CHAR_VALS:
+ raise TProtocolException(TProtocolException.INVALID_DATA,
+ "Unescaped control char")
+ elif sys.version_info[0] > 2:
+ utf8_bytes = bytearray([ord(character)])
+ while ord(self.reader.peek()) >= 0x80:
+ utf8_bytes.append(ord(self.reader.read()))
+ character = utf8_bytes.decode('utf8')
+ string.append(character)
+
+ if highSurrogate:
+ raise TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected low surrogate char")
+ return ''.join(string)
+
+ def isJSONNumeric(self, character):
+ return (True if NUMERIC_CHAR.find(character) != - 1 else False)
+
+ def readJSONQuotes(self):
+ if (self.context.escapeNum()):
+ self.readJSONSyntaxChar(QUOTE)
+
+ def readJSONNumericChars(self):
+ numeric = []
+ while True:
+ character = self.reader.peek()
+ if self.isJSONNumeric(character) is False:
+ break
+ numeric.append(self.reader.read())
+ return b''.join(numeric).decode('ascii')
+
+ def readJSONInteger(self):
+ self.context.read()
+ self.readJSONQuotes()
+ numeric = self.readJSONNumericChars()
+ self.readJSONQuotes()
+ try:
+ return int(numeric)
+ except ValueError:
+ raise TProtocolException(TProtocolException.INVALID_DATA,
+ "Bad data encounted in numeric data")
+
+ def readJSONDouble(self):
+ self.context.read()
+ if self.reader.peek() == QUOTE:
+ string = self.readJSONString(True)
+ try:
+ double = float(string)
+ if (self.context.escapeNum is False and
+ not math.isinf(double) and
+ not math.isnan(double)):
+ raise TProtocolException(
+ TProtocolException.INVALID_DATA,
+ "Numeric data unexpectedly quoted")
+ return double
+ except ValueError:
+ raise TProtocolException(TProtocolException.INVALID_DATA,
+ "Bad data encounted in numeric data")
+ else:
+ if self.context.escapeNum() is True:
+ self.readJSONSyntaxChar(QUOTE)
+ try:
+ return float(self.readJSONNumericChars())
+ except ValueError:
+ raise TProtocolException(TProtocolException.INVALID_DATA,
+ "Bad data encounted in numeric data")
+
+ def readJSONBase64(self):
+ string = self.readJSONString(False)
+ size = len(string)
+ m = size % 4
+ # Force padding since b64encode method does not allow it
+ if m != 0:
+ for i in range(4 - m):
+ string += '='
+ return base64.b64decode(string)
+
+ def readJSONObjectStart(self):
+ self.context.read()
+ self.readJSONSyntaxChar(LBRACE)
+ self.pushContext(JSONPairContext(self))
+
+ def readJSONObjectEnd(self):
+ self.readJSONSyntaxChar(RBRACE)
+ self.popContext()
+
+ def readJSONArrayStart(self):
+ self.context.read()
+ self.readJSONSyntaxChar(LBRACKET)
+ self.pushContext(JSONListContext(self))
+
+ def readJSONArrayEnd(self):
+ self.readJSONSyntaxChar(RBRACKET)
+ self.popContext()
+
+
+class TJSONProtocol(TJSONProtocolBase):
+
+ def readMessageBegin(self):
+ self.resetReadContext()
+ self.readJSONArrayStart()
+ if self.readJSONInteger() != VERSION:
+ raise TProtocolException(TProtocolException.BAD_VERSION,
+ "Message contained bad version.")
+ name = self.readJSONString(False)
+ typen = self.readJSONInteger()
+ seqid = self.readJSONInteger()
+ return (name, typen, seqid)
+
+ def readMessageEnd(self):
+ self.readJSONArrayEnd()
+
+ def readStructBegin(self):
+ self.readJSONObjectStart()
+
+ def readStructEnd(self):
+ self.readJSONObjectEnd()
+
+ def readFieldBegin(self):
+ character = self.reader.peek()
+ ttype = 0
+ id = 0
+ if character == RBRACE:
+ ttype = TType.STOP
+ else:
+ id = self.readJSONInteger()
+ self.readJSONObjectStart()
+ ttype = JTYPES[self.readJSONString(False)]
+ return (None, ttype, id)
+
+ def readFieldEnd(self):
+ self.readJSONObjectEnd()
+
+ def readMapBegin(self):
+ self.readJSONArrayStart()
+ keyType = JTYPES[self.readJSONString(False)]
+ valueType = JTYPES[self.readJSONString(False)]
+ size = self.readJSONInteger()
+ self.readJSONObjectStart()
+ return (keyType, valueType, size)
+
+ def readMapEnd(self):
+ self.readJSONObjectEnd()
+ self.readJSONArrayEnd()
+
+ def readCollectionBegin(self):
+ self.readJSONArrayStart()
+ elemType = JTYPES[self.readJSONString(False)]
+ size = self.readJSONInteger()
+ return (elemType, size)
+ readListBegin = readCollectionBegin
+ readSetBegin = readCollectionBegin
+
+ def readCollectionEnd(self):
+ self.readJSONArrayEnd()
+ readSetEnd = readCollectionEnd
+ readListEnd = readCollectionEnd
+
+ def readBool(self):
+ return (False if self.readJSONInteger() == 0 else True)
+
+ def readNumber(self):
+ return self.readJSONInteger()
+ readByte = readNumber
+ readI16 = readNumber
+ readI32 = readNumber
+ readI64 = readNumber
+
+ def readDouble(self):
+ return self.readJSONDouble()
+
+ def readString(self):
+ return self.readJSONString(False)
+
+ def readBinary(self):
+ return self.readJSONBase64()
+
+ def writeMessageBegin(self, name, request_type, seqid):
+ self.resetWriteContext()
+ self.writeJSONArrayStart()
+ self.writeJSONNumber(VERSION)
+ self.writeJSONString(name)
+ self.writeJSONNumber(request_type)
+ self.writeJSONNumber(seqid)
+
+ def writeMessageEnd(self):
+ self.writeJSONArrayEnd()
+
+ def writeStructBegin(self, name):
+ self.writeJSONObjectStart()
+
+ def writeStructEnd(self):
+ self.writeJSONObjectEnd()
+
+ def writeFieldBegin(self, name, ttype, id):
+ self.writeJSONNumber(id)
+ self.writeJSONObjectStart()
+ self.writeJSONString(CTYPES[ttype])
+
+ def writeFieldEnd(self):
+ self.writeJSONObjectEnd()
+
+ def writeFieldStop(self):
+ pass
+
+ def writeMapBegin(self, ktype, vtype, size):
+ self.writeJSONArrayStart()
+ self.writeJSONString(CTYPES[ktype])
+ self.writeJSONString(CTYPES[vtype])
+ self.writeJSONNumber(size)
+ self.writeJSONObjectStart()
+
+ def writeMapEnd(self):
+ self.writeJSONObjectEnd()
+ self.writeJSONArrayEnd()
+
+ def writeListBegin(self, etype, size):
+ self.writeJSONArrayStart()
+ self.writeJSONString(CTYPES[etype])
+ self.writeJSONNumber(size)
+
+ def writeListEnd(self):
+ self.writeJSONArrayEnd()
+
+ def writeSetBegin(self, etype, size):
+ self.writeJSONArrayStart()
+ self.writeJSONString(CTYPES[etype])
+ self.writeJSONNumber(size)
+
+ def writeSetEnd(self):
+ self.writeJSONArrayEnd()
+
+ def writeBool(self, boolean):
+ self.writeJSONNumber(1 if boolean is True else 0)
+
+ def writeByte(self, byte):
+ checkIntegerLimits(byte, 8)
+ self.writeJSONNumber(byte)
+
+ def writeI16(self, i16):
+ checkIntegerLimits(i16, 16)
+ self.writeJSONNumber(i16)
+
+ def writeI32(self, i32):
+ checkIntegerLimits(i32, 32)
+ self.writeJSONNumber(i32)
+
+ def writeI64(self, i64):
+ checkIntegerLimits(i64, 64)
+ self.writeJSONNumber(i64)
+
+ def writeDouble(self, dbl):
+ # 17 significant digits should be just enough for any double precision
+ # value.
+ self.writeJSONNumber(dbl, '{0:.17g}')
+
+ def writeString(self, string):
+ self.writeJSONString(string)
+
+ def writeBinary(self, binary):
+ self.writeJSONBase64(binary)
+
+
+class TJSONProtocolFactory(TProtocolFactory):
+ def getProtocol(self, trans):
+ return TJSONProtocol(trans)
+
+ @property
+ def string_length_limit(senf):
+ return None
+
+ @property
+ def container_length_limit(senf):
+ return None
+
+
+class TSimpleJSONProtocol(TJSONProtocolBase):
+ """Simple, readable, write-only JSON protocol.
+
+ Useful for interacting with scripting languages.
+ """
+
+ def readMessageBegin(self):
+ raise NotImplementedError()
+
+ def readMessageEnd(self):
+ raise NotImplementedError()
+
+ def readStructBegin(self):
+ raise NotImplementedError()
+
+ def readStructEnd(self):
+ raise NotImplementedError()
+
+ def writeMessageBegin(self, name, request_type, seqid):
+ self.resetWriteContext()
+
+ def writeMessageEnd(self):
+ pass
+
+ def writeStructBegin(self, name):
+ self.writeJSONObjectStart()
+
+ def writeStructEnd(self):
+ self.writeJSONObjectEnd()
+
+ def writeFieldBegin(self, name, ttype, fid):
+ self.writeJSONString(name)
+
+ def writeFieldEnd(self):
+ pass
+
+ def writeMapBegin(self, ktype, vtype, size):
+ self.writeJSONObjectStart()
+
+ def writeMapEnd(self):
+ self.writeJSONObjectEnd()
+
+ def _writeCollectionBegin(self, etype, size):
+ self.writeJSONArrayStart()
+
+ def _writeCollectionEnd(self):
+ self.writeJSONArrayEnd()
+ writeListBegin = _writeCollectionBegin
+ writeListEnd = _writeCollectionEnd
+ writeSetBegin = _writeCollectionBegin
+ writeSetEnd = _writeCollectionEnd
+
+ def writeByte(self, byte):
+ checkIntegerLimits(byte, 8)
+ self.writeJSONNumber(byte)
+
+ def writeI16(self, i16):
+ checkIntegerLimits(i16, 16)
+ self.writeJSONNumber(i16)
+
+ def writeI32(self, i32):
+ checkIntegerLimits(i32, 32)
+ self.writeJSONNumber(i32)
+
+ def writeI64(self, i64):
+ checkIntegerLimits(i64, 64)
+ self.writeJSONNumber(i64)
+
+ def writeBool(self, boolean):
+ self.writeJSONNumber(1 if boolean is True else 0)
+
+ def writeDouble(self, dbl):
+ self.writeJSONNumber(dbl)
+
+ def writeString(self, string):
+ self.writeJSONString(string)
+
+ def writeBinary(self, binary):
+ self.writeJSONBase64(binary)
+
+
+class TSimpleJSONProtocolFactory(TProtocolFactory):
+
+ def getProtocol(self, trans):
+ return TSimpleJSONProtocol(trans)
diff --git a/src/jaegertracing/thrift/lib/py/src/protocol/TMultiplexedProtocol.py b/src/jaegertracing/thrift/lib/py/src/protocol/TMultiplexedProtocol.py
new file mode 100644
index 000000000..0f8390fdb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/protocol/TMultiplexedProtocol.py
@@ -0,0 +1,39 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from thrift.Thrift import TMessageType
+from thrift.protocol import TProtocolDecorator
+
+SEPARATOR = ":"
+
+
+class TMultiplexedProtocol(TProtocolDecorator.TProtocolDecorator):
+ def __init__(self, protocol, serviceName):
+ self.serviceName = serviceName
+
+ def writeMessageBegin(self, name, type, seqid):
+ if (type == TMessageType.CALL or
+ type == TMessageType.ONEWAY):
+ super(TMultiplexedProtocol, self).writeMessageBegin(
+ self.serviceName + SEPARATOR + name,
+ type,
+ seqid
+ )
+ else:
+ super(TMultiplexedProtocol, self).writeMessageBegin(name, type, seqid)
diff --git a/src/jaegertracing/thrift/lib/py/src/protocol/TProtocol.py b/src/jaegertracing/thrift/lib/py/src/protocol/TProtocol.py
new file mode 100644
index 000000000..3456e8f0e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/protocol/TProtocol.py
@@ -0,0 +1,422 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from thrift.Thrift import TException, TType, TFrozenDict
+from thrift.transport.TTransport import TTransportException
+from ..compat import binary_to_str, str_to_binary
+
+import six
+import sys
+from itertools import islice
+from six.moves import zip
+
+
+class TProtocolException(TException):
+ """Custom Protocol Exception class"""
+
+ UNKNOWN = 0
+ INVALID_DATA = 1
+ NEGATIVE_SIZE = 2
+ SIZE_LIMIT = 3
+ BAD_VERSION = 4
+ NOT_IMPLEMENTED = 5
+ DEPTH_LIMIT = 6
+ INVALID_PROTOCOL = 7
+
+ def __init__(self, type=UNKNOWN, message=None):
+ TException.__init__(self, message)
+ self.type = type
+
+
+class TProtocolBase(object):
+ """Base class for Thrift protocol driver."""
+
+ def __init__(self, trans):
+ self.trans = trans
+ self._fast_decode = None
+ self._fast_encode = None
+
+ @staticmethod
+ def _check_length(limit, length):
+ if length < 0:
+ raise TTransportException(TTransportException.NEGATIVE_SIZE,
+ 'Negative length: %d' % length)
+ if limit is not None and length > limit:
+ raise TTransportException(TTransportException.SIZE_LIMIT,
+ 'Length exceeded max allowed: %d' % limit)
+
+ def writeMessageBegin(self, name, ttype, seqid):
+ pass
+
+ def writeMessageEnd(self):
+ pass
+
+ def writeStructBegin(self, name):
+ pass
+
+ def writeStructEnd(self):
+ pass
+
+ def writeFieldBegin(self, name, ttype, fid):
+ pass
+
+ def writeFieldEnd(self):
+ pass
+
+ def writeFieldStop(self):
+ pass
+
+ def writeMapBegin(self, ktype, vtype, size):
+ pass
+
+ def writeMapEnd(self):
+ pass
+
+ def writeListBegin(self, etype, size):
+ pass
+
+ def writeListEnd(self):
+ pass
+
+ def writeSetBegin(self, etype, size):
+ pass
+
+ def writeSetEnd(self):
+ pass
+
+ def writeBool(self, bool_val):
+ pass
+
+ def writeByte(self, byte):
+ pass
+
+ def writeI16(self, i16):
+ pass
+
+ def writeI32(self, i32):
+ pass
+
+ def writeI64(self, i64):
+ pass
+
+ def writeDouble(self, dub):
+ pass
+
+ def writeString(self, str_val):
+ self.writeBinary(str_to_binary(str_val))
+
+ def writeBinary(self, str_val):
+ pass
+
+ def writeUtf8(self, str_val):
+ self.writeString(str_val.encode('utf8'))
+
+ def readMessageBegin(self):
+ pass
+
+ def readMessageEnd(self):
+ pass
+
+ def readStructBegin(self):
+ pass
+
+ def readStructEnd(self):
+ pass
+
+ def readFieldBegin(self):
+ pass
+
+ def readFieldEnd(self):
+ pass
+
+ def readMapBegin(self):
+ pass
+
+ def readMapEnd(self):
+ pass
+
+ def readListBegin(self):
+ pass
+
+ def readListEnd(self):
+ pass
+
+ def readSetBegin(self):
+ pass
+
+ def readSetEnd(self):
+ pass
+
+ def readBool(self):
+ pass
+
+ def readByte(self):
+ pass
+
+ def readI16(self):
+ pass
+
+ def readI32(self):
+ pass
+
+ def readI64(self):
+ pass
+
+ def readDouble(self):
+ pass
+
+ def readString(self):
+ return binary_to_str(self.readBinary())
+
+ def readBinary(self):
+ pass
+
+ def readUtf8(self):
+ return self.readString().decode('utf8')
+
+ def skip(self, ttype):
+ if ttype == TType.BOOL:
+ self.readBool()
+ elif ttype == TType.BYTE:
+ self.readByte()
+ elif ttype == TType.I16:
+ self.readI16()
+ elif ttype == TType.I32:
+ self.readI32()
+ elif ttype == TType.I64:
+ self.readI64()
+ elif ttype == TType.DOUBLE:
+ self.readDouble()
+ elif ttype == TType.STRING:
+ self.readString()
+ elif ttype == TType.STRUCT:
+ name = self.readStructBegin()
+ while True:
+ (name, ttype, id) = self.readFieldBegin()
+ if ttype == TType.STOP:
+ break
+ self.skip(ttype)
+ self.readFieldEnd()
+ self.readStructEnd()
+ elif ttype == TType.MAP:
+ (ktype, vtype, size) = self.readMapBegin()
+ for i in range(size):
+ self.skip(ktype)
+ self.skip(vtype)
+ self.readMapEnd()
+ elif ttype == TType.SET:
+ (etype, size) = self.readSetBegin()
+ for i in range(size):
+ self.skip(etype)
+ self.readSetEnd()
+ elif ttype == TType.LIST:
+ (etype, size) = self.readListBegin()
+ for i in range(size):
+ self.skip(etype)
+ self.readListEnd()
+ else:
+ raise TProtocolException(
+ TProtocolException.INVALID_DATA,
+ "invalid TType")
+
+ # tuple of: ( 'reader method' name, is_container bool, 'writer_method' name )
+ _TTYPE_HANDLERS = (
+ (None, None, False), # 0 TType.STOP
+ (None, None, False), # 1 TType.VOID # TODO: handle void?
+ ('readBool', 'writeBool', False), # 2 TType.BOOL
+ ('readByte', 'writeByte', False), # 3 TType.BYTE and I08
+ ('readDouble', 'writeDouble', False), # 4 TType.DOUBLE
+ (None, None, False), # 5 undefined
+ ('readI16', 'writeI16', False), # 6 TType.I16
+ (None, None, False), # 7 undefined
+ ('readI32', 'writeI32', False), # 8 TType.I32
+ (None, None, False), # 9 undefined
+ ('readI64', 'writeI64', False), # 10 TType.I64
+ ('readString', 'writeString', False), # 11 TType.STRING and UTF7
+ ('readContainerStruct', 'writeContainerStruct', True), # 12 *.STRUCT
+ ('readContainerMap', 'writeContainerMap', True), # 13 TType.MAP
+ ('readContainerSet', 'writeContainerSet', True), # 14 TType.SET
+ ('readContainerList', 'writeContainerList', True), # 15 TType.LIST
+ (None, None, False), # 16 TType.UTF8 # TODO: handle utf8 types?
+ (None, None, False) # 17 TType.UTF16 # TODO: handle utf16 types?
+ )
+
+ def _ttype_handlers(self, ttype, spec):
+ if spec == 'BINARY':
+ if ttype != TType.STRING:
+ raise TProtocolException(type=TProtocolException.INVALID_DATA,
+ message='Invalid binary field type %d' % ttype)
+ return ('readBinary', 'writeBinary', False)
+ if sys.version_info[0] == 2 and spec == 'UTF8':
+ if ttype != TType.STRING:
+ raise TProtocolException(type=TProtocolException.INVALID_DATA,
+ message='Invalid string field type %d' % ttype)
+ return ('readUtf8', 'writeUtf8', False)
+ return self._TTYPE_HANDLERS[ttype] if ttype < len(self._TTYPE_HANDLERS) else (None, None, False)
+
+ def _read_by_ttype(self, ttype, spec, espec):
+ reader_name, _, is_container = self._ttype_handlers(ttype, espec)
+ if reader_name is None:
+ raise TProtocolException(type=TProtocolException.INVALID_DATA,
+ message='Invalid type %d' % (ttype))
+ reader_func = getattr(self, reader_name)
+ read = (lambda: reader_func(espec)) if is_container else reader_func
+ while True:
+ yield read()
+
+ def readFieldByTType(self, ttype, spec):
+ return next(self._read_by_ttype(ttype, spec, spec))
+
+ def readContainerList(self, spec):
+ ttype, tspec, is_immutable = spec
+ (list_type, list_len) = self.readListBegin()
+ # TODO: compare types we just decoded with thrift_spec
+ elems = islice(self._read_by_ttype(ttype, spec, tspec), list_len)
+ results = (tuple if is_immutable else list)(elems)
+ self.readListEnd()
+ return results
+
+ def readContainerSet(self, spec):
+ ttype, tspec, is_immutable = spec
+ (set_type, set_len) = self.readSetBegin()
+ # TODO: compare types we just decoded with thrift_spec
+ elems = islice(self._read_by_ttype(ttype, spec, tspec), set_len)
+ results = (frozenset if is_immutable else set)(elems)
+ self.readSetEnd()
+ return results
+
+ def readContainerStruct(self, spec):
+ (obj_class, obj_spec) = spec
+ obj = obj_class()
+ obj.read(self)
+ return obj
+
+ def readContainerMap(self, spec):
+ ktype, kspec, vtype, vspec, is_immutable = spec
+ (map_ktype, map_vtype, map_len) = self.readMapBegin()
+ # TODO: compare types we just decoded with thrift_spec and
+ # abort/skip if types disagree
+ keys = self._read_by_ttype(ktype, spec, kspec)
+ vals = self._read_by_ttype(vtype, spec, vspec)
+ keyvals = islice(zip(keys, vals), map_len)
+ results = (TFrozenDict if is_immutable else dict)(keyvals)
+ self.readMapEnd()
+ return results
+
+ def readStruct(self, obj, thrift_spec, is_immutable=False):
+ if is_immutable:
+ fields = {}
+ self.readStructBegin()
+ while True:
+ (fname, ftype, fid) = self.readFieldBegin()
+ if ftype == TType.STOP:
+ break
+ try:
+ field = thrift_spec[fid]
+ except IndexError:
+ self.skip(ftype)
+ else:
+ if field is not None and ftype == field[1]:
+ fname = field[2]
+ fspec = field[3]
+ val = self.readFieldByTType(ftype, fspec)
+ if is_immutable:
+ fields[fname] = val
+ else:
+ setattr(obj, fname, val)
+ else:
+ self.skip(ftype)
+ self.readFieldEnd()
+ self.readStructEnd()
+ if is_immutable:
+ return obj(**fields)
+
+ def writeContainerStruct(self, val, spec):
+ val.write(self)
+
+ def writeContainerList(self, val, spec):
+ ttype, tspec, _ = spec
+ self.writeListBegin(ttype, len(val))
+ for _ in self._write_by_ttype(ttype, val, spec, tspec):
+ pass
+ self.writeListEnd()
+
+ def writeContainerSet(self, val, spec):
+ ttype, tspec, _ = spec
+ self.writeSetBegin(ttype, len(val))
+ for _ in self._write_by_ttype(ttype, val, spec, tspec):
+ pass
+ self.writeSetEnd()
+
+ def writeContainerMap(self, val, spec):
+ ktype, kspec, vtype, vspec, _ = spec
+ self.writeMapBegin(ktype, vtype, len(val))
+ for _ in zip(self._write_by_ttype(ktype, six.iterkeys(val), spec, kspec),
+ self._write_by_ttype(vtype, six.itervalues(val), spec, vspec)):
+ pass
+ self.writeMapEnd()
+
+ def writeStruct(self, obj, thrift_spec):
+ self.writeStructBegin(obj.__class__.__name__)
+ for field in thrift_spec:
+ if field is None:
+ continue
+ fname = field[2]
+ val = getattr(obj, fname)
+ if val is None:
+ # skip writing out unset fields
+ continue
+ fid = field[0]
+ ftype = field[1]
+ fspec = field[3]
+ self.writeFieldBegin(fname, ftype, fid)
+ self.writeFieldByTType(ftype, val, fspec)
+ self.writeFieldEnd()
+ self.writeFieldStop()
+ self.writeStructEnd()
+
+ def _write_by_ttype(self, ttype, vals, spec, espec):
+ _, writer_name, is_container = self._ttype_handlers(ttype, espec)
+ writer_func = getattr(self, writer_name)
+ write = (lambda v: writer_func(v, espec)) if is_container else writer_func
+ for v in vals:
+ yield write(v)
+
+ def writeFieldByTType(self, ttype, val, spec):
+ next(self._write_by_ttype(ttype, [val], spec, spec))
+
+
+def checkIntegerLimits(i, bits):
+ if bits == 8 and (i < -128 or i > 127):
+ raise TProtocolException(TProtocolException.INVALID_DATA,
+ "i8 requires -128 <= number <= 127")
+ elif bits == 16 and (i < -32768 or i > 32767):
+ raise TProtocolException(TProtocolException.INVALID_DATA,
+ "i16 requires -32768 <= number <= 32767")
+ elif bits == 32 and (i < -2147483648 or i > 2147483647):
+ raise TProtocolException(TProtocolException.INVALID_DATA,
+ "i32 requires -2147483648 <= number <= 2147483647")
+ elif bits == 64 and (i < -9223372036854775808 or i > 9223372036854775807):
+ raise TProtocolException(TProtocolException.INVALID_DATA,
+ "i64 requires -9223372036854775808 <= number <= 9223372036854775807")
+
+
+class TProtocolFactory(object):
+ def getProtocol(self, trans):
+ pass
diff --git a/src/jaegertracing/thrift/lib/py/src/protocol/TProtocolDecorator.py b/src/jaegertracing/thrift/lib/py/src/protocol/TProtocolDecorator.py
new file mode 100644
index 000000000..f5546c736
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/protocol/TProtocolDecorator.py
@@ -0,0 +1,26 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+class TProtocolDecorator(object):
+ def __new__(cls, protocol, *args, **kwargs):
+ decorated_cls = type(''.join(['Decorated', protocol.__class__.__name__]),
+ (cls, protocol.__class__),
+ protocol.__dict__)
+ return object.__new__(decorated_cls)
diff --git a/src/jaegertracing/thrift/lib/py/src/protocol/__init__.py b/src/jaegertracing/thrift/lib/py/src/protocol/__init__.py
new file mode 100644
index 000000000..06647a24b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/protocol/__init__.py
@@ -0,0 +1,21 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+__all__ = ['fastbinary', 'TBase', 'TBinaryProtocol', 'TCompactProtocol',
+ 'TJSONProtocol', 'TProtocol', 'TProtocolDecorator']
diff --git a/src/jaegertracing/thrift/lib/py/src/server/THttpServer.py b/src/jaegertracing/thrift/lib/py/src/server/THttpServer.py
new file mode 100644
index 000000000..47e817df7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/server/THttpServer.py
@@ -0,0 +1,131 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import ssl
+
+from six.moves import BaseHTTPServer
+
+from thrift.Thrift import TMessageType
+from thrift.server import TServer
+from thrift.transport import TTransport
+
+
+class ResponseException(Exception):
+ """Allows handlers to override the HTTP response
+
+ Normally, THttpServer always sends a 200 response. If a handler wants
+ to override this behavior (e.g., to simulate a misconfigured or
+ overloaded web server during testing), it can raise a ResponseException.
+ The function passed to the constructor will be called with the
+ RequestHandler as its only argument. Note that this is irrelevant
+ for ONEWAY requests, as the HTTP response must be sent before the
+ RPC is processed.
+ """
+ def __init__(self, handler):
+ self.handler = handler
+
+
+class THttpServer(TServer.TServer):
+ """A simple HTTP-based Thrift server
+
+ This class is not very performant, but it is useful (for example) for
+ acting as a mock version of an Apache-based PHP Thrift endpoint.
+ Also important to note the HTTP implementation pretty much violates the
+ transport/protocol/processor/server layering, by performing the transport
+ functions here. This means things like oneway handling are oddly exposed.
+ """
+ def __init__(self,
+ processor,
+ server_address,
+ inputProtocolFactory,
+ outputProtocolFactory=None,
+ server_class=BaseHTTPServer.HTTPServer,
+ **kwargs):
+ """Set up protocol factories and HTTP (or HTTPS) server.
+
+ See BaseHTTPServer for server_address.
+ See TServer for protocol factories.
+
+ To make a secure server, provide the named arguments:
+ * cafile - to validate clients [optional]
+ * cert_file - the server cert
+ * key_file - the server's key
+ """
+ if outputProtocolFactory is None:
+ outputProtocolFactory = inputProtocolFactory
+
+ TServer.TServer.__init__(self, processor, None, None, None,
+ inputProtocolFactory, outputProtocolFactory)
+
+ thttpserver = self
+ self._replied = None
+
+ class RequestHander(BaseHTTPServer.BaseHTTPRequestHandler):
+ def do_POST(self):
+ # Don't care about the request path.
+ thttpserver._replied = False
+ iftrans = TTransport.TFileObjectTransport(self.rfile)
+ itrans = TTransport.TBufferedTransport(
+ iftrans, int(self.headers['Content-Length']))
+ otrans = TTransport.TMemoryBuffer()
+ iprot = thttpserver.inputProtocolFactory.getProtocol(itrans)
+ oprot = thttpserver.outputProtocolFactory.getProtocol(otrans)
+ try:
+ thttpserver.processor.on_message_begin(self.on_begin)
+ thttpserver.processor.process(iprot, oprot)
+ except ResponseException as exn:
+ exn.handler(self)
+ else:
+ if not thttpserver._replied:
+ # If the request was ONEWAY we would have replied already
+ data = otrans.getvalue()
+ self.send_response(200)
+ self.send_header("Content-Length", len(data))
+ self.send_header("Content-Type", "application/x-thrift")
+ self.end_headers()
+ self.wfile.write(data)
+
+ def on_begin(self, name, type, seqid):
+ """
+ Inspect the message header.
+
+ This allows us to post an immediate transport response
+ if the request is a ONEWAY message type.
+ """
+ if type == TMessageType.ONEWAY:
+ self.send_response(200)
+ self.send_header("Content-Type", "application/x-thrift")
+ self.end_headers()
+ thttpserver._replied = True
+
+ self.httpd = server_class(server_address, RequestHander)
+
+ if (kwargs.get('cafile') or kwargs.get('cert_file') or kwargs.get('key_file')):
+ context = ssl.create_default_context(cafile=kwargs.get('cafile'))
+ context.check_hostname = False
+ context.load_cert_chain(kwargs.get('cert_file'), kwargs.get('key_file'))
+ context.verify_mode = ssl.CERT_REQUIRED if kwargs.get('cafile') else ssl.CERT_NONE
+ self.httpd.socket = context.wrap_socket(self.httpd.socket, server_side=True)
+
+ def serve(self):
+ self.httpd.serve_forever()
+
+ def shutdown(self):
+ self.httpd.socket.close()
+ # self.httpd.shutdown() # hangs forever, python doesn't handle POLLNVAL properly!
diff --git a/src/jaegertracing/thrift/lib/py/src/server/TNonblockingServer.py b/src/jaegertracing/thrift/lib/py/src/server/TNonblockingServer.py
new file mode 100644
index 000000000..f62d486eb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/server/TNonblockingServer.py
@@ -0,0 +1,370 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+"""Implementation of non-blocking server.
+
+The main idea of the server is to receive and send requests
+only from the main thread.
+
+The thread poool should be sized for concurrent tasks, not
+maximum connections
+"""
+
+import logging
+import select
+import socket
+import struct
+import threading
+
+from collections import deque
+from six.moves import queue
+
+from thrift.transport import TTransport
+from thrift.protocol.TBinaryProtocol import TBinaryProtocolFactory
+
+__all__ = ['TNonblockingServer']
+
+logger = logging.getLogger(__name__)
+
+
+class Worker(threading.Thread):
+ """Worker is a small helper to process incoming connection."""
+
+ def __init__(self, queue):
+ threading.Thread.__init__(self)
+ self.queue = queue
+
+ def run(self):
+ """Process queries from task queue, stop if processor is None."""
+ while True:
+ try:
+ processor, iprot, oprot, otrans, callback = self.queue.get()
+ if processor is None:
+ break
+ processor.process(iprot, oprot)
+ callback(True, otrans.getvalue())
+ except Exception:
+ logger.exception("Exception while processing request", exc_info=True)
+ callback(False, b'')
+
+
+WAIT_LEN = 0
+WAIT_MESSAGE = 1
+WAIT_PROCESS = 2
+SEND_ANSWER = 3
+CLOSED = 4
+
+
+def locked(func):
+ """Decorator which locks self.lock."""
+ def nested(self, *args, **kwargs):
+ self.lock.acquire()
+ try:
+ return func(self, *args, **kwargs)
+ finally:
+ self.lock.release()
+ return nested
+
+
+def socket_exception(func):
+ """Decorator close object on socket.error."""
+ def read(self, *args, **kwargs):
+ try:
+ return func(self, *args, **kwargs)
+ except socket.error:
+ logger.debug('ignoring socket exception', exc_info=True)
+ self.close()
+ return read
+
+
+class Message(object):
+ def __init__(self, offset, len_, header):
+ self.offset = offset
+ self.len = len_
+ self.buffer = None
+ self.is_header = header
+
+ @property
+ def end(self):
+ return self.offset + self.len
+
+
+class Connection(object):
+ """Basic class is represented connection.
+
+ It can be in state:
+ WAIT_LEN --- connection is reading request len.
+ WAIT_MESSAGE --- connection is reading request.
+ WAIT_PROCESS --- connection has just read whole request and
+ waits for call ready routine.
+ SEND_ANSWER --- connection is sending answer string (including length
+ of answer).
+ CLOSED --- socket was closed and connection should be deleted.
+ """
+ def __init__(self, new_socket, wake_up):
+ self.socket = new_socket
+ self.socket.setblocking(False)
+ self.status = WAIT_LEN
+ self.len = 0
+ self.received = deque()
+ self._reading = Message(0, 4, True)
+ self._rbuf = b''
+ self._wbuf = b''
+ self.lock = threading.Lock()
+ self.wake_up = wake_up
+ self.remaining = False
+
+ @socket_exception
+ def read(self):
+ """Reads data from stream and switch state."""
+ assert self.status in (WAIT_LEN, WAIT_MESSAGE)
+ assert not self.received
+ buf_size = 8192
+ first = True
+ done = False
+ while not done:
+ read = self.socket.recv(buf_size)
+ rlen = len(read)
+ done = rlen < buf_size
+ self._rbuf += read
+ if first and rlen == 0:
+ if self.status != WAIT_LEN or self._rbuf:
+ logger.error('could not read frame from socket')
+ else:
+ logger.debug('read zero length. client might have disconnected')
+ self.close()
+ while len(self._rbuf) >= self._reading.end:
+ if self._reading.is_header:
+ mlen, = struct.unpack('!i', self._rbuf[:4])
+ self._reading = Message(self._reading.end, mlen, False)
+ self.status = WAIT_MESSAGE
+ else:
+ self._reading.buffer = self._rbuf
+ self.received.append(self._reading)
+ self._rbuf = self._rbuf[self._reading.end:]
+ self._reading = Message(0, 4, True)
+ first = False
+ if self.received:
+ self.status = WAIT_PROCESS
+ break
+ self.remaining = not done
+
+ @socket_exception
+ def write(self):
+ """Writes data from socket and switch state."""
+ assert self.status == SEND_ANSWER
+ sent = self.socket.send(self._wbuf)
+ if sent == len(self._wbuf):
+ self.status = WAIT_LEN
+ self._wbuf = b''
+ self.len = 0
+ else:
+ self._wbuf = self._wbuf[sent:]
+
+ @locked
+ def ready(self, all_ok, message):
+ """Callback function for switching state and waking up main thread.
+
+ This function is the only function witch can be called asynchronous.
+
+ The ready can switch Connection to three states:
+ WAIT_LEN if request was oneway.
+ SEND_ANSWER if request was processed in normal way.
+ CLOSED if request throws unexpected exception.
+
+ The one wakes up main thread.
+ """
+ assert self.status == WAIT_PROCESS
+ if not all_ok:
+ self.close()
+ self.wake_up()
+ return
+ self.len = 0
+ if len(message) == 0:
+ # it was a oneway request, do not write answer
+ self._wbuf = b''
+ self.status = WAIT_LEN
+ else:
+ self._wbuf = struct.pack('!i', len(message)) + message
+ self.status = SEND_ANSWER
+ self.wake_up()
+
+ @locked
+ def is_writeable(self):
+ """Return True if connection should be added to write list of select"""
+ return self.status == SEND_ANSWER
+
+ # it's not necessary, but...
+ @locked
+ def is_readable(self):
+ """Return True if connection should be added to read list of select"""
+ return self.status in (WAIT_LEN, WAIT_MESSAGE)
+
+ @locked
+ def is_closed(self):
+ """Returns True if connection is closed."""
+ return self.status == CLOSED
+
+ def fileno(self):
+ """Returns the file descriptor of the associated socket."""
+ return self.socket.fileno()
+
+ def close(self):
+ """Closes connection"""
+ self.status = CLOSED
+ self.socket.close()
+
+
+class TNonblockingServer(object):
+ """Non-blocking server."""
+
+ def __init__(self,
+ processor,
+ lsocket,
+ inputProtocolFactory=None,
+ outputProtocolFactory=None,
+ threads=10):
+ self.processor = processor
+ self.socket = lsocket
+ self.in_protocol = inputProtocolFactory or TBinaryProtocolFactory()
+ self.out_protocol = outputProtocolFactory or self.in_protocol
+ self.threads = int(threads)
+ self.clients = {}
+ self.tasks = queue.Queue()
+ self._read, self._write = socket.socketpair()
+ self.prepared = False
+ self._stop = False
+
+ def setNumThreads(self, num):
+ """Set the number of worker threads that should be created."""
+ # implement ThreadPool interface
+ assert not self.prepared, "Can't change number of threads after start"
+ self.threads = num
+
+ def prepare(self):
+ """Prepares server for serve requests."""
+ if self.prepared:
+ return
+ self.socket.listen()
+ for _ in range(self.threads):
+ thread = Worker(self.tasks)
+ thread.setDaemon(True)
+ thread.start()
+ self.prepared = True
+
+ def wake_up(self):
+ """Wake up main thread.
+
+ The server usually waits in select call in we should terminate one.
+ The simplest way is using socketpair.
+
+ Select always wait to read from the first socket of socketpair.
+
+ In this case, we can just write anything to the second socket from
+ socketpair.
+ """
+ self._write.send(b'1')
+
+ def stop(self):
+ """Stop the server.
+
+ This method causes the serve() method to return. stop() may be invoked
+ from within your handler, or from another thread.
+
+ After stop() is called, serve() will return but the server will still
+ be listening on the socket. serve() may then be called again to resume
+ processing requests. Alternatively, close() may be called after
+ serve() returns to close the server socket and shutdown all worker
+ threads.
+ """
+ self._stop = True
+ self.wake_up()
+
+ def _select(self):
+ """Does select on open connections."""
+ readable = [self.socket.handle.fileno(), self._read.fileno()]
+ writable = []
+ remaining = []
+ for i, connection in list(self.clients.items()):
+ if connection.is_readable():
+ readable.append(connection.fileno())
+ if connection.remaining or connection.received:
+ remaining.append(connection.fileno())
+ if connection.is_writeable():
+ writable.append(connection.fileno())
+ if connection.is_closed():
+ del self.clients[i]
+ if remaining:
+ return remaining, [], [], False
+ else:
+ return select.select(readable, writable, readable) + (True,)
+
+ def handle(self):
+ """Handle requests.
+
+ WARNING! You must call prepare() BEFORE calling handle()
+ """
+ assert self.prepared, "You have to call prepare before handle"
+ rset, wset, xset, selected = self._select()
+ for readable in rset:
+ if readable == self._read.fileno():
+ # don't care i just need to clean readable flag
+ self._read.recv(1024)
+ elif readable == self.socket.handle.fileno():
+ try:
+ client = self.socket.accept()
+ if client:
+ self.clients[client.handle.fileno()] = Connection(client.handle,
+ self.wake_up)
+ except socket.error:
+ logger.debug('error while accepting', exc_info=True)
+ else:
+ connection = self.clients[readable]
+ if selected:
+ connection.read()
+ if connection.received:
+ connection.status = WAIT_PROCESS
+ msg = connection.received.popleft()
+ itransport = TTransport.TMemoryBuffer(msg.buffer, msg.offset)
+ otransport = TTransport.TMemoryBuffer()
+ iprot = self.in_protocol.getProtocol(itransport)
+ oprot = self.out_protocol.getProtocol(otransport)
+ self.tasks.put([self.processor, iprot, oprot,
+ otransport, connection.ready])
+ for writeable in wset:
+ self.clients[writeable].write()
+ for oob in xset:
+ self.clients[oob].close()
+ del self.clients[oob]
+
+ def close(self):
+ """Closes the server."""
+ for _ in range(self.threads):
+ self.tasks.put([None, None, None, None, None])
+ self.socket.close()
+ self.prepared = False
+
+ def serve(self):
+ """Serve requests.
+
+ Serve requests forever, or until stop() is called.
+ """
+ self._stop = False
+ self.prepare()
+ while not self._stop:
+ self.handle()
diff --git a/src/jaegertracing/thrift/lib/py/src/server/TProcessPoolServer.py b/src/jaegertracing/thrift/lib/py/src/server/TProcessPoolServer.py
new file mode 100644
index 000000000..fe6dc8162
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/server/TProcessPoolServer.py
@@ -0,0 +1,123 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+import logging
+
+from multiprocessing import Process, Value, Condition
+
+from .TServer import TServer
+from thrift.transport.TTransport import TTransportException
+
+logger = logging.getLogger(__name__)
+
+
+class TProcessPoolServer(TServer):
+ """Server with a fixed size pool of worker subprocesses to service requests
+
+ Note that if you need shared state between the handlers - it's up to you!
+ Written by Dvir Volk, doat.com
+ """
+ def __init__(self, *args):
+ TServer.__init__(self, *args)
+ self.numWorkers = 10
+ self.workers = []
+ self.isRunning = Value('b', False)
+ self.stopCondition = Condition()
+ self.postForkCallback = None
+
+ def setPostForkCallback(self, callback):
+ if not callable(callback):
+ raise TypeError("This is not a callback!")
+ self.postForkCallback = callback
+
+ def setNumWorkers(self, num):
+ """Set the number of worker threads that should be created"""
+ self.numWorkers = num
+
+ def workerProcess(self):
+ """Loop getting clients from the shared queue and process them"""
+ if self.postForkCallback:
+ self.postForkCallback()
+
+ while self.isRunning.value:
+ try:
+ client = self.serverTransport.accept()
+ if not client:
+ continue
+ self.serveClient(client)
+ except (KeyboardInterrupt, SystemExit):
+ return 0
+ except Exception as x:
+ logger.exception(x)
+
+ def serveClient(self, client):
+ """Process input/output from a client for as long as possible"""
+ itrans = self.inputTransportFactory.getTransport(client)
+ otrans = self.outputTransportFactory.getTransport(client)
+ iprot = self.inputProtocolFactory.getProtocol(itrans)
+ oprot = self.outputProtocolFactory.getProtocol(otrans)
+
+ try:
+ while True:
+ self.processor.process(iprot, oprot)
+ except TTransportException:
+ pass
+ except Exception as x:
+ logger.exception(x)
+
+ itrans.close()
+ otrans.close()
+
+ def serve(self):
+ """Start workers and put into queue"""
+ # this is a shared state that can tell the workers to exit when False
+ self.isRunning.value = True
+
+ # first bind and listen to the port
+ self.serverTransport.listen()
+
+ # fork the children
+ for i in range(self.numWorkers):
+ try:
+ w = Process(target=self.workerProcess)
+ w.daemon = True
+ w.start()
+ self.workers.append(w)
+ except Exception as x:
+ logger.exception(x)
+
+ # wait until the condition is set by stop()
+ while True:
+ self.stopCondition.acquire()
+ try:
+ self.stopCondition.wait()
+ break
+ except (SystemExit, KeyboardInterrupt):
+ break
+ except Exception as x:
+ logger.exception(x)
+
+ self.isRunning.value = False
+
+ def stop(self):
+ self.isRunning.value = False
+ self.stopCondition.acquire()
+ self.stopCondition.notify()
+ self.stopCondition.release()
diff --git a/src/jaegertracing/thrift/lib/py/src/server/TServer.py b/src/jaegertracing/thrift/lib/py/src/server/TServer.py
new file mode 100644
index 000000000..df2a7bb93
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/server/TServer.py
@@ -0,0 +1,323 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from six.moves import queue
+import logging
+import os
+import threading
+
+from thrift.protocol import TBinaryProtocol
+from thrift.protocol.THeaderProtocol import THeaderProtocolFactory
+from thrift.transport import TTransport
+
+logger = logging.getLogger(__name__)
+
+
+class TServer(object):
+ """Base interface for a server, which must have a serve() method.
+
+ Three constructors for all servers:
+ 1) (processor, serverTransport)
+ 2) (processor, serverTransport, transportFactory, protocolFactory)
+ 3) (processor, serverTransport,
+ inputTransportFactory, outputTransportFactory,
+ inputProtocolFactory, outputProtocolFactory)
+ """
+ def __init__(self, *args):
+ if (len(args) == 2):
+ self.__initArgs__(args[0], args[1],
+ TTransport.TTransportFactoryBase(),
+ TTransport.TTransportFactoryBase(),
+ TBinaryProtocol.TBinaryProtocolFactory(),
+ TBinaryProtocol.TBinaryProtocolFactory())
+ elif (len(args) == 4):
+ self.__initArgs__(args[0], args[1], args[2], args[2], args[3], args[3])
+ elif (len(args) == 6):
+ self.__initArgs__(args[0], args[1], args[2], args[3], args[4], args[5])
+
+ def __initArgs__(self, processor, serverTransport,
+ inputTransportFactory, outputTransportFactory,
+ inputProtocolFactory, outputProtocolFactory):
+ self.processor = processor
+ self.serverTransport = serverTransport
+ self.inputTransportFactory = inputTransportFactory
+ self.outputTransportFactory = outputTransportFactory
+ self.inputProtocolFactory = inputProtocolFactory
+ self.outputProtocolFactory = outputProtocolFactory
+
+ input_is_header = isinstance(self.inputProtocolFactory, THeaderProtocolFactory)
+ output_is_header = isinstance(self.outputProtocolFactory, THeaderProtocolFactory)
+ if any((input_is_header, output_is_header)) and input_is_header != output_is_header:
+ raise ValueError("THeaderProtocol servers require that both the input and "
+ "output protocols are THeaderProtocol.")
+
+ def serve(self):
+ pass
+
+
+class TSimpleServer(TServer):
+ """Simple single-threaded server that just pumps around one transport."""
+
+ def __init__(self, *args):
+ TServer.__init__(self, *args)
+
+ def serve(self):
+ self.serverTransport.listen()
+ while True:
+ client = self.serverTransport.accept()
+ if not client:
+ continue
+
+ itrans = self.inputTransportFactory.getTransport(client)
+ iprot = self.inputProtocolFactory.getProtocol(itrans)
+
+ # for THeaderProtocol, we must use the same protocol instance for
+ # input and output so that the response is in the same dialect that
+ # the server detected the request was in.
+ if isinstance(self.inputProtocolFactory, THeaderProtocolFactory):
+ otrans = None
+ oprot = iprot
+ else:
+ otrans = self.outputTransportFactory.getTransport(client)
+ oprot = self.outputProtocolFactory.getProtocol(otrans)
+
+ try:
+ while True:
+ self.processor.process(iprot, oprot)
+ except TTransport.TTransportException:
+ pass
+ except Exception as x:
+ logger.exception(x)
+
+ itrans.close()
+ if otrans:
+ otrans.close()
+
+
+class TThreadedServer(TServer):
+ """Threaded server that spawns a new thread per each connection."""
+
+ def __init__(self, *args, **kwargs):
+ TServer.__init__(self, *args)
+ self.daemon = kwargs.get("daemon", False)
+
+ def serve(self):
+ self.serverTransport.listen()
+ while True:
+ try:
+ client = self.serverTransport.accept()
+ if not client:
+ continue
+ t = threading.Thread(target=self.handle, args=(client,))
+ t.setDaemon(self.daemon)
+ t.start()
+ except KeyboardInterrupt:
+ raise
+ except Exception as x:
+ logger.exception(x)
+
+ def handle(self, client):
+ itrans = self.inputTransportFactory.getTransport(client)
+ iprot = self.inputProtocolFactory.getProtocol(itrans)
+
+ # for THeaderProtocol, we must use the same protocol instance for input
+ # and output so that the response is in the same dialect that the
+ # server detected the request was in.
+ if isinstance(self.inputProtocolFactory, THeaderProtocolFactory):
+ otrans = None
+ oprot = iprot
+ else:
+ otrans = self.outputTransportFactory.getTransport(client)
+ oprot = self.outputProtocolFactory.getProtocol(otrans)
+
+ try:
+ while True:
+ self.processor.process(iprot, oprot)
+ except TTransport.TTransportException:
+ pass
+ except Exception as x:
+ logger.exception(x)
+
+ itrans.close()
+ if otrans:
+ otrans.close()
+
+
+class TThreadPoolServer(TServer):
+ """Server with a fixed size pool of threads which service requests."""
+
+ def __init__(self, *args, **kwargs):
+ TServer.__init__(self, *args)
+ self.clients = queue.Queue()
+ self.threads = 10
+ self.daemon = kwargs.get("daemon", False)
+
+ def setNumThreads(self, num):
+ """Set the number of worker threads that should be created"""
+ self.threads = num
+
+ def serveThread(self):
+ """Loop around getting clients from the shared queue and process them."""
+ while True:
+ try:
+ client = self.clients.get()
+ self.serveClient(client)
+ except Exception as x:
+ logger.exception(x)
+
+ def serveClient(self, client):
+ """Process input/output from a client for as long as possible"""
+ itrans = self.inputTransportFactory.getTransport(client)
+ iprot = self.inputProtocolFactory.getProtocol(itrans)
+
+ # for THeaderProtocol, we must use the same protocol instance for input
+ # and output so that the response is in the same dialect that the
+ # server detected the request was in.
+ if isinstance(self.inputProtocolFactory, THeaderProtocolFactory):
+ otrans = None
+ oprot = iprot
+ else:
+ otrans = self.outputTransportFactory.getTransport(client)
+ oprot = self.outputProtocolFactory.getProtocol(otrans)
+
+ try:
+ while True:
+ self.processor.process(iprot, oprot)
+ except TTransport.TTransportException:
+ pass
+ except Exception as x:
+ logger.exception(x)
+
+ itrans.close()
+ if otrans:
+ otrans.close()
+
+ def serve(self):
+ """Start a fixed number of worker threads and put client into a queue"""
+ for i in range(self.threads):
+ try:
+ t = threading.Thread(target=self.serveThread)
+ t.setDaemon(self.daemon)
+ t.start()
+ except Exception as x:
+ logger.exception(x)
+
+ # Pump the socket for clients
+ self.serverTransport.listen()
+ while True:
+ try:
+ client = self.serverTransport.accept()
+ if not client:
+ continue
+ self.clients.put(client)
+ except Exception as x:
+ logger.exception(x)
+
+
+class TForkingServer(TServer):
+ """A Thrift server that forks a new process for each request
+
+ This is more scalable than the threaded server as it does not cause
+ GIL contention.
+
+ Note that this has different semantics from the threading server.
+ Specifically, updates to shared variables will no longer be shared.
+ It will also not work on windows.
+
+ This code is heavily inspired by SocketServer.ForkingMixIn in the
+ Python stdlib.
+ """
+ def __init__(self, *args):
+ TServer.__init__(self, *args)
+ self.children = []
+
+ def serve(self):
+ def try_close(file):
+ try:
+ file.close()
+ except IOError as e:
+ logger.warning(e, exc_info=True)
+
+ self.serverTransport.listen()
+ while True:
+ client = self.serverTransport.accept()
+ if not client:
+ continue
+ try:
+ pid = os.fork()
+
+ if pid: # parent
+ # add before collect, otherwise you race w/ waitpid
+ self.children.append(pid)
+ self.collect_children()
+
+ # Parent must close socket or the connection may not get
+ # closed promptly
+ itrans = self.inputTransportFactory.getTransport(client)
+ otrans = self.outputTransportFactory.getTransport(client)
+ try_close(itrans)
+ try_close(otrans)
+ else:
+ itrans = self.inputTransportFactory.getTransport(client)
+ iprot = self.inputProtocolFactory.getProtocol(itrans)
+
+ # for THeaderProtocol, we must use the same protocol
+ # instance for input and output so that the response is in
+ # the same dialect that the server detected the request was
+ # in.
+ if isinstance(self.inputProtocolFactory, THeaderProtocolFactory):
+ otrans = None
+ oprot = iprot
+ else:
+ otrans = self.outputTransportFactory.getTransport(client)
+ oprot = self.outputProtocolFactory.getProtocol(otrans)
+
+ ecode = 0
+ try:
+ try:
+ while True:
+ self.processor.process(iprot, oprot)
+ except TTransport.TTransportException:
+ pass
+ except Exception as e:
+ logger.exception(e)
+ ecode = 1
+ finally:
+ try_close(itrans)
+ if otrans:
+ try_close(otrans)
+
+ os._exit(ecode)
+
+ except TTransport.TTransportException:
+ pass
+ except Exception as x:
+ logger.exception(x)
+
+ def collect_children(self):
+ while self.children:
+ try:
+ pid, status = os.waitpid(0, os.WNOHANG)
+ except os.error:
+ pid = None
+
+ if pid:
+ self.children.remove(pid)
+ else:
+ break
diff --git a/src/jaegertracing/thrift/lib/py/src/server/__init__.py b/src/jaegertracing/thrift/lib/py/src/server/__init__.py
new file mode 100644
index 000000000..1bf6e254e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/server/__init__.py
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+__all__ = ['TServer', 'TNonblockingServer']
diff --git a/src/jaegertracing/thrift/lib/py/src/transport/THeaderTransport.py b/src/jaegertracing/thrift/lib/py/src/transport/THeaderTransport.py
new file mode 100644
index 000000000..c0d564012
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/transport/THeaderTransport.py
@@ -0,0 +1,352 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import struct
+import zlib
+
+from thrift.compat import BufferIO, byte_index
+from thrift.protocol.TBinaryProtocol import TBinaryProtocol
+from thrift.protocol.TCompactProtocol import TCompactProtocol, readVarint, writeVarint
+from thrift.Thrift import TApplicationException
+from thrift.transport.TTransport import (
+ CReadableTransport,
+ TMemoryBuffer,
+ TTransportBase,
+ TTransportException,
+)
+
+
+U16 = struct.Struct("!H")
+I32 = struct.Struct("!i")
+HEADER_MAGIC = 0x0FFF
+HARD_MAX_FRAME_SIZE = 0x3FFFFFFF
+
+
+class THeaderClientType(object):
+ HEADERS = 0x00
+
+ FRAMED_BINARY = 0x01
+ UNFRAMED_BINARY = 0x02
+
+ FRAMED_COMPACT = 0x03
+ UNFRAMED_COMPACT = 0x04
+
+
+class THeaderSubprotocolID(object):
+ BINARY = 0x00
+ COMPACT = 0x02
+
+
+class TInfoHeaderType(object):
+ KEY_VALUE = 0x01
+
+
+class THeaderTransformID(object):
+ ZLIB = 0x01
+
+
+READ_TRANSFORMS_BY_ID = {
+ THeaderTransformID.ZLIB: zlib.decompress,
+}
+
+
+WRITE_TRANSFORMS_BY_ID = {
+ THeaderTransformID.ZLIB: zlib.compress,
+}
+
+
+def _readString(trans):
+ size = readVarint(trans)
+ if size < 0:
+ raise TTransportException(
+ TTransportException.NEGATIVE_SIZE,
+ "Negative length"
+ )
+ return trans.read(size)
+
+
+def _writeString(trans, value):
+ writeVarint(trans, len(value))
+ trans.write(value)
+
+
+class THeaderTransport(TTransportBase, CReadableTransport):
+ def __init__(self, transport, allowed_client_types):
+ self._transport = transport
+ self._client_type = THeaderClientType.HEADERS
+ self._allowed_client_types = allowed_client_types
+
+ self._read_buffer = BufferIO(b"")
+ self._read_headers = {}
+
+ self._write_buffer = BufferIO()
+ self._write_headers = {}
+ self._write_transforms = []
+
+ self.flags = 0
+ self.sequence_id = 0
+ self._protocol_id = THeaderSubprotocolID.BINARY
+ self._max_frame_size = HARD_MAX_FRAME_SIZE
+
+ def isOpen(self):
+ return self._transport.isOpen()
+
+ def open(self):
+ return self._transport.open()
+
+ def close(self):
+ return self._transport.close()
+
+ def get_headers(self):
+ return self._read_headers
+
+ def set_header(self, key, value):
+ if not isinstance(key, bytes):
+ raise ValueError("header names must be bytes")
+ if not isinstance(value, bytes):
+ raise ValueError("header values must be bytes")
+ self._write_headers[key] = value
+
+ def clear_headers(self):
+ self._write_headers.clear()
+
+ def add_transform(self, transform_id):
+ if transform_id not in WRITE_TRANSFORMS_BY_ID:
+ raise ValueError("unknown transform")
+ self._write_transforms.append(transform_id)
+
+ def set_max_frame_size(self, size):
+ if not 0 < size < HARD_MAX_FRAME_SIZE:
+ raise ValueError("maximum frame size should be < %d and > 0" % HARD_MAX_FRAME_SIZE)
+ self._max_frame_size = size
+
+ @property
+ def protocol_id(self):
+ if self._client_type == THeaderClientType.HEADERS:
+ return self._protocol_id
+ elif self._client_type in (THeaderClientType.FRAMED_BINARY, THeaderClientType.UNFRAMED_BINARY):
+ return THeaderSubprotocolID.BINARY
+ elif self._client_type in (THeaderClientType.FRAMED_COMPACT, THeaderClientType.UNFRAMED_COMPACT):
+ return THeaderSubprotocolID.COMPACT
+ else:
+ raise TTransportException(
+ TTransportException.INVALID_CLIENT_TYPE,
+ "Protocol ID not know for client type %d" % self._client_type,
+ )
+
+ def read(self, sz):
+ # if there are bytes left in the buffer, produce those first.
+ bytes_read = self._read_buffer.read(sz)
+ bytes_left_to_read = sz - len(bytes_read)
+ if bytes_left_to_read == 0:
+ return bytes_read
+
+ # if we've determined this is an unframed client, just pass the read
+ # through to the underlying transport until we're reset again at the
+ # beginning of the next message.
+ if self._client_type in (THeaderClientType.UNFRAMED_BINARY, THeaderClientType.UNFRAMED_COMPACT):
+ return bytes_read + self._transport.read(bytes_left_to_read)
+
+ # we're empty and (maybe) framed. fill the buffers with the next frame.
+ self.readFrame(bytes_left_to_read)
+ return bytes_read + self._read_buffer.read(bytes_left_to_read)
+
+ def _set_client_type(self, client_type):
+ if client_type not in self._allowed_client_types:
+ raise TTransportException(
+ TTransportException.INVALID_CLIENT_TYPE,
+ "Client type %d not allowed by server." % client_type,
+ )
+ self._client_type = client_type
+
+ def readFrame(self, req_sz):
+ # the first word could either be the length field of a framed message
+ # or the first bytes of an unframed message.
+ first_word = self._transport.readAll(I32.size)
+ frame_size, = I32.unpack(first_word)
+ is_unframed = False
+ if frame_size & TBinaryProtocol.VERSION_MASK == TBinaryProtocol.VERSION_1:
+ self._set_client_type(THeaderClientType.UNFRAMED_BINARY)
+ is_unframed = True
+ elif (byte_index(first_word, 0) == TCompactProtocol.PROTOCOL_ID and
+ byte_index(first_word, 1) & TCompactProtocol.VERSION_MASK == TCompactProtocol.VERSION):
+ self._set_client_type(THeaderClientType.UNFRAMED_COMPACT)
+ is_unframed = True
+
+ if is_unframed:
+ bytes_left_to_read = req_sz - I32.size
+ if bytes_left_to_read > 0:
+ rest = self._transport.read(bytes_left_to_read)
+ else:
+ rest = b""
+ self._read_buffer = BufferIO(first_word + rest)
+ return
+
+ # ok, we're still here so we're framed.
+ if frame_size > self._max_frame_size:
+ raise TTransportException(
+ TTransportException.SIZE_LIMIT,
+ "Frame was too large.",
+ )
+ read_buffer = BufferIO(self._transport.readAll(frame_size))
+
+ # the next word is either going to be the version field of a
+ # binary/compact protocol message or the magic value + flags of a
+ # header protocol message.
+ second_word = read_buffer.read(I32.size)
+ version, = I32.unpack(second_word)
+ read_buffer.seek(0)
+ if version >> 16 == HEADER_MAGIC:
+ self._set_client_type(THeaderClientType.HEADERS)
+ self._read_buffer = self._parse_header_format(read_buffer)
+ elif version & TBinaryProtocol.VERSION_MASK == TBinaryProtocol.VERSION_1:
+ self._set_client_type(THeaderClientType.FRAMED_BINARY)
+ self._read_buffer = read_buffer
+ elif (byte_index(second_word, 0) == TCompactProtocol.PROTOCOL_ID and
+ byte_index(second_word, 1) & TCompactProtocol.VERSION_MASK == TCompactProtocol.VERSION):
+ self._set_client_type(THeaderClientType.FRAMED_COMPACT)
+ self._read_buffer = read_buffer
+ else:
+ raise TTransportException(
+ TTransportException.INVALID_CLIENT_TYPE,
+ "Could not detect client transport type.",
+ )
+
+ def _parse_header_format(self, buffer):
+ # make BufferIO look like TTransport for varint helpers
+ buffer_transport = TMemoryBuffer()
+ buffer_transport._buffer = buffer
+
+ buffer.read(2) # discard the magic bytes
+ self.flags, = U16.unpack(buffer.read(U16.size))
+ self.sequence_id, = I32.unpack(buffer.read(I32.size))
+
+ header_length = U16.unpack(buffer.read(U16.size))[0] * 4
+ end_of_headers = buffer.tell() + header_length
+ if end_of_headers > len(buffer.getvalue()):
+ raise TTransportException(
+ TTransportException.SIZE_LIMIT,
+ "Header size is larger than whole frame.",
+ )
+
+ self._protocol_id = readVarint(buffer_transport)
+
+ transforms = []
+ transform_count = readVarint(buffer_transport)
+ for _ in range(transform_count):
+ transform_id = readVarint(buffer_transport)
+ if transform_id not in READ_TRANSFORMS_BY_ID:
+ raise TApplicationException(
+ TApplicationException.INVALID_TRANSFORM,
+ "Unknown transform: %d" % transform_id,
+ )
+ transforms.append(transform_id)
+ transforms.reverse()
+
+ headers = {}
+ while buffer.tell() < end_of_headers:
+ header_type = readVarint(buffer_transport)
+ if header_type == TInfoHeaderType.KEY_VALUE:
+ count = readVarint(buffer_transport)
+ for _ in range(count):
+ key = _readString(buffer_transport)
+ value = _readString(buffer_transport)
+ headers[key] = value
+ else:
+ break # ignore unknown headers
+ self._read_headers = headers
+
+ # skip padding / anything we didn't understand
+ buffer.seek(end_of_headers)
+
+ payload = buffer.read()
+ for transform_id in transforms:
+ transform_fn = READ_TRANSFORMS_BY_ID[transform_id]
+ payload = transform_fn(payload)
+ return BufferIO(payload)
+
+ def write(self, buf):
+ self._write_buffer.write(buf)
+
+ def flush(self):
+ payload = self._write_buffer.getvalue()
+ self._write_buffer = BufferIO()
+
+ buffer = BufferIO()
+ if self._client_type == THeaderClientType.HEADERS:
+ for transform_id in self._write_transforms:
+ transform_fn = WRITE_TRANSFORMS_BY_ID[transform_id]
+ payload = transform_fn(payload)
+
+ headers = BufferIO()
+ writeVarint(headers, self._protocol_id)
+ writeVarint(headers, len(self._write_transforms))
+ for transform_id in self._write_transforms:
+ writeVarint(headers, transform_id)
+ if self._write_headers:
+ writeVarint(headers, TInfoHeaderType.KEY_VALUE)
+ writeVarint(headers, len(self._write_headers))
+ for key, value in self._write_headers.items():
+ _writeString(headers, key)
+ _writeString(headers, value)
+ self._write_headers = {}
+ padding_needed = (4 - (len(headers.getvalue()) % 4)) % 4
+ headers.write(b"\x00" * padding_needed)
+ header_bytes = headers.getvalue()
+
+ buffer.write(I32.pack(10 + len(header_bytes) + len(payload)))
+ buffer.write(U16.pack(HEADER_MAGIC))
+ buffer.write(U16.pack(self.flags))
+ buffer.write(I32.pack(self.sequence_id))
+ buffer.write(U16.pack(len(header_bytes) // 4))
+ buffer.write(header_bytes)
+ buffer.write(payload)
+ elif self._client_type in (THeaderClientType.FRAMED_BINARY, THeaderClientType.FRAMED_COMPACT):
+ buffer.write(I32.pack(len(payload)))
+ buffer.write(payload)
+ elif self._client_type in (THeaderClientType.UNFRAMED_BINARY, THeaderClientType.UNFRAMED_COMPACT):
+ buffer.write(payload)
+ else:
+ raise TTransportException(
+ TTransportException.INVALID_CLIENT_TYPE,
+ "Unknown client type.",
+ )
+
+ # the frame length field doesn't count towards the frame payload size
+ frame_bytes = buffer.getvalue()
+ frame_payload_size = len(frame_bytes) - 4
+ if frame_payload_size > self._max_frame_size:
+ raise TTransportException(
+ TTransportException.SIZE_LIMIT,
+ "Attempting to send frame that is too large.",
+ )
+
+ self._transport.write(frame_bytes)
+ self._transport.flush()
+
+ @property
+ def cstringio_buf(self):
+ return self._read_buffer
+
+ def cstringio_refill(self, partialread, reqlen):
+ result = bytearray(partialread)
+ while len(result) < reqlen:
+ result += self.read(reqlen - len(result))
+ self._read_buffer = BufferIO(result)
+ return self._read_buffer
diff --git a/src/jaegertracing/thrift/lib/py/src/transport/THttpClient.py b/src/jaegertracing/thrift/lib/py/src/transport/THttpClient.py
new file mode 100644
index 000000000..37b0a4d8d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/transport/THttpClient.py
@@ -0,0 +1,187 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from io import BytesIO
+import os
+import ssl
+import sys
+import warnings
+import base64
+
+from six.moves import urllib
+from six.moves import http_client
+
+from .TTransport import TTransportBase
+import six
+
+
+class THttpClient(TTransportBase):
+ """Http implementation of TTransport base."""
+
+ def __init__(self, uri_or_host, port=None, path=None, cafile=None, cert_file=None, key_file=None, ssl_context=None):
+ """THttpClient supports two different types of construction:
+
+ THttpClient(host, port, path) - deprecated
+ THttpClient(uri, [port=<n>, path=<s>, cafile=<filename>, cert_file=<filename>, key_file=<filename>, ssl_context=<context>])
+
+ Only the second supports https. To properly authenticate against the server,
+ provide the client's identity by specifying cert_file and key_file. To properly
+ authenticate the server, specify either cafile or ssl_context with a CA defined.
+ NOTE: if both cafile and ssl_context are defined, ssl_context will override cafile.
+ """
+ if port is not None:
+ warnings.warn(
+ "Please use the THttpClient('http{s}://host:port/path') constructor",
+ DeprecationWarning,
+ stacklevel=2)
+ self.host = uri_or_host
+ self.port = port
+ assert path
+ self.path = path
+ self.scheme = 'http'
+ else:
+ parsed = urllib.parse.urlparse(uri_or_host)
+ self.scheme = parsed.scheme
+ assert self.scheme in ('http', 'https')
+ if self.scheme == 'http':
+ self.port = parsed.port or http_client.HTTP_PORT
+ elif self.scheme == 'https':
+ self.port = parsed.port or http_client.HTTPS_PORT
+ self.certfile = cert_file
+ self.keyfile = key_file
+ self.context = ssl.create_default_context(cafile=cafile) if (cafile and not ssl_context) else ssl_context
+ self.host = parsed.hostname
+ self.path = parsed.path
+ if parsed.query:
+ self.path += '?%s' % parsed.query
+ try:
+ proxy = urllib.request.getproxies()[self.scheme]
+ except KeyError:
+ proxy = None
+ else:
+ if urllib.request.proxy_bypass(self.host):
+ proxy = None
+ if proxy:
+ parsed = urllib.parse.urlparse(proxy)
+ self.realhost = self.host
+ self.realport = self.port
+ self.host = parsed.hostname
+ self.port = parsed.port
+ self.proxy_auth = self.basic_proxy_auth_header(parsed)
+ else:
+ self.realhost = self.realport = self.proxy_auth = None
+ self.__wbuf = BytesIO()
+ self.__http = None
+ self.__http_response = None
+ self.__timeout = None
+ self.__custom_headers = None
+
+ @staticmethod
+ def basic_proxy_auth_header(proxy):
+ if proxy is None or not proxy.username:
+ return None
+ ap = "%s:%s" % (urllib.parse.unquote(proxy.username),
+ urllib.parse.unquote(proxy.password))
+ cr = base64.b64encode(ap).strip()
+ return "Basic " + cr
+
+ def using_proxy(self):
+ return self.realhost is not None
+
+ def open(self):
+ if self.scheme == 'http':
+ self.__http = http_client.HTTPConnection(self.host, self.port,
+ timeout=self.__timeout)
+ elif self.scheme == 'https':
+ self.__http = http_client.HTTPSConnection(self.host, self.port,
+ key_file=self.keyfile,
+ cert_file=self.certfile,
+ timeout=self.__timeout,
+ context=self.context)
+ if self.using_proxy():
+ self.__http.set_tunnel(self.realhost, self.realport,
+ {"Proxy-Authorization": self.proxy_auth})
+
+ def close(self):
+ self.__http.close()
+ self.__http = None
+ self.__http_response = None
+
+ def isOpen(self):
+ return self.__http is not None
+
+ def setTimeout(self, ms):
+ if ms is None:
+ self.__timeout = None
+ else:
+ self.__timeout = ms / 1000.0
+
+ def setCustomHeaders(self, headers):
+ self.__custom_headers = headers
+
+ def read(self, sz):
+ return self.__http_response.read(sz)
+
+ def write(self, buf):
+ self.__wbuf.write(buf)
+
+ def flush(self):
+ if self.isOpen():
+ self.close()
+ self.open()
+
+ # Pull data out of buffer
+ data = self.__wbuf.getvalue()
+ self.__wbuf = BytesIO()
+
+ # HTTP request
+ if self.using_proxy() and self.scheme == "http":
+ # need full URL of real host for HTTP proxy here (HTTPS uses CONNECT tunnel)
+ self.__http.putrequest('POST', "http://%s:%s%s" %
+ (self.realhost, self.realport, self.path))
+ else:
+ self.__http.putrequest('POST', self.path)
+
+ # Write headers
+ self.__http.putheader('Content-Type', 'application/x-thrift')
+ self.__http.putheader('Content-Length', str(len(data)))
+ if self.using_proxy() and self.scheme == "http" and self.proxy_auth is not None:
+ self.__http.putheader("Proxy-Authorization", self.proxy_auth)
+
+ if not self.__custom_headers or 'User-Agent' not in self.__custom_headers:
+ user_agent = 'Python/THttpClient'
+ script = os.path.basename(sys.argv[0])
+ if script:
+ user_agent = '%s (%s)' % (user_agent, urllib.parse.quote(script))
+ self.__http.putheader('User-Agent', user_agent)
+
+ if self.__custom_headers:
+ for key, val in six.iteritems(self.__custom_headers):
+ self.__http.putheader(key, val)
+
+ self.__http.endheaders()
+
+ # Write payload
+ self.__http.send(data)
+
+ # Get reply to flush the request
+ self.__http_response = self.__http.getresponse()
+ self.code = self.__http_response.status
+ self.message = self.__http_response.reason
+ self.headers = self.__http_response.msg
diff --git a/src/jaegertracing/thrift/lib/py/src/transport/TSSLSocket.py b/src/jaegertracing/thrift/lib/py/src/transport/TSSLSocket.py
new file mode 100644
index 000000000..5b3ae5991
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/transport/TSSLSocket.py
@@ -0,0 +1,408 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import logging
+import os
+import socket
+import ssl
+import sys
+import warnings
+
+from .sslcompat import _match_hostname, _match_has_ipaddress
+from thrift.transport import TSocket
+from thrift.transport.TTransport import TTransportException
+
+logger = logging.getLogger(__name__)
+warnings.filterwarnings(
+ 'default', category=DeprecationWarning, module=__name__)
+
+
+class TSSLBase(object):
+ # SSLContext is not available for Python < 2.7.9
+ _has_ssl_context = sys.hexversion >= 0x020709F0
+
+ # ciphers argument is not available for Python < 2.7.0
+ _has_ciphers = sys.hexversion >= 0x020700F0
+
+ # For python >= 2.7.9, use latest TLS that both client and server
+ # supports.
+ # SSL 2.0 and 3.0 are disabled via ssl.OP_NO_SSLv2 and ssl.OP_NO_SSLv3.
+ # For python < 2.7.9, use TLS 1.0 since TLSv1_X nor OP_NO_SSLvX is
+ # unavailable.
+ _default_protocol = ssl.PROTOCOL_SSLv23 if _has_ssl_context else \
+ ssl.PROTOCOL_TLSv1
+
+ def _init_context(self, ssl_version):
+ if self._has_ssl_context:
+ self._context = ssl.SSLContext(ssl_version)
+ if self._context.protocol == ssl.PROTOCOL_SSLv23:
+ self._context.options |= ssl.OP_NO_SSLv2
+ self._context.options |= ssl.OP_NO_SSLv3
+ else:
+ self._context = None
+ self._ssl_version = ssl_version
+
+ @property
+ def _should_verify(self):
+ if self._has_ssl_context:
+ return self._context.verify_mode != ssl.CERT_NONE
+ else:
+ return self.cert_reqs != ssl.CERT_NONE
+
+ @property
+ def ssl_version(self):
+ if self._has_ssl_context:
+ return self.ssl_context.protocol
+ else:
+ return self._ssl_version
+
+ @property
+ def ssl_context(self):
+ return self._context
+
+ SSL_VERSION = _default_protocol
+ """
+ Default SSL version.
+ For backwards compatibility, it can be modified.
+ Use __init__ keyword argument "ssl_version" instead.
+ """
+
+ def _deprecated_arg(self, args, kwargs, pos, key):
+ if len(args) <= pos:
+ return
+ real_pos = pos + 3
+ warnings.warn(
+ '%dth positional argument is deprecated.'
+ 'please use keyword argument instead.'
+ % real_pos, DeprecationWarning, stacklevel=3)
+
+ if key in kwargs:
+ raise TypeError(
+ 'Duplicate argument: %dth argument and %s keyword argument.'
+ % (real_pos, key))
+ kwargs[key] = args[pos]
+
+ def _unix_socket_arg(self, host, port, args, kwargs):
+ key = 'unix_socket'
+ if host is None and port is None and len(args) == 1 and key not in kwargs:
+ kwargs[key] = args[0]
+ return True
+ return False
+
+ def __getattr__(self, key):
+ if key == 'SSL_VERSION':
+ warnings.warn(
+ 'SSL_VERSION is deprecated.'
+ 'please use ssl_version attribute instead.',
+ DeprecationWarning, stacklevel=2)
+ return self.ssl_version
+
+ def __init__(self, server_side, host, ssl_opts):
+ self._server_side = server_side
+ if TSSLBase.SSL_VERSION != self._default_protocol:
+ warnings.warn(
+ 'SSL_VERSION is deprecated.'
+ 'please use ssl_version keyword argument instead.',
+ DeprecationWarning, stacklevel=2)
+ self._context = ssl_opts.pop('ssl_context', None)
+ self._server_hostname = None
+ if not self._server_side:
+ self._server_hostname = ssl_opts.pop('server_hostname', host)
+ if self._context:
+ self._custom_context = True
+ if ssl_opts:
+ raise ValueError(
+ 'Incompatible arguments: ssl_context and %s'
+ % ' '.join(ssl_opts.keys()))
+ if not self._has_ssl_context:
+ raise ValueError(
+ 'ssl_context is not available for this version of Python')
+ else:
+ self._custom_context = False
+ ssl_version = ssl_opts.pop('ssl_version', TSSLBase.SSL_VERSION)
+ self._init_context(ssl_version)
+ self.cert_reqs = ssl_opts.pop('cert_reqs', ssl.CERT_REQUIRED)
+ self.ca_certs = ssl_opts.pop('ca_certs', None)
+ self.keyfile = ssl_opts.pop('keyfile', None)
+ self.certfile = ssl_opts.pop('certfile', None)
+ self.ciphers = ssl_opts.pop('ciphers', None)
+
+ if ssl_opts:
+ raise ValueError(
+ 'Unknown keyword arguments: ', ' '.join(ssl_opts.keys()))
+
+ if self._should_verify:
+ if not self.ca_certs:
+ raise ValueError(
+ 'ca_certs is needed when cert_reqs is not ssl.CERT_NONE')
+ if not os.access(self.ca_certs, os.R_OK):
+ raise IOError('Certificate Authority ca_certs file "%s" '
+ 'is not readable, cannot validate SSL '
+ 'certificates.' % (self.ca_certs))
+
+ @property
+ def certfile(self):
+ return self._certfile
+
+ @certfile.setter
+ def certfile(self, certfile):
+ if self._server_side and not certfile:
+ raise ValueError('certfile is needed for server-side')
+ if certfile and not os.access(certfile, os.R_OK):
+ raise IOError('No such certfile found: %s' % (certfile))
+ self._certfile = certfile
+
+ def _wrap_socket(self, sock):
+ if self._has_ssl_context:
+ if not self._custom_context:
+ self.ssl_context.verify_mode = self.cert_reqs
+ if self.certfile:
+ self.ssl_context.load_cert_chain(self.certfile,
+ self.keyfile)
+ if self.ciphers:
+ self.ssl_context.set_ciphers(self.ciphers)
+ if self.ca_certs:
+ self.ssl_context.load_verify_locations(self.ca_certs)
+ return self.ssl_context.wrap_socket(
+ sock, server_side=self._server_side,
+ server_hostname=self._server_hostname)
+ else:
+ ssl_opts = {
+ 'ssl_version': self._ssl_version,
+ 'server_side': self._server_side,
+ 'ca_certs': self.ca_certs,
+ 'keyfile': self.keyfile,
+ 'certfile': self.certfile,
+ 'cert_reqs': self.cert_reqs,
+ }
+ if self.ciphers:
+ if self._has_ciphers:
+ ssl_opts['ciphers'] = self.ciphers
+ else:
+ logger.warning(
+ 'ciphers is specified but ignored due to old Python version')
+ return ssl.wrap_socket(sock, **ssl_opts)
+
+
+class TSSLSocket(TSocket.TSocket, TSSLBase):
+ """
+ SSL implementation of TSocket
+
+ This class creates outbound sockets wrapped using the
+ python standard ssl module for encrypted connections.
+ """
+
+ # New signature
+ # def __init__(self, host='localhost', port=9090, unix_socket=None,
+ # **ssl_args):
+ # Deprecated signature
+ # def __init__(self, host='localhost', port=9090, validate=True,
+ # ca_certs=None, keyfile=None, certfile=None,
+ # unix_socket=None, ciphers=None):
+ def __init__(self, host='localhost', port=9090, *args, **kwargs):
+ """Positional arguments: ``host``, ``port``, ``unix_socket``
+
+ Keyword arguments: ``keyfile``, ``certfile``, ``cert_reqs``,
+ ``ssl_version``, ``ca_certs``,
+ ``ciphers`` (Python 2.7.0 or later),
+ ``server_hostname`` (Python 2.7.9 or later)
+ Passed to ssl.wrap_socket. See ssl.wrap_socket documentation.
+
+ Alternative keyword arguments: (Python 2.7.9 or later)
+ ``ssl_context``: ssl.SSLContext to be used for SSLContext.wrap_socket
+ ``server_hostname``: Passed to SSLContext.wrap_socket
+
+ Common keyword argument:
+ ``validate_callback`` (cert, hostname) -> None:
+ Called after SSL handshake. Can raise when hostname does not
+ match the cert.
+ ``socket_keepalive`` enable TCP keepalive, default off.
+ """
+ self.is_valid = False
+ self.peercert = None
+
+ if args:
+ if len(args) > 6:
+ raise TypeError('Too many positional argument')
+ if not self._unix_socket_arg(host, port, args, kwargs):
+ self._deprecated_arg(args, kwargs, 0, 'validate')
+ self._deprecated_arg(args, kwargs, 1, 'ca_certs')
+ self._deprecated_arg(args, kwargs, 2, 'keyfile')
+ self._deprecated_arg(args, kwargs, 3, 'certfile')
+ self._deprecated_arg(args, kwargs, 4, 'unix_socket')
+ self._deprecated_arg(args, kwargs, 5, 'ciphers')
+
+ validate = kwargs.pop('validate', None)
+ if validate is not None:
+ cert_reqs_name = 'CERT_REQUIRED' if validate else 'CERT_NONE'
+ warnings.warn(
+ 'validate is deprecated. please use cert_reqs=ssl.%s instead'
+ % cert_reqs_name,
+ DeprecationWarning, stacklevel=2)
+ if 'cert_reqs' in kwargs:
+ raise TypeError('Cannot specify both validate and cert_reqs')
+ kwargs['cert_reqs'] = ssl.CERT_REQUIRED if validate else ssl.CERT_NONE
+
+ unix_socket = kwargs.pop('unix_socket', None)
+ socket_keepalive = kwargs.pop('socket_keepalive', False)
+ self._validate_callback = kwargs.pop('validate_callback', _match_hostname)
+ TSSLBase.__init__(self, False, host, kwargs)
+ TSocket.TSocket.__init__(self, host, port, unix_socket,
+ socket_keepalive=socket_keepalive)
+
+ def close(self):
+ try:
+ self.handle.settimeout(0.001)
+ self.handle = self.handle.unwrap()
+ except (ssl.SSLError, socket.error, OSError):
+ # could not complete shutdown in a reasonable amount of time. bail.
+ pass
+ TSocket.TSocket.close(self)
+
+ @property
+ def validate(self):
+ warnings.warn('validate is deprecated. please use cert_reqs instead',
+ DeprecationWarning, stacklevel=2)
+ return self.cert_reqs != ssl.CERT_NONE
+
+ @validate.setter
+ def validate(self, value):
+ warnings.warn('validate is deprecated. please use cert_reqs instead',
+ DeprecationWarning, stacklevel=2)
+ self.cert_reqs = ssl.CERT_REQUIRED if value else ssl.CERT_NONE
+
+ def _do_open(self, family, socktype):
+ plain_sock = socket.socket(family, socktype)
+ try:
+ return self._wrap_socket(plain_sock)
+ except Exception as ex:
+ plain_sock.close()
+ msg = 'failed to initialize SSL'
+ logger.exception(msg)
+ raise TTransportException(type=TTransportException.NOT_OPEN, message=msg, inner=ex)
+
+ def open(self):
+ super(TSSLSocket, self).open()
+ if self._should_verify:
+ self.peercert = self.handle.getpeercert()
+ try:
+ self._validate_callback(self.peercert, self._server_hostname)
+ self.is_valid = True
+ except TTransportException:
+ raise
+ except Exception as ex:
+ raise TTransportException(message=str(ex), inner=ex)
+
+
+class TSSLServerSocket(TSocket.TServerSocket, TSSLBase):
+ """SSL implementation of TServerSocket
+
+ This uses the ssl module's wrap_socket() method to provide SSL
+ negotiated encryption.
+ """
+
+ # New signature
+ # def __init__(self, host='localhost', port=9090, unix_socket=None, **ssl_args):
+ # Deprecated signature
+ # def __init__(self, host=None, port=9090, certfile='cert.pem', unix_socket=None, ciphers=None):
+ def __init__(self, host=None, port=9090, *args, **kwargs):
+ """Positional arguments: ``host``, ``port``, ``unix_socket``
+
+ Keyword arguments: ``keyfile``, ``certfile``, ``cert_reqs``, ``ssl_version``,
+ ``ca_certs``, ``ciphers`` (Python 2.7.0 or later)
+ See ssl.wrap_socket documentation.
+
+ Alternative keyword arguments: (Python 2.7.9 or later)
+ ``ssl_context``: ssl.SSLContext to be used for SSLContext.wrap_socket
+ ``server_hostname``: Passed to SSLContext.wrap_socket
+
+ Common keyword argument:
+ ``validate_callback`` (cert, hostname) -> None:
+ Called after SSL handshake. Can raise when hostname does not
+ match the cert.
+ """
+ if args:
+ if len(args) > 3:
+ raise TypeError('Too many positional argument')
+ if not self._unix_socket_arg(host, port, args, kwargs):
+ self._deprecated_arg(args, kwargs, 0, 'certfile')
+ self._deprecated_arg(args, kwargs, 1, 'unix_socket')
+ self._deprecated_arg(args, kwargs, 2, 'ciphers')
+
+ if 'ssl_context' not in kwargs:
+ # Preserve existing behaviors for default values
+ if 'cert_reqs' not in kwargs:
+ kwargs['cert_reqs'] = ssl.CERT_NONE
+ if'certfile' not in kwargs:
+ kwargs['certfile'] = 'cert.pem'
+
+ unix_socket = kwargs.pop('unix_socket', None)
+ self._validate_callback = \
+ kwargs.pop('validate_callback', _match_hostname)
+ TSSLBase.__init__(self, True, None, kwargs)
+ TSocket.TServerSocket.__init__(self, host, port, unix_socket)
+ if self._should_verify and not _match_has_ipaddress:
+ raise ValueError('Need ipaddress and backports.ssl_match_hostname '
+ 'module to verify client certificate')
+
+ def setCertfile(self, certfile):
+ """Set or change the server certificate file used to wrap new
+ connections.
+
+ @param certfile: The filename of the server certificate,
+ i.e. '/etc/certs/server.pem'
+ @type certfile: str
+
+ Raises an IOError exception if the certfile is not present or unreadable.
+ """
+ warnings.warn(
+ 'setCertfile is deprecated. please use certfile property instead.',
+ DeprecationWarning, stacklevel=2)
+ self.certfile = certfile
+
+ def accept(self):
+ plain_client, addr = self.handle.accept()
+ try:
+ client = self._wrap_socket(plain_client)
+ except (ssl.SSLError, socket.error, OSError):
+ logger.exception('Error while accepting from %s', addr)
+ # failed handshake/ssl wrap, close socket to client
+ plain_client.close()
+ # raise
+ # We can't raise the exception, because it kills most TServer derived
+ # serve() methods.
+ # Instead, return None, and let the TServer instance deal with it in
+ # other exception handling. (but TSimpleServer dies anyway)
+ return None
+
+ if self._should_verify:
+ client.peercert = client.getpeercert()
+ try:
+ self._validate_callback(client.peercert, addr[0])
+ client.is_valid = True
+ except Exception:
+ logger.warn('Failed to validate client certificate address: %s',
+ addr[0], exc_info=True)
+ client.close()
+ plain_client.close()
+ return None
+
+ result = TSocket.TSocket()
+ result.handle = client
+ return result
diff --git a/src/jaegertracing/thrift/lib/py/src/transport/TSocket.py b/src/jaegertracing/thrift/lib/py/src/transport/TSocket.py
new file mode 100644
index 000000000..df25d42db
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/transport/TSocket.py
@@ -0,0 +1,215 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import errno
+import logging
+import os
+import socket
+import sys
+
+from .TTransport import TTransportBase, TTransportException, TServerTransportBase
+
+logger = logging.getLogger(__name__)
+
+
+class TSocketBase(TTransportBase):
+ def _resolveAddr(self):
+ if self._unix_socket is not None:
+ return [(socket.AF_UNIX, socket.SOCK_STREAM, None, None,
+ self._unix_socket)]
+ else:
+ return socket.getaddrinfo(self.host,
+ self.port,
+ self._socket_family,
+ socket.SOCK_STREAM,
+ 0,
+ socket.AI_PASSIVE | socket.AI_ADDRCONFIG)
+
+ def close(self):
+ if self.handle:
+ self.handle.close()
+ self.handle = None
+
+
+class TSocket(TSocketBase):
+ """Socket implementation of TTransport base."""
+
+ def __init__(self, host='localhost', port=9090, unix_socket=None,
+ socket_family=socket.AF_UNSPEC,
+ socket_keepalive=False):
+ """Initialize a TSocket
+
+ @param host(str) The host to connect to.
+ @param port(int) The (TCP) port to connect to.
+ @param unix_socket(str) The filename of a unix socket to connect to.
+ (host and port will be ignored.)
+ @param socket_family(int) The socket family to use with this socket.
+ @param socket_keepalive(bool) enable TCP keepalive, default off.
+ """
+ self.host = host
+ self.port = port
+ self.handle = None
+ self._unix_socket = unix_socket
+ self._timeout = None
+ self._socket_family = socket_family
+ self._socket_keepalive = socket_keepalive
+
+ def setHandle(self, h):
+ self.handle = h
+
+ def isOpen(self):
+ return self.handle is not None
+
+ def setTimeout(self, ms):
+ if ms is None:
+ self._timeout = None
+ else:
+ self._timeout = ms / 1000.0
+
+ if self.handle is not None:
+ self.handle.settimeout(self._timeout)
+
+ def _do_open(self, family, socktype):
+ return socket.socket(family, socktype)
+
+ @property
+ def _address(self):
+ return self._unix_socket if self._unix_socket else '%s:%d' % (self.host, self.port)
+
+ def open(self):
+ if self.handle:
+ raise TTransportException(type=TTransportException.ALREADY_OPEN, message="already open")
+ try:
+ addrs = self._resolveAddr()
+ except socket.gaierror as gai:
+ msg = 'failed to resolve sockaddr for ' + str(self._address)
+ logger.exception(msg)
+ raise TTransportException(type=TTransportException.NOT_OPEN, message=msg, inner=gai)
+ for family, socktype, _, _, sockaddr in addrs:
+ handle = self._do_open(family, socktype)
+
+ # TCP_KEEPALIVE
+ if self._socket_keepalive:
+ handle.setsockopt(socket.IPPROTO_TCP, socket.SO_KEEPALIVE, 1)
+
+ handle.settimeout(self._timeout)
+ try:
+ handle.connect(sockaddr)
+ self.handle = handle
+ return
+ except socket.error:
+ handle.close()
+ logger.info('Could not connect to %s', sockaddr, exc_info=True)
+ msg = 'Could not connect to any of %s' % list(map(lambda a: a[4],
+ addrs))
+ logger.error(msg)
+ raise TTransportException(type=TTransportException.NOT_OPEN, message=msg)
+
+ def read(self, sz):
+ try:
+ buff = self.handle.recv(sz)
+ except socket.error as e:
+ if (e.args[0] == errno.ECONNRESET and
+ (sys.platform == 'darwin' or sys.platform.startswith('freebsd'))):
+ # freebsd and Mach don't follow POSIX semantic of recv
+ # and fail with ECONNRESET if peer performed shutdown.
+ # See corresponding comment and code in TSocket::read()
+ # in lib/cpp/src/transport/TSocket.cpp.
+ self.close()
+ # Trigger the check to raise the END_OF_FILE exception below.
+ buff = ''
+ elif e.args[0] == errno.ETIMEDOUT:
+ raise TTransportException(type=TTransportException.TIMED_OUT, message="read timeout", inner=e)
+ else:
+ raise TTransportException(message="unexpected exception", inner=e)
+ if len(buff) == 0:
+ raise TTransportException(type=TTransportException.END_OF_FILE,
+ message='TSocket read 0 bytes')
+ return buff
+
+ def write(self, buff):
+ if not self.handle:
+ raise TTransportException(type=TTransportException.NOT_OPEN,
+ message='Transport not open')
+ sent = 0
+ have = len(buff)
+ while sent < have:
+ try:
+ plus = self.handle.send(buff)
+ if plus == 0:
+ raise TTransportException(type=TTransportException.END_OF_FILE,
+ message='TSocket sent 0 bytes')
+ sent += plus
+ buff = buff[plus:]
+ except socket.error as e:
+ raise TTransportException(message="unexpected exception", inner=e)
+
+ def flush(self):
+ pass
+
+
+class TServerSocket(TSocketBase, TServerTransportBase):
+ """Socket implementation of TServerTransport base."""
+
+ def __init__(self, host=None, port=9090, unix_socket=None, socket_family=socket.AF_UNSPEC):
+ self.host = host
+ self.port = port
+ self._unix_socket = unix_socket
+ self._socket_family = socket_family
+ self.handle = None
+ self._backlog = 128
+
+ def setBacklog(self, backlog=None):
+ if not self.handle:
+ self._backlog = backlog
+ else:
+ # We cann't update backlog when it is already listening, since the
+ # handle has been created.
+ logger.warn('You have to set backlog before listen.')
+
+ def listen(self):
+ res0 = self._resolveAddr()
+ socket_family = self._socket_family == socket.AF_UNSPEC and socket.AF_INET6 or self._socket_family
+ for res in res0:
+ if res[0] is socket_family or res is res0[-1]:
+ break
+
+ # We need remove the old unix socket if the file exists and
+ # nobody is listening on it.
+ if self._unix_socket:
+ tmp = socket.socket(res[0], res[1])
+ try:
+ tmp.connect(res[4])
+ except socket.error as err:
+ eno, message = err.args
+ if eno == errno.ECONNREFUSED:
+ os.unlink(res[4])
+
+ self.handle = socket.socket(res[0], res[1])
+ self.handle.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ if hasattr(self.handle, 'settimeout'):
+ self.handle.settimeout(None)
+ self.handle.bind(res[4])
+ self.handle.listen(self._backlog)
+
+ def accept(self):
+ client, addr = self.handle.accept()
+ result = TSocket()
+ result.setHandle(client)
+ return result
diff --git a/src/jaegertracing/thrift/lib/py/src/transport/TTransport.py b/src/jaegertracing/thrift/lib/py/src/transport/TTransport.py
new file mode 100644
index 000000000..9dbe95df4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/transport/TTransport.py
@@ -0,0 +1,456 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from struct import pack, unpack
+from thrift.Thrift import TException
+from ..compat import BufferIO
+
+
+class TTransportException(TException):
+ """Custom Transport Exception class"""
+
+ UNKNOWN = 0
+ NOT_OPEN = 1
+ ALREADY_OPEN = 2
+ TIMED_OUT = 3
+ END_OF_FILE = 4
+ NEGATIVE_SIZE = 5
+ SIZE_LIMIT = 6
+ INVALID_CLIENT_TYPE = 7
+
+ def __init__(self, type=UNKNOWN, message=None, inner=None):
+ TException.__init__(self, message)
+ self.type = type
+ self.inner = inner
+
+
+class TTransportBase(object):
+ """Base class for Thrift transport layer."""
+
+ def isOpen(self):
+ pass
+
+ def open(self):
+ pass
+
+ def close(self):
+ pass
+
+ def read(self, sz):
+ pass
+
+ def readAll(self, sz):
+ buff = b''
+ have = 0
+ while (have < sz):
+ chunk = self.read(sz - have)
+ chunkLen = len(chunk)
+ have += chunkLen
+ buff += chunk
+
+ if chunkLen == 0:
+ raise EOFError()
+
+ return buff
+
+ def write(self, buf):
+ pass
+
+ def flush(self):
+ pass
+
+
+# This class should be thought of as an interface.
+class CReadableTransport(object):
+ """base class for transports that are readable from C"""
+
+ # TODO(dreiss): Think about changing this interface to allow us to use
+ # a (Python, not c) StringIO instead, because it allows
+ # you to write after reading.
+
+ # NOTE: This is a classic class, so properties will NOT work
+ # correctly for setting.
+ @property
+ def cstringio_buf(self):
+ """A cStringIO buffer that contains the current chunk we are reading."""
+ pass
+
+ def cstringio_refill(self, partialread, reqlen):
+ """Refills cstringio_buf.
+
+ Returns the currently used buffer (which can but need not be the same as
+ the old cstringio_buf). partialread is what the C code has read from the
+ buffer, and should be inserted into the buffer before any more reads. The
+ return value must be a new, not borrowed reference. Something along the
+ lines of self._buf should be fine.
+
+ If reqlen bytes can't be read, throw EOFError.
+ """
+ pass
+
+
+class TServerTransportBase(object):
+ """Base class for Thrift server transports."""
+
+ def listen(self):
+ pass
+
+ def accept(self):
+ pass
+
+ def close(self):
+ pass
+
+
+class TTransportFactoryBase(object):
+ """Base class for a Transport Factory"""
+
+ def getTransport(self, trans):
+ return trans
+
+
+class TBufferedTransportFactory(object):
+ """Factory transport that builds buffered transports"""
+
+ def getTransport(self, trans):
+ buffered = TBufferedTransport(trans)
+ return buffered
+
+
+class TBufferedTransport(TTransportBase, CReadableTransport):
+ """Class that wraps another transport and buffers its I/O.
+
+ The implementation uses a (configurable) fixed-size read buffer
+ but buffers all writes until a flush is performed.
+ """
+ DEFAULT_BUFFER = 4096
+
+ def __init__(self, trans, rbuf_size=DEFAULT_BUFFER):
+ self.__trans = trans
+ self.__wbuf = BufferIO()
+ # Pass string argument to initialize read buffer as cStringIO.InputType
+ self.__rbuf = BufferIO(b'')
+ self.__rbuf_size = rbuf_size
+
+ def isOpen(self):
+ return self.__trans.isOpen()
+
+ def open(self):
+ return self.__trans.open()
+
+ def close(self):
+ return self.__trans.close()
+
+ def read(self, sz):
+ ret = self.__rbuf.read(sz)
+ if len(ret) != 0:
+ return ret
+ self.__rbuf = BufferIO(self.__trans.read(max(sz, self.__rbuf_size)))
+ return self.__rbuf.read(sz)
+
+ def write(self, buf):
+ try:
+ self.__wbuf.write(buf)
+ except Exception as e:
+ # on exception reset wbuf so it doesn't contain a partial function call
+ self.__wbuf = BufferIO()
+ raise e
+
+ def flush(self):
+ out = self.__wbuf.getvalue()
+ # reset wbuf before write/flush to preserve state on underlying failure
+ self.__wbuf = BufferIO()
+ self.__trans.write(out)
+ self.__trans.flush()
+
+ # Implement the CReadableTransport interface.
+ @property
+ def cstringio_buf(self):
+ return self.__rbuf
+
+ def cstringio_refill(self, partialread, reqlen):
+ retstring = partialread
+ if reqlen < self.__rbuf_size:
+ # try to make a read of as much as we can.
+ retstring += self.__trans.read(self.__rbuf_size)
+
+ # but make sure we do read reqlen bytes.
+ if len(retstring) < reqlen:
+ retstring += self.__trans.readAll(reqlen - len(retstring))
+
+ self.__rbuf = BufferIO(retstring)
+ return self.__rbuf
+
+
+class TMemoryBuffer(TTransportBase, CReadableTransport):
+ """Wraps a cBytesIO object as a TTransport.
+
+ NOTE: Unlike the C++ version of this class, you cannot write to it
+ then immediately read from it. If you want to read from a
+ TMemoryBuffer, you must either pass a string to the constructor.
+ TODO(dreiss): Make this work like the C++ version.
+ """
+
+ def __init__(self, value=None, offset=0):
+ """value -- a value to read from for stringio
+
+ If value is set, this will be a transport for reading,
+ otherwise, it is for writing"""
+ if value is not None:
+ self._buffer = BufferIO(value)
+ else:
+ self._buffer = BufferIO()
+ if offset:
+ self._buffer.seek(offset)
+
+ def isOpen(self):
+ return not self._buffer.closed
+
+ def open(self):
+ pass
+
+ def close(self):
+ self._buffer.close()
+
+ def read(self, sz):
+ return self._buffer.read(sz)
+
+ def write(self, buf):
+ self._buffer.write(buf)
+
+ def flush(self):
+ pass
+
+ def getvalue(self):
+ return self._buffer.getvalue()
+
+ # Implement the CReadableTransport interface.
+ @property
+ def cstringio_buf(self):
+ return self._buffer
+
+ def cstringio_refill(self, partialread, reqlen):
+ # only one shot at reading...
+ raise EOFError()
+
+
+class TFramedTransportFactory(object):
+ """Factory transport that builds framed transports"""
+
+ def getTransport(self, trans):
+ framed = TFramedTransport(trans)
+ return framed
+
+
+class TFramedTransport(TTransportBase, CReadableTransport):
+ """Class that wraps another transport and frames its I/O when writing."""
+
+ def __init__(self, trans,):
+ self.__trans = trans
+ self.__rbuf = BufferIO(b'')
+ self.__wbuf = BufferIO()
+
+ def isOpen(self):
+ return self.__trans.isOpen()
+
+ def open(self):
+ return self.__trans.open()
+
+ def close(self):
+ return self.__trans.close()
+
+ def read(self, sz):
+ ret = self.__rbuf.read(sz)
+ if len(ret) != 0:
+ return ret
+
+ self.readFrame()
+ return self.__rbuf.read(sz)
+
+ def readFrame(self):
+ buff = self.__trans.readAll(4)
+ sz, = unpack('!i', buff)
+ self.__rbuf = BufferIO(self.__trans.readAll(sz))
+
+ def write(self, buf):
+ self.__wbuf.write(buf)
+
+ def flush(self):
+ wout = self.__wbuf.getvalue()
+ wsz = len(wout)
+ # reset wbuf before write/flush to preserve state on underlying failure
+ self.__wbuf = BufferIO()
+ # N.B.: Doing this string concatenation is WAY cheaper than making
+ # two separate calls to the underlying socket object. Socket writes in
+ # Python turn out to be REALLY expensive, but it seems to do a pretty
+ # good job of managing string buffer operations without excessive copies
+ buf = pack("!i", wsz) + wout
+ self.__trans.write(buf)
+ self.__trans.flush()
+
+ # Implement the CReadableTransport interface.
+ @property
+ def cstringio_buf(self):
+ return self.__rbuf
+
+ def cstringio_refill(self, prefix, reqlen):
+ # self.__rbuf will already be empty here because fastbinary doesn't
+ # ask for a refill until the previous buffer is empty. Therefore,
+ # we can start reading new frames immediately.
+ while len(prefix) < reqlen:
+ self.readFrame()
+ prefix += self.__rbuf.getvalue()
+ self.__rbuf = BufferIO(prefix)
+ return self.__rbuf
+
+
+class TFileObjectTransport(TTransportBase):
+ """Wraps a file-like object to make it work as a Thrift transport."""
+
+ def __init__(self, fileobj):
+ self.fileobj = fileobj
+
+ def isOpen(self):
+ return True
+
+ def close(self):
+ self.fileobj.close()
+
+ def read(self, sz):
+ return self.fileobj.read(sz)
+
+ def write(self, buf):
+ self.fileobj.write(buf)
+
+ def flush(self):
+ self.fileobj.flush()
+
+
+class TSaslClientTransport(TTransportBase, CReadableTransport):
+ """
+ SASL transport
+ """
+
+ START = 1
+ OK = 2
+ BAD = 3
+ ERROR = 4
+ COMPLETE = 5
+
+ def __init__(self, transport, host, service, mechanism='GSSAPI',
+ **sasl_kwargs):
+ """
+ transport: an underlying transport to use, typically just a TSocket
+ host: the name of the server, from a SASL perspective
+ service: the name of the server's service, from a SASL perspective
+ mechanism: the name of the preferred mechanism to use
+
+ All other kwargs will be passed to the puresasl.client.SASLClient
+ constructor.
+ """
+
+ from puresasl.client import SASLClient
+
+ self.transport = transport
+ self.sasl = SASLClient(host, service, mechanism, **sasl_kwargs)
+
+ self.__wbuf = BufferIO()
+ self.__rbuf = BufferIO(b'')
+
+ def open(self):
+ if not self.transport.isOpen():
+ self.transport.open()
+
+ self.send_sasl_msg(self.START, bytes(self.sasl.mechanism, 'ascii'))
+ self.send_sasl_msg(self.OK, self.sasl.process())
+
+ while True:
+ status, challenge = self.recv_sasl_msg()
+ if status == self.OK:
+ self.send_sasl_msg(self.OK, self.sasl.process(challenge))
+ elif status == self.COMPLETE:
+ if not self.sasl.complete:
+ raise TTransportException(
+ TTransportException.NOT_OPEN,
+ "The server erroneously indicated "
+ "that SASL negotiation was complete")
+ else:
+ break
+ else:
+ raise TTransportException(
+ TTransportException.NOT_OPEN,
+ "Bad SASL negotiation status: %d (%s)"
+ % (status, challenge))
+
+ def send_sasl_msg(self, status, body):
+ header = pack(">BI", status, len(body))
+ self.transport.write(header + body)
+ self.transport.flush()
+
+ def recv_sasl_msg(self):
+ header = self.transport.readAll(5)
+ status, length = unpack(">BI", header)
+ if length > 0:
+ payload = self.transport.readAll(length)
+ else:
+ payload = ""
+ return status, payload
+
+ def write(self, data):
+ self.__wbuf.write(data)
+
+ def flush(self):
+ data = self.__wbuf.getvalue()
+ encoded = self.sasl.wrap(data)
+ self.transport.write(pack("!i", len(encoded)) + encoded)
+ self.transport.flush()
+ self.__wbuf = BufferIO()
+
+ def read(self, sz):
+ ret = self.__rbuf.read(sz)
+ if len(ret) != 0:
+ return ret
+
+ self._read_frame()
+ return self.__rbuf.read(sz)
+
+ def _read_frame(self):
+ header = self.transport.readAll(4)
+ length, = unpack('!i', header)
+ encoded = self.transport.readAll(length)
+ self.__rbuf = BufferIO(self.sasl.unwrap(encoded))
+
+ def close(self):
+ self.sasl.dispose()
+ self.transport.close()
+
+ # based on TFramedTransport
+ @property
+ def cstringio_buf(self):
+ return self.__rbuf
+
+ def cstringio_refill(self, prefix, reqlen):
+ # self.__rbuf will already be empty here because fastbinary doesn't
+ # ask for a refill until the previous buffer is empty. Therefore,
+ # we can start reading new frames immediately.
+ while len(prefix) < reqlen:
+ self._read_frame()
+ prefix += self.__rbuf.getvalue()
+ self.__rbuf = BufferIO(prefix)
+ return self.__rbuf
diff --git a/src/jaegertracing/thrift/lib/py/src/transport/TTwisted.py b/src/jaegertracing/thrift/lib/py/src/transport/TTwisted.py
new file mode 100644
index 000000000..a27f0adad
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/transport/TTwisted.py
@@ -0,0 +1,329 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from io import BytesIO
+import struct
+
+from zope.interface import implementer, Interface, Attribute
+from twisted.internet.protocol import ServerFactory, ClientFactory, \
+ connectionDone
+from twisted.internet import defer
+from twisted.internet.threads import deferToThread
+from twisted.protocols import basic
+from twisted.web import server, resource, http
+
+from thrift.transport import TTransport
+
+
+class TMessageSenderTransport(TTransport.TTransportBase):
+
+ def __init__(self):
+ self.__wbuf = BytesIO()
+
+ def write(self, buf):
+ self.__wbuf.write(buf)
+
+ def flush(self):
+ msg = self.__wbuf.getvalue()
+ self.__wbuf = BytesIO()
+ return self.sendMessage(msg)
+
+ def sendMessage(self, message):
+ raise NotImplementedError
+
+
+class TCallbackTransport(TMessageSenderTransport):
+
+ def __init__(self, func):
+ TMessageSenderTransport.__init__(self)
+ self.func = func
+
+ def sendMessage(self, message):
+ return self.func(message)
+
+
+class ThriftClientProtocol(basic.Int32StringReceiver):
+
+ MAX_LENGTH = 2 ** 31 - 1
+
+ def __init__(self, client_class, iprot_factory, oprot_factory=None):
+ self._client_class = client_class
+ self._iprot_factory = iprot_factory
+ if oprot_factory is None:
+ self._oprot_factory = iprot_factory
+ else:
+ self._oprot_factory = oprot_factory
+
+ self.recv_map = {}
+ self.started = defer.Deferred()
+
+ def dispatch(self, msg):
+ self.sendString(msg)
+
+ def connectionMade(self):
+ tmo = TCallbackTransport(self.dispatch)
+ self.client = self._client_class(tmo, self._oprot_factory)
+ self.started.callback(self.client)
+
+ def connectionLost(self, reason=connectionDone):
+ # the called errbacks can add items to our client's _reqs,
+ # so we need to use a tmp, and iterate until no more requests
+ # are added during errbacks
+ if self.client:
+ tex = TTransport.TTransportException(
+ type=TTransport.TTransportException.END_OF_FILE,
+ message='Connection closed (%s)' % reason)
+ while self.client._reqs:
+ _, v = self.client._reqs.popitem()
+ v.errback(tex)
+ del self.client._reqs
+ self.client = None
+
+ def stringReceived(self, frame):
+ tr = TTransport.TMemoryBuffer(frame)
+ iprot = self._iprot_factory.getProtocol(tr)
+ (fname, mtype, rseqid) = iprot.readMessageBegin()
+
+ try:
+ method = self.recv_map[fname]
+ except KeyError:
+ method = getattr(self.client, 'recv_' + fname)
+ self.recv_map[fname] = method
+
+ method(iprot, mtype, rseqid)
+
+
+class ThriftSASLClientProtocol(ThriftClientProtocol):
+
+ START = 1
+ OK = 2
+ BAD = 3
+ ERROR = 4
+ COMPLETE = 5
+
+ MAX_LENGTH = 2 ** 31 - 1
+
+ def __init__(self, client_class, iprot_factory, oprot_factory=None,
+ host=None, service=None, mechanism='GSSAPI', **sasl_kwargs):
+ """
+ host: the name of the server, from a SASL perspective
+ service: the name of the server's service, from a SASL perspective
+ mechanism: the name of the preferred mechanism to use
+
+ All other kwargs will be passed to the puresasl.client.SASLClient
+ constructor.
+ """
+
+ from puresasl.client import SASLClient
+ self.SASLCLient = SASLClient
+
+ ThriftClientProtocol.__init__(self, client_class, iprot_factory, oprot_factory)
+
+ self._sasl_negotiation_deferred = None
+ self._sasl_negotiation_status = None
+ self.client = None
+
+ if host is not None:
+ self.createSASLClient(host, service, mechanism, **sasl_kwargs)
+
+ def createSASLClient(self, host, service, mechanism, **kwargs):
+ self.sasl = self.SASLClient(host, service, mechanism, **kwargs)
+
+ def dispatch(self, msg):
+ encoded = self.sasl.wrap(msg)
+ len_and_encoded = ''.join((struct.pack('!i', len(encoded)), encoded))
+ ThriftClientProtocol.dispatch(self, len_and_encoded)
+
+ @defer.inlineCallbacks
+ def connectionMade(self):
+ self._sendSASLMessage(self.START, self.sasl.mechanism)
+ initial_message = yield deferToThread(self.sasl.process)
+ self._sendSASLMessage(self.OK, initial_message)
+
+ while True:
+ status, challenge = yield self._receiveSASLMessage()
+ if status == self.OK:
+ response = yield deferToThread(self.sasl.process, challenge)
+ self._sendSASLMessage(self.OK, response)
+ elif status == self.COMPLETE:
+ if not self.sasl.complete:
+ msg = "The server erroneously indicated that SASL " \
+ "negotiation was complete"
+ raise TTransport.TTransportException(msg, message=msg)
+ else:
+ break
+ else:
+ msg = "Bad SASL negotiation status: %d (%s)" % (status, challenge)
+ raise TTransport.TTransportException(msg, message=msg)
+
+ self._sasl_negotiation_deferred = None
+ ThriftClientProtocol.connectionMade(self)
+
+ def _sendSASLMessage(self, status, body):
+ if body is None:
+ body = ""
+ header = struct.pack(">BI", status, len(body))
+ self.transport.write(header + body)
+
+ def _receiveSASLMessage(self):
+ self._sasl_negotiation_deferred = defer.Deferred()
+ self._sasl_negotiation_status = None
+ return self._sasl_negotiation_deferred
+
+ def connectionLost(self, reason=connectionDone):
+ if self.client:
+ ThriftClientProtocol.connectionLost(self, reason)
+
+ def dataReceived(self, data):
+ if self._sasl_negotiation_deferred:
+ # we got a sasl challenge in the format (status, length, challenge)
+ # save the status, let IntNStringReceiver piece the challenge data together
+ self._sasl_negotiation_status, = struct.unpack("B", data[0])
+ ThriftClientProtocol.dataReceived(self, data[1:])
+ else:
+ # normal frame, let IntNStringReceiver piece it together
+ ThriftClientProtocol.dataReceived(self, data)
+
+ def stringReceived(self, frame):
+ if self._sasl_negotiation_deferred:
+ # the frame is just a SASL challenge
+ response = (self._sasl_negotiation_status, frame)
+ self._sasl_negotiation_deferred.callback(response)
+ else:
+ # there's a second 4 byte length prefix inside the frame
+ decoded_frame = self.sasl.unwrap(frame[4:])
+ ThriftClientProtocol.stringReceived(self, decoded_frame)
+
+
+class ThriftServerProtocol(basic.Int32StringReceiver):
+
+ MAX_LENGTH = 2 ** 31 - 1
+
+ def dispatch(self, msg):
+ self.sendString(msg)
+
+ def processError(self, error):
+ self.transport.loseConnection()
+
+ def processOk(self, _, tmo):
+ msg = tmo.getvalue()
+
+ if len(msg) > 0:
+ self.dispatch(msg)
+
+ def stringReceived(self, frame):
+ tmi = TTransport.TMemoryBuffer(frame)
+ tmo = TTransport.TMemoryBuffer()
+
+ iprot = self.factory.iprot_factory.getProtocol(tmi)
+ oprot = self.factory.oprot_factory.getProtocol(tmo)
+
+ d = self.factory.processor.process(iprot, oprot)
+ d.addCallbacks(self.processOk, self.processError,
+ callbackArgs=(tmo,))
+
+
+class IThriftServerFactory(Interface):
+
+ processor = Attribute("Thrift processor")
+
+ iprot_factory = Attribute("Input protocol factory")
+
+ oprot_factory = Attribute("Output protocol factory")
+
+
+class IThriftClientFactory(Interface):
+
+ client_class = Attribute("Thrift client class")
+
+ iprot_factory = Attribute("Input protocol factory")
+
+ oprot_factory = Attribute("Output protocol factory")
+
+
+@implementer(IThriftServerFactory)
+class ThriftServerFactory(ServerFactory):
+
+ protocol = ThriftServerProtocol
+
+ def __init__(self, processor, iprot_factory, oprot_factory=None):
+ self.processor = processor
+ self.iprot_factory = iprot_factory
+ if oprot_factory is None:
+ self.oprot_factory = iprot_factory
+ else:
+ self.oprot_factory = oprot_factory
+
+
+@implementer(IThriftClientFactory)
+class ThriftClientFactory(ClientFactory):
+
+ protocol = ThriftClientProtocol
+
+ def __init__(self, client_class, iprot_factory, oprot_factory=None):
+ self.client_class = client_class
+ self.iprot_factory = iprot_factory
+ if oprot_factory is None:
+ self.oprot_factory = iprot_factory
+ else:
+ self.oprot_factory = oprot_factory
+
+ def buildProtocol(self, addr):
+ p = self.protocol(self.client_class, self.iprot_factory,
+ self.oprot_factory)
+ p.factory = self
+ return p
+
+
+class ThriftResource(resource.Resource):
+
+ allowedMethods = ('POST',)
+
+ def __init__(self, processor, inputProtocolFactory,
+ outputProtocolFactory=None):
+ resource.Resource.__init__(self)
+ self.inputProtocolFactory = inputProtocolFactory
+ if outputProtocolFactory is None:
+ self.outputProtocolFactory = inputProtocolFactory
+ else:
+ self.outputProtocolFactory = outputProtocolFactory
+ self.processor = processor
+
+ def getChild(self, path, request):
+ return self
+
+ def _cbProcess(self, _, request, tmo):
+ msg = tmo.getvalue()
+ request.setResponseCode(http.OK)
+ request.setHeader("content-type", "application/x-thrift")
+ request.write(msg)
+ request.finish()
+
+ def render_POST(self, request):
+ request.content.seek(0, 0)
+ data = request.content.read()
+ tmi = TTransport.TMemoryBuffer(data)
+ tmo = TTransport.TMemoryBuffer()
+
+ iprot = self.inputProtocolFactory.getProtocol(tmi)
+ oprot = self.outputProtocolFactory.getProtocol(tmo)
+
+ d = self.processor.process(iprot, oprot)
+ d.addCallback(self._cbProcess, request, tmo)
+ return server.NOT_DONE_YET
diff --git a/src/jaegertracing/thrift/lib/py/src/transport/TZlibTransport.py b/src/jaegertracing/thrift/lib/py/src/transport/TZlibTransport.py
new file mode 100644
index 000000000..e84857924
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/transport/TZlibTransport.py
@@ -0,0 +1,248 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+"""TZlibTransport provides a compressed transport and transport factory
+class, using the python standard library zlib module to implement
+data compression.
+"""
+
+from __future__ import division
+import zlib
+from .TTransport import TTransportBase, CReadableTransport
+from ..compat import BufferIO
+
+
+class TZlibTransportFactory(object):
+ """Factory transport that builds zlib compressed transports.
+
+ This factory caches the last single client/transport that it was passed
+ and returns the same TZlibTransport object that was created.
+
+ This caching means the TServer class will get the _same_ transport
+ object for both input and output transports from this factory.
+ (For non-threaded scenarios only, since the cache only holds one object)
+
+ The purpose of this caching is to allocate only one TZlibTransport where
+ only one is really needed (since it must have separate read/write buffers),
+ and makes the statistics from getCompSavings() and getCompRatio()
+ easier to understand.
+ """
+ # class scoped cache of last transport given and zlibtransport returned
+ _last_trans = None
+ _last_z = None
+
+ def getTransport(self, trans, compresslevel=9):
+ """Wrap a transport, trans, with the TZlibTransport
+ compressed transport class, returning a new
+ transport to the caller.
+
+ @param compresslevel: The zlib compression level, ranging
+ from 0 (no compression) to 9 (best compression). Defaults to 9.
+ @type compresslevel: int
+
+ This method returns a TZlibTransport which wraps the
+ passed C{trans} TTransport derived instance.
+ """
+ if trans == self._last_trans:
+ return self._last_z
+ ztrans = TZlibTransport(trans, compresslevel)
+ self._last_trans = trans
+ self._last_z = ztrans
+ return ztrans
+
+
+class TZlibTransport(TTransportBase, CReadableTransport):
+ """Class that wraps a transport with zlib, compressing writes
+ and decompresses reads, using the python standard
+ library zlib module.
+ """
+ # Read buffer size for the python fastbinary C extension,
+ # the TBinaryProtocolAccelerated class.
+ DEFAULT_BUFFSIZE = 4096
+
+ def __init__(self, trans, compresslevel=9):
+ """Create a new TZlibTransport, wrapping C{trans}, another
+ TTransport derived object.
+
+ @param trans: A thrift transport object, i.e. a TSocket() object.
+ @type trans: TTransport
+ @param compresslevel: The zlib compression level, ranging
+ from 0 (no compression) to 9 (best compression). Default is 9.
+ @type compresslevel: int
+ """
+ self.__trans = trans
+ self.compresslevel = compresslevel
+ self.__rbuf = BufferIO()
+ self.__wbuf = BufferIO()
+ self._init_zlib()
+ self._init_stats()
+
+ def _reinit_buffers(self):
+ """Internal method to initialize/reset the internal StringIO objects
+ for read and write buffers.
+ """
+ self.__rbuf = BufferIO()
+ self.__wbuf = BufferIO()
+
+ def _init_stats(self):
+ """Internal method to reset the internal statistics counters
+ for compression ratios and bandwidth savings.
+ """
+ self.bytes_in = 0
+ self.bytes_out = 0
+ self.bytes_in_comp = 0
+ self.bytes_out_comp = 0
+
+ def _init_zlib(self):
+ """Internal method for setting up the zlib compression and
+ decompression objects.
+ """
+ self._zcomp_read = zlib.decompressobj()
+ self._zcomp_write = zlib.compressobj(self.compresslevel)
+
+ def getCompRatio(self):
+ """Get the current measured compression ratios (in,out) from
+ this transport.
+
+ Returns a tuple of:
+ (inbound_compression_ratio, outbound_compression_ratio)
+
+ The compression ratios are computed as:
+ compressed / uncompressed
+
+ E.g., data that compresses by 10x will have a ratio of: 0.10
+ and data that compresses to half of ts original size will
+ have a ratio of 0.5
+
+ None is returned if no bytes have yet been processed in
+ a particular direction.
+ """
+ r_percent, w_percent = (None, None)
+ if self.bytes_in > 0:
+ r_percent = self.bytes_in_comp / self.bytes_in
+ if self.bytes_out > 0:
+ w_percent = self.bytes_out_comp / self.bytes_out
+ return (r_percent, w_percent)
+
+ def getCompSavings(self):
+ """Get the current count of saved bytes due to data
+ compression.
+
+ Returns a tuple of:
+ (inbound_saved_bytes, outbound_saved_bytes)
+
+ Note: if compression is actually expanding your
+ data (only likely with very tiny thrift objects), then
+ the values returned will be negative.
+ """
+ r_saved = self.bytes_in - self.bytes_in_comp
+ w_saved = self.bytes_out - self.bytes_out_comp
+ return (r_saved, w_saved)
+
+ def isOpen(self):
+ """Return the underlying transport's open status"""
+ return self.__trans.isOpen()
+
+ def open(self):
+ """Open the underlying transport"""
+ self._init_stats()
+ return self.__trans.open()
+
+ def listen(self):
+ """Invoke the underlying transport's listen() method"""
+ self.__trans.listen()
+
+ def accept(self):
+ """Accept connections on the underlying transport"""
+ return self.__trans.accept()
+
+ def close(self):
+ """Close the underlying transport,"""
+ self._reinit_buffers()
+ self._init_zlib()
+ return self.__trans.close()
+
+ def read(self, sz):
+ """Read up to sz bytes from the decompressed bytes buffer, and
+ read from the underlying transport if the decompression
+ buffer is empty.
+ """
+ ret = self.__rbuf.read(sz)
+ if len(ret) > 0:
+ return ret
+ # keep reading from transport until something comes back
+ while True:
+ if self.readComp(sz):
+ break
+ ret = self.__rbuf.read(sz)
+ return ret
+
+ def readComp(self, sz):
+ """Read compressed data from the underlying transport, then
+ decompress it and append it to the internal StringIO read buffer
+ """
+ zbuf = self.__trans.read(sz)
+ zbuf = self._zcomp_read.unconsumed_tail + zbuf
+ buf = self._zcomp_read.decompress(zbuf)
+ self.bytes_in += len(zbuf)
+ self.bytes_in_comp += len(buf)
+ old = self.__rbuf.read()
+ self.__rbuf = BufferIO(old + buf)
+ if len(old) + len(buf) == 0:
+ return False
+ return True
+
+ def write(self, buf):
+ """Write some bytes, putting them into the internal write
+ buffer for eventual compression.
+ """
+ self.__wbuf.write(buf)
+
+ def flush(self):
+ """Flush any queued up data in the write buffer and ensure the
+ compression buffer is flushed out to the underlying transport
+ """
+ wout = self.__wbuf.getvalue()
+ if len(wout) > 0:
+ zbuf = self._zcomp_write.compress(wout)
+ self.bytes_out += len(wout)
+ self.bytes_out_comp += len(zbuf)
+ else:
+ zbuf = ''
+ ztail = self._zcomp_write.flush(zlib.Z_SYNC_FLUSH)
+ self.bytes_out_comp += len(ztail)
+ if (len(zbuf) + len(ztail)) > 0:
+ self.__wbuf = BufferIO()
+ self.__trans.write(zbuf + ztail)
+ self.__trans.flush()
+
+ @property
+ def cstringio_buf(self):
+ """Implement the CReadableTransport interface"""
+ return self.__rbuf
+
+ def cstringio_refill(self, partialread, reqlen):
+ """Implement the CReadableTransport interface for refill"""
+ retstring = partialread
+ if reqlen < self.DEFAULT_BUFFSIZE:
+ retstring += self.read(self.DEFAULT_BUFFSIZE)
+ while len(retstring) < reqlen:
+ retstring += self.read(reqlen - len(retstring))
+ self.__rbuf = BufferIO(retstring)
+ return self.__rbuf
diff --git a/src/jaegertracing/thrift/lib/py/src/transport/__init__.py b/src/jaegertracing/thrift/lib/py/src/transport/__init__.py
new file mode 100644
index 000000000..c9596d9a6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/transport/__init__.py
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+__all__ = ['TTransport', 'TSocket', 'THttpClient', 'TZlibTransport']
diff --git a/src/jaegertracing/thrift/lib/py/src/transport/sslcompat.py b/src/jaegertracing/thrift/lib/py/src/transport/sslcompat.py
new file mode 100644
index 000000000..ab00cb2a8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/src/transport/sslcompat.py
@@ -0,0 +1,100 @@
+#
+# licensed to the apache software foundation (asf) under one
+# or more contributor license agreements. see the notice file
+# distributed with this work for additional information
+# regarding copyright ownership. the asf licenses this file
+# to you under the apache license, version 2.0 (the
+# "license"); you may not use this file except in compliance
+# with the license. you may obtain a copy of the license at
+#
+# http://www.apache.org/licenses/license-2.0
+#
+# unless required by applicable law or agreed to in writing,
+# software distributed under the license is distributed on an
+# "as is" basis, without warranties or conditions of any
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import logging
+import sys
+
+from thrift.transport.TTransport import TTransportException
+
+logger = logging.getLogger(__name__)
+
+
+def legacy_validate_callback(cert, hostname):
+ """legacy method to validate the peer's SSL certificate, and to check
+ the commonName of the certificate to ensure it matches the hostname we
+ used to make this connection. Does not support subjectAltName records
+ in certificates.
+
+ raises TTransportException if the certificate fails validation.
+ """
+ if 'subject' not in cert:
+ raise TTransportException(
+ TTransportException.NOT_OPEN,
+ 'No SSL certificate found from %s' % hostname)
+ fields = cert['subject']
+ for field in fields:
+ # ensure structure we get back is what we expect
+ if not isinstance(field, tuple):
+ continue
+ cert_pair = field[0]
+ if len(cert_pair) < 2:
+ continue
+ cert_key, cert_value = cert_pair[0:2]
+ if cert_key != 'commonName':
+ continue
+ certhost = cert_value
+ # this check should be performed by some sort of Access Manager
+ if certhost == hostname:
+ # success, cert commonName matches desired hostname
+ return
+ else:
+ raise TTransportException(
+ TTransportException.UNKNOWN,
+ 'Hostname we connected to "%s" doesn\'t match certificate '
+ 'provided commonName "%s"' % (hostname, certhost))
+ raise TTransportException(
+ TTransportException.UNKNOWN,
+ 'Could not validate SSL certificate from host "%s". Cert=%s'
+ % (hostname, cert))
+
+
+def _optional_dependencies():
+ try:
+ import ipaddress # noqa
+ logger.debug('ipaddress module is available')
+ ipaddr = True
+ except ImportError:
+ logger.warn('ipaddress module is unavailable')
+ ipaddr = False
+
+ if sys.hexversion < 0x030500F0:
+ try:
+ from backports.ssl_match_hostname import match_hostname, __version__ as ver
+ ver = list(map(int, ver.split('.')))
+ logger.debug('backports.ssl_match_hostname module is available')
+ match = match_hostname
+ if ver[0] * 10 + ver[1] >= 35:
+ return ipaddr, match
+ else:
+ logger.warn('backports.ssl_match_hostname module is too old')
+ ipaddr = False
+ except ImportError:
+ logger.warn('backports.ssl_match_hostname is unavailable')
+ ipaddr = False
+ try:
+ from ssl import match_hostname
+ logger.debug('ssl.match_hostname is available')
+ match = match_hostname
+ except ImportError:
+ logger.warn('using legacy validation callback')
+ match = legacy_validate_callback
+ return ipaddr, match
+
+
+_match_has_ipaddress, _match_hostname = _optional_dependencies()
diff --git a/src/jaegertracing/thrift/lib/py/test/_import_local_thrift.py b/src/jaegertracing/thrift/lib/py/test/_import_local_thrift.py
new file mode 100644
index 000000000..d22312298
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/test/_import_local_thrift.py
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import glob
+import os
+import sys
+
+SCRIPT_DIR = os.path.realpath(os.path.dirname(__file__))
+ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(SCRIPT_DIR)))
+
+for libpath in glob.glob(os.path.join(ROOT_DIR, 'lib', 'py', 'build', 'lib.*')):
+ if libpath.endswith('-%d.%d' % (sys.version_info[0], sys.version_info[1])):
+ sys.path.insert(0, libpath)
+ break
diff --git a/src/jaegertracing/thrift/lib/py/test/test_sslsocket.py b/src/jaegertracing/thrift/lib/py/test/test_sslsocket.py
new file mode 100644
index 000000000..f4c87f195
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/test/test_sslsocket.py
@@ -0,0 +1,353 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import inspect
+import logging
+import os
+import platform
+import ssl
+import sys
+import tempfile
+import threading
+import unittest
+import warnings
+from contextlib import contextmanager
+
+import _import_local_thrift # noqa
+
+SCRIPT_DIR = os.path.realpath(os.path.dirname(__file__))
+ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(SCRIPT_DIR)))
+SERVER_PEM = os.path.join(ROOT_DIR, 'test', 'keys', 'server.pem')
+SERVER_CERT = os.path.join(ROOT_DIR, 'test', 'keys', 'server.crt')
+SERVER_KEY = os.path.join(ROOT_DIR, 'test', 'keys', 'server.key')
+CLIENT_CERT_NO_IP = os.path.join(ROOT_DIR, 'test', 'keys', 'client.crt')
+CLIENT_KEY_NO_IP = os.path.join(ROOT_DIR, 'test', 'keys', 'client.key')
+CLIENT_CERT = os.path.join(ROOT_DIR, 'test', 'keys', 'client_v3.crt')
+CLIENT_KEY = os.path.join(ROOT_DIR, 'test', 'keys', 'client_v3.key')
+CLIENT_CA = os.path.join(ROOT_DIR, 'test', 'keys', 'CA.pem')
+
+TEST_CIPHERS = 'DES-CBC3-SHA:ECDHE-RSA-AES128-GCM-SHA256'
+
+
+class ServerAcceptor(threading.Thread):
+ def __init__(self, server, expect_failure=False):
+ super(ServerAcceptor, self).__init__()
+ self.daemon = True
+ self._server = server
+ self._listening = threading.Event()
+ self._port = None
+ self._port_bound = threading.Event()
+ self._client = None
+ self._client_accepted = threading.Event()
+ self._expect_failure = expect_failure
+ frame = inspect.stack(3)[2]
+ self.name = frame[3]
+ del frame
+
+ def run(self):
+ self._server.listen()
+ self._listening.set()
+
+ try:
+ address = self._server.handle.getsockname()
+ if len(address) > 1:
+ # AF_INET addresses are 2-tuples (host, port) and AF_INET6 are
+ # 4-tuples (host, port, ...), but in each case port is in the second slot.
+ self._port = address[1]
+ finally:
+ self._port_bound.set()
+
+ try:
+ self._client = self._server.accept()
+ if self._client:
+ self._client.read(5) # hello
+ self._client.write(b"there")
+ except Exception:
+ logging.exception('error on server side (%s):' % self.name)
+ if not self._expect_failure:
+ raise
+ finally:
+ self._client_accepted.set()
+
+ def await_listening(self):
+ self._listening.wait()
+
+ @property
+ def port(self):
+ self._port_bound.wait()
+ return self._port
+
+ @property
+ def client(self):
+ self._client_accepted.wait()
+ return self._client
+
+ def close(self):
+ if self._client:
+ self._client.close()
+ self._server.close()
+
+
+# Python 2.6 compat
+class AssertRaises(object):
+ def __init__(self, expected):
+ self._expected = expected
+
+ def __enter__(self):
+ pass
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ if not exc_type or not issubclass(exc_type, self._expected):
+ raise Exception('fail')
+ return True
+
+
+class TSSLSocketTest(unittest.TestCase):
+ def _server_socket(self, **kwargs):
+ return TSSLServerSocket(port=0, **kwargs)
+
+ @contextmanager
+ def _connectable_client(self, server, expect_failure=False, path=None, **client_kwargs):
+ acc = ServerAcceptor(server, expect_failure)
+ try:
+ acc.start()
+ acc.await_listening()
+
+ host, port = ('localhost', acc.port) if path is None else (None, None)
+ client = TSSLSocket(host, port, unix_socket=path, **client_kwargs)
+ yield acc, client
+ finally:
+ acc.close()
+
+ def _assert_connection_failure(self, server, path=None, **client_args):
+ logging.disable(logging.CRITICAL)
+ try:
+ with self._connectable_client(server, True, path=path, **client_args) as (acc, client):
+ # We need to wait for a connection failure, but not too long. 20ms is a tunable
+ # compromise between test speed and stability
+ client.setTimeout(20)
+ with self._assert_raises(TTransportException):
+ client.open()
+ client.write(b"hello")
+ client.read(5) # b"there"
+ finally:
+ logging.disable(logging.NOTSET)
+
+ def _assert_raises(self, exc):
+ if sys.hexversion >= 0x020700F0:
+ return self.assertRaises(exc)
+ else:
+ return AssertRaises(exc)
+
+ def _assert_connection_success(self, server, path=None, **client_args):
+ with self._connectable_client(server, path=path, **client_args) as (acc, client):
+ try:
+ client.open()
+ client.write(b"hello")
+ self.assertEqual(client.read(5), b"there")
+ self.assertTrue(acc.client is not None)
+ finally:
+ client.close()
+
+ # deprecated feature
+ def test_deprecation(self):
+ with warnings.catch_warnings(record=True) as w:
+ warnings.filterwarnings('always', category=DeprecationWarning, module=self.__module__)
+ TSSLSocket('localhost', 0, validate=True, ca_certs=SERVER_CERT)
+ self.assertEqual(len(w), 1)
+
+ with warnings.catch_warnings(record=True) as w:
+ warnings.filterwarnings('always', category=DeprecationWarning, module=self.__module__)
+ # Deprecated signature
+ # def __init__(self, host='localhost', port=9090, validate=True, ca_certs=None, keyfile=None, certfile=None, unix_socket=None, ciphers=None):
+ TSSLSocket('localhost', 0, True, SERVER_CERT, CLIENT_KEY, CLIENT_CERT, None, TEST_CIPHERS)
+ self.assertEqual(len(w), 7)
+
+ with warnings.catch_warnings(record=True) as w:
+ warnings.filterwarnings('always', category=DeprecationWarning, module=self.__module__)
+ # Deprecated signature
+ # def __init__(self, host=None, port=9090, certfile='cert.pem', unix_socket=None, ciphers=None):
+ TSSLServerSocket(None, 0, SERVER_PEM, None, TEST_CIPHERS)
+ self.assertEqual(len(w), 3)
+
+ # deprecated feature
+ def test_set_cert_reqs_by_validate(self):
+ with warnings.catch_warnings(record=True) as w:
+ warnings.filterwarnings('always', category=DeprecationWarning, module=self.__module__)
+ c1 = TSSLSocket('localhost', 0, validate=True, ca_certs=SERVER_CERT)
+ self.assertEqual(c1.cert_reqs, ssl.CERT_REQUIRED)
+
+ c1 = TSSLSocket('localhost', 0, validate=False)
+ self.assertEqual(c1.cert_reqs, ssl.CERT_NONE)
+
+ self.assertEqual(len(w), 2)
+
+ # deprecated feature
+ def test_set_validate_by_cert_reqs(self):
+ with warnings.catch_warnings(record=True) as w:
+ warnings.filterwarnings('always', category=DeprecationWarning, module=self.__module__)
+ c1 = TSSLSocket('localhost', 0, cert_reqs=ssl.CERT_NONE)
+ self.assertFalse(c1.validate)
+
+ c2 = TSSLSocket('localhost', 0, cert_reqs=ssl.CERT_REQUIRED, ca_certs=SERVER_CERT)
+ self.assertTrue(c2.validate)
+
+ c3 = TSSLSocket('localhost', 0, cert_reqs=ssl.CERT_OPTIONAL, ca_certs=SERVER_CERT)
+ self.assertTrue(c3.validate)
+
+ self.assertEqual(len(w), 3)
+
+ def test_unix_domain_socket(self):
+ if platform.system() == 'Windows':
+ print('skipping test_unix_domain_socket')
+ return
+ fd, path = tempfile.mkstemp()
+ os.close(fd)
+ os.unlink(path)
+ try:
+ server = self._server_socket(unix_socket=path, keyfile=SERVER_KEY, certfile=SERVER_CERT)
+ self._assert_connection_success(server, path=path, cert_reqs=ssl.CERT_NONE)
+ finally:
+ os.unlink(path)
+
+ def test_server_cert(self):
+ server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT)
+ self._assert_connection_success(server, cert_reqs=ssl.CERT_REQUIRED, ca_certs=SERVER_CERT)
+
+ server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT)
+ # server cert not in ca_certs
+ self._assert_connection_failure(server, cert_reqs=ssl.CERT_REQUIRED, ca_certs=CLIENT_CERT)
+
+ server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT)
+ self._assert_connection_success(server, cert_reqs=ssl.CERT_NONE)
+
+ def test_set_server_cert(self):
+ server = self._server_socket(keyfile=SERVER_KEY, certfile=CLIENT_CERT)
+ with self._assert_raises(Exception):
+ server.certfile = 'foo'
+ with self._assert_raises(Exception):
+ server.certfile = None
+ server.certfile = SERVER_CERT
+ self._assert_connection_success(server, cert_reqs=ssl.CERT_REQUIRED, ca_certs=SERVER_CERT)
+
+ def test_client_cert(self):
+ if not _match_has_ipaddress:
+ print('skipping test_client_cert')
+ return
+ server = self._server_socket(
+ cert_reqs=ssl.CERT_REQUIRED, keyfile=SERVER_KEY,
+ certfile=SERVER_CERT, ca_certs=CLIENT_CERT)
+ self._assert_connection_failure(server, cert_reqs=ssl.CERT_NONE, certfile=SERVER_CERT, keyfile=SERVER_KEY)
+
+ server = self._server_socket(
+ cert_reqs=ssl.CERT_REQUIRED, keyfile=SERVER_KEY,
+ certfile=SERVER_CERT, ca_certs=CLIENT_CA)
+ self._assert_connection_failure(server, cert_reqs=ssl.CERT_NONE, certfile=CLIENT_CERT_NO_IP, keyfile=CLIENT_KEY_NO_IP)
+
+ server = self._server_socket(
+ cert_reqs=ssl.CERT_REQUIRED, keyfile=SERVER_KEY,
+ certfile=SERVER_CERT, ca_certs=CLIENT_CA)
+ self._assert_connection_success(server, cert_reqs=ssl.CERT_NONE, certfile=CLIENT_CERT, keyfile=CLIENT_KEY)
+
+ server = self._server_socket(
+ cert_reqs=ssl.CERT_OPTIONAL, keyfile=SERVER_KEY,
+ certfile=SERVER_CERT, ca_certs=CLIENT_CA)
+ self._assert_connection_success(server, cert_reqs=ssl.CERT_NONE, certfile=CLIENT_CERT, keyfile=CLIENT_KEY)
+
+ def test_ciphers(self):
+ server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT, ciphers=TEST_CIPHERS)
+ self._assert_connection_success(server, ca_certs=SERVER_CERT, ciphers=TEST_CIPHERS)
+
+ if not TSSLSocket._has_ciphers:
+ # unittest.skip is not available for Python 2.6
+ print('skipping test_ciphers')
+ return
+ server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT)
+ self._assert_connection_failure(server, ca_certs=SERVER_CERT, ciphers='NULL')
+
+ server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT, ciphers=TEST_CIPHERS)
+ self._assert_connection_failure(server, ca_certs=SERVER_CERT, ciphers='NULL')
+
+ def test_ssl2_and_ssl3_disabled(self):
+ if not hasattr(ssl, 'PROTOCOL_SSLv3'):
+ print('PROTOCOL_SSLv3 is not available')
+ else:
+ server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT)
+ self._assert_connection_failure(server, ca_certs=SERVER_CERT, ssl_version=ssl.PROTOCOL_SSLv3)
+
+ server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT, ssl_version=ssl.PROTOCOL_SSLv3)
+ self._assert_connection_failure(server, ca_certs=SERVER_CERT)
+
+ if not hasattr(ssl, 'PROTOCOL_SSLv2'):
+ print('PROTOCOL_SSLv2 is not available')
+ else:
+ server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT)
+ self._assert_connection_failure(server, ca_certs=SERVER_CERT, ssl_version=ssl.PROTOCOL_SSLv2)
+
+ server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT, ssl_version=ssl.PROTOCOL_SSLv2)
+ self._assert_connection_failure(server, ca_certs=SERVER_CERT)
+
+ def test_newer_tls(self):
+ if not TSSLSocket._has_ssl_context:
+ # unittest.skip is not available for Python 2.6
+ print('skipping test_newer_tls')
+ return
+ if not hasattr(ssl, 'PROTOCOL_TLSv1_2'):
+ print('PROTOCOL_TLSv1_2 is not available')
+ else:
+ server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT, ssl_version=ssl.PROTOCOL_TLSv1_2)
+ self._assert_connection_success(server, ca_certs=SERVER_CERT, ssl_version=ssl.PROTOCOL_TLSv1_2)
+
+ if not hasattr(ssl, 'PROTOCOL_TLSv1_1'):
+ print('PROTOCOL_TLSv1_1 is not available')
+ else:
+ server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT, ssl_version=ssl.PROTOCOL_TLSv1_1)
+ self._assert_connection_success(server, ca_certs=SERVER_CERT, ssl_version=ssl.PROTOCOL_TLSv1_1)
+
+ if not hasattr(ssl, 'PROTOCOL_TLSv1_1') or not hasattr(ssl, 'PROTOCOL_TLSv1_2'):
+ print('PROTOCOL_TLSv1_1 and/or PROTOCOL_TLSv1_2 is not available')
+ else:
+ server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT, ssl_version=ssl.PROTOCOL_TLSv1_2)
+ self._assert_connection_failure(server, ca_certs=SERVER_CERT, ssl_version=ssl.PROTOCOL_TLSv1_1)
+
+ def test_ssl_context(self):
+ if not TSSLSocket._has_ssl_context:
+ # unittest.skip is not available for Python 2.6
+ print('skipping test_ssl_context')
+ return
+ server_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
+ server_context.load_cert_chain(SERVER_CERT, SERVER_KEY)
+ server_context.load_verify_locations(CLIENT_CA)
+ server_context.verify_mode = ssl.CERT_REQUIRED
+ server = self._server_socket(ssl_context=server_context)
+
+ client_context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
+ client_context.load_cert_chain(CLIENT_CERT, CLIENT_KEY)
+ client_context.load_verify_locations(SERVER_CERT)
+ client_context.verify_mode = ssl.CERT_REQUIRED
+
+ self._assert_connection_success(server, ssl_context=client_context)
+
+
+if __name__ == '__main__':
+ logging.basicConfig(level=logging.WARN)
+ from thrift.transport.TSSLSocket import TSSLSocket, TSSLServerSocket, _match_has_ipaddress
+ from thrift.transport.TTransport import TTransportException
+
+ unittest.main()
diff --git a/src/jaegertracing/thrift/lib/py/test/thrift_json.py b/src/jaegertracing/thrift/lib/py/test/thrift_json.py
new file mode 100644
index 000000000..40e7a47e3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/py/test/thrift_json.py
@@ -0,0 +1,51 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import sys
+import unittest
+
+import _import_local_thrift # noqa
+from thrift.protocol.TJSONProtocol import TJSONProtocol
+from thrift.transport import TTransport
+
+#
+# In order to run the test under Windows. We need to create symbolic link
+# name 'thrift' to '../src' folder by using:
+#
+# mklink /D thrift ..\src
+#
+
+
+class TestJSONString(unittest.TestCase):
+
+ def test_escaped_unicode_string(self):
+ unicode_json = b'"hello \\u0e01\\u0e02\\u0e03\\ud835\\udcab\\udb40\\udc70 unicode"'
+ unicode_text = u'hello \u0e01\u0e02\u0e03\U0001D4AB\U000E0070 unicode'
+
+ buf = TTransport.TMemoryBuffer(unicode_json)
+ transport = TTransport.TBufferedTransportFactory().getTransport(buf)
+ protocol = TJSONProtocol(transport)
+
+ if sys.version_info[0] == 2:
+ unicode_text = unicode_text.encode('utf8')
+ self.assertEqual(protocol.readString(), unicode_text)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/jaegertracing/thrift/lib/rb/Gemfile b/src/jaegertracing/thrift/lib/rb/Gemfile
new file mode 100644
index 000000000..1c86af95d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/Gemfile
@@ -0,0 +1,4 @@
+source "http://rubygems.org"
+
+gemspec
+
diff --git a/src/jaegertracing/thrift/lib/rb/Makefile.am b/src/jaegertracing/thrift/lib/rb/Makefile.am
new file mode 100755
index 000000000..1841065f5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/Makefile.am
@@ -0,0 +1,55 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+DESTDIR ?= /
+
+if HAVE_BUNDLER
+
+all-local:
+ $(BUNDLER) install
+ $(BUNDLER) exec rake build_ext
+
+install-exec-hook:
+ $(BUNDLER) exec rake install
+
+clean-local:
+ $(BUNDLER) install
+ $(BUNDLER) exec rake clean
+ $(RM) -r spec/gen-rb/
+
+check-local: all
+ $(BUNDLER) install
+ $(BUNDLER) exec rake
+
+endif
+
+dist-hook:
+ $(RM) -r $(distdir)/spec/gen-rb/
+
+EXTRA_DIST = \
+ coding_standards.md \
+ Rakefile \
+ Gemfile \
+ thrift.gemspec \
+ lib \
+ ext \
+ benchmark \
+ script \
+ spec \
+ README.md
diff --git a/src/jaegertracing/thrift/lib/rb/README.md b/src/jaegertracing/thrift/lib/rb/README.md
new file mode 100644
index 000000000..b6e9a0792
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/README.md
@@ -0,0 +1,43 @@
+Thrift Ruby Software Library
+ http://thrift.apache.org
+
+== LICENSE:
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+== DESCRIPTION:
+
+Thrift is a strongly-typed language-agnostic RPC system.
+This library is the ruby implementation for both clients and servers.
+
+== INSTALL:
+
+ $ gem install thrift
+
+== CAVEATS:
+
+This library provides the client and server implementations of thrift.
+It does <em>not</em> provide the compiler for the .thrift files. To compile
+.thrift files into language-specific implementations, please download the full
+thrift software package.
+
+== USAGE:
+
+This section should get written by someone with the time and inclination.
+In the meantime, look at existing code, such as the benchmark or the tutorial
+in the full thrift distribution.
diff --git a/src/jaegertracing/thrift/lib/rb/Rakefile b/src/jaegertracing/thrift/lib/rb/Rakefile
new file mode 100644
index 000000000..5e5e5acaa
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/Rakefile
@@ -0,0 +1,120 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'rubygems'
+require 'rake'
+require 'rake/clean'
+require 'rspec/core/rake_task'
+
+THRIFT = '../../compiler/cpp/thrift'
+
+task :default => [:gem]
+task :spec => [:'gen-rb', :build_ext, :realspec]
+
+RSpec::Core::RakeTask.new(:realspec) do |t|
+ t.rspec_opts = ['--color', '--format d']
+end
+
+RSpec::Core::RakeTask.new(:'spec:rcov') do |t|
+ t.rspec_opts = ['--color', '--format d']
+ t.rcov = true
+ t.rcov_opts = ['--exclude', '^spec,/gems/']
+end
+
+desc 'Compile the .thrift files for the specs'
+task :'gen-rb' => [:'gen-rb:spec', :'gen-rb:namespaced_spec', :'gen-rb:flat_spec', :'gen-rb:benchmark', :'gen-rb:debug_proto']
+namespace :'gen-rb' do
+ task :'spec' do
+ dir = File.dirname(__FILE__) + '/spec'
+ sh THRIFT, '--gen', 'rb', '-o', dir, "#{dir}/ThriftSpec.thrift"
+ end
+
+ task :'namespaced_spec' do
+ dir = File.dirname(__FILE__) + '/spec'
+ sh THRIFT, '--gen', 'rb:namespaced', '--recurse', '-o', dir, "#{dir}/ThriftNamespacedSpec.thrift"
+ sh THRIFT, '--gen', 'rb:namespaced', '--recurse', '-o', dir, "#{dir}/BaseService.thrift"
+ sh THRIFT, '--gen', 'rb:namespaced', '--recurse', '-o', dir, "#{dir}/ExtendedService.thrift"
+ end
+
+ task :'flat_spec' do
+ dir = File.dirname(__FILE__) + '/spec'
+ mkdir_p("#{dir}/gen-rb/flat")
+ sh THRIFT, '--gen', 'rb', '--recurse', '-out', "#{dir}/gen-rb/flat", "#{dir}/ThriftNamespacedSpec.thrift"
+ end
+
+ task :'benchmark' do
+ dir = File.dirname(__FILE__) + '/benchmark'
+ sh THRIFT, '--gen', 'rb', '-o', dir, "#{dir}/Benchmark.thrift"
+ end
+
+ task :'debug_proto' do
+ sh "mkdir", "-p", "test/debug_proto"
+ sh THRIFT, '--gen', 'rb', "-o", "test/debug_proto", "../../test/DebugProtoTest.thrift"
+ end
+end
+
+desc "Build the native library"
+task :build_ext => :'gen-rb' do
+ next if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
+ Dir::chdir(File::dirname('ext/extconf.rb')) do
+ unless sh "ruby #{File::basename('ext/extconf.rb')}"
+ $stderr.puts "Failed to run extconf"
+ break
+ end
+ unless sh "make"
+ $stderr.puts "make failed"
+ break
+ end
+ end
+end
+
+desc 'Run the compiler tests (requires full thrift checkout)'
+task :test do
+ # ensure this is a full thrift checkout and not a tarball of the ruby libs
+ cmd = 'head -1 ../../README.md 2>/dev/null | grep Thrift >/dev/null 2>/dev/null'
+ system(cmd) or fail "rake test requires a full thrift checkout"
+ sh 'make', '-C', File.dirname(__FILE__) + "/../../test/rb", "check"
+end
+
+desc 'Run benchmarking of NonblockingServer'
+task :benchmark do
+ ruby 'benchmark/benchmark.rb'
+end
+
+desc 'Builds the thrift gem'
+task :gem => [:spec, :build_ext] do
+ unless sh 'gem', 'build', 'thrift.gemspec'
+ $stderr.puts "Failed to build thrift gem"
+ break
+ end
+end
+
+desc 'Install the thrift gem'
+task :install => [:gem] do
+ unless sh 'gem', 'install', Dir.glob('thrift-*.gem').last
+ $stderr.puts "Failed to install thrift gem"
+ break
+ end
+end
+
+CLEAN.include [
+ '.bundle', 'benchmark/gen-rb', 'coverage', 'ext/*.{o,bundle,so,dll}', 'ext/mkmf.log',
+ 'ext/Makefile', 'ext/conftest.dSYM', 'Gemfile.lock', 'mkmf.log', 'pkg', 'spec/gen-rb',
+ 'test', 'thrift-*.gem'
+]
diff --git a/src/jaegertracing/thrift/lib/rb/benchmark/Benchmark.thrift b/src/jaegertracing/thrift/lib/rb/benchmark/Benchmark.thrift
new file mode 100644
index 000000000..eb5ae38e6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/benchmark/Benchmark.thrift
@@ -0,0 +1,24 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+namespace rb ThriftBenchmark
+
+service BenchmarkService {
+ i32 fibonacci(1:byte n)
+}
diff --git a/src/jaegertracing/thrift/lib/rb/benchmark/benchmark.rb b/src/jaegertracing/thrift/lib/rb/benchmark/benchmark.rb
new file mode 100644
index 000000000..3dc67dd8c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/benchmark/benchmark.rb
@@ -0,0 +1,271 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'rubygems'
+$:.unshift File.dirname(__FILE__) + '/../lib'
+require 'thrift'
+require 'stringio'
+
+HOST = '127.0.0.1'
+PORT = 42587
+
+###############
+## Server
+###############
+
+class Server
+ attr_accessor :serverclass
+ attr_accessor :interpreter
+ attr_accessor :host
+ attr_accessor :port
+
+ def initialize(opts)
+ @serverclass = opts.fetch(:class, Thrift::NonblockingServer)
+ @interpreter = opts.fetch(:interpreter, "ruby")
+ @host = opts.fetch(:host, ::HOST)
+ @port = opts.fetch(:port, ::PORT)
+ end
+
+ def start
+ return if @serverclass == Object
+ args = (File.basename(@interpreter) == "jruby" ? "-J-server" : "")
+ @pipe = IO.popen("#{@interpreter} #{args} #{File.dirname(__FILE__)}/server.rb #{@host} #{@port} #{@serverclass.name}", "r+")
+ Marshal.load(@pipe) # wait until the server has started
+ sleep 0.4 # give the server time to actually start spawning sockets
+ end
+
+ def shutdown
+ return unless @pipe
+ Marshal.dump(:shutdown, @pipe)
+ begin
+ @pipe.read(10) # block until the server shuts down
+ rescue EOFError
+ end
+ @pipe.close
+ @pipe = nil
+ end
+end
+
+class BenchmarkManager
+ def initialize(opts, server)
+ @socket = opts.fetch(:socket) do
+ @host = opts.fetch(:host, 'localhost')
+ @port = opts.fetch(:port)
+ nil
+ end
+ @num_processes = opts.fetch(:num_processes, 40)
+ @clients_per_process = opts.fetch(:clients_per_process, 10)
+ @calls_per_client = opts.fetch(:calls_per_client, 50)
+ @interpreter = opts.fetch(:interpreter, "ruby")
+ @server = server
+ @log_exceptions = opts.fetch(:log_exceptions, false)
+ end
+
+ def run
+ @pool = []
+ @benchmark_start = Time.now
+ puts "Spawning benchmark processes..."
+ @num_processes.times do
+ spawn
+ sleep 0.02 # space out spawns
+ end
+ collect_output
+ @benchmark_end = Time.now # we know the procs are done here
+ translate_output
+ analyze_output
+ report_output
+ end
+
+ def spawn
+ pipe = IO.popen("#{@interpreter} #{File.dirname(__FILE__)}/client.rb #{"-log-exceptions" if @log_exceptions} #{@host} #{@port} #{@clients_per_process} #{@calls_per_client}")
+ @pool << pipe
+ end
+
+ def socket_class
+ if @socket
+ Thrift::UNIXSocket
+ else
+ Thrift::Socket
+ end
+ end
+
+ def collect_output
+ puts "Collecting output..."
+ # read from @pool until all sockets are closed
+ @buffers = Hash.new { |h,k| h[k] = '' }
+ until @pool.empty?
+ rd, = select(@pool)
+ next if rd.nil?
+ rd.each do |fd|
+ begin
+ @buffers[fd] << fd.readpartial(4096)
+ rescue EOFError
+ @pool.delete fd
+ end
+ end
+ end
+ end
+
+ def translate_output
+ puts "Translating output..."
+ @output = []
+ @buffers.each do |fd, buffer|
+ strio = StringIO.new(buffer)
+ logs = []
+ begin
+ loop do
+ logs << Marshal.load(strio)
+ end
+ rescue EOFError
+ @output << logs
+ end
+ end
+ end
+
+ def analyze_output
+ puts "Analyzing output..."
+ call_times = []
+ client_times = []
+ connection_failures = []
+ connection_errors = []
+ shortest_call = 0
+ shortest_client = 0
+ longest_call = 0
+ longest_client = 0
+ @output.each do |logs|
+ cur_call, cur_client = nil
+ logs.each do |tok, time|
+ case tok
+ when :start
+ cur_client = time
+ when :call_start
+ cur_call = time
+ when :call_end
+ delta = time - cur_call
+ call_times << delta
+ longest_call = delta unless longest_call > delta
+ shortest_call = delta if shortest_call == 0 or delta < shortest_call
+ cur_call = nil
+ when :end
+ delta = time - cur_client
+ client_times << delta
+ longest_client = delta unless longest_client > delta
+ shortest_client = delta if shortest_client == 0 or delta < shortest_client
+ cur_client = nil
+ when :connection_failure
+ connection_failures << time
+ when :connection_error
+ connection_errors << time
+ end
+ end
+ end
+ @report = {}
+ @report[:total_calls] = call_times.inject(0.0) { |a,t| a += t }
+ @report[:avg_calls] = @report[:total_calls] / call_times.size
+ @report[:total_clients] = client_times.inject(0.0) { |a,t| a += t }
+ @report[:avg_clients] = @report[:total_clients] / client_times.size
+ @report[:connection_failures] = connection_failures.size
+ @report[:connection_errors] = connection_errors.size
+ @report[:shortest_call] = shortest_call
+ @report[:shortest_client] = shortest_client
+ @report[:longest_call] = longest_call
+ @report[:longest_client] = longest_client
+ @report[:total_benchmark_time] = @benchmark_end - @benchmark_start
+ @report[:fastthread] = $".include?('fastthread.bundle')
+ end
+
+ def report_output
+ fmt = "%.4f seconds"
+ puts
+ tabulate "%d",
+ [["Server class", "%s"], @server.serverclass == Object ? "" : @server.serverclass],
+ [["Server interpreter", "%s"], @server.interpreter],
+ [["Client interpreter", "%s"], @interpreter],
+ [["Socket class", "%s"], socket_class],
+ ["Number of processes", @num_processes],
+ ["Clients per process", @clients_per_process],
+ ["Calls per client", @calls_per_client],
+ [["Using fastthread", "%s"], @report[:fastthread] ? "yes" : "no"]
+ puts
+ failures = (@report[:connection_failures] > 0)
+ tabulate fmt,
+ [["Connection failures", "%d", [:red, :bold]], @report[:connection_failures]],
+ [["Connection errors", "%d", [:red, :bold]], @report[:connection_errors]],
+ ["Average time per call", @report[:avg_calls]],
+ ["Average time per client (%d calls)" % @calls_per_client, @report[:avg_clients]],
+ ["Total time for all calls", @report[:total_calls]],
+ ["Real time for benchmarking", @report[:total_benchmark_time]],
+ ["Shortest call time", @report[:shortest_call]],
+ ["Longest call time", @report[:longest_call]],
+ ["Shortest client time (%d calls)" % @calls_per_client, @report[:shortest_client]],
+ ["Longest client time (%d calls)" % @calls_per_client, @report[:longest_client]]
+ end
+
+ ANSI = {
+ :reset => 0,
+ :bold => 1,
+ :black => 30,
+ :red => 31,
+ :green => 32,
+ :yellow => 33,
+ :blue => 34,
+ :magenta => 35,
+ :cyan => 36,
+ :white => 37
+ }
+
+ def tabulate(fmt, *labels_and_values)
+ labels = labels_and_values.map { |l| Array === l ? l.first : l }
+ label_width = labels.inject(0) { |w,l| l.size > w ? l.size : w }
+ labels_and_values.each do |(l,v)|
+ f = fmt
+ l, f, c = l if Array === l
+ fmtstr = "%-#{label_width+1}s #{f}"
+ if STDOUT.tty? and c and v.to_i > 0
+ fmtstr = "\e[#{[*c].map { |x| ANSI[x] } * ";"}m" + fmtstr + "\e[#{ANSI[:reset]}m"
+ end
+ puts fmtstr % [l+":", v]
+ end
+ end
+end
+
+def resolve_const(const)
+ const and const.split('::').inject(Object) { |k,c| k.const_get(c) }
+end
+
+puts "Starting server..."
+args = {}
+args[:interpreter] = ENV['THRIFT_SERVER_INTERPRETER'] || ENV['THRIFT_INTERPRETER'] || "ruby"
+args[:class] = resolve_const(ENV['THRIFT_SERVER']) || Thrift::NonblockingServer
+args[:host] = ENV['THRIFT_HOST'] || HOST
+args[:port] = (ENV['THRIFT_PORT'] || PORT).to_i
+server = Server.new(args)
+server.start
+
+args = {}
+args[:host] = ENV['THRIFT_HOST'] || HOST
+args[:port] = (ENV['THRIFT_PORT'] || PORT).to_i
+args[:num_processes] = (ENV['THRIFT_NUM_PROCESSES'] || 40).to_i
+args[:clients_per_process] = (ENV['THRIFT_NUM_CLIENTS'] || 5).to_i
+args[:calls_per_client] = (ENV['THRIFT_NUM_CALLS'] || 50).to_i
+args[:interpreter] = ENV['THRIFT_CLIENT_INTERPRETER'] || ENV['THRIFT_INTERPRETER'] || "ruby"
+args[:log_exceptions] = !!ENV['THRIFT_LOG_EXCEPTIONS']
+BenchmarkManager.new(args, server).run
+
+server.shutdown
diff --git a/src/jaegertracing/thrift/lib/rb/benchmark/client.rb b/src/jaegertracing/thrift/lib/rb/benchmark/client.rb
new file mode 100644
index 000000000..703dc8f52
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/benchmark/client.rb
@@ -0,0 +1,74 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+$:.unshift File.dirname(__FILE__) + '/../lib'
+require 'thrift'
+$:.unshift File.dirname(__FILE__) + "/gen-rb"
+require 'benchmark_service'
+
+class Client
+ def initialize(host, port, clients_per_process, calls_per_client, log_exceptions)
+ @host = host
+ @port = port
+ @clients_per_process = clients_per_process
+ @calls_per_client = calls_per_client
+ @log_exceptions = log_exceptions
+ end
+
+ def run
+ @clients_per_process.times do
+ socket = Thrift::Socket.new(@host, @port)
+ transport = Thrift::FramedTransport.new(socket)
+ protocol = Thrift::BinaryProtocol.new(transport)
+ client = ThriftBenchmark::BenchmarkService::Client.new(protocol)
+ begin
+ start = Time.now
+ transport.open
+ Marshal.dump [:start, start], STDOUT
+ rescue => e
+ Marshal.dump [:connection_failure, Time.now], STDOUT
+ print_exception e if @log_exceptions
+ else
+ begin
+ @calls_per_client.times do
+ Marshal.dump [:call_start, Time.now], STDOUT
+ client.fibonacci(15)
+ Marshal.dump [:call_end, Time.now], STDOUT
+ end
+ transport.close
+ Marshal.dump [:end, Time.now], STDOUT
+ rescue Thrift::TransportException => e
+ Marshal.dump [:connection_error, Time.now], STDOUT
+ print_exception e if @log_exceptions
+ end
+ end
+ end
+ end
+
+ def print_exception(e)
+ STDERR.puts "ERROR: #{e.message}"
+ STDERR.puts "\t#{e.backtrace * "\n\t"}"
+ end
+end
+
+log_exceptions = true if ARGV[0] == '-log-exceptions' and ARGV.shift
+
+host, port, clients_per_process, calls_per_client = ARGV
+
+Client.new(host, port.to_i, clients_per_process.to_i, calls_per_client.to_i, log_exceptions).run
diff --git a/src/jaegertracing/thrift/lib/rb/benchmark/server.rb b/src/jaegertracing/thrift/lib/rb/benchmark/server.rb
new file mode 100644
index 000000000..74e13f414
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/benchmark/server.rb
@@ -0,0 +1,82 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+$:.unshift File.dirname(__FILE__) + '/../lib'
+require 'thrift'
+$:.unshift File.dirname(__FILE__) + "/gen-rb"
+require 'benchmark_service'
+
+module Server
+ include Thrift
+
+ class BenchmarkHandler
+ # 1-based index into the fibonacci sequence
+ def fibonacci(n)
+ seq = [1, 1]
+ 3.upto(n) do
+ seq << seq[-1] + seq[-2]
+ end
+ seq[n-1] # n is 1-based
+ end
+ end
+
+ def self.start_server(host, port, serverClass)
+ handler = BenchmarkHandler.new
+ processor = ThriftBenchmark::BenchmarkService::Processor.new(handler)
+ transport = ServerSocket.new(host, port)
+ transport_factory = FramedTransportFactory.new
+ args = [processor, transport, transport_factory, nil, 20]
+ if serverClass == NonblockingServer
+ logger = Logger.new(STDERR)
+ logger.level = Logger::WARN
+ args << logger
+ end
+ server = serverClass.new(*args)
+ @server_thread = Thread.new do
+ server.serve
+ end
+ @server = server
+ end
+
+ def self.shutdown
+ return if @server.nil?
+ if @server.respond_to? :shutdown
+ @server.shutdown
+ else
+ @server_thread.kill
+ end
+ end
+end
+
+def resolve_const(const)
+ const and const.split('::').inject(Object) { |k,c| k.const_get(c) }
+end
+
+host, port, serverklass = ARGV
+
+Server.start_server(host, port.to_i, resolve_const(serverklass))
+
+# let our host know that the interpreter has started
+# ideally we'd wait until the server was serving, but we don't have a hook for that
+Marshal.dump(:started, STDOUT)
+STDOUT.flush
+
+Marshal.load(STDIN) # wait until we're instructed to shut down
+
+Server.shutdown
diff --git a/src/jaegertracing/thrift/lib/rb/benchmark/thin_server.rb b/src/jaegertracing/thrift/lib/rb/benchmark/thin_server.rb
new file mode 100644
index 000000000..4de2eef38
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/benchmark/thin_server.rb
@@ -0,0 +1,44 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+$:.unshift File.dirname(__FILE__) + '/../lib'
+require 'thrift'
+$:.unshift File.dirname(__FILE__) + "/gen-rb"
+require 'benchmark_service'
+HOST = 'localhost'
+PORT = 42587
+
+class BenchmarkHandler
+ # 1-based index into the fibonacci sequence
+ def fibonacci(n)
+ seq = [1, 1]
+ 3.upto(n) do
+ seq << seq[-1] + seq[-2]
+ end
+ seq[n-1] # n is 1-based
+ end
+end
+
+handler = BenchmarkHandler.new
+processor = ThriftBenchmark::BenchmarkService::Processor.new(handler)
+transport = Thrift::ServerSocket.new(HOST, PORT)
+transport_factory = Thrift::FramedTransportFactory.new
+logger = Logger.new(STDERR)
+logger.level = Logger::WARN
+Thrift::NonblockingServer.new(processor, transport, transport_factory, nil, 20, logger).serve
diff --git a/src/jaegertracing/thrift/lib/rb/coding_standards.md b/src/jaegertracing/thrift/lib/rb/coding_standards.md
new file mode 100644
index 000000000..fa0390bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/coding_standards.md
@@ -0,0 +1 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
diff --git a/src/jaegertracing/thrift/lib/rb/ext/binary_protocol_accelerated.c b/src/jaegertracing/thrift/lib/rb/ext/binary_protocol_accelerated.c
new file mode 100644
index 000000000..65cbe5ffe
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/binary_protocol_accelerated.c
@@ -0,0 +1,460 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <ruby.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <constants.h>
+#include <struct.h>
+#include <macros.h>
+#include <bytes.h>
+
+VALUE rb_thrift_binary_proto_native_qmark(VALUE self) {
+ return Qtrue;
+}
+
+
+
+static int VERSION_1;
+static int VERSION_MASK;
+static int TYPE_MASK;
+static int BAD_VERSION;
+static ID rbuf_ivar_id;
+
+static void write_byte_direct(VALUE trans, int8_t b) {
+ WRITE(trans, (char*)&b, 1);
+}
+
+static void write_i16_direct(VALUE trans, int16_t value) {
+ char data[2];
+
+ data[1] = value;
+ data[0] = (value >> 8);
+
+ WRITE(trans, data, 2);
+}
+
+static void write_i32_direct(VALUE trans, int32_t value) {
+ char data[4];
+
+ data[3] = value;
+ data[2] = (value >> 8);
+ data[1] = (value >> 16);
+ data[0] = (value >> 24);
+
+ WRITE(trans, data, 4);
+}
+
+
+static void write_i64_direct(VALUE trans, int64_t value) {
+ char data[8];
+
+ data[7] = value;
+ data[6] = (value >> 8);
+ data[5] = (value >> 16);
+ data[4] = (value >> 24);
+ data[3] = (value >> 32);
+ data[2] = (value >> 40);
+ data[1] = (value >> 48);
+ data[0] = (value >> 56);
+
+ WRITE(trans, data, 8);
+}
+
+static void write_string_direct(VALUE trans, VALUE str) {
+ if (TYPE(str) != T_STRING) {
+ rb_raise(rb_eStandardError, "Value should be a string");
+ }
+ str = convert_to_utf8_byte_buffer(str);
+ write_i32_direct(trans, RSTRING_LEN(str));
+ rb_funcall(trans, write_method_id, 1, str);
+}
+
+//--------------------------------
+// interface writing methods
+//--------------------------------
+
+VALUE rb_thrift_binary_proto_write_message_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_struct_begin(VALUE self, VALUE name) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_struct_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_field_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_map_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_list_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_set_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_message_begin(VALUE self, VALUE name, VALUE type, VALUE seqid) {
+ VALUE trans = GET_TRANSPORT(self);
+ VALUE strict_write = GET_STRICT_WRITE(self);
+
+ if (strict_write == Qtrue) {
+ write_i32_direct(trans, VERSION_1 | FIX2INT(type));
+ write_string_direct(trans, name);
+ write_i32_direct(trans, FIX2INT(seqid));
+ } else {
+ write_string_direct(trans, name);
+ write_byte_direct(trans, FIX2INT(type));
+ write_i32_direct(trans, FIX2INT(seqid));
+ }
+
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_field_begin(VALUE self, VALUE name, VALUE type, VALUE id) {
+ VALUE trans = GET_TRANSPORT(self);
+ write_byte_direct(trans, FIX2INT(type));
+ write_i16_direct(trans, FIX2INT(id));
+
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_field_stop(VALUE self) {
+ write_byte_direct(GET_TRANSPORT(self), TTYPE_STOP);
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_map_begin(VALUE self, VALUE ktype, VALUE vtype, VALUE size) {
+ VALUE trans = GET_TRANSPORT(self);
+ write_byte_direct(trans, FIX2INT(ktype));
+ write_byte_direct(trans, FIX2INT(vtype));
+ write_i32_direct(trans, FIX2INT(size));
+
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_list_begin(VALUE self, VALUE etype, VALUE size) {
+ VALUE trans = GET_TRANSPORT(self);
+ write_byte_direct(trans, FIX2INT(etype));
+ write_i32_direct(trans, FIX2INT(size));
+
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_set_begin(VALUE self, VALUE etype, VALUE size) {
+ rb_thrift_binary_proto_write_list_begin(self, etype, size);
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_bool(VALUE self, VALUE b) {
+ write_byte_direct(GET_TRANSPORT(self), RTEST(b) ? 1 : 0);
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_byte(VALUE self, VALUE byte) {
+ CHECK_NIL(byte);
+ write_byte_direct(GET_TRANSPORT(self), NUM2INT(byte));
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_i16(VALUE self, VALUE i16) {
+ CHECK_NIL(i16);
+ write_i16_direct(GET_TRANSPORT(self), FIX2INT(i16));
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_i32(VALUE self, VALUE i32) {
+ CHECK_NIL(i32);
+ write_i32_direct(GET_TRANSPORT(self), NUM2INT(i32));
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_i64(VALUE self, VALUE i64) {
+ CHECK_NIL(i64);
+ write_i64_direct(GET_TRANSPORT(self), NUM2LL(i64));
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_double(VALUE self, VALUE dub) {
+ CHECK_NIL(dub);
+ // Unfortunately, bitwise_cast doesn't work in C. Bad C!
+ union {
+ double f;
+ int64_t t;
+ } transfer;
+ transfer.f = RFLOAT_VALUE(rb_Float(dub));
+ write_i64_direct(GET_TRANSPORT(self), transfer.t);
+
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_string(VALUE self, VALUE str) {
+ CHECK_NIL(str);
+ VALUE trans = GET_TRANSPORT(self);
+ write_string_direct(trans, str);
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_binary(VALUE self, VALUE buf) {
+ CHECK_NIL(buf);
+ VALUE trans = GET_TRANSPORT(self);
+ buf = force_binary_encoding(buf);
+ write_i32_direct(trans, RSTRING_LEN(buf));
+ rb_funcall(trans, write_method_id, 1, buf);
+ return Qnil;
+}
+
+//---------------------------------------
+// interface reading methods
+//---------------------------------------
+
+VALUE rb_thrift_binary_proto_read_string(VALUE self);
+VALUE rb_thrift_binary_proto_read_binary(VALUE self);
+VALUE rb_thrift_binary_proto_read_byte(VALUE self);
+VALUE rb_thrift_binary_proto_read_i32(VALUE self);
+VALUE rb_thrift_binary_proto_read_i16(VALUE self);
+
+static char read_byte_direct(VALUE self) {
+ VALUE byte = rb_funcall(GET_TRANSPORT(self), read_byte_method_id, 0);
+ return (char)(FIX2INT(byte));
+}
+
+static int16_t read_i16_direct(VALUE self) {
+ VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id);
+ rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(2));
+ return (int16_t)(((uint8_t)(RSTRING_PTR(rbuf)[1])) | ((uint16_t)((RSTRING_PTR(rbuf)[0]) << 8)));
+}
+
+static int32_t read_i32_direct(VALUE self) {
+ VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id);
+ rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(4));
+ return ((uint8_t)(RSTRING_PTR(rbuf)[3])) |
+ (((uint8_t)(RSTRING_PTR(rbuf)[2])) << 8) |
+ (((uint8_t)(RSTRING_PTR(rbuf)[1])) << 16) |
+ (((uint8_t)(RSTRING_PTR(rbuf)[0])) << 24);
+}
+
+static int64_t read_i64_direct(VALUE self) {
+ VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id);
+ rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(8));
+ uint64_t hi = ((uint8_t)(RSTRING_PTR(rbuf)[3])) |
+ (((uint8_t)(RSTRING_PTR(rbuf)[2])) << 8) |
+ (((uint8_t)(RSTRING_PTR(rbuf)[1])) << 16) |
+ (((uint8_t)(RSTRING_PTR(rbuf)[0])) << 24);
+ uint32_t lo = ((uint8_t)(RSTRING_PTR(rbuf)[7])) |
+ (((uint8_t)(RSTRING_PTR(rbuf)[6])) << 8) |
+ (((uint8_t)(RSTRING_PTR(rbuf)[5])) << 16) |
+ (((uint8_t)(RSTRING_PTR(rbuf)[4])) << 24);
+ return (hi << 32) | lo;
+}
+
+static VALUE get_protocol_exception(VALUE code, VALUE message) {
+ VALUE args[2];
+ args[0] = code;
+ args[1] = message;
+ return rb_class_new_instance(2, (VALUE*)&args, protocol_exception_class);
+}
+
+VALUE rb_thrift_binary_proto_read_message_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_read_struct_begin(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_read_struct_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_read_field_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_read_map_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_read_list_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_read_set_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_read_message_begin(VALUE self) {
+ VALUE strict_read = GET_STRICT_READ(self);
+ VALUE name, seqid;
+ int type;
+
+ int version = read_i32_direct(self);
+
+ if (version < 0) {
+ if ((version & VERSION_MASK) != VERSION_1) {
+ rb_exc_raise(get_protocol_exception(INT2FIX(BAD_VERSION), rb_str_new2("Missing version identifier")));
+ }
+ type = version & TYPE_MASK;
+ name = rb_thrift_binary_proto_read_string(self);
+ seqid = rb_thrift_binary_proto_read_i32(self);
+ } else {
+ if (strict_read == Qtrue) {
+ rb_exc_raise(get_protocol_exception(INT2FIX(BAD_VERSION), rb_str_new2("No version identifier, old protocol client?")));
+ }
+ name = READ(self, version);
+ type = read_byte_direct(self);
+ seqid = rb_thrift_binary_proto_read_i32(self);
+ }
+
+ return rb_ary_new3(3, name, INT2FIX(type), seqid);
+}
+
+VALUE rb_thrift_binary_proto_read_field_begin(VALUE self) {
+ int type = read_byte_direct(self);
+ if (type == TTYPE_STOP) {
+ return rb_ary_new3(3, Qnil, INT2FIX(type), INT2FIX(0));
+ } else {
+ VALUE id = rb_thrift_binary_proto_read_i16(self);
+ return rb_ary_new3(3, Qnil, INT2FIX(type), id);
+ }
+}
+
+VALUE rb_thrift_binary_proto_read_map_begin(VALUE self) {
+ VALUE ktype = rb_thrift_binary_proto_read_byte(self);
+ VALUE vtype = rb_thrift_binary_proto_read_byte(self);
+ VALUE size = rb_thrift_binary_proto_read_i32(self);
+ return rb_ary_new3(3, ktype, vtype, size);
+}
+
+VALUE rb_thrift_binary_proto_read_list_begin(VALUE self) {
+ VALUE etype = rb_thrift_binary_proto_read_byte(self);
+ VALUE size = rb_thrift_binary_proto_read_i32(self);
+ return rb_ary_new3(2, etype, size);
+}
+
+VALUE rb_thrift_binary_proto_read_set_begin(VALUE self) {
+ return rb_thrift_binary_proto_read_list_begin(self);
+}
+
+VALUE rb_thrift_binary_proto_read_bool(VALUE self) {
+ char byte = read_byte_direct(self);
+ return byte != 0 ? Qtrue : Qfalse;
+}
+
+VALUE rb_thrift_binary_proto_read_byte(VALUE self) {
+ return INT2FIX(read_byte_direct(self));
+}
+
+VALUE rb_thrift_binary_proto_read_i16(VALUE self) {
+ return INT2FIX(read_i16_direct(self));
+}
+
+VALUE rb_thrift_binary_proto_read_i32(VALUE self) {
+ return INT2NUM(read_i32_direct(self));
+}
+
+VALUE rb_thrift_binary_proto_read_i64(VALUE self) {
+ return LL2NUM(read_i64_direct(self));
+}
+
+VALUE rb_thrift_binary_proto_read_double(VALUE self) {
+ union {
+ double f;
+ int64_t t;
+ } transfer;
+ transfer.t = read_i64_direct(self);
+ return rb_float_new(transfer.f);
+}
+
+VALUE rb_thrift_binary_proto_read_string(VALUE self) {
+ VALUE buffer = rb_thrift_binary_proto_read_binary(self);
+ return convert_to_string(buffer);
+}
+
+VALUE rb_thrift_binary_proto_read_binary(VALUE self) {
+ int size = read_i32_direct(self);
+ return READ(self, size);
+}
+
+void Init_binary_protocol_accelerated() {
+ VALUE thrift_binary_protocol_class = rb_const_get(thrift_module, rb_intern("BinaryProtocol"));
+
+ VERSION_1 = rb_num2ll(rb_const_get(thrift_binary_protocol_class, rb_intern("VERSION_1")));
+ VERSION_MASK = rb_num2ll(rb_const_get(thrift_binary_protocol_class, rb_intern("VERSION_MASK")));
+ TYPE_MASK = rb_num2ll(rb_const_get(thrift_binary_protocol_class, rb_intern("TYPE_MASK")));
+
+ VALUE bpa_class = rb_define_class_under(thrift_module, "BinaryProtocolAccelerated", thrift_binary_protocol_class);
+
+ rb_define_method(bpa_class, "native?", rb_thrift_binary_proto_native_qmark, 0);
+
+ rb_define_method(bpa_class, "write_message_begin", rb_thrift_binary_proto_write_message_begin, 3);
+ rb_define_method(bpa_class, "write_field_begin", rb_thrift_binary_proto_write_field_begin, 3);
+ rb_define_method(bpa_class, "write_field_stop", rb_thrift_binary_proto_write_field_stop, 0);
+ rb_define_method(bpa_class, "write_map_begin", rb_thrift_binary_proto_write_map_begin, 3);
+ rb_define_method(bpa_class, "write_list_begin", rb_thrift_binary_proto_write_list_begin, 2);
+ rb_define_method(bpa_class, "write_set_begin", rb_thrift_binary_proto_write_set_begin, 2);
+ rb_define_method(bpa_class, "write_byte", rb_thrift_binary_proto_write_byte, 1);
+ rb_define_method(bpa_class, "write_bool", rb_thrift_binary_proto_write_bool, 1);
+ rb_define_method(bpa_class, "write_i16", rb_thrift_binary_proto_write_i16, 1);
+ rb_define_method(bpa_class, "write_i32", rb_thrift_binary_proto_write_i32, 1);
+ rb_define_method(bpa_class, "write_i64", rb_thrift_binary_proto_write_i64, 1);
+ rb_define_method(bpa_class, "write_double", rb_thrift_binary_proto_write_double, 1);
+ rb_define_method(bpa_class, "write_string", rb_thrift_binary_proto_write_string, 1);
+ rb_define_method(bpa_class, "write_binary", rb_thrift_binary_proto_write_binary, 1);
+ // unused methods
+ rb_define_method(bpa_class, "write_message_end", rb_thrift_binary_proto_write_message_end, 0);
+ rb_define_method(bpa_class, "write_struct_begin", rb_thrift_binary_proto_write_struct_begin, 1);
+ rb_define_method(bpa_class, "write_struct_end", rb_thrift_binary_proto_write_struct_end, 0);
+ rb_define_method(bpa_class, "write_field_end", rb_thrift_binary_proto_write_field_end, 0);
+ rb_define_method(bpa_class, "write_map_end", rb_thrift_binary_proto_write_map_end, 0);
+ rb_define_method(bpa_class, "write_list_end", rb_thrift_binary_proto_write_list_end, 0);
+ rb_define_method(bpa_class, "write_set_end", rb_thrift_binary_proto_write_set_end, 0);
+
+ rb_define_method(bpa_class, "read_message_begin", rb_thrift_binary_proto_read_message_begin, 0);
+ rb_define_method(bpa_class, "read_field_begin", rb_thrift_binary_proto_read_field_begin, 0);
+ rb_define_method(bpa_class, "read_map_begin", rb_thrift_binary_proto_read_map_begin, 0);
+ rb_define_method(bpa_class, "read_list_begin", rb_thrift_binary_proto_read_list_begin, 0);
+ rb_define_method(bpa_class, "read_set_begin", rb_thrift_binary_proto_read_set_begin, 0);
+ rb_define_method(bpa_class, "read_byte", rb_thrift_binary_proto_read_byte, 0);
+ rb_define_method(bpa_class, "read_bool", rb_thrift_binary_proto_read_bool, 0);
+ rb_define_method(bpa_class, "read_i16", rb_thrift_binary_proto_read_i16, 0);
+ rb_define_method(bpa_class, "read_i32", rb_thrift_binary_proto_read_i32, 0);
+ rb_define_method(bpa_class, "read_i64", rb_thrift_binary_proto_read_i64, 0);
+ rb_define_method(bpa_class, "read_double", rb_thrift_binary_proto_read_double, 0);
+ rb_define_method(bpa_class, "read_string", rb_thrift_binary_proto_read_string, 0);
+ rb_define_method(bpa_class, "read_binary", rb_thrift_binary_proto_read_binary, 0);
+ // unused methods
+ rb_define_method(bpa_class, "read_message_end", rb_thrift_binary_proto_read_message_end, 0);
+ rb_define_method(bpa_class, "read_struct_begin", rb_thrift_binary_proto_read_struct_begin, 0);
+ rb_define_method(bpa_class, "read_struct_end", rb_thrift_binary_proto_read_struct_end, 0);
+ rb_define_method(bpa_class, "read_field_end", rb_thrift_binary_proto_read_field_end, 0);
+ rb_define_method(bpa_class, "read_map_end", rb_thrift_binary_proto_read_map_end, 0);
+ rb_define_method(bpa_class, "read_list_end", rb_thrift_binary_proto_read_list_end, 0);
+ rb_define_method(bpa_class, "read_set_end", rb_thrift_binary_proto_read_set_end, 0);
+
+ rbuf_ivar_id = rb_intern("@rbuf");
+}
diff --git a/src/jaegertracing/thrift/lib/rb/ext/binary_protocol_accelerated.h b/src/jaegertracing/thrift/lib/rb/ext/binary_protocol_accelerated.h
new file mode 100644
index 000000000..37baf4142
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/binary_protocol_accelerated.h
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+void Init_binary_protocol_accelerated();
diff --git a/src/jaegertracing/thrift/lib/rb/ext/bytes.c b/src/jaegertracing/thrift/lib/rb/ext/bytes.c
new file mode 100644
index 000000000..8a6fac4ac
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/bytes.c
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <ruby.h>
+#ifdef HAVE_RUBY_ENCODING_H
+#include <ruby/encoding.h>
+#endif
+#include <constants.h>
+
+VALUE force_binary_encoding(VALUE buffer) {
+ return rb_funcall(thrift_bytes_module, force_binary_encoding_id, 1, buffer);
+}
+
+VALUE convert_to_utf8_byte_buffer(VALUE string) {
+ return rb_funcall(thrift_bytes_module, convert_to_utf8_byte_buffer_id, 1, string);
+}
+
+VALUE convert_to_string(VALUE utf8_buffer) {
+ return rb_funcall(thrift_bytes_module, convert_to_string_id, 1, utf8_buffer);
+}
diff --git a/src/jaegertracing/thrift/lib/rb/ext/bytes.h b/src/jaegertracing/thrift/lib/rb/ext/bytes.h
new file mode 100644
index 000000000..7108d83ff
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/bytes.h
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <ruby.h>
+
+/*
+ * A collection of utilities for working with bytes and byte buffers.
+ *
+ * These methods are the native analogies to some of the methods in
+ * Thrift::Bytes (thrift/bytes.rb).
+ */
+
+VALUE force_binary_encoding(VALUE buffer);
+VALUE convert_to_utf8_byte_buffer(VALUE string);
+VALUE convert_to_string(VALUE utf8_buffer);
diff --git a/src/jaegertracing/thrift/lib/rb/ext/compact_protocol.c b/src/jaegertracing/thrift/lib/rb/ext/compact_protocol.c
new file mode 100644
index 000000000..c0f46b958
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/compact_protocol.c
@@ -0,0 +1,637 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <ruby.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <constants.h>
+#include <struct.h>
+#include <macros.h>
+#include <bytes.h>
+
+#define LAST_ID(obj) FIX2INT(rb_ary_pop(rb_ivar_get(obj, last_field_id)))
+#define SET_LAST_ID(obj, val) rb_ary_push(rb_ivar_get(obj, last_field_id), val)
+
+VALUE rb_thrift_compact_proto_native_qmark(VALUE self) {
+ return Qtrue;
+}
+
+static ID last_field_id;
+static ID boolean_field_id;
+static ID bool_value_id;
+static ID rbuf_ivar_id;
+
+static int VERSION;
+static int VERSION_MASK;
+static int TYPE_MASK;
+static int TYPE_BITS;
+static int TYPE_SHIFT_AMOUNT;
+static int PROTOCOL_ID;
+
+static VALUE thrift_compact_protocol_class;
+
+static int CTYPE_BOOLEAN_TRUE = 0x01;
+static int CTYPE_BOOLEAN_FALSE = 0x02;
+static int CTYPE_BYTE = 0x03;
+static int CTYPE_I16 = 0x04;
+static int CTYPE_I32 = 0x05;
+static int CTYPE_I64 = 0x06;
+static int CTYPE_DOUBLE = 0x07;
+static int CTYPE_BINARY = 0x08;
+static int CTYPE_LIST = 0x09;
+static int CTYPE_SET = 0x0A;
+static int CTYPE_MAP = 0x0B;
+static int CTYPE_STRUCT = 0x0C;
+
+VALUE rb_thrift_compact_proto_write_i16(VALUE self, VALUE i16);
+
+// TODO: implement this
+static int get_compact_type(VALUE type_value) {
+ int type = FIX2INT(type_value);
+ if (type == TTYPE_BOOL) {
+ return CTYPE_BOOLEAN_TRUE;
+ } else if (type == TTYPE_BYTE) {
+ return CTYPE_BYTE;
+ } else if (type == TTYPE_I16) {
+ return CTYPE_I16;
+ } else if (type == TTYPE_I32) {
+ return CTYPE_I32;
+ } else if (type == TTYPE_I64) {
+ return CTYPE_I64;
+ } else if (type == TTYPE_DOUBLE) {
+ return CTYPE_DOUBLE;
+ } else if (type == TTYPE_STRING) {
+ return CTYPE_BINARY;
+ } else if (type == TTYPE_LIST) {
+ return CTYPE_LIST;
+ } else if (type == TTYPE_SET) {
+ return CTYPE_SET;
+ } else if (type == TTYPE_MAP) {
+ return CTYPE_MAP;
+ } else if (type == TTYPE_STRUCT) {
+ return CTYPE_STRUCT;
+ } else {
+ char str[50];
+ sprintf(str, "don't know what type: %d", type);
+ rb_raise(rb_eStandardError, "%s", str);
+ return 0;
+ }
+}
+
+static void write_byte_direct(VALUE transport, int8_t b) {
+ WRITE(transport, (char*)&b, 1);
+}
+
+static void write_field_begin_internal(VALUE self, VALUE type, VALUE id_value, VALUE type_override) {
+ int id = FIX2INT(id_value);
+ int last_id = LAST_ID(self);
+ VALUE transport = GET_TRANSPORT(self);
+
+ // if there's a type override, use that.
+ int8_t type_to_write = RTEST(type_override) ? FIX2INT(type_override) : get_compact_type(type);
+ // check if we can use delta encoding for the field id
+ int diff = id - last_id;
+ if (diff > 0 && diff <= 15) {
+ // write them together
+ write_byte_direct(transport, diff << 4 | (type_to_write & 0x0f));
+ } else {
+ // write them separate
+ write_byte_direct(transport, type_to_write & 0x0f);
+ rb_thrift_compact_proto_write_i16(self, id_value);
+ }
+
+ SET_LAST_ID(self, id_value);
+}
+
+static int32_t int_to_zig_zag(int32_t n) {
+ return (n << 1) ^ (n >> 31);
+}
+
+static uint64_t ll_to_zig_zag(int64_t n) {
+ return (n << 1) ^ (n >> 63);
+}
+
+static void write_varint32(VALUE transport, uint32_t n) {
+ while (true) {
+ if ((n & ~0x7F) == 0) {
+ write_byte_direct(transport, n & 0x7f);
+ break;
+ } else {
+ write_byte_direct(transport, (n & 0x7F) | 0x80);
+ n = n >> 7;
+ }
+ }
+}
+
+static void write_varint64(VALUE transport, uint64_t n) {
+ while (true) {
+ if ((n & ~0x7F) == 0) {
+ write_byte_direct(transport, n & 0x7f);
+ break;
+ } else {
+ write_byte_direct(transport, (n & 0x7F) | 0x80);
+ n = n >> 7;
+ }
+ }
+}
+
+static void write_collection_begin(VALUE transport, VALUE elem_type, VALUE size_value) {
+ int size = FIX2INT(size_value);
+ if (size <= 14) {
+ write_byte_direct(transport, size << 4 | get_compact_type(elem_type));
+ } else {
+ write_byte_direct(transport, 0xf0 | get_compact_type(elem_type));
+ write_varint32(transport, size);
+ }
+}
+
+
+//--------------------------------
+// interface writing methods
+//--------------------------------
+
+VALUE rb_thrift_compact_proto_write_i32(VALUE self, VALUE i32);
+VALUE rb_thrift_compact_proto_write_string(VALUE self, VALUE str);
+VALUE rb_thrift_compact_proto_write_binary(VALUE self, VALUE buf);
+
+VALUE rb_thrift_compact_proto_write_message_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_struct_begin(VALUE self, VALUE name) {
+ rb_ary_push(rb_ivar_get(self, last_field_id), INT2FIX(0));
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_struct_end(VALUE self) {
+ rb_ary_pop(rb_ivar_get(self, last_field_id));
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_field_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_map_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_list_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_set_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_message_begin(VALUE self, VALUE name, VALUE type, VALUE seqid) {
+ VALUE transport = GET_TRANSPORT(self);
+ write_byte_direct(transport, PROTOCOL_ID);
+ write_byte_direct(transport, (VERSION & VERSION_MASK) | ((FIX2INT(type) << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
+ write_varint32(transport, FIX2INT(seqid));
+ rb_thrift_compact_proto_write_string(self, name);
+
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_field_begin(VALUE self, VALUE name, VALUE type, VALUE id) {
+ if (FIX2INT(type) == TTYPE_BOOL) {
+ // we want to possibly include the value, so we'll wait.
+ rb_ivar_set(self, boolean_field_id, rb_ary_new3(2, type, id));
+ } else {
+ write_field_begin_internal(self, type, id, Qnil);
+ }
+
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_field_stop(VALUE self) {
+ write_byte_direct(GET_TRANSPORT(self), TTYPE_STOP);
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_map_begin(VALUE self, VALUE ktype, VALUE vtype, VALUE size_value) {
+ int size = FIX2INT(size_value);
+ VALUE transport = GET_TRANSPORT(self);
+ if (size == 0) {
+ write_byte_direct(transport, 0);
+ } else {
+ write_varint32(transport, size);
+ write_byte_direct(transport, get_compact_type(ktype) << 4 | get_compact_type(vtype));
+ }
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_list_begin(VALUE self, VALUE etype, VALUE size) {
+ write_collection_begin(GET_TRANSPORT(self), etype, size);
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_set_begin(VALUE self, VALUE etype, VALUE size) {
+ write_collection_begin(GET_TRANSPORT(self), etype, size);
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_bool(VALUE self, VALUE b) {
+ int8_t type = b == Qtrue ? CTYPE_BOOLEAN_TRUE : CTYPE_BOOLEAN_FALSE;
+ VALUE boolean_field = rb_ivar_get(self, boolean_field_id);
+ if (NIL_P(boolean_field)) {
+ // we're not part of a field, so just write the value.
+ write_byte_direct(GET_TRANSPORT(self), type);
+ } else {
+ // we haven't written the field header yet
+ write_field_begin_internal(self, rb_ary_entry(boolean_field, 0), rb_ary_entry(boolean_field, 1), INT2FIX(type));
+ rb_ivar_set(self, boolean_field_id, Qnil);
+ }
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_byte(VALUE self, VALUE byte) {
+ CHECK_NIL(byte);
+ write_byte_direct(GET_TRANSPORT(self), FIX2INT(byte));
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_i16(VALUE self, VALUE i16) {
+ rb_thrift_compact_proto_write_i32(self, i16);
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_i32(VALUE self, VALUE i32) {
+ CHECK_NIL(i32);
+ write_varint32(GET_TRANSPORT(self), int_to_zig_zag(NUM2INT(i32)));
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_i64(VALUE self, VALUE i64) {
+ CHECK_NIL(i64);
+ write_varint64(GET_TRANSPORT(self), ll_to_zig_zag(NUM2LL(i64)));
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_double(VALUE self, VALUE dub) {
+ CHECK_NIL(dub);
+ // Unfortunately, bitwise_cast doesn't work in C. Bad C!
+ union {
+ double f;
+ int64_t l;
+ } transfer;
+ transfer.f = RFLOAT_VALUE(rb_Float(dub));
+ char buf[8];
+ buf[0] = transfer.l & 0xff;
+ buf[1] = (transfer.l >> 8) & 0xff;
+ buf[2] = (transfer.l >> 16) & 0xff;
+ buf[3] = (transfer.l >> 24) & 0xff;
+ buf[4] = (transfer.l >> 32) & 0xff;
+ buf[5] = (transfer.l >> 40) & 0xff;
+ buf[6] = (transfer.l >> 48) & 0xff;
+ buf[7] = (transfer.l >> 56) & 0xff;
+ WRITE(GET_TRANSPORT(self), buf, 8);
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_string(VALUE self, VALUE str) {
+ str = convert_to_utf8_byte_buffer(str);
+ rb_thrift_compact_proto_write_binary(self, str);
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_binary(VALUE self, VALUE buf) {
+ buf = force_binary_encoding(buf);
+ VALUE transport = GET_TRANSPORT(self);
+ write_varint32(transport, RSTRING_LEN(buf));
+ WRITE(transport, StringValuePtr(buf), RSTRING_LEN(buf));
+ return Qnil;
+}
+
+//---------------------------------------
+// interface reading methods
+//---------------------------------------
+
+#define is_bool_type(ctype) (((ctype) & 0x0F) == CTYPE_BOOLEAN_TRUE || ((ctype) & 0x0F) == CTYPE_BOOLEAN_FALSE)
+
+VALUE rb_thrift_compact_proto_read_string(VALUE self);
+VALUE rb_thrift_compact_proto_read_binary(VALUE self);
+VALUE rb_thrift_compact_proto_read_byte(VALUE self);
+VALUE rb_thrift_compact_proto_read_i32(VALUE self);
+VALUE rb_thrift_compact_proto_read_i16(VALUE self);
+
+static int8_t get_ttype(int8_t ctype) {
+ if (ctype == TTYPE_STOP) {
+ return TTYPE_STOP;
+ } else if (ctype == CTYPE_BOOLEAN_TRUE || ctype == CTYPE_BOOLEAN_FALSE) {
+ return TTYPE_BOOL;
+ } else if (ctype == CTYPE_BYTE) {
+ return TTYPE_BYTE;
+ } else if (ctype == CTYPE_I16) {
+ return TTYPE_I16;
+ } else if (ctype == CTYPE_I32) {
+ return TTYPE_I32;
+ } else if (ctype == CTYPE_I64) {
+ return TTYPE_I64;
+ } else if (ctype == CTYPE_DOUBLE) {
+ return TTYPE_DOUBLE;
+ } else if (ctype == CTYPE_BINARY) {
+ return TTYPE_STRING;
+ } else if (ctype == CTYPE_LIST) {
+ return TTYPE_LIST;
+ } else if (ctype == CTYPE_SET) {
+ return TTYPE_SET;
+ } else if (ctype == CTYPE_MAP) {
+ return TTYPE_MAP;
+ } else if (ctype == CTYPE_STRUCT) {
+ return TTYPE_STRUCT;
+ } else {
+ char str[50];
+ sprintf(str, "don't know what type: %d", ctype);
+ rb_raise(rb_eStandardError, "%s", str);
+ return 0;
+ }
+}
+
+static char read_byte_direct(VALUE self) {
+ VALUE byte = rb_funcall(GET_TRANSPORT(self), read_byte_method_id, 0);
+ return (char)(FIX2INT(byte));
+}
+
+static int64_t zig_zag_to_ll(int64_t n) {
+ return (((uint64_t)n) >> 1) ^ -(n & 1);
+}
+
+static int32_t zig_zag_to_int(int32_t n) {
+ return (((uint32_t)n) >> 1) ^ -(n & 1);
+}
+
+static int64_t read_varint64(VALUE self) {
+ int shift = 0;
+ int64_t result = 0;
+ while (true) {
+ int8_t b = read_byte_direct(self);
+ result = result | ((uint64_t)(b & 0x7f) << shift);
+ if ((b & 0x80) != 0x80) {
+ break;
+ }
+ shift += 7;
+ }
+ return result;
+}
+
+static int16_t read_i16(VALUE self) {
+ return zig_zag_to_int((int32_t)read_varint64(self));
+}
+
+static VALUE get_protocol_exception(VALUE code, VALUE message) {
+ VALUE args[2];
+ args[0] = code;
+ args[1] = message;
+ return rb_class_new_instance(2, (VALUE*)&args, protocol_exception_class);
+}
+
+VALUE rb_thrift_compact_proto_read_message_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_read_struct_begin(VALUE self) {
+ rb_ary_push(rb_ivar_get(self, last_field_id), INT2FIX(0));
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_read_struct_end(VALUE self) {
+ rb_ary_pop(rb_ivar_get(self, last_field_id));
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_read_field_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_read_map_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_read_list_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_read_set_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_read_message_begin(VALUE self) {
+ int8_t protocol_id = read_byte_direct(self);
+ if (protocol_id != PROTOCOL_ID) {
+ char buf[100];
+ int len = sprintf(buf, "Expected protocol id %d but got %d", PROTOCOL_ID, protocol_id);
+ buf[len] = 0;
+ rb_exc_raise(get_protocol_exception(INT2FIX(-1), rb_str_new2(buf)));
+ }
+
+ int8_t version_and_type = read_byte_direct(self);
+ int8_t version = version_and_type & VERSION_MASK;
+ if (version != VERSION) {
+ char buf[100];
+ int len = sprintf(buf, "Expected version id %d but got %d", version, VERSION);
+ buf[len] = 0;
+ rb_exc_raise(get_protocol_exception(INT2FIX(-1), rb_str_new2(buf)));
+ }
+
+ int8_t type = (version_and_type >> TYPE_SHIFT_AMOUNT) & TYPE_BITS;
+ int32_t seqid = read_varint64(self);
+ VALUE messageName = rb_thrift_compact_proto_read_string(self);
+ return rb_ary_new3(3, messageName, INT2FIX(type), INT2NUM(seqid));
+}
+
+VALUE rb_thrift_compact_proto_read_field_begin(VALUE self) {
+ int8_t type = read_byte_direct(self);
+ // if it's a stop, then we can return immediately, as the struct is over.
+ if ((type & 0x0f) == TTYPE_STOP) {
+ return rb_ary_new3(3, Qnil, INT2FIX(0), INT2FIX(0));
+ } else {
+ int field_id = 0;
+
+ // mask off the 4 MSB of the type header. it could contain a field id delta.
+ uint8_t modifier = ((type & 0xf0) >> 4);
+
+ if (modifier == 0) {
+ // not a delta. look ahead for the zigzag varint field id.
+ (void) LAST_ID(self);
+ field_id = read_i16(self);
+ } else {
+ // has a delta. add the delta to the last read field id.
+ field_id = LAST_ID(self) + modifier;
+ }
+
+ // if this happens to be a boolean field, the value is encoded in the type
+ if (is_bool_type(type)) {
+ // save the boolean value in a special instance variable.
+ rb_ivar_set(self, bool_value_id, (type & 0x0f) == CTYPE_BOOLEAN_TRUE ? Qtrue : Qfalse);
+ }
+
+ // push the new field onto the field stack so we can keep the deltas going.
+ SET_LAST_ID(self, INT2FIX(field_id));
+ return rb_ary_new3(3, Qnil, INT2FIX(get_ttype(type & 0x0f)), INT2FIX(field_id));
+ }
+}
+
+VALUE rb_thrift_compact_proto_read_map_begin(VALUE self) {
+ int32_t size = read_varint64(self);
+ uint8_t key_and_value_type = size == 0 ? 0 : read_byte_direct(self);
+ return rb_ary_new3(3, INT2FIX(get_ttype(key_and_value_type >> 4)), INT2FIX(get_ttype(key_and_value_type & 0xf)), INT2FIX(size));
+}
+
+VALUE rb_thrift_compact_proto_read_list_begin(VALUE self) {
+ uint8_t size_and_type = read_byte_direct(self);
+ int32_t size = (size_and_type >> 4) & 0x0f;
+ if (size == 15) {
+ size = read_varint64(self);
+ }
+ uint8_t type = get_ttype(size_and_type & 0x0f);
+ return rb_ary_new3(2, INT2FIX(type), INT2FIX(size));
+}
+
+VALUE rb_thrift_compact_proto_read_set_begin(VALUE self) {
+ return rb_thrift_compact_proto_read_list_begin(self);
+}
+
+VALUE rb_thrift_compact_proto_read_bool(VALUE self) {
+ VALUE bool_value = rb_ivar_get(self, bool_value_id);
+ if (NIL_P(bool_value)) {
+ return read_byte_direct(self) == CTYPE_BOOLEAN_TRUE ? Qtrue : Qfalse;
+ } else {
+ rb_ivar_set(self, bool_value_id, Qnil);
+ return bool_value;
+ }
+}
+
+VALUE rb_thrift_compact_proto_read_byte(VALUE self) {
+ return INT2FIX(read_byte_direct(self));
+}
+
+VALUE rb_thrift_compact_proto_read_i16(VALUE self) {
+ return INT2FIX(read_i16(self));
+}
+
+VALUE rb_thrift_compact_proto_read_i32(VALUE self) {
+ return INT2NUM(zig_zag_to_int(read_varint64(self)));
+}
+
+VALUE rb_thrift_compact_proto_read_i64(VALUE self) {
+ return LL2NUM(zig_zag_to_ll(read_varint64(self)));
+}
+
+VALUE rb_thrift_compact_proto_read_double(VALUE self) {
+ union {
+ double f;
+ int64_t l;
+ } transfer;
+ VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id);
+ rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(8));
+ uint32_t lo = ((uint8_t)(RSTRING_PTR(rbuf)[0]))
+ | (((uint8_t)(RSTRING_PTR(rbuf)[1])) << 8)
+ | (((uint8_t)(RSTRING_PTR(rbuf)[2])) << 16)
+ | (((uint8_t)(RSTRING_PTR(rbuf)[3])) << 24);
+ uint64_t hi = (((uint8_t)(RSTRING_PTR(rbuf)[4])))
+ | (((uint8_t)(RSTRING_PTR(rbuf)[5])) << 8)
+ | (((uint8_t)(RSTRING_PTR(rbuf)[6])) << 16)
+ | (((uint8_t)(RSTRING_PTR(rbuf)[7])) << 24);
+ transfer.l = (hi << 32) | lo;
+
+ return rb_float_new(transfer.f);
+}
+
+VALUE rb_thrift_compact_proto_read_string(VALUE self) {
+ VALUE buffer = rb_thrift_compact_proto_read_binary(self);
+ return convert_to_string(buffer);
+}
+
+VALUE rb_thrift_compact_proto_read_binary(VALUE self) {
+ int64_t size = read_varint64(self);
+ return READ(self, size);
+}
+
+static void Init_constants() {
+ thrift_compact_protocol_class = rb_const_get(thrift_module, rb_intern("CompactProtocol"));
+
+ VERSION = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("VERSION")));
+ VERSION_MASK = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("VERSION_MASK")));
+ TYPE_MASK = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("TYPE_MASK")));
+ TYPE_BITS = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("TYPE_BITS")));
+ TYPE_SHIFT_AMOUNT = FIX2INT(rb_const_get(thrift_compact_protocol_class, rb_intern("TYPE_SHIFT_AMOUNT")));
+ PROTOCOL_ID = FIX2INT(rb_const_get(thrift_compact_protocol_class, rb_intern("PROTOCOL_ID")));
+
+ last_field_id = rb_intern("@last_field");
+ boolean_field_id = rb_intern("@boolean_field");
+ bool_value_id = rb_intern("@bool_value");
+ rbuf_ivar_id = rb_intern("@rbuf");
+}
+
+static void Init_rb_methods() {
+ rb_define_method(thrift_compact_protocol_class, "native?", rb_thrift_compact_proto_native_qmark, 0);
+
+ rb_define_method(thrift_compact_protocol_class, "write_message_begin", rb_thrift_compact_proto_write_message_begin, 3);
+ rb_define_method(thrift_compact_protocol_class, "write_field_begin", rb_thrift_compact_proto_write_field_begin, 3);
+ rb_define_method(thrift_compact_protocol_class, "write_field_stop", rb_thrift_compact_proto_write_field_stop, 0);
+ rb_define_method(thrift_compact_protocol_class, "write_map_begin", rb_thrift_compact_proto_write_map_begin, 3);
+ rb_define_method(thrift_compact_protocol_class, "write_list_begin", rb_thrift_compact_proto_write_list_begin, 2);
+ rb_define_method(thrift_compact_protocol_class, "write_set_begin", rb_thrift_compact_proto_write_set_begin, 2);
+ rb_define_method(thrift_compact_protocol_class, "write_byte", rb_thrift_compact_proto_write_byte, 1);
+ rb_define_method(thrift_compact_protocol_class, "write_bool", rb_thrift_compact_proto_write_bool, 1);
+ rb_define_method(thrift_compact_protocol_class, "write_i16", rb_thrift_compact_proto_write_i16, 1);
+ rb_define_method(thrift_compact_protocol_class, "write_i32", rb_thrift_compact_proto_write_i32, 1);
+ rb_define_method(thrift_compact_protocol_class, "write_i64", rb_thrift_compact_proto_write_i64, 1);
+ rb_define_method(thrift_compact_protocol_class, "write_double", rb_thrift_compact_proto_write_double, 1);
+ rb_define_method(thrift_compact_protocol_class, "write_string", rb_thrift_compact_proto_write_string, 1);
+ rb_define_method(thrift_compact_protocol_class, "write_binary", rb_thrift_compact_proto_write_binary, 1);
+
+ rb_define_method(thrift_compact_protocol_class, "write_message_end", rb_thrift_compact_proto_write_message_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "write_struct_begin", rb_thrift_compact_proto_write_struct_begin, 1);
+ rb_define_method(thrift_compact_protocol_class, "write_struct_end", rb_thrift_compact_proto_write_struct_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "write_field_end", rb_thrift_compact_proto_write_field_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "write_map_end", rb_thrift_compact_proto_write_map_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "write_list_end", rb_thrift_compact_proto_write_list_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "write_set_end", rb_thrift_compact_proto_write_set_end, 0);
+
+
+ rb_define_method(thrift_compact_protocol_class, "read_message_begin", rb_thrift_compact_proto_read_message_begin, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_field_begin", rb_thrift_compact_proto_read_field_begin, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_map_begin", rb_thrift_compact_proto_read_map_begin, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_list_begin", rb_thrift_compact_proto_read_list_begin, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_set_begin", rb_thrift_compact_proto_read_set_begin, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_byte", rb_thrift_compact_proto_read_byte, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_bool", rb_thrift_compact_proto_read_bool, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_i16", rb_thrift_compact_proto_read_i16, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_i32", rb_thrift_compact_proto_read_i32, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_i64", rb_thrift_compact_proto_read_i64, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_double", rb_thrift_compact_proto_read_double, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_string", rb_thrift_compact_proto_read_string, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_binary", rb_thrift_compact_proto_read_binary, 0);
+
+ rb_define_method(thrift_compact_protocol_class, "read_message_end", rb_thrift_compact_proto_read_message_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_struct_begin", rb_thrift_compact_proto_read_struct_begin, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_struct_end", rb_thrift_compact_proto_read_struct_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_field_end", rb_thrift_compact_proto_read_field_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_map_end", rb_thrift_compact_proto_read_map_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_list_end", rb_thrift_compact_proto_read_list_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_set_end", rb_thrift_compact_proto_read_set_end, 0);
+}
+
+void Init_compact_protocol() {
+ Init_constants();
+ Init_rb_methods();
+}
diff --git a/src/jaegertracing/thrift/lib/rb/ext/compact_protocol.h b/src/jaegertracing/thrift/lib/rb/ext/compact_protocol.h
new file mode 100644
index 000000000..163915e94
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/compact_protocol.h
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+void Init_compact_protocol();
diff --git a/src/jaegertracing/thrift/lib/rb/ext/constants.h b/src/jaegertracing/thrift/lib/rb/ext/constants.h
new file mode 100644
index 000000000..e7aec4478
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/constants.h
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+extern int TTYPE_STOP;
+extern int TTYPE_BOOL;
+extern int TTYPE_BYTE;
+extern int TTYPE_I16;
+extern int TTYPE_I32;
+extern int TTYPE_I64;
+extern int TTYPE_DOUBLE;
+extern int TTYPE_STRING;
+extern int TTYPE_MAP;
+extern int TTYPE_SET;
+extern int TTYPE_LIST;
+extern int TTYPE_STRUCT;
+
+extern ID validate_method_id;
+extern ID write_struct_begin_method_id;
+extern ID write_struct_end_method_id;
+extern ID write_field_begin_method_id;
+extern ID write_field_end_method_id;
+extern ID write_boolean_method_id;
+extern ID write_byte_method_id;
+extern ID write_i16_method_id;
+extern ID write_i32_method_id;
+extern ID write_i64_method_id;
+extern ID write_double_method_id;
+extern ID write_string_method_id;
+extern ID write_binary_method_id;
+extern ID write_map_begin_method_id;
+extern ID write_map_end_method_id;
+extern ID write_list_begin_method_id;
+extern ID write_list_end_method_id;
+extern ID write_set_begin_method_id;
+extern ID write_set_end_method_id;
+extern ID read_bool_method_id;
+extern ID read_byte_method_id;
+extern ID read_i16_method_id;
+extern ID read_i32_method_id;
+extern ID read_i64_method_id;
+extern ID read_string_method_id;
+extern ID read_binary_method_id;
+extern ID read_double_method_id;
+extern ID read_map_begin_method_id;
+extern ID read_map_end_method_id;
+extern ID read_list_begin_method_id;
+extern ID read_list_end_method_id;
+extern ID read_set_begin_method_id;
+extern ID read_set_end_method_id;
+extern ID read_struct_begin_method_id;
+extern ID read_struct_end_method_id;
+extern ID read_field_begin_method_id;
+extern ID read_field_end_method_id;
+extern ID keys_method_id;
+extern ID entries_method_id;
+extern ID write_field_stop_method_id;
+extern ID skip_method_id;
+extern ID write_method_id;
+extern ID read_all_method_id;
+extern ID read_into_buffer_method_id;
+extern ID force_binary_encoding_id;
+extern ID convert_to_utf8_byte_buffer_id;
+extern ID convert_to_string_id;
+
+extern ID fields_const_id;
+extern ID transport_ivar_id;
+extern ID strict_read_ivar_id;
+extern ID strict_write_ivar_id;
+
+extern VALUE type_sym;
+extern VALUE name_sym;
+extern VALUE key_sym;
+extern VALUE value_sym;
+extern VALUE element_sym;
+extern VALUE class_sym;
+extern VALUE binary_sym;
+
+extern VALUE rb_cSet;
+extern VALUE thrift_module;
+extern VALUE thrift_types_module;
+extern VALUE thrift_bytes_module;
+extern VALUE class_thrift_protocol;
+extern VALUE protocol_exception_class;
diff --git a/src/jaegertracing/thrift/lib/rb/ext/extconf.rb b/src/jaegertracing/thrift/lib/rb/ext/extconf.rb
new file mode 100644
index 000000000..b35f60bf3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/extconf.rb
@@ -0,0 +1,34 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
+ File.open('Makefile', 'w'){|f| f.puts "all:\n\ninstall:\n" }
+else
+ require 'mkmf'
+ require 'rbconfig'
+
+ $ARCH_FLAGS = RbConfig::CONFIG['CFLAGS'].scan( /(-arch )(\S+)/ ).map{|x,y| x + y + ' ' }.join('')
+
+
+ $CFLAGS = "-fsigned-char -g -O2 -Wall -Werror " + $ARCH_FLAGS
+
+ have_func("strlcpy", "string.h")
+
+ create_makefile 'thrift_native'
+end
diff --git a/src/jaegertracing/thrift/lib/rb/ext/macros.h b/src/jaegertracing/thrift/lib/rb/ext/macros.h
new file mode 100644
index 000000000..265f6930d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/macros.h
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define GET_TRANSPORT(obj) rb_ivar_get(obj, transport_ivar_id)
+#define GET_STRICT_READ(obj) rb_ivar_get(obj, strict_read_ivar_id)
+#define GET_STRICT_WRITE(obj) rb_ivar_get(obj, strict_write_ivar_id)
+#define WRITE(obj, data, length) rb_funcall(obj, write_method_id, 1, rb_str_new(data, length))
+#define CHECK_NIL(obj) if (NIL_P(obj)) { rb_raise(rb_eStandardError, "nil argument not allowed!");}
+#define READ(obj, length) rb_funcall(GET_TRANSPORT(obj), read_all_method_id, 1, INT2FIX(length))
+
+#ifndef RFLOAT_VALUE
+# define RFLOAT_VALUE(v) RFLOAT(rb_Float(v))->value
+#endif
+
+#ifndef RSTRING_LEN
+# define RSTRING_LEN(v) RSTRING(rb_String(v))->len
+#endif
+
+#ifndef RSTRING_PTR
+# define RSTRING_PTR(v) RSTRING(rb_String(v))->ptr
+#endif
+
+#ifndef RARRAY_LEN
+# define RARRAY_LEN(v) RARRAY(rb_Array(v))->len
+#endif
diff --git a/src/jaegertracing/thrift/lib/rb/ext/memory_buffer.c b/src/jaegertracing/thrift/lib/rb/ext/memory_buffer.c
new file mode 100644
index 000000000..8b52c4a3e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/memory_buffer.c
@@ -0,0 +1,134 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <ruby.h>
+#include <constants.h>
+#include <bytes.h>
+#include <macros.h>
+
+ID buf_ivar_id;
+ID index_ivar_id;
+
+ID slice_method_id;
+
+int GARBAGE_BUFFER_SIZE;
+
+#define GET_BUF(self) rb_ivar_get(self, buf_ivar_id)
+
+VALUE rb_thrift_memory_buffer_write(VALUE self, VALUE str);
+VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value);
+VALUE rb_thrift_memory_buffer_read_byte(VALUE self);
+VALUE rb_thrift_memory_buffer_read_into_buffer(VALUE self, VALUE buffer_value, VALUE size_value);
+
+VALUE rb_thrift_memory_buffer_write(VALUE self, VALUE str) {
+ VALUE buf = GET_BUF(self);
+ str = force_binary_encoding(str);
+ rb_str_buf_cat(buf, StringValuePtr(str), RSTRING_LEN(str));
+ return Qnil;
+}
+
+VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value) {
+ int length = FIX2INT(length_value);
+
+ VALUE index_value = rb_ivar_get(self, index_ivar_id);
+ int index = FIX2INT(index_value);
+
+ VALUE buf = GET_BUF(self);
+ VALUE data = rb_funcall(buf, slice_method_id, 2, index_value, length_value);
+
+ index += length;
+ if (index > RSTRING_LEN(buf)) {
+ index = RSTRING_LEN(buf);
+ }
+ if (index >= GARBAGE_BUFFER_SIZE) {
+ rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1)));
+ index = 0;
+ }
+ rb_ivar_set(self, index_ivar_id, INT2FIX(index));
+
+ if (RSTRING_LEN(data) < length) {
+ rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
+ }
+
+ return data;
+}
+
+VALUE rb_thrift_memory_buffer_read_byte(VALUE self) {
+ VALUE index_value = rb_ivar_get(self, index_ivar_id);
+ int index = FIX2INT(index_value);
+
+ VALUE buf = GET_BUF(self);
+ if (index >= RSTRING_LEN(buf)) {
+ rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
+ }
+ char byte = RSTRING_PTR(buf)[index++];
+
+ if (index >= GARBAGE_BUFFER_SIZE) {
+ rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1)));
+ index = 0;
+ }
+ rb_ivar_set(self, index_ivar_id, INT2FIX(index));
+
+ int result = (int) byte;
+ return INT2FIX(result);
+}
+
+VALUE rb_thrift_memory_buffer_read_into_buffer(VALUE self, VALUE buffer_value, VALUE size_value) {
+ int i = 0;
+ int size = FIX2INT(size_value);
+ int index;
+ VALUE buf = GET_BUF(self);
+
+ index = FIX2INT(rb_ivar_get(self, index_ivar_id));
+ while (i < size) {
+ if (index >= RSTRING_LEN(buf)) {
+ rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
+ }
+ char byte = RSTRING_PTR(buf)[index++];
+
+ if (i >= RSTRING_LEN(buffer_value)) {
+ rb_raise(rb_eIndexError, "index %d out of string", i);
+ }
+ ((char*)RSTRING_PTR(buffer_value))[i] = byte;
+ i++;
+ }
+
+ if (index >= GARBAGE_BUFFER_SIZE) {
+ rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1)));
+ index = 0;
+ }
+ rb_ivar_set(self, index_ivar_id, INT2FIX(index));
+
+ return INT2FIX(i);
+}
+
+void Init_memory_buffer() {
+ VALUE thrift_memory_buffer_class = rb_const_get(thrift_module, rb_intern("MemoryBufferTransport"));
+ rb_define_method(thrift_memory_buffer_class, "write", rb_thrift_memory_buffer_write, 1);
+ rb_define_method(thrift_memory_buffer_class, "read", rb_thrift_memory_buffer_read, 1);
+ rb_define_method(thrift_memory_buffer_class, "read_byte", rb_thrift_memory_buffer_read_byte, 0);
+ rb_define_method(thrift_memory_buffer_class, "read_into_buffer", rb_thrift_memory_buffer_read_into_buffer, 2);
+
+ buf_ivar_id = rb_intern("@buf");
+ index_ivar_id = rb_intern("@index");
+
+ slice_method_id = rb_intern("slice");
+
+ GARBAGE_BUFFER_SIZE = FIX2INT(rb_const_get(thrift_memory_buffer_class, rb_intern("GARBAGE_BUFFER_SIZE")));
+}
diff --git a/src/jaegertracing/thrift/lib/rb/ext/memory_buffer.h b/src/jaegertracing/thrift/lib/rb/ext/memory_buffer.h
new file mode 100644
index 000000000..b277fa6f6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/memory_buffer.h
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+void Init_memory_buffer();
diff --git a/src/jaegertracing/thrift/lib/rb/ext/protocol.c b/src/jaegertracing/thrift/lib/rb/ext/protocol.c
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/protocol.c
diff --git a/src/jaegertracing/thrift/lib/rb/ext/protocol.h b/src/jaegertracing/thrift/lib/rb/ext/protocol.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/protocol.h
diff --git a/src/jaegertracing/thrift/lib/rb/ext/strlcpy.c b/src/jaegertracing/thrift/lib/rb/ext/strlcpy.c
new file mode 100644
index 000000000..6700ff2d4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/strlcpy.c
@@ -0,0 +1,41 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "strlcpy.h"
+
+#ifndef HAVE_STRLCPY
+#define HAVE_STRLCPY
+size_t
+strlcpy (char *dst, const char *src, size_t dst_sz)
+{
+ size_t n;
+
+ for (n = 0; n < dst_sz; n++) {
+ if ((*dst++ = *src++) == '\0')
+ break;
+ }
+
+ if (n < dst_sz)
+ return n;
+ if (n > 0)
+ *(dst - 1) = '\0';
+ return n + strlen (src);
+}
+#endif
+
diff --git a/src/jaegertracing/thrift/lib/rb/ext/strlcpy.h b/src/jaegertracing/thrift/lib/rb/ext/strlcpy.h
new file mode 100644
index 000000000..f6fe0fe6b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/strlcpy.h
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy (char *dst, const char *src, size_t dst_sz);
+#else
+#if !__has_builtin(strlcpy)
+extern size_t strlcpy(char *, const char *, size_t);
+#endif
+#endif
+
diff --git a/src/jaegertracing/thrift/lib/rb/ext/struct.c b/src/jaegertracing/thrift/lib/rb/ext/struct.c
new file mode 100644
index 000000000..e3aa855ed
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/struct.c
@@ -0,0 +1,711 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "struct.h"
+#include "constants.h"
+#include "macros.h"
+#include "strlcpy.h"
+
+VALUE thrift_union_class;
+
+ID setfield_id;
+ID setvalue_id;
+
+ID to_s_method_id;
+ID name_to_id_method_id;
+static ID sorted_field_ids_method_id;
+
+#define IS_CONTAINER(ttype) ((ttype) == TTYPE_MAP || (ttype) == TTYPE_LIST || (ttype) == TTYPE_SET)
+#define STRUCT_FIELDS(obj) rb_const_get(CLASS_OF(obj), fields_const_id)
+
+//-------------------------------------------
+// Writing section
+//-------------------------------------------
+
+// default fn pointers for protocol stuff here
+
+VALUE default_write_bool(VALUE protocol, VALUE value) {
+ rb_funcall(protocol, write_boolean_method_id, 1, value);
+ return Qnil;
+}
+
+VALUE default_write_byte(VALUE protocol, VALUE value) {
+ rb_funcall(protocol, write_byte_method_id, 1, value);
+ return Qnil;
+}
+
+VALUE default_write_i16(VALUE protocol, VALUE value) {
+ rb_funcall(protocol, write_i16_method_id, 1, value);
+ return Qnil;
+}
+
+VALUE default_write_i32(VALUE protocol, VALUE value) {
+ rb_funcall(protocol, write_i32_method_id, 1, value);
+ return Qnil;
+}
+
+VALUE default_write_i64(VALUE protocol, VALUE value) {
+ rb_funcall(protocol, write_i64_method_id, 1, value);
+ return Qnil;
+}
+
+VALUE default_write_double(VALUE protocol, VALUE value) {
+ rb_funcall(protocol, write_double_method_id, 1, value);
+ return Qnil;
+}
+
+VALUE default_write_string(VALUE protocol, VALUE value) {
+ rb_funcall(protocol, write_string_method_id, 1, value);
+ return Qnil;
+}
+
+VALUE default_write_binary(VALUE protocol, VALUE value) {
+ rb_funcall(protocol, write_binary_method_id, 1, value);
+ return Qnil;
+}
+
+VALUE default_write_list_begin(VALUE protocol, VALUE etype, VALUE length) {
+ rb_funcall(protocol, write_list_begin_method_id, 2, etype, length);
+ return Qnil;
+}
+
+VALUE default_write_list_end(VALUE protocol) {
+ rb_funcall(protocol, write_list_end_method_id, 0);
+ return Qnil;
+}
+
+VALUE default_write_set_begin(VALUE protocol, VALUE etype, VALUE length) {
+ rb_funcall(protocol, write_set_begin_method_id, 2, etype, length);
+ return Qnil;
+}
+
+VALUE default_write_set_end(VALUE protocol) {
+ rb_funcall(protocol, write_set_end_method_id, 0);
+ return Qnil;
+}
+
+VALUE default_write_map_begin(VALUE protocol, VALUE ktype, VALUE vtype, VALUE length) {
+ rb_funcall(protocol, write_map_begin_method_id, 3, ktype, vtype, length);
+ return Qnil;
+}
+
+VALUE default_write_map_end(VALUE protocol) {
+ rb_funcall(protocol, write_map_end_method_id, 0);
+ return Qnil;
+}
+
+VALUE default_write_struct_begin(VALUE protocol, VALUE struct_name) {
+ rb_funcall(protocol, write_struct_begin_method_id, 1, struct_name);
+ return Qnil;
+}
+
+VALUE default_write_struct_end(VALUE protocol) {
+ rb_funcall(protocol, write_struct_end_method_id, 0);
+ return Qnil;
+}
+
+VALUE default_write_field_begin(VALUE protocol, VALUE name, VALUE type, VALUE id) {
+ rb_funcall(protocol, write_field_begin_method_id, 3, name, type, id);
+ return Qnil;
+}
+
+VALUE default_write_field_end(VALUE protocol) {
+ rb_funcall(protocol, write_field_end_method_id, 0);
+ return Qnil;
+}
+
+VALUE default_write_field_stop(VALUE protocol) {
+ rb_funcall(protocol, write_field_stop_method_id, 0);
+ return Qnil;
+}
+
+VALUE default_read_field_begin(VALUE protocol) {
+ return rb_funcall(protocol, read_field_begin_method_id, 0);
+}
+
+VALUE default_read_field_end(VALUE protocol) {
+ return rb_funcall(protocol, read_field_end_method_id, 0);
+}
+
+VALUE default_read_map_begin(VALUE protocol) {
+ return rb_funcall(protocol, read_map_begin_method_id, 0);
+}
+
+VALUE default_read_map_end(VALUE protocol) {
+ return rb_funcall(protocol, read_map_end_method_id, 0);
+}
+
+VALUE default_read_list_begin(VALUE protocol) {
+ return rb_funcall(protocol, read_list_begin_method_id, 0);
+}
+
+VALUE default_read_list_end(VALUE protocol) {
+ return rb_funcall(protocol, read_list_end_method_id, 0);
+}
+
+VALUE default_read_set_begin(VALUE protocol) {
+ return rb_funcall(protocol, read_set_begin_method_id, 0);
+}
+
+VALUE default_read_set_end(VALUE protocol) {
+ return rb_funcall(protocol, read_set_end_method_id, 0);
+}
+
+VALUE default_read_byte(VALUE protocol) {
+ return rb_funcall(protocol, read_byte_method_id, 0);
+}
+
+VALUE default_read_bool(VALUE protocol) {
+ return rb_funcall(protocol, read_bool_method_id, 0);
+}
+
+VALUE default_read_i16(VALUE protocol) {
+ return rb_funcall(protocol, read_i16_method_id, 0);
+}
+
+VALUE default_read_i32(VALUE protocol) {
+ return rb_funcall(protocol, read_i32_method_id, 0);
+}
+
+VALUE default_read_i64(VALUE protocol) {
+ return rb_funcall(protocol, read_i64_method_id, 0);
+}
+
+VALUE default_read_double(VALUE protocol) {
+ return rb_funcall(protocol, read_double_method_id, 0);
+}
+
+VALUE default_read_string(VALUE protocol) {
+ return rb_funcall(protocol, read_string_method_id, 0);
+}
+
+VALUE default_read_binary(VALUE protocol) {
+ return rb_funcall(protocol, read_binary_method_id, 0);
+}
+
+VALUE default_read_struct_begin(VALUE protocol) {
+ return rb_funcall(protocol, read_struct_begin_method_id, 0);
+}
+
+VALUE default_read_struct_end(VALUE protocol) {
+ return rb_funcall(protocol, read_struct_end_method_id, 0);
+}
+
+// end default protocol methods
+
+static VALUE rb_thrift_union_write (VALUE self, VALUE protocol);
+static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol);
+static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info);
+
+VALUE get_field_value(VALUE obj, VALUE field_name) {
+ char name_buf[RSTRING_LEN(field_name) + 2];
+
+ name_buf[0] = '@';
+ strlcpy(&name_buf[1], RSTRING_PTR(field_name), RSTRING_LEN(field_name) + 1);
+
+ VALUE value = rb_ivar_get(obj, rb_intern(name_buf));
+
+ return value;
+}
+
+static void write_container(int ttype, VALUE field_info, VALUE value, VALUE protocol) {
+ int sz, i;
+
+ if (ttype == TTYPE_MAP) {
+ VALUE keys;
+ VALUE key;
+ VALUE val;
+
+ Check_Type(value, T_HASH);
+
+ VALUE key_info = rb_hash_aref(field_info, key_sym);
+ VALUE keytype_value = rb_hash_aref(key_info, type_sym);
+ int keytype = FIX2INT(keytype_value);
+
+ VALUE value_info = rb_hash_aref(field_info, value_sym);
+ VALUE valuetype_value = rb_hash_aref(value_info, type_sym);
+ int valuetype = FIX2INT(valuetype_value);
+
+ keys = rb_funcall(value, keys_method_id, 0);
+
+ sz = RARRAY_LEN(keys);
+
+ default_write_map_begin(protocol, keytype_value, valuetype_value, INT2FIX(sz));
+
+ for (i = 0; i < sz; i++) {
+ key = rb_ary_entry(keys, i);
+ val = rb_hash_aref(value, key);
+
+ if (IS_CONTAINER(keytype)) {
+ write_container(keytype, key_info, key, protocol);
+ } else {
+ write_anything(keytype, key, protocol, key_info);
+ }
+
+ if (IS_CONTAINER(valuetype)) {
+ write_container(valuetype, value_info, val, protocol);
+ } else {
+ write_anything(valuetype, val, protocol, value_info);
+ }
+ }
+
+ default_write_map_end(protocol);
+ } else if (ttype == TTYPE_LIST) {
+ Check_Type(value, T_ARRAY);
+
+ sz = RARRAY_LEN(value);
+
+ VALUE element_type_info = rb_hash_aref(field_info, element_sym);
+ VALUE element_type_value = rb_hash_aref(element_type_info, type_sym);
+ int element_type = FIX2INT(element_type_value);
+
+ default_write_list_begin(protocol, element_type_value, INT2FIX(sz));
+ for (i = 0; i < sz; ++i) {
+ VALUE val = rb_ary_entry(value, i);
+ if (IS_CONTAINER(element_type)) {
+ write_container(element_type, element_type_info, val, protocol);
+ } else {
+ write_anything(element_type, val, protocol, element_type_info);
+ }
+ }
+ default_write_list_end(protocol);
+ } else if (ttype == TTYPE_SET) {
+ VALUE items;
+
+ if (TYPE(value) == T_ARRAY) {
+ items = value;
+ } else {
+ if (rb_cSet == CLASS_OF(value)) {
+ items = rb_funcall(value, entries_method_id, 0);
+ } else {
+ Check_Type(value, T_HASH);
+ items = rb_funcall(value, keys_method_id, 0);
+ }
+ }
+
+ sz = RARRAY_LEN(items);
+
+ VALUE element_type_info = rb_hash_aref(field_info, element_sym);
+ VALUE element_type_value = rb_hash_aref(element_type_info, type_sym);
+ int element_type = FIX2INT(element_type_value);
+
+ default_write_set_begin(protocol, element_type_value, INT2FIX(sz));
+
+ for (i = 0; i < sz; i++) {
+ VALUE val = rb_ary_entry(items, i);
+ if (IS_CONTAINER(element_type)) {
+ write_container(element_type, element_type_info, val, protocol);
+ } else {
+ write_anything(element_type, val, protocol, element_type_info);
+ }
+ }
+
+ default_write_set_end(protocol);
+ } else {
+ rb_raise(rb_eNotImpError, "can't write container of type: %d", ttype);
+ }
+}
+
+static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info) {
+ if (ttype == TTYPE_BOOL) {
+ default_write_bool(protocol, value);
+ } else if (ttype == TTYPE_BYTE) {
+ default_write_byte(protocol, value);
+ } else if (ttype == TTYPE_I16) {
+ default_write_i16(protocol, value);
+ } else if (ttype == TTYPE_I32) {
+ default_write_i32(protocol, value);
+ } else if (ttype == TTYPE_I64) {
+ default_write_i64(protocol, value);
+ } else if (ttype == TTYPE_DOUBLE) {
+ default_write_double(protocol, value);
+ } else if (ttype == TTYPE_STRING) {
+ VALUE is_binary = rb_hash_aref(field_info, binary_sym);
+ if (is_binary != Qtrue) {
+ default_write_string(protocol, value);
+ } else {
+ default_write_binary(protocol, value);
+ }
+ } else if (IS_CONTAINER(ttype)) {
+ write_container(ttype, field_info, value, protocol);
+ } else if (ttype == TTYPE_STRUCT) {
+ if (rb_obj_is_kind_of(value, thrift_union_class)) {
+ rb_thrift_union_write(value, protocol);
+ } else {
+ rb_thrift_struct_write(value, protocol);
+ }
+ } else {
+ rb_raise(rb_eNotImpError, "Unknown type for binary_encoding: %d", ttype);
+ }
+}
+
+static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol) {
+ // call validate
+ rb_funcall(self, validate_method_id, 0);
+
+ // write struct begin
+ default_write_struct_begin(protocol, rb_class_name(CLASS_OF(self)));
+
+ // iterate through all the fields here
+ VALUE struct_fields = STRUCT_FIELDS(self);
+ VALUE sorted_field_ids = rb_funcall(self, sorted_field_ids_method_id, 0);
+
+ int i = 0;
+ for (i=0; i < RARRAY_LEN(sorted_field_ids); i++) {
+ VALUE field_id = rb_ary_entry(sorted_field_ids, i);
+
+ VALUE field_info = rb_hash_aref(struct_fields, field_id);
+
+ VALUE ttype_value = rb_hash_aref(field_info, type_sym);
+ int ttype = FIX2INT(ttype_value);
+ VALUE field_name = rb_hash_aref(field_info, name_sym);
+
+ VALUE field_value = get_field_value(self, field_name);
+
+ if (!NIL_P(field_value)) {
+ default_write_field_begin(protocol, field_name, ttype_value, field_id);
+
+ write_anything(ttype, field_value, protocol, field_info);
+
+ default_write_field_end(protocol);
+ }
+ }
+
+ default_write_field_stop(protocol);
+
+ // write struct end
+ default_write_struct_end(protocol);
+
+ return Qnil;
+}
+
+//-------------------------------------------
+// Reading section
+//-------------------------------------------
+
+static VALUE rb_thrift_union_read(VALUE self, VALUE protocol);
+static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol);
+static void skip_map_contents(VALUE protocol, VALUE key_type_value, VALUE value_type_value, int size);
+static void skip_list_or_set_contents(VALUE protocol, VALUE element_type_value, int size);
+
+static void set_field_value(VALUE obj, VALUE field_name, VALUE value) {
+ char name_buf[RSTRING_LEN(field_name) + 2];
+
+ name_buf[0] = '@';
+ strlcpy(&name_buf[1], RSTRING_PTR(field_name), RSTRING_LEN(field_name)+1);
+
+ rb_ivar_set(obj, rb_intern(name_buf), value);
+}
+
+// Helper method to skip the contents of a map (assumes the map header has been read).
+static void skip_map_contents(VALUE protocol, VALUE key_type_value, VALUE value_type_value, int size) {
+ int i;
+ for (i = 0; i < size; i++) {
+ rb_funcall(protocol, skip_method_id, 1, key_type_value);
+ rb_funcall(protocol, skip_method_id, 1, value_type_value);
+ }
+}
+
+// Helper method to skip the contents of a list or set (assumes the list/set header has been read).
+static void skip_list_or_set_contents(VALUE protocol, VALUE element_type_value, int size) {
+ int i;
+ for (i = 0; i < size; i++) {
+ rb_funcall(protocol, skip_method_id, 1, element_type_value);
+ }
+}
+
+static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
+ VALUE result = Qnil;
+
+ if (ttype == TTYPE_BOOL) {
+ result = default_read_bool(protocol);
+ } else if (ttype == TTYPE_BYTE) {
+ result = default_read_byte(protocol);
+ } else if (ttype == TTYPE_I16) {
+ result = default_read_i16(protocol);
+ } else if (ttype == TTYPE_I32) {
+ result = default_read_i32(protocol);
+ } else if (ttype == TTYPE_I64) {
+ result = default_read_i64(protocol);
+ } else if (ttype == TTYPE_STRING) {
+ VALUE is_binary = rb_hash_aref(field_info, binary_sym);
+ if (is_binary != Qtrue) {
+ result = default_read_string(protocol);
+ } else {
+ result = default_read_binary(protocol);
+ }
+ } else if (ttype == TTYPE_DOUBLE) {
+ result = default_read_double(protocol);
+ } else if (ttype == TTYPE_STRUCT) {
+ VALUE klass = rb_hash_aref(field_info, class_sym);
+ result = rb_class_new_instance(0, NULL, klass);
+
+ if (rb_obj_is_kind_of(result, thrift_union_class)) {
+ rb_thrift_union_read(result, protocol);
+ } else {
+ rb_thrift_struct_read(result, protocol);
+ }
+ } else if (ttype == TTYPE_MAP) {
+ int i;
+
+ VALUE map_header = default_read_map_begin(protocol);
+ int key_ttype = FIX2INT(rb_ary_entry(map_header, 0));
+ int value_ttype = FIX2INT(rb_ary_entry(map_header, 1));
+ int num_entries = FIX2INT(rb_ary_entry(map_header, 2));
+
+ // Check the declared key and value types against the expected ones and skip the map contents
+ // if the types don't match.
+ VALUE key_info = rb_hash_aref(field_info, key_sym);
+ VALUE value_info = rb_hash_aref(field_info, value_sym);
+
+ if (!NIL_P(key_info) && !NIL_P(value_info)) {
+ int specified_key_type = FIX2INT(rb_hash_aref(key_info, type_sym));
+ int specified_value_type = FIX2INT(rb_hash_aref(value_info, type_sym));
+ if (num_entries == 0 || (specified_key_type == key_ttype && specified_value_type == value_ttype)) {
+ result = rb_hash_new();
+
+ for (i = 0; i < num_entries; ++i) {
+ VALUE key, val;
+
+ key = read_anything(protocol, key_ttype, key_info);
+ val = read_anything(protocol, value_ttype, value_info);
+
+ rb_hash_aset(result, key, val);
+ }
+ } else {
+ skip_map_contents(protocol, INT2FIX(key_ttype), INT2FIX(value_ttype), num_entries);
+ }
+ } else {
+ skip_map_contents(protocol, INT2FIX(key_ttype), INT2FIX(value_ttype), num_entries);
+ }
+
+ default_read_map_end(protocol);
+ } else if (ttype == TTYPE_LIST) {
+ int i;
+
+ VALUE list_header = default_read_list_begin(protocol);
+ int element_ttype = FIX2INT(rb_ary_entry(list_header, 0));
+ int num_elements = FIX2INT(rb_ary_entry(list_header, 1));
+
+ // Check the declared element type against the expected one and skip the list contents
+ // if the types don't match.
+ VALUE element_info = rb_hash_aref(field_info, element_sym);
+ if (!NIL_P(element_info)) {
+ int specified_element_type = FIX2INT(rb_hash_aref(element_info, type_sym));
+ if (specified_element_type == element_ttype) {
+ result = rb_ary_new2(num_elements);
+
+ for (i = 0; i < num_elements; ++i) {
+ rb_ary_push(result, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
+ }
+ } else {
+ skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
+ }
+ } else {
+ skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
+ }
+
+ default_read_list_end(protocol);
+ } else if (ttype == TTYPE_SET) {
+ VALUE items;
+ int i;
+
+ VALUE set_header = default_read_set_begin(protocol);
+ int element_ttype = FIX2INT(rb_ary_entry(set_header, 0));
+ int num_elements = FIX2INT(rb_ary_entry(set_header, 1));
+
+ // Check the declared element type against the expected one and skip the set contents
+ // if the types don't match.
+ VALUE element_info = rb_hash_aref(field_info, element_sym);
+ if (!NIL_P(element_info)) {
+ int specified_element_type = FIX2INT(rb_hash_aref(element_info, type_sym));
+ if (specified_element_type == element_ttype) {
+ items = rb_ary_new2(num_elements);
+
+ for (i = 0; i < num_elements; ++i) {
+ rb_ary_push(items, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
+ }
+
+ result = rb_class_new_instance(1, &items, rb_cSet);
+ } else {
+ skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
+ }
+ } else {
+ skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
+ }
+
+ default_read_set_end(protocol);
+ } else {
+ rb_raise(rb_eNotImpError, "read_anything not implemented for type %d!", ttype);
+ }
+
+ return result;
+}
+
+static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol) {
+ // read struct begin
+ default_read_struct_begin(protocol);
+
+ VALUE struct_fields = STRUCT_FIELDS(self);
+
+ // read each field
+ while (true) {
+ VALUE field_header = default_read_field_begin(protocol);
+ VALUE field_type_value = rb_ary_entry(field_header, 1);
+ int field_type = FIX2INT(field_type_value);
+
+ if (field_type == TTYPE_STOP) {
+ break;
+ }
+
+ // make sure we got a type we expected
+ VALUE field_info = rb_hash_aref(struct_fields, rb_ary_entry(field_header, 2));
+
+ if (!NIL_P(field_info)) {
+ int specified_type = FIX2INT(rb_hash_aref(field_info, type_sym));
+ if (field_type == specified_type) {
+ // read the value
+ VALUE name = rb_hash_aref(field_info, name_sym);
+ set_field_value(self, name, read_anything(protocol, field_type, field_info));
+ } else {
+ rb_funcall(protocol, skip_method_id, 1, field_type_value);
+ }
+ } else {
+ rb_funcall(protocol, skip_method_id, 1, field_type_value);
+ }
+
+ // read field end
+ default_read_field_end(protocol);
+ }
+
+ // read struct end
+ default_read_struct_end(protocol);
+
+ // call validate
+ rb_funcall(self, validate_method_id, 0);
+
+ return Qnil;
+}
+
+
+// --------------------------------
+// Union section
+// --------------------------------
+
+static VALUE rb_thrift_union_read(VALUE self, VALUE protocol) {
+ // read struct begin
+ default_read_struct_begin(protocol);
+
+ VALUE struct_fields = STRUCT_FIELDS(self);
+
+ VALUE field_header = default_read_field_begin(protocol);
+ VALUE field_type_value = rb_ary_entry(field_header, 1);
+ int field_type = FIX2INT(field_type_value);
+
+ // make sure we got a type we expected
+ VALUE field_info = rb_hash_aref(struct_fields, rb_ary_entry(field_header, 2));
+
+ if (!NIL_P(field_info)) {
+ int specified_type = FIX2INT(rb_hash_aref(field_info, type_sym));
+ if (field_type == specified_type) {
+ // read the value
+ VALUE name = rb_hash_aref(field_info, name_sym);
+ rb_iv_set(self, "@setfield", rb_str_intern(name));
+ rb_iv_set(self, "@value", read_anything(protocol, field_type, field_info));
+ } else {
+ rb_funcall(protocol, skip_method_id, 1, field_type_value);
+ }
+ } else {
+ rb_funcall(protocol, skip_method_id, 1, field_type_value);
+ }
+
+ // read field end
+ default_read_field_end(protocol);
+
+ field_header = default_read_field_begin(protocol);
+ field_type_value = rb_ary_entry(field_header, 1);
+ field_type = FIX2INT(field_type_value);
+
+ if (field_type != TTYPE_STOP) {
+ rb_raise(rb_eRuntimeError, "too many fields in union!");
+ }
+
+ // read struct end
+ default_read_struct_end(protocol);
+
+ // call validate
+ rb_funcall(self, validate_method_id, 0);
+
+ return Qnil;
+}
+
+static VALUE rb_thrift_union_write(VALUE self, VALUE protocol) {
+ // call validate
+ rb_funcall(self, validate_method_id, 0);
+
+ // write struct begin
+ default_write_struct_begin(protocol, rb_class_name(CLASS_OF(self)));
+
+ VALUE struct_fields = STRUCT_FIELDS(self);
+
+ VALUE setfield = rb_ivar_get(self, setfield_id);
+ VALUE setvalue = rb_ivar_get(self, setvalue_id);
+ VALUE field_id = rb_funcall(self, name_to_id_method_id, 1, rb_funcall(setfield, to_s_method_id, 0));
+
+ VALUE field_info = rb_hash_aref(struct_fields, field_id);
+
+ if(NIL_P(field_info)) {
+ rb_raise(rb_eRuntimeError, "set_field is not valid for this union!");
+ }
+
+ VALUE ttype_value = rb_hash_aref(field_info, type_sym);
+ int ttype = FIX2INT(ttype_value);
+
+ default_write_field_begin(protocol, setfield, ttype_value, field_id);
+
+ write_anything(ttype, setvalue, protocol, field_info);
+
+ default_write_field_end(protocol);
+
+ default_write_field_stop(protocol);
+
+ // write struct end
+ default_write_struct_end(protocol);
+
+ return Qnil;
+}
+
+void Init_struct() {
+ VALUE struct_module = rb_const_get(thrift_module, rb_intern("Struct"));
+
+ rb_define_method(struct_module, "write", rb_thrift_struct_write, 1);
+ rb_define_method(struct_module, "read", rb_thrift_struct_read, 1);
+
+ thrift_union_class = rb_const_get(thrift_module, rb_intern("Union"));
+
+ rb_define_method(thrift_union_class, "write", rb_thrift_union_write, 1);
+ rb_define_method(thrift_union_class, "read", rb_thrift_union_read, 1);
+
+ setfield_id = rb_intern("@setfield");
+ setvalue_id = rb_intern("@value");
+
+ to_s_method_id = rb_intern("to_s");
+ name_to_id_method_id = rb_intern("name_to_id");
+ sorted_field_ids_method_id = rb_intern("sorted_field_ids");
+}
diff --git a/src/jaegertracing/thrift/lib/rb/ext/struct.h b/src/jaegertracing/thrift/lib/rb/ext/struct.h
new file mode 100644
index 000000000..4748be5cb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/struct.h
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+#include <stdbool.h>
+#include <ruby.h>
+
+void Init_struct();
+void Init_union();
diff --git a/src/jaegertracing/thrift/lib/rb/ext/thrift_native.c b/src/jaegertracing/thrift/lib/rb/ext/thrift_native.c
new file mode 100644
index 000000000..3430b7c25
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/thrift_native.c
@@ -0,0 +1,201 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <ruby.h>
+#include <bytes.h>
+#include <struct.h>
+#include <binary_protocol_accelerated.h>
+#include <compact_protocol.h>
+#include <memory_buffer.h>
+
+// cached classes/modules
+VALUE rb_cSet;
+VALUE thrift_module;
+VALUE thrift_bytes_module;
+VALUE thrift_types_module;
+
+// TType constants
+int TTYPE_STOP;
+int TTYPE_BOOL;
+int TTYPE_BYTE;
+int TTYPE_I16;
+int TTYPE_I32;
+int TTYPE_I64;
+int TTYPE_DOUBLE;
+int TTYPE_STRING;
+int TTYPE_MAP;
+int TTYPE_SET;
+int TTYPE_LIST;
+int TTYPE_STRUCT;
+
+// method ids
+ID validate_method_id;
+ID write_struct_begin_method_id;
+ID write_struct_end_method_id;
+ID write_field_begin_method_id;
+ID write_field_end_method_id;
+ID write_boolean_method_id;
+ID write_byte_method_id;
+ID write_i16_method_id;
+ID write_i32_method_id;
+ID write_i64_method_id;
+ID write_double_method_id;
+ID write_string_method_id;
+ID write_binary_method_id;
+ID write_map_begin_method_id;
+ID write_map_end_method_id;
+ID write_list_begin_method_id;
+ID write_list_end_method_id;
+ID write_set_begin_method_id;
+ID write_set_end_method_id;
+ID read_bool_method_id;
+ID read_byte_method_id;
+ID read_i16_method_id;
+ID read_i32_method_id;
+ID read_i64_method_id;
+ID read_string_method_id;
+ID read_binary_method_id;
+ID read_double_method_id;
+ID read_map_begin_method_id;
+ID read_map_end_method_id;
+ID read_list_begin_method_id;
+ID read_list_end_method_id;
+ID read_set_begin_method_id;
+ID read_set_end_method_id;
+ID read_struct_begin_method_id;
+ID read_struct_end_method_id;
+ID read_field_begin_method_id;
+ID read_field_end_method_id;
+ID keys_method_id;
+ID entries_method_id;
+ID write_field_stop_method_id;
+ID skip_method_id;
+ID write_method_id;
+ID read_all_method_id;
+ID read_into_buffer_method_id;
+ID force_binary_encoding_id;
+ID convert_to_utf8_byte_buffer_id;
+ID convert_to_string_id;
+
+// constant ids
+ID fields_const_id;
+ID transport_ivar_id;
+ID strict_read_ivar_id;
+ID strict_write_ivar_id;
+
+// cached symbols
+VALUE type_sym;
+VALUE name_sym;
+VALUE key_sym;
+VALUE value_sym;
+VALUE element_sym;
+VALUE class_sym;
+VALUE binary_sym;
+VALUE protocol_exception_class;
+
+void Init_thrift_native() {
+ // cached classes
+ thrift_module = rb_const_get(rb_cObject, rb_intern("Thrift"));
+ thrift_bytes_module = rb_const_get(thrift_module, rb_intern("Bytes"));
+ thrift_types_module = rb_const_get(thrift_module, rb_intern("Types"));
+ rb_cSet = rb_const_get(rb_cObject, rb_intern("Set"));
+ protocol_exception_class = rb_const_get(thrift_module, rb_intern("ProtocolException"));
+
+ // Init ttype constants
+ TTYPE_BOOL = FIX2INT(rb_const_get(thrift_types_module, rb_intern("BOOL")));
+ TTYPE_BYTE = FIX2INT(rb_const_get(thrift_types_module, rb_intern("BYTE")));
+ TTYPE_I16 = FIX2INT(rb_const_get(thrift_types_module, rb_intern("I16")));
+ TTYPE_I32 = FIX2INT(rb_const_get(thrift_types_module, rb_intern("I32")));
+ TTYPE_I64 = FIX2INT(rb_const_get(thrift_types_module, rb_intern("I64")));
+ TTYPE_DOUBLE = FIX2INT(rb_const_get(thrift_types_module, rb_intern("DOUBLE")));
+ TTYPE_STRING = FIX2INT(rb_const_get(thrift_types_module, rb_intern("STRING")));
+ TTYPE_MAP = FIX2INT(rb_const_get(thrift_types_module, rb_intern("MAP")));
+ TTYPE_SET = FIX2INT(rb_const_get(thrift_types_module, rb_intern("SET")));
+ TTYPE_LIST = FIX2INT(rb_const_get(thrift_types_module, rb_intern("LIST")));
+ TTYPE_STRUCT = FIX2INT(rb_const_get(thrift_types_module, rb_intern("STRUCT")));
+
+ // method ids
+ validate_method_id = rb_intern("validate");
+ write_struct_begin_method_id = rb_intern("write_struct_begin");
+ write_struct_end_method_id = rb_intern("write_struct_end");
+ write_field_begin_method_id = rb_intern("write_field_begin");
+ write_field_end_method_id = rb_intern("write_field_end");
+ write_boolean_method_id = rb_intern("write_bool");
+ write_byte_method_id = rb_intern("write_byte");
+ write_i16_method_id = rb_intern("write_i16");
+ write_i32_method_id = rb_intern("write_i32");
+ write_i64_method_id = rb_intern("write_i64");
+ write_double_method_id = rb_intern("write_double");
+ write_string_method_id = rb_intern("write_string");
+ write_binary_method_id = rb_intern("write_binary");
+ write_map_begin_method_id = rb_intern("write_map_begin");
+ write_map_end_method_id = rb_intern("write_map_end");
+ write_list_begin_method_id = rb_intern("write_list_begin");
+ write_list_end_method_id = rb_intern("write_list_end");
+ write_set_begin_method_id = rb_intern("write_set_begin");
+ write_set_end_method_id = rb_intern("write_set_end");
+ read_bool_method_id = rb_intern("read_bool");
+ read_byte_method_id = rb_intern("read_byte");
+ read_i16_method_id = rb_intern("read_i16");
+ read_i32_method_id = rb_intern("read_i32");
+ read_i64_method_id = rb_intern("read_i64");
+ read_string_method_id = rb_intern("read_string");
+ read_binary_method_id = rb_intern("read_binary");
+ read_double_method_id = rb_intern("read_double");
+ read_map_begin_method_id = rb_intern("read_map_begin");
+ read_map_end_method_id = rb_intern("read_map_end");
+ read_list_begin_method_id = rb_intern("read_list_begin");
+ read_list_end_method_id = rb_intern("read_list_end");
+ read_set_begin_method_id = rb_intern("read_set_begin");
+ read_set_end_method_id = rb_intern("read_set_end");
+ read_struct_begin_method_id = rb_intern("read_struct_begin");
+ read_struct_end_method_id = rb_intern("read_struct_end");
+ read_field_begin_method_id = rb_intern("read_field_begin");
+ read_field_end_method_id = rb_intern("read_field_end");
+ keys_method_id = rb_intern("keys");
+ entries_method_id = rb_intern("entries");
+ write_field_stop_method_id = rb_intern("write_field_stop");
+ skip_method_id = rb_intern("skip");
+ write_method_id = rb_intern("write");
+ read_all_method_id = rb_intern("read_all");
+ read_into_buffer_method_id = rb_intern("read_into_buffer");
+ force_binary_encoding_id = rb_intern("force_binary_encoding");
+ convert_to_utf8_byte_buffer_id = rb_intern("convert_to_utf8_byte_buffer");
+ convert_to_string_id = rb_intern("convert_to_string");
+
+ // constant ids
+ fields_const_id = rb_intern("FIELDS");
+ transport_ivar_id = rb_intern("@trans");
+ strict_read_ivar_id = rb_intern("@strict_read");
+ strict_write_ivar_id = rb_intern("@strict_write");
+
+ // cached symbols
+ type_sym = ID2SYM(rb_intern("type"));
+ name_sym = ID2SYM(rb_intern("name"));
+ key_sym = ID2SYM(rb_intern("key"));
+ value_sym = ID2SYM(rb_intern("value"));
+ element_sym = ID2SYM(rb_intern("element"));
+ class_sym = ID2SYM(rb_intern("class"));
+ binary_sym = ID2SYM(rb_intern("binary"));
+
+ Init_struct();
+ Init_binary_protocol_accelerated();
+ Init_compact_protocol();
+ Init_memory_buffer();
+}
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift.rb
new file mode 100644
index 000000000..0f581229c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift.rb
@@ -0,0 +1,70 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# Contains some contributions under the Thrift Software License.
+# Please see doc/old-thrift-license.txt in the Thrift distribution for
+# details.
+
+$:.unshift File.dirname(__FILE__)
+
+require 'thrift/bytes'
+require 'thrift/core_ext'
+require 'thrift/exceptions'
+require 'thrift/types'
+require 'thrift/processor'
+require 'thrift/multiplexed_processor'
+require 'thrift/client'
+require 'thrift/struct'
+require 'thrift/union'
+require 'thrift/struct_union'
+
+# serializer
+require 'thrift/serializer/serializer'
+require 'thrift/serializer/deserializer'
+
+# protocol
+require 'thrift/protocol/base_protocol'
+require 'thrift/protocol/binary_protocol'
+require 'thrift/protocol/binary_protocol_accelerated'
+require 'thrift/protocol/compact_protocol'
+require 'thrift/protocol/json_protocol'
+require 'thrift/protocol/multiplexed_protocol'
+
+# transport
+require 'thrift/transport/base_transport'
+require 'thrift/transport/base_server_transport'
+require 'thrift/transport/socket'
+require 'thrift/transport/ssl_socket'
+require 'thrift/transport/server_socket'
+require 'thrift/transport/ssl_server_socket'
+require 'thrift/transport/unix_socket'
+require 'thrift/transport/unix_server_socket'
+require 'thrift/transport/buffered_transport'
+require 'thrift/transport/framed_transport'
+require 'thrift/transport/http_client_transport'
+require 'thrift/transport/io_stream_transport'
+require 'thrift/transport/memory_buffer_transport'
+
+# server
+require 'thrift/server/base_server'
+require 'thrift/server/nonblocking_server'
+require 'thrift/server/simple_server'
+require 'thrift/server/threaded_server'
+require 'thrift/server/thread_pool_server'
+
+require 'thrift/thrift_native'
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/bytes.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/bytes.rb
new file mode 100644
index 000000000..efd4f6440
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/bytes.rb
@@ -0,0 +1,131 @@
+# encoding: ascii-8bit
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Thrift
+ # A collection of utilities for working with bytes and byte buffers.
+ module Bytes
+ if RUBY_VERSION >= '1.9'
+ # Creates and empty byte buffer (String with BINARY encoding)
+ #
+ # size - The Integer size of the buffer (default: nil) to create
+ #
+ # Returns a String with BINARY encoding, filled with null characters
+ # if size is greater than zero
+ def self.empty_byte_buffer(size = nil)
+ if (size && size > 0)
+ "\0".force_encoding(Encoding::BINARY) * size
+ else
+ ''.force_encoding(Encoding::BINARY)
+ end
+ end
+
+ # Forces the encoding of the buffer to BINARY. If the buffer
+ # passed is frozen, then it will be duplicated.
+ #
+ # buffer - The String to force the encoding of.
+ #
+ # Returns the String passed with an encoding of BINARY; returned
+ # String may be a duplicate.
+ def self.force_binary_encoding(buffer)
+ buffer = buffer.dup if buffer.frozen?
+ buffer.force_encoding(Encoding::BINARY)
+ end
+
+ # Gets the byte value of a given position in a String.
+ #
+ # string - The String to retrive the byte value from.
+ # index - The Integer location of the byte value to retrieve.
+ #
+ # Returns an Integer value between 0 and 255.
+ def self.get_string_byte(string, index)
+ string.getbyte(index)
+ end
+
+ # Sets the byte value given to a given index in a String.
+ #
+ # string - The String to set the byte value in.
+ # index - The Integer location to set the byte value at.
+ # byte - The Integer value (0 to 255) to set in the string.
+ #
+ # Returns an Integer value of the byte value to set.
+ def self.set_string_byte(string, index, byte)
+ string.setbyte(index, byte)
+ end
+
+ # Converts the given String to a UTF-8 byte buffer.
+ #
+ # string - The String to convert.
+ #
+ # Returns a new String with BINARY encoding, containing the UTF-8
+ # bytes of the original string.
+ def self.convert_to_utf8_byte_buffer(string)
+ if string.encoding != Encoding::UTF_8
+ # transcode to UTF-8
+ string = string.encode(Encoding::UTF_8)
+ else
+ # encoding is already UTF-8, but a duplicate is needed
+ string = string.dup
+ end
+ string.force_encoding(Encoding::BINARY)
+ end
+
+ # Converts the given UTF-8 byte buffer into a String
+ #
+ # utf8_buffer - A String, with BINARY encoding, containing UTF-8 bytes
+ #
+ # Returns a new String with UTF-8 encoding,
+ def self.convert_to_string(utf8_buffer)
+ # duplicate the buffer, force encoding to UTF-8
+ utf8_buffer.dup.force_encoding(Encoding::UTF_8)
+ end
+ else
+ def self.empty_byte_buffer(size = nil)
+ if (size && size > 0)
+ "\0" * size
+ else
+ ''
+ end
+ end
+
+ def self.force_binary_encoding(buffer)
+ buffer
+ end
+
+ def self.get_string_byte(string, index)
+ string[index]
+ end
+
+ def self.set_string_byte(string, index, byte)
+ string[index] = byte
+ end
+
+ def self.convert_to_utf8_byte_buffer(string)
+ # This assumes $KCODE is 'UTF8'/'U', which would mean the String is already a UTF-8 byte buffer
+ # TODO consider handling other $KCODE values and transcoding with iconv
+ string
+ end
+
+ def self.convert_to_string(utf8_buffer)
+ # See comment in 'convert_to_utf8_byte_buffer' for relevant assumptions.
+ utf8_buffer
+ end
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/client.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/client.rb
new file mode 100644
index 000000000..64ef05956
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/client.rb
@@ -0,0 +1,71 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Thrift
+ module Client
+ def initialize(iprot, oprot=nil)
+ @iprot = iprot
+ @oprot = oprot || iprot
+ @seqid = 0
+ end
+
+ def send_message(name, args_class, args = {})
+ @oprot.write_message_begin(name, MessageTypes::CALL, @seqid)
+ send_message_args(args_class, args)
+ end
+
+ def send_oneway_message(name, args_class, args = {})
+ @oprot.write_message_begin(name, MessageTypes::ONEWAY, @seqid)
+ send_message_args(args_class, args)
+ end
+
+ def send_message_args(args_class, args)
+ data = args_class.new
+ args.each do |k, v|
+ data.send("#{k.to_s}=", v)
+ end
+ begin
+ data.write(@oprot)
+ rescue StandardError => e
+ @oprot.trans.close
+ raise e
+ end
+ @oprot.write_message_end
+ @oprot.trans.flush
+ end
+
+ def receive_message(result_klass)
+ fname, mtype, rseqid = @iprot.read_message_begin
+ handle_exception(mtype)
+ result = result_klass.new
+ result.read(@iprot)
+ @iprot.read_message_end
+ result
+ end
+
+ def handle_exception(mtype)
+ if mtype == MessageTypes::EXCEPTION
+ x = ApplicationException.new
+ x.read(@iprot)
+ @iprot.read_message_end
+ raise x
+ end
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/core_ext.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/core_ext.rb
new file mode 100644
index 000000000..f763cd534
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/core_ext.rb
@@ -0,0 +1,23 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+Dir[File.dirname(__FILE__) + "/core_ext/*.rb"].each do |file|
+ name = File.basename(file, '.rb')
+ require "thrift/core_ext/#{name}"
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/core_ext/fixnum.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/core_ext/fixnum.rb
new file mode 100644
index 000000000..b4fc90dd6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/core_ext/fixnum.rb
@@ -0,0 +1,29 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Versions of ruby pre 1.8.7 do not have an .ord method available in the Fixnum
+# class.
+#
+if RUBY_VERSION < "1.8.7"
+ class Fixnum
+ def ord
+ self
+ end
+ end
+end \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/exceptions.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/exceptions.rb
new file mode 100644
index 000000000..68cb9e03a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/exceptions.rb
@@ -0,0 +1,87 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Thrift
+ class Exception < StandardError
+ def initialize(message)
+ super
+ @message = message
+ end
+
+ attr_reader :message
+ end
+
+ class ApplicationException < Exception
+
+ UNKNOWN = 0
+ UNKNOWN_METHOD = 1
+ INVALID_MESSAGE_TYPE = 2
+ WRONG_METHOD_NAME = 3
+ BAD_SEQUENCE_ID = 4
+ MISSING_RESULT = 5
+ INTERNAL_ERROR = 6
+ PROTOCOL_ERROR = 7
+ INVALID_TRANSFORM = 8
+ INVALID_PROTOCOL = 9
+ UNSUPPORTED_CLIENT_TYPE = 10
+
+ attr_reader :type
+
+ def initialize(type=UNKNOWN, message=nil)
+ super(message)
+ @type = type
+ end
+
+ def read(iprot)
+ iprot.read_struct_begin
+ while true
+ fname, ftype, fid = iprot.read_field_begin
+ if ftype == Types::STOP
+ break
+ end
+ if fid == 1 and ftype == Types::STRING
+ @message = iprot.read_string
+ elsif fid == 2 and ftype == Types::I32
+ @type = iprot.read_i32
+ else
+ iprot.skip(ftype)
+ end
+ iprot.read_field_end
+ end
+ iprot.read_struct_end
+ end
+
+ def write(oprot)
+ oprot.write_struct_begin('Thrift::ApplicationException')
+ unless @message.nil?
+ oprot.write_field_begin('message', Types::STRING, 1)
+ oprot.write_string(@message)
+ oprot.write_field_end
+ end
+ unless @type.nil?
+ oprot.write_field_begin('type', Types::I32, 2)
+ oprot.write_i32(@type)
+ oprot.write_field_end
+ end
+ oprot.write_field_stop
+ oprot.write_struct_end
+ end
+
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/multiplexed_processor.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/multiplexed_processor.rb
new file mode 100644
index 000000000..c734c04ba
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/multiplexed_processor.rb
@@ -0,0 +1,76 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+require 'thrift/protocol/protocol_decorator'
+require 'thrift/protocol/base_protocol'
+
+module Thrift
+ class MultiplexedProcessor
+ def initialize
+ @actual_processors = {}
+ end
+
+ def register_processor(service_name, processor)
+ @actual_processors[service_name] = processor
+ end
+
+ def process(iprot, oprot)
+ name, type, seqid = iprot.read_message_begin
+ check_type(type)
+ check_separator(name)
+ service_name, method = name.split(':')
+ processor(service_name).process(StoredMessageProtocol.new(iprot, [method, type, seqid]), oprot)
+ end
+
+ protected
+
+ def processor(service_name)
+ if @actual_processors.has_key?(service_name)
+ @actual_processors[service_name]
+ else
+ raise Thrift::Exception.new("Service name not found: #{service_name}. Did you forget to call #{self.class.name}#register_processor?")
+ end
+ end
+
+ def check_type(type)
+ unless [MessageTypes::CALL, MessageTypes::ONEWAY].include?(type)
+ raise Thrift::Exception.new('This should not have happened!?')
+ end
+ end
+
+ def check_separator(name)
+ if name.count(':') < 1
+ raise Thrift::Exception.new("Service name not found in message name: #{name}. Did you forget to use a Thrift::Protocol::MultiplexedProtocol in your client?")
+ end
+ end
+ end
+
+ class StoredMessageProtocol < BaseProtocol
+
+ include ProtocolDecorator
+
+ def initialize(protocol, message_begin)
+ super(protocol)
+ @message_begin = message_begin
+ end
+
+ def read_message_begin
+ @message_begin
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/processor.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/processor.rb
new file mode 100644
index 000000000..ce21e120a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/processor.rb
@@ -0,0 +1,75 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'logger'
+
+module Thrift
+ module Processor
+ def initialize(handler, logger=nil)
+ @handler = handler
+ if logger.nil?
+ @logger = Logger.new(STDERR)
+ @logger.level = Logger::WARN
+ else
+ @logger = logger
+ end
+ end
+
+ def process(iprot, oprot)
+ name, type, seqid = iprot.read_message_begin
+ if respond_to?("process_#{name}")
+ begin
+ send("process_#{name}", seqid, iprot, oprot)
+ rescue => e
+ x = ApplicationException.new(ApplicationException::INTERNAL_ERROR, 'Internal error')
+ @logger.debug "Internal error : #{e.message}\n#{e.backtrace.join("\n")}"
+ write_error(x, oprot, name, seqid)
+ end
+ true
+ else
+ iprot.skip(Types::STRUCT)
+ iprot.read_message_end
+ x = ApplicationException.new(ApplicationException::UNKNOWN_METHOD, 'Unknown function '+name)
+ write_error(x, oprot, name, seqid)
+ false
+ end
+ end
+
+ def read_args(iprot, args_class)
+ args = args_class.new
+ args.read(iprot)
+ iprot.read_message_end
+ args
+ end
+
+ def write_result(result, oprot, name, seqid)
+ oprot.write_message_begin(name, MessageTypes::REPLY, seqid)
+ result.write(oprot)
+ oprot.write_message_end
+ oprot.trans.flush
+ end
+
+ def write_error(err, oprot, name, seqid)
+ oprot.write_message_begin(name, MessageTypes::EXCEPTION, seqid)
+ err.write(oprot)
+ oprot.write_message_end
+ oprot.trans.flush
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/base_protocol.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/base_protocol.rb
new file mode 100644
index 000000000..4d83a21dd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/base_protocol.rb
@@ -0,0 +1,387 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# this require is to make generated struct definitions happy
+require 'set'
+
+module Thrift
+ class ProtocolException < Exception
+
+ UNKNOWN = 0
+ INVALID_DATA = 1
+ NEGATIVE_SIZE = 2
+ SIZE_LIMIT = 3
+ BAD_VERSION = 4
+ NOT_IMPLEMENTED = 5
+ DEPTH_LIMIT = 6
+
+ attr_reader :type
+
+ def initialize(type=UNKNOWN, message=nil)
+ super(message)
+ @type = type
+ end
+ end
+
+ class BaseProtocol
+
+ attr_reader :trans
+
+ def initialize(trans)
+ @trans = trans
+ end
+
+ def native?
+ puts "wrong method is being called!"
+ false
+ end
+
+ def write_message_begin(name, type, seqid)
+ raise NotImplementedError
+ end
+
+ def write_message_end; nil; end
+
+ def write_struct_begin(name)
+ raise NotImplementedError
+ end
+
+ def write_struct_end; nil; end
+
+ def write_field_begin(name, type, id)
+ raise NotImplementedError
+ end
+
+ def write_field_end; nil; end
+
+ def write_field_stop
+ raise NotImplementedError
+ end
+
+ def write_map_begin(ktype, vtype, size)
+ raise NotImplementedError
+ end
+
+ def write_map_end; nil; end
+
+ def write_list_begin(etype, size)
+ raise NotImplementedError
+ end
+
+ def write_list_end; nil; end
+
+ def write_set_begin(etype, size)
+ raise NotImplementedError
+ end
+
+ def write_set_end; nil; end
+
+ def write_bool(bool)
+ raise NotImplementedError
+ end
+
+ def write_byte(byte)
+ raise NotImplementedError
+ end
+
+ def write_i16(i16)
+ raise NotImplementedError
+ end
+
+ def write_i32(i32)
+ raise NotImplementedError
+ end
+
+ def write_i64(i64)
+ raise NotImplementedError
+ end
+
+ def write_double(dub)
+ raise NotImplementedError
+ end
+
+ # Writes a Thrift String. In Ruby 1.9+, the String passed will be transcoded to UTF-8.
+ #
+ # str - The String to write.
+ #
+ # Raises EncodingError if the transcoding to UTF-8 fails.
+ #
+ # Returns nothing.
+ def write_string(str)
+ raise NotImplementedError
+ end
+
+ # Writes a Thrift Binary (Thrift String with no encoding). In Ruby 1.9+, the String passed
+ # will forced into BINARY encoding.
+ #
+ # buf - The String to write.
+ #
+ # Returns nothing.
+ def write_binary(buf)
+ raise NotImplementedError
+ end
+
+ def read_message_begin
+ raise NotImplementedError
+ end
+
+ def read_message_end; nil; end
+
+ def read_struct_begin
+ raise NotImplementedError
+ end
+
+ def read_struct_end; nil; end
+
+ def read_field_begin
+ raise NotImplementedError
+ end
+
+ def read_field_end; nil; end
+
+ def read_map_begin
+ raise NotImplementedError
+ end
+
+ def read_map_end; nil; end
+
+ def read_list_begin
+ raise NotImplementedError
+ end
+
+ def read_list_end; nil; end
+
+ def read_set_begin
+ raise NotImplementedError
+ end
+
+ def read_set_end; nil; end
+
+ def read_bool
+ raise NotImplementedError
+ end
+
+ def read_byte
+ raise NotImplementedError
+ end
+
+ def read_i16
+ raise NotImplementedError
+ end
+
+ def read_i32
+ raise NotImplementedError
+ end
+
+ def read_i64
+ raise NotImplementedError
+ end
+
+ def read_double
+ raise NotImplementedError
+ end
+
+ # Reads a Thrift String. In Ruby 1.9+, all Strings will be returned with an Encoding of UTF-8.
+ #
+ # Returns a String.
+ def read_string
+ raise NotImplementedError
+ end
+
+ # Reads a Thrift Binary (Thrift String without encoding). In Ruby 1.9+, all Strings will be returned
+ # with an Encoding of BINARY.
+ #
+ # Returns a String.
+ def read_binary
+ raise NotImplementedError
+ end
+
+ # Writes a field based on the field information, field ID and value.
+ #
+ # field_info - A Hash containing the definition of the field:
+ # :name - The name of the field.
+ # :type - The type of the field, which must be a Thrift::Types constant.
+ # :binary - A Boolean flag that indicates if Thrift::Types::STRING is a binary string (string without encoding).
+ # fid - The ID of the field.
+ # value - The field's value to write; object type varies based on :type.
+ #
+ # Returns nothing.
+ def write_field(*args)
+ if args.size == 3
+ # handles the documented method signature - write_field(field_info, fid, value)
+ field_info = args[0]
+ fid = args[1]
+ value = args[2]
+ elsif args.size == 4
+ # handles the deprecated method signature - write_field(name, type, fid, value)
+ field_info = {:name => args[0], :type => args[1]}
+ fid = args[2]
+ value = args[3]
+ else
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 3)"
+ end
+
+ write_field_begin(field_info[:name], field_info[:type], fid)
+ write_type(field_info, value)
+ write_field_end
+ end
+
+ # Writes a field value based on the field information.
+ #
+ # field_info - A Hash containing the definition of the field:
+ # :type - The Thrift::Types constant that determines how the value is written.
+ # :binary - A Boolean flag that indicates if Thrift::Types::STRING is a binary string (string without encoding).
+ # value - The field's value to write; object type varies based on field_info[:type].
+ #
+ # Returns nothing.
+ def write_type(field_info, value)
+ # if field_info is a Fixnum, assume it is a Thrift::Types constant
+ # convert it into a field_info Hash for backwards compatibility
+ if field_info.is_a? Fixnum
+ field_info = {:type => field_info}
+ end
+
+ case field_info[:type]
+ when Types::BOOL
+ write_bool(value)
+ when Types::BYTE
+ write_byte(value)
+ when Types::DOUBLE
+ write_double(value)
+ when Types::I16
+ write_i16(value)
+ when Types::I32
+ write_i32(value)
+ when Types::I64
+ write_i64(value)
+ when Types::STRING
+ if field_info[:binary]
+ write_binary(value)
+ else
+ write_string(value)
+ end
+ when Types::STRUCT
+ value.write(self)
+ else
+ raise NotImplementedError
+ end
+ end
+
+ # Reads a field value based on the field information.
+ #
+ # field_info - A Hash containing the pertinent data to write:
+ # :type - The Thrift::Types constant that determines how the value is written.
+ # :binary - A flag that indicates if Thrift::Types::STRING is a binary string (string without encoding).
+ #
+ # Returns the value read; object type varies based on field_info[:type].
+ def read_type(field_info)
+ # if field_info is a Fixnum, assume it is a Thrift::Types constant
+ # convert it into a field_info Hash for backwards compatibility
+ if field_info.is_a? Fixnum
+ field_info = {:type => field_info}
+ end
+
+ case field_info[:type]
+ when Types::BOOL
+ read_bool
+ when Types::BYTE
+ read_byte
+ when Types::DOUBLE
+ read_double
+ when Types::I16
+ read_i16
+ when Types::I32
+ read_i32
+ when Types::I64
+ read_i64
+ when Types::STRING
+ if field_info[:binary]
+ read_binary
+ else
+ read_string
+ end
+ else
+ raise NotImplementedError
+ end
+ end
+
+ def skip(type)
+ case type
+ when Types::BOOL
+ read_bool
+ when Types::BYTE
+ read_byte
+ when Types::I16
+ read_i16
+ when Types::I32
+ read_i32
+ when Types::I64
+ read_i64
+ when Types::DOUBLE
+ read_double
+ when Types::STRING
+ read_string
+ when Types::STRUCT
+ read_struct_begin
+ while true
+ name, type, id = read_field_begin
+ break if type == Types::STOP
+ skip(type)
+ read_field_end
+ end
+ read_struct_end
+ when Types::MAP
+ ktype, vtype, size = read_map_begin
+ size.times do
+ skip(ktype)
+ skip(vtype)
+ end
+ read_map_end
+ when Types::SET
+ etype, size = read_set_begin
+ size.times do
+ skip(etype)
+ end
+ read_set_end
+ when Types::LIST
+ etype, size = read_list_begin
+ size.times do
+ skip(etype)
+ end
+ read_list_end
+ else
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, 'Invalid data')
+ end
+ end
+
+ def to_s
+ "#{trans.to_s}"
+ end
+ end
+
+ class BaseProtocolFactory
+ def get_protocol(trans)
+ raise NotImplementedError
+ end
+
+ def to_s
+ "base"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/binary_protocol.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/binary_protocol.rb
new file mode 100644
index 000000000..d8279dbe6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/binary_protocol.rb
@@ -0,0 +1,244 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Thrift
+ class BinaryProtocol < BaseProtocol
+ VERSION_MASK = 0xffff0000
+ VERSION_1 = 0x80010000
+ TYPE_MASK = 0x000000ff
+
+ attr_reader :strict_read, :strict_write
+
+ def initialize(trans, strict_read=true, strict_write=true)
+ super(trans)
+ @strict_read = strict_read
+ @strict_write = strict_write
+
+ # Pre-allocated read buffer for fixed-size read methods. Needs to be at least 8 bytes long for
+ # read_i64() and read_double().
+ @rbuf = Bytes.empty_byte_buffer(8)
+ end
+
+ def write_message_begin(name, type, seqid)
+ # this is necessary because we added (needed) bounds checking to
+ # write_i32, and 0x80010000 is too big for that.
+ if strict_write
+ write_i16(VERSION_1 >> 16)
+ write_i16(type)
+ write_string(name)
+ write_i32(seqid)
+ else
+ write_string(name)
+ write_byte(type)
+ write_i32(seqid)
+ end
+ end
+
+ def write_struct_begin(name); nil; end
+
+ def write_field_begin(name, type, id)
+ write_byte(type)
+ write_i16(id)
+ end
+
+ def write_field_stop
+ write_byte(Thrift::Types::STOP)
+ end
+
+ def write_map_begin(ktype, vtype, size)
+ write_byte(ktype)
+ write_byte(vtype)
+ write_i32(size)
+ end
+
+ def write_list_begin(etype, size)
+ write_byte(etype)
+ write_i32(size)
+ end
+
+ def write_set_begin(etype, size)
+ write_byte(etype)
+ write_i32(size)
+ end
+
+ def write_bool(bool)
+ write_byte(bool ? 1 : 0)
+ end
+
+ def write_byte(byte)
+ raise RangeError if byte < -2**31 || byte >= 2**32
+ trans.write([byte].pack('c'))
+ end
+
+ def write_i16(i16)
+ trans.write([i16].pack('n'))
+ end
+
+ def write_i32(i32)
+ raise RangeError if i32 < -2**31 || i32 >= 2**31
+ trans.write([i32].pack('N'))
+ end
+
+ def write_i64(i64)
+ raise RangeError if i64 < -2**63 || i64 >= 2**64
+ hi = i64 >> 32
+ lo = i64 & 0xffffffff
+ trans.write([hi, lo].pack('N2'))
+ end
+
+ def write_double(dub)
+ trans.write([dub].pack('G'))
+ end
+
+ def write_string(str)
+ buf = Bytes.convert_to_utf8_byte_buffer(str)
+ write_binary(buf)
+ end
+
+ def write_binary(buf)
+ write_i32(buf.bytesize)
+ trans.write(buf)
+ end
+
+ def read_message_begin
+ version = read_i32
+ if version < 0
+ if (version & VERSION_MASK != VERSION_1)
+ raise ProtocolException.new(ProtocolException::BAD_VERSION, 'Missing version identifier')
+ end
+ type = version & TYPE_MASK
+ name = read_string
+ seqid = read_i32
+ [name, type, seqid]
+ else
+ if strict_read
+ raise ProtocolException.new(ProtocolException::BAD_VERSION, 'No version identifier, old protocol client?')
+ end
+ name = trans.read_all(version)
+ type = read_byte
+ seqid = read_i32
+ [name, type, seqid]
+ end
+ end
+
+ def read_struct_begin; nil; end
+
+ def read_field_begin
+ type = read_byte
+ if (type == Types::STOP)
+ [nil, type, 0]
+ else
+ id = read_i16
+ [nil, type, id]
+ end
+ end
+
+ def read_map_begin
+ ktype = read_byte
+ vtype = read_byte
+ size = read_i32
+ [ktype, vtype, size]
+ end
+
+ def read_list_begin
+ etype = read_byte
+ size = read_i32
+ [etype, size]
+ end
+
+ def read_set_begin
+ etype = read_byte
+ size = read_i32
+ [etype, size]
+ end
+
+ def read_bool
+ byte = read_byte
+ byte != 0
+ end
+
+ def read_byte
+ val = trans.read_byte
+ if (val > 0x7f)
+ val = 0 - ((val - 1) ^ 0xff)
+ end
+ val
+ end
+
+ def read_i16
+ trans.read_into_buffer(@rbuf, 2)
+ val, = @rbuf.unpack('n')
+ if (val > 0x7fff)
+ val = 0 - ((val - 1) ^ 0xffff)
+ end
+ val
+ end
+
+ def read_i32
+ trans.read_into_buffer(@rbuf, 4)
+ val, = @rbuf.unpack('N')
+ if (val > 0x7fffffff)
+ val = 0 - ((val - 1) ^ 0xffffffff)
+ end
+ val
+ end
+
+ def read_i64
+ trans.read_into_buffer(@rbuf, 8)
+ hi, lo = @rbuf.unpack('N2')
+ if (hi > 0x7fffffff)
+ hi ^= 0xffffffff
+ lo ^= 0xffffffff
+ 0 - (hi << 32) - lo - 1
+ else
+ (hi << 32) + lo
+ end
+ end
+
+ def read_double
+ trans.read_into_buffer(@rbuf, 8)
+ val = @rbuf.unpack('G').first
+ val
+ end
+
+ def read_string
+ buffer = read_binary
+ Bytes.convert_to_string(buffer)
+ end
+
+ def read_binary
+ size = read_i32
+ trans.read_all(size)
+ end
+
+ def to_s
+ "binary(#{super.to_s})"
+ end
+ end
+
+ class BinaryProtocolFactory < BaseProtocolFactory
+ def get_protocol(trans)
+ return Thrift::BinaryProtocol.new(trans)
+ end
+
+ def to_s
+ "binary"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/binary_protocol_accelerated.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/binary_protocol_accelerated.rb
new file mode 100644
index 000000000..09b02644d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/binary_protocol_accelerated.rb
@@ -0,0 +1,47 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+=begin
+The only change required for a transport to support BinaryProtocolAccelerated is to implement 2 methods:
+ * borrow(size), which takes an optional argument and returns atleast _size_ bytes from the transport,
+ or the default buffer size if no argument is given
+ * consume!(size), which removes size bytes from the front of the buffer
+
+See MemoryBuffer and BufferedTransport for examples.
+=end
+
+module Thrift
+ class BinaryProtocolAcceleratedFactory < BaseProtocolFactory
+ def get_protocol(trans)
+ if (defined? BinaryProtocolAccelerated)
+ BinaryProtocolAccelerated.new(trans)
+ else
+ BinaryProtocol.new(trans)
+ end
+ end
+
+ def to_s
+ if (defined? BinaryProtocolAccelerated)
+ "binary-accel"
+ else
+ "binary"
+ end
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/compact_protocol.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/compact_protocol.rb
new file mode 100644
index 000000000..1f9bd3060
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/compact_protocol.rb
@@ -0,0 +1,443 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Thrift
+ class CompactProtocol < BaseProtocol
+
+ PROTOCOL_ID = [0x82].pack('c').unpack('c').first
+ VERSION = 1
+ VERSION_MASK = 0x1f
+ TYPE_MASK = 0xE0
+ TYPE_BITS = 0x07
+ TYPE_SHIFT_AMOUNT = 5
+
+ TSTOP = ["", Types::STOP, 0]
+
+ #
+ # All of the on-wire type codes.
+ #
+ class CompactTypes
+ BOOLEAN_TRUE = 0x01
+ BOOLEAN_FALSE = 0x02
+ BYTE = 0x03
+ I16 = 0x04
+ I32 = 0x05
+ I64 = 0x06
+ DOUBLE = 0x07
+ BINARY = 0x08
+ LIST = 0x09
+ SET = 0x0A
+ MAP = 0x0B
+ STRUCT = 0x0C
+
+ def self.is_bool_type?(b)
+ (b & 0x0f) == BOOLEAN_TRUE || (b & 0x0f) == BOOLEAN_FALSE
+ end
+
+ COMPACT_TO_TTYPE = {
+ Types::STOP => Types::STOP,
+ BOOLEAN_FALSE => Types::BOOL,
+ BOOLEAN_TRUE => Types::BOOL,
+ BYTE => Types::BYTE,
+ I16 => Types::I16,
+ I32 => Types::I32,
+ I64 => Types::I64,
+ DOUBLE => Types::DOUBLE,
+ BINARY => Types::STRING,
+ LIST => Types::LIST,
+ SET => Types::SET,
+ MAP => Types::MAP,
+ STRUCT => Types::STRUCT
+ }
+
+ TTYPE_TO_COMPACT = {
+ Types::STOP => Types::STOP,
+ Types::BOOL => BOOLEAN_TRUE,
+ Types::BYTE => BYTE,
+ Types::I16 => I16,
+ Types::I32 => I32,
+ Types::I64 => I64,
+ Types::DOUBLE => DOUBLE,
+ Types::STRING => BINARY,
+ Types::LIST => LIST,
+ Types::SET => SET,
+ Types::MAP => MAP,
+ Types::STRUCT => STRUCT
+ }
+
+ def self.get_ttype(compact_type)
+ val = COMPACT_TO_TTYPE[compact_type & 0x0f]
+ raise "don't know what type: #{compact_type & 0x0f}" unless val
+ val
+ end
+
+ def self.get_compact_type(ttype)
+ val = TTYPE_TO_COMPACT[ttype]
+ raise "don't know what type: #{ttype & 0x0f}" unless val
+ val
+ end
+ end
+
+ def initialize(transport)
+ super(transport)
+
+ @last_field = [0]
+ @boolean_value = nil
+
+ # Pre-allocated read buffer for read_double().
+ @rbuf = Bytes.empty_byte_buffer(8)
+ end
+
+ def write_message_begin(name, type, seqid)
+ write_byte(PROTOCOL_ID)
+ write_byte((VERSION & VERSION_MASK) | ((type << TYPE_SHIFT_AMOUNT) & TYPE_MASK))
+ write_varint32(seqid)
+ write_string(name)
+ nil
+ end
+
+ def write_struct_begin(name)
+ @last_field.push(0)
+ nil
+ end
+
+ def write_struct_end
+ @last_field.pop
+ nil
+ end
+
+ def write_field_begin(name, type, id)
+ if type == Types::BOOL
+ # we want to possibly include the value, so we'll wait.
+ @boolean_field = [type, id]
+ else
+ write_field_begin_internal(type, id)
+ end
+ nil
+ end
+
+ #
+ # The workhorse of writeFieldBegin. It has the option of doing a
+ # 'type override' of the type header. This is used specifically in the
+ # boolean field case.
+ #
+ def write_field_begin_internal(type, id, type_override=nil)
+ last_id = @last_field.pop
+
+ # if there's a type override, use that.
+ typeToWrite = type_override || CompactTypes.get_compact_type(type)
+
+ # check if we can use delta encoding for the field id
+ if id > last_id && id - last_id <= 15
+ # write them together
+ write_byte((id - last_id) << 4 | typeToWrite)
+ else
+ # write them separate
+ write_byte(typeToWrite)
+ write_i16(id)
+ end
+
+ @last_field.push(id)
+ nil
+ end
+
+ def write_field_stop
+ write_byte(Types::STOP)
+ end
+
+ def write_map_begin(ktype, vtype, size)
+ if (size == 0)
+ write_byte(0)
+ else
+ write_varint32(size)
+ write_byte(CompactTypes.get_compact_type(ktype) << 4 | CompactTypes.get_compact_type(vtype))
+ end
+ end
+
+ def write_list_begin(etype, size)
+ write_collection_begin(etype, size)
+ end
+
+ def write_set_begin(etype, size)
+ write_collection_begin(etype, size);
+ end
+
+ def write_bool(bool)
+ type = bool ? CompactTypes::BOOLEAN_TRUE : CompactTypes::BOOLEAN_FALSE
+ unless @boolean_field.nil?
+ # we haven't written the field header yet
+ write_field_begin_internal(@boolean_field.first, @boolean_field.last, type)
+ @boolean_field = nil
+ else
+ # we're not part of a field, so just write the value.
+ write_byte(type)
+ end
+ end
+
+ def write_byte(byte)
+ @trans.write([byte].pack('c'))
+ end
+
+ def write_i16(i16)
+ write_varint32(int_to_zig_zag(i16))
+ end
+
+ def write_i32(i32)
+ write_varint32(int_to_zig_zag(i32))
+ end
+
+ def write_i64(i64)
+ write_varint64(long_to_zig_zag(i64))
+ end
+
+ def write_double(dub)
+ @trans.write([dub].pack("G").reverse)
+ end
+
+ def write_string(str)
+ buf = Bytes.convert_to_utf8_byte_buffer(str)
+ write_binary(buf)
+ end
+
+ def write_binary(buf)
+ write_varint32(buf.bytesize)
+ @trans.write(buf)
+ end
+
+ def read_message_begin
+ protocol_id = read_byte()
+ if protocol_id != PROTOCOL_ID
+ raise ProtocolException.new("Expected protocol id #{PROTOCOL_ID} but got #{protocol_id}")
+ end
+
+ version_and_type = read_byte()
+ version = version_and_type & VERSION_MASK
+ if (version != VERSION)
+ raise ProtocolException.new("Expected version #{VERSION} but got #{version}");
+ end
+
+ type = (version_and_type >> TYPE_SHIFT_AMOUNT) & TYPE_BITS
+ seqid = read_varint32()
+ messageName = read_string()
+ [messageName, type, seqid]
+ end
+
+ def read_struct_begin
+ @last_field.push(0)
+ ""
+ end
+
+ def read_struct_end
+ @last_field.pop()
+ nil
+ end
+
+ def read_field_begin
+ type = read_byte()
+
+ # if it's a stop, then we can return immediately, as the struct is over.
+ if (type & 0x0f) == Types::STOP
+ TSTOP
+ else
+ field_id = nil
+
+ # mask off the 4 MSB of the type header. it could contain a field id delta.
+ modifier = (type & 0xf0) >> 4
+ if modifier == 0
+ # not a delta. look ahead for the zigzag varint field id.
+ @last_field.pop
+ field_id = read_i16()
+ else
+ # has a delta. add the delta to the last read field id.
+ field_id = @last_field.pop + modifier
+ end
+
+ # if this happens to be a boolean field, the value is encoded in the type
+ if CompactTypes.is_bool_type?(type)
+ # save the boolean value in a special instance variable.
+ @bool_value = (type & 0x0f) == CompactTypes::BOOLEAN_TRUE
+ end
+
+ # push the new field onto the field stack so we can keep the deltas going.
+ @last_field.push(field_id)
+ ["", CompactTypes.get_ttype(type & 0x0f), field_id]
+ end
+ end
+
+ def read_map_begin
+ size = read_varint32()
+ key_and_value_type = size == 0 ? 0 : read_byte()
+ [CompactTypes.get_ttype(key_and_value_type >> 4), CompactTypes.get_ttype(key_and_value_type & 0xf), size]
+ end
+
+ def read_list_begin
+ size_and_type = read_byte()
+ size = (size_and_type >> 4) & 0x0f
+ if size == 15
+ size = read_varint32()
+ end
+ type = CompactTypes.get_ttype(size_and_type)
+ [type, size]
+ end
+
+ def read_set_begin
+ read_list_begin
+ end
+
+ def read_bool
+ unless @bool_value.nil?
+ bv = @bool_value
+ @bool_value = nil
+ bv
+ else
+ read_byte() == CompactTypes::BOOLEAN_TRUE
+ end
+ end
+
+ def read_byte
+ val = trans.read_byte
+ if (val > 0x7f)
+ val = 0 - ((val - 1) ^ 0xff)
+ end
+ val
+ end
+
+ def read_i16
+ zig_zag_to_int(read_varint32())
+ end
+
+ def read_i32
+ zig_zag_to_int(read_varint32())
+ end
+
+ def read_i64
+ zig_zag_to_long(read_varint64())
+ end
+
+ def read_double
+ trans.read_into_buffer(@rbuf, 8)
+ val = @rbuf.reverse.unpack('G').first
+ val
+ end
+
+ def read_string
+ buffer = read_binary
+ Bytes.convert_to_string(buffer)
+ end
+
+ def read_binary
+ size = read_varint32()
+ trans.read_all(size)
+ end
+
+ def to_s
+ "compact(#{super.to_s})"
+ end
+
+ private
+
+ #
+ # Abstract method for writing the start of lists and sets. List and sets on
+ # the wire differ only by the type indicator.
+ #
+ def write_collection_begin(elem_type, size)
+ if size <= 14
+ write_byte(size << 4 | CompactTypes.get_compact_type(elem_type))
+ else
+ write_byte(0xf0 | CompactTypes.get_compact_type(elem_type))
+ write_varint32(size)
+ end
+ end
+
+ def write_varint32(n)
+ # int idx = 0;
+ while true
+ if (n & ~0x7F) == 0
+ # i32buf[idx++] = (byte)n;
+ write_byte(n)
+ break
+ # return;
+ else
+ # i32buf[idx++] = (byte)((n & 0x7F) | 0x80);
+ write_byte((n & 0x7F) | 0x80)
+ n = n >> 7
+ end
+ end
+ # trans_.write(i32buf, 0, idx);
+ end
+
+ SEVEN_BIT_MASK = 0x7F
+ EVERYTHING_ELSE_MASK = ~SEVEN_BIT_MASK
+
+ def write_varint64(n)
+ while true
+ if (n & EVERYTHING_ELSE_MASK) == 0 #TODO need to find a way to make this into a long...
+ write_byte(n)
+ break
+ else
+ write_byte((n & SEVEN_BIT_MASK) | 0x80)
+ n >>= 7
+ end
+ end
+ end
+
+ def read_varint32()
+ read_varint64()
+ end
+
+ def read_varint64()
+ shift = 0
+ result = 0
+ while true
+ b = read_byte()
+ result |= (b & 0x7f) << shift
+ break if (b & 0x80) != 0x80
+ shift += 7
+ end
+ result
+ end
+
+ def int_to_zig_zag(n)
+ (n << 1) ^ (n >> 31)
+ end
+
+ def long_to_zig_zag(l)
+ # puts "zz encoded #{l} to #{(l << 1) ^ (l >> 63)}"
+ (l << 1) ^ (l >> 63)
+ end
+
+ def zig_zag_to_int(n)
+ (n >> 1) ^ -(n & 1)
+ end
+
+ def zig_zag_to_long(n)
+ (n >> 1) ^ -(n & 1)
+ end
+ end
+
+ class CompactProtocolFactory < BaseProtocolFactory
+ def get_protocol(trans)
+ CompactProtocol.new(trans)
+ end
+
+ def to_s
+ "compact"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/json_protocol.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/json_protocol.rb
new file mode 100644
index 000000000..91e74e46b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/json_protocol.rb
@@ -0,0 +1,786 @@
+# encoding: UTF-8
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'base64'
+
+module Thrift
+ class LookaheadReader
+ def initialize(trans)
+ @trans = trans
+ @hasData = false
+ @data = nil
+ end
+
+ def read
+ if @hasData
+ @hasData = false
+ else
+ @data = @trans.read(1)
+ end
+
+ return @data
+ end
+
+ def peek
+ if !@hasData
+ @data = @trans.read(1)
+ end
+ @hasData = true
+ return @data
+ end
+ end
+
+ #
+ # Class to serve as base JSON context and as base class for other context
+ # implementations
+ #
+ class JSONContext
+ @@kJSONElemSeparator = ','
+ #
+ # Write context data to the trans. Default is to do nothing.
+ #
+ def write(trans)
+ end
+
+ #
+ # Read context data from the trans. Default is to do nothing.
+ #
+ def read(reader)
+ end
+
+ #
+ # Return true if numbers need to be escaped as strings in this context.
+ # Default behavior is to return false.
+ #
+ def escapeNum
+ return false
+ end
+ end
+
+ # Context class for object member key-value pairs
+ class JSONPairContext < JSONContext
+ @@kJSONPairSeparator = ':'
+
+ def initialize
+ @first = true
+ @colon = true
+ end
+
+ def write(trans)
+ if (@first)
+ @first = false
+ @colon = true
+ else
+ trans.write(@colon ? @@kJSONPairSeparator : @@kJSONElemSeparator)
+ @colon = !@colon
+ end
+ end
+
+ def read(reader)
+ if (@first)
+ @first = false
+ @colon = true
+ else
+ ch = (@colon ? @@kJSONPairSeparator : @@kJSONElemSeparator)
+ @colon = !@colon
+ JsonProtocol::read_syntax_char(reader, ch)
+ end
+ end
+
+ # Numbers must be turned into strings if they are the key part of a pair
+ def escapeNum
+ return @colon
+ end
+ end
+
+ # Context class for lists
+ class JSONListContext < JSONContext
+
+ def initialize
+ @first = true
+ end
+
+ def write(trans)
+ if (@first)
+ @first = false
+ else
+ trans.write(@@kJSONElemSeparator)
+ end
+ end
+
+ def read(reader)
+ if (@first)
+ @first = false
+ else
+ JsonProtocol::read_syntax_char(reader, @@kJSONElemSeparator)
+ end
+ end
+ end
+
+ class JsonProtocol < BaseProtocol
+
+ @@kJSONObjectStart = '{'
+ @@kJSONObjectEnd = '}'
+ @@kJSONArrayStart = '['
+ @@kJSONArrayEnd = ']'
+ @@kJSONNewline = '\n'
+ @@kJSONBackslash = '\\'
+ @@kJSONStringDelimiter = '"'
+
+ @@kThriftVersion1 = 1
+
+ @@kThriftNan = "NaN"
+ @@kThriftInfinity = "Infinity"
+ @@kThriftNegativeInfinity = "-Infinity"
+
+ def initialize(trans)
+ super(trans)
+ @context = JSONContext.new
+ @contexts = Array.new
+ @reader = LookaheadReader.new(trans)
+ end
+
+ def get_type_name_for_type_id(id)
+ case id
+ when Types::BOOL
+ "tf"
+ when Types::BYTE
+ "i8"
+ when Types::I16
+ "i16"
+ when Types::I32
+ "i32"
+ when Types::I64
+ "i64"
+ when Types::DOUBLE
+ "dbl"
+ when Types::STRING
+ "str"
+ when Types::STRUCT
+ "rec"
+ when Types::MAP
+ "map"
+ when Types::SET
+ "set"
+ when Types::LIST
+ "lst"
+ else
+ raise NotImplementedError
+ end
+ end
+
+ def get_type_id_for_type_name(name)
+ if (name == "tf")
+ result = Types::BOOL
+ elsif (name == "i8")
+ result = Types::BYTE
+ elsif (name == "i16")
+ result = Types::I16
+ elsif (name == "i32")
+ result = Types::I32
+ elsif (name == "i64")
+ result = Types::I64
+ elsif (name == "dbl")
+ result = Types::DOUBLE
+ elsif (name == "str")
+ result = Types::STRING
+ elsif (name == "rec")
+ result = Types::STRUCT
+ elsif (name == "map")
+ result = Types::MAP
+ elsif (name == "set")
+ result = Types::SET
+ elsif (name == "lst")
+ result = Types::LIST
+ else
+ result = Types::STOP
+ end
+ if (result == Types::STOP)
+ raise NotImplementedError
+ end
+ return result
+ end
+
+ # Static helper functions
+
+ # Read 1 character from the trans and verify that it is the expected character ch.
+ # Throw a protocol exception if it is not.
+ def self.read_syntax_char(reader, ch)
+ ch2 = reader.read
+ if (ch2 != ch)
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected \'#{ch}\' got \'#{ch2}\'.")
+ end
+ end
+
+ # Return true if the character ch is in [-+0-9.Ee]; false otherwise
+ def is_json_numeric(ch)
+ case ch
+ when '+', '-', '.', '0' .. '9', 'E', "e"
+ return true
+ else
+ return false
+ end
+ end
+
+ def push_context(context)
+ @contexts.push(@context)
+ @context = context
+ end
+
+ def pop_context
+ @context = @contexts.pop
+ end
+
+ # Write the character ch as a JSON escape sequence ("\u00xx")
+ def write_json_escape_char(ch)
+ trans.write('\\u')
+ ch_value = ch[0]
+ if (ch_value.kind_of? String)
+ ch_value = ch.bytes.first
+ end
+ trans.write(ch_value.to_s(16).rjust(4,'0'))
+ end
+
+ # Write the character ch as part of a JSON string, escaping as appropriate.
+ def write_json_char(ch)
+ # This table describes the handling for the first 0x30 characters
+ # 0 : escape using "\u00xx" notation
+ # 1 : just output index
+ # <other> : escape using "\<other>" notation
+ kJSONCharTable = [
+ # 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, # 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 1
+ 1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 2
+ ]
+
+ ch_value = ch[0]
+ if (ch_value.kind_of? String)
+ ch_value = ch.bytes.first
+ end
+ if (ch_value >= 0x30)
+ if (ch == @@kJSONBackslash) # Only special character >= 0x30 is '\'
+ trans.write(@@kJSONBackslash)
+ trans.write(@@kJSONBackslash)
+ else
+ trans.write(ch)
+ end
+ else
+ outCh = kJSONCharTable[ch_value];
+ # Check if regular character, backslash escaped, or JSON escaped
+ if outCh.kind_of? String
+ trans.write(@@kJSONBackslash)
+ trans.write(outCh)
+ elsif outCh == 1
+ trans.write(ch)
+ else
+ write_json_escape_char(ch)
+ end
+ end
+ end
+
+ # Write out the contents of the string str as a JSON string, escaping characters as appropriate.
+ def write_json_string(str)
+ @context.write(trans)
+ trans.write(@@kJSONStringDelimiter)
+ str.split('').each do |ch|
+ write_json_char(ch)
+ end
+ trans.write(@@kJSONStringDelimiter)
+ end
+
+ # Write out the contents of the string as JSON string, base64-encoding
+ # the string's contents, and escaping as appropriate
+ def write_json_base64(str)
+ @context.write(trans)
+ trans.write(@@kJSONStringDelimiter)
+ trans.write(Base64.strict_encode64(str))
+ trans.write(@@kJSONStringDelimiter)
+ end
+
+ # Convert the given integer type to a JSON number, or a string
+ # if the context requires it (eg: key in a map pair).
+ def write_json_integer(num)
+ @context.write(trans)
+ escapeNum = @context.escapeNum
+ if (escapeNum)
+ trans.write(@@kJSONStringDelimiter)
+ end
+ trans.write(num.to_s);
+ if (escapeNum)
+ trans.write(@@kJSONStringDelimiter)
+ end
+ end
+
+ # Convert the given double to a JSON string, which is either the number,
+ # "NaN" or "Infinity" or "-Infinity".
+ def write_json_double(num)
+ @context.write(trans)
+ # Normalize output of thrift::to_string for NaNs and Infinities
+ special = false;
+ if (num.nan?)
+ special = true;
+ val = @@kThriftNan;
+ elsif (num.infinite?)
+ special = true;
+ val = @@kThriftInfinity;
+ if (num < 0.0)
+ val = @@kThriftNegativeInfinity;
+ end
+ else
+ val = num.to_s
+ end
+
+ escapeNum = special || @context.escapeNum
+ if (escapeNum)
+ trans.write(@@kJSONStringDelimiter)
+ end
+ trans.write(val)
+ if (escapeNum)
+ trans.write(@@kJSONStringDelimiter)
+ end
+ end
+
+ def write_json_object_start
+ @context.write(trans)
+ trans.write(@@kJSONObjectStart)
+ push_context(JSONPairContext.new);
+ end
+
+ def write_json_object_end
+ pop_context
+ trans.write(@@kJSONObjectEnd)
+ end
+
+ def write_json_array_start
+ @context.write(trans)
+ trans.write(@@kJSONArrayStart)
+ push_context(JSONListContext.new);
+ end
+
+ def write_json_array_end
+ pop_context
+ trans.write(@@kJSONArrayEnd)
+ end
+
+ def write_message_begin(name, type, seqid)
+ write_json_array_start
+ write_json_integer(@@kThriftVersion1)
+ write_json_string(name)
+ write_json_integer(type)
+ write_json_integer(seqid)
+ end
+
+ def write_message_end
+ write_json_array_end
+ end
+
+ def write_struct_begin(name)
+ write_json_object_start
+ end
+
+ def write_struct_end
+ write_json_object_end
+ end
+
+ def write_field_begin(name, type, id)
+ write_json_integer(id)
+ write_json_object_start
+ write_json_string(get_type_name_for_type_id(type))
+ end
+
+ def write_field_end
+ write_json_object_end
+ end
+
+ def write_field_stop; nil; end
+
+ def write_map_begin(ktype, vtype, size)
+ write_json_array_start
+ write_json_string(get_type_name_for_type_id(ktype))
+ write_json_string(get_type_name_for_type_id(vtype))
+ write_json_integer(size)
+ write_json_object_start
+ end
+
+ def write_map_end
+ write_json_object_end
+ write_json_array_end
+ end
+
+ def write_list_begin(etype, size)
+ write_json_array_start
+ write_json_string(get_type_name_for_type_id(etype))
+ write_json_integer(size)
+ end
+
+ def write_list_end
+ write_json_array_end
+ end
+
+ def write_set_begin(etype, size)
+ write_json_array_start
+ write_json_string(get_type_name_for_type_id(etype))
+ write_json_integer(size)
+ end
+
+ def write_set_end
+ write_json_array_end
+ end
+
+ def write_bool(bool)
+ write_json_integer(bool ? 1 : 0)
+ end
+
+ def write_byte(byte)
+ write_json_integer(byte)
+ end
+
+ def write_i16(i16)
+ write_json_integer(i16)
+ end
+
+ def write_i32(i32)
+ write_json_integer(i32)
+ end
+
+ def write_i64(i64)
+ write_json_integer(i64)
+ end
+
+ def write_double(dub)
+ write_json_double(dub)
+ end
+
+ def write_string(str)
+ write_json_string(str)
+ end
+
+ def write_binary(str)
+ write_json_base64(str)
+ end
+
+ ##
+ # Reading functions
+ ##
+
+ # Reads 1 byte and verifies that it matches ch.
+ def read_json_syntax_char(ch)
+ JsonProtocol::read_syntax_char(@reader, ch)
+ end
+
+ # Decodes the four hex parts of a JSON escaped string character and returns
+ # the character via out.
+ #
+ # Note - this only supports Unicode characters in the BMP (U+0000 to U+FFFF);
+ # characters above the BMP are encoded as two escape sequences (surrogate pairs),
+ # which is not yet implemented
+ def read_json_escape_char
+ str = @reader.read
+ str += @reader.read
+ str += @reader.read
+ str += @reader.read
+ if RUBY_VERSION >= '1.9'
+ str.hex.chr(Encoding::UTF_8)
+ else
+ str.hex.chr
+ end
+ end
+
+ # Decodes a JSON string, including unescaping, and returns the string via str
+ def read_json_string(skipContext = false)
+ # This string's characters must match up with the elements in escape_char_vals.
+ # I don't have '/' on this list even though it appears on www.json.org --
+ # it is not in the RFC -> it is. See RFC 4627
+ escape_chars = "\"\\/bfnrt"
+
+ # The elements of this array must match up with the sequence of characters in
+ # escape_chars
+ escape_char_vals = [
+ "\"", "\\", "\/", "\b", "\f", "\n", "\r", "\t",
+ ]
+
+ if !skipContext
+ @context.read(@reader)
+ end
+ read_json_syntax_char(@@kJSONStringDelimiter)
+ ch = ""
+ str = ""
+ while (true)
+ ch = @reader.read
+ if (ch == @@kJSONStringDelimiter)
+ break
+ end
+ if (ch == @@kJSONBackslash)
+ ch = @reader.read
+ if (ch == 'u')
+ ch = read_json_escape_char
+ else
+ pos = escape_chars.index(ch);
+ if (pos.nil?) # not found
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected control char, got \'#{ch}\'.")
+ end
+ ch = escape_char_vals[pos]
+ end
+ end
+ str += ch
+ end
+ return str
+ end
+
+ # Reads a block of base64 characters, decoding it, and returns via str
+ def read_json_base64
+ str = read_json_string
+ m = str.length % 4
+ if m != 0
+ # Add missing padding
+ (4 - m).times do
+ str += '='
+ end
+ end
+ Base64.strict_decode64(str)
+ end
+
+ # Reads a sequence of characters, stopping at the first one that is not
+ # a valid JSON numeric character.
+ def read_json_numeric_chars
+ str = ""
+ while (true)
+ ch = @reader.peek
+ if (!is_json_numeric(ch))
+ break;
+ end
+ ch = @reader.read
+ str += ch
+ end
+ return str
+ end
+
+ # Reads a sequence of characters and assembles them into a number,
+ # returning them via num
+ def read_json_integer
+ @context.read(@reader)
+ if (@context.escapeNum)
+ read_json_syntax_char(@@kJSONStringDelimiter)
+ end
+ str = read_json_numeric_chars
+
+ begin
+ num = Integer(str);
+ rescue
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected numeric value; got \"#{str}\"")
+ end
+
+ if (@context.escapeNum)
+ read_json_syntax_char(@@kJSONStringDelimiter)
+ end
+
+ return num
+ end
+
+ # Reads a JSON number or string and interprets it as a double.
+ def read_json_double
+ @context.read(@reader)
+ num = 0
+ if (@reader.peek == @@kJSONStringDelimiter)
+ str = read_json_string(true)
+ # Check for NaN, Infinity and -Infinity
+ if (str == @@kThriftNan)
+ num = (+1.0/0.0)/(+1.0/0.0)
+ elsif (str == @@kThriftInfinity)
+ num = +1.0/0.0
+ elsif (str == @@kThriftNegativeInfinity)
+ num = -1.0/0.0
+ else
+ if (!@context.escapeNum)
+ # Raise exception -- we should not be in a string in this case
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, "Numeric data unexpectedly quoted")
+ end
+ begin
+ num = Float(str)
+ rescue
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected numeric value; got \"#{str}\"")
+ end
+ end
+ else
+ if (@context.escapeNum)
+ # This will throw - we should have had a quote if escapeNum == true
+ read_json_syntax_char(@@kJSONStringDelimiter)
+ end
+ str = read_json_numeric_chars
+ begin
+ num = Float(str)
+ rescue
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected numeric value; got \"#{str}\"")
+ end
+ end
+ return num
+ end
+
+ def read_json_object_start
+ @context.read(@reader)
+ read_json_syntax_char(@@kJSONObjectStart)
+ push_context(JSONPairContext.new)
+ nil
+ end
+
+ def read_json_object_end
+ read_json_syntax_char(@@kJSONObjectEnd)
+ pop_context
+ nil
+ end
+
+ def read_json_array_start
+ @context.read(@reader)
+ read_json_syntax_char(@@kJSONArrayStart)
+ push_context(JSONListContext.new)
+ nil
+ end
+
+ def read_json_array_end
+ read_json_syntax_char(@@kJSONArrayEnd)
+ pop_context
+ nil
+ end
+
+ def read_message_begin
+ read_json_array_start
+ version = read_json_integer
+ if (version != @@kThriftVersion1)
+ raise ProtocolException.new(ProtocolException::BAD_VERSION, 'Message contained bad version.')
+ end
+ name = read_json_string
+ message_type = read_json_integer
+ seqid = read_json_integer
+ [name, message_type, seqid]
+ end
+
+ def read_message_end
+ read_json_array_end
+ nil
+ end
+
+ def read_struct_begin
+ read_json_object_start
+ nil
+ end
+
+ def read_struct_end
+ read_json_object_end
+ nil
+ end
+
+ def read_field_begin
+ # Check if we hit the end of the list
+ ch = @reader.peek
+ if (ch == @@kJSONObjectEnd)
+ field_type = Types::STOP
+ else
+ field_id = read_json_integer
+ read_json_object_start
+ field_type = get_type_id_for_type_name(read_json_string)
+ end
+ [nil, field_type, field_id]
+ end
+
+ def read_field_end
+ read_json_object_end
+ end
+
+ def read_map_begin
+ read_json_array_start
+ key_type = get_type_id_for_type_name(read_json_string)
+ val_type = get_type_id_for_type_name(read_json_string)
+ size = read_json_integer
+ read_json_object_start
+ [key_type, val_type, size]
+ end
+
+ def read_map_end
+ read_json_object_end
+ read_json_array_end
+ end
+
+ def read_list_begin
+ read_json_array_start
+ [get_type_id_for_type_name(read_json_string), read_json_integer]
+ end
+
+ def read_list_end
+ read_json_array_end
+ end
+
+ def read_set_begin
+ read_json_array_start
+ [get_type_id_for_type_name(read_json_string), read_json_integer]
+ end
+
+ def read_set_end
+ read_json_array_end
+ end
+
+ def read_bool
+ byte = read_byte
+ byte != 0
+ end
+
+ def read_byte
+ read_json_integer
+ end
+
+ def read_i16
+ read_json_integer
+ end
+
+ def read_i32
+ read_json_integer
+ end
+
+ def read_i64
+ read_json_integer
+ end
+
+ def read_double
+ read_json_double
+ end
+
+ def read_string
+ read_json_string
+ end
+
+ def read_binary
+ read_json_base64
+ end
+
+ def to_s
+ "json(#{super.to_s})"
+ end
+ end
+
+ class JsonProtocolFactory < BaseProtocolFactory
+ def get_protocol(trans)
+ return Thrift::JsonProtocol.new(trans)
+ end
+
+ def to_s
+ "json"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/multiplexed_protocol.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/multiplexed_protocol.rb
new file mode 100644
index 000000000..b4428a734
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/multiplexed_protocol.rb
@@ -0,0 +1,44 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+require 'thrift/protocol/protocol_decorator'
+
+module Thrift
+ class MultiplexedProtocol < BaseProtocol
+
+ include ProtocolDecorator
+
+ def initialize(protocol, service_name)
+ super(protocol)
+ @service_name = service_name
+ end
+
+ def write_message_begin(name, type, seqid)
+ case type
+ when MessageTypes::CALL, MessageTypes::ONEWAY
+ @protocol.write_message_begin("#{@service_name}:#{name}", type, seqid)
+ else
+ @protocol.write_message_begin(name, type, seqid)
+ end
+ end
+
+ def to_s
+ "multiplexed(#{@service_name=@protocol.to_s})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/protocol_decorator.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/protocol_decorator.rb
new file mode 100644
index 000000000..b1e3c155d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/protocol_decorator.rb
@@ -0,0 +1,194 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+module Thrift
+ module ProtocolDecorator
+
+ def initialize(protocol)
+ @protocol = protocol
+ end
+
+ def trans
+ @protocol.trans
+ end
+
+ def write_message_begin(name, type, seqid)
+ @protocol.write_message_begin
+ end
+
+ def write_message_end
+ @protocol.write_message_end
+ end
+
+ def write_struct_begin(name)
+ @protocol.write_struct_begin(name)
+ end
+
+ def write_struct_end
+ @protocol.write_struct_end
+ end
+
+ def write_field_begin(name, type, id)
+ @protocol.write_field_begin(name, type, id)
+ end
+
+ def write_field_end
+ @protocol.write_field_end
+ end
+
+ def write_field_stop
+ @protocol.write_field_stop
+ end
+
+ def write_map_begin(ktype, vtype, size)
+ @protocol.write_map_begin(ktype, vtype, size)
+ end
+
+ def write_map_end
+ @protocol.write_map_end
+ end
+
+ def write_list_begin(etype, size)
+ @protocol.write_list_begin(etype, size)
+ end
+
+ def write_list_end
+ @protocol.write_list_end
+ end
+
+ def write_set_begin(etype, size)
+ @protocol.write_set_begin(etype, size)
+ end
+
+ def write_set_end
+ @protocol.write_set_end
+ end
+
+ def write_bool(bool)
+ @protocol.write_bool(bool)
+ end
+
+ def write_byte(byte)
+ @protocol.write_byte(byte)
+ end
+
+ def write_i16(i16)
+ @protocol.write_i16(i16)
+ end
+
+ def write_i32(i32)
+ @protocol.write_i32(i32)
+ end
+
+ def write_i64(i64)
+ @protocol.write_i64(i64)
+ end
+
+ def write_double(dub)
+ @protocol.write_double(dub)
+ end
+
+ def write_string(str)
+ @protocol.write_string(str)
+ end
+
+ def write_binary(buf)
+ @protocol.write_binary(buf)
+ end
+
+ def read_message_begin
+ @protocol.read_message_begin
+ end
+
+ def read_message_end
+ @protocol.read_message_end
+ end
+
+ def read_struct_begin
+ @protocol.read_struct_begin
+ end
+
+ def read_struct_end
+ @protocol.read_struct_end
+ end
+
+ def read_field_begin
+ @protocol.read_field_begin
+ end
+
+ def read_field_end
+ @protocol.read_field_end
+ end
+
+ def read_map_begin
+ @protocol.read_map_begin
+ end
+
+ def read_map_end
+ @protocol.read_map_end
+ end
+
+ def read_list_begin
+ @protocol.read_list_begin
+ end
+
+ def read_list_end
+ @protocol.read_list_end
+ end
+
+ def read_set_begin
+ @protocol.read_set_begin
+ end
+
+ def read_set_end
+ @protocol.read_set_end
+ end
+
+ def read_bool
+ @protocol.read_bool
+ end
+
+ def read_byte
+ @protocol.read_byte
+ end
+
+ def read_i16
+ @protocol.read_i16
+ end
+
+ def read_i32
+ @protocol.read_i32
+ end
+
+ def read_i64
+ @protocol.read_i64
+ end
+
+ def read_double
+ @protocol.read_double
+ end
+
+ def read_string
+ @protocol.read_string
+ end
+
+ def read_binary
+ @protocol.read_binary
+ end
+ end
+end \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/serializer/deserializer.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/serializer/deserializer.rb
new file mode 100644
index 000000000..d2ee325a5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/serializer/deserializer.rb
@@ -0,0 +1,33 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Thrift
+ class Deserializer
+ def initialize(protocol_factory = BinaryProtocolFactory.new)
+ @transport = MemoryBufferTransport.new
+ @protocol = protocol_factory.get_protocol(@transport)
+ end
+
+ def deserialize(base, buffer)
+ @transport.reset_buffer(buffer)
+ base.read(@protocol)
+ base
+ end
+ end
+end \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/serializer/serializer.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/serializer/serializer.rb
new file mode 100644
index 000000000..22316395d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/serializer/serializer.rb
@@ -0,0 +1,34 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Thrift
+ class Serializer
+ def initialize(protocol_factory = BinaryProtocolFactory.new)
+ @transport = MemoryBufferTransport.new
+ @protocol = protocol_factory.get_protocol(@transport)
+ end
+
+ def serialize(base)
+ @transport.reset_buffer
+ base.write(@protocol)
+ @transport.read(@transport.available)
+ end
+ end
+end
+
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/server/base_server.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/base_server.rb
new file mode 100644
index 000000000..aa4d09ce4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/base_server.rb
@@ -0,0 +1,37 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Thrift
+ class BaseServer
+ def initialize(processor, server_transport, transport_factory=nil, protocol_factory=nil)
+ @processor = processor
+ @server_transport = server_transport
+ @transport_factory = transport_factory ? transport_factory : Thrift::BaseTransportFactory.new
+ @protocol_factory = protocol_factory ? protocol_factory : Thrift::BinaryProtocolFactory.new
+ end
+
+ def serve
+ raise NotImplementedError
+ end
+
+ def to_s
+ "server(#{@protocol_factory.to_s}(#{@transport_factory.to_s}(#{@server_transport.to_s})))"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/server/mongrel_http_server.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/mongrel_http_server.rb
new file mode 100644
index 000000000..de354c8f9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/mongrel_http_server.rb
@@ -0,0 +1,60 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'mongrel'
+
+## Sticks a service on a URL, using mongrel to do the HTTP work
+# <b>DEPRECATED:</b> Please use <tt>Thrift::ThinHTTPServer</tt> instead.
+module Thrift
+ class MongrelHTTPServer < BaseServer
+ class Handler < Mongrel::HttpHandler
+ def initialize(processor, protocol_factory)
+ @processor = processor
+ @protocol_factory = protocol_factory
+ end
+
+ def process(request, response)
+ if request.params["REQUEST_METHOD"] == "POST"
+ response.start(200) do |head, out|
+ head["Content-Type"] = "application/x-thrift"
+ transport = IOStreamTransport.new request.body, out
+ protocol = @protocol_factory.get_protocol transport
+ @processor.process protocol, protocol
+ end
+ else
+ response.start(404) { }
+ end
+ end
+ end
+
+ def initialize(processor, opts={})
+ Kernel.warn "[DEPRECATION WARNING] `Thrift::MongrelHTTPServer` is deprecated. Please use `Thrift::ThinHTTPServer` instead."
+ port = opts[:port] || 80
+ ip = opts[:ip] || "0.0.0.0"
+ path = opts[:path] || ""
+ protocol_factory = opts[:protocol_factory] || BinaryProtocolFactory.new
+ @server = Mongrel::HttpServer.new ip, port
+ @server.register "/#{path}", Handler.new(processor, protocol_factory)
+ end
+
+ def serve
+ @server.run.join
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/server/nonblocking_server.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/nonblocking_server.rb
new file mode 100644
index 000000000..740f3417e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/nonblocking_server.rb
@@ -0,0 +1,305 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'logger'
+require 'thread'
+
+module Thrift
+ # this class expects to always use a FramedTransport for reading messages
+ class NonblockingServer < BaseServer
+ def initialize(processor, server_transport, transport_factory=nil, protocol_factory=nil, num=20, logger=nil)
+ super(processor, server_transport, transport_factory, protocol_factory)
+ @num_threads = num
+ if logger.nil?
+ @logger = Logger.new(STDERR)
+ @logger.level = Logger::WARN
+ else
+ @logger = logger
+ end
+ @shutdown_semaphore = Mutex.new
+ @transport_semaphore = Mutex.new
+ end
+
+ def serve
+ @logger.info "Starting #{self}"
+ @server_transport.listen
+ @io_manager = start_io_manager
+
+ begin
+ loop do
+ break if @server_transport.closed?
+ begin
+ rd, = select([@server_transport], nil, nil, 0.1)
+ rescue Errno::EBADF => e
+ # In Ruby 1.9, calling @server_transport.close in shutdown paths causes the select() to raise an
+ # Errno::EBADF. If this happens, ignore it and retry the loop.
+ break
+ end
+ next if rd.nil?
+ socket = @server_transport.accept
+ @logger.debug "Accepted socket: #{socket.inspect}"
+ @io_manager.add_connection socket
+ end
+ rescue IOError => e
+ end
+ # we must be shutting down
+ @logger.info "#{self} is shutting down, goodbye"
+ ensure
+ @transport_semaphore.synchronize do
+ @server_transport.close
+ end
+ @io_manager.ensure_closed unless @io_manager.nil?
+ end
+
+ def shutdown(timeout = 0, block = true)
+ @shutdown_semaphore.synchronize do
+ return if @is_shutdown
+ @is_shutdown = true
+ end
+ # nonblocking is intended for calling from within a Handler
+ # but we can't change the order of operations here, so lets thread
+ shutdown_proc = lambda do
+ @io_manager.shutdown(timeout)
+ @transport_semaphore.synchronize do
+ @server_transport.close # this will break the accept loop
+ end
+ end
+ if block
+ shutdown_proc.call
+ else
+ Thread.new &shutdown_proc
+ end
+ end
+
+ private
+
+ def start_io_manager
+ iom = IOManager.new(@processor, @server_transport, @transport_factory, @protocol_factory, @num_threads, @logger)
+ iom.spawn
+ iom
+ end
+
+ class IOManager # :nodoc:
+ DEFAULT_BUFFER = 2**20
+
+ def initialize(processor, server_transport, transport_factory, protocol_factory, num, logger)
+ @processor = processor
+ @server_transport = server_transport
+ @transport_factory = transport_factory
+ @protocol_factory = protocol_factory
+ @num_threads = num
+ @logger = logger
+ @connections = []
+ @buffers = Hash.new { |h,k| h[k] = '' }
+ @signal_queue = Queue.new
+ @signal_pipes = IO.pipe
+ @signal_pipes[1].sync = true
+ @worker_queue = Queue.new
+ @shutdown_queue = Queue.new
+ end
+
+ def add_connection(socket)
+ signal [:connection, socket]
+ end
+
+ def spawn
+ @iom_thread = Thread.new do
+ @logger.debug "Starting #{self}"
+ run
+ end
+ end
+
+ def shutdown(timeout = 0)
+ @logger.debug "#{self} is shutting down workers"
+ @worker_queue.clear
+ @num_threads.times { @worker_queue.push [:shutdown] }
+ signal [:shutdown, timeout]
+ @shutdown_queue.pop
+ @signal_pipes[0].close
+ @signal_pipes[1].close
+ @logger.debug "#{self} is shutting down, goodbye"
+ end
+
+ def ensure_closed
+ kill_worker_threads if @worker_threads
+ @iom_thread.kill
+ end
+
+ private
+
+ def run
+ spin_worker_threads
+
+ loop do
+ rd, = select([@signal_pipes[0], *@connections])
+ if rd.delete @signal_pipes[0]
+ break if read_signals == :shutdown
+ end
+ rd.each do |fd|
+ begin
+ if fd.handle.eof?
+ remove_connection fd
+ else
+ read_connection fd
+ end
+ rescue Errno::ECONNRESET
+ remove_connection fd
+ end
+ end
+ end
+ join_worker_threads(@shutdown_timeout)
+ ensure
+ @shutdown_queue.push :shutdown
+ end
+
+ def read_connection(fd)
+ @buffers[fd] << fd.read(DEFAULT_BUFFER)
+ while(frame = slice_frame!(@buffers[fd]))
+ @logger.debug "#{self} is processing a frame"
+ @worker_queue.push [:frame, fd, frame]
+ end
+ end
+
+ def spin_worker_threads
+ @logger.debug "#{self} is spinning up worker threads"
+ @worker_threads = []
+ @num_threads.times do
+ @worker_threads << spin_thread
+ end
+ end
+
+ def spin_thread
+ Worker.new(@processor, @transport_factory, @protocol_factory, @logger, @worker_queue).spawn
+ end
+
+ def signal(msg)
+ @signal_queue << msg
+ @signal_pipes[1].write " "
+ end
+
+ def read_signals
+ # clear the signal pipe
+ # note that since read_nonblock is broken in jruby,
+ # we can only read up to a set number of signals at once
+ sigstr = @signal_pipes[0].readpartial(1024)
+ # now read the signals
+ begin
+ sigstr.length.times do
+ signal, obj = @signal_queue.pop(true)
+ case signal
+ when :connection
+ @connections << obj
+ when :shutdown
+ @shutdown_timeout = obj
+ return :shutdown
+ end
+ end
+ rescue ThreadError
+ # out of signals
+ # note that in a perfect world this would never happen, since we're
+ # only reading the number of signals pushed on the pipe, but given the lack
+ # of locks, in theory we could clear the pipe/queue while a new signal is being
+ # placed on the pipe, at which point our next read_signals would hit this error
+ end
+ end
+
+ def remove_connection(fd)
+ # don't explicitly close it, a thread may still be writing to it
+ @connections.delete fd
+ @buffers.delete fd
+ end
+
+ def join_worker_threads(shutdown_timeout)
+ start = Time.now
+ @worker_threads.each do |t|
+ if shutdown_timeout > 0
+ timeout = (start + shutdown_timeout) - Time.now
+ break if timeout <= 0
+ t.join(timeout)
+ else
+ t.join
+ end
+ end
+ kill_worker_threads
+ end
+
+ def kill_worker_threads
+ @worker_threads.each do |t|
+ t.kill if t.status
+ end
+ @worker_threads.clear
+ end
+
+ def slice_frame!(buf)
+ if buf.length >= 4
+ size = buf.unpack('N').first
+ if buf.length >= size + 4
+ buf.slice!(0, size + 4)
+ else
+ nil
+ end
+ else
+ nil
+ end
+ end
+
+ class Worker # :nodoc:
+ def initialize(processor, transport_factory, protocol_factory, logger, queue)
+ @processor = processor
+ @transport_factory = transport_factory
+ @protocol_factory = protocol_factory
+ @logger = logger
+ @queue = queue
+ end
+
+ def spawn
+ Thread.new do
+ @logger.debug "#{self} is spawning"
+ run
+ end
+ end
+
+ private
+
+ def run
+ loop do
+ cmd, *args = @queue.pop
+ case cmd
+ when :shutdown
+ @logger.debug "#{self} is shutting down, goodbye"
+ break
+ when :frame
+ fd, frame = args
+ begin
+ otrans = @transport_factory.get_transport(fd)
+ oprot = @protocol_factory.get_protocol(otrans)
+ membuf = MemoryBufferTransport.new(frame)
+ itrans = @transport_factory.get_transport(membuf)
+ iprot = @protocol_factory.get_protocol(itrans)
+ @processor.process(iprot, oprot)
+ rescue => e
+ @logger.error "#{Thread.current.inspect} raised error: #{e.inspect}\n#{e.backtrace.join("\n")}"
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/server/simple_server.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/simple_server.rb
new file mode 100644
index 000000000..905fe9bd8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/simple_server.rb
@@ -0,0 +1,47 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Thrift
+ class SimpleServer < BaseServer
+ def serve
+ begin
+ @server_transport.listen
+ loop do
+ client = @server_transport.accept
+ trans = @transport_factory.get_transport(client)
+ prot = @protocol_factory.get_protocol(trans)
+ begin
+ loop do
+ @processor.process(prot, prot)
+ end
+ rescue Thrift::TransportException, Thrift::ProtocolException
+ ensure
+ trans.close
+ end
+ end
+ ensure
+ @server_transport.close
+ end
+ end
+
+ def to_s
+ "simple(#{super.to_s})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/server/thin_http_server.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/thin_http_server.rb
new file mode 100644
index 000000000..4a81c6d17
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/thin_http_server.rb
@@ -0,0 +1,91 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'rack'
+require 'thin'
+
+##
+# Wraps the Thin web server to provide a Thrift server over HTTP.
+module Thrift
+ class ThinHTTPServer < BaseServer
+
+ ##
+ # Accepts a Thrift::Processor
+ # Options include:
+ # * :port
+ # * :ip
+ # * :path
+ # * :protocol_factory
+ def initialize(processor, options={})
+ port = options[:port] || 80
+ ip = options[:ip] || "0.0.0.0"
+ path = options[:path] || "/"
+ protocol_factory = options[:protocol_factory] || BinaryProtocolFactory.new
+ app = RackApplication.for(path, processor, protocol_factory)
+ @server = Thin::Server.new(ip, port, app)
+ end
+
+ ##
+ # Starts the server
+ def serve
+ @server.start
+ end
+
+ class RackApplication
+
+ THRIFT_HEADER = "application/x-thrift"
+
+ def self.for(path, processor, protocol_factory)
+ Rack::Builder.new do
+ use Rack::CommonLogger
+ use Rack::ShowExceptions
+ use Rack::Lint
+ map path do
+ run lambda { |env|
+ request = Rack::Request.new(env)
+ if RackApplication.valid_thrift_request?(request)
+ RackApplication.successful_request(request, processor, protocol_factory)
+ else
+ RackApplication.failed_request
+ end
+ }
+ end
+ end
+ end
+
+ def self.successful_request(rack_request, processor, protocol_factory)
+ response = Rack::Response.new([], 200, {'Content-Type' => THRIFT_HEADER})
+ transport = IOStreamTransport.new rack_request.body, response
+ protocol = protocol_factory.get_protocol transport
+ processor.process protocol, protocol
+ response
+ end
+
+ def self.failed_request
+ Rack::Response.new(['Not Found'], 404, {'Content-Type' => THRIFT_HEADER})
+ end
+
+ def self.valid_thrift_request?(rack_request)
+ rack_request.post? && rack_request.env["CONTENT_TYPE"] == THRIFT_HEADER
+ end
+
+ end
+
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/server/thread_pool_server.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/thread_pool_server.rb
new file mode 100644
index 000000000..bb754ad2b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/thread_pool_server.rb
@@ -0,0 +1,79 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'thread'
+
+module Thrift
+ class ThreadPoolServer < BaseServer
+ def initialize(processor, server_transport, transport_factory=nil, protocol_factory=nil, num=20)
+ super(processor, server_transport, transport_factory, protocol_factory)
+ @thread_q = SizedQueue.new(num)
+ @exception_q = Queue.new
+ @running = false
+ end
+
+ ## exceptions that happen in worker threads will be relayed here and
+ ## must be caught. 'retry' can be used to continue. (threads will
+ ## continue to run while the exception is being handled.)
+ def rescuable_serve
+ Thread.new { serve } unless @running
+ @running = true
+ raise @exception_q.pop
+ end
+
+ ## exceptions that happen in worker threads simply cause that thread
+ ## to die and another to be spawned in its place.
+ def serve
+ @server_transport.listen
+
+ begin
+ loop do
+ @thread_q.push(:token)
+ Thread.new do
+ begin
+ loop do
+ client = @server_transport.accept
+ trans = @transport_factory.get_transport(client)
+ prot = @protocol_factory.get_protocol(trans)
+ begin
+ loop do
+ @processor.process(prot, prot)
+ end
+ rescue Thrift::TransportException, Thrift::ProtocolException => e
+ ensure
+ trans.close
+ end
+ end
+ rescue => e
+ @exception_q.push(e)
+ ensure
+ @thread_q.pop # thread died!
+ end
+ end
+ end
+ ensure
+ @server_transport.close
+ end
+ end
+
+ def to_s
+ "threadpool(#{super.to_s})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/server/threaded_server.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/threaded_server.rb
new file mode 100644
index 000000000..88ee1833f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/threaded_server.rb
@@ -0,0 +1,51 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'thread'
+
+module Thrift
+ class ThreadedServer < BaseServer
+ def serve
+ begin
+ @server_transport.listen
+ loop do
+ client = @server_transport.accept
+ trans = @transport_factory.get_transport(client)
+ prot = @protocol_factory.get_protocol(trans)
+ Thread.new(prot, trans) do |p, t|
+ begin
+ loop do
+ @processor.process(p, p)
+ end
+ rescue Thrift::TransportException, Thrift::ProtocolException
+ ensure
+ t.close
+ end
+ end
+ end
+ ensure
+ @server_transport.close
+ end
+ end
+
+ def to_s
+ "threaded(#{super.to_s})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/struct.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/struct.rb
new file mode 100644
index 000000000..df9d6561c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/struct.rb
@@ -0,0 +1,237 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'set'
+
+module Thrift
+ module Struct
+ def initialize(d={}, &block)
+ # get a copy of the default values to work on, removing defaults in favor of arguments
+ fields_with_defaults = fields_with_default_values.dup
+
+ # check if the defaults is empty, or if there are no parameters for this
+ # instantiation, and if so, don't bother overriding defaults.
+ unless fields_with_defaults.empty? || d.empty?
+ d.each_key do |name|
+ fields_with_defaults.delete(name.to_s)
+ end
+ end
+
+ # assign all the user-specified arguments
+ unless d.empty?
+ d.each do |name, value|
+ unless name_to_id(name.to_s)
+ raise Exception, "Unknown key given to #{self.class}.new: #{name}"
+ end
+ Thrift.check_type(value, struct_fields[name_to_id(name.to_s)], name) if Thrift.type_checking
+ instance_variable_set("@#{name}", value)
+ end
+ end
+
+ # assign all the default values
+ unless fields_with_defaults.empty?
+ fields_with_defaults.each do |name, default_value|
+ instance_variable_set("@#{name}", (default_value.dup rescue default_value))
+ end
+ end
+
+ yield self if block_given?
+ end
+
+ def fields_with_default_values
+ fields_with_default_values = self.class.instance_variable_get(:@fields_with_default_values)
+ unless fields_with_default_values
+ fields_with_default_values = {}
+ struct_fields.each do |fid, field_def|
+ unless field_def[:default].nil?
+ fields_with_default_values[field_def[:name]] = field_def[:default]
+ end
+ end
+ self.class.instance_variable_set(:@fields_with_default_values, fields_with_default_values)
+ end
+ fields_with_default_values
+ end
+
+ def inspect(skip_optional_nulls = true)
+ fields = []
+ each_field do |fid, field_info|
+ name = field_info[:name]
+ value = instance_variable_get("@#{name}")
+ unless skip_optional_nulls && field_info[:optional] && value.nil?
+ fields << "#{name}:#{inspect_field(value, field_info)}"
+ end
+ end
+ "<#{self.class} #{fields.join(", ")}>"
+ end
+
+ def read(iprot)
+ iprot.read_struct_begin
+ loop do
+ fname, ftype, fid = iprot.read_field_begin
+ break if (ftype == Types::STOP)
+ handle_message(iprot, fid, ftype)
+ iprot.read_field_end
+ end
+ iprot.read_struct_end
+ validate
+ end
+
+ def write(oprot)
+ validate
+ oprot.write_struct_begin(self.class.name)
+ each_field do |fid, field_info|
+ name = field_info[:name]
+ type = field_info[:type]
+ value = instance_variable_get("@#{name}")
+ unless value.nil?
+ if is_container? type
+ oprot.write_field_begin(name, type, fid)
+ write_container(oprot, value, field_info)
+ oprot.write_field_end
+ else
+ oprot.write_field(field_info, fid, value)
+ end
+ end
+ end
+ oprot.write_field_stop
+ oprot.write_struct_end
+ end
+
+ def ==(other)
+ return false if other.nil?
+ each_field do |fid, field_info|
+ name = field_info[:name]
+ return false unless other.respond_to?(name) && self.send(name) == other.send(name)
+ end
+ true
+ end
+
+ def eql?(other)
+ self.class == other.class && self == other
+ end
+
+ # This implementation of hash() is inspired by Apache's Java HashCodeBuilder class.
+ def hash
+ total = 17
+ each_field do |fid, field_info|
+ name = field_info[:name]
+ value = self.send(name)
+ total = (total * 37 + value.hash) & 0xffffffff
+ end
+ total
+ end
+
+ def differences(other)
+ diffs = []
+ unless other.is_a?(self.class)
+ diffs << "Different class!"
+ else
+ each_field do |fid, field_info|
+ name = field_info[:name]
+ diffs << "#{name} differs!" unless self.instance_variable_get("@#{name}") == other.instance_variable_get("@#{name}")
+ end
+ end
+ diffs
+ end
+
+ def self.field_accessor(klass, field_info)
+ field_name_sym = field_info[:name].to_sym
+ klass.send :attr_reader, field_name_sym
+ klass.send :define_method, "#{field_info[:name]}=" do |value|
+ Thrift.check_type(value, field_info, field_info[:name]) if Thrift.type_checking
+ instance_variable_set("@#{field_name_sym}", value)
+ end
+ end
+
+ def self.generate_accessors(klass)
+ klass::FIELDS.values.each do |field_info|
+ field_accessor(klass, field_info)
+ qmark_isset_method(klass, field_info)
+ end
+ end
+
+ def self.qmark_isset_method(klass, field_info)
+ klass.send :define_method, "#{field_info[:name]}?" do
+ !self.send(field_info[:name].to_sym).nil?
+ end
+ end
+
+ def <=>(other)
+ if self.class == other.class
+ each_field do |fid, field_info|
+ v1 = self.send(field_info[:name])
+ v1_set = !v1.nil?
+ v2 = other.send(field_info[:name])
+ v2_set = !v2.nil?
+ if v1_set && !v2_set
+ return -1
+ elsif !v1_set && v2_set
+ return 1
+ elsif v1_set && v2_set
+ cmp = v1 <=> v2
+ if cmp != 0
+ return cmp
+ end
+ end
+ end
+ 0
+ else
+ self.class <=> other.class
+ end
+ end
+
+ protected
+
+ def self.append_features(mod)
+ if mod.ancestors.include? ::Exception
+ mod.send :class_variable_set, :'@@__thrift_struct_real_initialize', mod.instance_method(:initialize)
+ super
+ # set up our custom initializer so `raise Xception, 'message'` works
+ mod.send :define_method, :struct_initialize, mod.instance_method(:initialize)
+ mod.send :define_method, :initialize, mod.instance_method(:exception_initialize)
+ else
+ super
+ end
+ end
+
+ def exception_initialize(*args, &block)
+ if args.size == 1 and args.first.is_a? Hash
+ # looks like it's a regular Struct initialize
+ method(:struct_initialize).call(args.first)
+ else
+ # call the Struct initializer first with no args
+ # this will set our field default values
+ method(:struct_initialize).call()
+ # now give it to the exception
+ self.class.send(:class_variable_get, :'@@__thrift_struct_real_initialize').bind(self).call(*args, &block) if args.size > 0
+ # self.class.instance_method(:initialize).bind(self).call(*args, &block)
+ end
+ end
+
+ def handle_message(iprot, fid, ftype)
+ field = struct_fields[fid]
+ if field and field[:type] == ftype
+ value = read_field(iprot, field)
+ instance_variable_set("@#{field[:name]}", value)
+ else
+ iprot.skip(ftype)
+ end
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/struct_union.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/struct_union.rb
new file mode 100644
index 000000000..e21b39ebf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/struct_union.rb
@@ -0,0 +1,192 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+require 'set'
+
+module Thrift
+ module Struct_Union
+ def name_to_id(name)
+ names_to_ids = self.class.instance_variable_get(:@names_to_ids)
+ unless names_to_ids
+ names_to_ids = {}
+ struct_fields.each do |fid, field_def|
+ names_to_ids[field_def[:name]] = fid
+ end
+ self.class.instance_variable_set(:@names_to_ids, names_to_ids)
+ end
+ names_to_ids[name]
+ end
+
+ def sorted_field_ids
+ sorted_field_ids = self.class.instance_variable_get(:@sorted_field_ids)
+ unless sorted_field_ids
+ sorted_field_ids = struct_fields.keys.sort
+ self.class.instance_variable_set(:@sorted_field_ids, sorted_field_ids)
+ end
+ sorted_field_ids
+ end
+
+ def each_field
+ sorted_field_ids.each do |fid|
+ data = struct_fields[fid]
+ yield fid, data
+ end
+ end
+
+ def read_field(iprot, field = {})
+ case field[:type]
+ when Types::STRUCT
+ value = field[:class].new
+ value.read(iprot)
+ when Types::MAP
+ key_type, val_type, size = iprot.read_map_begin
+ # Skip the map contents if the declared key or value types don't match the expected ones.
+ if (size != 0 && (key_type != field[:key][:type] || val_type != field[:value][:type]))
+ size.times do
+ iprot.skip(key_type)
+ iprot.skip(val_type)
+ end
+ value = nil
+ else
+ value = {}
+ size.times do
+ k = read_field(iprot, field_info(field[:key]))
+ v = read_field(iprot, field_info(field[:value]))
+ value[k] = v
+ end
+ end
+ iprot.read_map_end
+ when Types::LIST
+ e_type, size = iprot.read_list_begin
+ # Skip the list contents if the declared element type doesn't match the expected one.
+ if (e_type != field[:element][:type])
+ size.times do
+ iprot.skip(e_type)
+ end
+ value = nil
+ else
+ value = Array.new(size) do |n|
+ read_field(iprot, field_info(field[:element]))
+ end
+ end
+ iprot.read_list_end
+ when Types::SET
+ e_type, size = iprot.read_set_begin
+ # Skip the set contents if the declared element type doesn't match the expected one.
+ if (e_type != field[:element][:type])
+ size.times do
+ iprot.skip(e_type)
+ end
+ else
+ value = Set.new
+ size.times do
+ element = read_field(iprot, field_info(field[:element]))
+ value << element
+ end
+ end
+ iprot.read_set_end
+ else
+ value = iprot.read_type(field)
+ end
+ value
+ end
+
+ def write_data(oprot, value, field)
+ if is_container? field[:type]
+ write_container(oprot, value, field)
+ else
+ oprot.write_type(field, value)
+ end
+ end
+
+ def write_container(oprot, value, field = {})
+ case field[:type]
+ when Types::MAP
+ oprot.write_map_begin(field[:key][:type], field[:value][:type], value.size)
+ value.each do |k, v|
+ write_data(oprot, k, field[:key])
+ write_data(oprot, v, field[:value])
+ end
+ oprot.write_map_end
+ when Types::LIST
+ oprot.write_list_begin(field[:element][:type], value.size)
+ value.each do |elem|
+ write_data(oprot, elem, field[:element])
+ end
+ oprot.write_list_end
+ when Types::SET
+ oprot.write_set_begin(field[:element][:type], value.size)
+ value.each do |v,| # the , is to preserve compatibility with the old Hash-style sets
+ write_data(oprot, v, field[:element])
+ end
+ oprot.write_set_end
+ else
+ raise "Not a container type: #{field[:type]}"
+ end
+ end
+
+ CONTAINER_TYPES = []
+ CONTAINER_TYPES[Types::LIST] = true
+ CONTAINER_TYPES[Types::MAP] = true
+ CONTAINER_TYPES[Types::SET] = true
+ def is_container?(type)
+ CONTAINER_TYPES[type]
+ end
+
+ def field_info(field)
+ { :type => field[:type],
+ :class => field[:class],
+ :key => field[:key],
+ :value => field[:value],
+ :element => field[:element] }
+ end
+
+ def inspect_field(value, field_info)
+ if enum_class = field_info[:enum_class]
+ "#{enum_class.const_get(:VALUE_MAP)[value]} (#{value})"
+ elsif value.is_a? Hash
+ if field_info[:type] == Types::MAP
+ map_buf = []
+ value.each do |k, v|
+ map_buf << inspect_field(k, field_info[:key]) + ": " + inspect_field(v, field_info[:value])
+ end
+ "{" + map_buf.join(", ") + "}"
+ else
+ # old-style set
+ inspect_collection(value.keys, field_info)
+ end
+ elsif value.is_a? Array
+ inspect_collection(value, field_info)
+ elsif value.is_a? Set
+ inspect_collection(value, field_info)
+ elsif value.is_a?(String) && field_info[:binary]
+ value.unpack("H*").first
+ else
+ value.inspect
+ end
+ end
+
+ def inspect_collection(collection, field_info)
+ buf = []
+ collection.each do |k|
+ buf << inspect_field(k, field_info[:element])
+ end
+ "[" + buf.join(", ") + "]"
+ end
+ end
+end \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/thrift_native.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/thrift_native.rb
new file mode 100644
index 000000000..4d8df61ff
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/thrift_native.rb
@@ -0,0 +1,24 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+begin
+ require "thrift_native"
+rescue LoadError
+ puts "Unable to load thrift_native extension. Defaulting to pure Ruby libraries."
+end \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/base_server_transport.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/base_server_transport.rb
new file mode 100644
index 000000000..0105463f8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/base_server_transport.rb
@@ -0,0 +1,37 @@
+# encoding: ascii-8bit
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Thrift
+ class BaseServerTransport
+ def listen
+ raise NotImplementedError
+ end
+
+ def accept
+ raise NotImplementedError
+ end
+
+ def close; nil; end
+
+ def closed?
+ raise NotImplementedError
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/base_transport.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/base_transport.rb
new file mode 100644
index 000000000..97e59352a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/base_transport.rb
@@ -0,0 +1,117 @@
+# encoding: ascii-8bit
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Thrift
+ class TransportException < Exception
+ UNKNOWN = 0
+ NOT_OPEN = 1
+ ALREADY_OPEN = 2
+ TIMED_OUT = 3
+ END_OF_FILE = 4
+
+ attr_reader :type
+
+ def initialize(type=UNKNOWN, message=nil)
+ super(message)
+ @type = type
+ end
+ end
+
+ module TransportUtils
+ # Deprecated: Use Thrift::Bytes instead
+ def self.get_string_byte(string, index)
+ Bytes.get_string_byte(string, index)
+ end
+
+ # Deprecated: Use Thrift::Bytes instead
+ def self.set_string_byte(string, index, byte)
+ Bytes.set_string_byte(string, index, byte)
+ end
+ end
+
+ class BaseTransport
+ def open?; end
+
+ def open; end
+
+ def close; end
+
+ # Reads a number of bytes from the transports. In Ruby 1.9+, the String returned will have a BINARY (aka ASCII8BIT) encoding.
+ #
+ # sz - The number of bytes to read from the transport.
+ #
+ # Returns a String acting as a byte buffer.
+ def read(sz)
+ raise NotImplementedError
+ end
+
+ # Returns an unsigned byte as a Fixnum in the range (0..255).
+ def read_byte
+ buf = read_all(1)
+ return Bytes.get_string_byte(buf, 0)
+ end
+
+ # Reads size bytes and copies them into buffer[0..size].
+ def read_into_buffer(buffer, size)
+ tmp = read_all(size)
+ i = 0
+ tmp.each_byte do |byte|
+ Bytes.set_string_byte(buffer, i, byte)
+ i += 1
+ end
+ i
+ end
+
+ def read_all(size)
+ return Bytes.empty_byte_buffer if size <= 0
+ buf = read(size)
+ while (buf.length < size)
+ chunk = read(size - buf.length)
+ buf << chunk
+ end
+
+ buf
+ end
+
+ # Writes the byte buffer to the transport. In Ruby 1.9+, the buffer will be forced into BINARY encoding.
+ #
+ # buf - A String acting as a byte buffer.
+ #
+ # Returns nothing.
+ def write(buf); end
+ alias_method :<<, :write
+
+ def flush; end
+
+ def to_s
+ "base"
+ end
+ end
+
+ class BaseTransportFactory
+ def get_transport(trans)
+ return trans
+ end
+
+ def to_s
+ "base"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/buffered_transport.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/buffered_transport.rb
new file mode 100644
index 000000000..4fe9c41a5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/buffered_transport.rb
@@ -0,0 +1,122 @@
+# encoding: ascii-8bit
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Thrift
+ class BufferedTransport < BaseTransport
+ DEFAULT_BUFFER = 4096
+
+ def initialize(transport)
+ @transport = transport
+ @wbuf = Bytes.empty_byte_buffer
+ @rbuf = Bytes.empty_byte_buffer
+ @index = 0
+ end
+
+ def open?
+ return @transport.open?
+ end
+
+ def open
+ @transport.open
+ end
+
+ def close
+ flush
+ @transport.close
+ end
+
+ def read(sz)
+ @index += sz
+ ret = @rbuf.slice(@index - sz, sz) || Bytes.empty_byte_buffer
+
+ if ret.length == 0
+ @rbuf = @transport.read([sz, DEFAULT_BUFFER].max)
+ @index = sz
+ ret = @rbuf.slice(0, sz) || Bytes.empty_byte_buffer
+ end
+
+ ret
+ end
+
+ def read_byte
+ # If the read buffer is exhausted, try to read up to DEFAULT_BUFFER more bytes into it.
+ if @index >= @rbuf.size
+ @rbuf = @transport.read(DEFAULT_BUFFER)
+ @index = 0
+ end
+
+ # The read buffer has some data now, read a single byte. Using get_string_byte() avoids
+ # allocating a temp string of size 1 unnecessarily.
+ @index += 1
+ return Bytes.get_string_byte(@rbuf, @index - 1)
+ end
+
+ # Reads a number of bytes from the transport into the buffer passed.
+ #
+ # buffer - The String (byte buffer) to write data to; this is assumed to have a BINARY encoding.
+ # size - The number of bytes to read from the transport and write to the buffer.
+ #
+ # Returns the number of bytes read.
+ def read_into_buffer(buffer, size)
+ i = 0
+ while i < size
+ # If the read buffer is exhausted, try to read up to DEFAULT_BUFFER more bytes into it.
+ if @index >= @rbuf.size
+ @rbuf = @transport.read(DEFAULT_BUFFER)
+ @index = 0
+ end
+
+ # The read buffer has some data now, so copy bytes over to the output buffer.
+ byte = Bytes.get_string_byte(@rbuf, @index)
+ Bytes.set_string_byte(buffer, i, byte)
+ @index += 1
+ i += 1
+ end
+ i
+ end
+
+ def write(buf)
+ @wbuf << Bytes.force_binary_encoding(buf)
+ end
+
+ def flush
+ unless @wbuf.empty?
+ @transport.write(@wbuf)
+ @wbuf = Bytes.empty_byte_buffer
+ end
+
+ @transport.flush
+ end
+
+ def to_s
+ "buffered(#{@transport.to_s})"
+ end
+ end
+
+ class BufferedTransportFactory < BaseTransportFactory
+ def get_transport(transport)
+ return BufferedTransport.new(transport)
+ end
+
+ def to_s
+ "buffered"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/framed_transport.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/framed_transport.rb
new file mode 100644
index 000000000..953177821
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/framed_transport.rb
@@ -0,0 +1,125 @@
+# encoding: ascii-8bit
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Thrift
+ class FramedTransport < BaseTransport
+ def initialize(transport, read=true, write=true)
+ @transport = transport
+ @rbuf = Bytes.empty_byte_buffer
+ @wbuf = Bytes.empty_byte_buffer
+ @read = read
+ @write = write
+ @index = 0
+ end
+
+ def open?
+ @transport.open?
+ end
+
+ def open
+ @transport.open
+ end
+
+ def close
+ @transport.close
+ end
+
+ def read(sz)
+ return @transport.read(sz) unless @read
+
+ return Bytes.empty_byte_buffer if sz <= 0
+
+ read_frame if @index >= @rbuf.length
+
+ @index += sz
+ @rbuf.slice(@index - sz, sz) || Bytes.empty_byte_buffer
+ end
+
+ def read_byte
+ return @transport.read_byte() unless @read
+
+ read_frame if @index >= @rbuf.length
+
+ # The read buffer has some data now, read a single byte. Using get_string_byte() avoids
+ # allocating a temp string of size 1 unnecessarily.
+ @index += 1
+ return Bytes.get_string_byte(@rbuf, @index - 1)
+ end
+
+ def read_into_buffer(buffer, size)
+ i = 0
+ while i < size
+ read_frame if @index >= @rbuf.length
+
+ # The read buffer has some data now, so copy bytes over to the output buffer.
+ byte = Bytes.get_string_byte(@rbuf, @index)
+ Bytes.set_string_byte(buffer, i, byte)
+ @index += 1
+ i += 1
+ end
+ i
+ end
+
+ def write(buf, sz=nil)
+ return @transport.write(buf) unless @write
+
+ buf = Bytes.force_binary_encoding(buf)
+ @wbuf << (sz ? buf[0...sz] : buf)
+ end
+
+ #
+ # Writes the output buffer to the stream in the format of a 4-byte length
+ # followed by the actual data.
+ #
+ def flush
+ return @transport.flush unless @write
+
+ out = [@wbuf.length].pack('N')
+ # Array#pack should return a BINARY encoded String, so it shouldn't be necessary to force encoding
+ out << @wbuf
+ @transport.write(out)
+ @transport.flush
+ @wbuf = Bytes.empty_byte_buffer
+ end
+
+ def to_s
+ "framed(#{@transport.to_s})"
+ end
+
+ private
+
+ def read_frame
+ sz = @transport.read_all(4).unpack('N').first
+
+ @index = 0
+ @rbuf = @transport.read_all(sz)
+ end
+ end
+
+ class FramedTransportFactory < BaseTransportFactory
+ def get_transport(transport)
+ return FramedTransport.new(transport)
+ end
+
+ def to_s
+ "framed"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/http_client_transport.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/http_client_transport.rb
new file mode 100644
index 000000000..5c1dd5c8a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/http_client_transport.rb
@@ -0,0 +1,61 @@
+# encoding: ascii-8bit
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'net/http'
+require 'net/https'
+require 'openssl'
+require 'uri'
+require 'stringio'
+
+module Thrift
+ class HTTPClientTransport < BaseTransport
+
+ def initialize(url, opts = {})
+ @url = URI url
+ @headers = {'Content-Type' => 'application/x-thrift'}
+ @outbuf = Bytes.empty_byte_buffer
+ @ssl_verify_mode = opts.fetch(:ssl_verify_mode, OpenSSL::SSL::VERIFY_PEER)
+ end
+
+ def open?; true end
+ def read(sz); @inbuf.read sz end
+ def write(buf); @outbuf << Bytes.force_binary_encoding(buf) end
+
+ def add_headers(headers)
+ @headers = @headers.merge(headers)
+ end
+
+ def flush
+ http = Net::HTTP.new @url.host, @url.port
+ http.use_ssl = @url.scheme == 'https'
+ http.verify_mode = @ssl_verify_mode if @url.scheme == 'https'
+ resp = http.post(@url.request_uri, @outbuf, @headers)
+ data = resp.body
+ data = Bytes.force_binary_encoding(data)
+ @inbuf = StringIO.new data
+ ensure
+ @outbuf = Bytes.empty_byte_buffer
+ end
+
+ def to_s
+ "@{self.url}"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/io_stream_transport.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/io_stream_transport.rb
new file mode 100644
index 000000000..ccec68f25
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/io_stream_transport.rb
@@ -0,0 +1,42 @@
+# encoding: ascii-8bit
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Very very simple implementation of wrapping two objects, one with a #read
+# method and one with a #write method, into a transport for thrift.
+#
+# Assumes both objects are open, remain open, don't require flushing, etc.
+#
+module Thrift
+ class IOStreamTransport < BaseTransport
+ def initialize(input, output)
+ @input = input
+ @output = output
+ end
+
+ def open?; not @input.closed? or not @output.closed? end
+ def read(sz); @input.read(sz) end
+ def write(buf); @output.write(Bytes.force_binary_encoding(buf)) end
+ def close; @input.close; @output.close end
+ def to_io; @input end # we're assuming this is used in a IO.select for reading
+ def to_s
+ "iostream(input=#{@input.to_s},output=#{@output.to_s})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/memory_buffer_transport.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/memory_buffer_transport.rb
new file mode 100644
index 000000000..469ea7396
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/memory_buffer_transport.rb
@@ -0,0 +1,129 @@
+# encoding: ascii-8bit
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Thrift
+ class MemoryBufferTransport < BaseTransport
+ GARBAGE_BUFFER_SIZE = 4*(2**10) # 4kB
+
+ # If you pass a string to this, you should #dup that string
+ # unless you want it to be modified by #read and #write
+ #--
+ # this behavior is no longer required. If you wish to change it
+ # go ahead, just make sure the specs pass
+ def initialize(buffer = nil)
+ @buf = buffer ? Bytes.force_binary_encoding(buffer) : Bytes.empty_byte_buffer
+ @index = 0
+ end
+
+ def open?
+ return true
+ end
+
+ def open
+ end
+
+ def close
+ end
+
+ def peek
+ @index < @buf.size
+ end
+
+ # this method does not use the passed object directly but copies it
+ def reset_buffer(new_buf = '')
+ @buf.replace Bytes.force_binary_encoding(new_buf)
+ @index = 0
+ end
+
+ def available
+ @buf.length - @index
+ end
+
+ def read(len)
+ data = @buf.slice(@index, len)
+ @index += len
+ @index = @buf.size if @index > @buf.size
+ if @index >= GARBAGE_BUFFER_SIZE
+ @buf = @buf.slice(@index..-1)
+ @index = 0
+ end
+ if data.size < len
+ raise EOFError, "Not enough bytes remain in buffer"
+ end
+ data
+ end
+
+ def read_byte
+ raise EOFError.new("Not enough bytes remain in buffer") if @index >= @buf.size
+ val = Bytes.get_string_byte(@buf, @index)
+ @index += 1
+ if @index >= GARBAGE_BUFFER_SIZE
+ @buf = @buf.slice(@index..-1)
+ @index = 0
+ end
+ val
+ end
+
+ def read_into_buffer(buffer, size)
+ i = 0
+ while i < size
+ raise EOFError.new("Not enough bytes remain in buffer") if @index >= @buf.size
+
+ # The read buffer has some data now, so copy bytes over to the output buffer.
+ byte = Bytes.get_string_byte(@buf, @index)
+ Bytes.set_string_byte(buffer, i, byte)
+ @index += 1
+ i += 1
+ end
+ if @index >= GARBAGE_BUFFER_SIZE
+ @buf = @buf.slice(@index..-1)
+ @index = 0
+ end
+ i
+ end
+
+ def write(wbuf)
+ @buf << Bytes.force_binary_encoding(wbuf)
+ end
+
+ def flush
+ end
+
+ def inspect_buffer
+ out = []
+ for idx in 0...(@buf.size)
+ # if idx != 0
+ # out << " "
+ # end
+
+ if idx == @index
+ out << ">"
+ end
+
+ out << @buf[idx].ord.to_s(16)
+ end
+ out.join(" ")
+ end
+
+ def to_s
+ "memory"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/server_socket.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/server_socket.rb
new file mode 100644
index 000000000..50002324e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/server_socket.rb
@@ -0,0 +1,68 @@
+# encoding: ascii-8bit
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'socket'
+
+module Thrift
+ class ServerSocket < BaseServerTransport
+ # call-seq: initialize(host = nil, port)
+ def initialize(host_or_port, port = nil)
+ if port
+ @host = host_or_port
+ @port = port
+ else
+ @host = nil
+ @port = host_or_port
+ end
+ @handle = nil
+ end
+
+ attr_reader :handle
+
+ def listen
+ @handle = TCPServer.new(@host, @port)
+ end
+
+ def accept
+ unless @handle.nil?
+ sock = @handle.accept
+ trans = Socket.new
+ trans.handle = sock
+ trans
+ end
+ end
+
+ def close
+ @handle.close unless @handle.nil? or @handle.closed?
+ @handle = nil
+ end
+
+ def closed?
+ @handle.nil? or @handle.closed?
+ end
+
+ alias to_io handle
+
+ def to_s
+ "socket(#{@host}:#{@port})"
+ end
+
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/socket.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/socket.rb
new file mode 100644
index 000000000..f5e6f3b85
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/socket.rb
@@ -0,0 +1,143 @@
+# encoding: ascii-8bit
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'socket'
+
+module Thrift
+ class Socket < BaseTransport
+ def initialize(host='localhost', port=9090, timeout=nil)
+ @host = host
+ @port = port
+ @timeout = timeout
+ @desc = "#{host}:#{port}"
+ @handle = nil
+ end
+
+ attr_accessor :handle, :timeout
+
+ def open
+ for addrinfo in ::Socket::getaddrinfo(@host, @port, nil, ::Socket::SOCK_STREAM) do
+ begin
+ socket = ::Socket.new(addrinfo[4], ::Socket::SOCK_STREAM, 0)
+ socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
+ sockaddr = ::Socket.sockaddr_in(addrinfo[1], addrinfo[3])
+ begin
+ socket.connect_nonblock(sockaddr)
+ rescue Errno::EINPROGRESS
+ unless IO.select(nil, [ socket ], nil, @timeout)
+ next
+ end
+ begin
+ socket.connect_nonblock(sockaddr)
+ rescue Errno::EISCONN
+ end
+ end
+ return @handle = socket
+ rescue StandardError => e
+ next
+ end
+ end
+ raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}: #{e}")
+ end
+
+ def open?
+ !@handle.nil? and !@handle.closed?
+ end
+
+ def write(str)
+ raise IOError, "closed stream" unless open?
+ str = Bytes.force_binary_encoding(str)
+ begin
+ if @timeout.nil? or @timeout == 0
+ @handle.write(str)
+ else
+ len = 0
+ start = Time.now
+ while Time.now - start < @timeout
+ rd, wr, = IO.select(nil, [@handle], nil, @timeout)
+ if wr and not wr.empty?
+ len += @handle.write_nonblock(str[len..-1])
+ break if len >= str.length
+ end
+ end
+ if len < str.length
+ raise TransportException.new(TransportException::TIMED_OUT, "Socket: Timed out writing #{str.length} bytes to #{@desc}")
+ else
+ len
+ end
+ end
+ rescue TransportException => e
+ # pass this on
+ raise e
+ rescue StandardError => e
+ @handle.close
+ @handle = nil
+ raise TransportException.new(TransportException::NOT_OPEN, e.message)
+ end
+ end
+
+ def read(sz)
+ raise IOError, "closed stream" unless open?
+
+ begin
+ if @timeout.nil? or @timeout == 0
+ data = @handle.readpartial(sz)
+ else
+ # it's possible to interrupt select for something other than the timeout
+ # so we need to ensure we've waited long enough, but not too long
+ start = Time.now
+ timespent = 0
+ rd = loop do
+ rd, = IO.select([@handle], nil, nil, @timeout - timespent)
+ timespent = Time.now - start
+ break rd if (rd and not rd.empty?) or timespent >= @timeout
+ end
+ if rd.nil? or rd.empty?
+ raise TransportException.new(TransportException::TIMED_OUT, "Socket: Timed out reading #{sz} bytes from #{@desc}")
+ else
+ data = @handle.readpartial(sz)
+ end
+ end
+ rescue TransportException => e
+ # don't let this get caught by the StandardError handler
+ raise e
+ rescue StandardError => e
+ @handle.close unless @handle.closed?
+ @handle = nil
+ raise TransportException.new(TransportException::NOT_OPEN, e.message)
+ end
+ if (data.nil? or data.length == 0)
+ raise TransportException.new(TransportException::UNKNOWN, "Socket: Could not read #{sz} bytes from #{@desc}")
+ end
+ data
+ end
+
+ def close
+ @handle.close unless @handle.nil? or @handle.closed?
+ @handle = nil
+ end
+
+ alias to_io handle
+
+ def to_s
+ "socket(#{@host}:#{@port})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/ssl_server_socket.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/ssl_server_socket.rb
new file mode 100644
index 000000000..3abd5ec3d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/ssl_server_socket.rb
@@ -0,0 +1,41 @@
+# encoding: ascii-8bit
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'socket'
+
+module Thrift
+ class SSLServerSocket < ServerSocket
+ def initialize(host_or_port, port = nil, ssl_context = nil)
+ super(host_or_port, port)
+ @ssl_context = ssl_context
+ end
+
+ attr_accessor :ssl_context
+
+ def listen
+ socket = TCPServer.new(@host, @port)
+ @handle = OpenSSL::SSL::SSLServer.new(socket, @ssl_context)
+ end
+
+ def to_s
+ "ssl(#{super.to_s})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/ssl_socket.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/ssl_socket.rb
new file mode 100644
index 000000000..7ab96ab45
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/ssl_socket.rb
@@ -0,0 +1,51 @@
+# encoding: ascii-8bit
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+module Thrift
+ class SSLSocket < Socket
+ def initialize(host='localhost', port=9090, timeout=nil, ssl_context=nil)
+ super(host, port, timeout)
+ @ssl_context = ssl_context
+ end
+
+ attr_accessor :ssl_context
+
+ def open
+ socket = super
+ @handle = OpenSSL::SSL::SSLSocket.new(socket, @ssl_context)
+ begin
+ @handle.connect_nonblock
+ @handle.post_connection_check(@host)
+ @handle
+ rescue IO::WaitReadable
+ IO.select([ @handle ], nil, nil, @timeout)
+ retry
+ rescue IO::WaitWritable
+ IO.select(nil, [ @handle ], nil, @timeout)
+ retry
+ rescue StandardError => e
+ raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}: #{e}")
+ end
+ end
+
+ def to_s
+ "ssl(#{super.to_s})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/unix_server_socket.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/unix_server_socket.rb
new file mode 100644
index 000000000..057d122e7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/unix_server_socket.rb
@@ -0,0 +1,64 @@
+# encoding: ascii-8bit
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'socket'
+
+module Thrift
+ class UNIXServerSocket < BaseServerTransport
+ def initialize(path)
+ @path = path
+ @handle = nil
+ end
+
+ attr_accessor :handle
+
+ def listen
+ @handle = ::UNIXServer.new(@path)
+ end
+
+ def accept
+ unless @handle.nil?
+ sock = @handle.accept
+ trans = UNIXSocket.new(nil)
+ trans.handle = sock
+ trans
+ end
+ end
+
+ def close
+ if @handle
+ @handle.close unless @handle.closed?
+ @handle = nil
+ # UNIXServer doesn't delete the socket file, so we have to do it ourselves
+ File.delete(@path)
+ end
+ end
+
+ def closed?
+ @handle.nil? or @handle.closed?
+ end
+
+ alias to_io handle
+
+ def to_s
+ "domain(#{@path})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/unix_socket.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/unix_socket.rb
new file mode 100644
index 000000000..5dffd59f2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/unix_socket.rb
@@ -0,0 +1,44 @@
+# encoding: ascii-8bit
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'socket'
+
+module Thrift
+ class UNIXSocket < Socket
+ def initialize(path, timeout=nil)
+ @path = path
+ @timeout = timeout
+ @desc = @path # for read()'s error
+ @handle = nil
+ end
+
+ def open
+ begin
+ @handle = ::UNIXSocket.new(@path)
+ rescue StandardError
+ raise TransportException.new(TransportException::NOT_OPEN, "Could not open UNIX socket at #{@path}")
+ end
+ end
+
+ def to_s
+ "domain(#{@path})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/types.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/types.rb
new file mode 100644
index 000000000..cac52691a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/types.rb
@@ -0,0 +1,101 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'set'
+
+module Thrift
+ module Types
+ STOP = 0
+ VOID = 1
+ BOOL = 2
+ BYTE = 3
+ DOUBLE = 4
+ I16 = 6
+ I32 = 8
+ I64 = 10
+ STRING = 11
+ STRUCT = 12
+ MAP = 13
+ SET = 14
+ LIST = 15
+ end
+
+ class << self
+ attr_accessor :type_checking
+ end
+
+ class TypeError < Exception
+ end
+
+ def self.check_type(value, field, name, skip_nil=true)
+ return if value.nil? and skip_nil
+ klasses = case field[:type]
+ when Types::VOID
+ NilClass
+ when Types::BOOL
+ [TrueClass, FalseClass]
+ when Types::BYTE, Types::I16, Types::I32, Types::I64
+ Integer
+ when Types::DOUBLE
+ Float
+ when Types::STRING
+ String
+ when Types::STRUCT
+ [Struct, Union]
+ when Types::MAP
+ Hash
+ when Types::SET
+ Set
+ when Types::LIST
+ Array
+ end
+ valid = klasses && [*klasses].any? { |klass| klass === value }
+ raise TypeError, "Expected #{type_name(field[:type])}, received #{value.class} for field #{name}" unless valid
+ # check elements now
+ case field[:type]
+ when Types::MAP
+ value.each_pair do |k,v|
+ check_type(k, field[:key], "#{name}.key", false)
+ check_type(v, field[:value], "#{name}.value", false)
+ end
+ when Types::SET, Types::LIST
+ value.each do |el|
+ check_type(el, field[:element], "#{name}.element", false)
+ end
+ when Types::STRUCT
+ raise TypeError, "Expected #{field[:class]}, received #{value.class} for field #{name}" unless field[:class] == value.class
+ end
+ end
+
+ def self.type_name(type)
+ Types.constants.each do |const|
+ return "Types::#{const}" if Types.const_get(const) == type
+ end
+ nil
+ end
+
+ module MessageTypes
+ CALL = 1
+ REPLY = 2
+ EXCEPTION = 3
+ ONEWAY = 4
+ end
+end
+
+Thrift.type_checking = false if Thrift.type_checking.nil?
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/union.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/union.rb
new file mode 100644
index 000000000..490c55c40
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/union.rb
@@ -0,0 +1,176 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Thrift
+ class Union
+ def initialize(name=nil, value=nil)
+ if name
+ if name.is_a? Hash
+ if name.size > 1
+ raise "#{self.class} cannot be instantiated with more than one field!"
+ end
+
+ name, value = name.keys.first, name.values.first
+ end
+
+ if Thrift.type_checking
+ raise Exception, "#{self.class} does not contain a field named #{name}!" unless name_to_id(name.to_s)
+ end
+
+ if value.nil?
+ raise Exception, "Union #{self.class} cannot be instantiated with setfield and nil value!"
+ end
+
+ Thrift.check_type(value, struct_fields[name_to_id(name.to_s)], name) if Thrift.type_checking
+ elsif !value.nil?
+ raise Exception, "Value provided, but no name!"
+ end
+ @setfield = name
+ @value = value
+ end
+
+ def inspect
+ if get_set_field
+ "<#{self.class} #{@setfield}: #{inspect_field(@value, struct_fields[name_to_id(@setfield.to_s)])}>"
+ else
+ "<#{self.class} >"
+ end
+ end
+
+ def read(iprot)
+ iprot.read_struct_begin
+ fname, ftype, fid = iprot.read_field_begin
+ handle_message(iprot, fid, ftype)
+ iprot.read_field_end
+
+ fname, ftype, fid = iprot.read_field_begin
+ raise "Too many fields for union" unless (ftype == Types::STOP)
+
+ iprot.read_struct_end
+ validate
+ end
+
+ def write(oprot)
+ validate
+ oprot.write_struct_begin(self.class.name)
+
+ fid = self.name_to_id(@setfield.to_s)
+
+ field_info = struct_fields[fid]
+ type = field_info[:type]
+ if is_container? type
+ oprot.write_field_begin(@setfield, type, fid)
+ write_container(oprot, @value, field_info)
+ oprot.write_field_end
+ else
+ oprot.write_field(@setfield, type, fid, @value)
+ end
+
+ oprot.write_field_stop
+ oprot.write_struct_end
+ end
+
+ def ==(other)
+ other.equal?(self) || other.instance_of?(self.class) && @setfield == other.get_set_field && @value == other.get_value
+ end
+ alias_method :eql?, :==
+
+ def hash
+ [self.class.name, @setfield, @value].hash
+ end
+
+ def self.field_accessor(klass, field_info)
+ klass.send :define_method, field_info[:name] do
+ if field_info[:name].to_sym == @setfield
+ @value
+ else
+ raise RuntimeError, "#{field_info[:name]} is not union's set field."
+ end
+ end
+
+ klass.send :define_method, "#{field_info[:name]}=" do |value|
+ Thrift.check_type(value, field_info, field_info[:name]) if Thrift.type_checking
+ @setfield = field_info[:name].to_sym
+ @value = value
+ end
+ end
+
+ def self.qmark_isset_method(klass, field_info)
+ klass.send :define_method, "#{field_info[:name]}?" do
+ get_set_field == field_info[:name].to_sym && !get_value.nil?
+ end
+ end
+
+ def self.generate_accessors(klass)
+ klass::FIELDS.values.each do |field_info|
+ field_accessor(klass, field_info)
+ qmark_isset_method(klass, field_info)
+ end
+ end
+
+ # get the symbol that indicates what the currently set field type is.
+ def get_set_field
+ @setfield
+ end
+
+ # get the current value of this union, regardless of what the set field is.
+ # generally, you should only use this method when you don't know in advance
+ # what field to expect.
+ def get_value
+ @value
+ end
+
+ def <=>(other)
+ if self.class == other.class
+ if get_set_field == other.get_set_field
+ if get_set_field.nil?
+ 0
+ else
+ get_value <=> other.get_value
+ end
+ else
+ if get_set_field && other.get_set_field.nil?
+ -1
+ elsif get_set_field.nil? && other.get_set_field
+ 1
+ elsif get_set_field.nil? && other.get_set_field.nil?
+ 0
+ else
+ name_to_id(get_set_field.to_s) <=> name_to_id(other.get_set_field.to_s)
+ end
+ end
+ else
+ self.class <=> other.class
+ end
+ end
+
+ protected
+
+ def handle_message(iprot, fid, ftype)
+ field = struct_fields[fid]
+ if field and field[:type] == ftype
+ @value = read_field(iprot, field)
+ name = field[:name].to_sym
+ @setfield = name
+ else
+ iprot.skip(ftype)
+ end
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/script/proto_benchmark.rb b/src/jaegertracing/thrift/lib/rb/script/proto_benchmark.rb
new file mode 100644
index 000000000..bb49e2e42
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/script/proto_benchmark.rb
@@ -0,0 +1,121 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require File.dirname(__FILE__) + "/../spec/spec_helper.rb"
+
+require "benchmark"
+# require "ruby-prof"
+
+obj = Fixtures::COMPACT_PROTOCOL_TEST_STRUCT
+
+HOW_MANY = 1_000
+
+binser = Thrift::Serializer.new
+bin_data = binser.serialize(obj)
+bindeser = Thrift::Deserializer.new
+accel_bin_ser = Thrift::Serializer.new(Thrift::BinaryProtocolAcceleratedFactory.new)
+accel_bin_deser = Thrift::Deserializer.new(Thrift::BinaryProtocolAcceleratedFactory.new)
+
+compact_ser = Thrift::Serializer.new(Thrift::CompactProtocolFactory.new)
+compact_data = compact_ser.serialize(obj)
+compact_deser = Thrift::Deserializer.new(Thrift::CompactProtocolFactory.new)
+
+Benchmark.bm(60) do |reporter|
+ reporter.report("binary protocol, write") do
+ HOW_MANY.times do
+ binser.serialize(obj)
+ end
+ end
+
+ reporter.report("accelerated binary protocol, write") do
+ HOW_MANY.times do
+ accel_bin_ser.serialize(obj)
+ end
+ end
+
+ reporter.report("compact protocol, write") do
+ # RubyProf.start
+ HOW_MANY.times do
+ compact_ser.serialize(obj)
+ end
+ # result = RubyProf.stop
+ # printer = RubyProf::GraphHtmlPrinter.new(result)
+ # file = File.open("profile.html", "w+")
+ # printer.print(file, 0)
+ # file.close
+ end
+
+ reporter.report("binary protocol, read") do
+ HOW_MANY.times do
+ bindeser.deserialize(obj, bin_data)
+ end
+ end
+
+ reporter.report("accelerated binary protocol, read") do
+ HOW_MANY.times do
+ accel_bin_deser.deserialize(obj, bin_data)
+ end
+ end
+
+ reporter.report("compact protocol, read") do
+ HOW_MANY.times do
+ compact_deser.deserialize(obj, compact_data)
+ end
+ end
+
+
+ # f = File.new("/tmp/testfile", "w")
+ # proto = Thrift::BinaryProtocolAccelerated.new(Thrift::IOStreamTransport.new(Thrift::MemoryBufferTransport.new, f))
+ # reporter.report("accelerated binary protocol, write (to disk)") do
+ # HOW_MANY.times do
+ # obj.write(proto)
+ # end
+ # f.flush
+ # end
+ # f.close
+ #
+ # f = File.new("/tmp/testfile", "r")
+ # proto = Thrift::BinaryProtocolAccelerated.new(Thrift::IOStreamTransport.new(f, Thrift::MemoryBufferTransport.new))
+ # reporter.report("accelerated binary protocol, read (from disk)") do
+ # HOW_MANY.times do
+ # obj.read(proto)
+ # end
+ # end
+ # f.close
+ #
+ # f = File.new("/tmp/testfile", "w")
+ # reporter.report("compact protocol, write (to disk)") do
+ # proto = Thrift::CompactProtocol.new(Thrift::IOStreamTransport.new(Thrift::MemoryBufferTransport.new, f))
+ # HOW_MANY.times do
+ # obj.write(proto)
+ # end
+ # f.flush
+ # end
+ # f.close
+ #
+ # f = File.new("/tmp/testfile", "r")
+ # reporter.report("compact protocol, read (from disk)") do
+ # proto = Thrift::CompactProtocol.new(Thrift::IOStreamTransport.new(f, Thrift::MemoryBufferTransport.new))
+ # HOW_MANY.times do
+ # obj.read(proto)
+ # end
+ # end
+ # f.close
+
+end
diff --git a/src/jaegertracing/thrift/lib/rb/script/read_struct.rb b/src/jaegertracing/thrift/lib/rb/script/read_struct.rb
new file mode 100644
index 000000000..831fcec90
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/script/read_struct.rb
@@ -0,0 +1,43 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require "spec/spec_helper"
+
+path, factory_class = ARGV
+
+factory = eval(factory_class).new
+
+deser = Thrift::Deserializer.new(factory)
+
+cpts = CompactProtoTestStruct.new
+CompactProtoTestStruct.constants.each do |const|
+ cpts.instance_variable_set("@#{const}", nil)
+end
+
+data = File.read(path)
+
+deser.deserialize(cpts, data)
+
+if cpts == Fixtures::COMPACT_PROTOCOL_TEST_STRUCT
+ puts "Object verified successfully!"
+else
+ puts "Object failed verification! Expected #{Fixtures::COMPACT_PROTOCOL_TEST_STRUCT.inspect} but got #{cpts.inspect}"
+
+ puts cpts.differences(Fixtures::COMPACT_PROTOCOL_TEST_STRUCT)
+end
diff --git a/src/jaegertracing/thrift/lib/rb/script/write_struct.rb b/src/jaegertracing/thrift/lib/rb/script/write_struct.rb
new file mode 100644
index 000000000..da1421975
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/script/write_struct.rb
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require "spec/spec_helper"
+
+path, factory_class = ARGV
+
+factory = eval(factory_class).new
+
+ser = Thrift::Serializer.new(factory)
+
+File.open(path, "w") do |file|
+ file.write(ser.serialize(Fixtures::COMPACT_PROTOCOL_TEST_STRUCT))
+end \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/rb/spec/BaseService.thrift b/src/jaegertracing/thrift/lib/rb/spec/BaseService.thrift
new file mode 100644
index 000000000..5c7d32a6c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/BaseService.thrift
@@ -0,0 +1,27 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+namespace rb Base
+
+struct Hello {
+ 1: string greeting = "hello world"
+}
+
+service BaseService {
+ Hello greeting(1:bool english)
+}
diff --git a/src/jaegertracing/thrift/lib/rb/spec/ExtendedService.thrift b/src/jaegertracing/thrift/lib/rb/spec/ExtendedService.thrift
new file mode 100644
index 000000000..1a6b705aa
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/ExtendedService.thrift
@@ -0,0 +1,25 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+namespace rb Extended
+
+include "BaseService.thrift"
+
+service ExtendedService extends BaseService.BaseService {
+ void ping()
+}
diff --git a/src/jaegertracing/thrift/lib/rb/spec/Referenced.thrift b/src/jaegertracing/thrift/lib/rb/spec/Referenced.thrift
new file mode 100644
index 000000000..98f183fe0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/Referenced.thrift
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+namespace rb OtherNamespace
+
+enum SomeEnum {
+ ONE
+ TWO
+}
diff --git a/src/jaegertracing/thrift/lib/rb/spec/ThriftNamespacedSpec.thrift b/src/jaegertracing/thrift/lib/rb/spec/ThriftNamespacedSpec.thrift
new file mode 100644
index 000000000..02f28895a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/ThriftNamespacedSpec.thrift
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+namespace rb NamespacedSpecNamespace
+
+include "Referenced.thrift"
+
+struct Hello {
+ 1: string greeting = "hello world"
+}
+
+service NamespacedNonblockingService {
+ Hello greeting(1:bool english)
+ bool block()
+ oneway void unblock(1:i32 n)
+ oneway void shutdown()
+ void sleep(1:double seconds)
+}
diff --git a/src/jaegertracing/thrift/lib/rb/spec/ThriftSpec.thrift b/src/jaegertracing/thrift/lib/rb/spec/ThriftSpec.thrift
new file mode 100644
index 000000000..b42481b32
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/ThriftSpec.thrift
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+namespace rb SpecNamespace
+
+struct Hello {
+ 1: string greeting = "hello world"
+}
+
+enum SomeEnum {
+ ONE
+ TWO
+}
+
+struct StructWithSomeEnum {
+ 1: SomeEnum some_enum;
+}
+
+union TestUnion {
+ /**
+ * A doc string
+ */
+ 1: string string_field;
+ 2: i32 i32_field;
+ 3: i32 other_i32_field;
+ 4: SomeEnum enum_field;
+ 5: binary binary_field;
+}
+
+struct Foo {
+ 1: i32 simple = 53,
+ 2: string words = "words",
+ 3: Hello hello = {'greeting' : "hello, world!"},
+ 4: list<i32> ints = [1, 2, 2, 3],
+ 5: map<i32, map<string, double>> complex,
+ 6: set<i16> shorts = [5, 17, 239],
+ 7: optional string opt_string
+ 8: bool my_bool
+}
+
+struct Foo2 {
+ 1: binary my_binary
+}
+
+struct BoolStruct {
+ 1: bool yesno = 1
+}
+
+struct SimpleList {
+ 1: list<bool> bools,
+ 2: list<byte> bytes,
+ 3: list<i16> i16s,
+ 4: list<i32> i32s,
+ 5: list<i64> i64s,
+ 6: list<double> doubles,
+ 7: list<string> strings,
+ 8: list<map<i16, i16>> maps,
+ 9: list<list<i16>> lists,
+ 10: list<set<i16>> sets,
+ 11: list<Hello> hellos
+}
+
+exception Xception {
+ 1: string message,
+ 2: i32 code = 1
+}
+
+service NonblockingService {
+ Hello greeting(1:bool english)
+ bool block()
+ oneway void unblock(1:i32 n)
+ oneway void shutdown()
+ void sleep(1:double seconds)
+}
+
+union My_union {
+ 1: bool im_true,
+ 2: byte a_bite,
+ 3: i16 integer16,
+ 4: i32 integer32,
+ 5: i64 integer64,
+ 6: double double_precision,
+ 7: string some_characters,
+ 8: i32 other_i32
+ 9: SomeEnum some_enum;
+ 10: map<SomeEnum, list<SomeEnum>> my_map;
+}
+
+struct Struct_with_union {
+ 1: My_union fun_union
+ 2: i32 integer32
+ 3: string some_characters
+}
+
+struct StructWithEnumMap {
+ 1: map<SomeEnum, list<SomeEnum>> my_map;
+}
+
+# Nested lists
+struct NestedListInList {
+ 1: list<list<byte>> value
+}
+
+struct NestedListInSet {
+ 1: set<list<byte>> value
+}
+
+struct NestedListInMapKey {
+ 1: map<list<byte>, byte> value
+}
+
+struct NestedListInMapValue {
+ 1: map<byte, list<byte>> value
+}
+
+# Nested sets
+struct NestedSetInList {
+ 1: list<set<byte>> value
+}
+
+struct NestedSetInSet {
+ 1: set<set<byte>> value
+}
+
+struct NestedSetInMapKey {
+ 1: map<set<byte>, byte> value
+}
+
+struct NestedSetInMapValue {
+ 1: map<byte, set<byte>> value
+}
+
+# Nested maps
+struct NestedMapInList {
+ 1: list<map<byte, byte>> value
+}
+
+struct NestedMapInSet {
+ 1: set<map<byte, byte>> value
+}
+
+struct NestedMapInMapKey {
+ 2: map<map<byte, byte>, byte> value
+}
+
+struct NestedMapInMapValue {
+ 2: map<byte, map<byte, byte>> value
+}
diff --git a/src/jaegertracing/thrift/lib/rb/spec/base_protocol_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/base_protocol_spec.rb
new file mode 100644
index 000000000..cfa7573d8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/base_protocol_spec.rb
@@ -0,0 +1,225 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+describe 'BaseProtocol' do
+
+ before(:each) do
+ @trans = double("MockTransport")
+ @prot = Thrift::BaseProtocol.new(@trans)
+ end
+
+ describe Thrift::BaseProtocol do
+ # most of the methods are stubs, so we can ignore them
+
+ it "should provide a reasonable to_s" do
+ expect(@trans).to receive(:to_s).once.and_return("trans")
+ expect(@prot.to_s).to eq("trans")
+ end
+
+ it "should make trans accessible" do
+ expect(@prot.trans).to eql(@trans)
+ end
+
+ it 'should write out a field nicely (deprecated write_field signature)' do
+ expect(@prot).to receive(:write_field_begin).with('field', 'type', 'fid').ordered
+ expect(@prot).to receive(:write_type).with({:name => 'field', :type => 'type'}, 'value').ordered
+ expect(@prot).to receive(:write_field_end).ordered
+ @prot.write_field('field', 'type', 'fid', 'value')
+ end
+
+ it 'should write out a field nicely' do
+ expect(@prot).to receive(:write_field_begin).with('field', 'type', 'fid').ordered
+ expect(@prot).to receive(:write_type).with({:name => 'field', :type => 'type', :binary => false}, 'value').ordered
+ expect(@prot).to receive(:write_field_end).ordered
+ @prot.write_field({:name => 'field', :type => 'type', :binary => false}, 'fid', 'value')
+ end
+
+ it 'should write out the different types (deprecated write_type signature)' do
+ expect(@prot).to receive(:write_bool).with('bool').ordered
+ expect(@prot).to receive(:write_byte).with('byte').ordered
+ expect(@prot).to receive(:write_double).with('double').ordered
+ expect(@prot).to receive(:write_i16).with('i16').ordered
+ expect(@prot).to receive(:write_i32).with('i32').ordered
+ expect(@prot).to receive(:write_i64).with('i64').ordered
+ expect(@prot).to receive(:write_string).with('string').ordered
+ struct = double('Struct')
+ expect(struct).to receive(:write).with(@prot).ordered
+ @prot.write_type(Thrift::Types::BOOL, 'bool')
+ @prot.write_type(Thrift::Types::BYTE, 'byte')
+ @prot.write_type(Thrift::Types::DOUBLE, 'double')
+ @prot.write_type(Thrift::Types::I16, 'i16')
+ @prot.write_type(Thrift::Types::I32, 'i32')
+ @prot.write_type(Thrift::Types::I64, 'i64')
+ @prot.write_type(Thrift::Types::STRING, 'string')
+ @prot.write_type(Thrift::Types::STRUCT, struct)
+ # all other types are not implemented
+ [Thrift::Types::STOP, Thrift::Types::VOID, Thrift::Types::MAP, Thrift::Types::SET, Thrift::Types::LIST].each do |type|
+ expect { @prot.write_type(type, type.to_s) }.to raise_error(NotImplementedError)
+ end
+ end
+
+ it 'should write out the different types' do
+ expect(@prot).to receive(:write_bool).with('bool').ordered
+ expect(@prot).to receive(:write_byte).with('byte').ordered
+ expect(@prot).to receive(:write_double).with('double').ordered
+ expect(@prot).to receive(:write_i16).with('i16').ordered
+ expect(@prot).to receive(:write_i32).with('i32').ordered
+ expect(@prot).to receive(:write_i64).with('i64').ordered
+ expect(@prot).to receive(:write_string).with('string').ordered
+ expect(@prot).to receive(:write_binary).with('binary').ordered
+ struct = double('Struct')
+ expect(struct).to receive(:write).with(@prot).ordered
+ @prot.write_type({:type => Thrift::Types::BOOL}, 'bool')
+ @prot.write_type({:type => Thrift::Types::BYTE}, 'byte')
+ @prot.write_type({:type => Thrift::Types::DOUBLE}, 'double')
+ @prot.write_type({:type => Thrift::Types::I16}, 'i16')
+ @prot.write_type({:type => Thrift::Types::I32}, 'i32')
+ @prot.write_type({:type => Thrift::Types::I64}, 'i64')
+ @prot.write_type({:type => Thrift::Types::STRING}, 'string')
+ @prot.write_type({:type => Thrift::Types::STRING, :binary => true}, 'binary')
+ @prot.write_type({:type => Thrift::Types::STRUCT}, struct)
+ # all other types are not implemented
+ [Thrift::Types::STOP, Thrift::Types::VOID, Thrift::Types::MAP, Thrift::Types::SET, Thrift::Types::LIST].each do |type|
+ expect { @prot.write_type({:type => type}, type.to_s) }.to raise_error(NotImplementedError)
+ end
+ end
+
+ it 'should read the different types (deprecated read_type signature)' do
+ expect(@prot).to receive(:read_bool).ordered
+ expect(@prot).to receive(:read_byte).ordered
+ expect(@prot).to receive(:read_i16).ordered
+ expect(@prot).to receive(:read_i32).ordered
+ expect(@prot).to receive(:read_i64).ordered
+ expect(@prot).to receive(:read_double).ordered
+ expect(@prot).to receive(:read_string).ordered
+ @prot.read_type(Thrift::Types::BOOL)
+ @prot.read_type(Thrift::Types::BYTE)
+ @prot.read_type(Thrift::Types::I16)
+ @prot.read_type(Thrift::Types::I32)
+ @prot.read_type(Thrift::Types::I64)
+ @prot.read_type(Thrift::Types::DOUBLE)
+ @prot.read_type(Thrift::Types::STRING)
+ # all other types are not implemented
+ [Thrift::Types::STOP, Thrift::Types::VOID, Thrift::Types::MAP,
+ Thrift::Types::SET, Thrift::Types::LIST, Thrift::Types::STRUCT].each do |type|
+ expect { @prot.read_type(type) }.to raise_error(NotImplementedError)
+ end
+ end
+
+ it 'should read the different types' do
+ expect(@prot).to receive(:read_bool).ordered
+ expect(@prot).to receive(:read_byte).ordered
+ expect(@prot).to receive(:read_i16).ordered
+ expect(@prot).to receive(:read_i32).ordered
+ expect(@prot).to receive(:read_i64).ordered
+ expect(@prot).to receive(:read_double).ordered
+ expect(@prot).to receive(:read_string).ordered
+ expect(@prot).to receive(:read_binary).ordered
+ @prot.read_type({:type => Thrift::Types::BOOL})
+ @prot.read_type({:type => Thrift::Types::BYTE})
+ @prot.read_type({:type => Thrift::Types::I16})
+ @prot.read_type({:type => Thrift::Types::I32})
+ @prot.read_type({:type => Thrift::Types::I64})
+ @prot.read_type({:type => Thrift::Types::DOUBLE})
+ @prot.read_type({:type => Thrift::Types::STRING})
+ @prot.read_type({:type => Thrift::Types::STRING, :binary => true})
+ # all other types are not implemented
+ [Thrift::Types::STOP, Thrift::Types::VOID, Thrift::Types::MAP,
+ Thrift::Types::SET, Thrift::Types::LIST, Thrift::Types::STRUCT].each do |type|
+ expect { @prot.read_type({:type => type}) }.to raise_error(NotImplementedError)
+ end
+ end
+
+ it "should skip the basic types" do
+ expect(@prot).to receive(:read_bool).ordered
+ expect(@prot).to receive(:read_byte).ordered
+ expect(@prot).to receive(:read_i16).ordered
+ expect(@prot).to receive(:read_i32).ordered
+ expect(@prot).to receive(:read_i64).ordered
+ expect(@prot).to receive(:read_double).ordered
+ expect(@prot).to receive(:read_string).ordered
+ @prot.skip(Thrift::Types::BOOL)
+ @prot.skip(Thrift::Types::BYTE)
+ @prot.skip(Thrift::Types::I16)
+ @prot.skip(Thrift::Types::I32)
+ @prot.skip(Thrift::Types::I64)
+ @prot.skip(Thrift::Types::DOUBLE)
+ @prot.skip(Thrift::Types::STRING)
+ end
+
+ it "should skip structs" do
+ real_skip = @prot.method(:skip)
+ expect(@prot).to receive(:read_struct_begin).ordered
+ expect(@prot).to receive(:read_field_begin).exactly(4).times.and_return(
+ ['field 1', Thrift::Types::STRING, 1],
+ ['field 2', Thrift::Types::I32, 2],
+ ['field 3', Thrift::Types::MAP, 3],
+ [nil, Thrift::Types::STOP, 0]
+ )
+ expect(@prot).to receive(:read_field_end).exactly(3).times
+ expect(@prot).to receive(:read_string).exactly(3).times
+ expect(@prot).to receive(:read_i32).ordered
+ expect(@prot).to receive(:read_map_begin).ordered.and_return([Thrift::Types::STRING, Thrift::Types::STRING, 1])
+ # @prot.should_receive(:read_string).exactly(2).times
+ expect(@prot).to receive(:read_map_end).ordered
+ expect(@prot).to receive(:read_struct_end).ordered
+ real_skip.call(Thrift::Types::STRUCT)
+ end
+
+ it "should skip maps" do
+ real_skip = @prot.method(:skip)
+ expect(@prot).to receive(:read_map_begin).ordered.and_return([Thrift::Types::STRING, Thrift::Types::STRUCT, 1])
+ expect(@prot).to receive(:read_string).ordered
+ expect(@prot).to receive(:read_struct_begin).ordered.and_return(["some_struct"])
+ expect(@prot).to receive(:read_field_begin).ordered.and_return([nil, Thrift::Types::STOP, nil]);
+ expect(@prot).to receive(:read_struct_end).ordered
+ expect(@prot).to receive(:read_map_end).ordered
+ real_skip.call(Thrift::Types::MAP)
+ end
+
+ it "should skip sets" do
+ real_skip = @prot.method(:skip)
+ expect(@prot).to receive(:read_set_begin).ordered.and_return([Thrift::Types::I64, 9])
+ expect(@prot).to receive(:read_i64).ordered.exactly(9).times
+ expect(@prot).to receive(:read_set_end)
+ real_skip.call(Thrift::Types::SET)
+ end
+
+ it "should skip lists" do
+ real_skip = @prot.method(:skip)
+ expect(@prot).to receive(:read_list_begin).ordered.and_return([Thrift::Types::DOUBLE, 11])
+ expect(@prot).to receive(:read_double).ordered.exactly(11).times
+ expect(@prot).to receive(:read_list_end)
+ real_skip.call(Thrift::Types::LIST)
+ end
+ end
+
+ describe Thrift::BaseProtocolFactory do
+ it "should raise NotImplementedError" do
+ # returning nil since Protocol is just an abstract class
+ expect {Thrift::BaseProtocolFactory.new.get_protocol(double("MockTransport"))}.to raise_error(NotImplementedError)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::BaseProtocolFactory.new.to_s).to eq("base")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/base_transport_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/base_transport_spec.rb
new file mode 100644
index 000000000..d2f60aaea
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/base_transport_spec.rb
@@ -0,0 +1,388 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+describe 'BaseTransport' do
+
+ describe Thrift::TransportException do
+ it "should make type accessible" do
+ exc = Thrift::TransportException.new(Thrift::TransportException::ALREADY_OPEN, "msg")
+ expect(exc.type).to eq(Thrift::TransportException::ALREADY_OPEN)
+ expect(exc.message).to eq("msg")
+ end
+ end
+
+ describe Thrift::BaseTransport do
+ it "should read the specified size" do
+ transport = Thrift::BaseTransport.new
+ expect(transport).to receive(:read).with(40).ordered.and_return("10 letters")
+ expect(transport).to receive(:read).with(30).ordered.and_return("fifteen letters")
+ expect(transport).to receive(:read).with(15).ordered.and_return("more characters")
+ expect(transport.read_all(40)).to eq("10 lettersfifteen lettersmore characters")
+ end
+
+ it "should stub out the rest of the methods" do
+ # can't test for stubbiness, so just make sure they're defined
+ [:open?, :open, :close, :read, :write, :flush].each do |sym|
+ expect(Thrift::BaseTransport.method_defined?(sym)).to be_truthy
+ end
+ end
+
+ it "should alias << to write" do
+ expect(Thrift::BaseTransport.instance_method(:<<)).to eq(Thrift::BaseTransport.instance_method(:write))
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::BaseTransport.new.to_s).to eq("base")
+ end
+ end
+
+ describe Thrift::BaseServerTransport do
+ it "should stub out its methods" do
+ [:listen, :accept, :close].each do |sym|
+ expect(Thrift::BaseServerTransport.method_defined?(sym)).to be_truthy
+ end
+ end
+ end
+
+ describe Thrift::BaseTransportFactory do
+ it "should return the transport it's given" do
+ transport = double("Transport")
+ expect(Thrift::BaseTransportFactory.new.get_transport(transport)).to eql(transport)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::BaseTransportFactory.new.to_s).to eq("base")
+ end
+ end
+
+ describe Thrift::BufferedTransport do
+ it "should provide a to_s that describes the encapsulation" do
+ trans = double("Transport")
+ expect(trans).to receive(:to_s).and_return("mock")
+ expect(Thrift::BufferedTransport.new(trans).to_s).to eq("buffered(mock)")
+ end
+
+ it "should pass through everything but write/flush/read" do
+ trans = double("Transport")
+ expect(trans).to receive(:open?).ordered.and_return("+ open?")
+ expect(trans).to receive(:open).ordered.and_return("+ open")
+ expect(trans).to receive(:flush).ordered # from the close
+ expect(trans).to receive(:close).ordered.and_return("+ close")
+ btrans = Thrift::BufferedTransport.new(trans)
+ expect(btrans.open?).to eq("+ open?")
+ expect(btrans.open).to eq("+ open")
+ expect(btrans.close).to eq("+ close")
+ end
+
+ it "should buffer reads in chunks of #{Thrift::BufferedTransport::DEFAULT_BUFFER}" do
+ trans = double("Transport")
+ expect(trans).to receive(:read).with(Thrift::BufferedTransport::DEFAULT_BUFFER).and_return("lorum ipsum dolor emet")
+ btrans = Thrift::BufferedTransport.new(trans)
+ expect(btrans.read(6)).to eq("lorum ")
+ expect(btrans.read(6)).to eq("ipsum ")
+ expect(btrans.read(6)).to eq("dolor ")
+ expect(btrans.read(6)).to eq("emet")
+ end
+
+ it "should buffer writes and send them on flush" do
+ trans = double("Transport")
+ btrans = Thrift::BufferedTransport.new(trans)
+ btrans.write("one/")
+ btrans.write("two/")
+ btrans.write("three/")
+ expect(trans).to receive(:write).with("one/two/three/").ordered
+ expect(trans).to receive(:flush).ordered
+ btrans.flush
+ end
+
+ it "should only send buffered data once" do
+ trans = double("Transport")
+ btrans = Thrift::BufferedTransport.new(trans)
+ btrans.write("one/")
+ btrans.write("two/")
+ btrans.write("three/")
+ expect(trans).to receive(:write).with("one/two/three/")
+ allow(trans).to receive(:flush)
+ btrans.flush
+ # Nothing to flush with no data
+ btrans.flush
+ end
+
+ it "should flush on close" do
+ trans = double("Transport")
+ expect(trans).to receive(:close)
+ btrans = Thrift::BufferedTransport.new(trans)
+ expect(btrans).to receive(:flush)
+ btrans.close
+ end
+
+ it "should not write to socket if there's no data" do
+ trans = double("Transport")
+ expect(trans).to receive(:flush)
+ btrans = Thrift::BufferedTransport.new(trans)
+ btrans.flush
+ end
+ end
+
+ describe Thrift::BufferedTransportFactory do
+ it "should wrap the given transport in a BufferedTransport" do
+ trans = double("Transport")
+ btrans = double("BufferedTransport")
+ expect(Thrift::BufferedTransport).to receive(:new).with(trans).and_return(btrans)
+ expect(Thrift::BufferedTransportFactory.new.get_transport(trans)).to eq(btrans)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::BufferedTransportFactory.new.to_s).to eq("buffered")
+ end
+ end
+
+ describe Thrift::FramedTransport do
+ before(:each) do
+ @trans = double("Transport")
+ end
+
+ it "should provide a to_s that describes the encapsulation" do
+ trans = double("Transport")
+ expect(trans).to receive(:to_s).and_return("mock")
+ expect(Thrift::FramedTransport.new(trans).to_s).to eq("framed(mock)")
+ end
+
+ it "should pass through open?/open/close" do
+ ftrans = Thrift::FramedTransport.new(@trans)
+ expect(@trans).to receive(:open?).ordered.and_return("+ open?")
+ expect(@trans).to receive(:open).ordered.and_return("+ open")
+ expect(@trans).to receive(:close).ordered.and_return("+ close")
+ expect(ftrans.open?).to eq("+ open?")
+ expect(ftrans.open).to eq("+ open")
+ expect(ftrans.close).to eq("+ close")
+ end
+
+ it "should pass through read when read is turned off" do
+ ftrans = Thrift::FramedTransport.new(@trans, false, true)
+ expect(@trans).to receive(:read).with(17).ordered.and_return("+ read")
+ expect(ftrans.read(17)).to eq("+ read")
+ end
+
+ it "should pass through write/flush when write is turned off" do
+ ftrans = Thrift::FramedTransport.new(@trans, true, false)
+ expect(@trans).to receive(:write).with("foo").ordered.and_return("+ write")
+ expect(@trans).to receive(:flush).ordered.and_return("+ flush")
+ expect(ftrans.write("foo")).to eq("+ write")
+ expect(ftrans.flush).to eq("+ flush")
+ end
+
+ it "should return a full frame if asked for >= the frame's length" do
+ frame = "this is a frame"
+ expect(@trans).to receive(:read_all).with(4).and_return("\000\000\000\017")
+ expect(@trans).to receive(:read_all).with(frame.length).and_return(frame)
+ expect(Thrift::FramedTransport.new(@trans).read(frame.length + 10)).to eq(frame)
+ end
+
+ it "should return slices of the frame when asked for < the frame's length" do
+ frame = "this is a frame"
+ expect(@trans).to receive(:read_all).with(4).and_return("\000\000\000\017")
+ expect(@trans).to receive(:read_all).with(frame.length).and_return(frame)
+ ftrans = Thrift::FramedTransport.new(@trans)
+ expect(ftrans.read(4)).to eq("this")
+ expect(ftrans.read(4)).to eq(" is ")
+ expect(ftrans.read(16)).to eq("a frame")
+ end
+
+ it "should return nothing if asked for <= 0" do
+ expect(Thrift::FramedTransport.new(@trans).read(-2)).to eq("")
+ end
+
+ it "should pull a new frame when the first is exhausted" do
+ frame = "this is a frame"
+ frame2 = "yet another frame"
+ expect(@trans).to receive(:read_all).with(4).and_return("\000\000\000\017", "\000\000\000\021")
+ expect(@trans).to receive(:read_all).with(frame.length).and_return(frame)
+ expect(@trans).to receive(:read_all).with(frame2.length).and_return(frame2)
+ ftrans = Thrift::FramedTransport.new(@trans)
+ expect(ftrans.read(4)).to eq("this")
+ expect(ftrans.read(8)).to eq(" is a fr")
+ expect(ftrans.read(6)).to eq("ame")
+ expect(ftrans.read(4)).to eq("yet ")
+ expect(ftrans.read(16)).to eq("another frame")
+ end
+
+ it "should buffer writes" do
+ ftrans = Thrift::FramedTransport.new(@trans)
+ expect(@trans).not_to receive(:write)
+ ftrans.write("foo")
+ ftrans.write("bar")
+ ftrans.write("this is a frame")
+ end
+
+ it "should write slices of the buffer" do
+ ftrans = Thrift::FramedTransport.new(@trans)
+ ftrans.write("foobar", 3)
+ ftrans.write("barfoo", 1)
+ allow(@trans).to receive(:flush)
+ expect(@trans).to receive(:write).with("\000\000\000\004foob")
+ ftrans.flush
+ end
+
+ it "should flush frames with a 4-byte header" do
+ ftrans = Thrift::FramedTransport.new(@trans)
+ expect(@trans).to receive(:write).with("\000\000\000\035one/two/three/this is a frame").ordered
+ expect(@trans).to receive(:flush).ordered
+ ftrans.write("one/")
+ ftrans.write("two/")
+ ftrans.write("three/")
+ ftrans.write("this is a frame")
+ ftrans.flush
+ end
+
+ it "should not flush the same buffered data twice" do
+ ftrans = Thrift::FramedTransport.new(@trans)
+ expect(@trans).to receive(:write).with("\000\000\000\007foo/bar")
+ allow(@trans).to receive(:flush)
+ ftrans.write("foo")
+ ftrans.write("/bar")
+ ftrans.flush
+ expect(@trans).to receive(:write).with("\000\000\000\000")
+ ftrans.flush
+ end
+ end
+
+ describe Thrift::FramedTransportFactory do
+ it "should wrap the given transport in a FramedTransport" do
+ trans = double("Transport")
+ expect(Thrift::FramedTransport).to receive(:new).with(trans)
+ Thrift::FramedTransportFactory.new.get_transport(trans)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::FramedTransportFactory.new.to_s).to eq("framed")
+ end
+ end
+
+ describe Thrift::MemoryBufferTransport do
+ before(:each) do
+ @buffer = Thrift::MemoryBufferTransport.new
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@buffer.to_s).to eq("memory")
+ end
+
+ it "should accept a buffer on input and use it directly" do
+ s = "this is a test"
+ @buffer = Thrift::MemoryBufferTransport.new(s)
+ expect(@buffer.read(4)).to eq("this")
+ s.slice!(-4..-1)
+ expect(@buffer.read(@buffer.available)).to eq(" is a ")
+ end
+
+ it "should always remain open" do
+ expect(@buffer).to be_open
+ @buffer.close
+ expect(@buffer).to be_open
+ end
+
+ it "should respond to peek and available" do
+ @buffer.write "some data"
+ expect(@buffer.peek).to be_truthy
+ expect(@buffer.available).to eq(9)
+ @buffer.read(4)
+ expect(@buffer.peek).to be_truthy
+ expect(@buffer.available).to eq(5)
+ @buffer.read(5)
+ expect(@buffer.peek).to be_falsey
+ expect(@buffer.available).to eq(0)
+ end
+
+ it "should be able to reset the buffer" do
+ @buffer.write "test data"
+ @buffer.reset_buffer("foobar")
+ expect(@buffer.available).to eq(6)
+ expect(@buffer.read(@buffer.available)).to eq("foobar")
+ @buffer.reset_buffer
+ expect(@buffer.available).to eq(0)
+ end
+
+ it "should copy the given string when resetting the buffer" do
+ s = "this is a test"
+ @buffer.reset_buffer(s)
+ expect(@buffer.available).to eq(14)
+ @buffer.read(10)
+ expect(@buffer.available).to eq(4)
+ expect(s).to eq("this is a test")
+ end
+
+ it "should return from read what was given in write" do
+ @buffer.write "test data"
+ expect(@buffer.read(4)).to eq("test")
+ expect(@buffer.read(@buffer.available)).to eq(" data")
+ @buffer.write "foo"
+ @buffer.write " bar"
+ expect(@buffer.read(@buffer.available)).to eq("foo bar")
+ end
+
+ it "should throw an EOFError when there isn't enough data in the buffer" do
+ @buffer.reset_buffer("")
+ expect{@buffer.read(1)}.to raise_error(EOFError)
+
+ @buffer.reset_buffer("1234")
+ expect{@buffer.read(5)}.to raise_error(EOFError)
+ end
+ end
+
+ describe Thrift::IOStreamTransport do
+ before(:each) do
+ @input = double("Input", :closed? => false)
+ @output = double("Output", :closed? => false)
+ @trans = Thrift::IOStreamTransport.new(@input, @output)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@input).to receive(:to_s).and_return("mock_input")
+ expect(@output).to receive(:to_s).and_return("mock_output")
+ expect(@trans.to_s).to eq("iostream(input=mock_input,output=mock_output)")
+ end
+
+ it "should be open as long as both input or output are open" do
+ expect(@trans).to be_open
+ allow(@input).to receive(:closed?).and_return(true)
+ expect(@trans).to be_open
+ allow(@input).to receive(:closed?).and_return(false)
+ allow(@output).to receive(:closed?).and_return(true)
+ expect(@trans).to be_open
+ allow(@input).to receive(:closed?).and_return(true)
+ expect(@trans).not_to be_open
+ end
+
+ it "should pass through read/write to input/output" do
+ expect(@input).to receive(:read).with(17).and_return("+ read")
+ expect(@output).to receive(:write).with("foobar").and_return("+ write")
+ expect(@trans.read(17)).to eq("+ read")
+ expect(@trans.write("foobar")).to eq("+ write")
+ end
+
+ it "should close both input and output when closed" do
+ expect(@input).to receive(:close)
+ expect(@output).to receive(:close)
+ @trans.close
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_accelerated_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_accelerated_spec.rb
new file mode 100644
index 000000000..b2cd04bec
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_accelerated_spec.rb
@@ -0,0 +1,46 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+require File.expand_path("#{File.dirname(__FILE__)}/binary_protocol_spec_shared")
+
+if defined? Thrift::BinaryProtocolAccelerated
+
+ describe 'BinaryProtocolAccelerated' do
+ # since BinaryProtocolAccelerated should be directly equivalent to
+ # BinaryProtocol, we don't need any custom specs!
+ it_should_behave_like 'a binary protocol'
+
+ def protocol_class
+ Thrift::BinaryProtocolAccelerated
+ end
+
+ describe Thrift::BinaryProtocolAcceleratedFactory do
+ it "should create a BinaryProtocolAccelerated" do
+ expect(Thrift::BinaryProtocolAcceleratedFactory.new.get_protocol(double("MockTransport"))).to be_instance_of(Thrift::BinaryProtocolAccelerated)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::BinaryProtocolAcceleratedFactory.new.to_s).to eq("binary-accel")
+ end
+ end
+ end
+else
+ puts "skipping BinaryProtocolAccelerated spec because it is not defined."
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_spec.rb
new file mode 100644
index 000000000..065f5ce29
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_spec.rb
@@ -0,0 +1,74 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+require File.expand_path("#{File.dirname(__FILE__)}/binary_protocol_spec_shared")
+
+describe 'BinaryProtocol' do
+
+ it_should_behave_like 'a binary protocol'
+
+ def protocol_class
+ Thrift::BinaryProtocol
+ end
+
+ describe Thrift::BinaryProtocol do
+
+ before(:each) do
+ @trans = Thrift::MemoryBufferTransport.new
+ @prot = protocol_class.new(@trans)
+ end
+
+ it "should read a message header" do
+ @trans.write([protocol_class.const_get(:VERSION_1) | Thrift::MessageTypes::REPLY].pack('N'))
+ @trans.write([42].pack('N'))
+ expect(@prot).to receive(:read_string).and_return('testMessage')
+ expect(@prot.read_message_begin).to eq(['testMessage', Thrift::MessageTypes::REPLY, 42])
+ end
+
+ it "should raise an exception if the message header has the wrong version" do
+ expect(@prot).to receive(:read_i32).and_return(-1)
+ expect { @prot.read_message_begin }.to raise_error(Thrift::ProtocolException, 'Missing version identifier') do |e|
+ e.type == Thrift::ProtocolException::BAD_VERSION
+ end
+ end
+
+ it "should raise an exception if the message header does not exist and strict_read is enabled" do
+ expect(@prot).to receive(:read_i32).and_return(42)
+ expect(@prot).to receive(:strict_read).and_return(true)
+ expect { @prot.read_message_begin }.to raise_error(Thrift::ProtocolException, 'No version identifier, old protocol client?') do |e|
+ e.type == Thrift::ProtocolException::BAD_VERSION
+ end
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@prot.to_s).to eq("binary(memory)")
+ end
+ end
+
+ describe Thrift::BinaryProtocolFactory do
+ it "should create a BinaryProtocol" do
+ expect(Thrift::BinaryProtocolFactory.new.get_protocol(double("MockTransport"))).to be_instance_of(Thrift::BinaryProtocol)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::BinaryProtocolFactory.new.to_s).to eq("binary")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_spec_shared.rb b/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_spec_shared.rb
new file mode 100644
index 000000000..58d65f040
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_spec_shared.rb
@@ -0,0 +1,458 @@
+# encoding: ascii-8bit
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+shared_examples_for 'a binary protocol' do
+ before(:each) do
+ @trans = Thrift::MemoryBufferTransport.new
+ @prot = protocol_class.new(@trans)
+ end
+
+ it "should define the proper VERSION_1, VERSION_MASK AND TYPE_MASK" do
+ expect(protocol_class.const_get(:VERSION_MASK)).to eq(0xffff0000)
+ expect(protocol_class.const_get(:VERSION_1)).to eq(0x80010000)
+ expect(protocol_class.const_get(:TYPE_MASK)).to eq(0x000000ff)
+ end
+
+ it "should make strict_read readable" do
+ expect(@prot.strict_read).to eql(true)
+ end
+
+ it "should make strict_write readable" do
+ expect(@prot.strict_write).to eql(true)
+ end
+
+ it "should write the message header" do
+ @prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17)
+ expect(@trans.read(@trans.available)).to eq([protocol_class.const_get(:VERSION_1) | Thrift::MessageTypes::CALL, "testMessage".size, "testMessage", 17].pack("NNa11N"))
+ end
+
+ it "should write the message header without version when writes are not strict" do
+ @prot = protocol_class.new(@trans, true, false) # no strict write
+ @prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17)
+ expect(@trans.read(@trans.available)).to eq("\000\000\000\vtestMessage\001\000\000\000\021")
+ end
+
+ it "should write the message header with a version when writes are strict" do
+ @prot = protocol_class.new(@trans) # strict write
+ @prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17)
+ expect(@trans.read(@trans.available)).to eq("\200\001\000\001\000\000\000\vtestMessage\000\000\000\021")
+ end
+
+
+ # message footer is a noop
+
+ it "should write the field header" do
+ @prot.write_field_begin('foo', Thrift::Types::DOUBLE, 3)
+ expect(@trans.read(@trans.available)).to eq([Thrift::Types::DOUBLE, 3].pack("cn"))
+ end
+
+ # field footer is a noop
+
+ it "should write the STOP field" do
+ @prot.write_field_stop
+ expect(@trans.read(1)).to eq("\000")
+ end
+
+ it "should write the map header" do
+ @prot.write_map_begin(Thrift::Types::STRING, Thrift::Types::LIST, 17)
+ expect(@trans.read(@trans.available)).to eq([Thrift::Types::STRING, Thrift::Types::LIST, 17].pack("ccN"));
+ end
+
+ # map footer is a noop
+
+ it "should write the list header" do
+ @prot.write_list_begin(Thrift::Types::I16, 42)
+ expect(@trans.read(@trans.available)).to eq([Thrift::Types::I16, 42].pack("cN"))
+ end
+
+ # list footer is a noop
+
+ it "should write the set header" do
+ @prot.write_set_begin(Thrift::Types::I16, 42)
+ expect(@trans.read(@trans.available)).to eq([Thrift::Types::I16, 42].pack("cN"))
+ end
+
+ it "should write a bool" do
+ @prot.write_bool(true)
+ @prot.write_bool(false)
+ expect(@trans.read(@trans.available)).to eq("\001\000")
+ end
+
+ it "should treat a nil bool as false" do
+ @prot.write_bool(nil)
+ expect(@trans.read(1)).to eq("\000")
+ end
+
+ it "should write a byte" do
+ # byte is small enough, let's check -128..127
+ (-128..127).each do |i|
+ @prot.write_byte(i)
+ expect(@trans.read(1)).to eq([i].pack('c'))
+ end
+ end
+
+ it "should clip numbers out of signed range" do
+ (128..255).each do |i|
+ @prot.write_byte(i)
+ expect(@trans.read(1)).to eq([i].pack('c'))
+ end
+ end
+
+ it "errors out with a Bignum" do
+ expect { @prot.write_byte(2**65) }.to raise_error(RangeError)
+ end
+
+ it "should error gracefully when trying to write a nil byte" do
+ expect { @prot.write_byte(nil) }.to raise_error
+ end
+
+ it "should write an i16" do
+ # try a random scattering of values
+ # include the signed i16 minimum/maximum
+ [-2**15, -1024, 17, 0, -10000, 1723, 2**15-1].each do |i|
+ @prot.write_i16(i)
+ end
+ # and try something out of signed range, it should clip
+ @prot.write_i16(2**15 + 5)
+
+ expect(@trans.read(@trans.available)).to eq("\200\000\374\000\000\021\000\000\330\360\006\273\177\377\200\005")
+
+ # a Bignum should error
+ # lambda { @prot.write_i16(2**65) }.should raise_error(RangeError)
+ end
+
+ it "should error gracefully when trying to write a nil i16" do
+ expect { @prot.write_i16(nil) }.to raise_error
+ end
+
+ it "should write an i32" do
+ # try a random scattering of values
+ # include the signed i32 minimum/maximum
+ [-2**31, -123123, -2532, -3, 0, 2351235, 12331, 2**31-1].each do |i|
+ @prot.write_i32(i)
+ end
+ # try something out of signed range, it should clip
+ expect(@trans.read(@trans.available)).to eq("\200\000\000\000" + "\377\376\037\r" + "\377\377\366\034" + "\377\377\377\375" + "\000\000\000\000" + "\000#\340\203" + "\000\0000+" + "\177\377\377\377")
+ [2 ** 31 + 5, 2 ** 65 + 5].each do |i|
+ expect { @prot.write_i32(i) }.to raise_error(RangeError)
+ end
+ end
+
+ it "should error gracefully when trying to write a nil i32" do
+ expect { @prot.write_i32(nil) }.to raise_error
+ end
+
+ it "should write an i64" do
+ # try a random scattering of values
+ # try the signed i64 minimum/maximum
+ [-2**63, -12356123612323, -23512351, -234, 0, 1231, 2351236, 12361236213, 2**63-1].each do |i|
+ @prot.write_i64(i)
+ end
+ # try something out of signed range, it should clip
+ expect(@trans.read(@trans.available)).to eq(["\200\000\000\000\000\000\000\000",
+ "\377\377\364\303\035\244+]",
+ "\377\377\377\377\376\231:\341",
+ "\377\377\377\377\377\377\377\026",
+ "\000\000\000\000\000\000\000\000",
+ "\000\000\000\000\000\000\004\317",
+ "\000\000\000\000\000#\340\204",
+ "\000\000\000\002\340\311~\365",
+ "\177\377\377\377\377\377\377\377"].join(""))
+ expect { @prot.write_i64(2 ** 65 + 5) }.to raise_error(RangeError)
+ end
+
+ it "should error gracefully when trying to write a nil i64" do
+ expect { @prot.write_i64(nil) }.to raise_error
+ end
+
+ it "should write a double" do
+ # try a random scattering of values, including min/max
+ values = [Float::MIN,-1231.15325, -123123.23, -23.23515123, 0, 12351.1325, 523.23, Float::MAX]
+ values.each do |f|
+ @prot.write_double(f)
+ expect(@trans.read(@trans.available)).to eq([f].pack("G"))
+ end
+ end
+
+ it "should error gracefully when trying to write a nil double" do
+ expect { @prot.write_double(nil) }.to raise_error
+ end
+
+ if RUBY_VERSION >= '1.9'
+ it 'should write a string' do
+ str = 'abc'
+ @prot.write_string(str)
+ a = @trans.read(@trans.available)
+ expect(a.encoding).to eq(Encoding::BINARY)
+ expect(a.unpack('C*')).to eq([0x00, 0x00, 0x00, 0x03, 0x61, 0x62, 0x63])
+ end
+
+ it 'should write a string with unicode characters' do
+ str = "abc \u20AC \u20AD".encode('UTF-8')
+ @prot.write_string(str)
+ a = @trans.read(@trans.available)
+ expect(a.encoding).to eq(Encoding::BINARY)
+ expect(a.unpack('C*')).to eq([0x00, 0x00, 0x00, 0x0B, 0x61, 0x62, 0x63, 0x20,
+ 0xE2, 0x82, 0xAC, 0x20, 0xE2, 0x82, 0xAD])
+ end
+
+ it 'should write should write a string with unicode characters and transcoding' do
+ str = "abc \u20AC".encode('ISO-8859-15')
+ @prot.write_string(str)
+ a = @trans.read(@trans.available)
+ expect(a.encoding).to eq(Encoding::BINARY)
+ expect(a.unpack('C*')).to eq([0x00, 0x00, 0x00, 0x07, 0x61, 0x62, 0x63, 0x20, 0xE2, 0x82, 0xAC])
+ end
+
+ it 'should write a binary string' do
+ buffer = [0, 1, 2, 3].pack('C*')
+ @prot.write_binary(buffer)
+ a = @trans.read(@trans.available)
+ expect(a.encoding).to eq(Encoding::BINARY)
+ expect(a.unpack('C*')).to eq([0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03])
+ end
+ else
+ it 'should write a string' do
+ str = 'abc'
+ @prot.write_string(str)
+ a = @trans.read(@trans.available)
+ expect(a.unpack('C*')).to eq([0x00, 0x00, 0x00, 0x03, 0x61, 0x62, 0x63])
+ end
+
+ it 'should write a binary string' do
+ buffer = [0, 1, 2, 3].pack('C*')
+ @prot.write_binary(buffer)
+ a = @trans.read(@trans.available)
+ expect(a.unpack('C*')).to eq([0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03])
+ end
+ end
+
+ it "should error gracefully when trying to write a nil string" do
+ expect { @prot.write_string(nil) }.to raise_error
+ end
+
+ it "should write the message header without version when writes are not strict" do
+ @prot = protocol_class.new(@trans, true, false) # no strict write
+ @prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17)
+ expect(@trans.read(@trans.available)).to eq("\000\000\000\vtestMessage\001\000\000\000\021")
+ end
+
+ it "should write the message header with a version when writes are strict" do
+ @prot = protocol_class.new(@trans) # strict write
+ @prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17)
+ expect(@trans.read(@trans.available)).to eq("\200\001\000\001\000\000\000\vtestMessage\000\000\000\021")
+ end
+
+ # message footer is a noop
+
+ it "should read a field header" do
+ @trans.write([Thrift::Types::STRING, 3].pack("cn"))
+ expect(@prot.read_field_begin).to eq([nil, Thrift::Types::STRING, 3])
+ end
+
+ # field footer is a noop
+
+ it "should read a stop field" do
+ @trans.write([Thrift::Types::STOP].pack("c"));
+ expect(@prot.read_field_begin).to eq([nil, Thrift::Types::STOP, 0])
+ end
+
+ it "should read a map header" do
+ @trans.write([Thrift::Types::DOUBLE, Thrift::Types::I64, 42].pack("ccN"))
+ expect(@prot.read_map_begin).to eq([Thrift::Types::DOUBLE, Thrift::Types::I64, 42])
+ end
+
+ # map footer is a noop
+
+ it "should read a list header" do
+ @trans.write([Thrift::Types::STRING, 17].pack("cN"))
+ expect(@prot.read_list_begin).to eq([Thrift::Types::STRING, 17])
+ end
+
+ # list footer is a noop
+
+ it "should read a set header" do
+ @trans.write([Thrift::Types::STRING, 17].pack("cN"))
+ expect(@prot.read_set_begin).to eq([Thrift::Types::STRING, 17])
+ end
+
+ # set footer is a noop
+
+ it "should read a bool" do
+ @trans.write("\001\000");
+ expect(@prot.read_bool).to eq(true)
+ expect(@prot.read_bool).to eq(false)
+ end
+
+ it "should read a byte" do
+ [-128, -57, -3, 0, 17, 24, 127].each do |i|
+ @trans.write([i].pack("c"))
+ expect(@prot.read_byte).to eq(i)
+ end
+ end
+
+ it "should read an i16" do
+ # try a scattering of values, including min/max
+ [-2**15, -5237, -353, 0, 1527, 2234, 2**15-1].each do |i|
+ @trans.write([i].pack("n"));
+ expect(@prot.read_i16).to eq(i)
+ end
+ end
+
+ it "should read an i32" do
+ # try a scattering of values, including min/max
+ [-2**31, -235125, -6236, 0, 2351, 123123, 2**31-1].each do |i|
+ @trans.write([i].pack("N"))
+ expect(@prot.read_i32).to eq(i)
+ end
+ end
+
+ it "should read an i64" do
+ # try a scattering of values, including min/max
+ [-2**63, -123512312, -6346, 0, 32, 2346322323, 2**63-1].each do |i|
+ @trans.write([i >> 32, i & 0xFFFFFFFF].pack("NN"))
+ expect(@prot.read_i64).to eq(i)
+ end
+ end
+
+ it "should read a double" do
+ # try a random scattering of values, including min/max
+ [Float::MIN, -231231.12351, -323.233513, 0, 123.2351235, 2351235.12351235, Float::MAX].each do |f|
+ @trans.write([f].pack("G"));
+ expect(@prot.read_double).to eq(f)
+ end
+ end
+
+ if RUBY_VERSION >= '1.9'
+ it 'should read a string' do
+ # i32 of value 3, followed by three characters/UTF-8 bytes 'a', 'b', 'c'
+ buffer = [0x00, 0x00, 0x00, 0x03, 0x61, 0x62, 0x63].pack('C*')
+ @trans.write(buffer)
+ a = @prot.read_string
+ expect(a).to eq('abc'.encode('UTF-8'))
+ expect(a.encoding).to eq(Encoding::UTF_8)
+ end
+
+ it 'should read a string containing unicode characters from UTF-8 encoded buffer' do
+ # i32 of value 3, followed by one character U+20AC made up of three bytes
+ buffer = [0x00, 0x00, 0x00, 0x03, 0xE2, 0x82, 0xAC].pack('C*')
+ @trans.write(buffer)
+ a = @prot.read_string
+ expect(a).to eq("\u20AC".encode('UTF-8'))
+ expect(a.encoding).to eq(Encoding::UTF_8)
+ end
+
+ it 'should read a binary string' do
+ buffer = [0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03].pack('C*')
+ @trans.write(buffer)
+ a = @prot.read_binary
+ expect(a).to eq([0x00, 0x01, 0x02, 0x03].pack('C*'))
+ expect(a.encoding).to eq(Encoding::BINARY)
+ end
+ else
+ it 'should read a string' do
+ # i32 of value 3, followed by three characters/UTF-8 bytes 'a', 'b', 'c'
+ buffer = [0x00, 0x00, 0x00, 0x03, 0x61, 0x62, 0x63].pack('C*')
+ @trans.write(buffer)
+ expect(@prot.read_string).to eq('abc')
+ end
+
+ it 'should read a binary string' do
+ buffer = [0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03].pack('C*')
+ @trans.write(buffer)
+ a = @prot.read_binary
+ expect(a).to eq([0x00, 0x01, 0x02, 0x03].pack('C*'))
+ end
+ end
+
+ it "should perform a complete rpc with no args or return" do
+ srv_test(
+ proc {|client| client.send_voidMethod()},
+ proc {|client| expect(client.recv_voidMethod).to eq(nil)}
+ )
+ end
+
+ it "should perform a complete rpc with a primitive return type" do
+ srv_test(
+ proc {|client| client.send_primitiveMethod()},
+ proc {|client| expect(client.recv_primitiveMethod).to eq(1)}
+ )
+ end
+
+ it "should perform a complete rpc with a struct return type" do
+ srv_test(
+ proc {|client| client.send_structMethod()},
+ proc {|client|
+ result = client.recv_structMethod
+ result.set_byte_map = nil
+ result.map_byte_map = nil
+ expect(result).to eq(Fixtures::COMPACT_PROTOCOL_TEST_STRUCT)
+ }
+ )
+ end
+
+ def get_socket_connection
+ server = Thrift::ServerSocket.new("localhost", 9090)
+ server.listen
+
+ clientside = Thrift::Socket.new("localhost", 9090)
+ clientside.open
+ serverside = server.accept
+ [clientside, serverside, server]
+ end
+
+ def srv_test(firstblock, secondblock)
+ clientside, serverside, server = get_socket_connection
+
+ clientproto = protocol_class.new(clientside)
+ serverproto = protocol_class.new(serverside)
+
+ processor = Thrift::Test::Srv::Processor.new(SrvHandler.new)
+
+ client = Thrift::Test::Srv::Client.new(clientproto, clientproto)
+
+ # first block
+ firstblock.call(client)
+
+ processor.process(serverproto, serverproto)
+
+ # second block
+ secondblock.call(client)
+ ensure
+ clientside.close
+ serverside.close
+ server.close
+ end
+
+ class SrvHandler
+ def voidMethod()
+ end
+
+ def primitiveMethod
+ 1
+ end
+
+ def structMethod
+ Fixtures::COMPACT_PROTOCOL_TEST_STRUCT
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/bytes_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/bytes_spec.rb
new file mode 100644
index 000000000..2e8653cfc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/bytes_spec.rb
@@ -0,0 +1,160 @@
+# encoding: UTF-8
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+describe Thrift::Bytes do
+ if RUBY_VERSION >= '1.9'
+ describe '.empty_byte_buffer' do
+ it 'should create an empty buffer' do
+ b = Thrift::Bytes.empty_byte_buffer
+ expect(b.length).to eq(0)
+ expect(b.encoding).to eq(Encoding::BINARY)
+ end
+
+ it 'should create an empty buffer of given size' do
+ b = Thrift::Bytes.empty_byte_buffer 2
+ expect(b.length).to eq(2)
+ expect(b.getbyte(0)).to eq(0)
+ expect(b.getbyte(1)).to eq(0)
+ expect(b.encoding).to eq(Encoding::BINARY)
+ end
+ end
+
+ describe '.force_binary_encoding' do
+ it 'should change encoding' do
+ e = 'STRING'.encode('UTF-8')
+ expect(e.encoding).not_to eq(Encoding::BINARY)
+ a = Thrift::Bytes.force_binary_encoding e
+ expect(a.encoding).to eq(Encoding::BINARY)
+ end
+ end
+
+ describe '.get_string_byte' do
+ it 'should get the byte at index' do
+ s = "\x41\x42"
+ expect(Thrift::Bytes.get_string_byte(s, 0)).to eq(0x41)
+ expect(Thrift::Bytes.get_string_byte(s, 1)).to eq(0x42)
+ end
+ end
+
+ describe '.set_string_byte' do
+ it 'should set byte value at index' do
+ s = "\x41\x42"
+ Thrift::Bytes.set_string_byte(s, 0, 0x43)
+ expect(s.getbyte(0)).to eq(0x43)
+ expect(s).to eq('CB')
+ end
+ end
+
+ describe '.convert_to_utf8_byte_buffer' do
+ it 'should convert UTF-8 String to byte buffer' do
+ e = "\u20AC".encode('UTF-8') # a string with euro sign character U+20AC
+ expect(e.length).to eq(1)
+
+ a = Thrift::Bytes.convert_to_utf8_byte_buffer e
+ expect(a.encoding).to eq(Encoding::BINARY)
+ expect(a.length).to eq(3)
+ expect(a.unpack('C*')).to eq([0xE2, 0x82, 0xAC])
+ end
+
+ it 'should convert ISO-8859-15 String to UTF-8 byte buffer' do
+ # Assumptions
+ e = "\u20AC".encode('ISO-8859-15') # a string with euro sign character U+20AC, then converted to ISO-8859-15
+ expect(e.length).to eq(1)
+ expect(e.unpack('C*')).to eq([0xA4]) # euro sign is a different code point in ISO-8859-15
+
+ a = Thrift::Bytes.convert_to_utf8_byte_buffer e
+ expect(a.encoding).to eq(Encoding::BINARY)
+ expect(a.length).to eq(3)
+ expect(a.unpack('C*')).to eq([0xE2, 0x82, 0xAC])
+ end
+ end
+
+ describe '.convert_to_string' do
+ it 'should convert UTF-8 byte buffer to a UTF-8 String' do
+ e = [0xE2, 0x82, 0xAC].pack("C*")
+ expect(e.encoding).to eq(Encoding::BINARY)
+ a = Thrift::Bytes.convert_to_string e
+ expect(a.encoding).to eq(Encoding::UTF_8)
+ expect(a).to eq("\u20AC")
+ end
+ end
+
+ else # RUBY_VERSION
+ describe '.empty_byte_buffer' do
+ it 'should create an empty buffer' do
+ b = Thrift::Bytes.empty_byte_buffer
+ expect(b.length).to eq(0)
+ end
+
+ it 'should create an empty buffer of given size' do
+ b = Thrift::Bytes.empty_byte_buffer 2
+ expect(b.length).to eq(2)
+ expect(b[0]).to eq(0)
+ expect(b[1]).to eq(0)
+ end
+ end
+
+ describe '.force_binary_encoding' do
+ it 'should be a no-op' do
+ e = 'STRING'
+ a = Thrift::Bytes.force_binary_encoding e
+ expect(a).to eq(e)
+ expect(a).to be(e)
+ end
+ end
+
+ describe '.get_string_byte' do
+ it 'should get the byte at index' do
+ s = "\x41\x42"
+ expect(Thrift::Bytes.get_string_byte(s, 0)).to eq(0x41)
+ expect(Thrift::Bytes.get_string_byte(s, 1)).to eq(0x42)
+ end
+ end
+
+ describe '.set_string_byte' do
+ it 'should set byte value at index' do
+ s = "\x41\x42"
+ Thrift::Bytes.set_string_byte(s, 0, 0x43)
+ expect(s[0]).to eq(0x43)
+ expect(s).to eq('CB')
+ end
+ end
+
+ describe '.convert_to_utf8_byte_buffer' do
+ it 'should be a no-op' do
+ e = 'STRING'
+ a = Thrift::Bytes.convert_to_utf8_byte_buffer e
+ expect(a).to eq(e)
+ expect(a).to be(e)
+ end
+ end
+
+ describe '.convert_to_string' do
+ it 'should be a no-op' do
+ e = 'STRING'
+ a = Thrift::Bytes.convert_to_string e
+ expect(a).to eq(e)
+ expect(a).to be(e)
+ end
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/client_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/client_spec.rb
new file mode 100644
index 000000000..d5d4ceedb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/client_spec.rb
@@ -0,0 +1,98 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+describe 'Client' do
+
+ class ClientSpec
+ include Thrift::Client
+ end
+
+ before(:each) do
+ @prot = double("MockProtocol")
+ @client = ClientSpec.new(@prot)
+ end
+
+ describe Thrift::Client do
+ it "should re-use iprot for oprot if not otherwise specified" do
+ expect(@client.instance_variable_get(:'@iprot')).to eql(@prot)
+ expect(@client.instance_variable_get(:'@oprot')).to eql(@prot)
+ end
+
+ it "should send a test message" do
+ expect(@prot).to receive(:write_message_begin).with('testMessage', Thrift::MessageTypes::CALL, 0)
+ mock_args = double('#<TestMessage_args:mock>')
+ expect(mock_args).to receive(:foo=).with('foo')
+ expect(mock_args).to receive(:bar=).with(42)
+ expect(mock_args).to receive(:write).with(@prot)
+ expect(@prot).to receive(:write_message_end)
+ expect(@prot).to receive(:trans) do
+ double('trans').tap do |trans|
+ expect(trans).to receive(:flush)
+ end
+ end
+ klass = double("TestMessage_args", :new => mock_args)
+ @client.send_message('testMessage', klass, :foo => 'foo', :bar => 42)
+ end
+
+ it "should increment the sequence id when sending messages" do
+ pending "it seems sequence ids are completely ignored right now"
+ @prot.expect(:write_message_begin).with('testMessage', Thrift::MessageTypes::CALL, 0).ordered
+ @prot.expect(:write_message_begin).with('testMessage2', Thrift::MessageTypes::CALL, 1).ordered
+ @prot.expect(:write_message_begin).with('testMessage3', Thrift::MessageTypes::CALL, 2).ordered
+ @prot.stub!(:write_message_end)
+ @prot.stub!(:trans).and_return double("trans").as_null_object
+ @client.send_message('testMessage', double("args class").as_null_object)
+ @client.send_message('testMessage2', double("args class").as_null_object)
+ @client.send_message('testMessage3', double("args class").as_null_object)
+ end
+
+ it "should receive a test message" do
+ expect(@prot).to receive(:read_message_begin).and_return [nil, Thrift::MessageTypes::CALL, 0]
+ expect(@prot).to receive(:read_message_end)
+ mock_klass = double("#<MockClass:mock>")
+ expect(mock_klass).to receive(:read).with(@prot)
+ @client.receive_message(double("MockClass", :new => mock_klass))
+ end
+
+ it "should handle received exceptions" do
+ expect(@prot).to receive(:read_message_begin).and_return [nil, Thrift::MessageTypes::EXCEPTION, 0]
+ expect(@prot).to receive(:read_message_end)
+ expect(Thrift::ApplicationException).to receive(:new) do
+ StandardError.new.tap do |mock_exc|
+ expect(mock_exc).to receive(:read).with(@prot)
+ end
+ end
+ expect { @client.receive_message(nil) }.to raise_error(StandardError)
+ end
+
+ it "should close the transport if an error occurs while sending a message" do
+ allow(@prot).to receive(:write_message_begin)
+ expect(@prot).not_to receive(:write_message_end)
+ mock_args = double("#<TestMessage_args:mock>")
+ expect(mock_args).to receive(:write).with(@prot).and_raise(StandardError)
+ trans = double("MockTransport")
+ allow(@prot).to receive(:trans).and_return(trans)
+ expect(trans).to receive(:close)
+ klass = double("TestMessage_args", :new => mock_args)
+ expect { @client.send_message("testMessage", klass) }.to raise_error(StandardError)
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/compact_protocol_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/compact_protocol_spec.rb
new file mode 100644
index 000000000..513dd69cf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/compact_protocol_spec.rb
@@ -0,0 +1,158 @@
+# encoding: UTF-8
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+describe Thrift::CompactProtocol do
+ TESTS = {
+ :byte => (-127..127).to_a,
+ :i16 => (0..14).map {|shift| [1 << shift, -(1 << shift)]}.flatten.sort,
+ :i32 => (0..30).map {|shift| [1 << shift, -(1 << shift)]}.flatten.sort,
+ :i64 => (0..62).map {|shift| [1 << shift, -(1 << shift)]}.flatten.sort,
+ :string => ["", "1", "short", "fourteen123456", "fifteen12345678", "unicode characters: \u20AC \u20AD", "1" * 127, "1" * 3000],
+ :binary => ["", "\001", "\001" * 5, "\001" * 14, "\001" * 15, "\001" * 127, "\001" * 3000],
+ :double => [0.0, 1.0, -1.0, 1.1, -1.1, 10000000.1, 1.0/0.0, -1.0/0.0],
+ :bool => [true, false]
+ }
+
+ it "should encode and decode naked primitives correctly" do
+ TESTS.each_pair do |primitive_type, test_values|
+ test_values.each do |value|
+ # puts "testing #{value}" if primitive_type == :i64
+ trans = Thrift::MemoryBufferTransport.new
+ proto = Thrift::CompactProtocol.new(trans)
+
+ proto.send(writer(primitive_type), value)
+ # puts "buf: #{trans.inspect_buffer}" if primitive_type == :i64
+ read_back = proto.send(reader(primitive_type))
+ expect(read_back).to eq(value)
+ end
+ end
+ end
+
+ it "should encode and decode primitives in fields correctly" do
+ TESTS.each_pair do |primitive_type, test_values|
+ final_primitive_type = primitive_type == :binary ? :string : primitive_type
+ thrift_type = Thrift::Types.const_get(final_primitive_type.to_s.upcase)
+ # puts primitive_type
+ test_values.each do |value|
+ trans = Thrift::MemoryBufferTransport.new
+ proto = Thrift::CompactProtocol.new(trans)
+
+ proto.write_field_begin(nil, thrift_type, 15)
+ proto.send(writer(primitive_type), value)
+ proto.write_field_end
+
+ proto = Thrift::CompactProtocol.new(trans)
+ name, type, id = proto.read_field_begin
+ expect(type).to eq(thrift_type)
+ expect(id).to eq(15)
+ read_back = proto.send(reader(primitive_type))
+ expect(read_back).to eq(value)
+ proto.read_field_end
+ end
+ end
+ end
+
+ it "should encode and decode a monster struct correctly" do
+ trans = Thrift::MemoryBufferTransport.new
+ proto = Thrift::CompactProtocol.new(trans)
+
+ struct = Thrift::Test::CompactProtoTestStruct.new
+ # sets and maps don't hash well... not sure what to do here.
+ struct.write(proto)
+
+ struct2 = Thrift::Test::CompactProtoTestStruct.new
+ struct2.read(proto)
+ expect(struct2).to eq(struct)
+ end
+
+ it "should make method calls correctly" do
+ client_out_trans = Thrift::MemoryBufferTransport.new
+ client_out_proto = Thrift::CompactProtocol.new(client_out_trans)
+
+ client_in_trans = Thrift::MemoryBufferTransport.new
+ client_in_proto = Thrift::CompactProtocol.new(client_in_trans)
+
+ processor = Thrift::Test::Srv::Processor.new(JankyHandler.new)
+
+ client = Thrift::Test::Srv::Client.new(client_in_proto, client_out_proto)
+ client.send_Janky(1)
+ # puts client_out_trans.inspect_buffer
+ processor.process(client_out_proto, client_in_proto)
+ expect(client.recv_Janky).to eq(2)
+ end
+
+ it "should deal with fields following fields that have non-delta ids" do
+ brcp = Thrift::Test::BreaksRubyCompactProtocol.new(
+ :field1 => "blah",
+ :field2 => Thrift::Test::BigFieldIdStruct.new(
+ :field1 => "string1",
+ :field2 => "string2"),
+ :field3 => 3)
+ ser = Thrift::Serializer.new(Thrift::CompactProtocolFactory.new)
+ bytes = ser.serialize(brcp)
+
+ deser = Thrift::Deserializer.new(Thrift::CompactProtocolFactory.new)
+ brcp2 = Thrift::Test::BreaksRubyCompactProtocol.new
+ deser.deserialize(brcp2, bytes)
+ expect(brcp2).to eq(brcp)
+ end
+
+ it "should deserialize an empty map to an empty hash" do
+ struct = Thrift::Test::SingleMapTestStruct.new(:i32_map => {})
+ ser = Thrift::Serializer.new(Thrift::CompactProtocolFactory.new)
+ bytes = ser.serialize(struct)
+
+ deser = Thrift::Deserializer.new(Thrift::CompactProtocolFactory.new)
+ struct2 = Thrift::Test::SingleMapTestStruct.new
+ deser.deserialize(struct2, bytes)
+ expect(struct).to eq(struct2)
+ end
+
+ it "should provide a reasonable to_s" do
+ trans = Thrift::MemoryBufferTransport.new
+ expect(Thrift::CompactProtocol.new(trans).to_s).to eq("compact(memory)")
+ end
+
+ class JankyHandler
+ def Janky(i32arg)
+ i32arg * 2
+ end
+ end
+
+ def writer(sym)
+ "write_#{sym.to_s}"
+ end
+
+ def reader(sym)
+ "read_#{sym.to_s}"
+ end
+end
+
+describe Thrift::CompactProtocolFactory do
+ it "should create a CompactProtocol" do
+ expect(Thrift::CompactProtocolFactory.new.get_protocol(double("MockTransport"))).to be_instance_of(Thrift::CompactProtocol)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::CompactProtocolFactory.new.to_s).to eq("compact")
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/exception_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/exception_spec.rb
new file mode 100644
index 000000000..379ae6980
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/exception_spec.rb
@@ -0,0 +1,141 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+describe 'Exception' do
+
+ describe Thrift::Exception do
+ it "should have an accessible message" do
+ e = Thrift::Exception.new("test message")
+ expect(e.message).to eq("test message")
+ end
+ end
+
+ describe Thrift::ApplicationException do
+ it "should inherit from Thrift::Exception" do
+ expect(Thrift::ApplicationException.superclass).to eq(Thrift::Exception)
+ end
+
+ it "should have an accessible type and message" do
+ e = Thrift::ApplicationException.new
+ expect(e.type).to eq(Thrift::ApplicationException::UNKNOWN)
+ expect(e.message).to be_nil
+ e = Thrift::ApplicationException.new(Thrift::ApplicationException::UNKNOWN_METHOD, "test message")
+ expect(e.type).to eq(Thrift::ApplicationException::UNKNOWN_METHOD)
+ expect(e.message).to eq("test message")
+ end
+
+ it "should read a struct off of a protocol" do
+ prot = double("MockProtocol")
+ expect(prot).to receive(:read_struct_begin).ordered
+ expect(prot).to receive(:read_field_begin).exactly(3).times.and_return(
+ ["message", Thrift::Types::STRING, 1],
+ ["type", Thrift::Types::I32, 2],
+ [nil, Thrift::Types::STOP, 0]
+ )
+ expect(prot).to receive(:read_string).ordered.and_return "test message"
+ expect(prot).to receive(:read_i32).ordered.and_return Thrift::ApplicationException::BAD_SEQUENCE_ID
+ expect(prot).to receive(:read_field_end).exactly(2).times
+ expect(prot).to receive(:read_struct_end).ordered
+
+ e = Thrift::ApplicationException.new
+ e.read(prot)
+ expect(e.message).to eq("test message")
+ expect(e.type).to eq(Thrift::ApplicationException::BAD_SEQUENCE_ID)
+ end
+
+ it "should skip bad fields when reading a struct" do
+ prot = double("MockProtocol")
+ expect(prot).to receive(:read_struct_begin).ordered
+ expect(prot).to receive(:read_field_begin).exactly(5).times.and_return(
+ ["type", Thrift::Types::I32, 2],
+ ["type", Thrift::Types::STRING, 2],
+ ["message", Thrift::Types::MAP, 1],
+ ["message", Thrift::Types::STRING, 3],
+ [nil, Thrift::Types::STOP, 0]
+ )
+ expect(prot).to receive(:read_i32).and_return Thrift::ApplicationException::INVALID_MESSAGE_TYPE
+ expect(prot).to receive(:skip).with(Thrift::Types::STRING).twice
+ expect(prot).to receive(:skip).with(Thrift::Types::MAP)
+ expect(prot).to receive(:read_field_end).exactly(4).times
+ expect(prot).to receive(:read_struct_end).ordered
+
+ e = Thrift::ApplicationException.new
+ e.read(prot)
+ expect(e.message).to be_nil
+ expect(e.type).to eq(Thrift::ApplicationException::INVALID_MESSAGE_TYPE)
+ end
+
+ it "should write a Thrift::ApplicationException struct to the oprot" do
+ prot = double("MockProtocol")
+ expect(prot).to receive(:write_struct_begin).with("Thrift::ApplicationException").ordered
+ expect(prot).to receive(:write_field_begin).with("message", Thrift::Types::STRING, 1).ordered
+ expect(prot).to receive(:write_string).with("test message").ordered
+ expect(prot).to receive(:write_field_begin).with("type", Thrift::Types::I32, 2).ordered
+ expect(prot).to receive(:write_i32).with(Thrift::ApplicationException::UNKNOWN_METHOD).ordered
+ expect(prot).to receive(:write_field_end).twice
+ expect(prot).to receive(:write_field_stop).ordered
+ expect(prot).to receive(:write_struct_end).ordered
+
+ e = Thrift::ApplicationException.new(Thrift::ApplicationException::UNKNOWN_METHOD, "test message")
+ e.write(prot)
+ end
+
+ it "should skip nil fields when writing to the oprot" do
+ prot = double("MockProtocol")
+ expect(prot).to receive(:write_struct_begin).with("Thrift::ApplicationException").ordered
+ expect(prot).to receive(:write_field_begin).with("message", Thrift::Types::STRING, 1).ordered
+ expect(prot).to receive(:write_string).with("test message").ordered
+ expect(prot).to receive(:write_field_end).ordered
+ expect(prot).to receive(:write_field_stop).ordered
+ expect(prot).to receive(:write_struct_end).ordered
+
+ e = Thrift::ApplicationException.new(nil, "test message")
+ e.write(prot)
+
+ prot = double("MockProtocol")
+ expect(prot).to receive(:write_struct_begin).with("Thrift::ApplicationException").ordered
+ expect(prot).to receive(:write_field_begin).with("type", Thrift::Types::I32, 2).ordered
+ expect(prot).to receive(:write_i32).with(Thrift::ApplicationException::BAD_SEQUENCE_ID).ordered
+ expect(prot).to receive(:write_field_end).ordered
+ expect(prot).to receive(:write_field_stop).ordered
+ expect(prot).to receive(:write_struct_end).ordered
+
+ e = Thrift::ApplicationException.new(Thrift::ApplicationException::BAD_SEQUENCE_ID)
+ e.write(prot)
+
+ prot = double("MockProtocol")
+ expect(prot).to receive(:write_struct_begin).with("Thrift::ApplicationException").ordered
+ expect(prot).to receive(:write_field_stop).ordered
+ expect(prot).to receive(:write_struct_end).ordered
+
+ e = Thrift::ApplicationException.new(nil)
+ e.write(prot)
+ end
+ end
+
+ describe Thrift::ProtocolException do
+ it "should have an accessible type" do
+ prot = Thrift::ProtocolException.new(Thrift::ProtocolException::SIZE_LIMIT, "message")
+ expect(prot.type).to eq(Thrift::ProtocolException::SIZE_LIMIT)
+ expect(prot.message).to eq("message")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/flat_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/flat_spec.rb
new file mode 100644
index 000000000..893056c10
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/flat_spec.rb
@@ -0,0 +1,62 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+describe 'generation' do
+ before do
+ require 'namespaced_nonblocking_service'
+ end
+
+ it "did not generate the wrong files" do
+ prefix = File.expand_path("../gen-rb/flat", __FILE__)
+ ["namespaced_spec_namespace/namespaced_nonblocking_service.rb",
+ "namespaced_spec_namespace/thrift_namespaced_spec_constants.rb",
+ "namespaced_spec_namespace/thrift_namespaced_spec_types.rb",
+ "other_namespace/referenced_constants.rb",
+ "other_namespace/referenced_types.rb"
+ ].each do |name|
+ expect(File.exist?(File.join(prefix, name))).not_to be_truthy
+ end
+ end
+
+ it "generated the right files" do
+ prefix = File.expand_path("../gen-rb/flat", __FILE__)
+ ["namespaced_nonblocking_service.rb",
+ "thrift_namespaced_spec_constants.rb",
+ "thrift_namespaced_spec_types.rb",
+ "referenced_constants.rb",
+ "referenced_types.rb"
+ ].each do |name|
+ expect(File.exist?(File.join(prefix, name))).to be_truthy
+ end
+ end
+
+ it "has a service class in the right place" do
+ expect(defined?(NamespacedSpecNamespace::NamespacedNonblockingService)).to be_truthy
+ end
+
+ it "has a struct in the right place" do
+ expect(defined?(NamespacedSpecNamespace::Hello)).to be_truthy
+ end
+
+ it "required an included file" do
+ expect(defined?(OtherNamespace::SomeEnum)).to be_truthy
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/http_client_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/http_client_spec.rb
new file mode 100644
index 000000000..df472ab33
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/http_client_spec.rb
@@ -0,0 +1,139 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+describe 'Thrift::HTTPClientTransport' do
+
+ describe Thrift::HTTPClientTransport do
+ before(:each) do
+ @client = Thrift::HTTPClientTransport.new("http://my.domain.com/path/to/service?param=value")
+ end
+
+ it "should provide a reasonable to_s" do
+ @client.to_s == "http://my.domain.com/path/to/service?param=value"
+ end
+
+ it "should always be open" do
+ expect(@client).to be_open
+ @client.close
+ expect(@client).to be_open
+ end
+
+ it "should post via HTTP and return the results" do
+ @client.write "a test"
+ @client.write " frame"
+ expect(Net::HTTP).to receive(:new).with("my.domain.com", 80) do
+ double("Net::HTTP").tap do |http|
+ expect(http).to receive(:use_ssl=).with(false)
+ expect(http).to receive(:post).with("/path/to/service?param=value", "a test frame", {"Content-Type"=>"application/x-thrift"}) do
+ double("Net::HTTPOK").tap do |response|
+ expect(response).to receive(:body).and_return "data"
+ end
+ end
+ end
+ end
+ @client.flush
+ expect(@client.read(10)).to eq("data")
+ end
+
+ it "should send custom headers if defined" do
+ @client.write "test"
+ custom_headers = {"Cookie" => "Foo"}
+ headers = {"Content-Type"=>"application/x-thrift"}.merge(custom_headers)
+
+ @client.add_headers(custom_headers)
+ expect(Net::HTTP).to receive(:new).with("my.domain.com", 80) do
+ double("Net::HTTP").tap do |http|
+ expect(http).to receive(:use_ssl=).with(false)
+ expect(http).to receive(:post).with("/path/to/service?param=value", "test", headers) do
+ double("Net::HTTPOK").tap do |response|
+ expect(response).to receive(:body).and_return "data"
+ end
+ end
+ end
+ end
+ @client.flush
+ end
+
+ it 'should reset the outbuf on HTTP failures' do
+ @client.write "test"
+
+ expect(Net::HTTP).to receive(:new).with("my.domain.com", 80) do
+ double("Net::HTTP").tap do |http|
+ expect(http).to receive(:use_ssl=).with(false)
+ expect(http).to receive(:post).with("/path/to/service?param=value", "test", {"Content-Type"=>"application/x-thrift"}) { raise Net::ReadTimeout }
+ end
+ end
+
+ @client.flush rescue
+ expect(@client.instance_variable_get(:@outbuf)).to eq(Thrift::Bytes.empty_byte_buffer)
+ end
+
+ end
+
+ describe 'ssl enabled' do
+ before(:each) do
+ @service_path = "/path/to/service?param=value"
+ @server_uri = "https://my.domain.com"
+ end
+
+ it "should use SSL for https" do
+ client = Thrift::HTTPClientTransport.new("#{@server_uri}#{@service_path}")
+
+ client.write "test"
+
+ expect(Net::HTTP).to receive(:new).with("my.domain.com", 443) do
+ double("Net::HTTP").tap do |http|
+ expect(http).to receive(:use_ssl=).with(true)
+ expect(http).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
+ expect(http).to receive(:post).with(@service_path, "test",
+ "Content-Type" => "application/x-thrift") do
+ double("Net::HTTPOK").tap do |response|
+ expect(response).to receive(:body).and_return "data"
+ end
+ end
+ end
+ end
+ client.flush
+ expect(client.read(4)).to eq("data")
+ end
+
+ it "should set SSL verify mode when specified" do
+ client = Thrift::HTTPClientTransport.new("#{@server_uri}#{@service_path}",
+ :ssl_verify_mode => OpenSSL::SSL::VERIFY_NONE)
+
+ client.write "test"
+ expect(Net::HTTP).to receive(:new).with("my.domain.com", 443) do
+ double("Net::HTTP").tap do |http|
+ expect(http).to receive(:use_ssl=).with(true)
+ expect(http).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
+ expect(http).to receive(:post).with(@service_path, "test",
+ "Content-Type" => "application/x-thrift") do
+ double("Net::HTTPOK").tap do |response|
+ expect(response).to receive(:body).and_return "data"
+ end
+ end
+ end
+ end
+ client.flush
+ expect(client.read(4)).to eq("data")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/json_protocol_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/json_protocol_spec.rb
new file mode 100644
index 000000000..fe1af7bb2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/json_protocol_spec.rb
@@ -0,0 +1,552 @@
+# encoding: UTF-8
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+describe 'JsonProtocol' do
+
+ describe Thrift::JsonProtocol do
+ before(:each) do
+ @trans = Thrift::MemoryBufferTransport.new
+ @prot = Thrift::JsonProtocol.new(@trans)
+ end
+
+ it "should write json escaped char" do
+ @prot.write_json_escape_char("\n")
+ expect(@trans.read(@trans.available)).to eq('\u000a')
+
+ @prot.write_json_escape_char(" ")
+ expect(@trans.read(@trans.available)).to eq('\u0020')
+ end
+
+ it "should write json char" do
+ @prot.write_json_char("\n")
+ expect(@trans.read(@trans.available)).to eq('\\n')
+
+ @prot.write_json_char(" ")
+ expect(@trans.read(@trans.available)).to eq(' ')
+
+ @prot.write_json_char("\\")
+ expect(@trans.read(@trans.available)).to eq("\\\\")
+
+ @prot.write_json_char("@")
+ expect(@trans.read(@trans.available)).to eq('@')
+ end
+
+ it "should write json string" do
+ @prot.write_json_string("this is a \\ json\nstring")
+ expect(@trans.read(@trans.available)).to eq("\"this is a \\\\ json\\nstring\"")
+ end
+
+ it "should write json base64" do
+ @prot.write_json_base64("this is a base64 string")
+ expect(@trans.read(@trans.available)).to eq("\"dGhpcyBpcyBhIGJhc2U2NCBzdHJpbmc=\"")
+ end
+
+ it "should write json integer" do
+ @prot.write_json_integer(45)
+ expect(@trans.read(@trans.available)).to eq("45")
+
+ @prot.write_json_integer(33000)
+ expect(@trans.read(@trans.available)).to eq("33000")
+
+ @prot.write_json_integer(3000000000)
+ expect(@trans.read(@trans.available)).to eq("3000000000")
+
+ @prot.write_json_integer(6000000000)
+ expect(@trans.read(@trans.available)).to eq("6000000000")
+ end
+
+ it "should write json double" do
+ @prot.write_json_double(12.3)
+ expect(@trans.read(@trans.available)).to eq("12.3")
+
+ @prot.write_json_double(-3.21)
+ expect(@trans.read(@trans.available)).to eq("-3.21")
+
+ @prot.write_json_double(((+1.0/0.0)/(+1.0/0.0)))
+ expect(@trans.read(@trans.available)).to eq("\"NaN\"")
+
+ @prot.write_json_double((+1.0/0.0))
+ expect(@trans.read(@trans.available)).to eq("\"Infinity\"")
+
+ @prot.write_json_double((-1.0/0.0))
+ expect(@trans.read(@trans.available)).to eq("\"-Infinity\"")
+ end
+
+ it "should write json object start" do
+ @prot.write_json_object_start
+ expect(@trans.read(@trans.available)).to eq("{")
+ end
+
+ it "should write json object end" do
+ @prot.write_json_object_end
+ expect(@trans.read(@trans.available)).to eq("}")
+ end
+
+ it "should write json array start" do
+ @prot.write_json_array_start
+ expect(@trans.read(@trans.available)).to eq("[")
+ end
+
+ it "should write json array end" do
+ @prot.write_json_array_end
+ expect(@trans.read(@trans.available)).to eq("]")
+ end
+
+ it "should write message begin" do
+ @prot.write_message_begin("name", 12, 32)
+ expect(@trans.read(@trans.available)).to eq("[1,\"name\",12,32")
+ end
+
+ it "should write message end" do
+ @prot.write_message_end
+ expect(@trans.read(@trans.available)).to eq("]")
+ end
+
+ it "should write struct begin" do
+ @prot.write_struct_begin("name")
+ expect(@trans.read(@trans.available)).to eq("{")
+ end
+
+ it "should write struct end" do
+ @prot.write_struct_end
+ expect(@trans.read(@trans.available)).to eq("}")
+ end
+
+ it "should write field begin" do
+ @prot.write_field_begin("name", Thrift::Types::STRUCT, 32)
+ expect(@trans.read(@trans.available)).to eq("32{\"rec\"")
+ end
+
+ it "should write field end" do
+ @prot.write_field_end
+ expect(@trans.read(@trans.available)).to eq("}")
+ end
+
+ it "should write field stop" do
+ @prot.write_field_stop
+ expect(@trans.read(@trans.available)).to eq("")
+ end
+
+ it "should write map begin" do
+ @prot.write_map_begin(Thrift::Types::STRUCT, Thrift::Types::LIST, 32)
+ expect(@trans.read(@trans.available)).to eq("[\"rec\",\"lst\",32,{")
+ end
+
+ it "should write map end" do
+ @prot.write_map_end
+ expect(@trans.read(@trans.available)).to eq("}]")
+ end
+
+ it "should write list begin" do
+ @prot.write_list_begin(Thrift::Types::STRUCT, 32)
+ expect(@trans.read(@trans.available)).to eq("[\"rec\",32")
+ end
+
+ it "should write list end" do
+ @prot.write_list_end
+ expect(@trans.read(@trans.available)).to eq("]")
+ end
+
+ it "should write set begin" do
+ @prot.write_set_begin(Thrift::Types::STRUCT, 32)
+ expect(@trans.read(@trans.available)).to eq("[\"rec\",32")
+ end
+
+ it "should write set end" do
+ @prot.write_set_end
+ expect(@trans.read(@trans.available)).to eq("]")
+ end
+
+ it "should write bool" do
+ @prot.write_bool(true)
+ expect(@trans.read(@trans.available)).to eq("1")
+
+ @prot.write_bool(false)
+ expect(@trans.read(@trans.available)).to eq("0")
+ end
+
+ it "should write byte" do
+ @prot.write_byte(100)
+ expect(@trans.read(@trans.available)).to eq("100")
+ end
+
+ it "should write i16" do
+ @prot.write_i16(1000)
+ expect(@trans.read(@trans.available)).to eq("1000")
+ end
+
+ it "should write i32" do
+ @prot.write_i32(3000000000)
+ expect(@trans.read(@trans.available)).to eq("3000000000")
+ end
+
+ it "should write i64" do
+ @prot.write_i64(6000000000)
+ expect(@trans.read(@trans.available)).to eq("6000000000")
+ end
+
+ it "should write double" do
+ @prot.write_double(1.23)
+ expect(@trans.read(@trans.available)).to eq("1.23")
+
+ @prot.write_double(-32.1)
+ expect(@trans.read(@trans.available)).to eq("-32.1")
+
+ @prot.write_double(((+1.0/0.0)/(+1.0/0.0)))
+ expect(@trans.read(@trans.available)).to eq("\"NaN\"")
+
+ @prot.write_double((+1.0/0.0))
+ expect(@trans.read(@trans.available)).to eq("\"Infinity\"")
+
+ @prot.write_double((-1.0/0.0))
+ expect(@trans.read(@trans.available)).to eq("\"-Infinity\"")
+ end
+
+ if RUBY_VERSION >= '1.9'
+ it 'should write string' do
+ @prot.write_string('this is a test string')
+ a = @trans.read(@trans.available)
+ expect(a).to eq('"this is a test string"'.force_encoding(Encoding::BINARY))
+ expect(a.encoding).to eq(Encoding::BINARY)
+ end
+
+ it 'should write string with unicode characters' do
+ @prot.write_string("this is a test string with unicode characters: \u20AC \u20AD")
+ a = @trans.read(@trans.available)
+ expect(a).to eq("\"this is a test string with unicode characters: \u20AC \u20AD\"".force_encoding(Encoding::BINARY))
+ expect(a.encoding).to eq(Encoding::BINARY)
+ end
+ else
+ it 'should write string' do
+ @prot.write_string('this is a test string')
+ expect(@trans.read(@trans.available)).to eq('"this is a test string"')
+ end
+ end
+
+ it "should write binary" do
+ @prot.write_binary("this is a base64 string")
+ expect(@trans.read(@trans.available)).to eq("\"dGhpcyBpcyBhIGJhc2U2NCBzdHJpbmc=\"")
+ end
+
+ it "should write long binary" do
+ @prot.write_binary((0...256).to_a.pack('C*'))
+ expect(@trans.read(@trans.available)).to eq("\"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==\"")
+ end
+
+ it "should get type name for type id" do
+ expect {@prot.get_type_name_for_type_id(Thrift::Types::STOP)}.to raise_error(NotImplementedError)
+ expect {@prot.get_type_name_for_type_id(Thrift::Types::VOID)}.to raise_error(NotImplementedError)
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::BOOL)).to eq("tf")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::BYTE)).to eq("i8")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::DOUBLE)).to eq("dbl")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::I16)).to eq("i16")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::I32)).to eq("i32")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::I64)).to eq("i64")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::STRING)).to eq("str")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::STRUCT)).to eq("rec")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::MAP)).to eq("map")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::SET)).to eq("set")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::LIST)).to eq("lst")
+ end
+
+ it "should get type id for type name" do
+ expect {@prot.get_type_id_for_type_name("pp")}.to raise_error(NotImplementedError)
+ expect(@prot.get_type_id_for_type_name("tf")).to eq(Thrift::Types::BOOL)
+ expect(@prot.get_type_id_for_type_name("i8")).to eq(Thrift::Types::BYTE)
+ expect(@prot.get_type_id_for_type_name("dbl")).to eq(Thrift::Types::DOUBLE)
+ expect(@prot.get_type_id_for_type_name("i16")).to eq(Thrift::Types::I16)
+ expect(@prot.get_type_id_for_type_name("i32")).to eq(Thrift::Types::I32)
+ expect(@prot.get_type_id_for_type_name("i64")).to eq(Thrift::Types::I64)
+ expect(@prot.get_type_id_for_type_name("str")).to eq(Thrift::Types::STRING)
+ expect(@prot.get_type_id_for_type_name("rec")).to eq(Thrift::Types::STRUCT)
+ expect(@prot.get_type_id_for_type_name("map")).to eq(Thrift::Types::MAP)
+ expect(@prot.get_type_id_for_type_name("set")).to eq(Thrift::Types::SET)
+ expect(@prot.get_type_id_for_type_name("lst")).to eq(Thrift::Types::LIST)
+ end
+
+ it "should read json syntax char" do
+ @trans.write('F')
+ expect {@prot.read_json_syntax_char('G')}.to raise_error(Thrift::ProtocolException)
+ @trans.write('H')
+ @prot.read_json_syntax_char('H')
+ end
+
+ it "should read json escape char" do
+ @trans.write('0054')
+ expect(@prot.read_json_escape_char).to eq('T')
+
+ @trans.write("\"\\\"\"")
+ expect(@prot.read_json_string(false)).to eq("\"")
+
+ @trans.write("\"\\\\\"")
+ expect(@prot.read_json_string(false)).to eq("\\")
+
+ @trans.write("\"\\/\"")
+ expect(@prot.read_json_string(false)).to eq("\/")
+
+ @trans.write("\"\\b\"")
+ expect(@prot.read_json_string(false)).to eq("\b")
+
+ @trans.write("\"\\f\"")
+ expect(@prot.read_json_string(false)).to eq("\f")
+
+ @trans.write("\"\\n\"")
+ expect(@prot.read_json_string(false)).to eq("\n")
+
+ @trans.write("\"\\r\"")
+ expect(@prot.read_json_string(false)).to eq("\r")
+
+ @trans.write("\"\\t\"")
+ expect(@prot.read_json_string(false)).to eq("\t")
+ end
+
+ it "should read json string" do
+ @trans.write("\"\\P")
+ expect {@prot.read_json_string(false)}.to raise_error(Thrift::ProtocolException)
+
+ @trans.write("\"this is a test string\"")
+ expect(@prot.read_json_string).to eq("this is a test string")
+ end
+
+ it "should read json base64" do
+ @trans.write("\"dGhpcyBpcyBhIHRlc3Qgc3RyaW5n\"")
+ expect(@prot.read_json_base64).to eq("this is a test string")
+ end
+
+ it "should is json numeric" do
+ expect(@prot.is_json_numeric("A")).to eq(false)
+ expect(@prot.is_json_numeric("+")).to eq(true)
+ expect(@prot.is_json_numeric("-")).to eq(true)
+ expect(@prot.is_json_numeric(".")).to eq(true)
+ expect(@prot.is_json_numeric("0")).to eq(true)
+ expect(@prot.is_json_numeric("1")).to eq(true)
+ expect(@prot.is_json_numeric("2")).to eq(true)
+ expect(@prot.is_json_numeric("3")).to eq(true)
+ expect(@prot.is_json_numeric("4")).to eq(true)
+ expect(@prot.is_json_numeric("5")).to eq(true)
+ expect(@prot.is_json_numeric("6")).to eq(true)
+ expect(@prot.is_json_numeric("7")).to eq(true)
+ expect(@prot.is_json_numeric("8")).to eq(true)
+ expect(@prot.is_json_numeric("9")).to eq(true)
+ expect(@prot.is_json_numeric("E")).to eq(true)
+ expect(@prot.is_json_numeric("e")).to eq(true)
+ end
+
+ it "should read json numeric chars" do
+ @trans.write("1.453E45T")
+ expect(@prot.read_json_numeric_chars).to eq("1.453E45")
+ end
+
+ it "should read json integer" do
+ @trans.write("1.45\"\"")
+ expect {@prot.read_json_integer}.to raise_error(Thrift::ProtocolException)
+ @prot.read_string
+
+ @trans.write("1453T")
+ expect(@prot.read_json_integer).to eq(1453)
+ end
+
+ it "should read json double" do
+ @trans.write("1.45e3e01\"\"")
+ expect {@prot.read_json_double}.to raise_error(Thrift::ProtocolException)
+ @prot.read_string
+
+ @trans.write("\"1.453e01\"")
+ expect {@prot.read_json_double}.to raise_error(Thrift::ProtocolException)
+
+ @trans.write("1.453e01\"\"")
+ expect(@prot.read_json_double).to eq(14.53)
+ @prot.read_string
+
+ @trans.write("\"NaN\"")
+ expect(@prot.read_json_double.nan?).to eq(true)
+
+ @trans.write("\"Infinity\"")
+ expect(@prot.read_json_double).to eq(+1.0/0.0)
+
+ @trans.write("\"-Infinity\"")
+ expect(@prot.read_json_double).to eq(-1.0/0.0)
+ end
+
+ it "should read json object start" do
+ @trans.write("{")
+ expect(@prot.read_json_object_start).to eq(nil)
+ end
+
+ it "should read json object end" do
+ @trans.write("}")
+ expect(@prot.read_json_object_end).to eq(nil)
+ end
+
+ it "should read json array start" do
+ @trans.write("[")
+ expect(@prot.read_json_array_start).to eq(nil)
+ end
+
+ it "should read json array end" do
+ @trans.write("]")
+ expect(@prot.read_json_array_end).to eq(nil)
+ end
+
+ it "should read_message_begin" do
+ @trans.write("[2,")
+ expect {@prot.read_message_begin}.to raise_error(Thrift::ProtocolException)
+
+ @trans.write("[1,\"name\",12,32\"\"")
+ expect(@prot.read_message_begin).to eq(["name", 12, 32])
+ end
+
+ it "should read message end" do
+ @trans.write("]")
+ expect(@prot.read_message_end).to eq(nil)
+ end
+
+ it "should read struct begin" do
+ @trans.write("{")
+ expect(@prot.read_struct_begin).to eq(nil)
+ end
+
+ it "should read struct end" do
+ @trans.write("}")
+ expect(@prot.read_struct_end).to eq(nil)
+ end
+
+ it "should read field begin" do
+ @trans.write("1{\"rec\"")
+ expect(@prot.read_field_begin).to eq([nil, 12, 1])
+ end
+
+ it "should read field end" do
+ @trans.write("}")
+ expect(@prot.read_field_end).to eq(nil)
+ end
+
+ it "should read map begin" do
+ @trans.write("[\"rec\",\"lst\",2,{")
+ expect(@prot.read_map_begin).to eq([12, 15, 2])
+ end
+
+ it "should read map end" do
+ @trans.write("}]")
+ expect(@prot.read_map_end).to eq(nil)
+ end
+
+ it "should read list begin" do
+ @trans.write("[\"rec\",2\"\"")
+ expect(@prot.read_list_begin).to eq([12, 2])
+ end
+
+ it "should read list end" do
+ @trans.write("]")
+ expect(@prot.read_list_end).to eq(nil)
+ end
+
+ it "should read set begin" do
+ @trans.write("[\"rec\",2\"\"")
+ expect(@prot.read_set_begin).to eq([12, 2])
+ end
+
+ it "should read set end" do
+ @trans.write("]")
+ expect(@prot.read_set_end).to eq(nil)
+ end
+
+ it "should read bool" do
+ @trans.write("0\"\"")
+ expect(@prot.read_bool).to eq(false)
+ @prot.read_string
+
+ @trans.write("1\"\"")
+ expect(@prot.read_bool).to eq(true)
+ end
+
+ it "should read byte" do
+ @trans.write("60\"\"")
+ expect(@prot.read_byte).to eq(60)
+ end
+
+ it "should read i16" do
+ @trans.write("1000\"\"")
+ expect(@prot.read_i16).to eq(1000)
+ end
+
+ it "should read i32" do
+ @trans.write("3000000000\"\"")
+ expect(@prot.read_i32).to eq(3000000000)
+ end
+
+ it "should read i64" do
+ @trans.write("6000000000\"\"")
+ expect(@prot.read_i64).to eq(6000000000)
+ end
+
+ it "should read double" do
+ @trans.write("12.23\"\"")
+ expect(@prot.read_double).to eq(12.23)
+ end
+
+ if RUBY_VERSION >= '1.9'
+ it 'should read string' do
+ @trans.write('"this is a test string"'.force_encoding(Encoding::BINARY))
+ a = @prot.read_string
+ expect(a).to eq('this is a test string')
+ expect(a.encoding).to eq(Encoding::UTF_8)
+ end
+
+ it 'should read string with unicode characters' do
+ @trans.write('"this is a test string with unicode characters: \u20AC \u20AD"'.force_encoding(Encoding::BINARY))
+ a = @prot.read_string
+ expect(a).to eq("this is a test string with unicode characters: \u20AC \u20AD")
+ expect(a.encoding).to eq(Encoding::UTF_8)
+ end
+ else
+ it 'should read string' do
+ @trans.write('"this is a test string"')
+ expect(@prot.read_string).to eq('this is a test string')
+ end
+ end
+
+ it "should read binary" do
+ @trans.write("\"dGhpcyBpcyBhIHRlc3Qgc3RyaW5n\"")
+ expect(@prot.read_binary).to eq("this is a test string")
+ end
+
+ it "should read long binary" do
+ @trans.write("\"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==\"")
+ expect(@prot.read_binary.bytes.to_a).to eq((0...256).to_a)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@prot.to_s).to eq("json(memory)")
+ end
+ end
+
+ describe Thrift::JsonProtocolFactory do
+ it "should create a JsonProtocol" do
+ expect(Thrift::JsonProtocolFactory.new.get_protocol(double("MockTransport"))).to be_instance_of(Thrift::JsonProtocol)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::JsonProtocolFactory.new.to_s).to eq("json")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/namespaced_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/namespaced_spec.rb
new file mode 100644
index 000000000..4d6d369e5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/namespaced_spec.rb
@@ -0,0 +1,67 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+describe 'namespaced generation' do
+ before do
+ require 'namespaced_spec_namespace/namespaced_nonblocking_service'
+ end
+
+ it "generated the right files" do
+ prefix = File.expand_path("../gen-rb", __FILE__)
+ ["namespaced_spec_namespace/namespaced_nonblocking_service.rb",
+ "namespaced_spec_namespace/thrift_namespaced_spec_constants.rb",
+ "namespaced_spec_namespace/thrift_namespaced_spec_types.rb",
+ "other_namespace/referenced_constants.rb",
+ "other_namespace/referenced_types.rb"
+ ].each do |name|
+ expect(File.exist?(File.join(prefix, name))).to be_truthy
+ end
+ end
+
+ it "did not generate the wrong files" do
+ prefix = File.expand_path("../gen-rb", __FILE__)
+ ["namespaced_nonblocking_service.rb",
+ "thrift_namespaced_spec_constants.rb",
+ "thrift_namespaced_spec_types.rb",
+ "referenced_constants.rb",
+ "referenced_types.rb"
+ ].each do |name|
+ expect(File.exist?(File.join(prefix, name))).not_to be_truthy
+ end
+ end
+
+ it "has a service class in the right place" do
+ expect(defined?(NamespacedSpecNamespace::NamespacedNonblockingService)).to be_truthy
+ end
+
+ it "has a struct in the right place" do
+ expect(defined?(NamespacedSpecNamespace::Hello)).to be_truthy
+ end
+
+ it "required an included file" do
+ expect(defined?(OtherNamespace::SomeEnum)).to be_truthy
+ end
+
+ it "extended a service" do
+ require "extended/extended_service"
+ end
+
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/nonblocking_server_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/nonblocking_server_spec.rb
new file mode 100644
index 000000000..613d88390
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/nonblocking_server_spec.rb
@@ -0,0 +1,263 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+describe 'NonblockingServer' do
+
+ class Handler
+ def initialize
+ @queue = Queue.new
+ end
+
+ attr_accessor :server
+
+ def greeting(english)
+ if english
+ SpecNamespace::Hello.new
+ else
+ SpecNamespace::Hello.new(:greeting => "Aloha!")
+ end
+ end
+
+ def block
+ @queue.pop
+ end
+
+ def unblock(n)
+ n.times { @queue.push true }
+ end
+
+ def sleep(time)
+ Kernel.sleep time
+ end
+
+ def shutdown
+ @server.shutdown(0, false)
+ end
+ end
+
+ class SpecTransport < Thrift::BaseTransport
+ def initialize(transport, queue)
+ @transport = transport
+ @queue = queue
+ @flushed = false
+ end
+
+ def open?
+ @transport.open?
+ end
+
+ def open
+ @transport.open
+ end
+
+ def close
+ @transport.close
+ end
+
+ def read(sz)
+ @transport.read(sz)
+ end
+
+ def write(buf,sz=nil)
+ @transport.write(buf, sz)
+ end
+
+ def flush
+ @queue.push :flushed unless @flushed or @queue.nil?
+ @flushed = true
+ @transport.flush
+ end
+ end
+
+ class SpecServerSocket < Thrift::ServerSocket
+ def initialize(host, port, queue)
+ super(host, port)
+ @queue = queue
+ end
+
+ def listen
+ super
+ @queue.push :listen
+ end
+ end
+
+ describe Thrift::NonblockingServer do
+ before(:each) do
+ @port = 43251
+ handler = Handler.new
+ processor = SpecNamespace::NonblockingService::Processor.new(handler)
+ queue = Queue.new
+ @transport = SpecServerSocket.new('localhost', @port, queue)
+ transport_factory = Thrift::FramedTransportFactory.new
+ logger = Logger.new(STDERR)
+ logger.level = Logger::WARN
+ @server = Thrift::NonblockingServer.new(processor, @transport, transport_factory, nil, 5, logger)
+ handler.server = @server
+ @server_thread = Thread.new(Thread.current) do |master_thread|
+ begin
+ @server.serve
+ rescue => e
+ p e
+ puts e.backtrace * "\n"
+ master_thread.raise e
+ end
+ end
+ queue.pop
+
+ @clients = []
+ @catch_exceptions = false
+ end
+
+ after(:each) do
+ @clients.each { |client, trans| trans.close }
+ # @server.shutdown(1)
+ @server_thread.kill
+ @transport.close
+ end
+
+ def setup_client(queue = nil)
+ transport = SpecTransport.new(Thrift::FramedTransport.new(Thrift::Socket.new('localhost', @port)), queue)
+ protocol = Thrift::BinaryProtocol.new(transport)
+ client = SpecNamespace::NonblockingService::Client.new(protocol)
+ transport.open
+ @clients << [client, transport]
+ client
+ end
+
+ def setup_client_thread(result)
+ queue = Queue.new
+ Thread.new do
+ begin
+ client = setup_client
+ while (cmd = queue.pop)
+ msg, *args = cmd
+ case msg
+ when :block
+ result << client.block
+ when :unblock
+ client.unblock(args.first)
+ when :hello
+ result << client.greeting(true) # ignore result
+ when :sleep
+ client.sleep(args[0] || 0.5)
+ result << :slept
+ when :shutdown
+ client.shutdown
+ when :exit
+ result << :done
+ break
+ end
+ end
+ @clients.each { |c,t| t.close and break if c == client } #close the transport
+ rescue => e
+ raise e unless @catch_exceptions
+ end
+ end
+ queue
+ end
+
+ it "should handle basic message passing" do
+ client = setup_client
+ expect(client.greeting(true)).to eq(SpecNamespace::Hello.new)
+ expect(client.greeting(false)).to eq(SpecNamespace::Hello.new(:greeting => 'Aloha!'))
+ @server.shutdown
+ end
+
+ it "should handle concurrent clients" do
+ queue = Queue.new
+ trans_queue = Queue.new
+ 4.times do
+ Thread.new(Thread.current) do |main_thread|
+ begin
+ queue.push setup_client(trans_queue).block
+ rescue => e
+ main_thread.raise e
+ end
+ end
+ end
+ 4.times { trans_queue.pop }
+ setup_client.unblock(4)
+ 4.times { expect(queue.pop).to be_truthy }
+ @server.shutdown
+ end
+
+ it "should handle messages from more than 5 long-lived connections" do
+ queues = []
+ result = Queue.new
+ 7.times do |i|
+ queues << setup_client_thread(result)
+ Thread.pass if i == 4 # give the server time to accept connections
+ end
+ client = setup_client
+ # block 4 connections
+ 4.times { |i| queues[i] << :block }
+ queues[4] << :hello
+ queues[5] << :hello
+ queues[6] << :hello
+ 3.times { expect(result.pop).to eq(SpecNamespace::Hello.new) }
+ expect(client.greeting(true)).to eq(SpecNamespace::Hello.new)
+ queues[5] << [:unblock, 4]
+ 4.times { expect(result.pop).to be_truthy }
+ queues[2] << :hello
+ expect(result.pop).to eq(SpecNamespace::Hello.new)
+ expect(client.greeting(false)).to eq(SpecNamespace::Hello.new(:greeting => 'Aloha!'))
+ 7.times { queues.shift << :exit }
+ expect(client.greeting(true)).to eq(SpecNamespace::Hello.new)
+ @server.shutdown
+ end
+
+ it "should shut down when asked" do
+ # connect first to ensure it's running
+ client = setup_client
+ client.greeting(false) # force a message pass
+ @server.shutdown
+ expect(@server_thread.join(2)).to be_an_instance_of(Thread)
+ end
+
+ it "should continue processing active messages when shutting down" do
+ result = Queue.new
+ client = setup_client_thread(result)
+ client << :sleep
+ sleep 0.1 # give the server time to start processing the client's message
+ @server.shutdown
+ expect(@server_thread.join(2)).to be_an_instance_of(Thread)
+ expect(result.pop).to eq(:slept)
+ end
+
+ it "should kill active messages when they don't expire while shutting down" do
+ result = Queue.new
+ client = setup_client_thread(result)
+ client << [:sleep, 10]
+ sleep 0.1 # start processing the client's message
+ @server.shutdown(1)
+ @catch_exceptions = true
+ expect(@server_thread.join(3)).not_to be_nil
+ expect(result).to be_empty
+ end
+
+ it "should allow shutting down in response to a message" do
+ client = setup_client
+ expect(client.greeting(true)).to eq(SpecNamespace::Hello.new)
+ client.shutdown
+ expect(@server_thread.join(2)).not_to be_nil
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/processor_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/processor_spec.rb
new file mode 100644
index 000000000..d30553f55
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/processor_spec.rb
@@ -0,0 +1,80 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+describe 'Processor' do
+
+ class ProcessorSpec
+ include Thrift::Processor
+ end
+
+ describe Thrift::Processor do
+ before(:each) do
+ @processor = ProcessorSpec.new(double("MockHandler"))
+ @prot = double("MockProtocol")
+ end
+
+ def mock_trans(obj)
+ expect(obj).to receive(:trans).ordered do
+ double("trans").tap do |trans|
+ expect(trans).to receive(:flush).ordered
+ end
+ end
+ end
+
+ it "should call process_<message> when it receives that message" do
+ expect(@prot).to receive(:read_message_begin).ordered.and_return ['testMessage', Thrift::MessageTypes::CALL, 17]
+ expect(@processor).to receive(:process_testMessage).with(17, @prot, @prot).ordered
+ expect(@processor.process(@prot, @prot)).to eq(true)
+ end
+
+ it "should raise an ApplicationException when the received message cannot be processed" do
+ expect(@prot).to receive(:read_message_begin).ordered.and_return ['testMessage', Thrift::MessageTypes::CALL, 4]
+ expect(@prot).to receive(:skip).with(Thrift::Types::STRUCT).ordered
+ expect(@prot).to receive(:read_message_end).ordered
+ expect(@prot).to receive(:write_message_begin).with('testMessage', Thrift::MessageTypes::EXCEPTION, 4).ordered
+ e = double(Thrift::ApplicationException)
+ expect(e).to receive(:write).with(@prot).ordered
+ expect(Thrift::ApplicationException).to receive(:new).with(Thrift::ApplicationException::UNKNOWN_METHOD, "Unknown function testMessage").and_return(e)
+ expect(@prot).to receive(:write_message_end).ordered
+ mock_trans(@prot)
+ @processor.process(@prot, @prot)
+ end
+
+ it "should pass args off to the args class" do
+ args_class = double("MockArgsClass")
+ args = double("#<MockArgsClass:mock>").tap do |args|
+ expect(args).to receive(:read).with(@prot).ordered
+ end
+ expect(args_class).to receive(:new).and_return args
+ expect(@prot).to receive(:read_message_end).ordered
+ expect(@processor.read_args(@prot, args_class)).to eql(args)
+ end
+
+ it "should write out a reply when asked" do
+ expect(@prot).to receive(:write_message_begin).with('testMessage', Thrift::MessageTypes::REPLY, 23).ordered
+ result = double("MockResult")
+ expect(result).to receive(:write).with(@prot).ordered
+ expect(@prot).to receive(:write_message_end).ordered
+ mock_trans(@prot)
+ @processor.write_result(result, @prot, 'testMessage', 23)
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/serializer_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/serializer_spec.rb
new file mode 100644
index 000000000..2a7dc6db9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/serializer_spec.rb
@@ -0,0 +1,67 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+describe 'Serializer' do
+
+ describe Thrift::Serializer do
+ it "should serialize structs to binary by default" do
+ serializer = Thrift::Serializer.new(Thrift::BinaryProtocolAcceleratedFactory.new)
+ data = serializer.serialize(SpecNamespace::Hello.new(:greeting => "'Ello guv'nor!"))
+ expect(data).to eq("\x0B\x00\x01\x00\x00\x00\x0E'Ello guv'nor!\x00")
+ end
+
+ it "should serialize structs to the given protocol" do
+ protocol = Thrift::BaseProtocol.new(double("transport"))
+ expect(protocol).to receive(:write_struct_begin).with("SpecNamespace::Hello")
+ expect(protocol).to receive(:write_field_begin).with("greeting", Thrift::Types::STRING, 1)
+ expect(protocol).to receive(:write_string).with("Good day")
+ expect(protocol).to receive(:write_field_end)
+ expect(protocol).to receive(:write_field_stop)
+ expect(protocol).to receive(:write_struct_end)
+ protocol_factory = double("ProtocolFactory")
+ allow(protocol_factory).to receive(:get_protocol).and_return(protocol)
+ serializer = Thrift::Serializer.new(protocol_factory)
+ serializer.serialize(SpecNamespace::Hello.new(:greeting => "Good day"))
+ end
+ end
+
+ describe Thrift::Deserializer do
+ it "should deserialize structs from binary by default" do
+ deserializer = Thrift::Deserializer.new
+ data = "\x0B\x00\x01\x00\x00\x00\x0E'Ello guv'nor!\x00"
+ expect(deserializer.deserialize(SpecNamespace::Hello.new, data)).to eq(SpecNamespace::Hello.new(:greeting => "'Ello guv'nor!"))
+ end
+
+ it "should deserialize structs from the given protocol" do
+ protocol = Thrift::BaseProtocol.new(double("transport"))
+ expect(protocol).to receive(:read_struct_begin).and_return("SpecNamespace::Hello")
+ expect(protocol).to receive(:read_field_begin).and_return(["greeting", Thrift::Types::STRING, 1],
+ [nil, Thrift::Types::STOP, 0])
+ expect(protocol).to receive(:read_string).and_return("Good day")
+ expect(protocol).to receive(:read_field_end)
+ expect(protocol).to receive(:read_struct_end)
+ protocol_factory = double("ProtocolFactory")
+ allow(protocol_factory).to receive(:get_protocol).and_return(protocol)
+ deserializer = Thrift::Deserializer.new(protocol_factory)
+ expect(deserializer.deserialize(SpecNamespace::Hello.new, "")).to eq(SpecNamespace::Hello.new(:greeting => "Good day"))
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/server_socket_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/server_socket_spec.rb
new file mode 100644
index 000000000..ec9e55005
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/server_socket_spec.rb
@@ -0,0 +1,84 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+require File.expand_path("#{File.dirname(__FILE__)}/socket_spec_shared")
+
+describe 'Thrift::ServerSocket' do
+
+ describe Thrift::ServerSocket do
+ before(:each) do
+ @socket = Thrift::ServerSocket.new(1234)
+ end
+
+ it "should create a handle when calling listen" do
+ expect(TCPServer).to receive(:new).with(nil, 1234)
+ @socket.listen
+ end
+
+ it "should accept an optional host argument" do
+ @socket = Thrift::ServerSocket.new('localhost', 1234)
+ expect(TCPServer).to receive(:new).with('localhost', 1234)
+ @socket.to_s == "server(localhost:1234)"
+ @socket.listen
+ end
+
+ it "should create a Thrift::Socket to wrap accepted sockets" do
+ handle = double("TCPServer")
+ expect(TCPServer).to receive(:new).with(nil, 1234).and_return(handle)
+ @socket.listen
+ sock = double("sock")
+ expect(handle).to receive(:accept).and_return(sock)
+ trans = double("Socket")
+ expect(Thrift::Socket).to receive(:new).and_return(trans)
+ expect(trans).to receive(:handle=).with(sock)
+ expect(@socket.accept).to eq(trans)
+ end
+
+ it "should close the handle when closed" do
+ handle = double("TCPServer", :closed? => false)
+ expect(TCPServer).to receive(:new).with(nil, 1234).and_return(handle)
+ @socket.listen
+ expect(handle).to receive(:close)
+ @socket.close
+ end
+
+ it "should return nil when accepting if there is no handle" do
+ expect(@socket.accept).to be_nil
+ end
+
+ it "should return true for closed? when appropriate" do
+ handle = double("TCPServer", :closed? => false)
+ allow(TCPServer).to receive(:new).and_return(handle)
+ @socket.listen
+ expect(@socket).not_to be_closed
+ allow(handle).to receive(:close)
+ @socket.close
+ expect(@socket).to be_closed
+ @socket.listen
+ expect(@socket).not_to be_closed
+ allow(handle).to receive(:closed?).and_return(true)
+ expect(@socket).to be_closed
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@socket.to_s).to eq("socket(:1234)")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/server_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/server_spec.rb
new file mode 100644
index 000000000..57f523776
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/server_spec.rb
@@ -0,0 +1,187 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+require 'spec_helper'
+
+describe 'Server' do
+
+ describe Thrift::BaseServer do
+ before(:each) do
+ @processor = double("Processor")
+ @serverTrans = double("ServerTransport")
+ @trans = double("BaseTransport")
+ @prot = double("BaseProtocol")
+ @server = described_class.new(@processor, @serverTrans, @trans, @prot)
+ end
+
+ it "should default to BaseTransportFactory and BinaryProtocolFactory when not specified" do
+ @server = Thrift::BaseServer.new(double("Processor"), double("BaseServerTransport"))
+ expect(@server.instance_variable_get(:'@transport_factory')).to be_an_instance_of(Thrift::BaseTransportFactory)
+ expect(@server.instance_variable_get(:'@protocol_factory')).to be_an_instance_of(Thrift::BinaryProtocolFactory)
+ end
+
+ it "should not serve" do
+ expect { @server.serve()}.to raise_error(NotImplementedError)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@serverTrans).to receive(:to_s).once.and_return("serverTrans")
+ expect(@trans).to receive(:to_s).once.and_return("trans")
+ expect(@prot).to receive(:to_s).once.and_return("prot")
+ expect(@server.to_s).to eq("server(prot(trans(serverTrans)))")
+ end
+ end
+
+ describe Thrift::SimpleServer do
+ before(:each) do
+ @processor = double("Processor")
+ @serverTrans = double("ServerTransport")
+ @trans = double("BaseTransport")
+ @prot = double("BaseProtocol")
+ @client = double("Client")
+ @server = described_class.new(@processor, @serverTrans, @trans, @prot)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@serverTrans).to receive(:to_s).once.and_return("serverTrans")
+ expect(@trans).to receive(:to_s).once.and_return("trans")
+ expect(@prot).to receive(:to_s).once.and_return("prot")
+ expect(@server.to_s).to eq("simple(server(prot(trans(serverTrans))))")
+ end
+
+ it "should serve in the main thread" do
+ expect(@serverTrans).to receive(:listen).ordered
+ expect(@serverTrans).to receive(:accept).exactly(3).times.and_return(@client)
+ expect(@trans).to receive(:get_transport).exactly(3).times.with(@client).and_return(@trans)
+ expect(@prot).to receive(:get_protocol).exactly(3).times.with(@trans).and_return(@prot)
+ x = 0
+ expect(@processor).to receive(:process).exactly(3).times.with(@prot, @prot) do
+ case (x += 1)
+ when 1 then raise Thrift::TransportException
+ when 2 then raise Thrift::ProtocolException
+ when 3 then throw :stop
+ end
+ end
+ expect(@trans).to receive(:close).exactly(3).times
+ expect(@serverTrans).to receive(:close).ordered
+ expect { @server.serve }.to throw_symbol(:stop)
+ end
+ end
+
+ describe Thrift::ThreadedServer do
+ before(:each) do
+ @processor = double("Processor")
+ @serverTrans = double("ServerTransport")
+ @trans = double("BaseTransport")
+ @prot = double("BaseProtocol")
+ @client = double("Client")
+ @server = described_class.new(@processor, @serverTrans, @trans, @prot)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@serverTrans).to receive(:to_s).once.and_return("serverTrans")
+ expect(@trans).to receive(:to_s).once.and_return("trans")
+ expect(@prot).to receive(:to_s).once.and_return("prot")
+ expect(@server.to_s).to eq("threaded(server(prot(trans(serverTrans))))")
+ end
+
+ it "should serve using threads" do
+ expect(@serverTrans).to receive(:listen).ordered
+ expect(@serverTrans).to receive(:accept).exactly(3).times.and_return(@client)
+ expect(@trans).to receive(:get_transport).exactly(3).times.with(@client).and_return(@trans)
+ expect(@prot).to receive(:get_protocol).exactly(3).times.with(@trans).and_return(@prot)
+ expect(Thread).to receive(:new).with(@prot, @trans).exactly(3).times.and_yield(@prot, @trans)
+ x = 0
+ expect(@processor).to receive(:process).exactly(3).times.with(@prot, @prot) do
+ case (x += 1)
+ when 1 then raise Thrift::TransportException
+ when 2 then raise Thrift::ProtocolException
+ when 3 then throw :stop
+ end
+ end
+ expect(@trans).to receive(:close).exactly(3).times
+ expect(@serverTrans).to receive(:close).ordered
+ expect { @server.serve }.to throw_symbol(:stop)
+ end
+ end
+
+ describe Thrift::ThreadPoolServer do
+ before(:each) do
+ @processor = double("Processor")
+ @server_trans = double("ServerTransport")
+ @trans = double("BaseTransport")
+ @prot = double("BaseProtocol")
+ @client = double("Client")
+ @server = described_class.new(@processor, @server_trans, @trans, @prot)
+ sleep(0.15)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@server_trans).to receive(:to_s).once.and_return("server_trans")
+ expect(@trans).to receive(:to_s).once.and_return("trans")
+ expect(@prot).to receive(:to_s).once.and_return("prot")
+ expect(@server.to_s).to eq("threadpool(server(prot(trans(server_trans))))")
+ end
+
+ it "should serve inside a thread" do
+ exception_q = @server.instance_variable_get(:@exception_q)
+ expect_any_instance_of(described_class).to receive(:serve) do
+ exception_q.push(StandardError.new('ERROR'))
+ end
+ expect { @server.rescuable_serve }.to(raise_error('ERROR'))
+ sleep(0.15)
+ end
+
+ it "should avoid running the server twice when retrying rescuable_serve" do
+ exception_q = @server.instance_variable_get(:@exception_q)
+ expect_any_instance_of(described_class).to receive(:serve) do
+ exception_q.push(StandardError.new('ERROR1'))
+ exception_q.push(StandardError.new('ERROR2'))
+ end
+ expect { @server.rescuable_serve }.to(raise_error('ERROR1'))
+ expect { @server.rescuable_serve }.to(raise_error('ERROR2'))
+ end
+
+ it "should serve using a thread pool" do
+ thread_q = double("SizedQueue")
+ exception_q = double("Queue")
+ @server.instance_variable_set(:@thread_q, thread_q)
+ @server.instance_variable_set(:@exception_q, exception_q)
+ expect(@server_trans).to receive(:listen).ordered
+ expect(thread_q).to receive(:push).with(:token)
+ expect(thread_q).to receive(:pop)
+ expect(Thread).to receive(:new).and_yield
+ expect(@server_trans).to receive(:accept).exactly(3).times.and_return(@client)
+ expect(@trans).to receive(:get_transport).exactly(3).times.and_return(@trans)
+ expect(@prot).to receive(:get_protocol).exactly(3).times.and_return(@prot)
+ x = 0
+ error = RuntimeError.new("Stopped")
+ expect(@processor).to receive(:process).exactly(3).times.with(@prot, @prot) do
+ case (x += 1)
+ when 1 then raise Thrift::TransportException
+ when 2 then raise Thrift::ProtocolException
+ when 3 then raise error
+ end
+ end
+ expect(@trans).to receive(:close).exactly(3).times
+ expect(exception_q).to receive(:push).with(error).and_throw(:stop)
+ expect(@server_trans).to receive(:close)
+ expect { @server.serve }.to(throw_symbol(:stop))
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/socket_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/socket_spec.rb
new file mode 100644
index 000000000..202c745ea
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/socket_spec.rb
@@ -0,0 +1,68 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+require File.expand_path("#{File.dirname(__FILE__)}/socket_spec_shared")
+
+describe 'Socket' do
+
+ describe Thrift::Socket do
+ before(:each) do
+ @socket = Thrift::Socket.new
+ @handle = double("Handle", :closed? => false)
+ allow(@handle).to receive(:close)
+ allow(@handle).to receive(:connect_nonblock)
+ allow(@handle).to receive(:setsockopt)
+ allow(::Socket).to receive(:new).and_return(@handle)
+ end
+
+ it_should_behave_like "a socket"
+
+ it "should raise a TransportException when it cannot open a socket" do
+ expect(::Socket).to receive(:getaddrinfo).with("localhost", 9090, nil, ::Socket::SOCK_STREAM).and_return([[]])
+ expect { @socket.open }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::NOT_OPEN) }
+ end
+
+ it "should open a ::Socket with default args" do
+ expect(::Socket).to receive(:new).and_return(double("Handle", :connect_nonblock => true, :setsockopt => nil))
+ expect(::Socket).to receive(:getaddrinfo).with("localhost", 9090, nil, ::Socket::SOCK_STREAM).and_return([[]])
+ expect(::Socket).to receive(:sockaddr_in)
+ @socket.to_s == "socket(localhost:9090)"
+ @socket.open
+ end
+
+ it "should accept host/port options" do
+ expect(::Socket).to receive(:new).and_return(double("Handle", :connect_nonblock => true, :setsockopt => nil))
+ expect(::Socket).to receive(:getaddrinfo).with("my.domain", 1234, nil, ::Socket::SOCK_STREAM).and_return([[]])
+ expect(::Socket).to receive(:sockaddr_in)
+ @socket = Thrift::Socket.new('my.domain', 1234).open
+ @socket.to_s == "socket(my.domain:1234)"
+ end
+
+ it "should accept an optional timeout" do
+ allow(::Socket).to receive(:new)
+ expect(Thrift::Socket.new('localhost', 8080, 5).timeout).to eq(5)
+ end
+
+ it "should provide a reasonable to_s" do
+ allow(::Socket).to receive(:new)
+ expect(Thrift::Socket.new('myhost', 8090).to_s).to eq("socket(myhost:8090)")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/socket_spec_shared.rb b/src/jaegertracing/thrift/lib/rb/spec/socket_spec_shared.rb
new file mode 100644
index 000000000..32bdb71f0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/socket_spec_shared.rb
@@ -0,0 +1,104 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+shared_examples_for "a socket" do
+ it "should open a socket" do
+ expect(@socket.open).to eq(@handle)
+ end
+
+ it "should be open whenever it has a handle" do
+ expect(@socket).not_to be_open
+ @socket.open
+ expect(@socket).to be_open
+ @socket.handle = nil
+ expect(@socket).not_to be_open
+ @socket.handle = @handle
+ @socket.close
+ expect(@socket).not_to be_open
+ end
+
+ it "should write data to the handle" do
+ @socket.open
+ expect(@handle).to receive(:write).with("foobar")
+ @socket.write("foobar")
+ expect(@handle).to receive(:write).with("fail").and_raise(StandardError)
+ expect { @socket.write("fail") }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::NOT_OPEN) }
+ end
+
+ it "should raise an error when it cannot read from the handle" do
+ @socket.open
+ expect(@handle).to receive(:readpartial).with(17).and_raise(StandardError)
+ expect { @socket.read(17) }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::NOT_OPEN) }
+ end
+
+ it "should return the data read when reading from the handle works" do
+ @socket.open
+ expect(@handle).to receive(:readpartial).with(17).and_return("test data")
+ expect(@socket.read(17)).to eq("test data")
+ end
+
+ it "should declare itself as closed when it has an error" do
+ @socket.open
+ expect(@handle).to receive(:write).with("fail").and_raise(StandardError)
+ expect(@socket).to be_open
+ expect { @socket.write("fail") }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::NOT_OPEN) }
+ expect(@socket).not_to be_open
+ end
+
+ it "should raise an error when the stream is closed" do
+ @socket.open
+ allow(@handle).to receive(:closed?).and_return(true)
+ expect(@socket).not_to be_open
+ expect { @socket.write("fail") }.to raise_error(IOError, "closed stream")
+ expect { @socket.read(10) }.to raise_error(IOError, "closed stream")
+ end
+
+ it "should support the timeout accessor for read" do
+ @socket.timeout = 3
+ @socket.open
+ expect(IO).to receive(:select).with([@handle], nil, nil, 3).and_return([[@handle], [], []])
+ expect(@handle).to receive(:readpartial).with(17).and_return("test data")
+ expect(@socket.read(17)).to eq("test data")
+ end
+
+ it "should support the timeout accessor for write" do
+ @socket.timeout = 3
+ @socket.open
+ expect(IO).to receive(:select).with(nil, [@handle], nil, 3).twice.and_return([[], [@handle], []])
+ expect(@handle).to receive(:write_nonblock).with("test data").and_return(4)
+ expect(@handle).to receive(:write_nonblock).with(" data").and_return(5)
+ expect(@socket.write("test data")).to eq(9)
+ end
+
+ it "should raise an error when read times out" do
+ @socket.timeout = 0.5
+ @socket.open
+ expect(IO).to receive(:select).once {sleep(0.5); nil}
+ expect { @socket.read(17) }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::TIMED_OUT) }
+ end
+
+ it "should raise an error when write times out" do
+ @socket.timeout = 0.5
+ @socket.open
+ allow(IO).to receive(:select).with(nil, [@handle], nil, 0.5).and_return(nil)
+ expect { @socket.write("test data") }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::TIMED_OUT) }
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/spec_helper.rb b/src/jaegertracing/thrift/lib/rb/spec/spec_helper.rb
new file mode 100644
index 000000000..5bf98d077
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/spec_helper.rb
@@ -0,0 +1,64 @@
+# encoding: UTF-8
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'rubygems'
+require 'rspec'
+
+$:.unshift File.join(File.dirname(__FILE__), *%w[.. ext])
+
+# pretend we already loaded fastthread, otherwise the nonblocking_server_spec
+# will get screwed up
+# $" << 'fastthread.bundle'
+
+require 'thrift'
+
+unless Object.method_defined? :tap
+ # if Object#tap isn't defined, then add it; this should only happen in Ruby < 1.8.7
+ class Object
+ def tap(&block)
+ block.call(self)
+ self
+ end
+ end
+end
+
+RSpec.configure do |configuration|
+ configuration.before(:each) do
+ Thrift.type_checking = true
+ end
+end
+
+$:.unshift File.join(File.dirname(__FILE__), *%w[.. test debug_proto gen-rb])
+require 'srv'
+require 'debug_proto_test_constants'
+
+$:.unshift File.join(File.dirname(__FILE__), *%w[gen-rb])
+require 'thrift_spec_types'
+require 'nonblocking_service'
+
+module Fixtures
+ COMPACT_PROTOCOL_TEST_STRUCT = Thrift::Test::COMPACT_TEST.dup
+ COMPACT_PROTOCOL_TEST_STRUCT.a_binary = [0,1,2,3,4,5,6,7,8].pack('c*')
+ COMPACT_PROTOCOL_TEST_STRUCT.set_byte_map = nil
+ COMPACT_PROTOCOL_TEST_STRUCT.map_byte_map = nil
+end
+
+$:.unshift File.join(File.dirname(__FILE__), *%w[gen-rb/flat])
+
diff --git a/src/jaegertracing/thrift/lib/rb/spec/ssl_server_socket_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/ssl_server_socket_spec.rb
new file mode 100644
index 000000000..82e651843
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/ssl_server_socket_spec.rb
@@ -0,0 +1,34 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+require File.expand_path("#{File.dirname(__FILE__)}/socket_spec_shared")
+
+describe 'SSLServerSocket' do
+
+ describe Thrift::SSLServerSocket do
+ before(:each) do
+ @socket = Thrift::SSLServerSocket.new(1234)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@socket.to_s).to eq("ssl(socket(:1234))")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/ssl_socket_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/ssl_socket_spec.rb
new file mode 100644
index 000000000..808d8d512
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/ssl_socket_spec.rb
@@ -0,0 +1,78 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+require File.expand_path("#{File.dirname(__FILE__)}/socket_spec_shared")
+
+describe 'SSLSocket' do
+
+ describe Thrift::SSLSocket do
+ before(:each) do
+ @context = OpenSSL::SSL::SSLContext.new
+ @socket = Thrift::SSLSocket.new
+ @simple_socket_handle = double("Handle", :closed? => false)
+ allow(@simple_socket_handle).to receive(:close)
+ allow(@simple_socket_handle).to receive(:connect_nonblock)
+ allow(@simple_socket_handle).to receive(:setsockopt)
+
+ @handle = double(double("SSLHandle", :connect_nonblock => true, :post_connection_check => true), :closed? => false)
+ allow(@handle).to receive(:connect_nonblock)
+ allow(@handle).to receive(:close)
+ allow(@handle).to receive(:post_connection_check)
+
+ allow(::Socket).to receive(:new).and_return(@simple_socket_handle)
+ allow(OpenSSL::SSL::SSLSocket).to receive(:new).and_return(@handle)
+ end
+
+ it_should_behave_like "a socket"
+
+ it "should raise a TransportException when it cannot open a ssl socket" do
+ expect(::Socket).to receive(:getaddrinfo).with("localhost", 9090, nil, ::Socket::SOCK_STREAM).and_return([[]])
+ expect { @socket.open }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::NOT_OPEN) }
+ end
+
+ it "should open a ::Socket with default args" do
+ expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(@simple_socket_handle, nil).and_return(@handle)
+ expect(@handle).to receive(:post_connection_check).with('localhost')
+ @socket.open
+ end
+
+ it "should accept host/port options" do
+ handle = double("Handle", :connect_nonblock => true, :setsockopt => nil)
+ allow(::Socket).to receive(:new).and_return(handle)
+ expect(::Socket).to receive(:getaddrinfo).with("my.domain", 1234, nil, ::Socket::SOCK_STREAM).and_return([[]])
+ expect(::Socket).to receive(:sockaddr_in)
+ expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(handle, nil).and_return(@handle)
+ expect(@handle).to receive(:post_connection_check).with('my.domain')
+ Thrift::SSLSocket.new('my.domain', 1234, 6000, nil).open
+ end
+
+ it "should accept an optional timeout" do
+ expect(Thrift::SSLSocket.new('localhost', 8080, 5).timeout).to eq(5)
+ end
+
+ it "should accept an optional context" do
+ expect(Thrift::SSLSocket.new('localhost', 8080, 5, @context).ssl_context).to eq(@context)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::SSLSocket.new('myhost', 8090).to_s).to eq("ssl(socket(myhost:8090))")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/struct_nested_containers_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/struct_nested_containers_spec.rb
new file mode 100644
index 000000000..d063569b5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/struct_nested_containers_spec.rb
@@ -0,0 +1,191 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+describe 'StructNestedContainers' do
+
+ def with_type_checking
+ saved_type_checking, Thrift.type_checking = Thrift.type_checking, true
+ begin
+ yield
+ ensure
+ Thrift.type_checking = saved_type_checking
+ end
+ end
+
+ describe Thrift::Struct do
+ # Nested container tests, see THRIFT-369.
+ it "should support nested lists inside lists" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedListInList.new, SpecNamespace::NestedListInList.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = [ [1, 2, 3], [2, 3, 4] ]
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value.push [3, 4, 5]
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested lists inside sets" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedListInSet.new, SpecNamespace::NestedListInSet.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = [ [1, 2, 3], [2, 3, 4] ].to_set
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value.add [3, 4, 5]
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested lists in map keys" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedListInMapKey.new, SpecNamespace::NestedListInMapKey.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = { [1, 2, 3] => 1, [2, 3, 4] => 2 }
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value[[3, 4, 5]] = 3
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested lists in map values" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedListInMapValue.new, SpecNamespace::NestedListInMapValue.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = { 1 => [1, 2, 3], 2 => [2, 3, 4] }
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value[3] = [3, 4, 5]
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested sets inside lists" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedSetInList.new, SpecNamespace::NestedSetInList.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = [ [1, 2, 3].to_set, [2, 3, 4].to_set ]
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value.push([3, 4, 5].to_set)
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested sets inside sets" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedSetInSet.new, SpecNamespace::NestedSetInSet.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = [ [1, 2, 3].to_set, [2, 3, 4].to_set ].to_set
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value.add([3, 4, 5].to_set)
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested sets in map keys" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedSetInMapKey.new, SpecNamespace::NestedSetInMapKey.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = { [1, 2, 3].to_set => 1, [2, 3, 4].to_set => 2 }
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value[[3, 4, 5].to_set] = 3
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested sets in map values" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedSetInMapValue.new, SpecNamespace::NestedSetInMapValue.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = { 1 => [1, 2, 3].to_set, 2 => [2, 3, 4].to_set }
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value[3] = [3, 4, 5].to_set
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested maps inside lists" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedMapInList.new, SpecNamespace::NestedMapInList.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = [ {1 => 2, 3 => 4}, {2 => 3, 4 => 5} ]
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value.push({ 3 => 4, 5 => 6 })
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested maps inside sets" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedMapInSet.new, SpecNamespace::NestedMapInSet.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = [ {1 => 2, 3 => 4}, {2 => 3, 4 => 5} ].to_set
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value.add({ 3 => 4, 5 => 6 })
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested maps in map keys" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedMapInMapKey.new, SpecNamespace::NestedMapInMapKey.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = { { 1 => 2, 3 => 4} => 1, {2 => 3, 4 => 5} => 2 }
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value[{3 => 4, 5 => 6}] = 3
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested maps in map values" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedMapInMapValue.new, SpecNamespace::NestedMapInMapValue.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = { 1 => { 1 => 2, 3 => 4}, 2 => {2 => 3, 4 => 5} }
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value[3] = { 3 => 4, 5 => 6 }
+ expect(a).not_to eq(b)
+ end
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/struct_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/struct_spec.rb
new file mode 100644
index 000000000..bbd502b62
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/struct_spec.rb
@@ -0,0 +1,293 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+describe 'Struct' do
+
+ describe Thrift::Struct do
+ it "should iterate over all fields properly" do
+ fields = {}
+ SpecNamespace::Foo.new.each_field { |fid,field_info| fields[fid] = field_info }
+ expect(fields).to eq(SpecNamespace::Foo::FIELDS)
+ end
+
+ it "should initialize all fields to defaults" do
+ validate_default_arguments(SpecNamespace::Foo.new)
+ end
+
+ it "should initialize all fields to defaults and accept a block argument" do
+ SpecNamespace::Foo.new do |f|
+ validate_default_arguments(f)
+ end
+ end
+
+ def validate_default_arguments(object)
+ expect(object.simple).to eq(53)
+ expect(object.words).to eq("words")
+ expect(object.hello).to eq(SpecNamespace::Hello.new(:greeting => 'hello, world!'))
+ expect(object.ints).to eq([1, 2, 2, 3])
+ expect(object.complex).to be_nil
+ expect(object.shorts).to eq(Set.new([5, 17, 239]))
+ end
+
+ it "should not share default values between instances" do
+ begin
+ struct = SpecNamespace::Foo.new
+ struct.ints << 17
+ expect(SpecNamespace::Foo.new.ints).to eq([1,2,2,3])
+ ensure
+ # ensure no leakage to other tests
+ SpecNamespace::Foo::FIELDS[4][:default] = [1,2,2,3]
+ end
+ end
+
+ it "should properly initialize boolean values" do
+ struct = SpecNamespace::BoolStruct.new(:yesno => false)
+ expect(struct.yesno).to be_falsey
+ end
+
+ it "should have proper == semantics" do
+ expect(SpecNamespace::Foo.new).not_to eq(SpecNamespace::Hello.new)
+ expect(SpecNamespace::Foo.new).to eq(SpecNamespace::Foo.new)
+ expect(SpecNamespace::Foo.new(:simple => 52)).not_to eq(SpecNamespace::Foo.new)
+ end
+
+ it "should print enum value names in inspect" do
+ expect(SpecNamespace::StructWithSomeEnum.new(:some_enum => SpecNamespace::SomeEnum::ONE).inspect).to eq("<SpecNamespace::StructWithSomeEnum some_enum:ONE (0)>")
+
+ expect(SpecNamespace::StructWithEnumMap.new(:my_map => {SpecNamespace::SomeEnum::ONE => [SpecNamespace::SomeEnum::TWO]}).inspect).to eq("<SpecNamespace::StructWithEnumMap my_map:{ONE (0): [TWO (1)]}>")
+ end
+
+ it "should pretty print binary fields" do
+ expect(SpecNamespace::Foo2.new(:my_binary => "\001\002\003").inspect).to eq("<SpecNamespace::Foo2 my_binary:010203>")
+ end
+
+ it "should offer field? methods" do
+ expect(SpecNamespace::Foo.new.opt_string?).to be_falsey
+ expect(SpecNamespace::Foo.new(:simple => 52).simple?).to be_truthy
+ expect(SpecNamespace::Foo.new(:my_bool => false).my_bool?).to be_truthy
+ expect(SpecNamespace::Foo.new(:my_bool => true).my_bool?).to be_truthy
+ end
+
+ it "should be comparable" do
+ s1 = SpecNamespace::StructWithSomeEnum.new(:some_enum => SpecNamespace::SomeEnum::ONE)
+ s2 = SpecNamespace::StructWithSomeEnum.new(:some_enum => SpecNamespace::SomeEnum::TWO)
+
+ expect(s1 <=> s2).to eq(-1)
+ expect(s2 <=> s1).to eq(1)
+ expect(s1 <=> s1).to eq(0)
+ expect(s1 <=> SpecNamespace::StructWithSomeEnum.new()).to eq(-1)
+ end
+
+ it "should read itself off the wire" do
+ struct = SpecNamespace::Foo.new
+ prot = Thrift::BaseProtocol.new(double("transport"))
+ expect(prot).to receive(:read_struct_begin).twice
+ expect(prot).to receive(:read_struct_end).twice
+ expect(prot).to receive(:read_field_begin).and_return(
+ ['complex', Thrift::Types::MAP, 5], # Foo
+ ['words', Thrift::Types::STRING, 2], # Foo
+ ['hello', Thrift::Types::STRUCT, 3], # Foo
+ ['greeting', Thrift::Types::STRING, 1], # Hello
+ [nil, Thrift::Types::STOP, 0], # Hello
+ ['simple', Thrift::Types::I32, 1], # Foo
+ ['ints', Thrift::Types::LIST, 4], # Foo
+ ['shorts', Thrift::Types::SET, 6], # Foo
+ [nil, Thrift::Types::STOP, 0] # Hello
+ )
+ expect(prot).to receive(:read_field_end).exactly(7).times
+ expect(prot).to receive(:read_map_begin).and_return(
+ [Thrift::Types::I32, Thrift::Types::MAP, 2], # complex
+ [Thrift::Types::STRING, Thrift::Types::DOUBLE, 2], # complex/1/value
+ [Thrift::Types::STRING, Thrift::Types::DOUBLE, 1] # complex/2/value
+ )
+ expect(prot).to receive(:read_map_end).exactly(3).times
+ expect(prot).to receive(:read_list_begin).and_return([Thrift::Types::I32, 4])
+ expect(prot).to receive(:read_list_end)
+ expect(prot).to receive(:read_set_begin).and_return([Thrift::Types::I16, 2])
+ expect(prot).to receive(:read_set_end)
+ expect(prot).to receive(:read_i32).and_return(
+ 1, 14, # complex keys
+ 42, # simple
+ 4, 23, 4, 29 # ints
+ )
+ expect(prot).to receive(:read_string).and_return("pi", "e", "feigenbaum", "apple banana", "what's up?")
+ expect(prot).to receive(:read_double).and_return(Math::PI, Math::E, 4.669201609)
+ expect(prot).to receive(:read_i16).and_return(2, 3)
+ expect(prot).not_to receive(:skip)
+ struct.read(prot)
+
+ expect(struct.simple).to eq(42)
+ expect(struct.complex).to eq({1 => {"pi" => Math::PI, "e" => Math::E}, 14 => {"feigenbaum" => 4.669201609}})
+ expect(struct.hello).to eq(SpecNamespace::Hello.new(:greeting => "what's up?"))
+ expect(struct.words).to eq("apple banana")
+ expect(struct.ints).to eq([4, 23, 4, 29])
+ expect(struct.shorts).to eq(Set.new([3, 2]))
+ end
+
+ it "should serialize false boolean fields correctly" do
+ b = SpecNamespace::BoolStruct.new(:yesno => false)
+ prot = Thrift::BinaryProtocol.new(Thrift::MemoryBufferTransport.new)
+ expect(prot).to receive(:write_bool).with(false)
+ b.write(prot)
+ end
+
+ it "should skip unexpected fields in structs and use default values" do
+ struct = SpecNamespace::Foo.new
+ prot = Thrift::BaseProtocol.new(double("transport"))
+ expect(prot).to receive(:read_struct_begin)
+ expect(prot).to receive(:read_struct_end)
+ expect(prot).to receive(:read_field_begin).and_return(
+ ['simple', Thrift::Types::I32, 1],
+ ['complex', Thrift::Types::STRUCT, 5],
+ ['thinz', Thrift::Types::MAP, 7],
+ ['foobar', Thrift::Types::I32, 3],
+ ['words', Thrift::Types::STRING, 2],
+ [nil, Thrift::Types::STOP, 0]
+ )
+ expect(prot).to receive(:read_field_end).exactly(5).times
+ expect(prot).to receive(:read_i32).and_return(42)
+ expect(prot).to receive(:read_string).and_return("foobar")
+ expect(prot).to receive(:skip).with(Thrift::Types::STRUCT)
+ expect(prot).to receive(:skip).with(Thrift::Types::MAP)
+ # prot.should_receive(:read_map_begin).and_return([Thrift::Types::I32, Thrift::Types::I32, 0])
+ # prot.should_receive(:read_map_end)
+ expect(prot).to receive(:skip).with(Thrift::Types::I32)
+ struct.read(prot)
+
+ expect(struct.simple).to eq(42)
+ expect(struct.complex).to be_nil
+ expect(struct.words).to eq("foobar")
+ expect(struct.hello).to eq(SpecNamespace::Hello.new(:greeting => 'hello, world!'))
+ expect(struct.ints).to eq([1, 2, 2, 3])
+ expect(struct.shorts).to eq(Set.new([5, 17, 239]))
+ end
+
+ it "should write itself to the wire" do
+ prot = Thrift::BaseProtocol.new(double("transport")) #mock("Protocol")
+ expect(prot).to receive(:write_struct_begin).with("SpecNamespace::Foo")
+ expect(prot).to receive(:write_struct_begin).with("SpecNamespace::Hello")
+ expect(prot).to receive(:write_struct_end).twice
+ expect(prot).to receive(:write_field_begin).with('ints', Thrift::Types::LIST, 4)
+ expect(prot).to receive(:write_i32).with(1)
+ expect(prot).to receive(:write_i32).with(2).twice
+ expect(prot).to receive(:write_i32).with(3)
+ expect(prot).to receive(:write_field_begin).with('complex', Thrift::Types::MAP, 5)
+ expect(prot).to receive(:write_i32).with(5)
+ expect(prot).to receive(:write_string).with('foo')
+ expect(prot).to receive(:write_double).with(1.23)
+ expect(prot).to receive(:write_field_begin).with('shorts', Thrift::Types::SET, 6)
+ expect(prot).to receive(:write_i16).with(5)
+ expect(prot).to receive(:write_i16).with(17)
+ expect(prot).to receive(:write_i16).with(239)
+ expect(prot).to receive(:write_field_stop).twice
+ expect(prot).to receive(:write_field_end).exactly(6).times
+ expect(prot).to receive(:write_field_begin).with('simple', Thrift::Types::I32, 1)
+ expect(prot).to receive(:write_i32).with(53)
+ expect(prot).to receive(:write_field_begin).with('hello', Thrift::Types::STRUCT, 3)
+ expect(prot).to receive(:write_field_begin).with('greeting', Thrift::Types::STRING, 1)
+ expect(prot).to receive(:write_string).with('hello, world!')
+ expect(prot).to receive(:write_map_begin).with(Thrift::Types::I32, Thrift::Types::MAP, 1)
+ expect(prot).to receive(:write_map_begin).with(Thrift::Types::STRING, Thrift::Types::DOUBLE, 1)
+ expect(prot).to receive(:write_map_end).twice
+ expect(prot).to receive(:write_list_begin).with(Thrift::Types::I32, 4)
+ expect(prot).to receive(:write_list_end)
+ expect(prot).to receive(:write_set_begin).with(Thrift::Types::I16, 3)
+ expect(prot).to receive(:write_set_end)
+
+ struct = SpecNamespace::Foo.new
+ struct.words = nil
+ struct.complex = {5 => {"foo" => 1.23}}
+ struct.write(prot)
+ end
+
+ it "should raise an exception if presented with an unknown container" do
+ # yeah this is silly, but I'm going for code coverage here
+ struct = SpecNamespace::Foo.new
+ expect { struct.send :write_container, nil, nil, {:type => "foo"} }.to raise_error(StandardError, "Not a container type: foo")
+ end
+
+ it "should support optional type-checking in Thrift::Struct.new" do
+ Thrift.type_checking = true
+ begin
+ expect { SpecNamespace::Hello.new(:greeting => 3) }.to raise_error(Thrift::TypeError, /Expected Types::STRING, received (Integer|Fixnum) for field greeting/)
+ ensure
+ Thrift.type_checking = false
+ end
+ expect { SpecNamespace::Hello.new(:greeting => 3) }.not_to raise_error
+ end
+
+ it "should support optional type-checking in field accessors" do
+ Thrift.type_checking = true
+ begin
+ hello = SpecNamespace::Hello.new
+ expect { hello.greeting = 3 }.to raise_error(Thrift::TypeError, /Expected Types::STRING, received (Integer|Fixnum) for field greeting/)
+ ensure
+ Thrift.type_checking = false
+ end
+ expect { hello.greeting = 3 }.not_to raise_error
+ end
+
+ it "should raise an exception when unknown types are given to Thrift::Struct.new" do
+ expect { SpecNamespace::Hello.new(:fish => 'salmon') }.to raise_error(Exception, "Unknown key given to SpecNamespace::Hello.new: fish")
+ end
+
+ it "should support `raise Xception, 'message'` for Exception structs" do
+ begin
+ raise SpecNamespace::Xception, "something happened"
+ rescue Thrift::Exception => e
+ expect(e.message).to eq("something happened")
+ expect(e.code).to eq(1)
+ # ensure it gets serialized properly, this is the really important part
+ prot = Thrift::BaseProtocol.new(double("trans"))
+ expect(prot).to receive(:write_struct_begin).with("SpecNamespace::Xception")
+ expect(prot).to receive(:write_struct_end)
+ expect(prot).to receive(:write_field_begin).with('message', Thrift::Types::STRING, 1)#, "something happened")
+ expect(prot).to receive(:write_string).with("something happened")
+ expect(prot).to receive(:write_field_begin).with('code', Thrift::Types::I32, 2)#, 1)
+ expect(prot).to receive(:write_i32).with(1)
+ expect(prot).to receive(:write_field_stop)
+ expect(prot).to receive(:write_field_end).twice
+
+ e.write(prot)
+ end
+ end
+
+ it "should support the regular initializer for exception structs" do
+ begin
+ raise SpecNamespace::Xception, :message => "something happened", :code => 5
+ rescue Thrift::Exception => e
+ expect(e.message).to eq("something happened")
+ expect(e.code).to eq(5)
+ prot = Thrift::BaseProtocol.new(double("trans"))
+ expect(prot).to receive(:write_struct_begin).with("SpecNamespace::Xception")
+ expect(prot).to receive(:write_struct_end)
+ expect(prot).to receive(:write_field_begin).with('message', Thrift::Types::STRING, 1)
+ expect(prot).to receive(:write_string).with("something happened")
+ expect(prot).to receive(:write_field_begin).with('code', Thrift::Types::I32, 2)
+ expect(prot).to receive(:write_i32).with(5)
+ expect(prot).to receive(:write_field_stop)
+ expect(prot).to receive(:write_field_end).twice
+
+ e.write(prot)
+ end
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/thin_http_server_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/thin_http_server_spec.rb
new file mode 100644
index 000000000..665391b7d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/thin_http_server_spec.rb
@@ -0,0 +1,141 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+require 'rack/test'
+require 'thrift/server/thin_http_server'
+
+describe Thrift::ThinHTTPServer do
+
+ let(:processor) { double('processor') }
+
+ describe "#initialize" do
+
+ context "when using the defaults" do
+
+ it "binds to port 80, with host 0.0.0.0, a path of '/'" do
+ expect(Thin::Server).to receive(:new).with('0.0.0.0', 80, an_instance_of(Rack::Builder))
+ Thrift::ThinHTTPServer.new(processor)
+ end
+
+ it 'creates a ThinHTTPServer::RackApplicationContext' do
+ expect(Thrift::ThinHTTPServer::RackApplication).to receive(:for).with("/", processor, an_instance_of(Thrift::BinaryProtocolFactory)).and_return(anything)
+ Thrift::ThinHTTPServer.new(processor)
+ end
+
+ it "uses the BinaryProtocolFactory" do
+ expect(Thrift::BinaryProtocolFactory).to receive(:new)
+ Thrift::ThinHTTPServer.new(processor)
+ end
+
+ end
+
+ context "when using the options" do
+
+ it 'accepts :ip, :port, :path' do
+ ip = "192.168.0.1"
+ port = 3000
+ path = "/thin"
+ expect(Thin::Server).to receive(:new).with(ip, port, an_instance_of(Rack::Builder))
+ Thrift::ThinHTTPServer.new(processor,
+ :ip => ip,
+ :port => port,
+ :path => path)
+ end
+
+ it 'creates a ThinHTTPServer::RackApplicationContext with a different protocol factory' do
+ expect(Thrift::ThinHTTPServer::RackApplication).to receive(:for).with("/", processor, an_instance_of(Thrift::JsonProtocolFactory)).and_return(anything)
+ Thrift::ThinHTTPServer.new(processor,
+ :protocol_factory => Thrift::JsonProtocolFactory.new)
+ end
+
+ end
+
+ end
+
+ describe "#serve" do
+
+ it 'starts the Thin server' do
+ underlying_thin_server = double('thin server', :start => true)
+ allow(Thin::Server).to receive(:new).and_return(underlying_thin_server)
+
+ thin_thrift_server = Thrift::ThinHTTPServer.new(processor)
+
+ expect(underlying_thin_server).to receive(:start)
+ thin_thrift_server.serve
+ end
+ end
+
+end
+
+describe Thrift::ThinHTTPServer::RackApplication do
+ include Rack::Test::Methods
+
+ let(:processor) { double('processor') }
+ let(:protocol_factory) { double('protocol factory') }
+
+ def app
+ Thrift::ThinHTTPServer::RackApplication.for("/", processor, protocol_factory)
+ end
+
+ context "404 response" do
+
+ it 'receives a non-POST' do
+ header('Content-Type', "application/x-thrift")
+ get "/"
+ expect(last_response.status).to be 404
+ end
+
+ it 'receives a header other than application/x-thrift' do
+ header('Content-Type', "application/json")
+ post "/"
+ expect(last_response.status).to be 404
+ end
+
+ end
+
+ context "200 response" do
+
+ before do
+ allow(protocol_factory).to receive(:get_protocol)
+ allow(processor).to receive(:process)
+ end
+
+ it 'creates an IOStreamTransport' do
+ header('Content-Type', "application/x-thrift")
+ expect(Thrift::IOStreamTransport).to receive(:new).with(an_instance_of(Rack::Lint::InputWrapper), an_instance_of(Rack::Response))
+ post "/"
+ end
+
+ it 'fetches the right protocol based on the Transport' do
+ header('Content-Type', "application/x-thrift")
+ expect(protocol_factory).to receive(:get_protocol).with(an_instance_of(Thrift::IOStreamTransport))
+ post "/"
+ end
+
+ it 'status code 200' do
+ header('Content-Type', "application/x-thrift")
+ post "/"
+ expect(last_response.ok?).to be_truthy
+ end
+
+ end
+
+end
+
diff --git a/src/jaegertracing/thrift/lib/rb/spec/types_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/types_spec.rb
new file mode 100644
index 000000000..d595ab563
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/types_spec.rb
@@ -0,0 +1,118 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+describe Thrift::Types do
+
+ before(:each) do
+ Thrift.type_checking = true
+ end
+
+ after(:each) do
+ Thrift.type_checking = false
+ end
+
+ context 'type checking' do
+ it "should return the proper name for each type" do
+ expect(Thrift.type_name(Thrift::Types::I16)).to eq("Types::I16")
+ expect(Thrift.type_name(Thrift::Types::VOID)).to eq("Types::VOID")
+ expect(Thrift.type_name(Thrift::Types::LIST)).to eq("Types::LIST")
+ expect(Thrift.type_name(42)).to be_nil
+ end
+
+ it "should check types properly" do
+ # lambda { Thrift.check_type(nil, Thrift::Types::STOP) }.should raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(3, {:type => Thrift::Types::STOP}, :foo) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::VOID}, :foo) }.not_to raise_error
+ expect { Thrift.check_type(3, {:type => Thrift::Types::VOID}, :foo) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(true, {:type => Thrift::Types::BOOL}, :foo) }.not_to raise_error
+ expect { Thrift.check_type(3, {:type => Thrift::Types::BOOL}, :foo) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(42, {:type => Thrift::Types::BYTE}, :foo) }.not_to raise_error
+ expect { Thrift.check_type(42, {:type => Thrift::Types::I16}, :foo) }.not_to raise_error
+ expect { Thrift.check_type(42, {:type => Thrift::Types::I32}, :foo) }.not_to raise_error
+ expect { Thrift.check_type(42, {:type => Thrift::Types::I64}, :foo) }.not_to raise_error
+ expect { Thrift.check_type(3.14, {:type => Thrift::Types::I32}, :foo) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(3.14, {:type => Thrift::Types::DOUBLE}, :foo) }.not_to raise_error
+ expect { Thrift.check_type(3, {:type => Thrift::Types::DOUBLE}, :foo) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type("3", {:type => Thrift::Types::STRING}, :foo) }.not_to raise_error
+ expect { Thrift.check_type(3, {:type => Thrift::Types::STRING}, :foo) }.to raise_error(Thrift::TypeError)
+ hello = SpecNamespace::Hello.new
+ expect { Thrift.check_type(hello, {:type => Thrift::Types::STRUCT, :class => SpecNamespace::Hello}, :foo) }.not_to raise_error
+ expect { Thrift.check_type("foo", {:type => Thrift::Types::STRUCT}, :foo) }.to raise_error(Thrift::TypeError)
+ field = {:type => Thrift::Types::MAP, :key => {:type => Thrift::Types::I32}, :value => {:type => Thrift::Types::STRING}}
+ expect { Thrift.check_type({1 => "one"}, field, :foo) }.not_to raise_error
+ expect { Thrift.check_type([1], field, :foo) }.to raise_error(Thrift::TypeError)
+ field = {:type => Thrift::Types::LIST, :element => {:type => Thrift::Types::I32}}
+ expect { Thrift.check_type([1], field, :foo) }.not_to raise_error
+ expect { Thrift.check_type({:foo => 1}, field, :foo) }.to raise_error(Thrift::TypeError)
+ field = {:type => Thrift::Types::SET, :element => {:type => Thrift::Types::I32}}
+ expect { Thrift.check_type(Set.new([1,2]), field, :foo) }.not_to raise_error
+ expect { Thrift.check_type([1,2], field, :foo) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type({:foo => true}, field, :foo) }.to raise_error(Thrift::TypeError)
+ end
+
+ it "should error out if nil is passed and skip_types is false" do
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::BOOL}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::BYTE}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::I16}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::I32}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::I64}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::DOUBLE}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::STRING}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::STRUCT}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::LIST}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::SET}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::MAP}, :foo, false) }.to raise_error(Thrift::TypeError)
+ end
+
+ it "should check element types on containers" do
+ field = {:type => Thrift::Types::LIST, :element => {:type => Thrift::Types::I32}}
+ expect { Thrift.check_type([1, 2], field, :foo) }.not_to raise_error
+ expect { Thrift.check_type([1, nil, 2], field, :foo) }.to raise_error(Thrift::TypeError)
+ field = {:type => Thrift::Types::MAP, :key => {:type => Thrift::Types::I32}, :value => {:type => Thrift::Types::STRING}}
+ expect { Thrift.check_type({1 => "one", 2 => "two"}, field, :foo) }.not_to raise_error
+ expect { Thrift.check_type({1 => "one", nil => "nil"}, field, :foo) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type({1 => nil, 2 => "two"}, field, :foo) }.to raise_error(Thrift::TypeError)
+ field = {:type => Thrift::Types::SET, :element => {:type => Thrift::Types::I32}}
+ expect { Thrift.check_type(Set.new([1, 2]), field, :foo) }.not_to raise_error
+ expect { Thrift.check_type(Set.new([1, nil, 2]), field, :foo) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(Set.new([1, 2.3, 2]), field, :foo) }.to raise_error(Thrift::TypeError)
+
+ field = {:type => Thrift::Types::STRUCT, :class => SpecNamespace::Hello}
+ expect { Thrift.check_type(SpecNamespace::BoolStruct, field, :foo) }.to raise_error(Thrift::TypeError)
+ end
+
+ it "should give the Thrift::TypeError a readable message" do
+ msg = /Expected Types::STRING, received (Integer|Fixnum) for field foo/
+ expect { Thrift.check_type(3, {:type => Thrift::Types::STRING}, :foo) }.to raise_error(Thrift::TypeError, msg)
+ msg = /Expected Types::STRING, received (Integer|Fixnum) for field foo.element/
+ field = {:type => Thrift::Types::LIST, :element => {:type => Thrift::Types::STRING}}
+ expect { Thrift.check_type([3], field, :foo) }.to raise_error(Thrift::TypeError, msg)
+ msg = "Expected Types::I32, received NilClass for field foo.element.key"
+ field = {:type => Thrift::Types::LIST,
+ :element => {:type => Thrift::Types::MAP,
+ :key => {:type => Thrift::Types::I32},
+ :value => {:type => Thrift::Types::I32}}}
+ expect { Thrift.check_type([{nil => 3}], field, :foo) }.to raise_error(Thrift::TypeError, msg)
+ msg = "Expected Types::I32, received NilClass for field foo.element.value"
+ expect { Thrift.check_type([{1 => nil}], field, :foo) }.to raise_error(Thrift::TypeError, msg)
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/union_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/union_spec.rb
new file mode 100644
index 000000000..efb385346
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/union_spec.rb
@@ -0,0 +1,214 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+
+describe 'Union' do
+
+ describe Thrift::Union do
+ it "should return nil value in unset union" do
+ union = SpecNamespace::My_union.new
+ expect(union.get_set_field).to eq(nil)
+ expect(union.get_value).to eq(nil)
+ end
+
+ it "should set a field and be accessible through get_value and the named field accessor" do
+ union = SpecNamespace::My_union.new
+ union.integer32 = 25
+ expect(union.get_set_field).to eq(:integer32)
+ expect(union.get_value).to eq(25)
+ expect(union.integer32).to eq(25)
+ end
+
+ it "should work correctly when instantiated with static field constructors" do
+ union = SpecNamespace::My_union.integer32(5)
+ expect(union.get_set_field).to eq(:integer32)
+ expect(union.integer32).to eq(5)
+ end
+
+ it "should raise for wrong set field" do
+ union = SpecNamespace::My_union.new
+ union.integer32 = 25
+ expect { union.some_characters }.to raise_error(RuntimeError, "some_characters is not union's set field.")
+ end
+
+ it "should raise for wrong set field when hash initialized and type checking is off" do
+ Thrift.type_checking = false
+ union = SpecNamespace::My_union.new({incorrect_field: :incorrect})
+ expect { Thrift::Serializer.new.serialize(union) }.to raise_error(RuntimeError, "set_field is not valid for this union!")
+ end
+
+ it "should not be equal to nil" do
+ union = SpecNamespace::My_union.new
+ expect(union).not_to eq(nil)
+ end
+
+ it "should not be equal with an empty String" do
+ union = SpecNamespace::My_union.new
+ expect(union).not_to eq('')
+ end
+
+ it "should not equate two different unions, i32 vs. string" do
+ union = SpecNamespace::My_union.new(:integer32, 25)
+ other_union = SpecNamespace::My_union.new(:some_characters, "blah!")
+ expect(union).not_to eq(other_union)
+ end
+
+ it "should properly reset setfield and setvalue" do
+ union = SpecNamespace::My_union.new(:integer32, 25)
+ expect(union.get_set_field).to eq(:integer32)
+ union.some_characters = "blah!"
+ expect(union.get_set_field).to eq(:some_characters)
+ expect(union.get_value).to eq("blah!")
+ expect { union.integer32 }.to raise_error(RuntimeError, "integer32 is not union's set field.")
+ end
+
+ it "should not equate two different unions with different values" do
+ union = SpecNamespace::My_union.new(:integer32, 25)
+ other_union = SpecNamespace::My_union.new(:integer32, 400)
+ expect(union).not_to eq(other_union)
+ end
+
+ it "should not equate two different unions with different fields" do
+ union = SpecNamespace::My_union.new(:integer32, 25)
+ other_union = SpecNamespace::My_union.new(:other_i32, 25)
+ expect(union).not_to eq(other_union)
+ end
+
+ it "should inspect properly" do
+ union = SpecNamespace::My_union.new(:integer32, 25)
+ expect(union.inspect).to eq("<SpecNamespace::My_union integer32: 25>")
+ end
+
+ it "should not allow setting with instance_variable_set" do
+ union = SpecNamespace::My_union.new(:integer32, 27)
+ union.instance_variable_set(:@some_characters, "hallo!")
+ expect(union.get_set_field).to eq(:integer32)
+ expect(union.get_value).to eq(27)
+ expect { union.some_characters }.to raise_error(RuntimeError, "some_characters is not union's set field.")
+ end
+
+ it "should serialize to binary correctly" do
+ trans = Thrift::MemoryBufferTransport.new
+ proto = Thrift::BinaryProtocol.new(trans)
+
+ union = SpecNamespace::My_union.new(:integer32, 25)
+ union.write(proto)
+
+ other_union = SpecNamespace::My_union.new(:integer32, 25)
+ other_union.read(proto)
+ expect(other_union).to eq(union)
+ end
+
+ it "should serialize to json correctly" do
+ trans = Thrift::MemoryBufferTransport.new
+ proto = Thrift::JsonProtocol.new(trans)
+
+ union = SpecNamespace::My_union.new(:integer32, 25)
+ union.write(proto)
+
+ other_union = SpecNamespace::My_union.new(:integer32, 25)
+ other_union.read(proto)
+ expect(other_union).to eq(union)
+ end
+
+ it "should raise when validating unset union" do
+ union = SpecNamespace::My_union.new
+ expect { union.validate }.to raise_error(StandardError, "Union fields are not set.")
+
+ other_union = SpecNamespace::My_union.new(:integer32, 1)
+ expect { other_union.validate }.not_to raise_error
+ end
+
+ it "should validate an enum field properly" do
+ union = SpecNamespace::TestUnion.new(:enum_field, 3)
+ expect(union.get_set_field).to eq(:enum_field)
+ expect { union.validate }.to raise_error(Thrift::ProtocolException, "Invalid value of field enum_field!")
+
+ other_union = SpecNamespace::TestUnion.new(:enum_field, 1)
+ expect { other_union.validate }.not_to raise_error
+ end
+
+ it "should properly serialize and match structs with a union" do
+ union = SpecNamespace::My_union.new(:integer32, 26)
+ swu = SpecNamespace::Struct_with_union.new(:fun_union => union)
+
+ trans = Thrift::MemoryBufferTransport.new
+ proto = Thrift::CompactProtocol.new(trans)
+
+ swu.write(proto)
+
+ other_union = SpecNamespace::My_union.new(:some_characters, "hello there")
+ swu2 = SpecNamespace::Struct_with_union.new(:fun_union => other_union)
+
+ expect(swu2).not_to eq(swu)
+
+ swu2.read(proto)
+ expect(swu2).to eq(swu)
+ end
+
+ it "should support old style constructor" do
+ union = SpecNamespace::My_union.new(:integer32 => 26)
+ expect(union.get_set_field).to eq(:integer32)
+ expect(union.get_value).to eq(26)
+ end
+
+ it "should not throw an error when inspected and unset" do
+ expect{SpecNamespace::TestUnion.new().inspect}.not_to raise_error
+ end
+
+ it "should print enum value name when inspected" do
+ expect(SpecNamespace::My_union.new(:some_enum => SpecNamespace::SomeEnum::ONE).inspect).to eq("<SpecNamespace::My_union some_enum: ONE (0)>")
+
+ expect(SpecNamespace::My_union.new(:my_map => {SpecNamespace::SomeEnum::ONE => [SpecNamespace::SomeEnum::TWO]}).inspect).to eq("<SpecNamespace::My_union my_map: {ONE (0): [TWO (1)]}>")
+ end
+
+ it "should offer field? methods" do
+ expect(SpecNamespace::My_union.new.some_enum?).to be_falsey
+ expect(SpecNamespace::My_union.new(:some_enum => SpecNamespace::SomeEnum::ONE).some_enum?).to be_truthy
+ expect(SpecNamespace::My_union.new(:im_true => false).im_true?).to be_truthy
+ expect(SpecNamespace::My_union.new(:im_true => true).im_true?).to be_truthy
+ end
+
+ it "should pretty print binary fields" do
+ expect(SpecNamespace::TestUnion.new(:binary_field => "\001\002\003").inspect).to eq("<SpecNamespace::TestUnion binary_field: 010203>")
+ end
+
+ it "should be comparable" do
+ relationships = [
+ [0, -1, -1, -1],
+ [1, 0, -1, -1],
+ [1, 1, 0, -1],
+ [1, 1, 1, 0]]
+
+ objs = [
+ SpecNamespace::TestUnion.new(:string_field, "blah"),
+ SpecNamespace::TestUnion.new(:string_field, "blahblah"),
+ SpecNamespace::TestUnion.new(:i32_field, 1),
+ SpecNamespace::TestUnion.new()]
+
+ for y in 0..3
+ for x in 0..3
+ # puts "#{objs[y].inspect} <=> #{objs[x].inspect} should == #{relationships[y][x]}"
+ expect(objs[y] <=> objs[x]).to eq(relationships[y][x])
+ end
+ end
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/unix_socket_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/unix_socket_spec.rb
new file mode 100644
index 000000000..8623e95a0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/unix_socket_spec.rb
@@ -0,0 +1,116 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'spec_helper'
+require File.expand_path("#{File.dirname(__FILE__)}/socket_spec_shared")
+
+describe 'UNIXSocket' do
+
+ describe Thrift::UNIXSocket do
+ before(:each) do
+ @path = '/tmp/thrift_spec_socket'
+ @socket = Thrift::UNIXSocket.new(@path)
+ @handle = double("Handle", :closed? => false)
+ allow(@handle).to receive(:close)
+ allow(::UNIXSocket).to receive(:new).and_return(@handle)
+ end
+
+ it_should_behave_like "a socket"
+
+ it "should raise a TransportException when it cannot open a socket" do
+ expect(::UNIXSocket).to receive(:new).and_raise(StandardError)
+ expect { @socket.open }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::NOT_OPEN) }
+ end
+
+ it "should accept an optional timeout" do
+ allow(::UNIXSocket).to receive(:new)
+ expect(Thrift::UNIXSocket.new(@path, 5).timeout).to eq(5)
+ end
+
+ it "should provide a reasonable to_s" do
+ allow(::UNIXSocket).to receive(:new)
+ expect(Thrift::UNIXSocket.new(@path).to_s).to eq("domain(#{@path})")
+ end
+ end
+
+ describe Thrift::UNIXServerSocket do
+ before(:each) do
+ @path = '/tmp/thrift_spec_socket'
+ @socket = Thrift::UNIXServerSocket.new(@path)
+ end
+
+ it "should create a handle when calling listen" do
+ expect(UNIXServer).to receive(:new).with(@path)
+ @socket.listen
+ end
+
+ it "should create a Thrift::UNIXSocket to wrap accepted sockets" do
+ handle = double("UNIXServer")
+ expect(UNIXServer).to receive(:new).with(@path).and_return(handle)
+ @socket.listen
+ sock = double("sock")
+ expect(handle).to receive(:accept).and_return(sock)
+ trans = double("UNIXSocket")
+ expect(Thrift::UNIXSocket).to receive(:new).and_return(trans)
+ expect(trans).to receive(:handle=).with(sock)
+ expect(@socket.accept).to eq(trans)
+ end
+
+ it "should close the handle when closed" do
+ handle = double("UNIXServer", :closed? => false)
+ expect(UNIXServer).to receive(:new).with(@path).and_return(handle)
+ @socket.listen
+ expect(handle).to receive(:close)
+ allow(File).to receive(:delete)
+ @socket.close
+ end
+
+ it "should delete the socket when closed" do
+ handle = double("UNIXServer", :closed? => false)
+ expect(UNIXServer).to receive(:new).with(@path).and_return(handle)
+ @socket.listen
+ allow(handle).to receive(:close)
+ expect(File).to receive(:delete).with(@path)
+ @socket.close
+ end
+
+ it "should return nil when accepting if there is no handle" do
+ expect(@socket.accept).to be_nil
+ end
+
+ it "should return true for closed? when appropriate" do
+ handle = double("UNIXServer", :closed? => false)
+ allow(UNIXServer).to receive(:new).and_return(handle)
+ allow(File).to receive(:delete)
+ @socket.listen
+ expect(@socket).not_to be_closed
+ allow(handle).to receive(:close)
+ @socket.close
+ expect(@socket).to be_closed
+ @socket.listen
+ expect(@socket).not_to be_closed
+ allow(handle).to receive(:closed?).and_return(true)
+ expect(@socket).to be_closed
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@socket.to_s).to eq("domain(#{@path})")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/thrift.gemspec b/src/jaegertracing/thrift/lib/rb/thrift.gemspec
new file mode 100644
index 000000000..869e5d95d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/thrift.gemspec
@@ -0,0 +1,40 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+
+Gem::Specification.new do |s|
+ s.name = 'thrift'
+ s.version = '0.13.0'
+ s.authors = ['Apache Thrift Developers']
+ s.email = ['dev@thrift.apache.org']
+ s.homepage = 'http://thrift.apache.org'
+ s.summary = %q{Ruby bindings for Apache Thrift}
+ s.description = %q{Ruby bindings for the Apache Thrift RPC system}
+ s.license = 'Apache-2.0'
+ s.extensions = ['ext/extconf.rb']
+
+ s.has_rdoc = true
+ s.rdoc_options = %w[--line-numbers --inline-source --title Thrift --main README]
+
+ s.rubyforge_project = 'thrift'
+
+ dir = File.expand_path(File.dirname(__FILE__))
+
+ s.files = Dir.glob("{lib,spec}/**/*")
+ s.test_files = Dir.glob("{test,spec,benchmark}/**/*")
+ s.executables = Dir.glob("{bin}/**/*")
+
+ s.extra_rdoc_files = %w[README.md] + Dir.glob("{ext,lib}/**/*.{c,h,rb}")
+
+ s.require_paths = %w[lib ext]
+
+ s.add_development_dependency 'bundler', '~> 1.11'
+ s.add_development_dependency 'pry', '~> 0.11.3'
+ s.add_development_dependency 'pry-byebug', '~> 3.6'
+ s.add_development_dependency 'pry-stack_explorer', '~> 0.4.9.2'
+ s.add_development_dependency 'rack', '~> 2.0'
+ s.add_development_dependency 'rack-test', '~> 0.8.3'
+ s.add_development_dependency 'rake', '~> 12.3'
+ s.add_development_dependency 'rspec', '~> 3.7'
+ s.add_development_dependency 'thin', '~> 1.7'
+end
+
diff --git a/src/jaegertracing/thrift/lib/rs/Cargo.toml b/src/jaegertracing/thrift/lib/rs/Cargo.toml
new file mode 100644
index 000000000..69da0f399
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "thrift"
+description = "Rust bindings for the Apache Thrift RPC system"
+version = "0.13.0"
+license = "Apache-2.0"
+authors = ["Apache Thrift Developers <dev@thrift.apache.org>"]
+homepage = "http://thrift.apache.org"
+documentation = "https://thrift.apache.org"
+readme = "README.md"
+exclude = ["Makefile*", "test/**", "*.iml"]
+keywords = ["thrift"]
+
+[dependencies]
+ordered-float = "1.0"
+byteorder = "1.3"
+integer-encoding = "1.0"
+log = "0.4"
+threadpool = "1.7"
diff --git a/src/jaegertracing/thrift/lib/rs/Makefile.am b/src/jaegertracing/thrift/lib/rs/Makefile.am
new file mode 100644
index 000000000..0a34120a3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/Makefile.am
@@ -0,0 +1,46 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+SUBDIRS = .
+
+if WITH_TESTS
+SUBDIRS += test
+endif
+
+install:
+ @echo '##############################################################'
+ @echo '##############################################################'
+ @echo 'The Rust client library should be installed via a Cargo.toml dependency - please see /lib/rs/README.md'
+ @echo '##############################################################'
+ @echo '##############################################################'
+
+check-local:
+ $(CARGO) test
+
+all-local:
+ $(CARGO) build
+
+clean-local:
+ $(CARGO) clean
+ -$(RM) Cargo.lock
+
+EXTRA_DIST = \
+ src \
+ Cargo.toml \
+ README.md
diff --git a/src/jaegertracing/thrift/lib/rs/README.md b/src/jaegertracing/thrift/lib/rs/README.md
new file mode 100644
index 000000000..f518f4eb6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/README.md
@@ -0,0 +1,120 @@
+# Rust Thrift library
+
+## Overview
+
+This crate implements the components required to build a working Thrift server
+and client. It is divided into the following modules:
+
+ 1. errors
+ 2. protocol
+ 3. transport
+ 4. server
+ 5. autogen
+
+The modules are layered as shown. The `generated` layer is code generated by the
+Thrift compiler's Rust plugin. It uses the components defined in this crate to
+serialize and deserialize types and implement RPC. Users interact with these
+types and services by writing their own code on top.
+
+ ```text
+ +-----------+
+ | app dev |
+ +-----------+
+ | generated | <-> errors/results
+ +-----------+
+ | protocol |
+ +-----------+
+ | transport |
+ +-----------+
+ ```
+
+## Using this crate
+
+Add `thrift = "x.y.z"` to your `Cargo.toml`, where `x.y.z` is the version of the
+Thrift compiler you're using.
+
+## API Documentation
+
+Full [Rustdoc](https://docs.rs/thrift/)
+
+## Compatibility
+
+The Rust library and auto-generated code targets Rust versions 1.28+.
+It does not currently use any Rust 2018 features.
+
+### Breaking Changes
+
+Breaking changes are minimized. When they are made they will be outlined below with transition guidelines.
+
+##### Thrift 0.13.0
+
+* **[THRIFT-4536]** - Use TryFrom from std, required rust 1.34.0 or higher
+
+ Previously TryFrom was from try_from crate, it is now from the std library,
+ but this functionality is only available in rust 1.34.0. Additionally,
+ ordered-float is now re-exported under the thrift module to reduce
+ possible dependency mismatches.
+
+##### Thrift 0.12.0
+
+* **[THRIFT-4529]** - Rust enum variants are now camel-cased instead of uppercased to conform to Rust naming conventions
+
+ Previously, enum variants were uppercased in the auto-generated code.
+ For example, the following thrift enum:
+
+ ```thrift
+ // THRIFT
+ enum Operation {
+ ADD,
+ SUBTRACT,
+ MULTIPLY,
+ DIVIDE,
+ }
+ ```
+
+ used to generate:
+
+ ```rust
+ // OLD AUTO-GENERATED RUST
+ pub enum Operation {
+ ADD,
+ SUBTRACT,
+ MULTIPLY,
+ DIVIDE,
+ }
+ ```
+ It *now* generates:
+ ```rust
+ // NEW AUTO-GENERATED RUST
+ pub enum Operation {
+ Add,
+ Subtract,
+ Multiply,
+ Divide,
+ }
+ ```
+
+ You will have to change all enum variants in your code to use camel-cased names.
+ This should be a search and replace.
+
+## Contributing
+
+Bug reports and PRs are always welcome! Please see the
+[Thrift website](https://thrift.apache.org/) for more details.
+
+Thrift Rust support requires code in several directories:
+
+* `compiler/cpp/src/thrift/generate/t_rs_generator.cc`: binding code generator
+* `lib/rs`: runtime library
+* `lib/rs/test`: supplemental tests
+* `tutorial/rs`: tutorial client and server
+* `test/rs`: cross-language test client and server
+
+All library code, test code and auto-generated code compiles and passes clippy
+without warnings. All new code must do the same! When making changes ensure that:
+
+* `rustc` does does output any warnings
+* `clippy` with default settings does not output any warnings (includes auto-generated code)
+* `cargo test` is successful
+* `make precross` and `make check` are successful
+* `tutorial/bin/tutorial_client` and `tutorial/bin/tutorial_server` communicate
diff --git a/src/jaegertracing/thrift/lib/rs/RELEASING.md b/src/jaegertracing/thrift/lib/rs/RELEASING.md
new file mode 100644
index 000000000..073d7a02a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/RELEASING.md
@@ -0,0 +1,57 @@
+# Publishing the thrift crate
+
+Publishing the Rust thrift crate is straightforward, and involves two major steps:
+
+1. Setting up your [crates.io](https://www.crates.io) account _(one-time)_
+
+2. Packaging/publishing the Rust thrift crate itself
+
+## Set up your crates.io account (one-time)
+
+1. Go to [crates.io](https://www.crates.io) and click the `Log In` button at the top right.
+
+ Log in **as the Github user with write permissions to the thrift repo!**
+
+2. Click your user icon button at the top right and select `Account Settings`.
+
+3. Click `New Token` next to `API Access`.
+
+ This generates a new API key that cargo uses to publish packages to crates.io.
+ Store this API key somewhere safe. If you will only use this Github account to
+ publish crates to crates.io you can follow the instructions to save the
+ generated key to `~/.cargo/credentials`.
+
+## Package and Publish
+
+You can use the automated script or run the release steps manually.
+
+**Important**: `cargo` expects that version numbers follow the semantic versioning format.
+This means that `THRIFT_RELEASE_VERSION` must have a major, minor and patch number, i.e., must
+be in the form `#.##.##`.
+
+#### Automated
+
+Run `./release.sh [THRIFT_RELEASE_VERSION]`.
+
+_Requires you to have stored your credentials in `~/.cargo/credentials`._
+
+#### Manual
+
+1. Edit `Cargo.toml` and update the `version = 1.0` key to `version = [THRIFT_RELEASE_VERSION]`
+
+2. `git add Cargo.toml`
+
+3. `git commit -m "Update thrift crate version to [THRIFT_RELEASE_VERSION]" -m "Client: rs"`
+
+4. `cargo login`
+
+ _(not required if you have stored your credentials in `~/.cargo/credentials`)_
+
+5. `cargo clean`
+
+6. `cargo package`
+
+ This step fails if there are any uncommitted or ignored files. Do **not** use the `--allow-dirty`
+ flag! Instead, add the highlighted files as entries in the `Cargo.toml` `exclude` key.
+
+7. `cargo publish`
diff --git a/src/jaegertracing/thrift/lib/rs/release.sh b/src/jaegertracing/thrift/lib/rs/release.sh
new file mode 100755
index 000000000..c4e5b4892
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/release.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+set -o errexit
+set -o pipefail
+set -o nounset
+
+if ! [[ $# -eq 1 && $1 =~ ^[0-9](\.[0-9][0-9]*){2}$ ]]; then
+ (>&2 echo "Usage: ./publish-crate.sh [THRIFT_RELEASE_VERSION] ")
+ (>&2 echo " THRIFT_RELEASE_VERSION is in semantic versioning format, i.e. #.##.##")
+ exit 1
+fi
+
+THRIFT_RELEASE_VERSION=${1:-}
+
+echo "Updating Cargo.toml to ${THRIFT_RELEASE_VERSION}"
+sed -i.old -e "s/^version = .*$/version = \"${THRIFT_RELEASE_VERSION}\"/g" Cargo.toml
+rm Cargo.toml.old
+
+echo "Committing updated Cargo.toml"
+git add Cargo.toml
+git commit -m "Update thrift crate version to ${THRIFT_RELEASE_VERSION}" -m "Client: rs"
+
+echo "Packaging and releasing rust thrift crate with version ${THRIFT_RELEASE_VERSION}"
+cargo clean
+cargo package
+cargo publish
diff --git a/src/jaegertracing/thrift/lib/rs/src/autogen.rs b/src/jaegertracing/thrift/lib/rs/src/autogen.rs
new file mode 100644
index 000000000..6806a08ce
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/src/autogen.rs
@@ -0,0 +1,45 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Thrift compiler auto-generated support.
+//!
+//!
+//! Types and functions used internally by the Thrift compiler's Rust plugin
+//! to implement required functionality. Users should never have to use code
+//! in this module directly.
+
+use protocol::{TInputProtocol, TOutputProtocol};
+
+/// Specifies the minimum functionality an auto-generated client should provide
+/// to communicate with a Thrift server.
+pub trait TThriftClient {
+ /// Returns the input protocol used to read serialized Thrift messages
+ /// from the Thrift server.
+ fn i_prot_mut(&mut self) -> &mut dyn TInputProtocol;
+ /// Returns the output protocol used to write serialized Thrift messages
+ /// to the Thrift server.
+ fn o_prot_mut(&mut self) -> &mut dyn TOutputProtocol;
+ /// Returns the sequence number of the last message written to the Thrift
+ /// server. Returns `0` if no messages have been written. Sequence
+ /// numbers should *never* be negative, and this method returns an `i32`
+ /// simply because the Thrift protocol encodes sequence numbers as `i32` on
+ /// the wire.
+ fn sequence_number(&self) -> i32; // FIXME: consider returning a u32
+ /// Increments the sequence number, indicating that a message with that
+ /// number has been sent to the Thrift server.
+ fn increment_sequence_number(&mut self) -> i32;
+}
diff --git a/src/jaegertracing/thrift/lib/rs/src/errors.rs b/src/jaegertracing/thrift/lib/rs/src/errors.rs
new file mode 100644
index 000000000..68cdc9c17
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/src/errors.rs
@@ -0,0 +1,667 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use std::convert::{From, Into};
+use std::error::Error as StdError;
+use std::fmt::{Debug, Display, Formatter};
+use std::{error, fmt, io, string};
+use std::convert::TryFrom;
+
+use protocol::{TFieldIdentifier, TInputProtocol, TOutputProtocol, TStructIdentifier, TType};
+
+// FIXME: should all my error structs impl error::Error as well?
+// FIXME: should all fields in TransportError, ProtocolError and ApplicationError be optional?
+
+/// Error type returned by all runtime library functions.
+///
+/// `thrift::Error` is used throughout this crate as well as in auto-generated
+/// Rust code. It consists of four variants defined by convention across Thrift
+/// implementations:
+///
+/// 1. `Transport`: errors encountered while operating on I/O channels
+/// 2. `Protocol`: errors encountered during runtime-library processing
+/// 3. `Application`: errors encountered within auto-generated code
+/// 4. `User`: IDL-defined exception structs
+///
+/// The `Application` variant also functions as a catch-all: all handler errors
+/// are automatically turned into application errors.
+///
+/// All error variants except `Error::User` take an eponymous struct with two
+/// required fields:
+///
+/// 1. `kind`: variant-specific enum identifying the error sub-type
+/// 2. `message`: human-readable error info string
+///
+/// `kind` is defined by convention while `message` is freeform. If none of the
+/// enumerated kinds are suitable use `Unknown`.
+///
+/// To simplify error creation convenience constructors are defined for all
+/// variants, and conversions from their structs (`thrift::TransportError`,
+/// `thrift::ProtocolError` and `thrift::ApplicationError` into `thrift::Error`.
+///
+/// # Examples
+///
+/// Create a `TransportError`.
+///
+/// ```
+/// use thrift::{TransportError, TransportErrorKind};
+///
+/// // explicit
+/// let err0: thrift::Result<()> = Err(
+/// thrift::Error::Transport(
+/// TransportError {
+/// kind: TransportErrorKind::TimedOut,
+/// message: format!("connection to server timed out")
+/// }
+/// )
+/// );
+///
+/// // use conversion
+/// let err1: thrift::Result<()> = Err(
+/// thrift::Error::from(
+/// TransportError {
+/// kind: TransportErrorKind::TimedOut,
+/// message: format!("connection to server timed out")
+/// }
+/// )
+/// );
+///
+/// // use struct constructor
+/// let err2: thrift::Result<()> = Err(
+/// thrift::Error::Transport(
+/// TransportError::new(
+/// TransportErrorKind::TimedOut,
+/// "connection to server timed out"
+/// )
+/// )
+/// );
+///
+///
+/// // use error variant constructor
+/// let err3: thrift::Result<()> = Err(
+/// thrift::new_transport_error(
+/// TransportErrorKind::TimedOut,
+/// "connection to server timed out"
+/// )
+/// );
+/// ```
+///
+/// Create an error from a string.
+///
+/// ```
+/// use thrift::{ApplicationError, ApplicationErrorKind};
+///
+/// // we just use `From::from` to convert a `String` into a `thrift::Error`
+/// let err0: thrift::Result<()> = Err(
+/// thrift::Error::from("This is an error")
+/// );
+///
+/// // err0 is equivalent to...
+/// let err1: thrift::Result<()> = Err(
+/// thrift::Error::Application(
+/// ApplicationError {
+/// kind: ApplicationErrorKind::Unknown,
+/// message: format!("This is an error")
+/// }
+/// )
+/// );
+/// ```
+///
+/// Return an IDL-defined exception.
+///
+/// ```text
+/// // Thrift IDL exception definition.
+/// exception Xception {
+/// 1: i32 errorCode,
+/// 2: string message
+/// }
+/// ```
+///
+/// ```
+/// use std::error::Error;
+/// use std::fmt;
+/// use std::fmt::{Display, Formatter};
+///
+/// // auto-generated by the Thrift compiler
+/// #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
+/// pub struct Xception {
+/// pub error_code: Option<i32>,
+/// pub message: Option<String>,
+/// }
+///
+/// // auto-generated by the Thrift compiler
+/// impl Error for Xception {
+/// fn description(&self) -> &str {
+/// "remote service threw Xception"
+/// }
+/// }
+///
+/// // auto-generated by the Thrift compiler
+/// impl From<Xception> for thrift::Error {
+/// fn from(e: Xception) -> Self {
+/// thrift::Error::User(Box::new(e))
+/// }
+/// }
+///
+/// // auto-generated by the Thrift compiler
+/// impl Display for Xception {
+/// fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+/// self.description().fmt(f)
+/// }
+/// }
+///
+/// // in user code...
+/// let err: thrift::Result<()> = Err(
+/// thrift::Error::from(Xception { error_code: Some(1), message: None })
+/// );
+/// ```
+pub enum Error {
+ /// Errors encountered while operating on I/O channels.
+ ///
+ /// These include *connection closed* and *bind failure*.
+ Transport(TransportError),
+ /// Errors encountered during runtime-library processing.
+ ///
+ /// These include *message too large* and *unsupported protocol version*.
+ Protocol(ProtocolError),
+ /// Errors encountered within auto-generated code, or when incoming
+ /// or outgoing messages violate the Thrift spec.
+ ///
+ /// These include *out-of-order messages* and *missing required struct
+ /// fields*.
+ ///
+ /// This variant also functions as a catch-all: errors from handler
+ /// functions are automatically returned as an `ApplicationError`.
+ Application(ApplicationError),
+ /// IDL-defined exception structs.
+ User(Box<dyn error::Error + Sync + Send>),
+}
+
+impl Error {
+ /// Create an `ApplicationError` from its wire representation.
+ ///
+ /// Application code **should never** call this method directly.
+ pub fn read_application_error_from_in_protocol(
+ i: &mut dyn TInputProtocol,
+ ) -> ::Result<ApplicationError> {
+ let mut message = "general remote error".to_owned();
+ let mut kind = ApplicationErrorKind::Unknown;
+
+ i.read_struct_begin()?;
+
+ loop {
+ let field_ident = i.read_field_begin()?;
+
+ if field_ident.field_type == TType::Stop {
+ break;
+ }
+
+ let id = field_ident
+ .id
+ .expect("sender should always specify id for non-STOP field");
+
+ match id {
+ 1 => {
+ let remote_message = i.read_string()?;
+ i.read_field_end()?;
+ message = remote_message;
+ }
+ 2 => {
+ let remote_type_as_int = i.read_i32()?;
+ let remote_kind: ApplicationErrorKind = TryFrom::try_from(remote_type_as_int)
+ .unwrap_or(ApplicationErrorKind::Unknown);
+ i.read_field_end()?;
+ kind = remote_kind;
+ }
+ _ => {
+ i.skip(field_ident.field_type)?;
+ }
+ }
+ }
+
+ i.read_struct_end()?;
+
+ Ok(ApplicationError {
+ kind: kind,
+ message: message,
+ })
+ }
+
+ /// Convert an `ApplicationError` into its wire representation and write
+ /// it to the remote.
+ ///
+ /// Application code **should never** call this method directly.
+ pub fn write_application_error_to_out_protocol(
+ e: &ApplicationError,
+ o: &mut dyn TOutputProtocol,
+ ) -> ::Result<()> {
+ o.write_struct_begin(&TStructIdentifier {
+ name: "TApplicationException".to_owned(),
+ })?;
+
+ let message_field = TFieldIdentifier::new("message", TType::String, 1);
+ let type_field = TFieldIdentifier::new("type", TType::I32, 2);
+
+ o.write_field_begin(&message_field)?;
+ o.write_string(&e.message)?;
+ o.write_field_end()?;
+
+ o.write_field_begin(&type_field)?;
+ o.write_i32(e.kind as i32)?;
+ o.write_field_end()?;
+
+ o.write_field_stop()?;
+ o.write_struct_end()?;
+
+ o.flush()
+ }
+}
+
+impl error::Error for Error {
+ fn description(&self) -> &str {
+ match *self {
+ Error::Transport(ref e) => TransportError::description(e),
+ Error::Protocol(ref e) => ProtocolError::description(e),
+ Error::Application(ref e) => ApplicationError::description(e),
+ Error::User(ref e) => e.description(),
+ }
+ }
+}
+
+impl Debug for Error {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ match *self {
+ Error::Transport(ref e) => Debug::fmt(e, f),
+ Error::Protocol(ref e) => Debug::fmt(e, f),
+ Error::Application(ref e) => Debug::fmt(e, f),
+ Error::User(ref e) => Debug::fmt(e, f),
+ }
+ }
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ match *self {
+ Error::Transport(ref e) => Display::fmt(e, f),
+ Error::Protocol(ref e) => Display::fmt(e, f),
+ Error::Application(ref e) => Display::fmt(e, f),
+ Error::User(ref e) => Display::fmt(e, f),
+ }
+ }
+}
+
+impl From<String> for Error {
+ fn from(s: String) -> Self {
+ Error::Application(ApplicationError {
+ kind: ApplicationErrorKind::Unknown,
+ message: s,
+ })
+ }
+}
+
+impl<'a> From<&'a str> for Error {
+ fn from(s: &'a str) -> Self {
+ Error::Application(ApplicationError {
+ kind: ApplicationErrorKind::Unknown,
+ message: String::from(s),
+ })
+ }
+}
+
+impl From<TransportError> for Error {
+ fn from(e: TransportError) -> Self {
+ Error::Transport(e)
+ }
+}
+
+impl From<ProtocolError> for Error {
+ fn from(e: ProtocolError) -> Self {
+ Error::Protocol(e)
+ }
+}
+
+impl From<ApplicationError> for Error {
+ fn from(e: ApplicationError) -> Self {
+ Error::Application(e)
+ }
+}
+
+/// Create a new `Error` instance of type `Transport` that wraps a
+/// `TransportError`.
+pub fn new_transport_error<S: Into<String>>(kind: TransportErrorKind, message: S) -> Error {
+ Error::Transport(TransportError::new(kind, message))
+}
+
+/// Information about I/O errors.
+#[derive(Debug, Eq, PartialEq)]
+pub struct TransportError {
+ /// I/O error variant.
+ ///
+ /// If a specific `TransportErrorKind` does not apply use
+ /// `TransportErrorKind::Unknown`.
+ pub kind: TransportErrorKind,
+ /// Human-readable error message.
+ pub message: String,
+}
+
+impl TransportError {
+ /// Create a new `TransportError`.
+ pub fn new<S: Into<String>>(kind: TransportErrorKind, message: S) -> TransportError {
+ TransportError {
+ kind: kind,
+ message: message.into(),
+ }
+ }
+}
+
+/// I/O error categories.
+///
+/// This list may grow, and it is not recommended to match against it.
+#[derive(Clone, Copy, Eq, Debug, PartialEq)]
+pub enum TransportErrorKind {
+ /// Catch-all I/O error.
+ Unknown = 0,
+ /// An I/O operation was attempted when the transport channel was not open.
+ NotOpen = 1,
+ /// The transport channel cannot be opened because it was opened previously.
+ AlreadyOpen = 2,
+ /// An I/O operation timed out.
+ TimedOut = 3,
+ /// A read could not complete because no bytes were available.
+ EndOfFile = 4,
+ /// An invalid (buffer/message) size was requested or received.
+ NegativeSize = 5,
+ /// Too large a buffer or message size was requested or received.
+ SizeLimit = 6,
+}
+
+impl TransportError {
+ fn description(&self) -> &str {
+ match self.kind {
+ TransportErrorKind::Unknown => "transport error",
+ TransportErrorKind::NotOpen => "not open",
+ TransportErrorKind::AlreadyOpen => "already open",
+ TransportErrorKind::TimedOut => "timed out",
+ TransportErrorKind::EndOfFile => "end of file",
+ TransportErrorKind::NegativeSize => "negative size message",
+ TransportErrorKind::SizeLimit => "message too long",
+ }
+ }
+}
+
+impl Display for TransportError {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "{}", self.description())
+ }
+}
+
+impl TryFrom<i32> for TransportErrorKind {
+ type Error = Error;
+ fn try_from(from: i32) -> Result<Self, Self::Error> {
+ match from {
+ 0 => Ok(TransportErrorKind::Unknown),
+ 1 => Ok(TransportErrorKind::NotOpen),
+ 2 => Ok(TransportErrorKind::AlreadyOpen),
+ 3 => Ok(TransportErrorKind::TimedOut),
+ 4 => Ok(TransportErrorKind::EndOfFile),
+ 5 => Ok(TransportErrorKind::NegativeSize),
+ 6 => Ok(TransportErrorKind::SizeLimit),
+ _ => Err(Error::Protocol(ProtocolError {
+ kind: ProtocolErrorKind::Unknown,
+ message: format!("cannot convert {} to TransportErrorKind", from),
+ })),
+ }
+ }
+}
+
+impl From<io::Error> for Error {
+ fn from(err: io::Error) -> Self {
+ match err.kind() {
+ io::ErrorKind::ConnectionReset
+ | io::ErrorKind::ConnectionRefused
+ | io::ErrorKind::NotConnected => Error::Transport(TransportError {
+ kind: TransportErrorKind::NotOpen,
+ message: err.description().to_owned(),
+ }),
+ io::ErrorKind::AlreadyExists => Error::Transport(TransportError {
+ kind: TransportErrorKind::AlreadyOpen,
+ message: err.description().to_owned(),
+ }),
+ io::ErrorKind::TimedOut => Error::Transport(TransportError {
+ kind: TransportErrorKind::TimedOut,
+ message: err.description().to_owned(),
+ }),
+ io::ErrorKind::UnexpectedEof => Error::Transport(TransportError {
+ kind: TransportErrorKind::EndOfFile,
+ message: err.description().to_owned(),
+ }),
+ _ => {
+ Error::Transport(TransportError {
+ kind: TransportErrorKind::Unknown,
+ message: err.description().to_owned(), // FIXME: use io error's debug string
+ })
+ }
+ }
+ }
+}
+
+impl From<string::FromUtf8Error> for Error {
+ fn from(err: string::FromUtf8Error) -> Self {
+ Error::Protocol(ProtocolError {
+ kind: ProtocolErrorKind::InvalidData,
+ message: err.description().to_owned(), // FIXME: use fmt::Error's debug string
+ })
+ }
+}
+
+/// Create a new `Error` instance of type `Protocol` that wraps a
+/// `ProtocolError`.
+pub fn new_protocol_error<S: Into<String>>(kind: ProtocolErrorKind, message: S) -> Error {
+ Error::Protocol(ProtocolError::new(kind, message))
+}
+
+/// Information about errors that occur in the runtime library.
+#[derive(Debug, Eq, PartialEq)]
+pub struct ProtocolError {
+ /// Protocol error variant.
+ ///
+ /// If a specific `ProtocolErrorKind` does not apply use
+ /// `ProtocolErrorKind::Unknown`.
+ pub kind: ProtocolErrorKind,
+ /// Human-readable error message.
+ pub message: String,
+}
+
+impl ProtocolError {
+ /// Create a new `ProtocolError`.
+ pub fn new<S: Into<String>>(kind: ProtocolErrorKind, message: S) -> ProtocolError {
+ ProtocolError {
+ kind: kind,
+ message: message.into(),
+ }
+ }
+}
+
+/// Runtime library error categories.
+///
+/// This list may grow, and it is not recommended to match against it.
+#[derive(Clone, Copy, Eq, Debug, PartialEq)]
+pub enum ProtocolErrorKind {
+ /// Catch-all runtime-library error.
+ Unknown = 0,
+ /// An invalid argument was supplied to a library function, or invalid data
+ /// was received from a Thrift endpoint.
+ InvalidData = 1,
+ /// An invalid size was received in an encoded field.
+ NegativeSize = 2,
+ /// Thrift message or field was too long.
+ SizeLimit = 3,
+ /// Unsupported or unknown Thrift protocol version.
+ BadVersion = 4,
+ /// Unsupported Thrift protocol, server or field type.
+ NotImplemented = 5,
+ /// Reached the maximum nested depth to which an encoded Thrift field could
+ /// be skipped.
+ DepthLimit = 6,
+}
+
+impl ProtocolError {
+ fn description(&self) -> &str {
+ match self.kind {
+ ProtocolErrorKind::Unknown => "protocol error",
+ ProtocolErrorKind::InvalidData => "bad data",
+ ProtocolErrorKind::NegativeSize => "negative message size",
+ ProtocolErrorKind::SizeLimit => "message too long",
+ ProtocolErrorKind::BadVersion => "invalid thrift version",
+ ProtocolErrorKind::NotImplemented => "not implemented",
+ ProtocolErrorKind::DepthLimit => "maximum skip depth reached",
+ }
+ }
+}
+
+impl Display for ProtocolError {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "{}", self.description())
+ }
+}
+
+impl TryFrom<i32> for ProtocolErrorKind {
+ type Error = Error;
+ fn try_from(from: i32) -> Result<Self, Self::Error> {
+ match from {
+ 0 => Ok(ProtocolErrorKind::Unknown),
+ 1 => Ok(ProtocolErrorKind::InvalidData),
+ 2 => Ok(ProtocolErrorKind::NegativeSize),
+ 3 => Ok(ProtocolErrorKind::SizeLimit),
+ 4 => Ok(ProtocolErrorKind::BadVersion),
+ 5 => Ok(ProtocolErrorKind::NotImplemented),
+ 6 => Ok(ProtocolErrorKind::DepthLimit),
+ _ => Err(Error::Protocol(ProtocolError {
+ kind: ProtocolErrorKind::Unknown,
+ message: format!("cannot convert {} to ProtocolErrorKind", from),
+ })),
+ }
+ }
+}
+
+/// Create a new `Error` instance of type `Application` that wraps an
+/// `ApplicationError`.
+pub fn new_application_error<S: Into<String>>(kind: ApplicationErrorKind, message: S) -> Error {
+ Error::Application(ApplicationError::new(kind, message))
+}
+
+/// Information about errors in auto-generated code or in user-implemented
+/// service handlers.
+#[derive(Debug, Eq, PartialEq)]
+pub struct ApplicationError {
+ /// Application error variant.
+ ///
+ /// If a specific `ApplicationErrorKind` does not apply use
+ /// `ApplicationErrorKind::Unknown`.
+ pub kind: ApplicationErrorKind,
+ /// Human-readable error message.
+ pub message: String,
+}
+
+impl ApplicationError {
+ /// Create a new `ApplicationError`.
+ pub fn new<S: Into<String>>(kind: ApplicationErrorKind, message: S) -> ApplicationError {
+ ApplicationError {
+ kind: kind,
+ message: message.into(),
+ }
+ }
+}
+
+/// Auto-generated or user-implemented code error categories.
+///
+/// This list may grow, and it is not recommended to match against it.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum ApplicationErrorKind {
+ /// Catch-all application error.
+ Unknown = 0,
+ /// Made service call to an unknown service method.
+ UnknownMethod = 1,
+ /// Received an unknown Thrift message type. That is, not one of the
+ /// `thrift::protocol::TMessageType` variants.
+ InvalidMessageType = 2,
+ /// Method name in a service reply does not match the name of the
+ /// receiving service method.
+ WrongMethodName = 3,
+ /// Received an out-of-order Thrift message.
+ BadSequenceId = 4,
+ /// Service reply is missing required fields.
+ MissingResult = 5,
+ /// Auto-generated code failed unexpectedly.
+ InternalError = 6,
+ /// Thrift protocol error. When possible use `Error::ProtocolError` with a
+ /// specific `ProtocolErrorKind` instead.
+ ProtocolError = 7,
+ /// *Unknown*. Included only for compatibility with existing Thrift implementations.
+ InvalidTransform = 8, // ??
+ /// Thrift endpoint requested, or is using, an unsupported encoding.
+ InvalidProtocol = 9, // ??
+ /// Thrift endpoint requested, or is using, an unsupported auto-generated client type.
+ UnsupportedClientType = 10, // ??
+}
+
+impl ApplicationError {
+ fn description(&self) -> &str {
+ match self.kind {
+ ApplicationErrorKind::Unknown => "service error",
+ ApplicationErrorKind::UnknownMethod => "unknown service method",
+ ApplicationErrorKind::InvalidMessageType => "wrong message type received",
+ ApplicationErrorKind::WrongMethodName => "unknown method reply received",
+ ApplicationErrorKind::BadSequenceId => "out of order sequence id",
+ ApplicationErrorKind::MissingResult => "missing method result",
+ ApplicationErrorKind::InternalError => "remote service threw exception",
+ ApplicationErrorKind::ProtocolError => "protocol error",
+ ApplicationErrorKind::InvalidTransform => "invalid transform",
+ ApplicationErrorKind::InvalidProtocol => "invalid protocol requested",
+ ApplicationErrorKind::UnsupportedClientType => "unsupported protocol client",
+ }
+ }
+}
+
+impl Display for ApplicationError {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "{}", self.description())
+ }
+}
+
+impl TryFrom<i32> for ApplicationErrorKind {
+ type Error = Error;
+ fn try_from(from: i32) -> Result<Self, Self::Error> {
+ match from {
+ 0 => Ok(ApplicationErrorKind::Unknown),
+ 1 => Ok(ApplicationErrorKind::UnknownMethod),
+ 2 => Ok(ApplicationErrorKind::InvalidMessageType),
+ 3 => Ok(ApplicationErrorKind::WrongMethodName),
+ 4 => Ok(ApplicationErrorKind::BadSequenceId),
+ 5 => Ok(ApplicationErrorKind::MissingResult),
+ 6 => Ok(ApplicationErrorKind::InternalError),
+ 7 => Ok(ApplicationErrorKind::ProtocolError),
+ 8 => Ok(ApplicationErrorKind::InvalidTransform),
+ 9 => Ok(ApplicationErrorKind::InvalidProtocol),
+ 10 => Ok(ApplicationErrorKind::UnsupportedClientType),
+ _ => Err(Error::Application(ApplicationError {
+ kind: ApplicationErrorKind::Unknown,
+ message: format!("cannot convert {} to ApplicationErrorKind", from),
+ })),
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/rs/src/lib.rs b/src/jaegertracing/thrift/lib/rs/src/lib.rs
new file mode 100644
index 000000000..cdd60f0a9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/src/lib.rs
@@ -0,0 +1,91 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Rust runtime library for the Apache Thrift RPC system.
+//!
+//! This crate implements the components required to build a working
+//! Thrift server and client. It is divided into the following modules:
+//!
+//! 1. errors
+//! 2. protocol
+//! 3. transport
+//! 4. server
+//! 5. autogen
+//!
+//! The modules are layered as shown in the diagram below. The `autogen'd`
+//! layer is generated by the Thrift compiler's Rust plugin. It uses the
+//! types and functions defined in this crate to serialize and deserialize
+//! messages and implement RPC. Users interact with these types and services
+//! by writing their own code that uses the auto-generated clients and
+//! servers.
+//!
+//! ```text
+//! +-----------+
+//! | user app |
+//! +-----------+
+//! | autogen'd | (uses errors, autogen)
+//! +-----------+
+//! | protocol |
+//! +-----------+
+//! | transport |
+//! +-----------+
+//! ```
+
+#![crate_type = "lib"]
+#![doc(test(attr(allow(unused_variables), deny(warnings))))]
+#![deny(bare_trait_objects)]
+
+extern crate byteorder;
+extern crate ordered_float;
+extern crate integer_encoding;
+extern crate threadpool;
+
+#[macro_use]
+extern crate log;
+
+// NOTE: this macro has to be defined before any modules. See:
+// https://danielkeep.github.io/quick-intro-to-macros.html#some-more-gotchas
+
+/// Assert that an expression returning a `Result` is a success. If it is,
+/// return the value contained in the result, i.e. `expr.unwrap()`.
+#[cfg(test)]
+macro_rules! assert_success {
+ ($e: expr) => {{
+ let res = $e;
+ assert!(res.is_ok());
+ res.unwrap()
+ }};
+}
+
+pub mod protocol;
+pub mod server;
+pub mod transport;
+
+mod errors;
+pub use errors::*;
+
+mod autogen;
+pub use autogen::*;
+
+/// Result type returned by all runtime library functions.
+///
+/// As is convention this is a typedef of `std::result::Result`
+/// with `E` defined as the `thrift::Error` type.
+pub type Result<T> = std::result::Result<T, self::Error>;
+
+// Re-export ordered-float, since it is used by the generator
+pub use ordered_float::OrderedFloat as OrderedFloat; \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/rs/src/protocol/binary.rs b/src/jaegertracing/thrift/lib/rs/src/protocol/binary.rs
new file mode 100644
index 000000000..2069cf9fd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/src/protocol/binary.rs
@@ -0,0 +1,956 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt};
+use std::convert::{From, TryFrom};
+
+use super::{
+ TFieldIdentifier, TInputProtocol, TInputProtocolFactory, TListIdentifier, TMapIdentifier,
+ TMessageIdentifier, TMessageType,
+};
+use super::{TOutputProtocol, TOutputProtocolFactory, TSetIdentifier, TStructIdentifier, TType};
+use transport::{TReadTransport, TWriteTransport};
+use {ProtocolError, ProtocolErrorKind};
+
+const BINARY_PROTOCOL_VERSION_1: u32 = 0x80010000;
+
+/// Read messages encoded in the Thrift simple binary encoding.
+///
+/// There are two available modes: `strict` and `non-strict`, where the
+/// `non-strict` version does not check for the protocol version in the
+/// received message header.
+///
+/// # Examples
+///
+/// Create and use a `TBinaryInputProtocol`.
+///
+/// ```no_run
+/// use thrift::protocol::{TBinaryInputProtocol, TInputProtocol};
+/// use thrift::transport::TTcpChannel;
+///
+/// let mut channel = TTcpChannel::new();
+/// channel.open("localhost:9090").unwrap();
+///
+/// let mut protocol = TBinaryInputProtocol::new(channel, true);
+///
+/// let recvd_bool = protocol.read_bool().unwrap();
+/// let recvd_string = protocol.read_string().unwrap();
+/// ```
+#[derive(Debug)]
+pub struct TBinaryInputProtocol<T>
+where
+ T: TReadTransport,
+{
+ strict: bool,
+ pub transport: T, // FIXME: shouldn't be public
+}
+
+impl<'a, T> TBinaryInputProtocol<T>
+where
+ T: TReadTransport,
+{
+ /// Create a `TBinaryInputProtocol` that reads bytes from `transport`.
+ ///
+ /// Set `strict` to `true` if all incoming messages contain the protocol
+ /// version number in the protocol header.
+ pub fn new(transport: T, strict: bool) -> TBinaryInputProtocol<T> {
+ TBinaryInputProtocol {
+ strict: strict,
+ transport: transport,
+ }
+ }
+}
+
+impl<T> TInputProtocol for TBinaryInputProtocol<T>
+where
+ T: TReadTransport,
+{
+ #[cfg_attr(feature = "cargo-clippy", allow(collapsible_if))]
+ fn read_message_begin(&mut self) -> ::Result<TMessageIdentifier> {
+ let mut first_bytes = vec![0; 4];
+ self.transport.read_exact(&mut first_bytes[..])?;
+
+ // the thrift version header is intentionally negative
+ // so the first check we'll do is see if the sign bit is set
+ // and if so - assume it's the protocol-version header
+ if first_bytes[0] >= 8 {
+ // apparently we got a protocol-version header - check
+ // it, and if it matches, read the rest of the fields
+ if first_bytes[0..2] != [0x80, 0x01] {
+ Err(::Error::Protocol(ProtocolError {
+ kind: ProtocolErrorKind::BadVersion,
+ message: format!("received bad version: {:?}", &first_bytes[0..2]),
+ }))
+ } else {
+ let message_type: TMessageType = TryFrom::try_from(first_bytes[3])?;
+ let name = self.read_string()?;
+ let sequence_number = self.read_i32()?;
+ Ok(TMessageIdentifier::new(name, message_type, sequence_number))
+ }
+ } else {
+ // apparently we didn't get a protocol-version header,
+ // which happens if the sender is not using the strict protocol
+ if self.strict {
+ // we're in strict mode however, and that always
+ // requires the protocol-version header to be written first
+ Err(::Error::Protocol(ProtocolError {
+ kind: ProtocolErrorKind::BadVersion,
+ message: format!("received bad version: {:?}", &first_bytes[0..2]),
+ }))
+ } else {
+ // in the non-strict version the first message field
+ // is the message name. strings (byte arrays) are length-prefixed,
+ // so we've just read the length in the first 4 bytes
+ let name_size = BigEndian::read_i32(&first_bytes) as usize;
+ let mut name_buf: Vec<u8> = vec![0; name_size];
+ self.transport.read_exact(&mut name_buf)?;
+ let name = String::from_utf8(name_buf)?;
+
+ // read the rest of the fields
+ let message_type: TMessageType = self.read_byte().and_then(TryFrom::try_from)?;
+ let sequence_number = self.read_i32()?;
+ Ok(TMessageIdentifier::new(name, message_type, sequence_number))
+ }
+ }
+ }
+
+ fn read_message_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn read_struct_begin(&mut self) -> ::Result<Option<TStructIdentifier>> {
+ Ok(None)
+ }
+
+ fn read_struct_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn read_field_begin(&mut self) -> ::Result<TFieldIdentifier> {
+ let field_type_byte = self.read_byte()?;
+ let field_type = field_type_from_u8(field_type_byte)?;
+ let id = match field_type {
+ TType::Stop => Ok(0),
+ _ => self.read_i16(),
+ }?;
+ Ok(TFieldIdentifier::new::<Option<String>, String, i16>(
+ None, field_type, id,
+ ))
+ }
+
+ fn read_field_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn read_bytes(&mut self) -> ::Result<Vec<u8>> {
+ let num_bytes = self.transport.read_i32::<BigEndian>()? as usize;
+ let mut buf = vec![0u8; num_bytes];
+ self.transport
+ .read_exact(&mut buf)
+ .map(|_| buf)
+ .map_err(From::from)
+ }
+
+ fn read_bool(&mut self) -> ::Result<bool> {
+ let b = self.read_i8()?;
+ match b {
+ 0 => Ok(false),
+ _ => Ok(true),
+ }
+ }
+
+ fn read_i8(&mut self) -> ::Result<i8> {
+ self.transport.read_i8().map_err(From::from)
+ }
+
+ fn read_i16(&mut self) -> ::Result<i16> {
+ self.transport.read_i16::<BigEndian>().map_err(From::from)
+ }
+
+ fn read_i32(&mut self) -> ::Result<i32> {
+ self.transport.read_i32::<BigEndian>().map_err(From::from)
+ }
+
+ fn read_i64(&mut self) -> ::Result<i64> {
+ self.transport.read_i64::<BigEndian>().map_err(From::from)
+ }
+
+ fn read_double(&mut self) -> ::Result<f64> {
+ self.transport.read_f64::<BigEndian>().map_err(From::from)
+ }
+
+ fn read_string(&mut self) -> ::Result<String> {
+ let bytes = self.read_bytes()?;
+ String::from_utf8(bytes).map_err(From::from)
+ }
+
+ fn read_list_begin(&mut self) -> ::Result<TListIdentifier> {
+ let element_type: TType = self.read_byte().and_then(field_type_from_u8)?;
+ let size = self.read_i32()?;
+ Ok(TListIdentifier::new(element_type, size))
+ }
+
+ fn read_list_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn read_set_begin(&mut self) -> ::Result<TSetIdentifier> {
+ let element_type: TType = self.read_byte().and_then(field_type_from_u8)?;
+ let size = self.read_i32()?;
+ Ok(TSetIdentifier::new(element_type, size))
+ }
+
+ fn read_set_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn read_map_begin(&mut self) -> ::Result<TMapIdentifier> {
+ let key_type: TType = self.read_byte().and_then(field_type_from_u8)?;
+ let value_type: TType = self.read_byte().and_then(field_type_from_u8)?;
+ let size = self.read_i32()?;
+ Ok(TMapIdentifier::new(key_type, value_type, size))
+ }
+
+ fn read_map_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ // utility
+ //
+
+ fn read_byte(&mut self) -> ::Result<u8> {
+ self.transport.read_u8().map_err(From::from)
+ }
+}
+
+/// Factory for creating instances of `TBinaryInputProtocol`.
+#[derive(Default)]
+pub struct TBinaryInputProtocolFactory;
+
+impl TBinaryInputProtocolFactory {
+ /// Create a `TBinaryInputProtocolFactory`.
+ pub fn new() -> TBinaryInputProtocolFactory {
+ TBinaryInputProtocolFactory {}
+ }
+}
+
+impl TInputProtocolFactory for TBinaryInputProtocolFactory {
+ fn create(&self, transport: Box<dyn TReadTransport + Send>) -> Box<dyn TInputProtocol + Send> {
+ Box::new(TBinaryInputProtocol::new(transport, true))
+ }
+}
+
+/// Write messages using the Thrift simple binary encoding.
+///
+/// There are two available modes: `strict` and `non-strict`, where the
+/// `strict` version writes the protocol version number in the outgoing message
+/// header and the `non-strict` version does not.
+///
+/// # Examples
+///
+/// Create and use a `TBinaryOutputProtocol`.
+///
+/// ```no_run
+/// use thrift::protocol::{TBinaryOutputProtocol, TOutputProtocol};
+/// use thrift::transport::TTcpChannel;
+///
+/// let mut channel = TTcpChannel::new();
+/// channel.open("localhost:9090").unwrap();
+///
+/// let mut protocol = TBinaryOutputProtocol::new(channel, true);
+///
+/// protocol.write_bool(true).unwrap();
+/// protocol.write_string("test_string").unwrap();
+/// ```
+#[derive(Debug)]
+pub struct TBinaryOutputProtocol<T>
+where
+ T: TWriteTransport,
+{
+ strict: bool,
+ pub transport: T, // FIXME: do not make public; only public for testing!
+}
+
+impl<T> TBinaryOutputProtocol<T>
+where
+ T: TWriteTransport,
+{
+ /// Create a `TBinaryOutputProtocol` that writes bytes to `transport`.
+ ///
+ /// Set `strict` to `true` if all outgoing messages should contain the
+ /// protocol version number in the protocol header.
+ pub fn new(transport: T, strict: bool) -> TBinaryOutputProtocol<T> {
+ TBinaryOutputProtocol {
+ strict: strict,
+ transport: transport,
+ }
+ }
+}
+
+impl<T> TOutputProtocol for TBinaryOutputProtocol<T>
+where
+ T: TWriteTransport,
+{
+ fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> ::Result<()> {
+ if self.strict {
+ let message_type: u8 = identifier.message_type.into();
+ let header = BINARY_PROTOCOL_VERSION_1 | (message_type as u32);
+ self.transport.write_u32::<BigEndian>(header)?;
+ self.write_string(&identifier.name)?;
+ self.write_i32(identifier.sequence_number)
+ } else {
+ self.write_string(&identifier.name)?;
+ self.write_byte(identifier.message_type.into())?;
+ self.write_i32(identifier.sequence_number)
+ }
+ }
+
+ fn write_message_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn write_struct_begin(&mut self, _: &TStructIdentifier) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn write_struct_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> ::Result<()> {
+ if identifier.id.is_none() && identifier.field_type != TType::Stop {
+ return Err(::Error::Protocol(ProtocolError {
+ kind: ProtocolErrorKind::Unknown,
+ message: format!(
+ "cannot write identifier {:?} without sequence number",
+ &identifier
+ ),
+ }));
+ }
+
+ self.write_byte(field_type_to_u8(identifier.field_type))?;
+ if let Some(id) = identifier.id {
+ self.write_i16(id)
+ } else {
+ Ok(())
+ }
+ }
+
+ fn write_field_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn write_field_stop(&mut self) -> ::Result<()> {
+ self.write_byte(field_type_to_u8(TType::Stop))
+ }
+
+ fn write_bytes(&mut self, b: &[u8]) -> ::Result<()> {
+ self.write_i32(b.len() as i32)?;
+ self.transport.write_all(b).map_err(From::from)
+ }
+
+ fn write_bool(&mut self, b: bool) -> ::Result<()> {
+ if b {
+ self.write_i8(1)
+ } else {
+ self.write_i8(0)
+ }
+ }
+
+ fn write_i8(&mut self, i: i8) -> ::Result<()> {
+ self.transport.write_i8(i).map_err(From::from)
+ }
+
+ fn write_i16(&mut self, i: i16) -> ::Result<()> {
+ self.transport.write_i16::<BigEndian>(i).map_err(From::from)
+ }
+
+ fn write_i32(&mut self, i: i32) -> ::Result<()> {
+ self.transport.write_i32::<BigEndian>(i).map_err(From::from)
+ }
+
+ fn write_i64(&mut self, i: i64) -> ::Result<()> {
+ self.transport.write_i64::<BigEndian>(i).map_err(From::from)
+ }
+
+ fn write_double(&mut self, d: f64) -> ::Result<()> {
+ self.transport.write_f64::<BigEndian>(d).map_err(From::from)
+ }
+
+ fn write_string(&mut self, s: &str) -> ::Result<()> {
+ self.write_bytes(s.as_bytes())
+ }
+
+ fn write_list_begin(&mut self, identifier: &TListIdentifier) -> ::Result<()> {
+ self.write_byte(field_type_to_u8(identifier.element_type))?;
+ self.write_i32(identifier.size)
+ }
+
+ fn write_list_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> ::Result<()> {
+ self.write_byte(field_type_to_u8(identifier.element_type))?;
+ self.write_i32(identifier.size)
+ }
+
+ fn write_set_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> ::Result<()> {
+ let key_type = identifier
+ .key_type
+ .expect("map identifier to write should contain key type");
+ self.write_byte(field_type_to_u8(key_type))?;
+ let val_type = identifier
+ .value_type
+ .expect("map identifier to write should contain value type");
+ self.write_byte(field_type_to_u8(val_type))?;
+ self.write_i32(identifier.size)
+ }
+
+ fn write_map_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn flush(&mut self) -> ::Result<()> {
+ self.transport.flush().map_err(From::from)
+ }
+
+ // utility
+ //
+
+ fn write_byte(&mut self, b: u8) -> ::Result<()> {
+ self.transport.write_u8(b).map_err(From::from)
+ }
+}
+
+/// Factory for creating instances of `TBinaryOutputProtocol`.
+#[derive(Default)]
+pub struct TBinaryOutputProtocolFactory;
+
+impl TBinaryOutputProtocolFactory {
+ /// Create a `TBinaryOutputProtocolFactory`.
+ pub fn new() -> TBinaryOutputProtocolFactory {
+ TBinaryOutputProtocolFactory {}
+ }
+}
+
+impl TOutputProtocolFactory for TBinaryOutputProtocolFactory {
+ fn create(&self, transport: Box<dyn TWriteTransport + Send>) -> Box<dyn TOutputProtocol + Send> {
+ Box::new(TBinaryOutputProtocol::new(transport, true))
+ }
+}
+
+fn field_type_to_u8(field_type: TType) -> u8 {
+ match field_type {
+ TType::Stop => 0x00,
+ TType::Void => 0x01,
+ TType::Bool => 0x02,
+ TType::I08 => 0x03, // equivalent to TType::Byte
+ TType::Double => 0x04,
+ TType::I16 => 0x06,
+ TType::I32 => 0x08,
+ TType::I64 => 0x0A,
+ TType::String | TType::Utf7 => 0x0B,
+ TType::Struct => 0x0C,
+ TType::Map => 0x0D,
+ TType::Set => 0x0E,
+ TType::List => 0x0F,
+ TType::Utf8 => 0x10,
+ TType::Utf16 => 0x11,
+ }
+}
+
+fn field_type_from_u8(b: u8) -> ::Result<TType> {
+ match b {
+ 0x00 => Ok(TType::Stop),
+ 0x01 => Ok(TType::Void),
+ 0x02 => Ok(TType::Bool),
+ 0x03 => Ok(TType::I08), // Equivalent to TType::Byte
+ 0x04 => Ok(TType::Double),
+ 0x06 => Ok(TType::I16),
+ 0x08 => Ok(TType::I32),
+ 0x0A => Ok(TType::I64),
+ 0x0B => Ok(TType::String), // technically, also a UTF7, but we'll treat it as string
+ 0x0C => Ok(TType::Struct),
+ 0x0D => Ok(TType::Map),
+ 0x0E => Ok(TType::Set),
+ 0x0F => Ok(TType::List),
+ 0x10 => Ok(TType::Utf8),
+ 0x11 => Ok(TType::Utf16),
+ unkn => Err(::Error::Protocol(ProtocolError {
+ kind: ProtocolErrorKind::InvalidData,
+ message: format!("cannot convert {} to TType", unkn),
+ })),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+
+ use protocol::{
+ TFieldIdentifier, TInputProtocol, TListIdentifier, TMapIdentifier, TMessageIdentifier,
+ TMessageType, TOutputProtocol, TSetIdentifier, TStructIdentifier, TType,
+ };
+ use transport::{ReadHalf, TBufferChannel, TIoChannel, WriteHalf};
+
+ use super::*;
+
+ #[test]
+ fn must_write_strict_message_call_begin() {
+ let (_, mut o_prot) = test_objects(true);
+
+ let ident = TMessageIdentifier::new("test", TMessageType::Call, 1);
+ assert!(o_prot.write_message_begin(&ident).is_ok());
+
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ let expected: [u8; 16] = [
+ 0x80,
+ 0x01,
+ 0x00,
+ 0x01,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x04,
+ 0x74,
+ 0x65,
+ 0x73,
+ 0x74,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x01,
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_write_non_strict_message_call_begin() {
+ let (_, mut o_prot) = test_objects(false);
+
+ let ident = TMessageIdentifier::new("test", TMessageType::Call, 1);
+ assert!(o_prot.write_message_begin(&ident).is_ok());
+
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ let expected: [u8; 13] = [
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x04,
+ 0x74,
+ 0x65,
+ 0x73,
+ 0x74,
+ 0x01,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x01,
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_write_strict_message_reply_begin() {
+ let (_, mut o_prot) = test_objects(true);
+
+ let ident = TMessageIdentifier::new("test", TMessageType::Reply, 10);
+ assert!(o_prot.write_message_begin(&ident).is_ok());
+
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ let expected: [u8; 16] = [
+ 0x80,
+ 0x01,
+ 0x00,
+ 0x02,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x04,
+ 0x74,
+ 0x65,
+ 0x73,
+ 0x74,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x0A,
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_write_non_strict_message_reply_begin() {
+ let (_, mut o_prot) = test_objects(false);
+
+ let ident = TMessageIdentifier::new("test", TMessageType::Reply, 10);
+ assert!(o_prot.write_message_begin(&ident).is_ok());
+
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ let expected: [u8; 13] = [
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x04,
+ 0x74,
+ 0x65,
+ 0x73,
+ 0x74,
+ 0x02,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x0A,
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_strict_message_begin() {
+ let (mut i_prot, mut o_prot) = test_objects(true);
+
+ let sent_ident = TMessageIdentifier::new("test", TMessageType::Call, 1);
+ assert!(o_prot.write_message_begin(&sent_ident).is_ok());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ let received_ident = assert_success!(i_prot.read_message_begin());
+ assert_eq!(&received_ident, &sent_ident);
+ }
+
+ #[test]
+ fn must_round_trip_non_strict_message_begin() {
+ let (mut i_prot, mut o_prot) = test_objects(false);
+
+ let sent_ident = TMessageIdentifier::new("test", TMessageType::Call, 1);
+ assert!(o_prot.write_message_begin(&sent_ident).is_ok());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ let received_ident = assert_success!(i_prot.read_message_begin());
+ assert_eq!(&received_ident, &sent_ident);
+ }
+
+ #[test]
+ fn must_write_message_end() {
+ assert_no_write(|o| o.write_message_end(), true);
+ }
+
+ #[test]
+ fn must_write_struct_begin() {
+ assert_no_write(
+ |o| o.write_struct_begin(&TStructIdentifier::new("foo")),
+ true,
+ );
+ }
+
+ #[test]
+ fn must_write_struct_end() {
+ assert_no_write(|o| o.write_struct_end(), true);
+ }
+
+ #[test]
+ fn must_write_field_begin() {
+ let (_, mut o_prot) = test_objects(true);
+
+ assert!(o_prot
+ .write_field_begin(&TFieldIdentifier::new("some_field", TType::String, 22))
+ .is_ok());
+
+ let expected: [u8; 3] = [0x0B, 0x00, 0x16];
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_field_begin() {
+ let (mut i_prot, mut o_prot) = test_objects(true);
+
+ let sent_field_ident = TFieldIdentifier::new("foo", TType::I64, 20);
+ assert!(o_prot.write_field_begin(&sent_field_ident).is_ok());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ let expected_ident = TFieldIdentifier {
+ name: None,
+ field_type: TType::I64,
+ id: Some(20),
+ }; // no name
+ let received_ident = assert_success!(i_prot.read_field_begin());
+ assert_eq!(&received_ident, &expected_ident);
+ }
+
+ #[test]
+ fn must_write_stop_field() {
+ let (_, mut o_prot) = test_objects(true);
+
+ assert!(o_prot.write_field_stop().is_ok());
+
+ let expected: [u8; 1] = [0x00];
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_field_stop() {
+ let (mut i_prot, mut o_prot) = test_objects(true);
+
+ assert!(o_prot.write_field_stop().is_ok());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ let expected_ident = TFieldIdentifier {
+ name: None,
+ field_type: TType::Stop,
+ id: Some(0),
+ }; // we get id 0
+
+ let received_ident = assert_success!(i_prot.read_field_begin());
+ assert_eq!(&received_ident, &expected_ident);
+ }
+
+ #[test]
+ fn must_write_field_end() {
+ assert_no_write(|o| o.write_field_end(), true);
+ }
+
+ #[test]
+ fn must_write_list_begin() {
+ let (_, mut o_prot) = test_objects(true);
+
+ assert!(o_prot
+ .write_list_begin(&TListIdentifier::new(TType::Bool, 5))
+ .is_ok());
+
+ let expected: [u8; 5] = [0x02, 0x00, 0x00, 0x00, 0x05];
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_list_begin() {
+ let (mut i_prot, mut o_prot) = test_objects(true);
+
+ let ident = TListIdentifier::new(TType::List, 900);
+ assert!(o_prot.write_list_begin(&ident).is_ok());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ let received_ident = assert_success!(i_prot.read_list_begin());
+ assert_eq!(&received_ident, &ident);
+ }
+
+ #[test]
+ fn must_write_list_end() {
+ assert_no_write(|o| o.write_list_end(), true);
+ }
+
+ #[test]
+ fn must_write_set_begin() {
+ let (_, mut o_prot) = test_objects(true);
+
+ assert!(o_prot
+ .write_set_begin(&TSetIdentifier::new(TType::I16, 7))
+ .is_ok());
+
+ let expected: [u8; 5] = [0x06, 0x00, 0x00, 0x00, 0x07];
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_set_begin() {
+ let (mut i_prot, mut o_prot) = test_objects(true);
+
+ let ident = TSetIdentifier::new(TType::I64, 2000);
+ assert!(o_prot.write_set_begin(&ident).is_ok());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ let received_ident_result = i_prot.read_set_begin();
+ assert!(received_ident_result.is_ok());
+ assert_eq!(&received_ident_result.unwrap(), &ident);
+ }
+
+ #[test]
+ fn must_write_set_end() {
+ assert_no_write(|o| o.write_set_end(), true);
+ }
+
+ #[test]
+ fn must_write_map_begin() {
+ let (_, mut o_prot) = test_objects(true);
+
+ assert!(o_prot
+ .write_map_begin(&TMapIdentifier::new(TType::I64, TType::Struct, 32))
+ .is_ok());
+
+ let expected: [u8; 6] = [0x0A, 0x0C, 0x00, 0x00, 0x00, 0x20];
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_map_begin() {
+ let (mut i_prot, mut o_prot) = test_objects(true);
+
+ let ident = TMapIdentifier::new(TType::Map, TType::Set, 100);
+ assert!(o_prot.write_map_begin(&ident).is_ok());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ let received_ident = assert_success!(i_prot.read_map_begin());
+ assert_eq!(&received_ident, &ident);
+ }
+
+ #[test]
+ fn must_write_map_end() {
+ assert_no_write(|o| o.write_map_end(), true);
+ }
+
+ #[test]
+ fn must_write_bool_true() {
+ let (_, mut o_prot) = test_objects(true);
+
+ assert!(o_prot.write_bool(true).is_ok());
+
+ let expected: [u8; 1] = [0x01];
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_write_bool_false() {
+ let (_, mut o_prot) = test_objects(true);
+
+ assert!(o_prot.write_bool(false).is_ok());
+
+ let expected: [u8; 1] = [0x00];
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_read_bool_true() {
+ let (mut i_prot, _) = test_objects(true);
+
+ set_readable_bytes!(i_prot, &[0x01]);
+
+ let read_bool = assert_success!(i_prot.read_bool());
+ assert_eq!(read_bool, true);
+ }
+
+ #[test]
+ fn must_read_bool_false() {
+ let (mut i_prot, _) = test_objects(true);
+
+ set_readable_bytes!(i_prot, &[0x00]);
+
+ let read_bool = assert_success!(i_prot.read_bool());
+ assert_eq!(read_bool, false);
+ }
+
+ #[test]
+ fn must_allow_any_non_zero_value_to_be_interpreted_as_bool_true() {
+ let (mut i_prot, _) = test_objects(true);
+
+ set_readable_bytes!(i_prot, &[0xAC]);
+
+ let read_bool = assert_success!(i_prot.read_bool());
+ assert_eq!(read_bool, true);
+ }
+
+ #[test]
+ fn must_write_bytes() {
+ let (_, mut o_prot) = test_objects(true);
+
+ let bytes: [u8; 10] = [0x0A, 0xCC, 0xD1, 0x84, 0x99, 0x12, 0xAB, 0xBB, 0x45, 0xDF];
+
+ assert!(o_prot.write_bytes(&bytes).is_ok());
+
+ let buf = o_prot.transport.write_bytes();
+ assert_eq!(&buf[0..4], [0x00, 0x00, 0x00, 0x0A]); // length
+ assert_eq!(&buf[4..], bytes); // actual bytes
+ }
+
+ #[test]
+ fn must_round_trip_bytes() {
+ let (mut i_prot, mut o_prot) = test_objects(true);
+
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ let bytes: [u8; 25] = [
+ 0x20,
+ 0xFD,
+ 0x18,
+ 0x84,
+ 0x99,
+ 0x12,
+ 0xAB,
+ 0xBB,
+ 0x45,
+ 0xDF,
+ 0x34,
+ 0xDC,
+ 0x98,
+ 0xA4,
+ 0x6D,
+ 0xF3,
+ 0x99,
+ 0xB4,
+ 0xB7,
+ 0xD4,
+ 0x9C,
+ 0xA5,
+ 0xB3,
+ 0xC9,
+ 0x88,
+ ];
+
+ assert!(o_prot.write_bytes(&bytes).is_ok());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ let received_bytes = assert_success!(i_prot.read_bytes());
+ assert_eq!(&received_bytes, &bytes);
+ }
+
+ fn test_objects(
+ strict: bool,
+ ) -> (
+ TBinaryInputProtocol<ReadHalf<TBufferChannel>>,
+ TBinaryOutputProtocol<WriteHalf<TBufferChannel>>,
+ ) {
+ let mem = TBufferChannel::with_capacity(40, 40);
+
+ let (r_mem, w_mem) = mem.split().unwrap();
+
+ let i_prot = TBinaryInputProtocol::new(r_mem, strict);
+ let o_prot = TBinaryOutputProtocol::new(w_mem, strict);
+
+ (i_prot, o_prot)
+ }
+
+ fn assert_no_write<F>(mut write_fn: F, strict: bool)
+ where
+ F: FnMut(&mut TBinaryOutputProtocol<WriteHalf<TBufferChannel>>) -> ::Result<()>,
+ {
+ let (_, mut o_prot) = test_objects(strict);
+ assert!(write_fn(&mut o_prot).is_ok());
+ assert_eq!(o_prot.transport.write_bytes().len(), 0);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/rs/src/protocol/compact.rs b/src/jaegertracing/thrift/lib/rs/src/protocol/compact.rs
new file mode 100644
index 000000000..1750bc42e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/src/protocol/compact.rs
@@ -0,0 +1,2385 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
+use integer_encoding::{VarIntReader, VarIntWriter};
+use std::convert::{From, TryFrom};
+use std::io;
+
+use super::{
+ TFieldIdentifier, TInputProtocol, TInputProtocolFactory, TListIdentifier, TMapIdentifier,
+ TMessageIdentifier, TMessageType,
+};
+use super::{TOutputProtocol, TOutputProtocolFactory, TSetIdentifier, TStructIdentifier, TType};
+use transport::{TReadTransport, TWriteTransport};
+
+const COMPACT_PROTOCOL_ID: u8 = 0x82;
+const COMPACT_VERSION: u8 = 0x01;
+const COMPACT_VERSION_MASK: u8 = 0x1F;
+
+/// Read messages encoded in the Thrift compact protocol.
+///
+/// # Examples
+///
+/// Create and use a `TCompactInputProtocol`.
+///
+/// ```no_run
+/// use thrift::protocol::{TCompactInputProtocol, TInputProtocol};
+/// use thrift::transport::TTcpChannel;
+///
+/// let mut channel = TTcpChannel::new();
+/// channel.open("localhost:9090").unwrap();
+///
+/// let mut protocol = TCompactInputProtocol::new(channel);
+///
+/// let recvd_bool = protocol.read_bool().unwrap();
+/// let recvd_string = protocol.read_string().unwrap();
+/// ```
+#[derive(Debug)]
+pub struct TCompactInputProtocol<T>
+where
+ T: TReadTransport,
+{
+ // Identifier of the last field deserialized for a struct.
+ last_read_field_id: i16,
+ // Stack of the last read field ids (a new entry is added each time a nested struct is read).
+ read_field_id_stack: Vec<i16>,
+ // Boolean value for a field.
+ // Saved because boolean fields and their value are encoded in a single byte,
+ // and reading the field only occurs after the field id is read.
+ pending_read_bool_value: Option<bool>,
+ // Underlying transport used for byte-level operations.
+ transport: T,
+}
+
+impl<T> TCompactInputProtocol<T>
+where
+ T: TReadTransport,
+{
+ /// Create a `TCompactInputProtocol` that reads bytes from `transport`.
+ pub fn new(transport: T) -> TCompactInputProtocol<T> {
+ TCompactInputProtocol {
+ last_read_field_id: 0,
+ read_field_id_stack: Vec::new(),
+ pending_read_bool_value: None,
+ transport: transport,
+ }
+ }
+
+ fn read_list_set_begin(&mut self) -> ::Result<(TType, i32)> {
+ let header = self.read_byte()?;
+ let element_type = collection_u8_to_type(header & 0x0F)?;
+
+ let element_count;
+ let possible_element_count = (header & 0xF0) >> 4;
+ if possible_element_count != 15 {
+ // high bits set high if count and type encoded separately
+ element_count = possible_element_count as i32;
+ } else {
+ element_count = self.transport.read_varint::<u32>()? as i32;
+ }
+
+ Ok((element_type, element_count))
+ }
+}
+
+impl<T> TInputProtocol for TCompactInputProtocol<T>
+where
+ T: TReadTransport,
+{
+ fn read_message_begin(&mut self) -> ::Result<TMessageIdentifier> {
+ let compact_id = self.read_byte()?;
+ if compact_id != COMPACT_PROTOCOL_ID {
+ Err(::Error::Protocol(::ProtocolError {
+ kind: ::ProtocolErrorKind::BadVersion,
+ message: format!("invalid compact protocol header {:?}", compact_id),
+ }))
+ } else {
+ Ok(())
+ }?;
+
+ let type_and_byte = self.read_byte()?;
+ let received_version = type_and_byte & COMPACT_VERSION_MASK;
+ if received_version != COMPACT_VERSION {
+ Err(::Error::Protocol(::ProtocolError {
+ kind: ::ProtocolErrorKind::BadVersion,
+ message: format!(
+ "cannot process compact protocol version {:?}",
+ received_version
+ ),
+ }))
+ } else {
+ Ok(())
+ }?;
+
+ // NOTE: unsigned right shift will pad with 0s
+ let message_type: TMessageType = TMessageType::try_from(type_and_byte >> 5)?;
+ let sequence_number = self.read_i32()?;
+ let service_call_name = self.read_string()?;
+
+ self.last_read_field_id = 0;
+
+ Ok(TMessageIdentifier::new(
+ service_call_name,
+ message_type,
+ sequence_number,
+ ))
+ }
+
+ fn read_message_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn read_struct_begin(&mut self) -> ::Result<Option<TStructIdentifier>> {
+ self.read_field_id_stack.push(self.last_read_field_id);
+ self.last_read_field_id = 0;
+ Ok(None)
+ }
+
+ fn read_struct_end(&mut self) -> ::Result<()> {
+ self.last_read_field_id = self
+ .read_field_id_stack
+ .pop()
+ .expect("should have previous field ids");
+ Ok(())
+ }
+
+ fn read_field_begin(&mut self) -> ::Result<TFieldIdentifier> {
+ // we can read at least one byte, which is:
+ // - the type
+ // - the field delta and the type
+ let field_type = self.read_byte()?;
+ let field_delta = (field_type & 0xF0) >> 4;
+ let field_type = match field_type & 0x0F {
+ 0x01 => {
+ self.pending_read_bool_value = Some(true);
+ Ok(TType::Bool)
+ }
+ 0x02 => {
+ self.pending_read_bool_value = Some(false);
+ Ok(TType::Bool)
+ }
+ ttu8 => u8_to_type(ttu8),
+ }?;
+
+ match field_type {
+ TType::Stop => Ok(
+ TFieldIdentifier::new::<Option<String>, String, Option<i16>>(
+ None,
+ TType::Stop,
+ None,
+ ),
+ ),
+ _ => {
+ if field_delta != 0 {
+ self.last_read_field_id += field_delta as i16;
+ } else {
+ self.last_read_field_id = self.read_i16()?;
+ };
+
+ Ok(TFieldIdentifier {
+ name: None,
+ field_type: field_type,
+ id: Some(self.last_read_field_id),
+ })
+ }
+ }
+ }
+
+ fn read_field_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn read_bool(&mut self) -> ::Result<bool> {
+ match self.pending_read_bool_value.take() {
+ Some(b) => Ok(b),
+ None => {
+ let b = self.read_byte()?;
+ match b {
+ 0x01 => Ok(true),
+ 0x02 => Ok(false),
+ unkn => Err(::Error::Protocol(::ProtocolError {
+ kind: ::ProtocolErrorKind::InvalidData,
+ message: format!("cannot convert {} into bool", unkn),
+ })),
+ }
+ }
+ }
+ }
+
+ fn read_bytes(&mut self) -> ::Result<Vec<u8>> {
+ let len = self.transport.read_varint::<u32>()?;
+ let mut buf = vec![0u8; len as usize];
+ self.transport
+ .read_exact(&mut buf)
+ .map_err(From::from)
+ .map(|_| buf)
+ }
+
+ fn read_i8(&mut self) -> ::Result<i8> {
+ self.read_byte().map(|i| i as i8)
+ }
+
+ fn read_i16(&mut self) -> ::Result<i16> {
+ self.transport.read_varint::<i16>().map_err(From::from)
+ }
+
+ fn read_i32(&mut self) -> ::Result<i32> {
+ self.transport.read_varint::<i32>().map_err(From::from)
+ }
+
+ fn read_i64(&mut self) -> ::Result<i64> {
+ self.transport.read_varint::<i64>().map_err(From::from)
+ }
+
+ fn read_double(&mut self) -> ::Result<f64> {
+ self.transport.read_f64::<BigEndian>().map_err(From::from)
+ }
+
+ fn read_string(&mut self) -> ::Result<String> {
+ let bytes = self.read_bytes()?;
+ String::from_utf8(bytes).map_err(From::from)
+ }
+
+ fn read_list_begin(&mut self) -> ::Result<TListIdentifier> {
+ let (element_type, element_count) = self.read_list_set_begin()?;
+ Ok(TListIdentifier::new(element_type, element_count))
+ }
+
+ fn read_list_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn read_set_begin(&mut self) -> ::Result<TSetIdentifier> {
+ let (element_type, element_count) = self.read_list_set_begin()?;
+ Ok(TSetIdentifier::new(element_type, element_count))
+ }
+
+ fn read_set_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn read_map_begin(&mut self) -> ::Result<TMapIdentifier> {
+ let element_count = self.transport.read_varint::<u32>()? as i32;
+ if element_count == 0 {
+ Ok(TMapIdentifier::new(None, None, 0))
+ } else {
+ let type_header = self.read_byte()?;
+ let key_type = collection_u8_to_type((type_header & 0xF0) >> 4)?;
+ let val_type = collection_u8_to_type(type_header & 0x0F)?;
+ Ok(TMapIdentifier::new(key_type, val_type, element_count))
+ }
+ }
+
+ fn read_map_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ // utility
+ //
+
+ fn read_byte(&mut self) -> ::Result<u8> {
+ let mut buf = [0u8; 1];
+ self.transport
+ .read_exact(&mut buf)
+ .map_err(From::from)
+ .map(|_| buf[0])
+ }
+}
+
+impl<T> io::Seek for TCompactInputProtocol<T>
+where
+ T: io::Seek + TReadTransport,
+{
+ fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
+ self.transport.seek(pos)
+ }
+}
+
+/// Factory for creating instances of `TCompactInputProtocol`.
+#[derive(Default)]
+pub struct TCompactInputProtocolFactory;
+
+impl TCompactInputProtocolFactory {
+ /// Create a `TCompactInputProtocolFactory`.
+ pub fn new() -> TCompactInputProtocolFactory {
+ TCompactInputProtocolFactory {}
+ }
+}
+
+impl TInputProtocolFactory for TCompactInputProtocolFactory {
+ fn create(&self, transport: Box<dyn TReadTransport + Send>) -> Box<dyn TInputProtocol + Send> {
+ Box::new(TCompactInputProtocol::new(transport))
+ }
+}
+
+/// Write messages using the Thrift compact protocol.
+///
+/// # Examples
+///
+/// Create and use a `TCompactOutputProtocol`.
+///
+/// ```no_run
+/// use thrift::protocol::{TCompactOutputProtocol, TOutputProtocol};
+/// use thrift::transport::TTcpChannel;
+///
+/// let mut channel = TTcpChannel::new();
+/// channel.open("localhost:9090").unwrap();
+///
+/// let mut protocol = TCompactOutputProtocol::new(channel);
+///
+/// protocol.write_bool(true).unwrap();
+/// protocol.write_string("test_string").unwrap();
+/// ```
+#[derive(Debug)]
+pub struct TCompactOutputProtocol<T>
+where
+ T: TWriteTransport,
+{
+ // Identifier of the last field serialized for a struct.
+ last_write_field_id: i16,
+ // Stack of the last written field ids (new entry added each time a nested struct is written).
+ write_field_id_stack: Vec<i16>,
+ // Field identifier of the boolean field to be written.
+ // Saved because boolean fields and their value are encoded in a single byte
+ pending_write_bool_field_identifier: Option<TFieldIdentifier>,
+ // Underlying transport used for byte-level operations.
+ transport: T,
+}
+
+impl<T> TCompactOutputProtocol<T>
+where
+ T: TWriteTransport,
+{
+ /// Create a `TCompactOutputProtocol` that writes bytes to `transport`.
+ pub fn new(transport: T) -> TCompactOutputProtocol<T> {
+ TCompactOutputProtocol {
+ last_write_field_id: 0,
+ write_field_id_stack: Vec::new(),
+ pending_write_bool_field_identifier: None,
+ transport: transport,
+ }
+ }
+
+ // FIXME: field_type as unconstrained u8 is bad
+ fn write_field_header(&mut self, field_type: u8, field_id: i16) -> ::Result<()> {
+ let field_delta = field_id - self.last_write_field_id;
+ if field_delta > 0 && field_delta < 15 {
+ self.write_byte(((field_delta as u8) << 4) | field_type)?;
+ } else {
+ self.write_byte(field_type)?;
+ self.write_i16(field_id)?;
+ }
+ self.last_write_field_id = field_id;
+ Ok(())
+ }
+
+ fn write_list_set_begin(&mut self, element_type: TType, element_count: i32) -> ::Result<()> {
+ let elem_identifier = collection_type_to_u8(element_type);
+ if element_count <= 14 {
+ let header = (element_count as u8) << 4 | elem_identifier;
+ self.write_byte(header)
+ } else {
+ let header = 0xF0 | elem_identifier;
+ self.write_byte(header)?;
+ self.transport
+ .write_varint(element_count as u32)
+ .map_err(From::from)
+ .map(|_| ())
+ }
+ }
+
+ fn assert_no_pending_bool_write(&self) {
+ if let Some(ref f) = self.pending_write_bool_field_identifier {
+ panic!("pending bool field {:?} not written", f)
+ }
+ }
+}
+
+impl<T> TOutputProtocol for TCompactOutputProtocol<T>
+where
+ T: TWriteTransport,
+{
+ fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> ::Result<()> {
+ self.write_byte(COMPACT_PROTOCOL_ID)?;
+ self.write_byte((u8::from(identifier.message_type) << 5) | COMPACT_VERSION)?;
+ self.write_i32(identifier.sequence_number)?;
+ self.write_string(&identifier.name)?;
+ Ok(())
+ }
+
+ fn write_message_end(&mut self) -> ::Result<()> {
+ self.assert_no_pending_bool_write();
+ Ok(())
+ }
+
+ fn write_struct_begin(&mut self, _: &TStructIdentifier) -> ::Result<()> {
+ self.write_field_id_stack.push(self.last_write_field_id);
+ self.last_write_field_id = 0;
+ Ok(())
+ }
+
+ fn write_struct_end(&mut self) -> ::Result<()> {
+ self.assert_no_pending_bool_write();
+ self.last_write_field_id = self
+ .write_field_id_stack
+ .pop()
+ .expect("should have previous field ids");
+ Ok(())
+ }
+
+ fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> ::Result<()> {
+ match identifier.field_type {
+ TType::Bool => {
+ if self.pending_write_bool_field_identifier.is_some() {
+ panic!(
+ "should not have a pending bool while writing another bool with id: \
+ {:?}",
+ identifier
+ )
+ }
+ self.pending_write_bool_field_identifier = Some(identifier.clone());
+ Ok(())
+ }
+ _ => {
+ let field_type = type_to_u8(identifier.field_type);
+ let field_id = identifier.id.expect("non-stop field should have field id");
+ self.write_field_header(field_type, field_id)
+ }
+ }
+ }
+
+ fn write_field_end(&mut self) -> ::Result<()> {
+ self.assert_no_pending_bool_write();
+ Ok(())
+ }
+
+ fn write_field_stop(&mut self) -> ::Result<()> {
+ self.assert_no_pending_bool_write();
+ self.write_byte(type_to_u8(TType::Stop))
+ }
+
+ fn write_bool(&mut self, b: bool) -> ::Result<()> {
+ match self.pending_write_bool_field_identifier.take() {
+ Some(pending) => {
+ let field_id = pending.id.expect("bool field should have a field id");
+ let field_type_as_u8 = if b { 0x01 } else { 0x02 };
+ self.write_field_header(field_type_as_u8, field_id)
+ }
+ None => {
+ if b {
+ self.write_byte(0x01)
+ } else {
+ self.write_byte(0x02)
+ }
+ }
+ }
+ }
+
+ fn write_bytes(&mut self, b: &[u8]) -> ::Result<()> {
+ self.transport.write_varint(b.len() as u32)?;
+ self.transport.write_all(b).map_err(From::from)
+ }
+
+ fn write_i8(&mut self, i: i8) -> ::Result<()> {
+ self.write_byte(i as u8)
+ }
+
+ fn write_i16(&mut self, i: i16) -> ::Result<()> {
+ self.transport
+ .write_varint(i)
+ .map_err(From::from)
+ .map(|_| ())
+ }
+
+ fn write_i32(&mut self, i: i32) -> ::Result<()> {
+ self.transport
+ .write_varint(i)
+ .map_err(From::from)
+ .map(|_| ())
+ }
+
+ fn write_i64(&mut self, i: i64) -> ::Result<()> {
+ self.transport
+ .write_varint(i)
+ .map_err(From::from)
+ .map(|_| ())
+ }
+
+ fn write_double(&mut self, d: f64) -> ::Result<()> {
+ self.transport.write_f64::<BigEndian>(d).map_err(From::from)
+ }
+
+ fn write_string(&mut self, s: &str) -> ::Result<()> {
+ self.write_bytes(s.as_bytes())
+ }
+
+ fn write_list_begin(&mut self, identifier: &TListIdentifier) -> ::Result<()> {
+ self.write_list_set_begin(identifier.element_type, identifier.size)
+ }
+
+ fn write_list_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> ::Result<()> {
+ self.write_list_set_begin(identifier.element_type, identifier.size)
+ }
+
+ fn write_set_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> ::Result<()> {
+ if identifier.size == 0 {
+ self.write_byte(0)
+ } else {
+ self.transport.write_varint(identifier.size as u32)?;
+
+ let key_type = identifier
+ .key_type
+ .expect("map identifier to write should contain key type");
+ let key_type_byte = collection_type_to_u8(key_type) << 4;
+
+ let val_type = identifier
+ .value_type
+ .expect("map identifier to write should contain value type");
+ let val_type_byte = collection_type_to_u8(val_type);
+
+ let map_type_header = key_type_byte | val_type_byte;
+ self.write_byte(map_type_header)
+ }
+ }
+
+ fn write_map_end(&mut self) -> ::Result<()> {
+ Ok(())
+ }
+
+ fn flush(&mut self) -> ::Result<()> {
+ self.transport.flush().map_err(From::from)
+ }
+
+ // utility
+ //
+
+ fn write_byte(&mut self, b: u8) -> ::Result<()> {
+ self.transport.write(&[b]).map_err(From::from).map(|_| ())
+ }
+}
+
+/// Factory for creating instances of `TCompactOutputProtocol`.
+#[derive(Default)]
+pub struct TCompactOutputProtocolFactory;
+
+impl TCompactOutputProtocolFactory {
+ /// Create a `TCompactOutputProtocolFactory`.
+ pub fn new() -> TCompactOutputProtocolFactory {
+ TCompactOutputProtocolFactory {}
+ }
+}
+
+impl TOutputProtocolFactory for TCompactOutputProtocolFactory {
+ fn create(&self, transport: Box<dyn TWriteTransport + Send>) -> Box<dyn TOutputProtocol + Send> {
+ Box::new(TCompactOutputProtocol::new(transport))
+ }
+}
+
+fn collection_type_to_u8(field_type: TType) -> u8 {
+ match field_type {
+ TType::Bool => 0x01,
+ f => type_to_u8(f),
+ }
+}
+
+fn type_to_u8(field_type: TType) -> u8 {
+ match field_type {
+ TType::Stop => 0x00,
+ TType::I08 => 0x03, // equivalent to TType::Byte
+ TType::I16 => 0x04,
+ TType::I32 => 0x05,
+ TType::I64 => 0x06,
+ TType::Double => 0x07,
+ TType::String => 0x08,
+ TType::List => 0x09,
+ TType::Set => 0x0A,
+ TType::Map => 0x0B,
+ TType::Struct => 0x0C,
+ _ => panic!(format!(
+ "should not have attempted to convert {} to u8",
+ field_type
+ )),
+ }
+}
+
+fn collection_u8_to_type(b: u8) -> ::Result<TType> {
+ match b {
+ 0x01 => Ok(TType::Bool),
+ o => u8_to_type(o),
+ }
+}
+
+fn u8_to_type(b: u8) -> ::Result<TType> {
+ match b {
+ 0x00 => Ok(TType::Stop),
+ 0x03 => Ok(TType::I08), // equivalent to TType::Byte
+ 0x04 => Ok(TType::I16),
+ 0x05 => Ok(TType::I32),
+ 0x06 => Ok(TType::I64),
+ 0x07 => Ok(TType::Double),
+ 0x08 => Ok(TType::String),
+ 0x09 => Ok(TType::List),
+ 0x0A => Ok(TType::Set),
+ 0x0B => Ok(TType::Map),
+ 0x0C => Ok(TType::Struct),
+ unkn => Err(::Error::Protocol(::ProtocolError {
+ kind: ::ProtocolErrorKind::InvalidData,
+ message: format!("cannot convert {} into TType", unkn),
+ })),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+
+ use protocol::{
+ TFieldIdentifier, TInputProtocol, TListIdentifier, TMapIdentifier, TMessageIdentifier,
+ TMessageType, TOutputProtocol, TSetIdentifier, TStructIdentifier, TType,
+ };
+ use transport::{ReadHalf, TBufferChannel, TIoChannel, WriteHalf};
+
+ use super::*;
+
+ #[test]
+ fn must_write_message_begin_0() {
+ let (_, mut o_prot) = test_objects();
+
+ assert_success!(o_prot.write_message_begin(&TMessageIdentifier::new(
+ "foo",
+ TMessageType::Call,
+ 431
+ )));
+
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ let expected: [u8; 8] = [
+ 0x82, /* protocol ID */
+ 0x21, /* message type | protocol version */
+ 0xDE,
+ 0x06, /* zig-zag varint sequence number */
+ 0x03, /* message-name length */
+ 0x66,
+ 0x6F,
+ 0x6F /* "foo" */,
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_write_message_begin_1() {
+ let (_, mut o_prot) = test_objects();
+
+ assert_success!(o_prot.write_message_begin(&TMessageIdentifier::new(
+ "bar",
+ TMessageType::Reply,
+ 991828
+ )));
+
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ let expected: [u8; 9] = [
+ 0x82, /* protocol ID */
+ 0x41, /* message type | protocol version */
+ 0xA8,
+ 0x89,
+ 0x79, /* zig-zag varint sequence number */
+ 0x03, /* message-name length */
+ 0x62,
+ 0x61,
+ 0x72 /* "bar" */,
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_message_begin() {
+ let (mut i_prot, mut o_prot) = test_objects();
+
+ let ident = TMessageIdentifier::new("service_call", TMessageType::Call, 1283948);
+
+ assert_success!(o_prot.write_message_begin(&ident));
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ let res = assert_success!(i_prot.read_message_begin());
+ assert_eq!(&res, &ident);
+ }
+
+ #[test]
+ fn must_write_message_end() {
+ assert_no_write(|o| o.write_message_end());
+ }
+
+ // NOTE: structs and fields are tested together
+ //
+
+ #[test]
+ fn must_write_struct_with_delta_fields() {
+ let (_, mut o_prot) = test_objects();
+
+ // no bytes should be written however
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // write three fields with tiny field ids
+ // since they're small the field ids will be encoded as deltas
+
+ // since this is the first field (and it's zero) it gets the full varint write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I08, 0)));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta > 0 and < 15 it can be encoded as a delta
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I16, 4)));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta > 0 and < 15 it can be encoded as a delta
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::List, 9)));
+ assert_success!(o_prot.write_field_end());
+
+ // now, finish the struct off
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ let expected: [u8; 5] = [
+ 0x03, /* field type */
+ 0x00, /* first field id */
+ 0x44, /* field delta (4) | field type */
+ 0x59, /* field delta (5) | field type */
+ 0x00 /* field stop */,
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_struct_with_delta_fields() {
+ let (mut i_prot, mut o_prot) = test_objects();
+
+ // no bytes should be written however
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // write three fields with tiny field ids
+ // since they're small the field ids will be encoded as deltas
+
+ // since this is the first field (and it's zero) it gets the full varint write
+ let field_ident_1 = TFieldIdentifier::new("foo", TType::I08, 0);
+ assert_success!(o_prot.write_field_begin(&field_ident_1));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta > 0 and < 15 it can be encoded as a delta
+ let field_ident_2 = TFieldIdentifier::new("foo", TType::I16, 4);
+ assert_success!(o_prot.write_field_begin(&field_ident_2));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta > 0 and < 15 it can be encoded as a delta
+ let field_ident_3 = TFieldIdentifier::new("foo", TType::List, 9);
+ assert_success!(o_prot.write_field_begin(&field_ident_3));
+ assert_success!(o_prot.write_field_end());
+
+ // now, finish the struct off
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ // read the struct back
+ assert_success!(i_prot.read_struct_begin());
+
+ let read_ident_1 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_1,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_1
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_2 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_2,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_2
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_3 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_3,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_3
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_4 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_4,
+ TFieldIdentifier {
+ name: None,
+ field_type: TType::Stop,
+ id: None,
+ }
+ );
+
+ assert_success!(i_prot.read_struct_end());
+ }
+
+ #[test]
+ fn must_write_struct_with_non_zero_initial_field_and_delta_fields() {
+ let (_, mut o_prot) = test_objects();
+
+ // no bytes should be written however
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // write three fields with tiny field ids
+ // since they're small the field ids will be encoded as deltas
+
+ // gets a delta write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I32, 1)));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta > 0 and < 15 it can be encoded as a delta
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Set, 2)));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta > 0 and < 15 it can be encoded as a delta
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::String, 6)));
+ assert_success!(o_prot.write_field_end());
+
+ // now, finish the struct off
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ let expected: [u8; 4] = [
+ 0x15, /* field delta (1) | field type */
+ 0x1A, /* field delta (1) | field type */
+ 0x48, /* field delta (4) | field type */
+ 0x00 /* field stop */,
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_struct_with_non_zero_initial_field_and_delta_fields() {
+ let (mut i_prot, mut o_prot) = test_objects();
+
+ // no bytes should be written however
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // write three fields with tiny field ids
+ // since they're small the field ids will be encoded as deltas
+
+ // gets a delta write
+ let field_ident_1 = TFieldIdentifier::new("foo", TType::I32, 1);
+ assert_success!(o_prot.write_field_begin(&field_ident_1));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta > 0 and < 15 it can be encoded as a delta
+ let field_ident_2 = TFieldIdentifier::new("foo", TType::Set, 2);
+ assert_success!(o_prot.write_field_begin(&field_ident_2));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta > 0 and < 15 it can be encoded as a delta
+ let field_ident_3 = TFieldIdentifier::new("foo", TType::String, 6);
+ assert_success!(o_prot.write_field_begin(&field_ident_3));
+ assert_success!(o_prot.write_field_end());
+
+ // now, finish the struct off
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ // read the struct back
+ assert_success!(i_prot.read_struct_begin());
+
+ let read_ident_1 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_1,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_1
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_2 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_2,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_2
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_3 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_3,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_3
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_4 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_4,
+ TFieldIdentifier {
+ name: None,
+ field_type: TType::Stop,
+ id: None,
+ }
+ );
+
+ assert_success!(i_prot.read_struct_end());
+ }
+
+ #[test]
+ fn must_write_struct_with_long_fields() {
+ let (_, mut o_prot) = test_objects();
+
+ // no bytes should be written however
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // write three fields with field ids that cannot be encoded as deltas
+
+ // since this is the first field (and it's zero) it gets the full varint write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I32, 0)));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta is > 15 it is encoded as a zig-zag varint
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 16)));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta is > 15 it is encoded as a zig-zag varint
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Set, 99)));
+ assert_success!(o_prot.write_field_end());
+
+ // now, finish the struct off
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ let expected: [u8; 8] = [
+ 0x05, /* field type */
+ 0x00, /* first field id */
+ 0x06, /* field type */
+ 0x20, /* zig-zag varint field id */
+ 0x0A, /* field type */
+ 0xC6,
+ 0x01, /* zig-zag varint field id */
+ 0x00 /* field stop */,
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_struct_with_long_fields() {
+ let (mut i_prot, mut o_prot) = test_objects();
+
+ // no bytes should be written however
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // write three fields with field ids that cannot be encoded as deltas
+
+ // since this is the first field (and it's zero) it gets the full varint write
+ let field_ident_1 = TFieldIdentifier::new("foo", TType::I32, 0);
+ assert_success!(o_prot.write_field_begin(&field_ident_1));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta is > 15 it is encoded as a zig-zag varint
+ let field_ident_2 = TFieldIdentifier::new("foo", TType::I64, 16);
+ assert_success!(o_prot.write_field_begin(&field_ident_2));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta is > 15 it is encoded as a zig-zag varint
+ let field_ident_3 = TFieldIdentifier::new("foo", TType::Set, 99);
+ assert_success!(o_prot.write_field_begin(&field_ident_3));
+ assert_success!(o_prot.write_field_end());
+
+ // now, finish the struct off
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ // read the struct back
+ assert_success!(i_prot.read_struct_begin());
+
+ let read_ident_1 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_1,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_1
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_2 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_2,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_2
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_3 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_3,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_3
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_4 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_4,
+ TFieldIdentifier {
+ name: None,
+ field_type: TType::Stop,
+ id: None,
+ }
+ );
+
+ assert_success!(i_prot.read_struct_end());
+ }
+
+ #[test]
+ fn must_write_struct_with_mix_of_long_and_delta_fields() {
+ let (_, mut o_prot) = test_objects();
+
+ // no bytes should be written however
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // write three fields with field ids that cannot be encoded as deltas
+
+ // since the delta is > 0 and < 15 it gets a delta write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 1)));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta > 0 and < 15 it gets a delta write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I32, 9)));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta is > 15 it is encoded as a zig-zag varint
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Set, 1000)));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta is > 15 it is encoded as a zig-zag varint
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Set, 2001)));
+ assert_success!(o_prot.write_field_end());
+
+ // since this is only 3 up from the previous it is recorded as a delta
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Set, 2004)));
+ assert_success!(o_prot.write_field_end());
+
+ // now, finish the struct off
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ let expected: [u8; 10] = [
+ 0x16, /* field delta (1) | field type */
+ 0x85, /* field delta (8) | field type */
+ 0x0A, /* field type */
+ 0xD0,
+ 0x0F, /* zig-zag varint field id */
+ 0x0A, /* field type */
+ 0xA2,
+ 0x1F, /* zig-zag varint field id */
+ 0x3A, /* field delta (3) | field type */
+ 0x00 /* field stop */,
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_struct_with_mix_of_long_and_delta_fields() {
+ let (mut i_prot, mut o_prot) = test_objects();
+
+ // no bytes should be written however
+ let struct_ident = TStructIdentifier::new("foo");
+ assert_success!(o_prot.write_struct_begin(&struct_ident));
+
+ // write three fields with field ids that cannot be encoded as deltas
+
+ // since the delta is > 0 and < 15 it gets a delta write
+ let field_ident_1 = TFieldIdentifier::new("foo", TType::I64, 1);
+ assert_success!(o_prot.write_field_begin(&field_ident_1));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta > 0 and < 15 it gets a delta write
+ let field_ident_2 = TFieldIdentifier::new("foo", TType::I32, 9);
+ assert_success!(o_prot.write_field_begin(&field_ident_2));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta is > 15 it is encoded as a zig-zag varint
+ let field_ident_3 = TFieldIdentifier::new("foo", TType::Set, 1000);
+ assert_success!(o_prot.write_field_begin(&field_ident_3));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta is > 15 it is encoded as a zig-zag varint
+ let field_ident_4 = TFieldIdentifier::new("foo", TType::Set, 2001);
+ assert_success!(o_prot.write_field_begin(&field_ident_4));
+ assert_success!(o_prot.write_field_end());
+
+ // since this is only 3 up from the previous it is recorded as a delta
+ let field_ident_5 = TFieldIdentifier::new("foo", TType::Set, 2004);
+ assert_success!(o_prot.write_field_begin(&field_ident_5));
+ assert_success!(o_prot.write_field_end());
+
+ // now, finish the struct off
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ // read the struct back
+ assert_success!(i_prot.read_struct_begin());
+
+ let read_ident_1 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_1,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_1
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_2 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_2,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_2
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_3 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_3,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_3
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_4 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_4,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_4
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_5 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_5,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_5
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_6 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_6,
+ TFieldIdentifier {
+ name: None,
+ field_type: TType::Stop,
+ id: None,
+ }
+ );
+
+ assert_success!(i_prot.read_struct_end());
+ }
+
+ #[test]
+ fn must_write_nested_structs_0() {
+ // last field of the containing struct is a delta
+ // first field of the the contained struct is a delta
+
+ let (_, mut o_prot) = test_objects();
+
+ // start containing struct
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // containing struct
+ // since the delta is > 0 and < 15 it gets a delta write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 1)));
+ assert_success!(o_prot.write_field_end());
+
+ // containing struct
+ // since this delta > 0 and < 15 it gets a delta write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I32, 9)));
+ assert_success!(o_prot.write_field_end());
+
+ // start contained struct
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // contained struct
+ // since the delta is > 0 and < 15 it gets a delta write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I08, 7)));
+ assert_success!(o_prot.write_field_end());
+
+ // contained struct
+ // since this delta > 15 it gets a full write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Double, 24)));
+ assert_success!(o_prot.write_field_end());
+
+ // end contained struct
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ // end containing struct
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ let expected: [u8; 7] = [
+ 0x16, /* field delta (1) | field type */
+ 0x85, /* field delta (8) | field type */
+ 0x73, /* field delta (7) | field type */
+ 0x07, /* field type */
+ 0x30, /* zig-zag varint field id */
+ 0x00, /* field stop - contained */
+ 0x00 /* field stop - containing */,
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_nested_structs_0() {
+ // last field of the containing struct is a delta
+ // first field of the the contained struct is a delta
+
+ let (mut i_prot, mut o_prot) = test_objects();
+
+ // start containing struct
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // containing struct
+ // since the delta is > 0 and < 15 it gets a delta write
+ let field_ident_1 = TFieldIdentifier::new("foo", TType::I64, 1);
+ assert_success!(o_prot.write_field_begin(&field_ident_1));
+ assert_success!(o_prot.write_field_end());
+
+ // containing struct
+ // since this delta > 0 and < 15 it gets a delta write
+ let field_ident_2 = TFieldIdentifier::new("foo", TType::I32, 9);
+ assert_success!(o_prot.write_field_begin(&field_ident_2));
+ assert_success!(o_prot.write_field_end());
+
+ // start contained struct
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // contained struct
+ // since the delta is > 0 and < 15 it gets a delta write
+ let field_ident_3 = TFieldIdentifier::new("foo", TType::I08, 7);
+ assert_success!(o_prot.write_field_begin(&field_ident_3));
+ assert_success!(o_prot.write_field_end());
+
+ // contained struct
+ // since this delta > 15 it gets a full write
+ let field_ident_4 = TFieldIdentifier::new("foo", TType::Double, 24);
+ assert_success!(o_prot.write_field_begin(&field_ident_4));
+ assert_success!(o_prot.write_field_end());
+
+ // end contained struct
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ // end containing struct
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ // read containing struct back
+ assert_success!(i_prot.read_struct_begin());
+
+ let read_ident_1 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_1,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_1
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_2 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_2,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_2
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ // read contained struct back
+ assert_success!(i_prot.read_struct_begin());
+
+ let read_ident_3 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_3,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_3
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_4 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_4,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_4
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ // end contained struct
+ let read_ident_6 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_6,
+ TFieldIdentifier {
+ name: None,
+ field_type: TType::Stop,
+ id: None,
+ }
+ );
+ assert_success!(i_prot.read_struct_end());
+
+ // end containing struct
+ let read_ident_7 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_7,
+ TFieldIdentifier {
+ name: None,
+ field_type: TType::Stop,
+ id: None,
+ }
+ );
+ assert_success!(i_prot.read_struct_end());
+ }
+
+ #[test]
+ fn must_write_nested_structs_1() {
+ // last field of the containing struct is a delta
+ // first field of the the contained struct is a full write
+
+ let (_, mut o_prot) = test_objects();
+
+ // start containing struct
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // containing struct
+ // since the delta is > 0 and < 15 it gets a delta write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 1)));
+ assert_success!(o_prot.write_field_end());
+
+ // containing struct
+ // since this delta > 0 and < 15 it gets a delta write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I32, 9)));
+ assert_success!(o_prot.write_field_end());
+
+ // start contained struct
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // contained struct
+ // since this delta > 15 it gets a full write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Double, 24)));
+ assert_success!(o_prot.write_field_end());
+
+ // contained struct
+ // since the delta is > 0 and < 15 it gets a delta write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I08, 27)));
+ assert_success!(o_prot.write_field_end());
+
+ // end contained struct
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ // end containing struct
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ let expected: [u8; 7] = [
+ 0x16, /* field delta (1) | field type */
+ 0x85, /* field delta (8) | field type */
+ 0x07, /* field type */
+ 0x30, /* zig-zag varint field id */
+ 0x33, /* field delta (3) | field type */
+ 0x00, /* field stop - contained */
+ 0x00 /* field stop - containing */,
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_nested_structs_1() {
+ // last field of the containing struct is a delta
+ // first field of the the contained struct is a full write
+
+ let (mut i_prot, mut o_prot) = test_objects();
+
+ // start containing struct
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // containing struct
+ // since the delta is > 0 and < 15 it gets a delta write
+ let field_ident_1 = TFieldIdentifier::new("foo", TType::I64, 1);
+ assert_success!(o_prot.write_field_begin(&field_ident_1));
+ assert_success!(o_prot.write_field_end());
+
+ // containing struct
+ // since this delta > 0 and < 15 it gets a delta write
+ let field_ident_2 = TFieldIdentifier::new("foo", TType::I32, 9);
+ assert_success!(o_prot.write_field_begin(&field_ident_2));
+ assert_success!(o_prot.write_field_end());
+
+ // start contained struct
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // contained struct
+ // since this delta > 15 it gets a full write
+ let field_ident_3 = TFieldIdentifier::new("foo", TType::Double, 24);
+ assert_success!(o_prot.write_field_begin(&field_ident_3));
+ assert_success!(o_prot.write_field_end());
+
+ // contained struct
+ // since the delta is > 0 and < 15 it gets a delta write
+ let field_ident_4 = TFieldIdentifier::new("foo", TType::I08, 27);
+ assert_success!(o_prot.write_field_begin(&field_ident_4));
+ assert_success!(o_prot.write_field_end());
+
+ // end contained struct
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ // end containing struct
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ // read containing struct back
+ assert_success!(i_prot.read_struct_begin());
+
+ let read_ident_1 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_1,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_1
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_2 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_2,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_2
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ // read contained struct back
+ assert_success!(i_prot.read_struct_begin());
+
+ let read_ident_3 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_3,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_3
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_4 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_4,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_4
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ // end contained struct
+ let read_ident_6 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_6,
+ TFieldIdentifier {
+ name: None,
+ field_type: TType::Stop,
+ id: None,
+ }
+ );
+ assert_success!(i_prot.read_struct_end());
+
+ // end containing struct
+ let read_ident_7 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_7,
+ TFieldIdentifier {
+ name: None,
+ field_type: TType::Stop,
+ id: None,
+ }
+ );
+ assert_success!(i_prot.read_struct_end());
+ }
+
+ #[test]
+ fn must_write_nested_structs_2() {
+ // last field of the containing struct is a full write
+ // first field of the the contained struct is a delta write
+
+ let (_, mut o_prot) = test_objects();
+
+ // start containing struct
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // containing struct
+ // since the delta is > 0 and < 15 it gets a delta write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 1)));
+ assert_success!(o_prot.write_field_end());
+
+ // containing struct
+ // since this delta > 15 it gets a full write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::String, 21)));
+ assert_success!(o_prot.write_field_end());
+
+ // start contained struct
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // contained struct
+ // since this delta > 0 and < 15 it gets a delta write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Double, 7)));
+ assert_success!(o_prot.write_field_end());
+
+ // contained struct
+ // since the delta is > 0 and < 15 it gets a delta write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I08, 10)));
+ assert_success!(o_prot.write_field_end());
+
+ // end contained struct
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ // end containing struct
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ let expected: [u8; 7] = [
+ 0x16, /* field delta (1) | field type */
+ 0x08, /* field type */
+ 0x2A, /* zig-zag varint field id */
+ 0x77, /* field delta(7) | field type */
+ 0x33, /* field delta (3) | field type */
+ 0x00, /* field stop - contained */
+ 0x00 /* field stop - containing */,
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_nested_structs_2() {
+ let (mut i_prot, mut o_prot) = test_objects();
+
+ // start containing struct
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // containing struct
+ // since the delta is > 0 and < 15 it gets a delta write
+ let field_ident_1 = TFieldIdentifier::new("foo", TType::I64, 1);
+ assert_success!(o_prot.write_field_begin(&field_ident_1));
+ assert_success!(o_prot.write_field_end());
+
+ // containing struct
+ // since this delta > 15 it gets a full write
+ let field_ident_2 = TFieldIdentifier::new("foo", TType::String, 21);
+ assert_success!(o_prot.write_field_begin(&field_ident_2));
+ assert_success!(o_prot.write_field_end());
+
+ // start contained struct
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // contained struct
+ // since this delta > 0 and < 15 it gets a delta write
+ let field_ident_3 = TFieldIdentifier::new("foo", TType::Double, 7);
+ assert_success!(o_prot.write_field_begin(&field_ident_3));
+ assert_success!(o_prot.write_field_end());
+
+ // contained struct
+ // since the delta is > 0 and < 15 it gets a delta write
+ let field_ident_4 = TFieldIdentifier::new("foo", TType::I08, 10);
+ assert_success!(o_prot.write_field_begin(&field_ident_4));
+ assert_success!(o_prot.write_field_end());
+
+ // end contained struct
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ // end containing struct
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ // read containing struct back
+ assert_success!(i_prot.read_struct_begin());
+
+ let read_ident_1 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_1,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_1
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_2 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_2,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_2
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ // read contained struct back
+ assert_success!(i_prot.read_struct_begin());
+
+ let read_ident_3 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_3,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_3
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_4 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_4,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_4
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ // end contained struct
+ let read_ident_6 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_6,
+ TFieldIdentifier {
+ name: None,
+ field_type: TType::Stop,
+ id: None,
+ }
+ );
+ assert_success!(i_prot.read_struct_end());
+
+ // end containing struct
+ let read_ident_7 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_7,
+ TFieldIdentifier {
+ name: None,
+ field_type: TType::Stop,
+ id: None,
+ }
+ );
+ assert_success!(i_prot.read_struct_end());
+ }
+
+ #[test]
+ fn must_write_nested_structs_3() {
+ // last field of the containing struct is a full write
+ // first field of the the contained struct is a full write
+
+ let (_, mut o_prot) = test_objects();
+
+ // start containing struct
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // containing struct
+ // since the delta is > 0 and < 15 it gets a delta write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 1)));
+ assert_success!(o_prot.write_field_end());
+
+ // containing struct
+ // since this delta > 15 it gets a full write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::String, 21)));
+ assert_success!(o_prot.write_field_end());
+
+ // start contained struct
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // contained struct
+ // since this delta > 15 it gets a full write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Double, 21)));
+ assert_success!(o_prot.write_field_end());
+
+ // contained struct
+ // since the delta is > 0 and < 15 it gets a delta write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I08, 27)));
+ assert_success!(o_prot.write_field_end());
+
+ // end contained struct
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ // end containing struct
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ let expected: [u8; 8] = [
+ 0x16, /* field delta (1) | field type */
+ 0x08, /* field type */
+ 0x2A, /* zig-zag varint field id */
+ 0x07, /* field type */
+ 0x2A, /* zig-zag varint field id */
+ 0x63, /* field delta (6) | field type */
+ 0x00, /* field stop - contained */
+ 0x00 /* field stop - containing */,
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_nested_structs_3() {
+ // last field of the containing struct is a full write
+ // first field of the the contained struct is a full write
+
+ let (mut i_prot, mut o_prot) = test_objects();
+
+ // start containing struct
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // containing struct
+ // since the delta is > 0 and < 15 it gets a delta write
+ let field_ident_1 = TFieldIdentifier::new("foo", TType::I64, 1);
+ assert_success!(o_prot.write_field_begin(&field_ident_1));
+ assert_success!(o_prot.write_field_end());
+
+ // containing struct
+ // since this delta > 15 it gets a full write
+ let field_ident_2 = TFieldIdentifier::new("foo", TType::String, 21);
+ assert_success!(o_prot.write_field_begin(&field_ident_2));
+ assert_success!(o_prot.write_field_end());
+
+ // start contained struct
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // contained struct
+ // since this delta > 15 it gets a full write
+ let field_ident_3 = TFieldIdentifier::new("foo", TType::Double, 21);
+ assert_success!(o_prot.write_field_begin(&field_ident_3));
+ assert_success!(o_prot.write_field_end());
+
+ // contained struct
+ // since the delta is > 0 and < 15 it gets a delta write
+ let field_ident_4 = TFieldIdentifier::new("foo", TType::I08, 27);
+ assert_success!(o_prot.write_field_begin(&field_ident_4));
+ assert_success!(o_prot.write_field_end());
+
+ // end contained struct
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ // end containing struct
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ // read containing struct back
+ assert_success!(i_prot.read_struct_begin());
+
+ let read_ident_1 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_1,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_1
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_2 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_2,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_2
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ // read contained struct back
+ assert_success!(i_prot.read_struct_begin());
+
+ let read_ident_3 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_3,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_3
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_4 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_4,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_4
+ }
+ );
+ assert_success!(i_prot.read_field_end());
+
+ // end contained struct
+ let read_ident_6 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_6,
+ TFieldIdentifier {
+ name: None,
+ field_type: TType::Stop,
+ id: None,
+ }
+ );
+ assert_success!(i_prot.read_struct_end());
+
+ // end containing struct
+ let read_ident_7 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_7,
+ TFieldIdentifier {
+ name: None,
+ field_type: TType::Stop,
+ id: None,
+ }
+ );
+ assert_success!(i_prot.read_struct_end());
+ }
+
+ #[test]
+ fn must_write_bool_field() {
+ let (_, mut o_prot) = test_objects();
+
+ // no bytes should be written however
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+ // write three fields with field ids that cannot be encoded as deltas
+
+ // since the delta is > 0 and < 16 it gets a delta write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 1)));
+ assert_success!(o_prot.write_bool(true));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta > 0 and < 15 it gets a delta write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 9)));
+ assert_success!(o_prot.write_bool(false));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta > 15 it gets a full write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 26)));
+ assert_success!(o_prot.write_bool(true));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta > 15 it gets a full write
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 45)));
+ assert_success!(o_prot.write_bool(false));
+ assert_success!(o_prot.write_field_end());
+
+ // now, finish the struct off
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ let expected: [u8; 7] = [
+ 0x11, /* field delta (1) | true */
+ 0x82, /* field delta (8) | false */
+ 0x01, /* true */
+ 0x34, /* field id */
+ 0x02, /* false */
+ 0x5A, /* field id */
+ 0x00 /* stop field */,
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_bool_field() {
+ let (mut i_prot, mut o_prot) = test_objects();
+
+ // no bytes should be written however
+ let struct_ident = TStructIdentifier::new("foo");
+ assert_success!(o_prot.write_struct_begin(&struct_ident));
+
+ // write two fields
+
+ // since the delta is > 0 and < 16 it gets a delta write
+ let field_ident_1 = TFieldIdentifier::new("foo", TType::Bool, 1);
+ assert_success!(o_prot.write_field_begin(&field_ident_1));
+ assert_success!(o_prot.write_bool(true));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta > 0 and < 15 it gets a delta write
+ let field_ident_2 = TFieldIdentifier::new("foo", TType::Bool, 9);
+ assert_success!(o_prot.write_field_begin(&field_ident_2));
+ assert_success!(o_prot.write_bool(false));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta > 15 it gets a full write
+ let field_ident_3 = TFieldIdentifier::new("foo", TType::Bool, 26);
+ assert_success!(o_prot.write_field_begin(&field_ident_3));
+ assert_success!(o_prot.write_bool(true));
+ assert_success!(o_prot.write_field_end());
+
+ // since this delta > 15 it gets a full write
+ let field_ident_4 = TFieldIdentifier::new("foo", TType::Bool, 45);
+ assert_success!(o_prot.write_field_begin(&field_ident_4));
+ assert_success!(o_prot.write_bool(false));
+ assert_success!(o_prot.write_field_end());
+
+ // now, finish the struct off
+ assert_success!(o_prot.write_field_stop());
+ assert_success!(o_prot.write_struct_end());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ // read the struct back
+ assert_success!(i_prot.read_struct_begin());
+
+ let read_ident_1 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_1,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_1
+ }
+ );
+ let read_value_1 = assert_success!(i_prot.read_bool());
+ assert_eq!(read_value_1, true);
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_2 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_2,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_2
+ }
+ );
+ let read_value_2 = assert_success!(i_prot.read_bool());
+ assert_eq!(read_value_2, false);
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_3 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_3,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_3
+ }
+ );
+ let read_value_3 = assert_success!(i_prot.read_bool());
+ assert_eq!(read_value_3, true);
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_4 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_4,
+ TFieldIdentifier {
+ name: None,
+ ..field_ident_4
+ }
+ );
+ let read_value_4 = assert_success!(i_prot.read_bool());
+ assert_eq!(read_value_4, false);
+ assert_success!(i_prot.read_field_end());
+
+ let read_ident_5 = assert_success!(i_prot.read_field_begin());
+ assert_eq!(
+ read_ident_5,
+ TFieldIdentifier {
+ name: None,
+ field_type: TType::Stop,
+ id: None,
+ }
+ );
+
+ assert_success!(i_prot.read_struct_end());
+ }
+
+ #[test]
+ #[should_panic]
+ fn must_fail_if_write_field_end_without_writing_bool_value() {
+ let (_, mut o_prot) = test_objects();
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 1)));
+ o_prot.write_field_end().unwrap();
+ }
+
+ #[test]
+ #[should_panic]
+ fn must_fail_if_write_stop_field_without_writing_bool_value() {
+ let (_, mut o_prot) = test_objects();
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 1)));
+ o_prot.write_field_stop().unwrap();
+ }
+
+ #[test]
+ #[should_panic]
+ fn must_fail_if_write_struct_end_without_writing_bool_value() {
+ let (_, mut o_prot) = test_objects();
+ assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+ assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 1)));
+ o_prot.write_struct_end().unwrap();
+ }
+
+ #[test]
+ #[should_panic]
+ fn must_fail_if_write_struct_end_without_any_fields() {
+ let (_, mut o_prot) = test_objects();
+ o_prot.write_struct_end().unwrap();
+ }
+
+ #[test]
+ fn must_write_field_end() {
+ assert_no_write(|o| o.write_field_end());
+ }
+
+ #[test]
+ fn must_write_small_sized_list_begin() {
+ let (_, mut o_prot) = test_objects();
+
+ assert_success!(o_prot.write_list_begin(&TListIdentifier::new(TType::I64, 4)));
+
+ let expected: [u8; 1] = [0x46 /* size | elem_type */];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_small_sized_list_begin() {
+ let (mut i_prot, mut o_prot) = test_objects();
+
+ let ident = TListIdentifier::new(TType::I08, 10);
+
+ assert_success!(o_prot.write_list_begin(&ident));
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ let res = assert_success!(i_prot.read_list_begin());
+ assert_eq!(&res, &ident);
+ }
+
+ #[test]
+ fn must_write_large_sized_list_begin() {
+ let (_, mut o_prot) = test_objects();
+
+ let res = o_prot.write_list_begin(&TListIdentifier::new(TType::List, 9999));
+ assert!(res.is_ok());
+
+ let expected: [u8; 3] = [
+ 0xF9, /* 0xF0 | elem_type */
+ 0x8F, 0x4E, /* size as varint */
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_large_sized_list_begin() {
+ let (mut i_prot, mut o_prot) = test_objects();
+
+ let ident = TListIdentifier::new(TType::Set, 47381);
+
+ assert_success!(o_prot.write_list_begin(&ident));
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ let res = assert_success!(i_prot.read_list_begin());
+ assert_eq!(&res, &ident);
+ }
+
+ #[test]
+ fn must_write_list_end() {
+ assert_no_write(|o| o.write_list_end());
+ }
+
+ #[test]
+ fn must_write_small_sized_set_begin() {
+ let (_, mut o_prot) = test_objects();
+
+ assert_success!(o_prot.write_set_begin(&TSetIdentifier::new(TType::Struct, 2)));
+
+ let expected: [u8; 1] = [0x2C /* size | elem_type */];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_small_sized_set_begin() {
+ let (mut i_prot, mut o_prot) = test_objects();
+
+ let ident = TSetIdentifier::new(TType::I16, 7);
+
+ assert_success!(o_prot.write_set_begin(&ident));
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ let res = assert_success!(i_prot.read_set_begin());
+ assert_eq!(&res, &ident);
+ }
+
+ #[test]
+ fn must_write_large_sized_set_begin() {
+ let (_, mut o_prot) = test_objects();
+
+ assert_success!(o_prot.write_set_begin(&TSetIdentifier::new(TType::Double, 23891)));
+
+ let expected: [u8; 4] = [
+ 0xF7, /* 0xF0 | elem_type */
+ 0xD3, 0xBA, 0x01, /* size as varint */
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_large_sized_set_begin() {
+ let (mut i_prot, mut o_prot) = test_objects();
+
+ let ident = TSetIdentifier::new(TType::Map, 3928429);
+
+ assert_success!(o_prot.write_set_begin(&ident));
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ let res = assert_success!(i_prot.read_set_begin());
+ assert_eq!(&res, &ident);
+ }
+
+ #[test]
+ fn must_write_set_end() {
+ assert_no_write(|o| o.write_set_end());
+ }
+
+ #[test]
+ fn must_write_zero_sized_map_begin() {
+ let (_, mut o_prot) = test_objects();
+
+ assert_success!(o_prot.write_map_begin(&TMapIdentifier::new(TType::String, TType::I32, 0)));
+
+ let expected: [u8; 1] = [0x00]; // since size is zero we don't write anything
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_read_zero_sized_map_begin() {
+ let (mut i_prot, mut o_prot) = test_objects();
+
+ assert_success!(o_prot.write_map_begin(&TMapIdentifier::new(TType::Double, TType::I32, 0)));
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ let res = assert_success!(i_prot.read_map_begin());
+ assert_eq!(
+ &res,
+ &TMapIdentifier {
+ key_type: None,
+ value_type: None,
+ size: 0,
+ }
+ );
+ }
+
+ #[test]
+ fn must_write_map_begin() {
+ let (_, mut o_prot) = test_objects();
+
+ assert_success!(o_prot.write_map_begin(&TMapIdentifier::new(
+ TType::Double,
+ TType::String,
+ 238
+ )));
+
+ let expected: [u8; 3] = [
+ 0xEE, 0x01, /* size as varint */
+ 0x78, /* key type | val type */
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_map_begin() {
+ let (mut i_prot, mut o_prot) = test_objects();
+
+ let ident = TMapIdentifier::new(TType::Map, TType::List, 1928349);
+
+ assert_success!(o_prot.write_map_begin(&ident));
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ let res = assert_success!(i_prot.read_map_begin());
+ assert_eq!(&res, &ident);
+ }
+
+ #[test]
+ fn must_write_map_end() {
+ assert_no_write(|o| o.write_map_end());
+ }
+
+ #[test]
+ fn must_write_map_with_bool_key_and_value() {
+ let (_, mut o_prot) = test_objects();
+
+ assert_success!(o_prot.write_map_begin(&TMapIdentifier::new(TType::Bool, TType::Bool, 1)));
+ assert_success!(o_prot.write_bool(true));
+ assert_success!(o_prot.write_bool(false));
+ assert_success!(o_prot.write_map_end());
+
+ let expected: [u8; 4] = [
+ 0x01, /* size as varint */
+ 0x11, /* key type | val type */
+ 0x01, /* key: true */
+ 0x02, /* val: false */
+ ];
+
+ assert_eq_written_bytes!(o_prot, expected);
+ }
+
+ #[test]
+ fn must_round_trip_map_with_bool_value() {
+ let (mut i_prot, mut o_prot) = test_objects();
+
+ let map_ident = TMapIdentifier::new(TType::Bool, TType::Bool, 2);
+ assert_success!(o_prot.write_map_begin(&map_ident));
+ assert_success!(o_prot.write_bool(true));
+ assert_success!(o_prot.write_bool(false));
+ assert_success!(o_prot.write_bool(false));
+ assert_success!(o_prot.write_bool(true));
+ assert_success!(o_prot.write_map_end());
+
+ copy_write_buffer_to_read_buffer!(o_prot);
+
+ // map header
+ let rcvd_ident = assert_success!(i_prot.read_map_begin());
+ assert_eq!(&rcvd_ident, &map_ident);
+ // key 1
+ let b = assert_success!(i_prot.read_bool());
+ assert_eq!(b, true);
+ // val 1
+ let b = assert_success!(i_prot.read_bool());
+ assert_eq!(b, false);
+ // key 2
+ let b = assert_success!(i_prot.read_bool());
+ assert_eq!(b, false);
+ // val 2
+ let b = assert_success!(i_prot.read_bool());
+ assert_eq!(b, true);
+ // map end
+ assert_success!(i_prot.read_map_end());
+ }
+
+ #[test]
+ fn must_read_map_end() {
+ let (mut i_prot, _) = test_objects();
+ assert!(i_prot.read_map_end().is_ok()); // will blow up if we try to read from empty buffer
+ }
+
+ fn test_objects() -> (
+ TCompactInputProtocol<ReadHalf<TBufferChannel>>,
+ TCompactOutputProtocol<WriteHalf<TBufferChannel>>,
+ ) {
+ let mem = TBufferChannel::with_capacity(80, 80);
+
+ let (r_mem, w_mem) = mem.split().unwrap();
+
+ let i_prot = TCompactInputProtocol::new(r_mem);
+ let o_prot = TCompactOutputProtocol::new(w_mem);
+
+ (i_prot, o_prot)
+ }
+
+ fn assert_no_write<F>(mut write_fn: F)
+ where
+ F: FnMut(&mut TCompactOutputProtocol<WriteHalf<TBufferChannel>>) -> ::Result<()>,
+ {
+ let (_, mut o_prot) = test_objects();
+ assert!(write_fn(&mut o_prot).is_ok());
+ assert_eq!(o_prot.transport.write_bytes().len(), 0);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/rs/src/protocol/mod.rs b/src/jaegertracing/thrift/lib/rs/src/protocol/mod.rs
new file mode 100644
index 000000000..2d8513f2c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/src/protocol/mod.rs
@@ -0,0 +1,968 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Types used to send and receive primitives between a Thrift client and server.
+//!
+//! # Examples
+//!
+//! Create and use a `TInputProtocol`.
+//!
+//! ```no_run
+//! use thrift::protocol::{TBinaryInputProtocol, TInputProtocol};
+//! use thrift::transport::TTcpChannel;
+//!
+//! // create the I/O channel
+//! let mut channel = TTcpChannel::new();
+//! channel.open("127.0.0.1:9090").unwrap();
+//!
+//! // create the protocol to decode bytes into types
+//! let mut protocol = TBinaryInputProtocol::new(channel, true);
+//!
+//! // read types from the wire
+//! let field_identifier = protocol.read_field_begin().unwrap();
+//! let field_contents = protocol.read_string().unwrap();
+//! let field_end = protocol.read_field_end().unwrap();
+//! ```
+//!
+//! Create and use a `TOutputProtocol`.
+//!
+//! ```no_run
+//! use thrift::protocol::{TBinaryOutputProtocol, TFieldIdentifier, TOutputProtocol, TType};
+//! use thrift::transport::TTcpChannel;
+//!
+//! // create the I/O channel
+//! let mut channel = TTcpChannel::new();
+//! channel.open("127.0.0.1:9090").unwrap();
+//!
+//! // create the protocol to encode types into bytes
+//! let mut protocol = TBinaryOutputProtocol::new(channel, true);
+//!
+//! // write types
+//! protocol.write_field_begin(&TFieldIdentifier::new("string_thing", TType::String, 1)).unwrap();
+//! protocol.write_string("foo").unwrap();
+//! protocol.write_field_end().unwrap();
+//! ```
+
+use std::convert::{From, TryFrom};
+use std::fmt;
+use std::fmt::{Display, Formatter};
+
+use transport::{TReadTransport, TWriteTransport};
+use {ProtocolError, ProtocolErrorKind};
+
+#[cfg(test)]
+macro_rules! assert_eq_written_bytes {
+ ($o_prot:ident, $expected_bytes:ident) => {{
+ assert_eq!($o_prot.transport.write_bytes(), &$expected_bytes);
+ }};
+}
+
+// FIXME: should take both read and write
+#[cfg(test)]
+macro_rules! copy_write_buffer_to_read_buffer {
+ ($o_prot:ident) => {{
+ $o_prot.transport.copy_write_buffer_to_read_buffer();
+ }};
+}
+
+#[cfg(test)]
+macro_rules! set_readable_bytes {
+ ($i_prot:ident, $bytes:expr) => {
+ $i_prot.transport.set_readable_bytes($bytes);
+ };
+}
+
+mod binary;
+mod compact;
+mod multiplexed;
+mod stored;
+
+pub use self::binary::{
+ TBinaryInputProtocol, TBinaryInputProtocolFactory, TBinaryOutputProtocol,
+ TBinaryOutputProtocolFactory,
+};
+pub use self::compact::{
+ TCompactInputProtocol, TCompactInputProtocolFactory, TCompactOutputProtocol,
+ TCompactOutputProtocolFactory,
+};
+pub use self::multiplexed::TMultiplexedOutputProtocol;
+pub use self::stored::TStoredInputProtocol;
+
+// Default maximum depth to which `TInputProtocol::skip` will skip a Thrift
+// field. A default is necessary because Thrift structs or collections may
+// contain nested structs and collections, which could result in indefinite
+// recursion.
+const MAXIMUM_SKIP_DEPTH: i8 = 64;
+
+/// Converts a stream of bytes into Thrift identifiers, primitives,
+/// containers, or structs.
+///
+/// This trait does not deal with higher-level Thrift concepts like structs or
+/// exceptions - only with primitives and message or container boundaries. Once
+/// bytes are read they are deserialized and an identifier (for example
+/// `TMessageIdentifier`) or a primitive is returned.
+///
+/// All methods return a `thrift::Result`. If an `Err` is returned the protocol
+/// instance and its underlying transport should be terminated.
+///
+/// # Examples
+///
+/// Create and use a `TInputProtocol`
+///
+/// ```no_run
+/// use thrift::protocol::{TBinaryInputProtocol, TInputProtocol};
+/// use thrift::transport::TTcpChannel;
+///
+/// let mut channel = TTcpChannel::new();
+/// channel.open("127.0.0.1:9090").unwrap();
+///
+/// let mut protocol = TBinaryInputProtocol::new(channel, true);
+///
+/// let field_identifier = protocol.read_field_begin().unwrap();
+/// let field_contents = protocol.read_string().unwrap();
+/// let field_end = protocol.read_field_end().unwrap();
+/// ```
+pub trait TInputProtocol {
+ /// Read the beginning of a Thrift message.
+ fn read_message_begin(&mut self) -> ::Result<TMessageIdentifier>;
+ /// Read the end of a Thrift message.
+ fn read_message_end(&mut self) -> ::Result<()>;
+ /// Read the beginning of a Thrift struct.
+ fn read_struct_begin(&mut self) -> ::Result<Option<TStructIdentifier>>;
+ /// Read the end of a Thrift struct.
+ fn read_struct_end(&mut self) -> ::Result<()>;
+ /// Read the beginning of a Thrift struct field.
+ fn read_field_begin(&mut self) -> ::Result<TFieldIdentifier>;
+ /// Read the end of a Thrift struct field.
+ fn read_field_end(&mut self) -> ::Result<()>;
+ /// Read a bool.
+ fn read_bool(&mut self) -> ::Result<bool>;
+ /// Read a fixed-length byte array.
+ fn read_bytes(&mut self) -> ::Result<Vec<u8>>;
+ /// Read a word.
+ fn read_i8(&mut self) -> ::Result<i8>;
+ /// Read a 16-bit signed integer.
+ fn read_i16(&mut self) -> ::Result<i16>;
+ /// Read a 32-bit signed integer.
+ fn read_i32(&mut self) -> ::Result<i32>;
+ /// Read a 64-bit signed integer.
+ fn read_i64(&mut self) -> ::Result<i64>;
+ /// Read a 64-bit float.
+ fn read_double(&mut self) -> ::Result<f64>;
+ /// Read a fixed-length string (not null terminated).
+ fn read_string(&mut self) -> ::Result<String>;
+ /// Read the beginning of a list.
+ fn read_list_begin(&mut self) -> ::Result<TListIdentifier>;
+ /// Read the end of a list.
+ fn read_list_end(&mut self) -> ::Result<()>;
+ /// Read the beginning of a set.
+ fn read_set_begin(&mut self) -> ::Result<TSetIdentifier>;
+ /// Read the end of a set.
+ fn read_set_end(&mut self) -> ::Result<()>;
+ /// Read the beginning of a map.
+ fn read_map_begin(&mut self) -> ::Result<TMapIdentifier>;
+ /// Read the end of a map.
+ fn read_map_end(&mut self) -> ::Result<()>;
+ /// Skip a field with type `field_type` recursively until the default
+ /// maximum skip depth is reached.
+ fn skip(&mut self, field_type: TType) -> ::Result<()> {
+ self.skip_till_depth(field_type, MAXIMUM_SKIP_DEPTH)
+ }
+ /// Skip a field with type `field_type` recursively up to `depth` levels.
+ fn skip_till_depth(&mut self, field_type: TType, depth: i8) -> ::Result<()> {
+ if depth == 0 {
+ return Err(::Error::Protocol(ProtocolError {
+ kind: ProtocolErrorKind::DepthLimit,
+ message: format!("cannot parse past {:?}", field_type),
+ }));
+ }
+
+ match field_type {
+ TType::Bool => self.read_bool().map(|_| ()),
+ TType::I08 => self.read_i8().map(|_| ()),
+ TType::I16 => self.read_i16().map(|_| ()),
+ TType::I32 => self.read_i32().map(|_| ()),
+ TType::I64 => self.read_i64().map(|_| ()),
+ TType::Double => self.read_double().map(|_| ()),
+ TType::String => self.read_string().map(|_| ()),
+ TType::Struct => {
+ self.read_struct_begin()?;
+ loop {
+ let field_ident = self.read_field_begin()?;
+ if field_ident.field_type == TType::Stop {
+ break;
+ }
+ self.skip_till_depth(field_ident.field_type, depth - 1)?;
+ }
+ self.read_struct_end()
+ }
+ TType::List => {
+ let list_ident = self.read_list_begin()?;
+ for _ in 0..list_ident.size {
+ self.skip_till_depth(list_ident.element_type, depth - 1)?;
+ }
+ self.read_list_end()
+ }
+ TType::Set => {
+ let set_ident = self.read_set_begin()?;
+ for _ in 0..set_ident.size {
+ self.skip_till_depth(set_ident.element_type, depth - 1)?;
+ }
+ self.read_set_end()
+ }
+ TType::Map => {
+ let map_ident = self.read_map_begin()?;
+ for _ in 0..map_ident.size {
+ let key_type = map_ident
+ .key_type
+ .expect("non-zero sized map should contain key type");
+ let val_type = map_ident
+ .value_type
+ .expect("non-zero sized map should contain value type");
+ self.skip_till_depth(key_type, depth - 1)?;
+ self.skip_till_depth(val_type, depth - 1)?;
+ }
+ self.read_map_end()
+ }
+ u => Err(::Error::Protocol(ProtocolError {
+ kind: ProtocolErrorKind::Unknown,
+ message: format!("cannot skip field type {:?}", &u),
+ })),
+ }
+ }
+
+ // utility (DO NOT USE IN GENERATED CODE!!!!)
+ //
+
+ /// Read an unsigned byte.
+ ///
+ /// This method should **never** be used in generated code.
+ fn read_byte(&mut self) -> ::Result<u8>;
+}
+
+/// Converts Thrift identifiers, primitives, containers or structs into a
+/// stream of bytes.
+///
+/// This trait does not deal with higher-level Thrift concepts like structs or
+/// exceptions - only with primitives and message or container boundaries.
+/// Write methods take an identifier (for example, `TMessageIdentifier`) or a
+/// primitive. Any or all of the fields in an identifier may be omitted when
+/// writing to the transport. Write methods may even be noops. All of this is
+/// transparent to the caller; as long as a matching `TInputProtocol`
+/// implementation is used, received messages will be decoded correctly.
+///
+/// All methods return a `thrift::Result`. If an `Err` is returned the protocol
+/// instance and its underlying transport should be terminated.
+///
+/// # Examples
+///
+/// Create and use a `TOutputProtocol`
+///
+/// ```no_run
+/// use thrift::protocol::{TBinaryOutputProtocol, TFieldIdentifier, TOutputProtocol, TType};
+/// use thrift::transport::TTcpChannel;
+///
+/// let mut channel = TTcpChannel::new();
+/// channel.open("127.0.0.1:9090").unwrap();
+///
+/// let mut protocol = TBinaryOutputProtocol::new(channel, true);
+///
+/// protocol.write_field_begin(&TFieldIdentifier::new("string_thing", TType::String, 1)).unwrap();
+/// protocol.write_string("foo").unwrap();
+/// protocol.write_field_end().unwrap();
+/// ```
+pub trait TOutputProtocol {
+ /// Write the beginning of a Thrift message.
+ fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> ::Result<()>;
+ /// Write the end of a Thrift message.
+ fn write_message_end(&mut self) -> ::Result<()>;
+ /// Write the beginning of a Thrift struct.
+ fn write_struct_begin(&mut self, identifier: &TStructIdentifier) -> ::Result<()>;
+ /// Write the end of a Thrift struct.
+ fn write_struct_end(&mut self) -> ::Result<()>;
+ /// Write the beginning of a Thrift field.
+ fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> ::Result<()>;
+ /// Write the end of a Thrift field.
+ fn write_field_end(&mut self) -> ::Result<()>;
+ /// Write a STOP field indicating that all the fields in a struct have been
+ /// written.
+ fn write_field_stop(&mut self) -> ::Result<()>;
+ /// Write a bool.
+ fn write_bool(&mut self, b: bool) -> ::Result<()>;
+ /// Write a fixed-length byte array.
+ fn write_bytes(&mut self, b: &[u8]) -> ::Result<()>;
+ /// Write an 8-bit signed integer.
+ fn write_i8(&mut self, i: i8) -> ::Result<()>;
+ /// Write a 16-bit signed integer.
+ fn write_i16(&mut self, i: i16) -> ::Result<()>;
+ /// Write a 32-bit signed integer.
+ fn write_i32(&mut self, i: i32) -> ::Result<()>;
+ /// Write a 64-bit signed integer.
+ fn write_i64(&mut self, i: i64) -> ::Result<()>;
+ /// Write a 64-bit float.
+ fn write_double(&mut self, d: f64) -> ::Result<()>;
+ /// Write a fixed-length string.
+ fn write_string(&mut self, s: &str) -> ::Result<()>;
+ /// Write the beginning of a list.
+ fn write_list_begin(&mut self, identifier: &TListIdentifier) -> ::Result<()>;
+ /// Write the end of a list.
+ fn write_list_end(&mut self) -> ::Result<()>;
+ /// Write the beginning of a set.
+ fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> ::Result<()>;
+ /// Write the end of a set.
+ fn write_set_end(&mut self) -> ::Result<()>;
+ /// Write the beginning of a map.
+ fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> ::Result<()>;
+ /// Write the end of a map.
+ fn write_map_end(&mut self) -> ::Result<()>;
+ /// Flush buffered bytes to the underlying transport.
+ fn flush(&mut self) -> ::Result<()>;
+
+ // utility (DO NOT USE IN GENERATED CODE!!!!)
+ //
+
+ /// Write an unsigned byte.
+ ///
+ /// This method should **never** be used in generated code.
+ fn write_byte(&mut self, b: u8) -> ::Result<()>; // FIXME: REMOVE
+}
+
+impl<P> TInputProtocol for Box<P>
+where
+ P: TInputProtocol + ?Sized,
+{
+ fn read_message_begin(&mut self) -> ::Result<TMessageIdentifier> {
+ (**self).read_message_begin()
+ }
+
+ fn read_message_end(&mut self) -> ::Result<()> {
+ (**self).read_message_end()
+ }
+
+ fn read_struct_begin(&mut self) -> ::Result<Option<TStructIdentifier>> {
+ (**self).read_struct_begin()
+ }
+
+ fn read_struct_end(&mut self) -> ::Result<()> {
+ (**self).read_struct_end()
+ }
+
+ fn read_field_begin(&mut self) -> ::Result<TFieldIdentifier> {
+ (**self).read_field_begin()
+ }
+
+ fn read_field_end(&mut self) -> ::Result<()> {
+ (**self).read_field_end()
+ }
+
+ fn read_bool(&mut self) -> ::Result<bool> {
+ (**self).read_bool()
+ }
+
+ fn read_bytes(&mut self) -> ::Result<Vec<u8>> {
+ (**self).read_bytes()
+ }
+
+ fn read_i8(&mut self) -> ::Result<i8> {
+ (**self).read_i8()
+ }
+
+ fn read_i16(&mut self) -> ::Result<i16> {
+ (**self).read_i16()
+ }
+
+ fn read_i32(&mut self) -> ::Result<i32> {
+ (**self).read_i32()
+ }
+
+ fn read_i64(&mut self) -> ::Result<i64> {
+ (**self).read_i64()
+ }
+
+ fn read_double(&mut self) -> ::Result<f64> {
+ (**self).read_double()
+ }
+
+ fn read_string(&mut self) -> ::Result<String> {
+ (**self).read_string()
+ }
+
+ fn read_list_begin(&mut self) -> ::Result<TListIdentifier> {
+ (**self).read_list_begin()
+ }
+
+ fn read_list_end(&mut self) -> ::Result<()> {
+ (**self).read_list_end()
+ }
+
+ fn read_set_begin(&mut self) -> ::Result<TSetIdentifier> {
+ (**self).read_set_begin()
+ }
+
+ fn read_set_end(&mut self) -> ::Result<()> {
+ (**self).read_set_end()
+ }
+
+ fn read_map_begin(&mut self) -> ::Result<TMapIdentifier> {
+ (**self).read_map_begin()
+ }
+
+ fn read_map_end(&mut self) -> ::Result<()> {
+ (**self).read_map_end()
+ }
+
+ fn read_byte(&mut self) -> ::Result<u8> {
+ (**self).read_byte()
+ }
+}
+
+impl<P> TOutputProtocol for Box<P>
+where
+ P: TOutputProtocol + ?Sized,
+{
+ fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> ::Result<()> {
+ (**self).write_message_begin(identifier)
+ }
+
+ fn write_message_end(&mut self) -> ::Result<()> {
+ (**self).write_message_end()
+ }
+
+ fn write_struct_begin(&mut self, identifier: &TStructIdentifier) -> ::Result<()> {
+ (**self).write_struct_begin(identifier)
+ }
+
+ fn write_struct_end(&mut self) -> ::Result<()> {
+ (**self).write_struct_end()
+ }
+
+ fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> ::Result<()> {
+ (**self).write_field_begin(identifier)
+ }
+
+ fn write_field_end(&mut self) -> ::Result<()> {
+ (**self).write_field_end()
+ }
+
+ fn write_field_stop(&mut self) -> ::Result<()> {
+ (**self).write_field_stop()
+ }
+
+ fn write_bool(&mut self, b: bool) -> ::Result<()> {
+ (**self).write_bool(b)
+ }
+
+ fn write_bytes(&mut self, b: &[u8]) -> ::Result<()> {
+ (**self).write_bytes(b)
+ }
+
+ fn write_i8(&mut self, i: i8) -> ::Result<()> {
+ (**self).write_i8(i)
+ }
+
+ fn write_i16(&mut self, i: i16) -> ::Result<()> {
+ (**self).write_i16(i)
+ }
+
+ fn write_i32(&mut self, i: i32) -> ::Result<()> {
+ (**self).write_i32(i)
+ }
+
+ fn write_i64(&mut self, i: i64) -> ::Result<()> {
+ (**self).write_i64(i)
+ }
+
+ fn write_double(&mut self, d: f64) -> ::Result<()> {
+ (**self).write_double(d)
+ }
+
+ fn write_string(&mut self, s: &str) -> ::Result<()> {
+ (**self).write_string(s)
+ }
+
+ fn write_list_begin(&mut self, identifier: &TListIdentifier) -> ::Result<()> {
+ (**self).write_list_begin(identifier)
+ }
+
+ fn write_list_end(&mut self) -> ::Result<()> {
+ (**self).write_list_end()
+ }
+
+ fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> ::Result<()> {
+ (**self).write_set_begin(identifier)
+ }
+
+ fn write_set_end(&mut self) -> ::Result<()> {
+ (**self).write_set_end()
+ }
+
+ fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> ::Result<()> {
+ (**self).write_map_begin(identifier)
+ }
+
+ fn write_map_end(&mut self) -> ::Result<()> {
+ (**self).write_map_end()
+ }
+
+ fn flush(&mut self) -> ::Result<()> {
+ (**self).flush()
+ }
+
+ fn write_byte(&mut self, b: u8) -> ::Result<()> {
+ (**self).write_byte(b)
+ }
+}
+
+/// Helper type used by servers to create `TInputProtocol` instances for
+/// accepted client connections.
+///
+/// # Examples
+///
+/// Create a `TInputProtocolFactory` and use it to create a `TInputProtocol`.
+///
+/// ```no_run
+/// use thrift::protocol::{TBinaryInputProtocolFactory, TInputProtocolFactory};
+/// use thrift::transport::TTcpChannel;
+///
+/// let mut channel = TTcpChannel::new();
+/// channel.open("127.0.0.1:9090").unwrap();
+///
+/// let factory = TBinaryInputProtocolFactory::new();
+/// let protocol = factory.create(Box::new(channel));
+/// ```
+pub trait TInputProtocolFactory {
+ // Create a `TInputProtocol` that reads bytes from `transport`.
+ fn create(&self, transport: Box<dyn TReadTransport + Send>) -> Box<dyn TInputProtocol + Send>;
+}
+
+impl<T> TInputProtocolFactory for Box<T>
+where
+ T: TInputProtocolFactory + ?Sized,
+{
+ fn create(&self, transport: Box<dyn TReadTransport + Send>) -> Box<dyn TInputProtocol + Send> {
+ (**self).create(transport)
+ }
+}
+
+/// Helper type used by servers to create `TOutputProtocol` instances for
+/// accepted client connections.
+///
+/// # Examples
+///
+/// Create a `TOutputProtocolFactory` and use it to create a `TOutputProtocol`.
+///
+/// ```no_run
+/// use thrift::protocol::{TBinaryOutputProtocolFactory, TOutputProtocolFactory};
+/// use thrift::transport::TTcpChannel;
+///
+/// let mut channel = TTcpChannel::new();
+/// channel.open("127.0.0.1:9090").unwrap();
+///
+/// let factory = TBinaryOutputProtocolFactory::new();
+/// let protocol = factory.create(Box::new(channel));
+/// ```
+pub trait TOutputProtocolFactory {
+ /// Create a `TOutputProtocol` that writes bytes to `transport`.
+ fn create(&self, transport: Box<dyn TWriteTransport + Send>) -> Box<dyn TOutputProtocol + Send>;
+}
+
+impl<T> TOutputProtocolFactory for Box<T>
+where
+ T: TOutputProtocolFactory + ?Sized,
+{
+ fn create(&self, transport: Box<dyn TWriteTransport + Send>) -> Box<dyn TOutputProtocol + Send> {
+ (**self).create(transport)
+ }
+}
+
+/// Thrift message identifier.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct TMessageIdentifier {
+ /// Service call the message is associated with.
+ pub name: String,
+ /// Message type.
+ pub message_type: TMessageType,
+ /// Ordered sequence number identifying the message.
+ pub sequence_number: i32,
+}
+
+impl TMessageIdentifier {
+ /// Create a `TMessageIdentifier` for a Thrift service-call named `name`
+ /// with message type `message_type` and sequence number `sequence_number`.
+ pub fn new<S: Into<String>>(
+ name: S,
+ message_type: TMessageType,
+ sequence_number: i32,
+ ) -> TMessageIdentifier {
+ TMessageIdentifier {
+ name: name.into(),
+ message_type: message_type,
+ sequence_number: sequence_number,
+ }
+ }
+}
+
+/// Thrift struct identifier.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct TStructIdentifier {
+ /// Name of the encoded Thrift struct.
+ pub name: String,
+}
+
+impl TStructIdentifier {
+ /// Create a `TStructIdentifier` for a struct named `name`.
+ pub fn new<S: Into<String>>(name: S) -> TStructIdentifier {
+ TStructIdentifier { name: name.into() }
+ }
+}
+
+/// Thrift field identifier.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct TFieldIdentifier {
+ /// Name of the Thrift field.
+ ///
+ /// `None` if it's not sent over the wire.
+ pub name: Option<String>,
+ /// Field type.
+ ///
+ /// This may be a primitive, container, or a struct.
+ pub field_type: TType,
+ /// Thrift field id.
+ ///
+ /// `None` only if `field_type` is `TType::Stop`.
+ pub id: Option<i16>,
+}
+
+impl TFieldIdentifier {
+ /// Create a `TFieldIdentifier` for a field named `name` with type
+ /// `field_type` and field id `id`.
+ ///
+ /// `id` should be `None` if `field_type` is `TType::Stop`.
+ pub fn new<N, S, I>(name: N, field_type: TType, id: I) -> TFieldIdentifier
+ where
+ N: Into<Option<S>>,
+ S: Into<String>,
+ I: Into<Option<i16>>,
+ {
+ TFieldIdentifier {
+ name: name.into().map(|n| n.into()),
+ field_type: field_type,
+ id: id.into(),
+ }
+ }
+}
+
+/// Thrift list identifier.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct TListIdentifier {
+ /// Type of the elements in the list.
+ pub element_type: TType,
+ /// Number of elements in the list.
+ pub size: i32,
+}
+
+impl TListIdentifier {
+ /// Create a `TListIdentifier` for a list with `size` elements of type
+ /// `element_type`.
+ pub fn new(element_type: TType, size: i32) -> TListIdentifier {
+ TListIdentifier {
+ element_type: element_type,
+ size: size,
+ }
+ }
+}
+
+/// Thrift set identifier.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct TSetIdentifier {
+ /// Type of the elements in the set.
+ pub element_type: TType,
+ /// Number of elements in the set.
+ pub size: i32,
+}
+
+impl TSetIdentifier {
+ /// Create a `TSetIdentifier` for a set with `size` elements of type
+ /// `element_type`.
+ pub fn new(element_type: TType, size: i32) -> TSetIdentifier {
+ TSetIdentifier {
+ element_type: element_type,
+ size: size,
+ }
+ }
+}
+
+/// Thrift map identifier.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct TMapIdentifier {
+ /// Map key type.
+ pub key_type: Option<TType>,
+ /// Map value type.
+ pub value_type: Option<TType>,
+ /// Number of entries in the map.
+ pub size: i32,
+}
+
+impl TMapIdentifier {
+ /// Create a `TMapIdentifier` for a map with `size` entries of type
+ /// `key_type -> value_type`.
+ pub fn new<K, V>(key_type: K, value_type: V, size: i32) -> TMapIdentifier
+ where
+ K: Into<Option<TType>>,
+ V: Into<Option<TType>>,
+ {
+ TMapIdentifier {
+ key_type: key_type.into(),
+ value_type: value_type.into(),
+ size: size,
+ }
+ }
+}
+
+/// Thrift message types.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum TMessageType {
+ /// Service-call request.
+ Call,
+ /// Service-call response.
+ Reply,
+ /// Unexpected error in the remote service.
+ Exception,
+ /// One-way service-call request (no response is expected).
+ OneWay,
+}
+
+impl Display for TMessageType {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ match *self {
+ TMessageType::Call => write!(f, "Call"),
+ TMessageType::Reply => write!(f, "Reply"),
+ TMessageType::Exception => write!(f, "Exception"),
+ TMessageType::OneWay => write!(f, "OneWay"),
+ }
+ }
+}
+
+impl From<TMessageType> for u8 {
+ fn from(message_type: TMessageType) -> Self {
+ match message_type {
+ TMessageType::Call => 0x01,
+ TMessageType::Reply => 0x02,
+ TMessageType::Exception => 0x03,
+ TMessageType::OneWay => 0x04,
+ }
+ }
+}
+
+impl TryFrom<u8> for TMessageType {
+ type Error = ::Error;
+ fn try_from(b: u8) -> Result<Self, Self::Error> {
+ match b {
+ 0x01 => Ok(TMessageType::Call),
+ 0x02 => Ok(TMessageType::Reply),
+ 0x03 => Ok(TMessageType::Exception),
+ 0x04 => Ok(TMessageType::OneWay),
+ unkn => Err(::Error::Protocol(ProtocolError {
+ kind: ProtocolErrorKind::InvalidData,
+ message: format!("cannot convert {} to TMessageType", unkn),
+ })),
+ }
+ }
+}
+
+/// Thrift struct-field types.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum TType {
+ /// Indicates that there are no more serialized fields in this Thrift struct.
+ Stop,
+ /// Void (`()`) field.
+ Void,
+ /// Boolean.
+ Bool,
+ /// Signed 8-bit int.
+ I08,
+ /// Double-precision number.
+ Double,
+ /// Signed 16-bit int.
+ I16,
+ /// Signed 32-bit int.
+ I32,
+ /// Signed 64-bit int.
+ I64,
+ /// UTF-8 string.
+ String,
+ /// UTF-7 string. *Unsupported*.
+ Utf7,
+ /// Thrift struct.
+ Struct,
+ /// Map.
+ Map,
+ /// Set.
+ Set,
+ /// List.
+ List,
+ /// UTF-8 string.
+ Utf8,
+ /// UTF-16 string. *Unsupported*.
+ Utf16,
+}
+
+impl Display for TType {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ match *self {
+ TType::Stop => write!(f, "STOP"),
+ TType::Void => write!(f, "void"),
+ TType::Bool => write!(f, "bool"),
+ TType::I08 => write!(f, "i08"),
+ TType::Double => write!(f, "double"),
+ TType::I16 => write!(f, "i16"),
+ TType::I32 => write!(f, "i32"),
+ TType::I64 => write!(f, "i64"),
+ TType::String => write!(f, "string"),
+ TType::Utf7 => write!(f, "UTF7"),
+ TType::Struct => write!(f, "struct"),
+ TType::Map => write!(f, "map"),
+ TType::Set => write!(f, "set"),
+ TType::List => write!(f, "list"),
+ TType::Utf8 => write!(f, "UTF8"),
+ TType::Utf16 => write!(f, "UTF16"),
+ }
+ }
+}
+
+/// Compare the expected message sequence number `expected` with the received
+/// message sequence number `actual`.
+///
+/// Return `()` if `actual == expected`, `Err` otherwise.
+pub fn verify_expected_sequence_number(expected: i32, actual: i32) -> ::Result<()> {
+ if expected == actual {
+ Ok(())
+ } else {
+ Err(::Error::Application(::ApplicationError {
+ kind: ::ApplicationErrorKind::BadSequenceId,
+ message: format!("expected {} got {}", expected, actual),
+ }))
+ }
+}
+
+/// Compare the expected service-call name `expected` with the received
+/// service-call name `actual`.
+///
+/// Return `()` if `actual == expected`, `Err` otherwise.
+pub fn verify_expected_service_call(expected: &str, actual: &str) -> ::Result<()> {
+ if expected == actual {
+ Ok(())
+ } else {
+ Err(::Error::Application(::ApplicationError {
+ kind: ::ApplicationErrorKind::WrongMethodName,
+ message: format!("expected {} got {}", expected, actual),
+ }))
+ }
+}
+
+/// Compare the expected message type `expected` with the received message type
+/// `actual`.
+///
+/// Return `()` if `actual == expected`, `Err` otherwise.
+pub fn verify_expected_message_type(expected: TMessageType, actual: TMessageType) -> ::Result<()> {
+ if expected == actual {
+ Ok(())
+ } else {
+ Err(::Error::Application(::ApplicationError {
+ kind: ::ApplicationErrorKind::InvalidMessageType,
+ message: format!("expected {} got {}", expected, actual),
+ }))
+ }
+}
+
+/// Check if a required Thrift struct field exists.
+///
+/// Return `()` if it does, `Err` otherwise.
+pub fn verify_required_field_exists<T>(field_name: &str, field: &Option<T>) -> ::Result<()> {
+ match *field {
+ Some(_) => Ok(()),
+ None => Err(::Error::Protocol(::ProtocolError {
+ kind: ::ProtocolErrorKind::Unknown,
+ message: format!("missing required field {}", field_name),
+ })),
+ }
+}
+
+/// Extract the field id from a Thrift field identifier.
+///
+/// `field_ident` must *not* have `TFieldIdentifier.field_type` of type `TType::Stop`.
+///
+/// Return `TFieldIdentifier.id` if an id exists, `Err` otherwise.
+pub fn field_id(field_ident: &TFieldIdentifier) -> ::Result<i16> {
+ field_ident.id.ok_or_else(|| {
+ ::Error::Protocol(::ProtocolError {
+ kind: ::ProtocolErrorKind::Unknown,
+ message: format!("missing field in in {:?}", field_ident),
+ })
+ })
+}
+
+#[cfg(test)]
+mod tests {
+
+ use std::io::Cursor;
+
+ use super::*;
+ use transport::{TReadTransport, TWriteTransport};
+
+ #[test]
+ fn must_create_usable_input_protocol_from_concrete_input_protocol() {
+ let r: Box<dyn TReadTransport> = Box::new(Cursor::new([0, 1, 2]));
+ let mut t = TCompactInputProtocol::new(r);
+ takes_input_protocol(&mut t)
+ }
+
+ #[test]
+ fn must_create_usable_input_protocol_from_boxed_input() {
+ let r: Box<dyn TReadTransport> = Box::new(Cursor::new([0, 1, 2]));
+ let mut t: Box<dyn TInputProtocol> = Box::new(TCompactInputProtocol::new(r));
+ takes_input_protocol(&mut t)
+ }
+
+ #[test]
+ fn must_create_usable_output_protocol_from_concrete_output_protocol() {
+ let w: Box<dyn TWriteTransport> = Box::new(vec![0u8; 10]);
+ let mut t = TCompactOutputProtocol::new(w);
+ takes_output_protocol(&mut t)
+ }
+
+ #[test]
+ fn must_create_usable_output_protocol_from_boxed_output() {
+ let w: Box<dyn TWriteTransport> = Box::new(vec![0u8; 10]);
+ let mut t: Box<dyn TOutputProtocol> = Box::new(TCompactOutputProtocol::new(w));
+ takes_output_protocol(&mut t)
+ }
+
+ fn takes_input_protocol<R>(t: &mut R)
+ where
+ R: TInputProtocol,
+ {
+ t.read_byte().unwrap();
+ }
+
+ fn takes_output_protocol<W>(t: &mut W)
+ where
+ W: TOutputProtocol,
+ {
+ t.flush().unwrap();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/rs/src/protocol/multiplexed.rs b/src/jaegertracing/thrift/lib/rs/src/protocol/multiplexed.rs
new file mode 100644
index 000000000..aaee44f73
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/src/protocol/multiplexed.rs
@@ -0,0 +1,239 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use super::{
+ TFieldIdentifier, TListIdentifier, TMapIdentifier, TMessageIdentifier, TMessageType,
+ TOutputProtocol, TSetIdentifier, TStructIdentifier,
+};
+
+/// `TOutputProtocol` that prefixes the service name to all outgoing Thrift
+/// messages.
+///
+/// A `TMultiplexedOutputProtocol` should be used when multiple Thrift services
+/// send messages over a single I/O channel. By prefixing service identifiers
+/// to outgoing messages receivers are able to demux them and route them to the
+/// appropriate service processor. Rust receivers must use a `TMultiplexedProcessor`
+/// to process incoming messages, while other languages must use their
+/// corresponding multiplexed processor implementations.
+///
+/// For example, given a service `TestService` and a service call `test_call`,
+/// this implementation would identify messages as originating from
+/// `TestService:test_call`.
+///
+/// # Examples
+///
+/// Create and use a `TMultiplexedOutputProtocol`.
+///
+/// ```no_run
+/// use thrift::protocol::{TMessageIdentifier, TMessageType, TOutputProtocol};
+/// use thrift::protocol::{TBinaryOutputProtocol, TMultiplexedOutputProtocol};
+/// use thrift::transport::TTcpChannel;
+///
+/// let mut channel = TTcpChannel::new();
+/// channel.open("localhost:9090").unwrap();
+///
+/// let protocol = TBinaryOutputProtocol::new(channel, true);
+/// let mut protocol = TMultiplexedOutputProtocol::new("service_name", protocol);
+///
+/// let ident = TMessageIdentifier::new("svc_call", TMessageType::Call, 1);
+/// protocol.write_message_begin(&ident).unwrap();
+/// ```
+#[derive(Debug)]
+pub struct TMultiplexedOutputProtocol<P>
+where
+ P: TOutputProtocol,
+{
+ service_name: String,
+ inner: P,
+}
+
+impl<P> TMultiplexedOutputProtocol<P>
+where
+ P: TOutputProtocol,
+{
+ /// Create a `TMultiplexedOutputProtocol` that identifies outgoing messages
+ /// as originating from a service named `service_name` and sends them over
+ /// the `wrapped` `TOutputProtocol`. Outgoing messages are encoded and sent
+ /// by `wrapped`, not by this instance.
+ pub fn new(service_name: &str, wrapped: P) -> TMultiplexedOutputProtocol<P> {
+ TMultiplexedOutputProtocol {
+ service_name: service_name.to_owned(),
+ inner: wrapped,
+ }
+ }
+}
+
+// FIXME: avoid passthrough methods
+impl<P> TOutputProtocol for TMultiplexedOutputProtocol<P>
+where
+ P: TOutputProtocol,
+{
+ fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> ::Result<()> {
+ match identifier.message_type {
+ // FIXME: is there a better way to override identifier here?
+ TMessageType::Call | TMessageType::OneWay => {
+ let identifier = TMessageIdentifier {
+ name: format!("{}:{}", self.service_name, identifier.name),
+ ..*identifier
+ };
+ self.inner.write_message_begin(&identifier)
+ }
+ _ => self.inner.write_message_begin(identifier),
+ }
+ }
+
+ fn write_message_end(&mut self) -> ::Result<()> {
+ self.inner.write_message_end()
+ }
+
+ fn write_struct_begin(&mut self, identifier: &TStructIdentifier) -> ::Result<()> {
+ self.inner.write_struct_begin(identifier)
+ }
+
+ fn write_struct_end(&mut self) -> ::Result<()> {
+ self.inner.write_struct_end()
+ }
+
+ fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> ::Result<()> {
+ self.inner.write_field_begin(identifier)
+ }
+
+ fn write_field_end(&mut self) -> ::Result<()> {
+ self.inner.write_field_end()
+ }
+
+ fn write_field_stop(&mut self) -> ::Result<()> {
+ self.inner.write_field_stop()
+ }
+
+ fn write_bytes(&mut self, b: &[u8]) -> ::Result<()> {
+ self.inner.write_bytes(b)
+ }
+
+ fn write_bool(&mut self, b: bool) -> ::Result<()> {
+ self.inner.write_bool(b)
+ }
+
+ fn write_i8(&mut self, i: i8) -> ::Result<()> {
+ self.inner.write_i8(i)
+ }
+
+ fn write_i16(&mut self, i: i16) -> ::Result<()> {
+ self.inner.write_i16(i)
+ }
+
+ fn write_i32(&mut self, i: i32) -> ::Result<()> {
+ self.inner.write_i32(i)
+ }
+
+ fn write_i64(&mut self, i: i64) -> ::Result<()> {
+ self.inner.write_i64(i)
+ }
+
+ fn write_double(&mut self, d: f64) -> ::Result<()> {
+ self.inner.write_double(d)
+ }
+
+ fn write_string(&mut self, s: &str) -> ::Result<()> {
+ self.inner.write_string(s)
+ }
+
+ fn write_list_begin(&mut self, identifier: &TListIdentifier) -> ::Result<()> {
+ self.inner.write_list_begin(identifier)
+ }
+
+ fn write_list_end(&mut self) -> ::Result<()> {
+ self.inner.write_list_end()
+ }
+
+ fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> ::Result<()> {
+ self.inner.write_set_begin(identifier)
+ }
+
+ fn write_set_end(&mut self) -> ::Result<()> {
+ self.inner.write_set_end()
+ }
+
+ fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> ::Result<()> {
+ self.inner.write_map_begin(identifier)
+ }
+
+ fn write_map_end(&mut self) -> ::Result<()> {
+ self.inner.write_map_end()
+ }
+
+ fn flush(&mut self) -> ::Result<()> {
+ self.inner.flush()
+ }
+
+ // utility
+ //
+
+ fn write_byte(&mut self, b: u8) -> ::Result<()> {
+ self.inner.write_byte(b)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+
+ use protocol::{TBinaryOutputProtocol, TMessageIdentifier, TMessageType, TOutputProtocol};
+ use transport::{TBufferChannel, TIoChannel, WriteHalf};
+
+ use super::*;
+
+ #[test]
+ fn must_write_message_begin_with_prefixed_service_name() {
+ let mut o_prot = test_objects();
+
+ let ident = TMessageIdentifier::new("bar", TMessageType::Call, 2);
+ assert_success!(o_prot.write_message_begin(&ident));
+
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ let expected: [u8; 19] = [
+ 0x80,
+ 0x01, /* protocol identifier */
+ 0x00,
+ 0x01, /* message type */
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x07,
+ 0x66,
+ 0x6F,
+ 0x6F, /* "foo" */
+ 0x3A, /* ":" */
+ 0x62,
+ 0x61,
+ 0x72, /* "bar" */
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x02 /* sequence number */,
+ ];
+
+ assert_eq!(o_prot.inner.transport.write_bytes(), expected);
+ }
+
+ fn test_objects() -> TMultiplexedOutputProtocol<TBinaryOutputProtocol<WriteHalf<TBufferChannel>>>
+ {
+ let c = TBufferChannel::with_capacity(40, 40);
+ let (_, w_chan) = c.split().unwrap();
+ let prot = TBinaryOutputProtocol::new(w_chan, true);
+ TMultiplexedOutputProtocol::new("foo", prot)
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/rs/src/protocol/stored.rs b/src/jaegertracing/thrift/lib/rs/src/protocol/stored.rs
new file mode 100644
index 000000000..faa51288e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/src/protocol/stored.rs
@@ -0,0 +1,195 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use std::convert::Into;
+
+use super::{
+ TFieldIdentifier, TInputProtocol, TListIdentifier, TMapIdentifier, TMessageIdentifier,
+ TSetIdentifier, TStructIdentifier,
+};
+use ProtocolErrorKind;
+
+/// `TInputProtocol` required to use a `TMultiplexedProcessor`.
+///
+/// A `TMultiplexedProcessor` reads incoming message identifiers to determine to
+/// which `TProcessor` requests should be forwarded. However, once read, those
+/// message identifier bytes are no longer on the wire. Since downstream
+/// processors expect to read message identifiers from the given input protocol
+/// we need some way of supplying a `TMessageIdentifier` with the service-name
+/// stripped. This implementation stores the received `TMessageIdentifier`
+/// (without the service name) and passes it to the wrapped `TInputProtocol`
+/// when `TInputProtocol::read_message_begin(...)` is called. It delegates all
+/// other calls directly to the wrapped `TInputProtocol`.
+///
+/// This type **should not** be used by application code.
+///
+/// # Examples
+///
+/// Create and use a `TStoredInputProtocol`.
+///
+/// ```no_run
+/// use thrift::protocol::{TInputProtocol, TMessageIdentifier, TMessageType, TOutputProtocol};
+/// use thrift::protocol::{TBinaryInputProtocol, TBinaryOutputProtocol, TStoredInputProtocol};
+/// use thrift::server::TProcessor;
+/// use thrift::transport::{TIoChannel, TTcpChannel};
+///
+/// // sample processor
+/// struct ActualProcessor;
+/// impl TProcessor for ActualProcessor {
+/// fn process(
+/// &self,
+/// _: &mut TInputProtocol,
+/// _: &mut TOutputProtocol
+/// ) -> thrift::Result<()> {
+/// unimplemented!()
+/// }
+/// }
+/// let processor = ActualProcessor {};
+///
+/// // construct the shared transport
+/// let mut channel = TTcpChannel::new();
+/// channel.open("localhost:9090").unwrap();
+///
+/// let (i_chan, o_chan) = channel.split().unwrap();
+///
+/// // construct the actual input and output protocols
+/// let mut i_prot = TBinaryInputProtocol::new(i_chan, true);
+/// let mut o_prot = TBinaryOutputProtocol::new(o_chan, true);
+///
+/// // message identifier received from remote and modified to remove the service name
+/// let new_msg_ident = TMessageIdentifier::new("service_call", TMessageType::Call, 1);
+///
+/// // construct the proxy input protocol
+/// let mut proxy_i_prot = TStoredInputProtocol::new(&mut i_prot, new_msg_ident);
+/// let res = processor.process(&mut proxy_i_prot, &mut o_prot);
+/// ```
+// FIXME: implement Debug
+pub struct TStoredInputProtocol<'a> {
+ inner: &'a mut dyn TInputProtocol,
+ message_ident: Option<TMessageIdentifier>,
+}
+
+impl<'a> TStoredInputProtocol<'a> {
+ /// Create a `TStoredInputProtocol` that delegates all calls other than
+ /// `TInputProtocol::read_message_begin(...)` to a `wrapped`
+ /// `TInputProtocol`. `message_ident` is the modified message identifier -
+ /// with service name stripped - that will be passed to
+ /// `wrapped.read_message_begin(...)`.
+ pub fn new(
+ wrapped: &mut dyn TInputProtocol,
+ message_ident: TMessageIdentifier,
+ ) -> TStoredInputProtocol {
+ TStoredInputProtocol {
+ inner: wrapped,
+ message_ident: message_ident.into(),
+ }
+ }
+}
+
+impl<'a> TInputProtocol for TStoredInputProtocol<'a> {
+ fn read_message_begin(&mut self) -> ::Result<TMessageIdentifier> {
+ self.message_ident.take().ok_or_else(|| {
+ ::errors::new_protocol_error(
+ ProtocolErrorKind::Unknown,
+ "message identifier already read",
+ )
+ })
+ }
+
+ fn read_message_end(&mut self) -> ::Result<()> {
+ self.inner.read_message_end()
+ }
+
+ fn read_struct_begin(&mut self) -> ::Result<Option<TStructIdentifier>> {
+ self.inner.read_struct_begin()
+ }
+
+ fn read_struct_end(&mut self) -> ::Result<()> {
+ self.inner.read_struct_end()
+ }
+
+ fn read_field_begin(&mut self) -> ::Result<TFieldIdentifier> {
+ self.inner.read_field_begin()
+ }
+
+ fn read_field_end(&mut self) -> ::Result<()> {
+ self.inner.read_field_end()
+ }
+
+ fn read_bytes(&mut self) -> ::Result<Vec<u8>> {
+ self.inner.read_bytes()
+ }
+
+ fn read_bool(&mut self) -> ::Result<bool> {
+ self.inner.read_bool()
+ }
+
+ fn read_i8(&mut self) -> ::Result<i8> {
+ self.inner.read_i8()
+ }
+
+ fn read_i16(&mut self) -> ::Result<i16> {
+ self.inner.read_i16()
+ }
+
+ fn read_i32(&mut self) -> ::Result<i32> {
+ self.inner.read_i32()
+ }
+
+ fn read_i64(&mut self) -> ::Result<i64> {
+ self.inner.read_i64()
+ }
+
+ fn read_double(&mut self) -> ::Result<f64> {
+ self.inner.read_double()
+ }
+
+ fn read_string(&mut self) -> ::Result<String> {
+ self.inner.read_string()
+ }
+
+ fn read_list_begin(&mut self) -> ::Result<TListIdentifier> {
+ self.inner.read_list_begin()
+ }
+
+ fn read_list_end(&mut self) -> ::Result<()> {
+ self.inner.read_list_end()
+ }
+
+ fn read_set_begin(&mut self) -> ::Result<TSetIdentifier> {
+ self.inner.read_set_begin()
+ }
+
+ fn read_set_end(&mut self) -> ::Result<()> {
+ self.inner.read_set_end()
+ }
+
+ fn read_map_begin(&mut self) -> ::Result<TMapIdentifier> {
+ self.inner.read_map_begin()
+ }
+
+ fn read_map_end(&mut self) -> ::Result<()> {
+ self.inner.read_map_end()
+ }
+
+ // utility
+ //
+
+ fn read_byte(&mut self) -> ::Result<u8> {
+ self.inner.read_byte()
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/rs/src/server/mod.rs b/src/jaegertracing/thrift/lib/rs/src/server/mod.rs
new file mode 100644
index 000000000..b719d1ba8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/src/server/mod.rs
@@ -0,0 +1,123 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Types used to implement a Thrift server.
+
+use protocol::{TInputProtocol, TMessageIdentifier, TMessageType, TOutputProtocol};
+use {ApplicationError, ApplicationErrorKind};
+
+mod multiplexed;
+mod threaded;
+
+pub use self::multiplexed::TMultiplexedProcessor;
+pub use self::threaded::TServer;
+
+/// Handles incoming Thrift messages and dispatches them to the user-defined
+/// handler functions.
+///
+/// An implementation is auto-generated for each Thrift service. When used by a
+/// server (for example, a `TSimpleServer`), it will demux incoming service
+/// calls and invoke the corresponding user-defined handler function.
+///
+/// # Examples
+///
+/// Create and start a server using the auto-generated `TProcessor` for
+/// a Thrift service `SimpleService`.
+///
+/// ```no_run
+/// use thrift::protocol::{TInputProtocol, TOutputProtocol};
+/// use thrift::server::TProcessor;
+///
+/// //
+/// // auto-generated
+/// //
+///
+/// // processor for `SimpleService`
+/// struct SimpleServiceSyncProcessor;
+/// impl SimpleServiceSyncProcessor {
+/// fn new<H: SimpleServiceSyncHandler>(processor: H) -> SimpleServiceSyncProcessor {
+/// unimplemented!();
+/// }
+/// }
+///
+/// // `TProcessor` implementation for `SimpleService`
+/// impl TProcessor for SimpleServiceSyncProcessor {
+/// fn process(&self, i: &mut TInputProtocol, o: &mut TOutputProtocol) -> thrift::Result<()> {
+/// unimplemented!();
+/// }
+/// }
+///
+/// // service functions for SimpleService
+/// trait SimpleServiceSyncHandler {
+/// fn service_call(&self) -> thrift::Result<()>;
+/// }
+///
+/// //
+/// // user-code follows
+/// //
+///
+/// // define a handler that will be invoked when `service_call` is received
+/// struct SimpleServiceHandlerImpl;
+/// impl SimpleServiceSyncHandler for SimpleServiceHandlerImpl {
+/// fn service_call(&self) -> thrift::Result<()> {
+/// unimplemented!();
+/// }
+/// }
+///
+/// // instantiate the processor
+/// let processor = SimpleServiceSyncProcessor::new(SimpleServiceHandlerImpl {});
+///
+/// // at this point you can pass the processor to the server
+/// // let server = TServer::new(..., processor);
+/// ```
+pub trait TProcessor {
+ /// Process a Thrift service call.
+ ///
+ /// Reads arguments from `i`, executes the user's handler code, and writes
+ /// the response to `o`.
+ ///
+ /// Returns `()` if the handler was executed; `Err` otherwise.
+ fn process(&self, i: &mut dyn TInputProtocol, o: &mut dyn TOutputProtocol) -> ::Result<()>;
+}
+
+/// Convenience function used in generated `TProcessor` implementations to
+/// return an `ApplicationError` if thrift message processing failed.
+pub fn handle_process_result(
+ msg_ident: &TMessageIdentifier,
+ res: ::Result<()>,
+ o_prot: &mut dyn TOutputProtocol,
+) -> ::Result<()> {
+ if let Err(e) = res {
+ let e = match e {
+ ::Error::Application(a) => a,
+ _ => ApplicationError::new(ApplicationErrorKind::Unknown, format!("{:?}", e)),
+ };
+
+ let ident = TMessageIdentifier::new(
+ msg_ident.name.clone(),
+ TMessageType::Exception,
+ msg_ident.sequence_number,
+ );
+
+ o_prot.write_message_begin(&ident)?;
+ ::Error::write_application_error_to_out_protocol(&e, o_prot)?;
+ o_prot.write_message_end()?;
+ o_prot.flush()
+ } else {
+ Ok(())
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/rs/src/server/multiplexed.rs b/src/jaegertracing/thrift/lib/rs/src/server/multiplexed.rs
new file mode 100644
index 000000000..3f9bc78e4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/src/server/multiplexed.rs
@@ -0,0 +1,351 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use std::collections::HashMap;
+use std::convert::Into;
+use std::fmt;
+use std::fmt::{Debug, Formatter};
+use std::sync::{Arc, Mutex};
+
+use protocol::{TInputProtocol, TMessageIdentifier, TOutputProtocol, TStoredInputProtocol};
+
+use super::{handle_process_result, TProcessor};
+
+const MISSING_SEPARATOR_AND_NO_DEFAULT: &'static str =
+ "missing service separator and no default processor set";
+type ThreadSafeProcessor = Box<dyn TProcessor + Send + Sync>;
+
+/// A `TProcessor` that can demux service calls to multiple underlying
+/// Thrift services.
+///
+/// Users register service-specific `TProcessor` instances with a
+/// `TMultiplexedProcessor`, and then register that processor with a server
+/// implementation. Following that, all incoming service calls are automatically
+/// routed to the service-specific `TProcessor`.
+///
+/// A `TMultiplexedProcessor` can only handle messages sent by a
+/// `TMultiplexedOutputProtocol`.
+#[derive(Default)]
+pub struct TMultiplexedProcessor {
+ stored: Mutex<StoredProcessors>,
+}
+
+#[derive(Default)]
+struct StoredProcessors {
+ processors: HashMap<String, Arc<ThreadSafeProcessor>>,
+ default_processor: Option<Arc<ThreadSafeProcessor>>,
+}
+
+impl TMultiplexedProcessor {
+ /// Create a new `TMultiplexedProcessor` with no registered service-specific
+ /// processors.
+ pub fn new() -> TMultiplexedProcessor {
+ TMultiplexedProcessor {
+ stored: Mutex::new(StoredProcessors {
+ processors: HashMap::new(),
+ default_processor: None,
+ }),
+ }
+ }
+
+ /// Register a service-specific `processor` for the service named
+ /// `service_name`. This implementation is also backwards-compatible with
+ /// non-multiplexed clients. Set `as_default` to `true` to allow
+ /// non-namespaced requests to be dispatched to a default processor.
+ ///
+ /// Returns success if a new entry was inserted. Returns an error if:
+ /// * A processor exists for `service_name`
+ /// * You attempt to register a processor as default, and an existing default exists
+ #[cfg_attr(feature = "cargo-clippy", allow(map_entry))]
+ pub fn register<S: Into<String>>(
+ &mut self,
+ service_name: S,
+ processor: Box<dyn TProcessor + Send + Sync>,
+ as_default: bool,
+ ) -> ::Result<()> {
+ let mut stored = self.stored.lock().unwrap();
+
+ let name = service_name.into();
+ if !stored.processors.contains_key(&name) {
+ let processor = Arc::new(processor);
+
+ if as_default {
+ if stored.default_processor.is_none() {
+ stored.processors.insert(name, processor.clone());
+ stored.default_processor = Some(processor.clone());
+ Ok(())
+ } else {
+ Err("cannot reset default processor".into())
+ }
+ } else {
+ stored.processors.insert(name, processor);
+ Ok(())
+ }
+ } else {
+ Err(format!("cannot overwrite existing processor for service {}", name).into())
+ }
+ }
+
+ fn process_message(
+ &self,
+ msg_ident: &TMessageIdentifier,
+ i_prot: &mut dyn TInputProtocol,
+ o_prot: &mut dyn TOutputProtocol,
+ ) -> ::Result<()> {
+ let (svc_name, svc_call) = split_ident_name(&msg_ident.name);
+ debug!("routing svc_name {:?} svc_call {}", &svc_name, &svc_call);
+
+ let processor: Option<Arc<ThreadSafeProcessor>> = {
+ let stored = self.stored.lock().unwrap();
+ if let Some(name) = svc_name {
+ stored.processors.get(name).cloned()
+ } else {
+ stored.default_processor.clone()
+ }
+ };
+
+ match processor {
+ Some(arc) => {
+ let new_msg_ident = TMessageIdentifier::new(
+ svc_call,
+ msg_ident.message_type,
+ msg_ident.sequence_number,
+ );
+ let mut proxy_i_prot = TStoredInputProtocol::new(i_prot, new_msg_ident);
+ (*arc).process(&mut proxy_i_prot, o_prot)
+ }
+ None => Err(missing_processor_message(svc_name).into()),
+ }
+ }
+}
+
+impl TProcessor for TMultiplexedProcessor {
+ fn process(&self, i_prot: &mut dyn TInputProtocol, o_prot: &mut dyn TOutputProtocol) -> ::Result<()> {
+ let msg_ident = i_prot.read_message_begin()?;
+
+ debug!("process incoming msg id:{:?}", &msg_ident);
+ let res = self.process_message(&msg_ident, i_prot, o_prot);
+
+ handle_process_result(&msg_ident, res, o_prot)
+ }
+}
+
+impl Debug for TMultiplexedProcessor {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ let stored = self.stored.lock().unwrap();
+ write!(
+ f,
+ "TMultiplexedProcess {{ registered_count: {:?} default: {:?} }}",
+ stored.processors.keys().len(),
+ stored.default_processor.is_some()
+ )
+ }
+}
+
+fn split_ident_name(ident_name: &str) -> (Option<&str>, &str) {
+ ident_name
+ .find(':')
+ .map(|pos| {
+ let (svc_name, svc_call) = ident_name.split_at(pos);
+ let (_, svc_call) = svc_call.split_at(1); // remove colon from service call name
+ (Some(svc_name), svc_call)
+ })
+ .or_else(|| Some((None, ident_name)))
+ .unwrap()
+}
+
+fn missing_processor_message(svc_name: Option<&str>) -> String {
+ match svc_name {
+ Some(name) => format!("no processor found for service {}", name),
+ None => MISSING_SEPARATOR_AND_NO_DEFAULT.to_owned(),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::convert::Into;
+ use std::sync::atomic::{AtomicBool, Ordering};
+ use std::sync::Arc;
+
+ use protocol::{TBinaryInputProtocol, TBinaryOutputProtocol, TMessageIdentifier, TMessageType};
+ use transport::{ReadHalf, TBufferChannel, TIoChannel, WriteHalf};
+ use {ApplicationError, ApplicationErrorKind};
+
+ use super::*;
+
+ #[test]
+ fn should_split_name_into_proper_separator_and_service_call() {
+ let ident_name = "foo:bar_call";
+ let (serv, call) = split_ident_name(&ident_name);
+ assert_eq!(serv, Some("foo"));
+ assert_eq!(call, "bar_call");
+ }
+
+ #[test]
+ fn should_return_full_ident_if_no_separator_exists() {
+ let ident_name = "bar_call";
+ let (serv, call) = split_ident_name(&ident_name);
+ assert_eq!(serv, None);
+ assert_eq!(call, "bar_call");
+ }
+
+ #[test]
+ fn should_write_error_if_no_separator_found_and_no_default_processor_exists() {
+ let (mut i, mut o) = build_objects();
+
+ let sent_ident = TMessageIdentifier::new("foo", TMessageType::Call, 10);
+ o.write_message_begin(&sent_ident).unwrap();
+ o.flush().unwrap();
+ o.transport.copy_write_buffer_to_read_buffer();
+ o.transport.empty_write_buffer();
+
+ let p = TMultiplexedProcessor::new();
+ p.process(&mut i, &mut o).unwrap(); // at this point an error should be written out
+
+ i.transport.set_readable_bytes(&o.transport.write_bytes());
+ let rcvd_ident = i.read_message_begin().unwrap();
+ let expected_ident = TMessageIdentifier::new("foo", TMessageType::Exception, 10);
+ assert_eq!(rcvd_ident, expected_ident);
+ let rcvd_err = ::Error::read_application_error_from_in_protocol(&mut i).unwrap();
+ let expected_err = ApplicationError::new(
+ ApplicationErrorKind::Unknown,
+ MISSING_SEPARATOR_AND_NO_DEFAULT,
+ );
+ assert_eq!(rcvd_err, expected_err);
+ }
+
+ #[test]
+ fn should_write_error_if_separator_exists_and_no_processor_found() {
+ let (mut i, mut o) = build_objects();
+
+ let sent_ident = TMessageIdentifier::new("missing:call", TMessageType::Call, 10);
+ o.write_message_begin(&sent_ident).unwrap();
+ o.flush().unwrap();
+ o.transport.copy_write_buffer_to_read_buffer();
+ o.transport.empty_write_buffer();
+
+ let p = TMultiplexedProcessor::new();
+ p.process(&mut i, &mut o).unwrap(); // at this point an error should be written out
+
+ i.transport.set_readable_bytes(&o.transport.write_bytes());
+ let rcvd_ident = i.read_message_begin().unwrap();
+ let expected_ident = TMessageIdentifier::new("missing:call", TMessageType::Exception, 10);
+ assert_eq!(rcvd_ident, expected_ident);
+ let rcvd_err = ::Error::read_application_error_from_in_protocol(&mut i).unwrap();
+ let expected_err = ApplicationError::new(
+ ApplicationErrorKind::Unknown,
+ missing_processor_message(Some("missing")),
+ );
+ assert_eq!(rcvd_err, expected_err);
+ }
+
+ #[derive(Default)]
+ struct Service {
+ pub invoked: Arc<AtomicBool>,
+ }
+
+ impl TProcessor for Service {
+ fn process(&self, _: &mut dyn TInputProtocol, _: &mut dyn TOutputProtocol) -> ::Result<()> {
+ let res = self
+ .invoked
+ .compare_and_swap(false, true, Ordering::Relaxed);
+ if res {
+ Ok(())
+ } else {
+ Err("failed swap".into())
+ }
+ }
+ }
+
+ #[test]
+ fn should_route_call_to_correct_processor() {
+ let (mut i, mut o) = build_objects();
+
+ // build the services
+ let svc_1 = Service {
+ invoked: Arc::new(AtomicBool::new(false)),
+ };
+ let atm_1 = svc_1.invoked.clone();
+ let svc_2 = Service {
+ invoked: Arc::new(AtomicBool::new(false)),
+ };
+ let atm_2 = svc_2.invoked.clone();
+
+ // register them
+ let mut p = TMultiplexedProcessor::new();
+ p.register("service_1", Box::new(svc_1), false).unwrap();
+ p.register("service_2", Box::new(svc_2), false).unwrap();
+
+ // make the service call
+ let sent_ident = TMessageIdentifier::new("service_1:call", TMessageType::Call, 10);
+ o.write_message_begin(&sent_ident).unwrap();
+ o.flush().unwrap();
+ o.transport.copy_write_buffer_to_read_buffer();
+ o.transport.empty_write_buffer();
+
+ p.process(&mut i, &mut o).unwrap();
+
+ // service 1 should have been invoked, not service 2
+ assert_eq!(atm_1.load(Ordering::Relaxed), true);
+ assert_eq!(atm_2.load(Ordering::Relaxed), false);
+ }
+
+ #[test]
+ fn should_route_call_to_correct_processor_if_no_separator_exists_and_default_processor_set() {
+ let (mut i, mut o) = build_objects();
+
+ // build the services
+ let svc_1 = Service {
+ invoked: Arc::new(AtomicBool::new(false)),
+ };
+ let atm_1 = svc_1.invoked.clone();
+ let svc_2 = Service {
+ invoked: Arc::new(AtomicBool::new(false)),
+ };
+ let atm_2 = svc_2.invoked.clone();
+
+ // register them
+ let mut p = TMultiplexedProcessor::new();
+ p.register("service_1", Box::new(svc_1), false).unwrap();
+ p.register("service_2", Box::new(svc_2), true).unwrap(); // second processor is default
+
+ // make the service call (it's an old client, so we have to be backwards compatible)
+ let sent_ident = TMessageIdentifier::new("old_call", TMessageType::Call, 10);
+ o.write_message_begin(&sent_ident).unwrap();
+ o.flush().unwrap();
+ o.transport.copy_write_buffer_to_read_buffer();
+ o.transport.empty_write_buffer();
+
+ p.process(&mut i, &mut o).unwrap();
+
+ // service 2 should have been invoked, not service 1
+ assert_eq!(atm_1.load(Ordering::Relaxed), false);
+ assert_eq!(atm_2.load(Ordering::Relaxed), true);
+ }
+
+ fn build_objects() -> (
+ TBinaryInputProtocol<ReadHalf<TBufferChannel>>,
+ TBinaryOutputProtocol<WriteHalf<TBufferChannel>>,
+ ) {
+ let c = TBufferChannel::with_capacity(128, 128);
+ let (r_c, w_c) = c.split().unwrap();
+ (
+ TBinaryInputProtocol::new(r_c, true),
+ TBinaryOutputProtocol::new(w_c, true),
+ )
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/rs/src/server/threaded.rs b/src/jaegertracing/thrift/lib/rs/src/server/threaded.rs
new file mode 100644
index 000000000..8f8c082d6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/src/server/threaded.rs
@@ -0,0 +1,233 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use std::net::{TcpListener, TcpStream};
+use std::sync::Arc;
+use threadpool::ThreadPool;
+
+use protocol::{TInputProtocol, TInputProtocolFactory, TOutputProtocol, TOutputProtocolFactory};
+use transport::{TIoChannel, TReadTransportFactory, TTcpChannel, TWriteTransportFactory};
+use {ApplicationError, ApplicationErrorKind};
+
+use super::TProcessor;
+
+/// Fixed-size thread-pool blocking Thrift server.
+///
+/// A `TServer` listens on a given address and submits accepted connections
+/// to an **unbounded** queue. Connections from this queue are serviced by
+/// the first available worker thread from a **fixed-size** thread pool. Each
+/// accepted connection is handled by that worker thread, and communication
+/// over this thread occurs sequentially and synchronously (i.e. calls block).
+/// Accepted connections have an input half and an output half, each of which
+/// uses a `TTransport` and `TInputProtocol`/`TOutputProtocol` to translate
+/// messages to and from byes. Any combination of `TInputProtocol`, `TOutputProtocol`
+/// and `TTransport` may be used.
+///
+/// # Examples
+///
+/// Creating and running a `TServer` using Thrift-compiler-generated
+/// service code.
+///
+/// ```no_run
+/// use thrift::protocol::{TInputProtocolFactory, TOutputProtocolFactory};
+/// use thrift::protocol::{TBinaryInputProtocolFactory, TBinaryOutputProtocolFactory};
+/// use thrift::protocol::{TInputProtocol, TOutputProtocol};
+/// use thrift::transport::{TBufferedReadTransportFactory, TBufferedWriteTransportFactory,
+/// TReadTransportFactory, TWriteTransportFactory};
+/// use thrift::server::{TProcessor, TServer};
+///
+/// //
+/// // auto-generated
+/// //
+///
+/// // processor for `SimpleService`
+/// struct SimpleServiceSyncProcessor;
+/// impl SimpleServiceSyncProcessor {
+/// fn new<H: SimpleServiceSyncHandler>(processor: H) -> SimpleServiceSyncProcessor {
+/// unimplemented!();
+/// }
+/// }
+///
+/// // `TProcessor` implementation for `SimpleService`
+/// impl TProcessor for SimpleServiceSyncProcessor {
+/// fn process(&self, i: &mut TInputProtocol, o: &mut TOutputProtocol) -> thrift::Result<()> {
+/// unimplemented!();
+/// }
+/// }
+///
+/// // service functions for SimpleService
+/// trait SimpleServiceSyncHandler {
+/// fn service_call(&self) -> thrift::Result<()>;
+/// }
+///
+/// //
+/// // user-code follows
+/// //
+///
+/// // define a handler that will be invoked when `service_call` is received
+/// struct SimpleServiceHandlerImpl;
+/// impl SimpleServiceSyncHandler for SimpleServiceHandlerImpl {
+/// fn service_call(&self) -> thrift::Result<()> {
+/// unimplemented!();
+/// }
+/// }
+///
+/// // instantiate the processor
+/// let processor = SimpleServiceSyncProcessor::new(SimpleServiceHandlerImpl {});
+///
+/// // instantiate the server
+/// let i_tr_fact: Box<TReadTransportFactory> = Box::new(TBufferedReadTransportFactory::new());
+/// let i_pr_fact: Box<TInputProtocolFactory> = Box::new(TBinaryInputProtocolFactory::new());
+/// let o_tr_fact: Box<TWriteTransportFactory> = Box::new(TBufferedWriteTransportFactory::new());
+/// let o_pr_fact: Box<TOutputProtocolFactory> = Box::new(TBinaryOutputProtocolFactory::new());
+///
+/// let mut server = TServer::new(
+/// i_tr_fact,
+/// i_pr_fact,
+/// o_tr_fact,
+/// o_pr_fact,
+/// processor,
+/// 10
+/// );
+///
+/// // start listening for incoming connections
+/// match server.listen("127.0.0.1:8080") {
+/// Ok(_) => println!("listen completed"),
+/// Err(e) => println!("listen failed with error {:?}", e),
+/// }
+/// ```
+#[derive(Debug)]
+pub struct TServer<PRC, RTF, IPF, WTF, OPF>
+where
+ PRC: TProcessor + Send + Sync + 'static,
+ RTF: TReadTransportFactory + 'static,
+ IPF: TInputProtocolFactory + 'static,
+ WTF: TWriteTransportFactory + 'static,
+ OPF: TOutputProtocolFactory + 'static,
+{
+ r_trans_factory: RTF,
+ i_proto_factory: IPF,
+ w_trans_factory: WTF,
+ o_proto_factory: OPF,
+ processor: Arc<PRC>,
+ worker_pool: ThreadPool,
+}
+
+impl<PRC, RTF, IPF, WTF, OPF> TServer<PRC, RTF, IPF, WTF, OPF>
+where
+ PRC: TProcessor + Send + Sync + 'static,
+ RTF: TReadTransportFactory + 'static,
+ IPF: TInputProtocolFactory + 'static,
+ WTF: TWriteTransportFactory + 'static,
+ OPF: TOutputProtocolFactory + 'static,
+{
+ /// Create a `TServer`.
+ ///
+ /// Each accepted connection has an input and output half, each of which
+ /// requires a `TTransport` and `TProtocol`. `TServer` uses
+ /// `read_transport_factory` and `input_protocol_factory` to create
+ /// implementations for the input, and `write_transport_factory` and
+ /// `output_protocol_factory` to create implementations for the output.
+ pub fn new(
+ read_transport_factory: RTF,
+ input_protocol_factory: IPF,
+ write_transport_factory: WTF,
+ output_protocol_factory: OPF,
+ processor: PRC,
+ num_workers: usize,
+ ) -> TServer<PRC, RTF, IPF, WTF, OPF> {
+ TServer {
+ r_trans_factory: read_transport_factory,
+ i_proto_factory: input_protocol_factory,
+ w_trans_factory: write_transport_factory,
+ o_proto_factory: output_protocol_factory,
+ processor: Arc::new(processor),
+ worker_pool: ThreadPool::with_name("Thrift service processor".to_owned(), num_workers),
+ }
+ }
+
+ /// Listen for incoming connections on `listen_address`.
+ ///
+ /// `listen_address` should be in the form `host:port`,
+ /// for example: `127.0.0.1:8080`.
+ ///
+ /// Return `()` if successful.
+ ///
+ /// Return `Err` when the server cannot bind to `listen_address` or there
+ /// is an unrecoverable error.
+ pub fn listen(&mut self, listen_address: &str) -> ::Result<()> {
+ let listener = TcpListener::bind(listen_address)?;
+ for stream in listener.incoming() {
+ match stream {
+ Ok(s) => {
+ let (i_prot, o_prot) = self.new_protocols_for_connection(s)?;
+ let processor = self.processor.clone();
+ self.worker_pool
+ .execute(move || handle_incoming_connection(processor, i_prot, o_prot));
+ }
+ Err(e) => {
+ warn!("failed to accept remote connection with error {:?}", e);
+ }
+ }
+ }
+
+ Err(::Error::Application(ApplicationError {
+ kind: ApplicationErrorKind::Unknown,
+ message: "aborted listen loop".into(),
+ }))
+ }
+
+ fn new_protocols_for_connection(
+ &mut self,
+ stream: TcpStream,
+ ) -> ::Result<(Box<dyn TInputProtocol + Send>, Box<dyn TOutputProtocol + Send>)> {
+ // create the shared tcp stream
+ let channel = TTcpChannel::with_stream(stream);
+
+ // split it into two - one to be owned by the
+ // input tran/proto and the other by the output
+ let (r_chan, w_chan) = channel.split()?;
+
+ // input protocol and transport
+ let r_tran = self.r_trans_factory.create(Box::new(r_chan));
+ let i_prot = self.i_proto_factory.create(r_tran);
+
+ // output protocol and transport
+ let w_tran = self.w_trans_factory.create(Box::new(w_chan));
+ let o_prot = self.o_proto_factory.create(w_tran);
+
+ Ok((i_prot, o_prot))
+ }
+}
+
+fn handle_incoming_connection<PRC>(
+ processor: Arc<PRC>,
+ i_prot: Box<dyn TInputProtocol>,
+ o_prot: Box<dyn TOutputProtocol>,
+) where
+ PRC: TProcessor,
+{
+ let mut i_prot = i_prot;
+ let mut o_prot = o_prot;
+ loop {
+ let r = processor.process(&mut *i_prot, &mut *o_prot);
+ if let Err(e) = r {
+ warn!("processor completed with error: {:?}", e);
+ break;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/rs/src/transport/buffered.rs b/src/jaegertracing/thrift/lib/rs/src/transport/buffered.rs
new file mode 100644
index 000000000..b33eb4f55
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/src/transport/buffered.rs
@@ -0,0 +1,483 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use std::cmp;
+use std::io;
+use std::io::{Read, Write};
+
+use super::{TReadTransport, TReadTransportFactory, TWriteTransport, TWriteTransportFactory};
+
+/// Default capacity of the read buffer in bytes.
+const READ_CAPACITY: usize = 4096;
+
+/// Default capacity of the write buffer in bytes..
+const WRITE_CAPACITY: usize = 4096;
+
+/// Transport that reads messages via an internal buffer.
+///
+/// A `TBufferedReadTransport` maintains a fixed-size internal read buffer.
+/// On a call to `TBufferedReadTransport::read(...)` one full message - both
+/// fixed-length header and bytes - is read from the wrapped channel and buffered.
+/// Subsequent read calls are serviced from the internal buffer until it is
+/// exhausted, at which point the next full message is read from the wrapped
+/// channel.
+///
+/// # Examples
+///
+/// Create and use a `TBufferedReadTransport`.
+///
+/// ```no_run
+/// use std::io::Read;
+/// use thrift::transport::{TBufferedReadTransport, TTcpChannel};
+///
+/// let mut c = TTcpChannel::new();
+/// c.open("localhost:9090").unwrap();
+///
+/// let mut t = TBufferedReadTransport::new(c);
+///
+/// t.read(&mut vec![0u8; 1]).unwrap();
+/// ```
+#[derive(Debug)]
+pub struct TBufferedReadTransport<C>
+where
+ C: Read,
+{
+ buf: Box<[u8]>,
+ pos: usize,
+ cap: usize,
+ chan: C,
+}
+
+impl<C> TBufferedReadTransport<C>
+where
+ C: Read,
+{
+ /// Create a `TBufferedTransport` with default-sized internal read and
+ /// write buffers that wraps the given `TIoChannel`.
+ pub fn new(channel: C) -> TBufferedReadTransport<C> {
+ TBufferedReadTransport::with_capacity(READ_CAPACITY, channel)
+ }
+
+ /// Create a `TBufferedTransport` with an internal read buffer of size
+ /// `read_capacity` and an internal write buffer of size
+ /// `write_capacity` that wraps the given `TIoChannel`.
+ pub fn with_capacity(read_capacity: usize, channel: C) -> TBufferedReadTransport<C> {
+ TBufferedReadTransport {
+ buf: vec![0; read_capacity].into_boxed_slice(),
+ pos: 0,
+ cap: 0,
+ chan: channel,
+ }
+ }
+
+ fn get_bytes(&mut self) -> io::Result<&[u8]> {
+ if self.cap - self.pos == 0 {
+ self.pos = 0;
+ self.cap = self.chan.read(&mut self.buf)?;
+ }
+
+ Ok(&self.buf[self.pos..self.cap])
+ }
+
+ fn consume(&mut self, consumed: usize) {
+ // TODO: was a bug here += <-- test somehow
+ self.pos = cmp::min(self.cap, self.pos + consumed);
+ }
+}
+
+impl<C> Read for TBufferedReadTransport<C>
+where
+ C: Read,
+{
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ let mut bytes_read = 0;
+
+ loop {
+ let nread = {
+ let avail_bytes = self.get_bytes()?;
+ let avail_space = buf.len() - bytes_read;
+ let nread = cmp::min(avail_space, avail_bytes.len());
+ buf[bytes_read..(bytes_read + nread)].copy_from_slice(&avail_bytes[..nread]);
+ nread
+ };
+
+ self.consume(nread);
+ bytes_read += nread;
+
+ if bytes_read == buf.len() || nread == 0 {
+ break;
+ }
+ }
+
+ Ok(bytes_read)
+ }
+}
+
+/// Factory for creating instances of `TBufferedReadTransport`.
+#[derive(Default)]
+pub struct TBufferedReadTransportFactory;
+
+impl TBufferedReadTransportFactory {
+ pub fn new() -> TBufferedReadTransportFactory {
+ TBufferedReadTransportFactory {}
+ }
+}
+
+impl TReadTransportFactory for TBufferedReadTransportFactory {
+ /// Create a `TBufferedReadTransport`.
+ fn create(&self, channel: Box<dyn Read + Send>) -> Box<dyn TReadTransport + Send> {
+ Box::new(TBufferedReadTransport::new(channel))
+ }
+}
+
+/// Transport that writes messages via an internal buffer.
+///
+/// A `TBufferedWriteTransport` maintains a fixed-size internal write buffer.
+/// All writes are made to this buffer and are sent to the wrapped channel only
+/// when `TBufferedWriteTransport::flush()` is called. On a flush a fixed-length
+/// header with a count of the buffered bytes is written, followed by the bytes
+/// themselves.
+///
+/// # Examples
+///
+/// Create and use a `TBufferedWriteTransport`.
+///
+/// ```no_run
+/// use std::io::Write;
+/// use thrift::transport::{TBufferedWriteTransport, TTcpChannel};
+///
+/// let mut c = TTcpChannel::new();
+/// c.open("localhost:9090").unwrap();
+///
+/// let mut t = TBufferedWriteTransport::new(c);
+///
+/// t.write(&[0x00]).unwrap();
+/// t.flush().unwrap();
+/// ```
+#[derive(Debug)]
+pub struct TBufferedWriteTransport<C>
+where
+ C: Write,
+{
+ buf: Vec<u8>,
+ cap: usize,
+ channel: C,
+}
+
+impl<C> TBufferedWriteTransport<C>
+where
+ C: Write,
+{
+ /// Create a `TBufferedTransport` with default-sized internal read and
+ /// write buffers that wraps the given `TIoChannel`.
+ pub fn new(channel: C) -> TBufferedWriteTransport<C> {
+ TBufferedWriteTransport::with_capacity(WRITE_CAPACITY, channel)
+ }
+
+ /// Create a `TBufferedTransport` with an internal read buffer of size
+ /// `read_capacity` and an internal write buffer of size
+ /// `write_capacity` that wraps the given `TIoChannel`.
+ pub fn with_capacity(write_capacity: usize, channel: C) -> TBufferedWriteTransport<C> {
+ assert!(
+ write_capacity > 0,
+ "write buffer size must be a positive integer"
+ );
+
+ TBufferedWriteTransport {
+ buf: Vec::with_capacity(write_capacity),
+ cap: write_capacity,
+ channel: channel,
+ }
+ }
+}
+
+impl<C> Write for TBufferedWriteTransport<C>
+where
+ C: Write,
+{
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ if !buf.is_empty() {
+ let mut avail_bytes;
+
+ loop {
+ avail_bytes = cmp::min(buf.len(), self.cap - self.buf.len());
+
+ if avail_bytes == 0 {
+ self.flush()?;
+ } else {
+ break;
+ }
+ }
+
+ let avail_bytes = avail_bytes;
+
+ self.buf.extend_from_slice(&buf[..avail_bytes]);
+ assert!(self.buf.len() <= self.cap, "copy overflowed buffer");
+
+ Ok(avail_bytes)
+ } else {
+ Ok(0)
+ }
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.channel.write_all(&self.buf)?;
+ self.channel.flush()?;
+ self.buf.clear();
+ Ok(())
+ }
+}
+
+/// Factory for creating instances of `TBufferedWriteTransport`.
+#[derive(Default)]
+pub struct TBufferedWriteTransportFactory;
+
+impl TBufferedWriteTransportFactory {
+ pub fn new() -> TBufferedWriteTransportFactory {
+ TBufferedWriteTransportFactory {}
+ }
+}
+
+impl TWriteTransportFactory for TBufferedWriteTransportFactory {
+ /// Create a `TBufferedWriteTransport`.
+ fn create(&self, channel: Box<dyn Write + Send>) -> Box<dyn TWriteTransport + Send> {
+ Box::new(TBufferedWriteTransport::new(channel))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::io::{Read, Write};
+
+ use super::*;
+ use transport::TBufferChannel;
+
+ #[test]
+ fn must_return_zero_if_read_buffer_is_empty() {
+ let mem = TBufferChannel::with_capacity(10, 0);
+ let mut t = TBufferedReadTransport::with_capacity(10, mem);
+
+ let mut b = vec![0; 10];
+ let read_result = t.read(&mut b);
+
+ assert_eq!(read_result.unwrap(), 0);
+ }
+
+ #[test]
+ fn must_return_zero_if_caller_reads_into_zero_capacity_buffer() {
+ let mem = TBufferChannel::with_capacity(10, 0);
+ let mut t = TBufferedReadTransport::with_capacity(10, mem);
+
+ let read_result = t.read(&mut []);
+
+ assert_eq!(read_result.unwrap(), 0);
+ }
+
+ #[test]
+ fn must_return_zero_if_nothing_more_can_be_read() {
+ let mem = TBufferChannel::with_capacity(4, 0);
+ let mut t = TBufferedReadTransport::with_capacity(4, mem);
+
+ t.chan.set_readable_bytes(&[0, 1, 2, 3]);
+
+ // read buffer is exactly the same size as bytes available
+ let mut buf = vec![0u8; 4];
+ let read_result = t.read(&mut buf);
+
+ // we've read exactly 4 bytes
+ assert_eq!(read_result.unwrap(), 4);
+ assert_eq!(&buf, &[0, 1, 2, 3]);
+
+ // try read again
+ let buf_again = vec![0u8; 4];
+ let read_result = t.read(&mut buf);
+
+ // this time, 0 bytes and we haven't changed the buffer
+ assert_eq!(read_result.unwrap(), 0);
+ assert_eq!(&buf_again, &[0, 0, 0, 0])
+ }
+
+ #[test]
+ fn must_fill_user_buffer_with_only_as_many_bytes_as_available() {
+ let mem = TBufferChannel::with_capacity(4, 0);
+ let mut t = TBufferedReadTransport::with_capacity(4, mem);
+
+ t.chan.set_readable_bytes(&[0, 1, 2, 3]);
+
+ // read buffer is much larger than the bytes available
+ let mut buf = vec![0u8; 8];
+ let read_result = t.read(&mut buf);
+
+ // we've read exactly 4 bytes
+ assert_eq!(read_result.unwrap(), 4);
+ assert_eq!(&buf[..4], &[0, 1, 2, 3]);
+
+ // try read again
+ let read_result = t.read(&mut buf[4..]);
+
+ // this time, 0 bytes and we haven't changed the buffer
+ assert_eq!(read_result.unwrap(), 0);
+ assert_eq!(&buf, &[0, 1, 2, 3, 0, 0, 0, 0])
+ }
+
+ #[test]
+ fn must_read_successfully() {
+ // this test involves a few loops within the buffered transport
+ // itself where it has to drain the underlying transport in order
+ // to service a read
+
+ // we have a much smaller buffer than the
+ // underlying transport has bytes available
+ let mem = TBufferChannel::with_capacity(10, 0);
+ let mut t = TBufferedReadTransport::with_capacity(2, mem);
+
+ // fill the underlying transport's byte buffer
+ let mut readable_bytes = [0u8; 10];
+ for i in 0..10 {
+ readable_bytes[i] = i as u8;
+ }
+
+ t.chan.set_readable_bytes(&readable_bytes);
+
+ // we ask to read into a buffer that's much larger
+ // than the one the buffered transport has; as a result
+ // it's going to have to keep asking the underlying
+ // transport for more bytes
+ let mut buf = [0u8; 8];
+ let read_result = t.read(&mut buf);
+
+ // we should have read 8 bytes
+ assert_eq!(read_result.unwrap(), 8);
+ assert_eq!(&buf, &[0, 1, 2, 3, 4, 5, 6, 7]);
+
+ // let's clear out the buffer and try read again
+ for i in 0..8 {
+ buf[i] = 0;
+ }
+ let read_result = t.read(&mut buf);
+
+ // this time we were only able to read 2 bytes
+ // (all that's remaining from the underlying transport)
+ // let's also check that the remaining bytes are untouched
+ assert_eq!(read_result.unwrap(), 2);
+ assert_eq!(&buf[0..2], &[8, 9]);
+ assert_eq!(&buf[2..], &[0, 0, 0, 0, 0, 0]);
+
+ // try read again (we should get 0)
+ // and all the existing bytes were untouched
+ let read_result = t.read(&mut buf);
+ assert_eq!(read_result.unwrap(), 0);
+ assert_eq!(&buf[0..2], &[8, 9]);
+ assert_eq!(&buf[2..], &[0, 0, 0, 0, 0, 0]);
+ }
+
+ #[test]
+ fn must_return_error_when_nothing_can_be_written_to_underlying_channel() {
+ let mem = TBufferChannel::with_capacity(0, 0);
+ let mut t = TBufferedWriteTransport::with_capacity(1, mem);
+
+ let b = vec![0; 10];
+ let r = t.write(&b);
+
+ // should have written 1 byte
+ assert_eq!(r.unwrap(), 1);
+
+ // let's try again...
+ let r = t.write(&b[1..]);
+
+ // this time we'll error out because the auto-flush failed
+ assert!(r.is_err());
+ }
+
+ #[test]
+ fn must_return_zero_if_caller_calls_write_with_empty_buffer() {
+ let mem = TBufferChannel::with_capacity(0, 10);
+ let mut t = TBufferedWriteTransport::with_capacity(10, mem);
+
+ let r = t.write(&[]);
+ let expected: [u8; 0] = [];
+
+ assert_eq!(r.unwrap(), 0);
+ assert_eq_transport_written_bytes!(t, expected);
+ }
+
+ #[test]
+ fn must_auto_flush_if_write_buffer_full() {
+ let mem = TBufferChannel::with_capacity(0, 8);
+ let mut t = TBufferedWriteTransport::with_capacity(4, mem);
+
+ let b0 = [0x00, 0x01, 0x02, 0x03];
+ let b1 = [0x04, 0x05, 0x06, 0x07];
+
+ // write the first 4 bytes; we've now filled the transport's write buffer
+ let r = t.write(&b0);
+ assert_eq!(r.unwrap(), 4);
+
+ // try write the next 4 bytes; this causes the transport to auto-flush the first 4 bytes
+ let r = t.write(&b1);
+ assert_eq!(r.unwrap(), 4);
+
+ // check that in writing the second 4 bytes we auto-flushed the first 4 bytes
+ assert_eq_transport_num_written_bytes!(t, 4);
+ assert_eq_transport_written_bytes!(t, b0);
+ t.channel.empty_write_buffer();
+
+ // now flush the transport to push the second 4 bytes to the underlying channel
+ assert!(t.flush().is_ok());
+
+ // check that we wrote out the second 4 bytes
+ assert_eq_transport_written_bytes!(t, b1);
+ }
+
+ #[test]
+ fn must_write_to_inner_transport_on_flush() {
+ let mem = TBufferChannel::with_capacity(10, 10);
+ let mut t = TBufferedWriteTransport::new(mem);
+
+ let b: [u8; 5] = [0, 1, 2, 3, 4];
+ assert_eq!(t.write(&b).unwrap(), 5);
+ assert_eq_transport_num_written_bytes!(t, 0);
+
+ assert!(t.flush().is_ok());
+
+ assert_eq_transport_written_bytes!(t, b);
+ }
+
+ #[test]
+ fn must_write_successfully_after_flush() {
+ let mem = TBufferChannel::with_capacity(0, 5);
+ let mut t = TBufferedWriteTransport::with_capacity(5, mem);
+
+ // write and flush
+ let b: [u8; 5] = [0, 1, 2, 3, 4];
+ assert_eq!(t.write(&b).unwrap(), 5);
+ assert!(t.flush().is_ok());
+
+ // check the flushed bytes
+ assert_eq_transport_written_bytes!(t, b);
+
+ // reset our underlying transport
+ t.channel.empty_write_buffer();
+
+ // write and flush again
+ assert_eq!(t.write(&b).unwrap(), 5);
+ assert!(t.flush().is_ok());
+
+ // check the flushed bytes
+ assert_eq_transport_written_bytes!(t, b);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/rs/src/transport/framed.rs b/src/jaegertracing/thrift/lib/rs/src/transport/framed.rs
new file mode 100644
index 000000000..98ad1bb2f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/src/transport/framed.rs
@@ -0,0 +1,459 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
+use std::cmp;
+use std::io;
+use std::io::{Read, Write};
+
+use super::{TReadTransport, TReadTransportFactory, TWriteTransport, TWriteTransportFactory};
+
+/// Default capacity of the read buffer in bytes.
+const READ_CAPACITY: usize = 4096;
+
+/// Default capacity of the write buffer in bytes.
+const WRITE_CAPACITY: usize = 4096;
+
+/// Transport that reads framed messages.
+///
+/// A `TFramedReadTransport` maintains a fixed-size internal read buffer.
+/// On a call to `TFramedReadTransport::read(...)` one full message - both
+/// fixed-length header and bytes - is read from the wrapped channel and
+/// buffered. Subsequent read calls are serviced from the internal buffer
+/// until it is exhausted, at which point the next full message is read
+/// from the wrapped channel.
+///
+/// # Examples
+///
+/// Create and use a `TFramedReadTransport`.
+///
+/// ```no_run
+/// use std::io::Read;
+/// use thrift::transport::{TFramedReadTransport, TTcpChannel};
+///
+/// let mut c = TTcpChannel::new();
+/// c.open("localhost:9090").unwrap();
+///
+/// let mut t = TFramedReadTransport::new(c);
+///
+/// t.read(&mut vec![0u8; 1]).unwrap();
+/// ```
+#[derive(Debug)]
+pub struct TFramedReadTransport<C>
+where
+ C: Read,
+{
+ buf: Vec<u8>,
+ pos: usize,
+ cap: usize,
+ chan: C,
+}
+
+impl<C> TFramedReadTransport<C>
+where
+ C: Read,
+{
+ /// Create a `TFramedReadTransport` with a default-sized
+ /// internal read buffer that wraps the given `TIoChannel`.
+ pub fn new(channel: C) -> TFramedReadTransport<C> {
+ TFramedReadTransport::with_capacity(READ_CAPACITY, channel)
+ }
+
+ /// Create a `TFramedTransport` with an internal read buffer
+ /// of size `read_capacity` that wraps the given `TIoChannel`.
+ pub fn with_capacity(read_capacity: usize, channel: C) -> TFramedReadTransport<C> {
+ TFramedReadTransport {
+ buf: vec![0; read_capacity], // FIXME: do I actually have to do this?
+ pos: 0,
+ cap: 0,
+ chan: channel,
+ }
+ }
+}
+
+impl<C> Read for TFramedReadTransport<C>
+where
+ C: Read,
+{
+ fn read(&mut self, b: &mut [u8]) -> io::Result<usize> {
+ if self.cap - self.pos == 0 {
+ let message_size = self.chan.read_i32::<BigEndian>()? as usize;
+
+ let buf_capacity = cmp::max(message_size, READ_CAPACITY);
+ self.buf.resize(buf_capacity, 0);
+
+ self.chan.read_exact(&mut self.buf[..message_size])?;
+ self.cap = message_size as usize;
+ self.pos = 0;
+ }
+
+ let nread = cmp::min(b.len(), self.cap - self.pos);
+ b[..nread].clone_from_slice(&self.buf[self.pos..self.pos + nread]);
+ self.pos += nread;
+
+ Ok(nread)
+ }
+}
+
+/// Factory for creating instances of `TFramedReadTransport`.
+#[derive(Default)]
+pub struct TFramedReadTransportFactory;
+
+impl TFramedReadTransportFactory {
+ pub fn new() -> TFramedReadTransportFactory {
+ TFramedReadTransportFactory {}
+ }
+}
+
+impl TReadTransportFactory for TFramedReadTransportFactory {
+ /// Create a `TFramedReadTransport`.
+ fn create(&self, channel: Box<dyn Read + Send>) -> Box<dyn TReadTransport + Send> {
+ Box::new(TFramedReadTransport::new(channel))
+ }
+}
+
+/// Transport that writes framed messages.
+///
+/// A `TFramedWriteTransport` maintains a fixed-size internal write buffer. All
+/// writes are made to this buffer and are sent to the wrapped channel only
+/// when `TFramedWriteTransport::flush()` is called. On a flush a fixed-length
+/// header with a count of the buffered bytes is written, followed by the bytes
+/// themselves.
+///
+/// # Examples
+///
+/// Create and use a `TFramedWriteTransport`.
+///
+/// ```no_run
+/// use std::io::Write;
+/// use thrift::transport::{TFramedWriteTransport, TTcpChannel};
+///
+/// let mut c = TTcpChannel::new();
+/// c.open("localhost:9090").unwrap();
+///
+/// let mut t = TFramedWriteTransport::new(c);
+///
+/// t.write(&[0x00]).unwrap();
+/// t.flush().unwrap();
+/// ```
+#[derive(Debug)]
+pub struct TFramedWriteTransport<C>
+where
+ C: Write,
+{
+ buf: Vec<u8>,
+ channel: C,
+}
+
+impl<C> TFramedWriteTransport<C>
+where
+ C: Write,
+{
+ /// Create a `TFramedWriteTransport` with default-sized internal
+ /// write buffer that wraps the given `TIoChannel`.
+ pub fn new(channel: C) -> TFramedWriteTransport<C> {
+ TFramedWriteTransport::with_capacity(WRITE_CAPACITY, channel)
+ }
+
+ /// Create a `TFramedWriteTransport` with an internal write buffer
+ /// of size `write_capacity` that wraps the given `TIoChannel`.
+ pub fn with_capacity(write_capacity: usize, channel: C) -> TFramedWriteTransport<C> {
+ TFramedWriteTransport {
+ buf: Vec::with_capacity(write_capacity),
+ channel,
+ }
+ }
+}
+
+impl<C> Write for TFramedWriteTransport<C>
+where
+ C: Write,
+{
+ fn write(&mut self, b: &[u8]) -> io::Result<usize> {
+ let current_capacity = self.buf.capacity();
+ let available_space = current_capacity - self.buf.len();
+ if b.len() > available_space {
+ let additional_space = cmp::max(b.len() - available_space, current_capacity);
+ self.buf.reserve(additional_space);
+ }
+
+ self.buf.extend_from_slice(b);
+ Ok(b.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ let message_size = self.buf.len();
+
+ if let 0 = message_size {
+ return Ok(());
+ } else {
+ self.channel.write_i32::<BigEndian>(message_size as i32)?;
+ }
+
+ // will spin if the underlying channel can't be written to
+ let mut byte_index = 0;
+ while byte_index < message_size {
+ let nwrite = self.channel.write(&self.buf[byte_index..message_size])?;
+ byte_index = cmp::min(byte_index + nwrite, message_size);
+ }
+
+ let buf_capacity = cmp::min(self.buf.capacity(), WRITE_CAPACITY);
+ self.buf.resize(buf_capacity, 0);
+ self.buf.clear();
+
+ self.channel.flush()
+ }
+}
+
+/// Factory for creating instances of `TFramedWriteTransport`.
+#[derive(Default)]
+pub struct TFramedWriteTransportFactory;
+
+impl TFramedWriteTransportFactory {
+ pub fn new() -> TFramedWriteTransportFactory {
+ TFramedWriteTransportFactory {}
+ }
+}
+
+impl TWriteTransportFactory for TFramedWriteTransportFactory {
+ /// Create a `TFramedWriteTransport`.
+ fn create(&self, channel: Box<dyn Write + Send>) -> Box<dyn TWriteTransport + Send> {
+ Box::new(TFramedWriteTransport::new(channel))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use transport::mem::TBufferChannel;
+
+ // FIXME: test a forced reserve
+
+ #[test]
+ fn must_read_message_smaller_than_initial_buffer_size() {
+ let c = TBufferChannel::with_capacity(10, 10);
+ let mut t = TFramedReadTransport::with_capacity(8, c);
+
+ t.chan.set_readable_bytes(&[
+ 0x00, 0x00, 0x00, 0x04, /* message size */
+ 0x00, 0x01, 0x02, 0x03, /* message body */
+ ]);
+
+ let mut buf = vec![0; 8];
+
+ // we've read exactly 4 bytes
+ assert_eq!(t.read(&mut buf).unwrap(), 4);
+ assert_eq!(&buf[..4], &[0x00, 0x01, 0x02, 0x03]);
+ }
+
+ #[test]
+ fn must_read_message_greater_than_initial_buffer_size() {
+ let c = TBufferChannel::with_capacity(10, 10);
+ let mut t = TFramedReadTransport::with_capacity(2, c);
+
+ t.chan.set_readable_bytes(&[
+ 0x00, 0x00, 0x00, 0x04, /* message size */
+ 0x00, 0x01, 0x02, 0x03, /* message body */
+ ]);
+
+ let mut buf = vec![0; 8];
+
+ // we've read exactly 4 bytes
+ assert_eq!(t.read(&mut buf).unwrap(), 4);
+ assert_eq!(&buf[..4], &[0x00, 0x01, 0x02, 0x03]);
+ }
+
+ #[test]
+ fn must_read_multiple_messages_in_sequence_correctly() {
+ let c = TBufferChannel::with_capacity(10, 10);
+ let mut t = TFramedReadTransport::with_capacity(2, c);
+
+ //
+ // 1st message
+ //
+
+ t.chan.set_readable_bytes(&[
+ 0x00, 0x00, 0x00, 0x04, /* message size */
+ 0x00, 0x01, 0x02, 0x03, /* message body */
+ ]);
+
+ let mut buf = vec![0; 8];
+
+ // we've read exactly 4 bytes
+ assert_eq!(t.read(&mut buf).unwrap(), 4);
+ assert_eq!(&buf, &[0x00, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00]);
+
+ //
+ // 2nd message
+ //
+
+ t.chan.set_readable_bytes(&[
+ 0x00, 0x00, 0x00, 0x01, /* message size */
+ 0x04, /* message body */
+ ]);
+
+ let mut buf = vec![0; 8];
+
+ // we've read exactly 1 byte
+ assert_eq!(t.read(&mut buf).unwrap(), 1);
+ assert_eq!(&buf, &[0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
+ }
+
+ #[test]
+ fn must_write_message_smaller_than_buffer_size() {
+ let mem = TBufferChannel::with_capacity(0, 0);
+ let mut t = TFramedWriteTransport::with_capacity(20, mem);
+
+ let b = vec![0; 10];
+
+ // should have written 10 bytes
+ assert_eq!(t.write(&b).unwrap(), 10);
+ }
+
+ #[test]
+ fn must_return_zero_if_caller_calls_write_with_empty_buffer() {
+ let mem = TBufferChannel::with_capacity(0, 10);
+ let mut t = TFramedWriteTransport::with_capacity(10, mem);
+
+ let expected: [u8; 0] = [];
+
+ assert_eq!(t.write(&[]).unwrap(), 0);
+ assert_eq_transport_written_bytes!(t, expected);
+ }
+
+ #[test]
+ fn must_write_to_inner_transport_on_flush() {
+ let mem = TBufferChannel::with_capacity(10, 10);
+ let mut t = TFramedWriteTransport::new(mem);
+
+ let b: [u8; 5] = [0x00, 0x01, 0x02, 0x03, 0x04];
+ assert_eq!(t.write(&b).unwrap(), 5);
+ assert_eq_transport_num_written_bytes!(t, 0);
+
+ assert!(t.flush().is_ok());
+
+ let expected_bytes = [
+ 0x00, 0x00, 0x00, 0x05, /* message size */
+ 0x00, 0x01, 0x02, 0x03, 0x04, /* message body */
+ ];
+
+ assert_eq_transport_written_bytes!(t, expected_bytes);
+ }
+
+ #[test]
+ fn must_write_message_greater_than_buffer_size_00() {
+ let mem = TBufferChannel::with_capacity(0, 10);
+
+ // IMPORTANT: DO **NOT** CHANGE THE WRITE_CAPACITY OR THE NUMBER OF BYTES TO BE WRITTEN!
+ // these lengths were chosen to be just long enough
+ // that doubling the capacity is a **worse** choice than
+ // simply resizing the buffer to b.len()
+
+ let mut t = TFramedWriteTransport::with_capacity(1, mem);
+ let b = [0x00, 0x01, 0x02];
+
+ // should have written 3 bytes
+ assert_eq!(t.write(&b).unwrap(), 3);
+ assert_eq_transport_num_written_bytes!(t, 0);
+
+ assert!(t.flush().is_ok());
+
+ let expected_bytes = [
+ 0x00, 0x00, 0x00, 0x03, /* message size */
+ 0x00, 0x01, 0x02, /* message body */
+ ];
+
+ assert_eq_transport_written_bytes!(t, expected_bytes);
+ }
+
+ #[test]
+ fn must_write_message_greater_than_buffer_size_01() {
+ let mem = TBufferChannel::with_capacity(0, 10);
+
+ // IMPORTANT: DO **NOT** CHANGE THE WRITE_CAPACITY OR THE NUMBER OF BYTES TO BE WRITTEN!
+ // these lengths were chosen to be just long enough
+ // that doubling the capacity is a **better** choice than
+ // simply resizing the buffer to b.len()
+
+ let mut t = TFramedWriteTransport::with_capacity(2, mem);
+ let b = [0x00, 0x01, 0x02];
+
+ // should have written 3 bytes
+ assert_eq!(t.write(&b).unwrap(), 3);
+ assert_eq_transport_num_written_bytes!(t, 0);
+
+ assert!(t.flush().is_ok());
+
+ let expected_bytes = [
+ 0x00, 0x00, 0x00, 0x03, /* message size */
+ 0x00, 0x01, 0x02, /* message body */
+ ];
+
+ assert_eq_transport_written_bytes!(t, expected_bytes);
+ }
+
+ #[test]
+ fn must_return_error_if_nothing_can_be_written_to_inner_transport_on_flush() {
+ let mem = TBufferChannel::with_capacity(0, 0);
+ let mut t = TFramedWriteTransport::with_capacity(1, mem);
+
+ let b = vec![0; 10];
+
+ // should have written 10 bytes
+ assert_eq!(t.write(&b).unwrap(), 10);
+
+ // let's flush
+ let r = t.flush();
+
+ // this time we'll error out because the flush can't write to the underlying channel
+ assert!(r.is_err());
+ }
+
+ #[test]
+ fn must_write_successfully_after_flush() {
+ // IMPORTANT: write capacity *MUST* be greater
+ // than message sizes used in this test + 4-byte frame header
+ let mem = TBufferChannel::with_capacity(0, 10);
+ let mut t = TFramedWriteTransport::with_capacity(5, mem);
+
+ // write and flush
+ let first_message: [u8; 5] = [0x00, 0x01, 0x02, 0x03, 0x04];
+ assert_eq!(t.write(&first_message).unwrap(), 5);
+ assert!(t.flush().is_ok());
+
+ let mut expected = Vec::new();
+ expected.write_all(&[0x00, 0x00, 0x00, 0x05]).unwrap(); // message size
+ expected.extend_from_slice(&first_message);
+
+ // check the flushed bytes
+ assert_eq!(t.channel.write_bytes(), expected);
+
+ // reset our underlying transport
+ t.channel.empty_write_buffer();
+
+ let second_message: [u8; 3] = [0x05, 0x06, 0x07];
+ assert_eq!(t.write(&second_message).unwrap(), 3);
+ assert!(t.flush().is_ok());
+
+ expected.clear();
+ expected.write_all(&[0x00, 0x00, 0x00, 0x03]).unwrap(); // message size
+ expected.extend_from_slice(&second_message);
+
+ // check the flushed bytes
+ assert_eq!(t.channel.write_bytes(), expected);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/rs/src/transport/mem.rs b/src/jaegertracing/thrift/lib/rs/src/transport/mem.rs
new file mode 100644
index 000000000..82c4b579f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/src/transport/mem.rs
@@ -0,0 +1,385 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use std::cmp;
+use std::io;
+use std::sync::{Arc, Mutex};
+
+use super::{ReadHalf, TIoChannel, WriteHalf};
+
+/// In-memory read and write channel with fixed-size read and write buffers.
+///
+/// On a `write` bytes are written to the internal write buffer. Writes are no
+/// longer accepted once this buffer is full. Callers must `empty_write_buffer()`
+/// before subsequent writes are accepted.
+///
+/// You can set readable bytes in the internal read buffer by filling it with
+/// `set_readable_bytes(...)`. Callers can then read until the buffer is
+/// depleted. No further reads are accepted until the internal read buffer is
+/// replenished again.
+#[derive(Debug)]
+pub struct TBufferChannel {
+ read: Arc<Mutex<ReadData>>,
+ write: Arc<Mutex<WriteData>>,
+}
+
+#[derive(Debug)]
+struct ReadData {
+ buf: Box<[u8]>,
+ pos: usize,
+ idx: usize,
+ cap: usize,
+}
+
+#[derive(Debug)]
+struct WriteData {
+ buf: Box<[u8]>,
+ pos: usize,
+ cap: usize,
+}
+
+impl TBufferChannel {
+ /// Constructs a new, empty `TBufferChannel` with the given
+ /// read buffer capacity and write buffer capacity.
+ pub fn with_capacity(read_capacity: usize, write_capacity: usize) -> TBufferChannel {
+ TBufferChannel {
+ read: Arc::new(Mutex::new(ReadData {
+ buf: vec![0; read_capacity].into_boxed_slice(),
+ idx: 0,
+ pos: 0,
+ cap: read_capacity,
+ })),
+ write: Arc::new(Mutex::new(WriteData {
+ buf: vec![0; write_capacity].into_boxed_slice(),
+ pos: 0,
+ cap: write_capacity,
+ })),
+ }
+ }
+
+ /// Return a copy of the bytes held by the internal read buffer.
+ /// Returns an empty vector if no readable bytes are present.
+ pub fn read_bytes(&self) -> Vec<u8> {
+ let rdata = self.read.as_ref().lock().unwrap();
+ let mut buf = vec![0u8; rdata.idx];
+ buf.copy_from_slice(&rdata.buf[..rdata.idx]);
+ buf
+ }
+
+ // FIXME: do I really need this API call?
+ // FIXME: should this simply reset to the last set of readable bytes?
+ /// Reset the number of readable bytes to zero.
+ ///
+ /// Subsequent calls to `read` will return nothing.
+ pub fn empty_read_buffer(&mut self) {
+ let mut rdata = self.read.as_ref().lock().unwrap();
+ rdata.pos = 0;
+ rdata.idx = 0;
+ }
+
+ /// Copy bytes from the source buffer `buf` into the internal read buffer,
+ /// overwriting any existing bytes. Returns the number of bytes copied,
+ /// which is `min(buf.len(), internal_read_buf.len())`.
+ pub fn set_readable_bytes(&mut self, buf: &[u8]) -> usize {
+ self.empty_read_buffer();
+ let mut rdata = self.read.as_ref().lock().unwrap();
+ let max_bytes = cmp::min(rdata.cap, buf.len());
+ rdata.buf[..max_bytes].clone_from_slice(&buf[..max_bytes]);
+ rdata.idx = max_bytes;
+ max_bytes
+ }
+
+ /// Return a copy of the bytes held by the internal write buffer.
+ /// Returns an empty vector if no bytes were written.
+ pub fn write_bytes(&self) -> Vec<u8> {
+ let wdata = self.write.as_ref().lock().unwrap();
+ let mut buf = vec![0u8; wdata.pos];
+ buf.copy_from_slice(&wdata.buf[..wdata.pos]);
+ buf
+ }
+
+ /// Resets the internal write buffer, making it seem like no bytes were
+ /// written. Calling `write_buffer` after this returns an empty vector.
+ pub fn empty_write_buffer(&mut self) {
+ let mut wdata = self.write.as_ref().lock().unwrap();
+ wdata.pos = 0;
+ }
+
+ /// Overwrites the contents of the read buffer with the contents of the
+ /// write buffer. The write buffer is emptied after this operation.
+ pub fn copy_write_buffer_to_read_buffer(&mut self) {
+ // FIXME: redo this entire method
+ let buf = {
+ let wdata = self.write.as_ref().lock().unwrap();
+ let b = &wdata.buf[..wdata.pos];
+ let mut b_ret = vec![0; b.len()];
+ b_ret.copy_from_slice(b);
+ b_ret
+ };
+
+ let bytes_copied = self.set_readable_bytes(&buf);
+ assert_eq!(bytes_copied, buf.len());
+
+ self.empty_write_buffer();
+ }
+}
+
+impl TIoChannel for TBufferChannel {
+ fn split(self) -> ::Result<(ReadHalf<Self>, WriteHalf<Self>)>
+ where
+ Self: Sized,
+ {
+ Ok((
+ ReadHalf {
+ handle: TBufferChannel {
+ read: self.read.clone(),
+ write: self.write.clone(),
+ },
+ },
+ WriteHalf {
+ handle: TBufferChannel {
+ read: self.read.clone(),
+ write: self.write.clone(),
+ },
+ },
+ ))
+ }
+}
+
+impl io::Read for TBufferChannel {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ let mut rdata = self.read.as_ref().lock().unwrap();
+ let nread = cmp::min(buf.len(), rdata.idx - rdata.pos);
+ buf[..nread].clone_from_slice(&rdata.buf[rdata.pos..rdata.pos + nread]);
+ rdata.pos += nread;
+ Ok(nread)
+ }
+}
+
+impl io::Write for TBufferChannel {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let mut wdata = self.write.as_ref().lock().unwrap();
+ let nwrite = cmp::min(buf.len(), wdata.cap - wdata.pos);
+ let (start, end) = (wdata.pos, wdata.pos + nwrite);
+ wdata.buf[start..end].clone_from_slice(&buf[..nwrite]);
+ wdata.pos += nwrite;
+ Ok(nwrite)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(()) // nothing to do on flush
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::io::{Read, Write};
+
+ use super::TBufferChannel;
+
+ #[test]
+ fn must_empty_write_buffer() {
+ let mut t = TBufferChannel::with_capacity(0, 1);
+
+ let bytes_to_write: [u8; 1] = [0x01];
+ let result = t.write(&bytes_to_write);
+ assert_eq!(result.unwrap(), 1);
+ assert_eq!(&t.write_bytes(), &bytes_to_write);
+
+ t.empty_write_buffer();
+ assert_eq!(t.write_bytes().len(), 0);
+ }
+
+ #[test]
+ fn must_accept_writes_after_buffer_emptied() {
+ let mut t = TBufferChannel::with_capacity(0, 2);
+
+ let bytes_to_write: [u8; 2] = [0x01, 0x02];
+
+ // first write (all bytes written)
+ let result = t.write(&bytes_to_write);
+ assert_eq!(result.unwrap(), 2);
+ assert_eq!(&t.write_bytes(), &bytes_to_write);
+
+ // try write again (nothing should be written)
+ let result = t.write(&bytes_to_write);
+ assert_eq!(result.unwrap(), 0);
+ assert_eq!(&t.write_bytes(), &bytes_to_write); // still the same as before
+
+ // now reset the buffer
+ t.empty_write_buffer();
+ assert_eq!(t.write_bytes().len(), 0);
+
+ // now try write again - the write should succeed
+ let result = t.write(&bytes_to_write);
+ assert_eq!(result.unwrap(), 2);
+ assert_eq!(&t.write_bytes(), &bytes_to_write);
+ }
+
+ #[test]
+ fn must_accept_multiple_writes_until_buffer_is_full() {
+ let mut t = TBufferChannel::with_capacity(0, 10);
+
+ // first write (all bytes written)
+ let bytes_to_write_0: [u8; 2] = [0x01, 0x41];
+ let write_0_result = t.write(&bytes_to_write_0);
+ assert_eq!(write_0_result.unwrap(), 2);
+ assert_eq!(t.write_bytes(), &bytes_to_write_0);
+
+ // second write (all bytes written, starting at index 2)
+ let bytes_to_write_1: [u8; 7] = [0x24, 0x41, 0x32, 0x33, 0x11, 0x98, 0xAF];
+ let write_1_result = t.write(&bytes_to_write_1);
+ assert_eq!(write_1_result.unwrap(), 7);
+ assert_eq!(&t.write_bytes()[2..], &bytes_to_write_1);
+
+ // third write (only 1 byte written - that's all we have space for)
+ let bytes_to_write_2: [u8; 3] = [0xBF, 0xDA, 0x98];
+ let write_2_result = t.write(&bytes_to_write_2);
+ assert_eq!(write_2_result.unwrap(), 1);
+ assert_eq!(&t.write_bytes()[9..], &bytes_to_write_2[0..1]); // how does this syntax work?!
+
+ // fourth write (no writes are accepted)
+ let bytes_to_write_3: [u8; 3] = [0xBF, 0xAA, 0xFD];
+ let write_3_result = t.write(&bytes_to_write_3);
+ assert_eq!(write_3_result.unwrap(), 0);
+
+ // check the full write buffer
+ let mut expected: Vec<u8> = Vec::with_capacity(10);
+ expected.extend_from_slice(&bytes_to_write_0);
+ expected.extend_from_slice(&bytes_to_write_1);
+ expected.extend_from_slice(&bytes_to_write_2[0..1]);
+ assert_eq!(t.write_bytes(), &expected[..]);
+ }
+
+ #[test]
+ fn must_empty_read_buffer() {
+ let mut t = TBufferChannel::with_capacity(1, 0);
+
+ let bytes_to_read: [u8; 1] = [0x01];
+ let result = t.set_readable_bytes(&bytes_to_read);
+ assert_eq!(result, 1);
+ assert_eq!(t.read_bytes(), &bytes_to_read);
+
+ t.empty_read_buffer();
+ assert_eq!(t.read_bytes().len(), 0);
+ }
+
+ #[test]
+ fn must_allow_readable_bytes_to_be_set_after_read_buffer_emptied() {
+ let mut t = TBufferChannel::with_capacity(1, 0);
+
+ let bytes_to_read_0: [u8; 1] = [0x01];
+ let result = t.set_readable_bytes(&bytes_to_read_0);
+ assert_eq!(result, 1);
+ assert_eq!(t.read_bytes(), &bytes_to_read_0);
+
+ t.empty_read_buffer();
+ assert_eq!(t.read_bytes().len(), 0);
+
+ let bytes_to_read_1: [u8; 1] = [0x02];
+ let result = t.set_readable_bytes(&bytes_to_read_1);
+ assert_eq!(result, 1);
+ assert_eq!(t.read_bytes(), &bytes_to_read_1);
+ }
+
+ #[test]
+ fn must_accept_multiple_reads_until_all_bytes_read() {
+ let mut t = TBufferChannel::with_capacity(10, 0);
+
+ let readable_bytes: [u8; 10] = [0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0x00, 0x1A, 0x2B, 0x3C, 0x4D];
+
+ // check that we're able to set the bytes to be read
+ let result = t.set_readable_bytes(&readable_bytes);
+ assert_eq!(result, 10);
+ assert_eq!(t.read_bytes(), &readable_bytes);
+
+ // first read
+ let mut read_buf_0 = vec![0; 5];
+ let read_result = t.read(&mut read_buf_0);
+ assert_eq!(read_result.unwrap(), 5);
+ assert_eq!(read_buf_0.as_slice(), &(readable_bytes[0..5]));
+
+ // second read
+ let mut read_buf_1 = vec![0; 4];
+ let read_result = t.read(&mut read_buf_1);
+ assert_eq!(read_result.unwrap(), 4);
+ assert_eq!(read_buf_1.as_slice(), &(readable_bytes[5..9]));
+
+ // third read (only 1 byte remains to be read)
+ let mut read_buf_2 = vec![0; 3];
+ let read_result = t.read(&mut read_buf_2);
+ assert_eq!(read_result.unwrap(), 1);
+ read_buf_2.truncate(1); // FIXME: does the caller have to do this?
+ assert_eq!(read_buf_2.as_slice(), &(readable_bytes[9..]));
+
+ // fourth read (nothing should be readable)
+ let mut read_buf_3 = vec![0; 10];
+ let read_result = t.read(&mut read_buf_3);
+ assert_eq!(read_result.unwrap(), 0);
+ read_buf_3.truncate(0);
+
+ // check that all the bytes we received match the original (again!)
+ let mut bytes_read = Vec::with_capacity(10);
+ bytes_read.extend_from_slice(&read_buf_0);
+ bytes_read.extend_from_slice(&read_buf_1);
+ bytes_read.extend_from_slice(&read_buf_2);
+ bytes_read.extend_from_slice(&read_buf_3);
+ assert_eq!(&bytes_read, &readable_bytes);
+ }
+
+ #[test]
+ fn must_allow_reads_to_succeed_after_read_buffer_replenished() {
+ let mut t = TBufferChannel::with_capacity(3, 0);
+
+ let readable_bytes_0: [u8; 3] = [0x02, 0xAB, 0x33];
+
+ // check that we're able to set the bytes to be read
+ let result = t.set_readable_bytes(&readable_bytes_0);
+ assert_eq!(result, 3);
+ assert_eq!(t.read_bytes(), &readable_bytes_0);
+
+ let mut read_buf = vec![0; 4];
+
+ // drain the read buffer
+ let read_result = t.read(&mut read_buf);
+ assert_eq!(read_result.unwrap(), 3);
+ assert_eq!(t.read_bytes(), &read_buf[0..3]);
+
+ // check that a subsequent read fails
+ let read_result = t.read(&mut read_buf);
+ assert_eq!(read_result.unwrap(), 0);
+
+ // we don't modify the read buffer on failure
+ let mut expected_bytes = Vec::with_capacity(4);
+ expected_bytes.extend_from_slice(&readable_bytes_0);
+ expected_bytes.push(0x00);
+ assert_eq!(&read_buf, &expected_bytes);
+
+ // replenish the read buffer again
+ let readable_bytes_1: [u8; 2] = [0x91, 0xAA];
+
+ // check that we're able to set the bytes to be read
+ let result = t.set_readable_bytes(&readable_bytes_1);
+ assert_eq!(result, 2);
+ assert_eq!(t.read_bytes(), &readable_bytes_1);
+
+ // read again
+ let read_result = t.read(&mut read_buf);
+ assert_eq!(read_result.unwrap(), 2);
+ assert_eq!(t.read_bytes(), &read_buf[0..2]);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/rs/src/transport/mod.rs b/src/jaegertracing/thrift/lib/rs/src/transport/mod.rs
new file mode 100644
index 000000000..32c07998a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/src/transport/mod.rs
@@ -0,0 +1,291 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Types used to send and receive bytes over an I/O channel.
+//!
+//! The core types are the `TReadTransport`, `TWriteTransport` and the
+//! `TIoChannel` traits, through which `TInputProtocol` or
+//! `TOutputProtocol` can receive and send primitives over the wire. While
+//! `TInputProtocol` and `TOutputProtocol` instances deal with language primitives
+//! the types in this module understand only bytes.
+
+use std::io;
+use std::io::{Read, Write};
+use std::ops::{Deref, DerefMut};
+
+#[cfg(test)]
+macro_rules! assert_eq_transport_num_written_bytes {
+ ($transport:ident, $num_written_bytes:expr) => {{
+ assert_eq!($transport.channel.write_bytes().len(), $num_written_bytes);
+ }};
+}
+
+#[cfg(test)]
+macro_rules! assert_eq_transport_written_bytes {
+ ($transport:ident, $expected_bytes:ident) => {{
+ assert_eq!($transport.channel.write_bytes(), &$expected_bytes);
+ }};
+}
+
+mod buffered;
+mod framed;
+mod mem;
+mod socket;
+
+pub use self::buffered::{
+ TBufferedReadTransport, TBufferedReadTransportFactory, TBufferedWriteTransport,
+ TBufferedWriteTransportFactory,
+};
+pub use self::framed::{
+ TFramedReadTransport, TFramedReadTransportFactory, TFramedWriteTransport,
+ TFramedWriteTransportFactory,
+};
+pub use self::mem::TBufferChannel;
+pub use self::socket::TTcpChannel;
+
+/// Identifies a transport used by a `TInputProtocol` to receive bytes.
+pub trait TReadTransport: Read {}
+
+/// Helper type used by a server to create `TReadTransport` instances for
+/// accepted client connections.
+pub trait TReadTransportFactory {
+ /// Create a `TTransport` that wraps a channel over which bytes are to be read.
+ fn create(&self, channel: Box<dyn Read + Send>) -> Box<dyn TReadTransport + Send>;
+}
+
+/// Identifies a transport used by `TOutputProtocol` to send bytes.
+pub trait TWriteTransport: Write {}
+
+/// Helper type used by a server to create `TWriteTransport` instances for
+/// accepted client connections.
+pub trait TWriteTransportFactory {
+ /// Create a `TTransport` that wraps a channel over which bytes are to be sent.
+ fn create(&self, channel: Box<dyn Write + Send>) -> Box<dyn TWriteTransport + Send>;
+}
+
+impl<T> TReadTransport for T where T: Read {}
+
+impl<T> TWriteTransport for T where T: Write {}
+
+// FIXME: implement the Debug trait for boxed transports
+
+impl<T> TReadTransportFactory for Box<T>
+where
+ T: TReadTransportFactory + ?Sized,
+{
+ fn create(&self, channel: Box<dyn Read + Send>) -> Box<dyn TReadTransport + Send> {
+ (**self).create(channel)
+ }
+}
+
+impl<T> TWriteTransportFactory for Box<T>
+where
+ T: TWriteTransportFactory + ?Sized,
+{
+ fn create(&self, channel: Box<dyn Write + Send>) -> Box<dyn TWriteTransport + Send> {
+ (**self).create(channel)
+ }
+}
+
+/// Identifies a splittable bidirectional I/O channel used to send and receive bytes.
+pub trait TIoChannel: Read + Write {
+ /// Split the channel into a readable half and a writable half, where the
+ /// readable half implements `io::Read` and the writable half implements
+ /// `io::Write`. Returns `None` if the channel was not initialized, or if it
+ /// cannot be split safely.
+ ///
+ /// Returned halves may share the underlying OS channel or buffer resources.
+ /// Implementations **should ensure** that these two halves can be safely
+ /// used independently by concurrent threads.
+ fn split(self) -> ::Result<(::transport::ReadHalf<Self>, ::transport::WriteHalf<Self>)>
+ where
+ Self: Sized;
+}
+
+/// The readable half of an object returned from `TIoChannel::split`.
+#[derive(Debug)]
+pub struct ReadHalf<C>
+where
+ C: Read,
+{
+ handle: C,
+}
+
+/// The writable half of an object returned from `TIoChannel::split`.
+#[derive(Debug)]
+pub struct WriteHalf<C>
+where
+ C: Write,
+{
+ handle: C,
+}
+
+impl<C> ReadHalf<C>
+where
+ C: Read,
+{
+ /// Create a `ReadHalf` associated with readable `handle`
+ pub fn new(handle: C) -> ReadHalf<C> {
+ ReadHalf { handle }
+ }
+}
+
+impl<C> WriteHalf<C>
+where
+ C: Write,
+{
+ /// Create a `WriteHalf` associated with writable `handle`
+ pub fn new(handle: C) -> WriteHalf<C> {
+ WriteHalf { handle }
+ }
+}
+
+impl<C> Read for ReadHalf<C>
+where
+ C: Read,
+{
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ self.handle.read(buf)
+ }
+}
+
+impl<C> Write for WriteHalf<C>
+where
+ C: Write,
+{
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.handle.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.handle.flush()
+ }
+}
+
+impl<C> Deref for ReadHalf<C>
+where
+ C: Read,
+{
+ type Target = C;
+
+ fn deref(&self) -> &Self::Target {
+ &self.handle
+ }
+}
+
+impl<C> DerefMut for ReadHalf<C>
+where
+ C: Read,
+{
+ fn deref_mut(&mut self) -> &mut C {
+ &mut self.handle
+ }
+}
+
+impl<C> Deref for WriteHalf<C>
+where
+ C: Write,
+{
+ type Target = C;
+
+ fn deref(&self) -> &Self::Target {
+ &self.handle
+ }
+}
+
+impl<C> DerefMut for WriteHalf<C>
+where
+ C: Write,
+{
+ fn deref_mut(&mut self) -> &mut C {
+ &mut self.handle
+ }
+}
+
+#[cfg(test)]
+mod tests {
+
+ use std::io::Cursor;
+
+ use super::*;
+
+ #[test]
+ fn must_create_usable_read_channel_from_concrete_read_type() {
+ let r = Cursor::new([0, 1, 2]);
+ let _ = TBufferedReadTransport::new(r);
+ }
+
+ #[test]
+ fn must_create_usable_read_channel_from_boxed_read() {
+ let r: Box<dyn Read> = Box::new(Cursor::new([0, 1, 2]));
+ let _ = TBufferedReadTransport::new(r);
+ }
+
+ #[test]
+ fn must_create_usable_write_channel_from_concrete_write_type() {
+ let w = vec![0u8; 10];
+ let _ = TBufferedWriteTransport::new(w);
+ }
+
+ #[test]
+ fn must_create_usable_write_channel_from_boxed_write() {
+ let w: Box<dyn Write> = Box::new(vec![0u8; 10]);
+ let _ = TBufferedWriteTransport::new(w);
+ }
+
+ #[test]
+ fn must_create_usable_read_transport_from_concrete_read_transport() {
+ let r = Cursor::new([0, 1, 2]);
+ let mut t = TBufferedReadTransport::new(r);
+ takes_read_transport(&mut t)
+ }
+
+ #[test]
+ fn must_create_usable_read_transport_from_boxed_read() {
+ let r = Cursor::new([0, 1, 2]);
+ let mut t: Box<dyn TReadTransport> = Box::new(TBufferedReadTransport::new(r));
+ takes_read_transport(&mut t)
+ }
+
+ #[test]
+ fn must_create_usable_write_transport_from_concrete_write_transport() {
+ let w = vec![0u8; 10];
+ let mut t = TBufferedWriteTransport::new(w);
+ takes_write_transport(&mut t)
+ }
+
+ #[test]
+ fn must_create_usable_write_transport_from_boxed_write() {
+ let w = vec![0u8; 10];
+ let mut t: Box<dyn TWriteTransport> = Box::new(TBufferedWriteTransport::new(w));
+ takes_write_transport(&mut t)
+ }
+
+ fn takes_read_transport<R>(t: &mut R)
+ where
+ R: TReadTransport,
+ {
+ t.bytes();
+ }
+
+ fn takes_write_transport<W>(t: &mut W)
+ where
+ W: TWriteTransport,
+ {
+ t.flush().unwrap();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/rs/src/transport/socket.rs b/src/jaegertracing/thrift/lib/rs/src/transport/socket.rs
new file mode 100644
index 000000000..0bef67bed
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/src/transport/socket.rs
@@ -0,0 +1,168 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use std::convert::From;
+use std::io;
+use std::io::{ErrorKind, Read, Write};
+use std::net::{Shutdown, TcpStream};
+
+use super::{ReadHalf, TIoChannel, WriteHalf};
+use {new_transport_error, TransportErrorKind};
+
+/// Bidirectional TCP/IP channel.
+///
+/// # Examples
+///
+/// Create a `TTcpChannel`.
+///
+/// ```no_run
+/// use std::io::{Read, Write};
+/// use thrift::transport::TTcpChannel;
+///
+/// let mut c = TTcpChannel::new();
+/// c.open("localhost:9090").unwrap();
+///
+/// let mut buf = vec![0u8; 4];
+/// c.read(&mut buf).unwrap();
+/// c.write(&vec![0, 1, 2]).unwrap();
+/// ```
+///
+/// Create a `TTcpChannel` by wrapping an existing `TcpStream`.
+///
+/// ```no_run
+/// use std::io::{Read, Write};
+/// use std::net::TcpStream;
+/// use thrift::transport::TTcpChannel;
+///
+/// let stream = TcpStream::connect("127.0.0.1:9189").unwrap();
+///
+/// // no need to call c.open() since we've already connected above
+/// let mut c = TTcpChannel::with_stream(stream);
+///
+/// let mut buf = vec![0u8; 4];
+/// c.read(&mut buf).unwrap();
+/// c.write(&vec![0, 1, 2]).unwrap();
+/// ```
+#[derive(Debug, Default)]
+pub struct TTcpChannel {
+ stream: Option<TcpStream>,
+}
+
+impl TTcpChannel {
+ /// Create an uninitialized `TTcpChannel`.
+ ///
+ /// The returned instance must be opened using `TTcpChannel::open(...)`
+ /// before it can be used.
+ pub fn new() -> TTcpChannel {
+ TTcpChannel { stream: None }
+ }
+
+ /// Create a `TTcpChannel` that wraps an existing `TcpStream`.
+ ///
+ /// The passed-in stream is assumed to have been opened before being wrapped
+ /// by the created `TTcpChannel` instance.
+ pub fn with_stream(stream: TcpStream) -> TTcpChannel {
+ TTcpChannel {
+ stream: Some(stream),
+ }
+ }
+
+ /// Connect to `remote_address`, which should have the form `host:port`.
+ pub fn open(&mut self, remote_address: &str) -> ::Result<()> {
+ if self.stream.is_some() {
+ Err(new_transport_error(
+ TransportErrorKind::AlreadyOpen,
+ "tcp connection previously opened",
+ ))
+ } else {
+ match TcpStream::connect(&remote_address) {
+ Ok(s) => {
+ self.stream = Some(s);
+ Ok(())
+ }
+ Err(e) => Err(From::from(e)),
+ }
+ }
+ }
+
+ /// Shut down this channel.
+ ///
+ /// Both send and receive halves are closed, and this instance can no
+ /// longer be used to communicate with another endpoint.
+ pub fn close(&mut self) -> ::Result<()> {
+ self.if_set(|s| s.shutdown(Shutdown::Both))
+ .map_err(From::from)
+ }
+
+ fn if_set<F, T>(&mut self, mut stream_operation: F) -> io::Result<T>
+ where
+ F: FnMut(&mut TcpStream) -> io::Result<T>,
+ {
+ if let Some(ref mut s) = self.stream {
+ stream_operation(s)
+ } else {
+ Err(io::Error::new(
+ ErrorKind::NotConnected,
+ "tcp endpoint not connected",
+ ))
+ }
+ }
+}
+
+impl TIoChannel for TTcpChannel {
+ fn split(self) -> ::Result<(ReadHalf<Self>, WriteHalf<Self>)>
+ where
+ Self: Sized,
+ {
+ let mut s = self;
+
+ s.stream
+ .as_mut()
+ .and_then(|s| s.try_clone().ok())
+ .map(|cloned| {
+ let read_half = ReadHalf::new(TTcpChannel {
+ stream: s.stream.take(),
+ });
+ let write_half = WriteHalf::new(TTcpChannel {
+ stream: Some(cloned),
+ });
+ (read_half, write_half)
+ })
+ .ok_or_else(|| {
+ new_transport_error(
+ TransportErrorKind::Unknown,
+ "cannot clone underlying tcp stream",
+ )
+ })
+ }
+}
+
+impl Read for TTcpChannel {
+ fn read(&mut self, b: &mut [u8]) -> io::Result<usize> {
+ self.if_set(|s| s.read(b))
+ }
+}
+
+impl Write for TTcpChannel {
+ fn write(&mut self, b: &[u8]) -> io::Result<usize> {
+ self.if_set(|s| s.write(b))
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.if_set(|s| s.flush())
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/rs/test/Cargo.toml b/src/jaegertracing/thrift/lib/rs/test/Cargo.toml
new file mode 100644
index 000000000..dc4ffe32b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/test/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "kitchen-sink"
+version = "0.1.0"
+license = "Apache-2.0"
+authors = ["Apache Thrift Developers <dev@thrift.apache.org>"]
+publish = false
+
+[dependencies]
+clap = "<2.28.0"
+ordered-float = "1.0"
+try_from = "0.3"
+
+[dependencies.thrift]
+path = "../"
+
diff --git a/src/jaegertracing/thrift/lib/rs/test/Makefile.am b/src/jaegertracing/thrift/lib/rs/test/Makefile.am
new file mode 100644
index 000000000..486188cfe
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/test/Makefile.am
@@ -0,0 +1,55 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+stubs: thrifts/Base_One.thrift thrifts/Base_Two.thrift thrifts/Midlayer.thrift thrifts/Ultimate.thrift $(top_builddir)/test/Recursive.thrift $(THRIFT)
+ $(THRIFT) -I ./thrifts -out src --gen rs thrifts/Base_One.thrift
+ $(THRIFT) -I ./thrifts -out src --gen rs thrifts/Base_Two.thrift
+ $(THRIFT) -I ./thrifts -out src --gen rs thrifts/Midlayer.thrift
+ $(THRIFT) -I ./thrifts -out src --gen rs thrifts/Ultimate.thrift
+ $(THRIFT) -out src --gen rs $(top_builddir)/test/Recursive.thrift
+ $(THRIFT) -out src --gen rs $(top_builddir)/test/Identifiers.thrift #THRIFT-4953
+
+check: stubs
+ $(CARGO) build
+ $(CARGO) test
+ [ -d bin ] || mkdir bin
+ cp target/debug/kitchen_sink_server bin/kitchen_sink_server
+ cp target/debug/kitchen_sink_client bin/kitchen_sink_client
+
+clean-local:
+ $(CARGO) clean
+ -$(RM) Cargo.lock
+ -$(RM) src/base_one.rs
+ -$(RM) src/base_two.rs
+ -$(RM) src/midlayer.rs
+ -$(RM) src/ultimate.rs
+ -$(RM) -r bin
+
+EXTRA_DIST = \
+ Cargo.toml \
+ thrifts/Base_One.thrift \
+ thrifts/Base_Two.thrift \
+ thrifts/Midlayer.thrift \
+ thrifts/Ultimate.thrift \
+ src/lib.rs \
+ src/bin/kitchen_sink_server.rs \
+ src/bin/kitchen_sink_client.rs
+
diff --git a/src/jaegertracing/thrift/lib/rs/test/src/bin/kitchen_sink_client.rs b/src/jaegertracing/thrift/lib/rs/test/src/bin/kitchen_sink_client.rs
new file mode 100644
index 000000000..d295c8870
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/test/src/bin/kitchen_sink_client.rs
@@ -0,0 +1,239 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#[macro_use]
+extern crate clap;
+
+extern crate kitchen_sink;
+extern crate thrift;
+
+use std::convert::Into;
+
+use kitchen_sink::base_two::{TNapkinServiceSyncClient, TRamenServiceSyncClient};
+use kitchen_sink::midlayer::{MealServiceSyncClient, TMealServiceSyncClient};
+use kitchen_sink::recursive;
+use kitchen_sink::recursive::{CoRec, CoRec2, RecList, RecTree, TTestServiceSyncClient};
+use kitchen_sink::ultimate::{FullMealServiceSyncClient, TFullMealServiceSyncClient};
+use thrift::protocol::{
+ TBinaryInputProtocol, TBinaryOutputProtocol, TCompactInputProtocol, TCompactOutputProtocol,
+ TInputProtocol, TOutputProtocol,
+};
+use thrift::transport::{
+ ReadHalf, TFramedReadTransport, TFramedWriteTransport, TIoChannel, TTcpChannel, WriteHalf,
+};
+
+fn main() {
+ match run() {
+ Ok(()) => println!("kitchen sink client completed successfully"),
+ Err(e) => {
+ println!("kitchen sink client failed with error {:?}", e);
+ std::process::exit(1);
+ }
+ }
+}
+
+fn run() -> thrift::Result<()> {
+ let matches = clap_app!(rust_kitchen_sink_client =>
+ (version: "0.1.0")
+ (author: "Apache Thrift Developers <dev@thrift.apache.org>")
+ (about: "Thrift Rust kitchen sink client")
+ (@arg host: --host +takes_value "Host on which the Thrift test server is located")
+ (@arg port: --port +takes_value "Port on which the Thrift test server is listening")
+ (@arg protocol: --protocol +takes_value "Thrift protocol implementation to use (\"binary\", \"compact\")")
+ (@arg service: --service +takes_value "Service type to contact (\"part\", \"full\", \"recursive\")")
+ )
+ .get_matches();
+
+ let host = matches.value_of("host").unwrap_or("127.0.0.1");
+ let port = value_t!(matches, "port", u16).unwrap_or(9090);
+ let protocol = matches.value_of("protocol").unwrap_or("compact");
+ let service = matches.value_of("service").unwrap_or("part");
+
+ let (i_chan, o_chan) = tcp_channel(host, port)?;
+ let (i_tran, o_tran) = (
+ TFramedReadTransport::new(i_chan),
+ TFramedWriteTransport::new(o_chan),
+ );
+
+ let (i_prot, o_prot): (Box<TInputProtocol>, Box<TOutputProtocol>) = match protocol {
+ "binary" => (
+ Box::new(TBinaryInputProtocol::new(i_tran, true)),
+ Box::new(TBinaryOutputProtocol::new(o_tran, true)),
+ ),
+ "compact" => (
+ Box::new(TCompactInputProtocol::new(i_tran)),
+ Box::new(TCompactOutputProtocol::new(o_tran)),
+ ),
+ unmatched => return Err(format!("unsupported protocol {}", unmatched).into()),
+ };
+
+ run_client(service, i_prot, o_prot)
+}
+
+fn run_client(
+ service: &str,
+ i_prot: Box<TInputProtocol>,
+ o_prot: Box<TOutputProtocol>,
+) -> thrift::Result<()> {
+ match service {
+ "full" => exec_full_meal_client(i_prot, o_prot),
+ "part" => exec_meal_client(i_prot, o_prot),
+ "recursive" => exec_recursive_client(i_prot, o_prot),
+ _ => Err(thrift::Error::from(format!(
+ "unknown service type {}",
+ service
+ ))),
+ }
+}
+
+fn tcp_channel(
+ host: &str,
+ port: u16,
+) -> thrift::Result<(ReadHalf<TTcpChannel>, WriteHalf<TTcpChannel>)> {
+ let mut c = TTcpChannel::new();
+ c.open(&format!("{}:{}", host, port))?;
+ c.split()
+}
+
+fn exec_meal_client(
+ i_prot: Box<TInputProtocol>,
+ o_prot: Box<TOutputProtocol>,
+) -> thrift::Result<()> {
+ let mut client = MealServiceSyncClient::new(i_prot, o_prot);
+
+ // client.full_meal(); // <-- IMPORTANT: if you uncomment this, compilation *should* fail
+ // this is because the MealService struct does not contain the appropriate service marker
+
+ // only the following three calls work
+ execute_call("part", "ramen", || client.ramen(50)).map(|_| ())?;
+ execute_call("part", "meal", || client.meal()).map(|_| ())?;
+ execute_call("part", "napkin", || client.napkin()).map(|_| ())?;
+
+ Ok(())
+}
+
+fn exec_full_meal_client(
+ i_prot: Box<TInputProtocol>,
+ o_prot: Box<TOutputProtocol>,
+) -> thrift::Result<()> {
+ let mut client = FullMealServiceSyncClient::new(i_prot, o_prot);
+
+ execute_call("full", "ramen", || client.ramen(100)).map(|_| ())?;
+ execute_call("full", "meal", || client.meal()).map(|_| ())?;
+ execute_call("full", "napkin", || client.napkin()).map(|_| ())?;
+ execute_call("full", "full meal", || client.full_meal()).map(|_| ())?;
+
+ Ok(())
+}
+
+fn exec_recursive_client(
+ i_prot: Box<TInputProtocol>,
+ o_prot: Box<TOutputProtocol>,
+) -> thrift::Result<()> {
+ let mut client = recursive::TestServiceSyncClient::new(i_prot, o_prot);
+
+ let tree = RecTree {
+ children: Some(vec![Box::new(RecTree {
+ children: Some(vec![
+ Box::new(RecTree {
+ children: None,
+ item: Some(3),
+ }),
+ Box::new(RecTree {
+ children: None,
+ item: Some(4),
+ }),
+ ]),
+ item: Some(2),
+ })]),
+ item: Some(1),
+ };
+
+ let expected_tree = RecTree {
+ children: Some(vec![Box::new(RecTree {
+ children: Some(vec![
+ Box::new(RecTree {
+ children: Some(Vec::new()), // remote returns an empty list
+ item: Some(3),
+ }),
+ Box::new(RecTree {
+ children: Some(Vec::new()), // remote returns an empty list
+ item: Some(4),
+ }),
+ ]),
+ item: Some(2),
+ })]),
+ item: Some(1),
+ };
+
+ let returned_tree = execute_call("recursive", "echo_tree", || client.echo_tree(tree.clone()))?;
+ if returned_tree != expected_tree {
+ return Err(format!(
+ "mismatched recursive tree {:?} {:?}",
+ expected_tree, returned_tree
+ )
+ .into());
+ }
+
+ let list = RecList {
+ nextitem: Some(Box::new(RecList {
+ nextitem: Some(Box::new(RecList {
+ nextitem: None,
+ item: Some(3),
+ })),
+ item: Some(2),
+ })),
+ item: Some(1),
+ };
+ let returned_list = execute_call("recursive", "echo_list", || client.echo_list(list.clone()))?;
+ if returned_list != list {
+ return Err(format!("mismatched recursive list {:?} {:?}", list, returned_list).into());
+ }
+
+ let co_rec = CoRec {
+ other: Some(Box::new(CoRec2 {
+ other: Some(CoRec {
+ other: Some(Box::new(CoRec2 { other: None })),
+ }),
+ })),
+ };
+ let returned_co_rec = execute_call("recursive", "echo_co_rec", || {
+ client.echo_co_rec(co_rec.clone())
+ })?;
+ if returned_co_rec != co_rec {
+ return Err(format!("mismatched co_rec {:?} {:?}", co_rec, returned_co_rec).into());
+ }
+
+ Ok(())
+}
+
+fn execute_call<F, R>(service_type: &str, call_name: &str, mut f: F) -> thrift::Result<R>
+where
+ F: FnMut() -> thrift::Result<R>,
+{
+ let res = f();
+
+ match res {
+ Ok(_) => println!("{}: completed {} call", service_type, call_name),
+ Err(ref e) => println!(
+ "{}: failed {} call with error {:?}",
+ service_type, call_name, e
+ ),
+ }
+
+ res
+}
diff --git a/src/jaegertracing/thrift/lib/rs/test/src/bin/kitchen_sink_server.rs b/src/jaegertracing/thrift/lib/rs/test/src/bin/kitchen_sink_server.rs
new file mode 100644
index 000000000..73801eaf8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/test/src/bin/kitchen_sink_server.rs
@@ -0,0 +1,313 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#[macro_use]
+extern crate clap;
+extern crate kitchen_sink;
+extern crate thrift;
+
+use thrift::protocol::{
+ TBinaryInputProtocolFactory, TBinaryOutputProtocolFactory, TCompactInputProtocolFactory,
+ TCompactOutputProtocolFactory, TInputProtocolFactory, TOutputProtocolFactory,
+};
+use thrift::server::TServer;
+use thrift::transport::{
+ TFramedReadTransportFactory, TFramedWriteTransportFactory, TReadTransportFactory,
+ TWriteTransportFactory,
+};
+
+use kitchen_sink::base_one::Noodle;
+use kitchen_sink::base_two::{
+ BrothType, Napkin, NapkinServiceSyncHandler, Ramen, RamenServiceSyncHandler,
+};
+use kitchen_sink::midlayer::{
+ Dessert, Meal, MealServiceSyncHandler, MealServiceSyncProcessor, Pie,
+};
+use kitchen_sink::recursive;
+use kitchen_sink::ultimate::FullMealAndDrinksServiceSyncHandler;
+use kitchen_sink::ultimate::{
+ Drink, FullMeal, FullMealAndDrinks, FullMealAndDrinksServiceSyncProcessor,
+ FullMealServiceSyncHandler,
+};
+
+fn main() {
+ match run() {
+ Ok(()) => println!("kitchen sink server completed successfully"),
+ Err(e) => {
+ println!("kitchen sink server failed with error {:?}", e);
+ std::process::exit(1);
+ }
+ }
+}
+
+fn run() -> thrift::Result<()> {
+ let matches = clap_app!(rust_kitchen_sink_server =>
+ (version: "0.1.0")
+ (author: "Apache Thrift Developers <dev@thrift.apache.org>")
+ (about: "Thrift Rust kitchen sink test server")
+ (@arg port: --port +takes_value "port on which the test server listens")
+ (@arg protocol: --protocol +takes_value "Thrift protocol implementation to use (\"binary\", \"compact\")")
+ (@arg service: --service +takes_value "Service type to contact (\"part\", \"full\", \"recursive\")")
+ )
+ .get_matches();
+
+ let port = value_t!(matches, "port", u16).unwrap_or(9090);
+ let protocol = matches.value_of("protocol").unwrap_or("compact");
+ let service = matches.value_of("service").unwrap_or("part");
+ let listen_address = format!("127.0.0.1:{}", port);
+
+ println!("binding to {}", listen_address);
+
+ let r_transport_factory = TFramedReadTransportFactory::new();
+ let w_transport_factory = TFramedWriteTransportFactory::new();
+
+ let (i_protocol_factory, o_protocol_factory): (
+ Box<TInputProtocolFactory>,
+ Box<TOutputProtocolFactory>,
+ ) = match &*protocol {
+ "binary" => (
+ Box::new(TBinaryInputProtocolFactory::new()),
+ Box::new(TBinaryOutputProtocolFactory::new()),
+ ),
+ "compact" => (
+ Box::new(TCompactInputProtocolFactory::new()),
+ Box::new(TCompactOutputProtocolFactory::new()),
+ ),
+ unknown => {
+ return Err(format!("unsupported transport type {}", unknown).into());
+ }
+ };
+
+ // FIXME: should processor be boxed as well?
+ //
+ // [sigh] I hate Rust generics implementation
+ //
+ // I would have preferred to build a server here, return it, and then do
+ // the common listen-and-handle stuff, but since the server doesn't have a
+ // common type (because each match arm instantiates a server with a
+ // different processor) this isn't possible.
+ //
+ // Since what I'm doing is uncommon I'm just going to duplicate the code
+ match &*service {
+ "part" => run_meal_server(
+ &listen_address,
+ r_transport_factory,
+ i_protocol_factory,
+ w_transport_factory,
+ o_protocol_factory,
+ ),
+ "full" => run_full_meal_server(
+ &listen_address,
+ r_transport_factory,
+ i_protocol_factory,
+ w_transport_factory,
+ o_protocol_factory,
+ ),
+ "recursive" => run_recursive_server(
+ &listen_address,
+ r_transport_factory,
+ i_protocol_factory,
+ w_transport_factory,
+ o_protocol_factory,
+ ),
+ unknown => Err(format!("unsupported service type {}", unknown).into()),
+ }
+}
+
+fn run_meal_server<RTF, IPF, WTF, OPF>(
+ listen_address: &str,
+ r_transport_factory: RTF,
+ i_protocol_factory: IPF,
+ w_transport_factory: WTF,
+ o_protocol_factory: OPF,
+) -> thrift::Result<()>
+where
+ RTF: TReadTransportFactory + 'static,
+ IPF: TInputProtocolFactory + 'static,
+ WTF: TWriteTransportFactory + 'static,
+ OPF: TOutputProtocolFactory + 'static,
+{
+ let processor = MealServiceSyncProcessor::new(PartHandler {});
+ let mut server = TServer::new(
+ r_transport_factory,
+ i_protocol_factory,
+ w_transport_factory,
+ o_protocol_factory,
+ processor,
+ 1,
+ );
+
+ server.listen(listen_address)
+}
+
+fn run_full_meal_server<RTF, IPF, WTF, OPF>(
+ listen_address: &str,
+ r_transport_factory: RTF,
+ i_protocol_factory: IPF,
+ w_transport_factory: WTF,
+ o_protocol_factory: OPF,
+) -> thrift::Result<()>
+where
+ RTF: TReadTransportFactory + 'static,
+ IPF: TInputProtocolFactory + 'static,
+ WTF: TWriteTransportFactory + 'static,
+ OPF: TOutputProtocolFactory + 'static,
+{
+ let processor = FullMealAndDrinksServiceSyncProcessor::new(FullHandler {});
+ let mut server = TServer::new(
+ r_transport_factory,
+ i_protocol_factory,
+ w_transport_factory,
+ o_protocol_factory,
+ processor,
+ 1,
+ );
+
+ server.listen(listen_address)
+}
+
+struct PartHandler;
+
+impl MealServiceSyncHandler for PartHandler {
+ fn handle_meal(&self) -> thrift::Result<Meal> {
+ println!("part: handling meal call");
+ Ok(meal())
+ }
+}
+
+impl RamenServiceSyncHandler for PartHandler {
+ fn handle_ramen(&self, _: i32) -> thrift::Result<Ramen> {
+ println!("part: handling ramen call");
+ Ok(ramen())
+ }
+}
+
+impl NapkinServiceSyncHandler for PartHandler {
+ fn handle_napkin(&self) -> thrift::Result<Napkin> {
+ println!("part: handling napkin call");
+ Ok(napkin())
+ }
+}
+
+// full service
+//
+
+struct FullHandler;
+
+impl FullMealAndDrinksServiceSyncHandler for FullHandler {
+ fn handle_full_meal_and_drinks(&self) -> thrift::Result<FullMealAndDrinks> {
+ println!("full_meal_and_drinks: handling full meal and drinks call");
+ Ok(FullMealAndDrinks::new(full_meal(), Drink::CanadianWhisky))
+ }
+
+ fn handle_best_pie(&self) -> thrift::Result<Pie> {
+ println!("full_meal_and_drinks: handling pie call");
+ Ok(Pie::MississippiMud) // I prefer Pie::Pumpkin, but I have to check that casing works
+ }
+}
+
+impl FullMealServiceSyncHandler for FullHandler {
+ fn handle_full_meal(&self) -> thrift::Result<FullMeal> {
+ println!("full: handling full meal call");
+ Ok(full_meal())
+ }
+}
+
+impl MealServiceSyncHandler for FullHandler {
+ fn handle_meal(&self) -> thrift::Result<Meal> {
+ println!("full: handling meal call");
+ Ok(meal())
+ }
+}
+
+impl RamenServiceSyncHandler for FullHandler {
+ fn handle_ramen(&self, _: i32) -> thrift::Result<Ramen> {
+ println!("full: handling ramen call");
+ Ok(ramen())
+ }
+}
+
+impl NapkinServiceSyncHandler for FullHandler {
+ fn handle_napkin(&self) -> thrift::Result<Napkin> {
+ println!("full: handling napkin call");
+ Ok(napkin())
+ }
+}
+
+fn full_meal() -> FullMeal {
+ FullMeal::new(meal(), Dessert::Port("Graham's Tawny".to_owned()))
+}
+
+fn meal() -> Meal {
+ Meal::new(noodle(), ramen())
+}
+
+fn noodle() -> Noodle {
+ Noodle::new("spelt".to_owned(), 100)
+}
+
+fn ramen() -> Ramen {
+ Ramen::new("Mr Ramen".to_owned(), 72, BrothType::Miso)
+}
+
+fn napkin() -> Napkin {
+ Napkin {}
+}
+
+fn run_recursive_server<RTF, IPF, WTF, OPF>(
+ listen_address: &str,
+ r_transport_factory: RTF,
+ i_protocol_factory: IPF,
+ w_transport_factory: WTF,
+ o_protocol_factory: OPF,
+) -> thrift::Result<()>
+where
+ RTF: TReadTransportFactory + 'static,
+ IPF: TInputProtocolFactory + 'static,
+ WTF: TWriteTransportFactory + 'static,
+ OPF: TOutputProtocolFactory + 'static,
+{
+ let processor = recursive::TestServiceSyncProcessor::new(RecursiveTestServerHandler {});
+ let mut server = TServer::new(
+ r_transport_factory,
+ i_protocol_factory,
+ w_transport_factory,
+ o_protocol_factory,
+ processor,
+ 1,
+ );
+
+ server.listen(listen_address)
+}
+
+struct RecursiveTestServerHandler;
+impl recursive::TestServiceSyncHandler for RecursiveTestServerHandler {
+ fn handle_echo_tree(&self, tree: recursive::RecTree) -> thrift::Result<recursive::RecTree> {
+ println!("{:?}", tree);
+ Ok(tree)
+ }
+
+ fn handle_echo_list(&self, lst: recursive::RecList) -> thrift::Result<recursive::RecList> {
+ println!("{:?}", lst);
+ Ok(lst)
+ }
+
+ fn handle_echo_co_rec(&self, item: recursive::CoRec) -> thrift::Result<recursive::CoRec> {
+ println!("{:?}", item);
+ Ok(item)
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/rs/test/src/lib.rs b/src/jaegertracing/thrift/lib/rs/test/src/lib.rs
new file mode 100644
index 000000000..9debdca54
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/test/src/lib.rs
@@ -0,0 +1,55 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+extern crate thrift;
+
+pub mod base_one;
+pub mod base_two;
+pub mod midlayer;
+pub mod ultimate;
+pub mod recursive;
+
+#[cfg(test)]
+mod tests {
+
+ use std::default::Default;
+
+ use super::*;
+
+ #[test]
+ fn must_be_able_to_use_constructor() {
+ let _ = midlayer::Meal::new(Some(base_one::Noodle::default()), None);
+ }
+
+ #[test]
+ fn must_be_able_to_use_constructor_with_no_fields() {
+ let _ = midlayer::Meal::new(None, None);
+ }
+
+ #[test]
+ fn must_be_able_to_use_constructor_without_option_wrap() {
+ let _ = midlayer::Meal::new(base_one::Noodle::default(), None);
+ }
+
+ #[test]
+ fn must_be_able_to_use_defaults() {
+ let _ = midlayer::Meal {
+ noodle: Some(base_one::Noodle::default()),
+ ..Default::default()
+ };
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/rs/test/thrifts/Base_One.thrift b/src/jaegertracing/thrift/lib/rs/test/thrifts/Base_One.thrift
new file mode 100644
index 000000000..c5fa6c20d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/test/thrifts/Base_One.thrift
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+typedef i64 Temperature
+
+typedef i8 Size
+
+typedef string Location
+
+const i32 BoilingPoint = 100
+
+const list<Temperature> Temperatures = [10, 11, 22, 33]
+
+// IMPORTANT: temps should end with ".0" because this tests
+// that we don't have a problem with const float list generation
+const list<double> CommonTemperatures = [300.0, 450.0]
+
+const double MealsPerDay = 2.5;
+
+const string DefaultRecipeName = "Soup-rise of the Day"
+const binary DefaultRecipeBinary = "Soup-rise of the 01010101"
+
+struct Noodle {
+ 1: string flourType
+ 2: Temperature cookTemp
+}
+
+struct Spaghetti {
+ 1: optional list<Noodle> noodles
+}
+
+const Noodle SpeltNoodle = { "flourType": "spelt", "cookTemp": 110 }
+
+struct MeasuringSpoon {
+ 1: Size size
+}
+
+struct MeasuringCup {
+ 1: double millis
+}
+
+union MeasuringAids {
+ 1: MeasuringSpoon spoon
+ 2: MeasuringCup cup
+}
+
+struct CookingTemperatures {
+ 1: set<double> commonTemperatures
+ 2: list<double> usedTemperatures
+ 3: map<double, double> fahrenheitToCentigradeConversions
+}
+
+struct Recipe {
+ 1: string recipeName
+ 2: string cuisine
+ 3: i8 page
+}
+
+union CookingTools {
+ 1: set<MeasuringSpoon> measuringSpoons
+ 2: map<Size, Location> measuringCups,
+ 3: list<Recipe> recipes
+}
+
diff --git a/src/jaegertracing/thrift/lib/rs/test/thrifts/Base_Two.thrift b/src/jaegertracing/thrift/lib/rs/test/thrifts/Base_Two.thrift
new file mode 100644
index 000000000..caa6acb86
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/test/thrifts/Base_Two.thrift
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+const i32 WaterWeight = 200
+
+enum brothType {
+ Miso,
+ shouyu,
+}
+
+struct Ramen {
+ 1: optional string ramenType
+ 2: required i32 noodleCount
+ 3: brothType broth
+}
+
+struct Napkin {
+ // empty
+}
+
+service NapkinService {
+ Napkin napkin()
+}
+
+service RamenService extends NapkinService {
+ Ramen ramen(1: i32 requestedNoodleCount)
+}
+
+/* const struct CookedRamen = { "bar": 10 } */
+
diff --git a/src/jaegertracing/thrift/lib/rs/test/thrifts/Midlayer.thrift b/src/jaegertracing/thrift/lib/rs/test/thrifts/Midlayer.thrift
new file mode 100644
index 000000000..16ff49b0e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/test/thrifts/Midlayer.thrift
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+include "Base_One.thrift"
+include "Base_Two.thrift"
+
+const i32 WaterBoilingPoint = Base_One.BoilingPoint
+
+const map<string, Base_One.Temperature> TemperatureNames = { "freezing": 0, "boiling": 100 }
+
+const map<set<i32>, map<list<string>, string>> MyConstNestedMap = {
+ [0, 1, 2, 3]: { ["foo"]: "bar" },
+ [20]: { ["nut", "ton"] : "bar" },
+ [30, 40]: { ["bouncy", "tinkly"]: "castle" }
+}
+
+const list<list<i32>> MyConstNestedList = [
+ [0, 1, 2],
+ [3, 4, 5],
+ [6, 7, 8]
+]
+
+const set<set<i32>> MyConstNestedSet = [
+ [0, 1, 2],
+ [3, 4, 5],
+ [6, 7, 8]
+]
+
+enum Pie {
+ PUMPKIN,
+ apple, // intentionally poorly cased
+ STRAWBERRY_RHUBARB,
+ Key_Lime, // intentionally poorly cased
+ coconut_Cream, // intentionally poorly cased
+ mississippi_mud, // intentionally poorly cased
+}
+
+struct Meal {
+ 1: Base_One.Noodle noodle
+ 2: Base_Two.Ramen ramen
+}
+
+union Dessert {
+ 1: string port
+ 2: string iceWine
+}
+
+service MealService extends Base_Two.RamenService {
+ Meal meal()
+}
+
diff --git a/src/jaegertracing/thrift/lib/rs/test/thrifts/Ultimate.thrift b/src/jaegertracing/thrift/lib/rs/test/thrifts/Ultimate.thrift
new file mode 100644
index 000000000..72fa100a6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rs/test/thrifts/Ultimate.thrift
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+include "Midlayer.thrift"
+
+enum Drink {
+ WATER,
+ WHISKEY,
+ WINE,
+ scotch, // intentionally poorly cased
+ LATE_HARVEST_WINE,
+ India_Pale_Ale, // intentionally poorly cased
+ apple_cider, // intentially poorly cased
+ belgian_Ale, // intentionally poorly cased
+ Canadian_whisky, // intentionally poorly cased
+}
+
+const map<i8, Midlayer.Pie> RankedPies = {
+ 1: Midlayer.Pie.PUMPKIN,
+ 2: Midlayer.Pie.STRAWBERRY_RHUBARB,
+ 3: Midlayer.Pie.apple,
+ 4: Midlayer.Pie.mississippi_mud,
+ 5: Midlayer.Pie.coconut_Cream,
+ 6: Midlayer.Pie.Key_Lime,
+}
+
+struct FullMeal {
+ 1: required Midlayer.Meal meal
+ 2: required Midlayer.Dessert dessert
+}
+
+struct FullMealAndDrinks {
+ 1: required FullMeal fullMeal
+ 2: optional Drink drink
+}
+
+service FullMealService extends Midlayer.MealService {
+ FullMeal fullMeal()
+}
+
+service FullMealAndDrinksService extends FullMealService {
+ FullMealAndDrinks fullMealAndDrinks()
+
+ Midlayer.Pie bestPie()
+}
+
diff --git a/src/jaegertracing/thrift/lib/st/README.md b/src/jaegertracing/thrift/lib/st/README.md
new file mode 100644
index 000000000..5b5fdeefc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/st/README.md
@@ -0,0 +1,39 @@
+Thrift SmallTalk Software Library
+
+Last updated Nov 2007
+
+License
+=======
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+Contains some contributions under the Thrift Software License.
+Please see doc/old-thrift-license.txt in the Thrift distribution for
+details.
+
+Library
+=======
+
+To get started, just file in thrift.st with Squeak, run thrift -st
+on the tutorial .thrift files (and file in the resulting code), and
+then:
+
+calc := CalculatorClient binaryOnHost: 'localhost' port: '9090'
+calc addNum1: 10 num2: 15
+
+Tested in Squeak 3.7, but should work fine with anything later.
diff --git a/src/jaegertracing/thrift/lib/st/coding_standards.md b/src/jaegertracing/thrift/lib/st/coding_standards.md
new file mode 100644
index 000000000..fa0390bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/st/coding_standards.md
@@ -0,0 +1 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
diff --git a/src/jaegertracing/thrift/lib/st/package.xml b/src/jaegertracing/thrift/lib/st/package.xml
new file mode 100644
index 000000000..21b7adcd2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/st/package.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ -->
+<!-- Apache Thrift Smalltalk library version 0.13.0 -->
+<package>
+ <name>libthrift-st</name>
+ <file>thrift.st</file>
+ <filein>thrift.st</filein>
+</package>
+
diff --git a/src/jaegertracing/thrift/lib/st/thrift.st b/src/jaegertracing/thrift/lib/st/thrift.st
new file mode 100644
index 000000000..fdb66dfc0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/st/thrift.st
@@ -0,0 +1,815 @@
+"
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+License); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+AS IS BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+Contains some contributions under the Thrift Software License.
+Please see doc/old-thrift-license.txt in the Thrift distribution for
+details.
+"
+
+SystemOrganization addCategory: #Thrift!
+SystemOrganization addCategory: #'Thrift-Protocol'!
+SystemOrganization addCategory: #'Thrift-Transport'!
+
+Error subclass: #TError
+ instanceVariableNames: 'code'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift'!
+
+!TError class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:28'!
+signalWithCode: anInteger
+ self new code: anInteger; signal! !
+
+!TError methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:28'!
+code
+ ^ code! !
+
+!TError methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:28'!
+code: anInteger
+ code := anInteger! !
+
+TError subclass: #TProtocolError
+ instanceVariableNames: ''
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift-Protocol'!
+
+!TProtocolError class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 18:39'!
+badVersion
+ ^ 4! !
+
+!TProtocolError class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 18:39'!
+invalidData
+ ^ 1! !
+
+!TProtocolError class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 18:39'!
+negativeSize
+ ^ 2! !
+
+!TProtocolError class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 18:40'!
+sizeLimit
+ ^ 3! !
+
+!TProtocolError class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 18:40'!
+unknown
+ ^ 0! !
+
+TError subclass: #TTransportError
+ instanceVariableNames: ''
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift-Transport'!
+
+TTransportError subclass: #TTransportClosedError
+ instanceVariableNames: ''
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift-Transport'!
+
+Object subclass: #TClient
+ instanceVariableNames: 'iprot oprot seqid remoteSeqid'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift'!
+
+!TClient class methodsFor: 'as yet unclassified' stamp: 'pc 11/7/2007 06:00'!
+binaryOnHost: aString port: anInteger
+ | sock |
+ sock := TSocket new host: aString; port: anInteger; open; yourself.
+ ^ self new
+ inProtocol: (TBinaryProtocol new transport: sock);
+ yourself! !
+
+!TClient methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 23:03'!
+inProtocol: aProtocol
+ iprot := aProtocol.
+ oprot ifNil: [oprot := aProtocol]! !
+
+!TClient methodsFor: 'as yet unclassified' stamp: 'pc 10/26/2007 04:28'!
+nextSeqid
+ ^ seqid
+ ifNil: [seqid := 0]
+ ifNotNil: [seqid := seqid + 1]! !
+
+!TClient methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 22:51'!
+outProtocol: aProtocol
+ oprot := aProtocol! !
+
+!TClient methodsFor: 'as yet unclassified' stamp: 'pc 10/28/2007 15:32'!
+validateRemoteMessage: aMsg
+ remoteSeqid
+ ifNil: [remoteSeqid := aMsg seqid]
+ ifNotNil:
+ [(remoteSeqid + 1) = aMsg seqid ifFalse:
+ [TProtocolError signal: 'Bad seqid: ', aMsg seqid asString,
+ '; wanted: ', remoteSeqid asString].
+ remoteSeqid := aMsg seqid]! !
+
+Object subclass: #TField
+ instanceVariableNames: 'name type id'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift-Protocol'!
+
+!TField methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:05'!
+id
+ ^ id ifNil: [0]! !
+
+!TField methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:44'!
+id: anInteger
+ id := anInteger! !
+
+!TField methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:04'!
+name
+ ^ name ifNil: ['']! !
+
+!TField methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:44'!
+name: anObject
+ name := anObject! !
+
+!TField methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:05'!
+type
+ ^ type ifNil: [TType stop]! !
+
+!TField methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:44'!
+type: anInteger
+ type := anInteger! !
+
+Object subclass: #TMessage
+ instanceVariableNames: 'name seqid type'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift-Protocol'!
+
+TMessage subclass: #TCallMessage
+ instanceVariableNames: ''
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift-Protocol'!
+
+!TCallMessage methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 22:53'!
+type
+ ^ 1! !
+
+!TMessage methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:05'!
+name
+ ^ name ifNil: ['']! !
+
+!TMessage methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:35'!
+name: aString
+ name := aString! !
+
+!TMessage methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:05'!
+seqid
+ ^ seqid ifNil: [0]! !
+
+!TMessage methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:35'!
+seqid: anInteger
+ seqid := anInteger! !
+
+!TMessage methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:06'!
+type
+ ^ type ifNil: [0]! !
+
+!TMessage methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:35'!
+type: anInteger
+ type := anInteger! !
+
+Object subclass: #TProtocol
+ instanceVariableNames: 'transport'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift-Protocol'!
+
+TProtocol subclass: #TBinaryProtocol
+ instanceVariableNames: ''
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift-Protocol'!
+
+!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 11/1/2007 04:24'!
+intFromByteArray: buf
+ | vals |
+ vals := Array new: buf size.
+ 1 to: buf size do: [:n | vals at: n put: ((buf at: n) bitShift: (buf size - n) * 8)].
+ ^ vals sum! !
+
+!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 18:46'!
+readBool
+ ^ self readByte isZero not! !
+
+!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/25/2007 00:02'!
+readByte
+ ^ (self transport read: 1) first! !
+
+!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/28/2007 16:24'!
+readDouble
+ | val |
+ val := Float new: 2.
+ ^ val basicAt: 1 put: (self readRawInt: 4);
+ basicAt: 2 put: (self readRawInt: 4);
+ yourself! !
+
+!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 20:02'!
+readFieldBegin
+ | field |
+ field := TField new type: self readByte.
+
+ ^ field type = TType stop
+ ifTrue: [field]
+ ifFalse: [field id: self readI16; yourself]! !
+
+!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:15'!
+readI16
+ ^ self readInt: 2! !
+
+!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:20'!
+readI32
+ ^ self readInt: 4! !
+
+!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:20'!
+readI64
+ ^ self readInt: 8! !
+
+!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 11/1/2007 02:35'!
+readInt: size
+ | buf val |
+ buf := transport read: size.
+ val := self intFromByteArray: buf.
+ ^ buf first > 16r7F
+ ifTrue: [self unsignedInt: val size: size]
+ ifFalse: [val]! !
+
+!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:57'!
+readListBegin
+ ^ TList new
+ elemType: self readByte;
+ size: self readI32! !
+
+!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:58'!
+readMapBegin
+ ^ TMap new
+ keyType: self readByte;
+ valueType: self readByte;
+ size: self readI32! !
+
+!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 11/1/2007 04:22'!
+readMessageBegin
+ | version |
+ version := self readI32.
+
+ (version bitAnd: self versionMask) = self version1
+ ifFalse: [TProtocolError signalWithCode: TProtocolError badVersion].
+
+ ^ TMessage new
+ type: (version bitAnd: 16r000000FF);
+ name: self readString;
+ seqid: self readI32! !
+
+!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/28/2007 16:24'!
+readRawInt: size
+ ^ self intFromByteArray: (transport read: size)! !
+
+!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 11/1/2007 00:59'!
+readSetBegin
+ "element type, size"
+ ^ TSet new
+ elemType: self readByte;
+ size: self readI32! !
+
+!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 02/07/2009 19:00'!
+readString
+ | sz |
+ sz := self readI32.
+ ^ sz > 0 ifTrue: [(transport read: sz) asString] ifFalse: ['']! !
+
+!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 11/1/2007 04:22'!
+unsignedInt: val size: size
+ ^ 0 - ((val - 1) bitXor: ((2 raisedTo: (size * 8)) - 1))! !
+
+!TBinaryProtocol methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 22:13'!
+version1
+ ^ 16r80010000 ! !
+
+!TBinaryProtocol methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 18:01'!
+versionMask
+ ^ 16rFFFF0000! !
+
+!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 18:35'!
+write: aString
+ transport write: aString! !
+
+!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:23'!
+writeBool: bool
+ bool ifTrue: [self writeByte: 1]
+ ifFalse: [self writeByte: 0]! !
+
+!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/26/2007 09:31'!
+writeByte: aNumber
+ aNumber > 16rFF ifTrue: [TError signal: 'writeByte too big'].
+ transport write: (Array with: aNumber)! !
+
+!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/28/2007 16:16'!
+writeDouble: aDouble
+ self writeI32: (aDouble basicAt: 1);
+ writeI32: (aDouble basicAt: 2)! !
+
+!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:56'!
+writeField: aField
+ self writeByte: aField type;
+ writeI16: aField id! !
+
+!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/25/2007 00:01'!
+writeFieldBegin: aField
+ self writeByte: aField type.
+ self writeI16: aField id! !
+
+!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 18:04'!
+writeFieldStop
+ self writeByte: TType stop! !
+
+!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 11/1/2007 02:06'!
+writeI16: i16
+ self writeInt: i16 size: 2! !
+
+!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 11/1/2007 02:06'!
+writeI32: i32
+ self writeInt: i32 size: 4! !
+
+!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 11/1/2007 02:06'!
+writeI64: i64
+ self writeInt: i64 size: 8! !
+
+!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 11/1/2007 04:23'!
+writeInt: val size: size
+ 1 to: size do: [:n | self writeByte: ((val bitShift: (size negated + n) * 8) bitAnd: 16rFF)]! !
+
+!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 11/1/2007 00:48'!
+writeListBegin: aList
+ self writeByte: aList elemType; writeI32: aList size! !
+
+!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:55'!
+writeMapBegin: aMap
+ self writeByte: aMap keyType;
+ writeByte: aMap valueType;
+ writeI32: aMap size! !
+
+!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 20:36'!
+writeMessageBegin: msg
+ self writeI32: (self version1 bitOr: msg type);
+ writeString: msg name;
+ writeI32: msg seqid! !
+
+!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 11/1/2007 00:56'!
+writeSetBegin: aSet
+ self writeByte: aSet elemType; writeI32: aSet size! !
+
+!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 18:35'!
+writeString: aString
+ self writeI32: aString size;
+ write: aString! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'!
+readBool! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'!
+readByte! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'!
+readDouble! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'!
+readFieldBegin! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'!
+readFieldEnd! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'!
+readI16! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'!
+readI32! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'!
+readI64! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'!
+readListBegin! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'!
+readListEnd! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'!
+readMapBegin! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'!
+readMapEnd! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:39'!
+readMessageBegin! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:39'!
+readMessageEnd! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'!
+readSetBegin! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'!
+readSetEnd! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/25/2007 16:10'!
+readSimpleType: aType
+ aType = TType bool ifTrue: [^ self readBool].
+ aType = TType byte ifTrue: [^ self readByte].
+ aType = TType double ifTrue: [^ self readDouble].
+ aType = TType i16 ifTrue: [^ self readI16].
+ aType = TType i32 ifTrue: [^ self readI32].
+ aType = TType i64 ifTrue: [^ self readI64].
+ aType = TType list ifTrue: [^ self readBool].! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'!
+readString! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'!
+readStructBegin
+ ! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'!
+readStructEnd! !
+
+!TProtocol methodsFor: 'reading' stamp: 'pc 10/26/2007 21:34'!
+skip: aType
+ aType = TType stop ifTrue: [^ self].
+ aType = TType bool ifTrue: [^ self readBool].
+ aType = TType byte ifTrue: [^ self readByte].
+ aType = TType i16 ifTrue: [^ self readI16].
+ aType = TType i32 ifTrue: [^ self readI32].
+ aType = TType i64 ifTrue: [^ self readI64].
+ aType = TType string ifTrue: [^ self readString].
+ aType = TType double ifTrue: [^ self readDouble].
+ aType = TType struct ifTrue:
+ [| field |
+ self readStructBegin.
+ [(field := self readFieldBegin) type = TType stop] whileFalse:
+ [self skip: field type. self readFieldEnd].
+ ^ self readStructEnd].
+ aType = TType map ifTrue:
+ [| map |
+ map := self readMapBegin.
+ map size timesRepeat: [self skip: map keyType. self skip: map valueType].
+ ^ self readMapEnd].
+ aType = TType list ifTrue:
+ [| list |
+ list := self readListBegin.
+ list size timesRepeat: [self skip: list elemType].
+ ^ self readListEnd].
+ aType = TType set ifTrue:
+ [| set |
+ set := self readSetBegin.
+ set size timesRepeat: [self skip: set elemType].
+ ^ self readSetEnd].
+
+ self error: 'Unknown type'! !
+
+!TProtocol methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 23:02'!
+transport
+ ^ transport! !
+
+!TProtocol methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:28'!
+transport: aTransport
+ transport := aTransport! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'!
+writeBool: aBool! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'!
+writeByte: aByte! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:38'!
+writeDouble: aFloat! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:38'!
+writeFieldBegin: aField! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'!
+writeFieldEnd! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'!
+writeFieldStop! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'!
+writeI16: i16! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'!
+writeI32: i32! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'!
+writeI64: i64! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:39'!
+writeListBegin: aList! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'!
+writeListEnd! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:39'!
+writeMapBegin: aMap! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'!
+writeMapEnd! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:36'!
+writeMessageBegin! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:36'!
+writeMessageEnd! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:39'!
+writeSetBegin: aSet! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'!
+writeSetEnd! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:38'!
+writeString: aString! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:38'!
+writeStructBegin: aStruct! !
+
+!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'!
+writeStructEnd! !
+
+Object subclass: #TResult
+ instanceVariableNames: 'success oprot iprot exception'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift'!
+
+!TResult methodsFor: 'as yet unclassified' stamp: 'pc 10/26/2007 21:35'!
+exception
+ ^ exception! !
+
+!TResult methodsFor: 'as yet unclassified' stamp: 'pc 10/26/2007 21:35'!
+exception: anError
+ exception := anError! !
+
+!TResult methodsFor: 'as yet unclassified' stamp: 'pc 10/26/2007 14:43'!
+success
+ ^ success! !
+
+!TResult methodsFor: 'as yet unclassified' stamp: 'pc 10/26/2007 14:43'!
+success: anObject
+ success := anObject! !
+
+Object subclass: #TSizedObject
+ instanceVariableNames: 'size'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift-Protocol'!
+
+TSizedObject subclass: #TList
+ instanceVariableNames: 'elemType'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift-Protocol'!
+
+!TList methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:04'!
+elemType
+ ^ elemType ifNil: [TType stop]! !
+
+!TList methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:42'!
+elemType: anInteger
+ elemType := anInteger! !
+
+TList subclass: #TSet
+ instanceVariableNames: ''
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift-Protocol'!
+
+TSizedObject subclass: #TMap
+ instanceVariableNames: 'keyType valueType'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift-Protocol'!
+
+!TMap methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:04'!
+keyType
+ ^ keyType ifNil: [TType stop]! !
+
+!TMap methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:45'!
+keyType: anInteger
+ keyType := anInteger! !
+
+!TMap methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:04'!
+valueType
+ ^ valueType ifNil: [TType stop]! !
+
+!TMap methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:45'!
+valueType: anInteger
+ valueType := anInteger! !
+
+!TSizedObject methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 20:03'!
+size
+ ^ size ifNil: [0]! !
+
+!TSizedObject methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 20:06'!
+size: anInteger
+ size := anInteger! !
+
+Object subclass: #TSocket
+ instanceVariableNames: 'host port stream'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift-Transport'!
+
+!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 22:34'!
+close
+ self isOpen ifTrue: [stream close]! !
+
+!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 22:23'!
+connect
+ ^ (self socketStream openConnectionToHost:
+ (NetNameResolver addressForName: host) port: port)
+ timeout: 180;
+ binary;
+ yourself! !
+
+!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 20:35'!
+flush
+ stream flush! !
+
+!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:08'!
+host: aString
+ host := aString! !
+
+!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 20:34'!
+isOpen
+ ^ stream isNil not
+ and: [stream socket isConnected]
+ and: [stream socket isOtherEndClosed not]! !
+
+!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 22:22'!
+open
+ stream := self connect! !
+
+!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:09'!
+port: anInteger
+ port := anInteger! !
+
+!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:17'!
+read: size
+ | data |
+ [data := stream next: size.
+ data isEmpty ifTrue: [TTransportError signal: 'Could not read ', size asString, ' bytes'].
+ ^ data]
+ on: ConnectionClosed
+ do: [TTransportClosedError signal]! !
+
+!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 22:18'!
+socketStream
+ ^ Smalltalk at: #FastSocketStream ifAbsent: [SocketStream] ! !
+
+!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 22:17'!
+write: aCollection
+ [stream nextPutAll: aCollection]
+ on: ConnectionClosed
+ do: [TTransportClosedError signal]! !
+
+Object subclass: #TStruct
+ instanceVariableNames: 'name'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift-Protocol'!
+
+!TStruct methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:47'!
+name
+ ^ name! !
+
+!TStruct methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:47'!
+name: aString
+ name := aString! !
+
+Object subclass: #TTransport
+ instanceVariableNames: ''
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift-Transport'!
+
+!TTransport methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:18'!
+close
+ self subclassResponsibility! !
+
+!TTransport methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:22'!
+flush
+ self subclassResponsibility! !
+
+!TTransport methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:18'!
+isOpen
+ self subclassResponsibility! !
+
+!TTransport methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:18'!
+open
+ self subclassResponsibility! !
+
+!TTransport methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:18'!
+read: anInteger
+ self subclassResponsibility! !
+
+!TTransport methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:22'!
+readAll: anInteger
+ ^ String streamContents: [:str |
+ [str size < anInteger] whileTrue:
+ [str nextPutAll: (self read: anInteger - str size)]]! !
+
+!TTransport methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:22'!
+write: aString
+ self subclassResponsibility! !
+
+Object subclass: #TType
+ instanceVariableNames: ''
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Thrift'!
+
+!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:03'!
+bool
+ ^ 2! !
+
+!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:03'!
+byte
+ ^ 3! !
+
+!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/25/2007 15:55'!
+codeOf: aTypeName
+ self typeMap do: [:each | each first = aTypeName ifTrue: [^ each second]].
+ ^ nil! !
+
+!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:03'!
+double
+ ^ 4! !
+
+!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:04'!
+i16
+ ^ 6! !
+
+!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:04'!
+i32
+ ^ 8! !
+
+!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:04'!
+i64
+ ^ 10! !
+
+!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:04'!
+list
+ ^ 15! !
+
+!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:04'!
+map
+ ^ 13! !
+
+!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/25/2007 15:56'!
+nameOf: aTypeCode
+ self typeMap do: [:each | each second = aTypeCode ifTrue: [^ each first]].
+ ^ nil! !
+
+!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:04'!
+set
+ ^ 14! !
+
+!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:03'!
+stop
+ ^ 0! !
+
+!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:04'!
+string
+ ^ 11! !
+
+!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:04'!
+struct
+ ^ 12! !
+
+!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/25/2007 15:51'!
+typeMap
+ ^ #((bool 2) (byte 3) (double 4) (i16 6) (i32 8) (i64 10) (list 15)
+ (map 13) (set 15) (stop 0) (string 11) (struct 12) (void 1))! !
+
+!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:03'!
+void
+ ^ 1! !
diff --git a/src/jaegertracing/thrift/lib/swift/Makefile.am b/src/jaegertracing/thrift/lib/swift/Makefile.am
new file mode 100644
index 000000000..6b88b06a7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Makefile.am
@@ -0,0 +1,46 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+SUBDIRS = .
+
+all-local:
+ swift build --configuration release
+
+install-exec-hook:
+ swift install
+
+clean-local:
+ swift package clean
+ rm -rf .build
+
+precross:
+ swift
+
+check-local:
+ swift test
+
+EXTRA_DIST = \
+ Package.swift \
+ Sources \
+ Tests \
+ README.md
+
+MAINTAINERCLEANFILES = \
+ Makefile \
+ Makefile.in
diff --git a/src/jaegertracing/thrift/lib/swift/Package.swift b/src/jaegertracing/thrift/lib/swift/Package.swift
new file mode 100644
index 000000000..b533f6086
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Package.swift
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import PackageDescription
+
+let package = Package(
+ name: "Thrift"
+)
diff --git a/src/jaegertracing/thrift/lib/swift/README.md b/src/jaegertracing/thrift/lib/swift/README.md
new file mode 100644
index 000000000..6f1096184
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/README.md
@@ -0,0 +1,215 @@
+Thrift Swift Library
+=========================
+
+License
+-------
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+
+## Build
+ swift build
+
+## Test
+ swift test
+
+## Install Library
+##### Cocoapods
+Add the following to your podfile
+```ruby
+ pod 'Thrift-swift3', :git => 'git@github.com:apache/thrift.git', :branch => 'master'
+```
+
+##### SPM
+Unfortunately due to some limitations in SPM, the Package manifest and Sources directory must be at the root of the project.
+To get around that for the time being, you can use this mirrored repo.
+Add the following to your Package.swift
+```swift
+dependencies: [
+ .Package(url: "https://github.com/apocolipse/Thrift-Swift.git", majorVersion: 1)
+]
+```
+
+## Thrift Compiler
+
+You can compile IDL sources for Swift 3 with the following command:
+
+ thrift --gen swift thrift_file
+
+## Client Example
+```swift
+let transport = TSocketTransport(hostname: "localhost", port: 9090)!
+
+// var proto = TCompactProtocol(transport: transport)
+let proto = TBinaryProtocol(on: transport)
+// var client = HermesClient(inoutProtocol: proto)
+let client = ThriftTestClient(inoutProtocol: proto)
+do {
+ try client.testVoid()
+} catch let error {
+ print("\(error)")
+}
+```
+
+## Library Notes
+- Eliminated Protocol Factories, They were only used in async clients and server implementations, where Generics provide a better alternative.
+- Swifty Errors, All `TError` types have a nested `ErrorCode` Enum as well as some extra flavor where needed.
+- Value typed everything. `TTransport` operates on value typed `Data` rather than reference typed `NSData` or `UnsafeBufferPointer`s
+- Swift 3 Named protocols. Swift 3 naming conventions suggest the elimination of redundant words that can be inferred from variable/function signatures. This renaming is applied throughout the Swift 3 library converting most naming conventions used in the Swift2/Cocoa library to Swift 3-esque naming conventions. eg.
+```swift
+func readString() throws -> String
+func writeString(_ val: String) throws
+```
+have been renamed to eliminate redundant words:
+```swift
+func read() throws -> String
+func write(_ val: String) throws
+```
+
+- Eliminated `THTTPTransport` that uses `NSURLConnection` due to it being deprecated and not available at all in Swift 3 for Linux. `THTTPSessionTransport` from the Swift2/Cocoa library that uses `NSURLSession` has been renamed to `THTTPTransport` for this library and leverages `URLSession`, providing both synchronous (with semaphores) and asynchronous behavior.
+- Probably some More things I've missed here.
+
+## Generator Notes
+#### Generator Flags
+| Flag | Description |
+| ------------- |:-------------:|
+| async_clients | Generate clients which invoke asynchronously via block syntax. Asynchronous classes are appended with `_Async` |
+| no_strict* | Generates non-strict structs |
+| debug_descriptions | Allow use of debugDescription so the app can add description via a cateogory/extension |
+| log_unexpected | Log every time an unexpected field ID or type is encountered. |
+| safe_enums | Generate enum types with an unknown case to handle unspecified values rather than throw a serialization error |
+
+
+
+*Most thrift libraries allow empty initialization of Structs, initializing `required` fields with nil/null/None (Python and Node generators). Swift on the other hand requires initializers to initialize all non-Optional fields, and thus the Swift 3 generator does not provide default values (unlike the Swift 2/Cocoa generator). In other languages, this allows the sending of NULL values in fields that are marked `required`, and thus will throw an error in Swift clients attempting to validate fields. The `no_strict` option here will ignore the validation check, as well as behave similar to the Swift2/Cocoa generator and initialize required fields with empty initializers (where possible).
+
+
+## Whats implemented
+#### Library
+##### Transports
+- [x] TSocketTransport - CFSocket and PosixSocket variants available. CFSocket variant only currently available for Darwin platforms
+- [x] THTTPTransport - Currently only available for Darwin platforms, Swift Foundation URLSession implementation needs completion on linux.
+- [x] TSocketServer - Uses CFSockets only for binding, should be working on linux
+- [x] TFramedTransport
+- [x] TMemoryBufferTransport
+- [x] TFileTransport - A few variants using File handles and file descriptors.
+- [x] TStreamTransport - Fully functional in Darwin, Foundation backing not yet completed in Linux (This limits TCFSocketTransport to Darwin)
+- [ ] HTTPServer - Currently there is no lightweight HTTPServer implementation the Swift Standard Library, so other 3rd party alternatives are required and out of scope for the Thrift library. Examples using Perfect will be provided.
+- [ ] Other (gz, etc)
+
+##### Protocols
+- [x] TBinaryProtocol
+- [x] TCompactProtocol
+- [ ] TJSONProtocol - This will need to be implemented
+
+##### Generator
+- [x] Code Complete Generator
+- [x] Async clients
+- [x] Documentation Generation - Generator will transplant IDL docs to Swift code for easy lookup in Xcode
+- [ ] Default Values - TODO
+- [ ] no_strict mode - TODO
+- [ ] Namespacing - Still haven't nailed down a good paradigm for namespacing. It will likely involve creating subdirectories for different namespaces and expecting the developer to import each subdirectory as separate modules. It could extend to creating SPM Package manifests with sub-modules within the generated module
+
+
+
+## Example HTTP Server with Perfect
+```swift
+import PerfectLib
+import PerfectHTTP
+import PerfectHTTPServer
+import Dispatch
+
+let logQueue = DispatchQueue(label: "log", qos: .background, attributes: .concurrent)
+let pQueue = DispatchQueue(label: "log", qos: .userInitiated, attributes: .concurrent)
+
+
+class TPerfectServer<InProtocol: TProtocol, OutProtocol: TProtocol> {
+
+ private var server = HTTPServer()
+ private var processor: TProcessor
+
+ init(address: String? = nil,
+ path: String? = nil,
+ port: Int,
+ processor: TProcessor,
+ inProtocol: InProtocol.Type,
+ outProtocol: OutProtocol.Type) throws {
+
+ self.processor = processor
+
+ if let address = address {
+ server.serverAddress = address
+ }
+ server.serverPort = UInt16(port)
+
+ var routes = Routes()
+ var uri = "/"
+ if let path = path {
+ uri += path
+ }
+ routes.add(method: .post, uri: uri) { request, response in
+ pQueue.async {
+ response.setHeader(.contentType, value: "application/x-thrift")
+
+ let itrans = TMemoryBufferTransport()
+ if let bytes = request.postBodyBytes {
+ let data = Data(bytes: bytes)
+ itrans.reset(readBuffer: data)
+ }
+
+ let otrans = TMemoryBufferTransport(flushHandler: { trans, buff in
+ let array = buff.withUnsafeBytes {
+ Array<UInt8>(UnsafeBufferPointer(start: $0, count: buff.count))
+ }
+ response.status = .ok
+ response.setBody(bytes: array)
+ response.completed()
+ })
+
+ let inproto = InProtocol(on: itrans)
+ let outproto = OutProtocol(on: otrans)
+
+ do {
+ try processor.process(on: inproto, outProtocol: outproto)
+ try otrans.flush()
+ } catch {
+ response.status = .badRequest
+ response.completed()
+ }
+ }
+ }
+ server.addRoutes(routes)
+ }
+
+ func serve() throws {
+ try server.start()
+ }
+}
+```
+
+#### Example Usage
+```swift
+class ServiceHandler : Service {
+ ...
+}
+let server = try? TPerfectServer(port: 9090,
+ processor: ServiceProcessor(service: ServiceHandler()),
+ inProtocol: TBinaryProtocol.self,
+ outProtocol: TBinaryProtocol.self)
+
+try? server?.serve()
+```
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/LinuxHelper.swift b/src/jaegertracing/thrift/lib/swift/Sources/LinuxHelper.swift
new file mode 100644
index 000000000..66d92bb4d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/LinuxHelper.swift
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+import Foundation
+import CoreFoundation
+
+#if os(Linux)
+/// Extensions for Linux for incomplete Foundation API's.
+/// swift-corelibs-foundation is not yet 1:1 with OSX/iOS Foundation
+
+extension CFSocketError {
+ public static let success = kCFSocketSuccess
+}
+
+extension UInt {
+ public static func &(lhs: UInt, rhs: Int) -> UInt {
+ let cast = unsafeBitCast(rhs, to: UInt.self)
+ return lhs & cast
+ }
+}
+
+#else
+extension CFStreamPropertyKey {
+ static let shouldCloseNativeSocket = CFStreamPropertyKey(kCFStreamPropertyShouldCloseNativeSocket)
+ // Exists as Stream.PropertyKey.socketSecuritylevelKey but doesn't work with CFReadStreamSetProperty
+ static let socketSecurityLevel = CFStreamPropertyKey(kCFStreamPropertySocketSecurityLevel)
+ static let SSLSettings = CFStreamPropertyKey(kCFStreamPropertySSLSettings)
+}
+#endif
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TApplicationError.swift b/src/jaegertracing/thrift/lib/swift/Sources/TApplicationError.swift
new file mode 100644
index 000000000..bc3939680
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TApplicationError.swift
@@ -0,0 +1,157 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+
+public struct TApplicationError : TError {
+ public enum Code : TErrorCode {
+ case unknown
+ case unknownMethod(methodName: String?)
+ case invalidMessageType
+ case wrongMethodName(methodName: String?)
+ case badSequenceId
+ case missingResult(methodName: String?)
+ case internalError
+ case protocolError
+ case invalidTransform
+ case invalidProtocol
+ case unsupportedClientType
+
+
+ /// Initialize a TApplicationError with a Thrift error code
+ /// Normally this would be achieved with RawRepresentable however
+ /// by doing this we can allow for associated properties on enum cases for
+ /// case specific context data in a Swifty, type-safe manner.
+ ///
+ /// - parameter thriftErrorCode: Integer TApplicationError(exception) error code.
+ /// Default to 0 (.unknown)
+ public init(thriftErrorCode: Int) {
+ switch thriftErrorCode {
+ case 1: self = .unknownMethod(methodName: nil)
+ case 2: self = .invalidMessageType
+ case 3: self = .wrongMethodName(methodName: nil)
+ case 4: self = .badSequenceId
+ case 5: self = .missingResult(methodName: nil)
+ case 6: self = .internalError
+ case 7: self = .protocolError
+ case 8: self = .invalidProtocol
+ case 9: self = .invalidTransform
+ case 10: self = .unsupportedClientType
+ default: self = .unknown
+ }
+ }
+ public var thriftErrorCode: Int {
+ switch self {
+ case .unknown: return 0
+ case .unknownMethod: return 1
+ case .invalidMessageType: return 2
+ case .wrongMethodName: return 3
+ case .badSequenceId: return 4
+ case .missingResult: return 5
+ case .internalError: return 6
+ case .protocolError: return 7
+ case .invalidProtocol: return 8
+ case .invalidTransform: return 9
+ case .unsupportedClientType: return 10
+ }
+ }
+
+ public var description: String {
+ /// Output "for #methodName" if method is not nil else empty
+ let methodUnwrap: (String?) -> String = { method in
+ return "\(method == nil ? "" : " for \(method ?? "")")"
+ }
+ switch self {
+ case .unknown: return "Unknown TApplicationError"
+ case .unknownMethod(let method): return "Unknown Method\(methodUnwrap(method))"
+ case .invalidMessageType: return "Invalid Message Type"
+ case .wrongMethodName(let method): return "Wrong Method Name\(methodUnwrap(method))"
+ case .badSequenceId: return "Bad Sequence ID"
+ case .missingResult(let method): return "Missing Result\(methodUnwrap(method))"
+ case .internalError: return "Internal Error"
+ case .protocolError: return "Protocol Error"
+ case .invalidProtocol: return "Invalid Protocol"
+ case .invalidTransform: return "Invalid Transform"
+ case .unsupportedClientType: return "Unsupported Client Type"
+ }
+ }
+ }
+
+ public init() { }
+
+ public init(thriftErrorCode code: Int, message: String? = nil) {
+ self.error = Code(thriftErrorCode: code)
+ self.message = message
+ }
+
+ public var error: Code = .unknown
+ public var message: String? = nil
+ public static var defaultCase: Code { return .unknown }
+}
+
+extension TApplicationError : TSerializable {
+ public static var thriftType: TType { return .struct }
+
+ public static func read(from proto: TProtocol) throws -> TApplicationError {
+ var errorCode: Int = 0
+ var message: String? = nil
+ _ = try proto.readStructBegin()
+ fields: while true {
+ let (_, fieldType, fieldID) = try proto.readFieldBegin()
+
+ switch (fieldID, fieldType) {
+ case (_, .stop):
+ break fields
+ case (1, .string):
+ message = try proto.read()
+ case (2, .i32):
+ errorCode = Int(try proto.read() as Int32)
+
+ case let (_, unknownType):
+ try proto.skip(type: unknownType)
+ }
+
+ try proto.readFieldEnd()
+ }
+ try proto.readStructEnd()
+ return TApplicationError(thriftErrorCode: errorCode, message: message)
+ }
+
+ public func write(to proto: TProtocol) throws {
+ try proto.writeStructBegin(name: "TApplicationException")
+
+ try proto.writeFieldBegin(name: "message", type: .string, fieldID: 1)
+ try proto.write(message ?? "")
+ try proto.writeFieldEnd()
+
+ try proto.writeFieldBegin(name: "type", type: .i32, fieldID: 2)
+ let val = Int32(error.thriftErrorCode)
+ try proto.write(val)
+ try proto.writeFieldEnd()
+ try proto.writeFieldStop()
+ try proto.writeStructEnd()
+ }
+
+ public var hashValue: Int {
+ return error.thriftErrorCode &+ (message?.hashValue ?? 0)
+ }
+}
+
+public func ==(lhs: TApplicationError, rhs: TApplicationError) -> Bool {
+ return lhs.error.thriftErrorCode == rhs.error.thriftErrorCode && lhs.message == rhs.message
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TBinary.swift b/src/jaegertracing/thrift/lib/swift/Sources/TBinary.swift
new file mode 100644
index 000000000..4be56441a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TBinary.swift
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import Foundation
+
+extension Data : TSerializable {
+ public static var thriftType: TType { return .string }
+
+ public static func read(from proto: TProtocol) throws -> Data {
+ return try proto.read() as Data
+ }
+
+ public func write(to proto: TProtocol) throws {
+ try proto.write(self)
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TBinaryProtocol.swift b/src/jaegertracing/thrift/lib/swift/Sources/TBinaryProtocol.swift
new file mode 100644
index 000000000..85acce045
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TBinaryProtocol.swift
@@ -0,0 +1,384 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import Foundation
+
+public struct TBinaryProtocolVersion {
+ static let version1 = Int32(bitPattern: 0x80010000)
+ static let versionMask = Int32(bitPattern: 0xffff0000)
+}
+
+public class TBinaryProtocol: TProtocol {
+ public var messageSizeLimit: UInt32 = 0
+
+ public var transport: TTransport
+
+ // class level properties for setting global config (useful for server in lieu of Factory design)
+ public static var strictRead: Bool = false
+ public static var strictWrite: Bool = true
+
+ private var strictRead: Bool
+ private var strictWrite: Bool
+
+ var currentMessageName: String?
+ var currentFieldName: String?
+
+
+ public convenience init(transport: TTransport, strictRead: Bool, strictWrite: Bool) {
+ self.init(on: transport)
+ self.strictRead = strictRead
+ self.strictWrite = strictWrite
+ }
+
+ public required init(on transport: TTransport) {
+ self.transport = transport
+ self.strictWrite = TBinaryProtocol.strictWrite
+ self.strictRead = TBinaryProtocol.strictRead
+ }
+
+ func readStringBody(_ size: Int) throws -> String {
+
+ var data = Data()
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport read failed")) {
+ data = try self.transport.readAll(size: size)
+ }
+
+ return String(data: data, encoding: String.Encoding.utf8) ?? ""
+ }
+
+ /// Mark: - TProtocol
+
+ public func readMessageBegin() throws -> (String, TMessageType, Int32) {
+ let size: Int32 = try read()
+ var messageName = ""
+ var type = TMessageType.exception
+
+ if size < 0 {
+ let version = size & TBinaryProtocolVersion.versionMask
+ if version != TBinaryProtocolVersion.version1 {
+ throw TProtocolError(error: .badVersion(expected: "\(TBinaryProtocolVersion.version1)",
+ got: "\(version)"))
+ }
+ type = TMessageType(rawValue: Int32(size) & 0x00FF) ?? type
+ messageName = try read()
+ } else {
+ if strictRead {
+ let errorMessage = "Missing message version, old client? Message Name: \(currentMessageName ?? "")"
+ throw TProtocolError(error: .invalidData,
+ message: errorMessage)
+ }
+ if messageSizeLimit > 0 && size > Int32(messageSizeLimit) {
+ throw TProtocolError(error: .sizeLimit(limit: Int(messageSizeLimit), got: Int(size)))
+ }
+
+ messageName = try readStringBody(Int(size))
+ type = TMessageType(rawValue: Int32(try read() as UInt8)) ?? type
+ }
+
+ let seqID: Int32 = try read()
+ return (messageName, type, seqID)
+ }
+
+ public func readMessageEnd() throws {
+ return
+ }
+
+ public func readStructBegin() throws -> String {
+ return ""
+ }
+
+ public func readStructEnd() throws {
+ return
+ }
+
+ public func readFieldBegin() throws -> (String, TType, Int32) {
+
+ let fieldType = TType(rawValue: Int32(try read() as UInt8)) ?? TType.stop
+ var fieldID: Int32 = 0
+
+ if fieldType != .stop {
+ fieldID = Int32(try read() as Int16)
+ }
+
+ return ("", fieldType, fieldID)
+ }
+
+ public func readFieldEnd() throws {
+ return
+ }
+
+ public func readMapBegin() throws -> (TType, TType, Int32) {
+ var raw = Int32(try read() as UInt8)
+ guard let keyType = TType(rawValue: raw) else {
+ throw TProtocolError(message: "Unknown value for keyType TType: \(raw)")
+ }
+
+ raw = Int32(try read() as UInt8)
+ guard let valueType = TType(rawValue: raw) else {
+ throw TProtocolError(message: "Unknown value for valueType TType: \(raw)")
+ }
+ let size: Int32 = try read()
+
+ return (keyType, valueType, size)
+ }
+
+ public func readMapEnd() throws {
+ return
+ }
+
+ public func readSetBegin() throws -> (TType, Int32) {
+ let raw = Int32(try read() as UInt8)
+ guard let elementType = TType(rawValue: raw) else {
+ throw TProtocolError(message: "Unknown value for elementType TType: \(raw)")
+ }
+
+ let size: Int32 = try read()
+
+ return (elementType, size)
+ }
+
+ public func readSetEnd() throws {
+ return
+ }
+
+ public func readListBegin() throws -> (TType, Int32) {
+ let raw = Int32(try read() as UInt8)
+ guard let elementType = TType(rawValue: raw) else {
+ throw TProtocolError(message: "Unknown value for elementType TType: \(raw)")
+ }
+ let size: Int32 = try read()
+
+ return (elementType, size)
+ }
+
+ public func readListEnd() throws {
+ return
+ }
+
+ public func read() throws -> String {
+ let data: Data = try read()
+ guard let str = String.init(data: data, encoding: .utf8) else {
+ throw TProtocolError(error: .invalidData, message: "Couldn't encode UTF-8 from data read")
+ }
+ return str
+ }
+
+ public func read() throws -> Bool {
+ return (try read() as UInt8) == 1
+ }
+
+ public func read() throws -> UInt8 {
+ var buff = Data()
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
+ buff = try self.transport.readAll(size: 1)
+ }
+ return buff[0]
+ }
+
+ public func read() throws -> Int16 {
+ var buff = Data()
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
+ buff = try self.transport.readAll(size: 2)
+ }
+ var ret = Int16(buff[0] & 0xff) << 8
+ ret |= Int16(buff[1] & 0xff)
+ return ret
+ }
+
+ public func read() throws -> Int32 {
+ var buff = Data()
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
+ buff = try self.transport.readAll(size: 4)
+ }
+ var ret = Int32(buff[0] & 0xff) << 24
+ ret |= Int32(buff[1] & 0xff) << 16
+ ret |= Int32(buff[2] & 0xff) << 8
+ ret |= Int32(buff[3] & 0xff)
+
+ return ret
+ }
+
+ public func read() throws -> Int64 {
+ var buff = Data()
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
+ buff = try self.transport.readAll(size: 8)
+ }
+ var ret = Int64(buff[0] & 0xff) << 56
+ ret |= Int64(buff[1] & 0xff) << 48
+ ret |= Int64(buff[2] & 0xff) << 40
+ ret |= Int64(buff[3] & 0xff) << 32
+ ret |= Int64(buff[4] & 0xff) << 24
+ ret |= Int64(buff[5] & 0xff) << 16
+ ret |= Int64(buff[6] & 0xff) << 8
+ ret |= Int64(buff[7] & 0xff)
+
+ return ret
+ }
+
+ public func read() throws -> Double {
+ let val = try read() as Int64
+ return Double(bitPattern: UInt64(bitPattern: val))
+ }
+
+ public func read() throws -> Data {
+ let size = Int(try read() as Int32)
+ var data = Data()
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
+ data = try self.transport.readAll(size: size)
+ }
+
+ return data
+ }
+
+ // Write methods
+
+ public func writeMessageBegin(name: String, type messageType: TMessageType, sequenceID: Int32) throws {
+ if strictWrite {
+ let version = TBinaryProtocolVersion.version1 | Int32(messageType.rawValue)
+ try write(version)
+ try write(name)
+ try write(sequenceID)
+ } else {
+ try write(name)
+ try write(UInt8(messageType.rawValue))
+ try write(sequenceID)
+ }
+ currentMessageName = name
+ }
+
+ public func writeMessageEnd() throws {
+ currentMessageName = nil
+ }
+
+ public func writeStructBegin(name: String) throws {
+ return
+ }
+
+ public func writeStructEnd() throws {
+ return
+ }
+
+ public func writeFieldBegin(name: String, type fieldType: TType, fieldID: Int32) throws {
+ try write(UInt8(fieldType.rawValue))
+ try write(Int16(fieldID))
+ }
+
+ public func writeFieldStop() throws {
+ try write(UInt8(TType.stop.rawValue))
+ }
+
+ public func writeFieldEnd() throws {
+ return
+ }
+
+ public func writeMapBegin(keyType: TType, valueType: TType, size: Int32) throws {
+ try write(UInt8(keyType.rawValue))
+ try write(UInt8(valueType.rawValue))
+ try write(size)
+ }
+
+ public func writeMapEnd() throws {
+ return
+ }
+
+ public func writeSetBegin(elementType: TType, size: Int32) throws {
+ try write(UInt8(elementType.rawValue))
+ try write(size)
+ }
+
+ public func writeSetEnd() throws {
+ return
+ }
+
+ public func writeListBegin(elementType: TType, size: Int32) throws {
+ try write(UInt8(elementType.rawValue))
+ try write(size)
+ }
+
+ public func writeListEnd() throws {
+ return
+ }
+
+ public func write(_ value: String) throws {
+ try write(value.data(using: .utf8)!)
+ }
+
+ public func write(_ value: Bool) throws {
+ let byteVal: UInt8 = value ? 1 : 0
+ try write(byteVal)
+ }
+
+ public func write(_ value: UInt8) throws {
+ let buff = Data([value])
+
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) {
+ try self.transport.write(data: buff)
+ }
+ }
+
+ public func write(_ value: Int16) throws {
+ var buff = Data()
+ buff.append(Data([UInt8(0xff & (value >> 8))]))
+ buff.append(Data([UInt8(0xff & (value))]))
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) {
+ try self.transport.write(data: buff)
+ }
+ }
+
+ public func write(_ value: Int32) throws {
+ var buff = Data()
+ buff.append(Data([UInt8(0xff & (value >> 24))]))
+ buff.append(Data([UInt8(0xff & (value >> 16))]))
+ buff.append(Data([UInt8(0xff & (value >> 8))]))
+ buff.append(Data([UInt8(0xff & (value))]))
+
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) {
+ try self.transport.write(data: buff)
+ }
+ }
+
+ public func write(_ value: Int64) throws {
+ var buff = Data()
+ buff.append(Data([UInt8(0xff & (value >> 56))]))
+ buff.append(Data([UInt8(0xff & (value >> 48))]))
+ buff.append(Data([UInt8(0xff & (value >> 40))]))
+ buff.append(Data([UInt8(0xff & (value >> 32))]))
+ buff.append(Data([UInt8(0xff & (value >> 24))]))
+ buff.append(Data([UInt8(0xff & (value >> 16))]))
+ buff.append(Data([UInt8(0xff & (value >> 8))]))
+ buff.append(Data([UInt8(0xff & (value))]))
+
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) {
+ try self.transport.write(data: buff)
+ }
+ }
+
+ public func write(_ value: Double) throws {
+ // Notably unsafe, since Double and Int64 are the same size, this should work fine
+ try self.write(Int64(bitPattern: value.bitPattern))
+ }
+
+ public func write(_ data: Data) throws {
+ try write(Int32(data.count))
+
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) {
+ try self.transport.write(data: data)
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TClient.swift b/src/jaegertracing/thrift/lib/swift/Sources/TClient.swift
new file mode 100644
index 000000000..cc3288a8c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TClient.swift
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+open class TClient {
+ public let inProtocol: TProtocol
+ public let outProtocol: TProtocol
+
+ required public init(inoutProtocol: TProtocol) {
+ self.inProtocol = inoutProtocol
+ self.outProtocol = inoutProtocol
+ }
+
+ required public init(inProtocol: TProtocol, outProtocol: TProtocol) {
+ self.inProtocol = inProtocol
+ self.outProtocol = outProtocol
+ }
+}
+
+
+open class TAsyncClient<Protocol: TProtocol, Factory: TAsyncTransportFactory> {
+ public var factory: Factory
+ public init(with protocol: Protocol.Type, factory: Factory) {
+ self.factory = factory
+ }
+}
+
+
+public enum TAsyncResult<T> {
+ case success(T)
+ case error(Swift.Error)
+
+ public func value() throws -> T {
+ switch self {
+ case .success(let t): return t
+ case .error(let e): throw e
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TCompactProtocol.swift b/src/jaegertracing/thrift/lib/swift/Sources/TCompactProtocol.swift
new file mode 100644
index 000000000..5b302d38f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TCompactProtocol.swift
@@ -0,0 +1,571 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import Foundation
+import CoreFoundation
+
+public enum TCType: UInt8 {
+ case stop = 0x00
+ case boolean_TRUE = 0x01
+ case boolean_FALSE = 0x02
+ case i8 = 0x03
+ case i16 = 0x04
+ case i32 = 0x05
+ case i64 = 0x06
+ case double = 0x07
+ case binary = 0x08
+ case list = 0x09
+ case set = 0x0A
+ case map = 0x0B
+ case `struct` = 0x0C
+
+ public static let typeMask: UInt8 = 0xE0 // 1110 0000
+ public static let typeBits: UInt8 = 0x07 // 0000 0111
+ public static let typeShiftAmount = 5
+
+}
+
+
+public class TCompactProtocol: TProtocol {
+ public static let protocolID: UInt8 = 0x82
+ public static let version: UInt8 = 1
+ public static let versionMask: UInt8 = 0x1F // 0001 1111
+
+ public var transport: TTransport
+
+ var lastField: [UInt8] = []
+ var lastFieldId: UInt8 = 0
+
+ var boolFieldName: String?
+ var boolFieldType: TType?
+ var boolFieldId: Int32?
+ var booleanValue: Bool?
+
+ var currentMessageName: String?
+
+ public required init(on transport: TTransport) {
+ self.transport = transport
+ }
+
+
+ /// Mark: - TCompactProtocol helpers
+
+ func writebyteDirect(_ byte: UInt8) throws {
+ let byte = Data([byte])
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport Write Failed")) {
+ try self.transport.write(data: byte)
+ }
+ }
+
+ func writeVarint32(_ val: UInt32) throws {
+ var val = val
+ var i32buf = [UInt8](repeating: 0, count: 5)
+ var idx = 0
+ while true {
+ if (val & ~0x7F) == 0 {
+ i32buf[idx] = UInt8(val)
+ idx += 1
+ break
+ } else {
+ i32buf[idx] = UInt8((val & 0x7F) | 0x80)
+ idx += 1
+ val >>= 7
+ }
+ }
+
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport Write Failed")) {
+ try self.transport.write(data: Data(i32buf[0..<idx]))
+ }
+ }
+
+ func writeVarint64(_ val: UInt64) throws {
+ var val = val
+ var varint64out = [UInt8](repeating: 0, count: 10)
+ var idx = 0
+ while true {
+ if (val & ~0x7F) == 0{
+ varint64out[idx] = UInt8(val)
+ idx += 1
+ break
+ } else {
+ varint64out[idx] = UInt8(val & 0x7F) | 0x80
+ idx += 1
+ val >>= 7
+ }
+ }
+
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport Write Failed")) {
+ try self.transport.write(data: Data(varint64out[0..<idx]))
+ }
+ }
+
+ func writeCollectionBegin(_ elementType: TType, size: Int32) throws {
+ let ctype = compactType(elementType).rawValue
+ if size <= 14 {
+ try writebyteDirect(UInt8(size << 4) | ctype)
+ } else {
+ try writebyteDirect(0xF0 | ctype)
+ try writeVarint32(UInt32(size))
+ }
+ }
+
+ func readBinary(_ size: Int) throws -> Data {
+ var result = Data()
+ if size != 0 {
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
+ result = try self.transport.readAll(size: size)
+ }
+ }
+ return result
+ }
+
+ func readVarint32() throws -> UInt32 {
+ var result: UInt32 = 0
+ var shift: UInt32 = 0
+ while true {
+ let byte: UInt8 = try read()
+
+ result |= UInt32(byte & 0x7F) << shift
+ if (byte & 0x80) == 0 {
+ break
+ }
+
+ shift += 7
+ }
+
+ return result
+ }
+
+ func readVarint64() throws -> UInt64 {
+ var result: UInt64 = 0
+ var shift: UInt64 = 0
+
+ while true {
+ let byte: UInt8 = try read()
+
+ result |= UInt64(byte & 0x7F) << shift
+ if (byte & 0x80) == 0 {
+ break
+ }
+
+ shift += 7
+ }
+ return result
+ }
+
+
+ func ttype(_ compactTypeVal: UInt8) throws -> TType {
+ guard let compactType = TCType(rawValue: compactTypeVal) else {
+ throw TProtocolError(message: "Unknown TCType value: \(compactTypeVal)")
+ }
+
+ switch compactType {
+ case .stop: return .stop;
+ case .boolean_FALSE, .boolean_TRUE: return .bool;
+ case .i8: return .i8;
+ case .i16: return .i16;
+ case .i32: return .i32;
+ case .i64: return .i64;
+ case .double: return .double;
+ case .binary: return .string;
+ case .list: return .list;
+ case .set: return .set;
+ case .map: return .map;
+ case .struct: return .struct;
+ }
+ }
+
+ func compactType(_ ttype: TType) -> TCType {
+ switch ttype {
+ case .stop: return .stop
+ case .void: return .i8
+ case .bool: return .boolean_FALSE
+ case .i8: return .i8
+ case .double: return .double
+ case .i16: return .i16
+ case .i32: return .i32
+ case .i64: return .i64
+ case .string: return .binary
+ case .struct: return .struct
+ case .map: return .map
+ case .set: return .set
+ case .list: return .list
+ case .utf8: return .binary
+ case .utf16: return .binary
+ }
+ }
+
+ /// ZigZag encoding maps signed integers to unsigned integers so that
+ /// numbers with a small absolute value (for instance, -1) have
+ /// a small varint encoded value too. It does this in a way that
+ /// "zig-zags" back and forth through the positive and negative integers,
+ /// so that -1 is encoded as 1, 1 is encoded as 2, -2 is encoded as 3, and so
+ ///
+ /// - parameter n: number to zigzag
+ ///
+ /// - returns: zigzaged UInt32
+ func i32ToZigZag(_ n : Int32) -> UInt32 {
+ return UInt32(bitPattern: Int32(n << 1) ^ Int32(n >> 31))
+ }
+
+ func i64ToZigZag(_ n : Int64) -> UInt64 {
+ return UInt64(bitPattern: Int64(n << 1) ^ Int64(n >> 63))
+ }
+
+ func zigZagToi32(_ n: UInt32) -> Int32 {
+ return Int32(n >> 1) ^ (-Int32(n & 1))
+ }
+
+ func zigZagToi64(_ n: UInt64) -> Int64 {
+ return Int64(n >> 1) ^ (-Int64(n & 1))
+ }
+
+
+
+ /// Mark: - TProtocol
+
+ public func readMessageBegin() throws -> (String, TMessageType, Int32) {
+ let protocolId: UInt8 = try read()
+
+ if protocolId != TCompactProtocol.protocolID {
+ let expected = String(format:"%2X", TCompactProtocol.protocolID)
+ let got = String(format:"%2X", protocolId)
+ throw TProtocolError(message: "Wrong Protocol ID \(got)",
+ extendedError: .mismatchedProtocol(expected: expected, got: got))
+ }
+
+ let versionAndType: UInt8 = try read()
+ let version: UInt8 = versionAndType & TCompactProtocol.versionMask
+ if version != TCompactProtocol.version {
+ throw TProtocolError(error: .badVersion(expected: "\(TCompactProtocol.version)",
+ got:"\(version)"))
+ }
+
+ let type = (versionAndType >> UInt8(TCType.typeShiftAmount)) & TCType.typeBits
+ guard let mtype = TMessageType(rawValue: Int32(type)) else {
+ throw TProtocolError(message: "Unknown TMessageType value: \(type)")
+ }
+ let sequenceId = try readVarint32()
+ let name: String = try read()
+
+ return (name, mtype, Int32(sequenceId))
+ }
+
+ public func readMessageEnd() throws { }
+
+ public func readStructBegin() throws -> String {
+ lastField.append(lastFieldId)
+ lastFieldId = 0
+ return ""
+ }
+
+ public func readStructEnd() throws {
+ lastFieldId = lastField.last ?? 0
+ lastField.removeLast()
+ }
+
+ public func readFieldBegin() throws -> (String, TType, Int32) {
+ let byte: UInt8 = try read()
+ guard let type = TCType(rawValue: byte & 0x0F) else {
+ throw TProtocolError(message: "Unknown TCType \(byte & 0x0F)")
+ }
+
+ // if it's a stop, then we can return immediately, as the struct is over
+ if type == .stop {
+ return ("", .stop, 0)
+ }
+
+ var fieldId: Int16 = 0
+
+ // mask off the 4MSB of the type header. it could contain a field id delta
+ let modifier = (byte & 0xF0) >> 4
+ if modifier == 0 {
+ // not a delta. look ahead for the zigzag varint field id
+ fieldId = try read()
+ } else {
+ // has a delta. add the delta to the last Read field id.
+ fieldId = Int16(lastFieldId + modifier)
+ }
+
+ let fieldType = try ttype(type.rawValue)
+
+ // if this happens to be a boolean field, the value is encoded in the type
+ if type == .boolean_TRUE || type == .boolean_FALSE {
+ // save the boolean value in a special instance variable
+ booleanValue = type == .boolean_TRUE
+ }
+
+ // push the new field onto the field stack so we can keep the deltas going
+ lastFieldId = UInt8(fieldId)
+ return ("", fieldType, Int32(fieldId))
+ }
+
+ public func readFieldEnd() throws { }
+
+ public func read() throws -> String {
+ let length = try readVarint32()
+
+ var result: String
+
+ if length != 0 {
+ let data = try readBinary(Int(length))
+ result = String(data: data, encoding: String.Encoding.utf8) ?? ""
+ } else {
+ result = ""
+ }
+
+ return result
+ }
+
+ public func read() throws -> Bool {
+ if let val = booleanValue {
+ self.booleanValue = nil
+ return val
+ } else {
+ let result = try read() as UInt8
+ return TCType(rawValue: result) == .boolean_TRUE
+ }
+ }
+
+ public func read() throws -> UInt8 {
+ var buff: UInt8 = 0
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
+ buff = try self.transport.readAll(size: 1)[0]
+ }
+ return buff
+ }
+
+ public func read() throws -> Int16 {
+ let v = try readVarint32()
+ return Int16(zigZagToi32(v))
+ }
+
+ public func read() throws -> Int32 {
+ let v = try readVarint32()
+ return zigZagToi32(v)
+ }
+
+ public func read() throws -> Int64 {
+ let v = try readVarint64()
+ return zigZagToi64(v)
+ }
+
+ public func read() throws -> Double {
+ var buff = Data()
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
+ buff = try self.transport.readAll(size: 8)
+ }
+
+ let i64: UInt64 = buff.withUnsafeBytes { (ptr: UnsafePointer<UInt8>) -> UInt64 in
+ return UnsafePointer<UInt64>(OpaquePointer(ptr)).pointee
+ }
+ let bits = CFSwapInt64LittleToHost(i64)
+ return Double(bitPattern: bits)
+ }
+
+ public func read() throws -> Data {
+ let length = try readVarint32()
+ return try readBinary(Int(length))
+ }
+
+ public func readMapBegin() throws -> (TType, TType, Int32) {
+ var keyAndValueType: UInt8 = 8
+ let size = try readVarint32()
+ if size != 0 {
+ keyAndValueType = try read()
+ }
+
+ let keyType = try ttype(keyAndValueType >> 4)
+ let valueType = try ttype(keyAndValueType & 0xF)
+
+ return (keyType, valueType, Int32(size))
+ }
+
+ public func readMapEnd() throws { }
+
+ public func readSetBegin() throws -> (TType, Int32) {
+ return try readListBegin()
+ }
+
+ public func readSetEnd() throws { }
+
+ public func readListBegin() throws -> (TType, Int32) {
+ let sizeAndType: UInt8 = try read()
+ var size: UInt32 = UInt32(sizeAndType >> 4) & 0x0f
+ if size == 15 {
+ size = try readVarint32()
+ }
+ let elementType = try ttype(sizeAndType & 0x0F)
+
+ return (elementType, Int32(size))
+ }
+
+ public func readListEnd() throws { }
+
+ public func writeMessageBegin(name: String,
+ type messageType: TMessageType,
+ sequenceID: Int32) throws {
+ try writebyteDirect(TCompactProtocol.protocolID)
+ let nextByte: UInt8 = (TCompactProtocol.version & TCompactProtocol.versionMask) |
+ (UInt8((UInt32(messageType.rawValue) << UInt32(TCType.typeShiftAmount))) &
+ TCType.typeMask)
+ try writebyteDirect(nextByte)
+ try writeVarint32(UInt32(sequenceID))
+ try write(name)
+
+ currentMessageName = name
+ }
+
+ public func writeMessageEnd() throws {
+ currentMessageName = nil
+ }
+
+ public func writeStructBegin(name: String) throws {
+ lastField.append(lastFieldId)
+ lastFieldId = 0
+ }
+
+ public func writeStructEnd() throws {
+ lastFieldId = lastField.last ?? 0
+ lastField.removeLast()
+ }
+
+ public func writeFieldBegin(name: String,
+ type fieldType: TType,
+ fieldID: Int32) throws {
+ if fieldType == .bool {
+ boolFieldName = name
+ boolFieldType = fieldType
+ boolFieldId = fieldID
+ return
+ } else {
+ try writeFieldBeginInternal(name: name,
+ type: fieldType,
+ fieldID: fieldID,
+ typeOverride: 0xFF)
+ }
+ }
+
+ func writeFieldBeginInternal(name: String,
+ type fieldType: TType,
+ fieldID: Int32,
+ typeOverride: UInt8) throws {
+
+ let typeToWrite = typeOverride == 0xFF ? compactType(fieldType).rawValue : typeOverride
+
+ // check if we can use delta encoding for the field id
+ let diff = UInt8(fieldID) - lastFieldId
+ if (UInt8(fieldID) > lastFieldId) && (diff <= 15) {
+ // Write them together
+ try writebyteDirect((UInt8(fieldID) - lastFieldId) << 4 | typeToWrite)
+
+ } else {
+ // Write them separate
+ try writebyteDirect(typeToWrite)
+ try write(Int16(fieldID))
+ }
+
+ lastFieldId = UInt8(fieldID)
+ }
+
+ public func writeFieldStop() throws {
+ try writebyteDirect(TCType.stop.rawValue)
+ }
+
+ public func writeFieldEnd() throws { }
+
+ public func writeMapBegin(keyType: TType, valueType: TType, size: Int32) throws {
+ if size == 0 {
+ try writebyteDirect(0)
+ } else {
+ try writeVarint32(UInt32(size))
+
+ let compactedTypes = compactType(keyType).rawValue << 4 | compactType(valueType).rawValue
+ try writebyteDirect(compactedTypes)
+ }
+ }
+
+ public func writeMapEnd() throws { }
+
+ public func writeSetBegin(elementType: TType, size: Int32) throws {
+ try writeCollectionBegin(elementType, size: size)
+ }
+
+ public func writeSetEnd() throws { }
+
+ public func writeListBegin(elementType: TType, size: Int32) throws {
+ try writeCollectionBegin(elementType, size: size)
+ }
+
+ public func writeListEnd() throws { }
+
+ public func write(_ value: String) throws {
+ try write(value.data(using: String.Encoding.utf8)!)
+ }
+
+ public func write(_ value: Bool) throws {
+ if let boolFieldId = boolFieldId, let boolFieldType = boolFieldType,
+ let boolFieldName = boolFieldName {
+
+ // we haven't written the field header yet
+ let compactType: TCType = value ? .boolean_TRUE : .boolean_FALSE
+ try writeFieldBeginInternal(name: boolFieldName, type: boolFieldType, fieldID: boolFieldId,
+ typeOverride: compactType.rawValue)
+ self.boolFieldId = nil
+ self.boolFieldType = nil
+ self.boolFieldName = nil
+ } else {
+ // we're not part of a field, so just write the value.
+ try writebyteDirect(value ? TCType.boolean_TRUE.rawValue : TCType.boolean_FALSE.rawValue)
+ }
+ }
+
+ public func write(_ value: UInt8) throws {
+ try writebyteDirect(value)
+ }
+
+ public func write(_ value: Int16) throws {
+ try writeVarint32(i32ToZigZag(Int32(value)))
+ }
+
+ public func write(_ value: Int32) throws {
+ try writeVarint32(i32ToZigZag(value))
+ }
+
+ public func write(_ value: Int64) throws {
+ try writeVarint64(i64ToZigZag(value))
+ }
+
+ public func write(_ value: Double) throws {
+ var bits = CFSwapInt64HostToLittle(value.bitPattern)
+ let data = withUnsafePointer(to: &bits) {
+ return Data(bytes: UnsafePointer<UInt8>(OpaquePointer($0)), count: MemoryLayout<UInt64>.size)
+ }
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport Write Failed")) {
+ try self.transport.write(data: data)
+ }
+ }
+
+ public func write(_ data: Data) throws {
+ try writeVarint32(UInt32(data.count))
+ try ProtocolTransportTry(error: TProtocolError(message: "Transport Write Failed")) {
+ try self.transport.write(data: data)
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TEnum.swift b/src/jaegertracing/thrift/lib/swift/Sources/TEnum.swift
new file mode 100644
index 000000000..fedfdb124
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TEnum.swift
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+public protocol TEnum : TSerializable, Hashable {
+ var rawValue: Int32 { get }
+}
+
+extension TEnum {
+ public static var thriftType: TType { return .i32 }
+ public var hashValue: Int { return rawValue.hashValue }
+
+ public func write(to proto: TProtocol) throws {
+ try proto.write(rawValue)
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TError.swift b/src/jaegertracing/thrift/lib/swift/Sources/TError.swift
new file mode 100644
index 000000000..79edba602
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TError.swift
@@ -0,0 +1,77 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+
+/// TErrorCode
+///
+/// Protocol for TError conformers' enum's to conform to.
+/// Generic Int Thrift error code to allow error cases to have
+/// associated values.
+public protocol TErrorCode : CustomStringConvertible {
+ var thriftErrorCode: Int { get }
+}
+
+/// TError
+///
+/// Base protocol for all Thrift Error(Exception) types to conform to
+public protocol TError : Error, CustomStringConvertible {
+
+ /// Enum for error cases. Can be typealiased to any conforming enum
+ /// or defined nested.
+ associatedtype Code: TErrorCode
+
+ /// Error Case, value from internal enum
+ var error: Code { get set }
+
+ /// Optional additional message
+ var message: String? { get set }
+
+ /// Default error case for the error type, used for generic init()
+ static var defaultCase: Code { get }
+
+ init()
+}
+
+extension TError {
+ /// Human readable description of error. Default provided for you in the
+ /// format \(Self.self): \(error.errorDescription) \n message
+ /// eg:
+ ///
+ /// TApplicationError (1): Invalid Message Type
+ /// An unknown Error has occured.
+ public var description: String {
+ var out = "\(Self.self) (\(error.thriftErrorCode)): " + error.description + "\n"
+ if let message = message {
+ out += "Message: \(message)"
+ }
+ return out
+ }
+
+ /// Simple default Initializer for TError's
+ ///
+ /// - parameter error: ErrorCode value. Default: defaultCase
+ /// - parameter message: Custom message with error. Optional
+ ///
+ /// - returns: <#return value description#>
+ public init(error: Code, message: String? = nil) {
+ self.init()
+ self.error = error
+ self.message = message
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TFileHandleTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/TFileHandleTransport.swift
new file mode 100644
index 000000000..f315fefda
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TFileHandleTransport.swift
@@ -0,0 +1,56 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import Foundation
+
+public class TFileHandleTransport: TTransport {
+ var inputFileHandle: FileHandle
+ var outputFileHandle: FileHandle
+
+ public init(inputFileHandle: FileHandle, outputFileHandle: FileHandle) {
+ self.inputFileHandle = inputFileHandle
+ self.outputFileHandle = outputFileHandle
+ }
+
+ public convenience init(fileHandle: FileHandle) {
+ self.init(inputFileHandle: fileHandle, outputFileHandle: fileHandle)
+ }
+
+ public func read(size: Int) throws -> Data {
+ var data = Data()
+ while data.count < size {
+ let read = inputFileHandle.readData(ofLength: size - data.count)
+ data.append(read)
+ if read.count == 0 {
+ break
+ }
+ }
+ return data
+ }
+
+ public func write(data: Data) throws {
+ outputFileHandle.write(data)
+ }
+
+ public func flush() throws {
+ return
+ }
+}
+
+
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TFileTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/TFileTransport.swift
new file mode 100644
index 000000000..fe2253da4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TFileTransport.swift
@@ -0,0 +1,101 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import Foundation
+
+#if os(OSX) || os(iOS) || os(watchOS) || os(tvOS)
+ import Darwin
+#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android)
+ import Glibc
+#endif
+
+/// TFileTransport
+/// Foundation-less Swift File transport.
+/// Uses C fopen/fread/fwrite,
+/// provided by Glibc in linux and Darwin on OSX/iOS
+public class TFileTransport: TTransport {
+ var fileHandle: UnsafeMutablePointer<FILE>? = nil
+
+ public init (fileHandle: UnsafeMutablePointer<FILE>) {
+ self.fileHandle = fileHandle
+ }
+
+ public convenience init(filename: String) throws {
+ var fileHandle: UnsafeMutablePointer<FILE>?
+ filename.withCString({ cFilename in
+ "rw".withCString({ cMode in
+ fileHandle = fopen(cFilename, cMode)
+ })
+ })
+ if let fileHandle = fileHandle {
+ self.init(fileHandle: fileHandle)
+ } else {
+ throw TTransportError(error: .notOpen)
+ }
+ }
+
+ deinit {
+ fclose(self.fileHandle)
+ }
+
+ public func readAll(size: Int) throws -> Data {
+ let read = try self.read(size: size)
+
+ if read.count != size {
+ throw TTransportError(error: .endOfFile)
+ }
+ return read
+ }
+
+ public func read(size: Int) throws -> Data {
+ // set up read buffer, position 0
+ var read = Data(capacity: size)
+ var position = 0
+
+ // read character buffer
+ var nextChar: UInt8 = 0
+
+ // continue until we've read size bytes
+ while read.count < size {
+ if fread(&nextChar, 1, 1, self.fileHandle) == 1 {
+ read[position] = nextChar
+
+ // Increment output byte pointer
+ position += 1
+
+ } else {
+ throw TTransportError(error: .endOfFile)
+ }
+ }
+ return read
+ }
+
+ public func write(data: Data) throws {
+ let bytesWritten = data.withUnsafeBytes {
+ fwrite($0, 1, data.count, self.fileHandle)
+ }
+ if bytesWritten != data.count {
+ throw TTransportError(error: .unknown)
+ }
+ }
+
+ public func flush() throws {
+ return
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TFramedTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/TFramedTransport.swift
new file mode 100644
index 000000000..59855eb9c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TFramedTransport.swift
@@ -0,0 +1,123 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import Foundation
+
+public class TFramedTransport: TTransport {
+ public static let headerSize = 4
+ public static let initFrameSize = 1024
+ private static let defaultMaxLength = 16384000
+
+ public var transport: TTransport
+ private var writeBuffer = Data()
+
+ private var maxSize = TFramedTransport.defaultMaxLength
+ private var remainingBytes = 0
+
+
+ public init(transport: TTransport, maxSize: Int) {
+ self.transport = transport
+ self.maxSize = maxSize
+ }
+
+ public convenience init(transport: TTransport) {
+ self.init(transport: transport, maxSize: TFramedTransport.defaultMaxLength)
+ }
+
+ func readHeader() throws {
+ let read = try transport.readAll(size: TFramedTransport.headerSize)
+ remainingBytes = Int(decodeFrameSize(data: read))
+ }
+
+ /// Mark: - TTransport
+
+ public func read(size: Int) throws -> Data {
+ while (remainingBytes <= 0) {
+ try readHeader()
+ }
+
+ let toRead = min(size, remainingBytes)
+
+ if toRead < 0 {
+ try close()
+ throw TTransportError(error: .negativeSize,
+ message: "Read a negative frame size (\(toRead))!")
+ }
+
+ if toRead > maxSize {
+ try close()
+ throw TTransportError(error: .sizeLimit(limit: maxSize, got: toRead))
+ }
+
+ return try transport.readAll(size: toRead)
+ }
+
+ public func flush() throws {
+ // copy buffer and reset
+ let buff = writeBuffer
+ writeBuffer = Data()
+
+ if buff.count - TFramedTransport.headerSize < 0 {
+ throw TTransportError(error: .unknown)
+ }
+
+ let frameSize = encodeFrameSize(size: UInt32(buff.count))
+
+ try transport.write(data: frameSize)
+ try transport.write(data: buff)
+ try transport.flush()
+ }
+
+ public func write(data: Data) throws {
+ writeBuffer.append(data)
+ }
+
+
+
+ private func encodeFrameSize(size: UInt32) -> Data {
+ var data = Data()
+ data.append(Data([UInt8(0xff & (size >> 24))]))
+ data.append(Data([UInt8(0xff & (size >> 16))]))
+ data.append(Data([UInt8(0xff & (size >> 8))]))
+ data.append(Data([UInt8(0xff & (size))]))
+
+ return data
+ }
+
+ private func decodeFrameSize(data: Data) -> UInt32 {
+ var size: UInt32
+ size = (UInt32(data[0] & 0xff) << 24)
+ size |= (UInt32(data[1] & 0xff) << 16)
+ size |= (UInt32(data[2] & 0xff) << 8)
+ size |= (UInt32(data[3] & 0xff))
+ return size
+ }
+
+ public func close() throws {
+ try transport.close()
+ }
+
+ public func open() throws {
+ try transport.open()
+ }
+
+ public func isOpen() throws -> Bool {
+ return try transport.isOpen()
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/THTTPSessionTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/THTTPSessionTransport.swift
new file mode 100644
index 000000000..3c0af8eb8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/THTTPSessionTransport.swift
@@ -0,0 +1,184 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import Foundation
+import Dispatch
+
+
+public class THTTPSessionTransport: TAsyncTransport {
+ public class Factory : TAsyncTransportFactory {
+ public var responseValidate: ((HTTPURLResponse?, Data?) throws -> Void)?
+
+ var session: URLSession
+ var url: URL
+
+ public class func setupDefaultsForSessionConfiguration(_ config: URLSessionConfiguration, withProtocolName protocolName: String?) {
+ var thriftContentType = "application/x-thrift"
+
+ if let protocolName = protocolName {
+ thriftContentType += "; p=\(protocolName)"
+ }
+
+ config.requestCachePolicy = .reloadIgnoringLocalCacheData
+ config.urlCache = nil
+
+ config.httpShouldUsePipelining = true
+ config.httpShouldSetCookies = true
+ config.httpAdditionalHeaders = ["Content-Type": thriftContentType,
+ "Accept": thriftContentType,
+ "User-Agent": "Thrift/Swift (Session)"]
+
+
+ }
+
+ public init(session: URLSession, url: URL) {
+ self.session = session
+ self.url = url
+ }
+
+ public func newTransport() -> THTTPSessionTransport {
+ return THTTPSessionTransport(factory: self)
+ }
+
+ func validateResponse(_ response: HTTPURLResponse?, data: Data?) throws {
+ try responseValidate?(response, data)
+ }
+
+ func taskWithRequest(_ request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> ()) throws -> URLSessionTask {
+
+ let newTask: URLSessionTask? = session.dataTask(with: request, completionHandler: completionHandler)
+ if let newTask = newTask {
+ return newTask
+ } else {
+ throw TTransportError(error: .unknown, message: "Failed to create session data task")
+ }
+ }
+ }
+
+ var factory: Factory
+ var requestData = Data()
+ var responseData = Data()
+ var responseDataOffset: Int = 0
+
+ init(factory: Factory) {
+ self.factory = factory
+ }
+
+ public func readAll(size: Int) throws -> Data {
+ let read = try self.read(size: size)
+ if read.count != size {
+ throw TTransportError(error: .endOfFile)
+ }
+ return read
+ }
+
+ public func read(size: Int) throws -> Data {
+ let avail = responseData.count - responseDataOffset
+ let (start, stop) = (responseDataOffset, responseDataOffset + min(size, avail))
+ let read = responseData.subdata(in: start..<stop)
+ responseDataOffset += read.count
+ return read
+ }
+
+ public func write(data: Data) throws {
+ requestData.append(data)
+ }
+
+ public func flush(_ completed: @escaping (TAsyncTransport, Error?) -> Void) {
+ var error: Error?
+ var task: URLSessionTask?
+
+ var request = URLRequest(url: factory.url)
+ request.httpMethod = "POST"
+ request.httpBody = requestData
+
+ requestData = Data()
+
+ do {
+ task = try factory.taskWithRequest(request, completionHandler: { (data, response, taskError) in
+
+ // Check if there was an error with the network
+ if taskError != nil {
+ error = TTransportError(error: .timedOut)
+ completed(self, error)
+ return
+ }
+
+ // Check response type
+ if taskError == nil && !(response is HTTPURLResponse) {
+ error = THTTPTransportError(error: .invalidResponse)
+ completed(self, error)
+ return
+ }
+
+ // Check status code
+ if let httpResponse = response as? HTTPURLResponse {
+ if taskError == nil && httpResponse.statusCode != 200 {
+ if httpResponse.statusCode == 401 {
+ error = THTTPTransportError(error: .authentication)
+ } else {
+ error = THTTPTransportError(error: .invalidStatus(statusCode: httpResponse.statusCode))
+ }
+ }
+
+ // Allow factory to check
+ if error != nil {
+ do {
+ try self.factory.validateResponse(httpResponse, data: data)
+ } catch let validateError {
+ error = validateError
+ }
+ }
+
+ self.responseDataOffset = 0
+ if error != nil {
+ self.responseData = Data()
+ } else {
+ self.responseData = data ?? Data()
+ }
+ completed(self, error)
+ }
+ })
+
+ } catch let taskError {
+ error = taskError
+ }
+
+ if let error = error, task == nil {
+ completed(self, error)
+ }
+ task?.resume()
+ }
+
+ public func flush() throws {
+ let completed = DispatchSemaphore(value: 0)
+ var internalError: Error?
+
+ flush() { _, error in
+ internalError = error
+ completed.signal()
+ }
+
+ _ = completed.wait(timeout: DispatchTime.distantFuture)
+
+ if let error = internalError {
+ throw error
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TList.swift b/src/jaegertracing/thrift/lib/swift/Sources/TList.swift
new file mode 100644
index 000000000..c239d10c5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TList.swift
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+public struct TList<Element : TSerializable> : RandomAccessCollection, MutableCollection, ExpressibleByArrayLiteral, TSerializable, Hashable {
+ public typealias Storage = Array<Element>
+ public typealias Indices = Storage.Indices
+
+ internal var storage = Storage()
+ public init() { }
+ public init(arrayLiteral elements: Element...) {
+ self.storage = Storage(elements)
+ }
+ public init<Source : Sequence>(_ sequence: Source) where Source.Iterator.Element == Element {
+ storage = Storage(sequence)
+ }
+
+ /// Mark: Hashable
+ public var hashValue : Int {
+ let prime = 31
+ var result = 1
+ for element in storage {
+ result = prime &* result &+ element.hashValue
+ }
+ return result
+ }
+
+ /// Mark: TSerializable
+ public static var thriftType : TType { return .list }
+
+ public static func read(from proto: TProtocol) throws -> TList {
+ let (elementType, size) = try proto.readListBegin()
+ if elementType != Element.thriftType {
+ throw TProtocolError(error: .invalidData,
+ extendedError: .unexpectedType(type: elementType))
+ }
+ var list = TList()
+ for _ in 0..<size {
+ let element = try Element.read(from: proto)
+ list.storage.append(element)
+ }
+ try proto.readListEnd()
+ return list
+ }
+
+ public func write(to proto: TProtocol) throws {
+ try proto.writeListBegin(elementType: Element.thriftType, size: Int32(self.count))
+ for element in self.storage {
+ try Element.write(element, to: proto)
+ }
+ try proto.writeListEnd()
+ }
+
+ /// Mark: MutableCollection
+
+ public typealias SubSequence = Storage.SubSequence
+ public typealias Index = Storage.Index
+
+ public subscript(position: Storage.Index) -> Element {
+ get {
+ return storage[position]
+ }
+ set {
+ storage[position] = newValue
+ }
+ }
+
+ public subscript(range: Range<Index>) -> SubSequence {
+ get {
+ return storage[range]
+ }
+ set {
+ storage[range] = newValue
+ }
+ }
+
+ public var startIndex: Index {
+ return storage.startIndex
+ }
+ public var endIndex: Index {
+ return storage.endIndex
+ }
+
+ public func formIndex(after i: inout Index) {
+ storage.formIndex(after: &i)
+ }
+
+ public func formIndex(before i: inout Int) {
+ storage.formIndex(before: &i)
+ }
+
+ public func index(after i: Index) -> Index {
+ return storage.index(after: i)
+ }
+
+ public func index(before i: Int) -> Int {
+ return storage.index(before: i)
+ }
+
+}
+
+extension TList : RangeReplaceableCollection {
+ public mutating func replaceSubrange<C: Collection>(_ subrange: Range<Index>, with newElements: C)
+ where C.Iterator.Element == Element {
+ storage.replaceSubrange(subrange, with: newElements)
+ }
+}
+
+extension TList : CustomStringConvertible, CustomDebugStringConvertible {
+
+ public var description : String {
+ return storage.description
+ }
+
+ public var debugDescription : String {
+ return storage.debugDescription
+ }
+
+}
+
+public func ==<Element>(lhs: TList<Element>, rhs: TList<Element>) -> Bool {
+ return lhs.storage.elementsEqual(rhs.storage) { $0 == $1 }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TMap.swift b/src/jaegertracing/thrift/lib/swift/Sources/TMap.swift
new file mode 100644
index 000000000..dcf1481c6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TMap.swift
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+public struct TMap<Key : TSerializable & Hashable, Value : TSerializable>: Collection, ExpressibleByDictionaryLiteral, Hashable, TSerializable {
+ public typealias Storage = Dictionary<Key, Value>
+ public typealias Element = Storage.Element
+ public typealias Index = Storage.Index
+ public typealias IndexDistance = Int
+ public typealias Indices = Storage.Indices
+ public typealias SubSequence = Storage.SubSequence
+ internal var storage = Storage()
+
+ /// Mark: Be Like Dictionary
+
+ public func indexForKey(_ key: Key) -> Index? {
+ return storage.index(forKey: key)
+ }
+
+ public mutating func updateValue(_ value: Value, forKey key: Key) -> Value? {
+ return storage.updateValue(value, forKey: key)
+ }
+
+ public mutating func removeValueForKey(_ key: Key) -> Value? {
+ return storage.removeValue(forKey: key)
+ }
+
+ public init(minimumCapacity: Int) {
+ storage = Storage(minimumCapacity: minimumCapacity)
+ }
+
+ /// init from Dictionary<K,V>
+ public init(_ dict: [Key: Value]) {
+ storage = dict
+ }
+
+ /// read only access to storage if needed as Dictionary<K,V>
+ public var dictionary: [Key: Value] {
+ return storage
+ }
+
+ public subscript (key: Key) -> Value? {
+ get {
+ return storage[key]
+ }
+ set {
+ storage[key] = newValue
+ }
+ }
+
+ /// Mark: Collection
+
+ public var indices: Indices {
+ return storage.indices
+ }
+
+ public func distance(from start: Index, to end: Index) -> IndexDistance {
+ return storage.distance(from: start, to: end)
+ }
+
+ public func index(_ i: Index, offsetBy n: IndexDistance) -> Index {
+ return storage.index(i, offsetBy: n)
+ }
+
+ public func index(_ i: Index, offsetBy n: IndexDistance, limitedBy limit: Index) -> Index? {
+ return storage.index(i, offsetBy: n, limitedBy: limit)
+ }
+
+ public subscript(position: Index) -> Element {
+ return storage[position]
+ }
+
+ /// Mark: IndexableBase
+
+ public var startIndex: Index { return storage.startIndex }
+ public var endIndex: Index { return storage.endIndex }
+ public func index(after i: Index) -> Index {
+ return storage.index(after: i)
+ }
+
+ public func formIndex(after i: inout Index) {
+ storage.formIndex(after: &i)
+ }
+
+ public subscript(bounds: Range<Index>) -> SubSequence {
+ return storage[bounds]
+ }
+
+ /// Mark: DictionaryLiteralConvertible
+
+ public init(dictionaryLiteral elements: (Key, Value)...) {
+ storage = Storage()
+ for (key, value) in elements {
+ storage[key] = value
+ }
+ }
+
+ /// Mark: Hashable
+
+ public var hashValue: Int {
+ let prime = 31
+ var result = 1
+ for (key, value) in storage {
+ result = prime &* result &+ key.hashValue
+ result = prime &* result &+ value.hashValue
+ }
+ return result
+ }
+
+ /// Mark: TSerializable
+
+ public static var thriftType : TType { return .map }
+ public init() {
+ storage = Storage()
+ }
+
+ public static func read(from proto: TProtocol) throws -> TMap {
+
+ let (keyType, valueType, size) = try proto.readMapBegin()
+ if size > 0 {
+ if keyType != Key.thriftType {
+ throw TProtocolError(error: .invalidData,
+ message: "Unexpected TMap Key Type",
+ extendedError: .unexpectedType(type: keyType))
+ }
+ if valueType != Value.thriftType {
+ throw TProtocolError(error: .invalidData,
+ message: "Unexpected TMap Value Type",
+ extendedError: .unexpectedType(type: valueType))
+ }
+ }
+
+ var map = TMap()
+ for _ in 0..<size {
+ let key = try Key.read(from: proto)
+ let value = try Value.read(from: proto)
+ map.storage[key] = value
+ }
+ try proto.readMapEnd()
+ return map
+ }
+
+ public func write(to proto: TProtocol) throws {
+ try proto.writeMapBegin(keyType: Key.thriftType,
+ valueType: Value.thriftType, size: Int32(self.count))
+ for (key, value) in self.storage {
+ try Key.write(key, to: proto)
+ try Value.write(value, to: proto)
+ }
+ try proto.writeMapEnd()
+ }
+}
+
+/// Mark: CustomStringConvertible, CustomDebugStringConvertible
+
+extension TMap : CustomStringConvertible, CustomDebugStringConvertible {
+
+ public var description : String {
+ return storage.description
+ }
+
+ public var debugDescription : String {
+ return storage.debugDescription
+ }
+
+}
+
+/// Mark: Equatable
+
+public func ==<Key, Value>(lhs: TMap<Key,Value>, rhs: TMap<Key, Value>) -> Bool {
+ if lhs.count != rhs.count {
+ return false
+ }
+ return lhs.storage.elementsEqual(rhs.storage) { $0.key == $1.key && $0.value == $1.value }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TMemoryBufferTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/TMemoryBufferTransport.swift
new file mode 100644
index 000000000..bd58b6ef7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TMemoryBufferTransport.swift
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import Foundation
+
+public class TMemoryBufferTransport : TTransport {
+ public private(set) var readBuffer = Data()
+ public private(set) var writeBuffer = Data()
+
+ public private(set) var position = 0
+
+ public var bytesRemainingInBuffer: Int {
+ return readBuffer.count - position
+ }
+
+ public func consumeBuffer(size: Int) {
+ position += size
+ }
+ public func clear() {
+ readBuffer = Data()
+ writeBuffer = Data()
+ }
+
+
+ private var flushHandler: ((TMemoryBufferTransport, Data) -> ())?
+
+ public init(flushHandler: ((TMemoryBufferTransport, Data) -> ())? = nil) {
+ self.flushHandler = flushHandler
+ }
+
+ public convenience init(readBuffer: Data, flushHandler: ((TMemoryBufferTransport, Data) -> ())? = nil) {
+ self.init()
+ self.readBuffer = readBuffer
+ }
+
+ public func reset(readBuffer: Data = Data(), writeBuffer: Data = Data()) {
+ self.readBuffer = readBuffer
+ self.writeBuffer = writeBuffer
+ }
+
+ public func read(size: Int) throws -> Data {
+ let amountToRead = min(bytesRemainingInBuffer, size)
+ if amountToRead > 0 {
+ let ret = readBuffer.subdata(in: Range(uncheckedBounds: (lower: position, upper: position + amountToRead)))
+ position += ret.count
+ return ret
+ }
+ return Data()
+ }
+
+ public func write(data: Data) throws {
+ writeBuffer.append(data)
+ }
+
+ public func flush() throws {
+ flushHandler?(self, writeBuffer)
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TMultiplexedProtocol.swift b/src/jaegertracing/thrift/lib/swift/Sources/TMultiplexedProtocol.swift
new file mode 100644
index 000000000..73a8d51ab
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TMultiplexedProtocol.swift
@@ -0,0 +1,47 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+public class TMultiplexedProtocol<Protocol: TProtocol>: TWrappedProtocol<Protocol> {
+ public let separator = ":"
+
+ public var serviceName = ""
+
+ public convenience init(on transport: TTransport, serviceName: String) {
+ self.init(on: transport)
+ self.serviceName = serviceName
+ }
+
+ override public func writeMessageBegin(name: String,
+ type messageType: TMessageType,
+ sequenceID: Int32) throws {
+ switch messageType {
+ case .call, .oneway:
+ var serviceFunction = serviceName
+ serviceFunction += serviceName == "" ? "" : separator
+ serviceFunction += name
+ return try super.writeMessageBegin(name: serviceFunction,
+ type: messageType,
+ sequenceID: sequenceID)
+ default:
+ return try super.writeMessageBegin(name: name,
+ type: messageType,
+ sequenceID: sequenceID)
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TProcessor.swift b/src/jaegertracing/thrift/lib/swift/Sources/TProcessor.swift
new file mode 100644
index 000000000..7ff222e41
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TProcessor.swift
@@ -0,0 +1,29 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+
+public typealias TProcessorMessageHandler<T> = (Int, TProtocol, TProtocol, T) -> Void
+
+public protocol TProcessor {
+ associatedtype Service
+ var service: Service { get set }
+ func process(on inProtocol: TProtocol, outProtocol: TProtocol) throws
+ init(service: Service)
+}
+
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TProtocol.swift b/src/jaegertracing/thrift/lib/swift/Sources/TProtocol.swift
new file mode 100644
index 000000000..b4e5dbe73
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TProtocol.swift
@@ -0,0 +1,182 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import Foundation
+
+public enum TMessageType: Int32 {
+ case call = 1
+ case reply = 2
+ case exception = 3
+ case oneway = 4
+}
+
+public enum TType: Int32 {
+ case stop = 0
+ case void = 1
+ case bool = 2
+ case i8 = 3
+ case double = 4
+ case i16 = 6
+ case i32 = 8
+ case i64 = 10
+ case string = 11
+ case `struct` = 12
+ case map = 13
+ case set = 14
+ case list = 15
+ case utf8 = 16
+ case utf16 = 17
+}
+
+public protocol TProtocol {
+ var transport: TTransport { get set }
+ init(on transport: TTransport)
+ // Reading Methods
+
+ func readMessageBegin() throws -> (String, TMessageType, Int32)
+ func readMessageEnd() throws
+ func readStructBegin() throws -> String
+ func readStructEnd() throws
+ func readFieldBegin() throws -> (String, TType, Int32)
+ func readFieldEnd() throws
+ func readMapBegin() throws -> (TType, TType, Int32)
+ func readMapEnd() throws
+ func readSetBegin() throws -> (TType, Int32)
+ func readSetEnd() throws
+ func readListBegin() throws -> (TType, Int32)
+ func readListEnd() throws
+
+ func read() throws -> String
+ func read() throws -> Bool
+ func read() throws -> UInt8
+ func read() throws -> Int16
+ func read() throws -> Int32
+ func read() throws -> Int64
+ func read() throws -> Double
+ func read() throws -> Data
+
+ // Writing methods
+
+ func writeMessageBegin(name: String, type messageType: TMessageType, sequenceID: Int32) throws
+ func writeMessageEnd() throws
+ func writeStructBegin(name: String) throws
+ func writeStructEnd() throws
+ func writeFieldBegin(name: String, type fieldType: TType, fieldID: Int32) throws
+ func writeFieldStop() throws
+ func writeFieldEnd() throws
+ func writeMapBegin(keyType: TType, valueType: TType, size: Int32) throws
+ func writeMapEnd() throws
+ func writeSetBegin(elementType: TType, size: Int32) throws
+ func writeSetEnd() throws
+ func writeListBegin(elementType: TType, size: Int32) throws
+ func writeListEnd() throws
+
+ func write(_ value: String) throws
+ func write(_ value: Bool) throws
+ func write(_ value: UInt8) throws
+ func write(_ value: Int16) throws
+ func write(_ value: Int32) throws
+ func write(_ value: Int64) throws
+ func write(_ value: Double) throws
+ func write(_ value: Data) throws
+}
+
+public extension TProtocol {
+ func writeFieldValue(_ value: TSerializable, name: String, type: TType, id: Int32) throws {
+ try writeFieldBegin(name: name, type: type, fieldID: id)
+ try value.write(to: self)
+ try writeFieldEnd()
+ }
+
+ func validateValue(_ value: Any?, named name: String) throws {
+ if value == nil {
+ throw TProtocolError(error: .unknown, message: "Missing required value for field: \(name)")
+ }
+ }
+
+ func readResultMessageBegin() throws {
+ let (_, type, _) = try readMessageBegin();
+ if type == .exception {
+ let x = try readException()
+ throw x
+ }
+ return
+ }
+
+ func readException() throws -> TApplicationError {
+ return try TApplicationError.read(from: self)
+ }
+
+ func writeException(messageName name: String, sequenceID: Int32, ex: TApplicationError) throws {
+ try writeMessageBegin(name: name, type: .exception, sequenceID: sequenceID)
+ try ex.write(to: self)
+ try writeMessageEnd()
+ }
+
+ func skip(type: TType) throws {
+ switch type {
+ case .bool: _ = try read() as Bool
+ case .i8: _ = try read() as UInt8
+ case .i16: _ = try read() as Int16
+ case .i32: _ = try read() as Int32
+ case .i64: _ = try read() as Int64
+ case .double: _ = try read() as Double
+ case .string: _ = try read() as String
+
+ case .struct:
+ _ = try readStructBegin()
+ while true {
+ let (_, fieldType, _) = try readFieldBegin()
+ if fieldType == .stop {
+ break
+ }
+ try skip(type: fieldType)
+ try readFieldEnd()
+ }
+ try readStructEnd()
+
+
+ case .map:
+ let (keyType, valueType, size) = try readMapBegin()
+ for _ in 0..<size {
+ try skip(type: keyType)
+ try skip(type: valueType)
+ }
+ try readMapEnd()
+
+
+ case .set:
+ let (elemType, size) = try readSetBegin()
+ for _ in 0..<size {
+ try skip(type: elemType)
+ }
+ try readSetEnd()
+
+ case .list:
+ let (elemType, size) = try readListBegin()
+ for _ in 0..<size {
+ try skip(type: elemType)
+ }
+ try readListEnd()
+
+ default:
+ throw TProtocolError(error: .invalidData, message: "Invalid data")
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TProtocolError.swift b/src/jaegertracing/thrift/lib/swift/Sources/TProtocolError.swift
new file mode 100644
index 000000000..a5d14f9ea
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TProtocolError.swift
@@ -0,0 +1,146 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import Foundation
+
+public struct TProtocolError : TError {
+ public init() { }
+
+ public enum Code : TErrorCode {
+ case unknown
+ case invalidData
+ case negativeSize
+ case sizeLimit(limit: Int, got: Int)
+ case badVersion(expected: String, got: String)
+ case notImplemented
+ case depthLimit
+
+ public var thriftErrorCode: Int {
+ switch self {
+ case .unknown: return 0
+ case .invalidData: return 1
+ case .negativeSize: return 2
+ case .sizeLimit: return 3
+ case .badVersion: return 4
+ case .notImplemented: return 5
+ case .depthLimit: return 6
+ }
+
+ }
+ public var description: String {
+ switch self {
+ case .unknown: return "Unknown TProtocolError"
+ case .invalidData: return "Invalid Data"
+ case .negativeSize: return "Negative Size"
+ case .sizeLimit(let limit, let got):
+ return "Message exceeds size limit of \(limit) (received: \(got)"
+ case .badVersion(let expected, let got):
+ return "Bad Version. (Expected: \(expected), Got: \(got)"
+ case .notImplemented: return "Not Implemented"
+ case .depthLimit: return "Depth Limit"
+ }
+ }
+ }
+
+ public enum ExtendedErrorCode : TErrorCode {
+ case unknown
+ case missingRequiredField(fieldName: String)
+ case unexpectedType(type: TType)
+ case mismatchedProtocol(expected: String, got: String)
+ public var thriftErrorCode: Int {
+ switch self {
+ case .unknown: return 1000
+ case .missingRequiredField: return 1001
+ case .unexpectedType: return 1002
+ case .mismatchedProtocol: return 1003
+ }
+ }
+ public var description: String {
+ switch self {
+ case .unknown: return "Unknown TProtocolExtendedError"
+ case .missingRequiredField(let fieldName): return "Missing Required Field: \(fieldName)"
+ case .unexpectedType(let type): return "Unexpected Type \(type.self)"
+ case .mismatchedProtocol(let expected, let got): return "Mismatched Protocol. (Expected: \(expected), got \(got))"
+ }
+ }
+ }
+
+ public var extendedError: ExtendedErrorCode? = nil
+
+ public init(error: Code = .unknown,
+ message: String? = nil,
+ extendedError: ExtendedErrorCode? = nil) {
+ self.error = error
+ self.message = message
+ self.extendedError = extendedError
+ }
+
+ /// Mark: TError
+ public var error: Code = .unknown
+ public var message: String? = nil
+ public static var defaultCase: Code { return .unknown }
+
+ public var description: String {
+ var out = "\(TProtocolError.self): (\(error.thriftErrorCode) \(error.description)\n"
+ if let extendedError = extendedError {
+ out += "TProtocolExtendedError (\(extendedError.thriftErrorCode)): \(extendedError.description)"
+ }
+ if let message = message {
+ out += "Message: \(message)"
+ }
+ return out
+ }
+}
+
+
+/// Wrapper for Transport errors in Protocols. Inspired by Thrift-Cocoa PROTOCOL_TRANSPORT_ERROR
+/// macro. Modified to be more Swift-y. Catches any TError thrown within the block and
+/// rethrows a given TProtocolError, the original error's description is appended to the new
+/// TProtocolError's message. sourceFile, sourceLine, sourceMethod are auto-populated and should
+/// be ignored when calling.
+///
+/// - parameter error: TProtocolError to throw if the block throws
+/// - parameter sourceFile: throwing file, autopopulated
+/// - parameter sourceLine: throwing line, autopopulated
+/// - parameter sourceMethod: throwing method, autopopulated
+/// - parameter block: throwing block
+///
+/// - throws: TProtocolError Default is TProtocolError.ErrorCode.unknown. Underlying
+/// error's description appended to TProtocolError.message
+func ProtocolTransportTry(error: TProtocolError = TProtocolError(),
+ sourceFile: String = #file,
+ sourceLine: Int = #line,
+ sourceMethod: String = #function,
+ block: () throws -> ()) throws {
+ // Need mutable copy
+ var error = error
+ do {
+ try block()
+ } catch let err as TError {
+ var message = error.message ?? ""
+ message += "\nFile: \(sourceFile)\n"
+ message += "Line: \(sourceLine)\n"
+ message += "Method: \(sourceMethod)"
+ message += "\nOriginal Error:\n" + err.description
+ error.message = message
+ throw error
+ }
+}
+
+
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TSSLSocketTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/TSSLSocketTransport.swift
new file mode 100644
index 000000000..d350f821e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TSSLSocketTransport.swift
@@ -0,0 +1,236 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import Foundation
+import CoreFoundation
+
+#if !swift(>=4.2)
+// Swift 3/4 compatibility
+fileprivate extension RunLoopMode {
+ static let `default` = defaultRunLoopMode
+}
+#endif
+
+#if os(Linux)
+public class TSSLSocketTransport {
+ init(hostname: String, port: UInt16) {
+ // FIXME!
+ assert(false, "Security not available in Linux, TSSLSocketTransport Unavilable for now")
+ }
+}
+#else
+let isLittleEndian = Int(OSHostByteOrder()) == OSLittleEndian
+let htons = isLittleEndian ? _OSSwapInt16 : { $0 }
+let htonl = isLittleEndian ? _OSSwapInt32 : { $0 }
+
+public class TSSLSocketTransport: TStreamTransport {
+ var sslHostname: String
+ var sd: Int32 = 0
+
+ public init(hostname: String, port: UInt16) throws {
+ sslHostname = hostname
+ var readStream: Unmanaged<CFReadStream>?
+ var writeStream: Unmanaged<CFWriteStream>?
+
+ /* create a socket structure */
+ var pin: sockaddr_in = sockaddr_in()
+ var hp: UnsafeMutablePointer<hostent>? = nil
+ for i in 0..<10 {
+
+ hp = gethostbyname(hostname.cString(using: String.Encoding.utf8)!)
+ if hp == nil {
+ print("failed to resolve hostname \(hostname)")
+ herror("resolv")
+ if i == 9 {
+ super.init(inputStream: nil, outputStream: nil) // have to init before throwing
+ throw TSSLSocketTransportError(error: .hostanameResolution(hostname: hostname))
+ }
+ Thread.sleep(forTimeInterval: 0.2)
+ } else {
+ break
+ }
+ }
+ pin.sin_family = UInt8(AF_INET)
+ pin.sin_addr = in_addr(s_addr: UInt32((hp?.pointee.h_addr_list.pointee?.pointee)!)) // Is there a better way to get this???
+ pin.sin_port = htons(port)
+
+ /* create the socket */
+ sd = socket(Int32(AF_INET), Int32(SOCK_STREAM), Int32(IPPROTO_TCP))
+ if sd == -1 {
+ super.init(inputStream: nil, outputStream: nil) // have to init before throwing
+ throw TSSLSocketTransportError(error: .socketCreate(port: Int(port)))
+ }
+
+ /* open a connection */
+ // need a non-self ref to sd, otherwise the j complains
+ let sd_local = sd
+ let connectResult = withUnsafePointer(to: &pin) {
+ connect(sd_local, UnsafePointer<sockaddr>(OpaquePointer($0)), socklen_t(MemoryLayout<sockaddr_in>.size))
+ }
+ if connectResult == -1 {
+ super.init(inputStream: nil, outputStream: nil) // have to init before throwing
+ throw TSSLSocketTransportError(error: .connect)
+ }
+
+ CFStreamCreatePairWithSocket(kCFAllocatorDefault, sd, &readStream, &writeStream)
+
+ CFReadStreamSetProperty(readStream?.takeRetainedValue(), .socketNativeHandle, kCFBooleanTrue)
+ CFWriteStreamSetProperty(writeStream?.takeRetainedValue(), .socketNativeHandle, kCFBooleanTrue)
+
+ var inputStream: InputStream? = nil
+ var outputStream: OutputStream? = nil
+ if readStream != nil && writeStream != nil {
+
+ CFReadStreamSetProperty(readStream?.takeRetainedValue(),
+ .socketSecurityLevel,
+ kCFStreamSocketSecurityLevelTLSv1)
+
+ let settings: [String: Bool] = [kCFStreamSSLValidatesCertificateChain as String: true]
+
+ CFReadStreamSetProperty(readStream?.takeRetainedValue(),
+ .SSLSettings,
+ settings as CFTypeRef)
+
+ CFWriteStreamSetProperty(writeStream?.takeRetainedValue(),
+ .SSLSettings,
+ settings as CFTypeRef)
+
+ inputStream = readStream!.takeRetainedValue()
+ inputStream?.schedule(in: .current, forMode: .default)
+ inputStream?.open()
+
+ outputStream = writeStream!.takeRetainedValue()
+ outputStream?.schedule(in: .current, forMode: .default)
+ outputStream?.open()
+
+ readStream?.release()
+ writeStream?.release()
+ }
+
+
+ super.init(inputStream: inputStream, outputStream: outputStream)
+ self.input?.delegate = self
+ self.output?.delegate = self
+ }
+
+ func recoverFromTrustFailure(_ myTrust: SecTrust, lastTrustResult: SecTrustResultType) -> Bool {
+ let trustTime = SecTrustGetVerifyTime(myTrust)
+ let currentTime = CFAbsoluteTimeGetCurrent()
+
+ let timeIncrement = 31536000 // from TSSLSocketTransport.m
+ let newTime = currentTime - Double(timeIncrement)
+
+ if trustTime - newTime != 0 {
+ let newDate = CFDateCreate(nil, newTime)
+ SecTrustSetVerifyDate(myTrust, newDate!)
+
+ var tr = lastTrustResult
+ let success = withUnsafeMutablePointer(to: &tr) { trPtr -> Bool in
+ if SecTrustEvaluate(myTrust, trPtr) != errSecSuccess {
+ return false
+ }
+ return true
+ }
+ if !success { return false }
+ }
+ if lastTrustResult == .proceed || lastTrustResult == .unspecified {
+ return false
+ }
+
+ print("TSSLSocketTransport: Unable to recover certificate trust failure")
+ return true
+ }
+
+ public func isOpen() -> Bool {
+ return sd > 0
+ }
+}
+
+extension TSSLSocketTransport: StreamDelegate {
+ public func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
+
+ switch eventCode {
+ case Stream.Event(): break
+ case Stream.Event.hasBytesAvailable: break
+ case Stream.Event.openCompleted: break
+ case Stream.Event.hasSpaceAvailable:
+ var proceed = false
+ var trustResult: SecTrustResultType = .invalid
+
+ var newPolicies: CFMutableArray?
+
+ repeat {
+ let trust: SecTrust = aStream.property(forKey: .SSLPeerTrust) as! SecTrust
+
+ // Add new policy to current list of policies
+ let policy = SecPolicyCreateSSL(false, sslHostname as CFString?)
+ var ppolicy = policy // mutable for pointer
+ let policies: UnsafeMutablePointer<CFArray?>? = nil
+ if SecTrustCopyPolicies(trust, policies!) != errSecSuccess {
+ break
+ }
+ withUnsafeMutablePointer(to: &ppolicy) { ptr in
+ newPolicies = CFArrayCreateMutableCopy(nil, 0, policies?.pointee)
+ CFArrayAppendValue(newPolicies, ptr)
+ }
+
+ // update trust policies
+ if SecTrustSetPolicies(trust, newPolicies!) != errSecSuccess {
+ break
+ }
+
+ // Evaluate the trust chain
+ let success = withUnsafeMutablePointer(to: &trustResult) { trustPtr -> Bool in
+ if SecTrustEvaluate(trust, trustPtr) != errSecSuccess {
+ return false
+ }
+ return true
+ }
+
+ if !success {
+ break
+ }
+
+
+ switch trustResult {
+ case .proceed: proceed = true
+ case .unspecified: proceed = true
+ case .recoverableTrustFailure:
+ proceed = self.recoverFromTrustFailure(trust, lastTrustResult: trustResult)
+
+ case .deny: break
+ case .fatalTrustFailure: break
+ case .otherError: break
+ case .invalid: break
+ default: break
+ }
+ } while false
+
+ if !proceed {
+ print("TSSLSocketTransport: Cannot trust certificate. Result: \(trustResult)")
+ aStream.close()
+ }
+
+ case Stream.Event.errorOccurred: break
+ case Stream.Event.endEncountered: break
+ default: break
+ }
+ }
+}
+#endif
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TSSLSocketTransportError.swift b/src/jaegertracing/thrift/lib/swift/Sources/TSSLSocketTransportError.swift
new file mode 100644
index 000000000..fda162bd5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TSSLSocketTransportError.swift
@@ -0,0 +1,48 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+public struct TSSLSocketTransportError: TError {
+ public enum ErrorCode: TErrorCode {
+ case hostanameResolution(hostname: String)
+ case socketCreate(port: Int)
+ case connect
+
+ public var thriftErrorCode: Int {
+ switch self {
+ case .hostanameResolution: return -10000
+ case .socketCreate: return -10001
+ case .connect: return -10002
+ }
+ }
+
+ public var description: String {
+ switch self {
+ case .hostanameResolution(let hostname): return "Failed to resolve hostname: \(hostname)"
+ case .socketCreate(let port): return "Could not create socket on port: \(port)"
+ case .connect: return "Connect error"
+ }
+ }
+
+ }
+ public var error: ErrorCode = .connect
+ public var message: String?
+ public static var defaultCase: ErrorCode { return .connect }
+
+ public init() { }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TSerializable.swift b/src/jaegertracing/thrift/lib/swift/Sources/TSerializable.swift
new file mode 100644
index 000000000..b45096b69
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TSerializable.swift
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import Foundation
+
+
+public protocol TSerializable {
+ var hashValue: Int { get }
+
+ /// TType for instance
+ static var thriftType: TType { get }
+
+ /// Read TSerializable instance from Protocol
+ static func read(from proto: TProtocol) throws -> Self
+
+ /// Write TSerializable instance to Protocol
+ func write(to proto: TProtocol) throws
+
+}
+
+extension TSerializable {
+ public static func write(_ value: Self, to proto: TProtocol) throws {
+ try value.write(to: proto)
+ }
+
+ /// convenience for member access
+ public var thriftType: TType { return Self.thriftType }
+}
+
+public func ==<T>(lhs: T, rhs: T) -> Bool where T : TSerializable {
+ return lhs.hashValue == rhs.hashValue
+}
+
+/// Default read/write for primitave Thrift types:
+/// Bool, Int8 (byte), Int16, Int32, Int64, Double, String
+
+extension Bool : TSerializable {
+ public static var thriftType: TType { return .bool }
+
+ public static func read(from proto: TProtocol) throws -> Bool {
+ return try proto.read()
+ }
+
+ public func write(to proto: TProtocol) throws {
+ try proto.write(self)
+ }
+}
+
+extension Int8 : TSerializable {
+ public static var thriftType: TType { return .i8 }
+
+ public static func read(from proto: TProtocol) throws -> Int8 {
+ return Int8(try proto.read() as UInt8)
+ }
+
+ public func write(to proto: TProtocol) throws {
+ try proto.write(UInt8(self))
+ }
+}
+
+extension Int16 : TSerializable {
+ public static var thriftType: TType { return .i16 }
+
+ public static func read(from proto: TProtocol) throws -> Int16 {
+ return try proto.read()
+ }
+
+ public func write(to proto: TProtocol) throws {
+ try proto.write(self)
+ }
+}
+
+extension Int32 : TSerializable {
+ public static var thriftType: TType { return .i32 }
+
+ public static func read(from proto: TProtocol) throws -> Int32 {
+ return try proto.read()
+ }
+
+ public func write(to proto: TProtocol) throws {
+ try proto.write(self)
+ }
+}
+
+
+extension Int64 : TSerializable {
+ public static var thriftType: TType { return .i64 }
+
+ public static func read(from proto: TProtocol) throws -> Int64 {
+ return try proto.read()
+ }
+
+ public func write(to proto: TProtocol) throws {
+ try proto.write(self)
+ }
+}
+
+extension Double : TSerializable {
+ public static var thriftType: TType { return .double }
+
+ public static func read(from proto: TProtocol) throws -> Double {
+ return try proto.read()
+ }
+
+ public func write(to proto: TProtocol) throws {
+ try proto.write(self)
+ }
+}
+
+extension String : TSerializable {
+ public static var thriftType: TType { return .string }
+
+ public static func read(from proto: TProtocol) throws -> String {
+ return try proto.read()
+ }
+
+ public func write(to proto: TProtocol) throws {
+ try proto.write(self)
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TSet.swift b/src/jaegertracing/thrift/lib/swift/Sources/TSet.swift
new file mode 100644
index 000000000..6891c11e6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TSet.swift
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import Foundation
+
+public struct TSet<Element : TSerializable & Hashable> : SetAlgebra, Hashable, Collection, ExpressibleByArrayLiteral, TSerializable {
+ /// Typealias for Storage type
+ public typealias Storage = Set<Element>
+
+
+ /// Internal Storage used for TSet (Set\<Element\>)
+ internal var storage : Storage
+
+
+ /// Mark: Collection
+
+ public typealias Indices = Storage.Indices
+ public typealias Index = Storage.Index
+ public typealias IndexDistance = Int
+ public typealias SubSequence = Storage.SubSequence
+
+
+ public var indices: Indices { return storage.indices }
+
+ // Must implement isEmpty even though both SetAlgebra and Collection provide it due to their conflciting default implementations
+ public var isEmpty: Bool { return storage.isEmpty }
+
+ public func distance(from start: Index, to end: Index) -> IndexDistance {
+ return storage.distance(from: start, to: end)
+ }
+
+ public func index(_ i: Index, offsetBy n: IndexDistance) -> Index {
+ return storage.index(i, offsetBy: n)
+ }
+
+ public func index(_ i: Index, offsetBy n: IndexDistance, limitedBy limit: Index) -> Index? {
+ return storage.index(i, offsetBy: n, limitedBy: limit)
+ }
+
+ #if swift(>=3.2)
+ public subscript (position: Storage.Index) -> Element {
+ return storage[position]
+ }
+ #else
+ public subscript (position: Storage.Index) -> Element? {
+ return storage[position]
+ }
+ #endif
+
+ /// Mark: SetAlgebra
+ internal init(storage: Set<Element>) {
+ self.storage = storage
+ }
+
+ public func contains(_ member: Element) -> Bool {
+ return storage.contains(member)
+ }
+
+ public mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element) {
+ return storage.insert(newMember)
+ }
+
+ public mutating func remove(_ member: Element) -> Element? {
+ return storage.remove(member)
+ }
+
+ public func union(_ other: TSet<Element>) -> TSet {
+ return TSet(storage: storage.union(other.storage))
+ }
+
+ public mutating func formIntersection(_ other: TSet<Element>) {
+ return storage.formIntersection(other.storage)
+ }
+
+ public mutating func formSymmetricDifference(_ other: TSet<Element>) {
+ return storage.formSymmetricDifference(other.storage)
+ }
+
+ public mutating func formUnion(_ other: TSet<Element>) {
+ return storage.formUnion(other.storage)
+ }
+
+ public func intersection(_ other: TSet<Element>) -> TSet {
+ return TSet(storage: storage.intersection(other.storage))
+ }
+
+ public func symmetricDifference(_ other: TSet<Element>) -> TSet {
+ return TSet(storage: storage.symmetricDifference(other.storage))
+ }
+
+ public mutating func update(with newMember: Element) -> Element? {
+ return storage.update(with: newMember)
+ }
+
+ /// Mark: IndexableBase
+
+ public var startIndex: Index { return storage.startIndex }
+ public var endIndex: Index { return storage.endIndex }
+ public func index(after i: Index) -> Index {
+ return storage.index(after: i)
+ }
+
+ public func formIndex(after i: inout Storage.Index) {
+ storage.formIndex(after: &i)
+ }
+
+ public subscript(bounds: Range<Index>) -> SubSequence {
+ return storage[bounds]
+ }
+
+
+ /// Mark: Hashable
+ public var hashValue : Int {
+ let prime = 31
+ var result = 1
+ for element in storage {
+ result = prime &* result &+ element.hashValue
+ }
+ return result
+ }
+
+ /// Mark: TSerializable
+ public static var thriftType : TType { return .set }
+
+ public init() {
+ storage = Storage()
+ }
+
+ public init(arrayLiteral elements: Element...) {
+ self.storage = Storage(elements)
+ }
+
+ public init<Source : Sequence>(_ sequence: Source) where Source.Iterator.Element == Element {
+ storage = Storage(sequence)
+ }
+
+ public static func read(from proto: TProtocol) throws -> TSet {
+ let (elementType, size) = try proto.readSetBegin()
+ if elementType != Element.thriftType {
+ throw TProtocolError(error: .invalidData,
+ extendedError: .unexpectedType(type: elementType))
+ }
+ var set = TSet()
+ for _ in 0..<size {
+ let element = try Element.read(from: proto)
+ set.storage.insert(element)
+ }
+ try proto.readSetEnd()
+ return set
+ }
+
+ public func write(to proto: TProtocol) throws {
+ try proto.writeSetBegin(elementType: Element.thriftType, size: Int32(self.count))
+ for element in self.storage {
+ try Element.write(element, to: proto)
+ }
+ try proto.writeSetEnd()
+ }
+}
+
+extension TSet: CustomStringConvertible, CustomDebugStringConvertible {
+ public var description : String {
+ return storage.description
+ }
+ public var debugDescription : String {
+ return storage.debugDescription
+ }
+
+}
+
+public func ==<Element>(lhs: TSet<Element>, rhs: TSet<Element>) -> Bool {
+ return lhs.storage == rhs.storage
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TSocketServer.swift b/src/jaegertracing/thrift/lib/swift/Sources/TSocketServer.swift
new file mode 100644
index 000000000..7367c7edc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TSocketServer.swift
@@ -0,0 +1,148 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+#if os(OSX) || os(iOS) || os(watchOS) || os(tvOS)
+ import Darwin
+#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android)
+ import Glibc
+ import Dispatch
+#endif
+
+import Foundation
+import CoreFoundation
+
+public let TSocketServerClientConnectionFinished = "TSocketServerClientConnectionFinished"
+public let TSocketServerProcessorKey = "TSocketServerProcessor"
+public let TSocketServerTransportKey = "TSocketServerTransport"
+
+class TSocketServer<InProtocol: TProtocol, OutProtocol: TProtocol, Processor: TProcessor, Service> where Processor.Service == Service {
+ var socketFileHandle: FileHandle
+ var processingQueue = DispatchQueue(label: "TSocketServer.processing",
+ qos: .background,
+ attributes: .concurrent)
+ var serviceHandler: Service
+
+ public init(port: Int,
+ service: Service,
+ inProtocol: InProtocol.Type,
+ outProtocol: OutProtocol.Type,
+ processor: Processor.Type) throws {
+ // set service handler
+ self.serviceHandler = service
+
+ // create a socket
+ var fd: Int32 = -1
+ #if os(Linux)
+ let sock = CFSocketCreate(kCFAllocatorDefault, PF_INET, Int32(SOCK_STREAM.rawValue), Int32(IPPROTO_TCP), 0, nil, nil)
+ #else
+ let sock = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, 0, nil, nil)
+ #endif
+ if sock != nil {
+ CFSocketSetSocketFlags(sock, CFSocketGetSocketFlags(sock) & ~kCFSocketCloseOnInvalidate)
+
+ fd = CFSocketGetNative(sock)
+ var yes = 1
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, UInt32(MemoryLayout<Int>.size))
+
+ #if os(Linux)
+ var addr = sockaddr_in(sin_family: sa_family_t(AF_INET),
+ sin_port: in_port_t(port.bigEndian),
+ sin_addr: in_addr(s_addr: in_addr_t(0)),
+ sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
+ #else
+ var addr = sockaddr_in(sin_len: UInt8(MemoryLayout<sockaddr_in>.size),
+ sin_family: sa_family_t(AF_INET),
+ sin_port: in_port_t(port.bigEndian),
+ sin_addr: in_addr(s_addr: in_addr_t(0)),
+ sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
+ #endif
+
+ let ptr = withUnsafePointer(to: &addr) {
+ return UnsafePointer<UInt8>(OpaquePointer($0))
+ }
+
+ let address = Data(bytes: ptr, count: MemoryLayout<sockaddr_in>.size)
+
+ let cfaddr = address.withUnsafeBytes {
+ CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, $0, address.count, nil)
+ }
+ if CFSocketSetAddress(sock, cfaddr) != CFSocketError.success { //kCFSocketSuccess {
+ CFSocketInvalidate(sock)
+ print("TSocketServer: Could not bind to address")
+ throw TTransportError(error: .notOpen, message: "Could not bind to address")
+ }
+
+ } else {
+ print("TSocketServer: No server socket")
+ throw TTransportError(error: .notOpen, message: "Could not create socket")
+ }
+
+ // wrap it in a file handle so we can get messages from it
+ socketFileHandle = FileHandle(fileDescriptor: fd, closeOnDealloc: true)
+
+ // throw away our socket
+ CFSocketInvalidate(sock)
+
+ // register for notifications of accepted incoming connections
+ _ = NotificationCenter.default.addObserver(forName: .NSFileHandleConnectionAccepted,
+ object: nil, queue: nil) {
+ [weak self] notification in
+ guard let strongSelf = self else { return }
+ strongSelf.connectionAccepted(strongSelf.socketFileHandle)
+ }
+
+ // tell socket to listen
+ socketFileHandle.acceptConnectionInBackgroundAndNotify()
+
+ print("TSocketServer: Listening on TCP port \(port)")
+ }
+
+ deinit {
+ NotificationCenter.default.removeObserver(self)
+ }
+
+ func connectionAccepted(_ socket: FileHandle) {
+ // Now that we have a client connected, handle the request on queue
+ processingQueue.async {
+ self.handleClientConnection(socket)
+ }
+ }
+
+ func handleClientConnection(_ clientSocket: FileHandle) {
+
+ let transport = TFileHandleTransport(fileHandle: clientSocket)
+ let processor = Processor(service: serviceHandler)
+
+ let inProtocol = InProtocol(on: transport)
+ let outProtocol = OutProtocol(on: transport)
+
+ do {
+ try processor.process(on: inProtocol, outProtocol: outProtocol)
+ } catch let error {
+ print("Error processign request: \(error)")
+ }
+ DispatchQueue.main.async {
+ NotificationCenter.default
+ .post(name: Notification.Name(rawValue: TSocketServerClientConnectionFinished),
+ object: self,
+ userInfo: [TSocketServerProcessorKey: processor,
+ TSocketServerTransportKey: transport])
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TSocketTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/TSocketTransport.swift
new file mode 100644
index 000000000..21325033b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TSocketTransport.swift
@@ -0,0 +1,216 @@
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+#if os(OSX) || os(iOS) || os(watchOS) || os(tvOS)
+ import Darwin
+#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android)
+ import Glibc
+ import Dispatch
+#endif
+
+import Foundation
+import CoreFoundation
+
+#if !swift(>=4.2)
+// Swift 3/4 compatibility
+fileprivate extension RunLoopMode {
+ static let `default` = defaultRunLoopMode
+}
+#endif
+
+private struct Sys {
+ #if os(Linux)
+ static let read = Glibc.read
+ static let write = Glibc.write
+ static let close = Glibc.close
+ #else
+ static let read = Darwin.read
+ static let write = Darwin.write
+ static let close = Darwin.close
+ #endif
+}
+
+extension in_addr {
+ public init?(hostent: hostent?) {
+ guard let host = hostent, host.h_addr_list != nil, host.h_addr_list.pointee != nil else {
+ return nil
+ }
+ self.init()
+ memcpy(&self, host.h_addr_list.pointee!, Int(host.h_length))
+
+ }
+}
+
+
+#if os(Linux)
+ /// TCFSocketTransport currently unavailable
+ /// remove comments and build to see why/fix
+ /// currently CF[Read|Write]Stream's can't cast to [Input|Output]Streams which breaks thigns
+#else
+extension Stream.PropertyKey {
+ static let SSLPeerTrust = Stream.PropertyKey(kCFStreamPropertySSLPeerTrust as String)
+}
+
+/// TCFSocketTransport, uses CFSockets and (NS)Stream's
+public class TCFSocketTransport: TStreamTransport {
+ public init?(hostname: String, port: Int, secure: Bool = false) {
+
+ var inputStream: InputStream
+ var outputStream: OutputStream
+
+ var readStream: Unmanaged<CFReadStream>?
+ var writeStream: Unmanaged<CFWriteStream>?
+ CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
+ hostname as CFString,
+ UInt32(port),
+ &readStream,
+ &writeStream)
+
+ if let readStream = readStream?.takeRetainedValue(),
+ let writeStream = writeStream?.takeRetainedValue() {
+ CFReadStreamSetProperty(readStream, .shouldCloseNativeSocket, kCFBooleanTrue)
+ CFWriteStreamSetProperty(writeStream, .shouldCloseNativeSocket, kCFBooleanTrue)
+
+ if secure {
+ CFReadStreamSetProperty(readStream, .socketSecurityLevel, StreamSocketSecurityLevel.negotiatedSSL.rawValue as CFString)
+ CFWriteStreamSetProperty(writeStream, .socketSecurityLevel, StreamSocketSecurityLevel.negotiatedSSL.rawValue as CFString)
+ }
+
+ inputStream = readStream as InputStream
+ inputStream.schedule(in: .current, forMode: .default)
+ inputStream.open()
+
+ outputStream = writeStream as OutputStream
+ outputStream.schedule(in: .current, forMode: .default)
+ outputStream.open()
+
+ } else {
+
+ if readStream != nil {
+ readStream?.release()
+ }
+ if writeStream != nil {
+ writeStream?.release()
+ }
+ super.init(inputStream: nil, outputStream: nil)
+ return nil
+ }
+
+ super.init(inputStream: inputStream, outputStream: outputStream)
+
+ self.input?.delegate = self
+ self.output?.delegate = self
+ }
+}
+
+extension TCFSocketTransport: StreamDelegate { }
+#endif
+
+
+/// TSocketTransport, posix sockets. Supports IPv4 only for now
+public class TSocketTransport : TTransport {
+ public var socketDescriptor: Int32
+
+
+
+ /// Initialize from an already set up socketDescriptor.
+ /// Expects socket thats already bound/connected (i.e. from listening)
+ ///
+ /// - parameter socketDescriptor: posix socket descriptor (Int32)
+ public init(socketDescriptor: Int32) {
+ self.socketDescriptor = socketDescriptor
+ }
+
+
+ public convenience init(hostname: String, port: Int) throws {
+ guard let hp = gethostbyname(hostname.cString(using: .utf8)!)?.pointee,
+ let hostAddr = in_addr(hostent: hp) else {
+ throw TTransportError(error: .unknown, message: "Invalid address: \(hostname)")
+ }
+
+
+ #if os(Linux)
+ let sock = socket(AF_INET, Int32(SOCK_STREAM.rawValue), 0)
+ var addr = sockaddr_in(sin_family: sa_family_t(AF_INET),
+ sin_port: in_port_t(htons(UInt16(port))),
+ sin_addr: hostAddr,
+ sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
+ #else
+ let sock = socket(AF_INET, SOCK_STREAM, 0)
+
+ var addr = sockaddr_in(sin_len: UInt8(MemoryLayout<sockaddr_in>.size),
+ sin_family: sa_family_t(AF_INET),
+ sin_port: in_port_t(htons(UInt16(port))),
+ sin_addr: in_addr(s_addr: in_addr_t(0)),
+ sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
+
+ #endif
+
+ let addrPtr = withUnsafePointer(to: &addr){ UnsafePointer<sockaddr>(OpaquePointer($0)) }
+
+ let connected = connect(sock, addrPtr, UInt32(MemoryLayout<sockaddr_in>.size))
+ if connected != 0 {
+ throw TTransportError(error: .notOpen, message: "Error binding to host: \(hostname) \(port)")
+ }
+
+ self.init(socketDescriptor: sock)
+ }
+
+ deinit {
+ close()
+ }
+
+ public func readAll(size: Int) throws -> Data {
+ var out = Data()
+ while out.count < size {
+ out.append(try self.read(size: size))
+ }
+ return out
+ }
+
+ public func read(size: Int) throws -> Data {
+ var buff = Array<UInt8>.init(repeating: 0, count: size)
+ let readBytes = Sys.read(socketDescriptor, &buff, size)
+
+ return Data(buff[0..<readBytes])
+ }
+
+ public func write(data: Data) {
+ var bytesToWrite = data.count
+ var writeBuffer = data
+ while bytesToWrite > 0 {
+ let written = writeBuffer.withUnsafeBytes {
+ Sys.write(socketDescriptor, $0, writeBuffer.count)
+ }
+ writeBuffer = writeBuffer.subdata(in: written ..< writeBuffer.count)
+ bytesToWrite -= written
+ }
+ }
+
+ public func flush() throws {
+ // nothing to do
+ }
+
+ public func close() {
+ shutdown(socketDescriptor, Int32(SHUT_RDWR))
+ _ = Sys.close(socketDescriptor)
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TStreamTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/TStreamTransport.swift
new file mode 100644
index 000000000..d9c957422
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TStreamTransport.swift
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import Foundation
+import CoreFoundation
+
+#if !swift(>=4.2)
+// Swift 3/4 compatibility
+fileprivate extension RunLoopMode {
+ static let `default` = defaultRunLoopMode
+}
+#endif
+
+#if os(Linux)
+ /// Currently unavailable in Linux
+ /// Remove comments and build to fix
+ /// Currently kConstants for CFSockets don't exist in linux and not all have been moved
+ /// to property structs yet
+#else
+ // Must inherit NSObject for NSStreamDelegate conformance
+ public class TStreamTransport : NSObject, TTransport {
+ public var input: InputStream? = nil
+ public var output: OutputStream? = nil
+
+ public init(inputStream: InputStream?, outputStream: OutputStream?) {
+ input = inputStream
+ output = outputStream
+ }
+
+ public convenience init(inputStream: InputStream?) {
+ self.init(inputStream: inputStream, outputStream: nil)
+ }
+
+ public convenience init(outputStream: OutputStream?) {
+ self.init(inputStream: nil, outputStream: outputStream)
+ }
+
+ deinit {
+ close()
+ }
+
+ public func readAll(size: Int) throws -> Data {
+ guard let input = input else {
+ throw TTransportError(error: .unknown)
+ }
+
+ var read = Data()
+ while read.count < size {
+ var buffer = Array<UInt8>(repeating: 0, count: size - read.count)
+
+ let bytesRead = buffer.withUnsafeMutableBufferPointer { bufferPtr in
+ return input.read(bufferPtr.baseAddress!, maxLength: size - read.count)
+ }
+
+ if bytesRead <= 0 {
+ throw TTransportError(error: .notOpen)
+ }
+ read.append(Data(buffer))
+ }
+ return read
+ }
+
+ public func read(size: Int) throws -> Data {
+ guard let input = input else {
+ throw TTransportError(error: .unknown)
+ }
+
+ var read = Data()
+ while read.count < size {
+ var buffer = Array<UInt8>(repeating: 0, count: size - read.count)
+ let bytesRead = buffer.withUnsafeMutableBufferPointer {
+ input.read($0.baseAddress!, maxLength: size - read.count)
+ }
+
+ if bytesRead <= 0 {
+ break
+ }
+
+ read.append(Data(buffer))
+ }
+ return read
+ }
+
+ public func write(data: Data) throws {
+ guard let output = output else {
+ throw TTransportError(error: .unknown)
+ }
+
+ var bytesWritten = 0
+ while bytesWritten < data.count {
+ bytesWritten = data.withUnsafeBytes {
+ return output.write($0, maxLength: data.count)
+ }
+
+ if bytesWritten == -1 {
+ throw TTransportError(error: .notOpen)
+ } else if bytesWritten == 0 {
+ throw TTransportError(error: .endOfFile)
+ }
+ }
+ }
+
+
+ public func flush() throws {
+ return
+ }
+
+ public func close() {
+
+ if input != nil {
+ // Close and reset inputstream
+ if let cf: CFReadStream = input {
+ CFReadStreamSetProperty(cf, .shouldCloseNativeSocket, kCFBooleanTrue)
+ }
+
+ input?.delegate = nil
+ input?.close()
+ input?.remove(from: .current, forMode: .default)
+ input = nil
+ }
+
+ if output != nil {
+ // Close and reset output stream
+ if let cf: CFWriteStream = output {
+ CFWriteStreamSetProperty(cf, .shouldCloseNativeSocket, kCFBooleanTrue)
+ }
+ output?.delegate = nil
+ output?.close()
+ output?.remove(from: .current, forMode: .default)
+ output = nil
+ }
+ }
+ }
+#endif
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TStruct.swift b/src/jaegertracing/thrift/lib/swift/Sources/TStruct.swift
new file mode 100644
index 000000000..38e51e702
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TStruct.swift
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+/// Protocol for Generated Structs to conform to
+/// Dictionary maps field names to internal IDs and uses Reflection
+/// to iterate through all fields.
+/// `writeFieldValue(_:name:type:id:)` calls `TSerializable.write(to:)` internally
+/// giving a nice recursive behavior for nested TStructs, TLists, TMaps, and TSets
+public protocol TStruct : TSerializable {
+ static var fieldIds: [String: Int32] { get }
+ static var structName: String { get }
+}
+
+public extension TStruct {
+ static var fieldIds: [String: (id: Int32, type: TType)] { return [:] }
+ static var thriftType: TType { return .struct }
+
+ func write(to proto: TProtocol) throws {
+ // Write struct name first
+ try proto.writeStructBegin(name: Self.structName)
+
+ try self.forEach { name, value, id in
+ // Write to protocol
+ try proto.writeFieldValue(value, name: name,
+ type: value.thriftType, id: id)
+ }
+ try proto.writeFieldStop()
+ try proto.writeStructEnd()
+ }
+
+ var hashValue: Int {
+ let prime = 31
+ var result = 1
+ self.forEach { _, value, _ in
+ result = prime &* result &+ (value.hashValue)
+ }
+ return result
+ }
+
+ /// Provides a block for handling each (available) thrift property using reflection
+ /// Caveat: Skips over optional values
+
+
+ /// Provides a block for handling each (available) thrift property using reflection
+ ///
+ /// - parameter block: block for handling property
+ ///
+ /// - throws: rethrows any Error thrown in block
+ private func forEach(_ block: (_ name: String, _ value: TSerializable, _ id: Int32) throws -> Void) rethrows {
+ // Mirror the object, getting (name: String?, value: Any) for every property
+ let mirror = Mirror(reflecting: self)
+
+ // Iterate through all children, ignore empty property names
+ for (propName, propValue) in mirror.children {
+ guard let propName = propName else { continue }
+
+ if let tval = unwrap(any: propValue) as? TSerializable, let id = Self.fieldIds[propName] {
+ try block(propName, tval, id)
+ }
+ }
+ }
+
+
+ /// Any can mysteriously be an Optional<Any> at the same time,
+ /// this checks and always returns Optional<Any> without double wrapping
+ /// we then try to bind value as TSerializable to ignore any extension properties
+ /// and the like and verify the property exists and grab the Thrift
+ /// property ID at the same time
+ ///
+ /// - parameter any: Any instance to attempt to unwrap
+ ///
+ /// - returns: Unwrapped Any as Optional<Any>
+ private func unwrap(any: Any) -> Any? {
+ let mi = Mirror(reflecting: any)
+
+ if mi.displayStyle != .optional { return any }
+ if mi.children.count == 0 { return nil }
+
+ let (_, some) = mi.children.first!
+ return some
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/TTransport.swift
new file mode 100644
index 000000000..e82bbe140
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TTransport.swift
@@ -0,0 +1,64 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import Foundation
+
+public protocol TTransport {
+
+ // Required
+ func read(size: Int) throws -> Data
+ func write(data: Data) throws
+ func flush() throws
+
+ // Optional (default provided)
+ func readAll(size: Int) throws -> Data
+ func isOpen() throws -> Bool
+ func open() throws
+ func close() throws
+}
+
+public extension TTransport {
+ func isOpen() throws -> Bool { return true }
+ func open() throws { }
+ func close() throws { }
+
+ func readAll(size: Int) throws -> Data {
+ var buff = Data()
+ var have = 0
+ while have < size {
+ let chunk = try self.read(size: size - have)
+ have += chunk.count
+ buff.append(chunk)
+ if chunk.count == 0 {
+ throw TTransportError(error: .endOfFile)
+ }
+ }
+ return buff
+ }
+}
+
+public protocol TAsyncTransport : TTransport {
+ // Factory
+ func flush(_ completion: @escaping (TAsyncTransport, Error?) ->())
+}
+
+public protocol TAsyncTransportFactory {
+ associatedtype Transport : TAsyncTransport
+ func newTransport() -> Transport
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TTransportError.swift b/src/jaegertracing/thrift/lib/swift/Sources/TTransportError.swift
new file mode 100644
index 000000000..3fd005937
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TTransportError.swift
@@ -0,0 +1,86 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+public struct TTransportError: TError {
+ public enum ErrorCode: TErrorCode {
+ case unknown
+ case notOpen
+ case alreadyOpen
+ case timedOut
+ case endOfFile
+ case negativeSize
+ case sizeLimit(limit: Int, got: Int)
+
+ public var thriftErrorCode: Int {
+ switch self {
+ case .unknown: return 0
+ case .notOpen: return 1
+ case .alreadyOpen: return 2
+ case .timedOut: return 3
+ case .endOfFile: return 4
+ case .negativeSize: return 5
+ case .sizeLimit: return 6
+ }
+ }
+ public var description: String {
+ switch self {
+ case .unknown: return "Unknown TTransportError"
+ case .notOpen: return "Not Open"
+ case .alreadyOpen: return "Already Open"
+ case .timedOut: return "Timed Out"
+ case .endOfFile: return "End Of File"
+ case .negativeSize: return "Negative Size"
+ case .sizeLimit(let limit, let got):
+ return "Message exceeds size limit of \(limit) (received: \(got)"
+ }
+ }
+ }
+ public var error: ErrorCode = .unknown
+ public var message: String? = nil
+ public static var defaultCase: ErrorCode { return .unknown }
+
+ public init() { }
+
+}
+
+/// THTTPTransportError
+///
+/// Error's thrown on HTTP Transport
+public struct THTTPTransportError: TError {
+ public enum ErrorCode: TErrorCode {
+ case invalidResponse
+ case invalidStatus(statusCode: Int)
+ case authentication
+
+ public var description: String {
+ switch self {
+ case .invalidResponse: return "Invalid HTTP Response"
+ case .invalidStatus(let statusCode): return "Invalid HTTP Status Code (\(statusCode))"
+ case .authentication: return "Authentication Error"
+ }
+ }
+ public var thriftErrorCode: Int { return 0 }
+ }
+ public var error: ErrorCode = .invalidResponse
+ public var message: String? = nil
+ public static var defaultCase: ErrorCode { return .invalidResponse }
+
+ public init() { }
+}
+
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TWrappedProtocol.swift b/src/jaegertracing/thrift/lib/swift/Sources/TWrappedProtocol.swift
new file mode 100644
index 000000000..8e8577bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/TWrappedProtocol.swift
@@ -0,0 +1,208 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import Foundation // For (NS)Data
+
+
+/// Generic protocol, implementes TProtocol and wraps a concrete protocol.
+/// Useful for generically subclassing protocols to override specific methods
+/// (i.e. TMultiplexedProtocol)
+open class TWrappedProtocol<Protocol: TProtocol> : TProtocol {
+ var concreteProtocol: Protocol
+
+ public var transport: TTransport {
+ get {
+ return concreteProtocol.transport
+ }
+ set {
+ concreteProtocol.transport = newValue
+ }
+ }
+
+ public required init(on transport: TTransport) {
+ self.concreteProtocol = Protocol(on: transport)
+ }
+
+ // Read methods
+
+ public func readMessageBegin() throws -> (String, TMessageType, Int32) {
+ return try concreteProtocol.readMessageBegin()
+ }
+
+ public func readMessageEnd() throws {
+ try concreteProtocol.readMessageEnd()
+ }
+
+ public func readStructBegin() throws -> String {
+ return try concreteProtocol.readStructBegin()
+ }
+
+ public func readStructEnd() throws {
+ try concreteProtocol.readStructEnd()
+ }
+
+ public func readFieldBegin() throws -> (String, TType, Int32) {
+ return try concreteProtocol.readFieldBegin()
+ }
+
+ public func readFieldEnd() throws {
+ try concreteProtocol.readFieldEnd()
+ }
+
+ public func readMapBegin() throws -> (TType, TType, Int32) {
+ return try concreteProtocol.readMapBegin()
+ }
+
+ public func readMapEnd() throws {
+ try concreteProtocol.readMapEnd()
+ }
+
+ public func readSetBegin() throws -> (TType, Int32) {
+ return try concreteProtocol.readSetBegin()
+ }
+
+ public func readSetEnd() throws {
+ try concreteProtocol.readSetEnd()
+ }
+
+ public func readListBegin() throws -> (TType, Int32) {
+ return try concreteProtocol.readListBegin()
+ }
+
+ public func readListEnd() throws {
+ try concreteProtocol.readListEnd()
+ }
+
+ public func read() throws -> String {
+ return try concreteProtocol.read()
+ }
+
+ public func read() throws -> Bool {
+ return try concreteProtocol.read()
+ }
+
+ public func read() throws -> UInt8 {
+ return try concreteProtocol.read()
+ }
+
+ public func read() throws -> Int16 {
+ return try concreteProtocol.read()
+ }
+
+ public func read() throws -> Int32 {
+ return try concreteProtocol.read()
+ }
+
+ public func read() throws -> Int64 {
+ return try concreteProtocol.read()
+ }
+
+ public func read() throws -> Double {
+ return try concreteProtocol.read()
+ }
+
+ public func read() throws -> Data {
+ return try concreteProtocol.read()
+ }
+
+ // Write methods
+
+ public func writeMessageBegin(name: String, type messageType: TMessageType, sequenceID: Int32) throws {
+ return try concreteProtocol.writeMessageBegin(name: name, type: messageType, sequenceID: sequenceID)
+ }
+
+ public func writeMessageEnd() throws {
+ try concreteProtocol.writeMessageEnd()
+ }
+
+ public func writeStructBegin(name: String) throws {
+ try concreteProtocol.writeStructBegin(name: name)
+ }
+
+ public func writeStructEnd() throws {
+ try concreteProtocol.writeStructEnd()
+ }
+
+ public func writeFieldBegin(name: String, type fieldType: TType, fieldID: Int32) throws {
+ try concreteProtocol.writeFieldBegin(name: name, type: fieldType, fieldID: fieldID)
+ }
+
+ public func writeFieldStop() throws {
+ try concreteProtocol.writeFieldStop()
+ }
+
+ public func writeFieldEnd() throws {
+ try concreteProtocol.writeFieldEnd()
+ }
+
+ public func writeMapBegin(keyType: TType, valueType: TType, size: Int32) throws {
+ try concreteProtocol.writeMapBegin(keyType: keyType, valueType: valueType, size: size)
+ }
+
+ public func writeMapEnd() throws {
+ try concreteProtocol.writeMapEnd()
+ }
+
+ public func writeSetBegin(elementType: TType, size: Int32) throws {
+ try concreteProtocol.writeSetBegin(elementType: elementType, size: size)
+ }
+
+ public func writeSetEnd() throws {
+ try concreteProtocol.writeSetEnd()
+ }
+
+ public func writeListBegin(elementType: TType, size: Int32) throws {
+ try concreteProtocol.writeListBegin(elementType: elementType, size: size)
+ }
+
+ public func writeListEnd() throws {
+ try concreteProtocol.writeListEnd()
+ }
+ public func write(_ value: String) throws {
+ try concreteProtocol.write(value)
+ }
+
+ public func write(_ value: Bool) throws {
+ try concreteProtocol.write(value)
+ }
+
+ public func write(_ value: UInt8) throws {
+ try concreteProtocol.write(value)
+ }
+
+ public func write(_ value: Int16) throws {
+ try concreteProtocol.write(value)
+ }
+
+ public func write(_ value: Int32) throws {
+ try concreteProtocol.write(value)
+ }
+
+ public func write(_ value: Int64) throws {
+ try concreteProtocol.write(value)
+ }
+
+ public func write(_ value: Double) throws {
+ try concreteProtocol.write(value)
+ }
+
+ public func write(_ data: Data) throws {
+ try concreteProtocol.write(data)
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Sources/Thrift.swift b/src/jaegertracing/thrift/lib/swift/Sources/Thrift.swift
new file mode 100644
index 000000000..45c68f2e5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Sources/Thrift.swift
@@ -0,0 +1,3 @@
+class Thrift {
+ let version = "0.13.0"
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Tests/LinuxMain.swift b/src/jaegertracing/thrift/lib/swift/Tests/LinuxMain.swift
new file mode 100644
index 000000000..288fec9e4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Tests/LinuxMain.swift
@@ -0,0 +1,8 @@
+import XCTest
+@testable import ThriftTests
+
+XCTMain([
+ testCase(ThriftTests.allTests),
+ testCase(TBinaryProtocolTests.allTests),
+ testCase(TCompactProtocolTests.allTests),
+])
diff --git a/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/TBinaryProtocolTests.swift b/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/TBinaryProtocolTests.swift
new file mode 100644
index 000000000..56a557261
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/TBinaryProtocolTests.swift
@@ -0,0 +1,168 @@
+//
+// TBinaryProtocolTests.swift
+// Thrift
+//
+// Created by Christopher Simpson on 8/18/16.
+//
+//
+
+import XCTest
+import Foundation
+@testable import Thrift
+
+
+/// Testing Binary protocol read/write against itself
+/// Uses separate read/write transport/protocols
+class TBinaryProtocolTests: XCTestCase {
+ var transport: TMemoryBufferTransport = TMemoryBufferTransport(flushHandler: {
+ $0.reset(readBuffer: $1)
+ })
+
+ var proto: TBinaryProtocol!
+
+ override func setUp() {
+ super.setUp()
+ proto = TBinaryProtocol(on: transport)
+ transport.reset()
+ }
+
+ override func tearDown() {
+ super.tearDown()
+ transport.reset()
+ }
+
+ func testInt8WriteRead() {
+ let writeVal: UInt8 = 250
+ try? proto.write(writeVal)
+ try? transport.flush()
+
+ let readVal: UInt8 = (try? proto.read()) ?? 0
+ XCTAssertEqual(writeVal, readVal, "Error with UInt8, wrote \(writeVal) but read \(readVal)")
+ }
+
+ func testInt16WriteRead() {
+
+ let writeVal: Int16 = 12312
+ try? proto.write(writeVal)
+ try? transport.flush()
+ let readVal: Int16 = (try? proto.read()) ?? 0
+ XCTAssertEqual(writeVal, readVal, "Error with Int16, wrote \(writeVal) but read \(readVal)")
+ }
+
+ func testInt32WriteRead() {
+ let writeVal: Int32 = 2029234
+ try? proto.write(writeVal)
+ try? transport.flush()
+
+ let readVal: Int32 = (try? proto.read()) ?? 0
+ XCTAssertEqual(writeVal, readVal, "Error with Int32, wrote \(writeVal) but read \(readVal)")
+ }
+
+ func testInt64WriteRead() {
+ let writeVal: Int64 = 234234981374134
+ try? proto.write(writeVal)
+ try? transport.flush()
+
+ let readVal: Int64 = (try? proto.read()) ?? 0
+ XCTAssertEqual(writeVal, readVal, "Error with Int64, wrote \(writeVal) but read \(readVal)")
+ }
+
+ func testDoubleWriteRead() {
+ let writeVal: Double = 3.1415926
+ try? proto.write(writeVal)
+ try? transport.flush()
+
+ let readVal: Double = (try? proto.read()) ?? 0.0
+ XCTAssertEqual(writeVal, readVal, "Error with Double, wrote \(writeVal) but read \(readVal)")
+ }
+
+ func testBoolWriteRead() {
+ let writeVal: Bool = true
+ try? proto.write(writeVal)
+ try? transport.flush()
+
+ let readVal: Bool = (try? proto.read()) ?? false
+ XCTAssertEqual(writeVal, readVal, "Error with Bool, wrote \(writeVal) but read \(readVal)")
+ }
+
+ func testStringWriteRead() {
+ let writeVal: String = "Hello World"
+ try? proto.write(writeVal)
+ try? transport.flush()
+
+ let readVal: String!
+ do {
+ readVal = try proto.read()
+ } catch let error {
+ XCTAssertFalse(true, "Error reading \(error)")
+ return
+ }
+
+ XCTAssertEqual(writeVal, readVal, "Error with String, wrote \(writeVal) but read \(readVal)")
+ }
+
+ func testDataWriteRead() {
+ let writeVal: Data = "Data World".data(using: .utf8)!
+ try? proto.write(writeVal)
+ try? transport.flush()
+
+ let readVal: Data = (try? proto.read()) ?? "Goodbye World".data(using: .utf8)!
+ XCTAssertEqual(writeVal, readVal, "Error with Data, wrote \(writeVal) but read \(readVal)")
+ }
+
+ func testStructWriteRead() {
+ let msg = "Test Protocol Error"
+ let writeVal = TApplicationError(error: .protocolError, message: msg)
+ do {
+ try writeVal.write(to: proto)
+ try? transport.flush()
+
+ } catch let error {
+ XCTAssertFalse(true, "Caught Error attempting to write \(error)")
+ }
+
+ do {
+ let readVal = try TApplicationError.read(from: proto)
+ XCTAssertEqual(readVal.error.thriftErrorCode, writeVal.error.thriftErrorCode, "Error case mismatch, expected \(readVal.error) got \(writeVal.error)")
+ XCTAssertEqual(readVal.message, writeVal.message, "Error message mismatch, expected \(readVal.message) got \(writeVal.message)")
+ } catch let error {
+ XCTAssertFalse(true, "Caught Error attempting to read \(error)")
+ }
+ }
+ func testUnsafeBitcastUpdate() {
+ let value: Double = 3.14159
+ let val: Int64 = 31415926
+ let uval: UInt64 = 31415926
+
+ let i64 = Int64(bitPattern: value.bitPattern)
+ let ubc = unsafeBitCast(value, to: Int64.self)
+
+ XCTAssertEqual(i64, ubc, "Bitcast Double-> i64 Values don't match")
+
+ let dbl = Double(bitPattern: UInt64(val))
+ let ubdb = unsafeBitCast(val, to: Double.self)
+
+ XCTAssertEqual(dbl, ubdb, "Bitcast i64 -> Double Values don't match")
+
+ let db2 = Double(bitPattern: uval)
+ let usbc2 = unsafeBitCast(uval, to: Double.self)
+
+ XCTAssertEqual(db2, usbc2, "Bitcast u64 -> Double Values don't match")
+
+
+ }
+
+ static var allTests : [(String, (TBinaryProtocolTests) -> () throws -> Void)] {
+ return [
+ ("testInt8WriteRead", testInt8WriteRead),
+ ("testInt16WriteRead", testInt16WriteRead),
+ ("testInt32WriteRead", testInt32WriteRead),
+ ("testInt64WriteRead", testInt64WriteRead),
+ ("testDoubleWriteRead", testDoubleWriteRead),
+ ("testBoolWriteRead", testBoolWriteRead),
+ ("testStringWriteRead", testStringWriteRead),
+ ("testDataWriteRead", testDataWriteRead),
+ ("testStructWriteRead", testStructWriteRead)
+ ]
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/TCompactProtocolTests.swift b/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/TCompactProtocolTests.swift
new file mode 100644
index 000000000..882c26068
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/TCompactProtocolTests.swift
@@ -0,0 +1,210 @@
+//
+// TCompactProtocolTests.swift
+// Thrift
+//
+// Created by Christopher Simpson on 8/19/16.
+//
+//
+
+import XCTest
+import Foundation
+@testable import Thrift
+
+
+/// Testing Binary protocol read/write against itself
+/// Uses separate read/write transport/protocols
+class TCompactProtocolTests: XCTestCase {
+ var transport: TMemoryBufferTransport = TMemoryBufferTransport(flushHandler: {
+ $0.reset(readBuffer: $1)
+ })
+ var proto: TCompactProtocol!
+
+ override func setUp() {
+ super.setUp()
+ proto = TCompactProtocol(on: transport)
+ transport.reset()
+ }
+
+ override func tearDown() {
+ super.tearDown()
+ transport.reset()
+ }
+
+ func testInt8WriteRead() {
+ let writeVal: UInt8 = 250
+ try? proto.write(writeVal)
+ try? transport.flush()
+ let readVal: UInt8 = (try? proto.read()) ?? 0
+ XCTAssertEqual(writeVal, readVal, "Error with UInt8, wrote \(writeVal) but read \(readVal)")
+ }
+
+ func testInt16WriteRead() {
+ let writeVal: Int16 = 12312
+ try? proto.write(writeVal)
+ try? transport.flush()
+
+ let readVal: Int16 = (try? proto.read()) ?? 0
+ XCTAssertEqual(writeVal, readVal, "Error with Int16, wrote \(writeVal) but read \(readVal)")
+ }
+
+ func testInt32WriteRead() {
+ let writeVal: Int32 = 2029234
+ try? proto.write(writeVal)
+ try? transport.flush()
+
+ let readVal: Int32 = (try? proto.read()) ?? 0
+ XCTAssertEqual(writeVal, readVal, "Error with Int32, wrote \(writeVal) but read \(readVal)")
+ }
+
+ func testInt64WriteRead() {
+ let writeVal: Int64 = 234234981374134
+ try? proto.write(writeVal)
+ try? transport.flush()
+
+ let readVal: Int64 = (try? proto.read()) ?? 0
+ XCTAssertEqual(writeVal, readVal, "Error with Int64, wrote \(writeVal) but read \(readVal)")
+ }
+
+ func testDoubleWriteRead() {
+ let writeVal: Double = 3.1415926
+ try? proto.write(writeVal)
+ try? transport.flush()
+
+ let readVal: Double = (try? proto.read()) ?? 0.0
+ XCTAssertEqual(writeVal, readVal, "Error with Double, wrote \(writeVal) but read \(readVal)")
+ }
+
+ func testBoolWriteRead() {
+ let writeVal: Bool = true
+ try? proto.write(writeVal)
+ try? transport.flush()
+
+ let readVal: Bool = (try? proto.read()) ?? false
+ XCTAssertEqual(writeVal, readVal, "Error with Bool, wrote \(writeVal) but read \(readVal)")
+ }
+
+ func testStringWriteRead() {
+ let writeVal: String = "Hello World"
+ try? proto.write(writeVal)
+ try? transport.flush()
+
+ let readVal: String!
+ do {
+ readVal = try proto.read()
+ } catch let error {
+ XCTAssertFalse(true, "Error reading \(error)")
+ return
+ }
+
+ XCTAssertEqual(writeVal, readVal, "Error with String, wrote \(writeVal) but read \(readVal)")
+ }
+
+ func testDataWriteRead() {
+ let writeVal: Data = "Data World".data(using: .utf8)!
+ try? proto.write(writeVal)
+ try? transport.flush()
+
+ let readVal: Data = (try? proto.read()) ?? "Goodbye World".data(using: .utf8)!
+ XCTAssertEqual(writeVal, readVal, "Error with Data, wrote \(writeVal) but read \(readVal)")
+ }
+
+ func testStructWriteRead() {
+ let msg = "Test Protocol Error"
+ let writeVal = TApplicationError(error: .protocolError, message: msg)
+ do {
+ try writeVal.write(to: proto)
+ try transport.flush()
+
+ } catch let error {
+ XCTAssertFalse(true, "Caught Error attempting to write \(error)")
+ }
+
+ do {
+ let readVal = try TApplicationError.read(from: proto)
+ XCTAssertEqual(readVal.error.thriftErrorCode, writeVal.error.thriftErrorCode, "Error case mismatch, expected \(readVal.error) got \(writeVal.error)")
+ XCTAssertEqual(readVal.message, writeVal.message, "Error message mismatch, expected \(readVal.message) got \(writeVal.message)")
+ } catch let error {
+ XCTAssertFalse(true, "Caught Error attempting to read \(error)")
+ }
+ }
+
+ func testInt32ZigZag() {
+ let zero: Int32 = 0
+ let one: Int32 = 1
+ let nOne: Int32 = -1
+ let two: Int32 = 2
+ let nTwo: Int32 = -2
+ let max = Int32.max
+ let min = Int32.min
+
+ XCTAssertEqual(proto.i32ToZigZag(zero), UInt32(0), "Error 32bit zigzag on \(zero)")
+ XCTAssertEqual(proto.zigZagToi32(0), zero, "Error 32bit zigzag on \(zero)")
+
+ XCTAssertEqual(proto.i32ToZigZag(nOne), UInt32(1), "Error 32bit zigzag on \(nOne)")
+ XCTAssertEqual(proto.zigZagToi32(1), nOne, "Error 32bit zigzag on \(nOne)")
+
+ XCTAssertEqual(proto.i32ToZigZag(one), UInt32(2), "Error 32bit zigzag on \(one)")
+ XCTAssertEqual(proto.zigZagToi32(2), one, "Error 32bit zigzag on \(one)")
+
+ XCTAssertEqual(proto.i32ToZigZag(nTwo), UInt32(3), "Error 32bit zigzag on \(nTwo)")
+ XCTAssertEqual(proto.zigZagToi32(3), nTwo, "Error 32bit zigzag on \(nTwo)")
+
+ XCTAssertEqual(proto.i32ToZigZag(two), UInt32(4), "Error 32bit zigzag on \(two)")
+ XCTAssertEqual(proto.zigZagToi32(4), two, "Error 32bit zigzag on \(two)")
+
+ let uMaxMinusOne: UInt32 = UInt32.max - 1
+ XCTAssertEqual(proto.i32ToZigZag(max), uMaxMinusOne, "Error 32bit zigzag on \(max)")
+ XCTAssertEqual(proto.zigZagToi32(uMaxMinusOne), max, "Error 32bit zigzag on \(max)")
+
+ XCTAssertEqual(proto.i32ToZigZag(min), UInt32.max, "Error 32bit zigzag on \(min)")
+ XCTAssertEqual(proto.zigZagToi32(UInt32.max), min, "Error 32bit zigzag on \(min)")
+ }
+
+ func testInt64ZigZag() {
+ let zero: Int64 = 0
+ let one: Int64 = 1
+ let nOne: Int64 = -1
+ let two: Int64 = 2
+ let nTwo: Int64 = -2
+ let max = Int64.max
+ let min = Int64.min
+
+ XCTAssertEqual(proto.i64ToZigZag(zero), UInt64(0), "Error 64bit zigzag on \(zero)")
+ XCTAssertEqual(proto.zigZagToi64(0), zero, "Error 64bit zigzag on \(zero)")
+
+ XCTAssertEqual(proto.i64ToZigZag(nOne), UInt64(1), "Error 64bit zigzag on \(nOne)")
+ XCTAssertEqual(proto.zigZagToi64(1), nOne, "Error 64bit zigzag on \(nOne)")
+
+ XCTAssertEqual(proto.i64ToZigZag(one), UInt64(2), "Error 64bit zigzag on \(one)")
+ XCTAssertEqual(proto.zigZagToi64(2), one, "Error 64bit zigzag on \(one)")
+
+ XCTAssertEqual(proto.i64ToZigZag(nTwo), UInt64(3), "Error 64bit zigzag on \(nTwo)")
+ XCTAssertEqual(proto.zigZagToi64(3), nTwo, "Error 64bit zigzag on \(nTwo)")
+
+ XCTAssertEqual(proto.i64ToZigZag(two), UInt64(4), "Error 64bit zigzag on \(two)")
+ XCTAssertEqual(proto.zigZagToi64(4), two, "Error 64bit zigzag on \(two)")
+
+ let uMaxMinusOne: UInt64 = UInt64.max - 1
+ XCTAssertEqual(proto.i64ToZigZag(max), uMaxMinusOne, "Error 64bit zigzag on \(max)")
+ XCTAssertEqual(proto.zigZagToi64(uMaxMinusOne), max, "Error 64bit zigzag on \(max)")
+
+ XCTAssertEqual(proto.i64ToZigZag(min), UInt64.max, "Error 64bit zigzag on \(min)")
+ XCTAssertEqual(proto.zigZagToi64(UInt64.max), min, "Error 64bit zigzag on \(min)")
+ }
+
+ static var allTests : [(String, (TCompactProtocolTests) -> () throws -> Void)] {
+ return [
+ ("testInt8WriteRead", testInt8WriteRead),
+ ("testInt16WriteRead", testInt16WriteRead),
+ ("testInt32WriteRead", testInt32WriteRead),
+ ("testInt64WriteRead", testInt64WriteRead),
+ ("testDoubleWriteRead", testDoubleWriteRead),
+ ("testBoolWriteRead", testBoolWriteRead),
+ ("testStringWriteRead", testStringWriteRead),
+ ("testDataWriteRead", testDataWriteRead),
+ ("testStructWriteRead", testStructWriteRead),
+ ("testInt32ZigZag", testInt32ZigZag),
+ ("testInt64ZigZag", testInt64ZigZag)
+ ]
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/ThriftTests.swift b/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/ThriftTests.swift
new file mode 100644
index 000000000..ae47f388c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/ThriftTests.swift
@@ -0,0 +1,18 @@
+import XCTest
+@testable import Thrift
+
+class ThriftTests: XCTestCase {
+ func testVersion() {
+ XCTAssertEqual(Thrift().version, "0.13.0")
+ }
+
+ func test_in_addr_extension() {
+
+ }
+
+ static var allTests : [(String, (ThriftTests) -> () throws -> Void)] {
+ return [
+ ("testVersion", testVersion),
+ ]
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/ts/.gitignore b/src/jaegertracing/thrift/lib/ts/.gitignore
new file mode 100644
index 000000000..24f250ed0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/.gitignore
@@ -0,0 +1,2 @@
+test/build/
+test/gen-*
diff --git a/src/jaegertracing/thrift/lib/ts/Gruntfile.js b/src/jaegertracing/thrift/lib/ts/Gruntfile.js
new file mode 100644
index 000000000..fcd79f8e5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/Gruntfile.js
@@ -0,0 +1,163 @@
+//To build dist/thrift.js, dist/thrift.min.js and doc/*
+//run grunt at the command line in this directory.
+//Prerequisites:
+// Node Setup - nodejs.org
+// Grunt Setup - npm install //reads the ./package.json and installs project dependencies
+// Run grunt - npx grunt // uses project-local installed version of grunt (from package.json)
+
+module.exports = function(grunt) {
+ 'use strict';
+
+ grunt.initConfig({
+ pkg: grunt.file.readJSON('package.json'),
+ concat: {
+ options: {
+ separator: ';'
+ },
+ dist: {
+ src: ['src/**/*.js'],
+ dest: 'dist/<%= pkg.name %>.js'
+ }
+ },
+ shell: {
+ InstallThriftJS: {
+ command: 'mkdir -p test/build/ts/lib; cp ../js/src/thrift.js test/build/ts/thrift.js'
+ },
+ InstallThriftNodeJSDep: {
+ command: 'cd ../..; npm install'
+ },
+ InstallTestLibs: {
+ command: 'cd test; ant download_jslibs'
+ },
+ ThriftGen: {
+ command: [
+ 'mkdir -p test/gen-js',
+ '../../compiler/cpp/thrift -gen js:ts --out test/gen-js ../../test/ThriftTest.thrift',
+ 'mkdir -p test/gen-nodejs',
+ '../../compiler/cpp/thrift -gen js:node,ts --out test/gen-nodejs ../../test/ThriftTest.thrift',
+ ].join(' && ')
+ },
+ ThriftBrowserifyNodeInt64: {
+ command: [
+ './node_modules/browserify/bin/cmd.js ./node_modules/node-int64/Int64.js -s Int64 -o test/build/js/lib/Int64.js',
+ './node_modules/browserify/bin/cmd.js ../nodejs/lib/thrift/int64_util.js -s Int64Util -o test/build/js/lib/Int64Util.js',
+ './node_modules/browserify/bin/cmd.js ./node_modules/json-int64/index.js -s JSONInt64 -o test/build/js/lib/JSONInt64.js'
+ ].join(' && ')
+ },
+ ThriftGenInt64: {
+ command: '../../compiler/cpp/thrift -gen js:ts -o test ../../test/Int64Test.thrift'
+ },
+ ThriftTestServer: {
+ options: {
+ async: true,
+ execOptions: {
+ cwd: "./test",
+ env: {NODE_PATH: "../../nodejs/lib:../../../node_modules"}
+ }
+ },
+ command: "node server_http.js",
+ },
+ BuildTS: {
+ options: {
+ execOptions: {
+ cwd: "./test",
+ }
+ },
+ command : "../node_modules/typescript/bin/tsc --listFiles --outDir build/ts"
+ },
+ BrowserifyCompiledTS: {
+ command: [
+ "./node_modules/browserify/bin/cmd.js test/build/ts/test.js -o test/build/ts/lib/test.js --standalone test",
+ "./node_modules/browserify/bin/cmd.js test/build/ts/test-int64.js -o test/build/ts/lib/test-int64.js --standalone testInt64",
+ ].join(" && ")
+ },
+ InstallGeneratedCode: {
+ command: [
+ "mkdir -p test/build/ts",
+ "cp -r test/gen-js test/build/ts"
+ ].join(" && ")
+ },
+ },
+ qunit: {
+ ThriftJS: {
+ options: {
+ urls: [
+ 'http://localhost:8089/test.html'
+ ],
+ puppeteer: {
+ headless: true,
+ args: ['--no-sandbox'],
+ },
+ }
+ },
+ ThriftJS_Int64: {
+ options: {
+ urls: [
+ 'http://localhost:8089/test-int64.html'
+ ],
+ puppeteer: {
+ headless: true,
+ args: ['--no-sandbox'],
+ ignoreHTTPSErrors: true,
+ },
+ }
+ },
+ },
+ jshint: {
+ // The main Thrift library file. not es6 yet :(
+ lib: {
+ src: ['../js/src/**/*.js'],
+ },
+ // The test files use es6
+ test: {
+ src: ['Gruntfile.js', 'test/*.js'],
+ options: {
+ esversion: 6,
+ }
+ },
+ gen_js_code: {
+ src: ['test/gen-js/*.js'],
+ },
+ gen_node_code: {
+ src: ['test/gen-nodejs/*.js'],
+ options: {
+ node: true,
+ }
+ },
+ },
+ });
+
+ grunt.loadNpmTasks('grunt-contrib-jshint');
+ grunt.loadNpmTasks('grunt-contrib-qunit');
+ grunt.loadNpmTasks('grunt-shell-spawn');
+
+ grunt.registerTask('wait', 'Wait just one second for the server to start', function () {
+ var done = this.async();
+ setTimeout(function() {
+ done(true);
+ }, 1000);
+ });
+
+ grunt.registerTask('installAndGenerate', [
+ 'shell:InstallThriftJS',
+ 'shell:InstallThriftNodeJSDep',
+ 'shell:ThriftGen',
+ 'shell:ThriftBrowserifyNodeInt64',
+ 'shell:ThriftGenInt64',
+ 'shell:InstallTestLibs',
+ 'shell:BuildTS',
+ 'shell:InstallGeneratedCode',
+ 'shell:BrowserifyCompiledTS',
+ ]);
+
+ grunt.registerTask('test', [
+ 'installAndGenerate',
+ 'jshint',
+ 'shell:ThriftTestServer',
+ 'wait',
+ 'qunit:ThriftJS',
+ 'qunit:ThriftJS_Int64',
+ 'shell:ThriftTestServer:kill',
+ ]);
+ grunt.registerTask('default', ['test']);
+};
diff --git a/src/jaegertracing/thrift/lib/ts/Makefile.am b/src/jaegertracing/thrift/lib/ts/Makefile.am
new file mode 100644
index 000000000..62ea2069f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/Makefile.am
@@ -0,0 +1,58 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Make sure this doesn't fail if ant is not configured.
+# We call install twice to work around npm issues
+#
+if HAVE_NPM
+
+prereq:
+ $(NPM) install || $(NPM) install
+ $(NPM) list
+
+check-local: prereq all
+ ./node_modules/.bin/grunt
+
+doc: prereq
+ ./node_modules/.bin/grunt jsdoc
+
+endif
+
+clean-local:
+ $(RM) -r dist
+ $(RM) -r doc
+ $(RM) -r node_modules
+ $(RM) -r test/build/
+ $(RM) -r test/gen-*/
+
+dist-hook:
+ $(RM) -r $(distdir)/dist/
+ $(RM) -r $(distdir)/doc/
+ $(RM) -r $(distdir)/node_modules/
+ $(RM) -r $(distdir)/test/build/
+ $(RM) -r $(distdir)/test/gen-*/
+
+EXTRA_DIST = \
+ coding_standards.md \
+ Gruntfile.js \
+ package.json \
+ package-lock.json \
+ thrift.d.ts \
+ tsconfig.json
+
diff --git a/src/jaegertracing/thrift/lib/ts/coding_standards.md b/src/jaegertracing/thrift/lib/ts/coding_standards.md
new file mode 100644
index 000000000..fa0390bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/coding_standards.md
@@ -0,0 +1 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
diff --git a/src/jaegertracing/thrift/lib/ts/dist/thrift.js b/src/jaegertracing/thrift/lib/ts/dist/thrift.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/dist/thrift.js
diff --git a/src/jaegertracing/thrift/lib/ts/dist/thrift.min.js b/src/jaegertracing/thrift/lib/ts/dist/thrift.min.js
new file mode 100644
index 000000000..8e14f2d07
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/dist/thrift.min.js
@@ -0,0 +1 @@
+/*! thrift 07-01-2019 */
diff --git a/src/jaegertracing/thrift/lib/ts/package-lock.json b/src/jaegertracing/thrift/lib/ts/package-lock.json
new file mode 100644
index 000000000..8d0a7ff2f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/package-lock.json
@@ -0,0 +1,4955 @@
+{
+ "name": "thrift",
+ "version": "0.13.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@types/node": {
+ "version": "10.12.18",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz",
+ "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==",
+ "dev": true
+ },
+ "@types/node-int64": {
+ "version": "0.4.29",
+ "resolved": "https://registry.npmjs.org/@types/node-int64/-/node-int64-0.4.29.tgz",
+ "integrity": "sha512-rHXvenLTj/CcsmNAebaBOhxQ2MqEGl3yXZZcZ21XYR+gzGTTcpOy2N4IxpvTCz48loyQNatHvfn6GhIbbZ1R3Q==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/phantom": {
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/@types/phantom/-/phantom-3.2.5.tgz",
+ "integrity": "sha512-7m36DoKSvZgBGWp0xiJ74eHnuotyrpDyQ6m+lers5iMvW4QX+RvBENn7PCjNix7OVqPWlBM+7AqzYVIQ7NrKrA==",
+ "dev": true
+ },
+ "@types/qunit": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/@types/qunit/-/qunit-2.5.4.tgz",
+ "integrity": "sha512-VHi2lEd4/zp8OOouf43JXGJJ5ZxHvdLL1dU0Yakp6Iy73SjpuXl7yjwAwmh1qhTv8krDgHteSwaySr++uXX9YQ==",
+ "dev": true
+ },
+ "JSONStream": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+ "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+ "dev": true,
+ "requires": {
+ "jsonparse": "^1.2.0",
+ "through": ">=2.2.7 <3"
+ }
+ },
+ "abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+ },
+ "acorn": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.4.tgz",
+ "integrity": "sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg==",
+ "dev": true
+ },
+ "acorn-dynamic-import": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz",
+ "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==",
+ "dev": true
+ },
+ "acorn-node": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.6.2.tgz",
+ "integrity": "sha512-rIhNEZuNI8ibQcL7ANm/mGyPukIaZsRNX9psFNQURyJW0nu6k8wjSDld20z6v2mDBWqX13pIEnk9gGZJHIlEXg==",
+ "dev": true,
+ "requires": {
+ "acorn": "^6.0.2",
+ "acorn-dynamic-import": "^4.0.0",
+ "acorn-walk": "^6.1.0",
+ "xtend": "^4.0.1"
+ }
+ },
+ "acorn-walk": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz",
+ "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==",
+ "dev": true
+ },
+ "agent-base": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
+ "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
+ "dev": true,
+ "requires": {
+ "es6-promisify": "^5.0.0"
+ }
+ },
+ "ajv": {
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.7.0.tgz",
+ "integrity": "sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^2.0.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "align-text": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
+ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2",
+ "longest": "^1.0.1",
+ "repeat-string": "^1.5.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ },
+ "dependencies": {
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ }
+ }
+ },
+ "arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+ "dev": true
+ },
+ "arr-flatten": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+ "dev": true
+ },
+ "arr-union": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+ "dev": true
+ },
+ "array-each": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz",
+ "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=",
+ "dev": true
+ },
+ "array-filter": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
+ "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=",
+ "dev": true
+ },
+ "array-find-index": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
+ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
+ "dev": true
+ },
+ "array-map": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
+ "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=",
+ "dev": true
+ },
+ "array-reduce": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
+ "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=",
+ "dev": true
+ },
+ "array-slice": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
+ "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==",
+ "dev": true
+ },
+ "array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+ "dev": true
+ },
+ "asn1": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+ "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "asn1.js": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
+ "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "assert": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
+ "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
+ "dev": true,
+ "requires": {
+ "util": "0.10.3"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+ "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
+ "dev": true
+ },
+ "util": {
+ "version": "0.10.3",
+ "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz",
+ "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.1"
+ }
+ }
+ }
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+ "dev": true
+ },
+ "assign-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+ "dev": true
+ },
+ "async": {
+ "version": "1.5.2",
+ "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
+ "dev": true
+ },
+ "async-limiter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
+ "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
+ "dev": true
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+ "dev": true
+ },
+ "atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "dev": true
+ },
+ "aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+ "dev": true
+ },
+ "aws4": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
+ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
+ "dev": true
+ },
+ "babylon": {
+ "version": "7.0.0-beta.19",
+ "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.19.tgz",
+ "integrity": "sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A=="
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "base": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "dev": true,
+ "requires": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "base64-js": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
+ "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==",
+ "dev": true
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+ "dev": true,
+ "requires": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "bluebird": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz",
+ "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw=="
+ },
+ "bn.js": {
+ "version": "4.11.8",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
+ "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
+ "dev": true
+ },
+ "browser-pack": {
+ "version": "6.1.0",
+ "resolved": "http://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz",
+ "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==",
+ "dev": true,
+ "requires": {
+ "JSONStream": "^1.0.3",
+ "combine-source-map": "~0.8.0",
+ "defined": "^1.0.0",
+ "safe-buffer": "^5.1.1",
+ "through2": "^2.0.0",
+ "umd": "^3.0.0"
+ }
+ },
+ "browser-resolve": {
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz",
+ "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==",
+ "dev": true,
+ "requires": {
+ "resolve": "1.1.7"
+ },
+ "dependencies": {
+ "resolve": {
+ "version": "1.1.7",
+ "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+ "dev": true
+ }
+ }
+ },
+ "browserify": {
+ "version": "16.2.3",
+ "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.2.3.tgz",
+ "integrity": "sha512-zQt/Gd1+W+IY+h/xX2NYMW4orQWhqSwyV+xsblycTtpOuB27h1fZhhNQuipJ4t79ohw4P4mMem0jp/ZkISQtjQ==",
+ "dev": true,
+ "requires": {
+ "JSONStream": "^1.0.3",
+ "assert": "^1.4.0",
+ "browser-pack": "^6.0.1",
+ "browser-resolve": "^1.11.0",
+ "browserify-zlib": "~0.2.0",
+ "buffer": "^5.0.2",
+ "cached-path-relative": "^1.0.0",
+ "concat-stream": "^1.6.0",
+ "console-browserify": "^1.1.0",
+ "constants-browserify": "~1.0.0",
+ "crypto-browserify": "^3.0.0",
+ "defined": "^1.0.0",
+ "deps-sort": "^2.0.0",
+ "domain-browser": "^1.2.0",
+ "duplexer2": "~0.1.2",
+ "events": "^2.0.0",
+ "glob": "^7.1.0",
+ "has": "^1.0.0",
+ "htmlescape": "^1.1.0",
+ "https-browserify": "^1.0.0",
+ "inherits": "~2.0.1",
+ "insert-module-globals": "^7.0.0",
+ "labeled-stream-splicer": "^2.0.0",
+ "mkdirp": "^0.5.0",
+ "module-deps": "^6.0.0",
+ "os-browserify": "~0.3.0",
+ "parents": "^1.0.1",
+ "path-browserify": "~0.0.0",
+ "process": "~0.11.0",
+ "punycode": "^1.3.2",
+ "querystring-es3": "~0.2.0",
+ "read-only-stream": "^2.0.0",
+ "readable-stream": "^2.0.2",
+ "resolve": "^1.1.4",
+ "shasum": "^1.0.0",
+ "shell-quote": "^1.6.1",
+ "stream-browserify": "^2.0.0",
+ "stream-http": "^2.0.0",
+ "string_decoder": "^1.1.1",
+ "subarg": "^1.0.0",
+ "syntax-error": "^1.1.1",
+ "through2": "^2.0.0",
+ "timers-browserify": "^1.0.1",
+ "tty-browserify": "0.0.1",
+ "url": "~0.11.0",
+ "util": "~0.10.1",
+ "vm-browserify": "^1.0.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "browserify-aes": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+ "dev": true,
+ "requires": {
+ "buffer-xor": "^1.0.3",
+ "cipher-base": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.3",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "browserify-cipher": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
+ "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
+ "dev": true,
+ "requires": {
+ "browserify-aes": "^1.0.4",
+ "browserify-des": "^1.0.0",
+ "evp_bytestokey": "^1.0.0"
+ }
+ },
+ "browserify-des": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
+ "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.1",
+ "des.js": "^1.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "browserify-rsa": {
+ "version": "4.0.1",
+ "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+ "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "randombytes": "^2.0.1"
+ }
+ },
+ "browserify-sign": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
+ "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.1",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "create-hmac": "^1.1.2",
+ "elliptic": "^6.0.0",
+ "inherits": "^2.0.1",
+ "parse-asn1": "^5.0.0"
+ }
+ },
+ "browserify-zlib": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+ "dev": true,
+ "requires": {
+ "pako": "~1.0.5"
+ }
+ },
+ "buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz",
+ "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==",
+ "dev": true,
+ "requires": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4"
+ }
+ },
+ "buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+ "dev": true
+ },
+ "buffer-shims": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
+ "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=",
+ "dev": true
+ },
+ "buffer-xor": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
+ "dev": true
+ },
+ "bufferutil": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz",
+ "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==",
+ "requires": {
+ "node-gyp-build": "~3.7.0"
+ }
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+ "dev": true
+ },
+ "builtin-status-codes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
+ "dev": true
+ },
+ "cache-base": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "dev": true,
+ "requires": {
+ "collection-visit": "^1.0.0",
+ "component-emitter": "^1.2.1",
+ "get-value": "^2.0.6",
+ "has-value": "^1.0.0",
+ "isobject": "^3.0.1",
+ "set-value": "^2.0.0",
+ "to-object-path": "^0.3.0",
+ "union-value": "^1.0.0",
+ "unset-value": "^1.0.0"
+ }
+ },
+ "cached-path-relative": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz",
+ "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+ "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
+ "dev": true
+ },
+ "camelcase-keys": {
+ "version": "2.1.0",
+ "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
+ "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
+ "dev": true,
+ "requires": {
+ "camelcase": "^2.0.0",
+ "map-obj": "^1.0.0"
+ }
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+ "dev": true
+ },
+ "catharsis": {
+ "version": "0.8.9",
+ "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz",
+ "integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=",
+ "requires": {
+ "underscore-contrib": "~0.3.0"
+ }
+ },
+ "center-align": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
+ "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
+ "dev": true,
+ "requires": {
+ "align-text": "^0.1.3",
+ "lazy-cache": "^1.0.3"
+ }
+ },
+ "chalk": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
+ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "cipher-base": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "class-utils": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "define-property": "^0.2.5",
+ "isobject": "^3.0.0",
+ "static-extend": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "cli": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz",
+ "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=",
+ "dev": true,
+ "requires": {
+ "exit": "0.1.2",
+ "glob": "^7.1.1"
+ }
+ },
+ "cliui": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
+ "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+ "dev": true,
+ "requires": {
+ "center-align": "^0.1.1",
+ "right-align": "^0.1.1",
+ "wordwrap": "0.0.2"
+ }
+ },
+ "coffeescript": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.10.0.tgz",
+ "integrity": "sha1-56qDAZF+9iGzXYo580jc3R234z4=",
+ "dev": true
+ },
+ "collection-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+ "dev": true,
+ "requires": {
+ "map-visit": "^1.0.0",
+ "object-visit": "^1.0.0"
+ }
+ },
+ "color": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz",
+ "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.1",
+ "color-string": "^1.5.2"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "color-string": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
+ "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
+ "dev": true,
+ "requires": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "colornames": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz",
+ "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.1.2",
+ "resolved": "http://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
+ "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
+ "dev": true
+ },
+ "colorspace": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.1.tgz",
+ "integrity": "sha512-pI3btWyiuz7Ken0BWh9Elzsmv2bM9AhA7psXib4anUXy/orfZ/E0MbQwhSOG/9L8hLlalqrU0UhOuqxW1YjmVw==",
+ "dev": true,
+ "requires": {
+ "color": "3.0.x",
+ "text-hex": "1.0.x"
+ }
+ },
+ "combine-source-map": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz",
+ "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=",
+ "dev": true,
+ "requires": {
+ "convert-source-map": "~1.1.0",
+ "inline-source-map": "~0.6.0",
+ "lodash.memoize": "~3.0.3",
+ "source-map": "~0.5.3"
+ }
+ },
+ "combined-stream": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
+ "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
+ "dev": true,
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ }
+ },
+ "console-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
+ "dev": true,
+ "requires": {
+ "date-now": "^0.1.4"
+ }
+ },
+ "constants-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+ "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "1.1.3",
+ "resolved": "http://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz",
+ "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=",
+ "dev": true
+ },
+ "copy-descriptor": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true
+ },
+ "create-ecdh": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
+ "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "elliptic": "^6.0.0"
+ }
+ },
+ "create-hash": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.1",
+ "inherits": "^2.0.1",
+ "md5.js": "^1.3.4",
+ "ripemd160": "^2.0.1",
+ "sha.js": "^2.4.0"
+ }
+ },
+ "create-hmac": {
+ "version": "1.1.7",
+ "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.3",
+ "create-hash": "^1.1.0",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "crypto-browserify": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
+ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+ "dev": true,
+ "requires": {
+ "browserify-cipher": "^1.0.0",
+ "browserify-sign": "^4.0.0",
+ "create-ecdh": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "create-hmac": "^1.1.0",
+ "diffie-hellman": "^5.0.0",
+ "inherits": "^2.0.1",
+ "pbkdf2": "^3.0.3",
+ "public-encrypt": "^4.0.0",
+ "randombytes": "^2.0.0",
+ "randomfill": "^1.0.3"
+ }
+ },
+ "currently-unhandled": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
+ "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
+ "dev": true,
+ "requires": {
+ "array-find-index": "^1.0.1"
+ }
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "date-now": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+ "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
+ "dev": true
+ },
+ "dateformat": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz",
+ "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=",
+ "dev": true,
+ "requires": {
+ "get-stdin": "^4.0.1",
+ "meow": "^3.3.0"
+ }
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true
+ },
+ "decode-uri-component": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+ "dev": true
+ },
+ "define-property": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.2",
+ "isobject": "^3.0.1"
+ },
+ "dependencies": {
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "defined": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
+ "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
+ "dev": true
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+ "dev": true
+ },
+ "deps-sort": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz",
+ "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=",
+ "dev": true,
+ "requires": {
+ "JSONStream": "^1.0.3",
+ "shasum": "^1.0.0",
+ "subarg": "^1.0.0",
+ "through2": "^2.0.0"
+ }
+ },
+ "des.js": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
+ "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "detect-file": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
+ "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
+ "dev": true
+ },
+ "detective": {
+ "version": "5.1.0",
+ "resolved": "http://registry.npmjs.org/detective/-/detective-5.1.0.tgz",
+ "integrity": "sha512-TFHMqfOvxlgrfVzTEkNBSh9SvSNX/HfF4OFI2QFGCyPm02EsyILqnUeb5P6q7JZ3SFNTBL5t2sePRgrN4epUWQ==",
+ "dev": true,
+ "requires": {
+ "acorn-node": "^1.3.0",
+ "defined": "^1.0.0",
+ "minimist": "^1.1.1"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true
+ }
+ }
+ },
+ "diagnostics": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz",
+ "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==",
+ "dev": true,
+ "requires": {
+ "colorspace": "1.1.x",
+ "enabled": "1.0.x",
+ "kuler": "1.0.x"
+ }
+ },
+ "diffie-hellman": {
+ "version": "5.0.3",
+ "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+ "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "miller-rabin": "^4.0.0",
+ "randombytes": "^2.0.0"
+ }
+ },
+ "dom-serializer": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
+ "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
+ "dev": true,
+ "requires": {
+ "domelementtype": "~1.1.1",
+ "entities": "~1.1.1"
+ },
+ "dependencies": {
+ "domelementtype": {
+ "version": "1.1.3",
+ "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
+ "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=",
+ "dev": true
+ },
+ "entities": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
+ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
+ "dev": true
+ }
+ }
+ },
+ "domain-browser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
+ "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
+ "dev": true
+ },
+ "domelementtype": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
+ "dev": true
+ },
+ "domhandler": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz",
+ "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=",
+ "dev": true,
+ "requires": {
+ "domelementtype": "1"
+ }
+ },
+ "domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
+ "dev": true,
+ "requires": {
+ "dom-serializer": "0",
+ "domelementtype": "1"
+ }
+ },
+ "duplexer2": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+ "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+ "dev": true,
+ "requires": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "elliptic": {
+ "version": "6.4.1",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
+ "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.4.0",
+ "brorand": "^1.0.1",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.0"
+ }
+ },
+ "enabled": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz",
+ "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=",
+ "dev": true,
+ "requires": {
+ "env-variable": "0.0.x"
+ }
+ },
+ "entities": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
+ "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=",
+ "dev": true
+ },
+ "env-variable": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz",
+ "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==",
+ "dev": true
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "es6-promise": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz",
+ "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==",
+ "dev": true
+ },
+ "es6-promisify": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+ "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
+ "dev": true,
+ "requires": {
+ "es6-promise": "^4.0.3"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
+ },
+ "esprima": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
+ "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
+ "dev": true
+ },
+ "eventemitter2": {
+ "version": "0.4.14",
+ "resolved": "http://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
+ "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=",
+ "dev": true
+ },
+ "events": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-2.1.0.tgz",
+ "integrity": "sha512-3Zmiobend8P9DjmKAty0Era4jV8oJ0yGYe2nJJAxgymF9+N8F2m0hhZiMoWtcfepExzNKZumFU3ksdQbInGWCg==",
+ "dev": true
+ },
+ "evp_bytestokey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+ "dev": true,
+ "requires": {
+ "md5.js": "^1.3.4",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+ "dev": true
+ },
+ "expand-brackets": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+ "dev": true,
+ "requires": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "expand-tilde": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
+ "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
+ "dev": true,
+ "requires": {
+ "homedir-polyfill": "^1.0.1"
+ }
+ },
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "extglob": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "dev": true,
+ "requires": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "extract-zip": {
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz",
+ "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=",
+ "dev": true,
+ "requires": {
+ "concat-stream": "1.6.2",
+ "debug": "2.6.9",
+ "mkdirp": "0.5.1",
+ "yauzl": "2.4.1"
+ }
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+ "dev": true
+ },
+ "fast-deep-equal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
+ "dev": true
+ },
+ "fast-safe-stringify": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz",
+ "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==",
+ "dev": true
+ },
+ "fd-slicer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
+ "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
+ "dev": true,
+ "requires": {
+ "pend": "~1.2.0"
+ }
+ },
+ "fecha": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz",
+ "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==",
+ "dev": true
+ },
+ "figures": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
+ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5",
+ "object-assign": "^4.1.0"
+ }
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "find-up": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+ "dev": true,
+ "requires": {
+ "path-exists": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "findup-sync": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz",
+ "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=",
+ "dev": true,
+ "requires": {
+ "glob": "~5.0.0"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "5.0.15",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+ "dev": true,
+ "requires": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "fined": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.1.tgz",
+ "integrity": "sha512-jQp949ZmEbiYHk3gkbdtpJ0G1+kgtLQBNdP5edFP7Fh+WAYceLQz6yO1SBj72Xkg8GVyTB3bBzAYrHJVh5Xd5g==",
+ "dev": true,
+ "requires": {
+ "expand-tilde": "^2.0.2",
+ "is-plain-object": "^2.0.3",
+ "object.defaults": "^1.1.0",
+ "object.pick": "^1.2.0",
+ "parse-filepath": "^1.0.1"
+ }
+ },
+ "flagged-respawn": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz",
+ "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==",
+ "dev": true
+ },
+ "for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+ "dev": true
+ },
+ "for-own": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
+ "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
+ "dev": true,
+ "requires": {
+ "for-in": "^1.0.1"
+ }
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+ "dev": true
+ },
+ "form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "fragment-cache": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+ "dev": true,
+ "requires": {
+ "map-cache": "^0.2.2"
+ }
+ },
+ "fs-extra": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz",
+ "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^2.1.0",
+ "klaw": "^1.0.0"
+ },
+ "dependencies": {
+ "klaw": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
+ "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.9"
+ }
+ }
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "get-assigned-identifiers": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz",
+ "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==",
+ "dev": true
+ },
+ "get-stdin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
+ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
+ "dev": true
+ },
+ "get-value": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+ "dev": true
+ },
+ "getobject": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz",
+ "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=",
+ "dev": true
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "global-modules": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
+ "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
+ "dev": true,
+ "requires": {
+ "global-prefix": "^1.0.1",
+ "is-windows": "^1.0.1",
+ "resolve-dir": "^1.0.0"
+ }
+ },
+ "global-prefix": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
+ "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
+ "dev": true,
+ "requires": {
+ "expand-tilde": "^2.0.2",
+ "homedir-polyfill": "^1.0.1",
+ "ini": "^1.3.4",
+ "is-windows": "^1.0.1",
+ "which": "^1.2.14"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.1.15",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA=="
+ },
+ "grunt": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.3.tgz",
+ "integrity": "sha512-/JzmZNPfKorlCrrmxWqQO4JVodO+DVd5XX4DkocL/1WlLlKVLE9+SdEIempOAxDhWPysLle6afvn/hg7Ck2k9g==",
+ "dev": true,
+ "requires": {
+ "coffeescript": "~1.10.0",
+ "dateformat": "~1.0.12",
+ "eventemitter2": "~0.4.13",
+ "exit": "~0.1.1",
+ "findup-sync": "~0.3.0",
+ "glob": "~7.0.0",
+ "grunt-cli": "~1.2.0",
+ "grunt-known-options": "~1.1.0",
+ "grunt-legacy-log": "~2.0.0",
+ "grunt-legacy-util": "~1.1.1",
+ "iconv-lite": "~0.4.13",
+ "js-yaml": "~3.5.2",
+ "minimatch": "~3.0.2",
+ "mkdirp": "~0.5.1",
+ "nopt": "~3.0.6",
+ "path-is-absolute": "~1.0.0",
+ "rimraf": "~2.6.2"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
+ "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.2",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "grunt-cli": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz",
+ "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=",
+ "dev": true,
+ "requires": {
+ "findup-sync": "~0.3.0",
+ "grunt-known-options": "~1.1.0",
+ "nopt": "~3.0.6",
+ "resolve": "~1.1.0"
+ }
+ },
+ "nopt": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+ "dev": true,
+ "requires": {
+ "abbrev": "1"
+ }
+ },
+ "resolve": {
+ "version": "1.1.7",
+ "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-cli": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.3.2.tgz",
+ "integrity": "sha512-8OHDiZZkcptxVXtMfDxJvmN7MVJNE8L/yIcPb4HB7TlyFD1kDvjHrb62uhySsU14wJx9ORMnTuhRMQ40lH/orQ==",
+ "dev": true,
+ "requires": {
+ "grunt-known-options": "~1.1.0",
+ "interpret": "~1.1.0",
+ "liftoff": "~2.5.0",
+ "nopt": "~4.0.1",
+ "v8flags": "~3.1.1"
+ }
+ },
+ "grunt-contrib-concat": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-concat/-/grunt-contrib-concat-1.0.1.tgz",
+ "integrity": "sha1-YVCYYwhOhx1+ht5IwBUlntl3Rb0=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.0.0",
+ "source-map": "^0.5.3"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz",
+ "integrity": "sha1-Np2QmyWTxA6L55lAshNAhQx5Oaw=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.1.1",
+ "hooker": "^0.2.3",
+ "jshint": "~2.9.4"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-contrib-qunit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-qunit/-/grunt-contrib-qunit-3.1.0.tgz",
+ "integrity": "sha512-mdk8UltH6mxCD63E0hTXMAts42DOi4z4bBBrY7qnuHiShflMF7IueSMYe0zWaZ2dO8mgujh57Zfny2EbigJhRg==",
+ "dev": true,
+ "requires": {
+ "eventemitter2": "^5.0.1",
+ "p-each-series": "^1.0.0",
+ "puppeteer": "^1.11.0"
+ },
+ "dependencies": {
+ "eventemitter2": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz",
+ "integrity": "sha1-YZegldX7a1folC9v1+qtY6CclFI=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-contrib-uglify": {
+ "version": "1.0.2",
+ "resolved": "http://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-1.0.2.tgz",
+ "integrity": "sha1-rmekb5FT7dTLEYE6Vetpxw19svs=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.0.0",
+ "lodash": "^4.0.1",
+ "maxmin": "^1.1.0",
+ "uglify-js": "~2.6.2",
+ "uri-path": "^1.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-jsdoc": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/grunt-jsdoc/-/grunt-jsdoc-2.3.0.tgz",
+ "integrity": "sha512-gC66TCRXeQMj3HIyqVSBJm8zdUz43e5vaG/PLO/627A1edbJnzxhJV7nF0KqLwMM0RDNu1istC6fvfnYqFKi3w==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^6.0.5",
+ "jsdoc": "~3.5.5",
+ "marked": "^0.5.0"
+ },
+ "dependencies": {
+ "marked": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-0.5.2.tgz",
+ "integrity": "sha512-fdZvBa7/vSQIZCi4uuwo2N3q+7jJURpMVCcbaX0S1Mg65WZ5ilXvC67MviJAsdjqqgD+CEq4RKo5AYGgINkVAA==",
+ "dev": true
+ }
+ }
+ },
+ "grunt-known-options": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.1.tgz",
+ "integrity": "sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ==",
+ "dev": true
+ },
+ "grunt-legacy-log": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-2.0.0.tgz",
+ "integrity": "sha512-1m3+5QvDYfR1ltr8hjiaiNjddxGdQWcH0rw1iKKiQnF0+xtgTazirSTGu68RchPyh1OBng1bBUjLmX8q9NpoCw==",
+ "dev": true,
+ "requires": {
+ "colors": "~1.1.2",
+ "grunt-legacy-log-utils": "~2.0.0",
+ "hooker": "~0.2.3",
+ "lodash": "~4.17.5"
+ }
+ },
+ "grunt-legacy-log-utils": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.0.1.tgz",
+ "integrity": "sha512-o7uHyO/J+i2tXG8r2bZNlVk20vlIFJ9IEYyHMCQGfWYru8Jv3wTqKZzvV30YW9rWEjq0eP3cflQ1qWojIe9VFA==",
+ "dev": true,
+ "requires": {
+ "chalk": "~2.4.1",
+ "lodash": "~4.17.10"
+ }
+ },
+ "grunt-legacy-util": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.1.1.tgz",
+ "integrity": "sha512-9zyA29w/fBe6BIfjGENndwoe1Uy31BIXxTH3s8mga0Z5Bz2Sp4UCjkeyv2tI449ymkx3x26B+46FV4fXEddl5A==",
+ "dev": true,
+ "requires": {
+ "async": "~1.5.2",
+ "exit": "~0.1.1",
+ "getobject": "~0.1.0",
+ "hooker": "~0.2.3",
+ "lodash": "~4.17.10",
+ "underscore.string": "~3.3.4",
+ "which": "~1.3.0"
+ }
+ },
+ "grunt-shell-spawn": {
+ "version": "0.3.12",
+ "resolved": "https://registry.npmjs.org/grunt-shell-spawn/-/grunt-shell-spawn-0.3.12.tgz",
+ "integrity": "sha512-TprZct92sQ4M2Q92piaeLsCrx4+gq/ageuxjZsRG6cglKt7x7rGA3YHt8D30+G789v+/pw4l0tDjEyrkMXx2tA==",
+ "dev": true,
+ "requires": {
+ "grunt": ">=0.4.x"
+ }
+ },
+ "gzip-size": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-1.0.0.tgz",
+ "integrity": "sha1-Zs+LEBBHInuVus5uodoMF37Vwi8=",
+ "dev": true,
+ "requires": {
+ "browserify-zlib": "^0.1.4",
+ "concat-stream": "^1.4.1"
+ },
+ "dependencies": {
+ "browserify-zlib": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz",
+ "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=",
+ "dev": true,
+ "requires": {
+ "pako": "~0.2.0"
+ }
+ },
+ "pako": {
+ "version": "0.2.9",
+ "resolved": "http://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
+ "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=",
+ "dev": true
+ }
+ }
+ },
+ "har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+ "dev": true
+ },
+ "har-validator": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
+ "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.5.5",
+ "har-schema": "^2.0.0"
+ }
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "has-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.6",
+ "has-values": "^1.0.0",
+ "isobject": "^3.0.0"
+ }
+ },
+ "has-values": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "kind-of": "^4.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "hash-base": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
+ "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "hash.js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "hasha": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/hasha/-/hasha-2.2.0.tgz",
+ "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=",
+ "dev": true,
+ "requires": {
+ "is-stream": "^1.0.1",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+ "dev": true,
+ "requires": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "homedir-polyfill": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz",
+ "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=",
+ "dev": true,
+ "requires": {
+ "parse-passwd": "^1.0.0"
+ }
+ },
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz",
+ "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=",
+ "dev": true
+ },
+ "hosted-git-info": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
+ "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
+ "dev": true
+ },
+ "htmlescape": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz",
+ "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=",
+ "dev": true
+ },
+ "htmlparser2": {
+ "version": "3.8.3",
+ "resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
+ "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=",
+ "dev": true,
+ "requires": {
+ "domelementtype": "1",
+ "domhandler": "2.3",
+ "domutils": "1.5",
+ "entities": "1.0",
+ "readable-stream": "1.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "1.1.14",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+ "dev": true
+ }
+ }
+ },
+ "http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ }
+ },
+ "https-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
+ "dev": true
+ },
+ "https-proxy-agent": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz",
+ "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==",
+ "dev": true,
+ "requires": {
+ "agent-base": "^4.1.0",
+ "debug": "^3.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ }
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ieee754": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz",
+ "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==",
+ "dev": true
+ },
+ "indent-string": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
+ "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
+ "dev": true,
+ "requires": {
+ "repeating": "^2.0.0"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ },
+ "ini": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+ "dev": true
+ },
+ "inline-source-map": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz",
+ "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=",
+ "dev": true,
+ "requires": {
+ "source-map": "~0.5.3"
+ }
+ },
+ "insert-module-globals": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.0.tgz",
+ "integrity": "sha512-VE6NlW+WGn2/AeOMd496AHFYmE7eLKkUY6Ty31k4og5vmA3Fjuwe9v6ifH6Xx/Hz27QvdoMoviw1/pqWRB09Sw==",
+ "dev": true,
+ "requires": {
+ "JSONStream": "^1.0.3",
+ "acorn-node": "^1.5.2",
+ "combine-source-map": "^0.8.0",
+ "concat-stream": "^1.6.1",
+ "is-buffer": "^1.1.0",
+ "path-is-absolute": "^1.0.1",
+ "process": "~0.11.0",
+ "through2": "^2.0.0",
+ "undeclared-identifiers": "^1.1.2",
+ "xtend": "^4.0.0"
+ }
+ },
+ "interpret": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
+ "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=",
+ "dev": true
+ },
+ "is-absolute": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
+ "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
+ "dev": true,
+ "requires": {
+ "is-relative": "^1.0.0",
+ "is-windows": "^1.0.1"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "is-builtin-module": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
+ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
+ "dev": true,
+ "requires": {
+ "builtin-modules": "^1.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-finite": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
+ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "is-relative": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
+ "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
+ "dev": true,
+ "requires": {
+ "is-unc-path": "^1.0.0"
+ }
+ },
+ "is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+ "dev": true
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+ "dev": true
+ },
+ "is-unc-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
+ "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
+ "dev": true,
+ "requires": {
+ "unc-path-regex": "^0.1.2"
+ }
+ },
+ "is-utf8": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
+ "dev": true
+ },
+ "is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.5.5",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.5.5.tgz",
+ "integrity": "sha1-A3fDgBfKvHMisNH7zSWkkWQfL74=",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.2",
+ "esprima": "^2.6.0"
+ }
+ },
+ "js2xmlparser": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-3.0.0.tgz",
+ "integrity": "sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=",
+ "requires": {
+ "xmlcreate": "^1.0.1"
+ }
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+ "dev": true
+ },
+ "jsdoc": {
+ "version": "3.5.5",
+ "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.5.tgz",
+ "integrity": "sha512-6PxB65TAU4WO0Wzyr/4/YhlGovXl0EVYfpKbpSroSj0qBxT4/xod/l40Opkm38dRHRdQgdeY836M0uVnJQG7kg==",
+ "requires": {
+ "babylon": "7.0.0-beta.19",
+ "bluebird": "~3.5.0",
+ "catharsis": "~0.8.9",
+ "escape-string-regexp": "~1.0.5",
+ "js2xmlparser": "~3.0.0",
+ "klaw": "~2.0.0",
+ "marked": "~0.3.6",
+ "mkdirp": "~0.5.1",
+ "requizzle": "~0.2.1",
+ "strip-json-comments": "~2.0.1",
+ "taffydb": "2.6.2",
+ "underscore": "~1.8.3"
+ }
+ },
+ "jshint": {
+ "version": "2.9.7",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.7.tgz",
+ "integrity": "sha512-Q8XN38hGsVQhdlM+4gd1Xl7OB1VieSuCJf+fEJjpo59JH99bVJhXRXAh26qQ15wfdd1VPMuDWNeSWoNl53T4YA==",
+ "dev": true,
+ "requires": {
+ "cli": "~1.0.0",
+ "console-browserify": "1.1.x",
+ "exit": "0.1.x",
+ "htmlparser2": "3.8.x",
+ "lodash": "~4.17.10",
+ "minimatch": "~3.0.2",
+ "shelljs": "0.3.x",
+ "strip-json-comments": "1.0.x"
+ },
+ "dependencies": {
+ "strip-json-comments": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
+ "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=",
+ "dev": true
+ }
+ }
+ },
+ "jslint": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/jslint/-/jslint-0.12.0.tgz",
+ "integrity": "sha512-RoCsyICcKA+6TFsbys9DpKTfPVaC71Mm5QSjvrWA0lDVN+LIvx6apa42FFisMqmCTvJ8DxkcoQGJ0j7m3kTVow==",
+ "dev": true,
+ "requires": {
+ "exit": "~0.1.2",
+ "glob": "~7.1.2",
+ "nopt": "~3.0.1",
+ "readable-stream": "~2.1.5"
+ },
+ "dependencies": {
+ "nopt": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+ "dev": true,
+ "requires": {
+ "abbrev": "1"
+ }
+ },
+ "process-nextick-args": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+ "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "2.1.5",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz",
+ "integrity": "sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA=",
+ "dev": true,
+ "requires": {
+ "buffer-shims": "^1.0.0",
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~1.0.6",
+ "string_decoder": "~0.10.x",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+ "dev": true
+ }
+ }
+ },
+ "json-int64": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-int64/-/json-int64-1.0.0.tgz",
+ "integrity": "sha512-yrTg9swToElhEPETLMdZkEzDhbXLs+cxkw/b2rglMPOBlM1DE0utH1EReSMLcnpYJk5iUvD12r0fP2/xHitF5Q==",
+ "requires": {
+ "node-int64": "0.4.0"
+ }
+ },
+ "json-schema": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz",
+ "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=",
+ "dev": true,
+ "requires": {
+ "jsonify": "~0.0.0"
+ }
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+ "dev": true
+ },
+ "jsonfile": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
+ "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "jsonify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
+ "dev": true
+ },
+ "jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
+ "dev": true
+ },
+ "jsprim": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+ "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.2.3",
+ "verror": "1.10.0"
+ }
+ },
+ "kew": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz",
+ "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+ "dev": true
+ },
+ "klaw": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/klaw/-/klaw-2.0.0.tgz",
+ "integrity": "sha1-WcEo4Nxc5BAgEVEZTuucv4WGUPY=",
+ "requires": {
+ "graceful-fs": "^4.1.9"
+ }
+ },
+ "kuler": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz",
+ "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==",
+ "dev": true,
+ "requires": {
+ "colornames": "^1.1.1"
+ }
+ },
+ "labeled-stream-splicer": {
+ "version": "2.0.1",
+ "resolved": "http://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.1.tgz",
+ "integrity": "sha512-MC94mHZRvJ3LfykJlTUipBqenZz1pacOZEMhhQ8dMGcDHs0SBE5GbsavUXV7YtP3icBW17W0Zy1I0lfASmo9Pg==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "isarray": "^2.0.4",
+ "stream-splicer": "^2.0.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.4.tgz",
+ "integrity": "sha512-GMxXOiUirWg1xTKRipM0Ek07rX+ubx4nNVElTJdNLYmNO/2YrDkgJGw9CljXn+r4EWiDQg/8lsRdHyg2PJuUaA==",
+ "dev": true
+ }
+ }
+ },
+ "lazy-cache": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
+ "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=",
+ "dev": true
+ },
+ "liftoff": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz",
+ "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=",
+ "dev": true,
+ "requires": {
+ "extend": "^3.0.0",
+ "findup-sync": "^2.0.0",
+ "fined": "^1.0.1",
+ "flagged-respawn": "^1.0.0",
+ "is-plain-object": "^2.0.4",
+ "object.map": "^1.0.0",
+ "rechoir": "^0.6.2",
+ "resolve": "^1.1.7"
+ },
+ "dependencies": {
+ "findup-sync": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz",
+ "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=",
+ "dev": true,
+ "requires": {
+ "detect-file": "^1.0.0",
+ "is-glob": "^3.1.0",
+ "micromatch": "^3.0.4",
+ "resolve-dir": "^1.0.1"
+ }
+ }
+ }
+ },
+ "load-json-file": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "strip-bom": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.11",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+ "dev": true
+ },
+ "lodash.memoize": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz",
+ "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=",
+ "dev": true
+ },
+ "logform": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/logform/-/logform-1.10.0.tgz",
+ "integrity": "sha512-em5ojIhU18fIMOw/333mD+ZLE2fis0EzXl1ZwHx4iQzmpQi6odNiY/t+ITNr33JZhT9/KEaH+UPIipr6a9EjWg==",
+ "dev": true,
+ "requires": {
+ "colors": "^1.2.1",
+ "fast-safe-stringify": "^2.0.4",
+ "fecha": "^2.3.3",
+ "ms": "^2.1.1",
+ "triple-beam": "^1.2.0"
+ },
+ "dependencies": {
+ "colors": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz",
+ "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ }
+ }
+ },
+ "longest": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
+ "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
+ "dev": true
+ },
+ "loud-rejection": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
+ "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
+ "dev": true,
+ "requires": {
+ "currently-unhandled": "^0.4.1",
+ "signal-exit": "^3.0.0"
+ }
+ },
+ "make-iterator": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
+ "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.2"
+ }
+ },
+ "map-cache": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+ "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+ "dev": true
+ },
+ "map-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+ "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
+ "dev": true
+ },
+ "map-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+ "dev": true,
+ "requires": {
+ "object-visit": "^1.0.0"
+ }
+ },
+ "marked": {
+ "version": "0.3.19",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz",
+ "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg=="
+ },
+ "maxmin": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-1.1.0.tgz",
+ "integrity": "sha1-cTZehKmd2Piz99X94vANHn9zvmE=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.0.0",
+ "figures": "^1.0.1",
+ "gzip-size": "^1.0.0",
+ "pretty-bytes": "^1.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "md5.js": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+ "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+ "dev": true,
+ "requires": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "meow": {
+ "version": "3.7.0",
+ "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+ "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
+ "dev": true,
+ "requires": {
+ "camelcase-keys": "^2.0.0",
+ "decamelize": "^1.1.2",
+ "loud-rejection": "^1.0.0",
+ "map-obj": "^1.0.1",
+ "minimist": "^1.1.3",
+ "normalize-package-data": "^2.3.4",
+ "object-assign": "^4.0.1",
+ "read-pkg-up": "^1.0.1",
+ "redent": "^1.0.0",
+ "trim-newlines": "^1.0.0"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true
+ }
+ }
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ }
+ },
+ "miller-rabin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.0.0",
+ "brorand": "^1.0.1"
+ }
+ },
+ "mime": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz",
+ "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==",
+ "dev": true
+ },
+ "mime-db": {
+ "version": "1.37.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
+ "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.21",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
+ "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
+ "dev": true,
+ "requires": {
+ "mime-db": "~1.37.0"
+ }
+ },
+ "minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "dev": true
+ },
+ "minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ },
+ "mixin-deep": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
+ "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
+ "dev": true,
+ "requires": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "module-deps": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.0.tgz",
+ "integrity": "sha512-hKPmO06so6bL/ZvqVNVqdTVO8UAYsi3tQWlCa+z9KuWhoN4KDQtb5hcqQQv58qYiDE21wIvnttZEPiDgEbpwbA==",
+ "dev": true,
+ "requires": {
+ "JSONStream": "^1.0.3",
+ "browser-resolve": "^1.7.0",
+ "cached-path-relative": "^1.0.0",
+ "concat-stream": "~1.6.0",
+ "defined": "^1.0.0",
+ "detective": "^5.0.2",
+ "duplexer2": "^0.1.2",
+ "inherits": "^2.0.1",
+ "parents": "^1.0.0",
+ "readable-stream": "^2.0.2",
+ "resolve": "^1.4.0",
+ "stream-combiner2": "^1.1.1",
+ "subarg": "^1.0.0",
+ "through2": "^2.0.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "nanomatch": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "fragment-cache": "^0.2.1",
+ "is-windows": "^1.0.2",
+ "kind-of": "^6.0.2",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ }
+ },
+ "nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
+ },
+ "node-gyp-build": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz",
+ "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w=="
+ },
+ "node-int64": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+ "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs="
+ },
+ "nopt": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
+ "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
+ "requires": {
+ "abbrev": "1",
+ "osenv": "^0.1.4"
+ }
+ },
+ "normalize-package-data": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "is-builtin-module": "^1.0.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+ "dev": true
+ },
+ "oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true
+ },
+ "object-copy": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+ "dev": true,
+ "requires": {
+ "copy-descriptor": "^0.1.0",
+ "define-property": "^0.2.5",
+ "kind-of": "^3.0.3"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "object-visit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.0"
+ }
+ },
+ "object.defaults": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz",
+ "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=",
+ "dev": true,
+ "requires": {
+ "array-each": "^1.0.1",
+ "array-slice": "^1.0.0",
+ "for-own": "^1.0.0",
+ "isobject": "^3.0.0"
+ }
+ },
+ "object.map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
+ "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=",
+ "dev": true,
+ "requires": {
+ "for-own": "^1.0.0",
+ "make-iterator": "^1.0.0"
+ }
+ },
+ "object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "one-time": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz",
+ "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=",
+ "dev": true
+ },
+ "os-browserify": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
+ "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
+ "dev": true
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
+ },
+ "osenv": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+ "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "p-each-series": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz",
+ "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=",
+ "dev": true,
+ "requires": {
+ "p-reduce": "^1.0.0"
+ }
+ },
+ "p-reduce": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz",
+ "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=",
+ "dev": true
+ },
+ "pako": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.7.tgz",
+ "integrity": "sha512-3HNK5tW4x8o5mO8RuHZp3Ydw9icZXx0RANAOMzlMzx7LVXhMJ4mo3MOBpzyd7r/+RUu8BmndP47LXT+vzjtWcQ==",
+ "dev": true
+ },
+ "parents": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz",
+ "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=",
+ "dev": true,
+ "requires": {
+ "path-platform": "~0.11.15"
+ }
+ },
+ "parse-asn1": {
+ "version": "5.1.1",
+ "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz",
+ "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==",
+ "dev": true,
+ "requires": {
+ "asn1.js": "^4.0.0",
+ "browserify-aes": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.0",
+ "pbkdf2": "^3.0.3"
+ }
+ },
+ "parse-filepath": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz",
+ "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=",
+ "dev": true,
+ "requires": {
+ "is-absolute": "^1.0.0",
+ "map-cache": "^0.2.0",
+ "path-root": "^0.1.1"
+ }
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.2.0"
+ }
+ },
+ "parse-passwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
+ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
+ "dev": true
+ },
+ "pascalcase": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+ "dev": true
+ },
+ "path-browserify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
+ "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+ "dev": true,
+ "requires": {
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "path-platform": {
+ "version": "0.11.15",
+ "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz",
+ "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=",
+ "dev": true
+ },
+ "path-root": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz",
+ "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=",
+ "dev": true,
+ "requires": {
+ "path-root-regex": "^0.1.0"
+ }
+ },
+ "path-root-regex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz",
+ "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=",
+ "dev": true
+ },
+ "path-type": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "pbkdf2": {
+ "version": "3.0.17",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
+ "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
+ "dev": true,
+ "requires": {
+ "create-hash": "^1.1.2",
+ "create-hmac": "^1.1.4",
+ "ripemd160": "^2.0.1",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
+ "dev": true
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+ "dev": true
+ },
+ "phantom": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/phantom/-/phantom-6.0.3.tgz",
+ "integrity": "sha512-8bb8urWoUiZ0E+JC4goaYBDPxljTnnxGwogz5cvash2SQovf//QAPoshXQz06kY/tpI+5caBVng0K0oZkVMNIQ==",
+ "dev": true,
+ "requires": {
+ "phantomjs-prebuilt": "^2.1.16",
+ "split": "^1.0.1",
+ "winston": "^3.0.0"
+ }
+ },
+ "phantomjs-prebuilt": {
+ "version": "2.1.16",
+ "resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.16.tgz",
+ "integrity": "sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=",
+ "dev": true,
+ "requires": {
+ "es6-promise": "^4.0.3",
+ "extract-zip": "^1.6.5",
+ "fs-extra": "^1.0.0",
+ "hasha": "^2.2.0",
+ "kew": "^0.7.0",
+ "progress": "^1.1.8",
+ "request": "^2.81.0",
+ "request-progress": "^2.0.1",
+ "which": "^1.2.10"
+ },
+ "dependencies": {
+ "progress": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz",
+ "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=",
+ "dev": true
+ }
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "dev": true,
+ "requires": {
+ "pinkie": "^2.0.0"
+ }
+ },
+ "posix-character-classes": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+ "dev": true
+ },
+ "pretty-bytes": {
+ "version": "1.0.4",
+ "resolved": "http://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz",
+ "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=",
+ "dev": true,
+ "requires": {
+ "get-stdin": "^4.0.1",
+ "meow": "^3.1.0"
+ }
+ },
+ "process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
+ "dev": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
+ "dev": true
+ },
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true
+ },
+ "proxy-from-env": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz",
+ "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=",
+ "dev": true
+ },
+ "psl": {
+ "version": "1.1.31",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz",
+ "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==",
+ "dev": true
+ },
+ "public-encrypt": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
+ "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "parse-asn1": "^5.0.0",
+ "randombytes": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+ "dev": true
+ },
+ "puppeteer": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.11.0.tgz",
+ "integrity": "sha512-iG4iMOHixc2EpzqRV+pv7o3GgmU2dNYEMkvKwSaQO/vMZURakwSOn/EYJ6OIRFYOque1qorzIBvrytPIQB3YzQ==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "extract-zip": "^1.6.6",
+ "https-proxy-agent": "^2.2.1",
+ "mime": "^2.0.3",
+ "progress": "^2.0.1",
+ "proxy-from-env": "^1.0.0",
+ "rimraf": "^2.6.1",
+ "ws": "^6.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ }
+ }
+ },
+ "qs": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+ "dev": true
+ },
+ "querystring": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+ "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
+ "dev": true
+ },
+ "querystring-es3": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
+ "dev": true
+ },
+ "randombytes": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz",
+ "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "randomfill": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
+ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.0.5",
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "read-only-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz",
+ "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "read-pkg": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^1.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^1.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+ "dev": true,
+ "requires": {
+ "find-up": "^1.0.0",
+ "read-pkg": "^1.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ },
+ "dependencies": {
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "rechoir": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
+ "dev": true,
+ "requires": {
+ "resolve": "^1.1.6"
+ }
+ },
+ "redent": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
+ "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
+ "dev": true,
+ "requires": {
+ "indent-string": "^2.1.0",
+ "strip-indent": "^1.0.1"
+ }
+ },
+ "regex-not": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "repeat-element": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+ "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
+ "dev": true
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+ "dev": true
+ },
+ "repeating": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+ "dev": true,
+ "requires": {
+ "is-finite": "^1.0.0"
+ }
+ },
+ "request": {
+ "version": "2.88.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
+ "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
+ "dev": true,
+ "requires": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "har-validator": "~5.1.0",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "oauth-sign": "~0.9.0",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.4.3",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ }
+ },
+ "request-progress": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz",
+ "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=",
+ "dev": true,
+ "requires": {
+ "throttleit": "^1.0.0"
+ }
+ },
+ "requizzle": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz",
+ "integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=",
+ "requires": {
+ "underscore": "~1.6.0"
+ },
+ "dependencies": {
+ "underscore": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
+ "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
+ }
+ }
+ },
+ "resolve": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz",
+ "integrity": "sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "resolve-dir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
+ "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
+ "dev": true,
+ "requires": {
+ "expand-tilde": "^2.0.0",
+ "global-modules": "^1.0.0"
+ }
+ },
+ "resolve-url": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+ "dev": true
+ },
+ "ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "dev": true
+ },
+ "right-align": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
+ "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
+ "dev": true,
+ "requires": {
+ "align-text": "^0.1.1"
+ }
+ },
+ "rimraf": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
+ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.0.5"
+ }
+ },
+ "ripemd160": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
+ "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
+ "dev": true,
+ "requires": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "safe-regex": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+ "dev": true,
+ "requires": {
+ "ret": "~0.1.10"
+ }
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
+ "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
+ "dev": true
+ },
+ "set-value": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
+ "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "sha.js": {
+ "version": "2.4.11",
+ "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "shasum": {
+ "version": "1.0.2",
+ "resolved": "http://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz",
+ "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=",
+ "dev": true,
+ "requires": {
+ "json-stable-stringify": "~0.0.0",
+ "sha.js": "~2.4.4"
+ }
+ },
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+ "dev": true
+ },
+ "shell-quote": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz",
+ "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=",
+ "dev": true,
+ "requires": {
+ "array-filter": "~0.0.0",
+ "array-map": "~0.0.0",
+ "array-reduce": "~0.0.0",
+ "jsonify": "~0.0.0"
+ }
+ },
+ "shelljs": {
+ "version": "0.3.0",
+ "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz",
+ "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=",
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+ "dev": true
+ },
+ "simple-concat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
+ "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=",
+ "dev": true
+ },
+ "simple-swizzle": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+ "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.3.1"
+ },
+ "dependencies": {
+ "is-arrayish": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
+ "dev": true
+ }
+ }
+ },
+ "snapdragon": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "dev": true,
+ "requires": {
+ "base": "^0.11.1",
+ "debug": "^2.2.0",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "map-cache": "^0.2.2",
+ "source-map": "^0.5.6",
+ "source-map-resolve": "^0.5.0",
+ "use": "^3.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "snapdragon-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.0",
+ "snapdragon-util": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "snapdragon-util": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.2.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ },
+ "source-map-resolve": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
+ "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==",
+ "dev": true,
+ "requires": {
+ "atob": "^2.1.1",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "source-map-url": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
+ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
+ "dev": true
+ },
+ "spdx-correct": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
+ "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
+ "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+ "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz",
+ "integrity": "sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg==",
+ "dev": true
+ },
+ "split": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
+ "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
+ "dev": true,
+ "requires": {
+ "through": "2"
+ }
+ },
+ "split-string": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.0"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
+ "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
+ "dev": true
+ },
+ "sshpk": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz",
+ "integrity": "sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ==",
+ "dev": true,
+ "requires": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ }
+ },
+ "stack-trace": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+ "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=",
+ "dev": true
+ },
+ "static-extend": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+ "dev": true,
+ "requires": {
+ "define-property": "^0.2.5",
+ "object-copy": "^0.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "stream-browserify": {
+ "version": "2.0.1",
+ "resolved": "http://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz",
+ "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=",
+ "dev": true,
+ "requires": {
+ "inherits": "~2.0.1",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "stream-combiner2": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz",
+ "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=",
+ "dev": true,
+ "requires": {
+ "duplexer2": "~0.1.0",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "stream-http": {
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz",
+ "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==",
+ "dev": true,
+ "requires": {
+ "builtin-status-codes": "^3.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.3.6",
+ "to-arraybuffer": "^1.0.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "stream-splicer": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz",
+ "integrity": "sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "string_decoder": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz",
+ "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-bom": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+ "dev": true,
+ "requires": {
+ "is-utf8": "^0.2.0"
+ }
+ },
+ "strip-indent": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
+ "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
+ "dev": true,
+ "requires": {
+ "get-stdin": "^4.0.1"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
+ },
+ "subarg": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz",
+ "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.1.0"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true
+ }
+ }
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "syntax-error": {
+ "version": "1.4.0",
+ "resolved": "http://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz",
+ "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==",
+ "dev": true,
+ "requires": {
+ "acorn-node": "^1.2.0"
+ }
+ },
+ "taffydb": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz",
+ "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg="
+ },
+ "text-hex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
+ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
+ "dev": true
+ },
+ "throttleit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz",
+ "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=",
+ "dev": true
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+ "dev": true
+ },
+ "through2": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dev": true,
+ "requires": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ }
+ },
+ "timers-browserify": {
+ "version": "1.4.2",
+ "resolved": "http://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz",
+ "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=",
+ "dev": true,
+ "requires": {
+ "process": "~0.11.0"
+ }
+ },
+ "to-arraybuffer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
+ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=",
+ "dev": true
+ },
+ "to-object-path": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "to-regex": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "regex-not": "^1.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ },
+ "tough-cookie": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
+ "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
+ "dev": true,
+ "requires": {
+ "psl": "^1.1.24",
+ "punycode": "^1.4.1"
+ }
+ },
+ "trim-newlines": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
+ "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
+ "dev": true
+ },
+ "triple-beam": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
+ "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==",
+ "dev": true
+ },
+ "tty-browserify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz",
+ "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==",
+ "dev": true
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+ "dev": true
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+ "dev": true
+ },
+ "typescript": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.4.tgz",
+ "integrity": "sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==",
+ "dev": true
+ },
+ "uglify-js": {
+ "version": "2.6.4",
+ "resolved": "http://registry.npmjs.org/uglify-js/-/uglify-js-2.6.4.tgz",
+ "integrity": "sha1-ZeovswWck5RpLxX+2HwrNsFrmt8=",
+ "dev": true,
+ "requires": {
+ "async": "~0.2.6",
+ "source-map": "~0.5.1",
+ "uglify-to-browserify": "~1.0.0",
+ "yargs": "~3.10.0"
+ },
+ "dependencies": {
+ "async": {
+ "version": "0.2.10",
+ "resolved": "http://registry.npmjs.org/async/-/async-0.2.10.tgz",
+ "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=",
+ "dev": true
+ }
+ }
+ },
+ "uglify-to-browserify": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
+ "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
+ "dev": true
+ },
+ "umd": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz",
+ "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==",
+ "dev": true
+ },
+ "unc-path-regex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
+ "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=",
+ "dev": true
+ },
+ "undeclared-identifiers": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.2.tgz",
+ "integrity": "sha512-13EaeocO4edF/3JKime9rD7oB6QI8llAGhgn5fKOPyfkJbRb6NFv9pYV6dFEmpa4uRjKeBqLZP8GpuzqHlKDMQ==",
+ "dev": true,
+ "requires": {
+ "acorn-node": "^1.3.0",
+ "get-assigned-identifiers": "^1.2.0",
+ "simple-concat": "^1.0.0",
+ "xtend": "^4.0.1"
+ }
+ },
+ "underscore": {
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
+ "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI="
+ },
+ "underscore-contrib": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz",
+ "integrity": "sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=",
+ "requires": {
+ "underscore": "1.6.0"
+ },
+ "dependencies": {
+ "underscore": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
+ "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
+ }
+ }
+ },
+ "underscore.string": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz",
+ "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "^1.0.3",
+ "util-deprecate": "^1.0.2"
+ }
+ },
+ "union-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
+ "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^0.4.3"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "set-value": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz",
+ "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.1",
+ "to-object-path": "^0.3.0"
+ }
+ }
+ }
+ },
+ "unset-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+ "dev": true,
+ "requires": {
+ "has-value": "^0.3.1",
+ "isobject": "^3.0.0"
+ },
+ "dependencies": {
+ "has-value": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.3",
+ "has-values": "^0.1.4",
+ "isobject": "^2.0.0"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "dev": true,
+ "requires": {
+ "isarray": "1.0.0"
+ }
+ }
+ }
+ },
+ "has-values": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+ "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+ "dev": true
+ }
+ }
+ },
+ "uri-js": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ }
+ }
+ },
+ "uri-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/uri-path/-/uri-path-1.0.0.tgz",
+ "integrity": "sha1-l0fwGDWJM8Md4PzP2C0TjmcmLjI=",
+ "dev": true
+ },
+ "urix": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+ "dev": true
+ },
+ "url": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+ "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+ "dev": true,
+ "requires": {
+ "punycode": "1.3.2",
+ "querystring": "0.2.0"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+ "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
+ "dev": true
+ }
+ }
+ },
+ "use": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+ "dev": true
+ },
+ "util": {
+ "version": "0.10.4",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
+ "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "uuid": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+ "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
+ "dev": true
+ },
+ "v8flags": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.2.tgz",
+ "integrity": "sha512-MtivA7GF24yMPte9Rp/BWGCYQNaUj86zeYxV/x2RRJMKagImbbv3u8iJC57lNhWLPcGLJmHcHmFWkNsplbbLWw==",
+ "dev": true,
+ "requires": {
+ "homedir-polyfill": "^1.0.1"
+ }
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "vm-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz",
+ "integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==",
+ "dev": true
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "window-size": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
+ "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
+ "dev": true
+ },
+ "winston": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/winston/-/winston-3.1.0.tgz",
+ "integrity": "sha512-FsQfEE+8YIEeuZEYhHDk5cILo1HOcWkGwvoidLrDgPog0r4bser1lEIOco2dN9zpDJ1M88hfDgZvxe5z4xNcwg==",
+ "dev": true,
+ "requires": {
+ "async": "^2.6.0",
+ "diagnostics": "^1.1.1",
+ "is-stream": "^1.1.0",
+ "logform": "^1.9.1",
+ "one-time": "0.0.4",
+ "readable-stream": "^2.3.6",
+ "stack-trace": "0.0.x",
+ "triple-beam": "^1.3.0",
+ "winston-transport": "^4.2.0"
+ },
+ "dependencies": {
+ "async": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
+ "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.10"
+ }
+ }
+ }
+ },
+ "winston-transport": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz",
+ "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.3.6",
+ "triple-beam": "^1.2.0"
+ }
+ },
+ "wordwrap": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+ "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
+ "dev": true
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "ws": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.3.tgz",
+ "integrity": "sha512-tbSxiT+qJI223AP4iLfQbkbxkwdFcneYinM2+x46Gx2wgvbaOMO36czfdfVUBRTHvzAMRhDd98sA5d/BuWbQdg==",
+ "dev": true,
+ "requires": {
+ "async-limiter": "~1.0.0"
+ }
+ },
+ "xmlcreate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz",
+ "integrity": "sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8="
+ },
+ "xtend": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
+ "dev": true
+ },
+ "yargs": {
+ "version": "3.10.0",
+ "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
+ "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
+ "dev": true,
+ "requires": {
+ "camelcase": "^1.0.2",
+ "cliui": "^2.1.0",
+ "decamelize": "^1.0.0",
+ "window-size": "0.1.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+ "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
+ "dev": true
+ }
+ }
+ },
+ "yauzl": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz",
+ "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=",
+ "dev": true,
+ "requires": {
+ "fd-slicer": "~1.0.1"
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/ts/package.json b/src/jaegertracing/thrift/lib/ts/package.json
new file mode 100644
index 000000000..eda1c0aea
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "thrift",
+ "version": "0.13.0",
+ "description": "Thrift is a software framework for scalable cross-language services development.",
+ "author": {
+ "name": "Apache Thrift Developers",
+ "email": "dev@thrift.apache.org"
+ },
+ "bugs": "https://issues.apache.org/jira/projects/THRIFT/summary",
+ "homepage": "http://thrift.apache.org",
+ "repository": "https://github.com/apache/thrift",
+ "license": "Apache-2.0",
+ "devDependencies": {
+ "@types/node-int64": "^0.4.29",
+ "@types/phantom": "^3.2.5",
+ "@types/qunit": "^2.5.4",
+ "browserify": "^16.2.3",
+ "bufferutil": "^4.0.1",
+ "grunt": "^1.0.3",
+ "grunt-cli": "^1.2.0",
+ "grunt-contrib-concat": "^1.0.1",
+ "grunt-contrib-jshint": "^1.0.0",
+ "grunt-contrib-qunit": "^3.1.0",
+ "grunt-contrib-uglify": "^1.0.1",
+ "grunt-jsdoc": "^2.2.1",
+ "grunt-shell-spawn": "^0.3.12",
+ "jslint": "^0.12.0",
+ "node-int64": "^0.4.0",
+ "phantom": "^6.0.3",
+ "typescript": "^3.2.4"
+ },
+ "dependencies": {
+ "bufferutil": "^4.0.1",
+ "jsdoc": "^3.5.5",
+ "json-int64": "^1.0.0",
+ "nopt": "^4.0.1"
+ },
+ "types": "./thrift.d.ts"
+}
diff --git a/src/jaegertracing/thrift/lib/ts/test/build.xml b/src/jaegertracing/thrift/lib/ts/test/build.xml
new file mode 100755
index 000000000..5c3a4a87b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/test/build.xml
@@ -0,0 +1,250 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="Java Script Test" default="test" basedir="."
+ xmlns:artifact="antlib:org.apache.maven.artifact.ant"
+ xmlns:jsl="antlib:com.googlecode.jslint4java">
+
+ <description>Java Script Test based on Thrift Java Library</description>
+
+ <property name="src" location="src" />
+ <property name="genjava" location="gen-java" />
+ <property name="genjs" location="gen-js" />
+ <property name="build" location="build" />
+ <property name="jar.file" location="${build}/jstest.jar" />
+
+ <!-- the root directory, where you unpack thrift distibution (e.g.: thrift-0.x.x.tar.gz) -->
+ <property name="thrift.dir" location="../../../" />
+ <property name="thrift.java.dir" location="${thrift.dir}/lib/java" />
+ <property name="build.tools.dir" location="${thrift.java.dir}/build/tools/"/>
+ <property file="${basedir}/build.properties"/>
+
+ <!-- Include the base Java properties file -->
+ <property file="${thrift.java.dir}/gradle.properties" />
+
+ <property name="thrift.compiler" location="${thrift.dir}/compiler/cpp/thrift" />
+
+ <path id="libs.classpath">
+ <fileset dir="${thrift.java.dir}/build/libs">
+ <include name="libthrift*.jar" />
+ <exclude name="libthrift*javadoc.jar" />
+ <exclude name="libthrift*sources.jar" />
+ </fileset>
+ <fileset dir="${thrift.java.dir}/build/deps">
+ <include name="*.jar" />
+ </fileset>
+ <fileset dir="${build}/lib">
+ <include name="*.jar" />
+ </fileset>
+ </path>
+
+ <path id="test.classpath">
+ <path refid="libs.classpath" />
+ <pathelement location="${jar.file}" />
+ </path>
+
+ <target name="dependencies">
+ <fail>
+ <condition>
+ <not>
+ <resourcecount count="2">
+ <fileset id="fs" dir="${thrift.java.dir}/build/libs">
+ <include name="libthrift*.jar" />
+ <exclude name="libthrift*javadoc.jar" />
+ <exclude name="libthrift*sources.jar" />
+ </fileset>
+ </resourcecount>
+ </not>
+ </condition>
+ You need libthrift*.jar and libthrift*test.jar located at
+ ${thrift.java.dir}/build/libs
+ Did you compile Thrift Java library and its test suite by "ant compile-test"?
+ </fail>
+ <fail>
+ <condition>
+ <not>
+ <resourcecount count="1">
+ <fileset id="fs" dir="${thrift.dir}" includes="compiler/cpp/thrift"/>
+ </resourcecount>
+ </not>
+ </condition>
+ Thrift compiler is missing !
+ </fail>
+ </target>
+
+ <target name="init" depends="dependencies">
+ <tstamp />
+ <mkdir dir="${build.tools.dir}"/>
+ <mkdir dir="${build}"/>
+ <mkdir dir="${build}/js/lib"/>
+ <mkdir dir="${build}/lib"/>
+ <mkdir dir="${build}/log"/>
+ <mkdir dir="${build}/test"/>
+ <mkdir dir="${build}/test/log"/>
+ </target>
+
+ <target name="download_jslibs">
+ <get src="http://code.jquery.com/jquery-1.11.3.min.js" dest="${build}/js/lib/jquery.js" usetimestamp="true"/>
+ <get src="http://code.jquery.com/qunit/qunit-2.6.2.js" dest="${build}/js/lib/qunit.js" usetimestamp="true"/>
+ <get src="http://code.jquery.com/qunit/qunit-2.6.2.css" dest="${build}/js/lib/qunit.css" usetimestamp="true"/>
+ <get src="http://code.jquery.com/qunit/qunit-2.6.2.js" dest="${build}/ts/qunit.js" usetimestamp="true"/>
+ <get src="http://code.jquery.com/qunit/qunit-2.6.2.css" dest="${build}/ts/qunit.css" usetimestamp="true"/>
+ </target>
+
+ <target name="jslibs" depends="init, proxy, download_jslibs">
+ </target>
+
+ <target name="compile" description="compile the test suite" depends="init, generate, resolve">
+ <!-- //TODO enable <compilerarg value="-Xlint"/>-->
+ <javac compiler="modern" includeantruntime="false" srcdir="${genjava}" destdir="${build}/test" classpathref="libs.classpath"/>
+ <javac compiler="modern" includeantruntime="false" srcdir="${src}" destdir="${build}/test" classpathref="libs.classpath"/>
+ </target>
+
+ <target name="jstest" description="create the test suite jar file" depends="compile">
+ <jar jarfile="${jar.file}" basedir="${build}/test"/>
+ </target>
+
+ <target name="testserver" description="run the test server" depends="jstest, jslibs">
+ <java classname="test.Httpd" fork="true"
+ classpathref="test.classpath" failonerror="true">
+ <arg value="../" />
+ </java>
+ </target>
+
+ <target name="proxy" if="proxy.enabled">
+ <setproxy proxyhost="${proxy.host}" proxyport="${proxy.port}"
+ proxyuser="${proxy.user}" proxypassword="${proxy.pass}"/>
+ </target>
+
+ <target name="xvfb">
+ <echo>check if Xvfb is available:</echo>
+ <exec executable="Xvfb" failifexecutionfails="no" resultproperty="xvfb.present" failonerror="false" output="${build}/log/xvfb.log">
+ <arg line="--version"/>
+ </exec>
+ </target>
+
+ <target name="phantomjs" depends="xvfb" if="xvfb.present">
+ <echo>check if phantomjs is available:</echo>
+ <exec executable="phantomjs" failifexecutionfails="no" resultproperty="phantomjs.present" failonerror="false" output="${build}/log/phantomjs.log">
+ <arg line="--version"/>
+ </exec>
+ </target>
+
+ <target name="unittest" description="do unit tests with headless browser phantomjs" depends="init, phantomjs, jstest, jslibs" if="phantomjs.present">
+ <parallel>
+ <exec executable="Xvfb" spawn="true" failonerror="false">
+ <arg line=":99" />
+ </exec>
+ <java classname="test.Httpd" fork="true" timeout="10000"
+ classpathref="test.classpath" failonerror="false" output="${build}/log/unittest.log">
+ <arg value="../" />
+ </java>
+ <sequential>
+ <sleep seconds="2"/>
+ <echo>Running Unit Tests with headless browser!</echo>
+ <exec executable="phantomjs" failonerror="true">
+ <env key="DISPLAY" value=":99"/>
+ <arg line="phantomjs-qunit.js http://localhost:8088/test/test.html" />
+ </exec>
+ </sequential>
+ </parallel>
+ </target>
+
+ <target name="generate">
+ <exec executable="${thrift.compiler}" failonerror="true">
+ <arg line="--gen java ${thrift.dir}/test/ThriftTest.thrift" />
+ </exec>
+ <exec executable="${thrift.compiler}" failonerror="true">
+ <arg line="--gen js:jquery ${thrift.dir}/test/ThriftTest.thrift" />
+ </exec>
+ <exec executable="${thrift.compiler}" failonerror="true">
+ <arg line="--gen js:jquery ${thrift.dir}/test/DoubleConstantsTest.thrift" />
+ </exec>
+ </target>
+
+ <target name="test" description="run test suite (lint, unittest)" depends="lint, unittest"/>
+
+ <target name="lint" description="code quality checks (jslint and gjslint if available)" depends="generate, gjslint, jslint"/>
+
+ <target name="jslint" depends="resolve">
+ <taskdef uri="antlib:com.googlecode.jslint4java" resource="com/googlecode/jslint4java/antlib.xml" classpathref="libs.classpath" />
+ <!--
+ the following options would probably make sense in the future:
+ browser,undef,eqeqeq,plusplus,bitwise,regexp,strict,newcap,immed
+ -->
+ <jsl:jslint options="evil,forin,browser,bitwise,regexp,newcap,immed" encoding="UTF-8">
+ <formatter type="plain" />
+ <fileset dir="../src" includes="thrift.js" />
+
+ <!-- issues with unsafe character -->
+ <!-- fileset dir="." includes="*test*.js" /> -->
+ </jsl:jslint>
+ </target>
+
+ <target name="check-gjslint">
+ <echo>check if gjslint is available:</echo>
+ <exec executable="gjslint" failifexecutionfails="no" resultproperty="gjslint.present" failonerror="false">
+ <arg line="--helpshort"/>
+ </exec>
+ </target>
+
+ <target name="gjslint" depends="check-gjslint" if="gjslint.present">
+ <exec executable="gjslint" failifexecutionfails="no">
+ <arg line="--nojsdoc"/>
+ <arg line="${genjs}/*.js"/>
+ <arg line="../src/thrift.js"/>
+
+ <!-- issues with unsafe character, etc. -->
+ <!-- <arg line="*test*.js"/> -->
+ </exec>
+ </target>
+
+ <target name="clean">
+ <delete dir="${build}" />
+ <delete dir="${genjava}" />
+ <delete dir="${genjs}" />
+ </target>
+
+ <target name="mvn.ant.tasks.download" depends="init,mvn.ant.tasks.check" unless="mvn.ant.tasks.found">
+ <get src="${mvn.ant.task.url}/${mvn.ant.task.jar}" dest="${build.tools.dir}/${mvn.ant.task.jar}" usetimestamp="true"/>
+ </target>
+
+ <target name="mvn.ant.tasks.check">
+ <condition property="mvn.ant.tasks.found">
+ <typefound uri="antlib:org.apache.maven.artifact.ant" name="artifact"/>
+ </condition>
+ </target>
+
+ <target name="resolve" depends="mvn.ant.tasks.download" unless="mvn.finished">
+ <typedef uri="antlib:org.apache.maven.artifact.ant" classpath="${thrift.java.dir}/build/tools/${mvn.ant.task.jar}"/>
+
+ <artifact:dependencies filesetId="js.test.dependency.jars">
+ <dependency groupId="org.apache.httpcomponents" artifactId="httpclient" version="4.0.1"/>
+ <dependency groupId="com.googlecode.jslint4java" artifactId="jslint4java-ant" version="1.4.6"/>
+ <dependency groupId="eu.medsea.mimeutil" artifactId="mime-util" version="2.1.3"/>
+ </artifact:dependencies>
+
+ <!-- Copy the dependencies to the build/lib dir -->
+ <copy todir="${build}/lib">
+ <fileset refid="js.test.dependency.jars"/>
+ <mapper type="flatten"/>
+ </copy>
+
+ <property name="mvn.finished" value="true"/>
+ </target>
+</project>
diff --git a/src/jaegertracing/thrift/lib/ts/test/phantom-client.ts b/src/jaegertracing/thrift/lib/ts/test/phantom-client.ts
new file mode 100644
index 000000000..551893725
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/test/phantom-client.ts
@@ -0,0 +1,352 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ /* jshint -W100 */
+
+import { ThriftTest } from "./gen-js/ThriftTest_types";
+import "./gen-js/ThriftTest";
+var Int64 = require("node-int64");
+var phantom = require("phantom");
+
+const int64_2_pow_60: typeof Int64 = new Int64('1000000000000000');
+const int64_minus_2_pow_60: typeof Int64 = new Int64('f000000000000000');
+
+ (function() {
+ 'use strict';
+
+ // Rudimentary test helper functions
+ // TODO: Return error code based on kind of errors rather than throw
+ var ok = function(t, msg) {
+ if (!t) {
+ console.log('*** FAILED ***');
+ throw new Error(msg);
+ }
+ };
+ var equal = function(a, b) {
+ if (a !== b) {
+ console.log('*** FAILED ***');
+ throw new Error();
+ }
+ };
+ var test = function(name, f) {
+ console.log('TEST : ' + name);
+ f();
+ console.log('OK\n');
+ };
+
+ var parseArgs = function(args) {
+ var skips = [
+ '--transport=http',
+ '--protocol=json'
+ ];
+ var opts = {
+ port: '9090'
+ // protocol: 'json',
+ };
+ var keys = {};
+ for (var key in opts) {
+ keys['--' + key + '='] = key;
+ }
+ for (var i in args) {
+ var arg = args[i];
+ if (skips.indexOf(arg) != -1) {
+ continue;
+ }
+ var hit = false;
+ for (var k in keys) {
+ if (arg.slice(0, k.length) === k) {
+ opts[keys[k]] = arg.slice(k.length);
+ hit = true;
+ break;
+ }
+ }
+ if (!hit) {
+ throw new Error('Unknown argument: ' + arg);
+ }
+ }
+ var portAsInt: number = parseInt(opts.port, 10);
+ if (!opts.port || portAsInt < 1 || portAsInt > 65535) {
+ throw new Error('Invalid port number');
+ }
+ return opts;
+ };
+
+ var execute = function() {
+ console.log('### Apache Thrift Javascript standalone test client');
+ console.log('------------------------------------------------------------');
+
+ phantom.page.injectJs('thrift.js');
+ phantom.page.injectJs('gen-js/ThriftTest_types.js');
+ phantom.page.injectJs('gen-js/ThriftTest.js');
+
+ var system = require('system');
+ var opts = parseArgs(system.args.slice(1));
+ var port = opts.port;
+ var transport = new Thrift.Transport('http://localhost:' + port + '/service');
+ var protocol = new Thrift.Protocol(transport);
+ var client = new ThriftTest.ThriftTestClient(protocol);
+
+
+ // TODO: Remove duplicate code with test.js.
+ // all Languages in UTF-8
+ var stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, Asturianu, Aymar aru, AzÉ™rbaycan, Башҡорт, Boarisch, ŽemaitÄ—Å¡ka, БеларуÑкаÑ, БеларуÑÐºÐ°Ñ (тарашкевіца), БългарÑки, Bamanankan, বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Ðохчийн, Cebuano, á£áŽ³áŽ©, ÄŒesky, СловѣÌньÑкъ / ⰔⰎⰑⰂⰡâ°â° â°”â°â°Ÿ, Чӑвашла, Cymraeg, Dansk, Zazaki, Þ‹Þ¨ÞˆÞ¬Þ€Þ¨Þ„Þ¦ÞÞ°, Ελληνικά, Emiliàn e rumagnòl, English, Esperanto, Español, Eesti, Euskara, Ùارسی, Suomi, Võro, Føroyskt, Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, Avañe'ẽ, ગà«àªœàª°àª¾àª¤à«€, Gaelg, עברית, हिनà¥à¤¦à¥€, Fiji Hindi, Hrvatski, Kreyòl ayisyen, Magyar, Õ€Õ¡ÕµÕ¥Ö€Õ¥Õ¶, Interlingua, Bahasa Indonesia, Ilokano, Ido, Ãslenska, Italiano, 日本語, Lojban, Basa Jawa, ქáƒáƒ áƒ—ული, Kongo, Kalaallisut, ಕನà³à²¨à²¡, 한국어, Къарачай-Малкъар, Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, LatvieÅ¡u, Basa Banyumasan, Malagasy, МакедонÑки, മലയാളം, मराठी, Bahasa Melayu, مازÙرونی, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪Norsk (nynorsk)‬, ‪Norsk (bokmÃ¥l)‬, Nouormand, Diné bizaad, Occitan, Иронау, Papiamentu, Deitsch, Norfuk / Pitkern, Polski, پنجابی, پښتو, Português, Runa Simi, Rumantsch, Romani, Română, РуÑÑкий, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple English, SlovenÄina, SlovenÅ¡Äina, СрпÑки / Srpski, Seeltersk, Svenska, Kiswahili, தமிழà¯, తెలà±à°—à±, Тоҷикӣ, ไทย, Türkmençe, Tagalog, Türkçe, Татарча/Tatarça, УкраїнÑька, اردو, Tiếng Việt, Volapük, Walon, Winaray, å´è¯­, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, Bân-lâm-gú, 粵語";
+
+ function checkRecursively(map1, map2) {
+ if (typeof map1 !== 'function' && typeof map2 !== 'function') {
+ if (!map1 || typeof map1 !== 'object') {
+ equal(map1, map2);
+ } else {
+ for (var key in map1) {
+ checkRecursively(map1[key], map2[key]);
+ }
+ }
+ }
+ }
+
+ test('Void', function() {
+ equal(client.testVoid(), undefined);
+ });
+ test('Binary (String)', function() {
+ var binary: string = '';
+ for (var v = 255; v >= 0; --v) {
+ binary += String.fromCharCode(v);
+ }
+ equal(client.testBinary(binary), binary);
+ });
+ test('Binary (Uint8Array)', function() {
+ var binary: string = '';
+ for (var v = 255; v >= 0; --v) {
+ binary += String.fromCharCode(v);
+ }
+ var arr = new Uint8Array(binary.length);
+ for (var i = 0; i < binary.length; ++i) {
+ arr[i] = binary[i].charCodeAt(0);
+ }
+ const hexEncodedString = Array.from(arr, function(byte) {
+ return String.fromCharCode(byte);
+ }).join('')
+ equal(client.testBinary(hexEncodedString), binary);
+ });
+ test('String', function() {
+ equal(client.testString(''), '');
+ equal(client.testString(stringTest), stringTest);
+
+ var specialCharacters = 'quote: \" backslash:' +
+ ' forwardslash-escaped: \/ ' +
+ ' backspace: \b formfeed: \f newline: \n return: \r tab: ' +
+ ' now-all-of-them-together: "\\\/\b\n\r\t' +
+ ' now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><';
+ equal(client.testString(specialCharacters), specialCharacters);
+ });
+ test('Double', function() {
+ equal(client.testDouble(0), 0);
+ equal(client.testDouble(-1), -1);
+ equal(client.testDouble(3.14), 3.14);
+ equal(client.testDouble(Math.pow(2, 60)), Math.pow(2, 60));
+ });
+ test('Bool', function() {
+ equal(client.testBool(true), true);
+ equal(client.testBool(false), false);
+ });
+ test('I8', function() {
+ equal(client.testByte(0), 0);
+ equal(client.testByte(0x01), 0x01);
+ });
+ test('I32', function() {
+ equal(client.testI32(0), 0);
+ equal(client.testI32(Math.pow(2, 30)), Math.pow(2, 30));
+ equal(client.testI32(-Math.pow(2, 30)), -Math.pow(2, 30));
+ });
+ test('I64', function() {
+ equal(client.testI64(new Int64(0)), 0);
+ equal(client.testI64(int64_2_pow_60), Math.pow(2, 52));
+ equal(client.testI64(int64_minus_2_pow_60), -Math.pow(2, 52));
+ });
+
+ test('Struct', function() {
+ var structTestInput: ThriftTest.Xtruct = new ThriftTest.Xtruct();
+ structTestInput.string_thing = 'worked';
+ structTestInput.byte_thing = 0x01;
+ structTestInput.i32_thing = Math.pow(2, 30);
+ structTestInput.i64_thing = int64_2_pow_60;
+
+ var structTestOutput: ThriftTest.Xtruct = client.testStruct(structTestInput);
+
+ equal(structTestOutput.string_thing, structTestInput.string_thing);
+ equal(structTestOutput.byte_thing, structTestInput.byte_thing);
+ equal(structTestOutput.i32_thing, structTestInput.i32_thing);
+ equal(structTestOutput.i64_thing, structTestInput.i64_thing);
+
+ equal(JSON.stringify(structTestOutput), JSON.stringify(structTestInput));
+ });
+
+ test('Nest', function() {
+ var xtrTestInput: ThriftTest.Xtruct = new ThriftTest.Xtruct();
+ xtrTestInput.string_thing = 'worked';
+ xtrTestInput.byte_thing = 0x01;
+ xtrTestInput.i32_thing = Math.pow(2, 30);
+ xtrTestInput.i64_thing = int64_2_pow_60;
+
+ var nestTestInput: ThriftTest.Xtruct2 = new ThriftTest.Xtruct2();
+ nestTestInput.byte_thing = 0x02;
+ nestTestInput.struct_thing = xtrTestInput;
+ nestTestInput.i32_thing = Math.pow(2, 15);
+
+ var nestTestOutput = client.testNest(nestTestInput);
+
+ equal(nestTestOutput.byte_thing, nestTestInput.byte_thing);
+ equal(nestTestOutput.struct_thing.string_thing, nestTestInput.struct_thing.string_thing);
+ equal(nestTestOutput.struct_thing.byte_thing, nestTestInput.struct_thing.byte_thing);
+ equal(nestTestOutput.struct_thing.i32_thing, nestTestInput.struct_thing.i32_thing);
+ equal(nestTestOutput.struct_thing.i64_thing, nestTestInput.struct_thing.i64_thing);
+ equal(nestTestOutput.i32_thing, nestTestInput.i32_thing);
+
+ equal(JSON.stringify(nestTestOutput), JSON.stringify(nestTestInput));
+ });
+
+ test('Map', function() {
+ var mapTestInput: {[k: number]: number;} = {7: 77, 8: 88, 9: 99};
+
+ var mapTestOutput: {[k: number]: number;} = client.testMap(mapTestInput);
+
+ for (var key in mapTestOutput) {
+ equal(mapTestOutput[key], mapTestInput[key]);
+ }
+ });
+
+ test('StringMap', function() {
+ var mapTestInput: {[k: string]: string;} = {
+ 'a': '123', 'a b': 'with spaces ', 'same': 'same', '0': 'numeric key',
+ 'longValue': stringTest, stringTest: 'long key'
+ };
+
+ var mapTestOutput: {[k: string]: string;} = client.testStringMap(mapTestInput);
+
+ for (var key in mapTestOutput) {
+ equal(mapTestOutput[key], mapTestInput[key]);
+ }
+ });
+
+ test('Set', function() {
+ var setTestInput: number[] = [1, 2, 3];
+ ok(client.testSet(setTestInput), setTestInput);
+ });
+
+ test('List', function() {
+ var listTestInput: number[] = [1, 2, 3];
+ ok(client.testList(listTestInput), listTestInput);
+ });
+
+ test('Enum', function() {
+ equal(client.testEnum(ThriftTest.Numberz.ONE), ThriftTest.Numberz.ONE);
+ });
+
+ test('TypeDef', function() {
+ equal(client.testTypedef(new Int64(69)), 69);
+ });
+
+ test('MapMap', function() {
+ var mapMapTestExpectedResult: {[K: number]: {[k: number]: number}} = {
+ '4': {'1': 1, '2': 2, '3': 3, '4': 4},
+ '-4': {'-4': -4, '-3': -3, '-2': -2, '-1': -1}
+ };
+
+ var mapMapTestOutput = client.testMapMap(1);
+
+
+ for (var key in mapMapTestOutput) {
+ for (var key2 in mapMapTestOutput[key]) {
+ equal(mapMapTestOutput[key][key2], mapMapTestExpectedResult[key][key2]);
+ }
+ }
+
+ checkRecursively(mapMapTestOutput, mapMapTestExpectedResult);
+ });
+
+ test('Xception', function() {
+ try {
+ client.testException('Xception');
+ ok(false, "expected an exception but there was no exception");
+ } catch (e) {
+ equal(e.errorCode, 1001);
+ equal(e.message, 'Xception');
+ }
+ });
+
+ test('no Exception', function() {
+ try {
+ client.testException('no Exception');
+ } catch (e) {
+ ok(false, "expected no exception but here was an exception");
+ }
+ });
+
+ test('TException', function() {
+ try {
+ client.testException('TException');
+ ok(false, "expected an exception but there was no exception");
+ } catch (e) {
+ ok(ok, "succesfully got exception");
+ }
+ });
+
+ const crazy: ThriftTest.Insanity = {
+ 'userMap': { '5': new Int64(5), '8': new Int64(8) },
+ 'xtructs': [{
+ 'string_thing': 'Goodbye4',
+ 'byte_thing': 4,
+ 'i32_thing': 4,
+ 'i64_thing': new Int64(4)
+ },
+ {
+ 'string_thing': 'Hello2',
+ 'byte_thing': 2,
+ 'i32_thing': 2,
+ 'i64_thing': new Int64(2)
+ }]
+ };
+ test('Insanity', function() {
+ const insanity: {[k: number]: (ThriftTest.Insanity | {[k:number]: ThriftTest.Insanity})} = {
+ '1': {
+ '2': crazy,
+ '3': crazy
+ },
+ '2': { '6': new ThriftTest.Insanity() }
+ };
+ var res = client.testInsanity(new ThriftTest.Insanity(crazy));
+ ok(res, JSON.stringify(res));
+ ok(insanity, JSON.stringify(insanity));
+
+ checkRecursively(res, insanity);
+ });
+
+ console.log('------------------------------------------------------------');
+ console.log('### All tests succeeded.');
+ return 0;
+ };
+
+ try {
+ var ret = execute();
+ phantom.exit(ret);
+ } catch (err) {
+ // Catch all and exit to avoid hang.
+ console.error(err);
+ phantom.exit(1);
+ }
+ })();
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/ts/test/server_http.js b/src/jaegertracing/thrift/lib/ts/test/server_http.js
new file mode 100644
index 000000000..8380c3a77
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/test/server_http.js
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// This HTTP server is designed to serve the test.html browser
+// based JavaScript test page (which must be in the current directory).
+// This server also supplies the Thrift based test service, which depends
+// on the standard ThriftTest.thrift IDL service (which must be compiled
+// for Node and browser based JavaScript in ./gen-nodejs and ./gen-js
+// respectively).
+//
+// Using the command flag --es6, this server can be run using nodejs code built
+// for the es6 environment or for pre-es6 environment.
+//
+
+const thrift = require('../../nodejs/lib/thrift');
+const es6Mode = process.argv.includes('--es6');
+const genFolder = es6Mode ? 'gen-nodejs-es6' : 'gen-nodejs';
+const ThriftTestSvc = require(`./${genFolder}/ThriftTest.js`);
+const ThriftTestHandler = require('./test_handler').ThriftTestHandler;
+
+const ThriftTestSvcOpt = {
+ transport: thrift.TBufferedTransport,
+ protocol: thrift.TJSONProtocol,
+ processor: ThriftTestSvc,
+ handler: ThriftTestHandler
+};
+
+const ThriftWebServerOptions = {
+ files: __dirname,
+ services: {
+ '/service': ThriftTestSvcOpt
+ }
+};
+
+const server = thrift.createWebServer(ThriftWebServerOptions);
+const port = es6Mode ? 8088 : 8089;
+server.listen(port);
+console.log(`Serving files from: ${__dirname}`);
+console.log(`Http/Thrift Server (ES6 mode ${es6Mode}) running on port: ${port}`);
diff --git a/src/jaegertracing/thrift/lib/ts/test/test-int64.html b/src/jaegertracing/thrift/lib/ts/test/test-int64.html
new file mode 100644
index 000000000..75ac27839
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/test/test-int64.html
@@ -0,0 +1,46 @@
++<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>Int64 Constants in JS: Unit Test</title>
+
+ <!-- QUnit Test framework-->
+ <script type="text/javascript" src="build/js/lib/qunit.js" charset="utf-8"></script>
+ <link rel="stylesheet" href="build/js/lib/qunit.css" type="text/css" media="screen" />
+
+ <!-- the Test Suite-->
+ <script type="text/javascript" src="build/ts/lib/test-int64.js" charset="utf-8"></script>
+ </head>
+<body>
+ <h1 id="qunit-header">Int64 Constants in JS: Unit Test</h1>
+ <h2 id="qunit-banner"></h2>
+ <div id="qunit-testrunner-toolbar"></div>
+ <h2 id="qunit-userAgent"></h2>
+ <ol id="qunit-tests"><li><!-- get valid xhtml strict--></li></ol>
+ <!-- Uncomment this to check the validity. This significantly slows down the test.
+ <p>
+ <a href="http://validator.w3.org/check/referer"><img
+ src="http://www.w3.org/Icons/valid-xhtml10"
+ alt="Valid XHTML 1.0!" height="31" width="88" /></a>
+ </p>
+ -->
+</body>
+</html>
diff --git a/src/jaegertracing/thrift/lib/ts/test/test-int64.ts b/src/jaegertracing/thrift/lib/ts/test/test-int64.ts
new file mode 100644
index 000000000..254d8d7bf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/test/test-int64.ts
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ /* jshint -W100 */
+
+var Int64 = require("node-int64");
+var JSONInt64 = require("json-int64");
+import { Int64Test } from "./gen-js/Int64Test_types";
+
+
+// Work around for old API used by QUnitAdapter of jsTestDriver
+if (typeof QUnit.log == 'function') {
+ // When using real QUnit (fron PhantomJS) log failures to console
+ QUnit.log(function(details) {
+ if (!details.result) {
+ console.log('======== FAIL ========');
+ console.log('TestName: ' + details.name);
+ if (details.message) console.log(details.message);
+ console.log('Expected: ' + details.expected);
+ console.log('Actual : ' + details.actual);
+ console.log('======================');
+ }
+ });
+}
+
+QUnit.module('Int64');
+
+ QUnit.test('Int64', function(assert) {
+ console.log('Int64 test -- starts');
+ const EXPECTED_SMALL_INT64_AS_NUMBER: number = 42;
+ const EXPECTED_SMALL_INT64: typeof Int64 = new Int64(42);
+ const EXPECTED_MAX_JS_SAFE_INT64: typeof Int64 = new Int64(Number.MAX_SAFE_INTEGER);
+ const EXPECTED_MIN_JS_SAFE_INT64: typeof Int64 = new Int64(Number.MIN_SAFE_INTEGER);
+ const EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64: typeof Int64 = new Int64("0020000000000000"); // hex-encoded
+ const EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64: typeof Int64 = new Int64("ffe0000000000000"); // hex-encoded 2's complement
+ const EXPECTED_MAX_SIGNED_INT64: typeof Int64 = new Int64("7fffffffffffffff"); // hex-encoded
+ const EXPECTED_MIN_SIGNED_INT64: typeof Int64 = new Int64("8000000000000000"); // hex-encoded 2's complement
+ const EXPECTED_INT64_LIST = [
+ EXPECTED_SMALL_INT64,
+ EXPECTED_MAX_JS_SAFE_INT64,
+ EXPECTED_MIN_JS_SAFE_INT64,
+ EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64,
+ EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64,
+ EXPECTED_MAX_SIGNED_INT64,
+ EXPECTED_MIN_SIGNED_INT64
+ ];
+ assert.ok(EXPECTED_SMALL_INT64.equals(Int64Test.SMALL_INT64));
+ assert.ok(EXPECTED_MAX_JS_SAFE_INT64.equals(Int64Test.MAX_JS_SAFE_INT64));
+ assert.ok(EXPECTED_MIN_JS_SAFE_INT64.equals(Int64Test.MIN_JS_SAFE_INT64));
+ assert.ok(
+ EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64.equals(
+ Int64Test.MAX_JS_SAFE_PLUS_ONE_INT64
+ )
+ );
+ assert.ok(
+ EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64.equals(
+ Int64Test.MIN_JS_SAFE_MINUS_ONE_INT64
+ )
+ );
+ assert.ok(EXPECTED_MAX_SIGNED_INT64.equals(Int64Test.MAX_SIGNED_INT64));
+ assert.ok(EXPECTED_MIN_SIGNED_INT64.equals(Int64Test.MIN_SIGNED_INT64));
+ assert.equal(
+ EXPECTED_SMALL_INT64_AS_NUMBER,
+ Int64Test.SMALL_INT64.toNumber()
+ );
+ assert.equal(
+ Number.MAX_SAFE_INTEGER,
+ Int64Test.MAX_JS_SAFE_INT64.toNumber()
+ );
+ assert.equal(
+ Number.MIN_SAFE_INTEGER,
+ Int64Test.MIN_JS_SAFE_INT64.toNumber()
+ );
+
+ for (let i = 0; i < EXPECTED_INT64_LIST.length; ++i) {
+ assert.ok(EXPECTED_INT64_LIST[i].equals(Int64Test.INT64_LIST[i]));
+ }
+
+ for (let i = 0; i < EXPECTED_INT64_LIST.length; ++i){
+ let int64Object = EXPECTED_INT64_LIST[i];
+ assert.ok(Int64Test.INT64_2_INT64_MAP[JSONInt64.toDecimalString(int64Object)].equals(int64Object));
+ }
+
+ console.log('Int64 test -- ends');
+ });
+
diff --git a/src/jaegertracing/thrift/lib/ts/test/test.html b/src/jaegertracing/thrift/lib/ts/test/test.html
new file mode 100755
index 000000000..ec7d7ebe4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/test/test.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>Thrift Javascript Bindings: Unit Test</title>
+
+ <script src="build/js/lib/Int64.js" type="text/javascript" charset="utf-8"></script>
+ <script src="build/js/lib/Int64Util.js" type="text/javascript" charset="utf-8"></script>
+ <script src="build/js/lib/JSONInt64.js" type="text/javascript" charset="utf-8"></script>
+ <script src="build/ts/thrift.js" type="text/javascript" charset="utf-8"></script>
+ <script src="gen-js/ThriftTest_types.js" type="text/javascript" charset="utf-8"></script>
+ <script src="gen-js/ThriftTest.js" type="text/javascript" charset="utf-8"></script>
+
+ <!-- QUnit Test framework-->
+ <!-- <script type="text/javascript" src="build/js/lib/qunit.js" charset="utf-8"></script> -->
+ <link rel="stylesheet" href="build/js/lib/qunit.css" type="text/css" media="screen" />
+
+ <!-- the Test Suite-->
+ <script type="text/javascript" src="build/ts/lib/test.js" charset="utf-8"></script>
+</head>
+<body>
+ <h1 id="qunit-header">Thrift Javascript Bindings: Unit Test (<a href="https://git-wip-us.apache.org/repos/asf?p=thrift.git;a=blob;f=test/ThriftTest.thrift;hb=HEAD">ThriftTest.thrift</a>)</h1>
+ <h2 id="qunit-banner"></h2>
+ <div id="qunit-testrunner-toolbar"></div>
+ <h2 id="qunit-userAgent"></h2>
+ <ol id="qunit-tests"><li><!-- get valid xhtml strict--></li></ol>
+ <!-- Uncomment this to check the validity. This significantly slows down the test.
+ <p>
+ <a href="http://validator.w3.org/check/referer"><img
+ src="http://www.w3.org/Icons/valid-xhtml10"
+ alt="Valid XHTML 1.0!" height="31" width="88" /></a>
+ </p>
+ -->
+</body>
+</html>
diff --git a/src/jaegertracing/thrift/lib/ts/test/test.ts b/src/jaegertracing/thrift/lib/ts/test/test.ts
new file mode 100644
index 000000000..31fdcbff8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/test/test.ts
@@ -0,0 +1,342 @@
+import { ThriftTest } from "./gen-js/ThriftTest_types";
+import "./gen-js/ThriftTest";
+
+var Int64 = require("node-int64");
+var JSONInt64 = require("json-int64");
+var QUnit = require("./qunit");
+
+const transport: Thrift.Transport = new Thrift.Transport("/service");
+const protocol: Thrift.Protocol = new Thrift.Protocol(transport);
+const client: ThriftTest.ThriftTestClient = new ThriftTest.ThriftTestClient(protocol);
+
+const int64_2_pow_60: typeof Int64 = new Int64('1000000000000000');
+const int64_minus_2_pow_60: typeof Int64 = new Int64('f000000000000000');
+
+// Work around for old API used by QUnitAdapter of jsTestDriver
+if (typeof QUnit.log == 'function') {
+ // When using real QUnit (fron PhantomJS) log failures to console
+ QUnit.log(function(details) {
+ if (!details.result) {
+ console.log('======== FAIL ========');
+ console.log('TestName: ' + details.name);
+ if (details.message) console.log(details.message);
+ console.log('Expected: ' + JSONInt64.stringify(details.expected));
+ console.log('Actual : ' + JSONInt64.stringify(details.actual));
+ console.log('======================');
+ }
+ });
+}
+
+// all Languages in UTF-8
+const stringTest: string = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, Asturianu, Aymar aru, AzÉ™rbaycan, Башҡорт, Boarisch, ŽemaitÄ—Å¡ka, БеларуÑкаÑ, БеларуÑÐºÐ°Ñ (тарашкевіца), БългарÑки, Bamanankan, বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Ðохчийн, Cebuano, á£áŽ³áŽ©, ÄŒesky, СловѣÌньÑкъ / ⰔⰎⰑⰂⰡâ°â° â°”â°â°Ÿ, Чӑвашла, Cymraeg, Dansk, Zazaki, Þ‹Þ¨ÞˆÞ¬Þ€Þ¨Þ„Þ¦ÞÞ°, Ελληνικά, Emiliàn e rumagnòl, English, Esperanto, Español, Eesti, Euskara, Ùارسی, Suomi, Võro, Føroyskt, Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, Avañe'ẽ, ગà«àªœàª°àª¾àª¤à«€, Gaelg, עברית, हिनà¥à¤¦à¥€, Fiji Hindi, Hrvatski, Kreyòl ayisyen, Magyar, Õ€Õ¡ÕµÕ¥Ö€Õ¥Õ¶, Interlingua, Bahasa Indonesia, Ilokano, Ido, Ãslenska, Italiano, 日本語, Lojban, Basa Jawa, ქáƒáƒ áƒ—ული, Kongo, Kalaallisut, ಕನà³à²¨à²¡, 한국어, Къарачай-Малкъар, Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, LatvieÅ¡u, Basa Banyumasan, Malagasy, МакедонÑки, മലയാളം, मराठी, Bahasa Melayu, مازÙرونی, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪Norsk (nynorsk)‬, ‪Norsk (bokmÃ¥l)‬, Nouormand, Diné bizaad, Occitan, Иронау, Papiamentu, Deitsch, Norfuk / Pitkern, Polski, پنجابی, پښتو, Português, Runa Simi, Rumantsch, Romani, Română, РуÑÑкий, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple English, SlovenÄina, SlovenÅ¡Äina, СрпÑки / Srpski, Seeltersk, Svenska, Kiswahili, தமிழà¯, తెలà±à°—à±, Тоҷикӣ, ไทย, Türkmençe, Tagalog, Türkçe, Татарча/Tatarça, УкраїнÑька, اردو, Tiếng Việt, Volapük, Walon, Winaray, å´è¯­, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, Bân-lâm-gú, 粵語";
+
+
+function checkRecursively(assert, map1: Object, map2: Object): void {
+ if (typeof map1 !== 'function' && typeof map2 !== 'function') {
+ if (!map1 || typeof map1 !== 'object') {
+ assert.equal(map1, map2);
+ } else {
+ for (let key in map1) {
+ checkRecursively(assert, map1[key], map2[key]);
+ }
+ }
+ }
+}
+
+
+QUnit.module('Base Types');
+
+ QUnit.test('Void', function(assert) {
+ assert.equal(client.testVoid(), undefined);
+ });
+ QUnit.test('Binary (String)', function(assert) {
+ let binary: string = '';
+ for (let v = 255; v >= 0; --v) {
+ binary += String.fromCharCode(v);
+ }
+ assert.equal(client.testBinary(binary), binary);
+ });
+ QUnit.test('Binary (Uint8Array)', function(assert) {
+ let binary: string = '';
+ for (let v = 255; v >= 0; --v) {
+ binary += String.fromCharCode(v);
+ }
+ const arr: Uint8Array = new Uint8Array(binary.length);
+ for (let i = 0; i < binary.length; ++i) {
+ arr[i] = binary[i].charCodeAt(0);
+ }
+ const hexEncodedString = Array.from(arr, function(byte) {
+ return String.fromCharCode(byte);
+ }).join('')
+ assert.equal(client.testBinary(hexEncodedString), binary);
+ });
+ QUnit.test('String', function(assert) {
+ assert.equal(client.testString(''), '');
+ assert.equal(client.testString(stringTest), stringTest);
+
+ const specialCharacters: string = 'quote: \" backslash:' +
+ ' forwardslash-escaped: \/ ' +
+ ' backspace: \b formfeed: \f newline: \n return: \r tab: ' +
+ ' now-all-of-them-together: "\\\/\b\n\r\t' +
+ ' now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><';
+ assert.equal(client.testString(specialCharacters), specialCharacters);
+ });
+ QUnit.test('Double', function(assert) {
+ assert.equal(client.testDouble(0), 0);
+ assert.equal(client.testDouble(-1), -1);
+ assert.equal(client.testDouble(3.14), 3.14);
+ assert.equal(client.testDouble(Math.pow(2, 60)), Math.pow(2, 60));
+ });
+ QUnit.test('Byte', function(assert) {
+ assert.equal(client.testByte(0), 0);
+ assert.equal(client.testByte(0x01), 0x01);
+ });
+ QUnit.test('I32', function(assert) {
+ assert.equal(client.testI32(0), 0);
+ assert.equal(client.testI32(Math.pow(2, 30)), Math.pow(2, 30));
+ assert.equal(client.testI32(-Math.pow(2, 30)), -Math.pow(2, 30));
+ });
+ QUnit.test('I64', function(assert) {
+ assert.equal(client.testI64(new Int64(0)), 0);
+
+ let int64_2_pow_60_result: typeof Int64 = client.testI64(int64_2_pow_60);
+ assert.ok(int64_2_pow_60.equals(int64_2_pow_60_result));
+
+ let int64_minus_2_pow_60_result: typeof Int64 = client.testI64(int64_minus_2_pow_60);
+ assert.ok(int64_minus_2_pow_60.equals(int64_minus_2_pow_60_result));
+ });
+
+
+QUnit.module('Structured Types');
+
+ QUnit.test('Struct', function(assert) {
+ const structTestInput: ThriftTest.Xtruct = new ThriftTest.Xtruct();
+ structTestInput.string_thing = 'worked';
+ structTestInput.byte_thing = 0x01;
+ structTestInput.i32_thing = Math.pow(2, 30);
+ structTestInput.i64_thing = int64_2_pow_60;
+
+ const structTestOutput: ThriftTest.Xtruct = client.testStruct(structTestInput);
+
+ assert.equal(structTestOutput.string_thing, structTestInput.string_thing);
+ assert.equal(structTestOutput.byte_thing, structTestInput.byte_thing);
+ assert.equal(structTestOutput.i32_thing, structTestInput.i32_thing);
+ assert.ok(structTestOutput.i64_thing.equals(structTestInput.i64_thing));
+ assert.ok(structTestInput.i64_thing.equals(structTestOutput.i64_thing));
+
+ assert.equal(JSONInt64.stringify(structTestOutput), JSONInt64.stringify(structTestInput));
+ });
+
+ QUnit.test('Nest', function(assert) {
+ const xtrTestInput: ThriftTest.Xtruct = new ThriftTest.Xtruct();
+ xtrTestInput.string_thing = 'worked';
+ xtrTestInput.byte_thing = 0x01;
+ xtrTestInput.i32_thing = Math.pow(2, 30);
+ xtrTestInput.i64_thing = int64_2_pow_60;
+
+ const nestTestInput: ThriftTest.Xtruct2 = new ThriftTest.Xtruct2();
+ nestTestInput.byte_thing = 0x02;
+ nestTestInput.struct_thing = xtrTestInput;
+ nestTestInput.i32_thing = Math.pow(2, 15);
+
+ const nestTestOutput: ThriftTest.Xtruct2 = client.testNest(nestTestInput);
+
+ assert.equal(nestTestOutput.byte_thing, nestTestInput.byte_thing);
+ assert.equal(nestTestOutput.struct_thing.string_thing, nestTestInput.struct_thing.string_thing);
+ assert.equal(nestTestOutput.struct_thing.byte_thing, nestTestInput.struct_thing.byte_thing);
+ assert.equal(nestTestOutput.struct_thing.i32_thing, nestTestInput.struct_thing.i32_thing);
+ assert.ok(nestTestOutput.struct_thing.i64_thing.equals(nestTestInput.struct_thing.i64_thing));
+ assert.equal(nestTestOutput.i32_thing, nestTestInput.i32_thing);
+
+ assert.equal(JSONInt64.stringify(nestTestOutput), JSONInt64.stringify(nestTestInput));
+ });
+
+ QUnit.test('Map', function(assert) {
+ const mapTestInput: {[k: number]: number;} = {7: 77, 8: 88, 9: 99};
+
+ const mapTestOutput: {[k: number]: number;} = client.testMap(mapTestInput);
+
+ for (let key in mapTestOutput) {
+ assert.equal(mapTestOutput[key], mapTestInput[key]);
+ }
+ });
+
+ QUnit.test('StringMap', function(assert) {
+ const mapTestInput: {[k: string]: string;} = {
+ 'a': '123', 'a b': 'with spaces ', 'same': 'same', '0': 'numeric key',
+ 'longValue': stringTest, stringTest: 'long key'
+ };
+
+ const mapTestOutput: {[k: string]: string;} = client.testStringMap(mapTestInput);
+
+ for (let key in mapTestOutput) {
+ assert.equal(mapTestOutput[key], mapTestInput[key]);
+ }
+ });
+
+ QUnit.test('Set', function(assert) {
+ const setTestInput: number[] = [1, 2, 3];
+ assert.ok(client.testSet(setTestInput), setTestInput);
+ });
+
+ QUnit.test('List', function(assert) {
+ const listTestInput: number[] = [1, 2, 3];
+ assert.ok(client.testList(listTestInput), listTestInput);
+ });
+
+ QUnit.test('Enum', function(assert) {
+ assert.equal(client.testEnum(ThriftTest.Numberz.ONE), ThriftTest.Numberz.ONE);
+ });
+
+ QUnit.test('TypeDef', function(assert) {
+ assert.equal(client.testTypedef(new Int64(69)), 69);
+ });
+
+
+QUnit.module('deeper!');
+
+ QUnit.test('MapMap', function(assert) {
+ const mapMapTestExpectedResult: {[K: number]: {[k: number]: number}} = {
+ '4': {'1': 1, '2': 2, '3': 3, '4': 4},
+ '-4': {'-4': -4, '-3': -3, '-2': -2, '-1': -1}
+ };
+
+ const mapMapTestOutput = client.testMapMap(1);
+
+
+ for (let key in mapMapTestOutput) {
+ for (let key2 in mapMapTestOutput[key]) {
+ assert.equal(mapMapTestOutput[key][key2], mapMapTestExpectedResult[key][key2]);
+ }
+ }
+
+ checkRecursively(assert, mapMapTestOutput, mapMapTestExpectedResult);
+ });
+
+
+QUnit.module('Exception');
+
+ QUnit.test('Xception', function(assert) {
+ assert.expect(2);
+ const done = assert.async();
+ try {
+ client.testException('Xception');
+ assert.ok(false);
+ }catch (e) {
+ assert.equal(e.errorCode, 1001);
+ assert.equal(e.message, 'Xception');
+ done();
+ }
+ });
+
+ QUnit.test('no Exception', function(assert) {
+ assert.expect(1);
+ try {
+ client.testException('no Exception');
+ assert.ok(true);
+ }catch (e) {
+ assert.ok(false);
+ }
+ });
+
+ QUnit.test('TException', function(assert) {
+ //ThriftTest does not list TException as a legal exception so it will
+ // generate an exception on the server that does not propagate back to
+ // the client. This test has been modified to equate to "no exception"
+ assert.expect(1);
+ try {
+ client.testException('TException');
+ } catch (e) {
+ //assert.ok(false);
+ }
+ assert.ok(true);
+ });
+
+
+QUnit.module('Insanity');
+
+ const crazy: ThriftTest.Insanity = {
+ 'userMap': { '5': new Int64(5), '8': new Int64(8) },
+ 'xtructs': [{
+ 'string_thing': 'Goodbye4',
+ 'byte_thing': 4,
+ 'i32_thing': 4,
+ 'i64_thing': new Int64(4)
+ },
+ {
+ 'string_thing': 'Hello2',
+ 'byte_thing': 2,
+ 'i32_thing': 2,
+ 'i64_thing': new Int64(2)
+ }]
+ };
+ QUnit.test('testInsanity', function(assert) {
+ const insanity: {[k: number]: (ThriftTest.Insanity | {[k:number]: ThriftTest.Insanity})} = {
+ '1': {
+ '2': crazy,
+ '3': crazy
+ },
+ '2': { '6': new ThriftTest.Insanity() }
+ };
+ const res = client.testInsanity(new ThriftTest.Insanity(crazy));
+ assert.ok(res, JSONInt64.stringify(res));
+ assert.ok(insanity, JSONInt64.stringify(insanity));
+
+ checkRecursively(assert, res, insanity);
+ });
+
+
+//////////////////////////////////
+//Run same tests asynchronously
+
+QUnit.module('Async');
+
+ QUnit.test('Double', function(assert) {
+ assert.expect(1);
+
+ const done = assert.async();
+ client.testDouble(3.14159265, function(result) {
+ assert.equal(result, 3.14159265);
+ done();
+ });
+ });
+
+ QUnit.test('Byte', function(assert) {
+ assert.expect(1);
+
+ const done = assert.async();
+ client.testByte(0x01, function(result) {
+ assert.equal(result, 0x01);
+ done();
+ });
+ });
+
+ QUnit.test('I32', function(assert) {
+ assert.expect(2);
+
+ const done = assert.async(2);
+ client.testI32(Math.pow(2, 30), function(result) {
+ assert.equal(result, Math.pow(2, 30));
+ done();
+ });
+
+ client.testI32(Math.pow(-2, 31), function(result) {
+ assert.equal(result, Math.pow(-2, 31));
+ done();
+ });
+ });
+
+ QUnit.test('I64', function(assert) {
+ assert.expect(2);
+
+ const done = assert.async(2);
+ client.testI64(int64_2_pow_60, function(result) {
+ assert.ok(int64_2_pow_60.equals(result));
+ done();
+ });
+
+ client.testI64(int64_minus_2_pow_60, function(result) {
+ assert.ok(int64_minus_2_pow_60.equals(result));
+ done();
+ });
+ });
diff --git a/src/jaegertracing/thrift/lib/ts/test/test_handler.js b/src/jaegertracing/thrift/lib/ts/test/test_handler.js
new file mode 100644
index 000000000..8ba296ba1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/test/test_handler.js
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * 'License'); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+//This is the server side Node test handler for the standard
+// Apache Thrift test service.
+
+const es6Mode = process.argv.includes('--es6');
+const genFolder = es6Mode ? 'gen-nodejs-es6' : 'gen-nodejs';
+const ttypes = require(`./${genFolder}/ThriftTest_types`);
+const TException = require('../../nodejs/lib/thrift').TException;
+const Int64 = require('node-int64');
+
+exports.ThriftTestHandler = {
+ testVoid: function(result) {
+ console.log('testVoid()');
+ result(null);
+ },
+ testString: function(thing, result) {
+ console.log('testString(\'' + thing + '\')');
+ result(null, thing);
+ },
+ testByte: function(thing, result) {
+ console.log('testByte(' + thing + ')');
+ result(null, thing);
+ },
+ testI32: function(thing, result) {
+ console.log('testI32(' + thing + ')');
+ result(null, thing);
+ },
+ testI64: function(thing, result) {
+ console.log('testI64(' + thing + ')');
+ result(null, thing);
+ },
+ testDouble: function(thing, result) {
+ console.log('testDouble(' + thing + ')');
+ result(null, thing);
+ },
+ testBinary: function(thing, result) {
+ console.log('testBinary(\'' + thing + '\')');
+ result(null, thing);
+ },
+ testStruct: function(thing, result) {
+ console.log('testStruct(');
+ console.log(thing);
+ console.log(')');
+ result(null, thing);
+ },
+ testNest: function(nest, result) {
+ console.log('testNest(');
+ console.log(nest);
+ console.log(')');
+ result(null, nest);
+ },
+ testMap: function(thing, result) {
+ console.log('testMap(');
+ console.log(thing);
+ console.log(')');
+ result(null, thing);
+ },
+ testStringMap: function(thing, result) {
+ console.log('testStringMap(');
+ console.log(thing);
+ console.log(')');
+ result(null, thing);
+ },
+ testSet: function(thing, result) {
+ console.log('testSet(');
+ console.log(thing);
+ console.log(')');
+ result(null, thing);
+ },
+ testList: function(thing, result) {
+ console.log('testList(');
+ console.log(thing);
+ console.log(')');
+ result(null, thing);
+ },
+ testEnum: function(thing, result) {
+ console.log('testEnum(' + thing + ')');
+ result(null, thing);
+ },
+ testTypedef: function(thing, result) {
+ console.log('testTypedef(' + thing + ')');
+ result(null, thing);
+ },
+ testMapMap: function(hello, result) {
+ console.log('testMapMap(' + hello + ')');
+
+ const mapmap = [];
+ const pos = [];
+ const neg = [];
+ for (let i = 1; i < 5; i++) {
+ pos[i] = i;
+ neg[-i] = -i;
+ }
+ mapmap[4] = pos;
+ mapmap[-4] = neg;
+
+ result(null, mapmap);
+ },
+ testInsanity: function(argument, result) {
+ console.log('testInsanity(');
+ console.log(argument);
+ console.log(')');
+
+ const hello = new ttypes.Xtruct();
+ hello.string_thing = 'Hello2';
+ hello.byte_thing = 2;
+ hello.i32_thing = 2;
+ hello.i64_thing = new Int64(2);
+
+ const goodbye = new ttypes.Xtruct();
+ goodbye.string_thing = 'Goodbye4';
+ goodbye.byte_thing = 4;
+ goodbye.i32_thing = 4;
+ goodbye.i64_thing = new Int64(4);
+
+ const crazy = new ttypes.Insanity();
+ crazy.userMap = [];
+ crazy.userMap[ttypes.Numberz.EIGHT] = 8;
+ crazy.userMap[ttypes.Numberz.FIVE] = 5;
+ crazy.xtructs = [goodbye, hello];
+
+ const first_map = [];
+ const second_map = [];
+
+ first_map[ttypes.Numberz.TWO] = crazy;
+ first_map[ttypes.Numberz.THREE] = crazy;
+
+ const looney = new ttypes.Insanity();
+ second_map[ttypes.Numberz.SIX] = looney;
+
+ const insane = [];
+ insane[1] = first_map;
+ insane[2] = second_map;
+
+ console.log('insane result:');
+ console.log(insane);
+ result(null, insane);
+ },
+ testMulti: function(arg0, arg1, arg2, arg3, arg4, arg5, result) {
+ console.log('testMulti()');
+
+ const hello = new ttypes.Xtruct();
+ hello.string_thing = 'Hello2';
+ hello.byte_thing = arg0;
+ hello.i32_thing = arg1;
+ hello.i64_thing = arg2;
+ result(null, hello);
+ },
+ testException: function(arg, result) {
+ console.log('testException(' + arg + ')');
+ if (arg === 'Xception') {
+ const x = new ttypes.Xception();
+ x.errorCode = 1001;
+ x.message = arg;
+ result(x);
+ } else if (arg === 'TException') {
+ result(new TException(arg));
+ } else {
+ result(null);
+ }
+ },
+ testMultiException: function(arg0, arg1, result) {
+ console.log('testMultiException(' + arg0 + ', ' + arg1 + ')');
+ if (arg0 === ('Xception')) {
+ const x = new ttypes.Xception();
+ x.errorCode = 1001;
+ x.message = 'This is an Xception';
+ result(x);
+ } else if (arg0 === ('Xception2')) {
+ const x2 = new ttypes.Xception2();
+ x2.errorCode = 2002;
+ x2.struct_thing = new ttypes.Xtruct();
+ x2.struct_thing.string_thing = 'This is an Xception2';
+ result(x2);
+ }
+
+ const res = new ttypes.Xtruct();
+ res.string_thing = arg1;
+ result(null, res);
+ },
+ testOneway: function(sleepFor, result) {
+ console.log('testOneway(' + sleepFor + ') => JavaScript (like Rust) never sleeps!');
+ }
+}; //ThriftTestSvcHandler
diff --git a/src/jaegertracing/thrift/lib/ts/thrift.d.ts b/src/jaegertracing/thrift/lib/ts/thrift.d.ts
new file mode 100644
index 000000000..0ba46c914
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/thrift.d.ts
@@ -0,0 +1,699 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+declare module Thrift {
+ /**
+ * Thrift JavaScript library version.
+ */
+ var Version: string;
+
+ /**
+ * Thrift IDL type string to Id mapping.
+ * @property {number} STOP - End of a set of fields.
+ * @property {number} VOID - No value (only legal for return types).
+ * @property {number} BOOL - True/False integer.
+ * @property {number} BYTE - Signed 8 bit integer.
+ * @property {number} I08 - Signed 8 bit integer.
+ * @property {number} DOUBLE - 64 bit IEEE 854 floating point.
+ * @property {number} I16 - Signed 16 bit integer.
+ * @property {number} I32 - Signed 32 bit integer.
+ * @property {number} I64 - Signed 64 bit integer.
+ * @property {number} STRING - Array of bytes representing a string of characters.
+ * @property {number} UTF7 - Array of bytes representing a string of UTF7 encoded characters.
+ * @property {number} STRUCT - A multifield type.
+ * @property {number} MAP - A collection type (map/associative-array/dictionary).
+ * @property {number} SET - A collection type (unordered and without repeated values).
+ * @property {number} LIST - A collection type (unordered).
+ * @property {number} UTF8 - Array of bytes representing a string of UTF8 encoded characters.
+ * @property {number} UTF16 - Array of bytes representing a string of UTF16 encoded characters.
+ */
+ interface Type {
+ 'STOP': number;
+ 'VOID': number;
+ 'BOOL': number;
+ 'BYTE': number;
+ 'I08': number;
+ 'DOUBLE': number;
+ 'I16': number;
+ 'I32': number;
+ 'I64': number;
+ 'STRING': number;
+ 'UTF7': number;
+ 'STRUCT': number;
+ 'MAP': number;
+ 'SET': number;
+ 'LIST': number;
+ 'UTF8': number;
+ 'UTF16': number;
+ }
+ var Type: Type;
+
+ /**
+ * Thrift RPC message type string to Id mapping.
+ * @property {number} CALL - RPC call sent from client to server.
+ * @property {number} REPLY - RPC call normal response from server to client.
+ * @property {number} EXCEPTION - RPC call exception response from server to client.
+ * @property {number} ONEWAY - Oneway RPC call from client to server with no response.
+ */
+ interface MessageType {
+ 'CALL': number;
+ 'REPLY': number;
+ 'EXCEPTION': number;
+ 'ONEWAY': number;
+ }
+ var MessageType: MessageType;
+
+ /**
+ * Utility function returning the count of an object's own properties.
+ * @param {object} obj - Object to test.
+ * @returns {number} number of object's own properties
+ */
+ function objectLength(obj: Object): number;
+
+ /**
+ * Utility function to establish prototype inheritance.
+ * @param {function} constructor - Contstructor function to set as derived.
+ * @param {function} superConstructor - Contstructor function to set as base.
+ * @param {string} [name] - Type name to set as name property in derived prototype.
+ */
+ function inherits(constructor: Function, superConstructor: Function, name?: string): void;
+
+ /**
+ * TException is the base class for all Thrift exceptions types.
+ */
+ class TException implements Error {
+ name: string;
+ message: string;
+
+ /**
+ * Initializes a Thrift TException instance.
+ * @param {string} message - The TException message (distinct from the Error message).
+ */
+ constructor(message: string);
+
+ /**
+ * Returns the message set on the exception.
+ * @returns {string} exception message
+ */
+ getMessage(): string;
+ }
+
+ /**
+ * Thrift Application Exception type string to Id mapping.
+ * @property {number} UNKNOWN - Unknown/undefined.
+ * @property {number} UNKNOWN_METHOD - Client attempted to call a method unknown to the server.
+ * @property {number} INVALID_MESSAGE_TYPE - Client passed an unknown/unsupported MessageType.
+ * @property {number} WRONG_METHOD_NAME - Unused.
+ * @property {number} BAD_SEQUENCE_ID - Unused in Thrift RPC, used to flag proprietary sequence number errors.
+ * @property {number} MISSING_RESULT - Raised by a server processor if a handler fails to supply the required return result.
+ * @property {number} INTERNAL_ERROR - Something bad happened.
+ * @property {number} PROTOCOL_ERROR - The protocol layer failed to serialize or deserialize data.
+ * @property {number} INVALID_TRANSFORM - Unused.
+ * @property {number} INVALID_PROTOCOL - The protocol (or version) is not supported.
+ * @property {number} UNSUPPORTED_CLIENT_TYPE - Unused.
+ */
+ interface TApplicationExceptionType {
+ 'UNKNOWN': number;
+ 'UNKNOWN_METHOD': number;
+ 'INVALID_MESSAGE_TYPE': number;
+ 'WRONG_METHOD_NAME': number;
+ 'BAD_SEQUENCE_ID': number;
+ 'MISSING_RESULT': number;
+ 'INTERNAL_ERROR': number;
+ 'PROTOCOL_ERROR': number;
+ 'INVALID_TRANSFORM': number;
+ 'INVALID_PROTOCOL': number;
+ 'UNSUPPORTED_CLIENT_TYPE': number;
+ }
+ var TApplicationExceptionType: TApplicationExceptionType;
+
+ /**
+ * TApplicationException is the exception class used to propagate exceptions from an RPC server back to a calling client.
+ */
+ class TApplicationException extends TException {
+ message: string;
+ code: number;
+
+ /**
+ * Initializes a Thrift TApplicationException instance.
+ * @param {string} message - The TApplicationException message (distinct from the Error message).
+ * @param {Thrift.TApplicationExceptionType} [code] - The TApplicationExceptionType code.
+ */
+ constructor(message: string, code?: number);
+
+ /**
+ * Read a TApplicationException from the supplied protocol.
+ * @param {object} input - The input protocol to read from.
+ */
+ read(input: Object): void;
+
+ /**
+ * Write a TApplicationException to the supplied protocol.
+ * @param {object} output - The output protocol to write to.
+ */
+ write(output: Object): void;
+
+ /**
+ * Returns the application exception code set on the exception.
+ * @returns {Thrift.TApplicationExceptionType} exception code
+ */
+ getCode(): number;
+ }
+
+ /**
+ * The Apache Thrift Transport layer performs byte level I/O between RPC
+ * clients and servers. The JavaScript Transport object type uses Http[s]/XHR and is
+ * the sole browser based Thrift transport. Target servers must implement the http[s]
+ * transport (see: node.js example server).
+ */
+ class TXHRTransport {
+ url: string;
+ wpos: number;
+ rpos: number;
+ useCORS: any;
+ send_buf: string;
+ recv_buf: string;
+
+ /**
+ * If you do not specify a url then you must handle XHR operations on
+ * your own. This type can also be constructed using the Transport alias
+ * for backward compatibility.
+ * @param {string} [url] - The URL to connect to.
+ * @param {object} [options] - Options.
+ */
+ constructor(url?: string, options?: Object);
+
+ /**
+ * Gets the browser specific XmlHttpRequest Object.
+ * @returns {object} the browser XHR interface object
+ */
+ getXmlHttpRequestObject(): Object;
+
+ /**
+ * Sends the current XRH request if the transport was created with a URL and
+ * the async parameter if false. If the transport was not created with a URL
+ * or the async parameter is True or the URL is an empty string, the current
+ * send buffer is returned.
+ * @param {object} async - If true the current send buffer is returned.
+ * @param {function} callback - Optional async completion callback.
+ * @returns {undefined|string} Nothing or the current send buffer.
+ */
+ flush(async: any, callback?: Function): string;
+
+ /**
+ * Creates a jQuery XHR object to be used for a Thrift server call.
+ * @param {object} client - The Thrift Service client object generated by the IDL compiler.
+ * @param {object} postData - The message to send to the server.
+ * @param {function} args - The function to call if the request succeeds.
+ * @param {function} recv_method - The Thrift Service Client receive method for the call.
+ * @returns {object} A new jQuery XHR object.
+ */
+ jqRequest(client: Object, postData: any, args: Function, recv_method: Function): Object;
+
+ /**
+ * Sets the buffer to use when receiving server responses.
+ * @param {string} buf - The buffer to receive server responses.
+ */
+ setRecvBuffer(buf: string): void;
+
+ /**
+ * Returns true if the transport is open, in browser based JavaScript
+ * this function always returns true.
+ * @returns {boolean} Always True.
+ */
+ isOpen(): boolean;
+
+ /**
+ * Opens the transport connection, in browser based JavaScript
+ * this function is a nop.
+ */
+ open(): void;
+
+ /**
+ * Closes the transport connection, in browser based JavaScript
+ * this function is a nop.
+ */
+ close(): void;
+
+ /**
+ * Returns the specified number of characters from the response
+ * buffer.
+ * @param {number} len - The number of characters to return.
+ * @returns {string} Characters sent by the server.
+ */
+ read(len: number): string;
+
+ /**
+ * Returns the entire response buffer.
+ * @returns {string} Characters sent by the server.
+ */
+ readAll(): string;
+
+ /**
+ * Sets the send buffer to buf.
+ * @param {string} buf - The buffer to send.
+ */
+ write(buf: string): void;
+
+ /**
+ * Returns the send buffer.
+ * @returns {string} The send buffer.
+ */
+ getSendBuffer(): string;
+ }
+
+ /**
+ * Old alias of the TXHRTransport for backwards compatibility.
+ */
+ class Transport extends TXHRTransport { }
+
+ /**
+ * The Apache Thrift Transport layer performs byte level I/O
+ * between RPC clients and servers. The JavaScript TWebSocketTransport object
+ * uses the WebSocket protocol. Target servers must implement WebSocket.
+ */
+ class TWebSocketTransport {
+ url: string; //Where to connect
+ socket: any; //The web socket
+ callbacks: Function[]; //Pending callbacks
+ send_pending: any[]; //Buffers/Callback pairs waiting to be sent
+ send_buf: string; //Outbound data, immutable until sent
+ recv_buf: string; //Inbound data
+ rb_wpos: number; //Network write position in receive buffer
+ rb_rpos: number; //Client read position in receive buffer
+
+ /**
+ * Constructor Function for the WebSocket transport.
+ * @param {string } [url] - The URL to connect to.
+ */
+ constructor(url: string);
+
+ __reset(url: string): void;
+
+ /**
+ * Sends the current WS request and registers callback. The async
+ * parameter is ignored (WS flush is always async) and the callback
+ * function parameter is required.
+ * @param {object} async - Ignored.
+ * @param {function} callback - The client completion callback.
+ * @returns {undefined|string} Nothing (undefined)
+ */
+ flush(async: any, callback: Function): string;
+
+ __onOpen(): void;
+
+ __onClose(): void;
+
+ __onMessage(): void;
+
+ __onError(): void;
+
+ /**
+ * Sets the buffer to use when receiving server responses.
+ * @param {string} buf - The buffer to receive server responses.
+ */
+ setRecvBuffer(buf: string): void;
+
+ /**
+ * Returns true if the transport is open
+ * @returns {boolean}
+ */
+ isOpen(): boolean;
+
+ /**
+ * Opens the transport connection
+ */
+ open(): void;
+
+ /**
+ * Closes the transport connection
+ */
+ close(): void;
+
+ /**
+ * Returns the specified number of characters from the response
+ * buffer.
+ * @param {number} len - The number of characters to return.
+ * @returns {string} Characters sent by the server.
+ */
+ read(len: number): string;
+
+ /**
+ * Returns the entire response buffer.
+ * @returns {string} Characters sent by the server.
+ */
+ readAll(): string;
+
+ /**
+ * Sets the send buffer to buf.
+ * @param {string} buf - The buffer to send.
+ */
+ write(buf: string): void;
+
+ /**
+ * Returns the send buffer.
+ * @returns {string} The send buffer.
+ */
+ getSendBuffer(): string;
+ }
+
+ /**
+ * Apache Thrift Protocols perform serialization which enables cross
+ * language RPC. The Protocol type is the JavaScript browser implementation
+ * of the Apache Thrift TJSONProtocol.
+ */
+ class TJSONProtocol {
+ transport: Object;
+
+ /**
+ * Thrift IDL type Id to string mapping.
+ * The mapping table looks as follows:
+ * Thrift.Type.BOOL -> "tf": True/False integer.
+ * Thrift.Type.BYTE -> "i8": Signed 8 bit integer.
+ * Thrift.Type.I16 -> "i16": Signed 16 bit integer.
+ * Thrift.Type.I32 -> "i32": Signed 32 bit integer.
+ * Thrift.Type.I64 -> "i64": Signed 64 bit integer.
+ * Thrift.Type.DOUBLE -> "dbl": 64 bit IEEE 854 floating point.
+ * Thrift.Type.STRUCT -> "rec": A multifield type.
+ * Thrift.Type.STRING -> "str": Array of bytes representing a string of characters.
+ * Thrift.Type.MAP -> "map": A collection type (map/associative-array/dictionary).
+ * Thrift.Type.LIST -> "lst": A collection type (unordered).
+ * Thrift.Type.SET -> "set": A collection type (unordered and without repeated values).
+ */
+ Type: { [k: number]: string };
+
+ /**
+ * Thrift IDL type string to Id mapping.
+ * The mapping table looks as follows:
+ * "tf" -> Thrift.Type.BOOL
+ * "i8" -> Thrift.Type.BYTE
+ * "i16" -> Thrift.Type.I16
+ * "i32" -> Thrift.Type.I32
+ * "i64" -> Thrift.Type.I64
+ * "dbl" -> Thrift.Type.DOUBLE
+ * "rec" -> Thrift.Type.STRUCT
+ * "str" -> Thrift.Type.STRING
+ * "map" -> Thrift.Type.MAP
+ * "lst" -> Thrift.Type.LIST
+ * "set" -> Thrift.Type.SET
+ */
+ RType: { [k: string]: number };
+
+ /**
+ * The TJSONProtocol version number.
+ */
+ Version: number;
+
+ /**
+ * Initializes a Thrift JSON protocol instance.
+ * @param {Thrift.Transport} transport - The transport to serialize to/from.
+ */
+ constructor(transport: Object);
+
+ /**
+ * Returns the underlying transport.
+ * @returns {Thrift.Transport} The underlying transport.
+ */
+ getTransport(): Object;
+
+ /**
+ * Serializes the beginning of a Thrift RPC message.
+ * @param {string} name - The service method to call.
+ * @param {Thrift.MessageType} messageType - The type of method call.
+ * @param {number} seqid - The sequence number of this call (always 0 in Apache Thrift).
+ */
+ writeMessageBegin(name: string, messageType: number, seqid: number): void;
+
+ /**
+ * Serializes the end of a Thrift RPC message.
+ */
+ writeMessageEnd(): void;
+
+ /**
+ * Serializes the beginning of a struct.
+ * @param {string} name - The name of the struct.
+ */
+ writeStructBegin(name?: string): void;
+
+ /**
+ * Serializes the end of a struct.
+ */
+ writeStructEnd(): void;
+
+ /**
+ * Serializes the beginning of a struct field.
+ * @param {string} name - The name of the field.
+ * @param {Thrift.Protocol.Type} fieldType - The data type of the field.
+ * @param {number} fieldId - The field's unique identifier.
+ */
+ writeFieldBegin(name: string, fieldType: number, fieldId: number): void;
+
+ /**
+ * Serializes the end of a field.
+ */
+ writeFieldEnd(): void;
+
+ /**
+ * Serializes the end of the set of fields for a struct.
+ */
+ writeFieldStop(): void;
+
+ /**
+ * Serializes the beginning of a map collection.
+ * @param {Thrift.Type} keyType - The data type of the key.
+ * @param {Thrift.Type} valType - The data type of the value.
+ * @param {number} [size] - The number of elements in the map (ignored).
+ */
+ writeMapBegin(keyType: number, valType: number, size?: number): void;
+
+ /**
+ * Serializes the end of a map.
+ */
+ writeMapEnd(): void;
+
+ /**
+ * Serializes the beginning of a list collection.
+ * @param {Thrift.Type} elemType - The data type of the elements.
+ * @param {number} size - The number of elements in the list.
+ */
+ writeListBegin(elemType: number, size: number): void;
+
+ /**
+ * Serializes the end of a list.
+ */
+ writeListEnd(): void;
+
+ /**
+ * Serializes the beginning of a set collection.
+ * @param {Thrift.Type} elemType - The data type of the elements.
+ * @param {number} size - The number of elements in the list.
+ */
+ writeSetBegin(elemType: number, size: number): void;
+
+ /**
+ * Serializes the end of a set.
+ */
+ writeSetEnd(): void;
+
+ /** Serializes a boolean */
+ writeBool(value: boolean): void;
+
+ /** Serializes a number */
+ writeByte(i8: number): void;
+
+ /** Serializes a number */
+ writeI16(i16: number): void;
+
+ /** Serializes a number */
+ writeI32(i32: number): void;
+
+ /** Serializes a number */
+ writeI64(i64: number): void;
+
+ /** Serializes a number */
+ writeDouble(dbl: number): void;
+
+ /** Serializes a string */
+ writeString(str: string): void;
+
+ /** Serializes a string */
+ writeBinary(str: string): void;
+
+ /**
+ @class
+ @name AnonReadMessageBeginReturn
+ @property {string} fname - The name of the service method.
+ @property {Thrift.MessageType} mtype - The type of message call.
+ @property {number} rseqid - The sequence number of the message (0 in Thrift RPC).
+ */
+ /**
+ * Deserializes the beginning of a message.
+ * @returns {AnonReadMessageBeginReturn}
+ */
+ readMessageBegin(): { fname: string; mtype: number; rseqid: number };
+
+ /** Deserializes the end of a message. */
+ readMessageEnd(): void;
+
+ /**
+ * Deserializes the beginning of a struct.
+ * @param {string} [name] - The name of the struct (ignored).
+ * @returns {object} - An object with an empty string fname property.
+ */
+ readStructBegin(name?: string): { fname: string };
+
+ /** Deserializes the end of a struct. */
+ readStructEnd(): void;
+
+ /**
+ @class
+ @name AnonReadFieldBeginReturn
+ @property {string} fname - The name of the field (always '').
+ @property {Thrift.Type} ftype - The data type of the field.
+ @property {number} fid - The unique identifier of the field.
+ */
+ /**
+ * Deserializes the beginning of a field.
+ * @returns {AnonReadFieldBeginReturn}
+ */
+ readFieldBegin(): { fname: string; ftype: number; fid: number };
+
+ /** Deserializes the end of a field. */
+ readFieldEnd(): void;
+
+ /**
+ @class
+ @name AnonReadMapBeginReturn
+ @property {Thrift.Type} ktype - The data type of the key.
+ @property {Thrift.Type} vtype - The data type of the value.
+ @property {number} size - The number of elements in the map.
+ */
+ /**
+ * Deserializes the beginning of a map.
+ * @returns {AnonReadMapBeginReturn}
+ */
+ readMapBegin(): { ktype: number; vtype: number; size: number };
+
+ /** Deserializes the end of a map. */
+ readMapEnd(): void;
+
+ /**
+ @class
+ @name AnonReadColBeginReturn
+ @property {Thrift.Type} etype - The data type of the element.
+ @property {number} size - The number of elements in the collection.
+ */
+ /**
+ * Deserializes the beginning of a list.
+ * @returns {AnonReadColBeginReturn}
+ */
+ readListBegin(): { etype: number; size: number };
+
+ /** Deserializes the end of a list. */
+ readListEnd(): void;
+
+ /**
+ * Deserializes the beginning of a set.
+ * @param {Thrift.Type} elemType - The data type of the elements (ignored).
+ * @param {number} size - The number of elements in the list (ignored).
+ * @returns {AnonReadColBeginReturn}
+ */
+ readSetBegin(elemType?: number, size?: number): { etype: number; size: number };
+
+ /** Deserializes the end of a set. */
+ readSetEnd(): void;
+
+ /** Returns an object with a value property set to
+ * False unless the next number in the protocol buffer
+ * is 1, in which case the value property is True. */
+ readBool(): Object;
+
+ /** Returns an object with a value property set to the
+ next value found in the protocol buffer. */
+ readByte(): Object;
+
+ /** Returns an object with a value property set to the
+ next value found in the protocol buffer. */
+ readI16(): Object;
+
+ /** Returns an object with a value property set to the
+ next value found in the protocol buffer. */
+ readI32(f?: any): Object;
+
+ /** Returns an object with a value property set to the
+ next value found in the protocol buffer. */
+ readI64(): Object;
+
+ /** Returns an object with a value property set to the
+ next value found in the protocol buffer. */
+ readDouble(): Object;
+
+ /** Returns an object with a value property set to the
+ next value found in the protocol buffer. */
+ readString(): Object;
+
+ /** Returns an object with a value property set to the
+ next value found in the protocol buffer. */
+ readBinary(): Object;
+
+ /**
+ * Method to arbitrarily skip over data (not implemented).
+ */
+ skip(type: number): void;
+ }
+
+ /**
+ * Old alias of the TXHRTransport for backwards compatibility.
+ */
+ class Protocol extends TJSONProtocol { }
+
+ class MultiplexProtocol extends TJSONProtocol {
+ serviceName: string;
+
+ /**
+ * Initializes a MutilplexProtocol Implementation as a Wrapper for Thrift.Protocol.
+ * @param {string} srvName
+ * @param {Thrift.Transport} trans
+ * @param {any} [strictRead]
+ * @param {any} [strictWrite]
+ */
+ constructor(srvName: string, trans: Object, strictRead?: any, strictWrite?: any);
+
+ /**
+ * Override writeMessageBegin method of prototype
+ * Serializes the beginning of a Thrift RPC message.
+ * @param {string} name - The service method to call.
+ * @param {Thrift.MessageType} messageType - The type of method call.
+ * @param {number} seqid - The sequence number of this call (always 0 in Apache Thrift).
+ */
+ writeMessageBegin(name: string, type: number, seqid: number): void;
+ }
+
+ class Multiplexer {
+ seqid: number;
+
+ /**
+ * Instantiates a multiplexed client for a specific service.
+ * @param {String} serviceName - The transport to serialize to/from.
+ * @param {Thrift.ServiceClient} SCl - The Service Client Class.
+ * @param {Thrift.Transport} transport - Thrift.Transport instance which provides remote host:port.
+ */
+ createClient(serviceName: string, SCl: any, transport: Object): any;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/ts/tsconfig.json b/src/jaegertracing/thrift/lib/ts/tsconfig.json
new file mode 100644
index 000000000..97fa2258e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/ts/tsconfig.json
@@ -0,0 +1,31 @@
+{
+ "compilerOptions": {
+ "allowJs": false,
+ "alwaysStrict": true,
+ "baseUrl": ".",
+ "declaration": true,
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "noImplicitThis": true,
+ "noUnusedLocals": true,
+ "preserveConstEnums": true,
+ "removeComments": true,
+ "strictFunctionTypes": true,
+ "strictNullChecks": true,
+ "target": "es6",
+ "paths": {
+ "*": [
+ "*",
+ "test/",
+ "test/gen-js/*"
+
+ ]
+ },
+ },
+ "exclude": [
+ "./test/gen-nodejs/",
+ "./test/build/",
+ ]
+}
diff --git a/src/jaegertracing/thrift/lib/xml/Makefile.am b/src/jaegertracing/thrift/lib/xml/Makefile.am
new file mode 100644
index 000000000..bcad6bdd5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/xml/Makefile.am
@@ -0,0 +1,29 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+SUBDIRS =
+
+if WITH_JAVA
+# Schema validation test depends on java
+SUBDIRS += test
+endif
+
+EXTRA_DIST = \
+ thrift-idl.xsd \
+ test
diff --git a/src/jaegertracing/thrift/lib/xml/test/Makefile.am b/src/jaegertracing/thrift/lib/xml/test/Makefile.am
new file mode 100644
index 000000000..bb87a5203
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/xml/test/Makefile.am
@@ -0,0 +1,26 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+check:
+ $(ANT) $(ANT_FLAGS) test
+
+# Make sure this doesn't fail if ant is not configured.
+clean-local:
+ ANT=$(ANT) ; if test -z "$$ANT" ; then ANT=: ; fi ; \
+ $$ANT $(ANT_FLAGS) clean
diff --git a/src/jaegertracing/thrift/lib/xml/test/build.xml b/src/jaegertracing/thrift/lib/xml/test/build.xml
new file mode 100644
index 000000000..f0e95cf02
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/xml/test/build.xml
@@ -0,0 +1,112 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="XML Schema Test" default="test" basedir=".">
+
+ <description>XML Schema Validation Test</description>
+
+ <property name="xml.dir" location="${basedir}/.." />
+ <property name="gen.xml.dir" location="${basedir}/../gen-xml" />
+ <property name="idl.xml.schema" location="${xml.dir}/thrift-idl.xsd" />
+
+ <property name="thrift.dir" location="../../../" />
+ <property name="thrift.test.dir" location="${thrift.dir}/test" />
+ <property name="thrift.compiler" location="${thrift.dir}/compiler/cpp/thrift" />
+
+ <property file="${basedir}/build.properties" />
+
+ <target name="compiler.check">
+ <fail>
+ <condition>
+ <not>
+ <resourcecount count="1">
+ <fileset id="fs" file="${thrift.compiler}"/>
+ </resourcecount>
+ </not>
+ </condition>
+ Thrift compiler is missing !
+ </fail>
+ </target>
+
+ <target name="init" depends="compiler.check, mkdirs">
+ <tstamp />
+ </target>
+
+ <target name="mkdirs">
+ <mkdir dir="${gen.xml.dir}"/>
+ </target>
+
+ <target name="generate" depends="init">
+ <generate-xml file="${thrift.test.dir}/ThriftTest.thrift"/>
+ <generate-xml file="${thrift.test.dir}/Include.thrift"/>
+ <generate-xml file="${thrift.test.dir}/Recursive.thrift"/>
+ <generate-xml file="${thrift.test.dir}/ManyOptionals.thrift"/>
+ <generate-xml file="${thrift.test.dir}/OptionalRequiredTest.thrift"/>
+ <generate-xml file="${thrift.test.dir}/ConstantsDemo.thrift"/>
+ <generate-xml file="${thrift.test.dir}/TypedefTest.thrift" />
+ <generate-xml file="${thrift.test.dir}/AnnotationTest.thrift" />
+ <generate-xml file="${thrift.test.dir}/DocTest.thrift" />
+ <generate-xml file="${thrift.test.dir}/EnumTest.thrift" />
+ <generate-xml file="${thrift.test.dir}/ManyTypedefs.thrift" />
+ </target>
+
+ <target name="test" description="run schema validation"
+ depends="validate-generated-xml"/>
+
+ <target name="validate-generated-xml" depends="init, generate">
+ <validate-xml file="${gen.xml.dir}/ThriftTest.xml"/>
+ <validate-xml file="${gen.xml.dir}/Include.xml"/>
+ <validate-xml file="${gen.xml.dir}/Recursive.xml"/>
+ <validate-xml file="${gen.xml.dir}/ManyOptionals.xml"/>
+ <validate-xml file="${gen.xml.dir}/OptionalRequiredTest.xml"/>
+ <validate-xml file="${gen.xml.dir}/ConstantsDemo.xml"/>
+ <validate-xml file="${gen.xml.dir}/TypedefTest.xml"/>
+ <validate-xml file="${gen.xml.dir}/AnnotationTest.xml"/>
+ <validate-xml file="${gen.xml.dir}/DocTest.xml"/>
+ <validate-xml file="${gen.xml.dir}/EnumTest.xml"/>
+ <validate-xml file="${gen.xml.dir}/ManyTypedefs.xml"/>
+ </target>
+
+ <target name="clean">
+ <delete dir="${build.dir}" />
+ <delete dir="${gen.xml.dir}" />
+ </target>
+
+ <macrodef name="generate-xml">
+ <attribute name="file" />
+ <sequential>
+ <exec executable="${thrift.compiler}" failonerror="true">
+ <arg line="-gen xml:merge"/>
+ <arg line="-out ${gen.xml.dir}"/>
+ <arg line="@{file}"/>
+ </exec>
+ </sequential>
+ </macrodef>
+
+ <macrodef name="validate-xml">
+ <attribute name="file" />
+ <sequential>
+ <echo message="validating generated XML: @{file}" />
+ <schemavalidate file="@{file}">
+ <schema namespace="http://thrift.apache.org/xml/idl"
+ file="${idl.xml.schema}" />
+ </schemavalidate>
+ </sequential>
+ </macrodef>
+
+</project>
diff --git a/src/jaegertracing/thrift/lib/xml/thrift-idl.xsd b/src/jaegertracing/thrift/lib/xml/thrift-idl.xsd
new file mode 100644
index 000000000..09dd695e6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/xml/thrift-idl.xsd
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://thrift.apache.org/xml/idl"
+ xmlns:tns="http://thrift.apache.org/xml/idl"
+ elementFormDefault="qualified">
+
+ <element name="idl" type="tns:IDL" />
+ <element name="document" type="tns:Document" />
+
+ <complexType name="IDL">
+ <sequence>
+ <element ref="tns:document" minOccurs="1" maxOccurs="unbounded"/>
+ </sequence>
+ </complexType>
+
+ <complexType name="Document">
+ <sequence>
+ <choice minOccurs="0" maxOccurs="unbounded">
+ <element name="include" type="tns:Include" />
+ <element name="namespace" type="tns:Namespace" />
+ </choice>
+ <choice minOccurs="0" maxOccurs="unbounded">
+ <element name="exception" type="tns:Exception" />
+ <element name="typedef" type="tns:Typedef" />
+ <element name="service" type="tns:Service" />
+ <element name="struct" type="tns:Struct" />
+ <element name="const" type="tns:Const" />
+ <element name="union" type="tns:Union" />
+ <element name="enum" type="tns:Enum" />
+ </choice>
+ </sequence>
+ <attribute name="name" type="string" use="required" />
+ <attribute name="targetNamespace" type="anyURI" use="optional" />
+ <attribute name="doc" type="string" use="optional" />
+ </complexType>
+
+ <complexType name="Include">
+ <attribute name="file" type="string" use="optional" />
+ <attribute name="name" type="string" use="required" />
+ </complexType>
+
+ <complexType name="Namespace">
+ <sequence>
+ <element name="annotation" type="tns:Annotation"
+ minOccurs="0" maxOccurs="unbounded" />
+ </sequence>
+ <attribute name="name" type="string" use="required" />
+ <attribute name="value" type="string" use="required" />
+ <attribute name="doc" type="string" use="optional" />
+ </complexType>
+
+ <group name="AbstractStruct">
+ <sequence>
+ <element name="field" type="tns:Field"
+ minOccurs="0" maxOccurs="unbounded" />
+ <element name="annotation" type="tns:Annotation"
+ minOccurs="0" maxOccurs="unbounded" />
+ </sequence>
+ </group>
+
+ <attributeGroup name="StructAttributes">
+ <attribute name="name" type="string" use="required" />
+ <attribute name="doc" type="string" use="optional" />
+ </attributeGroup>
+
+ <complexType name="Exception">
+ <group ref="tns:AbstractStruct" />
+ <attributeGroup ref="tns:StructAttributes" />
+ </complexType>
+
+ <complexType name="Service">
+ <sequence>
+ <element name="method" type="tns:Method"
+ minOccurs="0" maxOccurs="unbounded" />
+ <element name="annotation" type="tns:Annotation"
+ minOccurs="0" maxOccurs="unbounded" />
+ </sequence>
+ <attribute name="name" type="string" use="required" />
+ <attribute name="targetNamespace" type="string" use="required" />
+ <attribute name="parent-module" type="string" use="optional" />
+ <attribute name="parent-id" type="string" use="optional" />
+ <attribute name="doc" type="string" use="optional" />
+ </complexType>
+
+ <complexType name="Method">
+ <sequence>
+ <element name="returns" type="tns:ThriftType" />
+ <element name="arg" type="tns:Field"
+ minOccurs="0" maxOccurs="unbounded" />
+ <element name="throws" type="tns:Field"
+ minOccurs="0" maxOccurs="unbounded" />
+ <element name="annotation" type="tns:Annotation"
+ minOccurs="0" maxOccurs="unbounded" />
+ </sequence>
+ <attribute name="name" type="string" use="required" />
+ <attribute name="oneway" type="boolean" use="optional" />
+ <attribute name="doc" type="string" use="optional" />
+ </complexType>
+
+ <complexType name="Typedef">
+ <complexContent>
+ <extension base="tns:ThriftType">
+ <sequence>
+ <element name="annotation" type="tns:Annotation"
+ minOccurs="0" maxOccurs="unbounded" />
+ </sequence>
+ <attribute name="name" type="string" use="required" />
+ <attribute name="doc" type="string" use="optional" />
+ </extension>
+ </complexContent>
+ </complexType>
+
+ <complexType name="Struct">
+ <group ref="tns:AbstractStruct" />
+ <attributeGroup ref="tns:StructAttributes" />
+ </complexType>
+
+ <complexType name="Union">
+ <group ref="tns:AbstractStruct" />
+ <attributeGroup ref="tns:StructAttributes" />
+ </complexType>
+
+ <complexType name="Enum">
+ <sequence>
+ <element name="member" minOccurs="1" maxOccurs="unbounded">
+ <complexType>
+ <sequence>
+ <element name="annotation" type="tns:Annotation"
+ minOccurs="0" maxOccurs="unbounded" />
+ </sequence>
+ <attribute name="name" type="string" use="required" />
+ <attribute name="value" type="int" />
+ <attribute name="explicit" type="boolean" />
+ <attribute name="doc" type="string" />
+ </complexType>
+ </element>
+ <element name="annotation" type="tns:Annotation"
+ minOccurs="0" maxOccurs="unbounded" />
+ </sequence>
+ <attribute name="name" type="string" use="required" />
+ <attribute name="doc" type="string" />
+ </complexType>
+
+ <complexType name="Field">
+ <complexContent>
+ <extension base="tns:ThriftType">
+ <sequence>
+ <element name="default" minOccurs="0" maxOccurs="1">
+ <complexType>
+ <group ref="tns:ConstValue" />
+ </complexType>
+ </element>
+ <element name="annotation" type="tns:Annotation"
+ minOccurs="0" maxOccurs="unbounded" />
+ </sequence>
+ <attribute name="field-id" type="long" />
+ <attribute name="name" type="string" use="required" />
+ <attribute name="required" type="tns:Requiredness" />
+ <attribute name="doc" type="string" />
+ </extension>
+ </complexContent>
+ </complexType>
+
+ <simpleType name="Requiredness">
+ <restriction base="string">
+ <enumeration value="required" />
+ <enumeration value="optional" />
+ </restriction>
+ </simpleType>
+
+ <complexType name="Annotation">
+ <attribute name="key" type="string" />
+ <attribute name="value" type="string" />
+ </complexType>
+
+ <complexType name="Const">
+ <complexContent>
+ <extension base="tns:ThriftType">
+ <sequence>
+ <group ref="tns:ConstValue" />
+ </sequence>
+ <attribute name="name" type="string" use="required" />
+ <attribute name="doc" type="string" />
+ </extension>
+ </complexContent>
+ </complexType>
+
+ <complexType name="ConstList">
+ <sequence>
+ <element name="entry" minOccurs="0" maxOccurs="unbounded">
+ <complexType>
+ <group ref="tns:ConstValue" />
+ </complexType>
+ </element>
+ </sequence>
+ </complexType>
+
+ <complexType name="ConstMap">
+ <sequence>
+ <element name="entry" minOccurs="0" maxOccurs="unbounded">
+ <complexType>
+ <sequence>
+ <element name="key">
+ <complexType>
+ <group ref="tns:ConstValue" />
+ </complexType>
+ </element>
+ <element name="value">
+ <complexType>
+ <group ref="tns:ConstValue" />
+ </complexType>
+ </element>
+ </sequence>
+ </complexType>
+ </element>
+ </sequence>
+ </complexType>
+
+ <group name="ConstValue">
+ <choice>
+ <element name="string" type="string" />
+ <element name="double" type="double" />
+ <element name="list" type="tns:ConstList" />
+ <element name="map" type="tns:ConstMap" />
+ <element name="int" type="long" />
+ </choice>
+ </group>
+
+ <complexType name="ThriftType">
+ <sequence>
+ <choice minOccurs="0" maxOccurs="1">
+ <element name="elemType" type="tns:ThriftType" />
+ <sequence>
+ <element name="keyType" type="tns:ThriftType"
+ minOccurs="1" maxOccurs="1" />
+ <element name="valueType" type="tns:ThriftType"
+ minOccurs="1" maxOccurs="1" />
+ </sequence>
+ </choice>
+ </sequence>
+ <attribute name="type" type="tns:TypeIdentifier" use="required" />
+ <attribute name="type-module" type="string" />
+ <attribute name="type-id" type="string" />
+ </complexType>
+
+ <simpleType name="TypeIdentifier">
+ <restriction base="string">
+ <enumeration value="void" />
+ <enumeration value="bool" />
+ <enumeration value="byte" />
+ <enumeration value="i8" />
+ <enumeration value="i16" />
+ <enumeration value="i32" />
+ <enumeration value="i64" />
+ <enumeration value="double" />
+ <enumeration value="binary" />
+ <enumeration value="string" />
+ <enumeration value="id" />
+ <enumeration value="map" />
+ <enumeration value="set" />
+ <enumeration value="list" />
+ </restriction>
+ </simpleType>
+
+</schema>